一、项目需求
(1)红外传感器检测有人通过并计数
(2) 计数值显示在LCD1602
(3) 允许通过时,LED1闪烁,蜂鸣器不响,继电器不闭合;
(4) 不允许通过时,
LED2
闪烁,蜂鸣器响,继电器闭合;
(5) 每次允许通过
5
个人,之后转为不允许通过,
3
秒后再转为允许通过
二、硬件清单
(1) 继电器(模拟匣机)
(2) 蜂鸣器
(3)红外避障模块
(4)LCD1602
(5)上官二号
(6)ST-Link
三、硬件接线
四、项目框图
五、状态机
状态机
状态机(
State Machine
)是一种用于描述系统行为或功能行为的数学模型。它通常包含一组状态、一组转
换条件以及动作执行。状态机通过在不同状态之间进行转换来模拟系统的行为。每个状态都代表系统的一个
特定配置或情况,而转换规则则定义了从一个状态转换到另一个状态的条件和动作。
状态机广泛应用于多个领域,包括但不限于计算机科学、电子工程、通信、自动化和机器人技术等。在计算
机科学中,状态机常用于描述软件系统的行为,特别是在硬件设计、编译器设计、网络协议以及用户界面交
互等方面。
状态机的主要特点包括:
1.
有限状态
:状态机通常具有有限数量的状态。这些状态可以是有序的、离散的或层次化的。
2.
转换条件
:状态之间的转换是基于特定条件触发的。当满足某个条件时,状态机会从当前状态转换到下
一个状态。
3.
动作执行
:在状态转换过程中,状态机可能会执行某些动作或操作。这些动作可以包括计算、数据更
新、输出信号等。
4.
确定性和非确定性
:状态机可以是确定性的(每个条件唯一对应一个转换)或非确定性的(一个条件可
能导致多个可能的转换)。
状态机的实现方式多种多样,可以使用编程语言中的条件语句、循环结构或专门的状态机库来实现。此外,
硬件设计领域中的有限状态机(
Finite State Machine, FSM
)也是状态机的一种重要应用。
伪代码:
while(1)
{
if(state == 开心)
{
KTV();
撸串();
if(女朋友跟人跑了)
state = 郁闷;
}
else if(state == 郁闷)
{
抽烟();
嫩模();
if(交新女朋友了)
state = 开心;
else if(刷到良许直播)
state = 打鸡血;
}
else if(state == 打鸡血)
{
写bug();
改bug();
if(成功入行了)
state = 开心;
}
}
六、项目代码
led.c
#include "led.h"
#include "sys.h"
//初始化GPIO函数
void led_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
//打开时钟
__HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
//调用GPIO初始化函数
gpio_initstruct.Pin = GPIO_PIN_8 | GPIO_PIN_9; // 两个LED对应的引脚
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)
{
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)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);
}
//点亮LED2的函数
void led2_on(void)
{
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)
{
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
beep.c
#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
#ifndef __BEEP_H__
#define __BEEP_H__
void beep_init(void);
void beep_on(void);
void beep_off(void);
#endif
gate.c
#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
#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
#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
#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
exti.c
#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
#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
task.c
#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");
}
}
task.h
#ifndef __TASKS_H__
#define __TASKS_H__
#include "sys.h"
void systick_isr(void);
void led1_task(void);
void led2_task(void);
void sensor_task(void);
#endif