STM32+摁键与定时器实现Led灯控制(中断)

这篇具有很好参考价值的文章主要介绍了STM32+摁键与定时器实现Led灯控制(中断)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

中断作为单片机开发必须掌握的内容,它能够在不搭载操作系统的情况下让我们体验多任务处理的快感,保证了高优先级任务的实时性,同时系统中断也能够提供给用户在核心发生错误之后进行处理的机会。STM32F103系列单片机中断非常强大,每个外设都可以产生中断,F103 在内核基础上搭载了一个中断响应系统, 支持为数众多的系统中断和外部中断。

本篇文章介绍了在STM32平台实现摁键中断控制LED亮灭以及定时器中断控制LED灯周期亮灭的功能。文章首先系统的介绍了中断有关的概念,然后通过摁键以及定时器两个实例带领读者直观的了解中断的作用。

中断概念

下面介绍一些中断的概念,这些概念依托于STM32平台,不同的芯片平台会有出入。

  • 中断:程序执行过程中CPU会遇到一些特殊情况,正在执行的程序被打断,cpu中止原来正在执行的程序,保存现场(保存上下文),转到处理异常情况或特殊事件的程序去执行,结束后恢复现场(被打断程序上下文),继续执行。

    定时器中断控制led灯闪烁,通用微控制器(MCU)开发,stm32,嵌入式硬件,单片机,物联网,嵌入式开发

  • 异常:异常是中断的一种类型,其中断源是芯片内部,中断原因为比如执行了未定义指令、算术溢出、除零运算等发生在CPU内部的意外事件,这些异常的发生,会引起CPU运行相应的异常处理程序。下面是Stm32异常中断。
    • Reset:处理器在工作时, 突然收到复位信号, 就会触发该异常。
    • NMI(Non Maskable Interrupt):不可屏蔽中断,产生这个中断的时候,表示系统发生了致命的错误。
    • HardFault:硬件错误中断,数组越界,野指针,任务堆栈溢出,未初始化硬件却开始操作,或无中断服务函数等,都会导致这个中断产生。(总线地址不可访问等),Memory Management Fault(在只读写的区域尝试执行代码),Usage Fault(除零异常等)等异常都会导致硬件错误中断。
    • MemManage:访问了内存管理单元(MPU)定义的不合法的内存区域,比如向只读区域写入数据。
    • BusFault:在fetch指令、数据读写、fetch中断向量或中断时存储恢复寄存器栈情况下,检测到内存访问错误则产生。
    • UsageFault:检测到未定义指令或在存取内存时有未对齐,检测到除数为0也产生该异常。
    • SVC:系统服务调用,SVC异常由SVC指令触发,在很多系统中SVC机制用于实现应用任务访问系统资源。
    • DebugMon:调试监视器(断电, 数据观察点, 或外部调试请求),大部分debug的时候就是调试的时候遇到。
    • PendSV:可挂起的系统调用,由于PendSV在系统中被设置为最低优先级,因此只有当没有其他异常或者中断在执行时才会被执行。在一个操作系统环境中,当没有其他异常正在执行时,可以使用PendSV来进行上下文的切换。
    • SysTick:系统定时中断,系统定时器中断 一般用在操作系统的延时 。
  • 保存现场:保存现场是当发生异常/中断时,CPU跳转之前,就需要把r0-r3, r12, lr, psr这几个寄存器的值保存到栈中。

    定时器中断控制led灯闪烁,通用微控制器(MCU)开发,stm32,嵌入式硬件,单片机,物联网,嵌入式开发

  • 恢复现场:STM实现了恢复现场的机制:CPU进入异常/中断服务程序时,LR寄存器保存的并不是中断前下一条指令的地址,而是会保存一个特殊的数值,被称为EXC_RETURN。异常/中断返回时,LR寄存器赋值给PC的值是一个被称为EXC_RETURN的值,而一旦CPU识别到PC的值等于EXC_RETURN的话,那么就会触发异常/中断返回机制,这个机制会帮我们把保存在栈中r0-r3, r12, lr, psr的寄存器的值恢复回去。定时器中断控制led灯闪烁,通用微控制器(MCU)开发,stm32,嵌入式硬件,单片机,物联网,嵌入式开发
  •  中断抢占优先级和子优先级(又称响应优先级):所谓抢占式优先级和响应优先级,具有高抢占式优先级的中断可以在低抢占式优先级中断处理过程中被响应,即中断嵌套。当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。每一个中断源都必须定义2个优先级。
  • 中断优先级分组:一般情况下,系统代码执行过程中,只设置一次中断优先级分组,比如分组2,设置好分组之后一般不会再改变分组。随意改变分组会导致中断管理混乱,程序出现意想不到的执行结果。不同分组方式会影响中断抢占优先级和子优先级范围。定时器中断控制led灯闪烁,通用微控制器(MCU)开发,stm32,嵌入式硬件,单片机,物联网,嵌入式开发

