1. 重点
- 加强理解DMA数据传输过程
- 加强掌握DMA的初始化流程
- 掌握DMA数据表查询
- 理解源和目标的配置
- 理解数据传输特点
- 能够动态配置源数据
2. 主要内容
2.1 需求
uint8_t data;
串口接收(&data);
data有数据了
实现串口的数据接收,要求采用dma的方式。
2.2 数据交互流程
- CPU配置好DMA
- 外部数据发送给串口外设
- 串口外设触发中断
- CPU处理中断逻辑,通知DMA干活
- DMA请求源数据
- DMA获取源数据
- DMA将获取的源数据交给目标
3. 开发流程
3.1 依赖引入
添加标准库中的gd32f4xx_dma.c
文件
3.2 DMA初始化
/**************** DMA p2m *******************/
// 时钟
rcu_periph_clock_enable(RCU_DMA1);
// 重置dma
dma_deinit(DMA1, DMA_CH5);
dma 配置
dma_single_data_parameter_struct dsdps;
dma_single_data_para_struct_init(&dsdps);
// 方向
dsdps.direction = DMA_PERIPH_TO_MEMORY;
// 内存: dst
dsdps.memory0_addr = (uint32_t)(g_recv_buff);
dsdps.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
// 外设: src
dsdps.periph_addr = (uint32_t)(&USART_DATA(USART0));
dsdps.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
// 数据长度
dsdps.number = USART_RECEIVE_LENGTH;
// dst数据宽度
dsdps.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
// 传输优先级
dsdps.priority = DMA_PRIORITY_ULTRA_HIGH;
dma_single_data_mode_init(DMA1, DMA_CH5, &dsdps);
配置 dma 子连接
dma_channel_subperipheral_select(DMA1, DMA_CH5, DMA_SUBPERI4);
// 默认开启接收
dma_flag_clear(DMA1, DMA_CH5, DMA_FLAG_FTF);
dma_channel_enable(DMA1, DMA_CH5);
- 配置时钟
- 初始化dma通道
- 配置dma与外设间的子链接
- 开启dma接收功能
3.3 接收中断
if (usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE) == SET) {
//读取缓冲区,清空缓冲区
usart_data_receive(USART0);
// 停止DMA传输,防止数据污染
dma_channel_disable(DMA1, DMA_CH5);
// 计算接收的数据长度
g_recv_length = USART_RECEIVE_LENGTH - dma_transfer_number_get(DMA1, DMA_CH5);
if(g_recv_length) {
g_recv_buff[g_recv_length] = '\0';
// TODO: g_recv_buff为接收的数据,g_recv_length为接收的长度
printf("rcv: %s\r\n", g_recv_buff);
}
g_recv_length = 0;
dma_flag_clear(DMA1, DMA_CH5, DMA_FLAG_FTF);
dma_channel_enable(DMA1, DMA_CH5);
}
- dma_channel_enable: 请求dma数据传输
- DMA_FLAG_FTF:为传输完成标记
3.4 串口外设DMA开启
// DMA接收功能配置
usart_dma_receive_config(usartx, USART_RECEIVE_DMA_ENABLE);
- 需要开启dma接收配置,才可以打开dam的功能
4. 串口接收理解
4.1 地址和目标地址
源地址和目标地址都是提前配置好的,当传输时,就会从源地址将数据传递给目标地址。
4.2 自增长
每传输一个字节数据,地址是否需要增长。这里包含了源地址是否需要增长,也包含了目标地址是否需要增长。
- 串口为数据源:寄存器地址不变,大小不变,我们只是从这里取数据,所以不需要增长。
- 内存为目标:每一个字节都要放在独立的索引里,需要自增长。
4.3 数据宽度
数据宽度表示一次传递多上个bit。
4.4 数据长度
最大的传输字节数。