开发者学堂课程【嵌入式之RFID开发与应用2020版:外部中断代码编写】学习笔记与课程紧密联系,让用户快速学习知识
课程地址:https://developer.aliyun.com/learning/course/665/detail/11156
外部中断代码编写
内容介绍:
一、触发方式
二、下拉输入
三、中断源
四、中断优先级
接下来讲代码,直接把写好的代码拿来,查询过程没有给,给一个带中断的按键,把它拖过来替换掉。
替换完后,在工程里要适当的修改一下,还要再添加一些文件进来,
再同步一下,
添加进来后,首先,代码写的会稍微复杂些,led也不是很复杂,
u16 time_count;
static u8 led_flag =
日;
static u8 buzzer flag = 0;6:
u8 led flicker count;
u8 buzzerflicker_count;8:
void led_init(void)
{
GpIo InitTypeDef GPIO Initstructure:
RCC APB2PeriphClockCmd(RCC XXXLED,ENABLE);
GPIo_Initstructure.GPIO_Pin = GPIO_XXX_LED;
GpIo Initstructure.GPIO Mode =GPIO Mode Out PP;
Gpio Initstructure.GPIO Speed = GPIO Speed 10MHz;
GPIo Init(GPIO XXX LED_PORT, &GPIO_Initstructure);
LED XXX OFF
();
//初始化结束熄灭LED
}
包括轰鸣器,在按键这里就有很大的区别。
重点来看,如果想让按键支持中断。那么首先就来明确一下让谁来输出固定的电频,由另一个引角作为终端的输入采集。
static signed char key_status=0;5:
void key init(void)7:{1
GPIO InitTypeDef GPIO Initstructure fix;
EXTI InitTypeDef ExTI Initstructure:
NVIC_InitTypeDefNVIC Initstructure;
GPIO InitTypeDef GPIO_Initstructure:
RCC_APB2PeriphClockCmd(RCC_XXX_KEYFIX,ENABLE);
GpIO Initstructure fixPIo PinmGPIO XXX KEYFIX:
GPIo Initstructure fix.GPIo Mode=GPIO Mode out PP:
GPIO_Initstructure fix.GPIO Speed=GPIO_Sped_10MHz;
GPIO Init(GPIO XXX KEY FIX PORT,&GPIO Initstructure fix):
GPIO SetBits(GPIO XXX_KEY FIX PORTGPIO XXX_KEY_
FIX);
要提醒一下在这里其实并没有去配置它的抢占优先级和子优先级。要去配置也是没有问题的,就是需要把>WVIC PriorityGroupConfig(NVIC PriorityGroup_1)复制放在代码的首位,
while(i--);
}
int main(void)11:
NVIC_PriorityGroupCohfig(NVIC_priorityGroup_1);
led_init();
buzzer init();
key_init();
while(1){
if(get_key_status()){
led_flicker();
buzzer_flicker();
}
把它配置成第二种情况,也就是让抢占优先级和子优先级各占两位。配置完后,按键是怎么初始化的?
首先,这里按键用的是A0,另一个是A1。把管角固定输出固定值,setbits说明固定是高电频。
ExtI InitTypeDef EXTI Initstructure;
NvIc InittypeDef NVIc Initstructure;
GPIo_InitTypeDef GPIO_Initstructure;
RCC APB2PeriphClockCmd(RCC XXX KEY FIX,ENABLE);
GpIo Initstructure fix.gpio Pin = GPIO XXX KEY FIX;//
输出固定值 1 GPIo Initstructure fix.GPIO Mode = GPIO Mode Out pp;
GPIo Initstructure fix.gpio Speed = GPIO Speed 10MHz;
GPIO_Init(GPIO_XXX_KEY_FIX_PORT,&GPIO_Initstructure fix); I
GPIo SetBits(GPIO XXX KEY FIX PORT,GPIO XXX KEY FIX);
RCC APB2PeriphClockCmd(RCC XXX KEYRCC APB2Periph AFIO, ENABLE);
GPIo Initstructure.GPIo Pin = GPIO XXX KEY;
GPIO Initstructure.GPIO Mode = GPIO Mode IPD;
GPIO Init(GPIO XXX KEY PORT, &GPIO Initstructure);
GPIO EXTILineConfig(GPIO XXX KEY PORTSOURCE, GPIO XXX KEY SOURCE);
另一个IPD管角就要把它设成输入。一个是输出高电频,另一个是输入,这刚好就符合按键图,A0是输出高电频,A1是设置输入,而要把它设成中断的方式,中断在没有键按下时相当于中断是不触发的,一旦按下它就触发,还要选择触发方式。
一、触发方式:
首先,利用有关GPLO的数据结构,把RCC打开,引角的确认模式为输出频率十兆,IO口配置set输出高电频完;另一个同理。除了要把按键的时钟打开外还增加个afio。
需要开启和中断相关的时钟配置,包括要用到串口并把串口的时钟打开,所以就多个过程,中断和io的时钟全部打开,并且将io设置成模式。
二、下拉输入:
ipd叫做下拉输入,它连接一个下拉电阻,并且是输入状态。
下拉电阻的含义是在没有按键时,pae默认应是低电阻,当按下去时,它就由低电频变高电频,所以就应有一个跳变,当你的手松开之后,它应又回到低电频。
它有一个上升音、高电频、下降音三种变化,要选一种作为它的处罚方式,设好后,把通关退完,接下来这里就是配置中断。
RCC APB2PeriphClockCmd(RCC XXX KEYRCC APB2Periph AFIO,ENABLE);
GPIO Initstructure.GPIO Pin =GPIO XXX KEY;
GPIO Initstructure.GPIO Mode =GPIO Mode IPD;
GPIO_Init(GPIO XXX KEY PORT,&GPIO_Initstructure);
GPIO EXTILineConfig(GPIO XXX KEYIPORTSOURCE, GPIO XXX KEY SOURCE);
三、中断源:
要指定这个案件的中断源,它是gpla的第几个引脚,
/*限位开关*/
#define RCC XXX KEY RCC APB2Periph GPIOA
#define GPIO_XXX KEY_PORT GPIOA
#define GPIO XXX KEY GPIO_Pin_1
#define GPIO XXX KEY PORTSOURCE GPIo PortSourceGPIOA
#define GPIO_XXX KEY_SOURCE GPIO PinSource1 I
#define GPIO XXX KEY LINE EXTI Line1
#define GPIO_XXX KEY_EXTI EXTI1_IRQn
用的是第一个引脚是Source1,如果是第零个引脚是Source0,第二个就是Source2,以此类推,每组io口就四五个引脚。
A组的第一个引脚,这就决定io不是普通的输入引脚,换句话说,因为在这后面的配置中,它自动去修改引导属性为中断模式。配置的是io的东西。
配置外部中断相关的,首先外部中断属于哪个中断线,它是A0就是第零个中断线,A1就是第一个,中断线A2就是第二个中断线。中断线有16个指的是外部中断,它有一些互联互通型的,还有一些特殊中断。
/* Configure Button EXTI line */
/*将KEY按键配置为中断模式,上升沿触发中断*/
EXTI Initstructure.EXTI Line =GPIO XXX KEY LINE;
EXTI Initstructure.EXTI Mode=EXTI Mode Interrupt:
EXTI Initstructure.EXTI Trigger = EXTI Trigger Rising
EXTT Initstructure EXTT lineCmd =FNABLE.
另外,它有中断模式、事件模式,处罚方式
有上升沿,下降沿,还有上升沿加下降沿(也就是双沿)。
typedef enum
EXTI TriggerIRising =0x08
EXTI Trigger Falling=0x0C,
EXTI Trigger Rising Falling =0x10
}EXTITriggerTypeDef;
选择上升沿触发,当按键在按下时由默认的下拉电阻低电频变成高电频。用下降沿也可以,因为按键一旦松开,它就会回到低电频。所以你上升沿或下降沿甚至是双沿都可以。中断就会进一次按键,所以这里用的是上升沿,保证键只要按下中断立马响应。
对于中断进行初始化(优先级配置),
/*Enable and set Button EXTI Interrupt to the lowest
priority*/
/* 将KEY按键的中断优先级配置为最低 */
NVIC Initstructure.NVIC IRQChannel=GPIO XXX KEY EXTI;
NvIc
Initstructure.NVIC IRChannelPreemptionPriority=0xOF;
Nvic
Initstructure.NVIc IRochannelsubPriority=0x0F;
NVIC Initstructure.NVIC IRQChannelCmd=ENABLE;
NVIC Init(&NVIc Initstructure);
四、中断优先级:
对于中断优先级,首先,先确定中断的通道,一共有七个,这里用的是EXTI1_IRQn,一共有七个中断的入口,后面还有五到九,十到一十五,那么一共七个,用的是第一个中断的通道,它是独立的。
如果用的是外部中断567,这就是共享,进到同一个函数里由自己去分辨是谁被触发。
抢占优先级总共提供的是四个比特位,数字化里定义成扩占两位,设成3,Nvic Initstructure.Nvic IrochannelPreemptionPriority=0x03,是最低的优先级,这也设成3 NVIC Initstructure.NVIC IROchannelsubPriority=0x03;//3,依然是最低的优先级,因为0就是数字,越小的优先级是越高的,(0到3)设成3,抢占优先级有四个,子优先级也有四个,最后,把优先级的通道初始化。中断配置就算是完成了。基本上stm32的开发只要有一个程序是正常的,其他的程序几乎就是故技重施了。
另外,中断一已经配置成功,要找到中断它对应的入口,EXT0到E15只有7个入口,外部中断1是独立的入口。
外部16个中断线对应的7个中断入口:
EXTIO_IRQHandler
EXTI1 IRQHandler
EXTI2 IRQHandler
EXTI3_IRQHandler
EXTI4 IRQHandler
EXTI5 9 IRQHandler
EXTI10 15 IRQHandler
具体实现请参考代码
其它中断入口可以参考startup stm32f10xxx件
入口函数它本身是已经在启动代码当中定义好,自己去写要去修改启动代码。EXTI1 IROHandler,给改掉后,就可以在程序里面写一个你自己的公关付款。
void EXTI1_IRQHandlen(void)
{
if(EXTI GetITstatus(GPIO XXX KEYLINE)!=RESET
{
delay_ms(KEY_SHAKE);//
去抖
if(XXX KEY STATUSGET()==1){
key_status = 1;
SET_BUZZER FLICKER(5);
EXTI_ClearITPendingBit(GPIO_XXX_KEY_LINE);
因为50有终端向量表,通过向量表跳转到指定的地址,它启动代码已经固定。
中断函数的特点是没有参数也没有返回。异常向量表是cpu调的,Cpu是让程序计数器PC直接发生偏转,PC直接指向某个入口,就并没有函数调用的上下文(没办法传参),也没有办法去处理返回值,返回值的参数是没有任何意义的。
进来后,可以通过 GetITStstus 去发现中断的标志位是不是被置位是不想中断的非常有效的方法。比如现在是一号中断,你判不判断都是它,中断是第五和第六要做判断,如果第五个中断触发,否则再加一段,要重新复制下来
}else
if(EXTI_GetITstatus(GPIO_XXX_KEY_LINE)!= RESET){
如果是Line1, Line5,要把它改成6,到底是5触发还是6触发,从而可以在同个中断服务函数里执行多个中断的响应。但要记住5和6同一时刻肯定只能有一个进来,当把5处理完后,如果再退出去,中断标注为6依然自卫,函数还会再执行,所以不会漏掉。
独立中断的好处是可以支持抢占,中断正在执行时,另一个中断打断还可以再次去,比如对于这种5到9的中断,把它设成抢占,5到9之间抢占就没有任何意义,自己的函数不是同一个函数没有必要去抢占。
这时就相当于函数就有点像递归了,去抖做法不是很科学,尽量不要在中断里面去延时,希望它更快的完成处理。
简单的演示,让按键更准确,在这里做一个延时delay ms(KEYSHAKE)://去抖 大概20毫秒过后,再去通过 ReadInputDataBit 去判断读出来的状态是一个高电频,表示按键确实是按下。
按下后, key_status给它制1,轰鸣器和灯闪烁五次 SET BUZZER FLICKER(5); 只设置一个标志位需要定时器的配合。可以先忽略这句话,执行完要把中断线的标志位给清除掉,这就是中断服务函数。
key_status状态其实是和其他的任务通信的,可以选择把代码直接放到这里,也可以给它制个标志位,然后在外面判断获取按键状态有没有 status 返回是1,返回来它就会把清零
//轮询接口
signed char get key status(void)
signed char temp = key_status;
key_status = 0;
return temp;
就是状态,相当于每获取一次就会清掉1。
get_key_status 在主程序里只要读到为灯飞离就让这个轰鸣器的灯闪一下,就达到按键触发轰鸣器和开灯关灯的动作,这就是中断。