新手213123 发表于 2015-4-17 20:13:57

MPU6050采集加速度一段时间后变成0

如题,我在用mpu6050做计步器,数据处理是在定时器中断里面写的。但是有一个问题就是,加速度采集的数据会周期性的从正常变到0在逐渐正常,往返很多次后,数据就一直为0了。如图所示,那根蓝色的线就是加速度,不知道到底是什么原因。很纠结!谢谢大神们的帮忙

suoma 发表于 2015-4-18 07:54:05

论坛里有相关案例,可以参考

新手213123 发表于 2015-4-18 11:55:13

suoma 发表于 2015-4-18 07:54 static/image/common/back.gif
论坛里有相关案例,可以参考

您好我知道看到一个输出全为0的帖子,跟我这个情况不太一样, 别的并没有找到。您可否告诉我一个帖子?谢谢!

suoma 发表于 2015-4-18 12:07:36

热搜MPU6050,我今天帮不了你

tsaiwn 发表于 2015-4-18 13:01:47

貌似你在定时器中断里面写太多事 ?
要有代码才能看出问题

新手213123 发表于 2015-4-18 16:18:14

tsaiwn 发表于 2015-4-18 13:01 static/image/common/back.gif
貌似你在定时器中断里面写太多事 ?
要有代码才能看出问题

那我把代码发上来看看吧!就是有点长,希望您可以帮帮忙~谢啦

新手213123 发表于 2015-4-18 16:19:35

//定时器T3中断处理函数
#pragma vector = T3_VECTOR
__interrupt void T3_ISR(void)
{
    IRCON = 0x00;         //清中断标志, 也可由硬件自动完成
    if(Count_20ms++ >= Sample_Period)      //取10时正好20ms,考虑到AD采样,应适当减少。
    {               
      LED1 = ~LED1;
      Count_20ms = 0;
      
      Result_Test = AD_Sample();
   
      Sample_AD = First_Order_Filter();   
      sprintf(strTemp, "%7d \t %7d \t %7d \t %7d \t %7d \t %7d \n", \
                     Sample_AD, step, Peak, Valley, Discrepency, time_peak);      
      UartSendString(strTemp, strlen(strTemp));   

      Find_Peak();
    }
}
/****************************************************************************
* 名    称: Find_Peak()
* 功    能: 峰峰值方法检测迈步
* 出口参数: 无
****************************************************************************/
void Find_Peak()
{
int16_t Add_Data;

Add_Data =Sample_AD;      
for(Move_Data = 0; Move_Data < Count_Window_Peak; Move_Data++)//数据搬移,所有数据往前一位,此处尽量修改
    Value_Buf = Value_Buf;

Value_Buf = Add_Data;      //添加新的数据
time_peak++;                                 //用来计算峰峰值之间的时间

if(Start == Flag_Find_Peak)
{
    if(Value_Buf<Value_Buf&&Value_Buf<Value_Buf&&Value_Buf<Value_Buf&&
      Value_Buf>Value_Buf&&Value_Buf>Value_Buf&&Value_Buf>Value_Buf)
    {
      Peak = Value_Buf;
      Flag_Find_Peak = Stop;
      time_peak = 0;                              //找到峰值时,time_peak重新开始计数
    }
}
if(Stop == Flag_Find_Peak)
{
    if(Value_Buf>Value_Buf&&Value_Buf>Value_Buf&&Value_Buf>Value_Buf&&
       Value_Buf<Value_Buf&&Value_Buf<Value_Buf&&Value_Buf<Value_Buf)
    {
      if(Low_Freq < Sample_Period*time_peak < High_Freq) //Sample_Period*time_peak计算峰峰值之间的时间
      {                                             //在限定时间内才算有效步伐
      Valley = Value_Buf;
      Flag_Find_Peak = Start;
      Flag_Start_Compare = Start;
      }
      else                                          //若不在限定时间内则算无效,需重新开始
      Flag_Find_Peak = Start;
    }
}
if(Start == Flag_Start_Compare )
{
    Discrepency = Peak-Valley;
    //得到Discrepency后,开始进行迈步检测
    if( Discrepency > 100)
    {
      if( Search_Mode == Mode )       //搜索模式,判断是否为短暂的行为
      {
      trans_step++;
      if( 5 == trans_step )      //若检测到连续的五次迈步,则进入确认模式
      {
          Mode = Confirm_Mode;
          trans_step = 0;
          stop_test = 0;
      }
      }
      if( Confirm_Mode == Mode )            //确认模式
      {
      if( TRUE == First_Confirm_Mode )   //第一次进入确认模式,需加上5此检测迈步
      {
          step = step + 5;
          First_Confirm_Mode = FAILURE;      
      }
      else
          step++;
      }
    }
    if(Discrepency <= 100)               //若差值过小,则说明不是正常迈步
    {
      stop_test++;
      if( 5 == stop_test )            //连续五次检测阈值过小,则重新进入搜索模式
      {
      First_Confirm_Mode = TRUE;
      Mode = Search_Mode;
      trans_step = 0;
      stop_test = 0;
      }
    }
    Flag_Start_Compare = Stop;
}
if (800 == time_peak)
{
    First_Confirm_Mode = TRUE;
    Mode = Search_Mode;
    trans_step = 0;
    stop_test = 0;
    time_peak = 0;
}
}
/****************************************************************************
* 名    称: First_Order_Filter()
* 功    能: 一阶惯性滤波
* 出口参数: 滤波的数值
****************************************************************************/
int16_t First_Order_Filter()
{
float Trans;

Current_Result =Result_Test;
                               
Trans = Filter_Param_1 * Current_Result;
Trans = Trans + Pre_Result;
Filter_Result= (int16_t)Trans;                                                 //送D/A输出
Pre_Result = Filter_Param * Trans;

return (Filter_Result);
}

