STM32-HAL库(上)

STM32-HAL库(上)

001 课程介绍

(1)基于项目学习单片机,前提C语言熟练;如何使用STM32,以及其外设,常用模块,通讯协议学习;

002 资料下载及参考文章

(1)资料讲解(软件,手册,源码,课件),博客https://www.lxlinux.net/e/学习;

003 如何高效学习本课程

(1)视频+实操;遇到问题勤思考,多搜索,找方法;
(2)硬件问题排查完后,排软件(代码比对工具:beyond compare,
在线文本比对链接:http://www.kuquidc.com/textDifference/)

004 电脑配置要求

(1)最好window电脑,cpu:i5及以上;内存:16G及以上

005 硬件及软件准备

(1)安装包及驱动:MDK5安装包,芯片固件包,STM32CubeMX安装包,CH340驱动,ST-Link驱动,示例代码
(2)硬件准备:STM32开发板,ST-Link,USB转TTL模块,杜邦线若干

006 使用STM32CubeMX生成工程文件

(1)STM32CubeMX能帮助开发者通过直观的图形界面快速完成STM32微控制器的硬件配置,外设初始化以及中间件设置等工作。
(2)STM32CubeMX编写跑马灯。

007 CH340及ST-Link驱动安装

(1)插入设备,驱动安装;

008 编译及下载程序

(1)MDK5编译:Translate 就是翻译当下修改过的文件,检查下有没有语法错误,不会去链接库文件,也不会生成可执行文件
Build 就是编译当下修改过的文件,它包含了语法检查,链接动态库文件,生成可执行文件
Rebuild 重新编译整个工程,跟 Build功能是一样的,不同的是它编译的是整个工程的所有文件,耗时巨大
(2)ST-Link下载程序:usb-typeC电源线,单片机-ST-Link模块杜邦线连接;
(扩充串口下载:usb-typeC电源线,单片机-CH340转TTL工具杜邦线交叉连接,搭配FlyMcu串口下载工具)
(3)keil5参数配置;

009 什么是单片机

(1)单片机(Single-Chip Microcomputer)单片机微型计算机,是一种集成电路芯片,把具有数据处理能力的中央处理器CPU、
随机存储器RAM、闪存flash、多种I/O口和中断系统、定时器/计数器等功能集成到一块硅片微型计算机系统。
(2)单片机主要组成:CPU,Flash,RAM,时钟,总线,外设(GPIO,DMA,ADC,IIC,SPI…)

010 Cortex-M系列介绍

(1)arm公司和st公司:arm公司制作内核设计和ip授权;st公司拿到授权后,进行芯片设计(类似公司有华为,高通等);
(2)arm内核系列:A系列:Application缩写。高性能应用,比如:手机、电脑、电视等。
R系列:Real-time缩写。实时性强,汽车电子、军工、无线基带等。
M系列:Microcontroller缩写。超低功耗,工控、消费电子、家电、医疗器械等。
性能由高到低,时钟频率由高到低。
(3)STM32命名规则:例STM32F103C8T6 ,ST --意法半导体,M – Microelectronics 微电子,32 – 总线宽度,
F --产品类别为"基础型",103 --特定功能(3位数字)为"STM32基础型",C --引脚数为"48&49引脚",8 --内存容量为"64kb",
T --封装为"QFP",6 --温度范围"-40到+85";

011-012 如何获取数据手册

(1)ST官网:www.st.com;
中文社区网:https://www.stmcu.com.cn/Designresource/list/STM32F1/document/datasheet
其他途径:百度,淘宝
(2)产品手册,参考手册(开发时主要看)了解;

013 认识上官二号开发板

(1)主控芯片–STM32F103C8T6,高速晶振–8M,低速晶振–32.768k,内核–Cortex-M3,Flash–64K x 8bit,
SRAM–20K x 8bit,GPIO–37个GPIO,分别为PA0-PA15、PB0-PB15、PC13-PC15、PD0-PD1,等等

014 STM32103C8T6引脚分布

(1)产品手册中引脚定义查阅:有引脚名称,类型,I/O电平,主功能,复用功能,重定义功能说明;
例–I = 输入,O = 输出,S = 电源,HiZ = 高阻,FT:容忍5V等介绍;
(2)引脚分类:电源引脚,晶振引脚,复位引脚,下载引脚(JTAG,SWD,串口),BOOT引脚,GPIO引脚;

015 STM32最小系统

(1)单片机最小系统是指能够将单片机芯片运行所必需的最少的硬件电路集成在一起的系统;
由主芯片,时钟电路,复位电路,电源电路,BOOT启动电路,程序下载电路,扩展接口电路组成;
(2)STM32晶振作用:1. 提供时钟信号;2. 决定CPU的工作速度;
3. 确保外围设备的正常工作;4. 解决电磁兼容性问题;

016 电脑基础知识

(1)嘉立创EDA:https://lceda.cn/editor (可在线画PCB板)
(2)常用电子元器件:电容、电阻、电感、三级管,三极管(NPN三极管,PNP三极管),MOS晶体管(P型MOS管,N型MOS管);
(3)逻辑电路:与门,或门,非门,异或门

017 如何查看原理图

(1)时钟电路,复位电路,电源电路,BOOT启动电路,程序下载电路,扩展接口;

018 官方安装包获取

(3)MDK5 是由 Keil 公司发布的一款嵌入式软件开发环境,
MDK5官网:https://www.keil.com/demo/eval/arm.htm#/DOWNLOAD,
芯片固件包官网:https://www.keil.arm.com/packs/
STM32CubeMX网:https://www.st.com/content/st_com/zh/stm32cubemx.html
零基础快速上手STM32开发:https://www.lxlinux.net/e/stm32/stm32-quick-start-for-beginner.html

020 为啥要安装CH340驱动

(1)PC的USB接口是USB电平标准,STM32中的串口是COMS电平标准,故需要安装CH340驱动进行转化;

021 五种方法下载程序

(1)参考文章:https://www.lxlinux.net/e/stm32/five-ways-to-flash-program-to-stm32.html
(2)串口下载、 ST-Link V2 下载、 ST-LINK Utility 下载、 JLink 下载、 STVP 下载

022 MDK5的基本使用

(1)keil5界面介绍

023 MDK5的一些推荐配置

(1)编辑器设置,代码提示及语法检测,快速定位函数或变量定义(F12定位)

024 MDK5的使用技巧

(1)查找匹配花括号:ctrl+e;TAB键shift+TAB键左右移动;代码段快速注释;ALT选中一列编辑;
快速打开头文件-右键open document;同时打开2个文件时可右键快速拆分窗口;查找替换功能;

025-026 MDK5调试功能

(1)参考文章:https://www.lxlinux.net/e/stm32/mdk-debug-tutorial.html
(2) Options for Target魔法棒配置:
C/C++ 选项卡中MDK的优化调成Level 0等级,Debug选项卡中Load Application at Startup」和「Run to main」勾上;
debug选项卡Settings中使用SW调试模式;Utilities 选项卡将Use Debug Driver打勾;
(3)开启调试后的调试小按钮:复位、全速运行、停止、进入函数、执行过此函数、跳出函数、执行到光标处、显示下一个将运行的代码;
(4)查看程序段/函数执行的时间:
Debug中打开Settings选项Trace选项卡,设置时钟频率为72MHz;
[执行到光标处按钮」(快捷键为 Ctrl+F10 )->[执行过此函数」(快捷键为 F10 ),2个时间相减即是函数执行时间;
(5)a.按钮是Watch Windows ,用于查看函数首地址以及变量的值;
例如在程序中定义一个全局变量 int temp; ,
在while循环中执行temp++ 右击temp,选择Add “temp” to… 添加到Wacth 1;
首先右击temp,会出现一个列表,点击Set Access Breakpoint at “temp”
b.按钮是Memory Windows ,可用于查看内存的地址与值;
例-编译一下,打开仿真工具,再打开Memory窗口,
在窗口中输入 temp ,这样我们就可以看到 temp 数组在内存中的地址与值

027-029 C语言基础复习

(1)数据类型:char,unsigned char,short,unsigned short,int,unsigned long,long,unsigned long,long long,unsigned long long,
float,double;注意位数,范围,stdint关键字;
(2)宏定义:是一种预处理指令,用于将一个标识符(宏名)替换为指定的字符串。
宏定义主要有以下作用:a.提升易读性;b.便于批量修改:
宏定义的一般形式如下:#define 宏名 字符串
定义一个常量:#define PI 3.14159
定义算术运算和逻辑运算:#define AREA(x) (x * x)
(3)typedef:是C语言中的一个关键字,用于为复杂的数据类型定义简单的别名。
typedef 可以用于为基本数据类型、结构体、联合体等定义别名:typedef unsigned int SIZE;
typedef与宏定义区别:宏定义新名字在左边,typedef在右边;
宏定义不需要分号,typedef需要分号;
宏定义一切都可以替换,而typedef只能替换数据类型;
宏定义只是无脑改名,而typedef会做检查;
(4)条件编译: 是一种预处理指令,它允许根据某些条件决定是否编译某段代码。
1. #if:根据条件决定是否编译某段代码。
2. #ifdef:如果宏已经定义,则编译某段代码。
3. #ifndef:如果宏未定义,则编译某段代码。
4. #else:在条件编译的其他指令之后使用,如果之前的条件不满足,则编译此处的代码。
5. #endif:条件编译结尾语句
(5)结构体:结构体可以包含不同类型的数据成员。
struct 结构体名 {数据类型 成员名1;数据类型 成员名2;…数据类型 成员名n;};
struct Student {char gender;int age;float score;}; struct Student stu1;
(6)指针:是一种特殊的变量,它存储了一个内存地址,这个地址可以是其他变量的地址,也可以是数组的元素、结构体、函数等类型的地址
指针的定义和声明:type* ptr;其中, type 表示指针所指向的数据类型, ptr 是变量名。
例:定义一个指向整数的指针(int* p;)

030 F1系列内核和芯片系统架构

(1)参考手册中"系统结构"(简洁),产品手册中"STM32F103xx增强型模块框图"(详细)都是系统架构图,
主要由驱动单元(Cortex™-M3内核DCode总线(D-bus),和系统总线(S-bus),通用DMA1和通用DMA2),
被动单元(内部SRAM,内部闪存存储器,FSMC,AHB到APB的桥(AHB2APBx),它连接所有的APB设备)组成。

031 存储器映射

(1)STM32寻址范围::232=4*210*210K=4*210M=4G
(2)地址所访问的存储单元是按字节编址的(32位编址):0x0000 0000 ~ 0xFFFF FFFF
(3)存储器映射:存储器本身不具有地址信息,给存储器分配地址的过程。
(4)存储器分类:ROM(只读存储器,非易失性,掉电不丢失的存储器(类似于硬盘)),
RAM(随机存储器,易失性,掉电丢失的存储器(类似内存))。
(5)ROM存储器有(程序存储器Flash,系统存储器,选项字节),RAM存储器有(运行内存SRAM,外设寄存器,内核外设寄存器)。

032 什么是寄存器

(1)寄存器本质也是内存,通过控制寄存器可以实现对外设工作的控制。

033 寄存器映射

(1)寄存器是特殊的存储器,给寄存器地址命名的过程,就叫作寄存器映射。
(2)总线:APB1,APB2,AHB;外设GPIO:GPIOA,GPIOB,GPIOC,GPIOD;
GPIOA寄存器:GPIOA_CRL,GPIOA_CRH,GPIOA_IDR,GPIOA_ODR,GPIOA_BSRR,GPIOA_BRR,GPIOA_LCKR;
如何进行外设地址映射:总线基地址+外设相对于总线基地址偏移量+寄存器相对于外设基地址偏移量

034-035 创建模板工程

(1)参考文章:https://www.lxlinux.net/e/stm32/create-stm32-hal-project-template.html
STM32Cube官方固件包官网:https://www.st.com/content/st_com/en.html
(2)工程模板5个目录构成:Drivers(存放与硬件相关的驱动层文件),Middlewares(存放中间层文件),
Output(存放工程编译输出文件),Projects(存放MDK工程文件),
Users(存放 HAL 库用户配置文件、main.c、中断处理文件,以及分散加载文件)。
(3)工程中创建目标(Template)及分组(Startup,User,Drivers/SYSTEM,Drivers/STM32F1xx_HAL_Driver),后在分组中添加文件;
(4)魔法棒设置(Target选项卡,Output与Listing选项卡,C/C++选项卡,Debug选项卡,Utilities选项卡)

036 什么是CMSIS

(1)ARM Cortex™ 微控制器软件接口标准(Cortex Microcontroller Software Interface Standard)
是CortexM处理器系列的与供应商无关的硬件抽象层。可以为处理器和外设实现一致且简单的软件接口,从而简化软件(
其实就是ARM公司做芯片内核,为了方便下游生产芯片,做了个软件标准,以便标准化生产)。
(2)标准库:寄存器底层操作封装,提供接口API供开发;每款芯片都都有一份自己的库文件;故降低了开发难度,但不便于移植。
HAL库:便于芯片间移植代码;故兼容所有芯片,但代码量大,执行效率低;

037 项目工程常见文件意义

(1)宏 USE_HAL_DRIVER 的意义:#if defined (USE_HAL_DRIVER) #include “stm32f1xx_hal.h” #endif
宏 STM32F103xB 的意义:#elif defined(STM32F103xB) #include “stm32f103xb.h” #elif
常见文件意义:例–startup_stm32f102xb.s 启动文件,用于初始化硬件、设置堆栈和中断向量表,然后跳转到C/C++的主程序入口点(main函数)。

038 STM32启动流程

(1)复位/上电
(2)根据BOOT0/BOOT1确定程序从哪个存储位置执行
(3)初始化SP及PC指针
将0X08000000位置的栈顶地址存放在SP(stack pointer)指针中
将0x08000004位置存放的向量地址装入PC程序计数器
(4)初始化系统时钟
(5)初始化用户堆栈
(6)进入main函数

039 map文件

(1)map文件是MDK编译代码后,产生的集程序、数据及IO空间的一种映射列表文件。
简单来说就是包括了:各种.c 文件、函数、符号等的地址、大小、引用关系等信息。
大致分为5部分:模块、段(入口)交叉引用(Section Cross References),
移除未使用的模块(Removing Unused input sections from the image),
映射符号表(Image Symbol Table),
内存(映射)分布(Memory Map of the image),
映射组件大小(Image component sizes);

040 时钟树是什么

(1)时钟是由电路产生的具有周期性的脉冲信号,相当于单片机的心脏,给单片机提供一个统一的信号。
(2)时钟类型:HSE = 高速外部时钟,HSI = 高速内部时钟,LSI = 低速内部时钟,LSE = 低速外部时钟
(3)使用单片机的外设必须开启相应的时钟。

041 系统时钟配置

(1)系统时钟配置sys.c,sys.h代码编写;(01. 自己动手配置时钟树–sys.c,sys.h)

/*
-------------------------------------------------------------------------
sys.c:
-------------------------------------------------------------------------
*/
#include "sys.h"
//HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef  *RCC_ClkInitStruct, uint32_t FLatency)
//HAL_StatusTypeDef HAL_RCC_OscConfig(RCC_OscInitTypeDef  *RCC_OscInitStruct)

void stm32_clock_init(uint32_t plln)
{
    HAL_StatusTypeDef ret = HAL_ERROR;
    RCC_OscInitTypeDef rcc_osc_init = {0};
    RCC_ClkInitTypeDef rcc_clk_init = {0};

    rcc_osc_init.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    rcc_osc_init.HSEState = RCC_HSE_ON;
    rcc_osc_init.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
    rcc_osc_init.PLL.PLLState = RCC_PLL_ON;
    rcc_osc_init.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    rcc_osc_init.PLL.PLLMUL = plln;
    ret = HAL_RCC_OscConfig(&rcc_osc_init);
    if(ret != HAL_OK)
   {
        while(1);
   }
   rcc_clk_init.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1| RCC_CLOCKTYPE_PCLK2);
   rcc_clk_init.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
   rcc_clk_init.AHBCLKDivider = RCC_SYSCLK_DIV1;
   rcc_clk_init.APB1CLKDivider = RCC_HCLK_DIV2;
   rcc_clk_init.APB2CLKDivider = RCC_HCLK_DIV1;
   ret = HAL_RCC_ClockConfig(&rcc_clk_init,FLASH_LATENCY_2);
   if(ret != HAL_OK)
   {
        while(1);
   }

}
/*
-------------------------------------------------------------------------
sys.h:
-------------------------------------------------------------------------
*/
#ifndef __SYS_H__
#define __SYS_H__
#include "stm32f1xx.h"
void stm32_clock_init(uint32_t plln);  /* 系统时钟初始化函数 */
#endif

042 GPIO口介绍

(1)GPIO英文简称为通用输入输出端口(General-purpose input/output)。
(2)可配置8种输入输出模式:输入浮空、输入上拉、输入下拉、模拟输入、开漏输出、开漏复用功能、推挽式输出以及推挽式复用功能。
(3)GPIO引脚个数:GPIO被分为很多组,每组最多有16个引脚,不同型号的 MCU 的GPIO个数是不同。
(4)引脚电平:0V~3.3V,部分引脚可容忍5V。

043 GPIO内部结构

(1)I/O端口位的基本结构,图形讲解,输入输出电路结构图。

044 GPIO工作模式

(1)浮空输入:浮空输入状态下,IO的电平状态是不确定的,完全由外部输入决定
上拉输入:IO口在无输入的情况下,保持高电平
下拉输入:IO口在无输入的情况下,保持低电平
模拟输入:输入信号不经施密特触发器直接接入,输入信号为模拟量
开漏输出:只能输出低电平
推挽输出:可以输出高、低电平
复用开漏输出:受内部外设控制,只能输出低电平
复用推挽输出:受内部外设控制,可以输出高、低电平

045-046 GPIO寄存器介绍

(1)端口配置低寄存器(GPIOx_CRL),端口配置高寄存器(GPIOx_CRH),端口输入数据寄存器(GPIOx_IDR),端口输出数据寄存器(GPIOx_ODR),
端口位设置/清除寄存器(GPIOx_BSRR),端口位清除寄存器(GPIOx_BRR),端口配置锁定寄存器(GPIOx_LCKR)
(2)函数
void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init),
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin),
void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin),
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)讲解;

