目录
1. 目标
使用HAL库控制DMA
2. 背景知识
在一般的代码中,搬运数据是通过CPU来完成的,为了减轻CPU负担让CPU完成更重要的任务,就可以用到DMA直接存储器访问控制器
F103只有DMA1(7通道),L4R5有DMA1(7通道)和DMA2(7通道),具体见手册
来源于外设和内存的数据通过DMA通道互相传输,数据的传输也具有优先级,每个DMA都有一个仲裁器来对此仲裁
DMA会因三个事件产生中断:传输完成,半传输,传输错误
翻阅手册,DMA框图长这样,DMAMUX为DMA请求复用器,可以不关心这个
3. 过程
比较简单,并没有深入DMA内核,还需进一步学习
3.1 内存 <=> 内存
在CubeMX中设置从内存到内存,使用了DMA1通道1,自增指针,数据宽度为字节
3.1.1 代码
将数据从from传送到to,一共四字节数据,上位机收到的数据是abce,假如只有源地址自增应该是e,只有目标地址自增那就是aaaa
虽然这里是通过DMA发送不会阻塞,但在使用之前仍应该通过检查DMA传输是否完成,形参可以是FULL传输完成或者HALF传输一半,然后通过返回值来判断状态
uint8_t from[4] = {'a', 'b', 'c', 'e'};
uint8_t to[5] = {};
HAL_DMA_Start(&hdma_memtomem_dma1_channel1, (uint32_t) from, (uint32_t) to, 4);
while (HAL_DMA_PollForTransfer(&hdma_memtomem_dma1_channel1, HAL_DMA_FULL_TRANSFER, 0) != HAL_OK);
printf("%s\r\n", to);
除了阻塞查询以外,还有常用的中断,这里的中断比较麻烦,需要自己对中断函数进行注册,注册函数第三个形参是状态,可选类型较多(完成,半传输,错误,中止,所有)
以下代码可以完成和上面阻塞式相同的任务
HAL_DMA_Start_IT(&hdma_memtomem_dma1_channel1, (uint32_t) from, (uint32_t) to, 4);
void dmaTransComplete (DMA_HandleTypeDef *_hdma) {
if (_hdma == &hdma_memtomem_dma1_channel1) {
printf("%s IT\r\n", to);
}
}
void dmaRegister () {
HAL_DMA_RegisterCallback(&hdma_memtomem_dma1_channel1, HAL_DMA_XFER_CPLT_CB_ID, dmaTransComplete);
}
3.1.2 问题
这一坨运行完成以后,可以发现该通道半传输标志位HTIF仍为1,但数据仍可正常传输,所以可以不管?
3.2 内存 <=> 外设
使用UART5作为外设,在CubeMX中配置初始参数,既可以在DMA那里配置也可以在UART那里配置
UART5接收,DMA循环,数据宽度字节(后面代码用的单次)
UART5发送,DMA单次,数据宽度字节
Normal单次,Circular循环
3.2.1 代码
对于发送,一行就行了
HAL_UART_Transmit_DMA(&huart5, from, 4);
HAL_Delay(1);
printf(" memToPer\r\n");
发送也差不多,和中断接收一样需编写接收函数
HAL_UART_Receive_DMA(&huart5, from, 4);
void HAL_UART_RxCpltCallback ( UART_HandleTypeDef * huart ) {
}
3.2.2 问题
在DMA发送中产生了一些问题(printf重定向的也是uart5),当去掉HAL_Delay后,printf内容并没有发出,后面的代码正常执行。推测,应该是DMA未发送完成导致碰撞
3.3 外设 <=> 外设
保留字段
附录