实现原理

摁键检测

EXTI(External interrupt/event controller):外部中断/事件控制器,管理了控制器的 20个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。 EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。

定时器中断控制led灯闪烁,通用微控制器(MCU)开发,stm32,嵌入式硬件,单片机,物联网,嵌入式开发

实现流程大体分为以下三个部分:

  1. 配置GPIO相应引脚:通过按键产生一个电平信号,然后经EXTI处理传入NVIC产生中断的,所以要配置连接按键的GPIO引脚,主要是设置相应的引脚模式为浮空输入
  2. 配置EXTI并映射GPIO引脚:打开相关的时钟,使用GPIO_EXTILineConfig()函数映射中断IO口,然后配置EXIT,包括中断源,触发类型等。
  3. 编写中断服务函数:stm32f10x_it.c中提前根据中断向量表生成了空的中断函数,我们只需要找到对应的中断函数编写中断服务内容即可。

定时器

定时器(Timer)最基本的功能就是定时了,本例我们使用的是通用定时器,它的时钟源经过以下路径计算得到:SYSCLK经过PLL的得到为72M,AHB时钟 =SYSCLK,APB1时钟=72M/2=36M,最终CK_INT的时钟频率倍频两倍为72M. 计数器的最终的频率还需要经过PSC预分频计算才能得到,基本定时器计数过程主要涉及到三个寄存器内容,分别是计数器寄存器(TIMx_CNT)、预分频器寄存器(TIMx_PSC)、自动重载寄存器(TIMx_ARR),这三个寄存器都是 16 位有效数字,即可设置值为 0至 65535。

定时器中断控制led灯闪烁,通用微控制器(MCU)开发,stm32,嵌入式硬件,单片机,物联网,嵌入式开发

定时事件生成时间主要由 TIMx_PSC 和 TIMx_ARR两个寄存器值决定,这个也就是定时器的周期。比如我们需要一个 1s周期的定时器,具体这两个寄存器值该如何设置? 假设,我们先设置 TIMx_ARR寄存器值为 9999,即当 TIMx_CNT从 0开始计算,刚好等于 9999时生成事件,总共计数 10000次,那么如果此时时钟源周期为 100us即可得到刚好 1s的定时周期。 接下来问题就是设置 TIMx_PSC寄存器值使得 CK_CNT 输出为 100us 周期(10000Hz)的时钟。预分频器的输入时钟 CK_PSC为 90MHz,所以设置预分频器值为(9000-1)即可满足。 


嵌入式程序

