前言
在前两章节中已经讲述了串口的通常用法, 【STM32】CubeMX+HAL库之串口
以及串口DMA空闲中断不定长接收与发送, 【STM32】串口DMA空闲中断不定长收发配自定义装包与解包
在本章节我们将介绍DMA双缓冲这一技术。在很多时候数据的发送频率与数据接收使用频率并不一致,数据来的太快,接收者还没来得及对其进行处理,下一帧的数据就到了,并将其覆盖,致使信息的有效性大大减弱。这时我们增加一个缓冲区来存放来不及处理的数据,就能更好地完成任务。双缓冲还有一个重要的应用是在图像部分,可以更好的解决显示图像时的闪烁延迟问题。
所用工具:
开发板:野火挑战者STM32H743IIT6
STM32CubeMX
IDE: Keil-MDK
满足怎样的需求
利用DMA传输方式节约CPU资源
利用串口空闲中断来拉起处理函数
提供掉线重启DMA的能力,保证热插拔的稳定性
总述流程
在主函数中调用初始化函数
main.c
/* USER CODE BEGIN PFP */
extern void uartInit(UART_HandleTypeDef *huart);
/* USER CODE END PFP */
/* USER CODE BEGIN 2 */
uartInit(&huart1);
/* USER CODE END 2 */
bsp_uart.c中的定义
uint8_t rx_buf[2][RX_BUFLEN]; //双缓冲Buffer
DMA_Stream_TypeDef *DMA_DBuf;
初始化函数实体
此部分初始化两个Buffer,与前部分的区别十分明显。
bsp_uart.c
void uartInit(UART_HandleTypeDef *huart)
{
DMA_DBuf = huart->hdmarx->Instance;
//enable the DMA transfer for the receiver request
//使能DMA串口接收
SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);
//enalbe idle interrupt
//使能空闲中断
__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);
//disable DMA
//失效DMA
__HAL_DMA_DISABLE(huart->hdmarx);
while(DMA_DBuf->CR & DMA_SxCR_EN)
{
__HAL_DMA_DISABLE(huart->hdmarx);
}
DMA_DBuf->PAR = (uint32_t) & (USART1->RDR); //F4中为DR
//memory buffer 1
//内存缓冲区1
DMA_DBuf->M0AR = (uint32_t)(rx_buf[0]);
//memory buffer 2
//内存缓冲区2
DMA_DBuf->M1AR = (uint32_t)(rx_buf[1]);
//data length
//数据长度
huart->RxXferSize = RX_BUFLEN * 2;
DMA_DBuf->NDTR = RX_BUFLEN * 2;
//enable double memory buffer
//使能双缓冲区
SET_BIT(DMA_DBuf->CR, DMA_SxCR_DBM);
//enable DMA
//使能DMA
__HAL_DMA_ENABLE(huart->hdmarx);
}
回调函数实体
在处理函数中也分别对两个Buffer进行处理。
bsp_uart.c
void BSP_UART_Callback(UART_HandleTypeDef *huart)
{
DMA_DBuf = huart->hdmarx->Instance;
if(huart->Instance == USART1)
{
if(huart->Instance->ISR & UART_FLAG_RXNE)//接收到数据 F4 为SR
{
__HAL_UART_CLEAR_PEFLAG(huart);
}
else if(huart->Instance->ISR & UART_FLAG_IDLE)
{
static uint16_t this_time_rx_len = 0;
__HAL_UART_CLEAR_PEFLAG(huart);
//disable DMA
//失效DMA
__HAL_DMA_DISABLE(huart->hdmarx);
//get receive data length, length = set_data_length - remain_length
//获取接收数据长度,长度 = 设定长度 - 剩余长度
this_time_rx_len = huart->RxXferSize - DMA_DBuf->NDTR;
//reset set_data_lenght
//重新设定数据长度
DMA_DBuf->NDTR = 36;
if ((DMA_DBuf->CR & DMA_SxCR_CT) == RESET)
{
//set memory buffer 1
//设定缓冲区1
DMA_DBuf->CR |= DMA_SxCR_CT;
//enable DMA
//使能DMA
__HAL_DMA_ENABLE(huart->hdmarx);
if(this_time_rx_len == 18)
{
//LoadData(0); 此部分写自己的解包程序
}
}
else
{
//设定缓冲区2
DMA1_Stream1->CR &= ~(DMA_SxCR_CT);
//enable DMA
//使能DMA
__HAL_DMA_ENABLE(huart->hdmarx);
if(this_time_rx_len == 18)
{
//LoadData(1); 此部分写自己的解包程序
}
}
}
}
}```
5. 在stm32h7xx_it.c中加入自定义回调函数
```c
/* USER CODE BEGIN PFP */
extern void BSP_UART_Callback(UART_HandleTypeDef *huart);
/* USER CODE END PFP */
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
BSP_UART_Callback(&huart1);
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
双缓冲的使用到这里就结束了,H7与其他芯片略有不同,需要自行找下问题,在代码中基本也都有注释,此部分学的还不够透彻,还望大佬指教更加官方的写法。
前言
在前两章节中已经讲述了串口的通常用法, 【STM32】CubeMX+HAL库之串口
以及串口DMA空闲中断不定长接收与发送, 【STM32】串口DMA空闲中断不定长收发配自定义装包与解包
在本章节我们将介绍DMA双缓冲这一技术。在很多时候数据的发送频率与数据接收使用频率并不一致,数据来的太快,接收者还没来得及对其进行处理,下一帧的数据就到了,并将其覆盖,致使信息的有效性大大减弱。这时我们增加一个缓冲区来存放来不及处理的数据,就能更好地完成任务。双缓冲还有一个重要的应用是在图像部分,可以更好的解决显示图像时的闪烁延迟问题。
所用工具:
开发板:野火挑战者STM32H743IIT6
STM32CubeMX
IDE: Keil-MDK
满足怎样的需求
利用DMA传输方式节约CPU资源
利用串口空闲中断来拉起处理函数
提供掉线重启DMA的能力,保证热插拔的稳定性
总述流程
在主函数中调用初始化函数
main.c
/* USER CODE BEGIN PFP */
extern void uartInit(UART_HandleTypeDef *huart);
/* USER CODE END PFP */
/* USER CODE BEGIN 2 */
uartInit(&huart1);
/* USER CODE END 2 */
bsp_uart.c中的定义
uint8_t rx_buf[2][RX_BUFLEN]; //双缓冲Buffer
DMA_Stream_TypeDef *DMA_DBuf;
初始化函数实体
此部分初始化两个Buffer,与前部分的区别十分明显。
bsp_uart.c
void uartInit(UART_HandleTypeDef *huart)
{
DMA_DBuf = huart->hdmarx->Instance;
//enable the DMA transfer for the receiver request
//使能DMA串口接收
SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);
//enalbe idle interrupt
//使能空闲中断
__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);
//disable DMA
//失效DMA
__HAL_DMA_DISABLE(huart->hdmarx);
while(DMA_DBuf->CR & DMA_SxCR_EN)
{
__HAL_DMA_DISABLE(huart->hdmarx);
}
DMA_DBuf->PAR = (uint32_t) & (USART1->RDR); //F4中为DR
//memory buffer 1
//内存缓冲区1
DMA_DBuf->M0AR = (uint32_t)(rx_buf[0]);
//memory buffer 2
//内存缓冲区2
DMA_DBuf->M1AR = (uint32_t)(rx_buf[1]);
//data length
//数据长度
huart->RxXferSize = RX_BUFLEN * 2;
DMA_DBuf->NDTR = RX_BUFLEN * 2;
//enable double memory buffer
//使能双缓冲区
SET_BIT(DMA_DBuf->CR, DMA_SxCR_DBM);
//enable DMA
//使能DMA
__HAL_DMA_ENABLE(huart->hdmarx);
}
回调函数实体
在处理函数中也分别对两个Buffer进行处理。
bsp_uart.c
void BSP_UART_Callback(UART_HandleTypeDef *huart)
{
DMA_DBuf = huart->hdmarx->Instance;
if(huart->Instance == USART1)
{
if(huart->Instance->ISR & UART_FLAG_RXNE)//接收到数据 F4 为SR
{
__HAL_UART_CLEAR_PEFLAG(huart);
}
else if(huart->Instance->ISR & UART_FLAG_IDLE)
{
static uint16_t this_time_rx_len = 0;
__HAL_UART_CLEAR_PEFLAG(huart);
//disable DMA
//失效DMA
__HAL_DMA_DISABLE(huart->hdmarx);
//get receive data length, length = set_data_length - remain_length
//获取接收数据长度,长度 = 设定长度 - 剩余长度
this_time_rx_len = huart->RxXferSize - DMA_DBuf->NDTR;
//reset set_data_lenght
//重新设定数据长度
DMA_DBuf->NDTR = 36;
if ((DMA_DBuf->CR & DMA_SxCR_CT) == RESET)
{
//set memory buffer 1
//设定缓冲区1
DMA_DBuf->CR |= DMA_SxCR_CT;
//enable DMA
//使能DMA
__HAL_DMA_ENABLE(huart->hdmarx);
if(this_time_rx_len == 18)
{
//LoadData(0); 此部分写自己的解包程序
}
}
else
{
//设定缓冲区2
DMA1_Stream1->CR &= ~(DMA_SxCR_CT);
//enable DMA
//使能DMA
__HAL_DMA_ENABLE(huart->hdmarx);
if(this_time_rx_len == 18)
{
//LoadData(1); 此部分写自己的解包程序
}
}
}
}
}```
5. 在stm32h7xx_it.c中加入自定义回调函数
```c
/* USER CODE BEGIN PFP */
extern void BSP_UART_Callback(UART_HandleTypeDef *huart);
/* USER CODE END PFP */
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
BSP_UART_Callback(&huart1);
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
双缓冲的使用到这里就结束了,H7与其他芯片略有不同,需要自行找下问题,在代码中基本也都有注释,此部分学的还不够透彻,还望大佬指教更加官方的写法。
举报