047 LED灯介绍

(1)LED是发光二极体(Light Emitting Diode)的简称,也被称作发光二极管,正向通电点亮,反向通电不亮。

048 点亮一颗LED灯

(1)在Drivers/BSP中新建led.c,led.h;魔法棒/C/C++t添加路径;工程项目管理/Drivers/BSP中添加led.c;
(2)led.c:初始化GPIO函数;点亮LED1函数;熄灭LED1函数;翻转LED1状态函数;
main.c:调用初始化HAL库;设置时钟, 72Mhz;初始化LED灯;LED1相关函数;
(02. 点亮一颗LED灯–main.c,led.c,led.h)

/*
-------------------------------------------------------------------------
main.c:
-------------------------------------------------------------------------
*/
#include "sys.h"
#include "delay.h"
#include "led.h"

int main(void)
{
    HAL_Init();  /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);   /* 设置时钟, 72Mhz */
    led_init(); /* 初始化LED灯 */
    while(1)
    { 
       led_on();
        delay_ms(500);
        led_off();
        delay_ms(500);
    }
}


/*
-------------------------------------------------------------------------
led.c:
-------------------------------------------------------------------------
*/
#include "led.h"
#include "sys.h"
//初始化GPIO函数
void led_init()
{
    GPIO_InitTypeDef gpio_initstruct;
    //打开时钟
    __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
    //调用GPIO初始化函数
//    HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)

    gpio_initstruct.Pin = GPIO_PIN_8; // LED1对应的引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
    gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
    HAL_GPIO_Init(GPIOB,&gpio_initstruct);
    //关闭LED
    led_off();
}
//点亮LED1函数
void led_on(void)
{
    //void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET); // 拉低LED1引脚,点亮LED1
}
//熄灭LED1函数
void led_off(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); // 拉高LED1引脚,熄灭LED1
}

//翻转LED1状态函数
void led_toggle(void)
{
    //void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
}
/*
-------------------------------------------------------------------------
led.h:
-------------------------------------------------------------------------
*/
#ifndef __LED_H__
#define __LED_H__
void led_init(void);
void led_on(void);
void led_off(void);
void led_toggle(void);
#endif

049 流水灯实验

(2)在点亮一颗LED灯代码基础上,新增一个引脚kongzhiLED2即可。(03. 流水灯实验–main.c,led.c,led.h)

/*
-------------------------------------------------------------------------
main.c:
-------------------------------------------------------------------------
*/
#include "sys.h"
#include "delay.h"
#include "led.h"

int main(void)
{
    HAL_Init();  /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);   /* 设置时钟, 72Mhz */
    led_init(); /* 初始化LED灯 */
    while(1)
    { 
        led1_on();
        led2_off();
        delay_ms(500);
        led2_on();
        led1_off();
        delay_ms(500);
    }
}

/*
-------------------------------------------------------------------------
led.c:
-------------------------------------------------------------------------
*/
#include "led.h"
#include "sys.h"
//初始化GPIO函数
void led_init()
{
    GPIO_InitTypeDef gpio_initstruct;
    //打开时钟
    __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
    //调用GPIO初始化函数
//    HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)

    gpio_initstruct.Pin = GPIO_PIN_8 | GPIO_PIN_9; // LED1及LED2对应的引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
    gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
    HAL_GPIO_Init(GPIOB,&gpio_initstruct);
    //关闭LED
    led1_off();
    led2_off();
}
//点亮LED1函数
void led1_on(void)
{
    //void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET); // 拉低LED1引脚,点亮LED1
}
//熄灭LED1函数
void led1_off(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); // 拉高LED1引脚,熄灭LED1
}

//翻转LED1状态函数
void led1_toggle(void)
{
    //void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
}

//点亮LED2函数
void led2_on(void)
{
    //void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET); // 拉低LED2引脚,点亮LED2
}
//熄灭LED2函数
void led2_off(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET); // 拉高LED2引脚,熄灭LED2
}

//翻转LED2状态函数
void led2_toggle(void)
{
    //void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);
}



/*
-------------------------------------------------------------------------
led.h:
-------------------------------------------------------------------------
*/
#ifndef __LED_H__
#define __LED_H__
void led_init(void);
void led1_on(void);
void led1_off(void);
void led1_toggle(void);
void led2_on(void);
void led2_off(void);
void led2_toggle(void);
#endif

050 蜂鸣器实验

(1)在点亮一颗LED灯代码基础上,改造成beep蜂鸣器就行;(04. 驱动蜂鸣器实验–main.c,beep.c,beep.h)

/*
-------------------------------------------------------------------------
main.h:
-------------------------------------------------------------------------
*/
#include "sys.h"
#include "delay.h"
#include "beep.h"

int main(void)
{
    HAL_Init();  /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);   /* 设置时钟, 72Mhz */
    beep_init(); /* 初始化LED灯 */
    while(1)
    { 
        beep_on();
        delay_ms(500);
        beep_off();
        delay_ms(500);
    }
}

/*
-------------------------------------------------------------------------
beep.c:
-------------------------------------------------------------------------
*/

#include "beep.h"
#include "sys.h"
//初始化GPIO函数
void beep_init()
{
    GPIO_InitTypeDef gpio_initstruct;
    //打开时钟
    __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
    //调用GPIO初始化函数
//    HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)

    gpio_initstruct.Pin = GPIO_PIN_8; // BEEP及LED2对应的引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
    gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
    HAL_GPIO_Init(GPIOB,&gpio_initstruct);
    //关闭LED
    beep_off();
}
//点亮BEEP函数
void beep_on(void)
{
    //void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET); // 拉低BEEP引脚,点亮BEEP
}
//熄灭BEEP函数
void beep_off(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); // 拉高BEEP引脚,熄灭BEEP
}

//翻转BEEP状态函数
void beep_toggle(void)
{
    //void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
}
/*
-------------------------------------------------------------------------
beep.h:
-------------------------------------------------------------------------
*/
#ifndef __BEEP_H__
#define __BEEP_H__
void beep_init(void);
void beep_on(void);
void beep_off(void);
void beep_toggle(void);
#endif

051 按键实验

