Nested interrupts with Kinetis  - Page 1
Forum section: electronics / general

author: luiHS
author_rank: Frequent Contributor
timestamp: October 27, 2020, 12:06:04 pm
content: Hi.I am with a project with MCUXpresso and a Kinetis MK66 microcontroller.Is there a way to find out if another interrupt has occurred when an interrupt routine is running?I think these are called nested interrupts, but I don't know how these work and if there is a way to detect them.I have two interrupts in the program, with one I capture the status data from a switch matrix, and with the other a lamp matrix. The switches are captured correctly, but the lamps are captured alternately, one good and one bad.Now I am solving it by saving only the data alternately, a good one, a bad one, a good one, etc ... but I would like to know why this happens and solve it properly. I suspect that while the program is in the routine of the first interrupt, alternately the second interrupt occurs, if this is the case and when it exits the first interrupt the second interrupt is going to be processed, then the data that it would capture would be incorrect because these would no longer be present on the data bus.So my question is whether it is possible to know if during an interrupt routine, another interrupt occurred, and if that second interrupt is processed when the first one ends or can it be configured to be ignored.Best Regards

author: andersm
author_rank: Super Contributor
timestamp: October 27, 2020, 01:01:15 pm
content: If you don't want one interrupt to preempt another, configure them to the same priority.

author: Siwastaja
author_rank: Super Contributor
timestamp: October 27, 2020, 04:00:24 pm
content: Start by realizing your KInetis is an ARM Cortex-M4 microcontroller. So relevant keywords are: Cortex-M4 interrupts, for a lot of good reading material.Because it's an ARM core feature, not a peripheral thing, CMSIS offers portable hardware abstraction using a few simple functions, and examples for any Cortex-M4 should work. Similarly, I can give you advice despite of never using Kinetis microcontrollers:Start by deciding and configuring if you want to use "sub-priority" bits or no - usually no for a beginner, dedicating the interrupt priority number completely for easy-to-understand pre-emptive priority:Code:NVIC_SetPriorityGrouping(0);You may want to look up how many priority bits your Kinetis implements. I think it's usuallyat least4 bits, so 0..15, so let's assume that. 0 is the highest priority, let's say "most urgent", because 15 would be the highestnumberbut the least urgent priority.Now, configure the priorities for your interrupts:Code:NVIC_SetPriority(PVD_IRQn, 2);NVIC_SetPriority(TIM2_IRQn, 3);NVIC_EnableIRQ(PVD_IRQn);NVIC_EnableIRQ(TIM2_IRQn);In this case, while serving TIM2 ISR, if PVD IRQ happens, TIM2 ISR will be interrupted and PVD interrupt served, because it has higher priority (lower priority number). When PVD ISR is done, the TIM2 ISR continues where it left.OTOH, while serving PVD ISR, if TIM2 IRQ happens, PVD is completely served first because it has more urgent priority number.If you set them to the same value, the ISR is not interrupted by another one, either way; they have the same priority after all.Now if two interrupts came inat the same time, you could add sub-priority bits and construct interrupt priority values so you can control which one is served first, but this is usually not something you need to consider; if you have things happening so close in time, they could have happened in whichever order anyway, so you are usually fine just serving in the order defined by the hard-wired priority (typically lower IRQn wins). But it's good to know for future reference you do have more control available if the need arises.Because the core handles context saving and register push/pop, It Just Works, nothing needs to be considered here. The ISRs are just normal functions.You can further turn off some less urgent interrupts, as an alternative for __disable_irq(), keeping critical ones on. This is the macro I use to turn off off interrupts configured for priorities >= 4:Code:#define DIS_IRQ() do{__set_BASEPRI(4UL << (8 - __NVIC_PRIO_BITS)); __DSB();}while(0)#define ENA_IRQ() do{__set_BASEPRI(0UL); __DSB();}while(0)

author: luiHS
author_rank: Frequent Contributor
timestamp: October 27, 2020, 09:48:17 pm
content: Thank you both for the information.I'm still not sure how to do it, I mean that it is not a question of priorities in the interruptions, but to avoid that when an interrupt is activated while another is being served, when the first one is finished the second is not executed.Then the only thing I can think of is that while an interrupt is being served, disable the other interrupt, so I will be sure that when the interrupt routine ends, another pending interrupt will not be served. Or clear any pending interrupt.1.- I have read that this would be the way to do it.DisableIRQ (device_IRQn); // Disable interrupt__DSB ();__IT IS B();And when I finish I enable it again:EnableIRQ (device_IRQn); // Enable interrupt2.- Or may be I can use this instruction to clear the pending interrupt:__NVIC_ClearPendingIRQ(IRQn_Type IRQn)Regarding the priorities, in the example source code that I started from to make my program, it doesn't seem that they are configured, I don't know what the default priorities are:EnableIRQ (PORTA_IRQn);EnableIRQ (PORTE_IRQn);EnableIRQ (PORTD_IRQn);// __NVIC_SetPriority (IRQn_Type IRQn, uint32_t priority)unsigned int prioA = __NVIC_GetPriority (PORTA_IRQn);unsigned int prioE = __NVIC_GetPriority (PORTE_IRQn);unsigned int prioD = __NVIC_GetPriority (PORTD_IRQn);

