室内检测有无人系统完整设计方案

1. 系统目标

本终端实现室内有无人检测,基于STM32F103C8T6单片机,并通过 LCD1602 本地显示和串口 JSON 上报完成状态输出。具体功能如下:

  1. 通过 1 路 PIR 人体红外传感器(LD2402或AM312)检测室内是否有人。
  2. PIR 检测到人体(高电平)时立即点亮 LED,并在无人持续 5 秒 后熄灭 LED。
  3. LCD1602 实时显示房间有人/无人状态与 LED 状态。
  4. USART1(调试)与 USART2(上报)以 JSON 格式同步输出状态数据。
  5. 业务逻辑相互独立,通过共享全局变量传递状态。

2. 硬件组成与引脚分配

2.1 完整引脚表

根据现有代码,引脚和外设的分配严格遵循下表(PIR 为下拉输入):

引脚 功能 配置方式 说明
PA0 PIR1 GPIO Input Pull-down 人体红外输入,高电平=检测到人
PB8 LED GPIO Push-Pull Output LED 指示灯(高电平亮)
PB0 LCD_D4 GPIO Push-Pull Output LCD1602 数据位 4
PB1 LCD_D5 GPIO Push-Pull Output LCD1602 数据位 5
PB10 LCD_D6 GPIO Push-Pull Output LCD1602 数据位 6
PB11 LCD_D7 GPIO Push-Pull Output LCD1602 数据位 7
PB12 LCD_RS GPIO Push-Pull Output LCD1602 寄存器选择
PB13 LCD_EN GPIO Push-Pull Output LCD1602 使能脉冲
PA9 USART1_TX 复用推挽输出 调试串口(接 Virtual Terminal)
PA10 USART1_RX 浮空输入 调试串口接收
PA2 USART2_TX 复用推挽输出 JSON 状态上报(接 COMPIM)
PA3 USART2_RX 浮空输入 上报接收

:LCD1602 采用 4-bit 并行接口模式。其 RW 引脚直接接地(GND),处于只写模式,不占用 MCU 引脚。

3. STM32CubeMX 配置参数

3.1 时钟配置 (HSI)

  • 内置高速时钟 HSI:8 MHz
  • 系统主频 (SYSCLK):64 MHz(经 PLL 16倍频)

3.2 外设配置参数

  • TIM2:预分频系数(Prescaler)= 6399,自动重装载值(Period)= 9999。开启全局中断(产生 1 Hz 定时节拍)。
  • USART1 & USART2:波特率 9600,8位数据位,1位停止位,无校验。

4. 软件架构与库函数实现

4.1 核心 LCD1602 标准显示库

代码集成了自适应 4 位总线的 51 移植版高级显示函数,移除了原方案中未使用的函数(如原方案的 LCD_Print,统一改回代码实际使用的命名):

函数原型 作用说明
void LCD_Init(void); 初始化 LCD(含 4 位总线软件复位唤醒序列)
void LCD_WriteCommand(unsigned char Command); 写入控制命令(0x01清屏时自动追加 3ms 延迟)
void LCD_WriteData(unsigned char Data); 写入字符数据
void LCD_ShowString(unsigned char Line, unsigned char Column, unsigned char *String); 在指定行(1-2)和列(1-16)显示字符串

4.2 三模块独立运行调度逻辑

main.c 的主循环中,系统通过各自的触发机制独立运行,避免互相嵌套阻塞:

  1. 模块1:PIR 与 LED 控制
    • 持续轮询 PA0 的状态。
    • 触发有人时,立即点亮 PB8,并清除定时器计数。
    • 无人时由 TIM2 中断服务函数 HAL_TIM_PeriodElapsedCallback 负责每秒累加,满 5 秒自动关灯。
  2. 模块2:LCD 本地显示刷新
    • 基于软件时间戳节拍,固定每 300 ms 调用一次 LCD_Task_Refresh() 刷新屏幕。
    • 刷新内容通过长字符串后补空格的方式,自动覆盖旧字符残余,避免频繁调用清屏导致闪烁。
  3. 模块3:串口 JSON 数据上报
    • 采用状态变化事件触发机制。
    • 仅在检测到 presenceled_state 发生改变时,触发 Send_Status_JSON() 发送数据,大大减少串口拥堵。

