You are on page 1of 65

In the following tutorial I will try all the basics about the practical use of the STM32

microcontroller from STMicroelectronics to convey. Here I am trying to keep the


proportion of theoretical descriptions as low as possible. For example, the focus should
be how the SPI interface of the STM32 is used instead of going into the workings of SPI
- for this, there are already plenty of good sources of information on the net.
To understand knowledge of the C programming language and the basic use of
microcontrollers is required. Related threads on mikrocontroller.net: STM32
Tutorial Note:The codes can be within a family (eg STM32F1xx) take good because the
same standard Peripheral Library is used and differs only the pinout and certain
hardware is not available under certain circumstances. Many other minor adjustments
are not necessary for the other families (STM32L1xx, STM32F0xx, STM32F2xx,
STM32F4xx).

1 IDE, Programmer & Eval-Boards

2 Important Documents

3 First start: Switch LED

4 Setting the clock

5 Standard Peripheral Library

6 RCC Reset and clock control

7 GPIO/AFIO General-purpose and alternate-function I/Os

8 General-purpose timers

8.1 PWM

8.2 Encoder Interface

9 Interrupts

9.1 Externe Interrupts

9.2 Timer Interrupts

10 I2C

10.1 Configuration

10.2 & send (STM32 = master)

11 CAN

11.1 Configuration

Send 11.2 News

Received 11.3 News

12 SPI

12.1 Configuration

12.2 & send (STM32 = master)

13 ADC

13.1 Introduction

13.2 Calibration

13.3 Regular & Injected Group

13.4 Triggering

13.5 Discontinuous Mode

13.6 Interrupts

13.7 Analog Watchdog

13.8 Dual ADC Mode

14 DMA

15 Common Mistakes

16 SPI with DMA

17 Independent Watchdog (IWDG)

18 Option Bytes

19 System Timer

20 RS485

21 DAC

21.1 Introduction

21.2 Dual DAC Channel Conversion

21.3 DMA

22 I2C & DMA

// 1 IDE, Programmer & Eval-Boards


To flash the STM32 I use the ST-Link V2 by STMicroelectronics. It provides a
convenient solution for programming and debugging STM8 STM32 and via JTAG / SWD
/ SWIM. As eval board I can STM32VL Discovery recommended for first home. On the
board there is the STM32F100RB and the ST-Link Programmer / Debugger with USB
interface, can be losgelegt that immediately. Those looking for something more
peripherals which can have a look at the OLIMEXINO STM32 Maple throw. The above
hardware, for example, inelectronic Watterott to obtain. As a development environment
I use CooCox . It is easy to set up and you can quickly begin with the first
projects. CoIDE is, as the name suggests, the IDE - here the code is written later. It is
important that in addition, the ARM GCC compiler is installed. A step-by-step
instructions on the coocox.org available. To flash the software in the microcontroller is
still CoFlash needed. It
supports
inter
alia
the
above
ST-Link.

// 2 Important Documents
Before it gets going now, here's a list of the most important information you will need
sooner or later, if you need to look more closely with the STM32:
( STM32F103RB )

Data
Sheet
description of the concrete chips for pin wiring

Reference
Manual
( STM32F103RB )
Detailed description of the modules of a family. It is possible that not all modules
used in chip available - see data sheet.

Programming
Manual
( Cortex-M3 )
contains, for example, information about the Interrupt Controller (NVIC).

Standard
Peripheral
Library
( STM32F10x )
In contrast to, for example, AVRs should appeal to the registers of the STM32 not
directly but through features of the Standard Peripheral Library. She is
on www.st.comalong with documentation (file: stm32f10x_stdperiph_lib_um.chm)
downloadable.

// 3 First start: Switch LED


In this example, only one LED via software should be switched to the steps for creating
a project to show in CoIDE, as well as to test the flash with CoFlash.
..\main.c
STM32F100RB
#include "stm32f10x_conf.h"
int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SystemInit ( ) ;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_WriteBit(GPIOC, GPIO_Pin_9, Bit_SET);
while(1)
{
}
}

In the screenshots below step by step example of STM32VL Discovery is shown, what
settings need to be made and how the code is loaded onto the chip. If the ST-Link and
another evaluation board / own circuit used, they must just another chip are selected
and in main.c in the third and eighth step GPIOC and Pin_9 be adjusted.

To later should save switching to the CoFlash program you CoIDE set so that there can
be flashed with one click of the processor. To do this, select from the menu Debug /
Debug Configuration and chooses for his project on adapter "ST-Link". About Flash /
Download program will now be controlled directly CoFlash (the window of CoFlash must
be closed).Alternatively, you can click on the appropriate button below the menu
bar. C99: I recommend the compiler in C99 mode to use. Otherwise, no variables can
be declared, for example, for loops. To do this, go to Project / Configuration and adds at
the end of the text box next to "compiler" still -std = c99 a parameter.

// Set 4 clock frequency


Before carrying out its own code in the main-Funktionn, should the function system
init ()call. It is implemented in the file system_stm32f10x.c. Here, the clock source
of the PLL factor and prescaler for AHB / APBx be established and a number of other
things
that
are
necessary
for
a
proper
start.
By default, the clock is set to 72 MHz, assuming that an 8 MHz crystal (HSE_VALUE ) is
connected. Attention: This high clock rate is not supported by every chip! More details
can
be
found
in
the
comments
in
the
file.
If you want to change the system clock on 8/24/36/48/56 MHz, only the respective line
must uncomment the line for 72 MHz and commented out. The block before #else is
compiled, if a STM32 Value Line is selected.
..\cmsis_boot\system_stm32f10x.c
STM32F103RB
#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* #define SYSCLK_FREQ_HSE HSE_VALUE */
#define SYSCLK_FREQ_24MHz 24000000
#else
/* #define SYSCLK_FREQ_HSE HSE_VALUE */
/* #define SYSCLK_FREQ_24MHz 24000000 */
/* #define SYSCLK_FREQ_36MHz 36000000 */
/* #define SYSCLK_FREQ_48MHz 48000000 */
/* #define SYSCLK_FREQ_56MHz 56000000 */
#define SYSCLK_FREQ_72MHz 72000000
#endif

If you want to configure a different from the values given above frequency, the matter is
somewhat more complicated. But first a few possibilities and limitations at a
glance. There are three sources for the system clock (SYSCLK) to choose from:

HSI: high speed internal clock interner RC Oszillator mit 8 MHz

HSE: high speed external clock - generated by a quartz (3 ... 25 MHz) or an


external clock source (up to 50 MHz)

PLL phase lock loop - Multiplies the frequency of the source with 2 ... 16th As a
source either HSI / 2 or HSE serves. Note: In the Connectivity Line are other factors
available for additional options.

SYSCLK is generated from the clock for AHB (Advanced High Performance Bus) via a
prescaler (512 1, 2, 4, ...). For this, in turn, two prescaler (1, 2, 4, 8, 16) and the clocks
for APB1 APB2 (Advanced Peripheral Bus) generated. Note the maximum frequencies
(lower, depending on the series):

SYSCLK / CSO / APB2: 72 MHz

APB1: 36 MHz

Depending on the chip, there are other possibilities. It is to read the datasheet useful in
any case.
As an example, I will now show how the clock frequency to 64 MHz reconfigured at a
STM32F103 (8 MHz quartz). APB1 is divided down to 32 MHz. The first one adds a
define for the new frequency.
..\cmsis_boot\system_stm32f10x.c
STM32F103RB
#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* #define SYSCLK_FREQ_HSE HSE_VALUE */
#define SYSCLK_FREQ_24MHz 24000000
#else
/* #define SYSCLK_FREQ_HSE HSE_VALUE */
/* #define SYSCLK_FREQ_24MHz 24000000 */
/* #define SYSCLK_FREQ_36MHz 36000000 */
/* #define SYSCLK_FREQ_48MHz 48000000 */
/* #define SYSCLK_FREQ_56MHz 56000000 */
#define SYSCLK_FREQ_64MHz 64000000
/* #define SYSCLK_FREQ_72MHz 72000000 */
#endif

CoIDE deposited all gray code, which is due to the lack defines not active. Therefore,
one can go through the file now well and at the crucial points are excerpts from the old
code Copy and customize for the required 64 MHz. The following code snippet must be
added.
..\cmsis_boot\system_stm32f10x.c
STM32F103RB
#elif defined SYSCLK_FREQ_64MHz
uint32_t SystemCoreClock = SYSCLK_FREQ_64MHz; /*!< System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_64MHz
static void SetSysClockTo64(void);
#elif defined SYSCLK_FREQ_64MHz
SetSysClockTo64();

As a final step you have to have the function SetSysClockTo64 (void) create. The
easiest way to do this by copying the function SetSysClockTo72
(void) including #elif defined SYSCLK_FREQ_72MHz . Then rename it to the
function + Define and following line fits:
..\cmsis_boot\system_stm32f10x.c
STM32F103RB
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);

to
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL8);

Herewith one has the factor in the PLL made from 9 to 8 (8 * 8 MHz = 64 MHz). There
remains the question of how to change the prescaler for CSO APB1 and APB2. In this
situation it is all right, since the prescaler for APB1 at 72 MHz configuration was already
set to 2. If you want to change the values, the following line should be amended
accordingly:
..\cmsis_boot\system_stm32f10x.c
STM32F103RB
/* HCLK = SYSCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;

// 5 Standard Peripheral Library


The Standard Peripheral Library provides all functions to comfortably access the
hardware.Thus, the registers in the microcontroller itself must never be used directly in
your code. In CoIDE the individual components of the library can be selected according
to need.

After that the header files must be included by uncomment.


..\cmsis_boot\stm32f10x_conf.h
STM32F103RB
/* Includes -----------------------------------------------------------*/
/* Uncomment the line below to enable peripheral header file inclusion */
/* #include "stm32f10x_adc.h" */
/* #include "stm32f10x_bkp.h" */
#include "stm32f10x_can.h"
/* #include "stm32f10x_cec.h" */
/* #include "stm32f10x_crc.h" */
/* #include "stm32f10x_dac.h" */
/* #include "stm32f10x_dbgmcu.h" */

#include "stm32f10x_dma.h"
/* #include "stm32f10x_exti.h" */
/* #include "stm32f10x_flash.h" */
/* #include "stm32f10x_fsmc.h" */
#include "stm32f10x_gpio.h"
#include "stm32f10x_i2c.h"
/* #include "stm32f10x_iwdg.h" */
/* #include "stm32f10x_pwr.h" */
#include "stm32f10x_rcc.h"
/* #include "stm32f10x_rtc.h" */
/* #include "stm32f10x_sdio.h" */
#include "stm32f10x_spi.h"
/* #include "stm32f10x_tim.h" */
#include "stm32f10x_usart.h"
/* #include "stm32f10x_wwdg.h" */
/* #include "misc.h" */ /* High
functions) */

level

functions

for

NVIC

and

SysTick

(add-on

to

CMSIS

// 6 RCC Reset and clock control


To use any peripheral module must this be made available first clock signal.
..\main.c
STM32F103RB
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

Enable clock for IO Port A


RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);

Enable clock for IO port A and B


RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);

Enable clock for CAN Interface


RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

Clock enable for DMA controller 1


Which module is operated which bus is given in the data sheet. If you want to disable
the clock of a module again, so is the second parameter DISABLE to pass

// 7 GPIO/AFIO General-purpose and alternate-function I/Os


To use a port must first be clock signal, as described previously, activated - as this for all
modules is basically the case, I will not explicitly address it in the next sections. In
addition, each pin must be initialized. For this purpose the function GPIO_Init
(GPIO_TypeDef * GPIOx, GPIO_InitTypeDef GPIO_InitStruct *) are

available. The first parameter specifies include the name of the port. The second
parameter, GPIO_InitTypeDef address of a structure is passed. In this structure, the
behavior of the / of the pins is set.
..\main.c
STM32F103RB
#include "stm32f10x_conf.h"
int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SystemInit ( ) ;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_WriteBit(GPIOB, GPIO_Pin_0, Bit_SET);
while(1){}
}

Configuring PB0 and PB1 as output and setting PB0 to high-level


