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

news2024/10/7 8:30:52

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

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

中断概念

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

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

  • 异常:异常是中断的一种类型,其中断源是芯片内部,中断原因为比如执行了未定义指令、算术溢出、除零运算等发生在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这几个寄存器的值保存到栈中。

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

实现原理

摁键检测

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

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

  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。

定时事件生成时间主要由 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.

欢迎关注 『十六宿舍』,大家喜欢的话,给个👍,更多关于嵌入式相关技术的内容持续更新中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1121419.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot +JdbcTemplate+VUE 实现在线输入SQL语句返回数据库结果

文章目录 前言框架选型SpringBoot 结合JdbcTemplate简单示例SpringBoot 结合JdbcTemplate实现运行SQL处理思路后端处理前端处理 前言 想起来要做这个功能是因为我们公司的预生产环境和生产环境如果想要连接数据库都需要登录堡垒机,然后再通过堡垒机进行跳转到对应定…

1024 蓝屏漏洞攻防战(第十九课)

1024 蓝屏漏洞攻防战(第十九课) 思维导图 一 永恒之蓝的介绍 漏洞为外界所知源于勒索病毒的爆发,该病毒利用NSA(美国国家安全局)泄露的网络攻击工具 永恒之蓝( EternalBlue )改造而成,漏洞通过TCP的445和139端口,利用SMB远程代码执行漏洞,攻击者可以在目标系统上执行…

详解对于ReadView 机制如何判断当前事务能够看见

ReadView 机制就是用来判断当前事务能够看见哪些版本的,一个 ReadView 主要包含如下几个部分: m_ids:生成 ReadView 时有哪些事务在执行但是还没提交的(称为 “活跃事务”),这些活跃事务的 id 就存在这个字…

for、while、do While、for in、forEach、map、reduce、every、some、filter的使用

for、while、do While、for in、forEach、map、reduce、every、some、filter的使用 for let arr [2, 4, 6, 56, 7, 88];//for for (let i 0; i < arr.length; i) {console.log(i : arr[i]) //0:2 1:4 2:6 3:56 4:7 5:88 }普通的for循环可以用数组的索引来访问或者修改…

AI篇-如何用AI辅助对图片进行鉴赏

前言 目录 前言 一、观众侧鉴赏图片 方法1&#xff1a;直接将图片发给文心一言&#xff0c;让文心一言分析。 方法2&#xff08;正确方法&#xff09;&#xff1a;将图片简单介绍并把图片发给文心一言&#xff0c;让文心一言分析。 二、作者介绍图片 方法&#xff08;正…

智慧公厕管理系统:让公厕更智能、更高效的利器

公厕是城市基础设施的重要组成部分&#xff0c;然而&#xff0c;由于管理不善和公共卫生意识的薄弱&#xff0c;公厕经常面临着脏乱差的问题&#xff0c;令人不愿意使用。为了改善公厕管理的现状&#xff0c;智慧公厕管理系统的出现成为了一种创新的解决方案。 智慧公厕管理系…

大模型微调发展-学习调研总结

模型微调前言 https://blog.csdn.net/weixin_39663060/article/details/130724730 针对于小公司&#xff0c;如何能够利用开源的大模型&#xff0c;在自己的数据上继续训练&#xff0c;从而应用于自己的业务场景&#xff1f;或低成本的方法微调大模型。 目前主流的方法包括201…

小程序之微信登录授权(6)

⭐⭐ 小程序专栏&#xff1a;小程序开发专栏 ⭐⭐ 个人主页&#xff1a;个人主页 目录 一.了解微信授权登录 小程序登录授权基本原理&#xff1a; 二.微信授权登录演示 三.微信授权与后端的交互 3.1后台代码&#xff1a; 3.2 前端代码&#xff1a; 四.微信退出 五.微信表情包…

DELM深度极限学习机回归预测研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

自然语言处理---Self Attention自注意力机制

Self-attention介绍 Self-attention是一种特殊的attention&#xff0c;是应用在transformer中最重要的结构之一。attention机制&#xff0c;它能够帮助找到子序列和全局的attention的关系&#xff0c;也就是找到权重值wi。Self-attention相对于attention的变化&#xff0c;其实…