5. JSON 数据上报格式

当状态改变时,USART1 和 USART2 会同时发送一行合规的标准 JSON 字符串:

JSON

{"id":"T5","presence":0,"led":0}
  • id:终端唯一标识,固定为 "T5"
  • presence:房间有人状态(1: 有人,0: 无人)。
  • led:当前灯光状态(1: 开启,0: 关闭)。
    proteus仿真在这里插入图片描述
    硬件接线在这里插入图片描述
    面包板效果
    在这里插入图片描述
    main.c代码(STM32CubeMX基于HAL库)
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : 终端5 室内有无人检测系统(4位总线 + 51经典标准显示库移植版)
  * 全部业务逻辑与高级显示库函数集成于本文件 USER CODE 区域
  *
  * 引脚分配:
  * PA0              —— PIR1(GPIO 输入,下拉)
  * PB8              —— LED(GPIO 推挽输出)
  * PB0  / PB1       —— LCD D4 / D5
  * PB10 / PB11      —— LCD D6 / D7
  * PB12             —— LCD RS
  * PB13             —— LCD EN
  * PA9  / PA10      —— USART1 TX/RX(调试 Virtual Terminal)
  * PA2  / PA3       —— USART2 TX/RX(上报 COMPIM)
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* ── PIR / LED ── */
#define DELAY_OFF_SECONDS   5U      /* 无人持续多少秒后关灯 */

/* ── LCD1602 引脚(全部在 GPIOB) ── */
#define LCD_D4_PIN          GPIO_PIN_0
#define LCD_D5_PIN          GPIO_PIN_1
#define LCD_D6_PIN          GPIO_PIN_10
#define LCD_D7_PIN          GPIO_PIN_11
#define LCD_RS_PIN          GPIO_PIN_12
#define LCD_EN_PIN          GPIO_PIN_13
#define LCD_GPIO_PORT       GPIOB

/* ── LED 引脚 ── */
#define LED_PIN             GPIO_PIN_8
#define LED_GPIO_PORT       GPIOB

/* ── PIR 引脚(仅保留PA0下拉) ── */
#define PIR1_PIN            GPIO_PIN_0
#define PIR_GPIO_PORT       GPIOA

/* ── 刷新节拍 ── */
#define LCD_REFRESH_MS      300U

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim2;
UART_HandleTypeDef huart1;
UART_HandleTypeDef huart2;

/* USER CODE BEGIN PV */

/* ── 系统状态变量 ── */
static volatile uint8_t  presence          = 0;   /* 1=有人  0=无人 */
static volatile uint8_t  led_state         = 0;   /* 1=亮    0=灭  */
static volatile uint32_t no_person_cnt     = 0;   /* 无人持续秒计数 */
static volatile uint8_t  counting_down     = 0;   /* 是否正在延时关灯 */

/* 记录上一次上报的状态,用于检测变化 */
static uint8_t last_reported_presence = 0;
static uint8_t last_reported_led      = 0;

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM2_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_USART2_UART_Init(void);

/* USER CODE BEGIN PFP */

/* ── LCD1602 移植标准库函数声明 ── */
static void Delay_us(uint32_t us);
static void LCD_SendNibble(uint8_t nibble);
static void LCD_SendByte(uint8_t byte, uint8_t rs);

void LCD_Init(void);
void LCD_WriteCommand(unsigned char Command);
void LCD_WriteData(unsigned char Data);
void LCD_SetCursor(unsigned char Line, unsigned char Column);
void LCD_ShowChar(unsigned char Line, unsigned char Column, unsigned char Char);
void LCD_ShowString(unsigned char Line, unsigned char Column, unsigned char *String);
void LCD_ShowNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line, unsigned char Column, int Number, unsigned char Length);
void LCD_ShowHexNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length);
void LCD_ShowBinNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length);
int  LCD_Pow(int X, int Y);