The structure GPIO_InitTypeDef has three elements:

GPIO_Mode
possible values: GPIO_Mode_AIN - Analog input GPIO_Mode_IN_FLOATING floating inputGPIO_Mode_IPD - input with pull-down GPIO_Mode_IPU - Input with
pull-upGPIO_Mode_Out_OD - output open drain GPIO_Mode_Out_PP - output pushpullGPIO_Mode_AF_OD - Alternative function open-drain GPIO_Mode_AF_PP Alternative function push -Pull The latter two values must be selected if the output pin
of
a
module
as
something
USART, SPI,
I22
is
used,
etc.

GPIO_Pin
possible values: GPIO_Pin_x - x: 0 ... 15 GPIO_Pin_All - all pins of ports Multiple

pins

are | be

configured

simultaneously.

GPIO_Speed
possible
values: GPIO_Speed_2MHz GPIO_Speed_10MHz GPIO_Speed_50MHz Only
relevant
for
outputs. Maximum
frequency
of
the
output
signal.

Individual bits can use the function GPIO_WriteBit be set or cleared. Other functions
shown in the following code snippet.
..\main.c
STM32F103RB
uint8_t dataByte;
uint16_t dataHalfWord;
// Setting PA0 and PA2
GPIO_SetBits ( GPIOA , GPIO_Pin_0 | GPIO_Pin_2 ) ;
// Delete PA1 and PA3
GPIO_ResetBits ( GPIOA , GPIO_Pin_1 | GPIO_Pin_3 ) ;
// 0x1234 on PORTB Write
GPIO_Write ( GPIOB , 0x1234 ) ;
// Lesen des Bits PC0
dataByte = GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_0);
// Lesen des Bits PC0 aus dem Output Register
dataByte = GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_0);
// Read from PORTC
data halfword = GPIO_ReadInputData ( GPIOC ) ;
// Read from the output register PORTC
data halfword = GPIO_ReadOutputData ( GPIOC ) ;

For many peripheral modules of the corresponding pins can choose between different
alternatives:
..\main.c
STM32F103RB
// default: PB6 - I2C1_SCL; PB7 - I2C1_SDA
// remap: PB8 - I2C1_SCL; PB9 - I2C1_SDA
GPIO_PinRemapConfig(GPIO_Remap_I2C1, ENABLE);

Remap the I 2 C1 pins when STM32F103

For a complete list of possible values for GPIO_PinRemapConfig (uint32_t


GPIO_Remap, Functional State NewState) , see the documentation for the
standard Peripheral Library.Which remaps the actual chip are possible, one can read
the data sheet.

// 8

General-purpose

timers

// 8.1 PWM
The timer of the STM32 have relatively many features, so I will confine myself in this
section to the first configuration with respect to the output of a PWM signal. It is to be
generated on PA0 means TIM2 a 1 kHz signal having a duty ratio of 10%:
..\main.c
STM32F103RB
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBase_InitStructure;
TIM_OCInitTypeDef TIM_OC_InitStructure;
systemnit ( ) ;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBase_InitStructure. TIM_ClockDivision = TIM_CKD_DIV1 ;
TIM_TimeBase_InitStructure. TIM_CounterMode = TIM_CounterMode_Up ;
TIM_TimeBase_InitStructure. TIM_Period = 999 ;
TIM_TimeBase_InitStructure. TIM_Prescaler = 71 ;
TIM_TimeBaseInit ( TIM2 , & TIM_TimeBase_InitStructure ) ;
TIM_OC_InitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OC_InitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
TIM_OC_InitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Set;
TIM_OC_InitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC_InitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OC_InitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OC_InitStructure.TIM_OutputNState = TIM_OutputNState_Disable;
TIM_OC_InitStructure.TIM_Pulse = 100;
TIM_OC1Init(TIM2, &TIM_OC_InitStructure);
TIM_Cmd(TIM2, ENABLE);

First, the clock for both the PortA, ie also for Timer 2 is activated as usual. It should be
noted that the clock of APB1 is automatically multiplied by the factor of 2, if the APB1
prescaler has a value different from 1. In this case, the 72 MHz clock is divided down to
36 MHz for APB1 according to the default settings. Consequently, the clock of timer 2 is
again
72
MHz.
The
code
then
PA0
initialized
as
an
alternative
function
output.
In the next two blocks registers for the time base and the output compare channel can
be
configured.
With TIM_CMD (...) , the timer is activated ,
TIM_TimeBaseInitTypeDef :

TIM_ClockDivision
prescaler if the timer gets its clock from an external input. Other values have no
effect on the clock, if the internal clock CK_INT is used - which is the case here.

TIM_CounterMode
possible
Werte: TIM_CounterMode_Up TIM_CounterMode_DownTIM_CounterMode_Ce
nterAligned1 TIM_CounterMode_CenterAligned2TIM_CounterMode_Cen
terAligned3

TIM_Period
The timer counts from 0 up to this value. Or the other way around, if another Counter
mode is selected. Maximum of 2 16 = 65535th

TIM_Prescaler
Tells down the input clock of the timer. Divider: TIM_Prescaler + 1. For example, is
divided by two when the clock TIM_Prescaler = 1 Maximum of 2. 16 = 65535th

TIM_OCInitTypeDef :

TIM_OCMode
With
regard
to
the
PWM
function,
the
two
values
TIM_OCMode_PWM1 andTIM_OCMode_PWM2 be selected. In the PWM1 output is
during the pulse duration on high and then low, if the rest of the settings are as
above. PWM 2 to generate the inverted waveform.

TIM_OCIdleState
state of OC pins in the Idle State - see note below.

TIM_OCNIdleState
state of the OCN pins in the Idle State - see note below.

TIM_OCPolarity
Here, the polarity of the OC output can be reversed.

TIM_OCNPolarity
Here, the polarity of the OCN output can be reversed. Note: If OC and OCN the
same polarity setting, OCN is compared with OC already inverted.

TIM_OutputState
active Erten / disable the OC output.

TIM_OutputNState
Erten active / deactivate the OCN output.

TIM_Pulse
Pulsweite des Signals.

Notes:
For the timers 1, 8, 15, 16 and 17, the PWM outputs must additionally with the
functionTIM_CtrlPWMOutputs
(...) .
are
activated
only if the timer is enabled but the PWM outputs are disabled get the settings for idle
State
to
bear.
The OCN outputs there is generally possible only with the timers 1, 8, 15, 16 and
17th The settings of the output compare channel can be set similarly for all three other
channels available. It is therefore possible, for example, four PWM outputs are
implemented with independent duty cycle at the same frequency. For this purpose exist
side TIM_OC1Init (...) nor the functions TIM_OC2Init (...) , TIM_OC3Init
(...) and TIM_OC4Init
(...) .

// 8.2 Encoder Interface


TIM1 & TIM8 and TIM2 ... TIM5 each have built a quadrature encoder, thus offering the
possibility of evaluating the signals of an incremental encoder directly.

The timer counts based on the signals TI1 and TI2, which corresponds to the levels of
TIMx_CH1 and TIMx_CH2 in the simplest case ,
..\main.c
STM32F103RB
GPIO_InitTypeDef GPIO_InitStructure;
systemnit ( ) ;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_EncoderInterfaceConfig
TIM_ICPolarity_Falling ) ;

TIM3

TIM_EncoderMode_TI12

TIM_ICPolarity_Rising

TIM_Cmd(TIM3, ENABLE);

The encoder of the timer is the function TIM_EncoderInterfaceConfig


(...) prepared.
The
second
parameter
specifies
the
Encoder
Mode
on. Possible
values:TIM_EncoderMode_TIx - at x = 1 or x = 2 the timer counts at edges on TI1 and
TI2; at x = 12 he enumerates edges on TI1 and TI2. The latter mode should be
selected, if should be counted in both directions, and there are two signal lines.
three,
and
four
parameters
define
the
polarity
of
the
individual
inputs. EitherTIM_ICPolarity_Falling or TIM_ICPolarity_Rising be
transferred. By turning the polarity of a signal the direction of a quadrature signal can be
inverted. The current count is withTIM_GetCounter (...) retrieved.
..\main.c
STM32F103RB
uint16_t counter = TIM_GetCounter(TIM3);

// 9

Interrupts

// 9.1 Externe Interrupts


To use the functions for External interrupts must be selected in the CoIDE repository
ExtI and MISC and in the file stm32f10x_conf.h be uncomment the appropriate
lines. The model for the interrupt service routines you should copy the files to the root
directory of his project from the Standard Peripheral Library (Project \
STM32F10x_StdPeriph_Template) stm32f10x_it.c and stm32f10x_it.h. The header file
must then still with "stm32f10x_it.h" #include be involved in stm32f10x_conf.h.

The following program will generate an interrupt on each rising edge on PA0. It is
compatible with the STM32VL Discovery.
..\main.c
STM32F100RB
#include "stm32f10x_conf.h"
int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
SystemInit ( ) ;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA
ENABLE);

RCC_APB2Periph_GPIOC

RCC_APB2Periph_AFIO,

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
NVIC_Init(&NVIC_InitStructure);
while(1){}
}

For external interrupts are 16 interrupt lines are available. Each line can be connected
to any port, the pin number is already set. EXTI0 can be interconnected with ... PA0,
PB0, but not, for example, with PA1. The function GPIO_EXTILineConfig know in the
above code, the EXTI0-Line PORTA, so PA0 to. . Note: There are 3 or 4 more ExtI lines
available, their sources are not configurable - see Reference Manual EXTI_Init
(EXTI_InitTypeDef * EXTI_InitStruct) initializes the ExtI Line. The structural
elements are self-explanatory.EXTI_Mode can alternatively EXTI_Mode_Event be set
- Events I will deal with in another chapter. EXTI_Trigger determines on which edge

at pin interrupt to be triggered. The block then sets the NVIC (Nested Vectored Interrupt
Controller) on - the module that is responsible for the interrupt management. Hint: To
NVIC
are
in
the
Programming
Manual,
not
in
the
Reference
Manual. NVIC_IRQChannel specifies which interrupt vector is to be initialized. In
hardware design, one should note that only EXTI0 ... occupied EXTI4 separate interrupt
vectors. EXTI9_5
and
EXTI15_10
summarize
the
remaining
ExtI
Lines.NVIC_IRQChannelPreemptionPriority defines the preemption priority level
of the interrupt between 0 and 15 proof. Interrupts with a lower number have a higher
priority. If an interrupt is triggered with a higher priority, while an interrupt a lower priority
is executed, the latter is stopped and started the execution of the interrupt handler of the
higher priority interrupts.NVIC_IRQChannelSubPriority sets in addition to each
group a specific Preemption Priority Levels set a sub Priority Level , If multiple interrupts
of the same preemption priority in the queue, the interrupt with the lower sub Priority
Level is executed first. This is, however, not interrupted by an interrupt of higher priority
sub if it has the same preemption priority.However, there are not simultaneously Sub
and Preemption Priority all values from 0 to 15 are available, since only a total of 4
priority
bits
are
present
in
the
STM32. These
are
the
function NVIC_PriorityGroupConfig (...) divided into sub and Preemption
Priority. In the example, all four bits are assigned for the Preemption Priority Level - so it
will not distinguish between different sub Priorities. The interrupt is now configured as
far done. In the next step the code of the corresponding interrupt handler has yet to be
written. Add to that the following code snippet to file a stm32f10x_it.c. The exact terms
of the functional heads of the various interrupt handler can be looked up in the file
startup_stm32f10x_xx_xx.c. At the beginning of the interrupt handler, the pending bit
must be cleared. If we write this statement in the last line before the interrupt handler is
exited, this leads to problems, if the code optimizer is turned on. For practical
applications, the code should be in terms debouncing yet refined.

..\stm32f10x_it.c
STM32F100RB
void EXTI0_IRQHandler(void){

EXTI_ClearITPendingBit(EXTI_Line0);
if(GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_9)){
GPIO_WriteBit(GPIOC, GPIO_Pin_9, RESET);
}else{
GPIO_WriteBit(GPIOC, GPIO_Pin_9, SET);
}
}

// 9.2 Timer Interrupts


