【开源】基于STM32的智能骑行头盔设计

该文章已生成可运行项目,

项目编号:STM32-T010

项目名称:基于STM32的智能骑行头盔设计

【摘要】

        本项目设计了一种基于STM32F103C8T6单片机的智能骑行头盔系统,集成多种传感器与智能控制功能,旨在提升骑行过程中的安全性与智能化体验。系统通过光敏电阻实时检测环境光照强度,当光照值低于设定阈值时自动开启LED照明灯,保障夜间骑行视野;心率检测模块实时监测佩戴者生理状态,若心率异常超过阈值,蜂鸣器立即报警。MPU6050陀螺仪模块用于识别头部转向动作,系统可智能点亮对应方向指示灯,提醒后方车辆注意。超声波模块用于检测后方来车距离,若小于设定阈值,OLED屏幕即时显示“后方来车”提示,增强后方预警功能。用户可通过按键设定光照、心率和距离等阈值,OLED模块同步显示当前环境数据和系统状态。该系统结构紧凑、功能全面,适用于日常骑行及长途骑行场景,具有良好的用户体验和广阔的市场应用前景。

【所用硬件】

1、STM32F103C8T6最小系统板

        STM32F103C8T6最小系统板是基于意法半导体(ST)Cortex-M3内核的微控制器开发板,核心芯片为STM32F103C8T6,主频72MHz,具有64KB Flash和20KB SRAM,满足中等复杂度的嵌入式应用需求。该板集成USB、USART、SPI、I2C等通信接口,支持多种外设扩展。最小系统板包含必要的外围电路,如8MHz晶振、复位电路、电源管理(3.3V稳压),并提供GPIO引脚引出,方便连接传感器、显示屏等模块。其低功耗、高性能特性使其广泛应用于智能硬件、工业控制等领域。在本设计中,它作为主控单元,负责数据采集、处理及外设控制,确保电子秤系统的稳定运行。

2、OLED显示模块

        OLED(有机发光二极管)显示屏模块是一种自发光显示器件,无需背光,具有高对比度、宽视角和快速响应的特点。本设计采用的OLED模块通常为0.96英寸或1.3英寸,分辨率为128×64像素,支持I2C或SPI通信协议,可直接与STM32等微控制器连接。其工作电压为3.3V~5V,功耗低,适合便携式设备。

3、轻触按键

        轻触按键(Tactile Switch)是一种瞬时接触式机械开关,通过按压触发导通,松开后自动复位。其结构简单、体积小巧,通常采用金属弹片或导电橡胶实现触点通断,具有手感清晰、寿命长(可达10万次以上操作)的特点。工作电压和电流范围宽(如12V/50mA),可直接与STM32等微控制器的GPIO引脚连接,需配合上拉/下拉电阻使用以避免信号浮动。

4、光敏电阻模块

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

5、照明LED灯

       LED灯模块是一种常用的光源驱动模块,内部集成了蓝色发光二极管及限流电阻,只需提供稳定的5V直流电源即可点亮,使用方便。该模块发出的蓝光亮度高、色彩纯净,常用于单片机实验、光控提示、装饰照明以及状态指示等场景。模块体积小巧,接口简单,既可单独作为指示灯使用,也能组合多颗实现丰富的视觉效果,具有寿命长、能耗低和响应速度快等优点,适合电子学习和小型智能系统应用。

6、MAX30102心率血氧检测模块

        MAX30102模块是一款集成脉搏血氧和心率监测功能的传感器模块,内部包含高效光学器件和低噪声信号处理电路,能够通过红光和红外光的透射或反射测量血氧饱和度及心率信号。其功耗低,体积小,适合便携式、可穿戴设备应用,常用于健康监测、运动追踪和医疗辅助系统中,具有检测精度高、响应速度快和通信接口简单等特点,便于与单片机、STM32等微控制器进行数据交互和二次开发。

