2

I'm very new with ARM microcontrollers and I've been doing a lite lecture of about 4 books to know how to configure this devices to start programming them. First I did a reading an how to use mbed but I think that I need to do some porting or a bootloader to make it work in my microcontroller.

Then I've decided to look for examples on how access GPIO ports and I know that first I need to configure clock to the port that I will use. I've found the example of Keil with the library stm32_Init, this library uses the file stm32f10x_lib.h and has an excellent config wizard made for it but I think is deprecated or something like that, in addition, the header stm32f10x_lib.h is not found by Keil v5.30 nor the installation folder and obviously not in the project folder.

After that I've read about CMSIS and that it was created to portability and uniformity of code between developers so I taught I can use this but the form of using it is confusing to me...or I don't understand anything about CMSIS, I don't know yet.

My code is right now:

#include <stdint.h>
#include <ARMCM3.h>
//#include "STM32_Init.h" 
//#include <stm32f10x_lib.h>// STM32F10x Library Definitions 
SystemInit(); 
int main() 
{ 
 
}

CMSIS uses a function called SystemInit() whose meaning as I saw is basically configure the system clock but even when I've included CMSIS support in my project when I use that function and It gives me the following errors and warnings when I pass the cursor over the exclamation symbol in the same line:

Error: Conflicting types for 'SystemInit' 
warning: type specifier is missing, default to 'int' 
warning: this function declaration is not a prototype.Code

and when I compile the file it gives me the following error:

C:/Users/valery/AppData/Local/Arm/Packs/ARM/CMSIS/5.7.0/Device/ARM/ARMCM3/Include/system_ARMCM3.h(48)
: note: previous declaration is here 
extern void SystemInit (void);

I see this like the project isn't seeing the CMSIS library or something like that.

Like you see I'm a little bit confused here so I need help on how and what to do here.

How can I start here?

NOTE: The file that has the config wizard is STM32_Init.h

JYelton
  • 32,302
  • 33
  • 134
  • 249
vram
  • 158
  • 12
  • 1
    Take a look at my sample project on github, though I've used STM32Cube : https://github.com/Hamid-R-Tanhaei/Signal-Generator – Hamid R. Tanhaei Jun 24 '20 at 02:22
  • 1
    There are far too many different questions here covering everything from matters of opinion to the role of header vs. source files in a C compiler. Try looking at some of the official examples packaged with your toolchain, or those for other toolchains. – Chris Stratton Jun 24 '20 at 03:48
  • 1
    The error you've made which is causing the `Conflicting types` error is that you've put the `SystemInit();` call *outside* of your `main()` function. The way you've written it makes it look to the compiler as if you're writing a function declaration or prototype for the `SystemInit()` function - and this prototype conflicts with the real one. Move `SystemInit();` inside `main()`. – brhans Jun 24 '20 at 12:03
  • 1
    Since you're using an STM32 device, you might want to consider using ST's STM32CubeMX software to build the framework for your firmware project. – brhans Jun 24 '20 at 12:04
  • Hey, thank you all for the comments, it really helped me and thanks @brhans for that, I've completelly forgotten to put the SystemInit inside main. – vram Jun 24 '20 at 14:24

3 Answers3

3

Im going to bring you down to pretty bare baremetal, and then you can complicate this from there, but should have a much higher chance of success.

This code blinks the led on port pin PC13.

This uses gnu tools, as written you can use pretty much any gcc for arm from say gcc 3.x.x or gcc 4.x.x to the present gcc 9.x.x. Can use the linux variants or the non-linux variants. (arm-none-eabi- or arm-linux-gnueabi, or other similar variations as it doesnt rely on libraries and uses the compiler as a compiler and linker as a linker and doesnt use the things that will vary). You can find pre-builts for windows, linux, macos. And can build your own from sources too.

flash.s

.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.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