In this example, Timer 2 is configured to every 500 ms (72 MHz processor clock)
generates an interrupt. In the interrupt handler to an LED PA5 is then depending on the
previous state either on or off.
..\main.c
STM32F103RB
#include "stm32f10x_conf.h"
int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBase_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
SystemInit ( ) ;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBase_InitStructure. TIM_ClockDivision = TIM_CKD_DIV1 ;
TIM_TimeBase_InitStructure. TIM_CounterMode = TIM_CounterMode_Up ;
TIM_TimeBase_InitStructure. TIM_Period = 1999 ;
TIM_TimeBase_InitStructure. TIM_Prescaler = 17999 ;
TIM_TimeBaseInit ( TIM2 , & TIM_TimeBase_InitStructure ) ;
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM2, ENABLE);
while(1){}
}

The configuration is very similar to the case of external interrupts. First you have the
module that triggers the interrupt will be communicated in which / what event / events, it

should do so. Using the function TIM_ITConfig here the update event is selected. It
always occurs when the timer count register with be the auto-reload register
( TIM_Period ) updated, has thus reached the highest count of 0 and starts the
counting
operation.
In the NVIC then you must still enable the interrupt. The last thing missing is nor the
code for the interrupt handler.
..\stm32f10x_it.c
STM32F103RB
void TIM2_IRQHandler(void){
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_5)){
GPIO_WriteBit(GPIOA, GPIO_Pin_5, RESET);
}else{
GPIO_WriteBit(GPIOA, GPIO_Pin_5, SET);
}
}

I2C

// 10
// 10.1 Configuration
..\main.c
STM32F103RB
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
I2C_InitTypeDef I2C_InitStructure;
systemnit ( ) ;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_PinRemapConfig(GPIO_Remap_I2C1, ENABLE);
NVIC_InitStructure. NVIC_IRQChannel = I2C1_EV_IRQn ;
NVIC_InitStructure. NVIC_IRQChannelPreemptionPriority =
NVIC_InitStructure. NVIC_IRQChannelSubPriority = 0 ;
NVIC_InitStructure. NVIC_IRQChannelCmd = ENABLE ;
NVIC_Init ( & NVIC_InitStructure ) ;
NVIC_InitStructure. NVIC_IRQChannel = I2C1_ER_IRQn ;
NVIC_InitStructure. NVIC_IRQChannelPreemptionPriority =
NVIC_InitStructure. NVIC_IRQChannelSubPriority = 0 ;
NVIC_InitStructure. NVIC_IRQChannelCmd = ENABLE ;
NVIC_Init ( & NVIC_InitStructure ) ;
I2C_DeInit(I2C1);

0 ;

0 ;

I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = 100000;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_OwnAddress1 = 0;
I2C_Init(I2C1, &I2C_InitStructure);
I2C_ITConfig(I2C1, I2C_IT_EVT, ENABLE);
I2C_ITConfig(I2C1, I2C_IT_BUF, ENABLE);
I2C_ITConfig(I2C1, I2C_IT_ERR, ENABLE);
I2C_Cmd(I2C1, ENABLE);

I2C_InitTypeDef:

I2C_Ack
Upon receipt of the own address or a data byte an acknowledgment bit is sent
ifI2C_Ack_Enable .

I2C_AcknowledgedAddress
bit
length
of
its
own
address
in
slave
mode. Possible
values:I2C_AcknowledgedAddress_7bit , I2C_AcknowledgedAddress_10bi
t

I2C_ClockSpeed
frequency of the clock line in Hz. Note: If not using the default configuration, but, for
example, a 16 MHz crystal, the internal calculation is wrong and the resulting
frequency deviates from the specified value.

I2C_DutyCycle
Sets the ratio of low to high in fast mode fixed (from 100kHz). Possible
values:I2C_DutyCycle_16_9 (T low /
T high =
16/9), I2C_DutyCycle_2 (T low /
T high = 2)
I2C_Mode

IC
/
SMBus

Mgliche
Werte: I2C_Mode_I2C, I2C_Mode_SMBusDevice,I2C_Mode_SMBusHost

I2C_OwnAddress1
definition of the first private address - in the STM32 can be used in addition a second
own address. Range: 0 ... 127/0 ... 1023 defined according to previously address
range. The setting is only useful if the STM32 operates as a slave.

Important : In order for the I2C module is working properly, it must be initialized before
the function I2C_DeInit (...) be uninitialized.
With I2C_ITConfig (...) the three different interrupt sources to be enabled. The
event interrupt is triggered when the start bit or the address was sent in master mode or
a byte transfer is complete. In slave mode, it is triggered upon receipt of your own
address, or stop bits, and a byte has been transmitted. The buffer interrupt occurs when
the receive buffer contains a new byte transmit buffer is empty or. Event and buffer
interrupts
land
in
the
same
interrupt
vector.
In addition, an Error Interrupt Vector is available.
The routines of the I 2 C module to be exemplary outsourced here in the file i2c.c. These
each have a corresponding function of the file i2c.c is called from within the interrupt
handler.
..\stm32f10x_it.c
STM32F103RB
void I2C1_EV_IRQHandler(void){
i2c_handleEventInterrupt();
}
void I2C1_ER_IRQHandler(void){
i2c_handleErrorInterrupt();
}

// 10.2 & send (STM32 = master)


..\i2c.h
STM32F103RB
#include "stm32f10x_conf.h"
void i2c_handleEventInterrupt(void);
void i2c_handleErrorInterrupt(void);
void i2c_create(I2C_TypeDef * I2Cx);
void i2c_writeByte(uint8_t address, uint8_t byte);
void i2c_writeTwoBytes(uint8_t address, uint8_t byte1, uint8_t byte0);
void i2c_readTwoBytes(uint8_t address);
uint16_t i2c_getData(void);

So that the following functions are visible both in main.c and stm32f10x_it.c they are
declared in the header file i2c.h. i2c.h still must then use #include "i2c.h" be
involved in stm32f10x_conf.h.
..\i2c.c
STM32F103RB
#include "i2c.h"

I2C_TypeDef * I2C_Module;
volatile uint8_t deviceAddress;
volatile uint8_t dataByte1;
volatile uint8_t dataByte0;
volatile uint8_t receivedDataByte1;
volatile uint8_t receivedDataByte0;
volatile uint8_t i2cDirectionWrite;
volatile uint8_t i2cByteCounter;
volatile uint8_t i2cBusyFlag;
void i2c_writeByte(uint8_t address, uint8_t byte){
while(i2cBusyFlag){}
while(I2C_GetFlagStatus(I2C_Module,I2C_FLAG_BUSY)){}
deviceAddress = address;
dataByte0 = byte;
i2cDirectionWrite = 1;
i2cBusyFlag = 1;
i2cByteCounter = 1;
I2C_GenerateSTART(I2C_Module, ENABLE);
}
void i2c_writeTwoBytes(uint8_t address, uint8_t byte1, uint8_t byte0){
while(i2cBusyFlag){}
while(I2C_GetFlagStatus(I2C_Module,I2C_FLAG_BUSY)){}
deviceAddress = address;
dataByte1 = byte1;
dataByte0 = byte0;
i2cDirectionWrite = 1;
i2cBusyFlag = 1;
i2cByteCounter = 2;
I2C_GenerateSTART(I2C_Module, ENABLE);
}
void i2c_readTwoBytes(uint8_t address){
while(i2cBusyFlag){}
while(I2C_GetFlagStatus(I2C_Module,I2C_FLAG_BUSY)){}
deviceAddress = address;
i2cDirectionWrite = 0;
i2cBusyFlag = 1;
i2cByteCounter = 2;
I2C_AcknowledgeConfig(I2C_Module, ENABLE);
I2C_GenerateSTART(I2C_Module, ENABLE);
}
void i2c_create(I2C_TypeDef * I2Cx){
I2C_Module = I2Cx;
i2cBusyFlag = 0;
}
uint16_t i2c_getData(void){
return (receivedDataByte1 << 8) | receivedDataByte0;
}
// ISR
void i2c_handleEventInterrupt(void){
if(I2C_GetFlagStatus(I2C_Module, I2C_FLAG_SB) == SET){
if(i2cDirectionWrite){
// STM32 Transmitter
I2C_Send7bitAddress(I2C_Module, deviceAddress, I2C_Direction_Transmitter);
}else{
// STM32 Receiver

I2C_Send7bitAddress(I2C_Module, deviceAddress, I2C_Direction_Receiver);


}
}else if(I2C_GetFlagStatus(I2C_Module, I2C_FLAG_ADDR) == SET || I2C_GetFlagStatus(I2C_Module,
I2C_FLAG_BTF) == SET){
I2C_ReadRegister(I2C_Module, I2C_Register_SR1);
I2C_ReadRegister(I2C_Module, I2C_Register_SR2);
if(i2cDirectionWrite){
// STM32 Transmitter
if(i2cByteCounter == 2){
I2C_SendData(I2C_Module, dataByte1);
i2cByteCounter--;
}else if(i2cByteCounter == 1){
I2C_SendData(I2C_Module, dataByte0);
i2cByteCounter--;
}else{
I2C_GenerateSTOP(I2C_Module, ENABLE);
i2cBusyFlag = 0;
}
}
}else if(I2C_GetFlagStatus(I2C_Module, I2C_FLAG_RXNE) == SET){
// STM32 Receiver
I2C_ReadRegister(I2C_Module, I2C_Register_SR1);
I2C_ReadRegister(I2C_Module, I2C_Register_SR2);
i2cByteCounter--;
if(i2cByteCounter == 1){
I2C_AcknowledgeConfig(I2C_Module, DISABLE);
I2C_GenerateSTOP(I2C_Module, ENABLE);
receivedDataByte1 = I2C_ReceiveData(I2C_Module);
}else{
receivedDataByte0 = I2C_ReceiveData(I2C_Module);
i2cBusyFlag = 0;
}
}
}
// ISR
void i2c_handleErrorInterrupt(void){
I2C_GenerateSTOP(I2C_Module, ENABLE);
i2cBusyFlag = 0;
I2C_ClearFlag(I2C_Module, I2C_FLAG_AF);
I2C_ClearFlag(I2C_Module, I2C_FLAG_ARLO);
I2C_ClearFlag(I2C_Module, I2C_FLAG_BERR);
}

The
functions I2C_WriteByte
(...) , i2c_writeTwoBytes
(...) and i2c_readTwoBytes (...) are implemented similarly. First, wait until the
variable i2cBusyFlag contains the value 0 to an active transfers not to interrupt. For
the same purpose, the following while loop, which is a status flag of the I 2 C module
queries. For practical use, you should not query a timeout counter in the loops so that
the processor can not catch. Alternative could be bound with return directly from the
function, if the I 2 C module is busy, so the processor in the meantime can do other
work.
Then all parameters into variables cached because these values are used in the

interrupt ,The following flags / counters should be self-explanatory.


At the end of the function I is 2 C module indicated that it should send a start signal. Due
to the configuration of these interrupts are then automatically starts. i2c_create
(...) must be in the main program with the desired I 2 are called C
module. Alternatively, you could also send the module #define set the code to keep
portable. i2c_getData (...) returns the received data. In the event interrupt
handler checks whether the SB-flag is set. If this is the case, then the expected I 2 Cmodule the address of the devices to be addressed in the next step. Otherwise, if one of
the flags ADDR (address sent - ADD10 previously set by the first byte of a 10-bit
address) or BTF (byte transfer Finished) is set, the block after the firstelse if .
performed here can read out the register SR1 and SR2 will not forget. Otherwise, the
flags are not cleared and the subsequent communication does not work anymore. When
the code optimizer must be prevented that this path-optimized code, for example, by
receiving the return value of a volatile variable. The part is relatively simple to send
bytes. If the byte counter is greater than 0, then successively more bytes are in the
I 2 C-pushed module. Otherwise, a stop signal is transmitted and the
variable i2cBusyFlag set to 0 to enable new transfers. In receive mode, sets the I 2 C
module RXNE the flag when it has completely received a byte and triggers an interrupt
from here also. When receiving bytes is to look for the right timing. The commands for
sending the NACK bits and the stop signal must be added directly before reading the
penultimate bytes. If only one byte is received, such measures shall be initiated after
sending the address. If an error occurs during communication, the error interrupt
handler is called. This may be the case if there is no device that uses ACK bit to the
transmitted address. In the handler so the bus by means of a stop signal should be
released
again. Also,
the
error
flag
must
be
cleared.

