stm32 之 中断按键初始化(注意事项)
- 格式:pdf
- 大小:214.43 KB
- 文档页数:7
在ARM编程领域中,凡是打断程序顺序执行的事件,都被称为异常。
除了外部中断外,当有指令执行了“非法操作”,或者访问被禁的内存区间,因各种错误产生的fault,以及不可屏蔽中断发生时,都会打断程序的执行,这些情况统称为异常。
简单来说:异常包括外部中断和内核fault。
外部中断(IRQ):原本处于正常状态,突然有个外部因素干扰,然后马上处理干扰事项,解决好后又回到原来正常状态。
在中断产生后一般会去执行中断服务函数,实现特定任务。
无特殊说明,后面:异常就是中断,中断就是异常在编译时,每一个函数都有一个入口地址,该地址就是函数名。
尽管函数不是变量,但它在内存中仍有其物理地址,该地址能够赋给指针变量。
函数名相当于一个指向其函数入口指针常量。
函数名后面加圆括号,表示函数调用。
若要得到函数的地址,直接用函数名就可以了。
函数名就是一个地址,是存放该函数代码在存储器空间上的起始地址。
以一个子函数为例,编译器会分配一段内存空间用于存放改子函数代码内容,这段内存空间的起始地址是一个具体值,在程序里边就是函数名,当我们在程序其他位置调用该子函数时候,实际上就是让程序跳转到该函数名地址去运行子函数内容。
Cotrex-M4支持大量的中断,包括16‐5(保留功能)-1=10个系统异常,和最多240个外部中断。
当一个中断发生时候,并由CM4内核接受后,会执行对应的中断服务函数。
所以可以想象需要定义非常多的中断服务函数(而实际上并不需要很多,因为一般都只使能我们需要用到的中断)。
为方便CM4找到对应的中断函数入口,CM4使用了“向量表查表机制”这里使用一张向量表。
向量表其实是一个WORD(32位整数)数组,每个下标对应一种中断,该下标元素的值则是该中断服务函数的入口地址。
●#1~15(系统异常)在CortexM4中定义,IRQ#0~239(外部中断)中断由各个芯片商定义●向量表定义了中断的处理例程的入口地址。
缺省情况下,CM4认为向量表位于零地址处●响应中断时,CM4会根据中断号从表中找出对应的中断处理程序的入口地址●每个表项占用4字节●位置0x00000000处保存的是MSP的初始值异常类型表项地址偏移量异常向量00x00MSP的初始值10x04复位20x08NMI30x0C硬fault40x10MemManage fault50x14总线fault 60x18用法fault7‐100x1c‐0x28保留110x2c SVC120x30调试监视器130x34保留140x38PendSV 150x3c SysTick 160x40IRQ #0170x44IRQ #1中断向量表的跳转●支持10个Cortex-M4系统异常和82个可屏蔽外部中断●16个可编程优先级(使用了4位中断优先级)●包括内核异常在内的所有中断均通过NVIC进行管理。
实训题目:按键控制LED模式并使用中断
描述:在这个实训中,你将使用STM32L151C8T6微控制器编写一个程序,以通过三个按键来控制两个LED灯的状态,而且这次LED有不同的模式,并使用中断来检测按键的状态变化。
具体要求如下:
要求:
使用STM32CubeIDE或其他适合的开发环境创建一个新的STM32L151C8T6项目。
配置两个GPIO引脚分别用于控制两个LED灯的开关,将其设置为输出模式。
配置三个GPIO引脚作为按键的输入引脚。
配置外部中断(例如,使用EXTI线路)以检测按键的状态变化(按下和释放)。
在中断处理程序中检测按键的状态变化,并实现以下LED模式切换:
当按下按键1时,切换LED1的模式(例如,常亮、闪烁、呼吸等)。
当按下按键2时,切换LED2的模式。
当按下按键3时,同时熄灭两个LED。
为每个LED模式实现不同的效果。
例如,如果选择闪烁模式,LED应该以不同的频率闪烁;如果选择呼吸模式,LED应该逐渐变亮和变暗。
编写一个无限循环,以让程序持续运行,允许按键切换LED的模式和控制LED的开关。
测试你的程序,确保按下不同的按键会导致相应的LED模式切换和LED状态变化。
这个实训题目要求你不仅要处理按键的状态变化,还要实现多种LED模式,每种模式有不同的效果。
这将帮助你深入了解STM32的GPIO配置、中断处理以及复杂的LED控制。
学习笔记:STM32的外部中断(库函数)在为某引脚配置中断前,同样要先初始化该引脚的配置,用GPIO_Init()函数初始化,不同的是,由于是外部中断,所以输入模式要设置上拉输入。
假设外部中断引脚为PE.2,则该引脚初始化配置的程序为:IOIO口作为外部中断输入是复用功能,因此在此基础上还需要对另一个时钟信号进行初始化。
这是IO口作为复用功能时需要进行初始化的时钟,另外,要注意的是,做一般功能使用的IO口只需要调用第一个函数即可,而作为复用功能的IO口,两个函数都要调用,两者缺一不可,否则不能正常使用。
STM32的每个IO都可以作为外部中断的中断输入口,这点也是STM32的强大之处。
STM32F103的中断控制器支持19个外部中断/事件请求。
每个中断设有状态位,每个中断/事件都有独立的触发和屏蔽设置。
STM32F103的19个外部中断为:线0~15:对应外部IO口的输入中断。
线16:连接到PVD输出。
线17:连接到RTC闹钟事件。
线18:连接到USB唤醒事件。
从上面可以看出,STM32供IO口使用的中断线只有16个,但是STM32的IO口却远远不止16个,那么STM32是怎么把16个中断线和IO口一一对应起来的呢?于是STM32就这样设计,GPIO的管脚GPIOx.0~GPIOx.15(x=A,B,C,D,E,F,G)分别对应中断线0~15。
这样每个中断线对应了最多7个IO口,以线0为例:它对应了GPIOA.0、GPIOB.0、GPIOC.0、GPIOD.0、GPIOE.0、GPIOF.0、GPIOG.0。
而中断线每次只能连接到1个IO口上,这样就需要通过配置来决定对应的中断线配置到哪个GPIO上了。
下面我们看看GPIO跟中断线的映射关系图:在库函数中,配置GPIO与中断线的映射关系是通过函数GPIO_EXTILineConfig()来实现的:void GPIO_EXTILineConfig(uint8_t GPIO_PortSource,uint8_t GPIO_PinSource)该函数将端口与中断线映射起来,使用示例是:GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);将中断线2与GPIOE映射起来,那么很显然是GPIOE.2与EXTI2中断线连接了。
stm32单片机设计定时器中断实现1s的led灯闪烁知识应用要实现1s的LED灯闪烁,可以使用STM32单片机的定时器中断来控制LED的开关。
以下是实现的步骤:1. 配置定时器:选择一个定时器(如TIM2)并设置适当的预分频和计数值,以实现1s的定时周期。
2. 配置中断:使能定时器中断,并将中断优先级设置为适当的值(较高优先级)。
3. 初始化LED引脚:将LED引脚设置为输出,并初始化为高电平(LED关闭)。
4. 编写中断处理程序:在中断处理程序(如TIM2_IRQHandler)中,切换LED引脚的状态。
例如,如果LED引脚当前为高电平,则将其设置为低电平,反之亦然。
5. 启动定时器:启动定时器以开始定时。
整个步骤如下所示的代码示例:```c#include "stm32fxx.h"void TIM2_IRQHandler(void){if(TIM2->SR & TIM_SR_UIF){TIM2->SR &= ~TIM_SR_UIF; // 清除中断标志位// 切换LED引脚状态if(GPIOC->ODR & GPIO_ODR_ODR0)GPIOC->ODR &= ~GPIO_ODR_ODR0; // 关闭LEDelseGPIOC->ODR |= GPIO_ODR_ODR0; // 打开LED}}int main(){// 初始化LED引脚RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; // 使能GPIOC时钟GPIOC->MODER |= GPIO_MODER_MODER0_0; // 将PC0设置为输出模式GPIOC->OSPEEDR |= GPIO_OSPEEDR_OSPEED0; // 设置PC0输出速度// 配置定时器RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // 使能TIM2时钟TIM2->PSC = 8399; // 将预分频设置为8400-1,得到10kHz 的计数频率TIM2->ARR = 9999; // 将计数值设置为10000-1,得到1s的定时周期// 配置中断TIM2->DIER |= TIM_DIER_UIE; // 使能更新中断NVIC_EnableIRQ(TIM2_IRQn); // 使能TIM2中断NVIC_SetPriority(TIM2_IRQn, 0); // 设置TIM2中断优先级为最高// 启动定时器TIM2->CR1 |= TIM_CR1_CEN; // 启动TIM2定时器while(1){// 程序主循环}return 0;}```以上代码使用了TIM2定时器和PC0引脚作为LED灯的控制。
在STM32微控制器上使用FreeRTOS实时操作系统时,中断服务例程(ISR)的写法需要遵循一定的规范,以确保中断处理能够在不同的任务之间正确地进行。
以下是一些基本步骤和注意事项,用于在FreeRTOS中实现中断服务例程:1. **中断向量表初始化**:在系统启动时,需要初始化中断向量表,将中断处理函数的地址映射到相应的中断号上。
2. **中断优先级设置**:在FreeRTOS中,可以通过NVIC(嵌套向量中断控制器)设置中断的优先级。
在中断服务例程中,可以使用`HAL_NVIC_SetPriority`函数设置优先级。
3. **中断使能**:在中断服务例程编写完成后,需要使用`HAL_NVIC_EnableIRQ`或`HAL_NVIC_Enable`函数使能中断。
4. **中断服务例程的原型**:中断服务例程应该有一个原型,其中包含了中断号和中断服务例程的函数指针。
5. **在中断服务例程中保护现场**:在进入中断服务例程之前,应该保存CPU的状态,包括程序计数器和其他必要的寄存器,以防止中断服务例程被其他中断打断。
6. **中断服务例程的退出**:在中断服务例程结束时,应该恢复之前保存的状态,并使用`HAL_NVIC_DisableIRQ`或`HAL_NVIC_Disable`函数禁用中断。
7. **使用FreeRTOS的API**:如果需要在中断服务例程中使用FreeRTOS的API,比如创建任务、等待消息等,应该确保这些操作不会导致中断被禁止的时间过长,以免影响系统的响应性。
以下是一个简单的STM32中断服务例程的示例代码:```cvoid EXTI0_IRQHandler(void) {// 保存状态HAL_NVIC_DisableIRQ(EXTI0_IRQn); // 禁用中断// 中断处理逻辑// ...// 恢复状态HAL_NVIC_EnableIRQ(EXTI0_IRQn); // 使能中断} ```。
stm32 定时器中断配置
stm32 中断
stm32 的Cortex 内核具有强大的异常响应系统,它把能够打断当前代码执行流程的事件分为异常(excepTIon)和中断(terryp),并把它们用二个表管理起来,编号为0~15 的称为内核异常,而16 以上的则称为外部中断(外,相对内核而言),这个表就称为中断向量表。
而STM32 对这个表重新进行了编排,把编号从-3 至6 的中断向量定义为系统异常,编号为负的内核异常不能被设置优先级,如复位(Reset)、不可屏蔽中断(NMI)、硬错误(Hardfault)。
从编号7 开始的为外部中断,这些中断的优先级都是可以自行设置的。
STM32 的中断如此之多,配置起来并不容易,因此,我们需要一个
强大而方便的中断控制器NVIC,NVIC 是属于Cortex 内核的器件。
stm32 中断配置
配置STM32 的中断只需要理解2 个内容,配置4 个变量即可。
STM32中断的详细介绍及使用流程STM32是一款常用的32位微控制器系列,由STMicroelectronics (ST)研发,广泛应用于各种嵌入式系统中。
中断是STM32中非常重要的特性之一,本文将详细介绍中断的概念、分类、优点以及使用流程。
一、中断的概念中断是一种机制,允许外部硬件设备打断正常的程序运行,并立即执行一个称为中断服务例程(ISR)的特定函数。
中断通常由外部硬件引发,例如定时器溢出、外部触发事件等。
一旦中断发生,控制器将暂停当前正在执行的任务,转而执行ISR,当ISR执行完毕后,控制权将返回到原来的任务。
二、中断的分类1.外部中断:STM32的GPIO引脚可以配置为外部中断触发。
当配置为外部中断模式时,如果引脚上的输入信号满足特定条件(上升沿、下降沿等),将会触发一个外部中断,并执行ISR。
2.内部中断:STM32具有许多内置的模块,例如定时器、UART等,这些模块常常产生中断信号。
例如,定时器溢出中断可以用于周期性任务的处理。
三、中断的优点1.实时性:中断能够立即响应外部事件,极大提高了系统的实时性。
2.节约CPU资源:中断是一种事件驱动的方式,只有在需要处理的时候才会触发中断,节约了CPU资源。
3.模块化设计:通过使用中断,可以将复杂的任务分解为更小的块,实现模块化的设计。
四、中断的使用流程使用中断的流程通常包括以下几个步骤:1.初始化中断:配置相关的中断源和中断服务例程。
在STM32中,中断使用时需要开启并配置相关的寄存器。
2.注册中断服务例程:编写中断服务例程的函数,这是中断发生时将要执行的函数。
在STM32中,可以使用标准库提供的函数来注册中断服务例程。
3.启用中断:开启中断并配置相应的优先级,以确定中断发生时的处理顺序。
在STM32中,可以设置中断控制器的中断使能位和优先级。
下面以STM32CubeIDE为例,展示一个外部中断的使用流程:1. 打开STM32CubeIDE,创建一个新的工程,并选择相应的芯片型号。
STM32外部中断(实现按键控制LED)引⾔“中断” 这个概念,指的是在单⽚机运⾏过程中,在指定条件发⽣时,停下当前所有⼯作,去执⾏中断处理函数内的内容。
就像我们在教室上课时,突然地震了,不出意外的话我们都需要停下⼿中学习任务,去进⾏⼀系列的避险动作。
这⼀节我们通过中断的⽅式,完成通过按键控制LED亮灭的操作。
准备环节中断相关知识STM32的中断控制器⽀持19个外部中断/事件请求。
这⼗九个外部中断为:线0~15:对应外部IO⼝的输⼊中断。
线16:连接到PVD输出。
线17:连接到连接到RTC时钟事件。
线18:连接到USB唤醒事件。
配置使⽤时,需要先将IO⼝与相应中断线的映射关系建⽴,再对其进⾏使⽤。
那映射关系是怎样的呢?GPIOx.0 ~ GPIOx.15(x = A,B,C,D,E,F,G)分别对应中断线0 ~ 15配置GPIO与中断线关系的函数是void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)eg:GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);把GPIO作为EXTI外部中断时,需要打开AFIO时钟。
APIO时钟何时需要打开,具体可以参考这篇⽂章:嵌套向量中断控制器(NVIC)初始化完线上中断和中断条件等内容,还需要配置中断分组。
配置中断分组之前,我们需要先确定如何进⾏分组。
这⾥就需要⽤到NVIC。
关于NVIC的具体内容可查看这篇博⽂:。
编码环节步骤初始化IO⼝输⼊开启AFIO时钟设置IO⼝与中断线的映射关系初始化线上中断、设置触发条件等配置中断分组,并使能中断编写中断服务函数main.c(以下内容均省略头⽂件)int main(void){delay_init();NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC终端分组2LED_Init();KEY_Init();EXTIX_Init(); //外部中断初始化LED1 = 0;}exti.h#ifndef __EXTI_H#define __EXIT_H#include "sys.h"void EXTIX_Init(void); //外部中断初始化#endifexti.cvoid EXTIX_Init(void){EXTI_InitTypeDef EXTI_InitStructure; //外部中断结构体初始化NVIC_InitTypeDef NVIC_InitStructure; //中断分组结构体初始化KEY_Init();RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启AFIO时钟GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); //映射IO⼝与中断线//以下为配置中断线初始化EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中断模式EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能中断线EXTI_InitStructure.EXTI_Line = EXTI_Line0; //中断线标号EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //触发⽅式EXTI_Init(&EXTI_InitStructure);//以下为中断优先级的配置NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //声明使⽤的中断是哪⼀个NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //设置抢占优先级为2 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03; //设置⼦优先级为3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断NVIC_Init(&NVIC_InitStructure);}void EXTI0_IRQHandler(void) //中断服务函数{delay_ms(10); //软件去抖if(WK_UP==1){LED0 = !LED0;LED1 = !LED1;}EXTI_ClearITPendingBit(EXTI_Line0); //清除中断位}补充中断服务函数中断服务函数的名称是固定的,写错会导致⽆法中断。
stm32 之中断按键初始化(注意事项)之前做终端按键的时候都是只做了一个,没有做多个,昨天在把所有按键都设置成中断模式的时候遇到问题,于是乎还跟一个网上的哥们进行了热议,后来还是我发现了问题!最终把问题给解决了!我的按键的GPIO连接有点奇葩,他不是连续的,这可能就是竞赛板故意设置的难度吧!首先管脚初始化:[cpp]view plaincopyprint?1. GPIO_InitTypeDef key;2.3. RCC->APB2ENR |= ((1<<0)|(1<<2)|(1<<3));4.5. key.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_8;6. key.GPIO_Mode = GPIO_Mode_IPD;7. GPIO_Init(GPIOA, &key);8.9. key.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2;10. key.GPIO_Mode = GPIO_Mode_IPD;11. GPIO_Init(GPIOB, &key);全部设置成输入模式,AFIO再时钟使能的时候不要忘记了!这里我就不多说了!然后就是中断组设置:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC初始化:[cpp]view plaincopyprint?1. key_nvic.NVIC_IRQChannel = EXTI0_IRQn;2. key_nvic.NVIC_IRQChannelCmd = ENABLE;3. key_nvic.NVIC_IRQChannelPreemptionPriority = 0;4. key_nvic.NVIC_IRQChannelSubPriority = 1;5. NVIC_Init(&key_nvic);重点都不在这,值得注意的是下面:我第一次在配置EXTI Line的时候这样配置!GPIO_EXTILineConfig(GPIO_PortSourceGPIOA|GPIO_PortSourceGPIOB,\ GPIO_PinSource0|GPIO_PinSource1|GPIO_PinSource2|GPIO_PinSource8); 大致一看,貌似很正常啊!但是问题就出在这!我们跳转到GPIO_PinSourcex和GPIO_PortSourceGPIOx哪里看看:[cpp]view plaincopyprint?1. #define GPIO_PortSourceGPIOA ((uint8_t)0x00)2. #define GPIO_PortSourceGPIOB ((uint8_t)0x01)3. #define GPIO_PortSourceGPIOC ((uint8_t)0x02)4. #define GPIO_PortSourceGPIOD ((uint8_t)0x03)5. #define GPIO_PortSourceGPIOE ((uint8_t)0x04)6. #define GPIO_PortSourceGPIOF ((uint8_t)0x05)7. #define GPIO_PortSourceGPIOG ((uint8_t)0x06)[cpp]view plaincopyprint?1. #define GPIO_PinSource0 ((uint8_t)0x00)2. #define GPIO_PinSource1 ((uint8_t)0x01)3. #define GPIO_PinSource2 ((uint8_t)0x02)4. #define GPIO_PinSource3 ((uint8_t)0x03)5. #define GPIO_PinSource4 ((uint8_t)0x04)6. #define GPIO_PinSource5 ((uint8_t)0x05)7. #define GPIO_PinSource6 ((uint8_t)0x06)8. #define GPIO_PinSource7 ((uint8_t)0x07)9. #define GPIO_PinSource8 ((uint8_t)0x08)10. #define GPIO_PinSource9 ((uint8_t)0x09)11. #define GPIO_PinSource10 ((uint8_t)0x0A)12. #define GPIO_PinSource11 ((uint8_t)0x0B)13. #define GPIO_PinSource12 ((uint8_t)0x0C)14. #define GPIO_PinSource13 ((uint8_t)0x0D)15. #define GPIO_PinSource14 ((uint8_t)0x0E)16. #define GPIO_PinSource15 ((uint8_t)0x0F)我们来计算下:GPIO_PortSourceGPIOA | GPIO_PortSourceGPIOB = 0x00 | 0x01 = 0x01 =GPIO_PortSourceGPIOBGPIO_PinSource0 | GPIO_PinSource1 | GPIO_PinSource2 | GPIO_PinSource8 = 0x00 | 0x01 | 0x02 | 0x08 = 0x0b = GPIO_PinSource11所以我最后初始化后的中断就成为:GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource11);最终让我事与愿违了。
STM32使用HAL库进行中断处理的流程1. 简介中断是嵌入式系统中常用的一种事件驱动机制,使得系统能够在执行任务的同时,及时响应重要的事件。
在STM32微控制器中,HAL库提供了一种方便的方式来进行中断处理。
本文将介绍使用HAL库进行中断处理的流程,并提供相应的代码示例。
2. 初始化中断控制器在使用HAL库进行中断处理之前,首先需要初始化中断控制器。
这可以通过调用HAL_NVIC_SetPriority()和HAL_NVIC_EnableIRQ()函数来实现。
以下是初始化中断控制器的示例代码:// 初始化中断控制器void init_interrupt_controller(){// 设置中断优先级HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint 32_t SubPriority);// 使能中断HAL_NVIC_EnableIRQ(IRQn_Type IRQn);}在示例代码中,IRQn_Type是中断号,PreemptPriority是抢占优先级,SubPriority是子优先级。
3. 编写中断处理函数使用HAL库进行中断处理时,需要编写相应的中断处理函数。
中断处理函数要按照一定的格式进行编写,以便与中断向量表关联。
以下是编写中断处理函数的示例代码:// 中断处理函数void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){// 中断处理代码// ...}在示例代码中,HAL_GPIO_EXTI_Callback是GPIO外部中断的中断处理函数。
根据不同的中断源和外设,中断处理函数的名称可能会有所不同。
4. 配置中断触发条件在使用HAL库进行中断处理时,需要配置中断触发条件。
这可以通过调用HAL_GPIO_Init()函数来实现。
以下是配置中断触发条件的示例代码:// 配置中断触发条件void configure_interrupt_trigger(){GPIO_InitTypeDef GPIO_InitStruct;// 配置GPIO引脚GPIO_InitStruct.Pin = GPIO_PIN_0;GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; // 上升沿触发GPIO_InitStruct.Pull = GPIO_PULLDOWN;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);// 配置中断线HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0); // 设置中断优先级HAL_NVIC_EnableIRQ(EXTI0_IRQn); // 使能中断线}在示例代码中,我们配置了GPIO引脚为上升沿触发,并设置了中断优先级。
分析初始化STM32串口后进入发送完成中断的现象最近在调试STM32 串口过程中发现一个奇怪的问题,初始化串口1 口,使能串口发送完成中断后,立刻就进入了发送完成中断,21ic 论坛上也有同样的问题讨论,而香水版主并没有解释原因。
为了彻底的搞明白产生这一现象的原因:我仔细的看了STM32 手册中的串口部分的介绍:以下是字符发送的配置过程,注意第6 点,在设置USART_CR1 中的TE 位时,会发送一个空闲帧作为第一次数据发送,所以即便你执行了USART_ClearFlag(USART1,USART_FLAG_TC); (这个函数肯定在空闲帧数据发送完成前执行),所以当空闲帧发送完后,就进入发送完成中断。
配置步骤:1.通过在USART_CR1 寄存器上置位UE 位来激活USART2.编程USART_CR1 的M 位来定义字长。
3.在USART_CR2 中编程停止位的位数。
4.如果采用多缓冲器通信,配置USART_CR3 中的DMA 使能位(DMAT)。
按多缓冲器通信中的描述配置DMA寄存器。
5.利用USART_BRR 寄存器选择要求的波特率。
6.设置USART_CR1中的TE 位,发送一个空闲帧作为第一次数据发送。
7.把要发送的数据写进USART_DR 寄存器(此动作清除TXE 位)。
在只有一个缓冲器的情况下,对每个待发送的数据重复步骤7。
8.在USART_DR 寄存器中写入最后一个数据字后,要等待TC=1,它表示最后一个数据帧的传输结束。
当需要关闭USART 或需要进入停机模式之前,需要确认传输结束,避免破坏最后一次传输。
解决的办法:方法一在执行USART_ITConfig(USART1, USART_IT_TC, ENABLE); 之前,先延时一段时间,基本上比一个字符发送的时间长一点就可以了,然后再执行USART_ClearFlag(USART1, USART_FLAG_TC);方法二:在执行USART_ITConfig(USART1, USART_IT_TC, ENABLE); 之前,USART_ClearFlag(USART1,。
STM32中断法USART串口简单使用
1.初始化USART外设:首先需要在STM32的寄存器中对USART进行初始化。
具体的步骤包括:选择时钟源、配置波特率、设置数据长度、设置停止位、设置校验位等。
这些设置都可以在USART的控制寄存器中进行。
2.配置串口引脚:需要将USART的引脚与STM32的GPIO引脚进行连接。
具体的配置方法包括将GPIO引脚设置为复用功能,并且选择对应的USART信号。
3.编写中断服务函数:为了使用中断方式接收和发送数据,需要编写中断服务函数。
中断服务函数通常由硬件自动调用,当USART接收到数据或发送数据完成时触发。
在中断服务函数中,我们可以读取接收到的数据或者发送下一个数据。
4.使能中断:要使能USART的串口接收中断,需要在USART的控制寄存器中设置相应的位。
通常有RXNE和TC中断位,分别表示接收缓冲区非空和发送完成。
5.启动USART:启动USART外设,使其处于工作状态。
可以在相应的控制寄存器中设置TE(发送使能)和RE(接收使能)位。
6.外部中断配置:在STM32中,需要在NVIC寄存器中配置和使能USART接收中断的优先级。
这样才能通过中断向量表触发中断。
通过上述步骤,可以完成USART串口的简单使用,实现数据的接收和发送。
在编写中断服务函数时,可以根据实际需求进行数据处理,例如打印接收的数据或根据接收到的数据触发其他功能。
STM32中断优先级的处理原则1. 引言在嵌入式系统中,中断是一种常用的机制,用于在特定事件发生时打断CPU的正常执行流程,转而执行特定的中断处理程序。
STM32系列微控制器提供了丰富的中断控制功能,并支持多个优先级的中断。
正确设置中断优先级是确保系统稳定性和可靠性的重要步骤。
本文将介绍STM32中断优先级处理原则,包括如何设置优先级、不同类型中断之间的关系以及注意事项等内容。
2. 中断优先级概述STM32微控制器支持多个优先级的中断,其中数字越小表示优先级越高。
当多个中断同时发生时,只有具有最高优先级的中断会被响应。
其他低优先级的中断将被挂起,等待当前正在处理的高优先级中断完成后再进行处理。
每个STM32微控制器都有一个向量表(Vector Table),其中存储了各个中断向量及其对应的ISR(Interrupt Service Routine)。
在初始化过程中,需要将需要使用到的ISR函数指针写入向量表相应位置。
3. 中断优先级设置原则在STM32微控制器上配置和设置各个外设的中断优先级时,需要遵循以下原则:3.1 高优先级中断的响应时间高优先级中断的响应时间应尽量短,以确保系统对紧急事件的及时响应。
通常情况下,系统启动和初始化过程中会配置一些必要的高优先级中断,如系统滴答定时器(SysTick)等。
3.2 低优先级中断的执行时间低优先级中断可能会被高优先级中断打断,在高优先级中断执行期间无法得到处理。
因此,低优先级中断的执行时间应尽量短,以减少对系统性能和实时性的影响。
STM32微控制器支持不同外设之间和相同外设内部的中断嵌套。
在设置嵌套中断时,需要注意以下原则: - 外设之间:不同外设之间的中断嵌套顺序应根据具体需求和业务逻辑进行设置。
- 外设内部:在具有多个可触发相同类型中断源的外设上,需要根据业务需求设置不同源之间的触发次序。
3.4 中断抢占与屏蔽STM32微控制器支持中断的抢占和屏蔽功能。
单片机中断初始化的流程下载温馨提示:该文档是我店铺精心编制而成,希望大家下载以后,能够帮助大家解决实际的问题。
文档下载后可定制随意修改,请根据实际需要进行相应的调整和使用,谢谢!并且,本店铺为大家提供各种各样类型的实用资料,如教育随笔、日记赏析、句子摘抄、古诗大全、经典美文、话题作文、工作总结、词语解析、文案摘录、其他资料等等,如想了解不同资料格式和写法,敬请关注!Download tips: This document is carefully compiled by theeditor. I hope that after you download them,they can help yousolve practical problems. The document can be customized andmodified after downloading,please adjust and use it according toactual needs, thank you!In addition, our shop provides you with various types ofpractical materials,such as educational essays, diaryappreciation,sentence excerpts,ancient poems,classic articles,topic composition,work summary,word parsing,copy excerpts,other materials and so on,want to know different data formats andwriting methods,please pay attention!1. 开启总中断:在单片机的寄存器中设置相应的位,以允许中断的发生。
STM32按键中断(HAL库版)
本文将介绍如何使用STM32F4的IO口作为中断触发源,通过串口显示按键被按下的日志。
一、运用到的资源、工具:
1.1开发板芯片STM32F407,PI9作为外部中断源、USART3串口向屏幕传输信息
1.2编译工具:MDK-ARM V5(keil5)
1.3辅助工具:STM32CubeMX
二、硬件设计
2.1原理图:
三、软件设计
3.1STM32cubeMX配置工程文件
选择Key1作为外部中断源、选择中断触发方式为下降沿触发、并设置中断优先级分组选择优先级
使能USART3串口配置为异步通信
最后生成工程文件
3.2串口输出重定向(重写fputc函数)
int fputc(int ch, FILE *p)
{
while(!(USART3->SR & (1 << 7)));USART3->DR = ch;
return ch;
}
3.3在中断回调函数中,打印KEY1 DOWN\n
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_9)
{
HAL_Delay(40);
if(HAL_GPIO_ReadPin(GPIOI, GPIO_PIN_9) == 0)
{
printf("KEY1 DOWN\n");
}
}
}
四、代码及运行结果
4.1运行结果按下KEY1、打印一次KEY1 DOWN。
STM32 硬件IIC中断使用方法--中北大学:马政贵本文详细描述了STM32硬件IIC的中断使用方法,包含流程图和对应代码,以及调试过程中的问题记录和分析解决。
之前,一直听说STM32的硬件IIC有问题,加之以前写好的模拟IIC模块用着一直没问题,就没有去使用STM32的硬件IIC。
后面项目中需要实时读取三个传感器的数据,三个传感器并在一起共用一个IIC口,通过地址进行区分通信,一个传感器要读取8字节数据。
采用模拟IIC方式,很多时间都浪费在高低电平的等待时间上,导致其他任务时间很紧迫。
于是想到使用硬件IIC的中断方式,来提升效率。
查看了下库自带的例程,使用的是查询方式,使用了大量的while来等待状态标志完成。
移植过来,虽然可以跑通,但使用的是查询方式,效率本质上和模拟IIC是一样的。
于是自己便把手册里的IIC模块看了一遍,自己根据手册来写硬件IIC的中断模式。
上图是传感器的通讯时序,属于标准的IIC通信,这里不做进一步展开,详细可以参看IIC总线规范。
应用中,MCU做主机,传感器做从机。
MCU先工作在主发送器模式,然后工作在主接收器模式。
在默认状态下,MCU接口工作于从模式。
接口在生成起始条件后自动地从从模式切换到主模式;当仲裁丢失或产生停止信号时,则从主模式切换到从模式。
在应用手册中,详细给出了IIC主模式的操作要求及顺序:这里需要注意的是怎样结束通信,然后重启通信,以便获取下一个传感器的数据。
应用手册根据情况分为了三种,我这里使用的是第一种方式,也就是把IIC的中断优先级设置为最高。
下面我以流程图的方式,把整个过程表示出来,然后再据此给出相应的代码,代码在中断中以状态机的方式进行。
先进行IIC管脚GPIO的配置:接着进行IIC的配置(中断先不使能,在初始化完传感器之后再使能。
我这里把IIC的事件中断优先级设置为最高,若不是,IIC的结束操作需参照应用手册中相应的情况进行):中断中的状态机如下:void I2C2_EV_IRQHandler(void){switch(IIC2_State){case 0:if(I2C_GetINTStatus(I2C2, I2C_INT_STARTF)){I2C_ClearITPendingBit(I2C2, I2C_INT_STARTF);(void)(I2C2->STS1);I2C_Send7bitAddress(I2C2, ALS31300_ADR[IIC_Device], I2C_Direction_Transmit);IIC2_State++;}else{I2C2->STS1 = 0;}break;case 1:if(I2C_GetINTStatus(I2C2, I2C_INT_ADDRF)){I2C_ClearITPendingBit(I2C2, I2C_INT_ADDRF);(void)(I2C2->STS1);(void)(I2C2->STS2);I2C_SendData(I2C2, 0x28);IIC2_State++;}else{I2C2->STS1 = 0;}break;case 2:if(I2C_GetINTStatus(I2C2, I2C_INT_TDE)){I2C_ClearITPendingBit(I2C2, I2C_INT_TDE);I2C_ClearITPendingBit(I2C2, I2C_INT_BTFF);(void)(I2C2->STS1);I2C_GenerateSTART(I2C2, ENABLE); /* Send STRAT condition a second time */IIC2_State++;}else{I2C2->STS1 = 0;}break;case 3:if(I2C_GetINTStatus(I2C2, I2C_INT_STARTF)){I2C_ClearITPendingBit(I2C2, I2C_INT_STARTF);(void)(I2C2->STS1);I2C_Send7bitAddress(I2C2, ALS31300_ADR[IIC_Device],I2C_Direction_Receive);IIC2_State++;}else{I2C2->STS1 = 0;}break;case 4:if(I2C_GetINTStatus(I2C2, I2C_INT_ADDRF)){I2C_ClearITPendingBit(I2C2, I2C_INT_ADDRF);(void)(I2C2->STS1);(void)(I2C2->STS2);IIC2_State++;}else{I2C2->STS1 = 0;}break;case 5:if(I2C_GetINTStatus(I2C2, I2C_INT_RDNE)){I2C_ClearITPendingBit(I2C2, I2C_INT_RDNE);(void)(I2C2->STS1);ALS31300_Reg_Data[IIC_Device][IIC_Rec_Num] = I2C_ReceiveData(I2C2);IIC_Rec_Num++;if(IIC_Rec_Num >= 6){IIC2_State++;}}else{I2C2->STS1 = 0;}break;case 6:if(I2C_GetINTStatus(I2C2, I2C_INT_RDNE)){I2C_ClearITPendingBit(I2C2, I2C_INT_RDNE);(void)(I2C2->STS1);ALS31300_Reg_Data[IIC_Device][IIC_Rec_Num] = I2C_ReceiveData(I2C2);I2C_AcknowledgeConfig(I2C2, DISABLE);I2C_GenerateSTOP(I2C2, ENABLE);IIC_Rec_Num++;IIC2_State++;}else{I2C2->STS1 = 0;}break;case 7:if(I2C_GetINTStatus(I2C2, I2C_INT_RDNE)){I2C_ClearITPendingBit(I2C2, I2C_INT_RDNE);(void)(I2C2->STS1);ALS31300_Reg_Data[IIC_Device][IIC_Rec_Num] =I2C_ReceiveData(I2C2);IIC_Device++;if(IIC_Device >= 3){IIC_Device = 0;IIC_Rec_OK = 1;}I2C_AcknowledgeConfig(I2C2, ENABLE);I2C_GenerateSTART(I2C2, ENABLE);IIC_Rec_Num = 0;IIC2_State = 0;}else{I2C2->STS1 = 0;}break;default:I2C2->STS1 = 0;break;}}初始化之后,产生开始信号之后,使能中断,此后就一直在中断的状态机中往复进行了:#define IIC_TIMEOUT 1000IIC2_GPIO_Config();IIC2_Config();time_count = IIC_TIMEOUT;while(I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSYF)){time_count--;if(0 == time_count){IIC_Wrong_Count++;return;}}I2C_AcknowledgeConfig(I2C2, ENABLE);I2C_GenerateSTART(I2C2, ENABLE);//初始化之后再开中断,只在读操作中使用中断I2C_INTConfig(I2C2, I2C_INT_EVT | I2C_INT_BUF | I2C_INT_ERR, ENABLE);编译调试,咦,状态机一直停留在0状态,也就是卡在总线一直BUSY。
stm32 之 中断按键初始化(注意事项)之前做终端按键的时候都是只做了一个,没有做多个,昨天在把所有按键都设置成中断模式的时候遇到问题,于是乎还跟一个网上的哥们进行了热议,后来还是我发现了问题!最终把问题给解决了!我的按键的GPIO连接有点奇葩,他不是连续的,这可能就是竞赛板故意设置的难度吧!首先管脚初始化:[cpp]view plaincopyprint?1. GPIO_InitTypeDef key;2.3. RCC->APB2ENR |= ((1<<0)|(1<<2)|(1<<3));4.5. key.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_8;6. key.GPIO_Mode = GPIO_Mode_IPD;7. GPIO_Init(GPIOA, &key);8.9. key.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2;10. key.GPIO_Mode = GPIO_Mode_IPD;11. GPIO_Init(GPIOB, &key);全部设置成输入模式,AFIO再时钟使能的时候不要忘记了!这里我就不多说了!然后就是中断组设置:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC初始化:[cpp]view plaincopyprint?1. key_nvic.NVIC_IRQChannel = EXTI0_IRQn;2. key_nvic.NVIC_IRQChannelCmd = ENABLE;3. key_nvic.NVIC_IRQChannelPreemptionPriority = 0;4. key_nvic.NVIC_IRQChannelSubPriority = 1;5. NVIC_Init(&key_nvic);重点都不在这,值得注意的是下面:我第一次在配置EXTI Line的时候这样配置!GPIO_EXTILineConfig(GPIO_PortSourceGPIOA|GPIO_PortSourceGPIOB,\ GPIO_PinSource0|GPIO_PinSource1|GPIO_PinSource2|GPIO_PinSource8);大致一看,貌似很正常啊!但是问题就出在这!我们跳转到GPIO_PinSourcex和GPIO_PortSourceGPIOx哪里看看:[cpp]view plaincopyprint?1. #define GPIO_PortSourceGPIOA ((uint8_t)0x00)2. #define GPIO_PortSourceGPIOB ((uint8_t)0x01)3. #define GPIO_PortSourceGPIOC ((uint8_t)0x02)4. #define GPIO_PortSourceGPIOD ((uint8_t)0x03)5. #define GPIO_PortSourceGPIOE ((uint8_t)0x04)6. #define GPIO_PortSourceGPIOF ((uint8_t)0x05)7. #define GPIO_PortSourceGPIOG ((uint8_t)0x06) [cpp]view plaincopyprint?1. #define GPIO_PinSource0 ((uint8_t)0x00)2. #define GPIO_PinSource1 ((uint8_t)0x01)3. #define GPIO_PinSource2 ((uint8_t)0x02)4. #define GPIO_PinSource3 ((uint8_t)0x03)5. #define GPIO_PinSource4 ((uint8_t)0x04)6. #define GPIO_PinSource5 ((uint8_t)0x05)7. #define GPIO_PinSource6 ((uint8_t)0x06)8. #define GPIO_PinSource7 ((uint8_t)0x07)9. #define GPIO_PinSource8 ((uint8_t)0x08)10. #define GPIO_PinSource9 ((uint8_t)0x09)11. #define GPIO_PinSource10 ((uint8_t)0x0A)12. #define GPIO_PinSource11 ((uint8_t)0x0B)13. #define GPIO_PinSource12 ((uint8_t)0x0C)14. #define GPIO_PinSource13 ((uint8_t)0x0D)15. #define GPIO_PinSource14 ((uint8_t)0x0E)16. #define GPIO_PinSource15 ((uint8_t)0x0F)我们来计算下:GPIO_PortSourceGPIOA | GPIO_PortSourceGPIOB = 0x00 | 0x01= 0x01 = GPIO_PortSourceGPIOBGPIO_PinSource0 | GPIO_PinSource1 | GPIO_PinSource2 |GPIO_PinSource8 = 0x00 | 0x01 | 0x02 | 0x08 = 0x0b =GPIO_PinSource11所以我最后初始化后的中断就成为:GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource11);最终让我事与愿违了。
发现这个问题后,我仔细研究了一下GPIO_EXTILineConfig函数[cpp]view plaincopyprint?1. void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO2. {3. uint32_t tmp = 0x00;4. /* Check the parameters */5. assert_param(IS_GPIO_EXTI_PORT_SOURCE(GPIO_PortSourc6. assert_param(IS_GPIO_PIN_SOURCE(GPIO_PinSource));7.8. tmp = ((uint32_t)0x0F) << (0x04 * (GPIO_PinSource & (uint8_t)0x09. AFIO->EXTICR[GPIO_PinSource >> 0x02] &= ~tmp;10. AFIO->EXTICR[GPIO_PinSource >> 0x02] |= (((uint32_t)GPIO_PortSour11. }明白了,没有什么好纠结的了!最后,我就感叹,他这个中断函数写的一点都不灵活!我还是喜欢我写的这个,详情看前面中断按键![cpp]view plaincopyprint?1. void init_interrupt(u8 group,u8 inter_id,u8 preempting,u8 subpriority2. {3. u32 aircr;4. u8 ip;5.6. /* Set Group :2 */7. aircr = SCB->AIRCR; //Get AIRCR register8. aircr &= 0x0000f8ff; //Clear Password & PriGroup9. aircr |= 0x05fa0000; //Set Password10. aircr |= ((~group&0x7)<<8); //Set PriGroup Group:2 0000 0010 => 1111 1101 [5 = 010<<811. SCB->AIRCR = aircr; //Set AIRCR12.13. /*14. * Group 2 2:215. * 0~3 : 0~316. * Set Preempting = 0 Subpriority = 017. * 1001 0000b = 0x00;18. */19. if(inter_id<32)20. NVIC->ISER[0] = 1<< inter_id;21. else22. NVIC->ISER[1] = 1<<(inter_id-32); //EXIT15_10 vector:3723. switch(group)24. {25. case 0: ip = 0x0f&subpriority;break;26. case 1: ip = (0x08&preempting) | (0x07&subpriority);break;27. case 2: ip = (0x0C&preempting) | (0x03&subpriority);break;28. case 3: ip = (0x0e&preempting) | (0x01&subpriority);break;29. case 4: ip = 0x0f&preempting;break;30. default: ip = 0x00;break;31. }32. NVIC->IP[inter_id] = 0xf0&(ip<<4);33. }不要看我的一些注释,那些注释是给我自己看的!没有什么参考价值!这个问题搞清楚了,就没有什么容易出错的了,下面是代码:[cpp]view plaincopyprint?1. void ITkey_init(void)2. {3. EXTI_InitTypeDef key_exti;4. NVIC_InitTypeDef key_nvic;5. GPIO_InitTypeDef key;6.7. RCC->APB2ENR |= ((1<<0)|(1<<2)|(1<<3));8.9. key.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_8;10. key.GPIO_Mode = GPIO_Mode_IPD;11. GPIO_Init(GPIOA, &key);12.13. key.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2;14. key.GPIO_Mode = GPIO_Mode_IPD;15. GPIO_Init(GPIOB, &key);16.17.18. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);19.20. key_nvic.NVIC_IRQChannel = EXTI0_IRQn;21. key_nvic.NVIC_IRQChannelCmd = ENABLE;22. key_nvic.NVIC_IRQChannelPreemptionPriority = 0;23. key_nvic.NVIC_IRQChannelSubPriority = 1;24. NVIC_Init(&key_nvic);25. GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSourc26. key_exti.EXTI_Line = EXTI_Line0;27. key_exti.EXTI_LineCmd = ENABLE;28. key_exti.EXTI_Mode = EXTI_Mode_Interrupt;29. key_exti.EXTI_Trigger = EXTI_Trigger_Rising;30. EXTI_Init(&key_exti);31.32.33. key_nvic.NVIC_IRQChannel = EXTI9_5_IRQn;34. NVIC_Init(&key_nvic);35. GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSourc36. key_exti.EXTI_Line = EXTI_Line8;37. EXTI_Init(&key_exti);38.39. key_nvic.NVIC_IRQChannel = EXTI1_IRQn;40. NVIC_Init(&key_nvic);41. GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSourc42. key_exti.EXTI_Line = EXTI_Line1;43. EXTI_Init(&key_exti);44.45. key_nvic.NVIC_IRQChannel = EXTI2_IRQn;46. NVIC_Init(&key_nvic);47. GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSourc48. key_exti.EXTI_Line = EXTI_Line2;49. EXTI_Init(&key_exti);50.51. }。