Beruflich Dokumente
Kultur Dokumente
2 Important Documents
8 General-purpose timers
8.1 PWM
9 Interrupts
10 I2C
10.1 Configuration
11 CAN
11.1 Configuration
12 SPI
12.1 Configuration
13 ADC
13.1 Introduction
13.2 Calibration
13.4 Triggering
13.6 Interrupts
14 DMA
15 Common Mistakes
18 Option Bytes
19 System Timer
20 RS485
21 DAC
21.1 Introduction
21.3 DMA
// 2 Important Documents
Before it gets going now, here's a list of the most important information you will need
sooner or later, if you need to look more closely with the STM32:
( STM32F103RB )
Data
Sheet
description of the concrete chips for pin wiring
Reference
Manual
( STM32F103RB )
Detailed description of the modules of a family. It is possible that not all modules
used in chip available - see data sheet.
Programming
Manual
( Cortex-M3 )
contains, for example, information about the Interrupt Controller (NVIC).
Standard
Peripheral
Library
( STM32F10x )
In contrast to, for example, AVRs should appeal to the registers of the STM32 not
directly but through features of the Standard Peripheral Library. She is
on www.st.comalong with documentation (file: stm32f10x_stdperiph_lib_um.chm)
downloadable.
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_WriteBit(GPIOC, GPIO_Pin_9, Bit_SET);
while(1)
{
}
}
In the screenshots below step by step example of STM32VL Discovery is shown, what
settings need to be made and how the code is loaded onto the chip. If the ST-Link and
another evaluation board / own circuit used, they must just another chip are selected
and in main.c in the third and eighth step GPIOC and Pin_9 be adjusted.
To later should save switching to the CoFlash program you CoIDE set so that there can
be flashed with one click of the processor. To do this, select from the menu Debug /
Debug Configuration and chooses for his project on adapter "ST-Link". About Flash /
Download program will now be controlled directly CoFlash (the window of CoFlash must
be closed).Alternatively, you can click on the appropriate button below the menu
bar. C99: I recommend the compiler in C99 mode to use. Otherwise, no variables can
be declared, for example, for loops. To do this, go to Project / Configuration and adds at
the end of the text box next to "compiler" still -std = c99 a parameter.
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:
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):
APB1: 36 MHz
Depending on the chip, there are other possibilities. It is to read the datasheet useful in
any case.
As an example, I will now show how the clock frequency to 64 MHz reconfigured at a
STM32F103 (8 MHz quartz). APB1 is divided down to 32 MHz. The first one adds a
define for the new frequency.
..\cmsis_boot\system_stm32f10x.c
STM32F103RB
#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* #define SYSCLK_FREQ_HSE HSE_VALUE */
#define SYSCLK_FREQ_24MHz 24000000
#else
/* #define SYSCLK_FREQ_HSE HSE_VALUE */
/* #define SYSCLK_FREQ_24MHz 24000000 */
/* #define SYSCLK_FREQ_36MHz 36000000 */
/* #define SYSCLK_FREQ_48MHz 48000000 */
/* #define SYSCLK_FREQ_56MHz 56000000 */
#define SYSCLK_FREQ_64MHz 64000000
/* #define SYSCLK_FREQ_72MHz 72000000 */
#endif
CoIDE deposited all gray code, which is due to the lack defines not active. Therefore,
one can go through the file now well and at the crucial points are excerpts from the old
code Copy and customize for the required 64 MHz. The following code snippet must be
added.
..\cmsis_boot\system_stm32f10x.c
STM32F103RB
#elif defined SYSCLK_FREQ_64MHz
uint32_t SystemCoreClock = SYSCLK_FREQ_64MHz; /*!< System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_64MHz
static void SetSysClockTo64(void);
#elif defined SYSCLK_FREQ_64MHz
SetSysClockTo64();
As a final step you have to have the function SetSysClockTo64 (void) create. The
easiest way to do this by copying the function SetSysClockTo72
(void) including #elif defined SYSCLK_FREQ_72MHz . Then rename it to the
function + Define and following line fits:
..\cmsis_boot\system_stm32f10x.c
STM32F103RB
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
to
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL8);
Herewith one has the factor in the PLL made from 9 to 8 (8 * 8 MHz = 64 MHz). There
remains the question of how to change the prescaler for CSO APB1 and APB2. In this
situation it is all right, since the prescaler for APB1 at 72 MHz configuration was already
set to 2. If you want to change the values, the following line should be amended
accordingly:
..\cmsis_boot\system_stm32f10x.c
STM32F103RB
/* HCLK = SYSCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
#include "stm32f10x_dma.h"
/* #include "stm32f10x_exti.h" */
/* #include "stm32f10x_flash.h" */
/* #include "stm32f10x_fsmc.h" */
#include "stm32f10x_gpio.h"
#include "stm32f10x_i2c.h"
/* #include "stm32f10x_iwdg.h" */
/* #include "stm32f10x_pwr.h" */
#include "stm32f10x_rcc.h"
/* #include "stm32f10x_rtc.h" */
/* #include "stm32f10x_sdio.h" */
#include "stm32f10x_spi.h"
/* #include "stm32f10x_tim.h" */
#include "stm32f10x_usart.h"
/* #include "stm32f10x_wwdg.h" */
/* #include "misc.h" */ /* High
functions) */
level
functions
for
NVIC
and
SysTick
(add-on
to
CMSIS
available. The first parameter specifies include the name of the port. The second
parameter, GPIO_InitTypeDef address of a structure is passed. In this structure, the
behavior of the / of the pins is set.
..\main.c
STM32F103RB
#include "stm32f10x_conf.h"
int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SystemInit ( ) ;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_WriteBit(GPIOB, GPIO_Pin_0, Bit_SET);
while(1){}
}
GPIO_Mode
possible values: GPIO_Mode_AIN - Analog input GPIO_Mode_IN_FLOATING floating inputGPIO_Mode_IPD - input with pull-down GPIO_Mode_IPU - Input with
pull-upGPIO_Mode_Out_OD - output open drain GPIO_Mode_Out_PP - output pushpullGPIO_Mode_AF_OD - Alternative function open-drain GPIO_Mode_AF_PP Alternative function push -Pull The latter two values must be selected if the output pin
of
a
module
as
something
USART, SPI,
I22
is
used,
etc.
GPIO_Pin
possible values: GPIO_Pin_x - x: 0 ... 15 GPIO_Pin_All - all pins of ports Multiple
pins
are | be
configured
simultaneously.
GPIO_Speed
possible
values: GPIO_Speed_2MHz GPIO_Speed_10MHz GPIO_Speed_50MHz Only
relevant
for
outputs. Maximum
frequency
of
the
output
signal.
Individual bits can use the function GPIO_WriteBit be set or cleared. Other functions
shown in the following code snippet.
..\main.c
STM32F103RB
uint8_t dataByte;
uint16_t dataHalfWord;
// Setting PA0 and PA2
GPIO_SetBits ( GPIOA , GPIO_Pin_0 | GPIO_Pin_2 ) ;
// Delete PA1 and PA3
GPIO_ResetBits ( GPIOA , GPIO_Pin_1 | GPIO_Pin_3 ) ;
// 0x1234 on PORTB Write
GPIO_Write ( GPIOB , 0x1234 ) ;
// Lesen des Bits PC0
dataByte = GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_0);
// Lesen des Bits PC0 aus dem Output Register
dataByte = GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_0);
// Read from PORTC
data halfword = GPIO_ReadInputData ( GPIOC ) ;
// Read from the output register PORTC
data halfword = GPIO_ReadOutputData ( GPIOC ) ;
For many peripheral modules of the corresponding pins can choose between different
alternatives:
..\main.c
STM32F103RB
// default: PB6 - I2C1_SCL; PB7 - I2C1_SDA
// remap: PB8 - I2C1_SCL; PB9 - I2C1_SDA
GPIO_PinRemapConfig(GPIO_Remap_I2C1, ENABLE);
// 8
General-purpose
timers
// 8.1 PWM
The timer of the STM32 have relatively many features, so I will confine myself in this
section to the first configuration with respect to the output of a PWM signal. It is to be
generated on PA0 means TIM2 a 1 kHz signal having a duty ratio of 10%:
..\main.c
STM32F103RB
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBase_InitStructure;
TIM_OCInitTypeDef TIM_OC_InitStructure;
systemnit ( ) ;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBase_InitStructure. TIM_ClockDivision = TIM_CKD_DIV1 ;
TIM_TimeBase_InitStructure. TIM_CounterMode = TIM_CounterMode_Up ;
TIM_TimeBase_InitStructure. TIM_Period = 999 ;
TIM_TimeBase_InitStructure. TIM_Prescaler = 71 ;
TIM_TimeBaseInit ( TIM2 , & TIM_TimeBase_InitStructure ) ;
TIM_OC_InitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OC_InitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
TIM_OC_InitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Set;
TIM_OC_InitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC_InitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OC_InitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OC_InitStructure.TIM_OutputNState = TIM_OutputNState_Disable;
TIM_OC_InitStructure.TIM_Pulse = 100;
TIM_OC1Init(TIM2, &TIM_OC_InitStructure);
TIM_Cmd(TIM2, ENABLE);
First, the clock for both the PortA, ie also for Timer 2 is activated as usual. It should be
noted that the clock of APB1 is automatically multiplied by the factor of 2, if the APB1
prescaler has a value different from 1. In this case, the 72 MHz clock is divided down to
36 MHz for APB1 according to the default settings. Consequently, the clock of timer 2 is
again
72
MHz.
The
code
then
PA0
initialized
as
an
alternative
function
output.
In the next two blocks registers for the time base and the output compare channel can
be
configured.
With TIM_CMD (...) , the timer is activated ,
TIM_TimeBaseInitTypeDef :
TIM_ClockDivision
prescaler if the timer gets its clock from an external input. Other values have no
effect on the clock, if the internal clock CK_INT is used - which is the case here.
TIM_CounterMode
possible
Werte: TIM_CounterMode_Up TIM_CounterMode_DownTIM_CounterMode_Ce
nterAligned1 TIM_CounterMode_CenterAligned2TIM_CounterMode_Cen
terAligned3
TIM_Period
The timer counts from 0 up to this value. Or the other way around, if another Counter
mode is selected. Maximum of 2 16 = 65535th
TIM_Prescaler
Tells down the input clock of the timer. Divider: TIM_Prescaler + 1. For example, is
divided by two when the clock TIM_Prescaler = 1 Maximum of 2. 16 = 65535th
TIM_OCInitTypeDef :
TIM_OCMode
With
regard
to
the
PWM
function,
the
two
values
TIM_OCMode_PWM1 andTIM_OCMode_PWM2 be selected. In the PWM1 output is
during the pulse duration on high and then low, if the rest of the settings are as
above. PWM 2 to generate the inverted waveform.
TIM_OCIdleState
state of OC pins in the Idle State - see note below.
TIM_OCNIdleState
state of the OCN pins in the Idle State - see note below.
TIM_OCPolarity
Here, the polarity of the OC output can be reversed.
TIM_OCNPolarity
Here, the polarity of the OCN output can be reversed. Note: If OC and OCN the
same polarity setting, OCN is compared with OC already inverted.
TIM_OutputState
active Erten / disable the OC output.
TIM_OutputNState
Erten active / deactivate the OCN output.
TIM_Pulse
Pulsweite des Signals.
Notes:
For the timers 1, 8, 15, 16 and 17, the PWM outputs must additionally with the
functionTIM_CtrlPWMOutputs
(...) .
are
activated
only if the timer is enabled but the PWM outputs are disabled get the settings for idle
State
to
bear.
The OCN outputs there is generally possible only with the timers 1, 8, 15, 16 and
17th The settings of the output compare channel can be set similarly for all three other
channels available. It is therefore possible, for example, four PWM outputs are
implemented with independent duty cycle at the same frequency. For this purpose exist
side TIM_OC1Init (...) nor the functions TIM_OC2Init (...) , TIM_OC3Init
(...) and TIM_OC4Init
(...) .
The timer counts based on the signals TI1 and TI2, which corresponds to the levels of
TIMx_CH1 and TIMx_CH2 in the simplest case ,
..\main.c
STM32F103RB
GPIO_InitTypeDef GPIO_InitStructure;
systemnit ( ) ;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_EncoderInterfaceConfig
TIM_ICPolarity_Falling ) ;
TIM3
TIM_EncoderMode_TI12
TIM_ICPolarity_Rising
TIM_Cmd(TIM3, ENABLE);
// 9
Interrupts
The following program will generate an interrupt on each rising edge on PA0. It is
compatible with the STM32VL Discovery.
..\main.c
STM32F100RB
#include "stm32f10x_conf.h"
int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
SystemInit ( ) ;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA
ENABLE);
RCC_APB2Periph_GPIOC
RCC_APB2Periph_AFIO,
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
NVIC_Init(&NVIC_InitStructure);
while(1){}
}
For external interrupts are 16 interrupt lines are available. Each line can be connected
to any port, the pin number is already set. EXTI0 can be interconnected with ... PA0,
PB0, but not, for example, with PA1. The function GPIO_EXTILineConfig know in the
above code, the EXTI0-Line PORTA, so PA0 to. . Note: There are 3 or 4 more ExtI lines
available, their sources are not configurable - see Reference Manual EXTI_Init
(EXTI_InitTypeDef * EXTI_InitStruct) initializes the ExtI Line. The structural
elements are self-explanatory.EXTI_Mode can alternatively EXTI_Mode_Event be set
- Events I will deal with in another chapter. EXTI_Trigger determines on which edge
at pin interrupt to be triggered. The block then sets the NVIC (Nested Vectored Interrupt
Controller) on - the module that is responsible for the interrupt management. Hint: To
NVIC
are
in
the
Programming
Manual,
not
in
the
Reference
Manual. NVIC_IRQChannel specifies which interrupt vector is to be initialized. In
hardware design, one should note that only EXTI0 ... occupied EXTI4 separate interrupt
vectors. EXTI9_5
and
EXTI15_10
summarize
the
remaining
ExtI
Lines.NVIC_IRQChannelPreemptionPriority defines the preemption priority level
of the interrupt between 0 and 15 proof. Interrupts with a lower number have a higher
priority. If an interrupt is triggered with a higher priority, while an interrupt a lower priority
is executed, the latter is stopped and started the execution of the interrupt handler of the
higher priority interrupts.NVIC_IRQChannelSubPriority sets in addition to each
group a specific Preemption Priority Levels set a sub Priority Level , If multiple interrupts
of the same preemption priority in the queue, the interrupt with the lower sub Priority
Level is executed first. This is, however, not interrupted by an interrupt of higher priority
sub if it has the same preemption priority.However, there are not simultaneously Sub
and Preemption Priority all values from 0 to 15 are available, since only a total of 4
priority
bits
are
present
in
the
STM32. These
are
the
function NVIC_PriorityGroupConfig (...) divided into sub and Preemption
Priority. In the example, all four bits are assigned for the Preemption Priority Level - so it
will not distinguish between different sub Priorities. The interrupt is now configured as
far done. In the next step the code of the corresponding interrupt handler has yet to be
written. Add to that the following code snippet to file a stm32f10x_it.c. The exact terms
of the functional heads of the various interrupt handler can be looked up in the file
startup_stm32f10x_xx_xx.c. At the beginning of the interrupt handler, the pending bit
must be cleared. If we write this statement in the last line before the interrupt handler is
exited, this leads to problems, if the code optimizer is turned on. For practical
applications, the code should be in terms debouncing yet refined.
..\stm32f10x_it.c
STM32F100RB
void EXTI0_IRQHandler(void){
EXTI_ClearITPendingBit(EXTI_Line0);
if(GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_9)){
GPIO_WriteBit(GPIOC, GPIO_Pin_9, RESET);
}else{
GPIO_WriteBit(GPIOC, GPIO_Pin_9, SET);
}
}
The configuration is very similar to the case of external interrupts. First you have the
module that triggers the interrupt will be communicated in which / what event / events, it
should do so. Using the function TIM_ITConfig here the update event is selected. It
always occurs when the timer count register with be the auto-reload register
( TIM_Period ) updated, has thus reached the highest count of 0 and starts the
counting
operation.
In the NVIC then you must still enable the interrupt. The last thing missing is nor the
code for the interrupt handler.
..\stm32f10x_it.c
STM32F103RB
void TIM2_IRQHandler(void){
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_5)){
GPIO_WriteBit(GPIOA, GPIO_Pin_5, RESET);
}else{
GPIO_WriteBit(GPIOA, GPIO_Pin_5, SET);
}
}
I2C
// 10
// 10.1 Configuration
..\main.c
STM32F103RB
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
I2C_InitTypeDef I2C_InitStructure;
systemnit ( ) ;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_PinRemapConfig(GPIO_Remap_I2C1, ENABLE);
NVIC_InitStructure. NVIC_IRQChannel = I2C1_EV_IRQn ;
NVIC_InitStructure. NVIC_IRQChannelPreemptionPriority =
NVIC_InitStructure. NVIC_IRQChannelSubPriority = 0 ;
NVIC_InitStructure. NVIC_IRQChannelCmd = ENABLE ;
NVIC_Init ( & NVIC_InitStructure ) ;
NVIC_InitStructure. NVIC_IRQChannel = I2C1_ER_IRQn ;
NVIC_InitStructure. NVIC_IRQChannelPreemptionPriority =
NVIC_InitStructure. NVIC_IRQChannelSubPriority = 0 ;
NVIC_InitStructure. NVIC_IRQChannelCmd = ENABLE ;
NVIC_Init ( & NVIC_InitStructure ) ;
I2C_DeInit(I2C1);
0 ;
0 ;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = 100000;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_OwnAddress1 = 0;
I2C_Init(I2C1, &I2C_InitStructure);
I2C_ITConfig(I2C1, I2C_IT_EVT, ENABLE);
I2C_ITConfig(I2C1, I2C_IT_BUF, ENABLE);
I2C_ITConfig(I2C1, I2C_IT_ERR, ENABLE);
I2C_Cmd(I2C1, ENABLE);
I2C_InitTypeDef:
I2C_Ack
Upon receipt of the own address or a data byte an acknowledgment bit is sent
ifI2C_Ack_Enable .
I2C_AcknowledgedAddress
bit
length
of
its
own
address
in
slave
mode. Possible
values:I2C_AcknowledgedAddress_7bit , I2C_AcknowledgedAddress_10bi
t
I2C_ClockSpeed
frequency of the clock line in Hz. Note: If not using the default configuration, but, for
example, a 16 MHz crystal, the internal calculation is wrong and the resulting
frequency deviates from the specified value.
I2C_DutyCycle
Sets the ratio of low to high in fast mode fixed (from 100kHz). Possible
values:I2C_DutyCycle_16_9 (T low /
T high =
16/9), I2C_DutyCycle_2 (T low /
T high = 2)
I2C_Mode
IC
/
SMBus
Mgliche
Werte: I2C_Mode_I2C, I2C_Mode_SMBusDevice,I2C_Mode_SMBusHost
I2C_OwnAddress1
definition of the first private address - in the STM32 can be used in addition a second
own address. Range: 0 ... 127/0 ... 1023 defined according to previously address
range. The setting is only useful if the STM32 operates as a slave.
Important : In order for the I2C module is working properly, it must be initialized before
the function I2C_DeInit (...) be uninitialized.
With I2C_ITConfig (...) the three different interrupt sources to be enabled. The
event interrupt is triggered when the start bit or the address was sent in master mode or
a byte transfer is complete. In slave mode, it is triggered upon receipt of your own
address, or stop bits, and a byte has been transmitted. The buffer interrupt occurs when
the receive buffer contains a new byte transmit buffer is empty or. Event and buffer
interrupts
land
in
the
same
interrupt
vector.
In addition, an Error Interrupt Vector is available.
The routines of the I 2 C module to be exemplary outsourced here in the file i2c.c. These
each have a corresponding function of the file i2c.c is called from within the interrupt
handler.
..\stm32f10x_it.c
STM32F103RB
void I2C1_EV_IRQHandler(void){
i2c_handleEventInterrupt();
}
void I2C1_ER_IRQHandler(void){
i2c_handleErrorInterrupt();
}
So that the following functions are visible both in main.c and stm32f10x_it.c they are
declared in the header file i2c.h. i2c.h still must then use #include "i2c.h" be
involved in stm32f10x_conf.h.
..\i2c.c
STM32F103RB
#include "i2c.h"
I2C_TypeDef * I2C_Module;
volatile uint8_t deviceAddress;
volatile uint8_t dataByte1;
volatile uint8_t dataByte0;
volatile uint8_t receivedDataByte1;
volatile uint8_t receivedDataByte0;
volatile uint8_t i2cDirectionWrite;
volatile uint8_t i2cByteCounter;
volatile uint8_t i2cBusyFlag;
void i2c_writeByte(uint8_t address, uint8_t byte){
while(i2cBusyFlag){}
while(I2C_GetFlagStatus(I2C_Module,I2C_FLAG_BUSY)){}
deviceAddress = address;
dataByte0 = byte;
i2cDirectionWrite = 1;
i2cBusyFlag = 1;
i2cByteCounter = 1;
I2C_GenerateSTART(I2C_Module, ENABLE);
}
void i2c_writeTwoBytes(uint8_t address, uint8_t byte1, uint8_t byte0){
while(i2cBusyFlag){}
while(I2C_GetFlagStatus(I2C_Module,I2C_FLAG_BUSY)){}
deviceAddress = address;
dataByte1 = byte1;
dataByte0 = byte0;
i2cDirectionWrite = 1;
i2cBusyFlag = 1;
i2cByteCounter = 2;
I2C_GenerateSTART(I2C_Module, ENABLE);
}
void i2c_readTwoBytes(uint8_t address){
while(i2cBusyFlag){}
while(I2C_GetFlagStatus(I2C_Module,I2C_FLAG_BUSY)){}
deviceAddress = address;
i2cDirectionWrite = 0;
i2cBusyFlag = 1;
i2cByteCounter = 2;
I2C_AcknowledgeConfig(I2C_Module, ENABLE);
I2C_GenerateSTART(I2C_Module, ENABLE);
}
void i2c_create(I2C_TypeDef * I2Cx){
I2C_Module = I2Cx;
i2cBusyFlag = 0;
}
uint16_t i2c_getData(void){
return (receivedDataByte1 << 8) | receivedDataByte0;
}
// ISR
void i2c_handleEventInterrupt(void){
if(I2C_GetFlagStatus(I2C_Module, I2C_FLAG_SB) == SET){
if(i2cDirectionWrite){
// STM32 Transmitter
I2C_Send7bitAddress(I2C_Module, deviceAddress, I2C_Direction_Transmitter);
}else{
// STM32 Receiver
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
// 11
CAN
// 11.1 Configuration
..\main.c
STM32F103RB
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
systemnit ( ) ;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_PinRemapConfig(GPIO_Remap1_CAN1 , ENABLE);
CAN_InitStructure.CAN_Prescaler = 2;
CAN_InitStructure.CAN_SJW = CAN_SJW_2tq;
CAN_InitStructure.CAN_BS1 = CAN_BS1_11tq;
CAN_InitStructure.CAN_BS2 = CAN_BS2_4tq;
CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
CAN_InitStructure.CAN_TTCM = DISABLE;
CAN_InitStructure.CAN_ABOM = DISABLE;
CAN_InitStructure.CAN_AWUM = DISABLE;
CAN_InitStructure.CAN_NART = ENABLE;
CAN_InitStructure.CAN_RFLM = DISABLE;
CAN_InitStructure.CAN_TXFP = DISABLE;
CAN_Init(CAN1, &CAN_InitStructure);
The bit rate of the CAN bus is calculated from APB1 / CAN_Prescaler / total time
quanta.There are three segments of time quanta that occur in the following order:
SYNC_SEG (the STM32 always 1), BS1 and BS2. The clock APB1 the sample code is
32 MHz.Consequently, the CAN bit rate is 32 MHz / 2 / (1 + 11 + 4) = 1 MBit / s.
The sample point lies between BS1 and BS2, and should be at about 75%.
(1 + tq tq 11): Here / (1 + 11 + 4 tq tq tq) = 75%.
In the hardware circuitry is to be noted that also for testing the RX and TX lines
(possibly via level converter and termination) are interconnected. Otherwise, the CAN
module itself can not "hear" and will not send a complete frames.
CAN_InitTypeDef:
CAN_Prescaler
prescaler - Value range: 1 ... 1024
CAN_SJW
Synchronization Jump Width - the CAN module can be a bit around this value
increase or reduce resynchronization, Range: CAN_SJW_1tq ... CAN_SJW_4tq
CAN_BS1
Time Quantum in Bit Segment 1 Wertebereich: CAN_BS1_1tq ... CAN_BS1_16tq
CAN_BS2
Time Quantum in Bit Segment 2 Wertebereich: CAN_BS2_1tq ... CAN_BS2_8tq
CAN_Mode
mgliche
Werte: CAN_Mode_Normal, CAN_Mode_LoopBack, CAN_Mode_Silent,CAN_Mode
_Silent_LoopBack
Alle Modes neben Normal dienen zu Testzwecken. Dabei kann das CAN-Modul seine
gesendeten Nachrichten selbst empfangen ohne dabei zu senden, ohne Nachrichten
vom Bus zu empfangen oder beides. Genaueres ist dem Reference Manual zu
entnehmen.
CAN_TTCM
time-triggered communication mode - the CAN module stores the times when a
message is sent or a message is received if to ENABLE set. How this is to be read is
in the Reference Manual.
CAN_ABOM
Automatic Bus-Off Management - The bus-off state is exited automatically when 128
times 11 recessive bits are detected on the bus if on ENABLE set. Otherwise, the
CAN module must be manually re-initialized. The bus-off state is triggered by 255
transmission errors in transmission and prevents further transmission and reception
of messages.
CAN_AWUM
Automatic Wakeup Mode - determines how the sleep mode is
exited, DISABLE means that the software will delete the SLEEP bits must awaken
the module, ENABLE means that the module is automatically awakened when a
message arrives on the CAN bus ,
CAN_NART
No Automatic Retransmission - If on ENABLE is set, each message is sent only once,
regardless of whether an error occurred. Otherwise, the CAN module tries to send
the message until no more transmission error occurs or the bus-off state is triggered.
CAN_RFLM
. Receive FIFO Locked Mode - defines what should happen when the receive FIFO
overflows DISABLE .: A new incoming message overwrites the previously arrived,
lying in the FIFO message ENABLE : A new incoming message is discarded.
CAN_TXFP
Transmit FIFO Priority - determines the order will be sent after the news, either
Identifier ( DISABLE ) or chronological ( ENABLE ).
// Send 11.2 News
..\main.c
STM32F103RB
...
CanTxMsg canMessage;
canMessage.StdId
canMessage.ExtId
canMessage.RTR =
canMessage.IDE =
canMessage.DLC =
= 0x123;
= 0;
CAN_RTR_DATA;
CAN_ID_STD;
8;
canMessage.Data[0]
canMessage.Data[1]
canMessage.Data[2]
canMessage.Data[3]
canMessage.Data[4]
canMessage.Data[5]
canMessage.Data[6]
canMessage.Data[7]
=
=
=
=
=
=
=
=
0;
1;
2;
3;
4;
5;
6;
7;
CAN_Transmit(CAN1, &canMessage);
CanTxMsg :
StdId
Standard Identifier - Range: 0 ... 0x7FF
EXTID
extended identifier - Value range: 0 ... 0x1FFFFFFF
RTR
Remote
Transmission
at CAN_RTR_Remote set.
Request. Features
remote
frame,
if
IDE
choice whether standard or extended identifier to be used for the message.
DLC
length of the payload in bytes - Value range: 0 ... 8
Data
[8]
payload. Data [0] is sent first. DLC is less than 8, so the last bytes are not
transmitted.
while(!(CAN1->TSR & CAN_TSR_TME0 || CAN1->TSR & CAN_TSR_TME1 || CAN1->TSR & CAN_TSR_TME2)){}
Before sending you should check whether at least one of the three transmit mailboxes is
free. If no mailbox is more freely while trying to send, the message will disappear in the
nirvana. The above code will remain as long as this in a while loop until at least one of
the corresponding flag is set to 1. This solution should at least be supplemented by a
timeout counter to make sure that the loop will eventually leave. For example,
if CAN_NART toDISABLE set and there are no other active network CAN node, the
Transmit Mailboxes will never be released under certain circumstances.
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
4x Standard ID
The following graph shows the meaning of the bits of CAN_FxR1 in 32-bit
mode. CAN_FxR2 is has exactly the same structure.
The 16-bit mode doubles the number of filter banks, but can be used only useful for
standard IDs.
The graphics are taken from the Reference Manual (RM0008) from page 640. More
details can be found there.
Pro CAN module there are two FIFOs, each offering space for three received
messages.Each filter bank is associated with a FIFO. Land there those messages that
have been "passed" by the filter. Each FIFO has its own interrupt vector.
..\main.c
STM32F103RB
int main(void) {
...
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CAN_FilterInitStructure.CAN_FilterNumber = 0;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0123 << 5;
CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0xFFFF;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0xFFFF;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
while (1) {}
}
void USB_LP_CAN1_RX0_IRQHandler(void) {
CanRxMsg RxMessage;
CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
if (RxMessage.Data[0] == 1) {
GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_SET);
} else {
GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_RESET);
}
}
In this example, a filter to the standard ID 0x123 is defined. All items with different IDs
will be ignored by the hardware. The filter is assigned FIFO 0. After receiving the
message, the interrupt handler is called. This copies the data from the FIFO in the
structure RxMessage and then turn an LED on PA5 corresponding byte 0 or off.
CAN_FilterInitTypeDef:
CAN_FilterNumber
Einzustellende Filter Bank, Werte: 0...13 (0...27 bei connectivity line devices)
CAN_FilterMode
CAN_FilterMode_IdList (2
Extended
oderCAN_FilterMode_IdMask (ID & Mask)
IDs
Standard
IDs)
CAN_FilterScale
CAN_FilterScale_16bit or CAN_FilterScale_32bit (16- / 32-bit mode, see
above)
CAN_FilterIdHigh
Upper
16
bits
of
Lower 16 bits of CAN_FxR2 (16-bit mode)
CAN_FilterIdLow
Lower 16 bits of CAN_FxR1
CAN_FilterMaskIdHigh
Upper 16 bits of CAN_FxR2
CAN_FilterMaskIdLow
Lower
16
bits
of
Upper 16 bits of CAN_FxR1 (16-bit mode)
CAN_FilterFIFOAssignment
Associated FIFO values: 0 or 1
CAN_FxR1
(32-bit
mode)
CAN_FxR2
(32-bit
mode)
CAN_FilterActivation
Specifies whether the filter should be turned on. Values: ENABLE or DISABLE
In connectivity line devices 28 Filter Banks are available. By default filter 0 ... 13 CAN1
and CAN2 14 ... 27 are assigned. The threshold at which the filter banks are used for
CAN2, using function CAN_SlaveStartBank (uint8_t CAN_BankNumber) 27 are
shifted to 1 ....
Finally, here are a few examples of the different combinations in the filter
initialization. By the bit shifts RTR & IDE flags and different meanings depending on the
filter width, the names of the structure elements are CAN_FilterIDHigh /
Low and CAN_FilterMaskIDHigh / Low unfortunately no longer very clearly. ST
has probably decided for performance reasons therefor.
STM32F103RB
// Alle IDs
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CAN_FilterInitStructure.CAN_FilterNumber = 0;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
// 0x12345670 ... 0x1234567F (Mask: 0xFFFFFFF0UL)
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CAN_FilterInitStructure.CAN_FilterNumber = 0;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
CAN_FilterInitStructure.CAN_FilterIdHigh = (0x12345670UL << 3) >> 16;
CAN_FilterInitStructure.CAN_FilterIdLow = 0x12345670UL << 3 | 4;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = (0xFFFFFFF0UL << 3) >> 16;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0xFFFFFFF0UL << 3 | 4;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
// 0x12345678 & 0x11223344
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CAN_FilterInitStructure.CAN_FilterNumber = 0;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdList;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
CAN_FilterInitStructure.CAN_FilterIdHigh = (0x12345678UL << 3) >> 16;
CAN_FilterInitStructure.CAN_FilterIdLow = 0x12345678UL << 3 | 4;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = (0x11223344L << 3) >> 16;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x11223344UL << 3 | 4;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
// 0x100 ... 0x11F (Mask: 0xFE0), 0x200 nur RTR;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CAN_FilterInitStructure.CAN_FilterNumber = 0;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_16bit;
CAN_FilterInitStructure.CAN_FilterIdHigh = 0x100 << 5;
CAN_FilterInitStructure.CAN_FilterIdLow = 0x200 << 5 | 0x10;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0xFE0 << 5;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0xFFF << 5 | 0x10;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
// 0x001, 0x011, 0x200 nur RTR, 0x333
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CAN_FilterInitStructure.CAN_FilterNumber = 0;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdList;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_16bit;
CAN_FilterInitStructure.CAN_FilterIdHigh = 0x001 << 5;
CAN_FilterInitStructure.CAN_FilterIdLow = 0x011 << 5;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x200 << 5 | 0x10;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x333 << 5;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
// 12
SPI
// 12.1 Configuration
..\main.c
STM32F103RB
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
systemnit ( ) ;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA
ENABLE);
RCC_APB2Periph_AFIO
RCC_APB2Periph_SPI1,
SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32 ;
SPI_CPHA = SPI_CPHA_2Edge ;
SPI_CPOL = SPI_CPOL_High ;
SPI_CRCPolynomial = 0 ;
SPI_DataSize = SPI_DataSize_8b ;
SPI_Direction = SPI_Direction_2Lines_FullDuplex ;
SPI_FirstBit = SPI_FirstBit_MSB ;
SPI_Mode = SPI_Mode_Master ;
SPI_NSS = SPI_NSS_Soft ;
& SPI_InitStructure ) ;
Pin-Konfiguration: PA8 = Chip Select, PA7 = MOSI, MISO = PA6, PA5 = SCK
SPI_InitTypeDef:
SPI_BaudRatePrescaler
divider
for
the
clock
signal
from
APB2. Possible
values: SPI_BaudRatePrescaler_xwith x = 2, 4, 8, 16, 32, 64, 128, 256
SPI_CPHA
edge of the clock line at which the data is to be transferred. Possible
values:SPI_CPHA_1Edge (CPHA = 0), SPI_CPHA_2Edge (CPHA = 1)
SPI_CPOL
voltage level of the clock is in the idle state. Possible values: SPI_CPOL_Low (CPOL
= 0), SPI_CPOL_High (CPOL = 1)
SPI_CRCPolynomial
The SPI module can during data reception calculate a CRC value. The polynomial to
be defined here. Depending on the selected word size (see below) is calculated
using either a CRC-8 or CRC-16 value.
SPI_DataSize
size of the data word transmitted in one piece - whereas it is the effective width of the
data register which is used for receiving and transmitting the data. Possible
values:SPI_DataSize_8b , SPI_DataSize_16b
SPI_Direction
choice between uni- and bi-directional data mode. Possible
SPI_FirstBit
bit order. Possible values: SPI_FirstBit_LSB , SPI_FirstBit_MSB
SPI_Mode
Determines whether the STM32 behaves
values:SPI_Mode_Master , SPI_Mode_Slave
as
master
or
slave. Possible
SPI_NSS
Slave Select Management - The chip select function of the SPI module can be
controlled directly from the NSS pin. This feature is of good use when the STM32
operates as a slave or is used in a multi-master system. Otherwise, you can use any
other pin of the chip select signal. With SPI_NSS_Soft the pin can be used as
normal GPIO pin. Alternatively value: SPI_NSS_Hard . The functionality can be
found in the Reference Manual.
..\stm32f10x_it.c
STM32F103RB
void SPI1_IRQHandler(void){
spi_handleSPI1Interrupt();
}
The sample code will initially have only three public functions: spi_create (...) .
defines the SPI module and the chip select pin spi_handleSPI1Interrupt () is
called by the interrupt handler when an interrupt has occurred. spi_writeTwoBytes
(...) sends two bytes, and stores the two bytes received in the buffer. For the return
of the received data other functions can be written when required.
..\spi.c
STM32F103RB
#include "spi.h"
#define BUFFER_SIZE 2
SPI_TypeDef * SPI_Module;
GPIO_TypeDef * CS_GPIO;
uint16_t CS_GPIO_Pin;
uint8_t
uint8_t
uint8_t
uint8_t
spiRxCounter;
spiTxCounter;
spiBusyFlag;
spiDataBuffer[BUFFER_SIZE];
The code should be self-explanatory, if one chapter to the I 2 has understood C module,
which is why I will discuss only a few characteristics of the SPI module here.
If the transmit buffer is empty, the TXE flag is set and periodically interrupt is
generated.For this reason, the Tx interrupt is disabled before transmission and must be
disabled in the transmit buffer after writing the last byte. Consequently, the order of the
"if" - "else if" query crucial. If one were to first check the TXE flag, the last byte received
could
not
be
picked
up.
The timing for collection of bytes received is also a factor not uncritical. Due to the
structure of the buffer in the SPI module two bytes are already sending in the queue
before the first byte has been received. If the first byte received is not read before the
second byte transfer is complete, it will be overwritten by the new second received
byte. If further interrupts are enabled in the STM32, the SPI module should have a very
high priority if you can not confirm that other interrupts do not take too much computing
time.
If the timing issue no solution can be found, the DMA controller should be used. This is
very useful especially for large data sets because the processor will not be busy
processing the interrupts. I will deal with it in a later chapter.
..\main.c
STM32F103RB
...
spi_create(SPI1, GPIOA, GPIO_Pin_8);
spi_writeTwoBytes(0x12, 0x34);
// 13
// 13.1 Introduction
ADC
The following code shows a minimal example for continuous reading of analog values. I
will then describe important improvements and further possibilities of the A / D
converter.
..\main.c
STM32F103RB
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
systemnit ( ) ;
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC
ENABLE);
RCC_APB2Periph_AFIO
RCC_APB2Periph_ADC1,
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_1Cycles5);
ADC_Cmd(ADC1, ENABLE);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
Pin configuration: When PC0 input, which is used herein as ADC12_IN10 of the A / D
converter is used.
RCC_ADCCLKConfig (...) sets the prescaler fixed for the ADC clock. The ADC clock
is generated from the clock of APB2 and may not exceed 14 MHz. Possible values for
the divisor are 2, 4, 6 and 8. Here: 72 MHz / 6 MHz = 12.
ADC_InitTypeDef:
ADC_ContinuousConvMode
If ENABLE , then automatically the next is started after the completion of a
conversion.
ADC_DataAlign
Possible values: ADC_DataAlign_Left , ADC_DataAlign_Right - The 12-bit
results are either left- or right-justified in the 16 bit registers stored. When Injected
Group Channels (more on that later) it should be noted that in the most significant bit
is always a sign is stored. The 12-bit value in the left-aligned case is thus shifted by 1
bit to the right. (See Reference Manual)
ADC_ExternalTrigConv
Specifies which external signal to trigger the start of a conversion.
ADC_Mode
ADC1 and ADC2 If used at the same time, we can roughly with this element with
respect to their interaction. the trigger behavior can be defined. For example, the two
modules on the same trigger signal can react simultaneously and thus are
synchronous. The number of different options are illustrated graphically very nice in
the Reference Manual.
ADC_NbrOfChannel
number of channels in the Channel Regular Group (see below)
ADC_ScanConvMode
If ENABLE , so several channels in the "Scan Mode" changed. (see below)
ADC_RegularChannelConfig
(...) selects channel 10 (PC0), ADC_Cmd
(...) activates
the
converter
ADC1
and ADC_SoftwareStartConvCmd
(...) triggers the first conversion. The function ADC_GetConversionValue
(...) is the current value of last accessed conversion.
..\main.c
STM32F103RB
uint16_t value = ADC_GetConversionValue(ADC1);
The above code can eg be used to periodically determine in a loop the value of a
potentiometer, but it is rather inconvenient if all individual measurements must be
processed. Also, if you want to submit multiple values there are better solutions.
// 13.2 Calibration
The ADC converter of the STM32 have a function to get yourself to calibrate. It is not
recommended to run once after power converter in order to increase their accuracy.
..\main.c
STM32F103RB
...
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
...
After switching on (with or without calibration) of the ADC needs a stabilization time until
it gives accurate results. The duration of t STAB can be found in the datasheet. It is, for
example when STM32F103 than 1 microseconds.
// 13.3 Regular & Injected Group
The ADCs of the STM32 have to use a multiplexer to various analog inputs
respectively.
The switching from one channel to another can be automated, so that the detection of
multiple analog signals is very comfortable. These different channels are organized into
a
group
that
is
executed
when
scan
mode
is
active.
In addition to the Regular Group, which can consist of up to 16 channels, there is the
Group Injected with a maximum of 4 channels. If the measurement of Injected Group
channels triggered while watching the channels of the Regular Group are scanned, the
ADC interrupts the current conversion. It all channels of Injected Group are then
detected before starting new with the conversion of the last interrupted channel. In the
following program, the ADC is initially configured for two channels. Also, the scan mode
must be activated. In scan mode, the data can not be read sure about interrupts, since
the individual channels are converted in rapid succession. If the delay until picked up by
an interrupt routines is too long, old data is overwritten. The solution is to use the Direct
Memory Access Controller (DMA), which I will describe in more detail, however, only in
the next chapter.
..\main.c
STM32F103RB
...
ADC_InitStructure.ADC_NbrOfChannel = 2;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
...
parameter, the result number from 1 to 16 in which the channels are run and the fourth
parameter
is
the
sample
time.
Possible values for the Sample Time in Cycles: 1.5, 7.5, 13.5, 28.5, 41.5 55.5 71.5,
239.5need to calculate the total time for the conversion of a channel still 12.5 Cycles are
added thereto. The duration of one Cycles corresponds to the period of ADCCLK. The
maximum permissible frequency for ADCCLK is 14 MHz. Consequently, the shortest
possible time is to convert one channel at (1.5 + 12.5) / 14 MHz = 1 microseconds.
..\main.c
STM32F103RB
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_1Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 2, ADC_SampleTime_1Cycles5);
The Injected Group can be configured in a similar manner as follows. Its length is about
the function ADC_InjectedSequencerLengthConfig (...) set.
..\main.c
STM32F103RB
ADC_InjectedSequencerLengthConfig(ADC1, 1);
ADC_InjectedChannelConfig(ADC1, ADC_Channel_12, 1, ADC_SampleTime_1Cycles5);
The Injected channels also offer the possibility to deduct an offset from the result of the
conversion. This allows you to get signed values are therefore provided in the result
registers sign bit. The offset can be defined separately for each channel Injected.
For the result of each Injected channels, there are also separate register so that you
can do without the DMA here or even need.
..\main.c
STM32F103RB
ADC_SetInjectedOffset(ADC1, ADC_InjectedChannel_1, 0x0800);
..\main.c
STM32F103RB
uint16_t value = ADC_GetInjectedConversionValue(ADC1, 1);
// 13.4 Triggering
The trigger for the conversion of a group is defined separately for Regular and Injected
Group. In order to trigger via software, the values must be explicitly set to None ..._.
..\main.c
STM32F103RB
...
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
...
ADC_ExternalTrigInjectedConvConfig(ADC1, ADC_ExternalTrigInjecConv_None);
Alternatively, different timer events are triggering available, as well ExtI-Line 11 (Regular
Group) and ExtI-Line 15 (Injected Group) at ADC1 / ADC2. For ADC3 only timer events
can
be
selected. More
accurate
is
the
Reference
Manual.
The trigger for non-software events must then each still be activated as follows.
..\main.c
STM32F103RB
ADC_ExternalTrigConvCmd(ADC1, ENABLE);
ADC_ExternalTrigInjectedConvCmd(ADC1, ENABLE);
If the Regular Group is to be run continuously after a single trigger, it can be the
elementADC_ContinuousConvMode of ADC_InitTypeDef during
initialization
to ENABLE set. MeansADC_AutoInjectedConvCmd (ADC1, ENABLE); the ADC is
configured so that after the execution of the Regular Group Injected Group is triggered
immediately. In this combination, all of which can be detected up to 20 ADC channels
sequentially continuous.
// 13.5 Discontinuous Mode
The evidence indicates that a complete Group (scan mode provided) is processed in
one piece after triggering. The STM32 but also provide an opportunity for both Regular
as "divide" also Injected Group in more pieces. In the discontinuous mode, you can
define a sequence of length 1 to 8 for the Regular Group (Group Injected sequence
length is always 1). For example, if 8 channels available and the sequence length is 3,
then the first three channels are converted after the first trigger signal. Thereafter, the
ADC is ready for the next triggering, until it proceeds to the following three
channels. After the third trigger the last two channels are converted - there will be no
overflow, the first channel in the Group does not belong to the third sequence.
..\main.c
STM32F103RB
ADC_DiscModeChannelCountConfig(ADC1, 1);
ADC_DiscModeCmd(ADC1, ENABLE);
ADC_InjectedDiscModeCmd(ADC1, ENABLE);
// 13.6 Interrupts
The ADC interrupt can be generated by three different events which can be activated
separately from one another:
Important: The ADC1 and ADC2 of interrupts are assigned to the same interrupt
vector.ADC3, if any, has its own interrupt vector.
..\main.c
STM32F103RB
ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);
ADC_ITConfig(ADC1, ADC_IT_JEOC, ENABLE);
ADC_ITConfig(ADC1, ADC_IT_AWD, ENABLE);
..\stm32f10x_it.c
STM32F103RB
void ADC1_2_IRQHandler(void){
}
In Dual ADC fashion ADC1 and ADC2 respond to the same trigger signal. For ADC2 no
way is provided which converted data (regular channels) via DMA save
money. However, to use the dual ADC mode, the measurement results of ADC2 in the
upper 16 bits of the 32 bit data register of ADC1 be saved. This can then be accessed
by the DMA controller. In the following example, the Regular Groups of ADC1 and ADC2
are to be recorded simultaneously. The acquisition is triggered by software. In the
Reference Manual is noted that the same channel should not be simultaneously
sampled from ADC1 and ADC2. Also, the Regular Groups of ADC1 and ADC2 should
be the same length or Triggerinterval be large enough, otherwise the ADC could be
triggered again with the shorter sequence before the other ADC is finished.
..\main.c
STM32F103RB
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Init(ADC2, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_1Cycles5);
ADC_RegularChannelConfig(ADC2, ADC_Channel_11, 1, ADC_SampleTime_1Cycles5);
ADC_ExternalTrigConvCmd(ADC1, ENABLE);
ADC_ExternalTrigConvCmd(ADC2, ENABLE);
ADC_Cmd(ADC1, ENABLE);
ADC_Cmd(ADC2, ENABLE);
... // Calibration
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
The initialization is initially the same as with only an independent ADC, with the
difference that ADC_Mode the value ADC_Mode_RegSimult is assigned. ADC2 is
initialized
with
the
same
values.
If you want to use external trigger, it must be set for ADC1 and ADC2 need
toADC_ExternalTrigConv_None be
configured.
Even if the ADCs are triggered by software, it must in any case, the external trigger for
both ADCs activated be. The calibration procedure must logically separately for both
ADC to run.
..\main.c
STM32F103RB
uint32_t value = ADC_GetConversionValue(ADC1);
uint16_t valueADC1 = value;
uint16_t valueADC2 = value >> 16;
// 14 DMA
The direct memory access controller allows to transfer data between peripherals and
memory,
without
burdening
the
CPU
directly.
The following example uses again the code from Chapter 13.1. He is extended by a
channel and DMA the sampled data is automatically copied into an array.
..\main.c
STM32F103RB
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
uint16_t ADCBuffer[] = {0xAAAA, 0xAAAA, 0xAAAA};
systemnit ( ) ;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_InitStructure.DMA_BufferSize = 2;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADCBuffer;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC
ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
RCC_APB2Periph_AFIO
RCC_APB2Periph_ADC1,
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_NbrOfChannel = 2;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_1Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 2, ADC_SampleTime_1Cycles5);
ADC_Cmd(ADC1, ENABLE);
ADC_DMACmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
For testing purposes, the array is ADCBuffer an element too large and initialize with a
defined bit sequence. This allows you to check whether new data has been written to
the DMA controller and really not too far writes in memory.
DMA_InitTypeDef:
DMA_BufferSize
number of data to be transmitted. Range: 0 ... 65535th The data is not buffered, here
is the identifier is chosen to be somewhat unhappy.
DMA_DIR
Possible
values: DMA_DIR_PeripheralDST O
is
the
transfer;DMA_DIR_PeripheralSRC - O is a source of transmission
act
of
DMA_M2M
Indicates whether the transfer of memory will be held to memory. In this case, the
"triggering" between two data words is performed automatically. Possible
values:DMA_M2M_Disable , DMA_M2M_Enable
DMA_MemoryBaseAddr
32-bit base address in memory, so the address of the memory cell into which the first
byte is written.
DMA_MemoryDataSize
size
of
a
"word"
on
the
side
of
the
store. Possible
values: DMA_MemoryDataSize_Byte(8
bits), DMA_MemoryDataSize_HalfWord (16-bit), DMA_MemoryDataSize_Word (3
2-bit)
DMA_MemoryInc
If DMA_MemoryInc_Enable increasing the memory address pointer automatically
after each transmission to the predetermined word length. Alternatively
value:DMA_MemoryInc_Disable
DMA_Mode
If DMA_Mode_Circular , the DMA controller will restart from the base address
when its internal data counter reaches 0. The data counter is reset to the preset
"Buffer
Size".
Alternatively: DMA_Mode_Normal - There will be no further transfers after the
number has been transferred in "buffer size" of values. In Memory-to-Memory Mode,
this is the only possible value.
DMA_PeripheralBaseAddr
32-bit base address of the periphery.
DMA_PeripheralDataSize
Possible
values: DMA_PeripheralDataSize_Byte , DMA_PeripheralDataSize_HalfW
ord, DMA_PeripheralDataSize_Word
DMA_PeripheralInc
Possible values: DMA_PeripheralInc_Disable , DMA_PeripheralInc_Enable
DMA_Priority
The DMA requests of higher priority are processed first. There is a choice of four
values: DMA_Priority_Low , DMA_Priority_Medium , DMA_Priority_High ,D
MA_Priority_VeryHigh
With DMA_Init (...) , the corresponding DMA channel is initialized. What hardware
is connected to which channel, you can refer to the Reference Manual. It should be
noted that there are several possible sources for each channel, but should never be
used simultaneously. About ADC_DMACmd (ADC1, ENABLE) is the ADC module
announced that it DMA requests should be sent to the DMA controller when it is a
conversion finished. Notes:In certain STM32 there are two DMA controller (DMA1,
DMA2). The DMA controller can stop the CPU, if both want the same goal (memory /
peripheral) simultaneously. It is, however, ensured by round-robin scheduling that at
least
half
the
bandwidth
for
the
CPU
remains
verfbar.
// 15 Common Mistakes
Below I will list some mistakes that undermine a frequently and can actually lead to
unnecessary Sucherei:
Forgot
timing
eg RCC_APB2PeriphClockCmd
(...)
In particular RCC_APB2PeriphClockCmd (RCC_APB2Periph_AFIO) if pins were
geremapped other functions.
Enabling hardware forget - eg DMA_Cmd (...)
In addition to enable an interrupt to the peripheral hardware forget the NVIC
adjusted appropriately so.
SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32 ;
SPI_CPHA = SPI_CPHA_1Edge ;
SPI_CPOL = SPI_CPOL_Low ;
SPI_CRCPolynomial = 0 ;
SPI_DataSize = SPI_DataSize_16b ;
SPI_Direction = SPI_Direction_2Lines_FullDuplex ;
SPI_FirstBit = SPI_FirstBit_MSB ;
SPI_Mode = SPI_Mode_Master ;
SPI_NSS = SPI_NSS_Soft ;
& SPI_InitStructure ) ;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPIBuffer;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_Init(DMA1_Channel5, &DMA_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
DMA_Cmd(DMA1_Channel4, DISABLE);
DMA_Cmd(DMA1_Channel5, DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel4, 2);
DMA_SetCurrDataCounter(DMA1_Channel5, 2);
SPIBuffer[0] = 0x1234;
SPIBuffer[1] = 0x5678;
// Chip Select Low
GPIO_WriteBit(GPIOB, GPIO_Pin_12, RESET);
DMA_Cmd(DMA1_Channel4, ENABLE);
DMA_Cmd(DMA1_Channel5, ENABLE);
Pin-Konfiguration: PB12 = Chip Select, PB15 = MOSI, PB14 = MISO, PB13 = SCK
..\stm32f10x_it.c
STM32F103RB
void DMA1_Channel4_IRQHandler(void){
spi_handleDMA1Ch4Interrupt();
DMA_ClearFlag(DMA1_FLAG_TC4);
}
..\main.c
STM32F103RB
void spi_handleDMA1Ch4Interrupt(void){
// Chip Select High
GPIO_WriteBit(GPIOB, GPIO_Pin_12, SET);
}
The initialization of the GPIO pins and the SPI module can consist of Chapter 12 are
applied. The only difference is that no more interrupts are enabled, but DMA requests
bySPI_I2S_DMACmd
(...) .
The description of each item of DMA_InitStructure may Chapter 14 are
removed. For SPI, the two channels 4 and 5 are used. If you want to only send or
receive
only,
then
a
channel
can
be
saved.
About DMA_DIR is set, the data direction: Channel 4 from SPI to the store; Channel 5
from memory to the SPI. DMA_Mode need to DMA_Mode_Normal be set. In contrast to
the ADC from Chapter 14 you do not wish to continuously transmit data, but only if more
data frames are expected. For Channel 4, the transfer-Complete-interrrupt be switched
to later make so that the switching of the chip select pins. The last code block Transfers
can be started. In practice, it is better wrap this section in a function. First, the DMA
channels must be turned off and on DMA_SetCurrDataCounter (...) the number
of words is set with the second parameter to be transferred. Subsequently, the chip
select line is pulled low and started by the activation of the DMA transfer of the
channels. Be sure you should turn on Channel 4 (RX) before Channel 5 (TX). Occurs
between the two function calls an interrupt on that interrupts the program flow for a
longer time, the SPI module would otherwise begin sending, Channel 5 but would still
be too late or not designated to answer the received data. The interrupt from Channel 4
(RX) is triggered when the last byte has been received, the entire SPI transfer is thus
completed. Now the chip select line are placed on high again. Channel 5 (TX) should be
used in any way for this purpose because it is ready to work early. If you need to
Channel 4 for any other purpose than SPI, so you can, for example, implement the
necessary
functionality
via
the
SPI
interrupt
and
a
byte
count.
IWDG_SetPrescaler ( IWDG_Prescaler_16 ) ;
IWDG_SetReload(2500);
IWDG_ReloadCounter();
IWDG_Enable();
Using the command IWDG_WriteAccessCmd (...) the write access to the registry
for Prescale and reload value is enabled. Preserved write permissions to
eitherIWDG_ReloadCounter
() or IWDG_Enable
() is
called. About IWDG_SetPrescaler (...) , the 40 kHz clock by a factor of 4, 8,
16, ..., 256 are divided down. With IWDG_SetReload (. ..)the value is set from
which counts down the watchdog. He reaches 0, a reset. Possible values:. 0 ...
4095 IWDG_ReloadCounter () resets the Watchdog to its set maximum value.This
function must be called periodically in the main program to prevent reset during normal
operation. IWDG_Enable () activates the watchdog. About Bytes option of IWDG can
be switched on continuously. Note: (be the IWDG must be enabled to do so) If the
Prescale / reload to change values dynamically in the program, so between two value
changes at least 5 RC Cycles have to be awaited, otherwise, the new values were not
applied. To check whether the IWDG is ready to accept new values, the status bits RVU
(reload value update) and PVU (prescaler value update) can be queried. The bits must
be
0,
so
that
a
new
update
can
be
done.
..\main.c
STM32F100RB
while(IWDG_GetFlagStatus(IWDG_FLAG_PVU) == SET);
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
IWDG_SetPrescaler(IWDG_Prescaler_64);
// 18 Option Bytes
Programming
Manual PM0075 (STM32F10xxx)
multiple option bytes are present in the STM32 Flash are determined by the specific
settings:
Read
Out
If enabled, the reading of the flash memory is prevented.
Protection
Brownout
Level
When the operating voltage of the chip is kept in reset state until the voltage value
set here is reached. If VDD operating below this value, a reset is also triggered.
Hardware
/
software
watchdog
Bit WDG_SW is set by default, which is not the IWDG automatically at startup, but
should only be switched via software. If the bit is cleared, so does the IWDG
immediately after power on / reset on his work.
Reset
Generation
for
standby
/
Stop
Mode
If nRST_STDBY / nRST_STOP is not set, a reset is triggered if the Standby / Stop
Mode is entered.
User
Data
two bytes for any data such as serial numbers.
WriteProtection
The individual pages of Flash can be protected against write access. Thus, for
example, can be prevented even delete programs that write access to the flash.
bytes
The option bytes can be written directly to CoFlash not currently. If you are an ST-Link
in possession, it can be used to change the parameters of the tool "STM32 ST-LINK
utility":
Depending on the microcontroller are some of the above options are not available or
there are additional settings. Further details are UM0892 (STM32 ST-Link Utility
software description) can be seen.
// 19 System Timer
Note: The information system timers are not included in the reference but in the
Programming Manual ( PM0056 for Cortex-M3). The system timer is an integral part of
the Cortex-M3 core and to initiate multiple tasks sequentially provided for the system
clock of an RTOS or just , His count register is 24-bits wide. In the following example, an
LED located at PC8 is the system timer operated at 1Hz. The system clock is 24 MHz.
..\main.c
STM32F100RB
int main(void) {
GPIO_InitTypeDef GPIO_InitStructure;
SystemInit ( ) ;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
SysTick_Config(1499999);
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
NVIC_SetPriority(SysTick_IRQn, 14);
while (1) {}
}
void SysTick_Handler(void) {
if (GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_8)) {
GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_RESET);
} else {
GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET);
}
}
SysTick_Config (...) sets the value at which the timer counts down. Maximum
value: 0xFFFFFF. In addition, the function activates the system timer and its exception
handler. It is part of the CMSIS. The system timer is supplied either directly by the CSO
with its clock or it is interposed a prescaler with the value 8. The prescaler can
functionSysTick_CLKSourceConfig (...) can be activated / deactivated. The
function must necessarily after SysTick_Config (...) are called. By default, the
system timer exception is the lowest priority and 15 is set at 14 exemplarily. For this
purpose, the function isNVIC_SetPriority (...) is called, which also belongs to
the CMSIS. Again, it must be ensured that the function only after SysTick_Config
(...) is
called.
// 20 RS485
As RS485 Transceiver SN65HVD1781 is used with the following circuit.
PA0:
PA2:
PA3: CPU_RS485_R
..\main.c
STM32F103RB
int main(void) {
SystemInit();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
/* DE Pin */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* TX Pin */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
CPU_RS485_DE
CPU_RS485_D
/* RX Pin */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
NVIC_InitTypeDef NVIC_InitStructure;
/* DMA Channel 6 (USART RX) */
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel6_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/ * USART2 (TX)*/
NVIC_InitStructure. NVIC_IRQChannel = USART2_IRQn ;
NVIC_InitStructure. NVIC_IRQChannelPreemptionPriority =
NVIC_InitStructure. NVIC_IRQChannelSubPriority = 0 ;
NVIC_InitStructure. NVIC_IRQChannelCmd = ENABLE ;
NVIC_Init ( & NVIC_InitStructure ) ;
1 ;
USART_InitStructure.
USART_InitStructure.
USART_InitStructure.
USART_InitStructure.
USART_InitStructure.
USART_InitStructure.
USART_Init ( USART2 ,
USART_BaudRate = 57600 ;
USART_WordLength = USART_WordLength_8b ;
USART_StopBits = USART_StopBits_1 ;
USART_Parity = USART_Parity_No ;
USART_HardwareFlowControl = USART_HardwareFlowControl_None ;
USART_Mode = USART_Mode_Rx | USART_Mode_Tx ;
& USART_InitStructure ) ;
ENABLE ) ;
ENABLE ) ;
// DE Pin high
DMA_SetCurrDataCounter(DMA1_Channel6, 2);
DMA_SetCurrDataCounter(DMA1_Channel7, 3);
DMA_ClearFlag(DMA1_FLAG_TC6);
DMA_ClearFlag(DMA1_FLAG_TC7);
DMA_Cmd(DMA1_Channel6, ENABLE);
DMA_Cmd(DMA1_Channel7, ENABLE);
while(1){}
}
void USART2_IRQHandler(void){
USART_ClearITPendingBit(USART2, USART_IT_TC);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, RESET);
}
// DE Pin low
void DMA1_Channel6_IRQHandler(void){
/* all data received */
DMA_ClearFlag(DMA1_FLAG_TC6);
}
USART_InitTypeDef :
USART_BaudRate
The required baud rate is directly specified. It should be noted that in the calculation
of
the
register
values
in USART_Init
(...) the
value HSE_VALUE or HSI_VALUE is expected from stm32f10x.h. If one uses such as
a quartz with a frequency equal to 8MHz clock as supply, it must HSE_VALUE be
adjusted.
USART_WordLength
It can be chosen for the word length of 8-9 bits.
USART_StopBits
Here,
the
"number"
1
and
2
0.5 and 1.5 for Smart Card Mode
of
stop
for
bits
are
USART
USART_Parity
Einstellung
fr
das
Bit: USART_Parity_No, USART_Parity_Even oderUSART_Parity_Odd
defined:
etc.
Parity-
USART_HardwareFlowControl
for flow control, RTS and CTS ports can be individually enabled with this option. This
feature is not available for all USART modules available.
USART_Mode
Determines whether the receive, transmit mode or both are enabled.
// 21
DAC
// 21.1 Introduction
DAC with the analog signals can be generated. The references for the output voltage
are VSSA and V REF + , wherein the voltage range of V REF + 2.4 V ... V DDA is limited. When
chips in small packages such as the STM32F100RB used here in LQFP64 V REF + not
led
to
the
outside,
but
directly
to
V DDA connected.
This example generates a voltage of 0.5 V * REF + to pin PA4.
..\main.c
STM32F100RB
GPIO_InitTypeDef GPIO_InitStructure;
DAC_InitTypeDef DAC_InitStructure;
systemnit ( ) ;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_Init(GPIOA, &GPIO_InitStructure);
DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0;
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable;
DAC_InitStructure.DAC_Trigger = DAC_Trigger_None;
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
DAC_Init(DAC_Channel_1, &DAC_InitStructure);
DAC_SetChannel1Data(DAC_Align_12b_R, 0x7FF);
DAC_Cmd(DAC_Channel_1, ENABLE);
After the DAC channel is enabled, the associated pin is automatically connected to the
DAC. Nevertheless, it is recommended to configure the pin as analog input to prevent
undesired parasitic power consumption.
DAC_InitTypeDef:
DAC_LFSRUnmask_TriangleAmplitude
The DAC can be set in a mode in which it generates noise or a triangle wave. Here,
the "LFSR mask" for the noise generator or the amplitude of the triangular signal can
be defined. Is not used in this example.
DAC_OutputBuffer
This option allows the DAC a buffer to be followed by the output impedance to
max.To reduce 15 kOhm. The disadvantage is that the output range of 0.2 V ...
V DDA - is 0.2 V limited. Without output buffer resistive load at the output must not fall
below 1.5 milliohms to still achieve an accuracy of 1%.
DAC_Trigger
The DAC uses a shadow register, either copies the values from the data register only
after a trigger pulse. The trigger different timers, ExtI Line 9, software or none can be
selected. In the latter case, the values are applied immediately.
DAC_WaveGeneration
Possible Mode)
With DAC_SetChannel1Data (...) a value into the data register of channel 1 of the
DAC can be written. About the first parameter specifies whether the 8-bit mode or 12-bit
mode (left / right justified) is used.
About DAC_Cmd (...) , the DAC channel 1 is activated.
DAC_Cmd(DAC_Channel_1, ENABLE);
DAC_Cmd(DAC_Channel_2, ENABLE);
// 21.3 DMA
In this example, a sine and a cosine PA4 is generated with each 10 kHz to PA5. All data
transfers are performed after initialization via DMA.
..\main.c
STM32F100RB
const uint16_t sinTable[32] = {
2047, 2447, 2831, 3185, 3498, 3750, 3939, 4056, 4095, 4056,
3939, 3750, 3495, 3185, 2831, 2447, 2047, 1647, 1263, 909,
599, 344, 155, 38, 0, 38, 155, 344, 599, 909, 1263, 1647};
uint32_t sinCosTable[32];
int main(void) {
GPIO_InitTypeDef GPIO_InitStructure;
DAC_InitTypeDef DAC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
DMA_InitTypeDef DMA_InitStructure;
SystemInit ( ) ;
for(uint8_t i = 0; i < 32; i++){
sinCosTable[i] = sinTable[i] << 16;
}
for(uint8_t i = 8; i < 32; i++){
sinCosTable[i - 8] |= sinTable[i];
}
for(uint8_t i = 0; i < 8; i++){
sinCosTable[i + 24] |= sinTable[i];
}
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC | RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBaseInitStructure.
TIM_TimeBaseInitStructure.
TIM_TimeBaseInitStructure.
TIM_TimeBaseInitStructure.
TIM_TimeBaseInitStructure.
TIM_TimeBaseInit ( TIM2 ,
TIM_ClockDivision = TIM_CKD_DIV1 ;
TIM_CounterMode = TIM_CounterMode_Up ;
TIM_Period = 74 ;
TIM_Prescaler = 0 ;
TIM_RepetitionCounter = 0 ;
& TIM_TimeBaseInitStructure ) ;
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
In the Application Note AN2824 the exact procedure is described for the I 2 C module
via
DMA
to
provide
them
with
data.
By using the DMA time-critical reading / writing the data register and setting the NACK
bits is avoided when receiving data. Consequently, no high-priority interrupts are no
longer
necessary.
In the Master Receiver mode is to be noted that mandatory minimum two bytes must be
received. To read the temperature registers of the TCN must first be set to 0 and then a
read transfer a register pointer be started. Further details are provided in the data
sheet.
..\main.c
STM32F103RB
volatile uint8_t i2cDirectionWrite;
uint8_t i2cRxBuffer[] = { 0xAA, 0xAA };
uint8_t i2cTxBuffer[] = { 0x00 };
int main(void) {
GPIO_InitTypeDef GPIO_InitStructure;
I2C_InitTypeDef I2C_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
SystemInit ( ) ;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
I2C_DeInit(I2C2);
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = 400000;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_OwnAddress1 = 0;
I2C_Init(I2C2, &I2C_InitStructure);
I2C_ITConfig(I2C2, I2C_IT_EVT, ENABLE);
I2C_Cmd(I2C2, ENABLE);
I2C_DMACmd(I2C2, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = I2C2_EV_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// DMA Channel 4 - I2C2 TX
DMA_InitStructure.DMA_BufferSize = 0;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) i2cTxBuffer;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) & I2C2->DR;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_Init(DMA1_Channel4, &DMA_InitStructure);
// DMA Channel 5 - I2C2 RX
DMA_InitStructure.DMA_BufferSize = 0;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) i2cRxBuffer;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) & I2C2->DR;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_Init(DMA1_Channel5, &DMA_InitStructure);
DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
while (1) {
for (volatile uint32_t i = 0; i < 1000000; i++);
i2cDirectionWrite = 1;
DMA_SetCurrDataCounter(DMA1_Channel4, 1);
DMA_SetCurrDataCounter(DMA1_Channel5, 2);
I2C_GenerateSTART(I2C2, ENABLE);
}
}
void I2C2_EV_IRQHandler(void) {
if (I2C_GetFlagStatus(I2C2, I2C_FLAG_SB) == SET) {
if (i2cDirectionWrite) {
// STM32 Transmitter
I2C_Send7bitAddress(I2C2, 0x9E, I2C_Direction_Transmitter);
} else {
// STM32 Receiver
I2C_Send7bitAddress(I2C2, 0x9E, I2C_Direction_Receiver);
}
} else if (I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)
== SUCCESS) {
if (i2cDirectionWrite) {
// STM32 Transmitter
DMA_Cmd(DMA1_Channel4, ENABLE);
}
} else if (I2C_GetFlagStatus(I2C2, I2C_FLAG_BTF)) {
if (i2cDirectionWrite) {
// STM32 Transmitter
DMA_Cmd(DMA1_Channel5, ENABLE);
I2C_DMALastTransferCmd(I2C2, ENABLE);
I2C_GenerateSTART(I2C2, ENABLE);
i2cDirectionWrite = 0;
I2C_ClearFlag(I2C2, I2C_FLAG_BTF);
}
}
}
void DMA1_Channel5_IRQHandler(void) {
DMA_ClearFlag(DMA1_FLAG_TC5);
I2C_GenerateSTOP(I2C2, ENABLE);
DMA_Cmd(DMA1_Channel4, DISABLE);
DMA_Cmd(DMA1_Channel5, DISABLE);
// Transmission of CAN Message
}
First, the variable is i2cDirectionWrite declared. It states later whether the module
is in writing ( 1 ) - or read mode ( 0 is). Furthermore, two fields to be sent / received
data is defined. Important : When you configure the I 2 C module buffer events may
( I2C_IT_BUF .) can not be activated via the function I2C_DMACmd (...) the module
to be caused to generate DMA requests. The channels here are 4 and 5 is for the DMA
controller. To the end of the I 2 to send C transfers a STOP signal and processing the
data to trigger is activated at Channel 5, the Transmission Complete interrupt. After
configuring the processor is in an infinite loop in the periodic data transfer is
initiated , For this purpose, the data counter the DMA channels are set and then sent a
START signal. If the I START signal transmitted successfully, then 2 C, an interrupt is
and I2C_Send7bitAddress (...) sent the slave address (here . 0x4F, for the
function by 1 to the left shifted) . After transmitting the slave address, an interrupt is
triggered and switched to send the data of the DMA Channel 4 again If all bytes have
been sent, the BTF flag (-> interrupt ) is set. In the following, a REPEAT-START signal
to be sent and the data is read in the subsequent read transfer. For this purpose DMA
Channel 5 is activated and then a new transfer with I2C_GenerateSTART
(...) started. The variable i2cDirectionWrite is to 0 set to go later in the branch,
which sends the address with a set R / W bit. I2C_DMALastTransferCmd
(...) know that I 2 C module to send a NACK after the last byte. If the data reception
completed of the predetermined number of bytes, the Transfer-Complete interrupt the
DMA channel 5 is raised. With I2C_GenerateSTOP (...) a STOP signal is
transmitted and then the two DMA channels are disabled. In the interrupt handler other
functions can not be called / flags are set to launch a processing of the received data
(eg, sending a CAN message). A complete CoIDE project including CAN transfer
can here be
downloaded.