(1)参照流水灯实验代码,新建key.c中含初始化GPIO,按键扫描函数;main函数根据获取到的按键值进行led灯翻转。
(05. 按键实验–main.c,key.c,key.h)

/*
-------------------------------------------------------------------------
main.c:
-------------------------------------------------------------------------
*/
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "key.h"

int main(void)
{
    HAL_Init();  /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);   /* 设置时钟, 72Mhz */
    led_init(); /* 初始化KEY */
    key_init(); /* 初始化LED灯 */
    uint8_t key_num = 0;
    while(1)
    {
        key_num = key_scan(); /* 扫描按键获取按下按键的值 */
        if(key_num == 1) /* 检测到按键1按下 */
            //void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
            led1_toggle();/* 翻转LED1 */
        if(key_num == 2) /* 检测到按键2按下 */
            led2_toggle();/* 翻转LED2 */
    }
}

/*
-------------------------------------------------------------------------
key.c:
-------------------------------------------------------------------------
*/
#include "key.h"
#include "sys.h"
#include "delay.h"

//初始化GPIO
void key_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    //打开时钟
    __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
    //调用GPIO初始化函数
    // HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
    gpio_initstruct.Pin = GPIO_PIN_0 | GPIO_PIN_1; // LED1及LED2对应的引脚
    gpio_initstruct.Mode = GPIO_MODE_INPUT; // 输入
    gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
    HAL_GPIO_Init(GPIOA,&gpio_initstruct);
}

//按键扫描函数
uint8_t key_scan(void){
    //GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
    //检测按键是否按下
    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)
    {
        //消抖
        delay_ms(10);
        //再次判断按键是否按下
        if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)
        {
            //如果确实是按下的状态,等待按键松开
            while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET);
            //返回按键值
            return 1;
        }       
    }
    //检测按键是否按下
    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)
    {
        //消抖
        delay_ms(10);
        //再次判断按键是否按下
        if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)
        {
            //如果确实是按下的状态,等待按键松开
            while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_SET);
            //返回按键值
            return 2;
        }     
    }
    //返回默认值
    return 0;
}



/*
-------------------------------------------------------------------------
key.h:
-------------------------------------------------------------------------
*/
#ifndef __KEY_H__
#define __KEY_H__
#include "sys.h"
void key_init(void);
uint8_t key_scan(void);
#endif

052 中断相关概念

(1)什么是中断:中断是单片机正在执行程序时,由于内部或外部事件的触发,打断当前程序,转而去处理这一事件,当处理
完成后再回到原来被打断的地方继续执行原程序的过程。
(2)中断意义:中断的主要意义在于提高CPU的效率,而不会一直占用CPU,实现对突发事件的实时处理。相较于轮询方式
(即按照一定的频率和周期不断地检测某些事件的发生),中断在处理一些偶然发生的事情时效率更高。
(3)中断优先级:中断具有优先级高低之分,两个中断同时触发,则优先响应高优先级中断,再响应低优先级中断。
(4)中断嵌套:如果一个高优先级的中断发生,它会立即打断当前正在处理的中断(如果其优先级较低),并首先处理这个
高优先级的中断,这就是所谓的中断嵌套。
(5)中断执行流程:当中断发生时,STM32的执行流程如下:首先,由外设发出中断请求;然后,处理器暂停当前执行的任务,
保护现场(如将当前位置的PC地址压栈);接着,程序跳转到对应的中断服务程序(ISR)并执行;中断服
务程序执行完毕后,恢复现场(如将栈顶的值送回PC);最后,处理器返回到被中断的位置,继续执行下一个指令。

053 STM32中断

(1)STM32F103C8T6含70个中断(10个内核中断,60个外部中断),外部中断包含 EXTI、TIM、USART、ADC、I2C、SPI 等等。
(2)中断向量表:是一个存储中断处理函数地址的数组,位于Flash区的起始位置。每个数组元素对应一个中断源,其地址指向相应的中断服务程序。
当中断发生时,处理器会根据中断号查找向量表,然后跳转到对应的中断服务程序执行。

054 NVIC基本介绍及工作原理

(1)NVIC,即Nested Vectored Interrupt Controller(嵌套向量中断控制器),是STM32中的中断控制器。
它负责管理和协调处理器的中断请求,是STM32中处理异步事件的重要机制。可决定是否触发中断及中断优先级;

055 中断优先级基本概念

(1)中断优先级被划分为抢占式优先级和响应优先级。
抢占优先级–如果一个中断的抢占优先级高于当前正在执行的中断,那么它可以打断当前中断,优先得到执行。数值越小,优先级越高。

响应优先级–如果两个中断同时到达,且它们的抢占优先级相同,那么响应优先级高的中断将首先得到响应。数值越小,优先级越高。

自然优先级–自然优先级是由硬件固定并预先设定的,用户无法更改。当抢占优先级和响应优先级都相同时,自然优先级将决定哪个中断先得到处理。

优先级执行顺序–
①首先由抢占优先级决定。若抢占优先级相同,则由响应优先级决定。若响应优先级相同,则最终由自然优先级决定。
②在中断嵌套的情况下,高抢占优先级的中断可以打断低抢占优先级的中断,但高响应优先级的中断不能打断低响应优先级的中断(当它们具有相同的抢占优先级时)。

优先级分组–优先级寄存器IPR有8位,但实际只使用到高4位,用于决定抢占优先级、响应优先级的等级。

056 NVIC寄存器及相关函数

(1)NVIC寄存器有:a(ISER),中断失能寄存器(ICER),应用程序中断及复位控制寄存器(AIRCR),中断优先级寄存器(IPR)。
(2)NVIC相关函数:void HAL_NVIC_EnableIRQ(IRQn_Type IRQn);void HAL_NVIC_DisableIRQ(IRQn_Type IRQn);
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority);
void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup);
NVIC配置方法:设置中断分组 → 设置中断优先级 → 使能中断;设置中断分组一般在 HAL_Init 函数中进行。

057 EXTI简介

(1)什么是EXTI:EXTI是External Interrupt的缩写,表示外部中断事件控制器。
过程:GPIO口的电平信号变化->EXTI ->内核的中断控制器NVIC->CPU。
EXTI支持的触发方式:上升沿、下降沿、双边沿或软件触发。
外部中断有哪些:提供了16个GPIO_Pin的中断线,以及额外的中断线如PVD输出、RTC闹钟、USB唤醒和以太网唤醒。
注意点:相同的 Pin 不能同时触发中断。例如,PA0 和 PB0 不能同时被配置为中断源。
中断/事件:中断会打断CPU当前正在执行的程序,转而去执行中断服务程序,待中断服务程序执行完毕后,CPU会返回到原来的程序执行点继续执行。
事件只是简单地表示某个动作或状态的变化,而不会打断CPU当前正在执行的程序。当事件发生时,它会根据配置来决定是否触发相应的中断。

058 EXTI基本结构及工作原理

(1)EXTI基本结构:GPIO->AFIO中断引脚选择->EXTI边沿检测及控制->NVIC
(2)外部中断/事件控制器框图:例–输入线->边沿检测电路->请求挂起寄存器&中断屏蔽寄存器->NVIC中断控制器

059 EXTI寄存器及相关函数

(1)EXTI相关寄存器:中断屏蔽寄存器(EXTI_IMR),上升沿触发选择寄存器(EXTI_RTSR),下降沿触发选择寄存器(EXTI_FTSR),挂起寄存器(EXTI_PR);
(2)相关函数:void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin);
void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);

060 AFIO

(1)AFIO是Alternate Function Input/Output的缩写,表示复用功能IO,主要用于实现I/O端口的复用功能以及外部中断的控制。
(2)AFIO相关寄存器:外部中断配置寄存器 1(AFIO_EXTICR1),外部中断配置寄存器 2(AFIO_EXTICR2),
外部中断配置寄存器 3(AFIO_EXTICR3),外部中断配置寄存器 4(AFIO_EXTICR4)。
AFIO相关函数: void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);

061 EXTI配置流程

(1)使能GPIO时钟{_HAL_RCC_GPIOx_CLK_ENABLE}
->设置GPIO输入模式,设置AFIO(开启时钟,IO口映射),设置EXTI(屏蔽,上/下沿){HAL_GPIO_Init}
->设置NVIC(优先级分组,设置优先级,使能中断){HAL_NVIC_SetPriorityGrouping,HAL_NVIC_SetPriority,HAL_NVIC_EnableIRQ}
->设置中断服务函数{EXTIx_IRQHandler}

062 中断实验

(1)按照EXTI配置流程进行外部中断0的函数配置,含有中断初始化函数,中断处理函数,中断回调函数;注设置中断分组一般在HAL_Init函数中进行。(06. 中断实验–exti.c,exti.h,main.c)

/*
-------------------------------------------------------------------------
main.c:
-------------------------------------------------------------------------
*/
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "exti.h"

int main(void)
{
    HAL_Init();  /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);   /* 设置时钟, 72Mhz */
    led_init(); /* 初始化LED灯 */
    exti_init();/*初始化EXTI*/
    while(1)
    { 
        led2_off();
        delay_ms(500);
        led2_on();
        delay_ms(500);
    }
}

/*
-------------------------------------------------------------------------
exti.c:
-------------------------------------------------------------------------
*/

#include "exti.h"
#include "sys.h"
#include "delay.h"
#include "led.h"
//初始化exti函数
void exti_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    //打开时钟
    __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
    //调用GPIO初始化函数
//    HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)

    gpio_initstruct.Pin = GPIO_PIN_0; //按键对应引脚
    gpio_initstruct.Mode = GPIO_MODE_IT_FALLING; // 下降沿触发
    gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
    HAL_GPIO_Init(GPIOA,&gpio_initstruct);
    HAL_NVIC_SetPriority(EXTI0_IRQn,2,0);// 设置EXTI0中断线的优先级
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);// 使能中断
   
}
//void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
void EXTI0_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}
//__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    delay_ms(20);
    if(GPIO_Pin == GPIO_PIN_0)
    {
        //GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
        if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET)
            led1_toggle();
    }
}



/*
-------------------------------------------------------------------------
exti.h:
-------------------------------------------------------------------------
*/
#ifndef __EXTI_H__
#define __EXTI_H__
void exti_init(void);
#endif

063 电动车报警项目概述

(1)项目需求:点击遥控器 A 按键,系统进入警戒模式,一旦检测到震动(小偷偷车),则喇叭发出声响报警,吓退小偷。
点击遥控器 B 按键,系统退出警戒模式,再怎么摇晃系统都不会报警,否则系统一直发出尖叫,让车主尴尬。

064 震动传感器介绍

(1)参考文章:https://www.lxlinux.net/e/stm32/SW-18010P-tutorial.html
(2)工作原理:在无震动或者震动强度达不到设定阈值时,DO 口输出高电平,当外界震动强度超过设定阈值时,模块 D0 输出低电平。

065 震动点灯

(1)参考"06. 中断实验"代码,新增了取值函数,设定值函数为方便主函数对外部变量操作;(07. 振动点灯–main.c,exti.c,exti.h)

/*
-------------------------------------------------------------------------
main.c:
-------------------------------------------------------------------------
*/
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "exti.h"

int main(void)
{
    HAL_Init();  /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);   /* 设置时钟, 72Mhz */
    led_init(); /* 初始化LED灯 */
    exti_init();/*初始化EXTI*/
    while(1)
    { 
        if(vibrate_flag_get() == TRUE)
        {
            led1_on();
            delay_ms(2000);
            led1_off();
            vibrate_flag_set(FALSE);
        }
    }
}



/*
-------------------------------------------------------------------------
exti.c:
-------------------------------------------------------------------------
*/
#include "exti.h"
#include "sys.h"
#include "delay.h"
#include "led.h"

uint8_t vibrate_flag = FALSE;
//初始化exti函数
void exti_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    //打开时钟
    __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
    //调用GPIO初始化函数
//    HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)

    gpio_initstruct.Pin = GPIO_PIN_4; //按键对应引脚
    gpio_initstruct.Mode = GPIO_MODE_IT_FALLING; // 下降沿触发
    gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
    HAL_GPIO_Init(GPIOA,&gpio_initstruct);
    HAL_NVIC_SetPriority(EXTI4_IRQn,2,0);// 设置EXTI0中断线的优先级
    HAL_NVIC_EnableIRQ(EXTI4_IRQn);// 使能中断
   
}
//void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
void EXTI4_IRQHandler(void) //中断处理函数
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
}
//__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)//中断回调函数
{
    //delay_ms(20);
    if(GPIO_Pin == GPIO_PIN_4)
    {
        //GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
        if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4) == GPIO_PIN_RESET)
            vibrate_flag = TRUE;
    }
}

