0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于RT-Thread的RoboMaster电控框架(三)

冬至子 来源:螺丝松掉的人 作者:螺丝松掉的人 2023-09-20 15:21 次阅读

背景

使用的开发板为大疆RoboMaster-C 型开发板,基础工程为 rt-thread>bsp>stm32f407-robomaster-c

BMI088模块开发

BMI088 为 robomaster-c 开发板上集成的6轴imu,在此为提高速度陀螺仪和加速度计均使用使用 SPI 通讯方式,

添加 SPI 通信 API

首先将飞控程序中针对 RT-Thread 的 SPI 设备驱动封装的 SPI 读写函数借鉴过来:

#define SPI_DIR_READ 0x80
#define SPI_DIR_WRITE 0x00
/**

This function write a 8 bit reg.

@param device the SPI device attached to SPI bus

@param reg Register address

@param val The value to be written

@return RT_EOK if write successfully.
/
rt_inline rt_err_t spi_write_reg8(rt_device_t spi_device, uint8_t reg, uint8_t val)
{
uint8_t buffer[2];
rt_size_t w_byte;
buffer[0] = SPI_DIR_WRITE | reg;
buffer[1] = val;
w_byte = rt_spi_transfer((struct rt_spi_device
)spi_device, buffer, NULL, 2);
return (w_byte == 2) ? RT_EOK : RT_ERROR;
}
/**
This function read a 8 bit reg.

@param device the SPI device attached to SPI bus

@param reg Register address

@param buffer Buffer of read data

@return RT_EOK if read successfully.
/
rt_inline rt_err_t spi_read_reg8(rt_device_t spi_device, uint8_t reg, uint8_t
buffer)
{
uint8_t reg_addr;
reg_addr = SPI_DIR_READ | reg;
return rt_spi_send_then_recv((struct rt_spi_device*)spi_device, (void*)®_addr, 1, (void*)buffer, 1);
}
/**
This function read multiple contiguous 8 bit regs.

@param device the SPI device attached to SPI bus

@param reg Start register address

@param buffer Buffer of read data

@param len The number of read registers

@return RT_EOK if read successfully.
/
rt_inline rt_err_t spi_read_multi_reg8(rt_device_t spi_device, uint8_t reg, uint8_t
buffer, uint8_t len)
{
uint8_t reg_addr;
reg_addr = SPI_DIR_READ | reg;
return rt_spi_send_then_recv((struct rt_spi_device*)spi_device, (void*)®_addr, 1, (void*)buffer, len);
}
因为C板上 STM32 与 BMI088 是通过 SPI1 相连接,Kconfig 文件中添加 SPI1 部分并使能,并且需要进入到CubeMX 中使能 SPI1,这一步最重要的是选取引脚,这样 RTT 中的 SPI1 设备驱动才能使用:

BMI088 驱动

主要就是先对 BMI088 上的陀螺仪和加速度计分别进行初始化,设置相关采样参数,需要注意的一点是加速度计上电后默认是 I2C 模式,需要其片选引脚上检测到电平后才会切换为 SPI 模式并保持,如果没有正确处理这一步可能会导致这个现象:上电后读不出来加速度计,reset 后又能读取。

static rt_err_t accelerometer_init(void)
{
uint8_t accel_id;
/* init spi bus /
rt_device_open(accel_spi_dev, RT_DEVICE_OFLAG_RDWR);
/
dummy read to let accel enter SPI mode /
spi_read_reg8(accel_spi_dev, BMI088_ACC_BGW_CHIPID, &accel_id);
rt_hw_us_delay(1000);
spi_read_reg8(accel_spi_dev, BMI088_ACC_BGW_CHIPID, &accel_id);
/
read accel id /
spi_read_reg8(accel_spi_dev, BMI088_ACC_BGW_CHIPID, &accel_id);
if (accel_id != BMI088_ACC_BGW_CHIPID_VALUE) {
LOG_W("Warning: not found BMI088 accel id: %02x", accel_id);
return RT_ERROR;
}
/
soft reset /
spi_write_reg8(accel_spi_dev, BMI088_ACC_SOFTRESET, 0xB6);
rt_hw_us_delay(2000);
/
dummy read to let accel enter SPI mode /
spi_read_reg8(accel_spi_dev, BMI088_ACC_BGW_CHIPID, &accel_id);
/
enter normal mode /
spi_write_reg8(accel_spi_dev, BMI088_ACC_PWR_CTRL, 0x04);
rt_hw_us_delay(55000);
/
set default range and bandwidth /
accel_set_range(6); /
6g /
accel_set_sample_rate(800); /
800Hz sample rate /
accel_set_bwp_odr(280); /
Normal BW /
/
enter active mode /
spi_write_reg8(accel_spi_dev, BMI088_ACC_PWR_CONF, 0x00);
rt_hw_us_delay(1000);
return RT_EOK;
}
static rt_err_t gyroscope_init(void)
{
uint8_t gyro_id;
/
init spi bus /
rt_device_open(gyro_spi_dev, RT_DEVICE_OFLAG_RDWR);
spi_read_reg8(gyro_spi_dev, BMI088_CHIP_ID_ADDR, &gyro_id);
if (gyro_id != BMI088_GRRO_CHIP_ID) {
LOG_W("Warning: not found BMI088 gyro id: %02x", gyro_id);
return RT_ERROR;
}
/
soft reset /
spi_write_reg8(gyro_spi_dev, BMI088_BGW_SOFT_RST_ADDR, 0xB6);
rt_hw_us_delay(35000); // > 30ms delay
gyro_set_range(2000); /
2000dps /
gyro_set_sample_rate(2000); /
OSR 2000KHz, Filter BW: 230Hz /
/
enable gyroscope /
__modify_reg(gyro_spi_dev, BMI088_MODE_LPM1_ADDR, REG_VAL(0, BIT(7) | BIT(5))); /
{0; 0} NORMAL mode */
rt_hw_us_delay(1000);
return RT_EOK;
}

