EXTI中断以及系统滴答定时器SysTick的配置和使用

news2024/10/7 10:13:43

前言

EXTI中断来判断按键按下

EXTI即外部中断/事件控制器,总共支持19个中断/事件请求。每一条中断线都有独立的使能和产生中断后的标志位。
在这里插入图片描述
上图可见,中断/时间线0-15,总共16条线分配给了IO,通过设置AFIO的AFIO_EXTICR1、AFIO_EXTICR2、AFIO_EXTICR3、AFIO_EXTICR4这四个寄存器来配置要选择哪一组pin作为外部中断输入,比如 AFIO_EXTICR1这个寄存器里面分为了:
在这里插入图片描述
在这里插入图片描述
意思是,如果我想设置为PA0,那就把 AFIO_EXTICR1 的0-3位设置为0000 ,如果想要设置PC3,那就设置AFIO_EXTICR1 的12-15位 为0010,以此类推,每个AFIO_EXTICRx寄存器控制的是4组Pin,AFIO_EXTICR1控制的是Pin0-Pin3,AFIO_EXTICR2控制的是Pin4-Pin7,AFIO_EXTICR3控制的是Pin8-P11,AFIO_EXTICR4控制的是Pin12-Pin15 。需要设置不同的Port就是根据当前要操作的Pin找到需要操作哪个AFIO_EXTICRx寄存器,然后根据需要设置的Port(即PA或PB或PC等…)把相应EXTIx的值填好。

写到这里心里面产生一个疑问,比如我只需要设置PA0为EXTI0输入线的输入信号,那么在配置的时候其他输入线应该也是有初值在的。后面应该是需要通过关闭不需要的输入信号的中断来实现只响应PA0的变化中断。

我这边新建了一个bsp_exti.c文件,内容如下:

#include "bsp_exti.h"

static void EXTI_NVIC_Config(void)
{
    NVIC_InitTypeDef NVIC_InitStruct;

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

    NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init(&NVIC_InitStruct);
}

void exti_key_gpio_init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    EXTI_InitTypeDef EXTI_InitStruct;

    //配置中断优先级
    EXTI_NVIC_Config();
    
    RCC_APB2PeriphClockCmd(GPIO_KEY_RCC,ENABLE);

    GPIO_InitStruct.GPIO_Pin = EXTI_GPIO_PIN;
    GPIO_InitStruct.GPIO_Mode = EXTI_GPIO_MODE;

    GPIO_Init(EXTI_GPIO,&GPIO_InitStruct);

    //初始化EXTI
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);                //GPIO用作EXTI中断,必须打开AFIO时钟
    GPIO_EXTILineConfig(EXTI_SOURCE,EXTI_PIN_SOURCE);                  //选择的输入线为 GPIOA.Pin15

    EXTI_InitStruct.EXTI_Line = EXTI_LINE;
    EXTI_InitStruct.EXTI_Mode = EXTI_MODE;
    EXTI_InitStruct.EXTI_Trigger = EXTI_TRI;
    EXTI_InitStruct.EXTI_LineCmd = ENABLE;

    EXTI_Init(&EXTI_InitStruct);
}

头文件如下:

#ifndef __BSP_EXTI_H
#define __BSP_EXTI_H

#include "stm32f10x.h"

#define     GPIO_KEY_RCC	RCC_APB2Periph_GPIOA
#define     EXTI_GPIO       GPIOA
#define     EXTI_GPIO_PIN   GPIO_Pin_15
#define     EXTI_GPIO_MODE  GPIO_Mode_IPU

#define     EXTI_SOURCE     GPIO_PortSourceGPIOA
#define     EXTI_PIN_SOURCE GPIO_PinSource15

#define     EXTI_LINE       EXTI_Line15
#define     EXTI_MODE       EXTI_Mode_Interrupt
#define     EXTI_TRI        EXTI_Trigger_Falling

void exti_key_gpio_init(void);


#endif

EXTI_NVIC_Config();这个函数负责配置中断优先级,因为这个函数是专门新建给EXTI用的,所以在EXTI的c文件里面静态声明一下,不允许其他地方调用。
首先就是像配置IO一样声明一个结构体变量,用于传参进去NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);函数,这个函数的参就是NVIC_InitTypeDef类型的结构体的地址,所以新建一个结构体变量,名字为NVIC_InitStruct。配置响应的参数给这个结构体变量。

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup); 这个函数是用于给中断分组,在NVIC_IPRx这个中断优先级寄存器中,高四位代表主优先级和子优先级,但是4个位中哪些位是代表主优先级哪些位代表子优先级,这个又是通过分组来控制的。如果选择的分组是0,那么NVIC_IPRx的高四位中,四位全是代表子优先级,主优先级默认是0,所以别的中断要跟EXTI中断比较的话,只有主优先级为0且子优先级的数字比EXTI的子优先级数字小的才能产生嵌套中断(主优先级和子优先级都相同的情况下,比较两个中断的序号,序号越前的优先级越高,但是这种情况几率很小,这里不讨论)。