// 11

CAN

// 11.1 Configuration
..\main.c
STM32F103RB
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
systemnit ( ) ;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_PinRemapConfig(GPIO_Remap1_CAN1 , ENABLE);
CAN_InitStructure.CAN_Prescaler = 2;
CAN_InitStructure.CAN_SJW = CAN_SJW_2tq;
CAN_InitStructure.CAN_BS1 = CAN_BS1_11tq;
CAN_InitStructure.CAN_BS2 = CAN_BS2_4tq;
CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
CAN_InitStructure.CAN_TTCM = DISABLE;
CAN_InitStructure.CAN_ABOM = DISABLE;
CAN_InitStructure.CAN_AWUM = DISABLE;
CAN_InitStructure.CAN_NART = ENABLE;
CAN_InitStructure.CAN_RFLM = DISABLE;
CAN_InitStructure.CAN_TXFP = DISABLE;
CAN_Init(CAN1, &CAN_InitStructure);

The bit rate of the CAN bus is calculated from APB1 / CAN_Prescaler / total time
quanta.There are three segments of time quanta that occur in the following order:
SYNC_SEG (the STM32 always 1), BS1 and BS2. The clock APB1 the sample code is
32 MHz.Consequently, the CAN bit rate is 32 MHz / 2 / (1 + 11 + 4) = 1 MBit / s.
The sample point lies between BS1 and BS2, and should be at about 75%.
(1 + tq tq 11): Here / (1 + 11 + 4 tq tq tq) = 75%.
In the hardware circuitry is to be noted that also for testing the RX and TX lines
(possibly via level converter and termination) are interconnected. Otherwise, the CAN
module itself can not "hear" and will not send a complete frames.
CAN_InitTypeDef:

CAN_Prescaler
prescaler - Value range: 1 ... 1024

CAN_SJW
Synchronization Jump Width - the CAN module can be a bit around this value
increase or reduce resynchronization, Range: CAN_SJW_1tq ... CAN_SJW_4tq

CAN_BS1
Time Quantum in Bit Segment 1 Wertebereich: CAN_BS1_1tq ... CAN_BS1_16tq

CAN_BS2
Time Quantum in Bit Segment 2 Wertebereich: CAN_BS2_1tq ... CAN_BS2_8tq

CAN_Mode
mgliche
Werte: CAN_Mode_Normal, CAN_Mode_LoopBack, CAN_Mode_Silent,CAN_Mode
_Silent_LoopBack
Alle Modes neben Normal dienen zu Testzwecken. Dabei kann das CAN-Modul seine
gesendeten Nachrichten selbst empfangen ohne dabei zu senden, ohne Nachrichten
vom Bus zu empfangen oder beides. Genaueres ist dem Reference Manual zu
entnehmen.

CAN_TTCM
time-triggered communication mode - the CAN module stores the times when a
message is sent or a message is received if to ENABLE set. How this is to be read is
in the Reference Manual.

CAN_ABOM
Automatic Bus-Off Management - The bus-off state is exited automatically when 128
times 11 recessive bits are detected on the bus if on ENABLE set. Otherwise, the
CAN module must be manually re-initialized. The bus-off state is triggered by 255
transmission errors in transmission and prevents further transmission and reception
of messages.

CAN_AWUM
Automatic Wakeup Mode - determines how the sleep mode is
exited, DISABLE means that the software will delete the SLEEP bits must awaken
the module, ENABLE means that the module is automatically awakened when a
message arrives on the CAN bus ,

CAN_NART
No Automatic Retransmission - If on ENABLE is set, each message is sent only once,

regardless of whether an error occurred. Otherwise, the CAN module tries to send
the message until no more transmission error occurs or the bus-off state is triggered.

CAN_RFLM
. Receive FIFO Locked Mode - defines what should happen when the receive FIFO
overflows DISABLE .: A new incoming message overwrites the previously arrived,
lying in the FIFO message ENABLE : A new incoming message is discarded.

CAN_TXFP
Transmit FIFO Priority - determines the order will be sent after the news, either
Identifier ( DISABLE ) or chronological ( ENABLE ).
// Send 11.2 News

..\main.c
STM32F103RB
...
CanTxMsg canMessage;
canMessage.StdId
canMessage.ExtId
canMessage.RTR =
canMessage.IDE =
canMessage.DLC =

= 0x123;
= 0;
CAN_RTR_DATA;
CAN_ID_STD;
8;

canMessage.Data[0]
canMessage.Data[1]
canMessage.Data[2]
canMessage.Data[3]
canMessage.Data[4]
canMessage.Data[5]
canMessage.Data[6]
canMessage.Data[7]

=
=
=
=
=
=
=
=

0;
1;
2;
3;
4;
5;
6;
7;

CAN_Transmit(CAN1, &canMessage);

CanTxMsg :

StdId
Standard Identifier - Range: 0 ... 0x7FF

EXTID
extended identifier - Value range: 0 ... 0x1FFFFFFF

RTR
Remote
Transmission
at CAN_RTR_Remote set.

Request. Features

remote

frame,

if

IDE
choice whether standard or extended identifier to be used for the message.

DLC
length of the payload in bytes - Value range: 0 ... 8

Data
[8]
payload. Data [0] is sent first. DLC is less than 8, so the last bytes are not
transmitted.
while(!(CAN1->TSR & CAN_TSR_TME0 || CAN1->TSR & CAN_TSR_TME1 || CAN1->TSR & CAN_TSR_TME2)){}

Before sending you should check whether at least one of the three transmit mailboxes is
free. If no mailbox is more freely while trying to send, the message will disappear in the
nirvana. The above code will remain as long as this in a while loop until at least one of
the corresponding flag is set to 1. This solution should at least be supplemented by a
timeout counter to make sure that the loop will eventually leave. For example,
if CAN_NART toDISABLE set and there are no other active network CAN node, the
Transmit Mailboxes will never be released under certain circumstances.

Received // 11.3 News


Since the CAN bus, all data is transmitted as broadcast, each node must check the ID
with each message to determine if the message contains interesting information or can
be ignored. Can be adjusted to relieve the CPU in the STM32 hardware filter. These are
14 "Filter Banks" provided (28 in connectivity line devices). Each filter bank consists of
two 32-bit registers (CAN_FxR1 & CAN_FxR2), which can each be used in one of the
following options:

Extended
1x
1x
Mask
ID
and
ID indicates which IDs are passed. Mask determines which bits are important in ID
filtering - thereby using a single filter bank, an area to be released with multiple IDs.

2x Extended ID

2x Standard ID und 2x Mask

4x Standard ID

The following graph shows the meaning of the bits of CAN_FxR1 in 32-bit
mode. CAN_FxR2 is has exactly the same structure.

The 16-bit mode doubles the number of filter banks, but can be used only useful for
standard IDs.

The graphics are taken from the Reference Manual (RM0008) from page 640. More
details can be found there.
Pro CAN module there are two FIFOs, each offering space for three received
messages.Each filter bank is associated with a FIFO. Land there those messages that
have been "passed" by the filter. Each FIFO has its own interrupt vector.
..\main.c
STM32F103RB
int main(void) {
...
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CAN_FilterInitStructure.CAN_FilterNumber = 0;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0123 << 5;
CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0xFFFF;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0xFFFF;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
while (1) {}
}
void USB_LP_CAN1_RX0_IRQHandler(void) {

CanRxMsg RxMessage;
CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
if (RxMessage.Data[0] == 1) {
GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_SET);
} else {
GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_RESET);
}
}

In this example, a filter to the standard ID 0x123 is defined. All items with different IDs
will be ignored by the hardware. The filter is assigned FIFO 0. After receiving the
message, the interrupt handler is called. This copies the data from the FIFO in the
structure RxMessage and then turn an LED on PA5 corresponding byte 0 or off.
CAN_FilterInitTypeDef:

CAN_FilterNumber
Einzustellende Filter Bank, Werte: 0...13 (0...27 bei connectivity line devices)

CAN_FilterMode
CAN_FilterMode_IdList (2
Extended
oderCAN_FilterMode_IdMask (ID & Mask)

IDs

Standard

IDs)

CAN_FilterScale
CAN_FilterScale_16bit or CAN_FilterScale_32bit (16- / 32-bit mode, see
above)

CAN_FilterIdHigh
Upper
16
bits
of
Lower 16 bits of CAN_FxR2 (16-bit mode)

CAN_FilterIdLow
Lower 16 bits of CAN_FxR1

CAN_FilterMaskIdHigh
Upper 16 bits of CAN_FxR2

CAN_FilterMaskIdLow
Lower
16
bits
of
Upper 16 bits of CAN_FxR1 (16-bit mode)

CAN_FilterFIFOAssignment
Associated FIFO values: 0 or 1

CAN_FxR1

(32-bit

mode)

CAN_FxR2

(32-bit

mode)

CAN_FilterActivation
Specifies whether the filter should be turned on. Values: ENABLE or DISABLE

In connectivity line devices 28 Filter Banks are available. By default filter 0 ... 13 CAN1
and CAN2 14 ... 27 are assigned. The threshold at which the filter banks are used for
CAN2, using function CAN_SlaveStartBank (uint8_t CAN_BankNumber) 27 are
shifted to 1 ....
Finally, here are a few examples of the different combinations in the filter
initialization. By the bit shifts RTR & IDE flags and different meanings depending on the
filter width, the names of the structure elements are CAN_FilterIDHigh /
Low and CAN_FilterMaskIDHigh / Low unfortunately no longer very clearly. ST
has probably decided for performance reasons therefor.
STM32F103RB
// Alle IDs
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CAN_FilterInitStructure.CAN_FilterNumber = 0;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
// 0x12345670 ... 0x1234567F (Mask: 0xFFFFFFF0UL)
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CAN_FilterInitStructure.CAN_FilterNumber = 0;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
CAN_FilterInitStructure.CAN_FilterIdHigh = (0x12345670UL << 3) >> 16;
CAN_FilterInitStructure.CAN_FilterIdLow = 0x12345670UL << 3 | 4;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = (0xFFFFFFF0UL << 3) >> 16;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0xFFFFFFF0UL << 3 | 4;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
// 0x12345678 & 0x11223344
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CAN_FilterInitStructure.CAN_FilterNumber = 0;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdList;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
CAN_FilterInitStructure.CAN_FilterIdHigh = (0x12345678UL << 3) >> 16;
CAN_FilterInitStructure.CAN_FilterIdLow = 0x12345678UL << 3 | 4;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = (0x11223344L << 3) >> 16;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x11223344UL << 3 | 4;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;

CAN_FilterInit(&CAN_FilterInitStructure);
// 0x100 ... 0x11F (Mask: 0xFE0), 0x200 nur RTR;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CAN_FilterInitStructure.CAN_FilterNumber = 0;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_16bit;
CAN_FilterInitStructure.CAN_FilterIdHigh = 0x100 << 5;
CAN_FilterInitStructure.CAN_FilterIdLow = 0x200 << 5 | 0x10;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0xFE0 << 5;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0xFFF << 5 | 0x10;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
// 0x001, 0x011, 0x200 nur RTR, 0x333
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CAN_FilterInitStructure.CAN_FilterNumber = 0;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdList;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_16bit;
CAN_FilterInitStructure.CAN_FilterIdHigh = 0x001 << 5;
CAN_FilterInitStructure.CAN_FilterIdLow = 0x011 << 5;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x200 << 5 | 0x10;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x333 << 5;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);

// 12

SPI

// 12.1 Configuration
..\main.c
STM32F103RB
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
systemnit ( ) ;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA
ENABLE);

RCC_APB2Periph_AFIO

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;


GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

RCC_APB2Periph_SPI1,

GPIO_WriteBit(GPIOA, GPIO_Pin_8, SET);


