先来演示一下需要实现的功能
智能排队系统
1. 硬件准备
1.1 蜂鸣器
蜂鸣器的另一个应用------电动车报警系统
继电器,可以被形象地比作一个开关
能够通过 控制小电流来打开或关闭大电流电路 ,实现电路的控制和信号的传递。
与普通的物理开关相比,继电器具有更高的可靠性和灵活性,能够应对复杂的电路控制需求
|-----|-----|-----|
| 蜂鸣器 | GND | GND |
| | I/O | B5 |
| | VCC | 3V |
1.2 继电器

|-----|-----|-----|
| 继电器 | IN | B6 |
| | GND | GND |
| | UCC | 5V |
1.3 红外模块

模块中蓝色的电位器用于调节灵敏度,顺时针旋转,灵敏度变高,检测距离变长;逆时针越小,灵敏度变低,检测距离变短。
上电后电源指示灯(绿灯)亮。
当红外反射回来, OUT 输出低电平,输出指示灯(绿灯)亮
|------|-----|-----|
| 红外模块 | VCC | 3V3 |
| | GND | GND |
| | OUT | PB4 |
红外避障模块上的一对红外线发射与接收管,发射管发射出一定频率的红外线,当检测方向遇到障碍物时, 红外线反射回来被接收管接收,经过比较器(LM393 )电路处理之后,信号输出接口输出低电平信号,同时绿色指示灯会亮起。
因为黑色能够吸收红外线(红外线不反射),而白色不行(红外线反射),所以除了避障可用作黑白线循迹、光电开关等等。
1.4 LCD1602
1.4.1 引脚配置

|---------|-----|--------|------------------------|
| LCD1602 | GND | GND | |
| | VDD | 5V | 电源正极 |
| | V0 | 通过电阻接地 | 液晶驱动电压, 调节对比度 |
| | RS | PB1 | 寄存器选择: 1:数据寄存器 2:指令寄存器 |
| | RW | PB2 | 读写操作选择: 1:读 2:写 |
| | E | PB10 | 使能信号 |
| | D0 | PA0 | 双向数据通信 |
| | D1 | PA1 | 双向数据通信 |
| | D2 | PA2 | 双向数据通信 |
| | D3 | PA3 | 双向数据通信 |
| | D4 | PA4 | 双向数据通信 |
| | D5 | PA5 | 双向数据通信 |
| | D6 | PA6 | 双向数据通信 |
| | D7 | PA7 | 双向数据通信 |
| | BLA | 3V3 | 背光正极 |
| | BLK | GND | 背光负极 |
V0:液品显示器对比度调整端,接正电源时对比度最高,接地时对比度最低,对比度过高时会产生"鬼影",使用时可以通过电位器或电阻调整对比度。电阻小了全是黑块,电阻大了不显示。
RW:读写信号线,高电平时进行读操作,低电平时进行写操作。
当 RS 和 RW 共同为低电平时可以写入指令成者显示地址;
当 RS 为低电平,RW 为高电平时可以读忙信号;
当 RS 为高电平,RW 为低电平时可以写入数据。
E:使能端,当E端由高电平跳变成低电平时,液晶模块执行命令。
1.4.2 基本操作时序
LCD1602 的基本的操作分为以下四种:
状态字读操作:输入 RS=0 、 RW=1 、 E=1 ;输出: D0~7 读出为状态字;
数据读出操作:输入 RS=1 、 RW=1 、 E=1 ;输出: D0~7 读出为数据;
指令写入操作:输入 RS=0 、 RW=0 、 E= 上升沿; 输出:无;
数据写入操作:输入 RS=1 、 RW=0 、 E= 上升沿; 输出:无
读操作的时序图
当我们要 写指令 时候, RS 置为低电平, RW 置为低电平, E 置为低电平,然后将指令数据送到数据口D0~D7,延时 tsp1 ,让 LCD1602 准备接收数据,这时候将 E 拉高,产生一个上升沿,这时候 指令 就开始写入 LCD1602 ,延时一段时间,将 E 置为低电平。
写操作的时序图
当我们要 写数据 的时候, RS 置为高电平, RW 置为低电平, E 置为低电平,然后将指令数据送到数据口D0~D7,延时 tsp1 ,让 LCD1602 准备接收数据,这时候将 E 拉高,产生一个上升沿,这时候 数据 就开始写入 LCD1602 ,延时一段时间,将 E 置为低电平。
对比一下可以发现,写指令和写数据在时序上只是 RS 的不同, 写指令 RS=0 ,写数据 RS=1 。
1.4.3 LCD1602显存地址