7、蜂鸣器模块

        鸣器模块是一种常见的声音报警装置,广泛应用于电子设备中用于提示或报警功能。该模块通常由一个蜂鸣器和驱动电路组成,能发出高频的蜂鸣声。按照驱动方式可分为有源蜂鸣器和无源蜂鸣器两种类型。有源蜂鸣器只需提供直流电压即可发声,控制简单,适用于固定频率提示场景;无源蜂鸣器则需要外部提供特定频率的驱动信号才能工作,适用于需要调节音调或生成特定声音模式的场合。模块通常采用5V或3.3V供电,可通过三极管或专用驱动芯片控制启停,响应速度快,可靠性高,常用于智能设备、安防系统、工业仪表等场景中。

8、MPU6050陀螺仪模块

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

9、超声波模块

超声波模块是一种利用超声波测距原理的传感器模块,常通过发射超声波并接收其反射信号来计算与目标物体的距离。该模块具有结构简单、成本低廉、精度较高和抗光线干扰能力强等优点,广泛应用于机器人避障、汽车倒车雷达、液位检测和智能家居等领域。其工作电压低,接口方式简单,易于与单片机、STM32等控制器结合使用,因而成为常见的测距与感知方案。

【系统框架图】

【软件流程图】

【核心代码展示】

#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 "dth11.h"
#include "ds18b20.h"
#include "PAJ7620.h"
#include "max30102.h"
#include "stm32f10x_adc.h"
#include "HCSR04.h"

u8 str[100];
OLED oled;
USER user;

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;


}

/**
 * @brief 按键任务
 *
 * @param pt
 */
void Key_Task(void *pt)
{
    switch (KEY_Scan(0))
    {
    case KEY1_PRICE:
    {
        if(oled.page == PAGE_1)
        {
            OLED_Clear();
            oled.page = PAGE_2;
            Task_Enable(4,1);
        }
        else if(oled.page == PAGE_2)
        {
            OLED_Clear();
            oled.page = PAGE_1;
            Task_Enable(4,0);
        }
        else if(oled.page == PAGE_SET1)
        {
            if (oled.x < 4)
                oled.x++;
            if (oled.x == 4)
                oled.x = 1;
        }
        
    };
    break;

    case KEY2_PRICE:
    {
        if (oled.x == 1)
        {
            if (user.max30102.dis_hr_value < 200)
                user.max30102.dis_hr_value++;
        }
        else if (oled.x == 2)
        {
            if (user.light_value < 100)
                user.light_value++;
        }
        else if (oled.x == 3)
        {
            if (user.distance_value < 100)
                user.distance_value++;
        }
    };
    break;

    case KEY3_PRICE:
    {
        if (oled.x == 1)
        {
            if (user.max30102.dis_hr_value > 0)
                user.max30102.dis_hr_value--;
        }
        else if (oled.x == 2)
        {
            if (user.light_value > 0)
                user.light_value--;
        }
        else if (oled.x == 3)
        {
            if (user.distance_value > 0)
                user.distance_value--;
        }
    };
    break;

    case KEY4_PRICE:
    {
        if (oled.page == PAGE_1 || oled.page == PAGE_2)
        {
            oled.page = PAGE_SET1;
            OLED_Clear();
            oled.x = 1;
            Task_Enable(4, 0);
        }
        else if (oled.page == PAGE_SET1)
        {
            oled.page = PAGE_1;
            OLED_Clear();
            oled.x = 1;
            Task_Enable(4, 1);
        }
    };
    break;
    }
}

/**
 * @brief OLED显示任务
 *
 * @param pt
 */
