0

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

这是完全不正常的值。

我的猜测是,

  1. 强 RF 信号会影响 SPI 总线,因此信号会发生变化。
  2. wifi TX 例程在 MPU9250 FIFO 读取过程中被调用,它会在 FIFO 读取过程中导致数据丢失。

不管是什么原因,我不知道如何解决这个问题。

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

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

图片1

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

网络服务器代码:

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

1 回答 1

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 数据表第 6 页的最后一行:状态:

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

下一行:

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

FIFO 寄存器(数据表第 27 页第 4.17 节将 FIFO 称为寄存器)既不是传感器也不是中断寄存器;因此通信应限制在 1 MHZ。任何高于此速率的扫描都可能(并且会在异相请求的情况下)给出无效/不稳定的结果。

我无法找到该MPU9250库的参考或给出的命令,以验证您的代码是否属于这种情况。

于 2019-05-10T23:16:07.163 回答