80
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

Home

  • Upload
    crsarin

  • View
    231

  • Download
    0

Embed Size (px)

DESCRIPTION

mmmm

Citation preview

Page 1: Home

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 I 2 C

10.1 Configuration

10.2 & send (STM32 = master)

11 CAN

11.1 Configuration

Page 2: Home

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 I 2 C & 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 /

Page 3: Home

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:

Data Sheet ( STM32F103RB ) 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.

Page 4: Home

..\main.cSTM32F100RB

#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.

   

 

 

Page 5: Home

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.cSTM32F103RB

#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)

Page 6: Home

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.cSTM32F103RB

#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.cSTM32F103RB

#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();

Page 7: Home

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.cSTM32F103RB

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.cSTM32F103RB

/* 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.

Page 8: Home

..\cmsis_boot\stm32f10x_conf.hSTM32F103RB

/* 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 level functions for NVIC and SysTick (add-on to CMSIS functions) */

  // 6 RCC – Reset and clock control

To use any peripheral module must this be made available first clock signal.

..\main.cSTM32F103RB

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

Page 9: Home

  // 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.cSTM32F103RB

#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_Modepossible 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 push-

pullGPIO_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, I 2 2 is used, etc. 

Page 10: Home

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

pins are | be configured simultaneously. 

GPIO_Speedpossible 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.cSTM32F103RB

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 PC0dataByte = GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_0); // Lesen des Bits PC0 aus dem Output RegisterdataByte = 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.cSTM32F103RB

Page 11: Home

// default: PB6 - I2C1_SCL; PB7 - I2C1_SDA// remap: PB8 - I2C1_SCL; PB9 - I2C1_SDAGPIO_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.cSTM32F103RB

GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBase_InitStructure;TIM_OCInitTypeDef TIM_OC_InitStructure; systemınit ( ) ; 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);

Page 12: Home

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_ClockDivisionprescaler 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_CounterModepossible Werte: TIM_CounterMode_Up TIM_CounterMode_DownTIM_CounterMode_CenterAligned1 TIM_CounterMode_CenterAligned2TIM_CounterMode_CenterAligned3

TIM_PeriodThe 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_PrescalerTells 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_OCModeWith regard to the PWM function, the two values 

Page 13: Home

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_OCIdleStatestate of OC pins in the Idle State - see note below.

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

TIM_OCPolarityHere, the polarity of the OC output can be reversed.

TIM_OCNPolarityHere, 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_OutputStateactive Erten / disable the OC output.

TIM_OutputNStateErten active / deactivate the OCN output.

TIM_PulsePulsweite 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.cSTM32F103RB

Page 14: Home

GPIO_InitTypeDef GPIO_InitStructure; systemınit ( ) ; 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 ( TIM3 , TIM_EncoderMode_TI12 , TIM_ICPolarity_Rising , TIM_ICPolarity_Falling ) ; 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.cSTM32F103RB

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.cSTM32F100RB

Page 15: Home

#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 | RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, 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_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

Page 16: Home

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.cSTM32F100RB

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); }

Page 17: Home

}

  // 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.cSTM32F103RB

#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. 

Page 18: Home

In the NVIC then you must still enable the interrupt. The last thing missing is nor the code for the interrupt handler. 

..\stm32f10x_it.cSTM32F103RB

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); }}

  // 10 I2C

  // 10.1 Configuration

..\main.cSTM32F103RB

GPIO_InitTypeDef GPIO_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;I2C_InitTypeDef I2C_InitStructure; systemınit ( ) ; 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 = 0 ; 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 = 0 ; NVIC_InitStructure. NVIC_IRQChannelSubPriority = 0 ; NVIC_InitStructure. NVIC_IRQChannelCmd = ENABLE ; NVIC_Init ( & NVIC_InitStructure ) ; I2C_DeInit(I2C1); 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;

Page 19: Home

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_AckUpon receipt of the own address or a data byte an acknowledgment bit is sent ifI2C_Ack_Enable .

I2C_AcknowledgedAddressbit length of its own address in slave mode. Possible values:I2C_AcknowledgedAddress_7bit , I2C_AcknowledgedAddress_10bit

I2C_ClockSpeedfrequency 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_DutyCycleSets 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

I2C / SMBus – Mögliche Werte: I2C_Mode_I2C, I2C_Mode_SMBusDevice,I2C_Mode_SMBusHost

I2C_OwnAddress1definition 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

Page 20: Home

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.cSTM32F103RB

void I2C1_EV_IRQHandler(void){ i2c_handleEventInterrupt();} void I2C1_ER_IRQHandler(void){ i2c_handleErrorInterrupt();}

  // 10.2 & send (STM32 = master)

