【AT32】浅谈AT32 串口DMA接收数据之大坑
·
DMA 介绍
DMA(Direct Memory Access,直接内存访问)是一种计算机系统中的硬件技术,允许外部设备(如硬盘、网卡、传感器等)直接与内存交换数据,而无需中央处理器(CPU)的全程干预。其核心目标是通过减少CPU负担、提高数据传输效率,从而优化系统整体性能。
串口DMA
AT32 MCU 芯片使用串口的DMA功能传输
打开Work Bench工具
进行这些配置
- 配置RX DMA

- 配置TX DMA

- 配置中断

生成代码
DMA生成的代码如下 需要用到的时候要进行修改
/* init dma1 channel1 */
wk_dma1_channel1_init();
/* config dma channel transfer parameter */
/* user need to modify define values DMAx_CHANNELy_XXX_BASE_ADDR and DMAx_CHANNELy_BUFFER_SIZE in at32xxx_wk_config.h */
wk_dma_channel_config(DMA1_CHANNEL1,
(uint32_t)&USART3->dt,
DMA1_CHANNEL1_MEMORY_BASE_ADDR,
DMA1_CHANNEL1_BUFFER_SIZE);
dma_channel_enable(DMA1_CHANNEL1, TRUE);
/* init dma1 channel2 */
wk_dma1_channel2_init();
/* config dma channel transfer parameter */
/* user need to modify define values DMAx_CHANNELy_XXX_BASE_ADDR and DMAx_CHANNELy_BUFFER_SIZE in at32xxx_wk_config.h */
wk_dma_channel_config(DMA1_CHANNEL2,
(uint32_t)&USART3->dt,
DMA1_CHANNEL2_MEMORY_BASE_ADDR,
DMA1_CHANNEL2_BUFFER_SIZE);
dma_channel_enable(DMA1_CHANNEL2, TRUE);
修改及添加的部分
- 创建用户初始化
dma_channel_enable(DMA1_CHANNEL1, FALSE);
wk_dma_channel_config(DMA1_CHANNEL1, //重新映射接受的地址和长度
(uint32_t)&USART3->dt,
(uint32_t)g_uart3_rx_buf,
256);
dma_channel_enable(DMA1_CHANNEL1, TRUE); // 开启DMA
dma_interrupt_enable(DMA1_CHANNEL2, DMA_FDT_INT, TRUE); // 启动DMA发送中断
usart_interrupt_enable(RS485_USART,USART_IDLE_INT,TRUE);// 启动Usart空闲中断
usart_interrupt_enable(RS485_USART, USART_TDBE_INT, TRUE);//发送完成中断
- 定义用户发送的函数
void usart_dma_data_send(uint8_t *datas, uint32_t len)
{
RS485_TX_EN() ;// 使能发送数据
// while(dma_flag_get(DMA1_FDT1_FLAG) == RESET);
dma_channel_enable(DMA1_CHANNEL2, FALSE);
wk_dma_channel_config(DMA1_CHANNEL2, (uint32_t)&USART3->dt, (uint32_t)datas, len);
// 重新初始化DMA
dma_channel_enable(DMA1_CHANNEL2, TRUE);
RS485_RX_EN() ;// 使能接收数据
}
- 定义中断函数
```c
/**
* @brief dma 接收回调 放到中断服务函数中去
* @param
*/
void UART_DMA_RX_CallBack(void)
{
if (dma_interrupt_flag_get(DMA1_FDT1_FLAG))
{
dma_flag_clear(DMA1_FDT1_FLAG);
}
}
/**
* @brief dma 发送回调 放到中断服务函数中去
* @param
*/
void UART_DMA_TX_CallBack(void)
{
if (dma_interrupt_flag_get(DMA1_FDT2_FLAG))
{
Led_485_TX_Flag = 1;
xSemaphoreGiveFromISR(g_UART3_TX_Semaphore, NULL); //操作信号量
dma_flag_clear(DMA1_FDT2_FLAG);
dma_channel_enable(DMA1_CHANNEL2, FALSE); // 关闭发生DMA
}
}
//这个函数放到串口中断中
void UART_DEVICE_CallBack(void)
{
uint32_t size=0;
// uint8_t uart_data = 0;
if (usart_flag_get(USART3, USART_RDBF_FLAG) != RESET)
{
for (int i = 0; i < 256; i++)
{
xQueueSendFromISR(g_xUART3_RX_Queue, (const void *)&g_uart3_rx_buf[i], NULL);
}
Led_485_RX_Flag = 1;
usart_flag_clear(USART3, USART_RDBF_FLAG);
usart_interrupt_enable(USART3, USART_RDBF_FLAG, FALSE);
}
// 发送中断
if (usart_interrupt_flag_get(USART3, USART_TDBE_FLAG) != RESET) // 发送空闲
{
Led_485_TX_Flag = 1;
xSemaphoreGiveFromISR(g_UART3_TX_Semaphore, NULL);
usart_flag_clear(USART3, USART_TDBE_FLAG);
usart_interrupt_enable(USART3, USART_TDBE_INT, FALSE);
}
if (usart_interrupt_flag_get(USART3, USART_IDLEF_FLAG) != RESET)
{
dma_channel_enable(DMA1_CHANNEL1, FALSE);
size = dma_data_number_get(DMA1_CHANNEL1);
for (int i = 0; i < 256-size; i++)
{
xQueueSendFromISR(g_xUART3_RX_Queue, (const void *)&g_uart3_rx_buf[i], NULL);
}
wk_dma_channel_config(DMA1_CHANNEL1,
(uint32_t)&USART3->dt,
(uint32_t)g_uart3_rx_buf,
256);
Led_485_RX_Flag = 1;
dma_flag_clear(DMA1_FDT1_FLAG);
dma_channel_enable(DMA1_CHANNEL1, TRUE);
usart_flag_clear(USART3, USART_IDLEF_FLAG);
}
}
天坑
注意在上面这个函数中不添加 重新映射的代码 每次收到的数据长度都是不固定的

这个函数也是迷惑用户 明明写的是接受多少数据长度 其实 DMA空间中剩余的长度,这个bug困扰了我两天 一直以为对消息队列操作有问题 导致我解析数据从第二帧开始不对。仿真也是看来很久才发现。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)