author: ejeffrey
author_rank: Super Contributor
timestamp: October 27, 2020, 10:32:35 pm
content: Quote from: luiHS on October 27, 2020, 09:48:17 pmNormally it doesn't make sense to try to detect if an interrupt is itself interrupted any more than it makes sense for main thread code to detect when it is interrupted -- excepting code that is explicitly waiting by polling a volatile flag waiting for new data. The inner interrupt should be restore things the way they are, and if they don't conflict you shouldn't notice anything except that the outer ISR appears to take a bit longer to execute. That is in the normal case where interrupts that can execute simultaneously don't share state, or at least not in a manner that matters. This is what you *strongly* should aspire to. Having data sharing between multiple interrupts that can be active at the same time is really hard.Is there any reason you need to use interrupt priority here at all? If you just make the interrupts the same priority, neither will interrupt the other and you shouldn't have to worry about it. They can even share state with each other (carefully). In fact, the simplest configuration is to make all interrupts on the system exactly the same priority.QuoteI'm not sure what you are getting at here. Pending interrupts are serviced after the current interrupt completes, and do not interrupt it. That shouldn't cause a problem at all.

author: ejeffrey
author_rank: Super Contributor
timestamp: October 27, 2020, 10:38:58 pm
content: Quote from: Siwastaja on October 27, 2020, 04:00:24 pm

author: luiHS
author_rank: Frequent Contributor
timestamp: October 27, 2020, 10:54:05 pm
content: QuoteThat's the problem, I don't want that to happen. I don't want a pending interrupt to be served that was triggered when the current interrupt was being served.My two routines for interrupts what do is read a data bus, just when an interrupt is triggered the first thing these routines do is read that data bus. So if an interrupt is pending and is not executed until the current interrupt ends, when the routine executes the data from the data bus is no longer available and it reads incorrect data.In any case, I still don't know if this is the reason. What happens in the program is that when the data bus is read by the interrupt two, it returns a good one, a bad one, a good one, a bad one, etc ... so I suspect that it may be a pending interrupt problem.What I want to find out first is whether another interrupt is being triggered during an interrupt routine, which would remain pending and would be executed when the current interrupt ends.I have been looking at the functions available to handle interrupts, and I think I could use this inside the interrupt one routine, to know if interrupt two has been triggered:// return 0 Interrupt status is not pending.// return 1 Interrupt status is pending.__NVIC_GetPendingIRQ (IRQn_Type IRQn)then I could clear it with__NVIC_ClearPendingIRQ (device_IRQn)

author: cv007
author_rank: Frequent Contributor
timestamp: October 27, 2020, 11:51:54 pm
content: You can also send both interrupts to the same isr function. Grab a copy of each 'bus' at the start of the interrupt, then figure out who caused the interrupt. Sounds like either the isr takes a relatively long time, or the 'bus' is a fast changing thing. With a single isr, you get a copy of the bus, then can take your time figuring out what is going on and do whatever you need to do.

author: luiHS
author_rank: Frequent Contributor
timestamp: October 28, 2020, 12:00:02 am
content: Quote from: cv007 on October 27, 2020, 11:51:54 pmWell, each interrupt is from a different GPIO port, one on port A and the other on D, I don't think the same function can be assigned for both. Now works with PORTA_IRQHandler(void) and PORTD_IRQHandler(void)What I am going to try to find out first is whether the other interrupt is actually triggered while the routine of one interrupt is in progress, because if that is not happening then the problem would not be interruptions.I am going to put an IF of this type into the routine of each interrupt, to detect it:if (__ NVIC_GetPendingIRQ (PORTD_IRQn)) printf ("\ r \ n Triggered interrupt SWITCHES in lamp routine \ r \ n");if (__ NVIC_GetPendingIRQ (PORTA_IRQn)) printf ("\ r \ n Triggered interrupt Lamps in routine Switches \ r \ n");