这里为了减小陀螺仪的零飘影响,可以对陀螺仪进行校准,得出补偿的值。

static void bmi088_calibrate(void){
static float start_time;
static uint16_t cali_times = 5000; // 需要足够多的数据才能得到有效陀螺仪零偏校准结果
float accel[3], gyro[3];
float gyroMax[3], gyroMin[3];
float gNormTemp, gNormMax, gNormMin;
static float gyroDiff[3], gNormDiff;
int16_t acc_raw[3];
start_time = dwt_get_time_s();
do
{
if (dwt_get_time_s() - start_time > 20)
{
// 校准超时
gyro_offset[0] = GxOFFSET;
gyro_offset[1] = GyOFFSET;
gyro_offset[2] = GzOFFSET;
bmi088_g_norm = gNORM;
break;
}
dwt_delay_s(0.005);
// 开始时先置零,避免对数据读取造成影响
bmi088_g_norm = 0;
gyro_offset[0] = 0;
gyro_offset[1] = 0;
gyro_offset[2] = 0;
for (uint16_t i = 0; i < cali_times; i++)
{
accel_read_raw(acc_raw);
accel[0] = accel_range_scale * acc_raw[0];
accel[1] = accel_range_scale * acc_raw[1];
accel[2] = accel_range_scale * acc_raw[2];
gNormTemp = sqrtf(accel[0] * accel[0] +
accel[1] * accel[1] +
accel[2] * accel[2]);
bmi088_g_norm += gNormTemp;
gyro_read_rad(gyro);
for(uint8_t j = 0; j < 3; j++){
gyro_offset[j] += gyro[j];
}
// 记录数据极差
if (i == 0)
{
gNormMax = gNormTemp;
gNormMin = gNormTemp;
for (uint8_t j = 0; j < 3; j++)
{
gyroMax[j] = gyro[j];
gyroMin[j] = gyro[j];
}
}
else
{
if (gNormTemp > gNormMax)
gNormMax = gNormTemp;
if (gNormTemp < gNormMin)
gNormMin = gNormTemp;
for (uint8_t j = 0; j < 3; j++)
{
if (gyro[j] > gyroMax[j])
gyroMax[j] = gyro[j];
if (gyro[j] < gyroMin[j])
gyroMin[j] = gyro[j];
}
}
// 数据差异过大认为收到外界干扰,需重新校准
gNormDiff = gNormMax - gNormMin;
for (uint8_t j = 0; j < 3; j++)
gyroDiff[j] = gyroMax[j] - gyroMin[j];
if (gNormDiff > 0.6f ||
gyroDiff[0] > 1.0f ||
gyroDiff[1] > 1.0f ||
gyroDiff[2] > 1.0f)
break;
LOG_I("gyroDiff: %f",gNormDiff);
for(uint8_t j = 0; j < 3; j++){
LOG_D("gyroDiff%d: %f",j ,gyroDiff[j]);
}
dwt_delay_s(0.0005);
}
// 取平均值得到标定结果
bmi088_g_norm /= (float)cali_times;
LOG_W("bmi088_g_norm: %f",bmi088_g_norm);
for (uint8_t i = 0; i < 3; i++)
{
gyro_offset[i] /= (float)cali_times;
LOG_W("gyro_offset: %f",gyro_offset[i]);
}
cali_count++;
} while (gNormDiff > 0.3f ||
fabsf(bmi088_g_norm - 9.8f) > 0.5f ||
gyroDiff[0] > 1.0f ||
gyroDiff[1] > 1.0f ||
gyroDiff[2] > 1.0f ||
fabsf(gyro_offset[0]) > 0.01f ||
fabsf(gyro_offset[1]) > 0.01f ||
fabsf(gyro_offset[2]) > 0.01f);
// 根据标定结果校准加速度计标度因数
accel_scale = 9.81f / bmi088_g_norm;
}

