一、功能简介

项目名:公交报站
项目编号:mcupark-dz-123
单片机类型:STM32F103C8T6
具体功能:
1、通过DS18B20测得公交车内温度
2、通过STM32内部的RTC获取当前时间
3、通过TTS语音播报模块播报不同的站点信息
4、通过按键可以修改时间、播报站台等
5、通过OLED显示时间、温度、站台信息
6、通过两个LED代表上下行指示
7、通过GPS获取经纬度,并显示在OLED上
 

二、系统框图设计

本设计以STM32单片机为核心控制器,加上其他的模块一起组成公交车报站的整个系统,其中包含中控部分、输入部分和输出部分。中控部分采用了STM32单片机,其主要作用是获取输入的部分数据,经过内部处理,控制输出部分。输入由四部分组成,第一部分是GPS定位模块,通过该模块获取当前的位置;第二部分是温度采集模块,通过该模块可以获取当前公交车内的温度;第三部分是按键模块,通过该模块切换界面、设置时间、播报站台等;第四部分是供电模块,通过该模块可给整个系统进行供电。输出由三部分组成,第一部分时OLED显示模块,显示温度及其时间及其站台等;第二部分是语音输出模块,公交车到站之后进行语音报告提醒乘客下车;第三部分是指示灯模块,指示上下行指示灯,系统框图如图3.1所示。

三、软件流程设计

 四、51实物图

单片机型号:STC89C52

板子为绿色PCB板,两层板,厚度1.2,上下覆铜接地。元器件基本上为插针式,个别降压芯片会使用贴片式。

供电接口:TYPE-C

 五、原理图

软件版本:AD2013

电路连线方式:网络标号连线方式

注意:原理图只是画出了模块的引脚图,而并不是模块的内部结构原理图

 六、PCB图

由原理图导出,封装很大一部分都是作者自己绘制,不提供封装库,只提供连接好的源文件。中间有一个项目编号,隐藏在单片机底座下,插入单片机后不会看到。

两层板,上下覆铜接地。

七、部分程序展示

软件版本:keil5

逻辑程序和驱动程序分开,分布于main.c和其他.c文件

*******监测函数
*****/
void Monitor_function(void)
{
    if(flag_display == 0)                                    //测量界面
    {
        if(time_num % 10 == 0)                            //获取数据
        {    
      temp_value = DS18B20_Get_Temp();    //获取温度
        }
    if(USART2_WaitRecive() == 0)        //通过GPS获取经纬度
    {
      if(strstr((char *)usart2_buf,"RMC"))
      {
        result1 = strstr((char *)usart2_buf,"A,");
        if(result1 != NULL)
        {
          result1 = result1+2;
          N_num = (float)atof(result1);
          N_du = (uint16_t)(N_num / 100.0);
          N_fen = N_num-N_du*100;
          N_num = N_du + N_fen/60.0;
          if(0 < N_num && N_num < 60)
            N_num_new = N_num;
        }
        
        result1 = strstr((char *)usart2_buf,"N,");
        if(result1 != NULL)
        {
          result1 = result1+2;
          E_num = (float)atof(result1);
          E_du = (uint16_t)(E_num / 100.0);
          E_fen = E_num-E_du*100;
          E_num = E_du + E_fen/60.0;
          if(70 < E_num && E_num < 150)
            E_num_new = E_num;
        }
      }
      USART2_Clear();
    }
    }
}

