Raspberry Pi 2 Bare Metal with interrupts

This is a more advanced example that shows how to configure a timer interrupt on Raspberry Pi 2 “bare metal”. You will need IAR Embedded Workbench for ARM, and follow the Project Options steps described in the Raspberry Pi 2 Bare Metal with IAR Embedded Workbench example.

This example blinks the two on-board LEDs, so no need for extra hardware.

This is main.c

#include <stdint.h>
#include <string.h>
#include <intrinsics.h>
#include "bcm2836.h"
 
#define DELAYCNT    2500000
#define TIMERCNT    0x400
 
static void delay(uint32_t cnt);
static void timer_config(void);
 
volatile uint32_t toggle;
 
/*--------------------------------------------------*/
int main(void)
{
    GPFSEL4_bit.FSEL47 = GPFSEL_OUT;    // GPIO pin 47 = Output
    GPFSEL3_bit.FSEL35 = GPFSEL_OUT;    // GPIO pin 35 = Output
 
    timer_config();
 
    while (1) {
        GPSET1 = GPSET_47;              // Set GPIO pin 47
        delay(DELAYCNT);
        GPCLR1 = GPCLR_47;              // Clear GPIO pin 47
        delay(DELAYCNT);
    }
}
 
/*--------------------------------------------------*/
static void delay(uint32_t cnt)
{
    uint32_t i;
    volatile uint32_t a;
     
    for (i = 0; i < cnt; i++) {
        a = i;
    }
}
 
/*--------------------------------------------------*/
static void timer_config(void)
{
    // Copy reset vector to address 0x0 for interrupts to work
    memcpy((char*)&Reset_vector, (char*)BCM_START_ADDR, 
           sizeof(struct reset_struct));
     
    // Enable timer and interrupts
    Base_IRQ_enable_bit.ARM_Timer = 1;
    Timer_load = TIMERCNT;
     
    Timer_ctrl_bit.count16_23 = TIMER_CNT_23BIT;
    Timer_ctrl_bit.enable     = 1;
    Timer_ctrl_bit.irq_enable = 1;
    Timer_ctrl_bit.prescale   = TIMER_PRESCALE_256;
     
    __enable_interrupt();
}
 
/*--------------------------------------------------*/
__irq __arm void IRQ_Handler(void)
{
    Timer_irq_clear = 1;
 
    if (toggle) {
        GPSET1 = GPSET_35;
        toggle = 0;
    } else {
        GPCLR1 = GPCLR_35;
        toggle = 1;
    }
}

This is the header file bcm2836.h

#ifndef _BCM2836_H_
#define _BCM2836_H_
 
/* GPIO Function Select */
#define GPFSEL_IN           0x0;
#define GPFSEL_OUT          0x1;
#define GPFSEL_ALT0         0x4;
#define GPFSEL_ALT1         0x5;
#define GPFSEL_ALT2         0x6;
#define GPFSEL_ALT3         0x7;
#define GPFSEL_ALT4         0x3;
#define GPFSEL_ALT5         0x2;
 
#define GPSET0_ADDR         0x3F20001C
#define GPSET1_ADDR         0x3F200020

#define GPFSEL0_ADDR        0x3F200000
#define GPFSEL1_ADDR        0x3F200004
#define GPFSEL2_ADDR        0x3F200008
#define GPFSEL3_ADDR        0x3F20000C
#define GPFSEL4_ADDR        0x3F200010

#define GPCLR0_ADDR         0x3F200028
#define GPCLR1_ADDR         0x3F20002C

/* GPIO Function Select 0 */
volatile __no_init union
{
 uint32_t GPFSEL0;
 struct GPFSEL0_bits {
 uint32_t FSEL0 : 3;
 uint32_t FSEL1 : 3;
 uint32_t FSEL2 : 3;
 uint32_t FSEL3 : 3;
 uint32_t FSEL4 : 3;
 uint32_t FSEL5 : 3;
 uint32_t FSEL6 : 3;
 uint32_t FSEL7 : 3;
 uint32_t FSEL8 : 3;
 uint32_t FSEL9 : 3;
 uint32_t reserved : 2;
 } GPFSEL0_bit;
} @ GPFSEL0_ADDR;

 
/* GPIO Function Select 2 */
volatile __no_init union
{
    uint32_t GPFSEL2;
    struct GPFSEL2_bits {
        uint32_t FSEL20     : 3;
        uint32_t FSEL21     : 3;
        uint32_t FSEL22     : 3;
        uint32_t FSEL23     : 3;
        uint32_t FSEL24     : 3;
        uint32_t FSEL25     : 3;
        uint32_t FSEL26     : 3;
        uint32_t FSEL27     : 3;
        uint32_t FSEL28     : 3;
        uint32_t FSEL29     : 3;
        uint32_t reserved   : 2;
    } GPFSEL2_bit;
} @ GPFSEL2_ADDR;
 
