一、实验要求
学习stm32中断、DMA通信原理和编程方法。使用stm32tubemx和HAL库分别完成以下编程练习:
1. 用stm32F103核心板的GPIOA端一管脚接一个LED,GPIOB端口一引脚接一个开关(用杜邦线模拟代替)。采用中断模式编程,当开关接高电平时,LED亮灯;接低电平时,LED灭灯。
2. 采用串口中断方式重做上周的串口通信作业。
3. STM32采用串口DMA方式,用115200bps或更高速率向上位机连续发送数据。
二、实验过程及结果
(一)任务1
1.中断概述
1)中断概念
中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。
根据中断源的不同,可以把中断分为硬件中断和软件中断两大类。
① 硬件中断分为外部中断和内部中断两类。
- 外部中断一般是指由计算机外设发出的中断请求,如:键盘中断、打印机中断、定时器中断等。外部中断是可屏蔽的中断,即利用中断控制器可以屏蔽外部设备的中断请求。
- 内部中断是指因硬件出错(如突然掉电、奇偶校验错等)或运算出错(除数为零、运算溢出、单步中断等)所引起的中断。内部中断是不可屏蔽的中断。
② 软件中断其实并不是真正的中断,它们只是可被调用执行的一般程序。
例如:ROM BIOS中的各种外部设备管理中断服务程序(键盘管理中断、显示器管理中断、打印机管理中断等,)以及DOS的系统功能调用(INT 21H)等都是软件中断。
2)中断过程
按照事件发生的顺序,中断过程包括:
① 中断源发出中断请求;
② 判断当前处理机是否允许中断和该中断源是否被屏蔽;
③ 优先权排队;
④ 处理机执行完当前指令或当前指令无法执行完,则立即停止当前程序,保护断点地址和处理机当前状态,转入相应的中断服务程序;
⑤ 执行中断服务程序;
⑥ 恢复被保护的状态,执行“中断返回”指令回到被中断的程序或转入其他程序。
上述过程中前四项操作是由硬件完成的,后两项是由软件完成的。
3)中断作用
中断使计算机系统具备应对突发事件的能力,使CPU在运行过程中对外部事件发出的中断请求及时地进行处理,处理完成后又立即返回断点,继续进行CPU原来的工作。
4)中断优先级
在某一时刻有几个中断源同时发出中断请求时,处理器只响应其中优先权最高的中断源。当处理机正在运行某个中断服务程序期间出现另一个中断源的请求时,如果后者的优先权低于前者,处理机不予理睬,反之,处理机立即响应后者,进入所谓的“嵌套中断”。
中断优先权的排序按其性质、重要性以及处理的方便性决定,由硬件的优先权仲裁逻辑或软件的顺序询问程序来实现。
5)中断向量
中断处理/服务程序:响应一个特定中断时处理器执行的函数。
中断向量:中断服务程序在内存中的入口地址。
中断向量表:把系统中所有的中断向量集中起来放到存储器的某一区。
2.STM32微控制器中断系统
1)中断通道
中断:由内核外部产生的,一般由硬件引起,比如外设中断和外部中断等。
异常:通常是内核自身产生的,大多是软件引起的,比如除法出错异常、预取值失败等。
微控制器片内集成了很多外设,对于单个外设而言,它通常具备若干个可以引起中断的中断源,而该外设的所有中断源只能通过指定的中断通道向内核申请中断。
2)中断优先级
中断优先级:
优先级分组:
3)外部中断控制器
① 管理23个外部中断线(EXTI Line);
② 0~15号外部中断线用于由GPIO引脚触发的外部中断;
③ 16~22号外部中断线用于RTC闹钟事件、以太网唤醒事件和USB唤醒事件等;
④ 当对应GPIO引脚与外部中断线连接后,GPIO引脚才具备外部中断的功能,可以设置外部中断的触发方式。
4)GPIO引脚的外部中断
触发方式:上升沿触发、下降沿触发、双边沿触发
3.实验思路
1)上拉式按键
① 按键按下,引脚 PB15 读到低电平
② 按键释放,引脚 PB15 读到高电平
2)触发方式
① 按键按下瞬间,形成下降沿
② 按键释放瞬间,形成上升沿
4.工程创建
具体过程参考串口通信小试牛刀
基础配置:
点击 File->New Pioject或ACCEE TO MCU SELECTOR,创建新工程
选择 STM32F103C8 芯片,点击 Start Project 进入工程
配置系统调试接口 SYS,选择 Serial Wire
配置外设 RCC ,选择 HSE (外部高速时钟)为 Crystal/Ceramic Resonator(晶振/陶瓷谐振器)
① 设置LED灯引脚为PA1
的输出模式 GPIO_Output
,按键引脚为PB15
的外部中断模式GPIO_EXIT1
② LED对应PA1
管脚,User Label
处设置为LED
③ 开关对应PB15
管脚,GPIO mode
处设置为上升沿触发
,User Label
处设置为 B_EXTI
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 上升/下降沿
④ 使能外部中断线,点击Enabled
⑤ 配置中断优先级
(大多数情况下不必设置中断优先级,直接使用中断编号设置的默认中断优先级)
⑥ 设置时钟树,选择PLLCLK
,修改HCLK
为36
MHz
⑦ 设置项目名称和路径,修改Toolchain/IDE
为MDK-ARM
5.程序编写
1)添加代码
进入设定的工程目标目录,打开MDK-ARM文件夹,通过keil打开项目,点击main.c,添加代码。
/*
* @brief EXTI line detection callbacks.
* @param GPIO_Pin: Specifies the pins connected EXTI line,
* @retval None
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if( GPIO_Pin == B_EXTI_Pin ) // 判断外部中断源
{
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); // 翻转LED状态
}
}
注意:本任务只设置一个外部中断引脚,因此不需要判断多个引脚。如果系统中设置了多个外部中断,建议使用 switch-case 进行多分支判断。
2)中断函数
① 外部中断通用处理函数
② 外部中断回调函数
3)分析代码
6.硬件操作
1)连接
使用USB-TTL直接进行串口下载,将USB-TTL的GND和3.3V接入STM32系统板的GND和3.3V,然后TXD
和RXD
分别接入A10
和A9
引脚。
接入后按照程序中GPIOx的引脚接上LED灯,且最小核心板要利用跳线帽实现boot0置1
,boot1置0
。
2)烧录
烧录后断电,将boot0
置0
,再接电。
7.实际效果
杜邦线与面包板有些接触不良
开关LED灯
当使用按键开关时,由于机械按键的弹性作用,按下按键并不会马上稳定接通或断开,即存在按键抖动。一般情况下我们需要通过软件或硬件的方式消抖才能得到理想的实验效果。软件消抖如通过定时器延时等方式,忽略前后的抖动;硬件消抖,如利用电容的充放电延时等方式。
(二)任务2
1.串口中断
1)特点
① 发送数据时,将一字节数据放入数据寄存器DR;接收数据时,将DR的内容存放到用户存储区;
② 中断方式不必等待数据的传输过程,只需要在每字节数据收发完成后,由中断标志位触发中断,在中断服务程序中放入新的一字节数据或者读取接收到的一字节数据;
③ 在传输数据量较大,且通信波特率较高(大于38400)时,如果采用中断方式,每收发一个字节的数据,CPU都会被打断,造成CPU无法处理其他事务。因此在批量数据传输,通信波特率较高时,建议采用DMA方式。
2)函数
① 发送函数
② 接收函数
③ 接口函数
串口中断通用处理函数:
串口发送中断回调函数:
串口接收中断回调函数:
串口中断使能函数:
串口中断标志查询函数:
空闲中断标志清除函数:
2.工程创建
具体参考串口通信小试牛刀
基本配置:
点击 File->New Pioject或ACCEE TO MCU SELECTOR,创建新工程
选择 STM32F103C8 芯片,点击 Start Project 进入工程
设置时钟RCC,点击HSE,选择Crystal/Ceramic Resonator
设置USART1,点击Mode,选择Asynchronous
① 设置中断,在NVIC Settings
中点击Enabled
。
② 点击Clock Configuration
选择HSE
和PLLCLK
,修改HCLK
值。
③ 设置项目名称和路径,修改Application Structure
为Basic
,Toolchain/IDE
为MDK-ARM
,生成项目。
3.程序编写
1)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;
}
2)main.c文件
① 在主函数main的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中添加开启接收中断的语句
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)波形观察
打开Serial Windows
中的UART #1
窗口和逻辑分析仪,添加要观察的端口,修改各个显示方式,将Display Type
处设置为Bit
。
观察波形:每隔一段时间(大约1s)电平位发生变化,程序周期性输出,UART #1
窗口循环显示hello windows!
。
4.硬件操作
1)连接
使用USB-TTL直接进行串口下载,将USB-TTL的GND和3.3V接入STM32系统板的GND和3.3V,然后TXD
和RXD
分别接入A10
和A9
引脚。
2)烧录
BOOT0置1
,BOOT1置0
,打开mcuisp,选择文件路径,开始烧录。
5.实际效果
BOOT0置0
,打开野火多功能调试助手,配置串口并打开。(数据换行后才能发送)
(三)任务3
1.DMA概述
1)基本概念
直接存储器访问(DMA):用于在外设与存储器之间以及存储器与存储器之间进行高速数据传输。DMA传输过程的初始化和启动由CPU完成,传输过程由DMA控制器来执行,无需CPU参与,从而节省CPU资源,提高利用率。
2)传输方式
DMA的作用就是实现数据的直接传输,而去掉了传统数据传输需要CPU寄存器参与的环节,主要涉及四种情况的数据传输,但本质上是一样的,都是从内存的某一区域传输到内存的另一区域(外设的数据寄存器本质上就是内存的一个存储单元)。
四种情况的数据传输:
① 外设到内存
② 内存到外设
③ 内存到内存
④ 外设到外设
3)DMA工作系统框图
在发生一个事件后,外设向DMA控制器发送一个请求信号。DMA控制器根据通道的优先权处理请求。当DMA控制器开始访问发出请求的外设时,DMA控制器立即发送给它一个应答信号。当从DMA控制器得到应答信号时,外设立即释放它的请求。一旦外设释放了这个请求,DMA控制器同时撤销应答信号。DMA传输结束,如果有更多的请求时,外设可以启动下一个周期。
每次DMA传送由3个操作组成:
① 从外设数据寄存器或者从当前外设/存储器地址寄存器指示的存储器地址取数据,第一次传输时的开始地址是DMA_CPARx或DMA_CMARx寄存器指定的外设基地址或存储器单元;
② 存数据到外设数据寄存器或者当前外设/存储器地址寄存器指示的存储器地址,第一次传输时的开始地址是DMA_CPARx或DMA_CMARx寄存器指定的外设基地址或存储器单元;
③ 执行一次DMA_CNDTRx寄存器的递减操作,该寄存器包含未完成的操作数目。
4)STM32的DMA控制器
① STM32F411微控制器具备两个DMA控制器:DMA1和DMA2,每个控制器有8个数据流,每个数据流可以映射到8个通道(或请求);
② 每一个DMA控制器用于管理一个或多个外设的存储器访问请求,并通过总线仲裁器来协调各个DMA请求的优先级;
③ 数据流(stream)是用于连接传输源和传输目标的数据通路,每个数据流可以配置为不同的传输源和传输目标,这些传输源和传输目标称为通道(Channel) ;
④ 具备16字节的FIFO。使能FIFO功能后,源数据先送入FIFO,达到FIFO的触发阈值后,再传送到目标地址。
5)接口函数
① 发送函数
② 接收函数
③ 获取未传输数据个数函数
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
。
1DMA基础设置
DMA传输的对应外设:DMA Request
注意: 如果在DMA设置界面添加DMA 而没有开启对应外设 ,默认为MENTOMEN
DMA传输通道设置:Channel
DMA1:DMA1 Channel 0~DMA1 Channel 7
DMA2:DMA2 Channel 1~DMA1 Channel 5
DMA传输方向:Dirction
外设到内存 Peripheral To Memory
内存到外设 Memory To Peripheral
内存到内存 Memory To Memory
外设到外设 Peripheral To Peripheral
DMA传输速度:Priority
最高优先级 Very High
高优先级 High
中等优先级 Medium
低优先级;Low
2DMA传输模式
正常模式:Normal
当一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次
循环模式:Circular
传输完成后又重新开始继续传输,不断循环永不停止
3DMA指针递增设置
地址指针递增:Increment Address
外设地址寄存器:Src Memory
功能:设置传输数据时外设地址是否递增。如果设置为递增,下一次传输时地址加 Data Width个字节。
内存地址寄存器:Dst Memory
功能:设置传输数据时内存地址是否递增。如果设置为递增,下一次传输时地址加 Data Width个字节。
② 设置时钟源,点击Clock Configuration
选择HSE
和PLLCLK
。
③ 设置项目文件,修改Application Structure
为Basic
,Toolchain/IDE
为MDK-ARM
。
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);
2)波形观察
打开Serial Windows
中的UART #1
窗口和逻辑分析仪,添加要观察的端口,修改各个显示方式,将Display Type
处设置为Bit
。
观察波形:每隔一段时间(大约1s)电平位发生变化,程序周期性输出,UART #1
窗口循环显示Goodbye
。
4.硬件操作
1)连接
使用USB-TTL直接进行串口下载,将USB-TTL的GND和3.3V接入STM32系统板的GND和3.3V,然后TXD
和RXD
分别接入A10
和A9
引脚。
2)烧录
BOOT0置1
,BOOT1置0
,打开mcuisp,选择文件路径,开始烧录。
5.实际效果
BOOT0置0
,打开野火多功能调试助手,配置串口并打开。
三、实验总结
通过本次STM32中断与DMA通信编程实验,我了解了stm32中断、DMA通信原理和编程方法,认识到DMA传输过程不占用CPU资源,较查询和中断方式更加高效。
查询方式:最简单的I/O方式,数据在CPU和外设之间的传送完全靠计算机程序控制,外设和CPU之间是串行工作,CPU效率低。
中断方式:外设主动提出数据传送要求的I/O方式,CPU在收到要求之前,则执行着本身的程序,突出的优点是CPU效率高。缺点是硬件结构相对复杂,服务开销时间较大。
DMA方式:完全由硬件执行I/O交换,其主要优点是数据传送速度很高,CPU根本不参加传送操作,而直接在内存和外设之间进行,传送速率仅受内存访问时间的限制。缺点是需要更多的硬件。
四、参考资料
1、中断-百度百科
2、第七章__中断系统(new).pdf
3、第九章__串口通信(new).pdf
4、【STM32】HAL库 STM32CubeMX教程四—UART串口通信详解
5、【STM32】HAL库 STM32CubeMX教程十一—DMA (串口DMA发送接收)