单片机学习小组
直播中

郭大

10年用户 970经验值
擅长:嵌入式技术
私信 关注

硬件i2c与LSM303AGR之间如何通信?

硬件i2c与LSM303AGR之间如何通信

回帖(1)

李恬恬

2022-2-16 11:21:20
  这一次用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


}
三,小结

这种简单粗暴的用加速度双重积分出距离其实非常不准确,积分误差会随时间而增加,需要其他方式进行校准。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分