/* GPIO Function Select 3 */
volatile __no_init union
{
    uint32_t GPFSEL3;
    struct GPFSEL3_bits {
        uint32_t FSEL30     : 3;
        uint32_t FSEL31     : 3;
        uint32_t FSEL32     : 3;
        uint32_t FSEL33     : 3;
        uint32_t FSEL34     : 3;
        uint32_t FSEL35     : 3;
        uint32_t FSEL36     : 3;
        uint32_t FSEL37     : 3;
        uint32_t FSEL38     : 3;
        uint32_t FSEL39     : 3;
        uint32_t reserved   : 2;
    } GPFSEL3_bit;
} @ GPFSEL3_ADDR;
 
/* GPIO Function Select 4 */
volatile __no_init union
{
    uint32_t GPFSEL4;
    struct GPFSEL4_bits {
        uint32_t FSEL40     : 3;
        uint32_t FSEL41     : 3;
        uint32_t FSEL42     : 3;
        uint32_t FSEL43     : 3;
        uint32_t FSEL44     : 3;
        uint32_t FSEL45     : 3;
        uint32_t FSEL46     : 3;
        uint32_t FSEL47     : 3;
        uint32_t FSEL48     : 3;
        uint32_t FSEL49     : 3;
        uint32_t reserved   : 2;
    } GPFSEL4_bit;
} @ GPFSEL4_ADDR;


/* GPIO Pin Output Set 0 */
volatile __no_init uint32_t GPSET0 @ GPSET0_ADDR;

#define GPSET_0       (1<<0)
#define GPSET_1       (1<<1)
#define GPSET_2       (1<<2)
#define GPSET_3       (1<<3)
#define GPSET_4       (1<<4)
#define GPSET_5       (1<<5)
#define GPSET_6       (1<<6)
#define GPSET_7       (1<<7)
#define GPSET_8       (1<<8)
#define GPSET_9       (1<<9)
#define GPSET_10      (1<<10)
#define GPSET_11      (1<<11)
#define GPSET_12      (1<<12)
#define GPSET_13      (1<<13)
#define GPSET_14      (1<<14)
#define GPSET_15      (1<<15)
#define GPSET_16      (1<<16)
#define GPSET_17      (1<<17)
#define GPSET_18      (1<<18)
#define GPSET_19      (1<<19)
#define GPSET_20      (1<<20)
#define GPSET_21      (1<<21)
#define GPSET_22      (1<<22)
#define GPSET_23      (1<<23)
#define GPSET_24      (1<<24)
#define GPSET_25      (1<<25)
#define GPSET_26      (1<<26)
#define GPSET_27      (1<<27)
#define GPSET_28      (1<<28)
#define GPSET_29      (1<<29)
#define GPSET_30      (1<<30)
#define GPSET_31      (1<<31)

/* GPIO Pin Output Set 1 */
volatile __no_init uint32_t GPSET1 @ GPSET1_ADDR;

#define GPSET_32      (1<<0)
#define GPSET_33      (1<<1)
#define GPSET_34      (1<<2)
#define GPSET_35      (1<<3)
#define GPSET_36      (1<<4)
#define GPSET_37      (1<<5)
#define GPSET_38      (1<<6)
#define GPSET_39      (1<<7)
#define GPSET_40      (1<<8)
#define GPSET_41      (1<<9)
#define GPSET_42      (1<<10)
#define GPSET_43      (1<<11)
#define GPSET_44      (1<<12)
#define GPSET_45      (1<<13)
#define GPSET_46      (1<<14)
#define GPSET_47      (1<<15)
#define GPSET_48      (1<<16)
#define GPSET_49      (1<<17)
#define GPSET_50      (1<<18)
#define GPSET_51      (1<<19)
#define GPSET_52      (1<<20)
#define GPSET_53      (1<<21)

/* GPIO Pin Output Clear 0 */
volatile __no_init uint32_t GPCLR0 @ GPCLR0_ADDR;

#define GPCLR_0       (1<<0)
#define GPCLR_1       (1<<1)
#define GPCLR_2       (1<<2)
#define GPCLR_3       (1<<3)
#define GPCLR_4       (1<<4)
#define GPCLR_5       (1<<5)
#define GPCLR_6       (1<<6)
#define GPCLR_7       (1<<7)
#define GPCLR_8       (1<<8)
#define GPCLR_9       (1<<9)
#define GPCLR_10      (1<<10)
#define GPCLR_11      (1<<11)
#define GPCLR_12      (1<<12)
#define GPCLR_13      (1<<13)
#define GPCLR_14      (1<<14)
#define GPCLR_15      (1<<15)
#define GPCLR_16      (1<<16)
#define GPCLR_17      (1<<17)
#define GPCLR_18      (1<<18)
#define GPCLR_19      (1<<19)
#define GPCLR_20      (1<<20)
#define GPCLR_21      (1<<21)
#define GPCLR_22      (1<<22)
#define GPCLR_23      (1<<23)
#define GPCLR_24      (1<<24)
#define GPCLR_25      (1<<25)
#define GPCLR_26      (1<<26)
#define GPCLR_27      (1<<27)
#define GPCLR_28      (1<<28)
#define GPCLR_29      (1<<29)
#define GPCLR_30      (1<<30)
#define GPCLR_31      (1<<31)


