What is wrong with my project by arm-none-eabi-gcc on stm32?

2

I just want to deal with stm32 programming in c by arm-none-eabi-gcc from scratch and just working by registers, not drivers! so very simple piece of code to set PortC.8 to 1 and switch on LED on stm32f1 discovery board. I am compiling by this script:

arm-none-eabi-gcc --specs=nosys.specs -T *.ld -o main.elf main.c system_*.c  *.s

and main.elf file has been made successfully... but not working!!! and LED is OFF.

files in the directory is :

core_cm3.c
core_cm3.h
main.c
main.elf
startup_stm32f100xb.s
STM32F100RBTx_FLASH.ld
stm32f10x.h
system_stm32f10x.c
system_stm32f10x.h

all files from STM32F10x_StdPeriph_Lib_V3.5.0 folder downloaded from st official website.

main.c:

#include "stm32f10x.h"
void EnableClock(void);
void InitPort(void);
int main(void)
{
    EnableClock();
    InitPort();

}

void EnableClock(void)
{
    //Enable HSI
    RCC->CR |= (uint32_t)(0x00000001);  
    //APB2 Prescalar to 1(HCLK not devided)
    RCC->CFGR &= (uint32_t)(0xFFFFFF0F);
    //APB2 GPIOC Enable
    RCC->APB2ENR |= (uint32_t)(0x00000010);
}

void InitPort(void)
{
    //PORTC Bit 8 set as output max speed 2MHz GPIOC_CRH->Mode8
    //PORTC Bit 8 set as Push Pull
    GPIOC->CRH &= (uint32_t)(0x00000002);
    GPIOC->ODR |= (uint32_t)(0x00000100);
}

I don't know where is the problem.. anyone may help me??

c
gcc
arm
embedded
stm32
asked on Stack Overflow Mar 11, 2017 by peyman khalili • edited Mar 12, 2017 by peyman khalili

2 Answers

2

Your main() terminates. What happens next depends on what follows the __main call in startup_stm32f100xb.s; possinly a forces reset, or an endless loop (until watchdog reset is enabled).

In embedded systems it is not normal for main() to return.

The "Enable HSI" is perhaps unnecessary or even incorrect. The core clock is HSI by default - a clock must already have been established for the code to be executing at all, and enabling the HSI will either have no effect, or disable a previously established clock, the system clocking will have been set up in system_stm32f10x.c, and if you have specific clocking requirements, you need to modify that to suit your board and/or application. Using the HSI directly allows 8MHz operation, feeding it to the PLL supports up-to 64MHz, and the chip will run at up-to 72MHz when the PLL is fed from an external 4-16MHz oscillator (HSE).

If you are not establishing the clock correctly in system_stm32f10x.c (or leaving it running HSI-direct), then it is possible that the execution is stalled in SystemInit() in system_stm32f10x.c waiting for PLL lock, or if you have it really wrong is not running at all.

To be working on any bare-metal processor at the board-bring-up level, you should ideally have a hardware debugger (JTAG/SWI) so you can use your tool-chain's debugger to determine exactly what your code is doing and where it is falling over. You can at least determine whether it is even running as far as main() - there is a significant initialisation implemented by startup_stm32f100xb.s and system_stm32f10x.c before main() run. The latter normally requires modification to suit your target.

answered on Stack Overflow Mar 11, 2017 by Clifford • edited Mar 11, 2017 by Clifford
2

Here is a complete example derived from one that blinks pc13 (untested at pc8, but worked at pc13, can test it if you have issues)

flash.s

@.cpu cortex-m0
@.cpu cortex-m3
.thumb


.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang

.thumb_func
reset:
    bl notmain
    b hang
.thumb_func
hang:   b .

.thumb_func
.globl PUT32
PUT32:
    str r1,[r0]
    bx lr

.thumb_func
.globl GET32
GET32:
    ldr r0,[r0]
    bx lr

.thumb_func
.globl dummy
dummy:
    bx lr

blinker01.c

void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );
void dummy ( unsigned int );

#define GPIOCBASE 0x40011000
#define RCCBASE   0x40021000

int notmain ( void )
{
    unsigned int ra;

    ra=GET32(RCCBASE+0x18);
    ra|=1<<4; //enable port c
    PUT32(RCCBASE+0x18,ra);
    //config
    ra=GET32(GPIOCBASE+0x04);
    ra&=~(0xF<<0);   //PC8
    ra|= (0x1<<0);   //PC8
    PUT32(GPIOCBASE+0x04,ra);
    while(1)
    {
        PUT32(GPIOCBASE+0x10,1<<(8+0));
        for(ra=0;ra<200000;ra++) dummy(ra);
        PUT32(GPIOCBASE+0x10,1<<(8+16));
        for(ra=0;ra<200000;ra++) dummy(ra);
    }
    return(0);
}

