一、STM32数据类型
typedef signed long s32;
typedef signed short s16;
typedef signed char s8;
typedef signed long const sc32; /\* Read Only \*/
typedef signed short const sc16; /\* Read Only \*/
typedef signed char const sc8; /\* Read Only \*/
typedef volatile signed long vs32;
typedef volatile signed short vs16;
typedef volatile signed char vs8;
typedef volatile signed long const vsc32; /\* Read Only \*/
typedef volatile signed short const vsc16; /\* Read Only \*/
typedef volatile signed char const vsc8; /\* Read Only \*/
typedef unsigned long u32;
typedef unsigned short u16;
typedef unsigned char u8;
typedef unsigned long const uc32; /\* Read Only \*/
typedef unsigned short const uc16; /\* Read Only \*/
typedef unsigned char const uc8; /\* Read Only \*/
typedef volatile unsigned long vu32;
typedef volatile unsigned short vu16;
typedef volatile unsigned char vu8;
typedef volatile unsigned long const vuc32; /\* Read Only \*/
typedef volatile unsigned short const vuc16; /\* Read Only \*/
typedef volatile unsigned char const vuc8; /\* Read Only \*/
二、 GPIO配置
(一) GPIO配置模式
1. 输入浮空
2. 输入上拉
3. 输入下拉
4. 模拟输入
5. 开漏输出
6. 推挽输出
7. 推挽式复用功能
8. 开漏式复用功能
(二)GPIO配置表
(三)STM32输出模式表
(四)GPIO配置方式
- GPIO模式
GPIO_Mode_AIN 模拟输入
GPIO_Mode_IN_FLOATING 浮空输入
GPIO_Mode_IPD 下拉输入
GPIO_Mode_IPU 上拉输入
GPIO_Mode_Out_OD 开漏输出
GPIO_Mode_Out_PP 通用推挽输出
GPIO_Mode_AF_OD 复用开漏输出
GPIO_Mode_AF_PP 复用推挽输出
2.GPIO速度
GPIO_Speed_ 10MHZ
GPIO_Speed_20MHZ
GPIO_Speed_50MHZ
3.初始化GPIO步骤
1. 初始化端口时钟
2. 端口配置
3. 模式配置
4. 速度配置
5. 初始化
例程:
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_SetBits(GPIOC,GPIO_Pin_13);
三、中断配置
(一)中断分类
\qquad
STM32中断类型分为:内核异常中断、外部中断。部分中断向量表如下:
\qquad
-3到6标黑区域为内核异常,内核异常不能够被打断,不能被设置优先级(也就是说优先级是凌驾于外部中断之上的)。常见的内核异常有以下几种:复位(reset),不可屏蔽中断(NMI),硬错误(Hardfault)……
\qquad
自第7开始及以后为外部中断。外部中断包括:线中断,定时器中断,IIC,SPI等所有外设中断,可配置优先级:抢占式优先级、响应式优先级。
\qquad
抢占式优先级:抢占优先级比较霸道,一言不和就插队。抢占优先级高的,能够打断优先级低的任务,等优先级较高的任务执行完毕后,再回来继续执行之前的任务。所以当存在多个抢占优先级不同的任务时,很有可能会产生任务的嵌套。
\qquad
响应式优先级:响应优先级则稍微谦逊些,比较有礼貌。响应优先级又被称为次优先级,若两个任务的抢占式优先级一样,那么响应优先级较高的任务则先执行,且在执行的同时不能被下一个响应优先级更高的任务打断,所以我说它比较有有礼貌。
(二) 中断控制器(NVIC)
\qquad
NVIC负责出SYSTICK之外的中断,标准库提供一套通过NVIC来控制中断的API,NVIC_Init()函数定义并且填充的NVIC_InitTypedef结构体如下:
NVIC_IRQChannel 需要配置的中断向量
NVIC_IRQChannelCmd 使能或者关闭相应中断向量的中断响应
NVIC_IRQChannelPreemptionPriority 配置相应中断向量的抢占优先级
NVIC_IRQChannelSubPriority 配置相应中断的响应优先级
\qquad
NVIC只可以配置16种中断向量的优先级,其抢占优先级和响应优先级都用一个4位的数字来决定。在库函数中,将其分为了5种不同的分配方式:
\qquad
第0组:所有的4位都有来表示响应优先级,能够配置16种不同的响应优先级。中断优先级则都相同。(24=16)PreemptionPriority(0),SubPriority(0-15)
\qquad
第1组:最高一位用来配置抢占优先级,剩余三位用来表示响应优先级。那么就有两种不同的抢占优先级(0和1)和8种不同的响应优先级(0~7)。(21=2,23=8)PreemptionPriority(0-1),SubPriority(0-7)
\qquad
第2组:高两位用来配置抢占优先级,低位用来配置响应优先级。那么两种优先级就各有4种。(22=4,22=4)PreemptionPriority(0-3),SubPriority(0-3)
\qquad
第3组:高三位用来配置抢占优先级,低位用来配置响应优先级。有8种抢占优先级和2种相应优先级。(23=8,21=2)PreemptionPriority(0-7),SubPriority(0-1)
\qquad
第4组:所有位都用来配置抢占优先级,即有16种抢占优先级,没有响应属性。(24=16)PreemptionPriority(0-15),SubPriority(0)
\qquad
数值越小,优先级越高。
\qquad
配置API如下:
NVIC_PriorityGroupConfig();
\qquad
输入以下一个参数,代表不同的分配方式:
NVIC_PriorityGroup_0 第0组
NVIC_PriorityGroup_1 第1组
NVIC_PriorityGroup_2 第2组
NVIC_PriorityGroup_3 第3组
NVIC_PriorityGroup_4 第4组
(三) NVIC配置步骤
- 使能某个外设中断,由外设相关中断使能位来控制
- 设置中断优先级分组,初始化NVIC_InitTypeDef结构体,设置抢占式优先级和响应式优先级。
\qquad
a) NVIC_IRQChannel:中断源设置,不同外设中断,中断源不一样,中断源名字不同,由
\qquad
\qquad
STM32库函数提供,中断源名字见stm32f1xx.h文件。
\qquad
b) NVIC_IRQChannelPreemptionPriority:抢占式优先级。
\qquad
c) NVIC_IRQChannelSubPriority:响应式优先级。
\qquad
d) NVIC_IRQChannelCmd:中断使能/失能。ENABLE/DISABLE。 - 初始化NVIC寄存器:NVIC_Init();
- 编写中断服务函数。(中断服务函数名固定,函数名见stm32f1xx_it.c文件)
例程:
TIM_ITConfig(TIM1,TIM_IT_Update,ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM1, ENABLE);
四、外部(EXTI)中断
(一)中断控制器(EXTI)
1. EXTI介绍
\qquad
STM32F103每个I/O口都可以作为外部中断的中断输入口,中断控制器支持19个外部中断/事件请求。STM32F4外部中断控制器包含23个产生事件/中断请求的边沿检测器,每根输入线都可以进行单独设置,用来选择类型(中断或事件)和相应的触发事件(上升沿触发、下降沿触发或者边沿触发)。每个中断设置有状态位,每个中断/事件可独立触发或者屏蔽设置,STM32的19(23)个外部中断为:
EXTI线 0-15 | 对应外部IO口的输入中断 |
---|
EXTI线16 | 连接到PVD输出 | EXTI线17 | 连接到RTC闹钟事件 | EXTI线18 | 连接到USB、OTG、FS唤醒事件 | EXTI线19 | 连接到以太网唤醒事件 | EXTI线20 | 连接到USB OTG HS(FS中配置)唤醒事件 | EXTI线21 | 连接到RTC入侵和时间戳事件 | EXTI线22 | 连接到RTC唤醒事件 |
\qquad
注:线18 STM32F1不具有OUG、FS唤醒事件。
\qquad
线19、20、21、22 STM32F4包含,STM32F1不包含。
2. GPIO与EXTI连接方式
\qquad
STM32的所有GPIO口引入到EXTI外部中断线上,经过配置所有的IO口均能够触发中断。GPIO与EXTI连接方式如下:
\qquad
由图可知每条中断线对应从PAx – PGx(PIx)共7/9个GPIO,也就是说,同一时刻每一个中断线智能响应一个GPIO端口中断,不能够同时响应所有端口中断,但是可以分时复用。
(二)GPIO与EXTI配置
- STM32的GPIO与EXTI映射关系函数为:
void GPIO_EXTILineConfig(uint_8 GPIO_PorstSource,uint_8 GPIO_PinSource)
- 中断线上中断初始化函数为:
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct)
- EXTI_InitTypeDef成员变量:
uint32_t EXTI_Line 中断线标号
EXTIMode_TypeDef EXTI_Mode 中断模式
EXTITrigger_TypeDef EXTI_Trigger 触发方式
FunctionlState EXTI_LineCmd 使能中断线
\qquad
a) 中断线标号参数:
EXTI_Line0 – EXTI_Line15
\qquad
b) 中断模式参数:
EXTI_Mode_Interrupt 中断
EXTI_Mode_Event 事件
\qquad
c)外部中断触发方式参数
EXTI_Trigger_Rising 上升沿触发
EXTI_Trigger_Falling 下降沿触发
EXTI_Trigger_Rising_Falling 上升沿或者下降沿触发
- EXTI中断服务函数说明:
中断服务函数存放于启动文件中外部中断函数名如下
EXTIO_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
EXTI9_5_IRQHandler
EXTI15_10_IRQHandler
\qquad
0-4中断线为独立函数,中断线5-9共用函数EXTI9_5_IRQHandler,中断线10-15共用函数 EXTI15_10_IRQHandler。
(三) 外部中断(EXTI)配置步骤
- 初始化IO口作为输入。
- 开启AFIO时钟。
- 设置IO口与中断线的映射关系。
- 初始化线上中断,设置触发条件等。
- 配置中断分组(NVIC),并使能中断。
- 编写中断服务函数。
五、定时器配置
(一)定时器分类
1、 基本定时器--------TIM6、TIM7.
2、 通用定时器--------TIM2—TIM5、TIM9------—TIM14.
3、 高级定时器--------TIM1、TIM8.
\qquad
注:TIM2、TIM3、TIM4、TIM5、TIM6、TIM7、TIM12、TIM13、TIM14挂载总线为APB1。
\qquad
TIM1、TIM8、TIM9、TIM10、TIM11、TIM15、TIM16、TIM17挂载总线为APB2。
(二)通用定时器TIMx功能介绍
- 16位/32位(TIM2 & TIM5)向上、向下、向上/向下自动装载计数器(TIMx_CNT),TIM9 – TIM14 仅仅支持向上计数。
- 16位预分频器(TIMX_PSC),计数器、时钟频率分频系数:1~65535.
- 4个独立通道(TIMx_CH1-4,TIM9-TIM14最多2个通道)。通道功能:
\qquad
a) 输入捕获
\qquad
b) 输出比较
\qquad
c) PWM生成 :边沿或中间对齐模式,TIM9-TIM14不支持中间对齐模式。 - 可使用外部信号(TIMx_ETR)控制定时器,且可以实现多个定时器互连(可以用1个定时器控制另一个定时器)的同步电路
- 发生如下事件产生中断/DMA请求(TIM9-TIM14不支持DMA)
\qquad
a) 更新:计数器向上溢出或向下溢出,计数器初始化(软件、内部、外部触发)
\qquad
b) 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
\qquad
c) 输入捕获
\qquad
d) 输出比较 - 支持针对定位的增量(正交)编码器和霍尔传感器电路(TIM9-TIM14不支持)
- 触发输入作为外部时钟或者周期的电流管理(TIM9-TIM14不支持)
(三) 时基单元
- 计数器寄存器 (TIMx_CNT)
- 预分频器寄存器 (TIMx_PSC)
- 自动装载寄存器 (TIMx_ARR)
\qquad
计数器由预分频器的时钟输出CK_CNT驱动,仅当设置了计数器TIMx_CR1寄存器中的计数器使能位(CEN)时,CK_CNT才有效。
\qquad
预分频器可以将计数器的时钟频率按1到65536之间的任意值分频,它是基于一个(在TIMx_PSC寄存器中的)16位寄存器控制的16位计数器,将定时器时钟CK_PSC分频为CK_CNT。
\qquad
自动装载寄存器是预先装载的,写或读自动重装载寄存器将访问预装载寄存器,当计数器达到溢出条件(向下计数时的下溢条件)并当TIMx_CR1寄存器中的UDIS位等于'0'时,产生更新事件。
(四) 定时器中断配置方法
- 定时中断溢出时间计算:
\qquad
Tout:定时器溢出时间;TIMx_Arr:定时器初值;
\qquad
TIMx_PSC:定时器预分频数;Tclk:定时器时钟频率
\qquad
通用定时器TIMx挂载在总线APB1下,总线APB1时钟由总线AHB时钟分频得到,一般情况AHB会设置为最高72MHZ,APB1时钟则由AHB时钟2分频,为最高36MHZ,由于APB1预分频系数不为1,通用定时器TIMx时钟为APB1时钟的2倍,72MHZ。
\qquad
TIMx_Arr TIMx_PSC 初值不要超过65536。
- 配置定时器中断步骤:
\qquad
(1) 使能定时器时钟。
RCC_APB1PeriphClockCmd();
\qquad
(2)初始化定时器、配置TIMx_Arr、TIMx_PSC。
TIM_TimeBaseInit();
\qquad
(3)开启定时器中断,配置NVIC。
TIM_ITConfig();
NVIC_Init();
\qquad
(4)使能定时器。
TIM_Cmd();
\qquad
(5)编写中断服务函数。
例程:
void TIM2_Init(unsigned short TIM2_Arr,unsigned short TIM2_PSC)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseStructure.TIM_Prescaler = TIM2_PSC;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = TIM2_Arr;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM2, ENABLE);
}
void TIM2_IRQHandler()
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
{
}
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
六、PWM配置
(一)PWM定时器产生
\qquad
STM32的定时器除TIM6、TIM7其它的定时器都可以用来产生PWM输出,高级定时器TIM1、TIM8可以同时产生7路PWM。通用定时器可以同时产生4路PWM输出(TIM9-TIM14最多产生2路PWM)。基本定时器TIM6、TIM7不能产生PWM。
\qquad
PWM为对外输出脉宽可调(占空比调节)的防波信号,信号频率由自动重装初值寄存器ARR的值决定,占空比由比较寄存器CCR的值决定。
\qquad
PWM的输出频率不变,改变CRR寄存器的值,将导致PWM输出信号的占空比发生改变。
\qquad
占空比:一个周期内高电平时间与周期的比值。
\qquad
PWM的输出比较模式总共有8种,具体由寄存器CCMRx的位0CxM[2:0]配置,常用模式有PWM1与PWM2。两种模式用法差不多,区别在于输出电平极性不同。
模式 | 计数器CNT计数方式 | 说明 |
---|
PWM1 | 递增 | CNT<CCR,通道CH输出高电平 | | 递减 | CNT>=CCR,通道CH输出低电平 | | | | PWM2 | 递增 | CNT<CCR,通道CH输出低电平 | | 递减 | CNT>=CCCR,通道CH输出高电平 |
(二) PWM模式
\qquad
PWM根据计数器CNT计数方式,可以分为边沿对齐模式和中心对齐模式:
- PWM边沿对齐模式:
\qquad
当TIMx_CR1寄存器中的DIR位为低时执行递增计数,计数器CNT从0计数到自动重载值(TIMx_ARR寄存器值),然后重新从0开始计数并且生成计数器上溢事件。
\qquad
参考PWM1。只要TIMx_CNT < TIMx_CCRx,PWM参考信号OCxREF便为高电平,否则为低电平.如果TIMx_CCRx中的比较值大于自动重载值(TIMx_ARR中),则OCxREF保持为"1",如果比较值为0,则保持为"0"。
\qquad
边沿对齐模式PWM波形图如下:
\qquad
当TIMx_CR1寄存器中的DIR位为高时执行递减计数,计数器CNT从自动重载值(TIMx_ARR寄存器值)递减计数到0,然后重新从TIMx_ARR开始计数并且生成计数器下溢事件。
\qquad
参考PWM1。只要TIMx_CNT >TIMx_CCRx,PWM参考信号OCxREF便为低电平,否则为高电平.如果TIMx_CCRx中的比较值大于自动重载值(TIMx_ARR中),则OCxREF保持为"1"。此模式无法产生0%的PWM波形。
- PWM中心对齐模式:
\qquad
在中心对齐模式下,计数器CNT是工作做递增/递减模式下,开始时,计数器CNT从0开始计数到自动重载值减1(ARR-1),生成计数器上溢事件;然后从自动重载值开始向下计数到1并生成计数器下溢事件。之后从0开始重新计数。
\qquad
中心对齐模式PWM波形图:
\qquad
参考ARR=8,CCRx=4.第一阶段计数器CNT工作在递增计数方式,从0开始计数,当TIMx_CNT < TIMx_CCRx时,PWM参考信号OCxREF为高电平,当TIMx_CNT >=TIMx_CCRx时,PWM参考信号OCxREF为低电平。第二阶段计数器CNT工作在递减计数方式,从ARR开始递减计数,当TIMx_CNT > TIMx_CCRx时,PWM参考信号OCxREF为低电平,当TIMx_CNT <= TIMx_CCRx时,PWM参考信号OCxREF为高电平。
\qquad
中心对齐模式又分为中心对齐模式1/2/3三种,由寄存器CR1位CMS[1:0]配置。
中心对齐模式 | 比较中断标志位CCxIF置1时间 |
---|
模式1 | CNT递减计数 | 模式2 | CNT递增计数 | 模式3 | CNT递减/递增计数 |
(三) PWM输出参数
\qquad
每个通用定时器有4路PWM输出通道(TIM9-TIM14只有2路),TIM_OcxInit中x可以为1/2/3/4;TIM_OCInitTypeDef结构体成员变量如下:
typedef struct
{
uint16_t TIM_OCMode;
uint16_t TIM_OutputState;
uint16_t TIM_OutputNState;
uint16_t TIM_Pulse;
uint16_t TIM_OCPolarity;
uint16_t TIM_OCNPolarity;
uint16_t TIM_OCIdleState;
uint16_t TIM_OCNIdleState;
} TIM_OCInitTypeDef;
\qquad
常用PWM模式所需成员变量:
- TIM_OCMode:比较输出模式选择,共8中,常用为PWM1和PWM2.
- IM_OutputState:比较输出使能,用来使能PWM输出到GPIO端口.
- TIM_OCPolarity:输出极性,用来设定输出通道电平极性,是高电平还是低电平。
TIM_OutputNState、TIM_OCNPolarity、TIM_OCIdleState和TIM_OCNIdleState为高级定时器配置使用。
(四) 通用定时器PWM输出配置步骤
-
设置定时器及端口时钟,设置引脚复用映射。 -
初始化定时器参数,包含自动重装值,分频系数,计数方式等。 -
初始化PWM输出参数,包含PWM模式、输出极性、使能等。 -
开启定时器。 -
修改TIMx_CRRx的值控制占空比。
例程:
void TIM3_PWM_Init(unsigned short TIM3_Arr,unsigned short TIM3_PSC)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Period = TIM3_Arr;
TIM_TimeBaseStructure.TIM_Prescaler =TIM3_PSC;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_Cmd(TIM3, ENABLE);
}
七、USART通信
(一)通信概念以及分类
- 串行通信:
\qquad
串行通信是指使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。只需要少数几条线就可以在系统之间交换信息。一般适用于计算机与计算机之间、计算机与外设之间的远距离通信。
\qquad
特点:传输线少,长距离传送成本低可以利用电话网等设备。
\qquad
缺点:数据的传送控制比并行通信复杂。
- 并行通信:
\qquad
并行通信将数据字节的各位用多条数据线同时进行传送,通常为8位、16位、32位等一起传输。
\qquad
\qquad
特点:控制简单,传输速度快。
\qquad
\qquad
缺点:传输线较多,长距离传送成本高,接收端同时接收存在困难,抗干扰能力差。
- 异步通信:
\qquad
异步通信指通信的发送与接收设备使用各自的时钟控制数据的发送与接收过程。为使双方的收发协调,要求发送设备与接收设备的时钟尽可能一致。
\qquad
异步通信是以字符(构成帧)为单位进行传输,字符与字符之间的时间间隔任意,但是每个字符中的各位以固定时间传送,即:字符之间不一定有"位间隔"的整数倍的关系。但是同一字符内的各位之间距离为"位间隔"的整数倍。
\qquad
异步通信的特点:不要求收发双方时钟的严格一致,实现容易,设备开销较小,但每个字符要附加2~3位用于起止位,各帧之间还有间隔,因此传输效率不高。
-
同步通信
\qquad
同步通信时要建立发送方时钟对接收方时钟的直接控制,使双方达到完全同步。此时,传输数据的位之间的距离均为"位间隔"的整数倍,同时传送的字符间不留间隙,即保持位同步关系,也保持字符同步关系。发送方对接收方的同步可以通过两种方法实现。 -
单、半、全双工通信 (1)单工通信:
\qquad
单工是指数据传输仅能沿一个方向,不能实现反向传输。
\qquad
(2)半双工通信
\qquad
\qquad
半双工是指数据传输可以沿两个方向,但需要分时进行。
\qquad
(3) 全双工通信
\qquad
\qquad
全双工是指数据可以同时进行双向传输。
6. 通信速率
\qquad
通信速率,通常以比特率(Bitrate)来表示。比特率是每秒钟传输二进制代码的位数,单位是:位/秒(bps)。如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位、1个停止位、8个数据位),这时的比特率为:
10位×240个/秒 = 2400 bps
(二)USART结构
\qquad
USART功能取决于结构,结构图如下:
- 模块1功能引脚介绍:
\qquad
(1) TX:发送数据输出引脚;
\qquad
(2)RX:接收数据输入引脚;
\qquad
(3) SW_RX:数据接收引脚,用于单线和智能卡模式,内部引脚,无外部引脚;
\qquad
(4)nRTS:请求以发送(Request To Send),n表示低电平有效;使能RTS流控制(只适用于硬件控制),当USART接收器转备好接收新数据时会将nRTS变成低电平;当接收寄存器满时,nRTS将设置为高电平;
\qquad
(5)nCTS:清除以发送(Clear To Send),n表示低电平有效;使能CTS流控制(只适用于硬件控制),发送器在发送下一帧数据之前会检测nCTS引脚,如果为低电平,便是可以发送数据,为高电平则在发送完当前数据帧之后停止发送。
\qquad
(6)SCLK:发送器时钟输出引脚(仅用于同步模式)。
- 模块2数据寄存器:
\qquad
USART数据寄存器(USART_DR)只有低9位有效,第9位数据是否有效取决于USART控制寄存器1(USART_CR1)的M位位置,当M位为0时表示8位数据字长,当M位为1时表示9位数据字长,一般采用8位。USART_DR包含已发送数据或者接收到的数据。
\qquad
USART_DR包含用于发送TDR与专门用于接收的可读RDR寄存器。执行发送操作时,往USART_DR写入数据会自动存储在TDR内;执行读取操作时,向USART_DR读取数据会自动提取RDR数据。
\qquad
TDR与RDR介于系统总线和移位寄存器之间。串行通信将数据一个位一个位进行传输,发送时将TDR寄存器内容转移到发送移位寄存器,然后把移位寄存器书记每一位发送出去,接收时将接收到的每一位顺序保存在移位寄存器内,然后转移到RDR。
\qquad
USART支持DMA传输。
- 模块3控制器:
\qquad
USART有发送器、接收器、唤醒单元、中断控制等。使用USART之前需要向USART_CR1寄存器的UE位置1使能USART。
\qquad
(1) 发送器
\qquad
发送器可发送8位或者9位数据,取决于M位的状态。发送时能位(TE)为1时,发送移位寄存器中的数据在TX引脚输出,同步通信中,相应的时钟脉冲在SCLK引脚输出。
\qquad
(2) 接收器
\qquad
USART_CR1寄存器的RE位置1,使能USART接收,使能接收器在RX线开始搜索起始位。确定起始位后RX线电平状态将数据存放在接收移位寄存器内。接收完成后将移位寄存器数据转移到RDR内,并且将USART_SR寄存器的RXNE置1,同时如果USART_CR2寄存器的RXNEIE置1,将产生中断。
\qquad
(3) 中断控制
\qquad
USART有多个中断请求
中断事件 | 事件标志 | 使能控制位 |
---|
发送数据寄存器为空 | TXE | TXEIE | CTS标志 | CTS | CTSE | 发送完成 | TC | TCIE | 准备好读取接收到的数据 | RXNE | RXNEIE | 检测到上溢错误 | ORE | | 检测到空闲线路 | IDLE | IDLEIE | 奇偶校验位错误 | PE | PEIE | 断路标志 | LBD | LBDIE | 多缓冲区通信中的噪声标志、上溢错误和帧错误 | NF\ORE\FE | EIE |
\qquad
USART中断事件中断向量:
- 模块4小数波特率生成
接收器和发送器(Rx&&Tx)的波特率均设置为相同值,波特率计算公式:
\qquad
fPCLK为USART时钟,OVER8为USART_CR1寄存器的OVER8位对应的值,USARTDIV是一个存放在波特率寄存器(USART_BRR)的一个无符号定点数。其中DIV_Mantissa[11:0]位定义USARTDIV的整数部分,DIV_Fraction[3:0]位定义USARTDIV的小数部分DIV_Fraction[3]位只有在OVER8位为0时有效,否则必须清零。
(三)USART串口参数
\qquad
初始化串口参数,包含波特率、字长、奇偶校验位等参数;USART_InitTypeDef结构体成员变量如下:
typedef struct
{
uint32_t USART_BaudRate;
uint16_t USART_WordLength;
uint16_t USART_StopBits;
uint16_t USART_Parity;
uint16_t USART_Mode;
uint16_t USART_HardwareFlowControl;
} USART_InitTypeDef;
-
结构体成员变量说明:
\qquad
(1) USART_BaudRate:波特率设置。常用波特率为4800、9600、115200等。标准库函数会根据设定值计算得到USARTDIV的值,并设置USART_BRR寄存器值。
\qquad
(2) USART_WordLength:数据帧字长。可以选择为8位或者9位,通过USART_CR1寄存器的M位的值决定。如果没有使能奇偶校验控制,一般使用8数据位,如果是能了奇偶校验位一般设置为9数据校验位。
\qquad
(3)USART_StopBits:停止位设置。可选择0.5个、1个、1.5个、2个停止位,它设定USART_CR2寄存器的STOP[1:0]位的值,一般选择1个停止位。
\qquad
(4)USART_Parity:奇偶校验控制选择。可以选择USART_Parity_No(无校验)、USART_Parity_Even(偶校验)、USART_Parity_Odd(奇校验)。它设定了USART_CR1寄存器的PCE位和PS位的值。
\qquad
(5) USART_Mode:USART模式选择。可以为USART_Mode_Rx和USART_Mode_Tx,允许使用逻辑或运算选择两个,它设定USART_CA1寄存器的RE位和TE位。
\qquad
(6) USART_HardwareFlowControl:硬件流控制选择。只有在硬件流控制模式才有效,可以选择:
\qquad
\qquad
a) 无硬件流:USART_HardwareFlowControl_None、
\qquad
\qquad
b) RTS控制:USART_HardwareFlowControl_RTS
\qquad
\qquad
c) CTS控制:USART_HardwareFlowControl_CTS
\qquad
\qquad
d) RTS和CTS控制:USART_HardwareFlowControl_RTS_CTS。 -
串口中断类型:
\qquad
串口中断类型可以在stm32f10x_usart.h/ stm32f40x_usart.h/ stm32f70x_usart.h文件中找到:
#define USART_IT_PE ((uint16_t)0x0028)
#define USART_IT_TXE ((uint16_t)0x0727)
#define USART_IT_TC ((uint16_t)0x0626)
#define USART_IT_RXNE ((uint16_t)0x0525)
#define USART_IT_IDLE ((uint16_t)0x0424)
#define USART_IT_LBD ((uint16_t)0x0846)
#define USART_IT_CTS ((uint16_t)0x096A)
#define USART_IT_ERR ((uint16_t)0x0060)
#define USART_IT_ORE ((uint16_t)0x0360)
#define USART_IT_NE ((uint16_t)0x0260)
#define USART_IT_FE ((uint16_t)0x0160)
(四) USART串口配置步骤:
- 使能串口时钟以及GPIO端口时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
- 设置引脚复用映射(视情况而定);
- GPIO端口模式设置,设置串口对应的引脚为复用功能;
GPIO_InitStrue.GPIO_Mode = GPIO_Mode_AF_PP;
- 初始化串口参数;
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct)
- 使能串口;
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState)
- 设置串口中断类型并使能;
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState)
- 设置串口中断优先级,使能串口中断通道;
- 编写串口中断服务函数
例程:
void Usart2_Init(unsigned long Baud)
{
GPIO_InitTypeDef GPIO_InitStrue;
USART_InitTypeDef USART_InitStrue;
NVIC_InitTypeDef NVIC_InitStrue;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
GPIO_InitStrue.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStrue.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStrue.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStrue);
GPIO_InitStrue.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStrue.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStrue.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStrue);
USART_InitStrue.USART_BaudRate = Baud;
USART_InitStrue.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_InitStrue.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;
USART_InitStrue.USART_Parity = USART_Parity_No;
USART_InitStrue.USART_StopBits = USART_StopBits_1;
USART_InitStrue.USART_WordLength = USART_WordLength_8b;
USART_Init(USART2,&USART_InitStrue);
USART_ClearFlag(USART2, USART_FLAG_TC);
USART_Cmd(USART2,ENABLE);
USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);
NVIC_InitStrue.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStrue.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStrue.NVIC_IRQChannelPreemptionPriority = USART2_Preemption_Priority;
NVIC_InitStrue.NVIC_IRQChannelSubPriority = USART2_Sub_Priority;
NVIC_Init(&NVIC_InitStrue);
}
void USART2_IRQHandler(void)
{
unsigned char Date;
if(USART_GetITStatus(USART2,USART_IT_RXNE))
{
Date = USART_ReceiveData(USART2);
USART_SendData(USART2,Date);
}
}
八、 看门狗(WDG)
(一)看门狗简介
\qquad
STM32芯片带有独立看门狗IWDG与窗口看门狗WWDG。看门狗用于检测并解决由软件错误导致的故障。独立看门狗为12位递减计数器,当计数器从某一个值递减到0时(看门狗激活状态下),系统会产生一次复位。在计数器递减到0之前刷新了计数器的值,系统则不会产生复位,刷新计数器值得过程称为喂狗。窗口看门狗与独立看门狗类似,为7位递减计数器向下递减计数,当减到固定值0x40时仍未喂狗,则MCU产生复位,(0x40为窗口下限,为固定值,不能改变)。窗口看门狗的计数器的值减到某一数之前喂狗也将产生复位,这个值为窗口上限,上限值可以用户设置。窗口看门狗计数器的值必须在上窗口与下窗口之间才能喂狗。看门狗功能由VDD电压域供电,在停止模式与待机模式下仍能供电。窗口看门狗时序图如下:
\qquad
T[6:0]是窗口控制寄存器(WWDG_CR)的低7位,W[6:0]是窗口配置寄存器(WWDG_CFR)低7位。T[6:0]就是窗口看门狗的计数器值,而W[6:0]是窗口看门狗的上窗口,下窗口是固定值0X40。当窗口看门狗的计数器在上窗口值之外或者低于下窗口值被刷新都会产生复位。
\qquad
上窗口值(W[6:0])是用户自己设定,根据需求设定窗口值,但是要保证窗口值大于0x40,否则窗口不存在。窗口看门狗通常被用来监测,由外部干扰或者不可预见的逻辑条件造成的应用程序背离正常运行的序列而产生的软件故障。
(二) 看门狗结构框图
- 独立看门狗
独立看门狗结构框图如下:
\qquad
(1)IWDG时钟:
\qquad
IWDG由其专用低速时钟(LSI)驱动,在主时钟产生故障时仍能正常工作。LSI频率一般在30-60KHZ,通常选择40KHZ。LSI时钟频率不是非常准确,独立看门狗适用于对时间精度要求较低场合。
\qquad
(2) 预分频寄存器:
\qquad
LSI时钟并不是直接提供给计数器时钟,通过8位预分频寄存器IWDG_PR分频后输入给计数器时钟,可以设置IWDG_PR寄存器设置分频因子,分频因子可以为4、8、16、32、64、128、256。
\qquad
分频后的计数器时钟为CK_CNT = 40/4*2PRE, PRE为预分频系数(0-6),4*2^PRE大小为(4、8、16、32、64、128、256)。每经过一个计数器时钟,计数器就减1。
\qquad
(3)计数器:
\qquad
独立看门狗为12位递减计数器,计数最大为0xFFF。
\qquad
(4)重装载寄存器:
\qquad
重装载寄存器为12位寄存器,装载着要刷新到计数器的值,值得大小决定独立看门狗溢出时间(ms):
\qquad
PRE:预分频系数(0-6);RLR:重装载寄存器的值;40:独立看门狗时钟。
\qquad
(5) 密钥寄存器:
\qquad
密钥寄存器(IWDG_KR)为独立看门狗的控制寄存器写入不同的值有不同的控制效果:
\qquad
a)0x5555:IWDG_PR,与IWDG_RLR寄存器具有写访问保护。若要修改寄存器,必须首先对密钥寄存器写0x5555,写入其他值将重启写保护。
\qquad
b)0xAAAA:把IWDG_RLR寄存器内值重装载到计数器中。
\qquad
c) 0xCCCC:启动IWDG功能。软启动,开启看门狗之后,无法关闭,只有复位可以关闭。
\qquad
(6)状态寄存器:
\qquad
状态寄存器IWDG_SR只有位0:PVU和位1:RVU有效,有硬件操作。
\qquad
RVU:看门狗计数器重装载值更新,硬件置1表示重装载值的更新正在进行中,更新完毕由硬件清零。
\qquad
PVU:看门狗预分频值更新,硬件置1表示预分频值得更新正在进行中,更新完毕由硬件清零。
\qquad
当PVU/RVU等于0时,才可以更新重装载寄存器/预分频寄存器。
- 窗口看门狗:
窗口看门狗结构框图如下:
\qquad
(1) WWDG时钟:
\qquad
窗口看门狗时钟来自PCLK1总线上,由RCC时钟控制器开启。APB1时钟最大为42M。
\qquad
(2)WDG预分频器:
\qquad
PCLK1时钟并不是直接提供给看门狗计数器时钟,通过WDG预分频寄存器分频后输入给计数器时钟,可以设置WWDG_CFR寄存器位8:7WDGTB[1:0]设置分频因子,分频因子可以为0、1、2、3。
\qquad
分频后的计数器时钟为CK_CNT =PCLK1/4096/(2^ WDGTB),PCLK1等于APB1时钟,WDGTB为分频因子(0-3)2^WDGTB就是1、2、4、8与库函数中的分频参数对应。每经过一个计数器时钟,计数器就减1。
\qquad
(3)计数器:
\qquad
窗口看门狗为7位递减计数器,计数最大值为0X7F,存放在控制寄存器WWDG_CR中的6:0位即T[6:0]。T6位变成0时,即0X40 -> 0X3F时产生看门狗复位。因此计数器的值只能在0X40-0X7F之间。实际上用来计数的是T[5:0]位。当计数器递减到0X40时,还不会马上产生复位,如果提前唤醒中断,窗口看门狗配置寄存器(WWDG_CFR)位9 EWI置1,则产生提前唤醒中断,在产生复位前一刻提醒需要进行喂狗,否则进行复位。通常在唤醒中断内向WWDG_CR重新写入计数器的值。注意:进入中断后,必须在不大于1个窗口周期内重新写WWDG_CR,否则看门狗将产生复位。
\qquad
如果不使用提前唤醒中断来喂狗,则需要计算看门狗超时时间,公式如下:
\qquad
Twwdg :看门狗超时时间,单位ms;
\qquad
PCLK1 :APB1时钟频率,最大42MHZ;
\qquad
WDGTB :看门狗预分频系数;
\qquad
T[5:0] :看门狗计数器低6位;
\qquad
4096 :数据手册规定;
\qquad
PCLK1为42MHZ时,最小-最大超时时间表如下:
WDGTB | 最小超时(us)T[5:0]=0X00 | 最大超时(ms)T[5:0]=0X3F |
---|
0 | 97.52 | 6.24 | 1 | 195.05 | 12.48 | 2 | 390.10 | 24.97 | 3 | 780.19 | 49.93 |
a. T[6:0]是减法计数器为什么这里用(T[5:0]+1):
\qquad
可以把这个超时时间理解为窗口看门狗的喂狗允许时间段,只有在这个时间段喂狗才会阻止复位,因为窗口下限是固定的0X3F,所以只能改变上窗口才能改变这个时间段的长短;而上窗口时间段必须大于0X3F,也就是说窗口看门狗的计数器低六位【5:0】就是上下窗口的差值,这个差值决定看门狗喂狗允许时间段的长短,所以这里用到的是【5:0】而不是【6:0】。
\qquad
纯属个人理解错了别来打我 (?`∧′)。
b. 4096怎么来的:
\qquad
数据手册规定,(・?ω・?)×禁止抬杠×,至于为什么这么规定,我也不知道,以后有人知道为什么这么规定了,可以告诉我,(? ˉ ? ˉ)??°。
\qquad
说一下个人理解<( ‵□′)>豪横:4096应该是4*1024;至于为什么这么理解,可以看一下独立看门狗的公式,独立看门狗的时钟为KHZ,算出来的时间是ms,而窗口看门狗的时钟为MHZ,相当于KKHZ(us),也就是说为了将时间转换为ms,就必须乘以K,但是计算机里面K不是1000,而是1024.
\qquad
(4)看门狗配置寄存器:
\qquad
窗口看门狗在窗口范围内喂狗不产生复位,下窗口为固定值0X40,不可改变。上窗口值由配置寄存器WWDG_CFR的位W[6:0]设置,需要大于0X40小于0X7F。
\qquad
(5) 系统复位信号:
\qquad
当计数器超过配置寄存器内的上窗口设置值或者低于下窗口值,且WDGA位置1,则开启窗口看门狗,产生一个系统复位信号。
(三) 看门狗配置步骤:
- 独立看门狗:
\qquad
(1)开启寄存器访问;
\qquad
(2)设置IWDG预分频系数和重装载值;
\qquad
(3)重载计数器值(喂狗)(给寄存器IWDG_KR写入0XAAAA);
\qquad
(4)开启IWDG(给寄存器IWDG_KR写入0XCCCC);
- 窗口看门狗:
\qquad
(1) 使能WWDG时钟;
\qquad
(2)设置WWDG窗口值和分频系数;
\qquad
(3)开启WWDG中断并分组;
\qquad
(4)设置计数器初始值并使能WWDG;
\qquad
(5)编写WWDG中断服务函数;
例程:
- 独立看门狗:
void IWDG_Init(unsigned char Pre,unsigned char Rlr)
{
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
IWDG_SetPrescaler(Pre);
IWDG_SetReload(Rlr);
IWDG_ReloadCounter();
IWDG_Enable();
}
void IWDG_FeedDog(void)
{
IWDG_ReloadCounter();
}
- 窗口看门狗:
void WWDG_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG,ENABLE);
WWDG_SetWindowValue(0x5f);
WWDG_SetPrescaler(WWDG_Prescaler_8);
NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
WWDG_Enable(0x7f);
WWDG_ClearFlag();
WWDG_EnableIT();
}
void WWDG_IRQHandler(void)
{
WWDG_SetCounter(0x7f);
WWDG_ClearFlag();
}
(四)独立看门狗与窗口看门狗区别
- 内部条件区别
\qquad
(1) 独立看门狗没有中断,窗口看门狗有中断
\qquad
(2) 独立看门狗有硬件软件之分,窗口看门狗只能软件控制
\qquad
(3) 独立看门狗只有下限,窗口看门狗又下限和上限
\qquad
(4) 独立看门狗是12位递减的。窗口看门狗是7位递减的
\qquad
(5) 独立看门狗是用的内部的大约40KHZ RC振荡器,窗口看门狗是用的系统时钟APB1。
- 使用条件区别:
\qquad
(1)独立看门狗:
\qquad
\qquad
a) 出现程序跑飞
\qquad
\qquad
b) 出现死循环
\qquad
\qquad
c) 睡眠不合理时
\qquad
\qquad
d) 休眠不合理时
\qquad
\qquad
e) 外部主晶振坏掉时
\qquad
\qquad
f ) 需要重新复位系统,不保留任何数据
\qquad
\qquad
g) ……
\qquad
(1)窗口看门狗
\qquad
\qquad
a)软件逻辑出现错误
\qquad
\qquad
b) 死机
\qquad
\qquad
c) 死循环
\qquad
\qquad
d) 软件执行不按照预期效果执行
\qquad
\qquad
e) 软件需要重新复位但是需要恢复数据
\qquad
\qquad
f ) ……
(五)看门狗里面的坑
- 当窗口值大于等于计数器的值,无论怎么更改配置的顺序,都是正确的运行结果。
- 当窗口值小于计数器的值,顺序一旦改变就运行错误。
\qquad
经过测试发现,当初始化的顺序不是正常顺序的话,就会把WWDG->SR置1,为什么我也不知道,谁知道片子里面怎么搞的 。
\qquad
你在开启中断就进入中断的,这时你又进行喂狗,就会复位的,因为这时计数器的值>上限窗口的值,所以会复位,所以就会一直出错下去。
\qquad
解决办法是,初始话的时候最后两句是先清除中断标志然后在开启中断,如果你不这么干,那么在初始化的时候很可能把WWDG->SR置位,那么你在开启中断,就会毫不犹豫的进入中断,你在中断重装计数器值得时候,就会产生复位。
|