NVIC_InitStructure.NVIC_IRQChannel = SPI1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
SPI_InitStructure.
SPI_InitStructure.
SPI_InitStructure.
SPI_InitStructure.
SPI_InitStructure.
SPI_InitStructure.
SPI_InitStructure.
SPI_InitStructure.
SPI_InitStructure.
SPI_Init ( SPI1 ,

SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32 ;
SPI_CPHA = SPI_CPHA_2Edge ;
SPI_CPOL = SPI_CPOL_High ;
SPI_CRCPolynomial = 0 ;
SPI_DataSize = SPI_DataSize_8b ;
SPI_Direction = SPI_Direction_2Lines_FullDuplex ;
SPI_FirstBit = SPI_FirstBit_MSB ;
SPI_Mode = SPI_Mode_Master ;
SPI_NSS = SPI_NSS_Soft ;
& SPI_InitStructure ) ;

SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_RXNE, ENABLE);


SPI_Cmd(SPI1, ENABLE);

Pin-Konfiguration: PA8 = Chip Select, PA7 = MOSI, MISO = PA6, PA5 = SCK
SPI_InitTypeDef:

SPI_BaudRatePrescaler
divider
for
the
clock
signal
from
APB2. Possible
values: SPI_BaudRatePrescaler_xwith x = 2, 4, 8, 16, 32, 64, 128, 256

SPI_CPHA
edge of the clock line at which the data is to be transferred. Possible
values:SPI_CPHA_1Edge (CPHA = 0), SPI_CPHA_2Edge (CPHA = 1)

SPI_CPOL
voltage level of the clock is in the idle state. Possible values: SPI_CPOL_Low (CPOL
= 0), SPI_CPOL_High (CPOL = 1)

SPI_CRCPolynomial
The SPI module can during data reception calculate a CRC value. The polynomial to
be defined here. Depending on the selected word size (see below) is calculated
using either a CRC-8 or CRC-16 value.

SPI_DataSize
size of the data word transmitted in one piece - whereas it is the effective width of the

data register which is used for receiving and transmitting the data. Possible
values:SPI_DataSize_8b , SPI_DataSize_16b

SPI_Direction
choice between uni- and bi-directional data mode. Possible

SPI_FirstBit
bit order. Possible values: SPI_FirstBit_LSB , SPI_FirstBit_MSB

SPI_Mode
Determines whether the STM32 behaves
values:SPI_Mode_Master , SPI_Mode_Slave

as

master

or

slave. Possible

SPI_NSS
Slave Select Management - The chip select function of the SPI module can be
controlled directly from the NSS pin. This feature is of good use when the STM32
operates as a slave or is used in a multi-master system. Otherwise, you can use any
other pin of the chip select signal. With SPI_NSS_Soft the pin can be used as
normal GPIO pin. Alternatively value: SPI_NSS_Hard . The functionality can be
found in the Reference Manual.

..\stm32f10x_it.c
STM32F103RB
void SPI1_IRQHandler(void){
spi_handleSPI1Interrupt();
}

// 12.2 & send (STM32 = master)


..\spi.h
STM32F103RB
#include "stm32f10x_conf.h"
void spi_create(SPI_TypeDef * SPIx, GPIO_TypeDef * CS_GPIOx, uint16_t CS_GPIO_Pin);
void spi_handleSPI1Interrupt(void);
void spi_writeTwoBytes(uint8_t byte1, uint8_t byte0);

The sample code will initially have only three public functions: spi_create (...) .
defines the SPI module and the chip select pin spi_handleSPI1Interrupt () is
called by the interrupt handler when an interrupt has occurred. spi_writeTwoBytes
(...) sends two bytes, and stores the two bytes received in the buffer. For the return
of the received data other functions can be written when required.
..\spi.c

STM32F103RB
#include "spi.h"
#define BUFFER_SIZE 2
SPI_TypeDef * SPI_Module;
GPIO_TypeDef * CS_GPIO;
uint16_t CS_GPIO_Pin;
uint8_t
uint8_t
uint8_t
uint8_t

spiRxCounter;
spiTxCounter;
spiBusyFlag;
spiDataBuffer[BUFFER_SIZE];

void spi_create(SPI_TypeDef * SPIx, GPIO_TypeDef * CS_GPIOx, uint16_t CS_GPIO_Pin_x){


SPI_Module = SPIx;
CS_GPIO = CS_GPIOx;
CS_GPIO_Pin = CS_GPIO_Pin_x;
spiBusyFlag = 0;
}
void spi_enableTxInterrupt(void){
SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_TXE, ENABLE);
}
void spi_disableTxInterrupt(void){
SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_TXE, DISABLE);
}
void spi_chipSelect(void){
GPIO_WriteBit(CS_GPIO, CS_GPIO_Pin, RESET);
}
void spi_chipDeselect(void){
GPIO_WriteBit(CS_GPIO, CS_GPIO_Pin, SET);
}
void spi_writeTwoBytes(uint8_t byte1, uint8_t byte0){
while(spiBusyFlag){}
spiTxCounter = 2;
spiRxCounter = 2;
spiBusyFlag = 1;
spiDataBuffer[0] = byte0;
spiDataBuffer[1] = byte1;
spi_chipSelect();
spi_enableTxInterrupt();
}
void spi_handleSPI1Interrupt(void){
if(SPI_I2S_GetFlagStatus(SPI_Module, SPI_I2S_FLAG_RXNE) == SET){
// Receive Buffer Not Empty
spiRxCounter--;
spiDataBuffer[spiRxCounter] = SPI_I2S_ReceiveData(SPI_Module);
if(spiRxCounter == 0){
spi_chipDeselect();
spiBusyFlag = 0;
}
}else if(SPI_I2S_GetFlagStatus(SPI_Module, SPI_I2S_FLAG_TXE) == SET){
// Transmit Buffer Empty
if(spiTxCounter != 0){

SPI_I2S_SendData(SPI_Module, spiDataBuffer[spiTxCounter - 1]);


spiTxCounter--;
}else{
spi_disableTxInterrupt();
}
}
}

The code should be self-explanatory, if one chapter to the I 2 has understood C module,
which is why I will discuss only a few characteristics of the SPI module here.
If the transmit buffer is empty, the TXE flag is set and periodically interrupt is
generated.For this reason, the Tx interrupt is disabled before transmission and must be
disabled in the transmit buffer after writing the last byte. Consequently, the order of the
"if" - "else if" query crucial. If one were to first check the TXE flag, the last byte received
could
not
be
picked
up.
The timing for collection of bytes received is also a factor not uncritical. Due to the
structure of the buffer in the SPI module two bytes are already sending in the queue
before the first byte has been received. If the first byte received is not read before the
second byte transfer is complete, it will be overwritten by the new second received
byte. If further interrupts are enabled in the STM32, the SPI module should have a very
high priority if you can not confirm that other interrupts do not take too much computing
time.
If the timing issue no solution can be found, the DMA controller should be used. This is
very useful especially for large data sets because the processor will not be busy
processing the interrupts. I will deal with it in a later chapter.
..\main.c
STM32F103RB
...
spi_create(SPI1, GPIOA, GPIO_Pin_8);
spi_writeTwoBytes(0x12, 0x34);

// 13
// 13.1 Introduction

ADC

The following code shows a minimal example for continuous reading of analog values. I
will then describe important improvements and further possibilities of the A / D
converter.
..\main.c
STM32F103RB
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
systemnit ( ) ;
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC
ENABLE);

RCC_APB2Periph_AFIO

RCC_APB2Periph_ADC1,

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_1Cycles5);
ADC_Cmd(ADC1, ENABLE);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);

Pin configuration: When PC0 input, which is used herein as ADC12_IN10 of the A / D
converter is used.
RCC_ADCCLKConfig (...) sets the prescaler fixed for the ADC clock. The ADC clock
is generated from the clock of APB2 and may not exceed 14 MHz. Possible values for
the divisor are 2, 4, 6 and 8. Here: 72 MHz / 6 MHz = 12.
ADC_InitTypeDef:

ADC_ContinuousConvMode
If ENABLE , then automatically the next is started after the completion of a
conversion.

ADC_DataAlign
Possible values: ADC_DataAlign_Left , ADC_DataAlign_Right - The 12-bit

results are either left- or right-justified in the 16 bit registers stored. When Injected
Group Channels (more on that later) it should be noted that in the most significant bit
is always a sign is stored. The 12-bit value in the left-aligned case is thus shifted by 1
bit to the right. (See Reference Manual)

ADC_ExternalTrigConv
Specifies which external signal to trigger the start of a conversion.

ADC_Mode
ADC1 and ADC2 If used at the same time, we can roughly with this element with
respect to their interaction. the trigger behavior can be defined. For example, the two
modules on the same trigger signal can react simultaneously and thus are
synchronous. The number of different options are illustrated graphically very nice in
the Reference Manual.

ADC_NbrOfChannel
number of channels in the Channel Regular Group (see below)

ADC_ScanConvMode
If ENABLE , so several channels in the "Scan Mode" changed. (see below)

ADC_RegularChannelConfig
(...) selects channel 10 (PC0), ADC_Cmd
(...) activates
the
converter
ADC1
and ADC_SoftwareStartConvCmd
(...) triggers the first conversion. The function ADC_GetConversionValue
(...) is the current value of last accessed conversion.
..\main.c
STM32F103RB
uint16_t value = ADC_GetConversionValue(ADC1);

The above code can eg be used to periodically determine in a loop the value of a
potentiometer, but it is rather inconvenient if all individual measurements must be
processed. Also, if you want to submit multiple values there are better solutions.
// 13.2 Calibration
The ADC converter of the STM32 have a function to get yourself to calibrate. It is not
recommended to run once after power converter in order to increase their accuracy.
..\main.c
STM32F103RB
...

ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
...

After switching on (with or without calibration) of the ADC needs a stabilization time until
it gives accurate results. The duration of t STAB can be found in the datasheet. It is, for
example when STM32F103 than 1 microseconds.
// 13.3 Regular & Injected Group
The ADCs of the STM32 have to use a multiplexer to various analog inputs
respectively.
The switching from one channel to another can be automated, so that the detection of
multiple analog signals is very comfortable. These different channels are organized into
a
group
that
is
executed
when
scan
mode
is
active.
In addition to the Regular Group, which can consist of up to 16 channels, there is the
Group Injected with a maximum of 4 channels. If the measurement of Injected Group
channels triggered while watching the channels of the Regular Group are scanned, the
ADC interrupts the current conversion. It all channels of Injected Group are then
detected before starting new with the conversion of the last interrupted channel. In the
following program, the ADC is initially configured for two channels. Also, the scan mode
must be activated. In scan mode, the data can not be read sure about interrupts, since
the individual channels are converted in rapid succession. If the delay until picked up by
an interrupt routines is too long, old data is overwritten. The solution is to use the Direct
Memory Access Controller (DMA), which I will describe in more detail, however, only in
the next chapter.
..\main.c
STM32F103RB
...
ADC_InitStructure.ADC_NbrOfChannel = 2;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
...

The individual analog channels with ADC_RegularChannelConfig (...) added to


the Regular Group. The second parameter of the ADC channel is specified in the third

parameter, the result number from 1 to 16 in which the channels are run and the fourth
parameter
is
the
sample
time.
Possible values for the Sample Time in Cycles: 1.5, 7.5, 13.5, 28.5, 41.5 55.5 71.5,
239.5need to calculate the total time for the conversion of a channel still 12.5 Cycles are
added thereto. The duration of one Cycles corresponds to the period of ADCCLK. The
maximum permissible frequency for ADCCLK is 14 MHz. Consequently, the shortest
possible time is to convert one channel at (1.5 + 12.5) / 14 MHz = 1 microseconds.

..\main.c
STM32F103RB
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_1Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 2, ADC_SampleTime_1Cycles5);

The Injected Group can be configured in a similar manner as follows. Its length is about
the function ADC_InjectedSequencerLengthConfig (...) set.
..\main.c
STM32F103RB
ADC_InjectedSequencerLengthConfig(ADC1, 1);
ADC_InjectedChannelConfig(ADC1, ADC_Channel_12, 1, ADC_SampleTime_1Cycles5);

