本帖最后由 FSL_TICS_A 于 2014-3-25 17:06 编辑
关于Kinetis L系列中断优先级设置问题的解决方案 最近在调试Kinties L系列的时候,使用官方例程中arm_cm0.c,使用其中的void set_irq_priority (int irq, int prio)函数设置了中断优先级,然后仿真的时候,在寄存器窗口发现NVIC_IPRx寄存器的值并没有改变,然后查看寄存器具体的地址的内存发现也没有改变, 后来将NVIC_IPRx寄存器地址的值赋给一个变量,然后使用printf将该变量打印出来,发现第一次能够打印正确的值,但是连续第二次打印,值又变为了0.当时感觉很奇怪,然后仔细阅读了下ARM Cortex-M0的内核文档:The Definitive Guide to the ARM cortex M0.pdf. 发现对于中断优先级的操作,和ARM Cortex-M4还是有区别的:主要在于对于M0+内核的NVIC_IPRx寄存器,每次操作都是一组32位操作,若要改变寄存器的值,首先先要读出相应的值,然后改变一个字节,再将值写回到原来的地址上。 M0内核文档是这么讲的:
图1 而M4内核就不一样,是可以字节操作的,这点在M4的内核文档上也可以找到。 所以,我认为,原来arm_cm0.c中set_irq_priority (int irq, int prio)函数不能实现优先级正确设置的原因之一,是由于采用了8位操作的方式如下: uint8 *prio_reg; prio_reg = (uint8 *)((uint32)&NVIC_IP(div)); *prio_reg = ( (prio&0x3) << (8 - ARM_INTERRUPT_LEVEL_BITS) ); 定义需要修改为: uint32 *prio_reg; prio_reg = (unsigned long *)((uint32)&NVIC_IP(div));//div 第二,从原来的代码可以看出,并不能准确的实现具体irq的定位与值的修改。 假设我们需要设置: set_irq_priority(17, 3); 可以看出,上面错误的代码只能实现irq=16值的修改,原因其只是定位到了NVIC_IPR4中的低字节,而没有在IPR4中4个字节做偏移,所以只能修改irq=16的优先级。 根据我们的reference manual可以知道,在每组IPR中,具体定位IRQ,可以通过8*(IRQ mod 4)+6的方法,注意mod是取余数,程序中是“%”,不要理解为“/”. 综合上面两点,我将set_irq_priority函数修改如下: void set_irq_priority (int irq, int prio) { /*irq priority pointer*/ uint8 *prio_reg; uint8 err = 0; uint8 div = 0; uint32 temp=0; uint32 *prio_reg1; /* Make sure that the IRQ is an allowable number. Right now up to 32 is * used. * * NOTE: If you are using the interrupt definitions from the header * file, you MUST SUBTRACT 16!!! */ if (irq > 32) { printf("\nERR! Invalid IRQ value passed to priority irq function!\n"); err = 1; } if (prio > 3) { printf("\nERR! Invalid priority value passed to priority irq function!\n"); err = 1; }
if (err != 1) { /* Determine which of the NVICIPx corresponds to the irq */ div = irq / 4; prio_reg1 = (unsigned long *)((uint32)&NVIC_IP(div));//div *prio_reg1 = ( (prio&0x3) << ((8 - ARM_INTERRUPT_LEVEL_BITS) + 8*(irq%4))); } } 同样以set_irq_priority(17, 3);为例,使用CW测试的结果如下:
图 2
图3 可以看到,结果已经成功改变。 希望该经验能够帮到大家。 |