目录
实验要求
使用 DMA 的方式将串口接收缓存寄存器的值搬运到内存中,同时闪烁 LED1 。
CubeMX配置
DMA配置:
串口中断配置
用到的库函数
1. __HAL_UART_ENABLE_IT
#define __HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__) ((((__INTERRUPT__) >> 28U) == UART_CR1_REG_INDEX)? ((__HANDLE__)->Instance->CR1 |= ((__INTERRUPT__) & UART_IT_MASK)): \
(((__INTERRUPT__) >> 28U) == UART_CR2_REG_INDEX)? ((__HANDLE__)->Instance->CR2 |= ((__INTERRUPT__) & UART_IT_MASK)): \
((__HANDLE__)->Instance->CR3 |= ((__INTERRUPT__) & UART_IT_MASK)))
- 参数一:HANDLE,串口句柄
- 参数二:INTERRUPT,需要使能的中断
- 返回值:无
2. HAL_UART_Receive_DMA
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData,uint16_t Size)
- 参数一:UART_HandleTypeDef *huart,串口句柄
- 参数二:uint8_t *pData,接收缓存首地址
- 参数三:uint16_t Size,接收缓存长度
- 返回值:HAL_StatusTypeDef,HAL状态(OK,busy,ERROR,TIMEOUT)
3. __HAL_UART_GET_FLAG
#define __HAL_UART_GET_FLAG(__HANDLE__, __FLAG__) (((__HANDLE__)->Instance->SR &(__FLAG__)) == (__FLAG__))
- 参数一:HANDLE,串口句柄
- 参数二:FLAG,需要查看的FLAG
- 返回值:FLAG的值
4. __HAL_UART_CLEAR_IDLEFLAG
#define __HAL_UART_CLEAR_IDLEFLAG(__HANDLE__) __HAL_UART_CLEAR_PEFLAG(__HANDLE__)
- 参数一:HANDLE,串口句柄
- 返回值:无
5. HAL_UART_DMAStop
HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart)
- 参数一:UART_HandleTypeDef *huart,串口句柄
- 返回值:HAL_StatusTypeDef,HAL状态(OK,busy,ERROR,TIMEOUT)
6. __HAL_DMA_GET_COUNTER
#define __HAL_DMA_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->CNDTR)
- 参数一:HANDLE,串口句柄
- 返回值:未传输数据大小
代码实现
如何判断串口接收是否完成?如何知道串口收到数据的长度?使用 串口空闲中断 ( IDLE )!
- 串口空闲时,触发空闲中断;
- 空闲中断标志位由硬件置1,软件清零
利用串口空闲中断,可以用如下流程实现 DMA 控制的任意长数据接收:
- 使能IDLE空闲中断;
- 使能DMA接收中断;
- 收到串口接收中断,DMA不断传输数据到缓冲区;
- 一帧数据接收完毕,串口暂时空闲,触发串口空闲中断;
- 在中断服务函数中,清除中断标志位,关闭DMA传输(防止干扰);
- 计算刚才收到了多少个字节的数据。
- 处理缓冲区数据,开启DMA传输,开始下一帧接收。
main.c
uint8_t rcvBUF[BUF_SIZE] = {0};// 接收数据缓存数组
uint8_t rcvlen = 0;// 接收一帧数据的长度
mian函数里
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);// 使能IDLE空闲中断
HAL_UART_Receive_DMA(&huart1,rcvBUF,BUF_SIZE);// 使能DMA接收中断
while (1)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
HAL_Delay(1000);
}
main.h
#define BUF_SIZE 100
stm32f1xx_it.c
extern uint8_t rcvBUF[BUF_SIZE];// 接收数据缓存数组
extern uint8_t rcvlen;// 接收一帧数据的长度
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart1);
if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) == SET) 判断IDLE标志位是否被置位
{
__HAL_UART_CLEAR_IDLEFLAG(&huart1);// 清除标志位
HAL_UART_DMAStop(&huart1);// 停止DMA传输,防止干扰
uint8_t tmp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
rcvlen = BUF_SIZE - tmp;//计算数据长度
HAL_UART_Transmit_DMA(&huart1,rcvBUF,rcvlen); //发送数据
HAL_UART_Receive_DMA(&huart1,rcvBUF,BUF_SIZE);//再次开启DMA,接收下一帧数据
}
}