STM32F103 AdvancedC
STM32F103 AdvancedC
Sepehr Naimi
www.NicerLand.com
In this Appendix, first we introduce some preprocessor directives, and then we explain how to
manipulate registers using the defined bits.
#define PI 3.1415
When the above line of code is written, the compiler replaces PI with 3.1415 at compile time. For
example, after defining the PI constant if we write “float a = PI*2.0;”, at compile time, the PI will be
considered as 3.1415 and then it will be compiled.
Example F-1: Find the values for variables a and b in the following program:
#define NUM_OF_LINES 4
int main ( )
{
int a = NUM_OF_LINES;
int b = NUM_OF_LINES * 20;
while(1);
}
Solution:
NUM_OF_LINES is replaced with 4 and the above program will be converted to:
int main ( )
{
int a = 4;
int b = 4 * 20;
while(1);
}
So, a and b will contain 4 and 80, respectively.
Using #define we can define nickname for any expression. For example, in the following program,
we name { and } as BEGIN and END respectively:
#define BEGIN {
#define END }
int main ()
BEGIN
while(1)
BEGIN
while(1);
END
END
Naming pins
It is a good practice to name pins of microcontrollers according to their usages. This makes the
code more readable and also more reusable. For example, see the following snippet of code:
#define LED_PORT GPIOC
#define LED_BIT 13
int main(){
...
LED_PORT->ODR |= (1<<LED_BIT);
}
#define and Macros
#define can be used to define macros in C language. For example, in the following piece of code,
a macro is defined that sets a bit of a variable:
Macros do not have the function call overhead and do not use the stack space, neither. But they
can increase the used program memory. It is good to use macros (or inline functions) when the
function is too small.
#undef
#undef is opposite of #define. It undefines a symbol which was defined by #define:
#undef symbol
For example, in the following program we undefine MAX_VALUE which is defined in a few lines
before:
#if condition
... //The program that should be compiled when the condition is met
#endif
In the following program, the #if preprocessor checks the value of DISPLAY_CONFIG and the printf
lines will be compiled only when DISPLAY_CONFIG is 1. Since DISPLAY_CONFIG is defined 0, the printf lines
are not be compiled, now.
#define DISPLAY_CONFIG 0
int main ( )
{
#if DISPLAY_CONFIG == 1
printf(“Hello World!\n\r”);
printf(“Now Display config is set to 1.”);
#endif
}
Together with conditional preprocessor, we can use #else as well:
#if condition
... /* The code that should be compiled when the condition is met */
#else
... /* The code will be compiled when the condition is not met */
#endif
In the following program i is defined as uint8_t when ARRAY_LEN is defined less than 256:
#define ARRAY_LEN 64
int main ( )
{
char ourArray[ARRAY_LEN];
#if ARRAY_LEN < 256
uint8_t i = 0;
#else
uint16_t i = 0;
#endif
int main ()
{
#ifdef DISPLAY_RESULTS
printf(“The result is as follows”);
#endif
}
In the “stm32f10x.h” header file, there are definitions for all bits of the I/O registers. For example,
the bits of APB2ENR register are defined as shown below:
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
Similarly, the following line of code, enables the clock for ADC1:
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
The bit definition naming follows the following convention:
peripheralName_registerName_bitName
For example, the BR5 bit for GPIOA->BSRR is named as GPIO_BSRR_BR5. So, the following line of
code, sets PA4 and clears PA5:
GPIOA->BSRR = GPIO_BSRR_BS4|GPIO_BSRR_BR5;
As another example, the following snippet of code toggles PC13:
while(1)
{
GPIOC->BRR = GPIO_BRR_BR13; /* make the pin low */
delay_ms(500); /* wait 0.5 sec. */
GPIOC->BSRR = GPIO_BSRR_BS13; /* make the pin high */
delay_ms(500); /* wait 0.5 sec. */
}
In cases that a field of a register is bigger than 1 bit, the field is named as
peripheralName_registerName_FieldName. To name the bits of the field, the bit number is added to the
end of field name. For example, in the CRL and CRH registers, the MODEx fields are 2-bit. So, the fields are
named as GPIO_CRL_MODEx and the bits are named as GPIO_CRL_MODEx_0 and GPIO_CRL_MODEx_1.
For example, in STM32F10x.h the followings are defined for MODE6:
Program F-1: Rewrite Program 8-5 using bit definitions (Toggle PC13 using BSRR and BRR)
#include <stm32f10x.h>
int main()
{
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; /* Enable clocks for GPIOs */
/* PC13 as output */
GPIOC->CRH &= (GPIO_CRH_MODE13|GPIO_CRH_CNF13); /* clear MODE13 and CNF13 fields */
GPIOC->CRH |= GPIO_CRH_MODE13_1|GPIO_CRH_MODE13_0; /* set MODE13 to 3 (Output) */
while(1)
{
GPIOC->BRR = GPIO_BRR_BR13; /* make the pin low */
delay_ms(500); /* wait 0.5 sec. */
GPIOC->BSRR = GPIO_BSRR_BS13; /* make the pin high */
delay_ms(500); /* wait 0.5 sec. */
}
}
// copy delay_ms from Program 8-1
Program F-2: Rewrite Program 8-9 using bit definitions (It toggles PC13 if PA2 is low)
#include <stm32f10x.h>
int main()
{
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN|RCC_APB2ENR_IOPCEN; /* Enable clocks for PB and
PC*/
/* PC13 as output */
GPIOC->CRH = (GPIOC->CRH&~GPIO_CRH_CNF13)|GPIO_CRH_MODE;
while(1)
{
if((GPIOA->IDR&GPIO_IDR_IDR2) == 0) /* is PA2 low? */
GPIOC->ODR ^= GPIO_ODR_ODR13; /* toggle PC13 */
else
GPIOC->ODR &= ~GPIO_ODR_ODR13;
delay_ms(500);
}
}
//copy delay_ms from Program 8-1 to here
Program F-3: Rewrite Program 8-6 using bit definitions (Reading from port B and writing to port A)
#include <stm32f10x.h>
int main()
{
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN|RCC_APB2ENR_IOPBEN; /* Enable clocks for PA and
PB */
while(1)
{
GPIOA->ODR = GPIOB->IDR; /* read from port B and write to port A */
}
}