BLE application with nRF51822: Startup code and the Main entry

If you remember what we talked about in the previous post, before running the application, the processor needs to setup several things, like initialize the “.data” section in RAM and set “.bss” values to zero. Also if you remember we defined a section for the ISR vectors, so now we need to declare all the nRF51822 supported ISR.

  • Create a startup.c file in startup folder:
        $ cd nrf51
        $ mkdir startup
        $ cd startup
        $ touch startup.c

We need to declare few variables to properly setup “.data”, “.bss” sections and the top of the stack address. For your information, these variables are defined and set by the linker script. We also need to declare the main function as an extern function.

extern unsigned long _etext; // End of the code section
extern unsigned long _sdata; // Start address for the .data section
extern unsigned long _edata; // End address for the .data section
extern unsigned long _sbss; // Start address for the .bss section
extern unsigned long _ebss; // End address for the .bss section
extern void _end_of_stack; // Top of stack

extern int main(void);

I’ll speak later about how to setup the nRF51 SDK provided by Nordic but the following informations are coming from the SDK source code. In the linker script we declare a specific section for the interrupt vectors. As we need to have access to these vectors in our application we can declare a function pointer table in startup.c. In that way we will be able to implement these function later in our code.

  • ISR vectors prototypes:
void default_irq(void);
void Reset_Handler(void);
void NMI_Handler(void) __attribute__ ((weak, alias ("default_irq")));
void HardFault_Handler(void) __attribute__ ((weak, alias ("default_irq")));
void SVC_Handler(void) __attribute__ ((weak, alias ("default_irq")));
void PendSV_Handler(void) __attribute__ ((weak, alias ("default_irq")));
void SysTick_Handler(void) __attribute__ ((weak, alias ("default_irq")));
void POWER_CLOCK_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));
void RADIO_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));
void UART0_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));
void SPI0_TWI0_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));
void SPI1_TWI1_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));
void GPIOTE_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));
void ADC_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));
void TIMER0_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));
void TIMER1_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));
void TIMER2_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));
void RTC0_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));
void TEMP_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));
void RNG_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));
void ECB_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));
void CCM_AAR_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));
void WDT_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));
void RTC1_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));
void QDEC_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));
void LPCOMP_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));
void SWI0_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));
void SWI1_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));
void SWI2_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));
void SWI3_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));
void SWI4_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));
void SWI5_IRQHandler(void) __attribute__ ((weak, alias ("default_irq")));

As you can see the attribute weak and alias are used for the ISR vector prototypes. Actually for an application we don’t necessary need all the IRQ, so by default, thanks to alias attribute, the function called in case of interruption will be default_irq. The weak attribute means that we can override this function. For example POWER_CLOCK_IRQHandler can either point to default_irq (in case we don’t used it in our code) or point to POWER_CLOCK_IRQHandler if we have implemented it.

  • ISR vectors table:
__attribute__ ((section(".isr_vector")))
void (* const g_pfnVectors[])(void) =
{
	&_end_of_stack,        // The initial stack pointer
	Reset_Handler,         // Reset handler
	NMI_Handler,           // NMI handler
	HardFault_Handler,     // Hard fault handler
	0,0,0,0,0,0,0,         // 7 unused addresses   
	SVC_Handler,           // SVC handler
	0,0,                   // 2 unused addresses
	PendSV_Handler,        // Pend SV handler
	SysTick_Handler,       // Sys tick handler
	POWER_CLOCK_IRQHandler,// Power clock handler
	RADIO_IRQHandler,      // Radio handler
	UART0_IRQHandler,      // Uart 0 handler
	SPI0_TWI0_IRQHandler,  // SPI 0 handler
	SPI1_TWI1_IRQHandler,  // SPI 1 handler
	0,                     // 1 unused address
	GPIOTE_IRQHandler,     // GPIO handler
	ADC_IRQHandler,        // ADC handler
	TIMER0_IRQHandler,     // TIMER 0 handler
	TIMER1_IRQHandler,     // TIMER 1 handler
	TIMER2_IRQHandler,     // TIMER 2 handler
	RTC0_IRQHandler,       // RTC0 handler
	TEMP_IRQHandler,       // TEMP handler
	RNG_IRQHandler,        // RNG handler
	ECB_IRQHandler,        // ECB handler
	CCM_AAR_IRQHandler,    // CCM AAR handler
	WDT_IRQHandler,        // WDT handler
	RTC1_IRQHandler,       // RTC1 handler
	QDEC_IRQHandler,       // QDEC handler
	LPCOMP_IRQHandler,     // LDCOMP handler
	SWI0_IRQHandler,       // SWI0 handler
	SWI1_IRQHandler,       // SWI1 handler
	SWI2_IRQHandler,       // SWI2 handler
	SWI3_IRQHandler,       // SWI3 handler
	SWI4_IRQHandler,       // SWI4 handler
	SWI5_IRQHandler,       // SWI5 handler
	0,0,0,0,0,0,           // 6 unused addresses
};

The attribute section is used to explicitly tell the compiler to place this tab into “.isr_section”.

Now we have declared all the variables and prototype functions in our code we can implement the Reset_Handler function. On ARM Cortex M0 architecture the first instruction called is the reset IRQ. In that function we have to initialize “.data” and “.bss” sections.

  • Reset handler:
void Reset_Handler(void)
{
	unsigned long *src, *dest;
	
	src = &_etext; // place pointer to the end of .text section, data value are stored after 
                       // code section
	
	//init RAM data with value contained in FLASH
	for(dest = &_sdata; dest < &_edata; )
	{
		*(dest++) = *(src++);
	}
	
	//init .bss section with 0 value
	for(dest = &_sbss; dest < &_ebss; )
	{
		*(dest++) = 0;
	}
	
	main(); // call main application entry point
}

The last function we need to implement is the default_isr one. This is a dummy function just here in case an interrupt is triggered without the appropriate handler.

  • Default isr handler:
void default_irq(void){
    // place warning debug here
}

This is it for the startup code. The last function called by the reset handler is the main function which is the entry point of your application. So we will create a main.c file and we will place it in led_blinking/src folder.

  • Create main.c file in led_blinking/src:
        $ cd nrf51</code>
        $ mkdir led_blinking
        $ cd led_blinking
        $ mkdir src
        $ cd src
        $ touch main.c

In the main.c source file we only for the moment declare the main function.

  • main.c:
int main(void){
    return 0;
}

To resume, to properly boot up the processor we need to retrieve specific memory addresses and sections from the linker script and then declare the interrupt vectors table and finally implement the reset handler to be able to call the main function of our application.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s