stm32中的常用函数

目录

一、定义声明类

[1.1 预定义](#1.1 预定义)

[1.2 条件编译](#1.2 条件编译)

[1.3 extern 声明](#1.3 extern 声明)

[1.3 typedef 类型别名](#1.3 typedef 类型别名)

[1.4 结构体](#1.4 结构体)

二、基础函数

[2.1 delay类函数](#2.1 delay类函数)

[2.2 printf函数](#2.2 printf函数)

三、GPIO

[3.1 硬件](#3.1 硬件)

[3.2 通用外设驱动模型](#3.2 通用外设驱动模型)

[3.3 例程](#3.3 例程)

四、中断

[4.1. 什么是中断](#4.1. 什么是中断)

[4.2. NVIC](#4.2. NVIC)

[4.3. EXTI](#4.3. EXTI)

[4.4. EXTI和IO映射关系](#4.4. EXTI和IO映射关系)

[4.5. 如何使用中断](#4.5. 如何使用中断)

[4.6. 例程(外部中断控制一个灯亮灭)](#4.6. 例程(外部中断控制一个灯亮灭))

[五. 串口通信](#五. 串口通信)

[5.1. 数据通信的基础概念](#5.1. 数据通信的基础概念)

[5.2. 串口(RS232)](#5.2. 串口(RS232))

[5.3. STM32的USART简介](#5.3. STM32的USART简介)

[5.4. HAL库](#5.4. HAL库)

[5.5. USART/UART异步通信配置步骤](#5.5. USART/UART异步通信配置步骤)

[5.6. IO引脚复用功能](#5.6. IO引脚复用功能)

[5.7. 编程实验](#5.7. 编程实验)

六、独立看门狗

[6.1. IWDG简介](#6.1. IWDG简介)

[6.2. STM32系统的复位](#6.2. STM32系统的复位)

[6.3. IWDG的作用](#6.3. IWDG的作用)

[6.4. IWDG工作原理](#6.4. IWDG工作原理)

[6.5. IWDG配置步骤](#6.5. IWDG配置步骤)

[6.6. 实验不及时喂狗,系统将复位](#6.6. 实验不及时喂狗,系统将复位)


一、定义声明类

1.1 预定义
cpp 复制代码
#define LED1(x) do{ x? \ Hal_GPIO_1:\
HAL_GPIO_2);\
}while(0)   //如果成立执行第一句,如果不成立执行第二句
1.2 条件编译

#if 类似If,如果则执行下面

#ifdef 判断是否已被定义

#ifndef 判断是否未被定义,未被定义则执行下面的

#endif 结束标志

1.3 extern 声明

放在函数/变量前,表示函数/变量在其它文件定义,本文引用

cpp 复制代码
extern uint16_t g_usart;
extern void delay_us(uint32_t nus);
1.3 typedef 类型别名

typedef 现有类型 新名字

cpp 复制代码
typedef unsigned char uint8_t;
typedef struct
{
-IO uint32_t CRL;
} gpio_ty;
gpio_ty giox;
1.4 结构体

若干基本数据类型集合组成

struct 结构体名

{

成员列表;

} 变量名列表(可选);

cpp 复制代码
struct student
{
int num;
} stu1,stu2;
struct student stu1,stu2;
stu1.num=10;

二、基础函数

2.1 delay类函数

delay_init()函数

用于延时初始化

cpp 复制代码
void delay_init(uint16_tsysclk)
//输入的unint16为mHz,如f407 168mHz,此处输入168即可

delay_us()函数

微秒延迟函数

cpp 复制代码
delay_us(unit32_t nus)
//unit32为延迟的微秒数

delay_ms()函数

毫秒延迟函数

cpp 复制代码
delay_us(unit16_t nms)
//unit16_t为延迟的微秒数
2.2 printf函数

printf()用户调用->C标准库(stdio.h)->fputc()实现输出

stdio.h包括:printf、scanf、putchar、puts等

(1)输出字符串

cpp 复制代码
printf("字符串\r\n");  //\r\n用于换行

(2)输出控制符

cpp 复制代码
uint32 t temp= 10;
printf("%d\r\n", temp);
/*%d是输出控制符,temp是输出参数*/

(3)printf("输出控制符1输出控制符2......",输出参数1,输出参数2,...);

cpp 复制代码
uint32_t templ=5,uint32 t temp2= 10;
printf("%d%x \r\n", templ,temp2);

(4)printf("非输出控制符 输出控制符 非输出控制符",输出参数);

cpp 复制代码
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)

  1. 读函数:从外设读取数据

  2. 写函数:往外设写入数据

  3. 中断服务函数:根据中断标志,处理外设各种中断事务

常用函数:

用于开启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)创建驱动文件

cpp 复制代码
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个引脚初始输出高电平,即灯灭
    
}

创建主函数

cpp 复制代码
#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);
        
    }
 
  1. 按键控制LED

读取按键转态,可从浮空、上拉、下拉和模拟输入输出选择,开关只有0和1两种状态,从前三种选即可。对于如下图按键电路,开关按下时接地为低电平,断开时为使其为高电平,故需选择上拉模式。由于浮空状态不确定有可能高有可能低,故选择输入上拉模式。

较流水灯,我们增加按键的定义和初始化,以及按键按下的读取:

cpp 复制代码
#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灯中断控制,按下为低电平,不按下上拉为高电平,选择下降沿触发

cpp 复制代码
/* 定义 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. 编程实验
cpp 复制代码
/* 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"

相关推荐
SRA.1 小时前
STM32——HAL库开发笔记22(定时器3—呼吸灯实验)(参考来源:b站铁头山羊)
笔记·stm32·嵌入式硬件
SRA.1 小时前
STM32——HAL库开发笔记21(定时器2—输出比较)(参考来源:b站铁头山羊)
笔记·stm32·嵌入式硬件
日记成书4 小时前
详细介绍嵌入式硬件设计
嵌入式硬件·深度学习·学习
wenchm4 小时前
细说STM32F407单片机1个ADC使用DMA同时采集3个输入通道的方法
stm32·单片机·嵌入式硬件
SRA.4 小时前
STM32——HAL库开发笔记23(定时器4—输入捕获)(参考来源:b站铁头山羊)
笔记·stm32·嵌入式硬件
打酱油的工程师5 小时前
w803|联盛德|WM IoT SDK2.X测试|window11|TOML 文件|外设|TFT_LCD|测试任务|(5):TFT_LCD_LVGL示例
单片机·物联网·lcd·tft·w80x
广药门徒5 小时前
(200): error: #29: expected an expression error: #40: expected an identifier
嵌入式硬件
沐欣工作室_lvyiyi6 小时前
基于物联网的家庭版防疫面罩设计与实现(论文+源码)
人工智能·stm32·单片机·物联网·目标跟踪
苏慕TRYACE9 小时前
RT-Thread+STM32L475VET6——USB鼠标模拟
stm32·单片机·嵌入式硬件·计算机外设·rt_thread
楼台的春风15 小时前
【MCU驱动开发概述】
c语言·驱动开发·单片机·嵌入式硬件·mcu·自动驾驶·嵌入式