摁键检测实现LED灯亮灭

  • 中断通用配置
    static void NVIC_Config(void) /* 主要是配置中断源的优先级与打开使能中断通道 */
    {
        NVIC_InitTypeDef NVIC_InitStruct ;
        
        /* 配置中断优先级分组(设置抢占优先级和子优先级的分配),在函数在misc.c */
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1) ;
        
        /* 配置初始化结构体 在misc.h中 */
        /* 配置中断源 在stm32f10x.h中 */
        NVIC_InitStruct.NVIC_IRQChannel = KEY1_EXTI_IRQN ;
        /* 配置抢占优先级 */
        NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1 ;
        /* 配置子优先级 */
        NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0 ;
        /* 使能中断通道 */
        NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ;
        /* 调用初始化函数 */
        NVIC_Init(&NVIC_InitStruct) ;
        
        /* 对key2执行相同操作 */
        NVIC_InitStruct.NVIC_IRQChannel = KEY2_EXTI_IRQN ;
        NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1 ;
        NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1 ;
        NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ;
        NVIC_Init(&NVIC_InitStruct) ;
        
    }
  • 外部中断配置
    void EXTI_Config() /* 主要是连接EXTI与GPIO */
    {
        GPIO_InitTypeDef GPIO_InitStruct ;
        EXTI_InitTypeDef EXTI_InitStruct ;
        
        NVIC_Config();
    
        /* 初始化要与EXTI连接的GPIO */
        /* 开启GPIOA与GPIOC的时钟 */
        RCC_APB2PeriphClockCmd(KEY1_EXTI_GPIO_CLK | KEY2_EXTI_GPIO_CLK, ENABLE) ;
        
        GPIO_InitStruct.GPIO_Pin = KEY1_EXTI_GPIO_PIN ;
        GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING ;
        GPIO_Init(KEY1_EXTI_GPIO_PORT , &GPIO_InitStruct) ;
        
        GPIO_InitStruct.GPIO_Pin = KEY2_EXTI_GPIO_PIN ;
        GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING ;
        GPIO_Init(KEY2_EXTI_GPIO_PORT , &GPIO_InitStruct) ;
        
        /* 初始化EXTI外设 */
        /* EXTI的时钟要设置AFIO寄存器 */
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE) ;
        /* 选择作为EXTI线的GPIO引脚 */
        GPIO_EXTILineConfig( KEY1_GPIO_PORTSOURCE , KEY1_GPIO_PINSOURCE) ;
        /* 配置中断or事件线 */
        EXTI_InitStruct.EXTI_Line = KEY1_EXTI_LINE ;
        /* 使能EXTI线 */
        EXTI_InitStruct.EXTI_LineCmd = ENABLE ;
        /* 配置模式:中断or事件 */
        EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt ;
        /* 配置边沿触发 上升or下降 */
        EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising ;
        EXTI_Init(&EXTI_InitStruct) ;
        
        GPIO_EXTILineConfig( KEY2_GPIO_PORTSOURCE , KEY2_GPIO_PINSOURCE) ;
        EXTI_InitStruct.EXTI_Line = KEY2_EXTI_LINE ;
        EXTI_InitStruct.EXTI_LineCmd = ENABLE ;
        EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt ;
        EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling ;
        EXTI_Init(&EXTI_InitStruct);
    }
    
  • 中断服务函数与主函数
    void EXTI0_IRQHandler(void)
    {
        if(  EXTI_GetITStatus(KEY1_EXTI_LINE)!=RESET)
        {
            LED1_TOGGLE;   //LED1的亮灭状态反转
        }
            
        EXTI_ClearITPendingBit(KEY1_EXTI_LINE);
            
    }
    
    
    void EXTI15_10_IRQHandler(void)
    {
        if(  EXTI_GetITStatus(KEY2_EXTI_LINE)!=RESET)
        {
            LED2_TOGGLE;   //LED2的亮灭状态反转
        }
            
        EXTI_ClearITPendingBit(KEY2_EXTI_LINE);
            
    }
    /
    #include "stm32f10x.h"
    #include "bsp_led.h"
    #include "bsp_key.h"
    
    int main(void)
    { 
        LED_GPIO_Config();
        EXTI_Config();
        
        while(1) 
        {
        }
    }
    
    

定时器实现LED灯闪烁

代码主要包含以下三个部分:

  1. 定时器初始化:TIM3时钟使能,设置TIM3_ARR和TIM3_PSC的值,设置TIM3_DIER允许更新中断,允许TIM3工作,TIM3中断分组设置。
  2. 编写中断服务函数:实现LED灯亮灭。
  3. 主函数:调用相关接口,完成IO口和定时器启动。
//定时器初始化
void TIM3_Int_Init()
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	NVIC_InitTypeDef    NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
	TIM_TimeBaseStructure.TIM_Prescaler =7199;     //设置用来作为TIMx时钟频率除数的预分频值  10Khz的计数频率  
	TIM_TimeBaseStructure.TIM_Period  = 4999;        //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	 计数到5000为500ms
	TIM_TimeBaseStructure.TIM_ClockDivision = 0;    //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位 
	TIM_ITConfig(TIM3, TIM_IT_Update,ENABLE);
	
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);     //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
	TIM_Cmd(TIM3, ENABLE);  //使能TIMx外设							 
}
//定时器中断服务函数
void TIM3_IRQHandler(void)   //TIM3中断
{
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 
		{
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx的中断待处理位:TIM 中断源 
		LED1=!LED1;
		}
}
//主函数
int main(void)
 {	
	delay_init();	    	 //延时函数初始化
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
	LED_Init();		  	//初始化与LED连接的硬件接口
	TIM3_Int_Init();   //10Khz的计数频率,计数到5000为500ms  
   	while(1)
	{
		LED0=!LED0;
		delay_ms(200);		   
	}
}

十六宿舍 原创作品,转载必须标注原文链接。

©2023 Yang Li. All rights reserved.

欢迎关注 『十六宿舍』,大家喜欢的话,给个👍,更多关于嵌入式相关技术的内容持续更新中。文章来源地址https://www.toymoban.com/news/detail-760606.html

