1. 重点
- 加强理解DMA数据传输过程
- 加强掌握DMA的初始化流程
- 掌握DMA数据表查询
- 理解源和目标的配置
- 理解数据传输特点
- 能够动态配置源数据
2. 主要内容
2.1 需求
uint8_t data = 0x01;
串口发送(data);
实现串口的发送数据, 要求采用dma的方式
2.2 数据交互流程
- CPU配置好DMA
- CPU通知DMA干活
- DMA请求源数据
- DMA获取源数据
- DMA将获取的源数据交给目标
2.3 开发流程
2.3.1 依赖引入
添加标准库中的gd32f4xx_dma.c
文件
2.3.2 DMA初始化
/***************** DMA m2p *******************/
// 时钟
rcu_periph_clock_enable(RCU_DMA1);
// 重置dma
dma_deinit(DMA1, DMA_CH7);
dma 配置
dma_single_data_parameter_struct dsdps;
dma_single_data_para_struct_init(&dsdps);
// 方向
dsdps.direction = DMA_MEMORY_TO_PERIPH;
// 内存: src
// dsdps.memory0_addr = (uint32_t)src;
dsdps.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
// 外设: dst
dsdps.periph_addr = (uint32_t)(&USART_DATA(USART0));
dsdps.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
// 数据长度
// dsdps.number = ARR_LEN;
// 数据宽度
dsdps.periph_memory_width = DMA_MEMORY_WIDTH_8BIT;
// 传输优先级
dsdps.priority = DMA_PRIORITY_ULTRA_HIGH;
dma_single_data_mode_init(DMA1, DMA_CH7, &dsdps);
配置 dma 子连接
dma_channel_subperipheral_select(DMA1, DMA_CH7, DMA_SUBPERI4);
- 配置时钟
- 初始化dma通道
- 配置dma与外设间的子链接
2.3.3 DMA传输请求
// 数据来源 和 长度
dma_memory_address_config(DMA1, DMA_CH7, DMA_MEMORY_0, (uint32_t)(&data));
dma_transfer_number_config(DMA1, DMA_CH7, len);
// 触发传输
dma_channel_enable(DMA1, DMA_CH7);
// 等待DMA传输完成
while(RESET == dma_flag_get(DMA1, DMA_CH7, DMA_FLAG_FTF));
// 清理标记
dma_flag_clear(DMA1, DMA_CH7, DMA_FLAG_FTF);
- dma_channel_enable: 请求dma数据传输
- DMA_FLAG_FTF:为传输完成标记
2.3.4 串口外设DMA开启
// DMA发送功能配置
usart_dma_transmit_config(usartx, USART_TRANSMIT_DMA_ENABLE);
- 需要开启dma发送配置,才可以打开dam的功能
2.3.5 发送功能
static void dma_send_byte(uint8_t data) {
// 数据来源 和 长度
dma_memory_address_config(DMA1, DMA_CH7, DMA_MEMORY_0, (uint32_t)(&data));
dma_transfer_number_config(DMA1, DMA_CH7, 1);
// 触发传输
dma_channel_enable(DMA1, DMA_CH7);
// 等待DMA传输完成
while(RESET == dma_flag_get(DMA1, DMA_CH7, DMA_FLAG_FTF));
// 清理标记
dma_flag_clear(DMA1, DMA_CH7, DMA_FLAG_FTF);
}
static void dma_send(uint8_t* data, uint32_t len) {
// 数据来源 和 长度
dma_memory_address_config(DMA1, DMA_CH7, DMA_MEMORY_0, (uint32_t)(&data));
dma_transfer_number_config(DMA1, DMA_CH7, len);
// 触发传输
dma_channel_enable(DMA1, DMA_CH7);
// 等待DMA传输完成
while(RESET == dma_flag_get(DMA1, DMA_CH7, DMA_FLAG_FTF));
// 清理标记
dma_flag_clear(DMA1, DMA_CH7, DMA_FLAG_FTF);
}
static void dma_send_string(const char* str) {
dma_send((uint8_t*)str, strlen(str));
}
3. 串口发送理解
3.1 源地址和目标地址
源地址和目标地址都是提前配置好的,当传输时,就会从源地址将数据传递给目标地址。
3.2 自增长
每传输一个字节数据,地址是否需要增长。这里包含了源地址是否需要增长,也包含了目标地址是否需要增长。
串口中,寄存器地址不变,大小不变,我们只是向这个里面放数据,所以不需要增长。
3.3 数据宽度
数据宽度表示一次传递多上个bit。
3.4 数据长度
传输了多少个数据宽度的数据。