cancel
Showing results for 
Search instead for 
Did you mean: 

NUCLEO-L011K4 Low Level Blinky not working

tim-hilt
Visitor

I'm starting to learn about low-level embedded development. My goal is to learn how I can write my own linker scripts and startup code. I also want to learn how to use raw register calls to do something useful. I want to use the NUCLEO-L011K4 board and tried reading through the reference manual to find register addresses and the expected values.

For some reason, my minimal blinky (LED connected to PB13) app doesn't work. It flashes just fine, but the board does nothing after that.

I am pretty sure (keep in mind: Still a newbie), that the registers are the right ones. However, I'm not so sure about the linker script.

Here's my linker script:

 

MEMORY 
{
    FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 16K
    RAM   (rwx) : ORIGIN = 0x20000000, LENGTH = 2K
}

ENTRY(main)
__reset_stack_pointer = ORIGIN(RAM) + LENGTH(RAM);

SECTIONS {
    .text : {
        LONG(__reset_stack_pointer);
        LONG(main | 1);
        /* The whole interrupt table is 332 bytes long. Advance to that position. */
        . += 332;
        *(.text)
        *(.rodata*)
        . = ALIGN(4);
    } > FLASH
}

 

and here's my main.c:

 

void main(void)
{
  volatile int *RCC_IOPENR = (int *)0X4002102C;
  volatile int *GPIOB_MODER = (int *)0X50000400;
  volatile int *GPIOB_ODR = (int *)0X50000410;

  *RCC_IOPENR |= 0b10;        // Enable GPIO Port B
  *GPIOB_MODER |= 0b10 << 26; // Set Pin 13 as output

  while (1)
  {
    *GPIOB_ODR ^= 0x0020; // Toggle output Pin

    for (int i = 0; i < 1000000; i++)
    {
      __asm__("nop");
    }
  }
}

 

I compile this code with

 

arm-none-eabi-gcc main.c -T LinkerScript.ld -o blink.elf -mcpu=cortex-m0plus -mthumb -nostdlib -Os

 

and I flash it to the target using the integrated STLink and this OpenOCD command:

 

openocd -f interface/stlink.cfg -f target/stm32l0.cfg -c "program blink.elf verify reset exit"

 

The commands are adapted from this blogpost, which is for the blue pill board and Zig. I adapted the code to C and it worked as expected.

I'm not sure if there are other things required in the linker script or if the size of the NVIC table is a different one for the L011K4. I have not found its size in any documentation.

Please tell me if this is not the right place to ask such questions. This is my first post to the forum and I'm still trying to find my way around.

Thanks!

5 REPLIES 5
gbm
Lead III

1. Use ST headers with register/bit definitions to avoid extra work and extra mistakes.

2. 0b10 MODER field means AF, 0b01 is OUT.

3. xor with 0x20 toggles bit 7, not 13.

4. Don't write to ODR, use BSRR and BRR to change output state.

5. Your "delay loop" may or may not delay at all.

6. Vector table size is 48 words for every Cortex-M0(+).

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice

@gbm wrote:

5. Your "delay loop" may or may not delay at all.


Need to declare i as volatile.

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.
Pavel A.
Evangelist III

>My goal is to learn how I can write my own linker scripts and startup code. I also want to learn how to use raw register calls to do something useful.

Quite strange goal. These days, folks learn edge AI, vision, motor control, communication. All for drones and robotics, all for the victory. Linker scripts... Chatgpt will write you a dozen, and explain.

 

Thanks for taking the time to respond! That's already really helpful. I'll gladly switch to the ST headers, once I've learned to properly read a reference manual and use the register addresses.


@gbm wrote:

4. Don't write to ODR, use BSRR and BRR to change output state.


why do you recommend this? I mainly used ODR because that was used in the blogpost I used to get the blue pill working.

Apart from that, I made the following changes to the code:

  1. Applied your suggestions
  2. Added .data and .bss as well as some symbols to the linkerscript (taken from here)
  3. Added init code for .bss and .data

Here's the code:

// LinkerScript.ld
MEMORY 
{
    FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 16K
    RAM   (rwx) : ORIGIN = 0x20000000, LENGTH = 2K
}

ENTRY(main)
__reset_stack_pointer = ORIGIN(RAM) + LENGTH(RAM);

SECTIONS {
    .text : {
        . = ALIGN(4);
        LONG(__reset_stack_pointer);
        LONG(main | 1);
        /* The whole interrupt table is 96 bytes long. Advance to that position. */
        . += 96;
        *(.text)
        *(.rodata*)
        . = ALIGN(4);
        _etext = .;
    } > FLASH

    .data : {
        . = ALIGN(4);
        _sdata = .;
        *(.data)
        . = ALIGN(4);
        _edata = .;
    } >RAM AT >FLASH

    .bss : {
        . = ALIGN(4);
        _sbss = .;
        *(.bss)
        . = ALIGN(4);
        _ebss = .;
     } >RAM
}

// main.c
extern unsigned int _etext, _sdata, _edata, _sbss, _ebss;

void init_memory(void)
{
  unsigned int data_size = (unsigned int)&_edata - (unsigned int)&_sdata;
  unsigned char *flash_data = (unsigned char *)&_etext;
  volatile unsigned char *sram_data = (unsigned char *)&_sdata;

  for (unsigned int i = 0; i < data_size; i++)
  {
    sram_data[i] = flash_data[i];
  }

  unsigned int bss_size = (unsigned int)&_ebss - (unsigned int)&_sbss;
  volatile unsigned char *bss = (unsigned char *)&_sbss;

  for (unsigned int i = 0; i < bss_size; i++)
  {
    bss[i] = 0;
  }
}

void main(void)
{
  init_memory();

  volatile int *RCC_IOPENR = (int *)0X4002102C;
  volatile int *GPIOB_MODER = (int *)0X50000400;
  volatile int *GPIOx_BSRR = (int *)0X50000418;

  *RCC_IOPENR |= 0b10;        // Enable GPIO Port B
  *GPIOB_MODER |= 0b01 << 26; // Set Pin 13 as output

  while (1)
  {
    *GPIOx_BSRR ^= 0x2000; // Switch pin on
    for (volatile int i = 0; i < 1000000; i++)
      ;
    *GPIOx_BSRR ^= 0x2000 << 16; // Switch pin off
    for (volatile int i = 0; i < 1000000; i++)
      ;
  }
}

The compile and flash commands haven't changed.

Thanks, that's a good suggestion. I know, that I should better be using timers for software delays. I'll add that, once I get at least something working :D