..\i2c.hSTM32F103RB

#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.cSTM32F103RB

#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){}

Page 21: Home

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;} // ISRvoid 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){

Page 22: Home

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; } }} // ISRvoid 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 C-module the address of the devices to be addressed in the next step. Otherwise, if one of

Page 23: Home

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.cSTM32F103RB

GPIO_InitTypeDef GPIO_InitStructure;CAN_InitTypeDef CAN_InitStructure; systemınit ( ) ; RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); 

Page 24: Home

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_Prescalerprescaler - Value range: 1 ... 1024

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

CAN_BS1Time Quantum in Bit Segment 1 – Wertebereich: CAN_BS1_1tq ... CAN_BS1_16tq

CAN_BS2Time Quantum in Bit Segment 2 – Wertebereich: CAN_BS2_1tq ... CAN_BS2_8tq

CAN_Modemögliche

Page 25: Home

Werte: CAN_Mode_Normal, CAN_Mode_LoopBack, CAN_Mode_Silent,CAN_Mode_Silent_LoopBackAlle 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_TTCMtime-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_AWUMAutomatic 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.cSTM32F103RB

... CanTxMsg canMessage; canMessage.StdId = 0x123;

Page 26: Home

canMessage.ExtId = 0;canMessage.RTR = CAN_RTR_DATA;canMessage.IDE = CAN_ID_STD;canMessage.DLC = 8; canMessage.Data[0] = 0;canMessage.Data[1] = 1;canMessage.Data[2] = 2;canMessage.Data[3] = 3;canMessage.Data[4] = 4;canMessage.Data[5] = 5;canMessage.Data[6] = 6;canMessage.Data[7] = 7; CAN_Transmit(CAN1, &canMessage);

CanTxMsg :

StdIdStandard Identifier - Range: 0 ... 0x7FF

EXTIDextended identifier - Value range: 0 ... 0x1FFFFFFF

RTRRemote Transmission Request. Features a remote frame, if at CAN_RTR_Remote set.

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

DLClength 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

Page 27: Home

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.cSTM32F103RB

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;

Page 28: Home

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_FilterNumberEinzustellende Filter Bank, Werte: 0...13 (0...27 bei connectivity line devices)

CAN_FilterModeCAN_FilterMode_IdList (2 Extended IDs / 4 Standard IDs)

oderCAN_FilterMode_IdMask (ID & Mask) CAN_FilterScale 

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

above) CAN_FilterIdHigh

Upper 16 bits of CAN_FxR1 (32-bit mode) Lower 16 bits of CAN_FxR2 (16-bit mode)

CAN_FilterIdLowLower 16 bits of CAN_FxR1

Page 29: Home

CAN_FilterMaskIdHighUpper 16 bits of CAN_FxR2

CAN_FilterMaskIdLowLower 16 bits of CAN_FxR2 (32-bit mode) Upper 16 bits of CAN_FxR1 (16-bit mode)

CAN_FilterFIFOAssignmentAssociated FIFO values: 0 or 1

CAN_FilterActivationSpecifies 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 IDsCAN_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

Page 30: Home

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, 0x333CAN_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.cSTM32F103RB

GPIO_InitTypeDef GPIO_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;SPI_InitTypeDef SPI_InitStructure; systemınit ( ) ; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO | RCC_APB2Periph_SPI1, ENABLE); 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); 

Page 31: Home

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); 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_BaudRatePrescaler = SPI_BaudRatePrescaler_32 ; SPI_InitStructure. SPI_CPHA = SPI_CPHA_2Edge ; SPI_InitStructure. SPI_CPOL = SPI_CPOL_High ; SPI_InitStructure. SPI_CRCPolynomial = 0 ; SPI_InitStructure. SPI_DataSize = SPI_DataSize_8b ; SPI_InitStructure. SPI_Direction = SPI_Direction_2Lines_FullDuplex ; SPI_InitStructure. SPI_FirstBit = SPI_FirstBit_MSB ; SPI_InitStructure. SPI_Mode = SPI_Mode_Master ; SPI_InitStructure. SPI_NSS = SPI_NSS_Soft ; SPI_Init ( SPI1 , & 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_BaudRatePrescalerdivider for the clock signal from APB2. Possible values: SPI_BaudRatePrescaler_xwith x = 2, 4, 8, 16, 32, 64, 128, 256

SPI_CPHAedge 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_CPOLvoltage 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.

Page 32: Home

SPI_DataSizesize 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_Directionchoice between uni- and bi-directional data mode. Possible

SPI_FirstBitbit order. Possible values: SPI_FirstBit_LSB , SPI_FirstBit_MSB

SPI_ModeDetermines whether the STM32 behaves as master or slave. Possible values:SPI_Mode_Master , SPI_Mode_Slave

SPI_NSSSlave 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.cSTM32F103RB

void SPI1_IRQHandler(void){ spi_handleSPI1Interrupt();}

  // 12.2 & send (STM32 = master)

..\spi.hSTM32F103RB

#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.cSTM32F103RB

#include "spi.h"

Page 33: Home

 #define BUFFER_SIZE 2 SPI_TypeDef * SPI_Module;GPIO_TypeDef * CS_GPIO;uint16_t CS_GPIO_Pin; uint8_t spiRxCounter;uint8_t spiTxCounter;uint8_t spiBusyFlag;uint8_t 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--;

Page 34: Home

}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.cSTM32F103RB

