======Writing a minimal C program for the STM32 Primer====== [[arm_cortex-m3:stm32:basics|Cortex-M3 basics]] and [[arm_cortex-m3:stm32:stm32-circle|Primer programming with OpenOCD]] provides the background material for this section. =====Blink the RED LED on the Primer!===== The Primer-1's RED LED is connected to GPIO Port B, pin 9 (PB9). Putting this on requires: *Enabling the peripheral clock to GPIO PORTB *Configuring PB9 as a push-pull output *Putting ON PB9 by writing to the BSRR (Bit Set/Reset register) All these things will be done by our //main// program whose address will be stored at memory location 0x4. We will store 0x20001000 at 0x0 - the processor will use this value to initialize the stack pointer register. ====Installing the toolchain==== CodeSourcery provides an easy to use installer for their GNU ARM toolchain - [[http://www.codesourcery.com/sgpp/lite/arm/portal/release642|download Sourcery G++ Lite 2008q3-66 from here]] it and simply execute the .bin file to get started with the installation. ====Building the code==== /* * Red LED on PB9, Green on PB8. We will put on Red * * GPIOB_CRH is initially 0x44444444 (reset state, all pins inputs * and floating) * * We will change GPIOB_CRH to 0x44444414 (PB9 push-pull output) * * It is possible to make PB9 go high without touching any of the * other pins by using the GPIOB_BSRR - we will set the 9th bit of * this register to set PB9; ie, we will write (1 << 9) to GPIOB_BSRR * * GPIOB registers start at 0x40010c00. * * GPIOB_CRH is at offset 0x4 and GPIOB_BSRR is at offset 0x10 * * The peripheral clock register has to be configured to enable clock * to GPIOB. This is done by setting bit 3 of RCC_APB2ENR. * * The clock registers start at 0x40021000 and RCC_APB2ENR is at * offset 0x018. * * The STM32F103RB processor has 128Kb flash and 20Kb RAM. * * 20Kb is 0x5000. RAM starts at 0x20000000. Lets initialize stack * pointer to 0x20001000. (4Kb stack) * */ #define GPIOB_CRH (*((volatile unsigned long*)(0x40010c00 + 0x4))) #define GPIOB_BSRR (*((volatile unsigned long*)(0x40010c00 + 0x10))) #define RCC_APB2ENR (*((volatile unsigned long*)(0x40021000 + 0x018))) __asm__(".word 0x20001000"); __asm__(".word main"); main() { unsigned int c = 0; RCC_APB2ENR = (1 << 3); GPIOB_CRH = 0x44444414; while(1) { GPIOB_BSRR = (1 << 9); // ON for(c = 0; c < 100000; c++); GPIOB_BSRR = (1 << 25); // OFF for(c = 0; c < 100000; c++); } } We need a //linker script// to compile the code into an a.out (it's also possible to manage without a linker script with a slightly different approach). Here is a sample script, //simple.lds// SECTIONS { . = 0x0; .text : { *(.text) } } The code is compiled into an a.out by running: arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -c blink.c arm-none-eabi-ld -T simple.lds blink.o It's essential that we peek into the executable to verify that things are laid out in memory the way they should be - this is done by running //objdump//: arm-none-eabi-objdump -D a.out This is part of the output we get: a.out: file format elf32-littlearm Disassembly of section .text: 00000000 : 0: 1000 asrs r0, r0, #32 2: 2000 movs r0, #0 4: 0009 lsls r1, r1, #0 ... 00000008
: 8: b480 push {r7} a: b083 sub sp, #12 c: af00 add r7, sp, #0 e: f04f 0300 mov.w r3, #0 ; 0x0 12: 607b str r3, [r7, #4] 14: f241 0318 movw r3, #4120 ; 0x1018 18: f2c4 0302 movt r3, #16386 ; 0x4002 1c: f04f 0208 mov.w r2, #8 ; 0x8 20: 601a str r2, [r3, #0] 22: f640 4204 movw r2, #3076 ; 0xc04 26: f2c4 0201 movt r2, #16385 ; 0x4001 2a: f244 4314 movw r3, #17428 ; 0x4414 2e: f2c4 4344 movt r3, #17476 ; 0x4444 32: 6013 str r3, [r2, #0] 34: f640 4310 movw r3, #3088 ; 0xc10 38: f2c4 0301 movt r3, #16385 ; 0x4001 3c: f44f 7200 mov.w r2, #512 ; 0x200 40: 601a str r2, [r3, #0] 42: f04f 0300 mov.w r3, #0 ; 0x0 46: 607b str r3, [r7, #4] 48: e003 b.n 52 4a: 687b ldr r3, [r7, #4] 4c: f103 0301 add.w r3, r3, #1 ; 0x1 50: 607b str r3, [r7, #4] 52: 687a ldr r2, [r7, #4] 54: f248 639f movw r3, #34463 ; 0x869f 58: f2c0 0301 movt r3, #1 ; 0x1 5c: 429a cmp r2, r3 5e: d9f4 bls.n 4a 60: f640 4310 movw r3, #3088 ; 0xc10 64: f2c4 0301 movt r3, #16385 ; 0x4001 68: f04f 7200 mov.w r2, #33554432 ; 0x2000000 6c: 601a str r2, [r3, #0] 6e: f04f 0300 mov.w r3, #0 ; 0x0 72: 607b str r3, [r7, #4] 74: e003 b.n 7e 76: 687b ldr r3, [r7, #4] 78: f103 0301 add.w r3, r3, #1 ; 0x1 7c: 607b str r3, [r7, #4] 7e: 687a ldr r2, [r7, #4] 80: f248 639f movw r3, #34463 ; 0x869f We will use //objcopy// to convert our //a.out// into a plain binary file: arm-none-eabi-objcopy -j .text a.out blink.bin -O binary Before writing the code to flash, let's verify it using //octal dump//: od -t x1 blink.bin Here is the output: 0000000 00 10 00 20 09 00 00 00 80 b4 83 b0 00 af 4f f0 0000020 00 03 7b 60 41 f2 18 03 c4 f2 02 03 4f f0 08 02 0000040 1a 60 40 f6 04 42 c4 f2 01 02 44 f2 14 43 c4 f2 0000060 44 43 13 60 40 f6 10 43 c4 f2 01 03 4f f4 00 72 0000100 1a 60 4f f0 00 03 7b 60 03 e0 7b 68 03 f1 01 03 0000120 7b 60 7a 68 48 f2 9f 63 c0 f2 01 03 9a 42 f4 d9 0000140 40 f6 10 43 c4 f2 01 03 4f f0 00 72 1a 60 4f f0 0000160 00 03 7b 60 03 e0 7b 68 03 f1 01 03 7b 60 7a 68 0000200 48 f2 9f 63 c0 f2 01 03 9a 42 f4 d9 d2 e7 c0 46 0000220 Let's look at the first four bytes - they represent 0x20001000 in little endian - these are the 4 bytes going to be stored at location 0x0 in the flash memory of the STM32 processor (to be used as the initial value for the stack pointer register). The value of the next 4 bytes will be used by the Cortex processor to fetch the first instruction from memory (the address of the first instruction in main). The output from //objdump// tells us that the first instruction in //main// is at address 0x8 - but we are seeing 0x9 here - don't be surprised - that's the way it should be! The instruction set requires that the least significant bit of the jump address should be 1. We can run through the rest of the //octal dump// and compare it with the machine code bytes in the .text section of the //objdump// - just to assure ourselves that nothing weird has occurred. Now, we have to get the code into the flash memory of the processor using OpenOCD: $ telnet localhost 4444 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. Open On-Chip Debugger > halt > stm32x unlock 0 stm32x unlocked > flash erase_sector 0 0 0 erased sectors 0 through 0 on flash bank 0 in 0.100966s > flash write_bank 0 /home/fosstronics/projects/stm32/mar1/blink.bin 0 wrote 144 byte from file /home/fosstronics/projects/stm32/mar1/blink.bin to flash bank 0 at offset 0x00000000 in 1.510967s (0.093070 kb/s) > reset JTAG tap: stm32.cpu tap/device found: 0x3ba00477 (Manufacturer: 0x23b, Part: 0xba00, Version: 0x3) JTAG Tap/device matched JTAG tap: stm32.bs tap/device found: 0x16410041 (Manufacturer: 0x020, Part: 0x6410, Version: 0x1) JTAG Tap/device matched target state: halted target halted due to breakpoint, current mode: Thread xPSR: 0x81000000 pc: 0x0000007c If you don't see the red LED blinking after issuing the //reset// command, power cycle the primer.