void OLED_Task(void *pt)
{
    if (oled.page == PAGE_1)
    {
        /**智能骑行头盔 */
        OLED_ShowChinese(16, 0, 14, 16, 1);
        OLED_ShowChinese(32, 0, 15, 16, 1);
        OLED_ShowChinese(48, 0, 16, 16, 1);
        OLED_ShowChinese(64, 0, 17, 16, 1);
        OLED_ShowChinese(80, 0, 18, 16, 1);
        OLED_ShowChinese(96, 0, 19, 16, 1);

        /**光照: */
        OLED_ShowChinese(0, 16, 20, 16, 1);
        OLED_ShowChinese(16, 16, 21, 16, 1);
        OLED_ShowChinese(32, 16, 22, 16, 1);
        sprintf(str, "%d%%", user.light);
        OLED_ShowString(48, 16, str, 16, 1);

        /**距离:*/
        OLED_ShowChinese(0, 32, 23, 16, 1);
        OLED_ShowChinese(16, 32, 24, 16, 1);
        OLED_ShowChinese(32, 32, 25, 16, 1);
        sprintf(str, "%2.1lfcm", user.distance);
        OLED_ShowString(48, 32, str, 16, 1);
    }
    else if(oled.page == PAGE_2)
    {
        /**智能骑行头盔 */
        OLED_ShowChinese(16, 0, 14, 16, 1);
        OLED_ShowChinese(32, 0, 15, 16, 1);
        OLED_ShowChinese(48, 0, 16, 16, 1);
        OLED_ShowChinese(64, 0, 17, 16, 1);
        OLED_ShowChinese(80, 0, 18, 16, 1);
        OLED_ShowChinese(96, 0, 19, 16, 1);

        /**心率: */
        OLED_ShowChinese(0, 16, 11, 16, 1);
        OLED_ShowChinese(16, 16, 12, 16, 1);
        OLED_ShowChinese(32, 16, 13, 16, 1);
        if (user.max30102.DataValid)
        {
            sprintf(str, "%3d BPM", user.max30102.dis_hr);
            OLED_ShowString(48, 16, str, 16, 1);
        }
        else
        {
            OLED_ShowString(48, 16, "--        ", 16, 1);
        }
    }
    else if (oled.page == PAGE_SET1)
    {
        /**阈值设置 */
        OLED_ShowChinese(32, 0, 31, 16, 1);
        OLED_ShowChinese(48, 0, 32, 16, 1);
        OLED_ShowChinese(64, 0, 33, 16, 1);
        OLED_ShowChinese(80, 0, 34, 16, 1);

        /**心率: */
        if (oled.x == 1)
        {
            OLED_ShowChinese(0, 16, 11, 16, 0);
            OLED_ShowChinese(16, 16, 12, 16, 0);
            OLED_ShowChinese(32, 16, 13, 16, 0);
            sprintf(str, "%3d BPM", user.max30102.dis_hr_value);
            OLED_ShowString(48, 16, str, 16, 0);
        }
        else
        {
            OLED_ShowChinese(0, 16, 11, 16, 1);
            OLED_ShowChinese(16, 16, 12, 16, 1);
            OLED_ShowChinese(32, 16, 13, 16, 1);
            sprintf(str, "%3d BPM", user.max30102.dis_hr_value);
            OLED_ShowString(48, 16, str, 16, 1);
        }

        if (oled.x == 2)
        {
            /**光照: */
            OLED_ShowChinese(0, 32, 20, 16, 0);
            OLED_ShowChinese(16, 32, 21, 16, 0);
            OLED_ShowChinese(32, 32, 22, 16, 0);
            sprintf(str, "%d%%", user.light_value);
            OLED_ShowString(48, 32, str, 16, 0);
        }
        else
        {
            /**光照: */
            OLED_ShowChinese(0, 32, 20, 16, 1);
            OLED_ShowChinese(16, 32, 21, 16, 1);
            OLED_ShowChinese(32, 32, 22, 16, 1);
            sprintf(str, "%d%%", user.light_value);
            OLED_ShowString(48, 32, str, 16, 1);
        }

        if (oled.x == 3)
        {
            /**距离:*/
            OLED_ShowChinese(0, 48, 23, 16, 0);
            OLED_ShowChinese(16, 48, 24, 16, 0);
            OLED_ShowChinese(32, 48, 25, 16, 0);
            sprintf(str, "%2.1lfcm", user.distance_value);
            OLED_ShowString(48, 48, str, 16, 0);
        }
        else
        {
            /**距离:*/
            OLED_ShowChinese(0, 48, 23, 16, 1);
            OLED_ShowChinese(16, 48, 24, 16, 1);
            OLED_ShowChinese(32, 48, 25, 16, 1);
            sprintf(str, "%2.1lfcm", user.distance_value);
            OLED_ShowString(48, 48, str, 16, 1);
        }
    }
    else if (oled.page == PAGE_ALARM)
    {

        /**后方来车! */
        OLED_ShowChinese(32, 32, 26, 16, 1);
        OLED_ShowChinese(48, 32, 27, 16, 1);
        OLED_ShowChinese(64, 32, 28, 16, 1);
        OLED_ShowChinese(80, 32, 29, 16, 1);
        OLED_ShowChinese(96, 32, 30, 16, 1);
    }

    OLED_Refresh();
}

