www.mikrocontroller.net

Mikrocontroller.net Forum WinARM / Yagarto / ARM-GCC > STR9 - wierd interrupt behavior

Posted by Dan Miller (gorlash)
on 20.06.2008 20:19
We have a STR9 project that we're porting from IAR to Yagarto.  We have
interrupts on UART0, RTC, ADC (all via the standard VIC interface).  The
RTC is driven by a 32Khz external clock.  The ADC interrupt is started
off in main(), then restarts itself in the ADC IRQ.  These interrupts
have all been running fine in the IAR implementation.

When the gcc build starts up, the RTC starts up, and ticks along
merrily.  Then, after a couple of seconds, we initiate the first ADC
conversion, and the ADC interrupt triggers... once...
After that ADC interrupt, no interrupts ever occur again in the system.
The RTC, ADC, and UART interrupts all simply fail to occur any further.
Main loop is running along happily.  The cpsr shows IRQ interrupt is
enabled (cpsr = 0x2000001F).  I don't understand what could be causing
the ADC interrupt to disable all interrupts (without disabling
interrupts)... ??

I'm using the same ISR to handle all the VIC interrupts (via IRQ
interrupt), so if I'm exiting from the RTC interrupt successfully, the
ADC should be fine also.

Does anyone have any insights on this odd issue?

#*************************************************************
My IRQ ISR code:

#  STR9 firmware method from ST (Ride code)
.macro Savecontext $r0,$r4
    STMFD  sp!,{r0-r4,lr}     /* Save The workspace plus the current
return*/
                                  /* address lr_ mode into the stack.*/
  .endm

#  STR9 firmware method from ST (Ride code)
.macro RestoreContext $r0,$r4

   LDMFD   sp!,{r0-r4,pc}^  /* Return to the instruction following...*/
                            /*...the exception interrupt.*/
  .endm

#*************************************************************
My IRQ setup code (from main())

  // Initialize the real time clock(RTC) for the timer that works on the
external 32.8K crystal
  RTC_DeInit();  // RTC default configuration : Reset configuration

  //  Configure the VIC
  VIC_Config(RTC_ITLine, VIC_IRQ, 8);  // set up the RTC interrupt
priority to 8
  VIC_ITCmd(RTC_ITLine, ENABLE);  //  Enable the VIC

  // Configure the RTC to generate an interrupt 16Hz
  RTC_PeriodicIntConfig(RTC_Per_16Hz);
  RTC_ITConfig(RTC_IT_Per, ENABLE);  // Enable the RTC Periodic
interrupt

  //SCU_APBPeriphClockConfig(__ADC, ENABLE);    // Enable the clock for
the ADC
  // Configure the GPIO4 as analog input 8 pins
  GPIO_ANAPinConfig(GPIO_ANAChannelALL, ENABLE);

  //  ADC setup omitted...

  // Enable the ADC
  ADC_Cmd(ENABLE);

  // Configure the ADC
  ADC_Init(&ADC_InitStructure);

  // VIC configuration
  VIC_Config(ADC_ITLine, VIC_IRQ, 15);
  VIC_ITCmd(ADC_ITLine, ENABLE);

  // ADC interrupt config
  ADC_ITConfig(ADC_IT_ECV, ENABLE);
Posted by Dan Miller (gorlash)
on 20.06.2008 20:21
Dan Miller wrote:
> We have a STR9 project that we're porting from IAR to Yagarto.  We have
> interrupts on UART0, RTC, ADC (all via the standard VIC interface).  The
> RTC is driven by a 32Khz external clock.  The ADC interrupt is started
> off in main(), then restarts itself in the ADC IRQ.  These interrupts
> have all been running fine in the IAR implementation.
> 
> When the gcc build starts up, the RTC starts up, and ticks along
> merrily.  Then, after a couple of seconds, we initiate the first ADC
> conversion, and the ADC interrupt triggers... once...
> After that ADC interrupt, no interrupts ever occur again in the system.
> The RTC, ADC, and UART interrupts all simply fail to occur any further.
> Main loop is running along happily.  The cpsr shows IRQ interrupt is
> enabled (cpsr = 0x2000001F).  I don't understand what could be causing
> the ADC interrupt to disable all interrupts (without disabling
> interrupts)... ??
> 
> I'm using the same ISR to handle all the VIC interrupts (via IRQ
> interrupt), so if I'm exiting from the RTC interrupt successfully, the
> ADC should be fine also.
> 
> Does anyone have any insights on this odd issue?
> 
> #*************************************************************
> My IRQ ISR code:
> 
> #  STR9 firmware method from ST (Ride code)
> .macro Savecontext $r0,$r4
>     STMFD  sp!,{r0-r4,lr}     /* Save The workspace plus the current
> return*/
>                                   /* address lr_ mode into the stack.*/
>   .endm
> 
> #  STR9 firmware method from ST (Ride code)
> .macro RestoreContext $r0,$r4
> 
>    LDMFD   sp!,{r0-r4,pc}^  /* Return to the instruction following...*/
>                             /*...the exception interrupt.*/
>   .endm

