一、項目介紹
隨著科技的發(fā)展,人們對低碳環(huán)保的認知和需求不斷提高。騎自行車既能夠低碳環(huán)保,又能夠鍛煉身體,成為了很多人出行的首選。然而,由于自行車本身沒有帶指示燈,比如剎車指示燈等,所以自行車的安全性并不是很好,如果人們在騎自行車時緊急剎車,后車無法及時判斷前方自行車的行為,容易造成交通事故。因此,設(shè)計一款自動剎車燈系統(tǒng)具有十分重要的意義。
本項目實現(xiàn)了通過安裝ADXL345陀螺儀和四枚LED燈還有STM32F103C8T6主控芯片來實現(xiàn)自行車自動剎車燈的功能。
當自行車上安裝了該設(shè)備后,ADXL345通過IIC通信協(xié)議將X,Y,Z三軸的加速度實時值發(fā)送給SMT32F103C8T6主控芯片,并結(jié)合STM32高級定時器的PWM功能,輸出不同占空比的脈沖,控制不同的LED燈輸出多種亮度等級,從而控制不同的LED的開關(guān)以及明暗,并且通過不同亮度的紅光和綠光混合,能夠得到黃色的LED燈光。
這樣,在自行車急剎或者加速時,實時地控制LED燈的亮度和顏色,讓后方車輛能夠更清楚地了解前方自行車的行為,從而做出快速的反應(yīng),保障騎行者以及后車的安全。
同時,該系統(tǒng)也能夠提高自行車的可見性,并且對于追求低碳環(huán)保的人群來說,讓自行車既能低碳環(huán)保,又能夠鍛煉身體。
二、設(shè)計思路
如果需要下載項目工程,可以去這里:
https://blog.csdn.net/xiaolong1126626497/category_10192120.html
2.1 項目目標
本項目通過安裝ADXL345陀螺儀和四枚LED燈還有STM32F103C8T6主控芯片來實現(xiàn)自行車自動剎車燈的功能,使得自行車在急剎或者加速時,實時地控制LED燈的亮度和顏色,提高其可見性,降低交通事故的風險。同時,該系統(tǒng)還能夠使自行車既能低碳環(huán)保,又能夠鍛煉身體。
2.2 項目硬件構(gòu)成
(1)自行車:作為安裝系統(tǒng)的物體,需要有一個固定的位置來安裝ADXL345陀螺儀和四枚LED燈。
(2)ADXL345陀螺儀:通過IIC通信協(xié)議與STM32F103C8T6主控芯片通信,并將X、Y、Z三軸的加速度實時值發(fā)送給SMT32F103C8T6主控芯片。
(3)四枚LED燈:使用不同亮度的紅光和綠光混合,能夠得到黃色的LED燈光。通過控制其亮度和顏色來提高自行車的可見性。
(4)STM32F103C8T6主控芯片:根據(jù)接收到的ADXL345數(shù)據(jù),結(jié)合STN32的高級定時器的PWM功能,輸出不同占空比的脈沖,控制不同的LED燈輸出多種亮度等級。
2.3 項目功能實現(xiàn)
(1)自行車加速度監(jiān)測:ADXL345陀螺儀通過IIC通信協(xié)議與STM32F103C8T6主控芯片通信,實時地感知自行車的加速度變化。
(2)LED燈亮度和顏色控制:STM32F103C8T6主控芯片運用高級定時器的PWM功能,能夠輸出不同占空比的脈沖,并控制不同的LED燈輸出多種亮度等級,通過不同亮度的紅光和綠光混合,能夠得到黃色的LED燈光,提高自行車的可見性。
(3)系統(tǒng)安裝和調(diào)試:需要將ADXL345陀螺儀和四枚LED燈與STM32F103C8T6主控芯片連接起來,并進行系統(tǒng)測試和調(diào)試。
三、系統(tǒng)測試
3.1 功能樣機安裝與焊接
繪制好電路原理圖之后,按照原理圖將自動剎車燈系統(tǒng)的各個模塊安裝在事先購買好的洞洞板上,然后用導(dǎo)線將他們連接在一起,最后再焊接在一起,做成完整的自動剎車燈電路板。
3.2 ADXL345模塊調(diào)試
當上電后,將自動剎車燈電路的串口2外設(shè)引腳連接至PC端,將加速度解算后的實際值發(fā)送至PC端,通過PC端串口調(diào)試助手顯示出具體數(shù)值,再觀察數(shù)值是否符合常理。
通過顯示的數(shù)據(jù)信息,可以推測出ADXL345陀螺儀能夠正常工作。
3.3 實物調(diào)試
最后階段,將對自行車自動剎車燈進行實物調(diào)試,確定其基本功能能夠正常實現(xiàn)。
當系統(tǒng)上電后,左右各一枚LED發(fā)出低亮黃色燈光,如下圖。
靜置30S后,所有LED均熄滅,如下圖。
當檢測到震動后,重新亮起兩盞黃色LED燈,如下圖。
當檢測到剎車時,四枚LED燈均以高亮發(fā)出紅色燈光,如下圖。
結(jié)合自行車自動剎車燈的功能需求和實物調(diào)試結(jié)果,可以發(fā)現(xiàn),調(diào)試結(jié)果完全符合自動剎車燈的預(yù)期功能。
四、代碼設(shè)計
4.1 主函數(shù)
#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(); //延時函數(shù)初始化
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); //設(shè)置TIMx捕獲比較1寄存器(通道1)值(脈沖寬度) 占空比%20
TIM_SetCompare2(TIM1, 50); //設(shè)置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低電平導(dǎo)通
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 類型的結(jié)構(gòu)體,名字叫GPIO_InitStructure
GPIO_InitTypeDef GPIO_InitStructure;
//使能GPIOC的外設(shè)時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
//選擇要用的GPIO引腳
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_13;
///設(shè)置引腳模式為推免輸出模式
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
//設(shè)置引腳速度為50MHZ
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//調(diào)用庫函數(shù),初始化GPIO
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
void TIME_INIT()
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure; //根據(jù)TIM_OCInitStruct中指定的參數(shù)初始化外設(shè)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;//設(shè)置初始PWM脈沖寬度為0
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //PWM輸出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;//當定時器計數(shù)值小于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; //復(fù)用推挽輸出
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位全分辨率,輸出數(shù)據(jù)右對齊,16g量程
ADXL345_WR_Reg(0X2C,0x0A); //數(shù)據(jù)輸出速度為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); //發(fā)送寫器件指令
IIC_Wait_Ack();
IIC_Send_Byte(addr); //發(fā)送寄存器地址
IIC_Wait_Ack();
IIC_Send_Byte(val); //發(fā)送值
IIC_Wait_Ack();
IIC_Stop(); //產(chǎn)生一個停止條件
}
//讀ADXL345寄存器
//addr:寄存器地址
//返回值:讀到的值
u8 ADXL345_RD_Reg(u8 addr)
{
u8 temp=0;
IIC_Start();
IIC_Send_Byte(ADXL_WRITE); //發(fā)送寫器件指令
temp=IIC_Wait_Ack();
IIC_Send_Byte(addr); //發(fā)送寄存器地址
temp=IIC_Wait_Ack();
IIC_Start(); //重新啟動
IIC_Send_Byte(ADXL_READ); //發(fā)送讀器件指令
temp=IIC_Wait_Ack();
temp=IIC_Read_Byte(0); //讀取一個字節(jié),不繼續(xù)再讀,發(fā)送NAK
IIC_Stop(); //產(chǎn)生一個停止條件
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位全分辨率,輸出數(shù)據(jù)右對齊,16g量程
ADXL345_WR_Reg(BW_RATE,0x0A); //數(shù)據(jù)輸出速度為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個軸的數(shù)據(jù)
//x,y,z:讀取到的數(shù)據(jù)
void ADXL345_RD_XYZ(short *x,short *y,short *z)
{
u8 buf[6];
u8 i;
IIC_Start();
IIC_Send_Byte(0X3A); //發(fā)送寫器件指令
IIC_Wait_Ack();
IIC_Send_Byte(0x32); //發(fā)送寄存器地址(數(shù)據(jù)緩存的起始地址為0X32)
IIC_Wait_Ack();
IIC_Start(); //重新啟動
IIC_Send_Byte(0X3B); //發(fā)送讀器件指令
IIC_Wait_Ack();
for(i=0;i<6;i++)
{
if(i==5)buf[i]=IIC_Read_Byte(0);//讀取一個字節(jié),不繼續(xù)再讀,發(fā)送NACK
else buf[i]=IIC_Read_Byte(1); //讀取一個字節(jié),繼續(xù)讀,發(fā)送ACK
delay_us(15);
IIC_Start(); //重新啟動
IIC_Send_Byte(0X3A); //發(fā)送寫器件指令
IIC_Wait_Ack();
IIC_Send_Byte(0x33+i); //發(fā)送寄存器地址(數(shù)據(jù)緩存的起始地址為0X32)
IIC_Wait_Ack();
IIC_Start(); //重新啟動
IIC_Send_Byte(0X3B); //發(fā)送讀器件指令
IIC_Wait_Ack();
}
IIC_Stop(); //產(chǎn)生一個停止條件
*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的數(shù)據(jù)times次,再取平均
//x,y,z:讀到的數(shù)據(jù)
//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)//讀取次數(shù)不為0
{
for(i=0;i<times;i++)//連續(xù)讀取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方向的重力加速度分量(不需要單位,直接數(shù)值即可)
//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;
}