完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
扫一扫,分享给好友
一、DMA简介
DMA(Direct Memory Access) 直接存储器存取,是单片机的一个外设,它的主要功能是用来搬数据,但是不需要占用 CPU,即在传输数据的时候,CPU 可以干其他的事情,好像是多线程一样。数据传输支持从外设到存储器或者存储器到存储器,这里的存储器可以是 SRAM 或者是 FLASH。DMA 控制器包含了 DMA1 和 DMA2,其中 DMA1 有 7 个通道,DMA2 有 5 个通道,这里的通道可以理解为传输数据的一种管道。 要注意的是 DMA2 只存在于大容量的单片机中。 二、DMA请求映像 DMA1 各个通道的请求映像 DMA2 各个通道的请求映像 其中 ADC3、SDIO 和 TIM8 的 DMA 请求只在大容量产品中存在,这个在具体项目时 要注意。 三、新建工程 1. 打开 STM32CubeMX 软件,点击“新建工程” 2. 选择 MCU 和封装 3. 配置时钟 RCC 设置,选择 HSE(外部高速时钟) 为 Crystal/Ceramic Resonator(晶振/陶瓷谐振器) 选择 Clock Configuration,配置系统时钟 SYSCLK 为 72MHz 修改 HCLK 的值为 72 后,输入回车,软件会自动修改所有配置 4. 配置调试模式 非常重要的一步,否则会造成第一次烧录程序后续无法识别调试器 SYS 设置,选择 Debug 为 Serial Wire 四、DMA1 4.1 配置串口 在 Connectivity 中选择 USART1 设置,并选择 Asynchronous 异步通信 波特率为 115200 Bits/s。传输数据长度为 8 Bit。奇偶检验 None,停止位 1 ,接收和发送都使能。 使能串口接收中断 4.2 配置DMA 点击 DMA Settings 添加 USART1 TX 和 USART1 RX 分别对应DMA1 的通道4和通道5。
4.3 生成代码 输入项目名和项目路径 选择应用的 IDE 开发环境 MDK-ARM V5 每个外设生成独立的 ’.c/.h’ 文件 不勾:所有初始化代码都生成在 main.c 勾选:初始化代码生成在对应的外设文件。 如 GPIO 初始化代码生成在 gpio.c 中。 点击 GENERATE CODE 生成代码 4.4 USART+DMA数据发送 新建一个变量 uint8_t sendBuff[] = "USART test by DMArn"; 1 在 man.c 中的主循环添加以下代码: /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ HAL_UART_Transmit_DMA(&huart1, (uint8_t *)sendBuff, sizeof(sendBuff)); HAL_Delay(1000); /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } 通过串口助手可以看到在接收区有数据不断的打印输出 注意:如果不开启串口中断,则程序只能发送一次数据,程序不能判断DMA传输是否完成,USART一直处于busy状态。 4.5 USART+DMA数据接收 在 main.c 头部添加全局变量 Buffer /* Private variables ---------------------------------------------------------*/ UART_HandleTypeDef huart1; /* USER CODE BEGIN PV */ uint8_t Buffer[1]; /* USER CODE END PV */ 在 stm32f1xx_it.c 头部声明全局变量 Buffer /* External variables --------------------------------------------------------*/ extern UART_HandleTypeDef huart1; /* USER CODE BEGIN EV */ extern uint8_t Buffer[1]; /* USER CODE END EV */ 在 main.c 中,while 循环前,串口初始化后,添加接收中断开启函数,这样在第一次接收到数据的时候才会触发中断。 /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_DMA_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ HAL_UART_Receive_DMA(&huart1, (uint8_t *)Buffer, 1); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } 在 stm32f1xx_it.c 这个文件的最下面添加 HAL_UART_RxCpltCallback() /* USER CODE BEGIN 1 */ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == USART1) { HAL_UART_Receive_DMA(&huart1, (uint8_t *)Buffer, 1); HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Buffer, 1); } } /* USER CODE END 1 */ 通过串口助手发送 OK,可以看到接收到 O,这是因为设置的接收数据是一个字符,如果要接收更多字符,请加大 Buffer。 4.6 串口IDLE空闲中断+DMA数据接收 特点: 可以实现任意字符串接收并输出。 在串口无数据接收的情况下,不会产生,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一但接收的数据断流,没有接收到数据,即产生IDLE中断。 在 main.c 中添加以下变量: uint8_t recvBuff[BUFFER_SIZE]; //接收数据缓存数组 volatile uint8_t recvLength = 0; //接收一帧数据的长度 volatile uint8_t recvDndFlag = 0; //一帧数据接收完成标志 在 main.h 中添加以下宏定义与变量: #define BUFFER_SIZE 256 extern uint8_t recvBuff[BUFFER_SIZE]; //接收数据缓存 extern volatile uint8_t recvLength; //接收一帧数据的长度 extern volatile uint8_t recvDndFlag; //一帧数据接收完成标志 在 main.c 中,while 循环前,串口初始化后,添加空闲中断和DMA接收开启函数,这样在第一次接收到数据的时候才会触发中断。 /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_DMA_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断 HAL_UART_Receive_DMA(&huart1, recvBuff, BUFFER_SIZE); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } 在 stm32f1xx_it.c 这个文件的最下面修改 USART1_IRQHandler() void USART1_IRQHandler(void) { /* USER CODE BEGIN USART1_IRQn 0 */ uint32_t tmpFlag = 0; uint32_t temp; tmpFlag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //获取IDLE标志位 if((tmpFlag != RESET))//idle标志被置位 { __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位 HAL_UART_DMAStop(&huart1); // temp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数 recvLength = BUFFER_SIZE - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数 recvDndFlag = 1; // 接受完成标志位置1 HAL_UART_Transmit_DMA(&huart1, recvBuff, recvLength); recvLength = 0;//清除计数 recvDndFlag = 0;//清除接收结束标志位 memset(recvBuff,0,recvLength); HAL_UART_Receive_DMA(&huart1, recvBuff, BUFFER_SIZE);//重新打开DMA接收,不然只能接收一次数据 } /* USER CODE END USART1_IRQn 0 */ HAL_UART_IRQHandler(&huart1); /* USER CODE BEGIN USART1_IRQn 1 */ /* USER CODE END USART1_IRQn 1 */ } 通过串口助手发送不定长数据 五、注意事项 用户代码要加在 USER CODE BEGIN N 和 USER CODE END N 之间,否则下次使用 STM32CubeMX 重新生成代码后,会被删除。 |
|
|
|
只有小组成员才能发言,加入小组>>
3210 浏览 9 评论
2896 浏览 16 评论
3404 浏览 1 评论
8847 浏览 16 评论
3998 浏览 18 评论
9629浏览 3评论
1008浏览 3评论
522浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
524浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2251浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-10-3 10:26 , Processed in 0.939900 second(s), Total 80, Slave 60 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号