项目总结-商品购买流程

&#xff08;1&#xff09;添加购物车 Controller&#xff1a; CartService&#xff1a; 实现类&#xff1a; CartDetail detaildao.queryByCdid(cid,gds.getId()); CartDao&#xff1a; //获取详情对象Select("select * from t_cartdetail where cid#{cid} and gid#{gid…

buu第五页 wp

[RootersCTF2019]babyWeb 预期解 一眼就是sql注入&#xff0c;发现过滤了 UNION SLEEP " OR - BENCHMARK盲注没法用了&#xff0c;因为union被过滤&#xff0c;堆叠注入也不考虑&#xff0c;发现报错有回显&#xff0c;尝试报错注入。 尝试&#xff1a; 1||(updatex…

ubuntu20.04下安装nc

前言 nc在网络渗透测试中非常好用&#xff0c;这里的主要记一下Ubuntu20.04中nc的安装 编译安装 第一种方式是自己编译安装&#xff0c;先下载安装包 nc.zip wget http://sourceforge.net/projects/netcat/files/netcat/0.7.1/netcat-0.7.1.tar.gz/download -O netcat-0.7.…

anyproxy 的安装和抓包使用

简介 AnyProxy是阿里开发的开源的代理服务器&#xff0c;主要特性包括&#xff1a; 基于Node.js&#xff0c;开放二次开发能力&#xff0c;允许自定义请求处理逻辑支持Https的解析提供GUI界面&#xff0c;用以观察请求 安装运行Anyproxy 首先需要电脑由安装 node&#xff0…

H3C SecParh堡垒机 data_provider.php 远程命令执行漏洞

构造poc执行远程命令&#xff1a; /audit/data_provider.php?ds_y2019&ds_m04&ds_d02&ds_hour09&ds_min40&server_cond&service$(id)&identity_cond&query_typeall&formatjson&browsetrue漏洞证明&#xff1a; 文笔生疏&#xff0c…

【大模型应用开发教程】02_LangChain介绍

LangChain介绍 什么是 LangChain1. 模型输入/输出2. 数据连接3. 链&#xff08;Chain&#xff09;4. 记忆&#xff08;Meomory&#xff09;5. 代理&#xff08;Agents&#xff09;6.回调&#xff08;Callback&#xff09;在哪里传入回调 ?你想在什么时候使用这些东西呢&#x…

1024常玩到的漏洞(第十六课)

1024常玩到的两个漏洞(第十六课) 漏洞扫描工具 1024渗透OpenVas扫描工具使用(第十四课)-CSDN博客 流程 一 ms12-020漏洞分析 MS12-020漏洞是一种远程桌面协议(RDP)漏洞。在攻击者利用该漏洞之前,它需要将攻击者的计算机连接到受害者的计算机上。攻击者可以通过向受害者计算…

跟着NatureMetabolism学作图:R语言ggplot2转录组差异表达火山图

论文 Independent phenotypic plasticity axes define distinct obesity sub-types https://www.nature.com/articles/s42255-022-00629-2#Sec15 s42255-022-00629-2.pdf 论文中没有公开代码&#xff0c;但是所有作图数据都公开了&#xff0c;我们可以试着用论文中提供的数据…

Linux进程与线程的内核实现

进程描述符task_struct 进程描述符&#xff08;struct task_struct&#xff09;pid与tgid进程id编号分配规则内存管理mm_struct进程与文件,文件系统 进程,线程创建的本质 clone函数原型线程创建的实现进程创建的实现 总结 进程描述符task_struct 进程描述符&#xff08;st…

自动驾驶的商业应用和市场前景

自动驾驶技术已经成为了交通运输领域的一项重要创新。它不仅在改善交通安全性和效率方面具有巨大潜力&#xff0c;还为各种商业应用提供了新的机会。本文将探讨自动驾驶在交通运输中的潜力&#xff0c;自动驾驶汽车的制造商和技术公司&#xff0c;以及自动驾驶的商业模式和市场…