关于STM32H7的串口DMA的使用:
最近在使用H743做项目,想用RT-THREAD来做,但是目前(2022/5/10)之前,RT对于H7系列的支持,特别是DMA的使用可以说完全没有适配,所以现在先裸机开发各个外设的DMA,到时候再做移植
前言
h743用的人属实不多,相关教程少,特别是一些教程的例子,都难以直接上手,于是决定自己写一篇,关于如何快速开发,用使用h7系列的串口DMA
本篇教程的目的在于如何最快的上手进行开发,并不会深入底层进行分析,所提供的方法并非性能最好的
一、环境准备
keil5.30及以上版本、cubemx(其中h7系列的支持包为1.9.1),
二、cubemx配置
时钟配置等请自行配置,实在做不到就照抄正点原子时钟初始化部分
打开串口设备1,所有配置默认
打开串口1的dma通道,打开fifo,其他设置全部默认
注意记得打开DMA以及串口1中断
串口1中断cubemx默认不打开,一定要手动打开
到NVIC确认中断打开情况
点开code generation把这几个中断初始化都勾上,免得后面自己写
三、修改代码
HAL库开发虽然很快,但是如果不熟悉的话,有好多东西很难去找到。
有好多博主的教程,对于h7的dma与cache的处理,将缓存数组存在axi ram里,这毋庸置疑是高性能的写法,但是对于我们快速开发来讲,串口的速度根本用不着如此大动干戈,优化的再好波特率也只有那么多,实在不必将精力用在这里
更新一下:之前看了st官方的NUCLEO-H743ZI例程,其中的Projects\NUCLEO-H743ZI\Examples\UART\UART_TwoBoards_ComDMA下演示了两块32通过串口DMA通信,加入了MPU保护,再调用SCB_InvalidateDCache_by_Addr()函数,简单有效,下面代码重新更新一下
定义MPU
void MPU_Config(void)
{
MPU_Region_InitTypeDef MPU_InitStruct;
/* Disable the MPU */
HAL_MPU_Disable();
/* Configure the MPU as Strongly ordered for not defined regions */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x00;
MPU_InitStruct.Size = MPU_REGION_SIZE_4GB;
MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x87;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* Enable the MPU */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
实际上是对所有地址进行MPU保护
首先是定义接收和发送缓存
uint8_t uattbuff[20]={"hello\n"};
uint8_t uattbuffdma[20]={"hello from DMA\n"};
uint8_t uatrbuffdma[128]={0};
定义了三个数组,分别用于普通模式的串口传输,DMA传输,以及DMA接收
在开始处
int main(void)
{
/* USER CODE BEGIN 1 */
MPU_Config();
/* USER CODE END 1 */
/* Enable I-Cache---------------------------------------------------------*/
SCB_EnableICache();
/* Enable D-Cache---------------------------------------------------------*/
SCB_EnableDCache();
/* MCU Configuration--------------------------------------------------------*/
加入MPU_Config配置
然后在主循环处
/* Infinite loop */
/* USER CODE BEGIN WHILE */
HAL_UART_Receive_DMA(&huart1,uatrbuffdma,5);
while (1)
{
HAL_Delay(2000);
HAL_UART_Transmit(&huart1,uattbuff,strlen(uattbuff),0xff);
HAL_Delay(1);
HAL_UART_Transmit_DMA(&huart1,uattbuffdma,strlen(uattbuffdma));
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
循环发送
此时将程序烧录进32,已经可以看到输出了
接下来是接收中断函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance==USART1)
{
SCB_InvalidateDCache_by_Addr ((uint32_t *)uatrbuffdma, 128);
HAL_UART_Transmit(&huart1,uattbuffdma,5);
HAL_UART_Receive_DMA(&huart1,uatrbuffdma,5);
}
}
重点在于SCB_InvalidateDCache();函数,原因就是内存一致性,此处不展开叙述,如果有需要的可自行搜索。
此处用到SCB_InvalidateDCache_by_Addr()函数,稍微优化了一下
此时再次将代码下载到32里
可以看到串口DMA的收发操作都实现了
四、总结
可以看到HAL库+cubemx搭配的开发效率有多快,当然前提是对HAL的代码结构有基础的了解。