/**
 * @brief 心率血氧获取任务
 *
 * @param pt
 */
void MAX30102_Task(void *pt)
{
    Get_MAX30102(&user.max30102);
}

/**
 * @brief 蜂鸣器报警任务
 *
 * @param pt
 */
void Alarm_Task(void *pt)
{
    if(oled.page == PAGE_1 || oled.page == PAGE_ALARM)
    {
        if(user.distance < user.distance_value && user.distance_flag == 1)
        {
            user.distance_flag=0;
            oled.page = PAGE_ALARM;
            OLED_Clear();
            BEEP = 0;
        }
        else if(user.distance >= user.distance_value && user.distance_flag == 0)
        {
            user.distance_flag = 1;
            oled.page = PAGE_1;
            OLED_Clear();
            BEEP = 1;
        }
    }
    else if(oled.page == PAGE_2)
    {
        if(user.max30102.dis_hr > user.max30102.dis_hr_value)
        {
            BEEP = 0;
        }
        else
        {
            BEEP = 1;
        }
    }
    
}

/**
 * @brief 陀螺仪任务
 *
 * @param pt
 */
void MPU6050_Task(void *pt)
{
    mpu_dmp_get_data(&user.mpu6050.Pitch, &user.mpu6050.Roll, &user.mpu6050.Yaw); // 角度
}

/**
 * @brief 灯光任务
 *
 * @param pt
 */
void Light_Task(void *pt)
{
    user.light = 100 - (Get_Adc(ADC_Channel_6) * 100 / 4095);
    if (user.light < user.light_value)
        LIGHT = 1;
    else
        LIGHT = 0;
}

/**
 * @brief 距离获取任务
 *
 * @param pt
 */
void Distance_Task(void *pt)
{
    user.distance = Getlength();
}


/**
 * @brief 
 * 
 * @param pt 
 */
void AngleControl_Task(void *pt)
{
    static MPU6050 last_angle;
    static u16 delay = 0;
    if (user.mpu6050.Yaw - last_angle.Yaw > 30)
    {
        delay = 0;
        user.direction = LEFT;
    }
    else if (user.mpu6050.Yaw - last_angle.Yaw < -30)
    {
        delay = 0;
        user.direction = RIGHT;
    }
    if (user.mpu6050.Roll - last_angle.Roll < -30)
    {
        delay = 0;
        user.direction = STOP;
    }
    last_angle.Roll = user.mpu6050.Roll;
    last_angle.Yaw = user.mpu6050.Yaw;
    if (user.direction != RUN)
    {
        if (delay++ > 6)
        {
            delay = 0;
            user.direction = RUN;
        }
    }
}

void Flicker_Task(void *pt)
{
    if(user.direction == LEFT) LED_LEFT = !LED_LEFT;
    else LED_LEFT = 1;
    if(user.direction == RIGHT) LED_RIGHT = !LED_RIGHT;
    else LED_RIGHT = 1;
}

void Task_Init(void)
{
    Task_Register(1, 1000, Led_Task, NULL);
    Task_Register(2, 1, Key_Task, NULL);
    Task_Register(3, 200, OLED_Task, NULL);
    Task_Register(4, 1000, MAX30102_Task, NULL);
    Task_Register(5, 100, Alarm_Task, NULL);
    Task_Register(6, 500, MPU6050_Task, NULL);
    Task_Register(7, 200, Light_Task, NULL);
    Task_Register(8, 100, Distance_Task, NULL);
    Task_Register(9, 500, AngleControl_Task, NULL);
    Task_Register(10, 200, Flicker_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的老人健康与跌倒监测系统

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值