ESP32的无线访问中断MPU9250的读取

时间:2019-04-26 07:20:49

标签: arduino esp32 imu

我正在编写一个程序,该程序使用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或在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                                                                           

完全是异常值。

我的猜测是

  1. 强大的RF信号会影响SPI总线,因此会改变信号。
  2. wifi TX例程在MPU9250 FIFO读取过程中被调用,并且在FIFO读取期间导致数据丢失。

无论是什么原因,我都不知道如何解决该问题。

任何可能的原因/解决方案将不胜感激。

以下是我目前的接线。但是,没什么特别的。 MPU9250连接到默认的VSPI端口,而INT引脚连接到GPIO34。其他未使用的连接。

PICTURE 1

PICTURE 2

下面的完整源代码供您参考。

主要代码:

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();
}

WEBSERVER CODE:

#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();
}

1 个答案:

答案 0 :(得分:0)

对于单处理器解决方案,我可以看到两种解决方案(因为您尚未说明最终目标,所以一个目标可能比另一个目标更为理想):

  1. 如果可以丢失大约15-25毫秒的数据(应该轻拍传感器的位置,则仍然可以记录),将spiread_fifo(...)中的spiread_raw_accgyr(buffer)更改为loop() 。这将在执行时将当前值读入缓冲区。绝对不适用于位置计算或地震事件的计算。

  2. 在中断例程(MPU9250中的硬件引脚或计时器)中读取和分析所需的数据,因为Web客户端更宽容了延迟(在合理范围内)。

无论哪种方式,都应该优化计算以减少指令数量(ESP32没有FPU,因此所有浮点运算都必须在软件中进行仿真):

行:

double m = sqrt( x * x + y * y + z * z ) / 2048;
  if(  m > 2.5 )

可以通过将等式的两边代数平方为:来简化(如果需要完美的(球面)精度):

double m =  (x * x + y * y + z * z)  / 4194304;
  if (m > 6.25)

或(最好,但在组合轴时精度稍差):

double m =  ((abs(x) + abs(y) + abs(z))  / 2048); // Manhattan distance(a diamond in 2D, a four sided, double pyramid in 3d)
if (m > 2.5)

MPU9250 Datasheet第6页的最后一行: 状态:

  

1MHz SPI串行接口,用于与所有寄存器通信

下一行:

  

20MHz SPI串行接口,用于读取传感器和中断寄存器

FIFO寄存器(Page 27 section 4.17 of the datasheet将FIFO称为寄存器)既不是传感器也不是中断寄存器。因此,通信应限制在1 MHZ。超过此速率的任何扫描都可能(并且在异相请求的情况下)将给出无效/不稳定的结果。

我无法找到MPU9250库或给定命令的引用来验证您的代码是否属于这种情况。