这一次用iic简单的读取lsm303的加速度数据,对于中断等不做操作;
2020 .7.29 优化了数据处理部分的代码。得到的数据更直观。精度是0.1mm
一,硬件连接
如图的连接,I2C的两根信号都通过R6,R7上拉,通过这点可知与LSM303的通信波特率是400khz。
二,获取数据
1,使用最新的freedom-e-sdk,freedom-e-sdk GitHub仓库,最新的sdk支持iic和pwm库函数开发,而且提供freertos的模板例程。在此基础上开发iic十分方便。
2,首先初始化开发板的iic接口,并通过iic接口配置lsm303的寄存器,设置加速度计的工作模式和输出速率(reg1_a寄存器),这里我设置的是普通模式,输出速率为400hz:
//reg1中是reg1_a寄存器的地址及写入的值
char reg1[2]={0x20,0x77};
//初始化iic为master 波特率400khz
metal_i2c_init(i2c, I2C_BAUDRATE, METAL_I2C_MASTER);
//写控制寄存器reg1_A 普通模式,400hz输出,使能x,y,z轴加速度计
metal_i2c_write(i2c, ACCELEROMETER_I2C_ADDR, 2,reg1 , METAL_I2C_STOP_ENABLE);
然后创建一个定时时间为dt的任务,在这个任务里,我们读取lsm303加速度的输出寄存器的高位数据,比较对精度要求不高的话可以忽略低位数据。然后将数据进行积分处理得到速度和路程。这个项目里我不需要y轴数据所以没有读取。
/*
* 由于普通模式有效数据10位,且数据为左对齐,只读取高位寄存器
* 将数据进行积分处理
*/
static void prvAccelerTask(void *pvParameters)
{
TickType_t xNextWakeTime;
char * pcMessage = "start accelertaskrn";
(void)pvParameters;
//out_add中的值是加速度计OUT_X_H_A,OUT_Z_H_A寄存器的地址,
static char out_addr[2]={0x29,0x2d},buff[2]={0};
//延时时间,同时也是时间的微分dt 单位是ms
Motor.dt=20;
xNextWakeTime = xTaskGetTickCount();
write(STDOUT_FILENO,pcMessage,strlen(pcMessage));
while(1)
{
//进入临界段,防止通信被打断
taskENTER_CRITICAL();
//分别读取OUT_X_H_A,OUT_Z_H_A高位寄存器
metal_i2c_write(i2c, ACCELEROMETER_I2C_ADDR, 1,out_addr , METAL_I2C_STOP_DISABLE);
metal_i2c_read(i2c, ACCELEROMETER_I2C_ADDR, 1,buff , METAL_I2C_STOP_ENABLE);
metal_i2c_write(i2c, ACCELEROMETER_I2C_ADDR, 1,&out_addr[1] , METAL_I2C_STOP_DISABLE);
metal_i2c_read(i2c, ACCELEROMETER_I2C_ADDR, 1,&buff[1] , METAL_I2C_STOP_ENABLE);
//赋值给lsm303对象
lsm303.acc_x_raw=buff[0];
lsm303.acc_z_raw=buff[1];
//对原生数据处理,转化成加速度,速度,位移
sensor_data_process();
taskEXIT_CRITICAL();
write(1,pcMessage,strlen(pcMessage));
vTaskDelayUntil( &xNextWakeTime, pdMS_TO_TICKS( Motor.dt ) );
}
}
这就是配置的寄存器;
我设置的是400hz,所以代码里写入的值是0x77,其实也不必太高,过高会影响精度。
以下是不同模式下,输出数据的有效位数,普通模式下,输出10位数据,再看看输出寄存器的描述,大概的意思就是输出的数据是左对齐的二进制补码。那就在处理数据时要解析出原码。至于不了解左对齐是什么的可以点我。
三,处理数据
上面得到的数据是原始的,原生态无污染的,要加工成加速度数据,也就是单位为m/s^2的数据。要计算实际的加速度,需要乘以一个比例系数,在开发文档称为灵敏度,灵敏度与加速度计工作模式与full scal(缩放)有关,缩放的配置在reg4_a寄存器,默认是0,也就是±2g。所以可知我配置的灵敏度为3.9mg。详细如下图:
因为板子是倾斜约45°放的,所以要得到水平的加速度需要经过直角坐标转换。
lsm303.acc_x_real=(int)lsm303.acc_x_raw*4*39;乘4是因为普通模式输出10位数据,而我只读了高八位,舍弃了低二位,所以要左移2位,也就是乘4,39就是灵敏度。经过单位换算,得到的加速度单位是100ug
/*
* 处理lsm303读取的数据,获取速度,距离
*/
void sensor_data_process()
{
//如果raw是负数的话需要取绝对值,乘以灵敏度,再将符号返回
if(lsm303.acc_x_raw&0x80)
{
lsm303.acc_x_raw =-lsm303.acc_x_raw;
lsm303.acc_x_real=(int)lsm303.acc_x_raw*4*39;
lsm303.acc_x_real=-lsm303.acc_x_real;
}
else lsm303.acc_x_real=(int)lsm303.acc_x_raw*4*39;
if(lsm303.acc_z_raw&0x80)
{
lsm303.acc_z_raw =-lsm303.acc_z_raw;
lsm303.acc_z_real=(int)lsm303.acc_z_raw*4*39;
lsm303.acc_z_real=-lsm303.acc_z_real;
}
else lsm303.acc_z_real=(int)lsm303.acc_z_raw*4*39;
//x=8112 z=-6552 滤除重力
lsm303.acc_x_filter=lsm303.acc_x_real-8112;
lsm303.acc_z_filter=6552+lsm303.acc_z_real;
//lsm303.acc_x_filter=lsm303.acc_x_filter & ~0x000000ff;
//lsm303.acc_z_filter=lsm303.acc_z_filter & ~0x000000ff;
//加速度分解到水平方向 sinθ=2/3 cosθ=3/4
Motor.acc=(lsm303.acc_x_filter*2/3+lsm303.acc_z_filter*3/4)/100;//cm/s^2
//加速度,速度积分
Motor.speed=Motor.speed+Motor.acc*Motor.dt/100;//mm
Motor.distance=Motor.distance+Motor.speed*Motor.dt/100;//0.1mm
}
三,小结
这种简单粗暴的用加速度双重积分出距离其实非常不准确,积分误差会随时间而增加,需要其他方式进行校准。
这一次用iic简单的读取lsm303的加速度数据,对于中断等不做操作;
2020 .7.29 优化了数据处理部分的代码。得到的数据更直观。精度是0.1mm
一,硬件连接
如图的连接,I2C的两根信号都通过R6,R7上拉,通过这点可知与LSM303的通信波特率是400khz。
二,获取数据
1,使用最新的freedom-e-sdk,freedom-e-sdk GitHub仓库,最新的sdk支持iic和pwm库函数开发,而且提供freertos的模板例程。在此基础上开发iic十分方便。
2,首先初始化开发板的iic接口,并通过iic接口配置lsm303的寄存器,设置加速度计的工作模式和输出速率(reg1_a寄存器),这里我设置的是普通模式,输出速率为400hz:
//reg1中是reg1_a寄存器的地址及写入的值
char reg1[2]={0x20,0x77};
//初始化iic为master 波特率400khz
metal_i2c_init(i2c, I2C_BAUDRATE, METAL_I2C_MASTER);
//写控制寄存器reg1_A 普通模式,400hz输出,使能x,y,z轴加速度计
metal_i2c_write(i2c, ACCELEROMETER_I2C_ADDR, 2,reg1 , METAL_I2C_STOP_ENABLE);
然后创建一个定时时间为dt的任务,在这个任务里,我们读取lsm303加速度的输出寄存器的高位数据,比较对精度要求不高的话可以忽略低位数据。然后将数据进行积分处理得到速度和路程。这个项目里我不需要y轴数据所以没有读取。
/*
* 由于普通模式有效数据10位,且数据为左对齐,只读取高位寄存器
* 将数据进行积分处理
*/
static void prvAccelerTask(void *pvParameters)
{
TickType_t xNextWakeTime;
char * pcMessage = "start accelertaskrn";
(void)pvParameters;
//out_add中的值是加速度计OUT_X_H_A,OUT_Z_H_A寄存器的地址,
static char out_addr[2]={0x29,0x2d},buff[2]={0};
//延时时间,同时也是时间的微分dt 单位是ms
Motor.dt=20;
xNextWakeTime = xTaskGetTickCount();
write(STDOUT_FILENO,pcMessage,strlen(pcMessage));
while(1)
{
//进入临界段,防止通信被打断
taskENTER_CRITICAL();
//分别读取OUT_X_H_A,OUT_Z_H_A高位寄存器
metal_i2c_write(i2c, ACCELEROMETER_I2C_ADDR, 1,out_addr , METAL_I2C_STOP_DISABLE);
metal_i2c_read(i2c, ACCELEROMETER_I2C_ADDR, 1,buff , METAL_I2C_STOP_ENABLE);
metal_i2c_write(i2c, ACCELEROMETER_I2C_ADDR, 1,&out_addr[1] , METAL_I2C_STOP_DISABLE);
metal_i2c_read(i2c, ACCELEROMETER_I2C_ADDR, 1,&buff[1] , METAL_I2C_STOP_ENABLE);
//赋值给lsm303对象
lsm303.acc_x_raw=buff[0];
lsm303.acc_z_raw=buff[1];
//对原生数据处理,转化成加速度,速度,位移
sensor_data_process();
taskEXIT_CRITICAL();
write(1,pcMessage,strlen(pcMessage));
vTaskDelayUntil( &xNextWakeTime, pdMS_TO_TICKS( Motor.dt ) );
}
}
这就是配置的寄存器;
我设置的是400hz,所以代码里写入的值是0x77,其实也不必太高,过高会影响精度。
以下是不同模式下,输出数据的有效位数,普通模式下,输出10位数据,再看看输出寄存器的描述,大概的意思就是输出的数据是左对齐的二进制补码。那就在处理数据时要解析出原码。至于不了解左对齐是什么的可以点我。
三,处理数据
上面得到的数据是原始的,原生态无污染的,要加工成加速度数据,也就是单位为m/s^2的数据。要计算实际的加速度,需要乘以一个比例系数,在开发文档称为灵敏度,灵敏度与加速度计工作模式与full scal(缩放)有关,缩放的配置在reg4_a寄存器,默认是0,也就是±2g。所以可知我配置的灵敏度为3.9mg。详细如下图:
因为板子是倾斜约45°放的,所以要得到水平的加速度需要经过直角坐标转换。
lsm303.acc_x_real=(int)lsm303.acc_x_raw*4*39;乘4是因为普通模式输出10位数据,而我只读了高八位,舍弃了低二位,所以要左移2位,也就是乘4,39就是灵敏度。经过单位换算,得到的加速度单位是100ug
/*
* 处理lsm303读取的数据,获取速度,距离
*/
void sensor_data_process()
{
//如果raw是负数的话需要取绝对值,乘以灵敏度,再将符号返回
if(lsm303.acc_x_raw&0x80)
{
lsm303.acc_x_raw =-lsm303.acc_x_raw;
lsm303.acc_x_real=(int)lsm303.acc_x_raw*4*39;
lsm303.acc_x_real=-lsm303.acc_x_real;
}
else lsm303.acc_x_real=(int)lsm303.acc_x_raw*4*39;
if(lsm303.acc_z_raw&0x80)
{
lsm303.acc_z_raw =-lsm303.acc_z_raw;
lsm303.acc_z_real=(int)lsm303.acc_z_raw*4*39;
lsm303.acc_z_real=-lsm303.acc_z_real;
}
else lsm303.acc_z_real=(int)lsm303.acc_z_raw*4*39;
//x=8112 z=-6552 滤除重力
lsm303.acc_x_filter=lsm303.acc_x_real-8112;
lsm303.acc_z_filter=6552+lsm303.acc_z_real;
//lsm303.acc_x_filter=lsm303.acc_x_filter & ~0x000000ff;
//lsm303.acc_z_filter=lsm303.acc_z_filter & ~0x000000ff;
//加速度分解到水平方向 sinθ=2/3 cosθ=3/4
Motor.acc=(lsm303.acc_x_filter*2/3+lsm303.acc_z_filter*3/4)/100;//cm/s^2
//加速度,速度积分
Motor.speed=Motor.speed+Motor.acc*Motor.dt/100;//mm
Motor.distance=Motor.distance+Motor.speed*Motor.dt/100;//0.1mm
}
三,小结
这种简单粗暴的用加速度双重积分出距离其实非常不准确,积分误差会随时间而增加,需要其他方式进行校准。
举报