The Injected channels also offer the possibility to deduct an offset from the result of the
conversion. This allows you to get signed values are therefore provided in the result
registers sign bit. The offset can be defined separately for each channel Injected.
For the result of each Injected channels, there are also separate register so that you
can do without the DMA here or even need.
..\main.c
STM32F103RB
ADC_SetInjectedOffset(ADC1, ADC_InjectedChannel_1, 0x0800);

..\main.c
STM32F103RB
uint16_t value = ADC_GetInjectedConversionValue(ADC1, 1);

// 13.4 Triggering
The trigger for the conversion of a group is defined separately for Regular and Injected
Group. In order to trigger via software, the values must be explicitly set to None ..._.
..\main.c
STM32F103RB
...
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;

...
ADC_ExternalTrigInjectedConvConfig(ADC1, ADC_ExternalTrigInjecConv_None);

Alternatively, different timer events are triggering available, as well ExtI-Line 11 (Regular
Group) and ExtI-Line 15 (Injected Group) at ADC1 / ADC2. For ADC3 only timer events
can
be
selected. More
accurate
is
the
Reference
Manual.
The trigger for non-software events must then each still be activated as follows.
..\main.c
STM32F103RB
ADC_ExternalTrigConvCmd(ADC1, ENABLE);
ADC_ExternalTrigInjectedConvCmd(ADC1, ENABLE);

The software trigger function provides two functions.


..\main.c
STM32F103RB
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
ADC_SoftwareStartInjectedConvCmd(ADC1, ENABLE);

If the Regular Group is to be run continuously after a single trigger, it can be the
elementADC_ContinuousConvMode of ADC_InitTypeDef during
initialization
to ENABLE set. MeansADC_AutoInjectedConvCmd (ADC1, ENABLE); the ADC is
configured so that after the execution of the Regular Group Injected Group is triggered
immediately. In this combination, all of which can be detected up to 20 ADC channels
sequentially continuous.
// 13.5 Discontinuous Mode
The evidence indicates that a complete Group (scan mode provided) is processed in
one piece after triggering. The STM32 but also provide an opportunity for both Regular
as "divide" also Injected Group in more pieces. In the discontinuous mode, you can
define a sequence of length 1 to 8 for the Regular Group (Group Injected sequence
length is always 1). For example, if 8 channels available and the sequence length is 3,
then the first three channels are converted after the first trigger signal. Thereafter, the
ADC is ready for the next triggering, until it proceeds to the following three
channels. After the third trigger the last two channels are converted - there will be no
overflow, the first channel in the Group does not belong to the third sequence.
..\main.c
STM32F103RB
ADC_DiscModeChannelCountConfig(ADC1, 1);
ADC_DiscModeCmd(ADC1, ENABLE);

ADC_InjectedDiscModeCmd(ADC1, ENABLE);

// 13.6 Interrupts
The ADC interrupt can be generated by three different events which can be activated
separately from one another:

Complete conversion of the Regular Group

Complete conversion of the Injected Group

Set status of the analog watchdog

Important: The ADC1 and ADC2 of interrupts are assigned to the same interrupt
vector.ADC3, if any, has its own interrupt vector.
..\main.c
STM32F103RB
ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);
ADC_ITConfig(ADC1, ADC_IT_JEOC, ENABLE);
ADC_ITConfig(ADC1, ADC_IT_AWD, ENABLE);

..\stm32f10x_it.c
STM32F103RB
void ADC1_2_IRQHandler(void){
}

// 13.7 Analog Watchdog


For each ADC module has an upper and lower bound can be set. If one of the selected
inputs a value outside these limits, the AWD flag is set and an interrupt is generated, if
enabled. The
flag
must
be
reset
by
software.
The input can either ADC_AnalogWatchdogSingleChannelConfig (...) a
particular channel (Regular / Injected / Both) or all inputs (Regular / Injected / Both) are
chosen to be monitored.
..\main.c
STM32F103RB
ADC_AnalogWatchdogThresholdsConfig(ADC1, 0x0CCC, 0x0333);
ADC_AnalogWatchdogCmd(ADC1, ADC_AnalogWatchdog_SingleRegEnable);
ADC_AnalogWatchdogSingleChannelConfig(ADC1, ADC_Channel_10);

// 13.8 Dual ADC Mode

In Dual ADC fashion ADC1 and ADC2 respond to the same trigger signal. For ADC2 no
way is provided which converted data (regular channels) via DMA save
money. However, to use the dual ADC mode, the measurement results of ADC2 in the
upper 16 bits of the 32 bit data register of ADC1 be saved. This can then be accessed
by the DMA controller. In the following example, the Regular Groups of ADC1 and ADC2
are to be recorded simultaneously. The acquisition is triggered by software. In the
Reference Manual is noted that the same channel should not be simultaneously
sampled from ADC1 and ADC2. Also, the Regular Groups of ADC1 and ADC2 should
be the same length or Triggerinterval be large enough, otherwise the ADC could be
triggered again with the shorter sequence before the other ADC is finished.

..\main.c
STM32F103RB
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Init(ADC2, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_1Cycles5);
ADC_RegularChannelConfig(ADC2, ADC_Channel_11, 1, ADC_SampleTime_1Cycles5);
ADC_ExternalTrigConvCmd(ADC1, ENABLE);
ADC_ExternalTrigConvCmd(ADC2, ENABLE);
ADC_Cmd(ADC1, ENABLE);
ADC_Cmd(ADC2, ENABLE);
... // Calibration
ADC_SoftwareStartConvCmd(ADC1, ENABLE);

The initialization is initially the same as with only an independent ADC, with the
difference that ADC_Mode the value ADC_Mode_RegSimult is assigned. ADC2 is
initialized
with
the
same
values.
If you want to use external trigger, it must be set for ADC1 and ADC2 need
toADC_ExternalTrigConv_None be
configured.
Even if the ADCs are triggered by software, it must in any case, the external trigger for
both ADCs activated be. The calibration procedure must logically separately for both
ADC to run.
..\main.c

STM32F103RB
uint32_t value = ADC_GetConversionValue(ADC1);
uint16_t valueADC1 = value;
uint16_t valueADC2 = value >> 16;

Exemplary Reading: In practice, DMA is preferable.


In addition to the Regular simultaneous fashion, five more to choose from, some of
which relate to the Injected Channel Group and can also be combined. More information
is available in the Reference Manual. A more detailed description of the ADC modes
can be found in Application Note AN3116 .

// 14 DMA
The direct memory access controller allows to transfer data between peripherals and
memory,
without
burdening
the
CPU
directly.
The following example uses again the code from Chapter 13.1. He is extended by a
channel and DMA the sampled data is automatically copied into an array.
..\main.c
STM32F103RB
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
uint16_t ADCBuffer[] = {0xAAAA, 0xAAAA, 0xAAAA};
systemnit ( ) ;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_InitStructure.DMA_BufferSize = 2;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADCBuffer;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC
ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

RCC_APB2Periph_AFIO

RCC_APB2Periph_ADC1,

GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_NbrOfChannel = 2;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_1Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 2, ADC_SampleTime_1Cycles5);
ADC_Cmd(ADC1, ENABLE);
ADC_DMACmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
ADC_SoftwareStartConvCmd(ADC1, ENABLE);

For testing purposes, the array is ADCBuffer an element too large and initialize with a
defined bit sequence. This allows you to check whether new data has been written to
the DMA controller and really not too far writes in memory.
DMA_InitTypeDef:

DMA_BufferSize
number of data to be transmitted. Range: 0 ... 65535th The data is not buffered, here
is the identifier is chosen to be somewhat unhappy.

DMA_DIR
Possible
values: DMA_DIR_PeripheralDST O
is
the
transfer;DMA_DIR_PeripheralSRC - O is a source of transmission

act

of

DMA_M2M
Indicates whether the transfer of memory will be held to memory. In this case, the
"triggering" between two data words is performed automatically. Possible
values:DMA_M2M_Disable , DMA_M2M_Enable

DMA_MemoryBaseAddr
32-bit base address in memory, so the address of the memory cell into which the first
byte is written.

DMA_MemoryDataSize
size
of
a
"word"
on
the
side
of
the
store. Possible
values: DMA_MemoryDataSize_Byte(8
bits), DMA_MemoryDataSize_HalfWord (16-bit), DMA_MemoryDataSize_Word (3
2-bit)

DMA_MemoryInc
If DMA_MemoryInc_Enable increasing the memory address pointer automatically
after each transmission to the predetermined word length. Alternatively
value:DMA_MemoryInc_Disable

DMA_Mode
If DMA_Mode_Circular , the DMA controller will restart from the base address
when its internal data counter reaches 0. The data counter is reset to the preset
"Buffer
Size".
Alternatively: DMA_Mode_Normal - There will be no further transfers after the
number has been transferred in "buffer size" of values. In Memory-to-Memory Mode,
this is the only possible value.

DMA_PeripheralBaseAddr
32-bit base address of the periphery.

DMA_PeripheralDataSize
Possible
values: DMA_PeripheralDataSize_Byte , DMA_PeripheralDataSize_HalfW
ord, DMA_PeripheralDataSize_Word

DMA_PeripheralInc
Possible values: DMA_PeripheralInc_Disable , DMA_PeripheralInc_Enable

DMA_Priority
The DMA requests of higher priority are processed first. There is a choice of four
values: DMA_Priority_Low , DMA_Priority_Medium , DMA_Priority_High ,D
MA_Priority_VeryHigh

With DMA_Init (...) , the corresponding DMA channel is initialized. What hardware
is connected to which channel, you can refer to the Reference Manual. It should be
noted that there are several possible sources for each channel, but should never be
used simultaneously. About ADC_DMACmd (ADC1, ENABLE) is the ADC module
announced that it DMA requests should be sent to the DMA controller when it is a
conversion finished. Notes:In certain STM32 there are two DMA controller (DMA1,
DMA2). The DMA controller can stop the CPU, if both want the same goal (memory /
peripheral) simultaneously. It is, however, ensured by round-robin scheduling that at
least
half
the
bandwidth
for
the
CPU
remains
verfbar.

// 15 Common Mistakes
Below I will list some mistakes that undermine a frequently and can actually lead to
unnecessary Sucherei:

Forgot
timing
eg RCC_APB2PeriphClockCmd
(...)
In particular RCC_APB2PeriphClockCmd (RCC_APB2Periph_AFIO) if pins were
geremapped other functions.
Enabling hardware forget - eg DMA_Cmd (...)
In addition to enable an interrupt to the peripheral hardware forget the NVIC
adjusted appropriately so.

// 16 SPI with DMA


This code shows how to supply the SPI module via direct memory access to data,
thereby relieving the processor.
..\main.c
STM32F103RB
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;

uint16_t SPIBuffer[] = {0xAAAA, 0xAAAA, 0xAAAA};


systemnit ( ) ;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_WriteBit(GPIOB, GPIO_Pin_12, SET);
SPI_InitStructure.
SPI_InitStructure.
SPI_InitStructure.
SPI_InitStructure.
SPI_InitStructure.
SPI_InitStructure.
SPI_InitStructure.
SPI_InitStructure.
SPI_InitStructure.
SPI_Init ( SPI2 ,

SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32 ;
SPI_CPHA = SPI_CPHA_1Edge ;
SPI_CPOL = SPI_CPOL_Low ;
SPI_CRCPolynomial = 0 ;
SPI_DataSize = SPI_DataSize_16b ;
SPI_Direction = SPI_Direction_2Lines_FullDuplex ;
SPI_FirstBit = SPI_FirstBit_MSB ;
SPI_Mode = SPI_Mode_Master ;
SPI_NSS = SPI_NSS_Soft ;
& SPI_InitStructure ) ;

SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);


SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, ENABLE);
SPI_Cmd(SPI2, ENABLE);
// DMA Channel 4 - SPI RX
DMA_InitStructure.DMA_BufferSize = 0;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPIBuffer;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_Init(DMA1_Channel4, &DMA_InitStructure);
DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);
// DMA Channel 5 - SPI TX
DMA_InitStructure.DMA_BufferSize = 0;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPIBuffer;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_Init(DMA1_Channel5, &DMA_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

DMA_Cmd(DMA1_Channel4, DISABLE);
DMA_Cmd(DMA1_Channel5, DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel4, 2);
DMA_SetCurrDataCounter(DMA1_Channel5, 2);
SPIBuffer[0] = 0x1234;
SPIBuffer[1] = 0x5678;
// Chip Select Low
GPIO_WriteBit(GPIOB, GPIO_Pin_12, RESET);
DMA_Cmd(DMA1_Channel4, ENABLE);
DMA_Cmd(DMA1_Channel5, ENABLE);

Pin-Konfiguration: PB12 = Chip Select, PB15 = MOSI, PB14 = MISO, PB13 = SCK
..\stm32f10x_it.c
STM32F103RB
void DMA1_Channel4_IRQHandler(void){
spi_handleDMA1Ch4Interrupt();
DMA_ClearFlag(DMA1_FLAG_TC4);
}

..\main.c
STM32F103RB
void spi_handleDMA1Ch4Interrupt(void){
// Chip Select High
GPIO_WriteBit(GPIOB, GPIO_Pin_12, SET);
}

The initialization of the GPIO pins and the SPI module can consist of Chapter 12 are
applied. The only difference is that no more interrupts are enabled, but DMA requests
bySPI_I2S_DMACmd
(...) .
The description of each item of DMA_InitStructure may Chapter 14 are
removed. For SPI, the two channels 4 and 5 are used. If you want to only send or
receive
only,
then
a
channel
can
be
saved.

About DMA_DIR is set, the data direction: Channel 4 from SPI to the store; Channel 5
from memory to the SPI. DMA_Mode need to DMA_Mode_Normal be set. In contrast to
the ADC from Chapter 14 you do not wish to continuously transmit data, but only if more
data frames are expected. For Channel 4, the transfer-Complete-interrrupt be switched
to later make so that the switching of the chip select pins. The last code block Transfers
can be started. In practice, it is better wrap this section in a function. First, the DMA
channels must be turned off and on DMA_SetCurrDataCounter (...) the number
of words is set with the second parameter to be transferred. Subsequently, the chip
select line is pulled low and started by the activation of the DMA transfer of the
channels. Be sure you should turn on Channel 4 (RX) before Channel 5 (TX). Occurs
between the two function calls an interrupt on that interrupts the program flow for a
longer time, the SPI module would otherwise begin sending, Channel 5 but would still
be too late or not designated to answer the received data. The interrupt from Channel 4
(RX) is triggered when the last byte has been received, the entire SPI transfer is thus
completed. Now the chip select line are placed on high again. Channel 5 (TX) should be
used in any way for this purpose because it is ready to work early. If you need to
Channel 4 for any other purpose than SPI, so you can, for example, implement the
necessary
functionality
via
the
SPI
interrupt
and
a
byte
count.

// 17 Independent Watchdog (IWDG)


In the STM32 two different watchdogs are available. Here we will examine the
independent watchdog. He is clocked from the internal RC oscillator 40 kHz (LSI). The
clock is relatively inaccurate (30kHz 60kHz ...), but the IWDG is well suited to operate
independently of the main program and to reset this error occurs.
..\main.c
STM32F100RB
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);

IWDG_SetPrescaler ( IWDG_Prescaler_16 ) ;
IWDG_SetReload(2500);
IWDG_ReloadCounter();
IWDG_Enable();

Using the command IWDG_WriteAccessCmd (...) the write access to the registry
for Prescale and reload value is enabled. Preserved write permissions to
eitherIWDG_ReloadCounter
() or IWDG_Enable
() is
called. About IWDG_SetPrescaler (...) , the 40 kHz clock by a factor of 4, 8,
16, ..., 256 are divided down. With IWDG_SetReload (. ..)the value is set from
which counts down the watchdog. He reaches 0, a reset. Possible values:. 0 ...
4095 IWDG_ReloadCounter () resets the Watchdog to its set maximum value.This
function must be called periodically in the main program to prevent reset during normal
operation. IWDG_Enable () activates the watchdog. About Bytes option of IWDG can
be switched on continuously. Note: (be the IWDG must be enabled to do so) If the
Prescale / reload to change values dynamically in the program, so between two value
changes at least 5 RC Cycles have to be awaited, otherwise, the new values were not
applied. To check whether the IWDG is ready to accept new values, the status bits RVU
(reload value update) and PVU (prescaler value update) can be queried. The bits must
be
0,
so
that
a
new
update
can
be
done.

..\main.c
STM32F100RB
while(IWDG_GetFlagStatus(IWDG_FLAG_PVU) == SET);
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
IWDG_SetPrescaler(IWDG_Prescaler_64);

// 18 Option Bytes
Programming
Manual PM0075 (STM32F10xxx)
multiple option bytes are present in the STM32 Flash are determined by the specific
settings:

Read
Out
If enabled, the reading of the flash memory is prevented.

Protection

Brownout
Level
When the operating voltage of the chip is kept in reset state until the voltage value
set here is reached. If VDD operating below this value, a reset is also triggered.

Hardware
/
software
watchdog
Bit WDG_SW is set by default, which is not the IWDG automatically at startup, but
should only be switched via software. If the bit is cleared, so does the IWDG
immediately after power on / reset on his work.

Reset
Generation
for
standby
/
Stop
Mode
If nRST_STDBY / nRST_STOP is not set, a reset is triggered if the Standby / Stop
Mode is entered.

User
Data
two bytes for any data such as serial numbers.

WriteProtection
The individual pages of Flash can be protected against write access. Thus, for
example, can be prevented even delete programs that write access to the flash.

bytes

The option bytes can be written directly to CoFlash not currently. If you are an ST-Link
in possession, it can be used to change the parameters of the tool "STM32 ST-LINK
utility":

Depending on the microcontroller are some of the above options are not available or
there are additional settings. Further details are UM0892 (STM32 ST-Link Utility
software description) can be seen.

// 19 System Timer

Note: The information system timers are not included in the reference but in the
Programming Manual ( PM0056 for Cortex-M3). The system timer is an integral part of
the Cortex-M3 core and to initiate multiple tasks sequentially provided for the system
clock of an RTOS or just , His count register is 24-bits wide. In the following example, an
LED located at PC8 is the system timer operated at 1Hz. The system clock is 24 MHz.

..\main.c
STM32F100RB
int main(void) {
GPIO_InitTypeDef GPIO_InitStructure;
SystemInit ( ) ;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
SysTick_Config(1499999);
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
NVIC_SetPriority(SysTick_IRQn, 14);
while (1) {}
}
void SysTick_Handler(void) {
if (GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_8)) {
GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_RESET);
} else {
GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET);
}
}

SysTick_Config (...) sets the value at which the timer counts down. Maximum
value: 0xFFFFFF. In addition, the function activates the system timer and its exception
handler. It is part of the CMSIS. The system timer is supplied either directly by the CSO
with its clock or it is interposed a prescaler with the value 8. The prescaler can
functionSysTick_CLKSourceConfig (...) can be activated / deactivated. The
function must necessarily after SysTick_Config (...) are called. By default, the
system timer exception is the lowest priority and 15 is set at 14 exemplarily. For this
purpose, the function isNVIC_SetPriority (...) is called, which also belongs to
the CMSIS. Again, it must be ensured that the function only after SysTick_Config
(...) is
called.

// 20 RS485
As RS485 Transceiver SN65HVD1781 is used with the following circuit.

PA0:
PA2:
PA3: CPU_RS485_R
..\main.c
STM32F103RB
int main(void) {
SystemInit();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
/* DE Pin */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* TX Pin */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

CPU_RS485_DE
CPU_RS485_D

/* RX Pin */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
NVIC_InitTypeDef NVIC_InitStructure;
/* DMA Channel 6 (USART RX) */
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel6_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/ * USART2 (TX)*/
NVIC_InitStructure. NVIC_IRQChannel = USART2_IRQn ;
NVIC_InitStructure. NVIC_IRQChannelPreemptionPriority =
NVIC_InitStructure. NVIC_IRQChannelSubPriority = 0 ;
NVIC_InitStructure. NVIC_IRQChannelCmd = ENABLE ;
NVIC_Init ( & NVIC_InitStructure ) ;

1 ;

uint8_t usart_receive_array[] = {0xAA, 0xAA, 0xAA};


uint8_t usart_transmit_array[] = {0x12, 0x34, 0x56};
DMA_InitTypeDef DMA_InitStructure;
/* DMA 1, Channel 7 for USART2 TX */
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(USART2->DR);
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) &usart_transmit_array;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = 0;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel7, &DMA_InitStructure);
/* DMA 1, Channel 6 for USART2 RX */
DMA_DeInit(DMA1_Channel6);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(USART2->DR);
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) &usart_receive_array;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 0;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel6, &DMA_InitStructure);
DMA_ITConfig(DMA1_Channel6, DMA_IT_TC, ENABLE);
/ * * USART /
USART_InitTypeDef USART_InitStructure ;

USART_InitStructure.
USART_InitStructure.
USART_InitStructure.
USART_InitStructure.
USART_InitStructure.
USART_InitStructure.
USART_Init ( USART2 ,

USART_BaudRate = 57600 ;
USART_WordLength = USART_WordLength_8b ;
USART_StopBits = USART_StopBits_1 ;
USART_Parity = USART_Parity_No ;
USART_HardwareFlowControl = USART_HardwareFlowControl_None ;
USART_Mode = USART_Mode_Rx | USART_Mode_Tx ;
& USART_InitStructure ) ;

USART_DMACmd ( USART2 , USART_DMAReq_Tx ,


USART_DMACmd ( USART2 , USART_DMAReq_Rx ,

ENABLE ) ;
ENABLE ) ;

USART_ITConfig(USART2, USART_IT_TC, ENABLE);


USART_Cmd(USART2, ENABLE);
/* start transmission */
DMA_Cmd(DMA1_Channel6, DISABLE);
DMA_Cmd(DMA1_Channel7, DISABLE);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, SET);

// DE Pin high

DMA_SetCurrDataCounter(DMA1_Channel6, 2);
DMA_SetCurrDataCounter(DMA1_Channel7, 3);
DMA_ClearFlag(DMA1_FLAG_TC6);
DMA_ClearFlag(DMA1_FLAG_TC7);
DMA_Cmd(DMA1_Channel6, ENABLE);
DMA_Cmd(DMA1_Channel7, ENABLE);
while(1){}
}
void USART2_IRQHandler(void){
USART_ClearITPendingBit(USART2, USART_IT_TC);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, RESET);
}

// DE Pin low

void DMA1_Channel6_IRQHandler(void){
/* all data received */
DMA_ClearFlag(DMA1_FLAG_TC6);
}

USART_InitTypeDef :

USART_BaudRate
The required baud rate is directly specified. It should be noted that in the calculation
of
the
register
values
in USART_Init
(...) the
value HSE_VALUE or HSI_VALUE is expected from stm32f10x.h. If one uses such as
a quartz with a frequency equal to 8MHz clock as supply, it must HSE_VALUE be
adjusted.

USART_WordLength
It can be chosen for the word length of 8-9 bits.

USART_StopBits
Here,
the
"number"
1
and
2
0.5 and 1.5 for Smart Card Mode

of

stop
for

bits

are
USART

USART_Parity
Einstellung
fr
das
Bit: USART_Parity_No, USART_Parity_Even oderUSART_Parity_Odd

defined:
etc.

Parity-

USART_HardwareFlowControl
for flow control, RTS and CTS ports can be individually enabled with this option. This
feature is not available for all USART modules available.

USART_Mode
Determines whether the receive, transmit mode or both are enabled.

With USART_ITConfig (...) the Transmission Complete Interrupt is turned on. In


the interrupt handler DE pin is pulled low to switch after sending all frames back to the
receive mode.
The sequence of / * start transmission * / packs are best in a function and
calls
it
for
each
transmission
with
new
parameters.
First, both DMA channels must be disabled to allow the transfer can not start
uncontrollably.
Then, the EN pin to high placed to put to the transceiver chip to transmit mode.
The following two functions, the counter of the DMA channel is set. DMA Channel 6 is
responsible for receiving, its count must be sufficiently large so as the expected number
of frames to be received by the sending process. DMA Channel 7 supplies the USART
module in accordance with data to be sent, so here 3 bytes in a row.
Finally, the transmission-Complete Flags of the DMA channels are deleted and then
activates the two channels, so the transmission process is started.
In this example, the three bytes are 0x12, 0x34 and 0x56 sent and subsequently
received two bytes if the connected device is responding correctly. The received data
are then in the array usart_receive_array and can be picked up as soon as
theDMA1_Channel6_IRQHandler was called.

