Skip to main content
Explorer
November 8, 2024
Solved

NUCLEO-L011K4 Low Level Blinky not working

  • November 8, 2024
  • 5 replies
  • 2470 views

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!

    This topic has been closed for replies.
    Best answer by gbm

    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(+).

    5 replies

    gbmAnswer
    Graduate
    November 8, 2024

    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(+).

    Technical Moderator
    November 8, 2024

    @gbm wrote:

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


    Need to declare i as volatile.

    tim-hiltAuthor
    Explorer
    November 8, 2024

    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

    Super User
    November 8, 2024

    >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.

     

    tim-hiltAuthor
    Explorer
    November 8, 2024

    I've succesfully applied AI, vision and vector DBs before, but want to understand small systems better. I guess I have a preference for compact solutions. The ultimate goal would be to try and see if ThreadX can run smoothly on the L011K4.


    Super User
    November 8, 2024

     The ultimate goal would be to try and see if ThreadX can run smoothly on the L011K4.

    This is the question you should've asked first.

    /* My answer would be negative, because 'L0 has only up to 20K (S)RAM - perhaps enough for a simple demo, but too little for a real life project with a RTOS */

    And may I introduce you to the debugger? tim-hilt, please meet the debugger, your best friend after chatgpt, and star of many youtube videos! ))

    Super User
    November 9, 2024

    LED on the Nucleo32 is at PB3, not at PB13 (PB13 is not available at the 32-pin package at all).

    waclawekjan_1-1731147311306.png

    JW

    PS.

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

    +1.

    Also, I recommend to use both startup code and linker scripts provided by ST in the CMSIS-mandated files and also the examples in CubeL0, again to avoid extra mistakes (e.g. with the interrupt vectors).

     

    tim-hiltAuthor
    Explorer
    November 9, 2024

    Oh no! I feel really dumb. I looked at the nucleo32 board description and mixed up the Arduino nano pin description (D13) with the stm32 pin (pb3). I’m not at my desk right now, so can’t try immediately.

     

    I recommend to use both startup code and linker scripts provided by ST in the CMSIS-mandated files and also the examples in CubeL0, again to avoid extra mistakes (e.g. with the interrupt vectors).

    I tried that, but reverted, because 1. the binary-size blew up 2. the startup code required me to add a lot of additional headers and a few source files.

    is this expected or did I get something wrong?

    Super User
    November 9, 2024

    > 1. the binary-size blew up

    Like by kilobytes, or by say 192 bytes (because 48 words is not 96 bytes ;) ) ?

    > 2. the startup code required me to add a lot of additional headers and a few source files.

    a) you *do* want the CMSIS headers (core_cmXX.h, core_cmInstr, core_cmFunc.h, core_cmSimd.h, and the gcc-specific cmsis_gcc.h), they provide useful CMSIS intrinsics like __NOP() and functions like NVIC_EnableIRQ () etc.

    b) you can avoid ST's system_stm32XXxx.h by simply defining an empty file with the same name

    c) you can delete the calls from the startup file which you don't want.

    My take on STM32 basic development environment here. YMMV.

    JW

     

    tim-hiltAuthor
    Explorer
    November 9, 2024

    because 48 words is not 96 bytes ;)

    oh no! Learned something again. I guess that’s what you get when you google „bytes vs words“ and just believe whatever Google previews at the top (I.e. a word is two bytes)

    The binary size actually blew from around 400 Bytes to around 2300 Bytes. I haven’t checked what the startup file actually does though. I just discarded it an thought „all that stuff probably isn’t required“.

    Thanks for linking to your post! I’ll read it later today.

    Super User
    November 10, 2024

    > just believe whatever Google previews at the top (I.e. a word is two bytes)

    Well, that's web for you; google is just diluted web and AI is diluted google.

    What constitutes a "word" is not set in stone. Often it is equal to the [typical] data width of the processor in question, but .for example in the 8-bit world quite often word means 16-bits. (Somewhat similarly to how sizeof(int) in C is not a fixed value).

    If you are young, then it may surprise you the fact that the *byte* width is not set in stone, either. These days it's quite reliably 8 bits, but it used to be anything between 6 and 72 bits in the past. That's why for example the internet "norms" (RFCs) tend to stick to the term "octet" when it comes to 8-bit long values.

    JW