wuyage

浅谈ARM Cortex-M0/M0+ 中的中断抢占问题

0
阅读(10583)

Cortex-M0/M0+, M3/M4 中的中断控制器英文名叫做NVIC,Nested Vectored Interrupt Controller,翻译过来就是嵌套向量中断控制器,所谓中断嵌套是指当正在执行一个中断服务程序时,这时如果来了优先级更高的中断,新来的中断会打断原来还没有处理完的中断服务程序,等新中断处理完毕之后再回到原中断服务继续处理。

下面让我们在Kinetis KL26芯片上来实际操作一把吧。KL26芯片为ARM Cortex M0+内核。

硬件:FRDM_KL26Z

软件:FRDM-KL26Z_SC_Rev_1.0 + IAR7.4

1) 先做第一个实验:程序中开启了两个定时器,一个为TPM2,周期10ms,另外一个PIT,周期1s。TPM2中断中断里打印一个字符X,PIT中断里while(1)循环,不断打印~~~~~~~。

int main (void) { char ch; printf("\n\r\n\r*** Running the NVIC_Test project ***\n\r"); enable_irq(INT_TPM2-16); init_TPM2(); enable_irq(INT_PIT-16); init_PIT(); while(1) { ch = in_char(); out_char(ch); } } /* 10 ms */ void init_TPM2(void) { SIM_SCGC6 |= (SIM_SCGC6_TPM2_MASK ); SIM_SOPT2 |= SIM_SOPT2_TPMSRC(1); //input clock source is MCGPLL/2=24MHz TPM2_SC = TPM_SC_DMA_MASK | TPM_SC_PS(3); //Divide by 3M=24M/8 TPM2_CNT = 0; TPM2_MOD = 30000; //timer interrupt triger with 100Hz TPM2_SC |= TPM_SC_TOIE_MASK; // enable Interrupt TPM2_SC |= TPM_SC_CMOD(1); //start the counter } void TPM2_IRQHandler(void) { if (TPM2_SC & TPM_SC_TOF_MASK ) TPM2_SC |= TPM_SC_TOF_MASK; // Clear Flag printf("X"); } /* 1 s */ void init_PIT(void) { SIM_SCGC6 |= SIM_SCGC6_PIT_MASK; PIT_MCR &= ~(1 << PIT_MCR_MDIS_SHIFT); PIT_LDVAL0 = 24000000; // 1 hz PIT_TFLG0 |= PIT_TFLG_TIF_MASK; PIT_TCTRL0 |= PIT_TCTRL_TIE_MASK | PIT_TCTRL_TEN_MASK; } void PIT_IRQHandler(void) { if(PIT_TFLG0 & PIT_TFLG_TIF_MASK) PIT_TFLG0 |= PIT_TFLG_TIF_MASK; // Clear Flag while(1) { printf("~~~~~~~~~~~~~~~"); } }

该实验现象如下:

1.jpg

刚开始一直打印X,也就是不断进入TPM2中断服务,然后就一直打印~,即进到PIT中断里,就再也出不来了。

出现该现象的原因是:TPM2和PIT的中断优先级是一样的(默认都是0),所以当进入到PIT中断服务后,由于while(1)原因,即使TPM2中断来了也无法响应。


2)让我们做第二个实验,加入优先级设置,将TPM2 的优先级设置为1,PIT的优先级设置为2。数字越小,表示优先级越高。

enable_irq(INT_TPM2-16); set_irq_priority(INT_TPM2-16,1); init_TPM2(); enable_irq(INT_PIT-16); set_irq_priority(INT_PIT-16,2); init_PIT();

理论来说TPM2中断会打算PIT中断,可是当我们观察实验现象时却发现,还是一直在PIT中断里,和第一个试验想象没有区别。

2.jpg

这到底是怎么回事??? 理论与实际不符啊!!!!!

经过调试才发现,原来是代码里的set_irq_priority 函数压根就没起作用,有图为证:

这是调用set_irq_priority 函数之前的寄存器值:

3.jpg

这是执行完set_irq_priotiy 函数之后的寄存器值:

4.jpg

该函数为什么不起作用呢?问题出在最后两条语句上,原因在于Cortex-M0/M0+ 中的NVIC寄存器只支持字传输,不能单字节的去操作。

void set_irq_priority (int irq, int prio) { /*irq priority pointer*/ uint8 *prio_reg; uint8 err = 0; uint8 div = 0; /* 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_reg = (uint8 *)((uint32)&NVIC_IP(div)); /* Assign priority to IRQ */ *prio_reg = ( (prio&0x3) << (8 - ARM_INTERRUPT_LEVEL_BITS) ); } }

针对这个问题,有两种途径去解决:

1) 修改set_irq_priority 函数,这个写法不唯一,有一点必须注意的是必须按字操作,也就是一下写四字节数据。

void set_irq_priority (int irq, int prio) { /*irq priority pointer*/ //uint8 *prio_reg; uint32 prio_reg; // wenxue uint8 err = 0; uint8 div = 0; /* 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_reg = (uint8 *)((uint32)&NVIC_IP(div)); prio_reg = *(uint32 *)((uint32)&NVIC_IP(div)); prio_reg = prio_reg & ( ~(0xFFUL << ((irq & 0x03UL) * 8UL)) ); prio_reg = prio_reg | (((prio << (8 - 2)) & (uint32_t)0xFFUL) << ((irq & 0x03UL) * 8UL) ); *(uint32 *)((uint32)&NVIC_IP(div)) =prio_reg ; } }

2) 最简单最可靠的是调用CMSIS 标准接口函数NVIC_SetPriority

void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) { if((int32_t)(IRQn) < 0) { SCB->SHP[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | (((priority << (8 - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); } else { NVIC->IP[_IP_IDX(IRQn)] = ((uint32_t)(NVIC->IP[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | (((priority << (8 - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); } }


修改完set_irq_priority 函数之后,我们观察调试结果,这时优先级确实做了改变

ttt.jpg

再次观察最后的实验现象,结果如下:

QQ截图20160203100423.jpg

我们可以看到TPM2中断会不断打断PIT中断。


总结如下:

1) 大家在使用FRDM-KL26Z_SC_Rev_1.0 代码时,注意set_irq_priority 函数有bug的,它起不了实际作用。

2) ARM Cortex-M0/M0+中断抢占只需要设置好优先级即可,优先级高的会抢占优先级低的中断。

补充说明:

ARM Cortex-M3/M4 中断抢占问题比M0/M0+ 复杂一点,大家可以参考以下视频介绍,说的很详细。

http://pan.baidu.com/share/link?shareid=644713788&uk=2870907047&fid=351504772388557

http://pan.baidu.com/share/link?shareid=644713788&uk=2870907047&fid=221308967281612

http://pan.baidu.com/share/link?shareid=644713788&uk=2870907047&fid=845340990569219

Cortex-M3/M4里面涉及到一个优先级分组概念,在Cortex-M0/M3 里面是没有的。



上述测试代码如下:

FRDM-KL26Z_SC.rar









Baidu
map