这里我配置的NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); 即给EXTI中断分组到分组1去,那么NVIC_IPRx寄存器的高四位中的最高位,即第七位为0或为1,控制的是EXTI的主优先级为0或者1,NVIC_IPRx的第4-6位控制是子优先级,子优先级有8级 (0-7)。

NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn; 这个配置的是EXTI产生的中断通道是 EXTI10-15通道(因为我用的按键是PA15,所以我选择的输入源就是PA15,输入线即为EXTI15,库函数规定输入线为10-15时,NVIC_IRQChannel的值要设置为EXTI15_10_IRQn)

NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
这两句则是设置主优先级为1 子优先级为1

NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
开对应Channel的NVIC中断

然后再调用初始化函数 把结构体变量的地址作为形参传进去NVIC_Init(&NVIC_InitStruct);

NVIC初始化函数就写好了,接下来就是初始化按键 初始化EXTI,并且调用EXTI_NVIC_Config();函数配置好EXTI中断的优先级和通道并开启中断响应……

  • 第一步–配置按键。先开按键所处的Port的所处的APB时钟线时钟。然后配置相应的Pin为输入上拉模式,最后调用GPIO_Init(EXTI_GPIO,&GPIO_InitStruct);函数,初始化相应的Pin。

  • 第二步–开始初始化EXTI

    首先配置时钟。调用RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);开启时钟,因为选择IO作为EXTI输入,需要用到AFIO,所以这里开时钟开的是AFIO的时钟。

    GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);

    这个函数第一个参数:GPIO_PortSource: selects the GPIO port to be used as source for EXTI lines.This parameter can be GPIO_PortSourceGPIOx where x can be (A…G).
    也就是说这里需要填选择哪一个Port作为EXTI输入

    第二个参数:GPIO_PinSource: specifies the EXTI line to be configured.This parameter can be GPIO_PinSourcex where x can be (0…15).
    也就是这里要填哪一个Pin作为EXTI输入

    其实这个GPIO_EXTILineConfig()函数就是根据我们传入的参,判断之后帮我们操作了AFIO_EXTICRx寄存器。

  • 第三步–配置EXTI结构体变量,调用初始化EXTI函数
    EXTI_Line : 选择是哪一天EXTI输入线,我这里是PA15,所以这里要填EXTI_Line15
    EXTI_Mode:选择输入信号产生符合要求的变化之后,是发生中断还是事件
    EXTI_Trigger:选择输入信号发生什么动作会产生中断/事件,可以选择上升沿、下降沿以及双边沿
    EXTI_LineCmd:选择是否使能EXTI产生中断/事件
    调用EXTI_Init(&EXTI_InitStruct);即可初始化EXTI中断。

在主函数中初始化EXTI按键中断:

unsigned char i=0;

int main(void)
{
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
	
	led_gpio_init();
	
	exti_key_gpio_init();
	
	while(1)
	{

	}
}

在中断服务函数的.c文件中:

void EXTI15_10_IRQHandler(void)
{
    if(EXTI_GetITStatus(EXTI_Line15) != RESET)          //确定产生了中断
    {

        GPIOA->ODR ^= GPIO_Pin_8;

    }
    EXTI_ClearITPendingBit(EXTI_Line15);
}

通过调用EXTI_GetITStatus();得到返回值来判断相应的Pin是否产生中断来判断按键是否按下。
EXTI_GetITStatus()这个函数其实就是根据我们传入的参(我们选择的EXTI线-EXTI15)。
首先判断一下我们是否打开了EXTI14这条EXTI线的中断,主要就是判断EXTI_IMR这个寄存器的对应位,这个寄存器的0-19位代表了EXTI0-19位是否使能中断。(如果设置为符合要求就产生事件,那么这函数判断的是EXTI_EMR这个寄存器的0-19位。)

然后再根据 挂起寄存器(EXTI_PR)的0-19位来判断相应的EXTI线是否产生了触发请求。

