项目编号:STM32-T005
项目名称:基于STM32的智能车尾灯
【摘要】
本项目设计了一种基于STM32F103单片机的智能车尾灯系统,旨在提升夜间骑行的安全性与个性化体验。系统融合MPU6050加速度与陀螺仪模块,实现对自行车运动状态的实时监测,如刹车、转弯及正常行驶;结合光敏电阻感知环境光照强度,智能判断是否需要启用尾灯照明。根据传感器采集的数据,STM32控制矩阵LED灯自动调整显示模式,包括闪烁频率和图案变化,从而有效提醒后方车辆并保障骑行者安全。同时,系统集成蓝牙模块,可与手机APP连接,实现闪烁频率、图案样式等个性化设置,增强用户交互体验。电源部分采用干电池供电,安装简便、功耗低。整体设计集成度高、功能实用,适用于智能出行、绿色交通等领域,具有较强的推广价值与市场前景。
【所用硬件】
1、STM32F103C8T6最小系统板
STM32F103C8T6最小系统板是基于意法半导体(ST)Cortex-M3内核的微控制器开发板,核心芯片为STM32F103C8T6,主频72MHz,具有64KB Flash和20KB SRAM,满足中等复杂度的嵌入式应用需求。该板集成USB、USART、SPI、I2C等通信接口,支持多种外设扩展。最小系统板包含必要的外围电路,如8MHz晶振、复位电路、电源管理(3.3V稳压),并提供GPIO引脚引出,方便连接传感器、显示屏等模块。其低功耗、高性能特性使其广泛应用于智能硬件、工业控制等领域。在本设计中,它作为主控单元,负责数据采集、处理及外设控制,确保电子秤系统的稳定运行。
2、mpu6050陀螺仪模块

MPU6050陀螺仪模块是一款常用的六轴传感器,内部集成了三轴加速度计和三轴陀螺仪,能够实时测量物体的加速度与角速度信息。该模块通过I2C接口与单片机或其他控制器进行通信,体积小巧、功耗低,适用于姿态检测、运动跟踪和机器人平衡控制等场景。由于其数据稳定、价格适中,MPU6050在智能车、无人机以及可穿戴设备中应用十分广泛。
3、MAX7219点阵屏2*2*4模块

MAX7219点阵屏224模块是一种基于MAX7219驱动芯片的LED点阵显示模块,由2行2列共4个点阵单元组合而成,通常采用8×8点阵屏拼接显示。模块通过SPI接口与单片机通信,仅需三根控制线即可驱动多个点阵级联,支持显示文字、数字、图形和动画。其特点是电路简洁、显示效果清晰、扩展方便,常用于电子钟、信息显示屏及创意电子项目中,适合学习和实际应用。
4、蓝牙模块

HC-05蓝牙模块是一款常用的串口通信蓝牙模块,支持蓝牙2.0协议,采用主从一体设计,可通过AT指令自由配置工作模式。它通过UART接口与单片机或电脑连接,实现无线串口数据传输,使用方便、成本低廉、兼容性好。该模块传输稳定、距离可达10米左右,广泛应用于无线控制、小型物联网设备、智能小车和数据采集系统中,是嵌入式开发与蓝牙通信学习的常见选择。
5、光敏电阻模块

光敏电阻模块是一种基于光敏电阻传感器的检测模块,能够根据环境光强变化调整电阻值,从而实现光照强度的检测。模块通常集成电位器、电阻和比较器电路,可输出模拟信号或数字信号,便于与单片机连接。它响应速度快、使用简单,常用于自动照明、智能小车循迹、光控开关及光照监测等应用场景,是入门级传感器模块中常见且实用的一种。
6、干电池

4节干电池模块是一种常见的电源供电模块,一般由电池盒和导线组成,可串联放置四节1.5V干电池,输出电压为6V,适合作为单片机、传感器模块或小型电机等低功耗电路的供电电源。该模块体积小巧、安装方便,通常配有开关和电源接口,使用灵活,能够为实验项目、电子制作和便携设备提供稳定的直流电源。
【系统框架图】

【软件流程图】