/* ── 业务功能函数 ── */
static void LCD_Task_Refresh(void);
static void Send_Status_JSON(void);

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/**
  * @brief  微秒级精确延迟(基于当前主频估计,规避 HAL_Delay 毫秒级阻塞)
  */
static void Delay_us(uint32_t us)
{
    uint32_t delay = us * 8; 
    while(delay--)
    {
        __NOP();
    }
}

/* ============================================================
 * 1. LCD1602 底层硬件驱动(4位并行模式)
 * ============================================================ */

static void LCD_SendNibble(uint8_t nibble)
{
    HAL_GPIO_WritePin(LCD_GPIO_PORT, LCD_D4_PIN, (nibble & 0x01) ? GPIO_PIN_SET : GPIO_PIN_RESET);
    HAL_GPIO_WritePin(LCD_GPIO_PORT, LCD_D5_PIN, (nibble & 0x02) ? GPIO_PIN_SET : GPIO_PIN_RESET);
    HAL_GPIO_WritePin(LCD_GPIO_PORT, LCD_D6_PIN, (nibble & 0x04) ? GPIO_PIN_SET : GPIO_PIN_RESET);
    HAL_GPIO_WritePin(LCD_GPIO_PORT, LCD_D7_PIN, (nibble & 0x08) ? GPIO_PIN_SET : GPIO_PIN_RESET);

    Delay_us(2); // 地址建立时间
    
    HAL_GPIO_WritePin(LCD_GPIO_PORT, LCD_EN_PIN, GPIO_PIN_SET);
    Delay_us(5); // 使能脉冲宽度
    HAL_GPIO_WritePin(LCD_GPIO_PORT, LCD_EN_PIN, GPIO_PIN_RESET); // 下降沿锁存
    
    Delay_us(2); // 保持时间
}

static void LCD_SendByte(uint8_t byte, uint8_t rs)
{
    HAL_GPIO_WritePin(LCD_GPIO_PORT, LCD_RS_PIN, rs ? GPIO_PIN_SET : GPIO_PIN_RESET);
    
    // 4位总线分高低半字节发送
    LCD_SendNibble(byte >> 4);
    LCD_SendNibble(byte & 0x0F);
    
    Delay_us(80); // 内部执行处理延迟
}

/* ============================================================
 * 2. 移植自 51 参考库的高级显示函数(针对 STM32 4位总线自适应)
 * ============================================================ */

void LCD_WriteCommand(unsigned char Command)
{
    LCD_SendByte(Command, 0);
    if (Command == 0x01) 
    {
        HAL_Delay(3); // 清屏需要额外长延迟
    }
}

void LCD_WriteData(unsigned char Data)
{
    LCD_SendByte(Data, 1);
}

void LCD_Init(void)
{
    HAL_Delay(50); // 上电稳定等待
    HAL_GPIO_WritePin(LCD_GPIO_PORT, LCD_RS_PIN, GPIO_PIN_RESET);

    // ── 4位并行总线软件复位唤醒序列 ──
    LCD_SendNibble(0x03); HAL_Delay(5);
    LCD_SendNibble(0x03); HAL_Delay(1);
    LCD_SendNibble(0x03); HAL_Delay(1);
    LCD_SendNibble(0x02); HAL_Delay(1); // 切换进入 4-bit 模式

    // ── 基础参数初始化配置 ──
    LCD_WriteCommand(0x28); // 4位总线,两行显示,5x7点阵
    LCD_WriteCommand(0x0C); // 显示开,光标关,闪烁关
    LCD_WriteCommand(0x06); // 写字符后光标自动加1
    LCD_WriteCommand(0x01); // 清屏
}

void LCD_SetCursor(unsigned char Line, unsigned char Column)
{
    if(Line == 1)
    {
        LCD_WriteCommand(0x80 | (Column - 1));
    }
    else
    {
        LCD_WriteCommand(0x80 | (Column - 1) + 0x40);
    }
}

void LCD_ShowChar(unsigned char Line, unsigned char Column, unsigned char Char)
{
    LCD_SetCursor(Line, Column);
    LCD_WriteData(Char);
}