#  oops, left out the actual interrupt handler:
/********************************************************************************
* Function Name  : IRQHandler
* Description    : This function is called when IRQ exception is 
entered.
* Input          : none
* Output         : none
********************************************************************************/

IRQHandler:
       SUB    lr,lr ,#4
       SaveContext r0,r4
       LDR    r0, = VectorAddress
       LDR    r0, [r0]  /* Read the routine address from VIC0 Vector 
Address register  */

       BLX    r0        /* Branch with link to the IRQ handler. */
       RestoreContext r0,r4

> 
> #*************************************************************
> My IRQ setup code (from main())
> 
>   // Initialize the real time clock(RTC) for the timer that works on the
> external 32.8K crystal
>   RTC_DeInit();  // RTC default configuration : Reset configuration
> 
>   //  Configure the VIC
>   VIC_Config(RTC_ITLine, VIC_IRQ, 8);  // set up the RTC interrupt
> priority to 8
>   VIC_ITCmd(RTC_ITLine, ENABLE);  //  Enable the VIC
> 
>   // Configure the RTC to generate an interrupt 16Hz
>   RTC_PeriodicIntConfig(RTC_Per_16Hz);
>   RTC_ITConfig(RTC_IT_Per, ENABLE);  // Enable the RTC Periodic
> interrupt
> 
>   //SCU_APBPeriphClockConfig(__ADC, ENABLE);    // Enable the clock for
> the ADC
>   // Configure the GPIO4 as analog input 8 pins
>   GPIO_ANAPinConfig(GPIO_ANAChannelALL, ENABLE);
> 
>   //  ADC setup omitted...
> 
>   // Enable the ADC
>   ADC_Cmd(ENABLE);
> 
>   // Configure the ADC
>   ADC_Init(&ADC_InitStructure);
> 
>   // VIC configuration
>   VIC_Config(ADC_ITLine, VIC_IRQ, 15);
>   VIC_ITCmd(ADC_ITLine, ENABLE);
> 
>   // ADC interrupt config
>   ADC_ITConfig(ADC_IT_ECV, ENABLE);
Posted by Clifford Slocombe (clifford)
on 21.06.2008 09:23
What you have not posted is the code to the specific device handlers. I 
would suggest that sight of all the code that executes when the ADC 
interrupt occurs might be useful.

Interrupt share a separate stack from the normal running code. Is it 
possible that your ADC interrupt has caused a stack overflow? How much 
have you allocated? It needs to be sufficient to handle worst-case 
interrupt nesting.

Clifford
Posted by Spencer Oliver (ntfreak)
on 22.06.2008 00:58
Attachment: vector.s (8 KB)
Dan Miller wrote:
> Dan Miller wrote:
>> We have a STR9 project that we're porting from IAR to Yagarto.  We have
>> interrupts on UART0, RTC, ADC (all via the standard VIC interface).  The
>> RTC is driven by a 32Khz external clock.  The ADC interrupt is started
>> off in main(), then restarts itself in the ADC IRQ.  These interrupts
>> have all been running fine in the IAR implementation.
>> 
>> When the gcc build starts up, the RTC starts up, and ticks along
>> merrily.  Then, after a couple of seconds, we initiate the first ADC
>> conversion, and the ADC interrupt triggers... once...
>> After that ADC interrupt, no interrupts ever occur again in the system.
>> The RTC, ADC, and UART interrupts all simply fail to occur any further.
>> Main loop is running along happily.  The cpsr shows IRQ interrupt is
>> enabled (cpsr = 0x2000001F).  I don't understand what could be causing
>> the ADC interrupt to disable all interrupts (without disabling
>> interrupts)... ??
>> 
>> I'm using the same ISR to handle all the VIC interrupts (via IRQ
>> interrupt), so if I'm exiting from the RTC interrupt successfully, the
>> ADC should be fine also.
>> 
>> Does anyone have any insights on this odd issue?
>> 
>> #*************************************************************