//取值函数,设定值函数为方便主函数对外部变量操作
uint8_t vibrate_flag_get(void) //取值函数
{
    uint8_t temp = vibrate_flag;
    vibrate_flag = FALSE;
    return temp;
}

void vibrate_flag_set(uint8_t value)//设定值函数
{
    vibrate_flag = value;
}



/*
-------------------------------------------------------------------------
exti.h:
-------------------------------------------------------------------------
*/
#ifndef __EXTI_H__
#define __EXTI_H__
#include "stdint.h"
#define TRUE 1
#define FALSE 0
void exti_init(void);
uint8_t vibrate_flag_get(void);
void vibrate_flag_set(uint8_t value);
#endif

066 继电器介绍

(1)参考文章:https://www.lxlinux.net/e/stm32/relay-tutorial.html
(2)工作原理:当继电器的电磁线圈通电时,产生的磁场会引起机械部件的运动,从而改变触点的状态,触点可以是常闭或常开。
它能够通过控制小电流来打开或关闭大电流电路,实现电路的控制和信号的传递。

067 继电器实验

(1)参考"02. 点亮一颗LED灯"代码,改为alarm文件,改变引脚即可。(08. 继电器实验–main.c,alarm.c,alarm.h)

/*
-------------------------------------------------------------------------
main.c:
-------------------------------------------------------------------------
*/
#include "sys.h"
#include "delay.h"
#include "alarm.h"

int main(void)
{
    HAL_Init();  /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);   /* 设置时钟, 72Mhz */
    alarm_init(); /* 初始化LED灯 */
    while(1)
    { 
       alarm_on();
       delay_ms(500);
       alarm_off();
       delay_ms(500);
    }
}
/*
-------------------------------------------------------------------------
alarm.c:
-------------------------------------------------------------------------
*/
#include "alarm.h"
#include "sys.h"
//初始化GPIO函数
void alarm_init()
{
    GPIO_InitTypeDef gpio_initstruct;
    //打开时钟
    __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
    //调用GPIO初始化函数
//    HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)

    gpio_initstruct.Pin = GPIO_PIN_7; // LED1对应的引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
    gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
    HAL_GPIO_Init(GPIOB,&gpio_initstruct);
    //关闭LED
    alarm_off();
}
//闭合继电器的函数
void alarm_on(void)
{
    //void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET); // 拉低LED1引脚,点亮LED1
}
//松开继电器的函数
void alarm_off(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET); // 拉高LED1引脚,熄灭LED1
}
//获取继电器状态的函数
uint8_t alarm_status_get(void)
{
    return (uint8_t)(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7));
}



/*
-------------------------------------------------------------------------
alarm.h:
-------------------------------------------------------------------------
*/
#ifndef __ALARM_H__
#define __ALARM_H__
#include "stdint.h"
#define ALARM_STATUS_ON 0
#define ALARM_STATUS_OFF 1
void alarm_init(void);
void alarm_on(void);
void alarm_off(void);
uint8_t alarm_status_get(void);
#endif

068 433M无线收发模块介绍

(1)参考文章:https://www.lxlinux.net/e/stm32/433m-tutorial.html
(2)本模块采用M4点动,「4」表示有4个按键,按一下按键A,D0将输出一个高电平。

069 433M模块实验

(1)实验目的:按下遥控器 A 键,LED1 亮/灭;按下遥控器 B 键,LED2 亮/灭;
(2)代码实现:参考"06. 中断实验",IO口为A12,B5为中断配置区别于引脚0-4,及同时作为中断的中断初始化配置。(09. 433M小实验–exti.c,exti.h,main.c)


/*
-------------------------------------------------------------------------
main.c:
-------------------------------------------------------------------------
*/
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "exti.h"

int main(void)
{
    HAL_Init();  /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);   /* 设置时钟, 72Mhz */
    led_init(); /* 初始化LED灯 */
    exti_init();/*初始化EXTI*/
    while(1)
    { 
        if(buttonA_flag_get() == TRUE)//如果检测到A键按下
        {
            led1_toggle(); //则翻转LED1状态
        }
        if(buttonB_flag_get() == TRUE) //如果检测到B键按下
        {
            led2_toggle(); //则翻转LED2状态
        }
    }
}


/*
-------------------------------------------------------------------------
exti.c:
-------------------------------------------------------------------------
*/
#include "exti.h"
#include "sys.h"
#include "delay.h"
#include "led.h"

uint8_t buttonA_flag = FALSE;
uint8_t buttonB_flag = FALSE;


//初始化exti函数
void exti_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    //打开时钟
    __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
    __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟    
    //调用GPIO初始化函数
//    HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
     //按键B中断配置
    gpio_initstruct.Pin = GPIO_PIN_12; //按键对应引脚
    gpio_initstruct.Mode = GPIO_MODE_IT_RISING; // 上升沿触发
    gpio_initstruct.Pull = GPIO_PULLDOWN; // 下拉
    HAL_GPIO_Init(GPIOA,&gpio_initstruct);
    HAL_NVIC_SetPriority(EXTI15_10_IRQn,2,0);// 设置EXTI12中断线的优先级
    HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);// 使能中断
     //按键A中断配置
    gpio_initstruct.Pin = GPIO_PIN_5; //按键对应引脚
    gpio_initstruct.Mode = GPIO_MODE_IT_RISING; // 上升沿触发
    gpio_initstruct.Pull = GPIO_PULLDOWN; // 下拉
    HAL_GPIO_Init(GPIOB,&gpio_initstruct);
    HAL_NVIC_SetPriority(EXTI9_5_IRQn,2,0);// 设置EXTI5中断线的优先级
    HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);// 使能中断
   
}
//void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
void EXTI15_10_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12);
}

void EXTI9_5_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5);
}
//__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    //delay_ms(20);
    if(GPIO_Pin == GPIO_PIN_12) //检测到B键按下
    {
        //GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
        if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_12) == GPIO_PIN_SET)
            buttonA_flag = TRUE;
    }
    else if(GPIO_Pin == GPIO_PIN_5)//检测到A键按下
    {
        //GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
        if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_5) == GPIO_PIN_SET)
            buttonB_flag = TRUE;
    }
}

uint8_t buttonA_flag_get(void)
{
    uint8_t temp = buttonA_flag;
    buttonA_flag = FALSE;
    return temp;
}

uint8_t buttonB_flag_get(void)
{
    uint8_t temp = buttonB_flag;
    buttonB_flag = FALSE;
    return temp;
}

void buttonA_flag_set(uint8_t value)
{
    buttonA_flag = value;
}

void buttonB_flag_set(uint8_t value)
{
    buttonB_flag = value;
}

/*
-------------------------------------------------------------------------
exti.h:
-------------------------------------------------------------------------
*/
#ifndef __EXTI_H__
#define __EXTI_H__
#include "stdint.h"
#define FALSE 0
#define TRUE 1
void exti_init(void);
uint8_t buttonA_flag_get(void);
uint8_t buttonB_flag_get(void);
void buttonA_flag_set(uint8_t value);
void buttonB_flag_set(uint8_t value);
#endif

070 高功率喇叭介绍

071 电动车报警器项目完结

(1)项目流程梳理,将"07. 振动点灯",“08. 继电器实验”,"09. 433M小实验"代码汇总后按照流程图编写业务代码;(10. 电动车报警器项目–main.c,exti.c,alarm.c,exti.h,alarm.h)

/*
-------------------------------------------------------------------------
main.c:
-------------------------------------------------------------------------
*/
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "exti.h"
#include "alarm.h"

int main(void)
{
    uint8_t alert_mode = FALSE;         //警戒模式标志
    HAL_Init();  /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);   /* 设置时钟, 72Mhz */
    led_init(); /* 初始化LED灯 */
    exti_init();/*初始化EXTI*/
    alarm_init(); /* 初始化继电器 */
    while(1)
    { 
        //若A键按下
        if(buttonA_flag_get() == TRUE)
        {
            //喇叭响2s,进入警戒模式
            alarm_on();
            delay_ms(2000);
            alarm_off();
            alert_mode = TRUE;
            //检测到震动
        
        }
        //若B键按下
        if(buttonB_flag_get() == TRUE)
        {
            if(alarm_status_get() == ALARM_STATUS_ON)
                alarm_off();
            else
            {
                //喇叭响1s,退出警戒模式                 
                alarm_on();
                delay_ms(1000);
                alarm_off();
                alert_mode = FALSE;  
            
            }      
        }
        if(alert_mode == TRUE)
        {
            if(vibrate_flag_get() == TRUE)
            //喇叭一直响
                alarm_on();           
            else
                vibrate_flag_set(FALSE);
                
        }
    }
}
/*
-------------------------------------------------------------------------
exti.c:
-------------------------------------------------------------------------
*/
#include "exti.h"
#include "sys.h"
#include "delay.h"
#include "led.h"

uint8_t buttonA_flag = FALSE;
uint8_t buttonB_flag = FALSE;
uint8_t vibrate_flag = FALSE;

//初始化exti函数
void exti_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    //打开时钟
    __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
    __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟    
    //调用GPIO初始化函数
//    HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
     //按键B中断配置
    gpio_initstruct.Pin = GPIO_PIN_12; //按键对应引脚
    gpio_initstruct.Mode = GPIO_MODE_IT_RISING; // 上升沿触发
    gpio_initstruct.Pull = GPIO_PULLDOWN; // 下拉
    HAL_GPIO_Init(GPIOA,&gpio_initstruct);
    HAL_NVIC_SetPriority(EXTI15_10_IRQn,2,0);// 设置EXTI12中断线的优先级
    HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);// 使能中断
     //按键A中断配置
    gpio_initstruct.Pin = GPIO_PIN_5; //按键对应引脚
    gpio_initstruct.Mode = GPIO_MODE_IT_RISING; // 上升沿触发
    gpio_initstruct.Pull = GPIO_PULLDOWN; // 下拉
    HAL_GPIO_Init(GPIOB,&gpio_initstruct);
    HAL_NVIC_SetPriority(EXTI9_5_IRQn,2,0);// 设置EXTI5中断线的优先级
    HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);// 使能中断
    //震动传感器中断配置
    gpio_initstruct.Pin = GPIO_PIN_4; //按键对应引脚
    gpio_initstruct.Mode = GPIO_MODE_IT_FALLING; // 下降沿触发
    gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
    HAL_GPIO_Init(GPIOA,&gpio_initstruct);
    HAL_NVIC_SetPriority(EXTI4_IRQn,2,0);// 设置EXTI0中断线的优先级
    HAL_NVIC_EnableIRQ(EXTI4_IRQn);// 使能中断
}
//void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
void EXTI15_10_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12);
}

void EXTI9_5_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5);
}

void EXTI4_IRQHandler(void) //中断处理函数
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
}
//__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    //delay_ms(20);
    if(GPIO_Pin == GPIO_PIN_12) //检测到B键按下
    {
        //GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
        if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_12) == GPIO_PIN_SET)
            buttonA_flag = TRUE;
    }
    else if(GPIO_Pin == GPIO_PIN_5)//检测到A键按下
    {
        //GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
        if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_5) == GPIO_PIN_SET)
            buttonB_flag = TRUE;
    }
    else if(GPIO_Pin == GPIO_PIN_4)
    {
        //GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
        if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4) == GPIO_PIN_RESET)
            vibrate_flag = TRUE;
    }
}

uint8_t buttonA_flag_get(void)
{
    uint8_t temp = buttonA_flag;
    buttonA_flag = FALSE;
    return temp;
}

uint8_t buttonB_flag_get(void)
{
    uint8_t temp = buttonB_flag;
    buttonB_flag = FALSE;
    return temp;
}

void buttonA_flag_set(uint8_t value)
{
    buttonA_flag = value;
}

void buttonB_flag_set(uint8_t value)
{
    buttonB_flag = value;
}
//取值函数,设定值函数为方便主函数对外部变量操作
uint8_t vibrate_flag_get(void) //取值函数
{
    uint8_t temp = vibrate_flag;
    vibrate_flag = FALSE;
    return temp;
}

