添加了编译参数提示,完成了bmi088阻塞周期访问版本的实现,添加了zeromalloc

This commit is contained in:
NeoZng 2023-02-04 22:48:53 +08:00
parent 429aa17fa4
commit 144596687c
8 changed files with 274 additions and 53 deletions

View File

@ -8,7 +8,7 @@
* @copyright Copyright (c) HNU YueLu EC 2022 all rights reserved
*
*/
#pragma once
#ifndef ROBOT_DEF_H
#define ROBOT_DEF_H
@ -16,6 +16,11 @@
#include "master_process.h"
#include "stdint.h"
#ifndef ROBOT_DEF_PARAM_WARNING
#define ROBOT_DEF_PARAM_WARNING
#warning BE SURED THAT YOU HAVE ALREADY MODIFIED THESE PARAMETER TO FIT THE ROBOT
#endif // !ROBOT_DEF_PARAM_WARNING
/* 开发板类型定义,烧录时注意不要弄错对应功能;修改定义后需要重新编译,只能存在一个定义! */
#define ONE_BOARD // 单板控制整车
// #define CHASSIS_BOARD //底盘板
@ -54,8 +59,7 @@
#error Conflict board definition! You can only define one board type.
#endif
#pragma pack(1) // 压缩结构体,取消字节对齐
#pragma pack(1) // 压缩结构体,取消字节对齐,下面的数据都可能被传输
/* -------------------------基本控制模式和数据类型定义-------------------------*/
/**
* @brief CMD控制数据和各应用的反馈数据的一部分

View File

@ -2,6 +2,7 @@
******************************************************************************
* @file bsp_dwt.c
* @author Wang Hongxi
* @author modified by Neo with annotation
* @version V1.1.0
* @date 2022/3/8
* @brief
@ -17,7 +18,21 @@ static uint32_t CPU_FREQ_Hz, CPU_FREQ_Hz_ms, CPU_FREQ_Hz_us;
static uint32_t CYCCNT_RountCount;
static uint32_t CYCCNT_LAST;
uint64_t CYCCNT64;
static void DWT_CNT_Update(void);
/**
* @brief ,DWT CYCCNT寄存器是否溢出,CYCCNT_RountCount
* @attention
*
*/
static void DWT_CNT_Update(void)
{
volatile uint32_t cnt_now = DWT->CYCCNT;
if (cnt_now < CYCCNT_LAST)
CYCCNT_RountCount++;
CYCCNT_LAST = cnt_now;
}
void DWT_Init(uint32_t CPU_Freq_mHz)
{
@ -101,15 +116,6 @@ uint64_t DWT_GetTimeline_us(void)
return DWT_Timelinef32;
}
static void DWT_CNT_Update(void)
{
volatile uint32_t cnt_now = DWT->CYCCNT;
if (cnt_now < CYCCNT_LAST)
CYCCNT_RountCount++;
CYCCNT_LAST = cnt_now;
}
void DWT_Delay(float Delay)
{

View File

@ -25,13 +25,63 @@ typedef struct
extern DWT_Time_t SysTime;
/**
* @brief DWT,CPU频率,MHz
*
* @param CPU_Freq_mHz c板为168MHz,A板为180MHz
*/
void DWT_Init(uint32_t CPU_Freq_mHz);
/**
* @brief ,/s
*
* @param cnt_last
* @return float ,/s
*/
float DWT_GetDeltaT(uint32_t *cnt_last);
/**
* @brief ,/s,
*
* @param cnt_last
* @return double ,/s
*/
double DWT_GetDeltaT64(uint32_t *cnt_last);
/**
* @brief ,/s,
*
* @return float
*/
float DWT_GetTimeline_s(void);
/**
* @brief ,/ms,
*
* @return float
*/
float DWT_GetTimeline_ms(void);
/**
* @brief ,/us,
*
* @return uint64_t
*/
uint64_t DWT_GetTimeline_us(void);
/**
* @brief DWT延时函数,/s
* @attention ,使
* @note __disable_irq()__enable_irq()使HAL_Delay(),使
*
* @param Delay ,/s
*/
void DWT_Delay(float Delay);
/**
* @brief DWT更新时间轴函数,timeline函数调用
* @attention timeline函数,,CYCCNT溢出后定时和时间轴不准确
*/
void DWT_SysTimeUpdate(void);

View File

@ -1,32 +1,8 @@
#include "bmi088_regNdef.h"
#include "bmi088.h"
#include "stdlib.h"
#include "memory.h"
#include "user_lib.h"
#define REG 0
#define DATA 1
#define ERROR 2
// BMI088初始化配置数组for accel,第一列为reg地址,第二列为写入的配置值,第三列为错误码(如果出错)
static uint8_t BMI088_Accel_Init_Table[BMI088_WRITE_ACCEL_REG_NUM][3] =
{
{BMI088_ACC_PWR_CTRL, BMI088_ACC_ENABLE_ACC_ON, BMI088_ACC_PWR_CTRL_ERROR},
{BMI088_ACC_PWR_CONF, BMI088_ACC_PWR_ACTIVE_MODE, BMI088_ACC_PWR_CONF_ERROR},
{BMI088_ACC_CONF, BMI088_ACC_NORMAL | BMI088_ACC_800_HZ | BMI088_ACC_CONF_MUST_Set, BMI088_ACC_CONF_ERROR},
{BMI088_ACC_RANGE, BMI088_ACC_RANGE_6G, BMI088_ACC_RANGE_ERROR},
{BMI088_INT1_IO_CTRL, BMI088_ACC_INT1_IO_ENABLE | BMI088_ACC_INT1_GPIO_PP | BMI088_ACC_INT1_GPIO_LOW, BMI088_INT1_IO_CTRL_ERROR},
{BMI088_INT_MAP_DATA, BMI088_ACC_INT1_DRDY_INTERRUPT, BMI088_INT_MAP_DATA_ERROR}};
// BMI088初始化配置数组for gyro,第一列为reg地址,第二列为写入的配置值,第三列为错误码(如果出错)
static uint8_t BMI088_Gyro_Init_Table[BMI088_WRITE_GYRO_REG_NUM][3] =
{
{BMI088_GYRO_RANGE, BMI088_GYRO_2000, BMI088_GYRO_RANGE_ERROR},
{BMI088_GYRO_BANDWIDTH, BMI088_GYRO_2000_230_HZ | BMI088_GYRO_BANDWIDTH_MUST_Set, BMI088_GYRO_BANDWIDTH_ERROR},
{BMI088_GYRO_LPM1, BMI088_GYRO_NORMAL_MODE, BMI088_GYRO_LPM1_ERROR},
{BMI088_GYRO_CTRL, BMI088_DRDY_ON, BMI088_GYRO_CTRL_ERROR},
{BMI088_GYRO_INT3_INT4_IO_CONF, BMI088_GYRO_INT3_GPIO_PP | BMI088_GYRO_INT3_GPIO_LOW, BMI088_GYRO_INT3_INT4_IO_CONF_ERROR},
{BMI088_GYRO_INT3_INT4_IO_MAP, BMI088_GYRO_DRDY_IO_INT3, BMI088_GYRO_INT3_INT4_IO_MAP_ERROR}};
// @attention : 以上两个数组配合各自的初始化函数使用. 若要修改请参照BMI088 datasheet
// ---------------------------以下私有函数,用于读写BMI088寄存器--------------------------------//
// ---------------------------以下私有函数,用于读写BMI088寄存器封装,blocking--------------------------------//
/**
* @brief BMI088寄存器Accel
*
@ -40,7 +16,7 @@ static void BMI088AccelRead(BMI088Instance *bmi088, uint8_t reg, uint8_t *datapt
static uint8_t tx[8] = {0x80, 0}; // 读取,第一个字节为0x80 | reg,第二个是dummy data
tx[0] |= reg;
SPITransRecv(bmi088->spi_acc, dataptr, tx, 2); // 第一个先发送reg地址,第二个发送dummy data
SPIRecv(bmi088->spi_acc, dataptr, len); // 第三个开始发送数据,别担心,会覆盖掉前面的数据(2个字节)
SPIRecv(bmi088->spi_acc, dataptr, len); // 第三个开始发送数据,别担心,会覆盖掉前面的数据(2个字节)
}
/**
@ -84,10 +60,31 @@ static void BMI088GyroWrite(BMI088Instance *bmi088, uint8_t reg, uint8_t data)
{
SPITransmit(bmi088->spi_gyro, &data, 1);
}
// -------------------------以上为私有函数,封装了BMI088寄存器读写函数,blocking--------------------------------//
// -------------------------以上为私有函数,用于读写BMI088寄存器--------------------------------//
// -------------------------以下为私有函数,用于初始化BMI088acc和gyro--------------------------------//
// -------------------------以下为私有函数,用于初始化BMI088acc和gyro的硬件和配置--------------------------------//
#define REG 0
#define DATA 1
#define ERROR 2
// BMI088初始化配置数组for accel,第一列为reg地址,第二列为写入的配置值,第三列为错误码(如果出错)
static uint8_t BMI088_Accel_Init_Table[BMI088_WRITE_ACCEL_REG_NUM][3] =
{
{BMI088_ACC_PWR_CTRL, BMI088_ACC_ENABLE_ACC_ON, BMI088_ACC_PWR_CTRL_ERROR},
{BMI088_ACC_PWR_CONF, BMI088_ACC_PWR_ACTIVE_MODE, BMI088_ACC_PWR_CONF_ERROR},
{BMI088_ACC_CONF, BMI088_ACC_NORMAL | BMI088_ACC_800_HZ | BMI088_ACC_CONF_MUST_Set, BMI088_ACC_CONF_ERROR},
{BMI088_ACC_RANGE, BMI088_ACC_RANGE_6G, BMI088_ACC_RANGE_ERROR},
{BMI088_INT1_IO_CTRL, BMI088_ACC_INT1_IO_ENABLE | BMI088_ACC_INT1_GPIO_PP | BMI088_ACC_INT1_GPIO_LOW, BMI088_INT1_IO_CTRL_ERROR},
{BMI088_INT_MAP_DATA, BMI088_ACC_INT1_DRDY_INTERRUPT, BMI088_INT_MAP_DATA_ERROR}};
// BMI088初始化配置数组for gyro,第一列为reg地址,第二列为写入的配置值,第三列为错误码(如果出错)
static uint8_t BMI088_Gyro_Init_Table[BMI088_WRITE_GYRO_REG_NUM][3] =
{
{BMI088_GYRO_RANGE, BMI088_GYRO_2000, BMI088_GYRO_RANGE_ERROR},
{BMI088_GYRO_BANDWIDTH, BMI088_GYRO_2000_230_HZ | BMI088_GYRO_BANDWIDTH_MUST_Set, BMI088_GYRO_BANDWIDTH_ERROR},
{BMI088_GYRO_LPM1, BMI088_GYRO_NORMAL_MODE, BMI088_GYRO_LPM1_ERROR},
{BMI088_GYRO_CTRL, BMI088_DRDY_ON, BMI088_GYRO_CTRL_ERROR},
{BMI088_GYRO_INT3_INT4_IO_CONF, BMI088_GYRO_INT3_GPIO_PP | BMI088_GYRO_INT3_GPIO_LOW, BMI088_GYRO_INT3_INT4_IO_CONF_ERROR},
{BMI088_GYRO_INT3_INT4_IO_MAP, BMI088_GYRO_DRDY_IO_INT3, BMI088_GYRO_INT3_INT4_IO_MAP_ERROR}};
// @attention : 以上两个数组配合各自的初始化函数使用. 若要修改请参照BMI088 datasheet
/**
* @brief BMI088加速度计,
@ -156,9 +153,94 @@ static uint8_t BMI088GyroInit(BMI088Instance *bmi088)
error |= BMI088_Gyro_Init_Table[i][ERROR];
//{i--;} 可以设置retry次数,如果retry次数用完了,则返回error
}
bmi088->acc_coef = 1.0; // 尚未初始化时设定为1,使得BMI088Acquire可以正常使用
bmi088->BMI088_GYRO_SEN = BMI088_GYRO_2000_SEN; // 后续改为从initTable中获取
bmi088->BMI088_ACCELL_SEN = BMI088_ACCEL_6G_SEN; // 用宏字符串拼接
return error;
}
// -------------------------以上为私有函数,用于初始化BMI088acc和gyro--------------------------------//
// -------------------------以上为私有函数,用于初始化BMI088acc和gyro的硬件和配置--------------------------------//
// -------------------------以下为公有函数,用于注册BMI088,标定和数据读取--------------------------------//
BMI088_Data_t BMI088Acquire(BMI088Instance *bmi088)
{
// 分配空间保存返回的数据,指针传递
static BMI088_Data_t data_store;
static float dt_imu = 1.0; // 初始化为1,这样也可以不用first_read_flag,各有优劣
// 如果是blocking模式,则主动触发一次读取并返回数据
static uint8_t buf[6] = {0}; // 最多读取6个byte(gyro)
static uint8_t first_read_flag; // 判断是否时第一次进入此函数(第一次读取)
// 用于初始化DWT的计数,暂时没想到更好的方法
if (!first_read_flag)
DWT_GetDeltaT(& bmi088->bias_dwt_cnt); // 初始化delta
else
dt_imu = DWT_GetDeltaT(&bmi088->bias_dwt_cnt);
// 读取accel的x轴数据首地址,bmi088内部自增读取地址
BMI088AccelRead(bmi088, BMI088_ACCEL_XOUT_L, buf, 6); // 3* sizeof(int16_t)
static float calc_coef_acc; // 防止重复计算
if (!first_read_flag) // 初始化的时候赋值
calc_coef_acc = bmi088->BMI088_ACCELL_SEN * bmi088->acc_coef;
bmi088->acc[0] = calc_coef_acc * (float)(int16_t)(((buf[1]) << 8) | buf[0]);
bmi088->acc[1] = calc_coef_acc * (float)(int16_t)(((buf[3]) << 8) | buf[2]);
bmi088->acc[3] = calc_coef_acc * (float)(int16_t)(((buf[5]) << 8) | buf[4]);
BMI088GyroRead(bmi088, BMI088_GYRO_X_L, buf, 6); // 连续读取3个(3*2=6)轴的角速度
static float gyrosen, bias1, bias2, bias3;
if (!first_read_flag)
{ // 先保存,减少访问内存的开销,直接访问栈上变量
gyrosen = bmi088->BMI088_GYRO_SEN;
bias1 = bmi088->gyro_offset[0];
bias2 = bmi088->gyro_offset[1];
bias3 = bmi088->gyro_offset[2];
first_read_flag = 1; // 最后在这里,完成一次读取,标志第一次读取完成
} // 别担心,初始化调用的时候offset(即零飘bias)是0
bmi088->gyro[0] = (float)(int16_t)(((buf[1]) << 8) | buf[0]) * gyrosen - bias1 * dt_imu;
bmi088->gyro[0] = (float)(int16_t)(((buf[3]) << 8) | buf[2]) * gyrosen - bias2 * dt_imu;
bmi088->gyro[0] = (float)(int16_t)(((buf[5]) << 8) | buf[4]) * gyrosen - bias3 * dt_imu;
BMI088AccelRead(bmi088,BMI088_TEMP_M,buf,2); // 读温度,温度传感器在accel上
bmi088->temperature= (float)(int16_t)(((buf[0] << 3) | (buf[1] >> 5))) * BMI088_TEMP_FACTOR + BMI088_TEMP_OFFSET;
return data_store;
// 如果是IT模式,则检查标志位.当传感器数据准备好会触发外部中断,中断服务函数会将标志位置1
if (bmi088->work_mode == BMI088_BLOCK_TRIGGER_MODE && bmi088->update_flag.imu_ready == 1)
return data_store;
// 如果数据还没准备好,则返回空数据?或者返回上一次的数据?或者返回错误码? @todo
if (bmi088->update_flag.imu_ready == 0)
return data_store;
}
/* pre calibrate parameter to go here */
#warning REMEMBER TO SET PRE CALIBRATE PARAMETER IF YOU CHOOSE NOT TO CALIBRATE
#define BMI088_PRE_CALI_ACC_X_OFFSET 0.0f
#define BMI088_PRE_CALI_ACC_Y_OFFSET 0.0f
// macro to go here... 预设标定参数
/**
* @brief BMI088 acc gyro
* @note bmi088->bias和gNorm中,
* @attention blocking还是IT,blocking模式,(RobotInit关闭了全局中断)
* @attention ,使线.线
*
* @param _bmi088 BMI088实例
*/
void BMI088CalibrateIMU(BMI088Instance *_bmi088)
{
if (_bmi088->cali_mode == BMI088_CALIBRATE_ONLINE_MODE)
{
}
else
{
// 读取标定数据
// code to go here ...
}
}
// 考虑阻塞模式和非阻塞模式的兼容性,通过条件编译(则需要在编译前修改宏定义)或runtime参数判断
// runtime的开销不大(一次性判断),但是需要修改函数原型,增加参数,代码长度增加(但不多)
@ -168,5 +250,43 @@ static uint8_t BMI088GyroInit(BMI088Instance *bmi088)
BMI088Instance *BMI088Register(BMI088_Init_Config_s *config)
{
uint8_t error = BMI088_NO_ERROR;
// 申请内存
BMI088Instance *bmi088_instance = (BMI088Instance *)zero_malloc(sizeof(BMI088Instance));
// 从右向左赋值,让bsp instance保存指向bmi088_instance的指针(父指针),便于在底层中断中访问bmi088_instance
config->acc_int_config.id =
config->gyro_int_config.id =
config->spi_acc_config.id =
config->spi_gyro_config.id =
config->heat_pwm_config.id = bmi088_instance;
// 目前只实现了!!!阻塞读取模式!!!.如果需要使用IT模式,则需要修改这里的代码,为spi和gpio注册callback(默认为NULL)
// 还需要设置SPI的传输模式为DMA模式或IT模式(默认为blocking)
// 可以通过conditional compilation或者runtime参数判断
// code to go here ...
// INT_ACC EXTI CALLBACK: 检查是否有传输正在进行,如果没有则开启SPI DMA传输,有则置位wait标志位;
// 第一次是加速度计,第二次是温度.
// INT_GYRO EXTI CALLBACK: 开启SPI DMA传输,不会出现等待传输的情况
// SPI_GYRO DMA CALLBACK: 解算陀螺仪数据,
// SPI_ACC DMA CALLBACK: 解算加速度计数据,清除温度wait标志位并启动温度传输,第二次进入中断时解算温度数据
// 还有其他方案可用,比如阻塞等待传输完成,但是比较笨.
bmi088_instance->spi_acc = SPIRegister(&config->spi_acc_config);
bmi088_instance->spi_gyro = SPIRegister(&config->spi_gyro_config);
bmi088_instance->acc_int = GPIORegister(&config->acc_int_config);
bmi088_instance->gyro_int = GPIORegister(&config->gyro_int_config);
bmi088_instance->heat_pwm = PWMRegister(&config->heat_pwm_config);
// 初始化acc和gyro
error |= BMI088AccelInit(bmi088_instance);
error |= BMI088GyroInit(bmi088_instance);
// 标定acc和gyro
BMI088CalibrateIMU(bmi088_instance);
return 0;
}

View File

@ -7,10 +7,6 @@
#include "bsp_pwm.h"
#include "stdint.h"
#define BMI088_PRE_CALI_ACC_X_OFFSET 0.0f
#define BMI088_PRE_CALI_ACC_Y_OFFSET 0.0f
// macro to go here... 预设标定参数
// bmi088工作模式枚举
typedef enum
{
@ -21,10 +17,23 @@ typedef enum
// bmi088标定方式枚举,若使用预设标定参数,注意修改预设参数
typedef enum
{
BMI088_CALIBRATE_MODE = 0, // 初始化时进行标定
BMI088_LOAD_PRE_CALI_MODE, // 使用预设标定参数,
BMI088_CALIBRATE_ONLINE_MODE = 0, // 初始化时进行标定
BMI088_LOAD_PRE_CALI_MODE, // 使用预设标定参数,
} BMI088_Calibrate_Mode_e;
#pragma pack(1) // 1字节对齐
/* BMI088数据*/
typedef struct
{
float gyro[3]; // 陀螺仪数据,xyz
float acc[3]; // 加速度计数据,xyz
float temperature; // 温度
// uint32_t timestamp; // 时间戳
// uint32_t count;
} BMI088_Data_t;
#pragma pack() // 恢复默认对齐,需要传输的结构体务必开启1字节对齐
/* BMI088实例结构体定义 */
typedef struct
{
@ -53,6 +62,18 @@ typedef struct
float BMI088_GYRO_SEN;
// 用于计算两次采样的时间间隔
uint32_t bias_dwt_cnt;
// 数据更新标志位
struct // 位域,节省空间提高可读性
{
uint8_t gyro : 1; // 1:有新数据,0:无新数据
uint8_t acc : 1;
uint8_t temp : 1;
uint8_t gyro_overrun : 1; // 1:数据溢出,0:无溢出
uint8_t acc_overrun : 1;
uint8_t temp_overrun : 1;
uint8_t imu_ready : 1; // 1:IMU数据准备好,0:IMU数据未准备好(gyro+acc)
// 后续可添加其他标志位
} update_flag;
} BMI088Instance;
/* BMI088初始化配置 */
@ -68,7 +89,6 @@ typedef struct
PWM_Init_Config_s heat_pwm_config;
} BMI088_Init_Config_s;
/**
* @brief BMI088,BMI088实例指针
* @note BMI088,BMI088Init而不是Register
@ -78,4 +98,11 @@ typedef struct
*/
BMI088Instance *BMI088Register(BMI088_Init_Config_s *config);
/**
* @brief BMI088数据
* @todo 7float数据开销较大,使DMA或双缓冲区直接传递指针
* @param bmi088 BMI088实例指针
* @return BMI088_Data_t
*/
BMI088_Data_t BMI088Acquire(BMI088Instance *bmi088);
#endif // !__BMI088_H__

View File

@ -39,7 +39,7 @@ write写入:
1. 当accel int中断发生,开启DMA SPI传输,完成后将acc ready置位
2. 当gyro int中断发生,开启DMA SPI传输,完成后将gyro ready置位
3. 当温度数据中断发生,开启DMA传输,完成后将温度标志位置位
3. 当温度数据中断(温度传感器在accel内部,也是accel int)发生,开启DMA传输,完成后将温度标志位置位
> 由于DMA传输非阻塞,启动传输后只有到传输完成时才会拉高片选结束SPI transfer,因此需要在callback中加入标志位置位的操作.
这可以通过条件编译完成.

View File

@ -25,6 +25,13 @@
uint8_t GlobalDebugMode = 7;
void* zero_malloc(size_t size)
{
void* ptr=malloc(size);
memset(ptr,0,size);
return ptr;
}
// 快速开方
float Sqrt(float x)
{

View File

@ -87,6 +87,13 @@ extern uint8_t GlobalDebugMode;
#define VAL_MIN(a, b) ((a) < (b) ? (a) : (b))
#define VAL_MAX(a, b) ((a) > (b) ? (a) : (b))
/**
* @brief ,
*
* @param size
* @return void*
*/
void* zero_malloc(size_t size);
//<2F><><EFBFBD>ٿ<EFBFBD><D9BF><EFBFBD>
float Sqrt(float x);