2024-11-08 10:51 AM - last edited on 2024-11-08 10:55 AM by SofLit
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!
2024-11-08 04:26 PM
> getting the led blinking via register writes is still important to me
A simple blinky with couple of threads is a simple demo that just demonstrates the use of threadx. If this is your final goal, fine.
The debugger lets you step thru the program and peek in various registers and memory, thus to verify your assumptions. But it is a non-trivial tool and has a learning ramp.
2024-11-09 02:18 AM - edited 2024-11-09 02:18 AM
LED on the Nucleo32 is at PB3, not at PB13 (PB13 is not available at the 32-pin package at all).
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).
2024-11-09 04:21 AM
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?
2024-11-09 07:28 AM - edited 2024-11-09 07:31 AM
> 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
2024-11-09 08:23 AM
> 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.
2024-11-10 07:34 AM
> 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