【开源】基于STM32的智能车尾灯

项目编号: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智能车尾灯

相关推荐
编程墨客5 小时前
STM32与Modbus RTU协议实战开发指南-fc3ab6a453
stm32·单片机·嵌入式硬件
LeenixP5 小时前
STM32的VSCode下开发环境搭建
vscode·stm32·单片机·嵌入式硬件·arm
LeoZY_6 小时前
开源超级终端PuTTY改进之:增加点对点网络协议IocHub,实现跨网段远程登录
运维·网络·stm32·嵌入式硬件·网络协议·运维开发
文火冰糖的硅基工坊6 小时前
[硬件电路-271]: RS-232 电平转换芯片MAX232AESE 功能概述与管脚定义
单片机·嵌入式硬件·系统架构·信号处理·跨学科融合
<man>7 小时前
STM32_03_库函数
stm32·单片机·嵌入式硬件
樊少泽7 小时前
单片机技术(关于端口中断)
stm32·单片机·嵌入式硬件
意法半导体STM327 小时前
STM32 USBx Device HID standalone 移植示例 LAT1466
javascript·stm32·嵌入式硬件·device·hid·standalone·usbx
wei-dong-183797540088 小时前
嵌入式硬件工程师:绝缘栅型场效应管
嵌入式硬件·场效应管
漫夜8558 小时前
day02-电路基础2
单片机·嵌入式硬件