写入显示地址时要求最高位 D7 为高电平,也就是说,如果想你想
写入第一行第一个字符应该是:
00000000B ( 00H ) + 10000000B(80H) = 10000000B(80H) 。
写入第二行第一个字符应该是:
01000000B ( 40H ) + 10000000B(80H) = 11000000B(C0H) 。
1.4.4 LCD1602****字符表
字符表如下,和****ASCII 码相似,这就和我们写入字符时带来了方便
例如我想写入a,在ASCII 码中a是97,在LCD1602字符表中a也是97,就不用换算了

1.4.5 LCD1602上电初始化

2. 分功能实现
2.1 蜂鸣器实验
回顾一下点灯实验,我们只需要把灯改成蜂鸣器就好了
不管任何实验,第一步肯定是初始化xxx
第一步:初始化蜂鸣器(假设把蜂鸣器接到PB7引脚)
初始化GPIO
配置GPIO
设置默认蜂鸣器关
第二步:写所需要函数
打开蜂鸣器(引脚接到低电平时蜂鸣器响)
关闭蜂鸣器
蜂鸣器状态获取(这一个往往在与别的硬件功能一块实现时使用)
cpp
#include "alarm.h"
#include "sys.h"
//初始化GPIO函数
void alarm_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
//打开时钟
__HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
//调用GPIO初始化函数
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();
}
//点亮LED1的函数
void alarm_on(void)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET); // 拉低LED1引脚,点亮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 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_7);
}
以上写在alarm.c,别忘了头文件alarm.h补充函数声明
若使用uint8_t,需要添加#include "stdint.h"t头文件
cpp
#ifndef __ALARM_H__
#define __ALARM_H__
#define ALARM_STATUS_ON 0
#define ALARM_STATUS_OFF 1
#include "stdint.h"
void alarm_init(void);
void alarm_on(void);
void alarm_off(void);
uint8_t alarm_status_get(void);
#endif
2.2 继电器实验
同蜂鸣器
2.3 红外模块实验
.c
cpp
#include "exti.h"
#include "sys.h"
#include "delay.h"
#include "led.h"
uint8_t ia_flag = FALSE; // 检测到有人标志位
void exti_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
//打开时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
//调用GPIO初始化函数
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 EXTI4_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
//delay_ms(20);
if (GPIO_Pin == GPIO_PIN_4)
{
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4) == GPIO_PIN_RESET)
//led1_toggle();
ia_flag = TRUE;
}
}
uint8_t ia_flag_get(void)
{
uint8_t temp = ia_flag_get();
ia_flag = FALSE;
return temp;
}
void ia_flag_set(uint8_t value)
{
ia_flag = value;
}
.h
cpp
#ifndef __EXTI_H__
#define __EXTI_H__
#include "stdint.h"
#define TRUE 1
#define FALSE 0
void exti_init(void);
uint8_t ia_flag_get(void);
void ia_flag_set(uint8_t value);
#endif
2.4 LCD1602显示实验
第一步:初始化LCD1602
cpp
void lcd1602_init(void)
{
lcd1602_gpio_init();//GPIO初始化
lcd1602_start();//上电初始化
}
①初始化GPIO,并配置GPIO
cpp
void lcd1602_gpio_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
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.Speed=GPIO_SPEED_FREQ_HIGH;
gpio_initstruct.Pull=GPIO_PULLUP;
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.Speed=GPIO_SPEED_FREQ_HIGH;
gpio_initstruct.Pull=GPIO_PULLUP;
HAL_GPIO_Init(GPIOB,&gpio_initstruct);
}
② LCD1602上电初始化
把1.4.5的图拿过来了,对应着写代码
cpp
void lcd1602_start(void)
{
delay_ms(15);
lcd1602_write_cmd(0x38);
delay_ms(5);
lcd1602_write_cmd(0x38);
lcd1602_write_cmd(0x08);
lcd1602_write_cmd(0x01);
lcd1602_write_cmd(0x06);
lcd1602_write_cmd(0x0C);
};
cpp
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;
};
引脚太多,就定义了一下,以后改着方便
cpp
//RS引脚定义
#define RS_GPIO_Port GPIOB
#define RS_GPIO_Pin GPIO_PIN_1
#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
#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
#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);
第二步:写所需要函数
①写入数据
cpp
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;
};
②显示数据
显示字符(这里假设显示字符B)
第一行第一个是0x80,想在第一行第二个位置显示B,就是0x80+0x02
cpp
void lcd1602_show_char(void)
{
//在哪里显示
lcd1602_write_cmd(0x80+0x02);
//显示什么
lcd1602_write_data('B');
}
显示字符串(一共2行)
cpp
void lcd1602_show_line(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;
}
}
3. 整合起来
3.1 智能排队控制系统框架