All your interrupt code looks wrong, it has code missing to correctly 
restore interrupts etc.

You either have a copy paste issue or have a very old and incorrect 
version of ride.

Attached is my own code that may help.

Cheers
Spen
Posted by Dan Miller (gorlash)
on 23.06.2008 20:16
Thanks for your comments, Spen!!

As it turns out, after I posted the above data, I went back and compared 
the IAR startup code (which our IAR project used) with the RideV7 code 
that I was using for reference, and I saw that I was missing alot of 
code.  Just to figure out what was going on, I just went to the ST 
website *this morning*, and downloaded um0233.zip, which is the firmware 
library that ST distributes on their support website.  Both the Ride and 
EWARM/IAR code reflect the code which I posted on friday, which is 
clearly incomplete.  I guess the moral is, when using ST's chips, 
*don't* use ST's code to make them work.

Anyway, the updated Savecontext, Restorecontext, and IRQ_Handler code 
that I'm using now agrees almost exactly with what you posted.  The only 
difference is that ours uses:
       BX     r0                /* Branch to the IRQ handler. */

while yours uses:
       MOV  pc, r0    /* Branch to the IRQ handler. */

Is there any practical difference between the two??

Otherwise, interrupts now appear to be working reliably, now that I'm 
finally using complete IRQ code.

//*******************************************************
Also, regarding your question about the ADC interrupt handler, I 
actually put a return statement at the first instruction in 
ADC_IRQHandler, to make sure it didn't have too much overhead; this made 
no difference in the symptoms that we saw...
Posted by Spencer Oliver (ntfreak)
on 24.06.2008 00:12
Dan,

> Anyway, the updated Savecontext, Restorecontext, and IRQ_Handler code 
> that I'm using now agrees almost exactly with what you posted.  The only 
> difference is that ours uses:
>        BX     r0                /* Branch to the IRQ handler. */
> 
> while yours uses:
>        MOV  pc, r0    /* Branch to the IRQ handler. */
> 
> Is there any practical difference between the two??
> 

a bx is a v5 instruction that is equiv to the following v4 instuctions
LDR    lr, =ReturnAddress    /* Read the return address. */
MOV    pc, r0        /* Branch to the IRQ handler. */

It also can switch between arm/thumb mode, so is a better instruction to 
use.

Cheers
Spen
Posted by Dan Miller (gorlash)
on 24.06.2008 01:37
Spencer Oliver wrote:
> Dan,
> 
> a bx is a v5 instruction that is equiv to the following v4 instuctions
> LDR    lr, =ReturnAddress    /* Read the return address. */
> MOV    pc, r0        /* Branch to the IRQ handler. */
> 
> It also can switch between arm/thumb mode, so is a better instruction to 
> use.
> 
Whoa!!!  Thanks for that clarification, *very* valuable!!
Soo... The fact that we have:

       LDR    lr, =IRQ_ReturnAddress /* ; Read the return address. */
       BX     r0                      /* ; Branch to the IRQ handler. */

is redundant??  8-{P
Posted by Spencer Oliver (ntfreak)
on 24.06.2008 09:31
Actually i am getting my instructions mixed up - i was thinking of BLX
you will still need the following with a BX

LDR    lr, =IRQ_ReturnAddress /* ; Read the return address. */
BX     r0                     /* ; Branch to the IRQ handler. */

with a blx you would just require
BLX    r0                     /* ; Branch to the IRQ handler. */

On the original instruction
MOV    pc, r0        /* Branch to the IRQ handler. */
the compiler will add the switch to thumb if required, so you may save a 
instruction by using the bx, you will have to check the asm to verify 
this.

I have not tested the above, so double check before using.

The only limiting factor with the B* instructions is they can only 
handle a +-32MB jump - whereas the LDR can jump anywhere in the arm 
address space.

Cheers
Spen