Friday, 29 May 2015

arm - STM32 Interrupt Priority (preemption) Problems


I have another problem (High resolution system timer in STM32) which I have tracked down (mostly) to this issue, and have created a test case here for the simplest STM32 processor I could find (on the STM32VLDISCOVERY board).


The problem is that I cannot get a higher priority interrupt to interrupt a lower priority one.


In the example, LED1 is flashed slowly by the SysTick interrupt, LED2 is flashed by the main loop.



Expected outcome


When BTN1 is pressed, the EXTI0 interrupt is called, it flashes LED2 quickly until the higher priority SysTick interrupt is fired, and then it exits. LED1 keeps on flashing as before.


Actual outcome


When BTN1 is pressed, the EXTI0 interrupt is called, it flashes LED2 quickly. The higher priority SysTick interrupt is never fired, LED1 never flashes and the LED2 continues to blink quickly.


Any ideas? Is interrupt preemption something that has to be turned on somehow?


#include "stm32f10x.h"

typedef char bool;
volatile bool toggle;


void delay(void) {
volatile int i = 100000;
while (i-- > 0) {
}
}
void delaySlow(void) {
volatile int i = 1000000;
while (i-- > 0) {
}
}


// Toggle LED1 on SysTick
void SysTick_Handler(void) {
if (toggle = !toggle)
GPIO_SetBits(GPIOC, GPIO_Pin_8);
else
GPIO_ResetBits(GPIOC, GPIO_Pin_8);
}

// On EXTI IRQ, flash LED2 quickly, and wait for a SysTick

void EXTI0_IRQHandler(void) {
bool lastToggle = toggle;
GPIO_SetBits(GPIOC, GPIO_Pin_9);
while (lastToggle==toggle) { // wait for systick
// Flash LED2 quickly
GPIO_SetBits(GPIOC, GPIO_Pin_9);
delay();
GPIO_ResetBits(GPIOC, GPIO_Pin_9);
delay();
}

GPIO_ResetBits(GPIOC, GPIO_Pin_9);

EXTI_ClearITPendingBit(EXTI_Line0);
}


int main(void){
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;


RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |
RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD |
RCC_APB2Periph_GPIOE, ENABLE);
// set preemption
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_Init(GPIOB, &GPIO_InitStructure);

GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_Init(GPIOE, &GPIO_InitStructure);
// button
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// leds
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
// systick
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
SysTick_Config(0xFFFFFF); // 24 bit
NVIC_InitStructure.NVIC_IRQChannel = SysTick_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // Highest priority
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

// exti 0
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F; // Lowest priority
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_Init(&NVIC_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
EXTI_InitTypeDef s;
EXTI_StructInit(&s);
s.EXTI_Line = EXTI_Line0;

s.EXTI_Mode = EXTI_Mode_Interrupt;
s.EXTI_Trigger = EXTI_Trigger_Rising;
s.EXTI_LineCmd = ENABLE;
EXTI_Init(&s);

while (1)
{
// Flash LED2 slowly
GPIO_SetBits(GPIOC, GPIO_Pin_9);
delaySlow();

GPIO_ResetBits(GPIOC, GPIO_Pin_9);
delaySlow();
}
}

Answer



I just found the answer from a very helpful poster on the STM32 forum


The following isn't correct. SysTick is a 'System Handler', and as such the priority isn't set in this way at all:


  NVIC_InitStructure.NVIC_IRQChannel = SysTick_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // Highest priority
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

It's actually set with:


  NVIC_SetPriority(SysTick_IRQn, 0);

Calling that code instead solves the problem!


No comments:

Post a Comment

arduino - Can I use TI's cc2541 BLE as micro controller to perform operations/ processing instead of ATmega328P AU to save cost?

I am using arduino pro mini (which contains Atmega328p AU ) along with cc2541(HM-10) to process and transfer data over BLE to smartphone. I...