/* GPIO Pin Output Clear 1 */
volatile __no_init uint32_t GPCLR1 @ GPCLR1_ADDR;

#define GPCLR_32      (1<<0)
#define GPCLR_33      (1<<1)
#define GPCLR_34      (1<<2)
#define GPCLR_35      (1<<3)
#define GPCLR_36      (1<<4)
#define GPCLR_37      (1<<5)
#define GPCLR_38      (1<<6)
#define GPCLR_39      (1<<7)
#define GPCLR_40      (1<<8)
#define GPCLR_41      (1<<9)
#define GPCLR_42      (1<<10)
#define GPCLR_43      (1<<11)
#define GPCLR_44      (1<<12)
#define GPCLR_45      (1<<13)
#define GPCLR_46      (1<<14)
#define GPCLR_47      (1<<15)
#define GPCLR_48      (1<<16)
#define GPCLR_49      (1<<17)
#define GPCLR_50      (1<<18)
#define GPCLR_51      (1<<19)
#define GPCLR_52      (1<<20)
#define GPCLR_53      (1<<21)


 
#define BASE_IRQ_ADDR       0x3F00B218
 
/* Base Interrupt enable register */
volatile __no_init union
{
    uint32_t Base_IRQ_enable;
    struct Base_IRQ_enable_bits {
        uint32_t ARM_Timer          : 1;
        uint32_t ARM_Mailbox        : 1;
        uint32_t ARM_Doorbell_0     : 1;
        uint32_t ARM_Doorbell_1     : 1;
        uint32_t GPU_0_Halted       : 1;
        uint32_t GPU_1_Halted       : 1;
        uint32_t Access_err_type_1  : 1;
        uint32_t Access_err_type_0  : 1;
    } Base_IRQ_enable_bit;
} @ BASE_IRQ_ADDR;
 
 
#define TIMER_CNT_23BIT             1
#define TIMER_CNT_16BIT             0
 
#define TIMER_PRESCALE_256          2
 
#define TIMER_LOAD_ADDR             0x3F00B400
#define TIMER_CLR_ADDR              0x3F00B40C
#define TIMER_CTRL_ADDR             0x3F00B408
 
/* Timer Load register */
volatile __no_init uint32_t Timer_load @ TIMER_LOAD_ADDR;
 
/* Timer IRQ clear register */
volatile __no_init uint32_t Timer_irq_clear @ TIMER_CLR_ADDR;
 
/* Timer control register */
volatile __no_init union
{
    uint32_t Timer_ctrl;
    struct Timer_ctrl_bits {
        uint32_t unused1            : 1;
        uint32_t count16_23         : 1;
        uint32_t prescale           : 2;
        uint32_t unused2            : 1;
        uint32_t irq_enable         : 1;
        uint32_t unused3            : 1;
        uint32_t enable             : 1;
        uint32_t debug_halt         : 1;
        uint32_t free_run           : 1;
        uint32_t unused4            : 6;
        uint32_t free_prescale      : 8;
    } Timer_ctrl_bit;
} @ TIMER_CTRL_ADDR;
 
 
#define BCM_START_ADDR 0x00008000
#define BCM_VECT_ADDR  0x00000000
 
volatile __no_init struct reset_struct
{
    uint32_t Reset;
    uint32_t Undefined;
    uint32_t SWI;
    uint32_t Prefetch;
    uint32_t Abort;
    uint32_t Reserved;
    uint32_t IRQ;
    uint32_t FIQ;
    uint32_t jump_table[7];
} Reset_vector @ BCM_VECT_ADDR;
 
 
#endif

More reading / details / thanks to:
https://github.com/dwelch67/raspberrypi
http://www.valvers.com/open-software/raspberry-pi/step01-bare-metal-programming-in-cpt1/

Tags: ,

2 Responses to “Raspberry Pi 2 Bare Metal with interrupts”

  1. Sohail Anjum Says:

    Hello,
    I have tried this example but interrupt is not working however timer is working fine. If just skip the __enable_interrupt(); function call then LED is working fine but when interrupt is enabled then when interrupt is called system is hang-up. It seems that something wrong with vector table. Can you help me to resolve this issue? Which steps I am missing?
    Thanks

    • kjarvel Says:

      Hi, it is difficult to say what’s wrong. Perhaps you forgot to call memcpy in timer_config – the one that has the comment “Copy reset vector to address 0x0 for interrupts to work”. Or, maybe you got the addresses mixed up. I recommend using a debugger to find the issue.

Leave a comment