void vibrate_flag_set(uint8_t value)//设定值函数
{
    vibrate_flag = value;
}

/*
-------------------------------------------------------------------------
alarm.c:
-------------------------------------------------------------------------
*/
#include "alarm.h"
#include "sys.h"
//初始化GPIO函数
void alarm_init()
{
    GPIO_InitTypeDef gpio_initstruct;
    //打开时钟
    __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
    //调用GPIO初始化函数
//    HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)

    gpio_initstruct.Pin = GPIO_PIN_7; // LED1对应的引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
    gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
    HAL_GPIO_Init(GPIOB,&gpio_initstruct);
    //关闭LED
    alarm_off();
}
//闭合继电器的函数
void alarm_on(void)
{
    //void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET); // 拉低LED1引脚,点亮LED1
}
//松开继电器的函数
void alarm_off(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET); // 拉高LED1引脚,熄灭LED1
}
//获取继电器状态的函数
uint8_t alarm_status_get(void)
{
    return (uint8_t)(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7));
}

/*
-------------------------------------------------------------------------
exti.h:
-------------------------------------------------------------------------
*/
#ifndef __EXTI_H__
#define __EXTI_H__
#include "stdint.h"
#define FALSE 0
#define TRUE 1
void exti_init(void);
uint8_t buttonA_flag_get(void);
uint8_t buttonB_flag_get(void);
void buttonA_flag_set(uint8_t value);
void buttonB_flag_set(uint8_t value);
//取值函数,设定值函数为方便主函数对外部变量操作
uint8_t vibrate_flag_get(void);
void vibrate_flag_set(uint8_t value);
#endif

/*
-------------------------------------------------------------------------
alarm.h:
-------------------------------------------------------------------------
*/
#ifndef __ALARM_H__
#define __ALARM_H__
#include "stdint.h"
#define ALARM_STATUS_ON 0
#define ALARM_STATUS_OFF 1
void alarm_init(void);
void alarm_on(void);
void alarm_off(void);
uint8_t alarm_status_get(void);
#endif

072 SysTick介绍

(1)SysTick解析:Systick,即滴答定时器,是内核中的一个特殊定时器,用于提供系统级的定时服务.该定时器是一个24位的递减计数器,具有自动重载值寄存器的功能。
(2)SysTick主要功能:①主要功能–实现简单的延时、生成定时中断以及进行精确定时和周期定时操作
②其他功能–操作系统的时基(如FreeRTOS),或者用于软件看门狗等系统调度操作。

073 SysTick寄存器

(1)SysTick控制及状态寄存器(CTRL),SysTick重装载数值寄存器(LOAD),SysTick当前数值寄存器(VAL)

074 HAL_Delay底层实现原理

(1)HAL_Delay底层源码解析

075 手撸延时函数

(1)void delay_us(uint32_t nus);函数编写,结合SysTick寄存器进行相应配置;(delay_ms(1000);中void delay_us(uint32_t nus)😉

076 手撸带操作系统延时函数思路

(1)不能随时关闭SysTick定时器,则采取一段时间看下计数值,通过计数值的总数判定时间;
(2)主要明白点:tnow<told则tcnt +=told-tnow,tnow>told则tcnt +=reload(tnow-told);(tnow为当前VAL值,told为上次循环VAL值,tcnt为累计计数值)

077 手撸带操作系统延时函数实操

(1)根据带操作系统延时函数流程图编码,对void delay_us(uint32_t nus)进行重新编码;(11. 带操作系统的延时函数)

#include "delay.h"
/**
  * @brief  微秒级延时
  * @param  nus 延时时长,范围:0~233015
  * @retval 无
  */
void delay_us(uint32_t nus)
{
    uint32_t ticks;
    uint32_t tcnt = 0, told, tnow;
    uint32_t reload = SysTick->LOAD;                //重装载值
    
    ticks = nus * 72;                               //需要计的节拍数
    told = SysTick->VAL;                            //刚进入while循环时计数器的值
    
    while(1)
    {
        tnow = SysTick->VAL;
        if(tnow != told)
        {
            if(tnow < told)
                tcnt += told - tnow;
            else
                tcnt += reload - (tnow -told);
            
            told = tnow;                            //下次进入while循环时,当前VAL的值作为told
            
            if(tcnt >= ticks)                       //已计的数超过/等于需要计的数时,退出循环
                break;
        }
    }
}

078 使用SysTick模拟多线程

(1)void SysTick_Handler(void)中添加systick_isr();systick_isr()主要用来统计1ms的个数并标记,本例有1000ms,500ms的(12. 使用SysTick模拟多线程–main.c,task.c,task.h)

/*
-------------------------------------------------------------------------
main.c:
-------------------------------------------------------------------------
*/
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "task.h"
int main(void)
{
    HAL_Init();  /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);   /* 设置时钟, 72Mhz */
    led_init(); /* 初始化LED灯 */
    while(1)
    { 
        task1();
        task2();
    }
}

/*
-------------------------------------------------------------------------
task.c:
-------------------------------------------------------------------------
*/
#include "task.h"
#include "led.h"
uint32_t task1_cnt = 0;
uint32_t task2_cnt = 0;
uint8_t task1_flag = 0;
uint8_t task2_flag = 0;
void Systick_isr(void)
{
    if(task1_cnt < 1000)
        task1_cnt++;
    else
    {
        task1_flag = 1;
        task1_cnt  = 0;
    }
    if(task2_cnt < 500)
        task2_cnt++;
    else
    {
        task2_flag = 1;
        task2_cnt  = 0;
    }
}  

void task1(void)
{
    if(task1_flag == 0)
        return;
    task1_flag = 0;
    led1_toggle();    
}   
void task2(void)
{
    if(task2_flag == 0)
        return;
    task2_flag = 0;
    led2_toggle();    
}


/*
-------------------------------------------------------------------------
task.h:
-------------------------------------------------------------------------
*/
#ifndef __TASK_H__
#define __TASK_H__
#include "sys.h"
void Systick_isr(void);
void task1(void);
void task2(void);
#endif

079 智能排队控制系统概述

(1)项目需求:①红外传感器检测有人通过并计数;
②计数值显示在LCD1602;
③允许通过时,LED1闪烁,蜂鸣器不响,继电器不闭合;
④不允许通过时,LED2闪烁,蜂鸣器响,继电器闭合;
⑤每次允许通过5个人,之后转为不允许通过,3秒后再转为允许通过;

080 红外模块介绍

(1)参考文档:https://www.lxlinux.net/e/stm32/ia-tutorial.html
(2)红外避障模块不断发射红外信号,当红外信号:
①有反射回来,OUT 输出低电平,输出指示灯(绿灯)亮。
②没反射回来,OUT 输出高电平,输出指示灯(绿灯)灭。

081 红外模块驱动代码

(1)参考"07. 振动点灯"代码即可,改变了GPIO口,进行exti函数使用;(13. 红外模块–main.c,exti.c,exti.h)

/*
-------------------------------------------------------------------------
main.c:
-------------------------------------------------------------------------
*/
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "exti.h"

int main(void)
{
    HAL_Init();  /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);   /* 设置时钟, 72Mhz */
    led_init(); /* 初始化LED灯 */
    exti_init();/*初始化EXTI*/
    while(1)
    { 
        if(infrared_flag_get() == TRUE)
        {
            led1_on();
            delay_ms(2000);
            led1_off();
            infrared_flag_set(FALSE);
        }
    }
}
/*
-------------------------------------------------------------------------
exti.c:
-------------------------------------------------------------------------
*/
#include "exti.h"
#include "sys.h"
#include "delay.h"
#include "led.h"

uint8_t infrared_flag = FALSE;
//初始化exti函数
void exti_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    //打开时钟
    __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOA时钟
    //调用GPIO初始化函数
//    HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)

    gpio_initstruct.Pin = GPIO_PIN_4; //按键对应引脚
    gpio_initstruct.Mode = GPIO_MODE_IT_FALLING; // 下降沿触发
    gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
    HAL_GPIO_Init(GPIOB,&gpio_initstruct);
    HAL_NVIC_SetPriority(EXTI4_IRQn,2,0);// 设置EXTI0中断线的优先级
    HAL_NVIC_EnableIRQ(EXTI4_IRQn);// 使能中断
   
}
//void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
void EXTI4_IRQHandler(void) //中断处理函数
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
}
//__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)//中断回调函数
{
    //delay_ms(20);
    if(GPIO_Pin == GPIO_PIN_4)
    {
        //GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
        if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_4) == GPIO_PIN_RESET)
            infrared_flag = TRUE;
    }
}

//取值函数,设定值函数为方便主函数对外部变量操作
uint8_t infrared_flag_get(void) //取值函数
{
    uint8_t temp = infrared_flag;
    infrared_flag = FALSE;
    return temp;
}

void infrared_flag_set(uint8_t value)//设定值函数
{
    infrared_flag = value;
}

/*
-------------------------------------------------------------------------
exti.h:
-------------------------------------------------------------------------
*/
#ifndef __EXTI_H__
#define __EXTI_H__
#include "stdint.h"
#define TRUE 1
#define FALSE 0
void exti_init(void);
uint8_t infrared_flag_get(void);
void infrared_flag_set(uint8_t value);
#endif

082 LCD1602简介

(1)参考文档:https://www.lxlinux.net/e/stm32/lcd1602-tutorial.html
(2)LCD1602( Liquid Crystal Display 1602),一种常见的字符型液晶显示模块。它能够显示16列2行,共32个字符字符。

083 LCD1602引脚介绍

(1)16个引脚:电源(VDD,GND),背光电源(BLA,BLK),液晶驱动电压(V0),数据线(D0-D7),
控制线(RS–寄存器选择,RW–读、写操作选择,E–使能信号)。

084 LCD1602读写时序

(1)主要看懂时序图,从中可知读操作,写操作中控制引脚(RS,RW,E)的配置情况。

085 LCD1602显示一个字符的逻辑

(1)显示地址:①写入第一行第一个字符应该是–00000000B(00H)+ 10000000B(80H) = 10000000B(80H)
②写入第二行第一个字符应该是–01000000B(40H)+ 10000000B(80H) = 11000000B(C0H)
(2)LCD1602字符表:同ASCII码表相似

086 LCD1602显示一个字符

(1)lcd1602初始化(GPIO初始化,上电初始化)函数,写命令函数,写数据函数,显示函数。(14. LCD1602显示一个字符–main.c,lcd1602.c,lcd1602.h)

/*
-------------------------------------------------------------------------
main.c:
-------------------------------------------------------------------------
*/
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "lcd1602.h"
int main(void)
{
    HAL_Init();  /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);   /* 设置时钟, 72Mhz */
    led_init(); /* 初始化LED灯 */
    lcd1602_init();
    lcd1602_show_char();
    while(1)
    { 
        
    }
}

/*
-------------------------------------------------------------------------
lcd1602.c:
-------------------------------------------------------------------------
*/

#include "lcd1602.h"
#include "delay.h"

//RS引脚定义
#define RS_GPIO_Port GPIOB
#define RS_GPIO_Pin GPIO_PIN_1
//void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
#define RS_HIGH HAL_GPIO_WritePin(RS_GPIO_Port,RS_GPIO_Pin,GPIO_PIN_SET);
#define RS_LOW HAL_GPIO_WritePin(RS_GPIO_Port,RS_GPIO_Pin,GPIO_PIN_RESET);

//RW引脚定义
#define RW_GPIO_Port GPIOB
#define RW_GPIO_Pin GPIO_PIN_2
//void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
#define RW_HIGH HAL_GPIO_WritePin(RW_GPIO_Port,RW_GPIO_Pin,GPIO_PIN_SET);
#define RW_LOW HAL_GPIO_WritePin(RW_GPIO_Port,RW_GPIO_Pin,GPIO_PIN_RESET);

//EN引脚定义
#define EN_GPIO_Port GPIOB
#define EN_GPIO_Pin GPIO_PIN_10
//void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
#define EN_HIGH HAL_GPIO_WritePin(EN_GPIO_Port,EN_GPIO_Pin,GPIO_PIN_SET);
#define EN_LOW HAL_GPIO_WritePin(EN_GPIO_Port,EN_GPIO_Pin,GPIO_PIN_RESET);