...spi_create(SPI1, GPIOA, GPIO_Pin_8);spi_writeTwoBytes(0x12, 0x34);

  // 13 ADC

  // 13.1 Introduction

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.

Page 35: Home

..\main.cSTM32F103RB

GPIO_InitTypeDef GPIO_InitStructure;ADC_InitTypeDef ADC_InitStructure; systemınit ( ) ; RCC_ADCCLKConfig(RCC_PCLK2_Div6); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO | RCC_APB2Periph_ADC1, ENABLE); 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_ContinuousConvModeIf 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)

Page 36: Home

ADC_ExternalTrigConvSpecifies which external signal to trigger the start of a conversion.

ADC_ModeADC1 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_NbrOfChannelnumber of channels in the Channel Regular Group (see below)

ADC_ScanConvModeIf 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.cSTM32F103RB

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.cSTM32F103RB

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

Page 37: Home

 ...

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.cSTM32F103RB

...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. 

Page 38: Home

..\main.cSTM32F103RB

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.cSTM32F103RB

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.cSTM32F103RB

ADC_SetInjectedOffset(ADC1, ADC_InjectedChannel_1, 0x0800);

..\main.cSTM32F103RB

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.cSTM32F103RB

...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.cSTM32F103RB

Page 39: Home

ADC_ExternalTrigConvCmd(ADC1, ENABLE);ADC_ExternalTrigInjectedConvCmd(ADC1, ENABLE);

The software trigger function provides two functions.

..\main.cSTM32F103RB

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.cSTM32F103RB

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

Page 40: Home

Important: The ADC1 and ADC2 of interrupts are assigned to the same interrupt vector.ADC3, if any, has its own interrupt vector.

..\main.cSTM32F103RB

ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);ADC_ITConfig(ADC1, ADC_IT_JEOC, ENABLE);ADC_ITConfig(ADC1, ADC_IT_AWD, ENABLE);

..\stm32f10x_it.cSTM32F103RB

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.cSTM32F103RB

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. 

Page 41: Home

..\main.cSTM32F103RB

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.cSTM32F103RB

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 .

Page 42: Home

  // 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.cSTM32F103RB

GPIO_InitTypeDef GPIO_InitStructure;ADC_InitTypeDef ADC_InitStructure;DMA_InitTypeDef DMA_InitStructure;uint16_t ADCBuffer[] = {0xAAAA, 0xAAAA, 0xAAAA}; systemınit ( ) ; 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 | RCC_APB2Periph_AFIO | RCC_APB2Periph_ADC1, ENABLE); 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); 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); 

Page 43: Home

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_BufferSizenumber of data to be transmitted. Range: 0 ... 65535th The data is not buffered, here is the identifier is chosen to be somewhat unhappy.

DMA_DIRPossible values: DMA_DIR_PeripheralDST - O is the act of

transfer;DMA_DIR_PeripheralSRC - O is a source of transmission 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_MemoryBaseAddr32-bit base address in memory, so the address of the memory cell into which the first byte is written.

DMA_MemoryDataSizesize 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_ModeIf DMA_Mode_Circular , the DMA controller will restart from the base address

Page 44: Home

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_PeripheralBaseAddr32-bit base address of the periphery.

DMA_PeripheralDataSizePossible values: DMA_PeripheralDataSize_Byte , DMA_PeripheralDataSize_HalfWord, DMA_PeripheralDataSize_Word

DMA_PeripheralIncPossible values: DMA_PeripheralInc_Disable , DMA_PeripheralInc_Enable

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

,DMA_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 verfübar. 

  // 15 Common Mistakes

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

Page 45: Home

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.cSTM32F103RB

