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. Because 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, because after updating document 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 configurations 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 application?
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 returns 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 peripherals
are divided into structures that hold peripherals registers. These 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 peripherals. Each structure holds all
registers of peripheral. Naming scheme of peripheral structures are following:
PeriphName_TypeDef. So for RCC peripheral structure definitions with registers
are "RCC_TypeDef". So let's search for "RCC_TypeDef"
string. We see, that registers name are slightly 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 is 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 slightly complicated, I prepared some macros for easier configuring GPIO ports on STM32 microcontrollers. With this macros pin configuration are easy and clear to read. These macros are in antilib_gpio.h file. Download this file, and save into folder with your 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 megahertz? This specified slew rate on
outputs pin. For driving LED we don’t 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 modifies value of all pins
of given GPIO port. Writing to BSRR/BRR registers modifies 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 modified value we can lose this change. So better use
atomic operations with BSRR / BRR register.
Finally, let's blink
the LED!
After getting all informations
from above discussion, we can write our blinking LED application.
How to make a delay?
Simplest way to make a
delay (let's call 'monkey delay') is loop counting some many times. Time of
this delay it's hard to define. Especially on ARM devices counting how CPU
cycles loop will be executed is difficult. Let's define variable named dly:
volatile uint32_t
dly;
|
Short explain about two
keywords before variable name. An 'volatile' keyword says to compiler
"Don't optimize access to this variable. Its value can change in any time
of execution of program". Second keyword defines size of variable - 32-bit
unsigned integer. Now, let's do a simple loop using this variable:
for(dly = 0; dly <
500000; dly++);
|
This code give us
500.000 loop cycles, that in effect give us some time of CPU spending in the
loop. Without keyword 'volatile' this code probably gives us no cycles and no
time delay.
Complete code of blink
LED application:
//=============================================================================
// STM32VLDISCOVERY tutorial
// Lesson 1. Blinking
the LED.
// Copyright : Gaurav
Verma, Assistant Professor, ECE Department.
// Jaypee
Institute of Information Technology, Sector-62, Noida.
// e-mail :
gaurav.iitkg@gmail.com
// Mobile No.:
9811506739
//=============================================================================
#include "stm32f10x.h"
#include
"antilib_gpio.h"
//=============================================================================
// main function
//=============================================================================
int main(void)
{
volatile uint32_t dly;
RCC->APB2ENR |=
RCC_APB2ENR_IOPCEN;
GPIOC->CRH =
(GPIOC->CRH & CONFMASKH(8)) | GPIOPINCONFH(8,
GPIOCONF(GPIO_MODE_OUTPUT2MHz, GPIO_CNF_OUTPUT_PUSHPULL));
while (1)
{
for(dly = 0; dly <
500000; dly++);
GPIOC->BSRR = (1
<< 8);
for(dly = 0; dly <
500000; dly++);
GPIOC->BRR = (1
<< 8);
}
} //============================================================================= // End of file
//=============================================================================
|
How to make this code
more universal? When LED diode will be connected to other pin, we need modify
in code all references to them. So let's use the preprocessor to define some
macros describing connected LED diode:
#define LED_BLUE_GPIO
GPIOC
#define LED_BLUE_PIN
8
|
Now we can use defined
macros in place to direct GPIO and pin number, but in case of configure GPIO
there is need to specify configuration register (low or high). We can do this
like in example:
#if (LED_BLUE_PIN
> 7)
LED_BLUE_GPIO->CRH
= (LED_BLUE_GPIO->CRH & CONFMASKH(LED_BLUE_PIN))
|GPIOPINCONFH(LED_BLUE_PIN,GPIOCONF(GPIO_MODE_OUTPUT2MHz,
GPIO_CNF_OUTPUT_PUSHPULL));
#else LED_BLUE_GPIO->CRL = (LED_BLUE_GPIO->CRL & CONFMASKL(LED_BLUE_PIN)) | GPIOPINCONFL(LED_BLUE_PIN, GPIOCONF(GPIO_MODE_OUTPUT2MHz, GPIO_CNF_OUTPUT_PUSHPULL)); #endif |
The #if is preprocessor
conditional directive, not the C language conditional instruction, so before
compilation the preprocessor depending on value of LED_BLUE_PIN choose proper
source code line and place them on source code file that will be compiled. Now,
to turn LED on we can use following sequence:
GPIOC->BSRR = (1
<< LED_BLUE_PIN);
|
and for turn led OFF :
GPIOC->BRR = (1
<< LED_BLUE_PIN);
|
Complete source code :
//=============================================================================
// STM32VLDISCOVERY
tutorial
// Lesson 1. Blinking
the LED.
// Copyright : Gaurav
Verma, Assistant Professor, ECE Department.
// Jaypee Institute
of Information Technology, Sector-62, Noida.
// e-mail :
gaurav.iitkg@gmail.com
// Mobile No.:
9811506739
//=============================================================================
#include "stm32f10x.h"
#include
"antilib_gpio.h"
//=============================================================================
// Defines
//=============================================================================
#define LED_BLUE_GPIO GPIOC
#define LED_BLUE_PIN
8
//=============================================================================
// main function //============================================================================= int main(void)
{
volatile uint32_t dly;
RCC->APB2ENR |=
RCC_APB2ENR_IOPCEN;
#if (LED_BLUE_PIN
> 7)
LED_BLUE_GPIO->CRH
= (LED_BLUE_GPIO->CRH & CONFMASKH(LED_BLUE_PIN)) |
GPIOPINCONFH(LED_BLUE_PIN,
GPIOCONF(GPIO_MODE_OUTPUT2MHz,
GPIO_CNF_OUTPUT_PUSHPULL));
#else LED_BLUE_GPIO->CRL = (LED_BLUE_GPIO->CRL & CONFMASKL(LED_BLUE_PIN))
|
GPIOPINCONFL(LED_BLUE_PIN,
GPIOCONF(GPIO_MODE_OUTPUT2MHz,
GPIO_CNF_OUTPUT_PUSHPULL));
#endif
while (1)
{
for(dly =
0; dly < 300000; dly++);
LED_BLUE_GPIO->BSRR
= (1 << LED_BLUE_PIN);
for(dly =
0; dly < 300000; dly++);
LED_BLUE_GPIO->BRR
= (1 << LED_BLUE_PIN);
}
} //============================================================================= // End of file
//=============================================================================
|
No comments:
Post a Comment