有人预言,RISC-V或将是继Intel和Arm之后的第三大主流处理器体系。欢迎访问全球首家只专注于RISC-V单片机行业应用的中文网站
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
本帖最后由 皋陶 于 2020-8-28 21:52 编辑
本文是第四篇:LSM303AGR姿态传感器 RISC-V Sifive learn inventor基础之硬件i2c与LSM303AGR通信
这一次用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 accelertask\r\n";
- (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
- }
复制代码
三,小结
这种简单粗暴的用加速度双重积分出距离其实非常不准确,积分误差会随时间而增加,需要其他方式进行校准。
本篇完,感谢关注:RISC-V单片机中文网
|