beep.c
cpp
#include "beep.h"
#include "sys.h"
//初始化GPIO函数
void beep_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
//打开时钟
__HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
//调用GPIO初始化函数
gpio_initstruct.Pin = GPIO_PIN_5; // 蜂鸣器对应的引脚
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);
//关闭蜂鸣器
beep_off();
}
//打开蜂鸣器的函数
void beep_on(void)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET); // 拉低蜂鸣器引脚,打开蜂鸣器
}
//关闭蜂鸣器的函数
void beep_off(void)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET); // 拉高蜂鸣器引脚,关闭蜂鸣器
}
beep.h
cpp
#ifndef __BEEP_H__
#define __BEEP_H__
void beep_init(void);
void beep_on(void);
void beep_off(void);
#endif
exti.c
cpp
#include "exti.h"
#include "sys.h"
#include "delay.h"
#include "led.h"
uint8_t ia_flag = FALSE; // 检测到震动标志位
void exti_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
//打开时钟
__HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOA时钟
//调用GPIO初始化函数
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 EXTI4_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
delay_ms(20);
if (GPIO_Pin == GPIO_PIN_4)
{
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4) == GPIO_PIN_RESET)
//led1_toggle();
ia_flag = TRUE;
}
}
uint8_t ia_flag_get(void)
{
uint8_t temp = ia_flag;
ia_flag = FALSE;
return temp;
}
void ia_flag_set(uint8_t value)
{
ia_flag = value;
}
exti.h
cpp
#ifndef __EXTI_H__
#define __EXTI_H__
#include "stdint.h"
#define TRUE 1
#define FALSE 0
void exti_init(void);
uint8_t ia_flag_get(void);
void ia_flag_set(uint8_t value);
#endif
gate.c
cpp
#include "gate.h"
#include "sys.h"
//初始化GPIO函数
void gate_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
//打开时钟
__HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
//调用GPIO初始化函数
gpio_initstruct.Pin = GPIO_PIN_6; // 继电器对应的引脚
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)
{
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);
}
gate.h
cpp
#ifndef __GATE_H__
#define __GATE_H__
#include "stdint.h"
#define GATE_STATUS_ON 0
#define GATE_STATUS_OFF 1
void gate_init(void);
void gate_on(void);
void gate_off(void);
uint8_t gate_status_get(void);
#endif
lcd1602.c
cpp
#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
#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
#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
#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)
/**
* @brief LCD1602 GPIO初始化
* @param 无
* @retval 无
*/
void lcd1602_gpio_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
//__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
// /*Configure GPIO pin Output Level */
// HAL_GPIO_WritePin(GPIOA, 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_PIN_SET);
// /*Configure GPIO pin Output Level */
// HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_10, GPIO_PIN_SET);
/*Configure GPIO pins : PA0 PA1 PA2 PA3
PA4 PA5 PA6 PA7 */
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);
/*Configure GPIO pins : PB1 PB2 PB10 */
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);
}
/**
* @brief lcd开始工作
* @param 无
* @retval 无
*/
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);
}
/**
* @brief LCD1602初始化
* @param 无
* @retval 无
*/
void lcd1602_init(void)
{
lcd1602_gpio_init();
lcd1602_start();
}
/**
* @brief 写指令
* @param cmd:指令
* @retval 无
*/
void lcd1602_write_cmd(char cmd)
{
RS_LOW;
RW_LOW;
EN_LOW;
GPIOA->ODR = cmd; //将一字节数据发到GPIOA 8个引脚
delay_ms(5);
EN_HIGH;
delay_ms(5);
EN_LOW;
}
/**
* @brief 写数据
* @param dataShow:显示的字符
* @retval 无
*/
void lcd1602_write_data(char dataShow)
{
RS_HIGH;
RW_LOW;
EN_LOW;
GPIOA->ODR = dataShow; //将一字节数据发到GPIOA 8个引脚
delay_ms(5);
EN_HIGH;
delay_ms(5);
EN_LOW;
}
/**
* @brief 画面右移
* @param 无
* @retval 无
*/
void lcd1602_right_move(void)
{
for(int i=0;i < 16;i++){
delay_ms(1000);
lcd1602_write_cmd(0x18); //画面右移一位 0x18
}
}
/**
* @brief 显示字符
* @param row: 显示行,col: 显示起始列,string:显示字符
* @retval 无
*/
void lcd1602_show_line(char row, char col, char *string)
{
switch(row){
case 1:
lcd1602_write_cmd(0x80+col); //最高位 D7 为高电平
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
cpp
#ifndef __LCD1602_H__
#define __LCD1602_H__
#include <stdint.h>
void lcd1602_init(void);
void lcd1602_write_cmd(char cmd);
void lcd1602_write_data(char dataShow);
void lcd1602_show_line(char row, char col, char *string);
void lcd1602_right_move(void);
#endif
led.c和led.h和之前文章的一样
task.c
cpp
#include "tasks.h"
#include "led.h"
uint32_t led1_task_cnt = 0;
uint32_t led2_task_cnt = 0;
uint8_t led1_task_flag = 0;
uint8_t led2_task_flag = 0;
void systick_isr(void)
{
if (led1_task_cnt < 1000)
led1_task_cnt++;
else
{
led1_task_flag = 1;
led1_task_cnt = 0;
}
if (led2_task_cnt < 200)
led2_task_cnt++;
else
{
led2_task_flag = 1;
led2_task_cnt = 0;
}
}
void task1(void)
{
if(led1_task_flag == 0)
return;
led1_task_flag = 0;
led1_toggle();
}
void task2(void)
{
if(led2_task_flag == 0)
return;
led2_task_flag = 0;
led2_toggle();
}
task.h
cpp
#ifndef __TASKS_H__
#define __TASKS_H__
#include "sys.h"
void systick_isr(void);
void task1(void);
void task2(void);
#endif
main.c
cpp
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "tasks.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(); /* 初始化LCD1602 */
lcd1602_show_line(1, 2, "Bai, Come on");
}
3.2 教你写出代码
在3.1中各部分还是独立的,现在通过状态机使它们能配合彼此工作

开始正式实现功能了,main函数里使用多线程
main.c
cpp
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "tasks.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(); /* 初始化LCD1602 */
lcd1602_show_line(1, 1, "PASS...");
while(1)
{
led1_task();
led2_task();
sensor_task();
}
}
在task.c中实现状态机

