用了一段时间HAL库,不知道是不是用外设固件库时间长一点的原因,个人感觉没有固件库来的直接。看DMA部分看的有点乱,捋一下。
DMA的使用方法及细节直接看官方的手册就可以了,注意的是如果原地址和目的地址的数据长度不一样的话只能使用FIFO模式,直接模式下原地址和目的地址的数据长度必须一致。
主要是简单整理一下使用DMA的初始化过程,以串口发送使用DMA为例:
(1) 定义一个外设的句柄,对外设的基本参数、IO、时钟、中断等进行初始化。
UART_HandleTypeDef UART1_Handler;
....
HAL_UART_Init(&UART1_Handler);
调用外设的初始化函数完成对外设的初始化
(2) 定义一个 DMA的句柄,对DMA的数据流、通道、传输方向、数据长度等基本参数进行初始化
DMA_HandleTypeDef UART1TxDMA_Handler;
....
HAL_DMA_Init(&UART1TxDMA_Handler);
调用初始化函数完成对句柄的填充。
(3) 使用HAL库中对DMA的一个宏定义完成外设发送DMAT或者接收DMAR和第二步的DMA句柄进行关联
__HAL_LINKDMA(&UART1_Handler,hdmatx,UART1TxDMA_Handler);
每个外设的句柄结构体中都一个该外设关于DMA相关的设置:
DMA_HandleTypeDef *hdmatx; UART TX 的DMA句柄参数
DMA_HandleTypeDef *hdmarx; UART RX 的DMA句柄参数
使用__HAL_LINKDMA 宏定义的作用就是将第二步初始化的DMA句柄赋值给外设的发送或者接收DMA句
柄,并且外设的句柄(第一步定义的)赋给DMA结构体中的.parent 参数,调用DMA的函数都会间接调用
UART1TxDMA_Handler.parent->hdmatx
对DMA的个别寄存器的参数进行填充,比如调用 HAL_DMA_Start()函数就会调用DMA_SetConfig()函数配置数据个数、源地址、目的地址等。
而hdmatx 和第二步的DMA句柄是一模一样的,感觉最后转了一圈又转回会来了。。。
(4) 外设初始化、DMA初始化、外设和DMA进行关联、外设使能DMA发送或者接收、DMA调用函数开始发送或者接收。
个人感觉有时候HAL库的确有些冗余了,这时候可能就需要部分进行寄存器操作了。
二、使用过程总结
1、传输数据数目设置
注意传输数据的个数的设置:如果源的数据宽度和目标数据宽度不一样,即PSIZE、MSIZE不相等时,DMA_SXNDTR寄存器配置要传输的数据项数目的宽度等于外设总线的宽度(和传输方向无关)。例如外设的宽度设置为字,存储器的宽度设置为半字,假如要传输100个字节的数据,NDTR寄存器配置要传输的数据数目 100/4.外设宽度是字=4个字节。
2、DMA初始化
HAL_DMA_Init() ; 初始化函数中会使能所有的中断,如果不想用中断,可以在初始化之后关闭。初始化完成后会自动使能流的DMA。
三、调试过程问题解决
1、直接模式中出现FIFO溢出错误,可能是DMA的源地址或者目的地址设置有问题。