//lcd1602初始化
void lcd1602_init(void)
{
    lcd1602_gpio_init();
    lcd1602_start();
}    

//gpio初始化
void lcd1602_gpio_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    //打开时钟
    __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOB时钟
    __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
    //调用GPIO初始化函数
    //HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
    
    gpio_initstruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7; 
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
    gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
    HAL_GPIO_Init(GPIOA,&gpio_initstruct);

    gpio_initstruct.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_10; 
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
    gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
    HAL_GPIO_Init(GPIOB,&gpio_initstruct);    
    
}
//上电初始化
void lcd1602_start(void)
{
//(1)延时 15ms 
    delay_ms(15);
//(2)写指令 38H(不检测忙信号) 
    lcd1602_write_cmd(0x38);
//(3)延时 5ms 
    delay_ms(5);
//(4)以后每次写指令,读/写数据操作均需要检测忙信号 
//(5)写指令 38H:显示模式设置 
    lcd1602_write_cmd(0x38);
//(6)写指令 08H:显示关闭 
    lcd1602_write_cmd(0x08);
//(7)写指令 01H:显示清屏 
    lcd1602_write_cmd(0x01);
//(8)写指令 06H:显示光标移动设置 
    lcd1602_write_cmd(0x06);
//(9)写指令 0CH:显示开及光标设置
    lcd1602_write_cmd(0x0C);
}

//lcd1602写命令
void lcd1602_write_cmd(char cmd)
{
    RS_LOW;
    RW_LOW;
    EN_LOW;
    GPIOA->ODR = cmd;
    delay_ms(5);
    EN_HIGH;
    delay_ms(5);
    EN_LOW;
}   
//lcd1602写数据
void lcd1602_write_data(char dataShow)
{
    RS_HIGH;
    RW_LOW;
    EN_LOW;
    GPIOA->ODR = dataShow;
    delay_ms(5);
    EN_HIGH;
    delay_ms(5);
    EN_LOW;    
}
//lcd1602显示字符
void lcd1602_show_char(void)
{
    //写地址
    lcd1602_write_cmd(0x80+0x02);
    //写数据
    lcd1602_write_data('A');
}   

/*
-------------------------------------------------------------------------
lcd1602.h:
-------------------------------------------------------------------------
*/
#ifndef __LCD1602_H__
#define __LCD1602_H__
#include "sys.h"
void lcd1602_init(void);
void lcd1602_gpio_init(void);
void lcd1602_start(void);
void lcd1602_write_cmd(char cmd);
void lcd1602_write_data(char dataShow);
void lcd1602_show_char(void);
#endif

087 使用LCD1602显示字符串

(2)显示一个字符代码上添加,显示一行函数(用指针变量)。(15. LCD1602显示一个字符串–main.c,lcd1602.c,lcd1602.h)

/*
-------------------------------------------------------------------------
main.c:
-------------------------------------------------------------------------
*/
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "lcd1602.h"
int main(void)
{
    HAL_Init();  /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);   /* 设置时钟, 72Mhz */
    led_init(); /* 初始化LED灯 */
    lcd1602_init();
    lcd1602_show_string(1,2,"qiuz");
    lcd1602_show_string(2,2,"come on!");    
    while(1)
    { 
    }
}
/*
-------------------------------------------------------------------------
lcd1602.c:
-------------------------------------------------------------------------
*/
#include "lcd1602.h"
#include "delay.h"

//RS引脚定义
#define RS_GPIO_Port GPIOB
#define RS_GPIO_Pin GPIO_PIN_1
//void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
#define RS_HIGH HAL_GPIO_WritePin(RS_GPIO_Port,RS_GPIO_Pin,GPIO_PIN_SET)
#define RS_LOW HAL_GPIO_WritePin(RS_GPIO_Port,RS_GPIO_Pin,GPIO_PIN_RESET)

//RW引脚定义
#define RW_GPIO_Port GPIOB
#define RW_GPIO_Pin GPIO_PIN_2
//void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
#define RW_HIGH HAL_GPIO_WritePin(RW_GPIO_Port,RW_GPIO_Pin,GPIO_PIN_SET)
#define RW_LOW HAL_GPIO_WritePin(RW_GPIO_Port,RW_GPIO_Pin,GPIO_PIN_RESET)

//EN引脚定义
#define EN_GPIO_Port GPIOB
#define EN_GPIO_Pin GPIO_PIN_10
//void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
#define EN_HIGH HAL_GPIO_WritePin(EN_GPIO_Port,EN_GPIO_Pin,GPIO_PIN_SET)
#define EN_LOW HAL_GPIO_WritePin(EN_GPIO_Port,EN_GPIO_Pin,GPIO_PIN_RESET)

//lcd1602初始化
void lcd1602_init(void)
{
    lcd1602_gpio_init();
    lcd1602_start();
}    

//gpio初始化
void lcd1602_gpio_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    //打开时钟
    __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOB时钟
    __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
    //调用GPIO初始化函数
    //HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
    
    gpio_initstruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7; 
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
    gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
    HAL_GPIO_Init(GPIOA,&gpio_initstruct);

    gpio_initstruct.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_10; 
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
    gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
    HAL_GPIO_Init(GPIOB,&gpio_initstruct);    
    
}
//上电初始化
void lcd1602_start(void)
{
//(1)延时 15ms 
    delay_ms(15);
//(2)写指令 38H(不检测忙信号) 
    lcd1602_write_cmd(0x38);
//(3)延时 5ms 
    delay_ms(5);
//(4)以后每次写指令,读/写数据操作均需要检测忙信号 
//(5)写指令 38H:显示模式设置 
    lcd1602_write_cmd(0x38);
//(6)写指令 08H:显示关闭 
    lcd1602_write_cmd(0x08);
//(7)写指令 01H:显示清屏 
    lcd1602_write_cmd(0x01);
//(8)写指令 06H:显示光标移动设置 
    lcd1602_write_cmd(0x06);
//(9)写指令 0CH:显示开及光标设置
    lcd1602_write_cmd(0x0C);
}

//lcd1602写命令
void lcd1602_write_cmd(char cmd)
{
    RS_LOW;
    RW_LOW;
    EN_LOW;
    GPIOA->ODR = cmd;
    delay_ms(5);
    EN_HIGH;
    delay_ms(5);
    EN_LOW;
}   
//lcd1602写数据
void lcd1602_write_data(char dataShow)
{
    RS_HIGH;
    RW_LOW;
    EN_LOW;
    GPIOA->ODR = dataShow;
    delay_ms(5);
    EN_HIGH;
    delay_ms(5);
    EN_LOW;    
}
//lcd1602显示字符
void lcd1602_show_char(void)
{
    //写地址
    lcd1602_write_cmd(0x80+0x02);
    //写数据
    lcd1602_write_data('A');
}   

//lcd1602显示字符串
void lcd1602_show_string(char row,char col,char *string)
{
    switch(row)
    {
        case 1:
            lcd1602_write_cmd(0x80+col);           
            while(*string)
            {
                lcd1602_write_data(*string);
                string++;
            }
            break;
        case 2:
            lcd1602_write_cmd(0x80+0x40+col);            
            while(*string)
            {
                lcd1602_write_data(*string);
                string++;
            }           
            break;
    }
}   
/*
-------------------------------------------------------------------------
lcd1602.h:
-------------------------------------------------------------------------
*/
#ifndef __LCD1602_H__
#define __LCD1602_H__
#include "sys.h"
void lcd1602_init(void);
void lcd1602_gpio_init(void);
void lcd1602_start(void);
void lcd1602_write_cmd(char cmd);
void lcd1602_write_data(char dataShow);
void lcd1602_show_char(void);
void lcd1602_show_string(char row,char col,char *string);
#endif

088 排队系统框架及硬件接线

(1)继电器(模拟匣机),蜂鸣器,红外避障模块,LCD1602,STM32开发板,ST-Link接线

089 排队系统代码框架搭建

(1)利用"12. 使用SysTick模拟多线程"代码,添加led模块,继电器模块,红外传感器模块,蜂鸣器模块,lcd1602模块代码,一一测试。
-(17. 排队控制系统项目框架–main.c,gate.c,beep.c,lcd1602.c,exti.c,task.c,led.c)

/*
-------------------------------------------------------------------------
main.c:
-------------------------------------------------------------------------
*/
#include "sys.h"
#include "delay.h"
#include "led.h"
//#include "task.h"
#include "gate.h"
#include "beep.h"
#include "exti.h"
#include "lcd1602.h"
int main(void)
{
    HAL_Init();  /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);   /* 设置时钟, 72Mhz */
    led_init(); /* 初始化LED灯 */
    gate_init();
    beep_init();
    exti_init();
    lcd1602_init();
    while(1)
    { 
        if(infrared_flag_get() == TRUE)
        {
            gate_on();
            delay_ms(500);
            gate_off();
               delay_ms(500);
        }
    }
}
/*
-------------------------------------------------------------------------
gate.c:
-------------------------------------------------------------------------
*/
#include "gate.h"
#include "sys.h"
//初始化GPIO函数
void gate_init()
{
    GPIO_InitTypeDef gpio_initstruct;
    //打开时钟
    __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
    //调用GPIO初始化函数
//    HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)

    gpio_initstruct.Pin = GPIO_PIN_6; // LED1对应的引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
    gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
    HAL_GPIO_Init(GPIOB,&gpio_initstruct);
    //关闭LED
    gate_off();
}
//闭合继电器的函数
void gate_on(void)
{
    //void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET); // 拉低LED1引脚,点亮LED1
}
//松开继电器的函数
void gate_off(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET); // 拉高LED1引脚,熄灭LED1
}
//获取继电器状态的函数
uint8_t gate_status_get(void)
{
    return (uint8_t)(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6));
}


/*
-------------------------------------------------------------------------
beep.c:
-------------------------------------------------------------------------
*/

#include "beep.h"
#include "sys.h"
//初始化GPIO函数
void beep_init()
{
    GPIO_InitTypeDef gpio_initstruct;
    //打开时钟
    __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
    //调用GPIO初始化函数
//    HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)

    gpio_initstruct.Pin = GPIO_PIN_5; // BEEP及LED2对应的引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
    gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
    HAL_GPIO_Init(GPIOB,&gpio_initstruct);
    //关闭LED
    beep_off();
}
//点亮BEEP函数
void beep_on(void)
{
    //void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET); // 拉低BEEP引脚,点亮BEEP
}
//熄灭BEEP函数
void beep_off(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET); // 拉高BEEP引脚,熄灭BEEP
}

//翻转BEEP状态函数
void beep_toggle(void)
{
    //void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);
}

/*
-------------------------------------------------------------------------
lcd1602.c:
-------------------------------------------------------------------------
*/
#include "lcd1602.h"
#include "delay.h"

//RS引脚定义
#define RS_GPIO_Port GPIOB
#define RS_GPIO_Pin GPIO_PIN_1
//void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
#define RS_HIGH HAL_GPIO_WritePin(RS_GPIO_Port,RS_GPIO_Pin,GPIO_PIN_SET)
#define RS_LOW HAL_GPIO_WritePin(RS_GPIO_Port,RS_GPIO_Pin,GPIO_PIN_RESET)

//RW引脚定义
#define RW_GPIO_Port GPIOB
#define RW_GPIO_Pin GPIO_PIN_2
//void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
#define RW_HIGH HAL_GPIO_WritePin(RW_GPIO_Port,RW_GPIO_Pin,GPIO_PIN_SET)
#define RW_LOW HAL_GPIO_WritePin(RW_GPIO_Port,RW_GPIO_Pin,GPIO_PIN_RESET)

//EN引脚定义
#define EN_GPIO_Port GPIOB
#define EN_GPIO_Pin GPIO_PIN_10
//void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
#define EN_HIGH HAL_GPIO_WritePin(EN_GPIO_Port,EN_GPIO_Pin,GPIO_PIN_SET)
#define EN_LOW HAL_GPIO_WritePin(EN_GPIO_Port,EN_GPIO_Pin,GPIO_PIN_RESET)

//lcd1602初始化
void lcd1602_init(void)
{
    lcd1602_gpio_init();
    lcd1602_start();
}    