GPIO_InitTypeDef GPIO_InitStructure;SPI_InitTypeDef SPI_InitStructure;DMA_InitTypeDef DMA_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;uint16_t SPIBuffer[] = {0xAAAA, 0xAAAA, 0xAAAA}; systemınit ( ) ; 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_BaudRatePrescaler = SPI_BaudRatePrescaler_32 ; SPI_InitStructure. SPI_CPHA = SPI_CPHA_1Edge ; SPI_InitStructure. SPI_CPOL = SPI_CPOL_Low ; SPI_InitStructure. SPI_CRCPolynomial = 0 ; SPI_InitStructure. SPI_DataSize = SPI_DataSize_16b ; SPI_InitStructure. SPI_Direction = SPI_Direction_2Lines_FullDuplex ; SPI_InitStructure. SPI_FirstBit = SPI_FirstBit_MSB ;

Page 46: Home

SPI_InitStructure. SPI_Mode = SPI_Mode_Master ; SPI_InitStructure. SPI_NSS = SPI_NSS_Soft ; SPI_Init ( SPI2 , & 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 RXDMA_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 TXDMA_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 LowGPIO_WriteBit(GPIOB, GPIO_Pin_12, RESET); DMA_Cmd(DMA1_Channel4, ENABLE);DMA_Cmd(DMA1_Channel5, ENABLE);

Page 47: Home

Pin-Konfiguration: PB12 = Chip Select, PB15 = MOSI, PB14 = MISO, PB13 = SCK

..\stm32f10x_it.cSTM32F103RB

void DMA1_Channel4_IRQHandler(void){ spi_handleDMA1Ch4Interrupt(); DMA_ClearFlag(DMA1_FLAG_TC4);}

..\main.cSTM32F103RB

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. 

Page 48: Home

  // 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.cSTM32F100RB

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. 

Page 49: Home

..\main.cSTM32F100RB

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 Protection If enabled, the reading of the flash memory is prevented.

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 bytes 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.

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":

Page 50: Home

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.cSTM32F100RB

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) {

Page 51: Home

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: CPU_RS485_DEPA2: CPU_RS485_DPA3: CPU_RS485_R

Page 52: Home

..\main.cSTM32F103RB

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);  /* 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 = 1 ; NVIC_InitStructure. NVIC_IRQChannelSubPriority = 0 ; NVIC_InitStructure. NVIC_IRQChannelCmd = ENABLE ; NVIC_Init ( & NVIC_InitStructure ) ;  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;

Page 53: Home

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_BaudRate = 57600 ; USART_InitStructure. USART_WordLength = USART_WordLength_8b ; USART_InitStructure. USART_StopBits = USART_StopBits_1 ; USART_InitStructure. USART_Parity = USART_Parity_No ; USART_InitStructure. USART_HardwareFlowControl = USART_HardwareFlowControl_None ; USART_InitStructure. USART_Mode = USART_Mode_Rx | USART_Mode_Tx ;  USART_Init ( USART2 , & USART_InitStructure ) ;  USART_DMACmd ( USART2 , USART_DMAReq_Tx , ENABLE ) ; USART_DMACmd ( USART2 , USART_DMAReq_Rx , 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} 

Page 54: Home

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

USART_InitTypeDef :

USART_BaudRateThe 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" of stop bits are defined: 1 and 2 for USART etc. 0.5 and 1.5 for Smart Card Mode

USART_ParityEinstellung für das Parity-Bit: USART_Parity_No, USART_Parity_Even oderUSART_Parity_Odd

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

USART_ModeDetermines 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. 

Page 55: Home

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.cSTM32F100RB

GPIO_InitTypeDef GPIO_InitStructure;DAC_InitTypeDef DAC_InitStructure; systemınit ( ) ; 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:

Page 56: Home

DAC_LFSRUnmask_TriangleAmplitudeThe 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_OutputBufferThis 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_TriggerThe 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_WaveGenerationPossible 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.cSTM32F100RB

GPIO_InitTypeDef GPIO_InitStructure;DAC_InitTypeDef DAC_InitStructure; systemınit ( ) ; 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;

Page 57: Home

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.cSTM32F100RB

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_ClockDivision = TIM_CKD_DIV1 ; TIM_TimeBaseInitStructure. TIM_CounterMode = TIM_CounterMode_Up ; TIM_TimeBaseInitStructure. TIM_Period = 74 ; TIM_TimeBaseInitStructure. TIM_Prescaler = 0 ; TIM_TimeBaseInitStructure. TIM_RepetitionCounter = 0 ; TIM_TimeBaseInit ( TIM2 , & TIM_TimeBaseInitStructure ) ; 

Page 58: Home

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.

Page 59: Home

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.cSTM32F103RB

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;

Page 60: Home

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); }

Page 61: Home

} 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

Page 62: Home

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.