到了这里,关于STM32+摁键与定时器实现Led灯控制(中断)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32使用定时器更新中断使LED亮灭交替1S闪烁

    在向上计数模式中,计数器从0计数到自动加载值(TIMx_ARR(自动装载寄存器)计数器的内容),然后重新从0开始计数并且产生一个计数器溢出事件。每次计数器溢出时可以产生更新事件,当使能了更新中断以后,计数器溢出时则产生更新中断。 上图源自一位b站up主的定时器中断

    2024年02月05日
    浏览(56)
  • 无缘蜂鸣器——stm32定时器PWM实现控制发出“哆瑞咪发…“七个音及简单音乐

    一、有缘蜂鸣器和无缘蜂鸣器 所谓的 有源蜂鸣器 是指蜂鸣器内部内置振荡电路,一通电就能响。但发生频率固定,音色单一; 无源蜂鸣 器内部不含振荡源,内部结构相当于电磁场扬声器,可以通过给他输出一定频率的信号才能发声。人耳能听到的频率范围在20Hz–20kHz之间

    2024年02月02日
    浏览(96)
  • STM32 HAL库 通用定时器介绍及相关应用例程 定时器中断 输出PWM (点亮LED呼吸灯、输出PWM、输入捕获) CubeMX

    (部分图引自于ATK) 前情提要(基本定时器) 点此进入 通用定时器类别 通用定时器和基本定时器相比大致的工作方式是相似的,不过通用定时器比基本定时器多了一些很好用的功能,比如: 外部输入捕获 输出比较 输出PWM 时钟源 CubeMX为我们提供了配置时钟的非常方便的工

    2024年04月15日
    浏览(85)
  • 搭建stm32电机控制代码框架(二)——Stm32CubeMx配置定时器

    搭建了基础的环境,配置了一个简单的工程后,CubeMx的基本操作就会了。然后基于这个操作往下推进,开始对关键模块定时器的攻略,这个部分需要先熟悉一下Stm32的定时器基本原理。 《STM32参考手册》中仅对定时器的介绍就已经占了100多页,这里就不一一展开,挑一些关键

    2024年02月06日
    浏览(80)
  • 用STM32定时器中断产生PWM控制步进电机

    控制步进电机可以使用PWM、定时器中断、延时,这里用的就是 定时器中断来让它转动。 板子用的是正点原子的STM32F103 mini板,驱动器是DM420(DM420驱动器资料),用开关电源供电,电机就是普通的42步进电机,步距角为1.8°,虽然按照图片来看它是个蠕动泵。如下图 PUL+——PB0,

    2023年04月09日
    浏览(51)
  • 【STM32笔记】STM32的定时器开发基础(二)(基于STM32CubeMX实现定时器中断)

      传统STM32外部中断 的设计步骤:  (1)将GPIO初始化为输入端口。  (2)配置相关I/O引脚与中断线的映射关系。  (3)设置该I/O引脚对印的中断触发条件。  (4)配置NVIC,并使能中断。  (5)编写中断服务函数。   基于STM32CubeMX的外部中断 设计步骤  (1)在STM3

    2024年02月20日
    浏览(62)
  • 51-定时器与按键控制LED流水灯模式&定时器时钟

    按键(以独立按键为例)控制LED流水灯模式: 在按键控制LED流水灯模式中,如果仅仅简单的把独立按键与LED流水灯拼接起来,则会出现一些问题:在LED流水灯的代码中会有长时间的Delay,此时按键检测就会很不灵敏:按下时不灵敏,需要一些时间才能被检测到,按下后再松手时

    2024年02月08日
    浏览(46)
  • STM32控制步进电机:基于HAL库定时器中断的闭环步进电机驱动+精准控制脉冲数

    该篇文章中用到的步进电机闭环驱动器为Emm42_V4.0步进电机闭环驱动器。该闭环驱动器自带FOC矢量闭环控制算法,能实现力矩、速度、位置三环控制。 如下图所示,该42步进闭环电机驱动器的A+、A-、B+、B-连接步进电机,通过右侧的使能、脉冲、方向端对步进电机进行驱动控制

    2024年02月01日
    浏览(55)
  • STM32学习笔记(4) 高级定时器-两路互补的PWM输出(带死区和刹车控制)

    目录 1.实验目的 2.实验效果 3.理论部分 3.1时钟源 3.2时基单元 3.3输入捕获 4.程序流程 4.1GPIO初始化结构体 4.2时基初始化结构体 4.3输出比较结构体 4.4刹车和死区结构体的初始化 5.程序源码 使用高级定时器,输出两路互补的PWM输出,需要有带死区和不带死区两种情况 图1:不带

    2024年02月13日
    浏览(44)
  • STM32定时器实现红外接收与解码

             红外遥控是一种比较常用的通讯方式,目前红外遥控的编码方式中,应用比较广泛的是NEC协议。NEC协议的特点如下: 载波频率为 38KHz 8 位地址和 8位指令长度 地址和命令2次传输(确保可靠性) PWM 脉冲位置调制,以发射红外载波的占空比代表“0”和“1”    

    2024年02月03日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包