DMA案例 外设到内存搬运
需求
使用DMA的方式将串口接收缓存寄存器的值搬运到内存中,同时闪烁LED1。
CubeMX配置
串口配置
DMA配置
串口中断配置
用到的库函数
- __HAL_UART_ENABLE
#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 需要使能的中断
返回值: 无
- 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)
- __HAL_UART_GET_FLAG
#define __HAL_UART_GET_FLAG(
__HANDLE__, // 参数一
__FLAG__ // 参数二
) (((__HANDLE__)->Instance->SR & (__FLAG__)) == (__FLAG__))
参数一:HANDLE 串口句柄
参数二:FLAG 需要查看的FLAG
返回值:FLAG的值
- __HAL_UART_CLEAR_IDLEFLAG
#define __HAL_UART_CLEAR_IDLEFLAG(
__HANDLE__ // 参数一
) __HAL_UART_CLEAR_PEFLAG(__HANDLE__)
参数一:HANDLE 串口句柄
返回值:无
- HAL_UART_DMAStop
HAL_StatusTypeDef HAL_UART_DMAStop(
UART_HandleTypeDef *huart
)
参数一:UART_HandleTypeDef *huart 串口句柄
返回值:HAL_StatusTypeDef,HAL状态(OK,busy,ERROR,TIMEOUT)
- __HAL_DMA_GET_COUNTER
#define __HAL_DMA_GET_COUNTER(
__HANDLE__ // 参数一
) ((__HANDLE__)->Instance->CNDTR)
参数一:HANDLE 串口句柄
返回值:未传输数据的大小
代码实现
如何判断串口接收是否完成?如何知道串口收到数据的长度?
使用串口空闲中断(IDLE)!
- 串口空闲时,触发空闲中断;
- 空闲中断标志位由硬件置1,软件清零
利用串口空闲中断,可以用如下流程实现DMA控制的任意长数据接收;
1. 使能IDLE空闲中断;
2. 使能DMA接收中断;
3. 收到串口接收中断,DMA不断传输数据到缓冲区;
4. 一帧数据接收完毕,串口暂时空闲,触发串口空闲中断;
5. 在中断服务函数中,清除中断标志位,关闭DMA传输(防止干扰);
6. 计算刚才收到了多少个字节的数据。
7. 处理缓冲区数据,开启DMA传输,开始下一帧接收。
// main.c
#define BUF_SIZE 100
// 使能IDLE空闲中断
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
// 使能DMA接收中断
HAL_UART_Receive_DMA(&huart1, rcvBuf, BUF_SIZE);
while (1)
{
/* USER CODE END WHILE */
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);
HAL_Delay(300);
/* USER CODE BEGIN 3 */
}
// main.h
/* USER CODE BEGIN EM */
#define BUF_SIZE 100
/* USER CODE END EM */
// stm32f1xx_it.c
extern uint8_t rcvBuf[BUF_SIZE];
extern uint8_t rcvLen;
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) == SET){ // 判断IDLE标志位是否被置位
// 清除标志位
__HAL_UART_CLEAR_IDLEFLAG(&huart1);
// 停止DMA传输,防止干扰
HAL_UART_DMAStop(&huart1);
uint8_t temp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
// 计算数据长度
rcvLen = BUF_SIZE - temp;
//发送数据
HAL_UART_Transmit_DMA(&huart1, rcvBuf, rcvLen);
//开启DMA
HAL_UART_Receive_DMA(&huart1, rcvBuf, BUF_SIZE);
}
/* USER CODE END USART1_IRQn 1 */
}