一、总体设计方案 智能防酒驾装置由一个主机设备从和三个从机(酒精传感器)组成,主机采用STM32F407作为主控设备,从机采用STM32F030作为主控芯片,主机和从机之间通过NRF24L01进行无线通信。主机负责酒驾情况的判定、通过GPS进行定位、通过GSM向用户发送短信、控制车辆状态、显示设备当前状态并向驾驶员发出语音提示等工作。从机主要负责采集车辆内各处的酒精浓度、向主机报告车辆各处的酒精浓度。酒精传感器采用TGS2620高灵敏度乙醇气体传感器对气体信号进行检测,当空气中有乙醇气体存在时,该气体的浓度越高传感器的电导率也会越高。将电导率的变化转换成与该气体浓度相对应的电压信号输出,根据电压信号进行酒精含量的判断。 该装置的工作流程为:驾驶司机进入车内后,会有一个装有气体传感器的装置强行检测司机的喝酒情况。当酒精浓度较低时,系统无反应并进行语音提示,车子可以正常行驶;但是当酒精浓度过高,超过安全驾驶的酒精浓度范围时,检测装置断开点火开关,使汽车无法发动,并发出语音提示驾驶员请勿酒驾。另外,系统会通过GPS对车辆进行定位,将司机醉酒驾驶的情况和当前位置以短信的形式发送给司机的家属、朋友,或向第三方发送位置,请求代驾。
二、硬件电路
1.主机 主机由STM32F407VET6作为主控芯片,将GPS模块、GSM模块、语音模块、NRF24L01无线通信模块、OLED显示模块集成在一起,对外提供继电器、热释电红外模块、酒精传感器和矩阵键盘的接口。主设备采用12V电源进行供电,首先经过MP1584en稳压模块将电压降至5V,为GSM模块、语音模块、继电器进行供电(原定采用AMS1117-5.0进行5V稳压,但由于AMS1117的最大输出电流只有1A,无法满足设备的供电需求)。然后在经过AMS1117-3.3将电压稳压到3.3V,为GPS模块、OLED显示屏、NRF24L01无线通信模块、热释电红外模块进行供电。
2.从机
从机由STM32F030F4P6作为主控芯片,主要负责对酒精传感器的信号进行采集,并通过NRF24L01将采集到的信号发送给主机做酒驾判断。为了减小从机的体积,从机部分没有采用外部的晶振提供时钟信号,只保留了稳压电路和滤波电路。
3.酒精传感器
酒精传感器由TGS2620作为敏感元件,首先让TGS2620输出的电压信号经过惠斯通电桥,形成一组差分信号,然后经过由LM358组成的差分运算放大器,从而获得到空气中酒精浓度的相对变化值。
三、程序设计
主机部分
1.GPS GPS通过USART3与主机进行通信,波特率为9600。使用NMEA-0183协议进行通信,获取GPS返回的GPGGA数据,然后进行相关的数据解析。 - <font size="3" color="#000000">//********************************************************************
- //得到GPS的经纬度
- //数据为char型数组
- //若没有得到数据则无返回结果
- //********************************************************************
- void GPS_GetData()
- {
- u8 i;
- if(USART3_RX_STA&0X8000) //接收到一次数据了
- {
- USART3_RX_STA=0; //启动下一次接收
-
- GPS_Analysis(&gpsx,(u8*)USART3_RX_BUF);//分析字符串
-
- if(gpsx.gpssta==1 ||gpsx.gpssta==2 ) //GPS定位成功
- {
- u8 i;
-
- GPS_positioning = 1;
- Transform_double_to_char(((double)(gpsx.longitude)/10000000-0.003466), longitude_char, 6, 1);
- Transform_double_to_char(((double)(gpsx.latitude)/10000000+0.323707), latitude_char, 6, 1);
-
- i =strlen(longitude_char);
- longitude_char[i] =gpsx.ewhemi;
- longitude_char[i+1] ='\0';
-
- i =strlen(latitude_char);
- latitude_char[i] =gpsx.nshemi;
- latitude_char[i+1] ='\0';
-
- printf("GPS定位成功\r\n");
- }
- else if(gpsx.gpssta==0)
- {
- if(GPS_positioning == 1 )
- {
- GPS_positioning = 2;
- printf("GPS信号弱\r\n");
- }
- else if(GPS_positioning == 0 )
- {
- printf("GPS无信号\r\n");
- }
- }
- gpsx.gpssta = 0;
- }
- }
- //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>END
- //********************************************************************
- //浮点型/整型转字符型
- //Decimal需转换小数
- //Character字符型储存的地址,在主函数定义
- //precision保留的精度
- //round是否四舍五入,1是,0否
- //注意可以转换的长度
- //********************************************************************
- void Transform_double_to_char(double Decimal, char *Character, short int precision, char round)
- {
- int i,x=1;
- int integer ,integer_decimals;
- char temporary_char[10];
- for(i=0;i<precision;i++)
- {
- x *=10;
- }
- integer = (int)Decimal; //整数部分
- integer_decimals = (int)((Decimal - integer) * x + 0.5*round);
- for(i=0 ;integer>0 ;i++)
- {
- temporary_char[i]= (integer % 10) + '0';
- integer = (int)(integer/10);
- }
- if( i==0)
- {
- temporary_char[i]= '0';
- i++;
- }
- for(x=0;x<i;x++)
- {
- Character[x] =temporary_char[i-x-1];
- }
- memset(temporary_char,NULL,sizeof(temporary_char));
- if(precision!=0)
- {
- Character[x]= '.';
- x++;
- for(i=0 ;i<precision ;i++)
- {
- temporary_char[i]= (integer_decimals % 10) + '0';
- integer_decimals = (int)(integer_decimals/10);
- }
- for(i=0 ;i<precision ;x++,i++)
- {
- Character[x] = temporary_char[precision-i-1];
- }
- }
- Character[x] ='\0';
- }
- //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>END</font>
复制代码
2.GSM GSM采用USART2进行通信,向模块发送相应的AT指令既可完成对模块相应的操作。 - <font size="3" color="#000000">//********************************************************************
- //SIM执行命令
- //0~3发送短信,0~3号联系人
- //********************************************************************
- char SIM900A_Executive_Command( char number )
- {
- if( number == 0 ||number == 1 ||number == 2 ||number == 3 )
- {
- int i ,Length;
- char Message_content[280];
- char AT_CMGS[4],Data_Length[4];
- char PUD_latitude[50],PUD_longitude[50];
- char PUD_longitude_latitude[100];
- char PUD_comma[5]={"002C"};
-
- PDU_Transform__Phone_number(Phone_number[number]);
-
- if(GPS_positioning != 0)
- {
- PDU_Transform__Message_content(Message_content,Message_content_original_1);
-
- Char_to_Unicode(latitude_char, PUD_latitude );
- Char_to_Unicode(longitude_char, PUD_longitude );
-
- memset(PUD_longitude_latitude,NULL,strlen(PUD_longitude_latitude));
- String_addition(PUD_longitude_latitude ,PUD_latitude ,0);
- String_addition(PUD_longitude_latitude ,PUD_comma ,strlen(PUD_longitude_latitude));
- String_addition(PUD_longitude_latitude ,PUD_longitude ,strlen(PUD_longitude_latitude));
-
- String_addition(Message_content ,PUD_longitude_latitude ,15*4);
- }
- else
- {
- PDU_Transform__Message_content(Message_content,Message_content_original_2);
- }
- Length = strlen(Message_content)/2;
- Transform_int_to_hexadecimal(Length ,Data_Length ,2);
- Transform_double_to_char( Length+15, AT_CMGS, 0, 0);
-
- printf("%s\r\n",PDU_Phone_number);
- printf("%s\r\n",Data_Length);
- printf("%s\r\n",Message_content);
-
- for(i=0;i<Number_retries;i++)
- {
- SIM900A_Send_AT( "AT+CMGS=" ,0 );
- SIM900A_Send_AT( AT_CMGS ,1 );
-
- if(USART2_Data_seek(USART2_Data ,">" ,500))
- {
- SIM900A_Send_AT( PDU_Phone_number ,0 );
- SIM900A_Send_AT( Data_Length ,0 );
- SIM900A_Send_AT( Message_content ,0 );
- USART_SendData( USART2, 0x1A );
- SIM900A_Send_AT( 0 ,1 );
- if(USART2_Data_seek(USART2_Data ,"ERROR" ,2000))
- {
- printf("发送失败\r\n");
- return 0xFF;
- }
- printf("发送成功\r\n");
- return 0x00;
- }
- else printf("AT+CMGS指令错误\r\n");
- }
- }
- printf("指令错误\r\n");
- return 0xFF;
- }
- //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>END
- //********************************************************************
- //通过串口2发送AT指令
- //可以直接发字符串或发数组
- //********************************************************************
- void SIM900A_Send_AT( char *AT_command ,char over )
- {
- for(;*AT_command!='\0';AT_command++)
- {
- while( USART_GetFlagStatus( USART2 , USART_FLAG_TXE ) == 0 );
- USART_SendData( USART2, *AT_command );
- }
- if( over )
- {
- while( USART_GetFlagStatus( USART2 , USART_FLAG_TXE ) == 0 );
- USART_SendData( USART2, 0x0d );// /r,0x0d,回车的作用只是移动光标至该行的起始位置
- while( USART_GetFlagStatus( USART2 , USART_FLAG_TXE ) == 0 );
- USART_SendData( USART2, 0x0a );// /n,0x0a,换行至下一行行首起始位置;
- }
- while( USART_GetFlagStatus( USART2 , USART_FLAG_TXE ) == 0 );
- }
- //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>END
- //*******************************************************************
- //将手机号码转换为PDU格式
- //*******************************************************************
- void PDU_Transform__Phone_number(char *Phone_number_input)
- {
- u8 i;
- Phone_number_input[11] = 'F';
- for(i =0 ;i<12 ;i++)
- {
- if( i%2 == 0)
- {
- PDU_Phone_number[i+12] = Phone_number_input[i+1];
- }
- else
- {
- PDU_Phone_number[i+12] = Phone_number_input[i-1];
- }
- }
- Phone_number_input[11] = '\0';
- }
- //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>END
- //*******************************************************************
- //字符串内添加字符串
- //原字符串String
- //需要添加的字符串Add_String
- //第Add_location字之后添加
- //*******************************************************************
- void String_addition(char *String ,char *Add_String ,int Add_location)
- {
- u8 i;
- int length_String ,length_Add;
- length_String = strlen(String);
- length_Add = strlen(Add_String);
-
- String[ length_String+length_Add ] ='\0';
-
- for( ;length_String> Add_location ; length_String--)
- {
- String[ length_String-1+length_Add ] = String[ length_String-1 ];
- }
-
- for( ;length_Add>0 ;length_Add--)
- {
- String[ Add_location-1+length_Add ] = Add_String[ length_Add-1 ];
- }
- }
- //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>END</font>
复制代码
3.NRF24L01 NRF24L01采用SPI进行通信,由从机发送信息,主机只进行接收信息,并对信息进行解析。 - <font size="3" color="#000000">**************************************
- //NRF24L01数据解析
- //********************************************************************
- void NRF24L01_Data_Parse(const char *data)
- {
- u8 i,x;
- char cache[8],slave;
- if( data[0]=='K' )
- {
- for(i=1,x=0; data[i]!='#'; i++)
- {
- switch(data[i])
- {
- case 'H' : for(i++,x=0 ;data[i]!='&' ;i++) //没有结束
- {
- cache[x] = data[i];
- x++;
- };
- cache[x]='\0';
- slave = Transform_char_to_double(cache);
- slave_disconnect[slave-1] = 0;
- // printf("slave=%d\r\n",slave);
- break;
-
- case 'V' :
- for(i++,x=0 ;data[i]!='&' ;i++) //没有结束
- {
- cache[x] = data[i];
- x++;
- };
- cache[x]='\0';
- slave_unit_voltage[slave-1] = Transform_char_to_double(cache);
- // printf("voltage=%lf\r\n",slave_unit_voltage[slave-1]);
- break;
- case 'A' : for(i++,x=0 ;data[i]!='&' ;i++) //没有结束
- {
- cache[x] = data[i];
- x++;
- };
- cache[x]='\0';
- slave_unit_alcohol_concentration[slave-1] = Transform_char_to_double(cache);
- // printf("alcohol_concentration=%d\r\n",slave_unit_alcohol_concentration[slave-1]);
- break;
- }
- memset(cache,NULL,strlen(cache));
- }
- }
- }
- //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>END
- //********************************************************************
- //接收到数据进入中断
- //分析接收到的数据
- //********************************************************************
- void EXTI15_10_IRQHandler()
- {
- if(EXTI_GetITStatus(EXTI_Line12))
- {
- char NRF_RX_Data[32];
- NRF24L01_RxPacket(NRF_RX_Data);
- // printf("%s\r\n",NRF_RX_Data);
- NRF24L01_Data_Parse(NRF_RX_Data);
- }
- EXTI_ClearITPendingBit(EXTI_Line12);
- }
- //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>END</font>
复制代码
4.OLED显示 OLED显示屏采用软件IIC进行数据通信,每200ms更新一次显示信息,每4s进行一次翻页,显示不同的设备状态信息。 - <font size="3" color="#000000">//********************************************************************
- //200ms进入一次中断
- //更新oled显示
- //********************************************************************
- void TIM1_TRG_COM_TIM11_IRQHandler()
- {
- if(TIM_GetITStatus(TIM11, TIM_IT_Update))
- {
- extern char PILOT;
- static int Enter_number =0,Page =0 ,Canned_format=0,aaa=0;
- Page = Enter_number/20 ;//控制翻页时间200*20ms
-
- //********************************************************************
- //第一页信息
- //GPS状态
- //********************************************************************
- if( Page ==0 )
- {
- extern char GPS_positioning;
- extern char longitude_char[15],latitude_char[15];
- if( Canned_format/10 != 1)
- {
- OLED_CLS();
- OLED_P8x16Str(36,0,"GPS",F8x16);
- OLED_P16x16Str(60,0,"状态",F16x16_Idx,F16x16);
- OLED_P6x8Str(122,0,"1",F6x8);
-
- Canned_format = 10;
- }
- if( GPS_positioning == 0)
- {
- if(Canned_format%10 != 1 )
- {
- OLED_P8x16Str(0,24,"GPS",F8x16);
- OLED_P16x16Str(24,24,"定位失败",F16x16_Idx,F16x16);
- Canned_format = 11;
- }
- }
- else if( GPS_positioning == 1)
- {
- if(Canned_format%10 != 2 )
- {
- OLED_CLS_y(16);
- OLED_CLS_y(24);
- OLED_CLS_y(32);
- OLED_P8x16Str(0,16,"GPS",F8x16);
- OLED_P16x16Str(24,16,"定位成功",F16x16_Idx,F16x16);
- Canned_format = 12;
- }
- OLED_P8x16Str(0,32,longitude_char,F8x16);
- OLED_P8x16Str(0,48,latitude_char,F8x16);
- }
- else if( GPS_positioning == 2)
- {
- if(Canned_format%10 != 3 )
- {
- OLED_CLS_y(16);
- OLED_CLS_y(24);
- OLED_P8x16Str(0,16,"GPS",F8x16);
- OLED_P16x16Str(24,16,"信号弱",F16x16_Idx,F16x16);
- OLED_P8x16Str(0,32,longitude_char,F8x16);
- OLED_P8x16Str(0,48,latitude_char,F8x16);
- Canned_format = 13;
- }
- }
- }
- //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>END
-
-
-
- //********************************************************************
- //第二页信息
- //设备连接情况
- //********************************************************************
- else if( Page ==1 )
- {
- extern u8 slave_disconnect[3];
-
- if( Canned_format/10 != 2)
- {
-
- OLED_CLS();
- OLED_P16x16Str(16,0,"设备连接",F16x16_Idx,F16x16);
- OLED_P16x16Str(80,0,"状态",F16x16_Idx,F16x16);
- OLED_P6x8Str(122,0,"2",F6x8);
- OLED_P16x16Str(0,16,"设备",F16x16_Idx,F16x16);
- OLED_P8x16Str(32,16," 1 ",F8x16);
- OLED_P16x16Str(0,32,"设备",F16x16_Idx,F16x16);
- OLED_P8x16Str(32,32," 2 ",F8x16);
- OLED_P16x16Str(0,48,"设备",F16x16_Idx,F16x16);
- OLED_P8x16Str(32,48," 3 ",F8x16);
-
- Canned_format = 20;
- }
-
- if( slave_disconnect[0] == 100)
- {
- if(Canned_format%10 != 1 )
- {
- OLED_P16x16Str(56,16,"断开连接",F16x16_Idx,F16x16);
- Canned_format = 21;
- }
- }
- else
- {
- if(Canned_format%10 != 2 )
- {
- OLED_P16x16Str(56,16,"连接成功",F16x16_Idx,F16x16);
- Canned_format = 22;
- }
- }
-
- if( slave_disconnect[1] == 100)
- {
- if(Canned_format%10 != 3 )
- {
- OLED_P16x16Str(56,32,"断开连接",F16x16_Idx,F16x16);
- Canned_format = 23;
- }
- }
- else
- {
- if(Canned_format%10 != 4 )
- {
- OLED_P16x16Str(56,32,"连接成功",F16x16_Idx,F16x16);
- Canned_format = 24;
- }
- }
-
- if( slave_disconnect[2] == 100)
- {
- if(Canned_format%10 != 5 )
- {
- OLED_P16x16Str(56,48,"断开连接",F16x16_Idx,F16x16);
- Canned_format = 25;
- }
- }
- else
- {
- if(Canned_format%10 != 6 )
- {
- OLED_P16x16Str(56,48,"连接成功",F16x16_Idx,F16x16);
- Canned_format = 26;
- }
- }
- }
- //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>END
-
-
-
- //********************************************************************
- //第三页信息
- //电量
- //********************************************************************
- else if( Page ==2 )
- {
- extern double voltage;
- extern double slave_unit_voltage[3];
-
- char OLED_voltage[4][6];
-
- if( Canned_format/10 != 3)
- {
- OLED_CLS();
- OLED_P16x16Str(48,0,"电量",F16x16_Idx,F16x16);
- OLED_P6x8Str(122,0,"3",F6x8);
- OLED_P8x16Str(0,24, "M: V",F8x16);
- OLED_P8x16Str(72,24,"S1: V",F8x16);
- OLED_P8x16Str(0,48, "S2: V",F8x16);
- OLED_P8x16Str(72,48,"S3: V",F8x16);
-
- Canned_format = 30;
- }
- Transform_double_to_char( voltage , OLED_voltage[0], 1, 1);
- Transform_double_to_char( slave_unit_voltage[0] , OLED_voltage[1], 1, 1);
- Transform_double_to_char( slave_unit_voltage[1] , OLED_voltage[2], 1, 1);
- Transform_double_to_char( slave_unit_voltage[2] , OLED_voltage[3], 1, 1);
-
- OLED_P8x16Str(16,24,OLED_voltage[0],F8x16);
- OLED_P8x16Str(94,24,OLED_voltage[1],F8x16);
- OLED_P8x16Str(24,48,OLED_voltage[2],F8x16);
- OLED_P8x16Str(94,48,OLED_voltage[3],F8x16);
- }
- //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>END
-
-
-
- //********************************************************************
- //第四页信息
- //酒精浓度
- //********************************************************************
- else if( Page ==3 )
- {
- extern short int alcohol_concentration;
- extern short int slave_unit_alcohol_concentration[3];
-
- char OLED_alcohol_concentration[4][6];
-
- if( Canned_format/10 != 4)
- {
- OLED_CLS();
- OLED_P16x16Str(32,0,"酒精浓度",F16x16_Idx,F16x16);
- OLED_P6x8Str(122,0,"3",F6x8);
-
- OLED_P8x16Str(0,24,"A1:",F8x16);
- OLED_P8x16Str(64,24,"A2:",F8x16);
- OLED_P8x16Str(0,48,"A3:",F8x16);
- OLED_P8x16Str(64,48,"A4:",F8x16);
-
- Canned_format = 40;
- }
-
- Transform_double_to_char( alcohol_concentration , OLED_alcohol_concentration[0], 0, 1);
- Transform_double_to_char( slave_unit_alcohol_concentration[0] , OLED_alcohol_concentration[1], 0, 1);
- Transform_double_to_char( slave_unit_alcohol_concentration[1] , OLED_alcohol_concentration[2], 0, 1);
- Transform_double_to_char( slave_unit_alcohol_concentration[2] , OLED_alcohol_concentration[3], 0, 1);
-
-
- OLED_P8x16Str(48,24," ",F8x16);
- OLED_P8x16Str(112,24," ",F8x16);
- OLED_P8x16Str(48,48," ",F8x16);
- OLED_P8x16Str(112,48," ",F8x16);
-
- OLED_P8x16Str(24,24,OLED_alcohol_concentration[0],F8x16);
- OLED_P8x16Str(88,24,OLED_alcohol_concentration[1],F8x16);
- OLED_P8x16Str(24,48,OLED_alcohol_concentration[2],F8x16);
- OLED_P8x16Str(88,48,OLED_alcohol_concentration[3],F8x16);
-
- }
- //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>END
-
-
-
- //********************************************************************
- //第五页信息
- //
- //********************************************************************
- else if( Page ==4 )
- {
- extern char Phone_number[4][12];
- if( Canned_format/10 != 5)
- {
- OLED_CLS();
- OLED_P16x16Str(44,0,"联系人",F16x16_Idx,F16x16);
- OLED_P6x8Str(122,0,"5",F6x8);
-
- OLED_P8x16Str(0,16,"C1:",F8x16);
- OLED_P8x16Str(24,16,Phone_number[0],F8x16);
- OLED_P8x16Str(0,32,"C2:",F8x16);
- OLED_P8x16Str(24,32,Phone_number[1],F8x16);
- OLED_P8x16Str(0,48,"C3:",F8x16);
- OLED_P8x16Str(24,48,Phone_number[2],F8x16);
-
- Canned_format = 50;
- }
- }
- //********************************************************************
- //第六页信息
- //车辆状态
- //********************************************************************
- else if( Page ==5 )//第一页
- {
- extern char DRUNK_DRIVING ;
- if( Canned_format/10 != 6)
- {
- OLED_CLS();
- OLED_P16x16Str(32,0,"车辆状态",F16x16_Idx,F16x16);
- OLED_P6x8Str(122,0,"4",F6x8);
-
- Canned_format = 60;
- }
-
- if(DRUNK_DRIVING==0)
- {
- if( Canned_format%10 != 1)
- {
- OLED_CLS_y(16);
- OLED_CLS_y(24);
- OLED_CLS_y(32);
- OLED_CLS_y(48);
- OLED_CLS_y(56);
- OLED_P16x16Str(16,32,"车辆正常行驶",F16x16_Idx,F16x16);
- Canned_format = 61;
- }
- }
- else
- {
- if( Canned_format%10 != 2)
- {
- OLED_CLS_y(16);
- OLED_CLS_y(24);
- OLED_CLS_y(32);
- OLED_CLS_y(48);
- OLED_CLS_y(56);
- OLED_P16x16Str(8,24,"发现驾驶员酒驾",F16x16_Idx,F16x16);
- OLED_P16x16Str(16,48,"车辆禁止行驶",F16x16_Idx,F16x16);
- Canned_format = 62;
- }
- }
- }
-
- //********************************************************************
- //重新开始显示第一页
- //********************************************************************
- else if(Page==6)
- {
- Enter_number =0;
- Canned_format=0;
- }
- Enter_number++;
- }
- TIM_ClearITPendingBit(TIM11, TIM_IT_Update);
- }
- //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>END</font>
复制代码
5.酒精传感器 酒精传感器使用ADC+DMA进行数据采集,每50ms进行一次ADC采集,每次采集20个数据,对这20个数据取平均值,获得一次酒精传感器的测量值。每100ms进入一次定时器中断,将主机采集的酒精传感器测量值与其余从机采集的酒精传感器测量值进行比较,从而判断驾驶员是否酒驾。 - <font size="3" color="#000000">//********************************************************************
- //100ms进入一次中断
- //判断是否酒驾
- //********************************************************************
- void TIM1_BRK_TIM9_IRQHandler()
- {
- extern short int alcohol_concentration;
- extern short int slave_unit_alcohol_concentration[3];
- extern char DRUNK_DRIVING;
- extern u8 voice_prompt[15] ;
- static u8 Number_of_drunk_driving= 0;
- if(TIM_GetITStatus(TIM9, TIM_IT_Update))
- {
- double Weight_M=1 ,Weight_S1=1 ,Weight_S2=1 ,Weight_S3=1 ;
- if( alcohol_concentration > 2000 || DRUNK_DRIVING == 1)//当主传感器大于规定值开始判断
- {
- //*****插入算法判断是否酒驾,之后需要修改
- if(Weight_M * alcohol_concentration > (Weight_S1*slave_unit_alcohol_concentration[0]+
- Weight_S2*slave_unit_alcohol_concentration[1] +
- Weight_S3*slave_unit_alcohol_concentration[2] +500) /3;
- )
- {
- Number_of_drunk_driving++;
- }
- else if(Number_of_drunk_driving!=0)
- {
- Number_of_drunk_driving--;
- }
- //>>>>>>>>>>>>>>>>>>>>>>>END
-
- //*****是否连续10次判定为酒驾
- if(Number_of_drunk_driving>10)
- {
- Number_of_drunk_driving = 10;
- DRUNK_DRIVING = 1;
- if( voice_prompt[3] == 0 )
- {
- JQ6500_play(4);
- voice_prompt[3] = 1;
- voice_prompt[4] = 0;
- }
- }
- else if(DRUNK_DRIVING == 1 && Number_of_drunk_driving ==0)
- {
- DRUNK_DRIVING = 0;
- if( voice_prompt[4] == 0 && voice_prompt[3] == 1)
- {
- JQ6500_play(5);
- voice_prompt[4] = 1;
- voice_prompt[3] = 0;
- }
- }
- //>>>>>>>>>>>>>>>>>>>>>>>END
- }
- }
- TIM_ClearITPendingBit(TIM9, TIM_IT_Update);
- }
- //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>END</font>
复制代码
6.语音模块 语音模块采用UART4进行通信,波特率为9600.通过UART4发送16进制指令对JQ6500语音模块进行控制。 - <font size="3" color="#000000">*****************************************************
- //JQ6500发送控制指令
- //********************************************************************
- void JQ6500_control_command(char command)
- {
-
- char JQ6500_instruct_1[5]={0x7E ,0x03 ,0x06 ,0x02 ,0xEF}; //音量00~1E
- char JQ6500_instruct_2[5]={0x7E ,0x03 ,0x06 ,0x17 ,0xEF}; //音量00~1E
- char JQ6500_instruct_3[5]={0x7E ,0x03 ,0x06 ,0x1E ,0xEF}; //音量00~1E
-
- char JQ6500_instruct_4[5]={0x7E ,0x03 ,0x11 ,0x04 ,0xEF}; //播放模式:0 1 2 3 4(ALL FOL ONE RAM ONE_STOP)
-
- char JQ6500_instruct_5[4]={0x7E ,0x02 ,0x0A ,0xEF}; //低功耗
- char JQ6500_instruct_6[4]={0x7E ,0x02 ,0x0C ,0xEF}; //复位
-
- switch (command)
- {
- case 1:Send_hexadecimal_UART4(JQ6500_instruct_1);break;
- case 2:Send_hexadecimal_UART4(JQ6500_instruct_2);break;
- case 3:Send_hexadecimal_UART4(JQ6500_instruct_3);break;
- case 4:Send_hexadecimal_UART4(JQ6500_instruct_4);break;
- case 5:Send_hexadecimal_UART4(JQ6500_instruct_5);break;
- case 6:Send_hexadecimal_UART4(JQ6500_instruct_6);break;
-
- default: break;
- }
- delay_ms(50);
- }
- //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>END
- //********************************************************************
- //JQ6500语音模块播放指定曲目
- //********************************************************************
- void JQ6500_play(u16 number)
- {
- char JQ6500_play_number[6]={0x7E ,0x04 ,0x03 ,0x00 ,0x00 ,0xEF}; //音量00~1E
- JQ6500_play_number[4] = (u8)number;
- JQ6500_play_number[3] = (u8)(number>>8);
-
- while(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_12)==1);
- while((GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_12)==0))
- {
- Send_hexadecimal_UART4(JQ6500_play_number);delay_ms(100);
- }
- while(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_12)==1);
- }
- //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>END
- //********************************************************************
- //UART4发送16进制数据
- //********************************************************************
- void Send_hexadecimal_UART4( char *array )
- {
- for( ;*array!=0Xff ;array++)
- {
- while( USART_GetFlagStatus( UART4 , USART_FLAG_TXE ) == 0 );
- USART_SendData( UART4, *array );
- while( USART_GetFlagStatus( UART4 , USART_FLAG_TXE ) == 0 );
- }
- }
- //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>END
- </font>
复制代码
从机部分
从机采用HAL库进行编程
1.NRF24L01 每次完成一次DMA传输后进行一次数据打包,在主函数中进行检测,如果数据准备好了,就启动一次数据的传输。
- <font size="3" color="#000000">void DMA1_Channel1_IRQHandler(void)
- {
- u8 i;
- double voltage;
- int alcohol_concentration;
- char Transform[10],data_head[5]={"KH1&"};
- u32 ADC_data1=0,ADC_data2=0;
- for(i=0;i<20;i++)
- {
- ADC_data1 += DMA_ADC_DATA[i][0];
- ADC_data2 += DMA_ADC_DATA[i][1];
- }
- alcohol_concentration = (double)(ADC_data1/20)+0.5;
- voltage = (double)(ADC_data2/20)/4096*3.3*(3-0.025)+0.005;
-
- if(NRF24l01_write_TXdata == 0)
- {
- memset(NRF_TX_Data,NULL,strlen(NRF_TX_Data)); //清空要发送的数据
- strcat(NRF_TX_Data, data_head); //发送数据加数据头
- NRF_TX_Data[strlen(NRF_TX_Data)] = 'V'; //添加第一段数据
- Transform_double_to_char(voltage, Transform, 2, 1);
- strcat(NRF_TX_Data, Transform);
- NRF_TX_Data[strlen(NRF_TX_Data)] = '&';
-
- NRF_TX_Data[strlen(NRF_TX_Data)] = 'A'; //添加第二段数据
- Transform_double_to_char(alcohol_concentration, Transform, 0, 1);
- strcat(NRF_TX_Data, Transform);
- NRF_TX_Data[strlen(NRF_TX_Data)] = '&';
-
- NRF_TX_Data[strlen(NRF_TX_Data)] = '#'; //添加结束符
- }
- // printf("%s\r\n",NRF_TX_Data);
-
- HAL_DMA_IRQHandler(&hdma_adc);
- HAL_ADC_Stop_DMA(&hadc);
- }</font>
复制代码
四、实物展示
五、改进方向
1.酒精传感器
刚开始使用MQ-3作为酒精传感器,测量灵敏度不足,所以采用电桥电路,并使用运放将信号进行放大。但之后更换TGS2620作为敏感元件,灵敏度大大增加,只使用简单的电阻分压电路就可以得到较为准确的测量值。目前的电桥电路设计也有一些问题,由于调零电阻的关系,导致同样酒精浓度变化下传感器测量值的变化灵敏度不一致,如果想使用电桥电路获得更精准的测量数据,需要改进电桥电路的调零部分。
2.短信
当前的GSM模块只可以发送经纬度数据,需要用户自己使用谷歌地球进行位置查询。今后可以使用GPRS数据传输与阿里云等平台进行网络连接,通过一些高德地图提供的API进行对经纬度进行逆地理编码,从而获得当前具体的所在位置。也可以使用微信小程序,直接查看当前所在位置的地图。
3.加入键盘
主机的PCB电路板已经预留了矩阵键盘的接口,加入键盘后可对联系人进行设置,OLED显示信息进行翻页等操作。如果使用了微信小程序,可在小程序上完成对联系人进行设置的功能,省略键盘。
4.加入人体检测
开始计划使用红外热释电传感器检测是否车内有人,当有人驾驶时,系统开始检测酒驾行为。无人驾驶时进入低功耗模式。但由于红外热释电只能检测运动的物体,无法精准检测人体活动,容易造成误检测。之后可以通过判断是否有启动车辆行为来决定系统是否进入工作模式,或采用其他模块检测人体活动。
|