重构bsp层,bsp层将和HAL的配置一致,修改CubeMX之后不需要修改bsp。重构bmi088。

This commit is contained in:
NeoZng
2022-12-30 23:39:04 +08:00
parent ab1a9726b1
commit a3631a5ca5
39 changed files with 842 additions and 256 deletions

105
bsp/usart/bsp_usart.c Normal file
View File

@@ -0,0 +1,105 @@
/**
* @file bsp_usart.c
* @author neozng
* @brief 串口bsp层的实现
* @version beta
* @date 2022-11-01
*
* @copyright Copyright (c) 2022
*
*/
#include "bsp_usart.h"
#include "stdlib.h"
#include "memory.h"
/* usart service instance, modules' info would be recoreded here using USARTRegister() */
/* usart服务实例,所有注册了usart的模块信息会被保存在这里 */
static USARTInstance *instance[DEVICE_USART_CNT] = {NULL};
/**
* @brief usart service will start automatically, after each module registered
* 串口服务会在每个实例注册之后自动启用
*
* @param _instance instance owned by module,模块拥有的串口实例
*/
static void USARTServiceInit(USARTInstance *_instance)
{
HAL_UARTEx_ReceiveToIdle_DMA(_instance->usart_handle, _instance->recv_buff, _instance->recv_buff_size);
// 关闭dma half transfer中断防止两次进入HAL_UARTEx_RxEventCallback()
// 这是HAL库的一个设计失误,发生DMA传输完成/半完成以及串口IDLE中断都会触发HAL_UARTEx_RxEventCallback()
// 我们只希望处理因此直接关闭DMA半传输中断第一种和第三种情况
__HAL_DMA_DISABLE_IT(_instance->usart_handle->hdmarx, DMA_IT_HT);
}
USARTInstance *USARTRegister(USART_Init_Config_s *init_config)
{
static uint8_t idx;
instance[idx] = (USARTInstance *)malloc(sizeof(USARTInstance));
memset(instance[idx], 0, sizeof(USARTInstance));
instance[idx]->module_callback = init_config->module_callback;
instance[idx]->recv_buff_size = init_config->recv_buff_size;
instance[idx]->usart_handle = init_config->usart_handle;
USARTServiceInit(instance[idx]);
return instance[idx++];
}
/* @todo 当前仅进行了形式上的封装,后续要进一步考虑是否将module的行为与bsp完全分离 */
void USARTSend(USARTInstance *_instance, uint8_t *send_buf, uint16_t send_size)
{
HAL_UART_Transmit_DMA(_instance->usart_handle, send_buf, send_size);
}
/**
* @brief 每次dma/idle中断发生时都会调用此函数.对于每个uart实例会调用对应的回调进行进一步的处理
* 例如:视觉协议解析/遥控器解析/裁判系统解析
*
* @todo neozng给HAL库的github repo提了issue, ST在最新的一次更新中为此提供了一个HAL_UARTEx_GetRxEventType()函数
* 这样就可以通过调用这个函数来确认是什么中断导致了回调函数的调用
*
* @note because DMA half transfer iterrupt(DMA_IT_HT) would call this callback function too, so we just
* disable it when transfer complete using macro: __HAL_DMA_DISABLE_IT(huart->hdmarx,DMA_IT_HT)
* 关闭dma half transfer中断防止两次进入HAL_UARTEx_RxEventCallback()
* 这是HAL库的一个设计失误,发生DMA传输完成/半完成以及串口IDLE中断都会触发HAL_UARTEx_RxEventCallback()
* 我们只希望处理因此直接关闭DMA半传输中断第一种和第三种情况
*
* @param huart uart handle indicate which uart is being handled 发生中断的串口
* @param Size not used temporarily,此次接收到的总数居量,暂时没用
*/
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
for (uint8_t i = 0; i < 3; ++i)
{
if (huart == instance[i]->usart_handle)
{
instance[i]->module_callback();
memset(instance[i]->recv_buff, 0, Size); // 接收结束后清空buffer,对于变长数据是必要的
HAL_UARTEx_ReceiveToIdle_DMA(instance[i]->usart_handle, instance[i]->recv_buff, instance[i]->recv_buff_size);
__HAL_DMA_DISABLE_IT(instance[i]->usart_handle->hdmarx, DMA_IT_HT);
break;
}
}
}
/**
* @brief when error occurs in the process of send/receive,this function will be called
* then just simply restart send/receive.
*
* @note most frequent error ex: parity/overrrun/frame error
*
* @param huart uart handle type, indicate where error comes from
*/
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
for (uint8_t i = 0; i < 3; ++i)
{
if (huart == instance[i]->usart_handle)
{
HAL_UARTEx_ReceiveToIdle_DMA(instance[i]->usart_handle, instance[i]->recv_buff, instance[i]->recv_buff_size);
__HAL_DMA_DISABLE_IT(instance[i]->usart_handle->hdmarx, DMA_IT_HT);
break;
}
}
}

50
bsp/usart/bsp_usart.h Normal file
View File