//gpio初始化
void lcd1602_gpio_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    //打开时钟
    __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOB时钟
    __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
    //调用GPIO初始化函数
    //HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
    
    gpio_initstruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7; 
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
    gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
    HAL_GPIO_Init(GPIOA,&gpio_initstruct);

    gpio_initstruct.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_10; 
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
    gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
    HAL_GPIO_Init(GPIOB,&gpio_initstruct);    
    
}
//上电初始化
void lcd1602_start(void)
{
//(1)延时 15ms 
    delay_ms(15);
//(2)写指令 38H(不检测忙信号) 
    lcd1602_write_cmd(0x38);
//(3)延时 5ms 
    delay_ms(5);
//(4)以后每次写指令,读/写数据操作均需要检测忙信号 
//(5)写指令 38H:显示模式设置 
    lcd1602_write_cmd(0x38);
//(6)写指令 08H:显示关闭 
    lcd1602_write_cmd(0x08);
//(7)写指令 01H:显示清屏 
    lcd1602_write_cmd(0x01);
//(8)写指令 06H:显示光标移动设置 
    lcd1602_write_cmd(0x06);
//(9)写指令 0CH:显示开及光标设置
    lcd1602_write_cmd(0x0C);
}

//lcd1602写命令
void lcd1602_write_cmd(char cmd)
{
    RS_LOW;
    RW_LOW;
    EN_LOW;
    GPIOA->ODR = cmd;
    delay_ms(5);
    EN_HIGH;
    delay_ms(5);
    EN_LOW;
}   
//lcd1602写数据
void lcd1602_write_data(char dataShow)
{
    RS_HIGH;
    RW_LOW;
    EN_LOW;
    GPIOA->ODR = dataShow;
    delay_ms(5);
    EN_HIGH;
    delay_ms(5);
    EN_LOW;    
}
//lcd1602显示字符
void lcd1602_show_char(void)
{
    //写地址
    lcd1602_write_cmd(0x80+0x02);
    //写数据
    lcd1602_write_data('A');
}   

//lcd1602显示字符串
void lcd1602_show_string(char row,char col,char *string)
{
    switch(row)
    {
        case 1:
            lcd1602_write_cmd(0x80+col);           
            while(*string)
            {
                lcd1602_write_data(*string);
                string++;
            }
            break;
        case 2:
            lcd1602_write_cmd(0x80+0x40+col);            
            while(*string)
            {
                lcd1602_write_data(*string);
                string++;
            }           
            break;
    }
}   

/*
-------------------------------------------------------------------------
exti.c:
-------------------------------------------------------------------------
*/
#include "exti.h"
#include "sys.h"
#include "delay.h"
#include "led.h"

uint8_t infrared_flag = FALSE;
//初始化exti函数
void exti_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    //打开时钟
    __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOA时钟
    //调用GPIO初始化函数
//    HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)

    gpio_initstruct.Pin = GPIO_PIN_4; //按键对应引脚
    gpio_initstruct.Mode = GPIO_MODE_IT_FALLING; // 下降沿触发
    gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
    HAL_GPIO_Init(GPIOB,&gpio_initstruct);
    HAL_NVIC_SetPriority(EXTI4_IRQn,2,0);// 设置EXTI0中断线的优先级
    HAL_NVIC_EnableIRQ(EXTI4_IRQn);// 使能中断
   
}
//void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
void EXTI4_IRQHandler(void) //中断处理函数
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
}
//__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)//中断回调函数
{
    //delay_ms(20);
    if(GPIO_Pin == GPIO_PIN_4)
    {
        //GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
        if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_4) == GPIO_PIN_RESET)
            infrared_flag = TRUE;
    }
}

//取值函数,设定值函数为方便主函数对外部变量操作
uint8_t infrared_flag_get(void) //取值函数
{
    uint8_t temp = infrared_flag;
    infrared_flag = FALSE;
    return temp;
}

void infrared_flag_set(uint8_t value)//设定值函数
{
    infrared_flag = value;
}



/*
-------------------------------------------------------------------------
task.c:
-------------------------------------------------------------------------
*/
#include "task.h"
#include "led.h"
uint32_t task1_cnt = 0;
uint32_t task2_cnt = 0;
uint8_t task1_flag = 0;
uint8_t task2_flag = 0;
void Systick_isr(void)
{
    if(task1_cnt < 1000)
        task1_cnt++;
    else
    {
        task1_flag = 1;
        task1_cnt  = 0;
    }
    if(task2_cnt < 500)
        task2_cnt++;
    else
    {
        task2_flag = 1;
        task2_cnt  = 0;
    }
}  

void task1(void)
{
    if(task1_flag == 0)
        return;
    task1_flag = 0;
    led1_toggle();    
}   
void task2(void)
{
    if(task2_flag == 0)
        return;
    task2_flag = 0;
    led2_toggle();    
}



/*
-------------------------------------------------------------------------
led.c:
-------------------------------------------------------------------------
*/
#include "led.h"
#include "sys.h"
//初始化GPIO函数
void led_init()
{
    GPIO_InitTypeDef gpio_initstruct;
    //打开时钟
    __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
    //调用GPIO初始化函数
//    HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)

    gpio_initstruct.Pin = GPIO_PIN_8 | GPIO_PIN_9; // LED1及LED2对应的引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
    gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
    HAL_GPIO_Init(GPIOB,&gpio_initstruct);
    //关闭LED
    led1_off();
    led2_off();
}
//点亮LED1函数
void led1_on(void)
{
    //void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET); // 拉低LED1引脚,点亮LED1
}
//熄灭LED1函数
void led1_off(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); // 拉高LED1引脚,熄灭LED1
}

//翻转LED1状态函数
void led1_toggle(void)
{
    //void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
}

//点亮LED2函数
void led2_on(void)
{
    //void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET); // 拉低LED2引脚,点亮LED2
}
//熄灭LED2函数
void led2_off(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET); // 拉高LED2引脚,熄灭LED2
}

//翻转LED2状态函数
void led2_toggle(void)
{
    //void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);
}

090 状态机

(1)状态机(State Machine)是一种用于描述系统行为或功能行为的数学模型。
它通常包含一组状态、一组转换条件以及动作执行。状态机通过在不同状态之间进行转换来模拟系统的行为。
(2)状态机的主要特点:
①有限状态:状态机通常具有有限数量的状态。
②转换条件:状态之间的转换是基于特定条件触发的。
③动作执行:在状态转换过程中,状态机可能会执行某些动作或操作。
④状态机可以是确定性的(每个条件唯一对应一个转换)或非确定性的(一个条件可能导致多个可能的转换)。
–已绘制流程图

091 排队系统项目完结

(1)task.c多线程代码中的Systick中断处理程序void Systick_isr(void)里面按照状态机编写程序就行,其中包含3个时间计算;
新增了3个任务函数void led1_task(void),void led2_task(void),void sensor_task(void);主函数中各模块初始化,后调用任务函数;
(18. 排队控制系统项目完结–main.c,gate.c,beep.c,lcd1602.c,exti.c,task.c,led.c)

/*
-------------------------------------------------------------------------
main.c:
-------------------------------------------------------------------------
*/
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "task.h"
#include "beep.h"
#include "gate.h"
#include "lcd1602.h"
#include "exti.h"
int main(void)
{
    HAL_Init();  /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);   /* 设置时钟, 72Mhz */
    led_init(); /* 初始化LED灯 */
    beep_init();/* 初始化蜂鸣器 */
    exti_init();/* 初始化红外传感器 */
    gate_init();/* 初始化继电器 */
    lcd1602_init(); /* 初始化LCD1602 */
    lcd1602_show_string(1, 1, "PASS...");
    while(1)
    { 
        led1_task();
        led2_task();
        sensor_task();

    }
}

/*
-------------------------------------------------------------------------
gate.c:
-------------------------------------------------------------------------
*/
#include "gate.h"
#include "sys.h"
//初始化GPIO函数
void gate_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    //打开时钟
    __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
    //调用GPIO初始化函数
//    HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)

    gpio_initstruct.Pin = GPIO_PIN_6; // LED1对应的引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
    gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
    HAL_GPIO_Init(GPIOB,&gpio_initstruct);
    //关闭LED
    gate_off();
}
//闭合继电器的函数
void gate_on(void)
{
    //void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET); // 拉低LED1引脚,点亮LED1
}
//松开继电器的函数
void gate_off(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET); // 拉高LED1引脚,熄灭LED1
}
//获取继电器状态的函数
uint8_t gate_status_get(void)
{
    return (uint8_t)HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6);
}

/*
-------------------------------------------------------------------------
beep.c:
-------------------------------------------------------------------------
*/
#include "beep.h"
#include "sys.h"
//初始化GPIO函数
void beep_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    //打开时钟
    __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
    //调用GPIO初始化函数
//    HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)

    gpio_initstruct.Pin = GPIO_PIN_5; // BEEP及LED2对应的引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
    gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
    HAL_GPIO_Init(GPIOB,&gpio_initstruct);
    //关闭LED
    beep_off();
}
//点亮BEEP函数
void beep_on(void)
{
    //void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET); // 拉低BEEP引脚,点亮BEEP
}
//熄灭BEEP函数
void beep_off(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET); // 拉高BEEP引脚,熄灭BEEP
}

//翻转BEEP状态函数
void beep_toggle(void)
{
    //void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);
}

/*
-------------------------------------------------------------------------
lcd1602.c:
-------------------------------------------------------------------------
*/
#include "lcd1602.h"
#include "string.h"
#include <stdarg.h>
#include "delay.h"

//RS引脚定义
#define RS_GPIO_Port GPIOB
#define RS_GPIO_Pin GPIO_PIN_1
//void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
#define RS_HIGH HAL_GPIO_WritePin(RS_GPIO_Port,RS_GPIO_Pin,GPIO_PIN_SET)
#define RS_LOW HAL_GPIO_WritePin(RS_GPIO_Port,RS_GPIO_Pin,GPIO_PIN_RESET)

//RW引脚定义
#define RW_GPIO_Port GPIOB
#define RW_GPIO_Pin GPIO_PIN_2
//void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
#define RW_HIGH HAL_GPIO_WritePin(RW_GPIO_Port,RW_GPIO_Pin,GPIO_PIN_SET)
#define RW_LOW HAL_GPIO_WritePin(RW_GPIO_Port,RW_GPIO_Pin,GPIO_PIN_RESET)

//EN引脚定义
#define EN_GPIO_Port GPIOB
#define EN_GPIO_Pin GPIO_PIN_10
//void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
#define EN_HIGH HAL_GPIO_WritePin(EN_GPIO_Port,EN_GPIO_Pin,GPIO_PIN_SET)
#define EN_LOW HAL_GPIO_WritePin(EN_GPIO_Port,EN_GPIO_Pin,GPIO_PIN_RESET)



//gpio初始化
void lcd1602_gpio_init(void)
{
    GPIO_InitTypeDef gpio_initstruct={0};
    //打开时钟
    __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOB时钟
    __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
    //调用GPIO初始化函数
    //HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
    
    gpio_initstruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7; 
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
    gpio_initstruct.Pull = GPIO_NOPULL; 
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_LOW; 
    HAL_GPIO_Init(GPIOA,&gpio_initstruct);

    gpio_initstruct.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_10; 
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
    gpio_initstruct.Pull = GPIO_NOPULL; 
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_LOW; // 高速
    HAL_GPIO_Init(GPIOB,&gpio_initstruct);    
    
}
//上电初始化
void lcd1602_start(void)
{
//(1)延时 15ms 
    delay_ms(15);
//(2)写指令 38H(不检测忙信号) 
    lcd1602_write_cmd(0x38);
//(3)延时 5ms 
    delay_ms(5);
//(4)以后每次写指令,读/写数据操作均需要检测忙信号 
//(5)写指令 38H:显示模式设置 
    lcd1602_write_cmd(0x38);
//(6)写指令 08H:显示关闭 
    lcd1602_write_cmd(0x08);
//(7)写指令 01H:显示清屏 
    lcd1602_write_cmd(0x01);
//(8)写指令 06H:显示光标移动设置 
    lcd1602_write_cmd(0x06);
//(9)写指令 0CH:显示开及光标设置
    lcd1602_write_cmd(0x0c);
}

