嵌入式项目实战:从零开始做一个智能小夜灯

为什么选小夜灯作为入门项目

很多人想上手嵌入式开发,但一上来就搞无人机、机器人,结果代码写了一堆,板子还是不亮。不如从小东西做起。比如一个能感应光线、自动开关的智能小夜灯,成本不到30元,两天就能搞定。既能练手,又能用在卧室或走廊,实用又省电。

这个项目用STM32F103C8T6最小系统板(也就是常说的“蓝丸”),加上光敏电阻和一个白色LED,再配个简单的电路,就能跑起来。

硬件连接很简单

把光敏电阻一端接3.3V,另一端接STM32的PA0引脚,同时在这端和地之间加一个10kΩ的下拉电阻。这样,光线越暗,PA0读到的电压就越低。LED正极通过一个220Ω电阻接到PB1,负极接地。控制PB1输出高低电平,就能开关灯了。

代码怎么写

先配置PA0为模拟输入,用来读取ADC值。PB1设为推挽输出。主循环里不断读ADC,判断是否低于某个阈值,比如1500(具体值要看你环境亮度),如果低于就点亮LED。

#include "stm32f10x.h"

void ADC_Config(void) {
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    ADC_InitTypeDef ADC_InitStructure;
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfChannel = 1;
    ADC_Init(ADC1, &ADC_InitStructure);
    
    ADC_Cmd(ADC1, ENABLE);
    ADC_ResetCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while(ADC_GetCalibrationStatus(ADC1));
}

uint16_t Read_ADC(void) {
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
    return ADC_GetConversionValue(ADC1);
}

int main(void) {
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    ADC_Config();
    
    while (1) {
        uint16_t adc_val = Read_ADC();
        if (adc_val < 1500) {
            GPIO_SetBits(GPIOB, GPIO_Pin_1);  // 开灯
        } else {
            GPIO_ResetBits(GPIOB, GPIO_Pin_1); // 关灯
        }
        for(int i=0; i<100000; i++); // 简单延时
    }
}

调试中常遇到的问题

第一次烧录后灯不亮?先别急着换芯片。拿万用表量一下PB1对地有没有电压变化。有时候只是程序没下载成功,或者引脚配置错了。还有人把光敏电阻接反了——它没正负之分,但下拉电阻位置错了也会导致读数不准。

如果发现灯在临界点频繁闪,可以加个小延迟或者设置回差。比如亮的时候阈值设1500,灭的时候设1700,避免来回抖动。

下一步还能怎么升级

做通了基础功能,可以加个红外传感器,变成有人靠近才亮;或者连上ESP8266模块,用手机APP控制开关。甚至把数据上传到Home Assistant,看看一周开了多少次灯。这些扩展都不难,关键是先把最基础的读ADC、控IO弄明白。

嵌入式不怕做得小,就怕不动手。你家床头缺盏灯?现在就开始焊吧。