@@ -0,0 +1,50 @@
#ifndef BSP_RC_H
#define BSP_RC_H
#include <stdint.h>
#include "main.h"
#define DEVICE_USART_CNT 3 // C板至多分配3个串口
#define USART_RXBUFF_LIMIT 256 // if your protocol needs bigger buff, modify here
/* application callback,which resolves specific protocol,解析协议的回调函数 */
typedef void (*usart_module_callback)();
/* USARTInstance struct,each app would have one instance */
typedef struct
{
// 更新:弃用malloc方案,使用了固定大小的数组方便debug时查看
uint8_t recv_buff[USART_RXBUFF_LIMIT]; // 预先定义的最大buff大小,如果太小请修改USART_RXBUFF_LIMIT
uint8_t recv_buff_size; // 模块接收一包数据的大小
UART_HandleTypeDef *usart_handle; // 实例对应的usart_handle
usart_module_callback module_callback; // 解析收到的数据的回调函数
} USARTInstance;
/* usart 初始化配置结构体 */
typedef struct
{
uint8_t recv_buff_size; // 模块接收一包数据的大小
UART_HandleTypeDef *usart_handle; // 实例对应的usart_handle
usart_module_callback module_callback; // 解析收到的数据的回调函数
} USART_Init_Config_s;
/**
* @brief 注册一个串口实例.
*
* @param init_config 传入串口初始化结构体
*/
USARTInstance *USARTRegister(USART_Init_Config_s *init_config);
/**
* @todo 是否需要进一步封装发送buff和size,并创建一个串口任务以一定频率自动发送?
* 若采用此方法,则串口实例的拥有者仅需要在自己的任务中设置发送值,不需要关心发送buffer大小以及何时发送.
*
* @brief api for sending data through a specific serial port,indicated by the first parameter:id
* 通过调用该函数可以发送一帧数据,需要传入一个usart实例,发送buff以及这一帧的长度
*
* @param id specify which usart would be used
* @param send_size how many bytes to send
*/
void USARTSend(USARTInstance *_instance, uint8_t *send_buf, uint16_t send_size);
#endif

79
bsp/usart/bsp_usart.md Normal file
View File

@@ -0,0 +1,79 @@
# bsp_usart
<p align='right'>neozng1@hnu.edu.cn</p>
> TODO:为初始化定义一个结构体`usart_init_config`用于保存初始化所需的参数从而避免单独赋值,使得整体风格统一。
## 使用说明
若你需要构建新的基于串口的module首先需要拥有一个`usart_instance`的指针用于操作串口对象。
需要在串口实例下设定接收的数据包的长度,实例对应的串口硬件(通过`UART_HandleTypeDef`指定,如`&huart1`),解析接收数据对应的回调函数这三个参数。然后,调用`USARTRegister()`并传入配置好的`usart_instance`指针即可。
若要发送数据,调用`USARTSend()`。注意buffsize务必小于buff的大小否则造成指针越界后果未知。
串口硬件收到数据时,会将其存入`usart_instance.recv_buff[]`中,当收到完整一包数据,会调用设定的回调函数`module_callback`(即你提供的解析函数)。在此函数中,你可以通过`usart_instance.recv_buff[]`访问串口收到的数据。
## 代码结构
.h文件内包括了外部接口和类型定义,以及模块对应的宏。c文件内为私有函数和外部接口的定义。
## 类型定义
```c
#define DEVICE_USART_CNT 3 // C板至多分配3个串口
#define USART_RXBUFF_LIMIT 128 // if your protocol needs bigger buff, modify here
typedef void (*usart_module_callback)();
/* usart_instance struct,each app would have one instance */
typedef struct
{
uint8_t recv_buff[USART_RXBUFF_LIMIT]; // 预先定义的最大buff大小,如果太小请修改USART_RXBUFF_LIMIT
uint8_t recv_buff_size; // 模块接收一包数据的大小
UART_HandleTypeDef *usart_handle; // 实例对应的usart_handle
usart_module_callback module_callback; // 解析收到的数据的回调函数
} usart_instance;
```
- `DEVICE_USART_CNT`是开发板上可用的串口数量。
- `USART_RXBUFF_LIMIT`是串口单次接收的数据长度上限暂时设为128如果需要更大的buffer容量修改该值。
- `usart_module_callback()`是模块提供给串口接收中断回调函数使用的协议解析函数指针。对于每个需要串口的模块,需要定义一个这样的函数用于解包数据。
- 每定义一个`usart_instance`,就代表一个串口的**实例**对象。一个串口实例内有接收buffer单个数据包的大小该串口对应的`HAL handle`(代表其使用的串口硬件具体是哪一个)以及用于解包数据的回调函数。
## 外部接口
```c
void USARTRegister(usart_instance *_instance);
void USARTSend(usart_instance *_instance, uint8_t *send_buf, uint16_t send_size);
```
- `USARTRegister`是用于初始化串口对象的接口module层的模块对象也应当为一个结构体内要包含一个`usart_instance`
**在调用该函数之前,需要先对其成员变量`*usart_handle`,`module_callback()`以及`recv_buff_size`进行赋值。**
- `USARTSend()`是通过模块通过其拥有的串口对象发送数据的接口调用时传入的参数为串口实例指针发送缓存以及此次要发送的数据长度8-bit\*n)。
## 私有函数和变量
在.c文件内设为static的函数和变量
```c
static usart_instance *instance[DEVICE_USART_CNT];
```
这是bsp层管理所有串口实例的入口。
```c
static void USARTServiceInit(usart_instance *_instance)
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
```
- `USARTServiceInit()`会被`USARTRegister()`调用,开启接收中断
- `HAL_UARTEx_RxEventCallback()``HAL_UART_ErrorCallback()`都是对HAL的回调函数的重定义原本的callback是`__week`修饰的弱定义),前者在发生**IDLE中断**或**单次DMA传输中断**后会被调用(说明收到了完整的一包数据),随后在里面根据中断来源,调用拥有产生了该中断的模块的协议解析函数进行数据解包;后者在串口传输出错的时候会被调用,重新开启接收。