kinetis 学习之(二)中断
2019-07-12 12:24 发布
生成海报
中断是微控制器处理异步事件的一种机制。一般而言,每个中断源都能够选择打开和关闭,默认状态下都是关闭的。为了能让 CPU 响应中断源的中断请求,必须打开对应的中断。
不同的的单片机中断的原理都大同小异,但是在具体编写代码时可能会不一样。这里介绍的freescale的K20是基于ARM的M4内核的,所以和其他的ARM芯片类似,需要在三个地方配置:①中断源本身存在中断使能寄存器位,该位决定中断源是否产生中断请求,比如GPIO就在这个引脚对应的寄存器里会有相应的中断使能位;②中断控制器( NVIC)相应的中断通道(各个中断源的中断请求连接在 NVIC 的不同的中断通道, NVIC 负责管理系统所有的中断信号,比如赋予各中断优先级等)需要被打开;③全局中断屏蔽位需要打开,该位能够屏蔽和打开所有的可屏蔽中断(换句话说,有一些中断是没有打开全局中断也可以进入中断的,它们是不可屏蔽中断,在K20里面一共有16个)。
这里介绍一种比较基础的中断,GPIO的中断。结合前面的GPIO的内容,我们使用按键来控制灯的闪烁方式。
依然是分三步。先看原理图。当按键没有按下时,引脚上是高电平,当按下时C1上的电荷释放,相对较慢变为0,但是这个滤波的效果其实并不好,所以在中断处理函数中最好通过软件的方法再处理,来去抖动。LED灯D1和D2的引脚对应到K20上的PTB11和PTB18两个引脚,按键UP对应的是K20上的PTA5引脚。
图1 按键的原理图
再看数据手册和参考手册上的寄存器。在PORTx_PCRn中有关于引脚功能的描述。其中ISF:中断状态标志位,w1c表示向该位写1将会将其清零;IRQC:中断配置,配置在什么情况下产生中断;MUX选择引脚启用的功能。如图2所示。
图2 中断功能选择
接下来思考实现用按键控制灯状态的整体思路:首先打开PORTA和PORTB的端口时钟,配置D1和D2对应的引脚为GPIO模式,设置为输出方向。对按键UP对应的引脚要配置为GPIO,选择下降沿触发和启用引脚的低通滤波功能(根据参考手册上当有用信号的频率大于2MHz时就不应该开启低通滤波功能)。接着使能PORTA的中断(根据参考手册可知PORTA的中断号(IRQ)为87),使能全局中断(在ARM中专门的一条汇编指令为asm(“
CPSIE i” ))。接下来需要编写中断服务函数,并把它写在保存有中断向量表的Project_Settings/Startup_Code/kinetis_sysinit.c这个文件中,且在中断向量表中对应IRQ号对应处保存到中断函数名,在中断函数中需要实现对一个标志符号led_mode的值翻转。最后,在主函数中使用while()循环,根据led_mode来判断要LED的闪烁方式。
这里需要注意的一点是如何将在kinetis_sysinit.h中定义的led_mode变量既可以在kinetis_sysinit.c又可以在main.c中调用。具体是: 在kinetis_sysinit.h中声明一个变量extern char led_mode ;在main.c中也要申明char led_mode ;在kinetis_sysinit.c中可以直接使用。需要注意的是在kinetis_sysinit.h中的含有extern申明时不可以赋初值。
由于暂时找不到上次文件的按钮,所以贴代码如下。
#define GPIO_PIN_MASK 0xFFu
#define GPIO_PIN(x) (((1)<<(x & GPIO_PIN_MASK)))
char led_mode = 0;
void enable_irq (int irq)
{
int div;
/* Determine which of the NVICISERs corresponds to the irq */
div = irq/32;
switch (div)
{
case 0x0:
NVICICPR0 = 1 << (irq%32);
NVICISER0 = 1 << (irq%32);
break;
case 0x1:
NVICICPR1 = 1 << (irq%32);
NVICISER1 = 1 << (irq%32);
break;
case 0x2:
NVICICPR2 = 1 << (irq%32);
NVICISER2 = 1 << (irq%32);
break;
}
}
//delay 1ms , unit is ms
void delay_1ms(int Ms)
{
unsigned short i,j;
for(i=0;i {
for(j=0;j<2000;j++)
asm("nop"); }
}
void flashing()
{ GPIOB_PDOR |= (GPIO_PIN(18) ); // turn off GPIOB_PDOR &= ~GPIO_PIN(11); // turn on delay_1ms(1000); GPIOB_PDOR &= ~(GPIO_PIN(18)); // turn on GPIOB_PDOR |= GPIO_PIN(11); // turn off delay_1ms(1000);
}
void twinkle()
{ GPIOB_PDOR |= (GPIO_PIN(18) | GPIO_PIN(11)); // turn off
delay_1ms(1000);
GPIOB_PDOR &= ~(GPIO_PIN(18) | GPIO_PIN(11)); // turn on delay_1ms(1000);
}
int main(void)
{ // turn on port D clocks SIM_SCGC5 |= (SIM_SCGC5_PORTB_MASK|SIM_SCGC5_PORTA_MASK); // Set pin18 of PORTB as GPIO PORTB_PCR18 |= PORT_PCR_MUX(1); // set pin11 of PORTB as GPIO PORTB_PCR11 |= PORT_PCR_MUX(1);
// set pin18, pin11 to be output GPIOB_PDDR |= GPIO_PIN(18)|GPIO_PIN(11);
//key as gpio port,falling interrupt PORTA_PCR5 |= ( PORT_PCR_IRQC(10)|PORT_PCR_MUX(1)|PORT_PCR_PFE_MASK);
// open globe intterrupt asm("CPSIE i"); // enable irq enable_irq(87);
while(1)
{ if(led_mode) flashing(); // flashing leds else twinkle(); // twinkle leds
} return 0;
}
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