我正在编写一个程序,它使用 MPU9250 的内部 FIFO 读取 MPU9250 的加速度计和陀螺仪数据并提供 Web 界面。
没有网络访问,一切都很好。
但是,在 Web 请求的确切时间上,在该时间读取的字节数会发生变化。
我当前的代码只是简单地显示模板网页并仅在其幅度大于 2.5 时打印 IMU 加速度计值。所以,如果我不触摸 MPU9250 传感器,它的值应该在 0.9~1.1 之间。但是,尽管完全没有触摸 MPU9250 传感器,但在刷新网页时(目前在 0.5 秒内自动刷新),它会打印一些大于 2.5 的错误值。
我正在使用 LOLIN D32 PRO 板。和 MPU9250 连接其默认的 VSPI 总线。
我在arduino的循环功能上读取了MPU 9250的FIFO数据,如下所示,
while( readBytes = spiread_fifo( buffer + readBytes_all / 2 ) )
readBytes_all += readBytes;
if ( readBytes_all == 0 )
return;
if( digitalRead( 4 ) == 1 ){
int x = buffer[ 0 ];
int y = buffer[ 1 ];
int z = buffer[ 2 ];
double m = sqrt( x * x + y * y + z * z ) / 2048;
if( m > 2.5 )
Serial.println( m );
spiread_fifo 定义如下
int spiread_fifo( volatile short * buffer ) {
int fifo_len = spiread( 0x72 ) * 256 + spiread( 0x73 );
if ( fifo_len > 512 )
return -1;
if ( fifo_len == 0 )
return 0;
MPU9250.beginTransaction( settingsB );
digitalWrite( SS, LOW );
MPU9250.transfer( 0x74 | 0x80 );
MPU9250.transfer( 0x00 ); // if I use SPI CLOCK more than 8~12Mhz, it gives me duplicated byte at the beginning. So just drop one of them.
for ( int i = 0; i < 3; i++ )
buffer[ i ] = MPU9250.transfer16( 0x00 );
for ( int i = 3; i < fifo_len / 2; i++ )
MPU9250.transfer16( 0x00 );
digitalWrite( SS, HIGH );
MPU9250.endTransaction();
for ( int i = 0; i < 12; i++ )
__asm__ __volatile__ ("nop\n\t");
return fifo_len;
}
如果我没有在 MPU9250 上触摸或施加任何力,串行控制台应该是静音的,如果没有 ESP32 的 webserber 访问,它实际上会静音。然而,它在网页访问的时间上给出了一些随机值,如下所示。(单个网页访问单个值)
15.53
16.11
15.60
13.59
16.86
2.55
2.55
3.85
3.85
3.37
6.79
2.63
2.56
5.80
10.18
5.88
3.65
5.80
5.48
2.95
4.01
4.01
3.10
2.90
3.17
9.31
14.97
7.08
16.29
这是完全不正常的值。
我的猜测是,
- 强 RF 信号会影响 SPI 总线,因此信号会发生变化。
- wifi TX 例程在 MPU9250 FIFO 读取过程中被调用,它会在 FIFO 读取过程中导致数据丢失。
不管是什么原因,我不知道如何解决这个问题。
任何可能的原因/解决方案将不胜感激。
以下是我目前的接线。然而,没有什么特别的。MPU9250 连接到默认的 VSPI 端口,INT 引脚连接到 GPIO34。其他剩余连接不使用。
下面的完整源代码供您参考。
主要代码:
extern volatile int cnt;
volatile short* buffer;
volatile short* buffer2;
volatile unsigned long *timestamp;
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
void printHex( int num, int precision) {
char tmp[16];
char format[128];
sprintf(format, "%%.%dX ", precision);
sprintf(tmp, format, num);
if ( strlen( tmp ) > precision + 1 ) {
int l = strlen( tmp ) - precision - 1;
for ( int i = 0; i < precision + 2; i++ ) {
tmp[ i ] = tmp[ i + l ];
}
}
Serial.print(tmp);
}
void setup() {
Serial.begin( 2000000 );
Serial.println( "Turning on...." );
buffer = ( short * )ps_malloc( 1000000 );
buffer2 = ( short * )ps_malloc( 1000000 );
BUTTONSetup();
MPU9250Setup();
OTASetup();
WEBSERVERSetup();
Serial.println( "Setup finished." );
}
void loop() {
OTAHandle();
WEBSERVERHandle();
int readBytes = 0, readBytes_all = 0;
while( readBytes = spiread_fifo( buffer + readBytes_all / 2 ) )
readBytes_all += readBytes;
if ( readBytes_all == 0 )
return;
if( digitalRead( 4 ) == 1 ){
int x = buffer[ 0 ];
int y = buffer[ 1 ];
int z = buffer[ 2 ];
double m = sqrt( x * x + y * y + z * z ) / 2048;
if( m > 2.5 )
Serial.println( m );
BUTTONHandle();
}
}
MPU9250 代码:
#include <SPI.h>
#define SCK 18
#define MISO 19
#define MOSI 23
#define SS 5
#define INT 34
SPIClass MPU9250( VSPI );
SPISettings settingsA( 1000000, MSBFIRST, SPI_MODE3 );
SPISettings settingsB( 20000000, MSBFIRST, SPI_MODE3 );
volatile int cnt = 0;
void IRAM_ATTR onInterrupt() {
portENTER_CRITICAL_ISR(&mux);
cnt++;
portEXIT_CRITICAL_ISR(&mux);
}
void spiwrite( byte a, byte b ) {
MPU9250.beginTransaction( settingsA );
digitalWrite( SS, LOW );
MPU9250.transfer( a );
MPU9250.transfer( b );
digitalWrite( SS, HIGH );
MPU9250.endTransaction();
}
byte spiread( byte a ) {
MPU9250.beginTransaction( settingsB );
digitalWrite( SS, LOW );
MPU9250.transfer( a | 0x80 );
byte r = MPU9250.transfer( 0x00 );
digitalWrite( SS, HIGH );
MPU9250.endTransaction();
return r;
}
int spiread_fifo( volatile short * buffer ) {
int fifo_len = spiread( 0x72 ) * 256 + spiread( 0x73 );
// fifo_len += 12;
// fifo_len = fifo_len / 12 * 12;
if ( fifo_len > 512 )
return -1;
if ( fifo_len == 0 )
return 0;
MPU9250.beginTransaction( settingsB );
digitalWrite( SS, LOW );
MPU9250.transfer( 0x74 | 0x80 );
MPU9250.transfer( 0x00 ); // if I use SPI CLOCK more than 8~12Mhz, it gives me duplicated byte at the beginning. So just drop one of them.
for ( int i = 0; i < 3; i++ )
buffer[ i ] = MPU9250.transfer16( 0x00 );
for ( int i = 3; i < fifo_len / 2; i++ )
MPU9250.transfer16( 0x00 );
// for( int i = fifo_len / 2 + 1; i < fifo_len; i++ )
// buffer[ i ] = 0;
digitalWrite( SS, HIGH );
MPU9250.endTransaction();
for ( int i = 0; i < 12; i++ )
__asm__ __volatile__ ("nop\n\t");
return fifo_len;
}
void spiread_raw( volatile short * buffer ) {
MPU9250.beginTransaction( settingsB );
digitalWrite( SS, LOW );
MPU9250.transfer( 0x3b | 0x80 );
for ( int i = 0; i < 3; i++ )
buffer[ i ] = MPU9250.transfer16( 0x00 );
digitalWrite( SS, HIGH );
MPU9250.endTransaction();
for ( int i = 0; i < 12; i++ )
__asm__ __volatile__ ("nop\n\t");
}
void spiread_raw_gyr( volatile short * buffer ) {
MPU9250.beginTransaction( settingsB );
digitalWrite( SS, LOW );
MPU9250.transfer( 0x43 | 0x80 );
for ( int i = 0; i < 3; i++ )
buffer[ i ] = MPU9250.transfer16( 0x00 );
digitalWrite( SS, HIGH );
MPU9250.endTransaction();
for ( int i = 0; i < 12; i++ )
__asm__ __volatile__ ("nop\n\t");
}
void spiread_raw_accgyr( volatile short * buffer ) {
MPU9250.beginTransaction( settingsB );
digitalWrite( SS, LOW );
MPU9250.transfer( 0x3b | 0x80 );
for ( int i = 0; i < 3; i++ )
buffer[ i ] = MPU9250.transfer16( 0x00 );
MPU9250.transfer16( 0x00 );
for ( int i = 3; i < 6; i++ )
buffer[ i ] = MPU9250.transfer16( 0x00 );
digitalWrite( SS, HIGH );
MPU9250.endTransaction();
for ( int i = 0; i < 12; i++ )
__asm__ __volatile__ ("nop\n\t");
}
void MPU9250Setup(){
pinMode( SS, OUTPUT );
pinMode( SCK, OUTPUT );
pinMode( MOSI, OUTPUT );
pinMode( INT, INPUT_PULLUP );
pinMode( MISO, INPUT );
pinMode( 4, INPUT );
MPU9250.begin( SCK, MISO, MOSI, SS ); //CLK,MISO,MOIS,SS
attachInterrupt( digitalPinToInterrupt( INT ), onInterrupt, FALLING );
spiwrite( 0x68, 0x07 );
spiwrite( 0x6A, 0x55 ); // FIFO_EN = 1, FIFO_RST = 1;
spiwrite( 0x19, 0x00 ); // SMPLRT_DIV = 0
spiwrite( 0x1B, 0x18 ); // GYRO_FS_SEL = 3, Fchoice_b = 0
spiwrite( 0x1C, 0x18 ); // ACCEL_FS_SEL = 3
spiwrite( 0x1D, 0x08 ); // accel_fchoice_b = 1
// spiwrite( 0x23, 0x78 ); // TEMP_OUT = 0, GYRO_XOUT = 1, GYRO_YOUT = 1, GYRO_ZOUT = 1, ACCEL = 1
// spiwrite( 0x23, 0x79 ); // TEMP_OUT = 0, GYRO_XOUT = 0, GYRO_YOUT = 0, GYRO_ZOUT = 0, ACCEL = 1
spiwrite( 0x23, 0x08 ); // TEMP_OUT = 0, GYRO_XOUT = 0, GYRO_YOUT = 0, GYRO_ZOUT = 0, ACCEL = 1
spiwrite( 0x37, 0x10 ); // INT_ANYRD_2CLEAR = 1
spiwrite( 0x38, 0xC1 ); // ACTL = 1, OPEN = 1, RAW_RDY_EN = 1
spiwrite( 0x1A, 0x07 ); // FIFO_MODE = 0, EXT_SYNC_SET = 0, DLPF_CFG = 7
}
OTA固件代码:
#include <WiFi.h>
#include "esp_wifi.h"
#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
const char* ssid = "XXXXXX";
const char* password = "XXXXXX";
void OTASetup(){
delay( 100 );
esp_wifi_set_max_tx_power( -100 );
WiFi.mode( WIFI_STA );
WiFi.setHostname( "LOLIN_D32_PRO_Sunkyue" );
delay( 100 );
WiFi.begin( ssid, password );
while( WiFi.waitForConnectResult() != WL_CONNECTED ){
Serial.println( "Connection Failed! Rebooting..." );
delay( 10 );
ESP.restart();
}
ArduinoOTA.setPort(53232);
ArduinoOTA.setHostname("LOLIN_D32_PRO_Sunkyue");
ArduinoOTA.setPassword("XXXXXX");
ArduinoOTA
.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH)
type = "sketch";
else // U_SPIFFS
type = "filesystem";
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
Serial.println("Start updating " + type);
})
.onEnd([]() {
Serial.println("\nEnd");
})
.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
})
.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
ArduinoOTA.begin();
Serial.println("Ready");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void OTAHandle(){
ArduinoOTA.handle();
}
网络服务器代码:
#include <WiFiClient.h>
#include <WebServer.h>
WebServer server(80);
void handleRoot() {
char temp[400];
int sec = millis() / 1000;
int min = sec / 60;
int hr = min / 60;
snprintf(temp, 400,
"<html>\
<head>\
<meta http-equiv='refresh' content='0.5'/>\
<title>ESP32 Demo</title>\
<style>\
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
</style>\
</head>\
<body>\
<h1>Hello from ESP32!</h1>\
<p>Uptime: %02d:%02d:%02d</p>\
<img src=\"/test.svg\" />\
</body>\
</html>",
hr, min % 60, sec % 60
);
server.send(200, "text/html", temp);
}
void handleNotFound() {
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
}
void drawGraph() {
String out = "";
char temp[100];
out += "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"400\" height=\"150\">\n";
out += "<rect width=\"400\" height=\"150\" fill=\"rgb(250, 230, 210)\" stroke-width=\"1\" stroke=\"rgb(0, 0, 0)\" />\n";
out += "<g stroke=\"black\">\n";
int y = rand() % 130;
for (int x = 10; x < 390; x += 10) {
int y2 = rand() % 130;
sprintf(temp, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke-width=\"1\" />\n", x, 140 - y, x + 10, 140 - y2);
out += temp;
y = y2;
}
out += "</g>\n</svg>\n";
server.send(200, "image/svg+xml", out);
}
void WEBSERVERSetup(){
if (MDNS.begin("esp32")) {
Serial.println("MDNS responder started");
}
server.on("/", handleRoot);
server.on("/test.svg", drawGraph);
server.on("/inline", []() {
server.send(200, "text/plain", "this works as well");
});
server.onNotFound(handleNotFound);
server.begin();
}
void WEBSERVERHandle(){
server.handleClient();
}