Before starting a programming tutorial i wanted to build a basic knowledge about embedded programming.
If you are designing windows applications in C, C++, C# ; data types are standard, your code can run on any machine running windows (if .net and other dependencies are supplied of course).
You don't have to know memory and peripheral addresses mostly. Bitwise operations are not common, you don't have to worry about cpu type, pin configuration, enabling clocks.
Probably you are not familiar with infinite while loop and RTOS (Real Time Operating System). Limited code and data memory restricts using big arrays.
Preprocessor directives and preprocessor macros are a little tricky usually. Portability is a big problem. If you write a program for STM32F1 even porting this program to STM32F4 can be pain in the ***.
1) Data Types
Don't get surprized when you see uint8_t, uint16_t etc. All type definitions are in stdint.h header file. Some of the definitions are below :
1 2 3 4 5 6 7 8 9 10 11 | /* exact-width signed integer types */ typedef signed char int8_t; typedef signed short int int16_t; typedef signed int int32_t; typedef signed __int64 int64_t; /* exact-width unsigned integer types */ typedef unsigned char uint8_t; typedef unsigned short int uint16_t; typedef unsigned int uint32_t; typedef unsigned __int64 uint64_t; |
2) Preprocessor Directives and Macros
Preprocessor directives and macros start with "#", sometimes you are going to encounter "\" character, it is used for line continuation. Some of the preprocessor directive keywords are :
1 2 3 4 5 6 7 8 9 10 11 12 13 | #define #if #else #elif #endif #ifdef #ifndef #include #line #message #pragma #undef #warning |
Take a look at these ebook :
https://gcc.gnu.org/onlinedocs/gcc-4.0.4/cpp.pdf
3) Microcontroller Dependent Register Names
Even if you are goint to use higher level libraries, it would be handy to know basic register names. In some tutorials and examples register based operations are used, for example below code does not mean anything to you if you don't know the register names :
1 | GPIOC->IDR & 0x0020 |
IDR is Input Data Register, this code reads Port C, Pin5. Below code is equivalent but easy to read.
1 | GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_5) |
4) Pin Configuration
Using GPIO pins is a little tricky for beginners at first. I am going to write a detailed gpio tutorial later but i want to make a brief explanation. Before using your MCU's gpio pins you have to make some effort, all pins are usually at floating input state initially.
- Enable clock for related port. (Port A etc.)
- Select related pins (Pin 0 etc.)
- Select Mode (Input/Output/Alternate Function/Analog)
- Select Speed (2-100Mhz)
- Select Output Type (Push Pull/Open Drain)
- Select Pull Property (Pull Up/Pull Down/No Pull)
1 2 3 4 5 6 7 8 | GPIO_InitTypeDef GPIO_InitStruct; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOC, &GPIO_InitStruct); |
Startup code is automatically generated by Keil and SystemInit function is called before main function. SystemInit function configures PLL parameters and sets the mcu at it's maximum frequency if possible.