/****
*******显示函数
*****/
void Display_function(void)
{
    switch(flag_display)                                    //根据不同的显示模式标志位,显示不同的界面
    {
        case 0:                                                      //界面0:显示温度、时间、站台信息,方向
      if(flag_location == 1)            //报站时显示经纬度,非报站时,显示温度、时间
      {
        Oled_ShowCHinese(1,0,"经:");
        sprintf(display_buf,"%f",E_num_new);
        Oled_ShowString(1,5,display_buf);
        Oled_ShowCHinese(2,0,"纬:");
        sprintf(display_buf,"%f",N_num_new);
        Oled_ShowString(2,5,display_buf);
      }
      else
      {
        sprintf(display_buf,"%02d:%02d:%02d",calendar.hour,calendar.min,calendar.sec);
        Oled_ShowString(1,4,display_buf);

        Oled_ShowCHinese(2, 0, "温度:");
        sprintf(display_buf,"%d.%dC ",temp_value/10,temp_value%10);
        Oled_ShowString(2, 6, display_buf);
      }
      Oled_ShowCHinese(3,0,"上站:");
      Oled_ShowCHinese(3,5,"号站");
            if(platform_c == 1 || platform_c == 6)
            {
                Oled_ShowCHinese(3,3,"终点");
            }
            else
            {
                sprintf(display_buf," %d  ",platform_c-1);
                Oled_ShowString(3,6,display_buf);
            }

      Oled_ShowCHinese(4,0,"下站:");
      Oled_ShowCHinese(4,5,"号站");
            if(platform_n == 1 || platform_n == 6)
            {
                Oled_ShowCHinese(4,3,"终点");
            }
            else
            {
                sprintf(display_buf," %d  ",platform_n-1);
                Oled_ShowString(4,6,display_buf);
            }

            if(flag_direction == 0) 
      {
        LED_UP = 0;
        LED_DW = 1;
      }
      else
      {
        LED_UP = 1;
        LED_DW = 0;
      }
        break;
        
        case 1:                                                            //界面1:显示设置时
            Oled_ShowCHinese(1,1,"设置当前时间");
            if(time_num % 5 == 0)
            {
                sprintf(display_buf,"%02d:%02d:%02d",calendar.hour,calendar.min,calendar.sec);
                Oled_ShowString(2,4,display_buf);
            }
            if(time_num % 10 == 0)
            {
                Oled_ShowString(2,4,"  ");
            }
        break;

        case 2:                                                            //界面2:显示设置分
            Oled_ShowCHinese(1,1,"设置当前时间");
            if(time_num % 5 == 0)
            {
                sprintf(display_buf,"%02d:%02d:%02d",calendar.hour,calendar.min,calendar.sec);
                Oled_ShowString(2,4,display_buf);
            }
            if(time_num % 10 == 0)
            {
                Oled_ShowString(2,7,"  ");
            }
        break;

        case 3:                                                            //界面3:显示设置秒
            Oled_ShowCHinese(1,1,"设置当前时间");
            if(time_num % 5 == 0)
            {
                sprintf(display_buf,"%02d:%02d:%02d",calendar.hour,calendar.min,calendar.sec);
                Oled_ShowString(2,4,display_buf);
            }
            if(time_num % 10 == 0)
            {
                Oled_ShowString(2,10,"  ");
            }
        break;

    case 4:                             //界面4:显示设置终点站位置
      Oled_ShowCHinese(1,0,"设置终点站位置");
      Oled_ShowCHinese(2,0,"经:");
      Oled_ShowCHinese(3,0,"纬:");
      sprintf(display_buf,"%.3f",platform_l[0][0]);
      Oled_ShowString(2,4,display_buf);
      sprintf(display_buf,"%.3f",platform_l[0][1]);
      Oled_ShowString(3,4,display_buf);
    break;

    case 5:                             //界面5:显示设置一号站位置
      Oled_ShowCHinese(1,0,"设置一号站位置");
      Oled_ShowCHinese(2,0,"经:");
      Oled_ShowCHinese(3,0,"纬:");
      sprintf(display_buf,"%.3f",platform_l[1][0]);
      Oled_ShowString(2,4,display_buf);
      sprintf(display_buf,"%.3f",platform_l[1][1]);
      Oled_ShowString(3,4,display_buf);
    break;

    case 6:                             //界面6:显示设置二号站位置
      Oled_ShowCHinese(1,0,"设置二号站位置");
      Oled_ShowCHinese(2,0,"经:");
      Oled_ShowCHinese(3,0,"纬:");
      sprintf(display_buf,"%.3f",platform_l[2][0]);
      Oled_ShowString(2,4,display_buf);
      sprintf(display_buf,"%.3f",platform_l[2][1]);
      Oled_ShowString(3,4,display_buf);
    break;

    case 7:                             //界面7:显示设置三号站位置
      Oled_ShowCHinese(1,0,"设置三号站位置");
      Oled_ShowCHinese(2,0,"经:");
      Oled_ShowCHinese(3,0,"纬:");
      sprintf(display_buf,"%.3f",platform_l[3][0]);
      Oled_ShowString(2,4,display_buf);
      sprintf(display_buf,"%.3f",platform_l[3][1]);
      Oled_ShowString(3,4,display_buf);
    break;

    case 8:                             //界面8:显示设置四号站位置
      Oled_ShowCHinese(1,0,"设置四号站位置");
      Oled_ShowCHinese(2,0,"经:");
      Oled_ShowCHinese(3,0,"纬:");
      sprintf(display_buf,"%.3f",platform_l[4][0]);
      Oled_ShowString(2,4,display_buf);
      sprintf(display_buf,"%.3f",platform_l[4][1]);
      Oled_ShowString(3,4,display_buf);
    break;

    case 9:                             //界面9:显示设置终点站位置
      Oled_ShowCHinese(1,0,"设置终点站位置");
      Oled_ShowCHinese(2,0,"经:");
      Oled_ShowCHinese(3,0,"纬:");
      sprintf(display_buf,"%.3f",platform_l[5][0]);
      Oled_ShowString(2,4,display_buf);
      sprintf(display_buf,"%.3f",platform_l[5][1]);
      Oled_ShowString(3,4,display_buf);
    break;

        default:
        break;
    }
}