根据这个图,我们得有允许通行状态PASS_STATE和不允许通行状态WAIT_STATE,一般用枚举的方法表示(enum),然后这部分补充代码完全按照上图进行功能实现,代码有详细注释
cpp
#include "tasks.h"
#include "led.h"
#include "beep.h"
#include "exti.h"
#include "lcd1602.h"
#include "gate.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以1秒的频率闪烁
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)
{
//LED2以200ms的频率闪烁
if (led2_task_cnt < 200)
led2_task_cnt++;
else
{
led2_task_flag = 1;
led2_task_cnt = 0;
}
//LED1不闪
led1_off();
//蜂鸣器响
beep_on();
//关门
gate_on();
//计时3秒,之后
if(wait_cnt < 3000)
wait_cnt++;
else
{
wait_cnt = 0;
//进入允许通行状态
state = PASS_STATE;
//LCD显示状态
lcd1602_show_line(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(ia_flag_get() == TRUE && state == PASS_STATE)
{
//计数加1
passenger++;
//LCD显示状态
sprintf(message, "PASS...%02d/05", passenger);
lcd1602_show_line(1, 1, message);
}
//如果通过的人数超过5个
if(passenger >= 5)
{
//计数清零
passenger = 0;
//进入不允许通行状态
state = WAIT_STATE;
//LCD显示状态
lcd1602_show_line(1, 1, "WAIT...00/05");
}
}

