NXP

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;
}