//lcd1602初始化
void lcd1602_init(void)
{
    lcd1602_gpio_init();
    lcd1602_start();
}    


//lcd1602写命令
void lcd1602_write_cmd(char cmd)
{
    RS_LOW;
    RW_LOW;
    EN_LOW;
    GPIOA->ODR = cmd;
    delay_ms(5);
    EN_HIGH;
    delay_ms(5);
    EN_LOW;
}   
//lcd1602写数据
void lcd1602_write_data(char dataShow)
{
    RS_HIGH;
    RW_LOW;
    EN_LOW;
    GPIOA->ODR = dataShow;
    delay_ms(5);
    EN_HIGH;
    delay_ms(5);
    EN_LOW;    
}
//lcd1602显示字符
void lcd1602_show_char(void)
{
    //写地址
    lcd1602_write_cmd(0x80+0x02);
    //写数据
    lcd1602_write_data('A');
}   

//lcd1602显示字符串
void lcd1602_show_string(char row,char col,char *string)
{
    switch(row)
    {
        case 1:
            lcd1602_write_cmd(0x80+col);           
            while(*string)
            {
                lcd1602_write_data(*string);
                string++;
            }
            break;
        case 2:
            lcd1602_write_cmd(0x80+0x40+col);            
            while(*string)
            {
                lcd1602_write_data(*string);
                string++;
            }           
            break;
    }
}   

/*
-------------------------------------------------------------------------
exti.c:
-------------------------------------------------------------------------
*/
#include "exti.h"
#include "sys.h"
#include "delay.h"
#include "led.h"

uint8_t infrared_flag = FALSE;
//初始化exti函数
void exti_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    //打开时钟
    __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOA时钟
    //调用GPIO初始化函数
//    HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)

    gpio_initstruct.Pin = GPIO_PIN_4; //按键对应引脚
    gpio_initstruct.Mode = GPIO_MODE_IT_FALLING; // 下降沿触发
    gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
    HAL_GPIO_Init(GPIOB,&gpio_initstruct);
    HAL_NVIC_SetPriority(EXTI4_IRQn,2,0);// 设置EXTI0中断线的优先级
    HAL_NVIC_EnableIRQ(EXTI4_IRQn);// 使能中断
   
}
//void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
void EXTI4_IRQHandler(void) //中断处理函数
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
}
//__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)//中断回调函数
{
    delay_ms(20);
    if (GPIO_Pin == GPIO_PIN_4)
    {
        //GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
        if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_4) == GPIO_PIN_RESET)
            infrared_flag = TRUE;
    }
}

//取值函数,设定值函数为方便主函数对外部变量操作
uint8_t infrared_flag_get(void) //取值函数
{
    uint8_t temp = infrared_flag;
    infrared_flag = FALSE;
    return temp;
}

void infrared_flag_set(uint8_t value)//设定值函数
{
    infrared_flag = value;
}

/*
-------------------------------------------------------------------------
task.c:
-------------------------------------------------------------------------
*/
#include "task.h"
#include "led.h"
#include "beep.h"
#include "gate.h"
#include "lcd1602.h"
#include "exti.h"
#include "stdio.h"
enum
{
    PASS_STATE,
    WAIT_STATE
};
uint32_t led1_task_cnt = 0;
uint32_t led2_task_cnt = 0;
uint32_t wait_cnt = 0;
uint32_t passenger = 0;
uint8_t led1_task_flag = 0;
uint8_t led2_task_flag = 0;
uint8_t state = PASS_STATE;

char message[16]={0};

void Systick_isr(void)
{
    //允许通行
    if(state == PASS_STATE)
    {
        //LED1间隔1s闪烁
        if(led1_task_cnt < 1000)
            led1_task_cnt++;
        else
        {
            led1_task_flag = 1;
            led1_task_cnt  = 0;
        }  
        //LED2不闪烁
        led2_off();
        //蜂鸣器不响
        beep_off();
        //开门
        gate_off();
    }
    //不允许通行
    else if(state == WAIT_STATE)
    {
        //LED1不闪烁

        //LED2间隔200ms闪烁
        if(led2_task_cnt < 500)
            led2_task_cnt++;
        else
        {
            led2_task_flag = 1;
            led2_task_cnt  = 0;
        }
        led1_off();
        //蜂鸣器响
        beep_on();        
        //关门    
        gate_on();
        //计时3秒,之后
        if(wait_cnt < 3000)
            wait_cnt++;
        else
        {
            wait_cnt = 0;
            //进入允许通行状态
            state = PASS_STATE;
            //LCD显示状态   
            lcd1602_show_string(1,1,"PASS...");
            
        }
    }
}  

void led1_task(void)
{
    if(led1_task_flag == 0)
        return;
    led1_task_flag = 0;
    led1_toggle();    
}   
void led2_task(void)
{
    if(led2_task_flag == 0)
        return;
    led2_task_flag = 0;
    led2_toggle();    
}
void sensor_task(void)
{
    //如果检测到有人通过
    if( state == PASS_STATE && infrared_flag_get()==TRUE)
    {
         //计数加1    
         passenger++;
        //LCD显示状态   
        sprintf(message,"PASS...%02d/05",passenger);
        lcd1602_show_string(1,1,message);
    }
   //如果通过的人数超过5个    
    if(passenger >= 5)
    {
        //计数清零
        passenger = 0;
        //进入不允许通行状态
        state = WAIT_STATE;
        //LCD显示状态    
        lcd1602_show_string(1,1,"WAIT...00/05");       
    }
}

/*
-------------------------------------------------------------------------
led.c:
-------------------------------------------------------------------------
*/
#include "led.h"
#include "sys.h"
//初始化GPIO函数
void led_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    //打开时钟
    __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
    //调用GPIO初始化函数
//    HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)

    gpio_initstruct.Pin = GPIO_PIN_8 | GPIO_PIN_9; // LED1及LED2对应的引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
    gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
    HAL_GPIO_Init(GPIOB,&gpio_initstruct);
    //关闭LED
    led1_off();
    led2_off();
}
//点亮LED1函数
void led1_on(void)
{
    //void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET); // 拉低LED1引脚,点亮LED1
}
//熄灭LED1函数
void led1_off(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); // 拉高LED1引脚,熄灭LED1
}

//翻转LED1状态函数
void led1_toggle(void)
{
    //void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
}

//点亮LED2函数
void led2_on(void)
{
    //void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET); // 拉低LED2引脚,点亮LED2
}
//熄灭LED2函数
void led2_off(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET); // 拉高LED2引脚,熄灭LED2
}

//翻转LED2状态函数
void led2_toggle(void)
{
    //void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);
}

092 定时器介绍

(1)STM32F103C8T6定时器作用:计时、延时、事件触发以及PWM波形生成、脉冲捕获等。
(2)STM32F103C8T6定时器基本组成:由一个16位计数器、预分频器和自动重装寄存器的时基单元组成。
(3)STM32F103C8T6定时器分类:
高级定时器(TM1)-不仅具备基本的定时中断功能,还拥有内外时钟源选择、输入捕获、输出比较、编码器接口以及主从触发模式等多种功能;
通用定时器(TIM2、TIM3和TIM4)-实现一些基本的定时任务,如LED闪烁、脉冲宽度测量;
(4)STM32F103C8T6定时器工作原理:PSC(预分频器)/ARR(自动重装载值)->CNT(计数器);

093 三种定时器介绍

(1)高级定时器:编号–TIM1,TIM8;总线–APB2;功能–拥有通用定时器全部功能,并额外具有重复计数器、死区生成、互补输出、刹车输入等功能
通用定时器:编号–TIM2,TIM3,TIM4,TIM5;总线–APB1;功能–拥有基本定时器全部功能,并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能
基本定时器:编号–TIM6,TIM7;总线–APB1;功能–拥有定时中断、主模式触发DAC的功能

094 定时器框图

(1)三种定时器框图了解,内部电路构成不一样的功能;

095 通用定时器时钟来源

(1)RCC内部时钟(内部时钟模式),ETR外部时钟(外部时钟模式2,外部时钟模式1),ITRx其他定时器(外部时钟模式1),Tlx捕获通道(外部时钟模式1,编码器模式)

096 定时器计数模式及溢出时间

(1)计数模式:向上计数–计数器溢出值CNT=ARR,计数器重装值CNT=0;
向下计数–计数器溢出值CNT=0,计数器重装值CNT=ARR
中心对齐计数–计数器溢出值CNT=ARR-1 -》计数器重装值CNT=ARR -》计数器溢出值CNT=1 -》计数器重装值CNT=0
(2)定时器溢出时间计算:
溢出时间 = (设定的)自动装载计数器 x 1/分频值;
Tout=(ARR+1)*(PSC+1)/Ft(Tout:定时器溢出时间;Ft:定时器的时钟源频率;ARR:自动重装载寄存器的值;PSC:预分频器寄存器的值)

097 定时器中断实验配置步骤

(1)步骤:时基工作参数配置(HAL_TIM_Base_Init)->msp初始化(GPIO,NVIC,CLOCK等)(HAL_TIM_Base_MspInit(),__HAL_RCC_TIMx_CLK_ENABLE(),HAL_NVIC_SetPriority,HAL_NVIC_EnableIRQ())
->使能更新中断(HAL_TIM_Base_Start_IT())->启动计数器(HAL_TIM_Base_Start_IT())->中断服务函数编写(TIMx_IRQHandler)->更新中断回调函数编写(HAL_TIM_PeriodElapsedCallback())

098 定时器中断点灯实验

(1)按照中断实验配置步骤编写代码,编写了定时器初始化函数,msp函数,中断服务函数,更新中断回调函数。(19. 定时器中断点灯实验–main.c,timer.c,timer.h)

/*
-------------------------------------------------------------------------
main.c:
-------------------------------------------------------------------------
*/
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "timer.h"

int main(void)
{
    HAL_Init();  /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);   /* 设置时钟, 72Mhz */
    led_init(); /* 初始化LED灯 */
    timer_init(5000 - 1, 7200 - 1);
    while(1)
    { 
//        led1_on();
//        led2_off();
//        delay_ms(500);
//        led2_on();
//        led1_off();
//        delay_ms(500);
    }
}

/*
-------------------------------------------------------------------------
timer.c:
-------------------------------------------------------------------------
*/
#include "timer.h"
#include "led.h"

TIM_HandleTypeDef timer_handler ={0};
//定时器初始化函数
void timer_init(uint16_t arr,uint16_t psc)
{
    
    timer_handler.Instance =TIM2;
    timer_handler.Init.Prescaler = psc;
    timer_handler.Init.Period = arr;
    timer_handler.Init.CounterMode = TIM_COUNTERMODE_UP;
    timer_handler.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    HAL_TIM_Base_Init(&timer_handler);
    HAL_TIM_Base_Start_IT(&timer_handler);
}    

//msp函数
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM2)
    {
        __HAL_RCC_TIM2_CLK_ENABLE();
        HAL_NVIC_SetPriority(TIM2_IRQn,2,2);
        HAL_NVIC_EnableIRQ(TIM2_IRQn);
    }
}
//中断服务函数
void TIM2_IRQHandler(void)
{
     HAL_TIM_IRQHandler(&timer_handler);
}
//更新中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM2)
    {
        led1_toggle();
    }
}    

/*
------------------------------------------------------------------------
timer.h:
-------------------------------------------------------------------------
*/
#ifndef __TIMER_H__
#define __TIMER_H__
#include "sys.h"
void timer_init(uint16_t arr,uint16_t psc);
#endif

099 输出比较原理

(1)输出比较可以通过比较定时计数器的值CNT与设定的比较值CCR,可以控制输出引脚的电平状态(置高或置低),从而实现生成一定频率和占空比的PWM波形。
(2)PWM有2种模式对应:PWM模式1(CNT<CCR输出有效,有效又分高电平有效,或低电平有效),PWM模式2(CNT>CCR输出有效,有效又分高电平有效,或低电平有效);

100 PWM介绍

(1)PWM波形(Pulse Width Modulation,脉冲宽度调制波形)是一种占空比可变的脉冲波形。PWM波形中的高电平持续时间和低电平持续时间可以根据需要进行调整,从而实现对模拟信号电平的数字编码。
(2)占空比=Th(一个周期内高电平持续时间)/Ts(一个周期时间)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值