由于校准时间较长,并且需要处于稳定的环境下,定期或更换开发板时进行一次校准即可,校准成功后手动修改 GxOFFSET 等宏;通过在 menuconfig 中使能 BSP_BMI088_CALI 进行校准;在串口终端可以查看校准进度,如多次校准失败,适当调大误差范围。

抽象设备

为提高程序的模块化,选用不同传感器时的灵活性,将 bmi088 抽象为 imu一类设备,抽象出 imu_init 、 gyro_read 、 gyro_config、accel_read、accel_config、temp_read 6个操作方法。

struct imu_ops{
rt_err_t (*imu_init)(void);
rt_err_t (*gyro_read)(float data[3]);
rt_err_t (*gyro_config)(struct gyro_configure cfg);
rt_err_t (*accel_read)(float data[3]);
rt_err_t (*accel_config)(struct accel_configure cfg);
float (*temp_read)(void);
};

项目选用不同的磁力计传感器时,对接这些个接口即可,以 bmi088 为例:

struct imu_ops imu_ops = {
.imu_init = bmi088_init,
.gyro_read = bim088_gyro_read,
.gyro_config = bim088_gyro_config,
.accel_read = bim088_accel_read,
.accel_config = bim088_accel_config,
.temp_read = bmi088_temp_read,
};

应用层需要使用磁力计时,调用 imu_ops 中的操作方法即可:

/* read data /
float gyro[3],acc[3],temp;
imu_ops.gyro_read(gyro);
imu_ops.accel_read(acc);
temp = imu_ops.temp_read();
/
config */
struct gyro_configure usr_conf_g = GYRO_CONFIG_DEFAULT;
struct acc_configure usr_conf_a = ACCEL_CONFIG_DEFAULT;

到此就可以使用imu模块获取传感器原始数据啦。

存在问题及优化方向

目前为了提高性能,imu设备的注册对接形式是比较简陋的;
目前对 imu 设备的抽象只考虑到6轴 imu;
需要注意陀螺仪校准处理部分。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 传感器
    +关注

    关注

    2525

    文章

    48137

    浏览量

    740441
  • 驱动器
    +关注

    关注

    51

    文章

    7333

    浏览量

    143063
  • 加速度计
    +关注

    关注

    6

    文章

    662

    浏览量

    45435
  • STM32F407
    +关注

    关注

    15

    文章

    187

    浏览量

    29024
  • RT-Thread
    +关注

    关注

    31

    文章

    1151

    浏览量

    38931
