室内检测有无人系统完整设计方案[资料下载](https://wwapn.lanzoul.com/b01d71wsba密码:1aw4)1. 系统目标本终端实现室内有无人检测基于STM32F103C8T6单片机并通过 LCD1602 本地显示和串口 JSON 上报完成状态输出。具体功能如下通过1 路 PIR人体红外传感器LD2402或AM312检测室内是否有人。PIR 检测到人体高电平时立即点亮 LED并在无人持续5 秒后熄灭 LED。LCD1602 实时显示房间有人/无人状态与 LED 状态。USART1调试与 USART2上报以 JSON 格式同步输出状态数据。业务逻辑相互独立通过共享全局变量传递状态。2. 硬件组成与引脚分配2.1 完整引脚表根据现有代码引脚和外设的分配严格遵循下表PIR 为下拉输入引脚功能配置方式说明PA0PIR1GPIO InputPull-down人体红外输入高电平检测到人PB8LEDGPIO Push-Pull OutputLED 指示灯高电平亮PB0LCD_D4GPIO Push-Pull OutputLCD1602 数据位 4PB1LCD_D5GPIO Push-Pull OutputLCD1602 数据位 5PB10LCD_D6GPIO Push-Pull OutputLCD1602 数据位 6PB11LCD_D7GPIO Push-Pull OutputLCD1602 数据位 7PB12LCD_RSGPIO Push-Pull OutputLCD1602 寄存器选择PB13LCD_ENGPIO Push-Pull OutputLCD1602 使能脉冲PA9USART1_TX复用推挽输出调试串口接 Virtual TerminalPA10USART1_RX浮空输入调试串口接收PA2USART2_TX复用推挽输出JSON 状态上报接 COMPIMPA3USART2_RX浮空输入上报接收注LCD1602 采用 4-bit 并行接口模式。其 RW 引脚直接接地GND处于只写模式不占用 MCU 引脚。3. STM32CubeMX 配置参数3.1 时钟配置 (HSI)内置高速时钟 HSI8 MHz系统主频 (SYSCLK)64 MHz经 PLL 16倍频3.2 外设配置参数TIM2预分频系数Prescaler6399自动重装载值Period9999。开启全局中断产生 1 Hz 定时节拍。USART1 USART2波特率96008位数据位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的主循环中系统通过各自的触发机制独立运行避免互相嵌套阻塞模块1PIR 与 LED 控制持续轮询 PA0 的状态。触发有人时立即点亮 PB8并清除定时器计数。无人时由 TIM2 中断服务函数HAL_TIM_PeriodElapsedCallback负责每秒累加满 5 秒自动关灯。模块2LCD 本地显示刷新基于软件时间戳节拍固定每300 ms调用一次LCD_Task_Refresh()刷新屏幕。刷新内容通过长字符串后补空格的方式自动覆盖旧字符残余避免频繁调用清屏导致闪烁。模块3串口 JSON 数据上报采用状态变化事件触发机制。仅在检测到presence或led_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 —— PIR1GPIO 输入下拉 * PB8 —— LEDGPIO 推挽输出 * 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; } } /* ── 模块2LCD1602 刷新 ── */ if ((HAL_GetTick() - lcd_last_tick) LCD_REFRESH_MS) { lcd_last_tick HAL_GetTick(); LCD_Task_Refresh(); } /* ── 模块3USART 上报 ── */ 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); /* PA0PIR 输入配置为下拉模式 */ 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 */