flash.ld

MEMORY
{
    rom : ORIGIN = 0x08000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
    .text : { *(.text*) } > rom
    .rodata : { *(.rodata*) } > rom
    .bss : { *(.bss*) } > ram
}

Makefile

ARMGNU = arm-none-eabi
#ARMGNU = arm-linux-gnueabi

AOPS = --warn --fatal-warnings -mcpu=cortex-m0
AOPS3 = --warn --fatal-warnings -mcpu=cortex-m3
COPS = -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -mcpu=cortex-m0 -march=armv6-m
COPS32 = -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -mcpu=cortex-m3 -march=armv7-m

all : blinker01.bin

clean:
    rm -f *.bin
    rm -f *.o
    rm -f *.elf
    rm -f *.list

flash.o : flash.s
    $(ARMGNU)-as $(AOPS) flash.s -o flash.o

blinker01.o : blinker01.c
    $(ARMGNU)-gcc $(COPS) -mthumb -c blinker01.c -o blinker01.o

blinker01.bin : flash.ld flash.o blinker01.o
    $(ARMGNU)-ld -o blinker01.elf -T flash.ld flash.o blinker01.o
    $(ARMGNU)-objdump -D blinker01.elf > blinker01.list
    $(ARMGNU)-objcopy blinker01.elf blinker01.bin -O binary

or

arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m0 flash.s -o flash.o
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -mcpu=cortex-m0 -march=armv6-m -mthumb -c blinker01.c -o blinker01.o
arm-none-eabi-ld -o blinker01.elf -T flash.ld flash.o blinker01.o
arm-none-eabi-objdump -D blinker01.elf > blinker01.list
arm-none-eabi-objcopy blinker01.elf blinker01.bin -O binary

You dont need to mess with the clock initially it comes out of reset using the internal oscillator. So unless you have your own firmware that sets it to something else you wont need to set it to HSI. A clock init would be needed to change it to use an external clock and/or to use the PLL.

GPIOC->CRH &= (uint32_t)(0x00000002);

This changes the register from 0x44444444 to 0x44444444 (sorry see edit below). which is another way of saying you didnt do anything. You want 0x44444442 for the 2mhz if you want to read-modify-write which is cleaner, but not necessary since we know the power on reset state you should do Like I did above read the value mask off the bits to zero then set the bits you want, the whole pattern. Or you can just shove the 0x44444442 or 0x44444441 or 0x44444443.

had you done an or equal then it would have made 0x44444446 which is an open drain output. So if there is a pull up then it would light the led perhaps, depends, but you would want to use open drain for sinking more than sourcing. Which end of the led is the gpio pin connected to do you need to source it or sink it to turn it on? You set the output to 1 to set the output high, which on open drain just means dont sink. Although for an input setting odr pulls it high for input pull up/down, but as an open drain output not sure if setting odr pulls it up. easier to try push-pull first and if you just want it on then BSRR makes it easy to set or reset an individual pin in the port rather than touching the whole port with odr.

Open Drain Mode: A “0” in the Output register activates the N-MOS while a “1” in the Output register leaves the port in Hi-Z. (the P-MOS is never activated)

So first step change your code to set the port to a push-pull output, and then if you want to really be from scratch you can replace the rest of the code you borrowed and own all of it. Dont have to use the put32/get32 assembly I use, I have many reasons and tons of experience but that is the beauty of bare metal, do your own thing, you can use the volatile pointer approach as well which is likely what the header files you linked do, and simplify the bootstrap. If you make sure that your entry point function never exits and you dont rely on .data nor .bss then you can reduce your bootstrap to

.thumb
.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word main
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
hang: b hang

Or define a C function for the other vectors or a dumping ground for the unused ones. Note that I didnt put all the vectors in the bootstrap, depends on the chip and core but could be hundreds of vectors (up to 128 or 256)

And match the amount of ram you have for the stack pointer initialization value (0x20001000 in the example above).

EDIT, actually, sorry 0x44444444&=0x00000002 = 0x00000000, which sets all the ports to analog inputs. you want it to be 0x44444441 or 0x44444442 or 0x44444443

answered on Stack Overflow Mar 11, 2017 by old_timer • edited Mar 12, 2017 by old_timer

User contributions licensed under CC BY-SA 3.0