void LCD_ShowString(unsigned char Line, unsigned char Column, unsigned char *String)
{
    unsigned char i;
    LCD_SetCursor(Line, Column);
    for(i = 0; String[i] != '\0'; i++)
    {
        LCD_WriteData(String[i]);
    }
}

int LCD_Pow(int X, int Y)
{
    unsigned char i;
    int Result = 1;
    for(i = 0; i < Y; i++)
    {
        Result *= X;
    }
    return Result;
}

void LCD_ShowNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length)
{
    unsigned char i;
    LCD_SetCursor(Line, Column);
    for(i = Length; i > 0; i--)
    {
        LCD_WriteData('0' + Number / LCD_Pow(10, i - 1) % 10);
    }
}

void LCD_ShowSignedNum(unsigned char Line, unsigned char Column, int Number, unsigned char Length)
{
    unsigned char i;
    unsigned int Number1;
    LCD_SetCursor(Line, Column);
    if(Number >= 0)
    {
        LCD_WriteData('+');
        Number1 = Number;
    }
    else
    {
        LCD_WriteData('-');
        Number1 = -Number;
    }
    for(i = Length; i > 0; i--)
    {
        LCD_WriteData('0' + Number1 / LCD_Pow(10, i - 1) % 10);
    }
}

void LCD_ShowHexNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length)
{
    unsigned char i;
    unsigned char SingleNumber;
    LCD_SetCursor(Line, Column);
    for(i = Length; i > 0; i--)
    {
        SingleNumber = Number / LCD_Pow(16, i - 1) % 16;
        if(SingleNumber < 10)
        {
            LCD_WriteData('0' + SingleNumber);
        }
        else
        {
            LCD_WriteData('A' + SingleNumber - 10);
        }
    }
}

void LCD_ShowBinNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length)
{
    unsigned char i;
    LCD_SetCursor(Line, Column);
    for(i = Length; i > 0; i--)
    {
        LCD_WriteData('0' + Number / LCD_Pow(2, i - 1) % 2);
    }
}

/* ============================================================
 * 3. 业务刷新与串口上报
 * ============================================================ */

static void LCD_Task_Refresh(void)
{
    // 利用高层 ShowString 接口,自动覆盖残余字符,更加简洁整齐
    if (presence)
        LCD_ShowString(1, 1, "Room:Person     ");
    else
        LCD_ShowString(1, 1, "Room:Empty      ");

    if (led_state)
        LCD_ShowString(2, 1, "LED :ON         ");
    else
        LCD_ShowString(2, 1, "LED :OFF        ");
}

static void Send_Status_JSON(void)
{
    char buf[48];
    int  len;

    len = snprintf(buf, sizeof(buf),
                   "{\"id\":\"T5\",\"presence\":%d,\"led\":%d}\r\n",
                   (int)presence,
                   (int)led_state);

    HAL_UART_Transmit(&huart1, (uint8_t *)buf, (uint16_t)len, 100);
    HAL_UART_Transmit(&huart2, (uint8_t *)buf, (uint16_t)len, 100);
}

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  HAL_Init();
  SystemClock_Config();

  MX_GPIO_Init();
  MX_TIM2_Init();
  MX_USART1_UART_Init();
  MX_USART2_UART_Init();

  /* USER CODE BEGIN 2 */

  HAL_TIM_Base_Start_IT(&htim2);
  LCD_Init();

  // 使用新标准的库接口显示欢迎界面
  LCD_ShowString(1, 1, "  Terminal 5  ");
  LCD_ShowString(2, 1, "  Initializing");
  HAL_Delay(1000);
  LCD_WriteCommand(0x01); // 清屏

  /* 上电发送一次初始状态 */
  Send_Status_JSON();
  last_reported_presence = presence;
  last_reported_led      = led_state;

  uint32_t lcd_last_tick = HAL_GetTick();

  /* USER CODE END 2 */

  while (1)
  {
    /* USER CODE BEGIN 3 */

    /* ── 模块1:单引脚 PIR 检测 (PA0 下拉) + LED 控制 ── */
    GPIO_PinState pir1 = HAL_GPIO_ReadPin(PIR_GPIO_PORT, PIR1_PIN);

    if (pir1 == GPIO_PIN_SET) 
    {
        presence      = 1;
        led_state     = 1;
        no_person_cnt = 0;
        counting_down = 0;
        HAL_GPIO_WritePin(LED_GPIO_PORT, LED_PIN, GPIO_PIN_SET);
    }
    else
    {
        presence = 0;
        if (led_state == 1 && counting_down == 0)
        {
            counting_down = 1;
            no_person_cnt = 0;
        }
    }

    /* ── 模块2:LCD1602 刷新 ── */
    if ((HAL_GetTick() - lcd_last_tick) >= LCD_REFRESH_MS)
    {
        lcd_last_tick = HAL_GetTick();
        LCD_Task_Refresh();
    }

    /* ── 模块3:USART 上报 ── */
    if (presence != last_reported_presence || led_state != last_reported_led)
    {
        last_reported_presence = presence;
        last_reported_led      = led_state;
        Send_Status_JSON();
    }

    /* USER CODE END 3 */
  }
}

