stm32中的常用函数
目录
一、定义声明类
1.1 预定义
#define LED1(x) do{ x? \ Hal_GPIO_1:\
HAL_GPIO_2);\
}while(0) //如果成立执行第一句,如果不成立执行第二句
1.2 条件编译
#if 类似If,如果则执行下面
#ifdef 判断是否已被定义
#ifndef 判断是否未被定义,未被定义则执行下面的
#endif 结束标志
1.3 extern 声明
放在函数/变量前,表示函数/变量在其它文件定义,本文引用
extern uint16_t g_usart;
extern void delay_us(uint32_t nus);
1.3 typedef 类型别名
typedef 现有类型 新名字
typedef unsigned char uint8_t;
typedef struct
{
-IO uint32_t CRL;
} gpio_ty;
gpio_ty giox;
1.4 结构体
若干基本数据类型集合组成
struct 结构体名
{
成员列表;
} 变量名列表(可选);
struct student
{
int num;
} stu1,stu2;
struct student stu1,stu2;
stu1.num=10;
二、基础函数
2.1 delay类函数
delay_init()函数
用于延时初始化
void delay_init(uint16_tsysclk)
//输入的unint16为mHz,如f407 168mHz,此处输入168即可
delay_us()函数
微秒延迟函数
delay_us(unit32_t nus)
//unit32为延迟的微秒数
delay_ms()函数
毫秒延迟函数
delay_us(unit16_t nms)
//unit16_t为延迟的微秒数
2.2 printf函数
printf()用户调用->C标准库(stdio.h)->fputc()实现输出
stdio.h包括:printf、scanf、putchar、puts等
(1)输出字符串
printf("字符串\r\n"); //\r\n用于换行
(2)输出控制符
uint32 t temp= 10;
printf("%d\r\n", temp);
/*%d是输出控制符,temp是输出参数*/
(3)printf("输出控制符1输出控制符2……",输出参数1,输出参数2,…);
uint32_t templ=5,uint32 t temp2= 10;
printf("%d%x \r\n", templ,temp2);
(4)printf("非输出控制符 输出控制符 非输出控制符",输出参数);
uint32_t temp= 10;
printf("temp= %d 收到over\r\n", temp);
三、GPIO
3.1 硬件
通用输入输出端口,用于采集外部器件信息或控制外部器件工作(输入输出)
STM32工作电压范围:2~3.6V;GPIO识别电压范围:CMOS(VIL -0.3-1.164V逻辑0 VIH1.833-3.6 逻辑1), TTL(FT 兼容5V);GPIO输出电流:单个IO最大25mA
具备快速翻转特性,每次翻转最快只需两个时钟周期(103默认频率72MHz,最快即36MHz)
每个IO口均可用于中断;
支持8中工作模式。
8种工作模式:
1. 输入浮空:输入用,完全浮空,状态不定(空闲IO状态不定);
2. 输入上拉(Pull->GPIO_PULL):输入用,内部上拉,默认是高电平(空闲IO高电平);
3. 输入下拉:输入用,用内部下拉,默认是低电平(空闲IO低电平);
4. 模拟功能:ADC、DAC(专门用于模拟信号输入输出)
5. 开漏输出:软件的IIC的SDC、SCL等(输出0时外部输出0,不能输出高电平,须外部或内部有上来才能输出高电平-F4内部具有上拉无需外部);
6. 推挽输出:驱动能力强,25mA(max)(可输出高低电平,驱动能力强);
7. 开漏式复用功能:片上外设功能(硬件IIC的SDL、SCL)(不用寄存器控制,由片上外设控制,对于F1同样需要外部上拉输出高电平)
8. 推挽式复用功能(Mode->GPIO_MODE_AF_PP):片上外设功能(SPI的SCK、MISO、MOSI引脚)(片上外设控制,可输出高低电平)
3.2 通用外设驱动模型
四步法:
1. 初始化
(1)时钟设置:选择时钟源,开启时钟源,
(2)参数设置:选择八种模式之一
使用函数:
(3)IO设置
(4)中断设置(开中断、设NVIC)
2. 读函数:从外设读取数据
3. 写函数:往外设写入数据
4. 中断服务函数:根据中断标志,处理外设各种中断事务
常用函数:
用于开启GPIO时钟:__HAL_RCC_GPIOx_CLK_ENABLE()
用于初始化GPIO:HAL_GPIO_Init()
用于控制IO输出高低电平:HAL_GPIO_WritenPin()
每次调用IO翻转一次:HAL_GPIO_TogglePin()
读取IO电平:HAL_GPIO_ReadPin()
3.3 例程
1. 控制LED灯
对于一个压降为1.83v的led电路,led正端电压3.3V,串联电阻510欧,电流为(3.3-1.83)/510=2.88ma。
8种模式中共4个可以用于输出控制,由于仅简单控制led,无需引入外部,故选择开漏或者推挽。推挽驱动能力强25ma,可以输出高低电平,故可以选择推挽。而开漏输入对于F1没有外部电路无法上拉高电平,但可为高阻态,仍可实现两边高电平。
(1)创建板机驱动文件
在Driver/BSP下创建驱动文件夹LED,添加驱动文件夹,并双击文件夹将新增的led.c文件增加至文文件夹中。
(2)创建驱动文件
led.h
#ifndef __LED_H
#define __LED_H
#include "./SYSTEM/sys/sys.h"
void led_init(void); //放入头文件,方便调用
#endif
led.c
#include "./BSP/LED/led.h"
void led_init(void)
{
GPIO_InitTypeDef gpio_init_struct; //定义GPIO初始化结构体
__HAL_RCC_GPIOF_CLK_ENABLE(); //开启GPIOF时钟
gpio_init_struct.Pin=GPIO_PIN_9; //选择led角5
gpio_init_struct.Mode=GPIO_MODE_OUTPUT_PP; //选择推挽模式
gpio_init_struct.Speed=GPIO_SPEED_FREQ_LOW; //设置低速
HAL_GPIO_Init(GPIOF, &gpio_init_struct);
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_SET); //设定GPIOF第9个引脚初始输出高电平,即灯灭
}
创建主函数
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
delay_init(168); /* 延时初始化 */
led_init(); /* 初始化LED */
while(1)
{
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_SET); //灯灭
delay_ms(1000);
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_RESET); //灯亮
delay_ms(1000);
}
2. 按键控制LED
读取按键转态,可从浮空、上拉、下拉和模拟输入输出选择,开关只有0和1两种状态,从前三种选即可。对于如下图按键电路,开关按下时接地为低电平,断开时为使其为高电平,故需选择上拉模式。由于浮空状态不确定有可能高有可能低,故选择输入上拉模式。
较流水灯,我们增加按键的定义和初始化,以及按键按下的读取:
#define KEY_PORT0 GPIO_PIN_4 //定义按键引脚
void key_init(void) //按键初始化
{
GPIO_InitTypeDef gpio_init_struct; //定义GPIO初始化结构体
__HAL_RCC_GPIOE_CLK_ENABLE(); //开启GPIOE时钟
gpio_init_struct.Mode=GPIO_MODE_INPUT; //选择输入模式
gpio_init_struct.Pull=GPIO_PULLUP; //选择输入上拉
gpio_init_struct.Pin=KEY_PORT0;
HAL_GPIO_Init(GPIOE, &gpio_init_struct);
}
uint8_t key_scan(void) //按键读取函数
{
if(HAL_GPIO_ReadPin(GPIOE, KEY_PORT0) ==0 )
{
delay_ms(10); //消抖
if(HAL_GPIO_ReadPin(GPIOE, KEY_PORT0) ==0)
{
while(HAL_GPIO_ReadPin(GPIOE, KEY_PORT0) ==0); //若处于按下状态则一直等待
return 1; //按键按下
}
}
return 0; //按键没有按下
}
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
delay_init(168); /* 延时初始化 */
led_init(); /* 初始化LED */
key_init();
while(1)
{
if (key_scan())
{
HAL_GPIO_TogglePin(GPIOF, LED_PORT0);
HAL_GPIO_TogglePin(GPIOF, LED_PORT1);
}
else
{
delay_ms(10);
}
}
}
四、中断
4.1. 什么是中断
打断CPU执行正常的程序,转而处理紧急程序,然后返回原暂停程序继续执行。
中断意义:高效处理紧急程序,不会一直占用CPU资源。
中断简图:GPIO(上拉输入、下拉输入、浮空输入)->AFIO(F1)/SYSCFG(F4/F7)->EXTI(屏蔽,上升/下降沿)->NVIC(使能、优先级控制)->CPU(处理中断)
4.2. NVIC
NVIC基本概念:嵌套向量中断控制器,属于内核(M3/4/7),支持256个中断(16内核+240外部),支持256个优先级,允许裁剪(ST公司裁剪到16个优先级)。
NVIC工作原理:对于外部中断,首先经过ISER/ICER即使能/失能控制器决定是否通过->经过IPR进行优先级排序->送入CPU;对于内部中断,不经过ISER或ICER直接送入SHPR()与IPR同级,进行优先级比对->送入CPU;
基本概念:
(1)抢占优先级(pre):高优先级可以打断正在执行的低优先级中断;
(2)响应优先级(sub):抢占优先级相同时,响应优先级高的先执行,但是不能互相打断;
(3)抢占和响应都相同的情况,自然优先级越高,先执行;
(4)自然优先级:中断向量表的优先级;
(5)数值越小,优先级越高
STM32中断优先级分组:0(0位抢占优先级,4为响应优先级-16个),1(1位抢占优先级-2个,3为响应优先级-8个),2(2位抢占优先级-4个,2为响应优先级-4个),3(3位抢占优先级-8个,1为响应优先级-2个),4(4位抢占优先级-16个,0位响应优先级0个)
一个工程中,一般只设置一次中断优先级分组
NVIC的使用:
(1)设置中断分组:使用函数 HAL_NVIC_SetPriorityGrouping (正点原子HAL_Init默认分组为2)
(2)设置中断优先级:使用函数HAL_NVIC_SetPriority
(3)使能中断:使用函数HAL_NVIC_EnableIRQ
4.3. EXTI
基本概念:外部中断事件控制器,包含20个产生事件/中断请求的边沿检测器,总共20条EXTI线(F1);
中断:要进入NVIC,有相应的中断服务函数,需要CPU处理
事件:不进入NVIC,仅用于内部硬件自动控制,如:TIM、DMA、ADC
EXTI线0~15:对应GPIO PIN0~15;
EXTI线16:PVD输出;
EXTI线17:RTC闹钟事件;
EXTI线18:USB OTG HS唤醒事件(能够在系统处于停止或者CPU出于CStop模式时生成唤醒事件的外设)
主要特性:每条EXTI线都可以单独配置,选择类型(中断或事件)、触发方式(上升沿、下降沿或者双边沿)、支持软件触发、开启/屏蔽、有挂起状态位
工作原理:对于外部输入,输入进来->边沿检测电路(根据上升沿/下降沿寄存器的配置,判断输入是否满足,如上升为1下降为0,则上升沿可以通过,如果都为0则始终不通过)->或门(选择软件触发或者硬件触发)->请求挂起寄存器->与门(另一测:中断屏蔽寄存器)
4.4. EXTI和IO映射关系
AFIO(F1):复用功能IO,用于重映射和外部中断映射配置
(1)调试IO配置
(2)重映射IO配置
(3)外部中断配置:配置EXTI中断线0-15对应到哪个具体IO口;
配置AFIO寄存器之前要使能AFIO时钟,使用函数__HAL_RCC_AFIO_CLK_ENABLE();
SYSCFG(F4/F7):系统配置控制器,用于外部中断映射配置,使用函数__HAL_RCC_SYSCFG_CLK_ENABLE(); 使能;
EXTI与IO的对应关系:
(1)EXTI0即控制Px0(0为A~K),由EXTI0[3:0]位控制决定(F1),一个时间只能连接一个;对于F4灯使用SYSCFG_EXTICER1的位控制;
4.5. 如何使用中断
设置输入模式(GPIO)->设置EXTI和IO映射关系(AFIO或SYSCFG)->EXTI(上升沿等)->NVIC(中断分组、优先级、使能)->CPU;
外设中断(USART/TIM/SPI)->NVIC->CPU
GPIO外部中断配置步骤:
(1)使能GPIO时钟;
(2)设置GPIO输入模式;(上下拉,浮空)
(3)使能AFIO/SYSCFG时钟;
(4)设置EXTI和IO对应关系;
(5)设置EXTI屏蔽,上下沿;
(6)设置NVIC:设置优先级分组、设置优先级、使能中断;
(7)设计中断服务函数:编写对应中断服务函数,清中断标志
使用HAL库,步骤2-5使用HAL_GPIO_Init一步到位
STM32 HAL库设置步骤(GPIO外部中断)
(1)使能GPIO时钟,使用__HAL_RCC_GPIOx_CLK_ENABLE
(2)GPIO/AFIO(SYSCFG)/EXTI,使用HAL_GPIO_Init
(3)设置中断分组:HAL_NVIC_SetPriorityGrouping,此函数只设置一次
(4)设置中断优先级:HAL_NVIC_SetPriority
(5)使能中断:HAL_NVIC_EnableIRQ
(6)设计中断服务函数:EXTIx_IRQHandle,中断服务函数,设置中断标志
通用外设驱动模型(四步法):
(1)初始化:时钟设置、参数设置、IO设置、中断设置值(开中断、设NVIC)(可选)
(2)读函数(可选):从外设读取数据
(3)写函数(可选):往外设写入数据
(4)中断服务函数(可选):根据中断标志,处理各种中断事务
4.6. 例程(外部中断控制一个灯亮灭)
选择KEY0作为LED灯中断控制,按下为低电平,不按下上拉为高电平,选择下降沿触发
/* 定义 exti.h 头文件 */
#ifndef __EXTI_H
#define __EXTI_H
#include "./SYSTEM/sys/sys.h"
#define EXTI_PORT GPIO_PIN_4
void exti_init(void);
#endif
/* 定义 exti.c */ 文件
#include "./BSP/EXTI/exti.h"
#include "./BSP/LED/led.h"
#include "./SYSTEM/delay/delay.h"
/* 中断初始化函数*/
void exti_init(void)
{
GPIO_InitTypeDef gpio_init_struct; //定义GPIO初始化结构体
__HAL_RCC_GPIOE_CLK_ENABLE(); //开启GPIOF时钟
gpio_init_struct.Pin=EXTI_PORT; //选择led角9
gpio_init_struct.Mode=GPIO_MODE_IT_FALLING; //选择中断_下降沿触发
gpio_init_struct.Pull=GPIO_PULLUP; //设置上拉
HAL_GPIO_Init(GPIOE, &gpio_init_struct);
/* 优先组已设为2故不必再设*/
HAL_NVIC_SetPriority(EXTI4_IRQn, 2, 0); /* 设定中断优先级,其中PIN为4故选择EXTI4,设定抢占优先级为2,响应优先级为0 */
HAL_NVIC_EnableIRQ(EXTI4_IRQn);
}
/* 中断服务函数 */
void EXTI4_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(EXTI_PORT); /*公共处理函数,当中断发生时调用此函数,此函数再调用回调函数*/
}
/* 中断回调函数 无需在头文件申明,已在内部申明*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
delay_ms(20); //消抖
if(GPIO_Pin == EXTI_PORT) //如果是设定的端口触发则执行
{
if(HAL_GPIO_ReadPin(GPIOE, EXTI_PORT) == 0)
{
HAL_GPIO_TogglePin(GPIOF, LED_PORT0);
}
}
}
五. 串口通信
5.1. 数据通信的基础概念
(1)串行/并行通信
串行:数据逐位按顺序依次传输,传输速率低,抗干扰能力强,通信距离较长,成本低
并行:数据各位通过多条线同时传输,传输速率较高,抗干扰能力较弱,通信距离较短,成本较高
单工:数据只能沿一个方向传输;
半双工:信道只有一个,数据可以沿两个方向传输,但需要分时;
全双工:信道有两条,数据可以同时进行双向传输。
同步通信:存在同步时钟,共用同一时钟信号,在同步时钟基础上进行通信;
异步通信:包含起始位、数据位、数据校验位和停止位,即没有时钟信号,加入一些同步信号。
比特率:每秒钟传送的比特数,单位bit/s;
波特率:每秒钟传送的码元数,单位Baud(如9编码11,上升沿编码1等)
比特率=波特率*log2M,M表示每个码元承载的信息量(二进制则为2,四进制位4)
二进制系统,波特率数值上等于比特率(市场上都为二进制设备,故一般二者数值相等)
(2)串行通信接口
UART:存在TXD发送端、RXD接受端、GND公共地,属于异步通信,全双工;
1-wire:一根数据线(发送/接收),异步通信,半双工;
IIC:包括SCL同步时钟、SDA数据输入/输出,属于同步通信,半双工;
SPI:包括SCK同步时钟、MISO主机输入从机输出、MOSI主机输出从机输入、CS片选信号,同步通信,全双工。
5.2. 串口(RS232)
定义:按位发送和接收的接口,如RS232/422/485等;
RS232(DB9):包括TXD(pin3)串口数据输出,RXD(pin2)串口数据输入、地线(pin5)信号地;
RS232电平:逻辑1:-15V~-3V,逻辑0:+3V~+15V;(STM32 CMOS电平,逻辑1 3.3V,逻辑0 0V)(51单片机 TTL 逻辑1 5V,逻辑0 0V)
CMOS/TTL电平不能与RS-232电平直接交换信息
(1)通信示意图(电平转换芯片,可以用MAX3232或SP3232等)
两个设备之间的TXD和RXD,必需交差连接,方可正常通信
(2)STM32串口与电脑USB口通信示意图
一帧数据的内容:启动位(占一个位长,保持逻辑0电平),有效数据位(可选5/6/7/8/9个位畅,LSB最低有效位位0在前,MSB最高有效位在后),校验位(可选占1个位长,也可以没有该位),停止位(必需有,可选占0.5/1/2个位畅,保持逻辑1电平)
5.3. STM32的USART简介
USART通用同步异步收发器,UART通用异步收发器;
USART和UART均可以与外部设备进行全双工异步通信;
串口数据接收过程:外部设备->串行数据输入(RXD引脚)->接收移动位寄存器->接收数据寄存器(RDR)->数据寄存器(DR)->CPU(F1/F4)
发送过程:CPU->数据寄存器(DR)->发送数据寄存器(TDR)->发送移位寄存器->TXD(串行数据输出)->外部设备;
设置波特率:波特率计算公式baud=fck(串口时钟, USART1为PCLK2 72M, 其它串口PCLK1 36M )/(16*USARTDIV分频),如:波特率设为115200,带入公式可得USARTDIV=39.0625
5.4. HAL库
HAL_PPP_Init()->用于初始化PPP外设的工作参数(GPIO/NVIC/CLOCK等)
如:USART,HAL_UART_Init()->调用MSP函数HAL_UART_Msp_Init()
HAL中断回调 HAL_PPP_IRQHandle()->调用一系列中断回调函数HAL_PPP_xxxCallback() (根据回调函数类型,编写对应的中断处理程序,用户可以选择是否重新定义该函数)
5.5. USART/UART异步通信配置步骤
(1)配置串口工作参数:HAL_UART_Init()
(2)串口底层初始化:HAL_UART_MspInit()
(3)开启串口异步接收中断:HAL_UART_Recive_IT()
(4)设置优先级,使能中断:HAL_NVIC_EnableIRQ()
(5)编写中断服务函数:USARTx_IRQHandle()、UARTx_IRQHandle()
(6)串口数据发送:USART_DR、HAL_UART_Transmit()
函数:HAL_UART_Init,可以设置波特率、字长、停止位、奇偶校验位、UART模式、硬件流设置、过采样设置;
函数:HAL_UART_Recive_IT(UART_HandleTypeDef*huart, uint8_t *pData, uint16_t Size),以中断的方式接收指定字节的数据,形参1是结构体类型指针变量、形参2是指向接收数据缓冲区、形参3是要接收的数据大小(以字节为单位)
函数:USART_DR、HAL_UART_Transmit(UART_HandleTypeDef*huart, uint8_t *pData, uint16_t Size, uint32_t Timeout),以阻塞的方式发送指定字节数据,形参1结构体指针变量、形参2指向要发送的数据地址、形参3要发送的数据大小、形参4设置的超时时间(以ms为单位)
5.6. IO引脚复用功能
通用:IO端口的输入或输出由GPIO外设控制;
复用:IO端口的输入或输出是由其它非GPIO外设控制;
IO复用冲突:同一时间IO只能用作一种复用功能,可考虑重映射功能(对于F4往后每个IO均有一个复用器。采用16路复用功能输入,复用器一次仅允许一个外设的复用功能连接至引脚,通过GPIOx_AFRL和GAIOx_AHRH寄存器配置,复位完成后,所有IO都会连接到系统的复用功能0)
5.7. 编程实验
/* main文件 */
usart_init(115200); /* 初始化串口 */
while(1)
{
if(g_usart1_rx_flag==1)
{
HAL_UART_Transmit(&g_uart1_handle, (uint8_t *)g_rx_buffer,1 ,1000); /*发送,其中1000是超时*/
while(__HAL_UART_GET_FLAG(&g_uart1_handle, UART_FLAG_TC) != 1); /*若未发送完,即标志位不为1,则一直等待*/
printf("\r\n\r\n"); /* 插入换行 */
g_usart1_rx_flag=0;
}
else
{
delay_ms(10); /*若未收到,延时10ms*/
}
}
}
/* usart.h文件文件 */
extern UART_HandleTypeDef g_uart1_handle; /* UART句柄 */
extern uint8_t g_rx_buffer[1]; /* 接收数据缓冲区 发送接收1个字符*/
extern uint8_t g_usart1_rx_flag; /*串口接收到数据标志*/
/* usart.c文件 */
/* 串口1初始化*/
uint8_t g_rx_buffer[1]; /* 接收数据缓冲区 发送接收1个字符*/
uint8_t g_usart1_rx_flag=0; /*串口接收到数据标志*/
UART_HandleTypeDef g_uart1_handle; /* UART句柄 */
void usart_init(uint32_t baudrate)
{
g_uart1_handle.Instance = USART1; /* 使用串口1 USART1 */
g_uart1_handle.Init.BaudRate = baudrate; /* 波特率 由初始化函数输入给定*/
g_uart1_handle.Init.WordLength = UART_WORDLENGTH_8B; /* 字长为8位数据格式 */
g_uart1_handle.Init.StopBits = UART_STOPBITS_1; /* 一个停止位 */
g_uart1_handle.Init.Parity = UART_PARITY_NONE; /* 无奇偶校验位 */
g_uart1_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE; /* 无硬件流控 */
g_uart1_handle.Init.Mode = UART_MODE_TX_RX; /* 收发模式 */
HAL_UART_Init(&g_uart1_handle); /* 使能UART1 */
/* 开启串口异步接收中断,形参1是句柄,形参2是接收数据缓冲区,形参3要接收的数据大小(以字节为单位)*/
HAL_UART_Receive_IT(&g_uart1_handle, (uint8_t *)g_rx_buffer, 1);
}
/*串口底层初始化*/
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
GPIO_InitTypeDef gpio_init_struct; /*定义初始化结构体变量*/
if(huart->Instance == USART_UX) /* 如果是串口1,进行串口1 MSP初始化 */
{
USART_UX_CLK_ENABLE(); /* USART1 时钟使能 */
USART_TX_GPIO_CLK_ENABLE(); /* 发送引脚时钟使能 */
USART_RX_GPIO_CLK_ENABLE(); /* 接收引脚时钟使能 */
gpio_init_struct.Pin = USART_TX_GPIO_PIN; /* TX引脚 */
gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 复用推挽 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
gpio_init_struct.Alternate = USART_TX_GPIO_AF; /* 复用为USART1 */
HAL_GPIO_Init(USART_TX_GPIO_PORT, &gpio_init_struct); /* 初始化发送引脚 */
gpio_init_struct.Pin = USART_RX_GPIO_PIN; /* RX引脚 */
gpio_init_struct.Alternate = USART_RX_GPIO_AF; /* 复用为USART1 */
HAL_GPIO_Init(USART_RX_GPIO_PORT, &gpio_init_struct); /* 初始化接收引脚 */
#if USART_EN_RX
HAL_NVIC_EnableIRQ(USART_UX_IRQn); /* 使能USART1中断通道 */
HAL_NVIC_SetPriority(USART_UX_IRQn, 3, 3); /* 抢占优先级3,子优先级3 */
#endif
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
g_usart1_rx_flag=1; /*查询此变量即可知道已接收数据*/
}
void USART_UX_IRQHandler(void)
{
HAL_UART_IRQHandler(&g_uart1_handle); /* 调用HAL库中断处理公用函数 */
HAL_UART_Receive_IT(&g_uart1_handle, (uint8_t *)g_rx_buffer, 1); /*调用完后会失能,故再次调用*/
}
六、独立看门狗
6.1. IWDG简介
IWDG, Independent watchdog, 独立看门狗
IWDG的本质,能产生系统复位信号的计数器
IWDG的特性,递减的计数器,时钟由独立的RC振荡器提供(可在待机和停机模式下运行),看门狗激活后,当递减计数器计数到0x000时产生复位
喂狗,计数器计数到0之前,重装在计数器的值,防止复位
6.2. STM32系统的复位
(1)NRST引脚的低电平(外部复位);
(2)窗口看门狗计数终止(WWDG复位);
(3)独立看门狗计数终止(IWDG复位);
(4)软件复位(SW复位);
(5)低功耗管理复位
6.3. IWDG的作用
(1)异常:外界电磁干扰或者自身系统(硬件或软件)异常造成程序跑飞(陷入某个死循环)
(2)作用:检测外界电磁干扰或硬件异常导致的程序跑飞问题
(3)应用:在一些高稳定性的产品中,并且对时间精度要求较低的场合
独立看门狗是异常处理的最后手段,不可依赖,应在设计时尽量避免异常的发生!
CPU必须及时喂狗,否则系统复位重启!
6.4. IWDG工作原理
(1)启用IWDG后,LSI时钟会自动开启;
(2)LSI时钟频率并不精确,F1用40KHz,F4/F7/H7用32kHz进行计算;
(3)键寄存器(key):写入0xAAAA进行喂狗,写入0x5555解除PR和PLR寄存器的访问保护,写入0xCCCC启动看门狗工作;
(4)预分频寄存器(IWDG_PR):设置000预分频=4,001=8,010=16,011=32,100=64,101=128,110=256,111=256;
(5)重装载计数器(IWDG_RLR):寄存器写入0xAAAA时,重装载值会被传送至计数器中;
(6)IWDG溢出事件计算:Tout=(psc*rlr)/fiwdg,其中Tout是溢出时间, fiwdg是看门狗的时钟源频率, psc是看门狗预分频系数,rlr是看门狗重装载值;
(7)IWDG最短最长超时时间:F1(40kHz):分频系数设置为4,最短时间0.1ms,分频系数设置为256,最长分频时间26214.4ms;F4(32kHz):最短时间0.125ms,最长时间32768ms。
6.5. IWDG配置步骤
(1)取消PR/RLR寄存器写保护,设置IWDG预分频系数和重装载值,启动IWDG。使用HAL_IWDG_Init(),使能IWDG,设置预分频系数和重转载值;
(2)及时喂狗,写入0xAAAA到IWDG_KR。使用HAL_IWDG_Refresh(),把重装载寄存器的值重载到计数器中,喂狗
6.6. 实验不及时喂狗,系统将复位
设计程序实现:系统初始化后,串口打印“您还没有喂狗,请及时喂狗!!!\r\n”,初始化IWDG的溢出时间为1s,判断1s内是否喂狗?若未喂狗则系统复位,喂狗则串口打印:已经喂狗\r\n”
原文地址:https://blog.csdn.net/qq_35379989/article/details/143721989
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!