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

中断作为单片机开发必须掌握的内容,它能够在不搭载操作系统的情况下让我们体验多任务处理的快感,保证了高优先级任务的实时性,同时系统中断也能够提供给用户在核心发生错误之后进行处理的机会。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为500msTIM_TimeBaseStructure.TIM_ClockDivision = 0;    //设置时钟分割:TDTS = Tck_timTIM_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);// 设置中断优先级分组2LED_Init();		  	//初始化与LED连接的硬件接口TIM3_Int_Init();   //10Khz的计数频率,计数到5000为500ms  while(1){LED0=!LED0;delay_ms(200);		   }
}

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

©2023 Yang Li. All rights reserved.

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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux中常见的权限问题

目录 前言1. 目录权限2. umask3. 粘滞位结语 前言 在了解完上一篇文章 Linux权限的理解与操作 之后,还有一些比较常见的权限问题需要我们去了解。其中包括目录的权限,umask 以及 粘滞位的使用。 1. 目录权限 问题一:进入一个目录&#xff0…

QT QGLWidge

QGLWidget 学习 前言1.四边形 QGLWidget 2*32. 正方体 1*2前言 1.四边形 QGLWidget 2*3 坐标 效果 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕和深度缓存glLoadIdentity(); //重置当前的模型观察矩阵glTranslate…

2001-2022年全国290+个地级市高铁开通数据

2001-2022年全国290个地级市高铁开通数据 1、时间:2001-2022年 2、范围:298地级市(293地级市数(其中莱芜市2019年撤市设区)4直辖市数 ) 3、来源:国家铁路局、铁路客货运输专刊及相关统计 国…

LNMP架构部署Discuz论坛系统

文章目录 LNMP架构&部署Discuz论坛系统部署LNMP架构环境前期准备安装Nginx安装mariadb安装php配置nginx 部署Discuz论坛系统下载Discuz论坛系统代码包部署Discuz论坛系统配置虚拟主机安装Discuz论坛访问站点尝试注册一个账号 LNMP架构&部署Discuz论坛系统 部署LNMP架构…

深度学习 | Pytorch深度学习实践 (Chapter 10、11 CNN)

十、CNN 卷积神经网络 基础篇 首先引入 —— 二维卷积:卷积层保留原空间信息关键:判断输入输出的维度大小特征提取:卷积层、下采样分类器:全连接 引例:RGB图像(栅格图像) 首先,老师…

Redis常见问题的解决方案(缓存穿透/缓存击穿/缓存雪崩/数据库缓存数据不一致)

Redis解决缓存数据库不一致的方案 用 先 操作数据库 再 操作缓存 的策略来实现缓存数据库数据一致具体做法是 更新数据库数据然后删除缓存 虽然还是会有线程安全问题 比如 假设此时缓存刚好失效了 线程1 查询缓存失败 从数据库读取了旧数据 还没写入缓存的时候 被调度到 线程…

C++-json(2)-unsigned char-unsigned char*-memcpy-strcpy-sizeof-strlen

1.类型转换: //1.赋值一个不知道长度的字符串unsigned char s[] "kobe8llJfFwFSPiy"; //1.用一个字符串初始化变量 unsigned int s_length strlen((char*)s); //2.获取字符串长度//2.字符串里有双引号"" 需要…

PAM从入门到精通(十九)

接前一篇文章:PAM从入门到精通(十八) 本文参考: 《The Linux-PAM Application Developers Guide》 PAM 的应用开发和内部实现源码分析 先再来重温一下PAM系统架构: 更加形象的形式: 六、整体流程示例 2.…

函数栈帧的创建和销毁

目录 引言: 1,函数栈帧的概念 2,函数栈帧的创建与销毁过程 2.1预备知识 2.2main函数栈帧的创建 2.2.1push ebp 2.2.2mov ebp,esp 2.2.3sub esp,0E4h 2.2.4push ebx ;push esi;push edi 2…

C++STL的迭代器(iterator)

一、定义 迭代器是一种检查容器内元素并且遍历容器内元素的数据类型。 【引用自:C迭代器(iterator)_c iterator_NiUoW的博客-CSDN博客】迭代器是一个变量,相当于容器和操纵容器的算法之间的中介。C更趋向于使用迭代器而不是数组下…

day01_matplotlib_demo

文章目录 折线图plot多个绘图区绘制数学函数图像散点图scatter柱状图bar直方图histogram饼图pie总结 折线图plot import matplotlib.pyplot as pltplt.figure(figsize(15, 6), dpi80) plt.plot([1, 0, 9], [4, 5, 6]) plt.show()### 展现一周天气温度情况 # 创建画布 plt.figu…

【C++】引用’‘的深入解析

💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤 📃个人主页 :阿然成长日记 …

C语言学习系列->动态内存管理

文章目录 前言概述🚩malloc and free🔜malloc🔜free 🚩calloc and realloc🔜calloc🔜realloc 前言 要想学好数据结构,在C语言学习过程中就需要把指针、结构体和动态内存管理学好。在前面的文章&…

makesense在线yolov5标注

文章目录 一、创建图片文件夹和label.txt二、在线标注数据 参考文章博主:风吹落叶花飘荡 一、创建图片文件夹和label.txt 创建一个放置图片的文件夹images,存放需要标注的图片(图片最好重命名为1,2,3…避免后面混淆) 创建label.t…

C++标准模板(STL)- 类型支持 (运行时类型识别,type_info )

运行时类型识别 定义于头文件 <typeinfo> 含有某个类型的信息&#xff0c;由实现生成。​​这是 typeid 运算符所返回的类。 std::type_info 定义于头文件 <typeinfo> class type_info; 类 type_info 保有一个类型的实现指定信息&#xff0c;包括类型的名称和…

我单方面宣布:腾讯云服务器优惠价格(双11特价)

2023腾讯云双十一服务器优惠价格表多少钱一年&#xff1f;轻量服务器2核2G3M、2核2G4M、2核4G5M、4核8G12M、8核16G18M、16核32G28M和云服务器CVM标准型S5实例优惠价格&#xff0c;腾讯云百科今年双11服务器价格会在当前的价格基础上享受个9折优惠&#xff0c;可领券 https://c…

正则表达式之学习笔记

正则表达式学习笔记 一、概念二、正则表达式组成三、常见的正则表达式3.1 .匹配任意字符3.2 * 匹配前一个字符的0个或多个实例3.3 ^ 匹配输入字符串的开头3.4 $ 匹配行尾3.5 [] 匹配字符集合\<\> 精确匹配符号 一、概念 正则表达式是由一系列特殊字符组成的字符串&#…

Linux---(三)基本指令大全

前提引入&#xff1a;历史上先出现的键盘还是鼠标&#xff1f; 答案&#xff1a;键盘 ✨所以刚开始的时候绝对没有图形化界面&#xff0c;因此操作系统刚开始兴起的时候绝对没有图形化界面&#xff0c;因为当时没有鼠标。 ✨因为没有图形化界面&#xff0c;只有键盘&#xff0c…

vue项目编译、打包、部署服务器运行

在vue项目执行npm run build,生成dis目录 打包dis上传 安装npm install -g http-server或者apt install node-http-server 运行http-server

postman自动化运行接口测试用例

做过接口测试的人&#xff0c;应该都知道postman &#xff0c;我们在日常的时候都可以利用postman做接口测试&#xff0c;我们可以把接口的case保存下来在collection里面&#xff0c;那么可能会有这样的需求&#xff0c;我们怎么把collection的用例放到jenkins中定时执行呢&…