【核心代码展示】
#include "app_task.h"
#include "main.h"
#include "m5310a.h"
#include "delay.h"
#include "usart.h"
#include "timer.h"
#include "esp8266.h"
#include "io.h"
#include "led.h"
#include "oled.h"
#include "sim800l.h"
#include "rtc.h"
#include "Encoder.h"
#include "beep.h"
#include "mpu6050.h"
#include "inv_mpu.h"
#include "adc.h"
#include "key.h"
#include "max7219.h"
#include "Preprocessdesign.h"
u8 str[100];
PAGE page;
POINT point;
CAR car;
TaskControlBlock TaskList[MAX_TASKS] = {0};
uint8_t RegisteredTasks = 0; // 已注册任务数
uint32_t SystemTick = 0; // 系统时间基准
// 任务注册函数
int8_t Task_Register(uint8_t id, uint32_t interval, void (*task_func)(void *), void *param)
{
if (RegisteredTasks >= MAX_TASKS)
return -1;
TaskList[RegisteredTasks].TaskID = id;
TaskList[RegisteredTasks].PollingInterval = interval;
TaskList[RegisteredTasks].TimerCounter = 0;
TaskList[RegisteredTasks].RunFlag = 0;
TaskList[RegisteredTasks].EnableFlag = 1;
TaskList[RegisteredTasks].TaskHook = task_func;
TaskList[RegisteredTasks].TaskParam = param;
TaskList[RegisteredTasks].Delay = 0;
RegisteredTasks++;
return 0;
}
// 任务使能控制
void Task_Enable(uint8_t id, uint8_t enable)
{
for (int i = 0; i < RegisteredTasks; i++)
{
if (TaskList[i].TaskID == id)
{
TaskList[i].EnableFlag = enable;
break;
}
}
}
/**
* @brief LED闪烁任务
*
* @param pt
*/
void Led_Task(void *pt)
{
LED1 = !LED1;
}
void MPU6050_Task(void *pt)
{
mpu_dmp_get_data(&car.mpu.Pitch,&car.mpu.Roll,&car.mpu.Yaw);
}
void MAX7219_Task(void *pt)
{
u8 i, j;
for (i = 1; i < 9; i++) // 点阵屏的第1行到第8行
{
MAX7219_CS = 0;
if (car.modle == ONE)
{
for (j = 0; j < 4; j++) // 因为有4个点阵屏,所以同一行需要发4次
{
switch (j) // 用switch只是为了方便看而已
{
case 0:
MAX7219_Write_byte(i);
if(car.direction == LEFT) MAX7219_Write_byte(_4x7Num[10][i - 1]);
else MAX7219_Write_byte(_4x7Num[22][i - 1]);
// 控制列的高低电平,行为低电平了,所以列为1时灯亮,如数据为
// 0xF0 那么这一行就是4个亮4个灭
break;
case 1:
MAX7219_Write_byte(i);
if(car.direction == STOP) MAX7219_Write_byte(_4x7Num[12][i - 1]);
else MAX7219_Write_byte(_4x7Num[22][i - 1]);
break;
case 2:
MAX7219_Write_byte(i);
if(car.direction == STOP) MAX7219_Write_byte(_4x7Num[13][i - 1]);
else MAX7219_Write_byte(_4x7Num[22][i - 1]);
break;
case 3:
MAX7219_Write_byte(i);
if(car.direction == RIGHT) MAX7219_Write_byte(_4x7Num[11][i - 1]);
else MAX7219_Write_byte(_4x7Num[22][i - 1]);
break;
}
}
}
else if (car.modle == TWO)
{
for (j = 0; j < 4; j++) // 因为有4个点阵屏,所以同一行需要发4次
{
switch (j) // 用switch只是为了方便看而已
{
case 0:
MAX7219_Write_byte(i);
if(car.direction == LEFT) MAX7219_Write_byte(_4x7Num[14][i - 1]);
else MAX7219_Write_byte(_4x7Num[22][i - 1]);
// 控制列的高低电平,行为低电平了,所以列为1时灯亮,如数据为
// 0xF0 那么这一行就是4个亮4个灭
break;
case 1:
MAX7219_Write_byte(i);
if(car.direction == STOP) MAX7219_Write_byte(_4x7Num[16][i - 1]);
else MAX7219_Write_byte(_4x7Num[22][i - 1]);
break;
case 2:
MAX7219_Write_byte(i);
if(car.direction == STOP) MAX7219_Write_byte(_4x7Num[17][i - 1]);
else MAX7219_Write_byte(_4x7Num[22][i - 1]);
break;
case 3:
MAX7219_Write_byte(i);
if(car.direction == RIGHT) MAX7219_Write_byte(_4x7Num[15][i - 1]);
else MAX7219_Write_byte(_4x7Num[22][i - 1]);
break;
}
}
}
else if (car.modle == THREE)
{
for (j = 0; j < 4; j++) // 因为有4个点阵屏,所以同一行需要发4次
{
switch (j) // 用switch只是为了方便看而已
{
case 0:
MAX7219_Write_byte(i);
if(car.direction == LEFT) MAX7219_Write_byte(_4x7Num[18][i - 1]);
else MAX7219_Write_byte(_4x7Num[22][i - 1]);
// 控制列的高低电平,行为低电平了,所以列为1时灯亮,如数据为
// 0xF0 那么这一行就是4个亮4个灭
break;
case 1:
MAX7219_Write_byte(i);
if(car.direction == STOP) MAX7219_Write_byte(_4x7Num[20][i - 1]);
else MAX7219_Write_byte(_4x7Num[22][i - 1]);
break;
case 2:
MAX7219_Write_byte(i);
if(car.direction == STOP) MAX7219_Write_byte(_4x7Num[21][i - 1]);
else MAX7219_Write_byte(_4x7Num[22][i - 1]);
break;
case 3:
MAX7219_Write_byte(i);
if(car.direction == RIGHT) MAX7219_Write_byte(_4x7Num[19][i - 1]);
else MAX7219_Write_byte(_4x7Num[22][i - 1]);
break;
}
}
}
MAX7219_CS = 1;
}
}
void Light_Task(void *pt)
{
u8 i;
car.light = 15 - (Get_Adc_Average(ADC_Channel_6, 5) * 15 / 4095);
MAX7219_CS = 0;
for (i = 0; i < LEDCOUNT; i++)
{
MAX7219_Write_Command(DECODEMODE, 0X00);
/*译码寄存器:1使用BCD码 0不使用(数码管的话建议用BCD码)*/
}
MAX7219_CS = 1; // 这里发送4次后CS拉高,那么4个点阵屏都收到数据,然后加载到寄存器中
MAX7219_CS = 0;
for (i = 0; i < LEDCOUNT; i++)
{
MAX7219_Write_Command(INTENSITY, car.light);
/*亮度控制:0x00~0x0F 0是最暗 0xFs是最亮*/
}
MAX7219_CS = 1;
MAX7219_CS = 0;
for (i = 0; i < LEDCOUNT; i++)
{
MAX7219_Write_Command(SCANLIMT, 0X07);
/*点阵屏的行数 数码管的段位 0是1行,7是8行*/
}
MAX7219_CS = 1;
MAX7219_CS = 0;
for (i = 0; i < LEDCOUNT; i++)
{
MAX7219_Write_Command(SHUTDOWN, 0X01);
/*掉电模式:0掉电模式 1正常模式*/
}
MAX7219_CS = 1;
MAX7219_CS = 0;
for (i = 0; i < LEDCOUNT; i++)
{
MAX7219_Write_Command(DISPLAYTEST, 0X00);
/*显示寄存器:0普通模式 1测试模式*/
}
MAX7219_CS = 1;
}
void Flicker_Task(void *pt)
{
static u16 delay=0,flag=0;
u8 i, j;
if (delay++ == car.Freq)
{
delay = 0;
flag++;
if(flag == 1) Task_Enable(3,0);
else{
flag=0;
Task_Enable(3,1);
}
for (i = 1; i < 9; i++) // 点阵屏的第1行到第8行
{
MAX7219_CS = 0;
for (j = 0; j < 4; j++) // 因为有4个点阵屏,所以同一行需要发4次
{
switch (j) // 用switch只是为了方便看而已
{
case 0:
MAX7219_Write_byte(i);
MAX7219_Write_byte(_4x7Num[22][i - 1]);
// 控制列的高低电平,行为低电平了,所以列为1时灯亮,如数据为
// 0xF0 那么这一行就是4个亮4个灭
break;
case 1:
MAX7219_Write_byte(i);
MAX7219_Write_byte(_4x7Num[22][i - 1]);
break;
case 2:
MAX7219_Write_byte(i);
MAX7219_Write_byte(_4x7Num[22][i - 1]);
break;
case 3:
MAX7219_Write_byte(i);
MAX7219_Write_byte(_4x7Num[22][i - 1]);
break;
}
}
MAX7219_CS = 1;
}
}
}
void AngleControl_Task(void *pt)
{
static MPU6050 last_angle;
static u16 delay=0;
if(car.mpu.Yaw-last_angle.Yaw>15){
delay=0;
car.direction = LEFT;
}
else if(car.mpu.Yaw-last_angle.Yaw<-15){
delay=0;
car.direction = RIGHT;
}
if(car.mpu.Roll-last_angle.Roll<-15){
delay=0;
car.direction = STOP;
}
last_angle.Roll = car.mpu.Roll;
last_angle.Yaw = car.mpu.Yaw;
if(car.direction != RUN)
{
if(delay++ > 15){
delay=0;
car.direction = RUN;
}
}
}
void Task_Init(void)
{
Task_Register(1, 1000, Led_Task, NULL);
Task_Register(2, 1, MPU6050_Task, NULL);
Task_Register(3, 100, MAX7219_Task, NULL);
Task_Register(4, 1, Flicker_Task, NULL);
Task_Register(5, 200, AngleControl_Task, NULL);
Task_Register(6, 300, Light_Task, NULL);
}
void Task_Run(void)
{
for (int i = 0; i < RegisteredTasks; i++)
{
if (TaskList[i].RunFlag && TaskList[i].EnableFlag)
{
// 执行任务函数
if (TaskList[i].TaskHook != NULL)
{
TaskList[i].TaskHook(TaskList[i].TaskParam);
}
// 清除运行标志
TaskList[i].RunFlag = 0;
}
}
}
void TaskTime(void)
{
static u32 tick_time = 0;
tick_time++;
// 遍历任务列表更新计数器
for (int i = 0; i < RegisteredTasks; i++)
{
if (TaskList[i].EnableFlag)
{
TaskList[i].TimerCounter++;
// 达到时间间隔时设置运行标志
if (TaskList[i].TimerCounter >= TaskList[i].PollingInterval)
{
TaskList[i].RunFlag = 1;
TaskList[i].TimerCounter = 0;
}
}
}
}
【原理图】

【硬件实物图】

【实物演示】
STM32-T005-基于stm32智能车尾灯