最后判断一下 如果当前的EXTI线允许中断/事件,且挂起寄存器里面的对应位被置1,即产生了触发请求。两者同时成立,那么就会产生中断/事件,我这里配置的是产生中断,所以当我按下按键之后,就会产生一个中断,并调用EXTI15_10_IRQHandler这个服务函数。值得注意的是EXTI15_10_IRQHandler()这个函数名不是乱起的,跟51单片机的中断入口的序号一样,这个名字是写在启动文件里面的,如果这个名字没写对,那么MCU会调用库里面弱定义的EXTI15_10_IRQHandler()函数,内容是while(1),即永远停在这个服务函数里面。

如果是EXTI8,即PA8、PB8……作为输入信号,那么中断服务函数需要用EXTI9_5_IRQHandler。
那么如果我们PA7、PA9同时作为输入信号,按下任意一个按键都能进入EXTI9_5_IRQHandler这个服务函数,那么就需要额外判断一下输入源,根据GPIO->IDR这个寄存器判断是哪个按键按下了,然后再执行相应的操作。

以上是EXTI中断的方法。

滴答定时器产生定时中断

SysTick—系统定时器是属于 CM3 内核中的一个外设,内嵌在 NVIC 中。系统定时器是一个 24bit
的向下递减的计数器,计数器每计数一次的时间为 1/SYSCLK,一般我们设置系统时钟 SYSCLK
等于 72M。当当前数值寄存器的值递减到 0 的时候,系统定时器就产生一次中断,以此循环往
复。

因为 SysTick 是属于 CM3 内核的外设,所以所有基于 CM3 内核的单片机都具有这个系统定时器,
使得软件在 CM3 单片机中可以很容易的移植。系统定时器一般用于操作系统,用于产生时基,维
持操作系统的心跳。

SysTick—系统定时器有 4 个寄存器,简要介绍如下。在使用 SysTick 产生定时的时候,只需要配
置前三个寄存器,最后一个校准寄存器不需要使用。
在这里插入图片描述

因为是内核里面的外设,配置的东西比较少,赋好值之后使能中断、使能定时器之后就可以产生中断。

我这里是新建了一个bsp_systick.c文件,内容如下:

#include "bsp_systick.h"


uint32_t delay_time=0;




void systick_init(uint32_t ms)
{
    if(SysTick_Config(ms))              //SysTick_Config(uint32_t ticks)  这个函数回返回一个uint32_t类型的变量 如果为1:说明滴答定时器的重载寄存器赋的初值太大 进入报错处理 为0说明正常
    {                                   //uint32_t ticks 这个形参传入的就是为重载寄存器赋的初值 范围是0-2^24(24位寄存器) 
        while (1)                       //函数的作用就是初始化系统的滴答定时器,并且开启中断,时间就按照传入的值来定
        {
            ;
        }
    }
}


void delay_xms(uint32_t xms)
{
    delay_time = xms;

    while (delay_time != 0)
    {
        ;
    } 
}


void delay_time_sub(void)
{
    if(delay_time > 0)
    {
        delay_time--;
    }
}

因为时钟输入是72M,那么计数一次的时间就是1/72000000,如果我想要计时10ms,则是需要填720000。公式是 计数值/频率(Hz)=时间(s)。那么就是计数值=时间x频率=0.01(秒)x72000000Hz=720000次。

所以在main函数初始化时就规定了,产生一次系统定时器中断的时间就是10ms,如果需要改,那么重新改变SysTick_Config()的形参(LOAD 重载寄存器的值)就可以改变产生一次中断的时间间隔。

main函数如下:

#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_systick.h"

unsigned char i=0;

int main(void)
{
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);

	led_gpio_init();
	
	systick_init(720000);		//10ms中断一次
	
	while(1)
	{
		for(i=0;i<2;i++)
		{
			delay_xms(100);
			GPIO_ResetBits(GPIOA,GPIO_Pin_8);
			delay_xms(100);
			GPIO_SetBits(GPIOA,GPIO_Pin_8);
		}
		SysTick -> CTRL &= ~SysTick_CTRL_ENABLE_Msk;
	}
}

执行效果就是 PA8这里的一颗红色LED每隔一秒切换一下状态,亮灭两次之后,长灭。(灯是低电平点亮)
是因为我把滴答定时器关掉了,所以灯在第二次灭之后就不在点亮了。
如果再想把滴答定时器打开,那么就SysTick -> CTRL |= SysTick_CTRL_ENABLE_Msk; 就可以重新打开滴答定时器了

