/*
* Smart Streetlight System - Wokwi Simulation
* Board: STM32 Nucleo-C031C6
* Features: LDR (Day/Night), PIR (Motion), PWM Dimming, Energy Calc
*/
#include "main.h"
#include <stdio.h>
/* --- 用户配置区 (User Configuration) --- */
// 如果你发现光照越强,数值越小,请把下面的 0 改成 1
#define LDR_INVERTED_LOGIC 0
// 白天/夜晚的阈值 (0 - 4095)
// 建议先运行代码看串口打印的数值,再调整这个值
#define DAY_NIGHT_THRESHOLD 2000
// PWM 亮度设置 (0 - 1000)
#define PWM_BRIGHTNESS_MAX 1000 // 有人时 (100%)
#define PWM_BRIGHTNESS_DIM 200 // 无人时 (20%)
#define PWM_OFF 0 // 白天
/* --- 全局变量 --- */
ADC_HandleTypeDef hadc1;
TIM_HandleTypeDef htim1;
UART_HandleTypeDef huart2;
float total_energy = 0.0; // 累计能耗 (焦耳)
/* --- 函数声明 --- */
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_ADC1_Init(void);
static void MX_TIM1_Init(void);
/* --- 重定向 printf 到串口 --- */
int _write(int file, char *ptr, int len) {
HAL_UART_Transmit(&huart2, (uint8_t *)ptr, len, 100);
return len;
}
/* --- 主程序 --- */
int main(void)
{
// 1. 初始化系统
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_ADC1_Init();
MX_TIM1_Init();
// 2. 启动 PWM 和 ADC
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
printf("System Started! Waiting for sensors...\r\n");
uint32_t adc_val = 0;
int is_daytime = 0;
int motion_detected = 0;
int target_pwm = 0;
float voltage = 0.0;
while (1)
{
// --- A. 读取传感器数据 ---
// 1. 读取光敏 (LDR)
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, 10);
adc_val = HAL_ADC_GetValue(&hadc1);
// 2. 读取人体感应 (PIR) - 假设接在 PA1
// 如果你的 PIR 接在 PA4,请改为 GPIO_PIN_4
motion_detected = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1);
// --- B. 逻辑判断 ---
/*
Debug 关键点:
先打印出当前的 ADC 值,这样你就知道现在的光照对应什么数字了。
*/
// 将 ADC 值转换为大致电压方便理解 (3.3V 参考)
voltage = (adc_val * 3.3) / 4095.0;
// 判断白天还是黑夜
#if LDR_INVERTED_LOGIC == 1
// 逻辑反转模式:数值越小越亮
if (adc_val < DAY_NIGHT_THRESHOLD) is_daytime = 1;
else is_daytime = 0;
#else
// 标准模式:数值越大越亮
if (adc_val > DAY_NIGHT_THRESHOLD) is_daytime = 1;
else is_daytime = 0;
#endif
// 决策输出亮度
if (is_daytime) {
target_pwm = PWM_OFF; // 白天强制关灯
} else {
// 晚上
if (motion_detected) {
target_pwm = PWM_BRIGHTNESS_MAX; // 有人:全亮
} else {
target_pwm = PWM_BRIGHTNESS_DIM; // 无人:微亮
}
}
// --- C. 执行控制 ---
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, target_pwm);
// --- D. 简易能耗计算 (假设 1000 PWM = 10W) ---
// 每次循环约 100ms (0.1s)
float power_now = (target_pwm / 1000.0) * 10.0;
total_energy += power_now * 0.1;
// --- E. 串口调试信息 ---
// 这里的输出非常重要,请在 Wokwi 下方的 Serial Monitor 查看
printf("LDR: %4lu (%.2fV) | Mode: %s | Motion: %d | LED: %4d | Energy: %.2f J\r\n",
adc_val, voltage,
is_daytime ? "DAY " : "NIGHT",
motion_detected,
target_pwm,
total_energy);
HAL_Delay(100); // 10Hz 刷新率
}
}
/* ------------------------------------------------------------------
* 下面是 STM32 底层初始化代码 (Boilerplate)
* 对应 Wokwi 的 Nucleo-C031C6 默认引脚配置
* ------------------------------------------------------------------ */
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.HSIDiv = RCC_HSI_DIV1;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { while(1); }
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { while(1); }
}
static void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV1;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.LowPowerAutoPowerOff = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc1.Init.SamplingTimeCommon1 = ADC_SAMPLETIME_1CYCLE_5;
hadc1.Init.SamplingTimeCommon2 = ADC_SAMPLETIME_1CYCLE_5;
hadc1.Init.OversamplingMode = DISABLE;
if (HAL_ADC_Init(&hadc1) != HAL_OK) { while(1); }
sConfig.Channel = ADC_CHANNEL_0; // PA0
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLINGTIME_COMMON_1;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { while(1); }
}
static void MX_TIM1_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
htim1.Instance = TIM1;
htim1.Init.Prescaler = 48-1; // 48MHz / 48 = 1MHz
htim1.Init.CounterPeriod = 1000-1; // 1kHz PWM
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK) { while(1); }
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK) { while(1); }
if (HAL_TIM_PWM_Init(&htim1) != HAL_OK) { while(1); }
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK) { while(1); } // PA8
HAL_TIM_MspPostInit(&htim1);
}
static void MX_USART2_UART_Init(void)
{
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
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;
huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart2.Init.ClockPrescaler = UART_PRESCALER_DIV1;
huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart2) != HAL_OK) { while(1); }
}
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/* PIR Input Pin Configuration: PA1 */
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* htim)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(htim->Instance==TIM1)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
/**TIM1 GPIO Configuration
PA8 ------> TIM1_CH1
*/
GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
}