一、項目介紹
由于自行車本身沒有帶指示燈,比如剎車指示燈等,所以自行車的安全性并不是很好,如果人們在騎自行車時緊急剎車,后車無法及時判斷前方自行車的行為,容易造成交通事故。
本項目為自行車騎行者提供一種智能化的安全提示系統,采用ADXL345陀螺儀、STM32F103C8T6主控芯片及四枚LED燈,通過實時監測自行車的加速度變化,實現自動剎車燈功能。
本項目實現了通過安裝ADXL345陀螺儀和四枚LED燈還有STM32F103C8T6主控芯片來實現自行車自動剎車燈的功能。當自行車上安裝了該設備后,ADXL345通過IIC通信協議將X,Y,Z三軸的加速度實時值發送給SMT32F103C8T6主控芯片,并結合STM32高級定時器的PWM功能,輸出不同占空比的脈沖,控制不同的LED燈輸出多種亮度等級,從而控制不同的LED的開關以及明暗,并且通過不同亮度的紅光和綠光混合,能夠得到黃色的LED燈光。這樣,在自行車急剎或者加速時,實時地控制LED燈的亮度和顏色,讓后方車輛能夠更清楚地了解前方自行車的行為,從而做出快速的反應,保障騎行者以及后車的安全。同時,該系統也能夠提高自行車的可見性,并且對于追求低碳環保的人群來說,讓自行車既能低碳環保,又能夠鍛煉身體。
整個項目的成品構造也很簡單,一個盒子,一塊鋰電池,一個ADXL345陀螺儀模塊、一個C8T6單片機、4顆高亮度LED燈即可。
下面這是做出的測試模型,通過實驗板測試項目的整個功能
二、設計思路
2.1 項目目標
本項目通過安裝ADXL345陀螺儀和四枚LED燈還有STM32F103C8T6主控芯片來實現自行車自動剎車燈的功能,使得自行車在急剎或者加速時,實時地控制LED燈的亮度和顏色,提高其可見性,降低交通事故的風險。同時,該系統還能夠使自行車既能低碳環保,又能夠鍛煉身體。
2.2 項目硬件構成
(1)自行車:作為安裝系統的物體,需要有一個固定的位置來安裝ADXL345陀螺儀和四枚LED燈。
(2)ADXL345陀螺儀:通過IIC通信協議與STM32F103C8T6主控芯片通信,并將X、Y、Z三軸的加速度實時值發送給SMT32F103C8T6主控芯片。
(3)四枚LED燈:使用不同亮度的紅光和綠光混合,能夠得到黃色的LED燈光。通過控制其亮度和顏色來提高自行車的可見性。
(4)STM32F103C8T6主控芯片:根據接收到的ADXL345數據,結合STN32的高級定時器的PWM功能,輸出不同占空比的脈沖,控制不同的LED燈輸出多種亮度等級。
2.3 項目功能實現
(1)自行車加速度監測:ADXL345陀螺儀通過IIC通信協議與STM32F103C8T6主控芯片通信,實時地感知自行車的加速度變化。
(2)LED燈亮度和顏色控制:STM32F103C8T6主控芯片運用高級定時器的PWM功能,能夠輸出不同占空比的脈沖,并控制不同的LED燈輸出多種亮度等級,通過不同亮度的紅光和綠光混合,能夠得到黃色的LED燈光,提高自行車的可見性。
(3)系統安裝和調試:需要將ADXL345陀螺儀和四枚LED燈與STM32F103C8T6主控芯片連接起來,并進行系統測試和調試。
2.4 項目的開發價值
本項目的存在價值主要體現在提升自行車騎行安全性、增強自行車可見性以及推動低碳環保理念的傳播等多個方面。
安全性是本項目最核心的價值之一。在自行車騎行中,由于缺乏像汽車那樣的剎車燈等警示系統,騎行者在急剎車或加速時,后方車輛往往難以及時做出反應,增加了交通事故的風險。通過該系統的實現,利用ADXL345陀螺儀實時檢測自行車的運動狀態,并通過PWM控制LED燈的亮度和顏色變化,能夠在剎車或加速時,及時向后方車輛傳遞明顯的信號,從而提高騎行者和其他交通參與者的安全性。
增強可見性是另一個重要的價值點。特別是在夜間或低能見度條件下,騎行者往往容易被忽視,增加了與其他車輛發生碰撞的風險。該系統通過控制LED燈的亮度和顏色,不僅提升了自行車的視覺存在感,還能通過紅綠光的混合形成黃色警示效果,使騎行者在夜間或復雜環境中更加容易被發現,大大減少了交通事故的發生幾率。
在如今提倡低碳環保的社會背景下,自行車作為綠色出行方式,正得到越來越多人的青睞。通過智能化的設計和系統的集成,項目不僅提升了自行車的安全性能,也進一步增強了自行車作為環保出行工具的吸引力。這種系統化的智能升級,不僅符合現代城市交通發展趨勢,也符合綠色環保的理念,助力推動低碳出行方式的普及。
本項目在保障騎行者安全的同時,也促進了智能硬件與綠色出行的結合,具有重要的實用價值和社會意義。
三、系統測試
3.1 功能樣機安裝與焊接
繪制好電路原理圖之后,按照原理圖將自動剎車燈系統的各個模塊安裝在事先購買好的洞洞板上,然后用導線將他們連接在一起,最后再焊接在一起,做成完整的自動剎車燈電路板。
3.2 ADXL345模塊調試
當上電后,將自動剎車燈電路的串口2外設引腳連接至PC端,將加速度解算后的實際值發送至PC端,通過PC端串口調試助手顯示出具體數值,再觀察數值是否符合常理。
通過顯示的數據信息,可以推測出ADXL345陀螺儀能夠正常工作。
3.3 實物調試
最后階段,將對自行車自動剎車燈進行實物調試,確定其基本功能能夠正常實現。
當系統上電后,左右各一枚LED發出低亮黃色燈光,如下圖。
靜置30S后,所有LED均熄滅,如下圖。
當檢測到震動后,重新亮起兩盞黃色LED燈,如下圖。
當檢測到剎車時,四枚LED燈均以高亮發出紅色燈光,如下圖。
結合自行車自動剎車燈的功能需求和實物調試結果,可以發現,調試結果完全符合自動剎車燈的預期功能。
四、代碼設計
[https://ccnr8sukk85n.feishu.cn/wiki/QjY8weDYHibqRYkFP2qcA9aGnvb?from=from_copylink]
4.1 主函數
#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.1frn",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;
}
}
}
4.2 LED燈控制
#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);
}
4.3 adxl345.c
#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;
}
審核編輯 黃宇
-
通信協議
+關注
關注
28文章
899瀏覽量
40344 -
STM32
+關注
關注
2270文章
10910瀏覽量
356665 -
IIC
+關注
關注
11文章
302瀏覽量
38385
發布評論請先 登錄
相關推薦
評論