notmain.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;
    unsigned int rx;

    ra=GET32(RCCBASE+0x18);
    ra|=1<<4; //enable port c
    PUT32(RCCBASE+0x18,ra);
    //config
    ra=GET32(GPIOCBASE+0x04);
    ra&=~(3<<20);   //PC13
    ra|=  1<<20;    //PC13
    ra&=~(3<<22);   //PC13
    ra|=  0<<22;    //PC13
    PUT32(GPIOCBASE+0x04,ra);

    for(rx=0;;rx++)
    {
        PUT32(GPIOCBASE+0x10,1<<(13+0));
        for(ra=0;ra<200000;ra++) dummy(ra);
        PUT32(GPIOCBASE+0x10,1<<(13+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
}

Then build

arm-linux-gnueabi-as --warn --fatal-warnings -mcpu=cortex-m0 flash.s -o flash.o
arm-linux-gnueabi-gcc -Wall -O2 -ffreestanding -mcpu=cortex-m0 -mthumb -c notmain.c -o notmain.o
arm-linux-gnueabi-ld -nostdlib -nostartfiles -T flash.ld flash.o notmain.o -o notmain.elf
arm-linux-gnueabi-objdump -D notmain.elf > notmain.list
arm-linux-gnueabi-objcopy -O binary notmain.elf notmain.bin

or

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

You dont always need the nostdlibs nostartfiles ffreestanding, but sometimes they come in handy.

Bare-metal leaves the programmer with lots of freedom and personal preferences as such some folks may comment very negatively to this answer. Some may not. I recommend you learn at this level and the just call the api level and levels in between.

ARM makes cores not chips, you will want the documentation for the arm core, in this case the st manual will tell you this is a cortex-m3 which in that technical reference manual for arm will tell you it is based on the armv7m architecture which you find in the armv7-m architectural reference manual from arm. infocenter.arm.com. Then the st data sheet for the part and the reference manual for the family (rm0008 it looks like for this one. At this time when you click on reference manuals, you are pointed at interesting manuals you dont need nor want but scroll up to find the reference manual thats the one you want and where you spend almost all your time.

the cortex-ms use a vector table at ARM's address 0x00000000 to boot, the first entry is a freebie to load the stack pointer, whatever is there goes into the stack pointer, you can certainly change the stack pointer in the bootstrap if you prefer. Then there is the reset vector and some others. the vectors are the address of the handler ORRed with 1 its a thumb instruction set vs arm instruction set thing, research this yourself.

This example the bootstrap simply jumps to the C entry point notmain. Some compilers in the past have added junk when linked with main() and to avoid that just dont call it main(). this trivial bootstrap means you cant expect

unsigned int x = 5;
unsigned int y;

To have 5 and 0 respectively when you start your C code. for now easy to do without. You can complicate the linker script and bootstrap to add support for .data and to zero .bss per your preferences. No need for that in this example.

The datasheet shows that PC13 and pretty much all of the gpio pins default to gpio mode, so no need to set the alternate function go do gpio things.

Early in the reference manual is a list of base addresses for this part/family for the various modules in the part.

The reference manual shows that RCC_APB2ENR bit 4 controls the clock enable to the GPIOC block. So a read-modify-write to set that bit to enable the clocks.

Now you can talk to the gpio peripheral. So most of the stm32 family uses a different gpio controller, some of these older/early stm32s you have this one register deal, vs a two register deal, either way. The cnf pins for the port we want general purpose push-pull, for mode one of the outputs speed doesnt matter here.

The pin is configured.

The BSRR register is pretty cool that you can set one or more bits in the register to change the state, the logic basically does the read-modify-write for you to that control bit in the logic that determines if it is high or low.

the loop calling the dummy() function simply burns time. By having the dummy() function in a separate object it cant optimize this loop away, if you didnt make this call in this way this could be dead code and the loop goes away and you dont have a delay and you dont blink it might glow at best. Other ways to do this, this allows for an optimized loop counter and is lower risk than other solutions. Have to hand tune the value it counts too to "see" the led blink, and can change it double, quadrouple, etc re-build and reprogram and get a warm fuzzy feelling that you are changing the firmware on the part.

Looking at the disassembly to confirm it will have a chance of booting and not just fail or worse brick the part.

Disassembly of section .text:

08000000 <_start>:
 8000000:   20001000    andcs   r1, r0, r0
 8000004:   08000011    stmdaeq r0, {r0, r4}
 8000008:   08000017    stmdaeq r0, {r0, r1, r2, r4}
 800000c:   08000017    stmdaeq r0, {r0, r1, r2, r4}

08000010 <reset>:
 8000010:   f000 f808   bl  8000024 <notmain>
 8000014:   e7ff        b.n 8000016 <hang>

08000016 <hang>:
 8000016:   e7fe        b.n 8000016 <hang>

vector table is at the right address (yes not 0x00000000 hang on) and the vectors are the address ORRed with one (.thumb_func makes that happen declaring the next label as a function address).

The application flash in most stm32 parts is at address 0x08000000 (some have other/faster flashes but also support this address space). Depending on the boot pins and how the part is booting it will as far as we are concerned mirror what is at 0x08000000 to 0x00000000 so when the arm reads 0x00000004 it sees 08000011, strips the lsbit and jumps to 0x08000010 our reset handler. And also moving the processor to the right address space. Some of these stm32 parts with larger flashes the full address space is only at the application address not at zero, so you want to build for the application space for that part. Some chip vendors dont do this some have their own other address spaces, gotta read the docs. Some of the mbed compatible nucleo boards wont load the flash if you have it built for zero or the incorrect address, I guess it looks at the vector table before programming the part (with a nucleo board which I highly recommend you simply copy the .bin file over to the virtual drive the board makes when you plug it in and the debug mcu programs the target mcu for you, super simple).

Getting this binary onto your part you will have to figure out. There is a factory bootloader that uses one of the uarts and other interfaces, the uart one is not hard to write a program to talk to and program the part that way or find a tool someone else wrote or maybe fake the gui to load the binary even if you dont use the gui to build the binary. Let you sort this out, all part of working at this level.

Lots of folks dont want to do the read-modify-write there are times when that is fine, but in general do it. I have bricked parts such that I couldnt recover them with shortcuts like that. Fortunately before the stm32Gxxxx parts the boot pins (boot0 sometimes boot1) provide a safe backdoor to unbrick the part assuming you always provide some form of access to those on your boards. The newer parts are moving away from this (the stm32g's will give you one shot for a new/erased app flash part, then after that its locked so that first program you want to have change the non-volatile bits to allow the bootloader). I suspect as with atmels arm based parts the factory bootloader is not long for this world in the stm32 parts. Will see.

Either start with nucleo boards first, but eventually invest in one or more usb to uart solutions as well as a debug front end. The wider/bigger (but still as cheap as $10) nucleo boards have a debug end that you can remove the jumpers and use that debugger to program st and other branded cortex-m parts with free tools like openocd. There are other swd solutions. but eventually your toolbox should have jumper wires with mixtures of male/female combinations on the ends and usb uart and usb swd solutions.

CMSIS is like the unified syntax, a mostly failed attempt by arm to try to control something they shouldnt be interfering with. It tries to make the header files and some other parts of the software solution look common across the vendors that use their cores. It has taken some badly written vendor libraries/headers and simply made them that much worse. but the idea here is as you have started you get some header file it then does some form of maybe scary maybe tolerable way of accessing registers, then you need more code.

Chip vendors at this level pretty much have to provide libraries, and tools as customers expect that. This is a really old part so the tools of the day are probably not even available. Vendors need to keep things fresh as they add new parts, get you to feel you need the latest tool and move away from the old tool and so on. If you avoid their stuff you dont have these problems but you do then have to be able to read the docs and program the parts yourself. Generally easy.

Professionally you should be able to work at all levels so should be aware and practice the various solutions (sometimes though that is best done buy buying the right board/part for the current version of the tool/library and not trying to shim it into something older or not completely supported).

At this lower/direct level you can then examine the libraries (often scary when you get in there, but not always) and figure out why the library is broken or why you can or cant do this, etc. And thats the goal here, attempt to get early success, so you dont give up, but then go and look at the libraries, whose startup routines often initialize way more stuff that you need for complicated applications but not simple ones like this. And they then should have gpio pin routines to configure the pin or whole port into an output, input, some alternate function, etc.

This is an output blink the led, if you wanted input then examine the reference manual see if the reset value is an input, it usually is or sometimes its an analog input and you want to change it to a floating input or pull up/pull down. And then you sample the input data register and mask off the individual pin(s) you are interested in and act on that being careful not to allow the compiler to optimize out future accesses (one of the many reasons I use an abstraction that doesnt get optimized out, there are other reasons).

Most MCU vendors have clock enables for the various peripherals/blocks, not always but a lot of the time and often the block has the clocks gated but not necessarily in reset (powers up not consuming a lot of power, turn things on you want to use rather than scramble to turn everything else off). Some vendors the address space is in the datasheet and the register offsets and bits are in some form of reference manual not necesarily by that name. Some do it all in the datasheet, some have two docs call the datasheet but one is a smaller file than the other and you want the big file.

This example built for the cortex-m0 which uses armv6-m instructions which are pretty close to the original armv4t thumb instruction set and to date the armv6m instructions work on all cortex-ms, but the armv7-ms dont and if you try to borrow code from someone else or yourself you can run into faults because its building instructions that your core doesnt support, quite frustrating to fail to boot immediately. Can then use the larger armv7-m instruction set to add performance later. (not necessarily smaller code, can go either way on size and performance isnt guaranteed can be slower to switch too).

Good luck.

If you want to dabble in the vendors library and toolchain solutions I highly recommend you pick a part/dev board that is currently supported and builds nice and clean examples before buying the board. Then use that board to learn the libraries/tool. Have one set of problems dont amplify/multiply by not using but manipulating the tool and trying to learn the part. Ideally learn the part or the library/tool, not both nor all three at the same time. The stm32f103 is not the part you want to start with if you want to use the tools, it is the part you want to temporarily play with as you can get boards from asia ready to use for a couple of bucks, but you need five to twenty dollars in other stuff to make that board work plus experience. Get a $10 nucleo, maybe something from atmel and/or the microbit or some others. A ti launchpad, etc. And dabble in the different tools and libraries...See where cmsis overlaps across the vendors and where it doesnt. And realistically, get the tools and play with them without or before buying any parts, if the tools dont just work and dont just have some blink the led example that just works as in it builds clean and easy. Then move on to the next one. Then buy a board/part.

old_timer
  • 8,203
  • 24
  • 33
  • 1
    The vendors at this time in history still probably have a pre-cmsis solution and post (with) solution. Some vendors may continue to do this, but will see they may all go cmsis for a while so long as it makes sense to do so. so per part/board there may be more than one sandbox solution and then there is the arduino and mbed paths that can be separate from the vendors tools, but are also paths to learn/explore. – old_timer Jun 24 '20 at 06:36
  • Well, thanks @old_timer for your answer, yo really gave me a class on how to do here. I'll follow your advices but in whats related to the card and that specific microcontroller obligatorily I must use the stm32f103c6 since that is a custom board that is already designed for an specific project. – vram Jun 24 '20 at 14:29
  • Another question old_timer, I needed to read your excellent class a couple of times to have a better understanding of what you explained. Then you passed wrote some sord of mini bootloader? – vram Jun 24 '20 at 16:02
  • dont understand your question. – old_timer Jun 24 '20 at 18:18
  • I used the wrong word there, what I tried to ask is if you made a bootloader in you explanation – vram Jun 24 '20 at 20:56
  • nope that is all the code there, all the code you need to make a working binary, just add gcc and binutils. The one line after reset:, bl notmain is the "boot strap" the small amount of code needed to prepare the system to run/enter C. The hardware takes care of the stack pointer from the vector table, this code does the branch to the C entry point, and by dictating that .data and .bss are not initialized, that is it. bootstrap done. If you want a bootloader then just make one it is just a program. – old_timer Jun 24 '20 at 21:01
  • Now these parts DO contain a bootloader in a flash or rom in the chip that we dont control we dont write. If you look in the documentation usually search for boot0 thats the easiest. (searching for reset is a very long afternoon). okay for this reference manual search for boot loader with a space in it – old_timer Jun 24 '20 at 21:03
  • The embedded boot loader is located in the System memory, programmed by ST during production. It is used to reprogram the Flash memory with one of the available serial interfaces: – old_timer Jun 24 '20 at 21:14
  • In low-, medium- and high-density devices the bootoader is activated through the USART1 interface. – old_timer Jun 24 '20 at 21:14
  • In XL-density devices the boot loader is activated through the following interfaces: USART1 or USART2 (remapped). – old_timer Jun 24 '20 at 21:15
  • In connectivity line devices the boot loader can be activated through one of the following interfaces: USART1, USART2 (remapped), CAN2 (remapped) or USB OTG FS in Device mode (DFU: device firmware upgrade). – old_timer Jun 24 '20 at 21:15
  • I usually search for boot0 in the doc, or search for bootloader or boot loader with a space. in this case boot0 takes you to the right place in the reference manual with the table that shows the boot0/boot1 combinations, not all parts have boot1 exposed but you can see you can do what you need usually with boot0 – old_timer Jun 24 '20 at 21:16
  • I had added and deleted a number of comments, the programming manual was misleading, the stm32f103 I am used to is on the stm32 blue pill (google it), which is an stm32f103c8 not c6, but the same reference manual to the part you are using, so its likely a flash size or packaging difference with the same internals. and was pretty sure the blue pill didnt do DFU. that would be nice. the web page says the c8 part is a medium density so that likely means uart only. – old_timer Jun 24 '20 at 21:19
  • I believe from the web page that the c6 is a low density so likely only supports the uart interface, but still thats an easy one (assuming before designing the board all of these things were researched and the right interfaces exposed for in circuit programming). Most important is to find out what interfaces you have access to. you are looking for usart1 and/or swdio/swclk as well as boot0 being exposed to either on board hardware or to some header that you can use external debug hardware to access. – old_timer Jun 24 '20 at 21:22
  • Thanks then so much for your explanation. – vram Jun 24 '20 at 22:38
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackexchange.com/rooms/109804/discussion-on-answer-by-old-timer-newbie-needs-help-on-which-libs-to-use-with-a). – Voltage Spike Jun 24 '20 at 22:46
1

To make a long story short, your immediate problem is in this line:

SystemInit();

If you are trying to invoke this function then you need to call it from inside another function. Typically that would be inside your main function. If, on the other hand, you are trying to provide a forward-declaration of the function then you have a syntax error (the function has no type declaration) and once you get past that you would probably get errors because the function is multiply defined...it will be defined in the included CMSIS headers.

Elliot Alderson
  • 31,192
  • 5
  • 29
  • 67
0

I prefer creating an empty project using STM32CubeIDE or TrueStudio, then adding CMSIS and ST headers manually. This way, you can enjoy using predefined register and bit names and also some CMSIS functions while avoiding the burden of Cube platform (or any other library).

You can get CMSIS files from their official github page.

For headers, you need to download STM32Cube package from ST website. This also includes CMSIS, but its version is generally behind the official repo.

Of course, you don't need all the files in these packages. For simple projects, only a small fraction of the header files are needed.

Here, you can find an example blinky project I created using STM32CubeIDE for the famous Blue Pill board. It uses only CMSIS and ST device headers.

Tagli
  • 1,222
  • 1
  • 6
  • 16