目录
一、DMA
1、DMA简介
2、工程建立
二、HAL库实验中断开关点灯
1、CubeMX工程建立
2、代码撰写
3、硬件连接
三、中断方式串口通信
1、建立工程
2、代码添加
四、总结
五、参考
【STM32】HAL库 STM32CubeMX教程十一---DMA (串口DMA发送接收)_Z小旋-CSDN博客
一、DMA
1、DMA简介
(1)
DMA,全称Direct Memory Access,即直接存储器访问。
DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。
(2)DMA定义:
DMA用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU的干预,通过DMA数据可以快速地移动。这就节省了CPU的资源来做其他操作。 (3)DMA传输方式
DMA的作用就是实现数据的直接传输,而去掉了传统数据传输需要CPU寄存器参与的环节,主要涉及四种情况的数据传输,但本质上是一样的,都是从内存的某一区域传输到内存的另一区域(外设的数据寄存器本质上就是内存的一个存储单元)。四种情况的数据传输如下:
外设到内存 内存到外设 内存到内存 外设到外设
(4)DMA传输参数
我们知道,数据传输,首先需要的是1 数据的源地址 2 数据传输位置的目标地址 ,3 传递数据多少的数据传输量 ,4 进行多少次传输的传输模式 DMA所需要的核心参数,便是这四个
当用户将参数设置好,主要涉及源地址、目标地址、传输数据量这三个,DMA控制器就会启动数据传输,当剩余传输数据量为0时 达到传输终点,结束DMA传输 ,当然,DMA 还有循环传输模式 当到达传输终点时会重新启动DMA传输。
也就是说只要剩余传输数据量不是0,而且DMA是启动状态,那么就会发生数据传输。
(5)DMA传输方式
方法1:DMA_Mode_Normal,正常模式,
当一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次
方法2:DMA_Mode_Circular ,循环传输模式
当传输结束时,硬件自动会将传输数据量寄存器进行重装,进行下一轮的数据传输。 也就是多次传输模式 (6 )仲裁器 仲裁器的作用是确定各个DMA传输的优先级
仲裁器根据通道请求的优先级来启动外设/存储器的访问。
优先权管理分2个阶段:
软件:每个通道的优先权可以在DMA_CCRx寄存器中设置,有4个等级:
最高优先级 高优先级 中等优先级 低优先级;
硬件:如果2个请求有相同的软件优先级,则较低编号的通道比较高编号的通道有较高的优先权。比如:如果软件优先级相同,通道2优先于通道4。
2、工程建立
(1)设置RCC。设置高速外部时钟HSE 选择外部时钟源
![](http://image109.360doc.com/DownloadImg/2022/02/1616/239536463_1_20220216042914571.png)
(2)设置串口
1点击USATR1 2设置MODE为异步通信(Asynchronous) 3基础参数:波特率为115200 Bits/s。传输数据长度为8 Bit。奇偶检验无,停止位1 接收和发送都使能 4GPIO引脚自动设置 USART1_RX/USART_TX 5 NVIC Settings 一栏使能接收中断
![](http://image109.360doc.com/DownloadImg/2022/02/1616/239536463_2_20220216042915194.png)
(3) DMA设置
- 点击DMASettings 点击 Add 添加通道
- 选择USART_RX USART_TX 传输速率设置为中速
- DMA传输模式为正常模式
- DMA内存地址自增,每次增加一个Byte(字节)
![](http://image109.360doc.com/DownloadImg/2022/02/1616/239536463_4_20220216042915788.png)
![](http://image109.360doc.com/DownloadImg/2022/02/1616/239536463_5_20220216042915944.png)
(4) DMA基础设置
右侧点击System Core 点击DMA
![](http://image109.360doc.com/DownloadImg/2022/02/1616/239536463_6_2022021604291638.png)
(5)时钟源设置
1.选择外部时钟HSE 8MHz 2.PLL锁相环倍频9倍 3.系统时钟来源选择为PLL 4.设置APB1分频器为 /2 5.使能CSS监视时钟
(6)项目文件设置
(7)创建工程文件并打开
点击GENERATE CODE 创建工程
(8)代码添加
在main.c文件添加代码
uint8_t Senbuff[] = 'Hello world!'; //定义数据发送数组
![](http://image109.360doc.com/DownloadImg/2022/02/1616/239536463_9_20220216042916694.png)
HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Senbuff, sizeof(Senbuff));
(9)编译成功即可烧录
C8T6核心板boot0接0,打开XCOM串口助手,打开串口即可接收信号
![](http://pubimage.360doc.com/wz/default.gif)
二、HAL库实验中断开关点灯
1、CubeMX工程建立
(1)外设设置
设置指示灯LED引脚PB5 ,引脚模式为输出模式GPIO_Output 设置按键引脚PA1 ,设置引脚为外部中断功能,PA1与外部中断线EXIT1连接GPIO_EXIT1
![](http://pubimage.360doc.com/wz/default.gif)
(2)LED对应的PB5 管脚,默认设置即可,名字设为LED
对应开关PA1管脚,设置触发方式为上升沿触发
External Interrupt Mode with Rising edge trigger detection上升沿 External Interrupt Mode with Falling edge trigger detection下降沿 External Interrupt Mode with Rising/Falling edge trigger detection上升沿和下降沿
User Label 处设置名字为 A1_EXTI
(3)使能对应的外部中断线,点击Enabled
![](http://pubimage.360doc.com/wz/default.gif)
(4) 配置中断优先级
(5)时钟设置 这里设置了36M
![](http://pubimage.360doc.com/wz/default.gif)
2、代码撰写
(1)
在Keil文件中的gpio.c文件可以找到中断服务函数
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
当捕获到上升沿,触发中断
然后就会执行HAL_GPIO_EXTI_Callback(GPIO_Pin) 函数,此函数为回调函数
__weak 表示此函数为虚函数,需要用户重写的
![](http://pubimage.360doc.com/wz/default.gif)
(2)
main.c文件下添加下列函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) if( GPIO_Pin == A1_EXTI_Pin)//判断外部中断源 HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);//翻转LED状态
![](http://pubimage.360doc.com/wz/default.gif)
(3) 编译运行将代码烧录到C8T6核心板中
3、硬件连接
(1)GPIO默认是3.3V高电平,当按下按键,GPIO变为低电平,此过程是下降沿。松开按键,GPIO又变为高电平,此过程为上升沿。因为设置的是上升沿中断,也就是松开按键的时候,灯会变化。
开关由杜邦线代替,拔出即为低电平,插入即为高电平
LED+ | 3V3 | LED- | PB5 | PA1 | 3V3(亮) | PA1 | GND(灭) |
(2)结果
![](http://pubimage.360doc.com/wz/default.gif)
三、中断方式串口通信
1、建立工程
(1)设置RCC,设置高速外部时钟HSE,选择外部时钟源
(2) 设置串口
点击USART1 设置MODE为异步通信 基础参数:波特率为115200 Bits/s。传输数据长度为8 Bit。奇偶检验无,停止位1,接收和发送都使能 GPIO引脚设置 USART1_RX/USART_TX(这里一般自动设置好了) NVIC Settings 一栏使能接收中断
![](http://pubimage.360doc.com/wz/default.gif)
![](http://pubimage.360doc.com/wz/default.gif)
(3) 时钟设置
(4) 创建工程文件
2、代码添加
(1)在main.c和usart.c中添加头文件#include 'stdio.h'
(2) 在usart.c文件中,添加如下代码
//加入以下代码,支持printf函数,而不需要选择use MicroLIB //#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) //#pragma import(__use_no_semihosting) //定义_sys_exit()以避免使用半主机模式 int fputc(int ch, FILE *f) HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0x0001);
(5)在main. c文件,依次在对应位置添加下列代码
/* USER CODE END WHILE */ printf('Hello windows!\r\n');
uint8_t aRxBuffer; //接收中断缓冲 uint8_t Uart1_RxBuff[256]; //接收缓冲 uint8_t Uart1_Rx_Cnt = 0; //接收缓冲计数 uint8_t cAlmStr[] = '数据溢出(大于256)\r\n';
HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);
* @brief Rx Transfer completed callbacks. * @param huart pointer to a UART_HandleTypeDef structure that contains * the configuration information for the specified UART module. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) /* Prevent unused argument(s) compilation warning */ /* NOTE: This function Should not be modified, when the callback is needed, the HAL_UART_TxCpltCallback could be implemented in the user file if(Uart1_Rx_Cnt >= 255) //溢出判断 memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff)); HAL_UART_Transmit(&huart1, (uint8_t *)&cAlmStr, sizeof(cAlmStr),0xFFFF); Uart1_RxBuff[Uart1_Rx_Cnt++] = aRxBuffer; //接收数据转存 if((Uart1_RxBuff[Uart1_Rx_Cnt-1] == 0x0A)&&(Uart1_RxBuff[Uart1_Rx_Cnt-2] == 0x0D)) //判断结束位 HAL_UART_Transmit(&huart1, (uint8_t *)&Uart1_RxBuff, Uart1_Rx_Cnt,0xFFFF); //将收到的信息发送出去 memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff)); //清空数组 HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1); //再开启接收中断
(6)编译运行,进行烧录
(7)串口通信结果
进行通信,boot0接0
串口每隔0.5s输出Hello windows
![](http://pubimage.360doc.com/wz/default.gif)
四、总结
中断方式不必等待数据的传输过程,只需要在每字节数据收发完成后,由中断标志位触发中断,在中断服务程序中放入新的一字节数据或者读取接收到的一字节数据。
DMA用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU的干预,通过DMA数据可以快速地移动。这就节省了CPU的资源来做其他操作。
五、参考
STM32 之 HAL库_戈扬的博客-CSDN博客_hal库
【STM32】HAL库 STM32CubeMX教程十一---DMA (串口DMA发送接收)_Z小旋-CSDN博客
|