Search code examples
cpointersnrf52

Using pointers for direct register access


I'm trying to use pointers to directly access the registers in a nrf52840 usb dongle. I'm using Segger Embedded Studio to compile and link everything, but it seems to generate incorrect RAM and Flash locations. After editing the linker file and recompiling, everything seems to be in the right place; however, after programming (done with NRF Connect) the LED still doesn't light. My thought is that I may be incorrectly addressing the registers. Can anyone tell me if I'm using the pointers correctly?

Note: Programming the nrf52840 usb dongle cannot be done with Segger Embedded Studio because the nrf52840 usb dongle does not have a debugger.

LED_Test.c

// RGB LED at pins 22(G), 23(R), and 24(B)

// Addresses to registers, tasks, and events for the clock
#define CLOCK_BASE_ADDRESS     0x40000000
#define TASKS_HFCLKSTART_OFFSET     0x000
#define TASKS_LFCLKSTART_OFFSET     0x008
#define EVENTS_HFCLKSTARTED_OFFSET  0x100
#define EVENTS_LFCLKSTARTED_OFFSET  0x104
#define LFCLKSRC_ADDRESS_OFFSET     0x518

// Addresses to registers, tasks, and events for the GPIO
#define GPIO_BASE_ADDRESS      0x50000000
#define OUTSET_ADDRESS_OFFSET       0x508  //  1's written to this register set corresponding pins (HIGH). 0's have no effect.
#define DIRSET_ADDRESS_OFFSET       0x518  //  1's written to this register setup corresponding pins as OUTPUT. 0's have no effect.

volatile unsigned long * startHFClk_reg     = (volatile unsigned long *)CLOCK_BASE_ADDRESS  + TASKS_HFCLKSTART_OFFSET;
volatile unsigned long * HFClkStarted_reg   = (volatile unsigned long *)CLOCK_BASE_ADDRESS  + EVENTS_HFCLKSTARTED_OFFSET;
volatile unsigned long * LFClkSource_reg    = (volatile unsigned long *)CLOCK_BASE_ADDRESS  + LFCLKSRC_ADDRESS_OFFSET;
volatile unsigned long * startLFClk_reg     = (volatile unsigned long *)CLOCK_BASE_ADDRESS  + TASKS_LFCLKSTART_OFFSET;
volatile unsigned long * LFClkStarted_reg   = (volatile unsigned long *)CLOCK_BASE_ADDRESS  + EVENTS_LFCLKSTARTED_OFFSET;

volatile unsigned long * setupOutputs_reg   = (volatile unsigned long *)GPIO_BASE_ADDRESS   + DIRSET_ADDRESS_OFFSET;
volatile unsigned long * setPins_reg        = (volatile unsigned long *)GPIO_BASE_ADDRESS   + OUTSET_ADDRESS_OFFSET;

void main(void){

    *startHFClk_reg     = 0x01;         //  Start external 64 MHz crystal oscillator
    while(!HFClkStarted_reg){}
    *LFClkSource_reg    = 0x01;         //  LF oscillator source = external xtal
    *startLFClk_reg     = 0x01;         //  Start external 32.768 kHz crystal oscillator
    while(!LFClkStarted_reg){}

    *setupOutputs_reg   = 0x01C00000;   //  Make pins 22, 23, and 24 OUTPUT

    for(;;){
        *setPins_reg    = 0x00400000;   //  Make pin 22 HIGH
    }
}

Solution

  • The offsets of your registers seem to be byte offsets from your base address.

    Then you create your pointers like this:

    volatile unsigned long * startHFClk_reg     = (volatile unsigned long *)CLOCK_BASE_ADDRESS  + TASKS_HFCLKSTART_OFFSET;
    

    You create a volatile unsigned long * from your CLOCK_BASE_ADDRESS and then add the offset. Here the rules for pointer arithmetics are applied. You add TASKS_HFCLKSTART_OFFSET * sizeof(unsigned long) to the base address.

    To avoid this, try

    volatile unsigned long * startHFClk_reg     = (volatile unsigned long *)(CLOCK_BASE_ADDRESS  + TASKS_HFCLKSTART_OFFSET);