【发布时间】:2019-04-26 07:20:49
【问题描述】:
我正在编写一个程序,它使用 MPU9250 的内部 FIFO 读取 MPU9250 的加速度计和陀螺仪数据并提供 Web 界面。
没有网络访问,一切都很好。
但是,在 Web 请求的确切时间上,在该时间读取的字节数会发生变化。
我当前的代码只是简单地显示模板网页并仅在其幅度大于 2.5 时打印 IMU 加速度计值。所以,如果我不触摸 MPU9250 传感器,它的值应该在 0.9~1.1 之间。但是,尽管完全没有触摸 MPU9250 传感器,但在刷新网页(目前在 0.5 秒内自动刷新)时,它会打印一些大于 2.5 的错误值。
我正在使用 LOLIN D32 PRO 板。并且MPU9250连接的是其默认的VSPI总线。
我在arduino的loop函数上读取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();
}
【问题讨论】:
-
自我回答。这只是缓冲区溢出。由于MPU9250的buffer在10ms内填满,server.handleClient耗时超过10ms,导致buffer溢出。我想我可以通过使用多核方法来解决这个问题,但是如果我只有单核,我不知道如何处理这种情况......