额外说下这个滴答定时器配置函数,这个函数是系统写好的,传入的参就是重载寄存器的数值,直接影响到中断一次的间隔。

static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{ 
  if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            /* Reload value impossible */
                                                               
  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;      /* set reload register */
  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /* set Priority for Cortex-M0 System Interrupts */
  SysTick->VAL   = 0;                                          /* Load the SysTick Counter Value */
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | 
                   SysTick_CTRL_TICKINT_Msk   | 
                   SysTick_CTRL_ENABLE_Msk;                    /* Enable SysTick IRQ and SysTick Timer */
  return (0);                                                  /* Function successful */
}

查了手册:
SysTick_CTRL_TICKINT_Msk 这个位代表的是 systick下数到0之后会不会产生异常请求,如果这个位为1就会产生异常请求,然后进入服务函数。
SysTick_CTRL_ENABLE_Msk 这个位代表的是 是否开启systick定时器

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

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

相关文章

内核调试之Kdump的原理及配置

术语介绍生产内核&#xff08;production kernel&#xff09;&#xff1a;产品或者线上服务器当前运行的内核。捕获内核&#xff08;capture kernel&#xff09;&#xff1a;系统崩溃时&#xff0c;使用kexec启动的内核&#xff0c;该内核用于捕获生产内核当前内存中的运行状态…

【windows terminal】普通用户免密ssh登录阿里云linux服务器

创建用户 [rootxxx~]# adduser linuxcool创建密钥 使用如下命令创建密钥 [rootxxx~]# ssh-keygen -t rsa创建的密钥路径 Your identification has been saved in /root/.ssh/id_rsa. Your public key has been saved in /root/.ssh/id_rsa.pub. 将公钥导入authorized_keys&…

《Go 并发数据结构和算法实践》学习笔记 Day 2

系列文章目录 这是本周期内系列打卡文章的所有文章的目录 《Go 并发数据结构和算法实践》学习笔记 Day 1_jahentao的博客-CSDN博客 文章目录系列文章目录前言一、Skiplist是什么&#xff1f;(What)Skiplist的基本接口查找元素插入元素删除元素二、并行化改造1.并发查找2.并发删…

struts漏洞总结

目录 概述 struts2漏洞 S2-001 漏洞原理 复现 struts2漏洞 S2-005 原理 分析一下003 复现 struts2漏洞s2-007 struts2漏洞 S2-008 原理 复现 struts2漏洞 S2-009 原理 复现 struts2漏洞 S2-012 原理 复现 struts2漏洞 S2-013 原理 复现 struts2漏洞 S2-0…

闭包中的内存泄漏

一.闭包的内存泄漏 1.为什么有些AO对象就不会被销毁&#xff1f; 1&#xff09;普通函数中 上述代码在执行foo函数的时候&#xff0c;内存中的过程。 执行完foo函数之后&#xff0c;foo的函数上下文被销毁了&#xff0c;那么就不会指向foo的AO对象了。 那么AO对象也会被销毁…

格创东智蝉联入选工信部工业互联网APP优秀解决方案名单

1月13日&#xff0c;工信部发布《2022年工业互联网APP优秀解决方案名单公示》&#xff0c;由格创东智研发的 “基于深度学习技术的 AI智能检测&#xff08;天枢AI&#xff09;APP 应用解决方案”、“面向泛半导体行业的设备自动控制&#xff08;EAP&#xff09;APP解决方案”入…

一文详解SPI通信原理

首先我们先了解一下单工、半双工、全双工是什么概念 概念:(Serial Peripheral Interface,串行外设接口),是一种全双工协议的外设总线&#xff0c;同步串行通信 单工:电视 半双工:呼叫机 全双工:电话 SPI是单主设备&#xff08;Single Master&#xff09;通信协议&#xff…

商业智能 BI 赋能年底的财务分析

职场中总有些不能说的秘密&#xff0c;比如月底和年底的时候&#xff0c;千万不要去惹财务&#xff01;尤其是年底&#xff0c;财务部门需要统计分析一整年的费用支出和经营收入&#xff0c;各种结算分析&#xff0c;还有新一年的预算&#xff0c;数据量庞大、业务系统分散、报…

【SpringCloud14】SpringCloud Bus消息总线

1.概述&#xff08;对于Config的加深和扩充&#xff09; 1.1 分布式自动刷新配置功能 SpringCloud Bus配合SpringCloud Config使用可以实现配置的动态刷新 Spring Cloud Bus是用来将分布式系统的节点与轻量级消息系统链接起来的框架&#xff0c;它整合了Java的事件处理机制和…

