MPU6050采集加速度一段时间后变成0
如题,我在用mpu6050做计步器,数据处理是在定时器中断里面写的。但是有一个问题就是,加速度采集的数据会周期性的从正常变到0在逐渐正常,往返很多次后,数据就一直为0了。如图所示,那根蓝色的线就是加速度,不知道到底是什么原因。很纠结!谢谢大神们的帮忙 论坛里有相关案例,可以参考 suoma 发表于 2015-4-18 07:54 static/image/common/back.gif论坛里有相关案例,可以参考
您好我知道看到一个输出全为0的帖子,跟我这个情况不太一样, 别的并没有找到。您可否告诉我一个帖子?谢谢! 热搜MPU6050,我今天帮不了你 貌似你在定时器中断里面写太多事 ?
要有代码才能看出问题
tsaiwn 发表于 2015-4-18 13:01 static/image/common/back.gif
貌似你在定时器中断里面写太多事 ?
要有代码才能看出问题
那我把代码发上来看看吧!就是有点长,希望您可以帮帮忙~谢啦 //定时器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);
}
没发主程序,因为主程序只有一些初始化,然后就是死循环了,死循环里面什么都没做 新手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:11 static/image/common/back.gif
果然你在中断 __interrupt void T3_ISR(void) 里面做太多事了 !
其中最慢的是 UartSendString(strTemp ...
不过如果你的中断来得太快,
可能会有漏掉的情况(因为来不及处理),
你有估计过中断大约多久一次吗?
tsaiwn 发表于 2015-4-18 19:23 static/image/common/back.gif
不过如果你的中断来得太快,
可能会有漏掉的情况(因为来不及处理),
你有估计过中断大约多久一次吗?
谢谢!我一直以为是ad转换最浪费时间,没想到是串口最费。我的中断是例程里面考过来的。据说进入10次为20ms。不过我现在放弃用中断来处理了,我把这些都放到主程序里面,问题就解决了!谢啦! 新手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( ) 就不会变化了 !
tsaiwn 发表于 2015-4-18 23:07 static/image/common/back.gif
(1) Congratulations
(2)进入10次为20ms, 那意思是每 2ms 进入一次
问题是, 你原先的写法,
好深奥!我以前一直怕中断里面进中断。那这样的话,之前那个一直出现0会是什么原因呢? tsaiwn 发表于 2015-4-18 23:07 static/image/common/back.gif
(1) Congratulations
(2)进入10次为20ms, 那意思是每 2ms 进入一次
问题是, 你原先的写法,
还有一个问题是,怎么估算程序运行的时间?谢谢 新手213123 发表于 2015-4-21 20:30 static/image/common/back.gif
好深奥!我以前一直怕中断里面进中断。那这样的话,之前那个一直出现0会是什么原因呢?
之前因为你在中断内做太多事,
导致太长时间没有中断, 那样取样就错了,
其实应该不只会出现 0,
你那些不是 0 的答案应该也都不太正确 !
新手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