author: cv007
author_rank: Frequent Contributor
timestamp: October 28, 2020, 12:43:52 am
content: > I don't think the same function can be assigned for bothYou have weak irq function names you need to override, but once that is taken care of you are are dealing with regular functions. You can call other functions.The cost is a single instruction, and if that is too much you can change the vector table to set both irq's to use the same function.void PORTAD_IRQHandler(){//code here}void PORTA_IRQHandler(){ PORTAD_IRQHandler(); }void PORTD_IRQHandler(){ PORTAD_IRQHandler(); }

author: ejeffrey
author_rank: Super Contributor
timestamp: October 28, 2020, 06:27:41 am
content: Quote from: luiHS on October 27, 2020, 10:54:05 pmInteresting. Ideally you would like to have an ISR that can run to completion without error in any case. If the condition that needed attention is no longer available, it should just return without doing anything. What bus are you reading? I was assuming that on a PORT interrupt you were just reading the digital inputs of the IO port that triggered the interrupt which shouldn't fail to do it a second time.Cancelling a pending ISR is possible, but is prone to race conditions. If you read the bus, then a new event happens that would trigger the opposing IRS and generate new data on the bus, then you clear the ISR you may miss an important. Maybe with your specific set of events and timings there is a way to avoid this or make it harmless, but it would be far better IMO to just fix the ISRs so they don't fail.

author: Siwastaja
author_rank: Super Contributor
timestamp: October 28, 2020, 07:59:55 am
content: Of course same function can be assigned to any number of interrupt sources. Just write the function address to the vector table, like you normally do.It appears you are using some compiler/library feature supposedly making your life easier, which hinders with this?I nowadays prefer to just relocate the vector table to RAM so I can simply (and dynamically) set the handlers in peripheral init, and change them if I need a state machine executing different handlers. I use this code (for STM32, though):Linker script:Code:SECTIONS{.nvic_vector :{*(.nvic_vector)} >ram_vectors AT>rom...startup code:Code:void stm32init();void early_nmi_handler();void early_hardfault_handler();void invalid_handler();extern unsigned int _STACKTOP;#define INITIAL_VECTOR_TBL_LEN 4#define FULL_VECTOR_TBL_LEN 166unsigned int * the_nvic_vector __attribute__ ((section(".nvic_vector"))) ={/* 0x0000 */ (unsigned int *) &_STACKTOP,/* 0x0004 RESET */ (unsigned int *) stm32init,/* 0x0008 NMI */ (unsigned int *) early_nmi_handler,/* 0x000C HARDFAULT */ (unsigned int *) early_hardfault_handler};// startup code relocating vectors:// Relocate the interrupt vector table to RAM.// Reading at address 0 causes gcc to not generate any code at all (no warnings); start copy from 4uint32_t* vect_begin = (uint32_t*)(VECTORS+4);uint32_t* vect_end = (uint32_t*)(VECTORS+INITIAL_VECTOR_TBL_LEN*4);uint32_t* vect_full_end = (uint32_t*)(VECTORS+FULL_VECTOR_TBL_LEN*4);uint32_t* vecti_begin = (uint32_t*)(ROM_ORIGIN+4);while(vect_begin < vect_end){*vect_begin = *vecti_begin;vect_begin++;vecti_begin++;}// Fill the rest of the vectors in RAM with illegal vector handlerwhile(vect_begin < vect_full_end){*vect_begin = (uint32_t)invalid_handler;vect_begin++;}// Do the relocation!SCB->VTOR = VECTORS;__DSB(); __ISB();In some header:Code:#define VECTORS 0x0000FC00UL // whatever you choose, make sure in linker script it doesn't place variables over this// Using pre-defined macros like TIM2_IRQn#define SET_VECT(interp, hand) do{*((volatile uint32_t*)(VECTORS+(interp)*4+0x40)) = (uint32_t)(hand); __DSB();}while(0)// Using address offsets to the interrupt table, because predefined interrupt number does not exist for all interrupt sources#define SET_VECT_FROM_ADDROFFS(interp, hand) do{*((volatile uint32_t*)(VECTORS+(interp))) = (uint32_t)(hand); __DSB();}while(0)Usage:Code:void spi_handler(){}void init_spi(){...SET_VECT(SPI4_IRQn, spi_handler);NVIC_SetPriority(SPI4_IRQn, 5);NVIC_EnableIRQ(SPI4_IRQn);}

author: newbrain
author_rank: Super Contributor
timestamp: October 28, 2020, 08:43:52 am
content: Playing with priorities and clearing or disabling interrupts might work, but as others have noted, is prone to race conditions.It might be worthy to consider a different code structure: it seems that you are doing all 'the work' in the interrupt service routines, and the data is too volatile for this to work.Would it be possible to use the interrupt service routines just to read the data (or put it in a queue) and raise a volatile flag for code in the main loop to work on it?This would bring down the ISR execution time to something more acceptable.If the system can generate IRQs so close to each other that the bus is changed before an ISR can get to read it, there is of course a more fundamental design problem.Another solution might be DMA, if this MCU can initiate a transfer on a GPIO signal, and the bus reading operation is simple peripheral read: use the DMA to safely store the data, then the DMA completion interrupt to work on it (or signal the main loop or task).

author: ejeffrey
author_rank: Super Contributor
timestamp: October 28, 2020, 03:07:58 pm
content: Quote from: Siwastaja on October 28, 2020, 07:59:55 amThis is true but I don't think it will address the OPs problem. The same ISR function for two interrupt sources will still get executed twice if both interrupts are triggered.

author: cv007
author_rank: Frequent Contributor
timestamp: October 28, 2020, 09:03:05 pm
content: >This is true but I don't think it will address the OPs problem. The same ISR function for two interrupt sources will still get executed twice if both interrupts are triggered.The single isr does not really do much that the separate isr's could not also do, was not a great idea I floated.Maybe there is an inadvertent triggering of the 'bad' isr because both rising/falling edge triggering are enabled, or some other cause that results in getting to the isr twice as often, with the correct data only showing up half the time.