**STM32中断与DMA通信编程**
一、学习任务
-
用stm32F103核心板的GPIOA端一管脚接一个LED,GPIOB端口一引脚接一个开关(用杜邦线模拟代替)。采用中断模式编程,当开关接高电平时,LED亮灯;接低电平时,LED灭灯。
-
采用串口中断方式重做上周的串口通信作业。
-
STM32采用串口DMA方式,用115200bps或更高速率向上位机连续发送数据。
二、采用中断模式编程控制LED
2.1中断概述
1.数据传输方式
2.中断概念
中断中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。
3.中断的作用
中断使计算机系统具备应对突发事件的能力,使CPU在运行过程中对外部事件发出的中断请求及时地进行处理,处理完成后又立即返回断点,继续进行CPU原来的工作。
4.中断优先级
处理器根据不同中断的重要程序设置不同的优先等级。不同优先级中断的处理原则是:高级中断可以打断低级中断;低级中断不能打断高级中断。在某一时刻有几个中断源同时发出中断请求时,处理器只响应其中优先权最高的中断源。当处理机正在运行某个中断服务程序期间出现另一个中断源的请求时,如果后者的优先权低于前者,处理机不予理睬,反之,处理机立即响应后者,进入所谓的“嵌套中断”。
5.中断向量
中断标识码(中断类型号):由硬件(通常是中断控制器)产生,以标识不同的中断源。
中断向量:中断服务程序的入口地址。在某些计算机中,中断向量的位置存放一条跳转到中断服务程序入口地址的跳转指令。
中断向量地址:存储中断向量的存储单元地址。
6.HAL库的外部中断处理流程
2.2STM32微控制器中断系统
1.中断通道
中断:由内核外部产生的,一般由硬件引起,比如外设中断和外部中断等。
异常:通常是内核自身产生的,大多是软件引起的,比如除法出错异常、预取值失败等。
2.
2.1中断优先级
2.2优先级分组
3.GPIO引脚的外部中断
触发方式:上升沿触发、下降沿触发、双边沿触发
2.3工程创建
基础设置及操作参考:https://blog.csdn.net/qq_46475595/article/details/120983297
1.选择芯片STM32F 103C8
配置系统调试接口 SYS,选择 Serial Wire
配置外设 RCC ,选择 HSE (外部高速时钟)为 Crystal/Ceramic Resonator(晶振/陶瓷谐振器)
2.设置LED引脚为PA1的输出模式 GPIO_Output,按键引脚为PB15的外部中断模式GPIO_EXTI1
PA1命名为LED1,PB1命名为EXTI1
开关对应PB1管脚,GPIO mode处设置为上升沿触发
使能外部中断线,点击Enabled
配置中断优先级,因为此处只有一个中断,因此它的优先级为0,为最高,如果有多个中断则可设为0,1,2,3。
设置时钟
设置项目名称和路径,修改Toolchain/IDE为MDK-ARM
自动生成程序。
2.4编写程序
1.添加代码
打开上述工程,点击main.c,添加代码。
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if( GPIO_Pin == EXTI1_Pin )
{
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
}
}
编译
2.5烧录
选择生成的hex文件并进行烧录(这里我烧录成功)
这里有明显接触不良,为接地原因
改良如下:
三、串口中断实现串口通信
3.1串口中断
特点:
① 发送数据时,将一字节数据放入数据寄存器DR;接收数据时,将DR的内容存放到用户存储区;
② 中断方式不必等待数据的传输过程,只需要在每字节数据收发完成后,由中断标志位触发中断,在中断服务程序中放入新的一字节数据或者读取接收到的一字节数据;
③ 在传输数据量较大,且通信波特率较高(大于38400)时,如果采用中断方式,每收发一个字节的数据,CPU都会被打断,造成CPU无法处理其他事务。因此在批量数据传输,通信波特率较高时,建议采用DMA方式。
函数介绍:
串口中断方式发送函数
串口中断方式接收函数
串口中断通用处理函数
串口发送中断回调函数
串口接受中断回调函数
串口中断使能函数
串口中断标志查询函数
空闲中断标志清除函数
3.2创建工程
1.选择芯片
设置时钟RCC,点击HSE,选择Crystal/Ceramic Resonator
设置USART1,点击Mode,选择Asynchronous
设置中断,在NVIC Settings中点击Enabled
点击Clock Configuration选择HSE和PLLCLK,修改HCLK值
设置项目名称和路径,修改Application Structure为Basic,Toolchain/IDE为MDK-ARM,生成项目
创建成功并打开工程
3.3代码编写
在stm32f1xx_hal.c文件添加头文件
#include <stdio.h>
extern UART_HandleTypeDef huart1; //声明串口
重写fget和fput函数
/**
* 函数功能: 重定向c库函数printf到DEBUG_USARTx
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
/**
* 函数功能: 重定向c库函数getchar,scanf到DEBUG_USARTx
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
return ch;
}
在主函数main.c文件的while循环中添加发送数据
printf("hello windows!\r\n");
HAL_Delay(1000);
添加定义,接收串口数据
#include "stdio.h"
#include <string.h>
#define RXBUFFERSIZE 256 //最大接收字节数
char RxBuffer[RXBUFFERSIZE]; //接收数据
uint8_t aRxBuffer; //接收中断缓冲
uint8_t Uart1_Rx_Cnt = 0; //接收缓冲计数
在主函数main.c文件中添加开启接收中断的语句
HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);
添加中断回调函数
/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(huart);
/* 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) //溢出判断
{
Uart1_Rx_Cnt = 0;
memset(RxBuffer,0x00,sizeof(RxBuffer));
HAL_UART_Transmit(&huart1, (uint8_t *)"数据溢出", 10,0xFFFF);
}
else
{
RxBuffer[Uart1_Rx_Cnt++] = aRxBuffer; //接收数据转存
if((RxBuffer[Uart1_Rx_Cnt-1] == 0x0A)&&(RxBuffer[Uart1_Rx_Cnt-2] == 0x0D)) //判断结束位
{
HAL_UART_Transmit(&huart1, (uint8_t *)&RxBuffer, Uart1_Rx_Cnt,0xFFFF); //将收到的信息发送出去
while(HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX);//检测UART发送结束
Uart1_Rx_Cnt = 0;
memset(RxBuffer,0x00,sizeof(RxBuffer)); //清空数组
}
}
HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1); //再开启接收中断
}
/* USER CODE END 4 */
编译结果
3.4仿真
烧录成功!!!
野火调试:
四、串口DMA接收发数据
4.1DMA
DMA定义:
DMA用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU的干预,通过DMA数据可以快速地移动。这就节省了CPU的资源来做其他操作。
DMA传输方式:
DMA的作用就是实现数据的直接传输,而去掉了传统数据传输需要CPU寄存器参与的环节,主要涉及四种情况的数据传输,但本质上是一样的,都是从内存的某一区域传输到内存的另一区域(外设的数据寄存器本质上就是内存的一个存储单元)。
四种情况的数据传输:
① 外设到内存
② 内存到外设
③ 内存到内存
④ 外设到外设
DMA数据传输方式
串口DMA方式发送函数:
串口DMA方式接收函数:
接口函数
4.2创建工程
点击 File->New Pioject或ACCEE TO MCU SELECTOR,创建新工程
选择 STM32F103C8 芯片,点击 Start Project 进入工程
设置时钟RCC,点击HSE,选择Crystal/Ceramic Resonator
设置USART1,点击Mode,选择Asynchronous,在NVIC Settings中点击Enabled(中断)
如上述步骤创建工程。
在USART1中,点击DMA Settings的Add,添加USART_RX 和USART_TX,传输速率设置为中速Medium,模式设置为Normal,右侧选择Memory;在最右侧的System view中选择DMA,点击Add,添加MEMTOMEM。
设置时钟源,点击Clock Configuration选择HSE和PLLCLK。
设置项目文件,修改Application Structure为Basic,Toolchain/IDE为MDK-ARM。
4.3编写代码
1.添加代码
main.c文件的主函数main
uint8_t Senbuff[] = "Goodbye\r\n"; //定义数据发送数组
主函数main的while循环
HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Senbuff, sizeof(Senbuff));
HAL_Delay(1000);
编译结果
4.4仿真
烧录
BOOT0置1,BOOT1置0,打开mcuisp,选择文件路径,开始烧录
野火调试:
五、总结
通过此次实验,我掌握了中断的基本概念和作用,了解了HAL库的中断处理流程,学会了DMA方式的串口通信,DMA可以直接访问存储器,而直接绕过CPU,为CPU减负。
六、参考资料
第九章__串口通信(new).pdf
第六章__通用输入输出接口(二)(new).pdf
第七章__中断系统(new).pdf
https://blog.csdn.net/as480133937/article/details/104827639/
https://www.cnblogs.com/breezy-ye/articles/12157442.html