/****************************************************************************
* 名    称: AD_Sample()
* 功    能: 一阶惯性滤波
* 出口参数: 滤波的数值
****************************************************************************/
int16_t AD_Sample()
{
int16_t ax, ay, az,sum;
MPU6050_GetRawAccelGyro(&ax, &ay, &az);

ax = ax>>3;
ay = ay>>3;
az = az>>3;

if(ax<0) ax = -ax;
if(ay<0) ay = -ay;
if(az<0) az = -az;

sum = (ax + ay + az)>>1;
return (sum);
}
没发主程序,因为主程序只有一些初始化,然后就是死循环了,死循环里面什么都没做

tsaiwn 发表于 2015-4-18 19:11:05

新手213123 发表于 2015-4-18 16:19 static/image/common/back.gif
//定时器T3中断处理函数
#pragma vector = T3_VECTOR
__interrupt void T3_ISR(void)



果然你在中断 __interrupt void T3_ISR(void) 里面做太多事了 !
其中最慢的是 UartSendString(strTemp, strlen(strTemp));   
由你的 sprintf( )可算出 strTemp 有 59 bytes;
   UartSendString( ) 是类似 Serial.print( ),
只是 Serial.print( ) 有串口缓存区,
只要缓存区没满会立即返回, 真正送出的动作不会占用 CPU 的时间,
但是 UartSendString( ) 则是自己 byte by byte 送给 UART (串口)寄存器 !
如果你是用波特率 9600, 每送出一 char 要大约 1.05 ms,
送出 59 个就花掉了 60ms;
如果你用波特率 115200, 则大约也要 6ms 左右!
可以先试着把事情移出 ISR( ),
改放到 loop( ) 内,
在 ISR( ) 内设定一个 flag 通知在 loop( ) 内要做采集相关工作,
int hasJob = 0;// 有事要做 !
__interrupt void T3_ISR(void)
{
    IRCON = 0x00;         //清中断标志, 也可由硬件自动完成
    if(Count_20ms++ >= Sample_Period)      //取10时正好20ms,考虑到AD采样,应适当减少。
    {               
      LED1 = ~LED1;
      Count_20ms = 0;
      hasJob = 1; // 通知 loop 内的程序码 !
    }
} // ISR()
当然, 在 loop( ) 内要检查 flag 并清除以免重复做, 类似这样:
if(hasJob != 0) {
    hasJob = 0;
    // do 采集相关工作 ...
}

tsaiwn 发表于 2015-4-18 19:23:53