/**
  * @brief System Clock Configuration
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  RCC_OscInitStruct.OscillatorType      = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState            = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState        = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource       = RCC_PLLSOURCE_HSI_DIV2;
  RCC_OscInitStruct.PLL.PLLMUL          = RCC_PLL_MUL16;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  RCC_ClkInitStruct.ClockType      = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
                                   | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource   = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider  = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

static void MX_TIM2_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig     = {0};

  htim2.Instance               = TIM2;
  htim2.Init.Prescaler         = 6399;
  htim2.Init.CounterMode       = TIM_COUNTERMODE_UP;
  htim2.Init.Period            = 9999;
  htim2.Init.ClockDivision     = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode     = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }

  /* 配置并开启 TIM2 的 NVIC 中断 */
  HAL_NVIC_SetPriority(TIM2_IRQn, 2, 0);
  HAL_NVIC_EnableIRQ(TIM2_IRQn);
}

static void MX_USART1_UART_Init(void)
{
  huart1.Instance          = USART1;
  huart1.Init.BaudRate     = 9600;
  huart1.Init.WordLength   = UART_WORDLENGTH_8B;
  huart1.Init.StopBits     = UART_STOPBITS_1;
  huart1.Init.Parity       = UART_PARITY_NONE;
  huart1.Init.Mode         = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl    = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
}

static void MX_USART2_UART_Init(void)
{
  huart2.Instance          = USART2;
  huart2.Init.BaudRate     = 9600;
  huart2.Init.WordLength   = UART_WORDLENGTH_8B;
  huart2.Init.StopBits     = UART_STOPBITS_1;
  huart2.Init.Parity       = UART_PARITY_NONE;
  huart2.Init.Mode         = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl    = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
}

static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  HAL_GPIO_WritePin(GPIOB,
                    GPIO_PIN_0  | GPIO_PIN_1  | GPIO_PIN_10 | GPIO_PIN_11 |
                    GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_8,
                    GPIO_PIN_RESET);

  /* PA0:PIR 输入,配置为下拉模式 */
  GPIO_InitStruct.Pin  = PIR1_PIN;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLDOWN; 
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* PB0/PB1/PB10/PB11(PB12/PB13/PB8) 高频推挽输出 */
  GPIO_InitStruct.Pin   = GPIO_PIN_0  | GPIO_PIN_1  | GPIO_PIN_10 | GPIO_PIN_11 |
                          GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_8;
  GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull  = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}

/* USER CODE BEGIN 4 */

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance != TIM2) return;

    if (counting_down)
    {
        no_person_cnt++;
        if (no_person_cnt >= DELAY_OFF_SECONDS)
        {
            led_state     = 0;
            counting_down = 0;
            no_person_cnt = 0;
            HAL_GPIO_WritePin(LED_GPIO_PORT, LED_PIN, GPIO_PIN_RESET);
        }
    }
}

/* USER CODE END 4 */

void Error_Handler(void)
{
  __disable_irq();
  while (1) {}
}

#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif /* USE_FULL_ASSERT */ 
``

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