eefocus_3863048 发表于 2025-3-2 23:58:57

《2024 DigiKey 汽车应用创意挑战赛》基于树莓派5...

本帖最后由 eefocus_3863048 于 2025-3-6 02:08 编辑

## 一,项目名称

**基于树莓派5的简易汽车仪表盘**

## 二,项目概述

**        **本项目主要应用于车载娱乐方面,因此,本项目致力于设计一款创新的车载中控面板,集成了多种先进技术,包括OBD(车载诊断系统)、卫星定位、陀螺仪和后视影像系统。这款中控面板不仅能够实时监测车辆的运行状态和故障信息,还能提供精准的定位服务,确保驾驶者在行驶过程。

## 三,硬件部分

**主控:**

* **树莓派5**
!(https://www.eefocus.com/forum/data/attachment/forum/202503/02/235108mizcpodjg3s5eh3e.png)
* **先楫HPM5361**
!(https://www.eefocus.com/forum/data/attachment/forum/202503/02/235132ydir77dw01oai7m8.png)
**传感器:**
* **GPS模块-ATGM332D-5N-11-0 **
* **mpu6050**
* **oled**
* **蜂鸣器**
* **sht30温湿度传感器**
* **usb摄像头**

**实物图如下:**
!(https://www.eefocus.com/forum/data/attachment/forum/202503/02/235158u45uddr5zr370zr6.png)
**详细原理图如下:**

* **电源部分**
**采用矽力杰的SY8089A1AAC高效1.5MHz 2A同步降压调节器。**

!(https://www.eefocus.com/forum/data/attachment/forum/202503/02/235225ffj6v65n463w5l95.png)
**充电部分**

**采用wsp4056,输出电压 4.2V 输入电压 4.0-8.0V 最大充电电流 1000ma**
!(https://www.eefocus.com/forum/data/attachment/forum/202503/02/235252y5xbfbai3axixhzf.png)
蜂鸣器部分
!(https://www.eefocus.com/forum/data/attachment/forum/202503/02/235310zvnkukfnnk8v88zq.png)
mpu6050
!(https://www.eefocus.com/forum/data/attachment/forum/202503/02/235325f1ll4f13zpmcjxzn.png)
GNSS
!(https://www.eefocus.com/forum/data/attachment/forum/202503/02/235341m8pqpjow8kpk8rqm.png)
**CAN**

**采用NXP的TJA1050T,can收发器**
!(https://www.eefocus.com/forum/data/attachment/forum/202503/02/235404ouo4o6uz60lh34zu.png)
OLED
!(https://www.eefocus.com/forum/data/attachment/forum/202503/02/235425fiq1i92xl90z2vll.png)
温湿度计
!(https://www.eefocus.com/forum/data/attachment/forum/202503/02/235438qzi117nx1qxss2r2.png)

## 四,系统框架

```mermaid
flowchart TB
   A[先楫HPM5361] -- 串口 --> C[树莓派5]
   B--iic-->A[先楫HPM5361]
   c--iic-->A[先楫HPM5361]
   d--USART-->A[先楫HPM5361]
   e--CAN-->A[先楫HPM5361]
   f--ADC-->A[先楫HPM5361]
   g-->C[树莓派5]
   h[屏幕] -- HDMI-->C[树莓派5]
```

## 五,软件部分

- OBD协议解析

~~~
void CAN_RxBytes(void)
{
        unsigned char data;
        uint16_t pid= 0;
        uint8_t data1 = 0;
        uint8_t data2 = 0;
        int16_t value;
        bool status;
        /*-------------------------------------mxz add------------------------------------*/

        send_pid(PID,7);
        status=CAN_ReceiveBytes(CAN,8,data,8);        // frameSize from 1 to 8
        pid= data;
        data1 = data;
        data2 = data;
        if (status)
        {
                switch (pid)
                {
                        case PID_RPM://转速
                                value = (data2 | data1 << 8) / 4;
                                g_odb_message.rpm = value * g_parm_config.RPM_config / 100;
//                                LTPrintf("PID_RPM %d \n",value);
                                break;
                        case PID_SPEED://车速
                                value = data1 ;
//                                LTPrintf("PID_SPEED :%d\n",value);
                                if(g_parm_config.Speed_Src == 0)//速度来自GPS
                                {
                                        g_odb_message.speed = gpsx.SOGK * g_parm_config.speed_config / 100;
                                }else
                                {
                                        g_odb_message.speed = value * g_parm_config.speed_config / 100;
                                }
                                break;
                        case PID_DISTANCE: // km//表示总行驶里程
                                value = (data2 | data1 << 8) ;
                                g_odb_message.Distance = value;
//                                LTPrintf("PID_DISTANCE :%d\n",value);
                                break;
                        case PID_CONTROL_MODULE_VOLTAGE: // 表示控制模块的电压,通常用于监控电源状态。 v
                                value = (data2 | data1 << 8) / 1000;
                                g_odb_message.Control_Module_Voltage = value;
//                                LTPrintf("PID_CONTROL_MODULE_VOLTAGE :%d\n",value);
                                break;
                        case PID_ENGINE_FUEL_RATE: // L/h       //表示单位时间内的燃油消耗量(通常以升每小时或加仑每小时为单位)。
                                value = (data2 | data1 << 8) / 20;
                                g_odb_message.engine_fuel_rate = value;
//                                LTPrintf("PID_ENGINE_FUEL_RATE :%d\n",value);
                                break;
                        case PID_EVAP_SYS_VAPOR_PRESSURE: // kPa 表示蒸发系统内的气压,通常用于监测油箱的密封性。
                                value = (data2 | data1 << 8) / 4;
                                g_odb_message.map = value * g_parm_config.MAP_config / 100;
//                                LTPrintf("PID_EVAP_SYS_VAPOR_PRESSURE :%d\n",value);
                                break;
                        case PID_COOLANT_TEMP: // 水温
                                value =data1 - 40;
                                g_odb_message.coolant_temp = value;
//                                LTPrintf("PID_COOLANT_TEMP :%d\n",value);
                                break;
#if 0
                        case PID_FUEL_PRESSURE: // kPa
                                value = data1 * 3;
                                break;
                        case PID_COOLANT_TEMP:
                        case PID_INT***E_TEMP:
                        case PID_AMBIENT_TEMP:
                        case PID_ENGINE_OIL_TEMP:
                                value = data1 - 40;
                                break;
                        case PID_THROTTLE:
                        case PID_COMMANDED_EGR:
                        case PID_COMMANDED_EVAPORATIVE_PURGE:
                        case PID_FUEL_LEVEL:
                        case PID_RELATIVE_THROTTLE_POS:
                        case PID_ABSOLUTE_THROTTLE_POS_B:
                        case PID_ABSOLUTE_THROTTLE_POS_C:
                        case PID_ACC_PEDAL_POS_D:
                        case PID_ACC_PEDAL_POS_E:
                        case PID_ACC_PEDAL_POS_F:
                        case PID_COMMANDED_THROTTLE_ACTUATOR:
                        case PID_ENGINE_LOAD:
                        case PID_ABSOLUTE_ENGINE_LOAD:
                        case PID_ETHANOL_FUEL:
                        case PID_HYBRID_BATTERY_PERCENTAGE:
                                value = data1 * 100 / 255;
                                break;
                        case PID_MAF_FLOW: // grams/sec
                                value = (data2 | data1 << 8) / 100;
                                break;
                        case PID_TIMING_ADVANCE:
                                value = (data1 / 2) - 64;
                                break;
                        case PID_DISTANCE_WITH_MIL: // km
                        case PID_TIME_WITH_MIL: // minute
                        case PID_TIME_SINCE_CODES_CLEARED: // minute
                        case PID_RUNTIME: // second
                        case PID_FUEL_RAIL_PRESSURE: // kPa
                        case PID_ENGINE_REF_TORQUE: // Nm
                                value = (data2 | data1 << 8);
                                break;
                        case PID_CONTROL_MODULE_VOLTAGE: // V
                                value = (data2 | data1 << 8) / 1000;
                                break;
                        case PID_ENGINE_FUEL_RATE: // L/h
                                value = (data2 | data1 << 8) / 20;
                                break;
                        case PID_ENGINE_TORQUE_DEMANDED: // %
                        case PID_ENGINE_TORQUE_PERCENTAGE: // %
                                value = data1 - 125;
                                break;
                        case PID_SHORT_TERM_FUEL_TRIM_1:
                        case PID_LONG_TERM_FUEL_TRIM_1:
                        case PID_SHORT_TERM_FUEL_TRIM_2:
                        case PID_LONG_TERM_FUEL_TRIM_2:
                        case PID_EGR_ERROR:
                                value = (data1 * 100 / 128) - 100;
                                break;
                        case PID_FUEL_INJECTION_TIMING:
                                value = ((data2 | data1 << 8) / 128) - 210;
                                break;
                        case PID_CATALYST_TEMP_B1S1:
                        case PID_CATALYST_TEMP_B2S1:
                        case PID_CATALYST_TEMP_B1S2:
                        case PID_CATALYST_TEMP_B2S2:
                                value = ((data2 | data1 << 8) / 10) - 40;
                                break;
                        case PID_AIR_FUEL_EQUIV_RATIO: // 0~200
                                value = (data2 | data1 << 8) * 2 / 65536;
                                break;
#endif
                        default:
                                break;
                }
        }
}
~~~
- GNSS数据解析

~~~
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date         Author       Notes
* 2025-03-02   Administrator       the first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
/*******************************************************************************************/
#include "gps.h"
#include <string.h>


nmea_msg gpsx;
/**
* @brief       NMEA_Comma_pos 从buf里面得到第n个逗号所在的位置
* @argument    数组
* @argument    地n个','
* @return      逗号的位置
*/
#if 1
u8 NMEA_Comma_Pos(u8 *buf,u8 n)
{
   u8 *ptr = buf;
   while(n)
   {
         //遇到'*'或者非法字符,则不存在第cx个逗号
         if(*ptr == '*' || *ptr < ' '|| *ptr > 'z')return 0xFF;
         if(*ptr == ',')n--;
         ptr++;
   }
   return ptr-buf;
}
/**
* @briefNMEA_Pow    m^n次方
*/
u32 NMEA_Pow(u8 m,u8 n)
{
   u32 result =1;
   while(n--)result *= m;
   return result;
}
/**
* @brief      字符串转数字
*/
int NMEA_Str2Num(u8 *buf,u8*dx)
{
   u8 *p = buf;
   u32 ires=0,fres=0;
   u8 ilen = 0,flen =0,i;
   u8 mask=0;
   int res;
   /*********得到个位十位***************/
   while(1)
   {
         if(*p == '-')
         {
             mask |= 0x02;
             p++;
         }
         if(*p == ',' || *p == '*')break;
         if(*p == '.')
         {
             mask |= 0x01;
             p++;
         }
         else if((*p >'9') || (*p < '0'))
         {
             ilen=0;
             flen=0;
             break;
         }
         if(mask & 0x01)flen++;//小数
         else ilen++;//整数
         p++;
   }
   if(mask&0x02)buf++;
   for(i = 0;i<ilen;i++)
   {
         ires += NMEA_Pow(10,ilen-1-i)*(buf-'0');
         //LTPrintf("buf[%d]:%d ,ires:%d\n",i,(buf-'0'),ires);
   }
   if(flen>5)flen = 5;
   *dx = flen;
   for(i=0;i<flen;i++)
   {
         fres += NMEA_Pow(10,flen-1-i)*(buf-'0');
   }
   //LTPrintf("ilen:%d flen%d ires:%d fres:%d\n",ilen,flen,ires,fres);
   res = ires*NMEA_Pow(10,flen)+fres;
   if(mask&0x02)res = -res;
   return res;
}
/**
* @brief       分析GPGSV信息信息
* @argument    nmea信息结构体
* @argument    buf接收数据的缓存区地址
* @return      无・
* @note      GSV表示可视的GNSS卫星。本语句包含可视的卫星数、卫星标识号、仰角、方位角和信噪比。每次传送,
*            一个GSV语句只能包含最多4颗卫星的数据,
*            因此可能需要多个语句才能获得完整的信息。由于GSV包含的卫星不用于定位解决方案,所以GSV语句指示的卫星可能比GGA多。
* @note      “GN”标识符不可用于该语句。如果可以多个卫星系统可视,则设备输出多条GSV语句,用不同的发送设备标识符表示相应的卫星。
**/
void NMEA_GPGSV_Analysis(nmea_msg *gpsx,u8 *buf)
{
    u8 *p,*p1,dx;
    u8 len,i,j,slx = 0;
    u8 posx;
    p = buf;
    p1 = (u8*)strstr((const char*)p,"$GPGSV");
    len = p1 - '0';                                  //得到GPGSV的条数语句总数。范围:1~9。
    posx=NMEA_Comma_Pos(p1,3);                        //得到可见卫星总数
    if(len!=0xFF)gpsx->svnum = NMEA_Str2Num(p1+posx,&dx);
    for(i=0;i<len;i++)
    {
      p1 = (u8*)strstr((const char*)p,"$GPGSV");
      for(j=0;j<4;j++)
      {
            posx=NMEA_Comma_Pos(p1,4+j*4);
            if(posx!=0xFF)gpsx->slmsg.num = NMEA_Str2Num(p1+posx,&dx);//得到卫星编号
            else    break;
            posx=NMEA_Comma_Pos(p1,5+j*4);
            if(posx!=0xFF)gpsx->slmsg.eledeg= NMEA_Str2Num(p1+posx,&dx);//得到卫星仰角
            else    break;
            posx=NMEA_Comma_Pos(p1,6+j*4);
            if(posx!=0xFF)gpsx->slmsg.azideg = NMEA_Str2Num(p1+posx,&dx);//得到卫星方位角
            else    break;
            posx=NMEA_Comma_Pos(p1,7+j*4);
            if(posx!=0xFF)gpsx->slmsg.sn = NMEA_Str2Num(p1+posx,&dx);//得到卫星信噪比
            else    break;
            slx++;
      }
    }
}
/**
* @brief       分析GPGGA信息
* @argument    nmea信息结构体
* @argument    buf接收数据的缓存区地址
* @return      无・
* @note      GGA提供全球定位系统定位数据。本语句包含GNSS接收机提供的时间、位置和定位相关数据
*1.QZSS和GPS星系配置下<TalkerID>均为GP;有关卫星标识符的详情,请参考表16:GNSS标识符。
*2. NMEA 0183协议指示GGA消息为GPS系统特有;但当接收器配置为多星系时,GGA消息的内容将从多星系解决方案中生成。
*3. 1) NMEA 0183协议定义的使用中卫星数量范围为00~12,然而,在多星系解决方案中,使用的卫星数量可能超过12颗。
**/
void NMEA_GPGGA_Analysis(nmea_msg *gpsx,u8 *buf)
{
    u8 *p1,dx;
    u8 posx;
    p1 = (u8*)strstr((const char*)buf,"$GNGGA");
    posx = NMEA_Comma_Pos(p1,1);//得到日期
    if(posx != 0xFF)
    {
      int temp = NMEA_Str2Num(p1+posx,&dx);
      gpsx->utc.sec = (temp / 1000) % 10;
      gpsx->utc.min = (temp / 100000) %100;
      gpsx->utc.hour = temp / 10000000;
    }

    posx = NMEA_Comma_Pos(p1,2);NMEA_Str2Num;
    if(posx!=0xFF)
    {
      int temp = NMEA_Str2Num(p1+posx,&dx);
      //LTPrintf("temp:%d dx:%d\n",temp,dx);
      gpsx->latitude = temp / NMEA_Pow(10,dx+2);//得到°
      float rs = temp%NMEA_Pow(10,dx+2);            //得到'
      gpsx->latitude=gpsx->latitude*NMEA_Pow(10,5)+(rs*NMEA_Pow(10,5-dx))/60;//转换为°
    }
    posx = NMEA_Comma_Pos(p1,3);//南纬还是北纬
    if(posx!=0xFF)gpsx->nshemi = *(p1+posx);
    posx = NMEA_Comma_Pos(p1,4);//得到经度
    if(posx!=0xFF)
    {
      int temp = NMEA_Str2Num(p1+posx,&dx);
      gpsx->longitude = temp / NMEA_Pow(10,dx+2);
      float rs = temp%NMEA_Pow(10,dx+2);
      gpsx->longitude = gpsx->longitude*NMEA_Pow(10,5)+(rs*NMEA_Pow(10,5-dx))/60;
    }
    posx = NMEA_Comma_Pos(p1,5);//东经还是**
    if(posx!=0xFF)gpsx->ewhemi = *(p1+posx);

    posx = NMEA_Comma_Pos(p1,6);                //GPS状态:0,未定位;1,非差分定位;2,差分定位;6,正在估算.
    if(posx!=0XFF)gpsx->gpssta = NMEA_Str2Num(p1+posx,&dx);
    posx = NMEA_Comma_Pos(p1,7);                //使用的卫星数。
    if(posx!=0xFF)gpsx->posslnum = NMEA_Str2Num(p1+posx,&dx);
    posx = NMEA_Comma_Pos(p1,9);                //得到海拔高度
    if(posx != 0xFF)gpsx->altitude = NMEA_Str2Num(p1+posx,&dx);
}
/**
* @brief       分析GPGSA信息
* @argument    nmea信息结构体
* @argument    buf接收数据的缓存区地址
* @return      无・
* @note      GSA表示GNSS精度因子(DOP)与有效卫星。本语句包含GNSS接收机工作模式,GGA或GNS
语句报告的导航解算中用到的卫星以及精度因子的值。
**/
void NMEA_GPGSA_Analysis(nmea_msg *gpsx,u8 *buf)
{
    u8 *p1,dx;
    u8 posx,i;
    //LTPrintf("===============NMEA_GPGSA_Analysis=======================\r\n");
    p1 = (u8*)strstr((const char*)buf,"$GNGSA");
    //LTPrintf("p1:%p   buf:%p\r\n",p1,buf);
    posx = NMEA_Comma_Pos(p1,2);    //得到定位类型
    //LTPrintf("posx:%d\r\n",posx);
    if(posx!=0xFF)gpsx->fixmode = NMEA_Str2Num(p1+posx,&dx);
    for(i=0;i<12;i++)//得到定位卫星编号
    {
      posx = NMEA_Comma_Pos(p1,3+i);
      if(posx!=0xFF)gpsx->possl = NMEA_Str2Num(p1+posx,&dx);
      else break;
    }
    posx = NMEA_Comma_Pos(p1,15);//位置精度因子
    if(posx != 0xFF)gpsx->pdop = NMEA_Str2Num(p1+posx,&dx);
    posx = NMEA_Comma_Pos(p1,16);//水平精度因子
    if(posx != 0xFF)gpsx->hdop = NMEA_Str2Num(p1+posx,&dx);
    posx = NMEA_Comma_Pos(p1,17);//垂直精度因子
    if(posx != 0xFF)gpsx->vdop = NMEA_Str2Num(p1+posx,&dx);
}
/**
* @brief       分析GPRMC信息
* @argument    nmea信息结构体
* @argument    buf接收数据的缓存区地址
* @return      无・
* @note      RMC表示推荐的最少专用GNSS数据。本语句包含GNSS接收机提供的时间、日期、位置、航迹向
和速度数据。
**/
void NMEA_GPRMC_Analysis(nmea_msg *gpsx,u8 *buf)
{
    u8 *p1,dx,posx;
    u32 temp;
    float rs;
    p1 = (u8 *)strstr((const char*)buf,"GNRMC");//"$GPRMC",经常有&和GPRMC分开的情况,故只判断GPRMC.
    //LTPrintf("buf:%pp1:%p\n",buf,p1);
    //LTPrintf("buf:%sp1:%s\n",buf,p1);
    posx = NMEA_Comma_Pos(p1,1);//获取时间,不要ms
    //LTPrintf("posx:%d ",posx);
    if(posx!=0xFF)
    {
      temp = NMEA_Str2Num(p1+posx,&dx) / NMEA_Pow(10,dx);
      //LTPrintf("temp:%d dx:%d\n",temp,dx);
      gpsx->utc.hour = temp/10000;
      gpsx->utc.min = (temp /100) %100;
      gpsx->utc.sec = temp % 100;
    }
    posx = NMEA_Comma_Pos(p1,3);NMEA_Str2Num;
    if(posx!=0xFF)
    {
      temp = NMEA_Str2Num(p1+posx,&dx);
      //LTPrintf("temp:%d dx:%d\n",temp,dx);
      gpsx->latitude = temp / NMEA_Pow(10,dx+2);//得到°
      rs = temp%NMEA_Pow(10,dx+2);            //得到'
      gpsx->latitude=gpsx->latitude*NMEA_Pow(10,5)+(rs*NMEA_Pow(10,5-dx))/60;//转换为°
    }
    posx = NMEA_Comma_Pos(p1,4);//南纬还是北纬
    if(posx!=0xFF)gpsx->nshemi = *(p1+posx);
    posx = NMEA_Comma_Pos(p1,5);//得到经度
    if(posx!=0xFF)
    {
      temp = NMEA_Str2Num(p1+posx,&dx);
      gpsx->longitude = temp / NMEA_Pow(10,dx+2);
      rs = temp%NMEA_Pow(10,dx+2);
      gpsx->longitude = gpsx->longitude*NMEA_Pow(10,5)+(rs*NMEA_Pow(10,5-dx))/60;
    }
    posx = NMEA_Comma_Pos(p1,6);//东经还是**
    if(posx!=0xFF)gpsx->ewhemi = *(p1+posx);
    posx = NMEA_Comma_Pos(p1,9);//得到日期
    if(posx != 0xFF)
    {
      temp = NMEA_Str2Num(p1+posx,&dx);
      gpsx->utc.date = temp / 10000;
      gpsx->utc.month = (temp / 100) %100;
      gpsx->utc.year = 2000 + temp % 100;
    }
}
/**
* @brief       分析GPVTG信息
* @argument    nmea信息结构体
* @argument    buf接收数据的缓存区地址
* @return      无・
* @note      VTG语句包含相对于地面的实际航向和速度
**/
void NMEA_GPVTG_Analysis(nmea_msg *gpsx,u8 *buf)
{
    u8 *p1,dx,posx;
    p1 = (u8*)strstr((const char*)buf,"$GNVTG");
    posx = NMEA_Comma_Pos(p1,1);//<COGT 对地航向(真北)
    if(posx!=0xFF)gpsx->cogt = NMEA_Str2Num(p1+posx,&dx) / NMEA_Pow(10,dx);
    posx = NMEA_Comma_Pos(p1,5);//对地速度 节
    if(posx!=0xFF)
    {
      gpsx->SOGN = NMEA_Str2Num(p1+posx,&dx) / NMEA_Pow(10,dx);

    }
    posx = NMEA_Comma_Pos(p1,7);//对地速度 km/h
    gpsx->SOGK = NMEA_Str2Num(p1+posx,&dx) / NMEA_Pow(10,dx);
}
/**
* @brief       提取NMEA-0183信息
* @argument    nmea信息结构体
* @argument    buf接收数据的缓存区地址
* @return      无・
* @note
**/

void GPS_Analysis(nmea_msg *gpsx,u8 *buf)
{
    NMEA_GPGSV_Analysis(gpsx,buf);//GPGSV解析
    NMEA_GPGGA_Analysis(gpsx,buf);//GPGGA解析
    NMEA_GPGSA_Analysis(gpsx,buf);//GPGSA解析
    NMEA_GPRMC_Analysis(gpsx,buf);//GPRMC解析
    NMEA_GPVTG_Analysis(gpsx,buf);//GPVTG解析
}
/**
* @brief       GPS校验和计算
* @argument
* @argument
* @return      无・
* @note
**/
void GPS_CheckSum(u8*buf,u8*checksum)
{
    *checksum = 0;
    u8 *ptr = buf;
    const char *start = strchr(ptr,'$');
    const char *end   = strchr(ptr,'*');
    if(start == NULL || end == NULL || (start >= end))return;
    for(ptr = (u8 *)(start + 1);ptr < (u8 *)end;ptr++)
    {
      *checksum ^= *ptr;
    }
}
/*-----------------------------------------PCAS_---------------------------*/
/**
* @brief       配置NMEA串口波特率
* @argument
* @argument
* @return      无・
* @note
**/
//void PCAS01(u8 *buf,u8 baud)
//{
//u8 *ptr = "$PCAS01,1*";
//u8 len = strlen(ptr);
//memncpy(ptr,buf,len-1);
//
//}
#endif


/****************************************************************************************/
#define SAMPLE_UART_NAME               "uart2" /* 串口设备名称 */

struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;/* 初始化配置参数 */

/* 用于接收消息的信号量 */
static struct rt_semaphore rx_sem;
static rt_device_t serial;


u16 RXD_index;
u8 RX_BUF;

//接收数据回调函数
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
    /* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
    if (size > 0)
   {
      rt_sem_release(&rx_sem);
   }
    return RT_EOK;
}

static void serial_thread_entry(void *parameter)
{
    char ch;

    while (1)
    {
      rt_sem_take(&rx_sem, RT_WAITING_FOREVER); // 等待信号量

      // 循环读取可用数据
      while (rt_device_read(serial, 0, &ch, 1) == 1)
      {
            // 存储接收到的数据
            if (RXD_index < (sizeof(RX_BUF) - 1)) // 确保不会超出缓冲区大小
            {
                RX_BUF = ch;
            }
            else
            {
               GPS_Analysis(&gpsx, RX_BUF);
               RX_BUF = '\0';
               for(int i = 0;i<RXD_index;i++)
                   rt_kprintf("%c",RX_BUF);
//               rt_kprintf("%s",RX_BUF);
               rt_kprintf("\n-----------------------------------------------\n");
               rt_kprintf("GPS Analysis Result:\n");
               rt_kprintf("Year: %d\n", gpsx.utc.date);
               rt_kprintf("Month: %d\n", gpsx.utc.month);
               rt_kprintf("Date: %d\n", gpsx.utc.date);
               rt_kprintf("hour: %d\n", gpsx.utc.hour);
               rt_kprintf("min: %d\n", gpsx.utc.min);
               rt_kprintf("sec: %d\n", gpsx.utc.sec);
//               rt_kprintf("Longitude: %.6f\n", (float)gpsx.longitude / 100000.0);
//               rt_kprintf("Latitude: %.6f\n", (float)gpsx.latitude / 100000.0);
               printf("Longitude: %.6f\n", (float)gpsx.longitude / 100000.0);
               printf("Latitude: %.6f\n", (float)gpsx.latitude / 100000.0);
               RXD_index = 0; // 处理完后重置索引

            }
      }

    }
}
/**
* @briefthread_serial
* @paramNone
* @retval ret
*/
int GPS_serial(void)
{
    rt_err_t ret = RT_EOK;
    char uart_name;
    char str[] = "hello RT-Thread!\\r\\n";

    rt_strncpy(uart_name, SAMPLE_UART_NAME, RT_NAME_MAX);

    /* 查找系统中的串口设备 */
    serial = rt_device_find(uart_name);
    if (!serial)
    {
      rt_kprintf("find %s failed!\\n", uart_name);
      return RT_ERROR;
    }
    /* 修改串口配置参数 */
    config.baud_rate = BAUD_RATE_9600;      //修改波特率为 115200
    config.data_bits = DATA_BITS_8;         //数据位 8
    config.stop_bits = STOP_BITS_1;         //停止位 1
    config.parity    = PARITY_NONE;         //无奇偶校验位

    /* 控制串口设备。通过控制接口传入命令控制字,与控制参数 */
    rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, &config);

    /* 初始化信号量 */
    rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
    /* 以中断接收及轮询发送模式打开串口设备 */
    rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);
    /* 设置接收回调函数 */
    rt_device_set_rx_indicate(serial, uart_input);
    /* 发送字符串 */
    rt_device_write(serial, 0, str, (sizeof(str) - 1));

    /* 创建 serial 线程 */
    rt_thread_t thread = rt_thread_create("serial", serial_thread_entry, RT_NULL, 1024, 25, 10);
    /* 创建成功则启动线程 */
    if (thread != RT_NULL)
    {
      rt_thread_startup(thread);
    }
    else
    {
      ret = RT_ERROR;
    }

    return ret;
}

/* 导出到 msh 命令列表中 */
//MSH_CMD_EXPORT(GPS_serial,GPS uart device sample);
// 导出函数自动运行,在系统初始化时调用usr_led_run函数
INIT_APP_EXPORT(GPS_serial);
~~~

!(https://www.eefocus.com/forum/data/attachment/forum/202503/02/235717u08mifrr995gi5i8.png)
!(https://www.eefocus.com/forum/data/attachment/forum/202503/02/235733b711cnbrdvjqe3j8.png)
仪表盘界面重绘:
!(https://www.eefocus.com/forum/data/attachment/forum/202503/06/015756cc64fk0az3s3ckk6.png)




eefocus_3863048 发表于 2025-3-6 02:09:26

markdown不知道为啥不能上传压缩包,我先放在这里
页: [1]
查看完整版本: 《2024 DigiKey 汽车应用创意挑战赛》基于树莓派5...