at91sam7x256 interrupts and FreeRTOS - gcc

johnkpierce wrote on Saturday, May 03, 2008:

I realize there have been other posts on this issue, but I still am up a creek.
I followed the atmel code which doesn’t use the RTOS and have 3 timer interrupts, a PIO interrupt, and 3 uarts working.  The UART code ports seamlessly to the RTOS.  I followed examples in the RTOS for the uIP EMAC, and also looked at another implementation example that had a full interrupt-setup interface as models for my setup routines.

My issue is that I don’t appear to be getting my PIO or my Timer interrupts.  Code compiles correctly, everything is in place, the interrupts are running in ARM mode, but they never trigger.  For the PIO, pins are set correctly.  For the timers, pins are not an issue.

In the port.c code for the at91sam7, I placed my timer interrupt setup along with the PIT setup, figuring that would be the right time to do it.  The actual interrupt is in a timer interrupt module that is compiled for ARM.  Here is the code:

in port.c:
* Setup the timer 0 to generate the tick interrupts at the required frequency.
static void prvSetupTimerInterrupt( void )
//! This is the original code !
    /* Setup the AIC for PIT interrupts.  The interrupt routine chosen depends
    on whether the preemptive or cooperative scheduler is being used. */
    #if configUSE_PREEMPTION == 0

        extern void ( vNonPreemptiveTick ) ( void );
        AT91F_AIC_ConfigureIt( AT91C_ID_SYS, AT91C_AIC_PRIOR_HIGHEST, portINT_LEVEL_SENSITIVE, ( void (*)(void) ) vNonPreemptiveTick );

        extern void ( vPreemptiveTick )( void );
        AT91F_AIC_ConfigureIt( AT91C_ID_SYS, AT91C_AIC_PRIOR_HIGHEST, portINT_LEVEL_SENSITIVE, ( void (*)(void) ) vPreemptiveTick );


    /* Configure the PIT period. */

    /* Enable the interrupt.  Global interrupts are disables at this point so
    this is safe. */
    AT91C_BASE_AIC->AIC_IECR = 0x1 << AT91C_ID_SYS;

//! This is the code I added !
    /* See if we can do timer interrupt here… */
    extern    void TimerSetup1(void);

//  (Here is the code from TimerSetup1()):
void    TimerSetup1(void) {
    AT91PS_TCB pTCB = AT91C_BASE_TCB;        // create a pointer to TC Global Register structure
    pTCB->TCB_BCR = 0;                        // SYNC trigger not used
    pTCB->TCB_BMR = AT91C_TCB_TC0XC0S_NONE | AT91C_TCB_TC1XC1S_NONE | AT91C_TCB_TC2XC2S_NONE;                    // external clocks not used   
    AT91PS_TC pTC = AT91C_BASE_TC1;        // create a pointer to channel 1 Register structure
    pTC->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;        // enable the clock    and start it

    pTC->TC_RC = 125;        // clk4
    pTC->TC_IER = AT91C_TC_CPCS;                // RC compare interrupt
    pTC->TC_IDR = ~AT91C_TC_CPCS;                // disable all except RC compare interrupt

// and here we are back to what is in port.c
    volatile AT91PS_AIC    pAIC = AT91C_BASE_AIC;       
    // enable the Timer’s peripheral clocks
    volatile AT91PS_PMC    pPMC = AT91C_BASE_PMC;            // pointer to PMC data structure
    pPMC->PMC_PCER = (1<<AT91C_ID_TC1);           
    // Set up the AIC  registers for Timer 1 
    extern void ( Timer1IrqHandler )( void );
    AT91F_AIC_ConfigureIt( AT91C_ID_TC1, 0x4, AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, ( void (*)(void) ) Timer1IrqHandler );

    pAIC->AIC_ICCR = (1<<AT91C_ID_TC1);                         // Clear the TC1 interrupt in AIC Interrupt Clear Command Register
    pAIC->AIC_IDCR &= (~(1<<AT91C_ID_TC1));                         // Remove disable timer 1 interrupt in AIC Interrupt Disable Command Reg           
    pAIC->AIC_IECR = (1<<AT91C_ID_TC1);                         // Enable the TC1 interrupt in AIC Interrupt Enable Command Register

Finally, here is the actual interrupt:
void Timer1IrqHandler( void ) __attribute__ ((interrupt ("IRQ")));
void Timer1IrqHandler (void) {
    volatile AT91PS_TC         pTC = AT91C_BASE_TC1;        // pointer to timer channel 1 register structure
    volatile AT91PS_PIO        pPIO = AT91C_BASE_PIOB;        // pointer to PIO register structure
    unsigned int             dummy;                        // temporary

    dummy = pTC->TC_SR;                                    // read TC1 Status Register to clear interrupt

// just a test function
    if (tickcount1++ >= 200)
        tickcount1 = 0;


The buzzer never sounds, although in the other Atmel example it buzzes just fine.

What am I missing???

rtel wrote on Monday, May 05, 2008:

Unfortunately I can’t go through the timer setup code just at the moment (this is SAM7 specific), but just to clarify, are you saying that the timer works fine with exactly the same code when not using a build, but never gets entered even once when using in your build?  Are there any significant differences in the way interrupts are handled within the startup code for the projects for which it works?  I cannot see why all the other interrupts would work (EMAC, UART, PIT) but not this one, unless there is some sort of peripheral resource sharing clash somewhere. 

The PIT uses the system interrupt, which is shared with the Debug COM port, are you using the Debug COM port?  Is the RTOS tick itnerrupt working when you also use your extra timer?


johnkpierce wrote on Monday, May 05, 2008:

To clarify,

1. - exactly the same code? - All the setup code is the same, although in the other examples I’m running the timer in THUMB mode and I changed it to compile in ARM mode with the added entry header as in the examples.
2. - “all the other interrupts.”  Unfortunately one of the clues is fuzzy, since I realized the UARTs are not using the interrupt examples, but just reading or writing the characters when the device buffer is ready.  Although it is true that the EMAC interrupt is working.
3. - The RTOS tick interrupt is working fine.
4. - You are right in pointing out the obvious, and I just need to go through and find the difference, I think, in setup.  I was hoping there was something basic I overlooked that someone would see.

Thanks for the feedback.  I’ll let you know if I find the issue.

johnkpierce wrote on Monday, May 05, 2008:

Well, the issue is solved.  It had to be fairly simple, I suppose.  The Atmel startup file had its own IRQ handler written in assembly code, which then vectors to the user’s routine.  The code just reads the AIC vector and branches to the routine.  The difference was that on return, the Atmel code clears the AIC interrupt, and the user’s routine has to do that in the environment.

Although, there is still a little quirk, which I will eventually track down and is not a show-stopper.  My timer setup routine is supposed to enable the timer and start it running, but after everything is set up I checked the timer and it wasn’t running.  So I wrote an extra enable+start command to the timer in my primary task, and then it started working…

Anyway, thanks.  It looks good now.