en.radzio.dxp.pl

Let's blink LED!
After succesfully creating and compiling first empty project we can add some source code to them. We want to blink LED LD4, which are connected to PC8 pin of STM32F100RBT6. So we need to configure this pin as output. At this moment, You need to download from STMicro website document called RM0041. In this Reference Manual are described all peripherals of STM32F100 microcontrollers. Beacuse this document has over 650 pages it impossible to copy all informations to this website. So I will reference to this document and suitable chapters concerning peripherals used in this tutorial. I will not reference to exact page, beacuse after updating documet by ST Micro page numbers can be not actual. I will be referenced to sections, figures and tables.

RCC configuration
Look at section 6 of RM0041, that concern about RCC (Reset and Clock Control) peripheral. In section 6.2 Figure 8 shows clock tree on STM32F100 low and medium density microcontrollers. Looks complicated, but almost all configuration are done by SystemInit function from system_stm32f10x.c file. Clocks will be configured to obtain specified core speed. Remember that - after reset all peripherals have disabled clock! So we need enable clock for every used peripheral. Go to section 6.3.7 of Reference Manual. This section describes APB2ENR register of RCC peripheral. This register is responsible for enabling clock signal for peripherals working on APB2 bus. All GPIO peripherals works on APB2 bus, so this register are interesting for us. Bits 2 to 8 are used to enable clock for each GPIO port (form GPIOA to GPIOG). We ned use only GPIOC port, so we need set bit number 4 of APB2ENR register. How do it? Probably You think about (1 << 4)? Right? That is wrong! Open document stm32f10x.h and search for bit name - "IOPCEN". You should find some macro definitions, which one from them is :

#define RCC_APB2ENR_IOPCEN ((uint32_t)0x00000010) /*!< I/O port C clock enable */

What is it? It's a bitmask for IOPCEN bit! So we don't need bitwise shifting one by pin number, we can use suitable bitmask! So, how will be looks first code line of us appilaction? Probably something like that :

#include "stm32f10x.h"

int main(void)
{
RCC_APB2ENR |= RCC_APB2ENR_IOPCEN;
do{
}while(1);
}

But this is wrong. After compiling application, compiler return following error :

main.c(14): error: #20: identifier "RCC_APB2ENR" is undefined

Why? We use register name from Reference Manual, so why it is wrong? This is wrong, because all periperals are divided into structures that holds peripherals registers. This structures are defined in stm32f10x.h file. So go to this file and search for "Peripheral_registers_structures" string. You will see some of typedefs describing structures for all of periperals. Each structure holds all registers of peripheral. Naming scheme of periperal structures are following : PeriphName_TypeDef. So for RCC peripheral structure definition with registers are "RCC_TypeDef". So let's search for "RCC_TypeDef" string. We see, that registers name are slighty different from names from Reference Manual. Names in structure are without part of peripheral name. Register described in Reference Manual as RCC_APB2ENR in structure has name APB2ENR. OK, now we need name of structure variable. So let search again for RCC_TypeDef string. You should find following line :

#define RCC ((RCC_TypeDef *) RCC_BASE)

Now all are clear! In stm32f10x.h file is defined macro RCC that in fact are pointer dereference to our RCC_TypeDef structure that resides at RCC_BASE address. If You search for RCC_BASE string, You find another macro, that defines RCC_BASE as base address of RCC peripheral in STM32F100 memory space. So now, we can use RCC macro as pointer to structure, that holds all register of RCC peripheral. Now we can write correct code for set IOPCEN bit in APB2ENR register of RCC peripheral :

#include "stm32f10x.h"

int main(void)
{
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
do{
}while(1);
}

Try to build application. Hooray! No errors! We have enabled clock for GPIOC peripheral and we can configure GPIO for driving LED.

GPIO configuration
Now, go to Section 7 of RM0041. As You can read, each GPIO port has several registers to configure and control input and output state of microcontroller pins. Most important information for us are that each GPIO port has two 32-bit configuration registers : CRL and CRH. CRL register is responsible for configuration of pins from 0 to 7 and CRH register is responsible for configuration of pins form 8 to 15. So for each GPIO pin we have four configuration bits. Table 16 from RM0041 shows all possible configurations of GPIO pins. Because configuration registers are slighty complicated, I prepared some macros for easier configuring GPIO ports on STM32 microcontrollers.With this macros pin configuration are easy and clear to read. This macros are in antilib_gpio.h file. Download this file, and save into folder with You application project. Example configuration for PC8 pin that works as output looks like that :

GPIOC->CRH = (GPIOC->CRH & CONFMASKH(8)) | GPIOPINCONFH(8, GPIOCONF(GPIO_MODE_OUTPUT2MHz, GPIO_CNF_OUTPUT_PUSHPULL));

Let me describe each element of this code line. GPIO->CRH mean of course access to GPIOC_CRH register. We want change configuration only for PC8, so remaining bits of this register should be unchanged. We realize that by reading contents of GPIO_CRH register and clear bits responsible for PC8 configuration. Macro CONFMASKH(8) give bitmask for clearing 4 lowest bits : 0xFFFFFFF0. Clearing this 4 bits will be done by bitwise AND of register with this bitmask. Next, we need to set configuration bits for PC8 suitable for driving LED. Output should be work as push-pull output. And what is this megahertzs? This specified slew rate on outputs pin. For driving LED we dont need fast edges of driving signals, so we use slowest slew ratio for PC8.

Controlling output state of GPIO pins can be realized on two ways : by writing port value to ODR (Output Data Register) register or by writing to BSRR (Bit Set/Reset Register) or BRR (Bit Reset Register). Writing to ODR register modyfies value of all pins of given GPIO port. Writing to BSRR/BRR registers modyfies state only this bits, that writing value has ones on bits position. For example to set bit PC8 we can use following code :

GPIOC->BSRR = (1 << 8);

After execution of this line bit number 8 (and only this bit) of GPIOC will be set. Other bits remain unchanged. Upper 16 bits of BSRR register can be used to clearing pin state :

GPIOC->BSRR = (1 << 24);

After execution of this line bit number 8 of GPIOC will be cleared. To resetting pin state we can use BRR register too :

GPIOC->BRR = (1 << 8);

After execution of this line, bit number 8 will be cleared. If we want clear single bit using ODR register, we must perform bitwise logical operations in Read-Modify-Write scheme :

GPIOC->ODR = GPIOC->ODR | (1 << 8);

but this operation is not atomic! After reading ODR state, his value can be changed (in interrupt for example), and after write modyfied value we can loose this change. So better use atomic operations with BSRR / BRR register.

 

 
(c) Radosław Kwiecień