收藏 人收藏

    评论

    相关推荐

    基于RT-ThreadRoboMaster电控框架设计

    由于 RT-Thread 稳定高效的内核,丰富的文档教程,积极活跃的社区氛围,以及设备驱动框架、Kconfig、Scons、日志系统、海量的软件包……很难不选择 RT-Thread 进行项目开发。
    发表于 09-06 15:21 413次阅读

    RT-Thread全球技术大会:RT-Thread上的单元测试框架与运行测试用例

    RT-Thread全球技术大会:RT-Thread上的单元测试框架与运行测试用例                 审核编辑:彭静
    的头像 发表于 05-27 16:21 1271次阅读
    <b class='flag-5'>RT-Thread</b>全球技术大会:<b class='flag-5'>RT-Thread</b>上的单元测试<b class='flag-5'>框架</b>与运行测试用例

    RT-Thread大会:RT-Thread 自动化测试

    为了保证RT-Thread相关仓库的代码质量,我们基于utest框架搭建了一套简易的自动化测试环境。
    的头像 发表于 05-27 16:26 1529次阅读
    <b class='flag-5'>RT-Thread</b>大会:<b class='flag-5'>RT-Thread</b> 自动化测试

    RT-Thread设备模型框架及创建注册设备的实现

    RT-Thread设备模型框架及创建注册设备的实现方式介绍如下:
    的头像 发表于 05-28 10:38 1822次阅读
    <b class='flag-5'>RT-Thread</b>设备模型<b class='flag-5'>框架</b>及创建注册设备的实现

    RT-Thread文档_RT-Thread 简介

    RT-Thread文档_RT-Thread 简介
    发表于 02-22 18:22 5次下载
    <b class='flag-5'>RT-Thread</b>文档_<b class='flag-5'>RT-Thread</b> 简介

    RT-Thread文档_RT-Thread SMP 介绍与移植

    RT-Thread文档_RT-Thread SMP 介绍与移植
    发表于 02-22 18:31 7次下载
    <b class='flag-5'>RT-Thread</b>文档_<b class='flag-5'>RT-Thread</b> SMP 介绍与移植

    RT-Thread文档_utest 测试框架

    RT-Thread文档_utest 测试框架
    发表于 02-22 18:43 2次下载
    <b class='flag-5'>RT-Thread</b>文档_utest 测试<b class='flag-5'>框架</b>

    浅析RT-Thread设备驱动框架

    RT-Thread 设备框架属于组件和服务层,是基于 RT-Thread 内核之上的上层软件。设备框架是针对某一类外设,抽象出来的一套统一的操作方法及接入标准,可以屏蔽硬件差异,为应用
    的头像 发表于 08-07 15:39 1293次阅读

    基于 RT-ThreadRoboMaster 电控框架(一)

    。但也正是因为这些优点的覆盖面较广,很多初学者会觉得无从下手,但只要步入 RT-Thread 的大门,你就发现她的美好。这系列文档将作为本人基于 RT-Thread 开发 RoboMaster
    的头像 发表于 09-19 19:55 423次阅读

    基于RT-ThreadRoboMaster电控框架(二)

    由于 RT-Thread 稳定高效的内核,丰富的文档教程,积极活跃的社区氛围,以及设备驱动框架、Kconfig、Scons、日志系统、海量的软件包
    的头像 发表于 09-20 15:16 483次阅读

    基于RT-ThreadRoboMaster电控框架(四)

    使用的开发板为大疆的 RoboMaster-C 型开发板,基础工程为 rt-thread>bsp>stm32f407-robomaster-c
    的头像 发表于 09-20 15:28 428次阅读

    RT-Thread框架下的SMP支持

    使其支持 RT-Thread 框架下的 SMP,最近就一直在研究 SMP,并在 Raspberry-Pico 上做了一些实验。
    的头像 发表于 10-11 10:34 517次阅读
    <b class='flag-5'>RT-Thread</b><b class='flag-5'>框架</b>下的SMP支持

    基于rt-thread的socket通信设计

    最近再研究 rt-thread 的通信 ,想设计出 eps8266(多个) rt-thread(作为中控) 服务器的通信框架,使用的开发板是 潘多拉
    的头像 发表于 10-13 15:02 702次阅读
    基于<b class='flag-5'>rt-thread</b>的socket通信设计

    基于RT-ThreadRoboMaster电控框架(五)

    使用的开发板为大疆的 RoboMaster-C 型开发板,基础工程为 rt-thread>bsp>stm32f407-robomaster-c
    的头像 发表于 10-30 17:10 446次阅读

    基于RT-ThreadRoboMaster电控框架(六)

    使用的开发板为大疆的 RoboMaster-C 型开发板,基础工程为 rt-thread>bsp>stm32f407-robomaster-c
    的头像 发表于 10-30 17:41 269次阅读