tsaiwn 发表于 2015-4-18 19:11 static/image/common/back.gif
果然你在中断 __interrupt void T3_ISR(void) 里面做太多事了 !
其中最慢的是 UartSendString(strTemp ...

不过如果你的中断来得太快,
可能会有漏掉的情况(因为来不及处理),
你有估计过中断大约多久一次吗?

新手213123 发表于 2015-4-18 21:12:04

tsaiwn 发表于 2015-4-18 19:23 static/image/common/back.gif
不过如果你的中断来得太快,
可能会有漏掉的情况(因为来不及处理),
你有估计过中断大约多久一次吗?

谢谢!我一直以为是ad转换最浪费时间,没想到是串口最费。我的中断是例程里面考过来的。据说进入10次为20ms。不过我现在放弃用中断来处理了,我把这些都放到主程序里面,问题就解决了!谢啦!

tsaiwn 发表于 2015-4-18 23:07:34

新手213123 发表于 2015-4-18 21:12 static/image/common/back.gif
谢谢!我一直以为是ad转换最浪费时间,没想到是串口最费。我的中断是例程里面考过来的。据说进入10次为20 ...

(1)        Congratulations
(2)进入10次为20ms, 那意思是每 2ms 进入一次
   问题是, 你原先的写法,
   即使你用波特率 115200,
   则大约也要 6ms 左右才有办法从 UartSendString( ) 返回,
   其他你做的估计大约 0.5ms 可做完,
   可是在该 6.5ms 内无法产生中断(因为中断内是禁止中断的)!
   这意思是说,
   本来是每 2ms 中断一次,
   但是当if(Count_20ms++ >= Sample_Period) 成立之时,
   会有大约 6.5ms 不会有中断(因为在忙着做UartSendString( ) 的事情) !!
注意, 在这 6.5ms 中, 连 millis( ) 也不会变化,
因为 millis( ) 是靠中断才能改变, 既然中断被禁止, millis( ) 就不会变化了 !

新手213123 发表于 2015-4-21 20:30:23

tsaiwn 发表于 2015-4-18 23:07 static/image/common/back.gif
(1)        Congratulations
(2)进入10次为20ms, 那意思是每 2ms 进入一次
   问题是, 你原先的写法,


好深奥!我以前一直怕中断里面进中断。那这样的话,之前那个一直出现0会是什么原因呢?

新手213123 发表于 2015-4-21 20:30:47

tsaiwn 发表于 2015-4-18 23:07 static/image/common/back.gif
(1)        Congratulations
(2)进入10次为20ms, 那意思是每 2ms 进入一次
   问题是, 你原先的写法,


还有一个问题是,怎么估算程序运行的时间?谢谢

tsaiwn 发表于 2015-4-22 01:04:21

新手213123 发表于 2015-4-21 20:30 static/image/common/back.gif
好深奥!我以前一直怕中断里面进中断。那这样的话,之前那个一直出现0会是什么原因呢?


之前因为你在中断内做太多事,
导致太长时间没有中断, 那样取样就错了,
其实应该不只会出现 0,
你那些不是 0 的答案应该也都不太正确 !

tsaiwn 发表于 2015-4-22 01:19:19

新手213123 发表于 2015-4-21 20:30 static/image/common/back.gif
还有一个问题是,怎么估算程序运行的时间?谢谢

因为 UNO 和大部分板子用 16MHz clock,
这样就是 16 个machine clock 要 1 us,
通常 C 语言的一个句子大约 4 个指令 到 10 个machine clock
例如 byte x = 65;这句要 2 个machine clock,
又如 unsigned long kk = 12345678;这句要 8 个machine clock
(因为 long 占用 4 个 bytes);
函数调用大约 4 个 clock, 如果有参数要额外时间,
又如果函数返回要把 value 放入 variable也要时间,
不过都只是多几个 clock,
但是调用 analogRead( ) 一次大约要 108 us( 因为要等 A to D 完成!)
请注意, 108us 是很久喔!
Serial 传送虽然很慢, 但只要你没有把串口的缓存区用满,
可以当作它不花 CPU 的时间(因为每个 char 大约花 3 us),
但是, 一旦你会把串口的缓存区用满,,
那多出的 char 无法送入缓存区, CPU 必须用 while( ) 等待无法做其他事,
这时(假设波特率9600)等于 CPU 要等待 1.1 ms 才送出一个 char !!
请注意, 1 ms = 1024 us 喔 !
有了这些概念
就可以估算出大略时间
如果要比较准
可在前后各调用一次 micros( ) 相减查知 !
页: [1] 2
查看完整版本: MPU6050采集加速度一段时间后变成0