10094-基于STM32的智能公交显示系统设计(APP源码+PCB+原理图+源代码工程+详细介绍说明书+PPT)
功能描述:
公共汽车行驶位置显示系统所需达到的需求进行了分析,归纳为:
1.对公交站台的播报,实现到站播报;
2.实现公交站台的到站显示,展示每个站台的站台名称;
3.可以实现自动化到站播报以及手动的方式进行站台信息播报;
4.实时的更新GPS的定位,当到站后进行站台信息播报;
5.可以对站点进行录入,根据不同的录入点实现站点的定位;
6.每个站台自动播报的范围,公交站台播报GPS定位精度在50m以内。
资料摘要:
公共汽车成为城市人们出行的必备交通工具。但是现在所面临的一个问题是,随着公交系统的发展,需要准确的了解到当前公交车运行系统中,车辆的实际地理位置,经过哪些站台以及乘车的状态等。这些问题需要及时解决,以此来保证当前公交车运行系统可以正确按照指定路线运行,当到达指定的地点可以及时的实现站台的准确播报等。针对此问题,本次系统设计出一款公交车运行位置显示系统。
本次设计的题目为基于单片机的智能公交显示系统。在系统中需要使用到控制芯片STM32F103,GPS定位芯片,液晶显示器用于展示公交车站台信息以及提示语等,通过语音芯片实现语音播报功能。并可通过蓝牙传输数据至站台,同步显示到站信息。通过对系统的电路设计、软件系统设计来保证系统的功能实现并且为人们的出行提供了方便。解决了以往公交报站系统中人工操作不便,误报站多,故障时不报站等缺点,实现智能化。在设计中考虑到了系统的定位精度、稳定性、抗干扰能力等,保证设计系统是一个可以经历市场验证的控制系统。
资料包含:
1、详细设计说明书-15312字
2、仿真工程文件(赠送-低配公交车显示系统仿真)
3、原理图工程文件
4、源代码工程文件
5、PCB工程文件
6、实物图片-超详细各个角度、功能拍摄
7、介绍PPT
8、APP源码及安卓安装apk文件
9、原理图截图
#include "my_include.h"
#define F_SIZE 12 //定义显示字体大小 汉字显示要定义了字模才可以
#define MyLCD_Show(m,n,p) LCD_ShowString(LCD_GetPos_X(F_SIZE,m),LCD_GetPos_Y(16,n),p,F_SIZE,false) //显示函数
#define ADDR_FLASH_WRITE (FLASH_BASE_ADDR+STM32_FLASH_SIZE*1024-STM_SECTOR_SIZE*1)
int saveData[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //0-5寸经度E 6-11寸纬度N
#define POS_MENU_X 1 //菜单显示的起始X 即'>'指向的位置
#define POS_MENU_Y 0//菜单显示起始行数
u16 remCurMenuId = 0xff;//记录当前菜单位置
u16 yPlace = 0; //显示位置y坐标值
void scanKeyAnddealKey(void);
void My_ESP8266_SendStrStr(USART_TypeDef* USARTx, const char *str);
unsigned char makeSureLinkCount=0; //确保链接变量
void displayOfCollectedData(void);//显示采集到的数据
unsigned int i;
char dis0[64];//液晶显示暂存数组
char dis1[32];//液晶显示暂存数组
u8 startCheckWireLess = 0;//启动检测无线种类
u8 wirelessFlag = 0;//无线种类 0蓝牙或无无线 1wifi
unsigned char setFlag = 0;//设置标志
#define STATION_NUM 6
char *staNameStr[] = {"● 火车站 ","● 人民公园站 ","● 长途汽车站 ","● 动物园站 ","● 工业园区站 ","● 大学城站 "};
typedef struct
{
u8 nowSt;//当前站标志 0-5 表示6站
u8 remSt;//记录当前站点
u8 runDir;//向下运行 //0向下运行 1向上
u8 dirCount;//向当前运行方向
u8 dirDelay;
u8 nowMode;
u8 setGpsFlag;
u8 remGpsFlag;
}_currentStaMes_obj;
_currentStaMes_obj nowMes ={0,0xff,0,0,0,0,0,0xff};
//以下字符串对应 语音音频名称的编号
#define HYNSY "01111" //欢迎使用
#define YYADD_1 "31761"
//站点 60-65
//到了 66
//下一站 67
//终点站 68
//公益播 69
//unsigned char needPlay=0;//需要语音播报
//unsigned char delayPlay=0;//播报延时
u8 needPlaySt_Key=0xff;//需要语音播报
u8 needPlaySt_Gps=0xff;//需要语音播报
char *yyAddDown[STATION_NUM]= {"6066676169","6166676269","6266676369","6366676469","6466676569","656668"};
char *yyAddUp[STATION_NUM] = {"606668","6166676069","6266676169","6366676269","6466676369","6566676469"};
//经纬度差1度对应距离111000米,则0.0001 相差11.1米
//#define OFFSET_NUM 400 //因为显示时/1000000 如500则相当于经纬度编号0.0005 距离上相差55.5米
u16 OFFSET_NUM = 500;
int offsetN; //经纬度偏移大小
int offsetE;
//int saveN[STATION_NUM] = {1,2,3,4,5,6};//用于纬度信息
//int saveE[STATION_NUM] = {1,2,3,4,5,6};//用于经度信息
//int testN = 32123456; //模拟gps实际数据
//int testE = 117123456;//模拟gps实际数据
u8 gpsErrCount = 0;//gps定位失败次数检测
#define OPEN_ANGLE 60
#define CLOSE_ANGLE -60
#define OPEN_DELAY 10
u16 openDjDelay = 0; //打开舵机时间
int main(void)
{
USARTx_Init(USART1,9600);//串口初始化为9600
USARTx_Init(USART2,9600);//串口初始化为9600
USARTx_Init(USART3,9600);//串口初始化为9600
My_GPS_Init(USART2);//GPS初始化
My_KEY_Init();//初始化输入
My_LED_Init(); //输出初始化
My_SG90_Init(0 ,TIM2,TIM_CH_2);//舵机初始化 -90到90度
My_STMFlash_SectorSaveInit(ADDR_FLASH_WRITE,saveData,sizeof(saveData));
delay_ms(50);
My_SG90_SetAngle(0,0); //控制转动方向
delay_ms(100);
My_SG90_SetAngle(0,CLOSE_ANGLE); //控制转动方向
delay_ms(100);//上电一会才能读取稳定
My_JR6001_Init(31); //初始化语音播报
delay_ms(10);
if(My_JR6001_IsBusy() == false ) My_JR6001_PlayByName(HYNSY); //播报 欢迎乘坐
for(i=0;i<5;i++)delay_ms(100); //延时一定时间
LCD_Init(); //tft初始化
LCD_Clear(Color16_BLACK);//清全屏
BACK_COLOR=Color16_BLACK;FRONT_COLOR=Color16_RED; //设置显示颜色
MyLCD_Show(4,1,"Init... ");//显示
MyLCD_Show(4,1,"Init... ");//显示
i = 5;startCheckWireLess=1;//启动检测 一段时间取消
while(i-- && wirelessFlag == 0) //此时判断是无线类型
{USARTSendString(USART1,"ATE0\r\n"); delay_ms(100);}
startCheckWireLess = 0;//取消检测
if(wirelessFlag == 1)
{
i = 50;while(i--){delay_ms(100);} //wifi启动 必须延时5s或以上
// USARTSendString(USART1,"AT+CWMODE_CUR=3\r\n");// 设置模式
// delay_ms(50);
// USARTSendString(USART1,"AT+CWSAP=\"AUAISOUT000\",\"\",1,0\r\n");//设置网络名字及密码
// delay_ms(50);
// USARTSendString(USART1,"AT+CIPAP_CUR=\"10.10.10.11\"\r\n");//设置本机ip
// delay_ms(50);
// USARTSendString(USART1,"AT+CIPMODE=0\r\n");//设置ip模式
// delay_ms(50);
USARTSendString(USART1,"AT+CIPMUX=1\r\n");//打开多连接
delay_ms(50);
USARTSendString(USART1,"AT+CIPSERVER=1,8080\r\n");//启动端口号
delay_ms(50); //以上作为服务端 局域网
}
My_Menu_Init();//初始化菜单
// needWriteFlash =40;// 约4s自动存储一次数据
openDjDelay = OPEN_DELAY; //打开舵机时间
while(1)
{
if(myReadFlag_tick == true ) //定时读取时间到
{
myReadFlag_tick = false; //清除标志
scanKeyAnddealKey();//按键扫描及处理
switch(My_Menu_GetSelectID())//获取菜单ID
{
case MENU_ID_ADD_NC:/未进入菜单
{
yPlace = 0 ;
if(KeyIsPress(KEY_1) || KeyIsPress(KEY_2)) //按键按下切换方向
{
if(nowMes.runDir == 0)nowMes.runDir =1 ;//改变方向
else nowMes.runDir =0;//切换方向
}
if(KeyIsPress(KEY_2)) //按键按下切换方向
{
OFFSET_NUM = OFFSET_NUM+100;
if(OFFSET_NUM>1200)OFFSET_NUM=100;
}
switch (nowMes.nowMode)//当前模式
{
case 0 : //手动按键模式
if(KeyIsPress(KEY_3)) //按键按下进入下一站
{
if(nowMes.runDir == 0) //向下执行箭头
{if(nowMes.nowSt<STATION_NUM-1)nowMes.nowSt++;}//指向下一站
else
{if(nowMes.nowSt>0)nowMes.nowSt--;} //指向下一站
needPlaySt_Key = 0xff;//需要播报语音 但还没有播报
openDjDelay = OPEN_DELAY; //打开舵机时间
}
if(needPlaySt_Key != 0 )//需要播报
{
if(My_JR6001_IsBusy() == false )//语音空闲状态
{
needPlaySt_Key = 0;//表示已经播报
if(nowMes.runDir == 0) My_JR6001_PlayGroup(yyAddDown[nowMes.nowSt]); //播报站点
else My_JR6001_PlayGroup(yyAddUp[nowMes.nowSt]); //播报站点
}
}
break;
case 1 : //GPS定位模式
for(i=0;i<STATION_NUM;i++)//循环检测gps信息
{
offsetE = abs((int)gps_info.location.longitude - saveData[i]);//对比数据 查看偏移情况
offsetN = abs((int)gps_info.location.latitude - saveData[i+6]);//对比数据 查看偏移情况
if(offsetN < OFFSET_NUM && offsetE < OFFSET_NUM)//说明当前gps和该站点位置
{
nowMes.nowSt = i;//确认记录当前站
if(needPlaySt_Gps != i)//之前未再次站播报
{
needPlaySt_Gps = i;//记录该站点 语音播报
if(nowMes.runDir == 0) My_JR6001_PlayGroup(yyAddDown[i]); //播报站点
else My_JR6001_PlayGroup(yyAddUp[i]); //播报站点
openDjDelay = OPEN_DELAY; //打开舵机时间
}
break;
}
}
break;
default:break;
}
nowMes.dirDelay++;//延时计数
if(nowMes.dirDelay>=4) //约200ms 用于显示 循环指向 箭头
{
nowMes.dirDelay=0;
for(i=0;i<STATION_NUM;i++)//该级菜单选项
{
FRONT_COLOR=Color16_RED;
if(nowMes.runDir == 0) //向下执行箭头
{
if(i == nowMes.dirCount) MyLCD_Show(18,i+1,"↓");//液晶显示字符串
else MyLCD_Show(18,i+1," ");//液晶显示字符串
}
else
{
if(i == nowMes.dirCount) MyLCD_Show(18,STATION_NUM-i,"↑");//液晶显示字符串
else MyLCD_Show(18,STATION_NUM-i," ");//液晶显示字符串
}
}
nowMes.dirCount++;
if(nowMes.dirCount>=STATION_NUM) nowMes.dirCount= 0;//重新计数
}
if(nowMes.remSt != nowMes.nowSt)//站点位置发生变化
{
nowMes.remSt = nowMes.nowSt;//记录最终位置点
FRONT_COLOR=Color16_RED;
MyLCD_Show(3,yPlace++,"智能语音报站系统");//液晶显示字符串
for(i=0;i<STATION_NUM;i++)//该级菜单选项
{
if(nowMes.nowSt == i)FRONT_COLOR=Color16_GREEN;
else FRONT_COLOR=Color16_WHITE;
MyLCD_Show(3,yPlace++,staNameStr[i]);//液晶显示字符串
}
}
}
FRONT_COLOR=Color16_YELLOW;//Color16_GRAY;
if(nowMes.nowMode == 0)MyLCD_Show(1,7,"模式:手动报站 ");//液晶显示字符串
else MyLCD_Show(1,7,"模式:定位报站 ");//液晶显示字符串
break;
case MENU_ID_ADD_X01:进入第一级菜单的第一项 设置模式
if(KeyIsPress(KEY_1)||KeyIsPress(KEY_2)) //按键按下切换模式
{
if(nowMes.nowMode == 0)nowMes.nowMode =1 ;//改变模式
else nowMes.nowMode =0;//切换模式
}
FRONT_COLOR=Color16_RED;
if(nowMes.nowMode == 0)MyLCD_Show(1,2,"模式:手动切换 ");//液晶显示字符串
else MyLCD_Show(1,2,"模式:定位报站 ");//液晶显示字符串
FRONT_COLOR=Color16_WHITE;
MyLCD_Show(1,4,"K1/K2设置模式");//液晶显示字符串
break;
case MENU_ID_ADD_X02:设置项
case MENU_ID_ADD_X03:///设置项
case MENU_ID_ADD_X04:///设置项
case MENU_ID_ADD_X05:///设置项
case MENU_ID_ADD_X06:///设置项
case MENU_ID_ADD_X07:///设置项
nowMes.setGpsFlag = (My_Menu_GetSelectID()&0x000f)-2;//将菜单设置地址0x8002-0x8007转化为对应站点位置0-5
//**All notes can be deleted and modified**//
nowMes.remGpsFlag = nowMes.setGpsFlag;//记录最后状态
yPlace = 1;
FRONT_COLOR=Color16_GREEN;
MyLCD_Show(1,yPlace++,"已设位置:");//液晶显示字符串
MyLCD_Show(2,yPlace++,"经度E:");//液晶显示字符串
MyLCD_Show(2,yPlace++,"纬度N:");//液晶显示字符串
FRONT_COLOR=Color16_WHITE;
MyLCD_Show(1,yPlace++,"此刻定位信息:");//液晶显示字符串
MyLCD_Show(2,yPlace++,"经度E:000.000000 ");//液晶显示字符串
MyLCD_Show(2,yPlace++,"纬度N:000.000000 ");//液晶显示字符串
MyLCD_Show(1,7,"K1/K2设置此刻定位");//液晶显示字符串
if(KeyIsPress(KEY_1)) //按键按下
{
saveData[nowMes.setGpsFlag] = (int)gps_info.location.longitude;//testE; //记录数据
saveData[nowMes.setGpsFlag+6] = (int)gps_info.location.latitude;//testN;
My_STMFlash_SaveUseSector(saveData); //将数据保存到flash 掉电不丢失
}
if(KeyIsPress(KEY_2)) //按键按下
{
saveData[nowMes.setGpsFlag] = 0; //记录数据
saveData[nowMes.setGpsFlag+6] = 0;
My_STMFlash_SaveUseSector(saveData); //将数据保存到flash 掉电不丢失
}
yPlace = 2;
FRONT_COLOR=Color16_GREEN;
sprintf(dis0,"经度E:%010.6lf ",(double)saveData[nowMes.setGpsFlag]/1000000);//打印数据
MyLCD_Show(2,yPlace++,dis0);//液晶显示字符串
sprintf(dis0,"纬度N:%010.6lf ",(double)saveData[nowMes.setGpsFlag+6]/1000000);//打印数据
MyLCD_Show(2,yPlace++,dis0);//液晶显示字符串
yPlace++;
FRONT_COLOR=Color16_WHITE;
if(gps_info.value == true)
{
gpsErrCount = 0;//gps数据正常
sprintf(dis0,"经度E:%010.6lf ",(double)gps_info.location.longitude/1000000);//打印数据
MyLCD_Show(2,yPlace++,dis0);//液晶显示字符串
sprintf(dis0,"纬度N:%010.6lf ",(double)gps_info.location.latitude/1000000);//打印数据
MyLCD_Show(2,yPlace++,dis0);//液晶显示字符串
}
else
{
if(gpsErrCount<25)gpsErrCount++;//记录gps异常次数 超过一定次数 说明掉线了 显示00.000000
else //此时数据已经为0了 所以执行一样
{
sprintf(dis0,"经度E:%010.6lf ",(double)gps_info.location.longitude/1000000);//打印数据
MyLCD_Show(2,yPlace++,dis0);//液晶显示字符串
sprintf(dis0,"纬度N:%010.6lf ",(double)gps_info.location.latitude/1000000);//打印数据
MyLCD_Show(2,yPlace++,dis0);//液晶显示字符串
}
}
break;
default:break;
}
if(remCurMenuId != My_Menu_GetSelectID())//切换菜单选择后 初始化相应变量
{
remCurMenuId = My_Menu_GetSelectID();
nowMes.remSt = 0xff;//重新记录数据 用于更新
nowMes.remGpsFlag =0xff;//重新记录数据 用于更新
needPlaySt_Key = 0xff;//需要播报语音 但还没有播报
needPlaySt_Gps = 0xff;//gps gps定位后播报语音 但还没有播报
}//**All notes can be deleted and modified**//
}
if(mySendFlag_tick == true )//定时发送时间到
{
mySendFlag_tick = false;//清除标志
if(openDjDelay >0)//舵机动作倒计时
{
openDjDelay--;//倒计时
My_SG90_SetAngle(0,OPEN_ANGLE); //控制转动方向
}
else {My_SG90_SetAngle(0,CLOSE_ANGLE);}//控制转动方向
sprintf((char*)dis0,"*D %02d S%02d #",(int)nowMes.runDir,(int)nowMes.nowSt);//填充数据 //**All notes can be deleted and modified**//
if(wirelessFlag == 0)USARTSendString(USART1 ,dis0); //发送 对应的 数据
else My_ESP8266_SendStrStr(USART1 ,dis0);//发送数据
}
My_UartMessage_Process();//处理串口数据
}
}
void scanKeyAnddealKey(void)
{
My_KeyScan();//按键扫描
if(selectedMenu!=NULL)
{
if(KeyIsPress(KEY_1))//按下了向上键
{
My_Menu_SelectUp(true);
}
if(KeyIsPress(KEY_2))//按下了向上键
{
My_Menu_SelectDown(true);
}
if(KeyIsPress(KEY_3))//按下了OK键
{
My_Menu_Into(true);
}
if(KeyIsPress(KEY_4))
{
// workState_menu=0;//回到初始状态
My_Menu_Back(true);
}
}
else if(KeyIsPress(KEY_4))
{
My_Menu_Into(true);
}
// if(My_Key_HasEvent(KEY_EVENT_RELEASE))
// {}
}
void My_Menu_Display(void)
{
static u8 remBack = 0xff;//首次推出菜单
static u8 firstBack = 0;//首次推出菜单
if(selectedMenu==NULL)//如果已经退出菜单
{
firstBack = 0;//记录当前所处模式
if(remBack != firstBack) //首次进入设置项
{remBack = firstBack;LCD_Clear(Color16_BLACK);}//清全屏
return;//退出函数
}
else
{
firstBack = 1;//记录当前所处模式
if(remBack != firstBack) //首次进入设置项
{remBack = firstBack;LCD_Clear(Color16_BLACK);}//清全屏
}
FRONT_COLOR = Color16_YELLOW;//设置前景色
if(My_Menu_UnderBottom())//最低一层菜单 说明其下面没有选择项只有标题
{
MyLCD_Show(POS_MENU_X+1,POS_MENU_Y,(char *)*(selectedMenu->contentPtrArray+selectedMenu->selectIndex));//液晶显示字符串
return;//退出函数
}
MyLCD_Show(POS_MENU_X+1,POS_MENU_Y,(char *)selectedMenu->title);//根菜单 第一级菜单标题
for(i=0;i<selectedMenu->itemCount;i++)//该级菜单选项
{
if(i==selectedMenu->selectIndex)
{
FRONT_COLOR = Color16_BLUE;//设置前景色
MyLCD_Show(POS_MENU_X,i+POS_MENU_Y+1,">");//液晶显示字符串
MyLCD_Show(POS_MENU_X+1,POS_MENU_Y+1+i,(char *)*(selectedMenu->contentPtrArray+i));//液晶显示字符串
FRONT_COLOR = Color16_WHITE;//设置前景色(画笔颜色)为白色
}
else
{
FRONT_COLOR=Color16_WHITE; //设置显示颜色
MyLCD_Show(POS_MENU_X,i+POS_MENU_Y+1," ");//液晶显示字符串
MyLCD_Show(POS_MENU_X+1,POS_MENU_Y+1+i,(char *)*(selectedMenu->contentPtrArray+i));//液晶显示字符串
}
}
}
void My_Menu_Clear(void)
{
LCD_Clear(Color16_BLACK);//清全屏 返回菜单 直接清屏
}
void OnGetUartMessage(const _uart_msg_obj *uartMsgRec)
{
char *strPtr;
if((strPtr=strstr(uartMsgRec->payload,"RTC"))!=NULL)//接收到rtc字符串 *RTC20200420173611#
{
// My_RTC_SetStr(strPtr+3);//设置时间
// My_ESP8266_SendStrStr(USART1 , "setRTC\r\n");//发送 wifi 数据
}
if((strPtr=strstr(uartMsgRec->payload,"%"))!=NULL)//接收到字符串
{
if(wirelessFlag == 0)USARTSendString(USART1,"\r\nTest Ok!!\r\n\r\n"); //发送 对应的 数据
else My_ESP8266_SendStrStr(USART1,"\r\nTest Ok!!\r\n\r\n");//发送数据
}
// needWriteFlash = 2; //需要更新存储数据
}
void My_ESP8266_SendStrStr(USART_TypeDef* USARTx, const char *str)
{
// u8 i;
// for(i=0;i<2;i++)
{
My_USART_printf(USARTx,"AT+CIPSEND=%d,%d\r\n",0,strlen(str));
delay_ms(10);
USARTSendBytes(USARTx,(const uint8_t *)str,strlen(str));//发送数据
USARTSendBytes(USARTx,"\r\n",2);//发送数据
delay_ms(5);
}
}
void checkWireLessMode(u8 recBuf)//检查无线类型
{
static u8 checkIn = 0;//查看第一次标志是否接受
if(startCheckWireLess == 1 && wirelessFlag == 0)//启动检测无线
{
if(recBuf == 'O')checkIn = 1;
else if (checkIn == 1 && recBuf == 'K')
{
wirelessFlag = 1; //1表示wifi
}
}
}