由于自行车本身没有带指示灯,比如刹车指示灯等,所以自行车的安全性并不是很好,如果人们在骑自行车时紧急刹车,后车无法及时判断前方自行车的行为,容易造成交通事故。
本项目为自行车骑行者提供一种智能化的安全提示系统,采用ADXL345陀螺仪、STM32F103C8T6主控芯片及四枚LED灯,通过实时监测自行车的加速度变化,实现自动刹车灯功能。
本项目实现了通过安装ADXL345陀螺仪和四枚LED灯还有STM32F103C8T6主控芯片来实现自行车自动刹车灯的功能。当自行车上安装了该设备后,ADXL345通过IIC通信协议将X,Y,Z三轴的加速度实时值发送给SMT32F103C8T6主控芯片,并结合STM32高级定时器的PWM功能,输出不同占空比的脉冲,控制不同的LED灯输出多种亮度等级,从而控制不同的LED的开关以及明暗,并且通过不同亮度的红光和绿光混合,能够得到黄色的LED灯光。这样,在自行车急刹或者加速时,实时地控制LED灯的亮度和颜色,让后方车辆能够更清楚地了解前方自行车的行为,从而做出快速的反应,保障骑行者以及后车的安全。同时,该系统也能够提高自行车的可见性,并且对于追求低碳环保的人群来说,让自行车既能低碳环保,又能够锻炼身体。
整个项目的成品构造也很简单,一个盒子,一块锂电池,一个ADXL345陀螺仪模块、一个C8T6单片机、4颗高亮度LED灯即可。
下面这是做出的测试模型,通过实验板测试项目的整个功能
本项目通过安装ADXL345陀螺仪和四枚LED灯还有STM32F103C8T6主控芯片来实现自行车自动刹车灯的功能,使得自行车在急刹或者加速时,实时地控制LED灯的亮度和颜色,提高其可见性,降低交通事故的风险。同时,该系统还能够使自行车既能低碳环保,又能够锻炼身体。
(1)自行车:作为安装系统的物体,需要有一个固定的位置来安装ADXL345陀螺仪和四枚LED灯。
(2)ADXL345陀螺仪:通过IIC通信协议与STM32F103C8T6主控芯片通信,并将X、Y、Z三轴的加速度实时值发送给SMT32F103C8T6主控芯片。
(3)四枚LED灯:使用不同亮度的红光和绿光混合,能够得到黄色的LED灯光。通过控制其亮度和颜色来提高自行车的可见性。
(4)STM32F103C8T6主控芯片:根据接收到的ADXL345数据,结合STN32的高级定时器的PWM功能,输出不同占空比的脉冲,控制不同的LED灯输出多种亮度等级。
(1)自行车加速度监测:ADXL345陀螺仪通过IIC通信协议与STM32F103C8T6主控芯片通信,实时地感知自行车的加速度变化。
(2)LED灯亮度和颜色控制:STM32F103C8T6主控芯片运用高级定时器的PWM功能,能够输出不同占空比的脉冲,并控制不同的LED灯输出多种亮度等级,通过不同亮度的红光和绿光混合,能够得到黄色的LED灯光,提高自行车的可见性。
(3)系统安装和调试:需要将ADXL345陀螺仪和四枚LED灯与STM32F103C8T6主控芯片连接起来,并进行系统测试和调试。
本项目的存在价值主要体现在提升自行车骑行安全性、增强自行车可见性以及推动低碳环保理念的传播等多个方面。
安全性是本项目最核心的价值之一。在自行车骑行中,由于缺乏像汽车那样的刹车灯等警示系统,骑行者在急刹车或加速时,后方车辆往往难以及时做出反应,增加了交通事故的风险。通过该系统的实现,利用ADXL345陀螺仪实时检测自行车的运动状态,并通过PWM控制LED灯的亮度和颜色变化,能够在刹车或加速时,及时向后方车辆传递明显的信号,从而提高骑行者和其他交通参与者的安全性。
增强可见性是另一个重要的价值点。特别是在夜间或低能见度条件下,骑行者往往容易被忽视,增加了与其他车辆发生碰撞的风险。该系统通过控制LED灯的亮度和颜色,不仅提升了自行车的视觉存在感,还能通过红绿光的混合形成黄色警示效果,使骑行者在夜间或复杂环境中更加容易被发现,大大减少了交通事故的发生几率。
在如今提倡低碳环保的社会背景下,自行车作为绿色出行方式,正得到越来越多人的青睐。通过智能化的设计和系统的集成,项目不仅提升了自行车的安全性能,也进一步增强了自行车作为环保出行工具的吸引力。这种系统化的智能升级,不仅符合现代城市交通发展趋势,也符合绿色环保的理念,助力推动低碳出行方式的普及。
本项目在保障骑行者安全的同时,也促进了智能硬件与绿色出行的结合,具有重要的实用价值和社会意义。
绘制好电路原理图之后,按照原理图将自动刹车灯系统的各个模块安装在事先购买好的洞洞板上,然后用导线将他们连接在一起,最后再焊接在一起,做成完整的自动刹车灯电路板。
当上电后,将自动刹车灯电路的串口2外设引脚连接至PC端,将加速度解算后的实际值发送至PC端,通过PC端串口调试助手显示出具体数值,再观察数值是否符合常理。
通过显示的数据信息,可以推测出ADXL345陀螺仪能够正常工作。
最后阶段,将对自行车自动刹车灯进行实物调试,确定其基本功能能够正常实现。
当系统上电后,左右各一枚LED发出低亮黄色灯光,如下图。
静置30S后,所有LED均熄灭,如下图。
当检测到震动后,重新亮起两盏黄色LED灯,如下图。
当检测到刹车时,四枚LED灯均以高亮发出红色灯光,如下图。
结合自行车自动刹车灯的功能需求和实物调试结果,可以发现,调试结果完全符合自动刹车灯的预期功能。
单片机相关的开发工具、传感器模块代码可以去网盘里下载:
https://ccnr8sukk85n.feishu.cn/wiki/QjY8weDYHibqRYkFP2qcA9aGnvb?from=from_copylink
#include "stm32f10x.h"
#include "usart.h"
#include "led.h"
#include "RTC_Time.h"
#include <stdio.h>
#include "delay.h"
#include "sys.h"
#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include "adxl345.h"
int main(void)
{
u32 flag=0;
short x, y, z;
float accelerated;
LED_GPIO_Config();//初始化LED
USART2_Config();
delay_init(); //延时函数初始化
PWM_LED_INIT(); //PWM PA8-9
LED_Init(); //PB7 LED-R
PBout(7) = 1;
ADXL345_Init(); //PB 10,11
ADXL345_Read_Average(&x, &y, &z, 20);
ADXL345_AUTO_Adjust((char *)&x, (char *)&y, (char *)&z);
TIM_SetCompare1(TIM1, 50); //设置TIMx捕获比较1寄存器(通道1)值(脉冲宽度) 占空比%20
TIM_SetCompare2(TIM1, 50); //设置TIMx捕获比较2寄存器(通道2)值(脉冲宽度) 占空比%20
while (1)
{
ADXL345_Read_Average(&x, &y, &z, 5); //读加速度值
accelerated=(x*3.9/1000*9.8); //加速度实际值
printf("X=%4.1f Y=%4.1f Z=%4.1f\r\n",accelerated,(y*3.9/1000*9.8),(z*3.9/1000*9.8));
while(flag>425)
{
TIM_SetCompare1(TIM1, 0); //通道2 占空比%0
TIM_SetCompare2(TIM1, 0); //通道2 占空比%0
ADXL345_Read_Average(&x, &y, &z, 5);
accelerated=(x*3.9/1000*9.8);
if(accelerated<-5||accelerated>5)
{
break;
}
}
flag++;
if(accelerated<-4)
{
//四个LED低电平导通
TIM_SetCompare1(TIM1, 0); //GREEN不亮
TIM_SetCompare2(TIM1, 1000); //RED高亮
PBout(7) = 0;
flag=0;
}
if(accelerated>0)
{
PBout(7) = 1;
TIM_SetCompare1(TIM1, 50); //RED低亮
TIM_SetCompare2(TIM1, 50); //GREEN低亮
}
if(accelerated>5)
{
flag=0;
}
}
}
#include "led.h"
#include "delay.h"
void LED_GPIO_Config(void)
{
//定义一个GPIO_InitTypeDef 类型的结构体,名字叫GPIO_InitStructure
GPIO_InitTypeDef GPIO_InitStructure;
//使能GPIOC的外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
//选择要用的GPIO引脚
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_13;
///设置引脚模式为推免输出模式
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
//设置引脚速度为50MHZ
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//调用库函数,初始化GPIO
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
void TIME_INIT()
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure; //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
//TIM1定时器初始化 10ms
TIM_TimeBaseInitStructure.TIM_Period = 999;
TIM_TimeBaseInitStructure.TIM_Prescaler = 719;
TIM_TimeBaseInitStructure.TIM_ClockDivision = 0;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStructure);
//TIM1的PWM配置
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_Pulse = 0;//设置初始PWM脉冲宽度为0
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //PWM输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;//当定时器计数值小于CCR_Val时为低电平
//通道的使能
TIM_OC1Init(TIM1, &TIM_OCInitStructure); //通道1
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_OC2Init(TIM1, &TIM_OCInitStructure); //通道2
TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIM1重载寄存器ARR
TIM_Cmd(TIM1, ENABLE); //使能
TIM_CtrlPWMOutputs(TIM1, ENABLE); //高级定时器必须加
}
void PWM_LED_INIT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE); //GPIOA8,9,10是TIM1的通道1,2,3
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIME_INIT();
}
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
#include "adxl345.h"
#include "sys.h"
#include "delay.h"
#include "math.h"
u8 ADXL345_Init(void)
{
IIC_Init(); //初始化IIC总线
if(ADXL345_RD_Reg(DEVICE_ID)==0XE5) //读取器件ID
{
ADXL345_WR_Reg(0X31,0X2B); //低电平中断输出,13位全分辨率,输出数据右对齐,16g量程
ADXL345_WR_Reg(0X2C,0x0A); //数据输出速度为100Hz
ADXL345_WR_Reg(0X2D,0x28); //链接使能,测量模式
ADXL345_WR_Reg(0X2E,0x00); //不使用中断
ADXL345_WR_Reg(0X1E,0x00);
ADXL345_WR_Reg(0X1F,0x00);
ADXL345_WR_Reg(0X20,0x00);
return 0;
}
return 1;
}
//写ADXL345寄存器
//addr:寄存器地址
//val:要写入的值
//返回值:无
void ADXL345_WR_Reg(u8 addr,u8 val)
{
IIC_Start();
IIC_Send_Byte(ADXL_WRITE); //发送写器件指令
IIC_Wait_Ack();
IIC_Send_Byte(addr); //发送寄存器地址
IIC_Wait_Ack();
IIC_Send_Byte(val); //发送值
IIC_Wait_Ack();
IIC_Stop(); //产生一个停止条件
}
//读ADXL345寄存器
//addr:寄存器地址
//返回值:读到的值
u8 ADXL345_RD_Reg(u8 addr)
{
u8 temp=0;
IIC_Start();
IIC_Send_Byte(ADXL_WRITE); //发送写器件指令
temp=IIC_Wait_Ack();
IIC_Send_Byte(addr); //发送寄存器地址
temp=IIC_Wait_Ack();
IIC_Start(); //重新启动
IIC_Send_Byte(ADXL_READ); //发送读器件指令
temp=IIC_Wait_Ack();
temp=IIC_Read_Byte(0); //读取一个字节,不继续再读,发送NAK
IIC_Stop(); //产生一个停止条件
return temp; //返回读到的值
}
//读取ADXL的平均值
//x,y,z:读取10次后取平均值
void ADXL345_RD_Avval(short *x,short *y,short *z)
{
short tx=0,ty=0,tz=0;
u8 i;
for(i=0;i<10;i++)
{
ADXL345_RD_XYZ(x,y,z);
delay_ms(10);
tx+=(short)*x;
ty+=(short)*y;
tz+=(short)*z;
}
*x=tx/10;
*y=ty/10;
*z=tz/10;
}
//自动校准
//xval,yval,zval:x,y,z轴的校准值
void ADXL345_AUTO_Adjust(char *xval,char *yval,char *zval)
{
short tx,ty,tz;
u8 i;
short offx=0,offy=0,offz=0;
ADXL345_WR_Reg(POWER_CTL,0x00); //先进入休眠模式.
delay_ms(100);
ADXL345_WR_Reg(DATA_FORMAT,0X2B); //低电平中断输出,13位全分辨率,输出数据右对齐,16g量程
ADXL345_WR_Reg(BW_RATE,0x0A); //数据输出速度为100Hz
ADXL345_WR_Reg(POWER_CTL,0x28); //链接使能,测量模式
ADXL345_WR_Reg(INT_ENABLE,0x00); //不使用中断
ADXL345_WR_Reg(OFSX,0x00);
ADXL345_WR_Reg(OFSY,0x00);
ADXL345_WR_Reg(OFSZ,0x00);
delay_ms(12);
for(i=0;i<10;i++)
{
ADXL345_RD_Avval(&tx,&ty,&tz);
offx+=tx;
offy+=ty;
offz+=tz;
}
offx/=10;
offy/=10;
offz/=10;
*xval=-offx/4;
*yval=-offy/4;
*zval=-(offz-256)/4;
ADXL345_WR_Reg(OFSX,*xval);
ADXL345_WR_Reg(OFSY,*yval);
ADXL345_WR_Reg(OFSZ,*zval);
}
//读取3个轴的数据
//x,y,z:读取到的数据
void ADXL345_RD_XYZ(short *x,short *y,short *z)
{
u8 buf[6];
u8 i;
IIC_Start();
IIC_Send_Byte(0X3A); //发送写器件指令
IIC_Wait_Ack();
IIC_Send_Byte(0x32); //发送寄存器地址(数据缓存的起始地址为0X32)
IIC_Wait_Ack();
IIC_Start(); //重新启动
IIC_Send_Byte(0X3B); //发送读器件指令
IIC_Wait_Ack();
for(i=0;i<6;i++)
{
if(i==5)buf[i]=IIC_Read_Byte(0);//读取一个字节,不继续再读,发送NACK
else buf[i]=IIC_Read_Byte(1); //读取一个字节,继续读,发送ACK
delay_us(15);
IIC_Start(); //重新启动
IIC_Send_Byte(0X3A); //发送写器件指令
IIC_Wait_Ack();
IIC_Send_Byte(0x33+i); //发送寄存器地址(数据缓存的起始地址为0X32)
IIC_Wait_Ack();
IIC_Start(); //重新启动
IIC_Send_Byte(0X3B); //发送读器件指令
IIC_Wait_Ack();
}
IIC_Stop(); //产生一个停止条件
*x=(short)(((u16)buf[1]<<8)+buf[0]);
*y=(short)(((u16)buf[3]<<8)+buf[2]);
*z=(short)(((u16)buf[5]<<8)+buf[4]);
}
//读取ADXL345的数据times次,再取平均
//x,y,z:读到的数据
//times:读取多少次
void ADXL345_Read_Average(short *x,short *y,short *z,u8 times)
{
u8 i;
short tx,ty,tz;
*x=0;
*y=0;
*z=0;
if(times)//读取次数不为0
{
for(i=0;i<times;i++)//连续读取times次
{
ADXL345_RD_XYZ(&tx,&ty,&tz);
*x+=tx;
*y+=ty;
*z+=tz;
delay_ms(5);
}
*x/=times;
*y/=times;
*z/=times;
}
}
//得到角度
//x,y,z:x,y,z方向的重力加速度分量(不需要单位,直接数值即可)
//dir:要获得的角度.0,与Z轴的角度;1,与X轴的角度;2,与Y轴的角度.
//返回值:角度值.单位0.1°.
short ADXL345_Get_Angle(float x,float y,float z,u8 dir)
{
float temp;
float res=0;
switch(dir)
{
case 0://与自然Z轴的角度
temp=sqrt((x*x+y*y))/z;
res=atan(temp);
break;
case 1://与自然X轴的角度
temp=x/sqrt((y*y+z*z));
res=atan(temp);
break;
case 2://与自然Y轴的角度
temp=y/sqrt((x*x+z*z));
res=atan(temp);
break;
}
return res*1800/3.14;
}