/****
*******处理函数
*****/
void Manage_function(void)
{
  u8 i;
  char str1[16],str2[16];
  if(flag_display == 0)                                            //测量界面
    {
    for(i = 0; i < 6; i++)                  //判断是否到达站点地址
    {
      sprintf(str1,"%.3f",E_num_new);
      sprintf(str2,"%.3f",platform_l[i][0]);
      if(strcmp(str1,str2) == 0)
      {
        sprintf(str1,"%.3f",N_num_new);
        sprintf(str2,"%.3f",platform_l[i][1]);
        if(strcmp(str1,str2) == 0)
        {
          break;
        }
      }
    }
    if(i < 6 && E_num_new != 0 && N_num_new != 0) //如果到达站点地址,播报站点
    {
      if(flag_broad == 0)
      {
        flag_broad = 1;
        platform_c = i+1;
        if(flag_direction == 0)     //公交上行
        {
          if(platform_c >= 6)     //如果到达终点站,播报终点站
          {
            UsartPrintf(USART3,"本站终点站");
            UsartPrintf(USART3,"感谢您乘坐本公交");
          }
          else                    //不是终点站,播报站号
          {
            platform_n = platform_c+1;
            if(platform_c == 1)
            {
              UsartPrintf(USART3,"本站终点站");
            }
            else
            {
              UsartPrintf(USART3,"已到达%d号站",platform_c-1);
            }
            if(platform_n != 6)
            {
              UsartPrintf(USART3,"下一站%d号站",platform_n-1);
            }
            else
            {
              UsartPrintf(USART3,"下一站终点站");
            }
          }
        }
        else                        //公交下行
        {
          if(platform_c <= 1)
          {
            UsartPrintf(USART3,"本站终点站");
            UsartPrintf(USART3,"感谢您乘坐本公交");
          }
          else                    //不是终点站,播报站号
          {
            platform_n = platform_c-1;
            if(platform_c == 6)
            {
              UsartPrintf(USART3,"本站终点站");
            }
            else
            {
              UsartPrintf(USART3,"已到达%d号站",platform_c-1);
            }
            if(platform_n != 1)
            {
              UsartPrintf(USART3,"下一站%d号站",platform_n-1);
            }
            else
            {
              UsartPrintf(USART3,"下一站终点站");
            }
          }
        }
        if(flag_location == 0)
        {
          flag_location = 1;
          flag_timer_begin_5s = 1;
          Oled_ShowString(1,0,"                ");
          Oled_ShowString(2,0,"                ");
        }
      }
    }
    else
    {
      flag_broad = 0;
    }
    
    if(flag_5s == 1)                        //报站结束,不再显示经纬度
    {
      flag_timer_begin_5s = 0;
      flag_5s = 0;
      flag_location = 0;
      Oled_ShowString(1,0,"                ");
      Oled_ShowString(2,0,"                ");
    }
  }
}

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