Web(八)

XML概念&#xff1a;Extensible Markup Language 可扩展标记语言可扩展&#xff1a;标签都是自定义的。 <user> <student>功能* 存储数据1. 配置文件2. 在网络中传输xml与html的区别1. xml标签都是自定义的&#xff0c;html标签是预定义。2. xml的语法严格&#xf…

使用 npm 包

小程序对 npm 的支持与限制 目前&#xff0c;小程序中已经支持使用npm 安装第三方包&#xff0c;从而来提高小程序的开发效率。但是&#xff0c;在小程序中使用npm包有如下3个限制: ① 不支持 依赖于Node.js内置库 的包 ② 不支持 依赖于浏览器内置对象 的包 ③不支持 依赖于C插…

代码签名证书是如何进行验证工作的

代码签名证书是通过对代码的数字签名来标识软件来源以及软件开发者的真实身份&#xff0c;保证代码在签名之后不被恶意篡改。使用户在下载已经签名的代码时&#xff0c;能够有效的验证该代码的可信度&#xff1b;同时证书又分为个人型&#xff08;iv&#xff09;、企业型&#…

进程大杂烩

1、fork函数的使用 使用fork()函数创建一个进程 pid_t fork(void) fork函数调用成功&#xff0c;返回两次 返回为0&#xff0c; 代表当前进程是子进程 返回为正数&#xff0c;代表当前进程为父进程 fork()函数运行后会创建一个进程&#xff0c;加上开始的进程一共有两个进程&am…

第一天总结 之 用户管理界面的实现 之 模糊查询和分页操作

第一天总结 之 用户管理界面的实现 之 模糊查询和分页操作 1、明确页面的跳转 当登录操作执行时 如果正确 跳转到 UserFuzzySelectServlet 即用户模糊查询的select 注&#xff1a;因为第一次写项目 对于很多操作都不熟悉 很多前期操作没 有 按着 见名之意 …

迎兔年 贺新春 | vLive虚拟直播新年场景上线!

为了带来更为舒适的用户体验给用户提供更好的线上活动品质vLive虚拟直播2.3.1版本进行了优化升级还有多个新春场景上新全新升级的vLive又增添了哪些亮点一起来快速了解一下吧场景上新&#xff1a;新春活动更出彩农历新春降至如何让拜年视频更出众&#xff1f;如何让新年直播更精…

【C++】为什么C++会支持函数重载

文章目录 函数重载 1.概念 2.支持函数重载的原理 2.1准备知识 2.2原理 函数重载 1.概念 在C语言中&#xff0c;是不允许同名函数存在的。但是在一个作用域种&#xff0c;比如加法函数&#xff0c;想要实现各种类型的数据相加&#xff0c;要定义多个函数&#xff0c;但是…

设计模式-七大原则

设计模式 聚合 设计模式追求的是 1.代码重用性&#xff08;相同功能的代码不用重复编写&#xff09; 2.可读性&#xff08;规范性&#xff0c;便于其他程序员阅读和理解&#xff09; 3.可扩展性&#xff08;增加新的功能非常方便&#xff09; 4.可靠性&#xff08;增加新的…

【国产可编程逻辑控制器plc调研】

国产可编程逻辑控制器plc调研1 高性能PLC&#xff08;ACxxx系列&#xff09;2 中型PLC&#xff08;AMx00系列&#xff09;3 小型PLC&#xff08;HxU、HxS&#xff09;4 小型紧凑型PLC&#xff08;Easy&#xff09;[新品]总结由于有国产化的需求&#xff0c;所以调研了一家国内的…

Internet Download Manager2023下载器Win系统经典下载工具

IDM下载器是一款非常经典的多线程下载工具&#xff0c;广受国内外用户喜爱。该软件专注于文件下载&#xff0c;没有任何多余功能&#xff0c;也没有烦人的弹窗广告打扰&#xff0c;简单易操作。特别是站点抓取功能&#xff0c;对于网站整站下载非常的好用。 整站下载器有很多&…

shell-将密码输入错误超过4次的IP地址通过firewalld防火墙阻止访问

应用场景&#xff1a;防止恶意IP尝试ssh登录 脚本说明&#xff1a;将密码输入错误超过四次得ip地址通过iptable防火墙访问。 分析&#xff1a; 首先&#xff0c;需要知道ssh远程访问记录在哪一个文件中 /var/log/secure其次&#xff0c;模拟远程访问输错密码&#xff0c;查…