// 21

DAC

// 21.1 Introduction
DAC with the analog signals can be generated. The references for the output voltage
are VSSA and V REF + , wherein the voltage range of V REF + 2.4 V ... V DDA is limited. When
chips in small packages such as the STM32F100RB used here in LQFP64 V REF + not
led
to
the
outside,
but
directly
to
V DDA connected.
This example generates a voltage of 0.5 V * REF + to pin PA4.
..\main.c
STM32F100RB
GPIO_InitTypeDef GPIO_InitStructure;
DAC_InitTypeDef DAC_InitStructure;
systemnit ( ) ;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_Init(GPIOA, &GPIO_InitStructure);
DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0;
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable;
DAC_InitStructure.DAC_Trigger = DAC_Trigger_None;
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
DAC_Init(DAC_Channel_1, &DAC_InitStructure);
DAC_SetChannel1Data(DAC_Align_12b_R, 0x7FF);
DAC_Cmd(DAC_Channel_1, ENABLE);

After the DAC channel is enabled, the associated pin is automatically connected to the
DAC. Nevertheless, it is recommended to configure the pin as analog input to prevent
undesired parasitic power consumption.
DAC_InitTypeDef:

DAC_LFSRUnmask_TriangleAmplitude
The DAC can be set in a mode in which it generates noise or a triangle wave. Here,
the "LFSR mask" for the noise generator or the amplitude of the triangular signal can
be defined. Is not used in this example.

DAC_OutputBuffer
This option allows the DAC a buffer to be followed by the output impedance to

max.To reduce 15 kOhm. The disadvantage is that the output range of 0.2 V ...
V DDA - is 0.2 V limited. Without output buffer resistive load at the output must not fall
below 1.5 milliohms to still achieve an accuracy of 1%.

DAC_Trigger
The DAC uses a shadow register, either copies the values from the data register only
after a trigger pulse. The trigger different timers, ExtI Line 9, software or none can be
selected. In the latter case, the values are applied immediately.

DAC_WaveGeneration
Possible Mode)

With DAC_SetChannel1Data (...) a value into the data register of channel 1 of the
DAC can be written. About the first parameter specifies whether the 8-bit mode or 12-bit
mode (left / right justified) is used.
About DAC_Cmd (...) , the DAC channel 1 is activated.

// 21.2 Dual DAC Channel Conversion


In Dual DAC channel mode, both channels on a common 32-bit registers can be
supplied with data. The real benefit is apparent only later in connection with DMA,
because this function is a DMA channel can be saved. In this mode, both DAC channels
can be triggered independently. The various options are listed in the Reference Manual.
..\main.c
STM32F100RB
GPIO_InitTypeDef GPIO_InitStructure;
DAC_InitTypeDef DAC_InitStructure;
systemnit ( ) ;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
GPIO_Init(GPIOA, &GPIO_InitStructure);
DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0;
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable;
DAC_InitStructure.DAC_Trigger = DAC_Trigger_None;
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
DAC_Init(DAC_Channel_1, &DAC_InitStructure);
DAC_Init(DAC_Channel_2, &DAC_InitStructure);
DAC_SetDualChannelData(DAC_Align_12b_R, 0x7FF, 0x3FF);

DAC_Cmd(DAC_Channel_1, ENABLE);
DAC_Cmd(DAC_Channel_2, ENABLE);

// 21.3 DMA
In this example, a sine and a cosine PA4 is generated with each 10 kHz to PA5. All data
transfers are performed after initialization via DMA.
..\main.c
STM32F100RB
const uint16_t sinTable[32] = {
2047, 2447, 2831, 3185, 3498, 3750, 3939, 4056, 4095, 4056,
3939, 3750, 3495, 3185, 2831, 2447, 2047, 1647, 1263, 909,
599, 344, 155, 38, 0, 38, 155, 344, 599, 909, 1263, 1647};
uint32_t sinCosTable[32];
int main(void) {
GPIO_InitTypeDef GPIO_InitStructure;
DAC_InitTypeDef DAC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
DMA_InitTypeDef DMA_InitStructure;
SystemInit ( ) ;
for(uint8_t i = 0; i < 32; i++){
sinCosTable[i] = sinTable[i] << 16;
}
for(uint8_t i = 8; i < 32; i++){
sinCosTable[i - 8] |= sinTable[i];
}
for(uint8_t i = 0; i < 8; i++){
sinCosTable[i + 24] |= sinTable[i];
}
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC | RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBaseInitStructure.
TIM_TimeBaseInitStructure.
TIM_TimeBaseInitStructure.
TIM_TimeBaseInitStructure.
TIM_TimeBaseInitStructure.
TIM_TimeBaseInit ( TIM2 ,

TIM_ClockDivision = TIM_CKD_DIV1 ;
TIM_CounterMode = TIM_CounterMode_Up ;
TIM_Period = 74 ;
TIM_Prescaler = 0 ;
TIM_RepetitionCounter = 0 ;
& TIM_TimeBaseInitStructure ) ;

TIM_SelectOutputTrigger ( TIM2 , TIM_TRGOSource_Update ) ;


DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0;
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable;
DAC_InitStructure.DAC_Trigger = DAC_Trigger_T2_TRGO;
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;

DAC_Init(DAC_Channel_1, &DAC_InitStructure);
DAC_Init(DAC_Channel_2, &DAC_InitStructure);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(DAC->DHR12RD);
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&sinCosTable;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = 32;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel3, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel3, ENABLE);
DAC_Cmd(DAC_Channel_1, ENABLE);
DAC_Cmd(DAC_Channel_2, ENABLE);
DAC_DMACmd(DAC_Channel_1, ENABLE);
TIM_Cmd(TIM2, ENABLE);
while (1) {}
}

The field sinTable contains 32 12-bit values by which a sine is approximated. About
three for loops, the two 16-bit half are sinCosTable filled. In the upper 16-bits, the
sine table is copied, but shifted in the same Table 8 values in the lower 16-bit (90
phase
shift).
The clock input for the loading of the next value in the DAC represents Timer 2
ready. The system clock is 24 MHz and is divided by a factor of 75 to 320 kHz. Since a
period contains 32 individual values, this produces a frequency of 10 kHz. The trigger
signal
the
update
event
is
selected.
Since the dual DAC channel is used fashion needs only one DMA channel are
occupied, here Channel. 3

// 22 I2C & DMA


In this chapter, the use of I is 2 C in conjunction with DMA demonstrated. At the STM32
is a temperature sensor TCN75 connected by Microchip.

In the Application Note AN2824 the exact procedure is described for the I 2 C module
via
DMA
to
provide
them
with
data.
By using the DMA time-critical reading / writing the data register and setting the NACK
bits is avoided when receiving data. Consequently, no high-priority interrupts are no
longer
necessary.
In the Master Receiver mode is to be noted that mandatory minimum two bytes must be
received. To read the temperature registers of the TCN must first be set to 0 and then a
read transfer a register pointer be started. Further details are provided in the data
sheet.
..\main.c
STM32F103RB
volatile uint8_t i2cDirectionWrite;
uint8_t i2cRxBuffer[] = { 0xAA, 0xAA };
uint8_t i2cTxBuffer[] = { 0x00 };
int main(void) {
GPIO_InitTypeDef GPIO_InitStructure;
I2C_InitTypeDef I2C_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
SystemInit ( ) ;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
I2C_DeInit(I2C2);
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;

I2C_InitStructure.I2C_ClockSpeed = 400000;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_OwnAddress1 = 0;
I2C_Init(I2C2, &I2C_InitStructure);
I2C_ITConfig(I2C2, I2C_IT_EVT, ENABLE);
I2C_Cmd(I2C2, ENABLE);
I2C_DMACmd(I2C2, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = I2C2_EV_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// DMA Channel 4 - I2C2 TX
DMA_InitStructure.DMA_BufferSize = 0;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) i2cTxBuffer;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) & I2C2->DR;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_Init(DMA1_Channel4, &DMA_InitStructure);
// DMA Channel 5 - I2C2 RX
DMA_InitStructure.DMA_BufferSize = 0;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) i2cRxBuffer;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) & I2C2->DR;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_Init(DMA1_Channel5, &DMA_InitStructure);
DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
while (1) {
for (volatile uint32_t i = 0; i < 1000000; i++);
i2cDirectionWrite = 1;
DMA_SetCurrDataCounter(DMA1_Channel4, 1);
DMA_SetCurrDataCounter(DMA1_Channel5, 2);
I2C_GenerateSTART(I2C2, ENABLE);
}

}
void I2C2_EV_IRQHandler(void) {
if (I2C_GetFlagStatus(I2C2, I2C_FLAG_SB) == SET) {
if (i2cDirectionWrite) {
// STM32 Transmitter
I2C_Send7bitAddress(I2C2, 0x9E, I2C_Direction_Transmitter);
} else {
// STM32 Receiver
I2C_Send7bitAddress(I2C2, 0x9E, I2C_Direction_Receiver);
}
} else if (I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)
== SUCCESS) {
if (i2cDirectionWrite) {
// STM32 Transmitter
DMA_Cmd(DMA1_Channel4, ENABLE);
}
} else if (I2C_GetFlagStatus(I2C2, I2C_FLAG_BTF)) {
if (i2cDirectionWrite) {
// STM32 Transmitter
DMA_Cmd(DMA1_Channel5, ENABLE);
I2C_DMALastTransferCmd(I2C2, ENABLE);
I2C_GenerateSTART(I2C2, ENABLE);
i2cDirectionWrite = 0;
I2C_ClearFlag(I2C2, I2C_FLAG_BTF);
}
}
}
void DMA1_Channel5_IRQHandler(void) {
DMA_ClearFlag(DMA1_FLAG_TC5);
I2C_GenerateSTOP(I2C2, ENABLE);
DMA_Cmd(DMA1_Channel4, DISABLE);
DMA_Cmd(DMA1_Channel5, DISABLE);
// Transmission of CAN Message
}

First, the variable is i2cDirectionWrite declared. It states later whether the module
is in writing ( 1 ) - or read mode ( 0 is). Furthermore, two fields to be sent / received
data is defined. Important : When you configure the I 2 C module buffer events may
( I2C_IT_BUF .) can not be activated via the function I2C_DMACmd (...) the module
to be caused to generate DMA requests. The channels here are 4 and 5 is for the DMA
controller. To the end of the I 2 to send C transfers a STOP signal and processing the
data to trigger is activated at Channel 5, the Transmission Complete interrupt. After
configuring the processor is in an infinite loop in the periodic data transfer is
initiated , For this purpose, the data counter the DMA channels are set and then sent a
START signal. If the I START signal transmitted successfully, then 2 C, an interrupt is
and I2C_Send7bitAddress (...) sent the slave address (here . 0x4F, for the
function by 1 to the left shifted) . After transmitting the slave address, an interrupt is
triggered and switched to send the data of the DMA Channel 4 again If all bytes have
been sent, the BTF flag (-> interrupt ) is set. In the following, a REPEAT-START signal

to be sent and the data is read in the subsequent read transfer. For this purpose DMA
Channel 5 is activated and then a new transfer with I2C_GenerateSTART
(...) started. The variable i2cDirectionWrite is to 0 set to go later in the branch,
which sends the address with a set R / W bit. I2C_DMALastTransferCmd
(...) know that I 2 C module to send a NACK after the last byte. If the data reception
completed of the predetermined number of bytes, the Transfer-Complete interrupt the
DMA channel 5 is raised. With I2C_GenerateSTOP (...) a STOP signal is
transmitted and then the two DMA channels are disabled. In the interrupt handler other
functions can not be called / flags are set to launch a processing of the received data
(eg, sending a CAN message). A complete CoIDE project including CAN transfer
can here be
downloaded.

You might also like