目录
- Sifive Learn Inventor 基础之gpio 按键中断
- Sifive learn inventor基础之串口&操作寄存器
- Sifive learn inventor基础之硬件pwm&寄存器
- risc-v Sifive learn inventor基础之硬件i2c与LSM303AGR通信
这一次用iic简单的读取lsm303的加速度数据,对于中断等不做操作;
一,硬件连接
如图的连接,I2C的两根信号都通过R6,R7上拉,通过这点可知与LSM303的通信波特率是400khz。
二,获取数据
1,使用最新的freedom-e-sdk,freedom-e-sdk GitHub仓库,最新的sdk支持iic和pwm库函数开发,而且提供freertos的模板例程。在此基础上开发iic十分方便。
2,先搞到一个可以每1秒打印字符串的任务,表示rtos正在运行,然后创建读取lsm303的任务,在任务里我们先初始化iic接口,设置加速度计的工作模式和输出速率,这里我设置的是普通模式,输出速率为400hz。
/*
* out_addr 是加速度计x,z轴高位输出寄存器
* buff用来缓存读取的数据
* reg1的第一元素是reg1_a寄存器的地址,该寄存器配置工作模式,输出速率;第二个元素是写入寄存器的值
*/
static void prvAccelerTask(void *pvParameters)
{
TickType_t xNextWakeTime;
const char * const pcMessage = "ACCELER\r\n";
(void)pvParameters;
static unsigned char out_addr[2]={0x29,0x2d},buff[2]={0},reg1[2]={0x20,0x77};
//延时时间,同时也是时间的微分
Motor.dt=200;
xNextWakeTime = xTaskGetTickCount();
metal_i2c_init(i2c, I2C_BAUDRATE, METAL_I2C_MASTER);
//配置为普通模式,400hz输出,使能x,y,z
metal_i2c_write(i2c, ACCELEROMETER_I2C_ADDR, 2,reg1 , METAL_I2C_STOP_ENABLE);
while(1){
write(STDOUT_FILENO,pcMessage,strlen(pcMessage));
taskENTER_CRITICAL();
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.acc_x_raw=buff[0];
lsm303.acc_z_raw=buff[1];
//对原生数据处理,转化成加速度,速度,位移
sensor_data_process();
taskEXIT_CRITICAL();
vTaskDelayUntil( &xNextWakeTime, pdMS_TO_TICKS( Motor.dt ) );
}
}
这就是配置的寄存器;
我设置的是400hz,所以代码里写入的值是0x77,其实也不必太高,过高会影响精度。
以下是不同模式下,输出数据的有效位数,普通模式下,输出10位数据,再看看输出寄存器的描述,大概的意思就是输出的数据是左对齐的二进制补码。那就在处理数据时要解析出原码。至于不了解左对齐是什么的可以点我。
三,处理数据
上面得到的数据是原始的,原生态无污染的,要转化成加速度数据,也就是单位为g的数据。首先需要解得二进制数据的原码,这时候还要乘上一个比例系数文档称为灵敏度的数据,才能表示真实的加速度数据。灵敏度与加速度计工作模式与full scal(缩放)有关,缩放的配置在reg4_a寄存器,默认是0,也就是±2g。所以可知我配置的灵敏度为3.9mg。
**因为板子是倾斜约45°放的,所以我的加速度需要经过直角坐标的转换才能得到水平的加速度。这里Motor.acc=(temp[0]0.66+temp[1]0.75)43900;得到的就是真实加速度数据,单位是ug,乘4是因为普通模式输出10位数据,而我只读了高八位,舍弃了低二位,所以要乘2^2,3900就是灵敏度。
void sensor_data_process()
{
unsigned char temp[2]={0};
//获取原码数据
if(lsm303.acc_x_raw>>7){
temp[0]=(~lsm303.acc_x_raw);
temp[0]|=(1<<7);
}
else {
temp[0]=(~lsm303.acc_x_raw);
temp[0]&=~(1<<7);
}
if(lsm303.acc_z_raw>>7){
temp[1]=(~lsm303.acc_z_raw);
temp[1]|=(1<<7);
}
else {
temp[1]=(~lsm303.acc_z_raw);
temp[1]&=~(1<<7);
}
lsm303.acc_x_comple=temp[0];
lsm303.acc_z_comple=temp[1];
//剔除重力分量的影响,77和-87分别是静止状态下的x,z输出
temp[0]=77-temp[0];
temp[1]=-(87+temp[1]);
//滤掉微小的抖动
temp[0]&=~0x07;
temp[1]&=~0x07;
lsm303.acc_x_filter=temp[0];
lsm303.acc_z_filter=temp[1];
//乘以三角系数得到水平方向的加速度
//sinθ=0.66 cosθ=0.75
Motor.acc=(temp[0]*0.66+temp[1]*0.75)*4*3900;//ug
//加速度积分
Motor.speed+=Motor.acc*Motor.dt;
//速度积分
Motor.distance+=Motor.speed*Motor.dt;
}
三,小结
这种简单粗暴的用加速度双重积分出距离其实非常不准确,积分误差会随时间而增加,需要其他方式进行校准。