STM32G070串口接收GPS北斗模块数据

串口接收GPS模块数据,只连接MCU的接收脚和GPS模块发送脚。

MCU的发送脚用来打印调试数据等用途。

串口通信程序:

复制代码
/**
 ****************************************************************************************************
 * @file        gps_uart1_bsp.h
 * @author      
 * @version     V1.0
 * @date        2023-02-22
 * @brief       
 * @license    
 ****************************************************************************************************
 * @attention
 *         接收GPS模块数据  串口1,中断接收方式,RXNE和IDLE
 *
 ****************************************************************************************************
 */

#ifndef __GPS_UART_BSP_H
#define __GPS_UART_BSP_H

//#include "./SYSTEM/sys/sys.h"
#include "stm32g0xx_hal.h"
/**
 *是否使用RT—thread操作系统
 * WIFI_USING_RT_THREAD_OS用于定义系统文件夹是否支持OS
 * 0,不支持OS
 * 1,支持OS
 */
#define  GPS_USING_RT_THREAD_OS 1
/* 如果使用os,则包括下面的头文件即可. */
#if GPS_USING_RT_THREAD_OS
#include <rtthread.h> /* os 使用 */
#endif

/* 引脚定义 */
#define GPS_UART_TX_GPIO_PORT           GPIOB
#define GPS_UART_TX_GPIO_PIN            GPIO_PIN_6
#define GPS_UART_TX_GPIO_CLK_ENABLE()   do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0) /* PC口时钟使能 */

#define GPS_UART_RX_GPIO_PORT           GPIOB
#define GPS_UART_RX_GPIO_PIN            GPIO_PIN_7
#define GPS_UART_RX_GPIO_CLK_ENABLE()   do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0) /* PC口时钟使能 */

#define GPS_UART_INTERFACE              USART1
#define GPS_UART_IRQn                   USART1_IRQn
void GPS_UART_IRQHandler(void);        //在中断USART3_4_IRQn中调用,见stm32****_it.c

#define GPS_UART_CLK_ENABLE()           do{ __HAL_RCC_UART1_CLK_ENABLE(); }while(0) /* UART5时钟使能 */

/* UART收发缓冲大小 */
#define GPS_UART_RX_BUF_SIZE            384
#define GPS_UART_TX_BUF_SIZE            16

#ifdef GPS_USING_RT_THREAD_OS
/*防止多个线程访问WIFI,调用wifi接口函数时使用*/
void gps_lock(void);   //获取互斥量,得到gps
void gps_unlock(void); //释放互斥量,失去gps
/*用于接收帧处理函数与接收中断通信*/
uint8_t gps_take_sem(rt_int32_t timeout);//已接收帧,但串口没有接收到新的字节。等待这种情况
#endif
/* 操作函数 */
void gps_delay_ms(uint16_t n);                  /*等待GPS应答的延时函数*/
void gps_uart_printf(char *fmt, ...);           /* GPS UART printf */
void gps_uart_sendData(uint8_t *p_frame,uint16_t len);/* 向GPS UART发送8位数据组*/
void gps_uart_rx_restart(void);                  /* GPS UART重新开始接收数据 */
uint8_t *gps_uart_rx_get_frame(void);       /* 获取GPS UART接收到的一帧数据 */
uint16_t gps_uart_rx_get_frame_len(void);   /* 获取GPS UART接收到的一帧数据的长度 */
void gps_uart_init(uint32_t baudrate);      /* GPS UART初始化 */

#endif
复制代码

 

复制代码
/**
 ****************************************************************************************************
 * @file        gps_uart_bsp.c
 * @author      
 * @version     V1.0
 * @date        2023-02-22
 * @brief       
 * @license    
 ****************************************************************************************************
 * @attention
 *        接收GPS模块数据  串口1,中断接收方式,RXNE和IDLE
 *
 ****************************************************************************************************
 */
#include <gps_uart1_bsp.h>

#include "usart.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>


static UART_HandleTypeDef gps_uart_handle;                    /* GPS UART */

static struct
{
    uint8_t buf[GPS_UART_RX_BUF_SIZE];                 /* 帧接收缓冲 */
    struct
    {
        uint16_t len    : 15;                               /* 帧接收长度,sta[14:0] */
        uint16_t finsh  : 1;                                /* 帧接收完成标志,sta[15] */
    } sta;                                                  /* 帧状态信息 */
} gps_uart_rx_frame = {0};                                    /* GPS UART接收帧缓冲信息结构体 */


static uint8_t g_uart_tx_buf[GPS_UART_TX_BUF_SIZE]; /* GPS UART发送缓冲 */

#if GPS_USING_RT_THREAD_OS

/* 定义信号量*/
rt_sem_t GPS_RX_IT_sem = RT_NULL;
/* 定义互斥量控制块 */
static rt_mutex_t GPS_usart_mux = RT_NULL;   // 防止多个线程同时使用WIFI及相应的串口外设


#endif

/**
 * @brief       等待GPS应答的延时函数
 * @param       无
 * @retval      无
 */
//static 
void gps_delay_one_ms(void)/*等待GPS应答的延时函数*/
{
    uint16_t i;
    /* 
         下面的时间是通过逻辑分析仪测试得到的。
    工作条件:CPU主频72MHz ,MDK编译环境,1级优化
  
        循环次数为10时,SCL频率 = 205KHz 
        循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us 
         循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us 
    */
    for (i = 0; i < 1366; i++);
}
//__weak 
void gps_delay_ms(uint16_t n)
{
      uint16_t i;
        for (i = 0; i < n; i++)
           gps_delay_one_ms();
}
/**
 * @brief       GPS UART printf
 * @param       fmt: 待打印的数据
 * @retval      无
 */
void gps_uart_printf(char *fmt, ...)
{
    va_list ap;//定义一具VA_LIST型的变量,包括 #include <stdarg.h>
    uint16_t len;
    
    va_start(ap, fmt);//用VA_START宏初始化变量刚定义的VA_LIST变量
    vsprintf((char *)g_uart_tx_buf, fmt, ap);//具VA_LIST型的变量
    va_end(ap);//最后用VA_END宏结束可变参数的获取
    
    len = strlen((const char *)g_uart_tx_buf);
    HAL_UART_Transmit(&gps_uart_handle, g_uart_tx_buf, len, HAL_MAX_DELAY);
}
/**
* @brief        向GPS UART发送8位数据组
 * @param       p_frame: 首个BYTE地址,len:字节数
 * @retval      无
 */
void gps_uart_sendData(uint8_t *p_frame,uint16_t len)// 向GPS UART发送8位数据组
{
    HAL_UART_Transmit(&gps_uart_handle, p_frame, len, HAL_MAX_DELAY);
}
/**
 * @brief       GPS UART重新开始接收数据
 * @param       无
 * @retval      无
 */
void gps_uart_rx_restart(void)
{
      //memset( gps_uart_rx_frame.buf,0,sizeof(gps_uart_rx_frame.buf));//清零
    gps_uart_rx_frame.sta.len     = 0;
    gps_uart_rx_frame.sta.finsh   = 0;
}

/**
 * @brief       获取GPS UART接收到的一帧数据
 * @param       无
 * @retval      NULL: 未接收到一帧数据
 *              其他: 接收到的一帧数据
 */
uint8_t *gps_uart_rx_get_frame(void)
{
    if (gps_uart_rx_frame.sta.finsh == 1)
    {
        gps_uart_rx_frame.buf[gps_uart_rx_frame.sta.len] = '\0';//末尾加字符串结束符
        return gps_uart_rx_frame.buf;
    }
    else
    {
        return NULL;
    }
}

/**
 * @brief       获取GPS UART接收到的一帧数据的长度
 * @param       无
 * @retval      0   : 未接收到一帧数据
 *              其他: 接收到的一帧数据的长度
 */
uint16_t gps_uart_rx_get_frame_len(void)
{
    if (gps_uart_rx_frame.sta.finsh == 1)
    {
        return gps_uart_rx_frame.sta.len;
    }
    else
    {
        return 0;
    }
}

/**
 * @brief       GPS UART初始化
 * @param       baudrate: UART通讯波特率
 * @retval      无
 */
void gps_uart_init(uint32_t baudrate)
{
    gps_uart_handle.Instance          = GPS_UART_INTERFACE;           /* GPS UART */
    gps_uart_handle.Init.BaudRate     = baudrate;                     /* 波特率 */
    gps_uart_handle.Init.WordLength   = UART_WORDLENGTH_8B;           /* 数据位 */
    gps_uart_handle.Init.StopBits     = UART_STOPBITS_1;              /* 停止位 */
    gps_uart_handle.Init.Parity       = UART_PARITY_NONE;             /* 校验位 */
    gps_uart_handle.Init.Mode         = UART_MODE_TX_RX;              /* 收发模式 */
    gps_uart_handle.Init.HwFlowCtl    = UART_HWCONTROL_NONE;          /* 无硬件流控 */
    gps_uart_handle.Init.OverSampling = UART_OVERSAMPLING_16;         /* 过采样 */
    HAL_UART_Init(&gps_uart_handle);                                  /* 使能GPS UART
                                                                     * HAL_UART_Init()会调用函数HAL_UART_MspInit()
                                                                     * 该函数定义在文件usart.c中
                                                                     */
      HAL_NVIC_SetPriority(GPS_UART_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(GPS_UART_IRQn);
    
    
     __HAL_UART_ENABLE_IT(&gps_uart_handle, UART_IT_IDLE);// 开空闲中断
     __HAL_UART_ENABLE_IT(&gps_uart_handle, UART_IT_RXNE);// 开接收非空中断    
    
    #if GPS_USING_RT_THREAD_OS
    /* 创建一个互斥信号*/  
        GPS_usart_mux = rt_mutex_create("gps_mux",RT_IPC_FLAG_PRIO);
    if (GPS_usart_mux == RT_NULL)
            rt_kprintf("error@ %s %s:%d,The mux for gps uart can‘t be created!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
    /* 创建一个信号*/
        GPS_RX_IT_sem = rt_sem_create("binary_sem",
                                                                                0,
                                                                                RT_IPC_FLAG_FIFO);
    if (GPS_RX_IT_sem == RT_NULL)
            rt_kprintf("error@ %s %s:%d,The semphore for gps uart can‘t be created!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
        
    #endif
}
/**
 * @brief       GPS_UART互斥锁
 * @param       baudrate: UART通讯波特率
 * @retval      无
 */
#if GPS_USING_RT_THREAD_OS
void gps_lock(void)
{
   rt_mutex_take( GPS_usart_mux,RT_WAITING_FOREVER); //获取互斥量
}
void gps_unlock(void)
{
   rt_mutex_release( GPS_usart_mux ); //释放互斥量 
}    
#endif

/**
 * @brief       线程调用的应用函数与串口通信中断同步信号
 * @param       timeout: 延时多少ticks
 * @retval      无
 */
#if GPS_USING_RT_THREAD_OS
uint8_t gps_take_sem(rt_int32_t timeout)
{
  rt_err_t uwRet = RT_EOK;
    //GPS_RX_IT_sem->value =0;//信号值清零
    uwRet = rt_sem_take(GPS_RX_IT_sem, timeout);//等待信号量
    if(-RT_ETIMEOUT == uwRet){
            rt_kprintf("error@ %s %s:%d, GPS_RX_IT_sem:time out!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
      return 1;//超时
    }         
    if(-RT_ERROR  == uwRet){
            rt_kprintf("error@ %s %s:%d,GPS_RX_IT_sem:ERROR!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
      return 2;//错误
    }              
    /* 如果信号接收完成并且正确 */
      return 0;
}
uint8_t gps_release_sem(void)
{
    rt_sem_release(GPS_RX_IT_sem);//放出信号量
    return 0;
}
#endif
/**
 * @brief       GPS UART中断回调函数
 * @param       无
 * @retval      无
 */
void GPS_UART_IRQHandler(void)
{
    uint8_t tmp;
    
    if (__HAL_UART_GET_FLAG(&gps_uart_handle, UART_FLAG_ORE) != RESET)        /* UART接收过载错误中断 */
    {
        __HAL_UART_CLEAR_OREFLAG(&gps_uart_handle);                           /* 清除接收过载错误中断标志 */
        (void)gps_uart_handle.Instance->ISR;//SR;                                   /* 先读SR寄存器,再读DR寄存器 */
        (void)gps_uart_handle.Instance->RDR;//DR;
    }
    
    if (__HAL_UART_GET_FLAG(&gps_uart_handle, UART_FLAG_RXNE) != RESET)       /* UART接收中断 */
    {
        HAL_UART_Receive(&gps_uart_handle, &tmp, 1, HAL_MAX_DELAY);           /* UART接收数据 */
        if(gps_uart_rx_frame.sta.finsh ==1 ) return;
            
        if (gps_uart_rx_frame.sta.len < (GPS_UART_RX_BUF_SIZE - 1))   /* 判断UART接收缓冲是否溢出
                                                                             * 留出一位给结束符'\0'
                                                                             */
        {
            gps_uart_rx_frame.buf[gps_uart_rx_frame.sta.len] = tmp;             /* 将接收到的数据写入缓冲 */
            gps_uart_rx_frame.sta.len++;                                      /* 更新接收到的数据长度 */
        }
        else                                                                /* UART接收缓冲溢出 */
        {
            gps_uart_rx_frame.sta.len = 0;                                    /* 覆盖之前收到的数据 */
            gps_uart_rx_frame.buf[gps_uart_rx_frame.sta.len] = tmp;             /* 将接收到的数据写入缓冲 */
            gps_uart_rx_frame.sta.len++;                                      /* 更新接收到的数据长度 */
        }
    }
    
    if (__HAL_UART_GET_FLAG(&gps_uart_handle, UART_FLAG_IDLE) != RESET)       /* UART总线空闲中断 */
    {
        gps_uart_rx_frame.sta.finsh = 1;                                      /* 标记帧接收完成 */
        
        __HAL_UART_CLEAR_IDLEFLAG(&gps_uart_handle);                          /* 清除UART总线空闲中断 */
            
            #if GPS_USING_RT_THREAD_OS
            gps_release_sem();                                                     /* 发送信号,通知帧接收完成 */
            #endif
    }
}
复制代码

在中断程序stm32g0xx_it.c中,添加代码如下

复制代码
/**
  * @brief This function handles USART1 global interrupt / USART1 wake-up interrupt through EXTI line 25.
  */
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
  GPS_UART_IRQHandler();
  /* USER CODE END USART1_IRQn 0 */
 // HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}
复制代码

GPS模块数据解析程序:

gps_driver.h
复制代码
/**
 ****************************************************************************************************
 * @file        gps_driver.h
 * @author      
 * @version     V1.0
 * @date        2023-02-22
 * @brief       
 * @license    
 ****************************************************************************************************
 * @attention
 *        AT指令操作WIFI模块的硬件驱动函数
 *
 ****************************************************************************************************
 */

#ifndef __GPS_DRIVER_H
#define __GPS_DRIVER_H
#include "stm32g0xx_hal.h"
//$ GNGLL,<1>,<2>,<3>,<4>,<5>,<6>,<7><8>
//$ GNGLL,2253.7220,N,11350.7025,E,012842.000,A,A4D
//<1> 纬度 ddmm.mmmmm(度分) 10个char
//<2> 纬度半球 N(北半球)或 S(南半球)1个char
//<3> 经度 dddmm.mmmmm(度分)11个char
//<4> 经度半球 E(东经)或 W(西经)
//<5> UTC 时间:hhmmss(时分秒)6个char
//<6> 定位状态,A=有效定位,V=无效定位  1个char
//<7> 模式指示(A=自主定位,D=差分,E=估算,N=数据无效) 1个char
//<8> 校验和 
typedef struct  
{                                            
    char latitude[10+1];        //纬度 
    char longitude[11+1];       //经度    
    char north[1+1];
      char east[1+1];
      char UTC[10+1];
    char validPosition[1+1];      //定位有效A 定位无效V 
    
}gps_gll_type;
//$GNRMC,132506.000,A,2233.87430,N,11407.13740,E,0.00,244.71,080522,,,A,V*0A
//$GNRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>,<13>,*CS
//RMC推荐的最少专用导航数据
//<1> 132506.000:定位点的UTC时间,hhmmss.sss(时分秒.毫秒)格式
//<2> A: 定位状态,A=定位,V=未定位
//<3> 2233.87430:纬度ddmm.mmmm(度分)格式(前导位数不足补0)
//<4> N:纬度半球N(北半球)或S(南半球)
//<5> 11407.13740:经度dddmm.mmmm(度分)格式(前导位数不足补0)
//<6> E:经度半球E(东经)或W(西经)
//<7> 0.00:对地航速,单位:Knots,范围:000.0-999.9
//<8> 244.71:对地航向,单位:度,以真北为参考基准,二维方向指向,相当于二维罗盘
//<9> 080522:定位到UTC日期,格式:ddmmyy(日月年)
//<10> :磁偏角,单位:度,范围:000-180
//<11> :磁偏角方向,E:东,W:西
//<12> A:定位模式标志,A:自主模式,E:估算模式,N:数据无效,D:差分模式
//<13> V:导航状态标志,V表示系统不输出导航状态信息
//<CS> *0A:校验和
typedef struct  
{                                            
    char latitude[10+1];        //纬度 
    char longitude[11+1];       //经度    
    char north[1+1];            //北半球
      char east[1+1];             //东半球
      char UTC_time[10+1];        //UTC 时间
      char UTC_date[6+1];         //UTC 日期
    char validPosition[1+1];    //定位有效A 定位无效V 
    
}gps_rmc_type;

void gps_setup(void);//启动GPS线程,开串口,收GPS报文
uint8_t gps_get_gll(uint32_t timeout);//经纬度,时间
uint8_t gps_get_rmc(uint32_t timeout);//经纬度,时间,日期
 __weak  void gps_callback(void);
#endif
复制代码
gps_driver.h
线程函数 完成 根据GPS数据修改STM32内RTC,并计算太阳位置。
复制代码
#include <gps_driver.h>
#include <gps_uart1_bsp.h>
#include <sunPosition.h>
#include "usart.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "rtc.h"
#include <sunPosition.h>
#include <timeformat.h>
#include "main.h"
gps_gll_type  gps_gll;
gps_rmc_type  gps_rmc;
volatile spa_data  mspa_data;
/* RT-Thread 定义线程控制块指针 */
static rt_thread_t gps_read_thread  = RT_NULL;


/*
***********************************************
* 函 数 名:  gps_read_thread_entry
* 功能说明:  线程程序,启动串口与GPS模块通信,将GPS字符串转为UTC时间和经纬度
* 形    参:无
* 返 回 值: 无
* 全局变量:
************************************************
*/ 
static  void gps_read_thread_entry(void* parameter)/*通过与GPS通信,得到经纬度、UTC时间等*/
{
    static int32_t  time;
    static int32_t  date; 
    int32_t temp;    
    float  longitude;
    float  latitude;
    float  ftemp;

    RTC_TimeTypeDef sTime = {0};
    RTC_DateTypeDef sDate = {0};    
    rt_enter_critical();     /* 进入临界段,禁切换线程 */
    gps_uart_init(9600);  /* GPS UART初始化 */
    /* 退出临界段 */
    rt_exit_critical(); /* 退出临界段,可切换线程 */    
    while(1)
    {
 // while(0 != gps_get_gll(1000)){};//阻塞方式,通过串口获得经纬度,时间
    while(0 !=     gps_get_rmc(1000)){};//阻塞方式,通过串口获得经纬度,时间    
            
        if(gps_rmc.validPosition[0] !='A') continue; /*无效GPS数据*/
        
        /*GPS字符串转为数值*/
        __disable_irq();//因中断处理gps_gll,需要关总中断
        /*转换与赋值*/
     gps_rmc.UTC_time[6] ='\0';
   time = atoi(gps_rmc.UTC_time);// BCD编码hh mm ss 字符串按照10进制转为数值
     date = atoi(gps_rmc.UTC_date);// BCD编码dd mm yy 字符串按照10进制转为数值
   longitude = atof(gps_rmc.longitude);// BCD编码ddd mm.mmmm
     latitude  = atof(gps_rmc.latitude); // BCD编码 dd mm.mmmm
     /*分割数据*/        
   temp = time%100;        
     sTime.Seconds = temp;
   temp =    time/100;        
   temp =    temp%100;        
     sTime.Minutes = temp;            
   temp =    time/10000;        
   temp =    temp%100;    
     sTime.Hours = temp;    
     /*分割数据*/         
   temp = date%100;        
     sDate.Year = temp;     
   temp =    date/100;        
   temp =    temp%100;     
     sDate.Month = temp;
   temp =    date/10000;        
   temp =    temp%100;     
     sDate.Date = temp;     
     /*解析数据*/         
     temp = (int32_t)(longitude);
     temp = temp/100;
     ftemp = longitude - temp*100;
     ftemp = ftemp/60;
     longitude = ftemp + temp;
     /*解析数据*/             
     temp = (int32_t)(latitude);
     temp = temp/100;
     ftemp = latitude - temp*100;
     ftemp = ftemp/60;
     latitude = ftemp + temp;     
     //更新RTC时间为NTC时间
  sTime.SubSeconds = 0x0;
  sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
  sTime.StoreOperation = RTC_STOREOPERATION_RESET;
  if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK)
  {
    Error_Handler();
  }
  sDate.WeekDay = RTC_WEEKDAY_MONDAY;

  if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BCD) != HAL_OK)
  {
    Error_Handler();
  }
        __enable_irq();//因中断处理gps_gll,需要开总中断    
       
   /*BCD编码转为16进制编码*/ 
     mspa_data.year  = Bcd2ToByte(sDate.Year);
     mspa_data.month = Bcd2ToByte(sDate.Month);     
     mspa_data.day   = Bcd2ToByte(sDate.Date);
   /*BCD编码转为16进制编码*/     
     mspa_data.hour    = Bcd2ToByte(sTime.Hours);
     mspa_data.minute  = Bcd2ToByte(sTime.Minutes);    
     mspa_data.second  = Bcd2ToByte(sTime.Seconds);    
   /*BCD编码转为16进制编码*/     
     mspa_data.longitude = longitude;
     mspa_data.latitude  = latitude;
    
     mspa_data.timezone =8;//北京时间,为东8区
    
     rt_kprintf("GPS SUNPOS! %s %s:%d\r\n", __FILE__, __FUNCTION__, __LINE__);
     
     // 根据日期算天数
   date2days_cal( (spa_data*)&mspa_data);
     // 计算太阳信息
     sunpos_cal((spa_data*)&mspa_data);
     // 打印spa_data
     sunpos_print((spa_data*)&mspa_data);
     gps_callback();
    }
}
/**
 * @brief       gps_callback 
 * @param       
 * @retval     
 *              
 */
 __weak  void gps_callback(void)
{
   rt_kprintf(" %s %s:%d\r\n", __FILE__, __FUNCTION__, __LINE__);
};
/**
 * @brief       gps_setup 
 * @param       
 * @retval     
 *              
 */
void gps_setup(void)
{

    
    /*创建线程控制块,并赋给一个该类型指针变量f*/
    gps_read_thread = /* 线程控制块指针 */
    rt_thread_create( "wifi_regs",  /* 线程名字 */
                                        gps_read_thread_entry, /* 线程入口函数 */
                                        RT_NULL, /* 线程入口函数参数 */
                                        1024, /* 线程栈大小 */
                                        1, /* 线程的优先级 */
                                        200); /* 线程时间片 */
    /* 启动线程,开启调度 */
    if (gps_read_thread != RT_NULL){
        rt_thread_startup(gps_read_thread);
        return;
    }
    rt_kprintf("error@ %s %s:%d\r\n",
                         __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码            
}
/**
 * @brief       gps_get_gll 经纬度,时间
 * @param       
 *              timeout: 等待超时时间
 * @retval      0     : 函数执行成功
 *              1     : 等待期望应答超时,函数执行失败
 */
//$ GNGLL,<1>,<2>,<3>,<4>,<5>,<6>,<7><8>
//$ GNGLL,2253.7220,N,11350.7025,E,012842.000,A,A4D
//<1> 纬度 ddmm.mmmmm(度分) 10个char
//<2> 纬度半球 N(北半球)或 S(南半球)1个char
//<3> 经度 dddmm.mmmmm(度分)11个char
//<4> 经度半球 E(东经)或 W(西经)
//<5> UTC 时间:hhmmss(时分秒)10个char
//<6> 定位状态,A=有效定位,V=无效定位  1个char
//<7> 模式指示(A=自主定位,D=差分,E=估算,N=数据无效) 1个char
//<8> 校验和                                             2个char
uint8_t gps_get_gll(uint32_t timeout)//经纬度,时间
{
    uint8_t *ret = NULL;
      uint8_t ubRet;
      uint16_t len;
      char *frame_start;
    char *frame_end;
      char *p_start;
      char *p_end;
    gps_uart_rx_restart();
    if (timeout == 0)
    { 
        return 2;
    }
    else
    {
            receive:
            ubRet = gps_take_sem(timeout);//已接收帧,但串口没有接收到新的字节。等待这种情况
            if( ubRet ==0){//从通信帧中查找ack
                ret = gps_uart_rx_get_frame();
                if (ret == NULL){// ret没有字符串
                    gps_uart_rx_restart();//清零RX缓冲
                    return  2;
                }    
              frame_start=strstr((const char *)ret, "GLL"); 
                if (frame_start== NULL)//在ret字符串中查找 "GNGLL"
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }    
                frame_end= 51+frame_start;
        /*纬度字符串*/    //<1> 纬度 ddmm.mmmmm(度分) 10个char            
        p_start = strstr((const char *)frame_start, ","); 
                if (p_start== NULL)//在ret字符串中查找 ","
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }    
        p_end = strstr((const char *)(p_start+1), ","); 
                if (p_end== NULL)//在ret字符串中查找 ","
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }    
                if(p_end>=frame_end) 
                {
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧                
                }
        len = p_end - (    p_start+1 ) ;                
        if(len!=10) 
                {
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧                    
                }                    
                memcpy(gps_gll.latitude, p_start+1, len);
                gps_gll.latitude[len] ='\0';
        /*北半球字符串*/    //<2> 纬度半球 N(北半球)或 S(南半球)1个char                
        p_start = strstr((const char *)(p_end), ","); 
                if (p_start== NULL)//在ret字符串中查找 ","
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }    
        p_end = strstr((const char *)(p_start+1), ","); 
                if (p_end== NULL)//在ret字符串中查找 ","
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }    
                if(p_end>=frame_end) 
                {
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧                
                }
        len = p_end - (    p_start+1 ) ;                
        if(len!=1) 
                {
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧                    
                }                    
                memcpy(gps_gll.north, p_start+1, len);
                gps_gll.north[len] ='\0';            
        /*经度字符串*/    //<3> 经度 dddmm.mmmmm(度分)11个char        
        p_start = strstr((const char *)(p_end), ","); 
                if (p_start== NULL)//在ret字符串中查找 ","
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }    
        p_end = strstr((const char *)(p_start+1), ","); 
                if (p_end== NULL)//在ret字符串中查找 ","
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }    
                if(p_end>=frame_end) 
                {
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧                
                }
        len = p_end - (    p_start+1 ) ;                
        if(len!=11) 
                {
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧                    
                }                    
                memcpy(gps_gll.longitude, p_start+1, len);
                gps_gll.longitude[len] ='\0';
        /*东半球字符串*/        //<4> 经度半球 E(东经)或 W(西经)    
        p_start = strstr((const char *)(p_end), ","); 
                if (p_start== NULL)//在ret字符串中查找 ","
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }    
        p_end = strstr((const char *)(p_start+1), ","); 
                if (p_end== NULL)//在ret字符串中查找 ","
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }    
                if(p_end>=frame_end) 
                {
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧                
                }
        len = p_end - (    p_start+1 ) ;                
        if(len!=1) 
                {
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧                    
                }                    
                memcpy(gps_gll.east, p_start+1, len);
                gps_gll.east[len] ='\0';
        
                /*时间字符串*/    //<5> UTC 时间:hhmmss(时分秒)10个char                
        p_start = strstr((const char *)(p_end), ","); 
                if (p_start== NULL)//在ret字符串中查找 ","
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }    
        p_end = strstr((const char *)(p_start+1), ","); 
                if (p_end== NULL)//在ret字符串中查找 ","
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }    
                if(p_end>=frame_end) 
                {
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧                
                }
        len = p_end - (    p_start+1 ) ;                
        if(len!=10) 
                {
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧                    
                }                    
                memcpy(gps_gll.UTC, p_start+1, len);
                gps_gll.UTC[len] ='\0';                
                
                /*定位状态字符串 定位状态,A=有效定位,V=无效定位*/    //<6> 定位状态,A=有效定位,V=无效定位  1个char
        p_start = strstr((const char *)(p_end), ","); 
                if (p_start== NULL)//在ret字符串中查找 ","
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }    
        p_end = strstr((const char *)(p_start+1), ","); 
                if (p_end== NULL)//在ret字符串中查找 ","
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }    
                if(p_end>=frame_end) 
                {
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧                
                }
        len = p_end - (    p_start+1 ) ;                
        if(len!=1) 
                {
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧                    
                }                    
                memcpy(gps_gll.validPosition, p_start+1, len);
                gps_gll.validPosition[len] ='\0';
        return 0;                
            }
            if(ubRet ==1){
                /*超时,没有接收数据*/
                return 1;        
            }
            if(ubRet ==2){
              /*错误,没有接收数据*/
              return 2;            
            }
            return 0;
    }
}
//串口助手:$GNRMC,095554.000,A,2318.1327,N,11319.7252,E,000.0,005.7,081215,,,A*73
// 需要根据具体报文编写
// $GNRMC,132506.000,A,2233.87430,N,11407.13740,E,0.00,244.71,080522,,,A,V*0A
//$GNRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>,<13>,*CS
//RMC推荐的最少专用导航数据
#define UTC_TIME_1  10 /* 10BYTE*/
//<1> 132506.000:定位点的UTC时间,hhmmss.sss(时分秒.毫秒)格式  10BYTE
#define LOCATE_STATE_2  1 /* 1 BYTE*/
//<2> A: 定位状态,A=定位,V=未定位
#define LATITUDE_3     9 /* 9 BYTE ,必须根据GPS模块修改*/
//<3> 2233.8743:纬度ddmm.mmmm(度分)格式(前导位数不足补0)
#define IS_NORTHERN_HEMISPHERE_4     1 /* 1 BYTE*/
//<4> N:纬度半球N(北半球)或S(南半球)
#define LONGITUDE_5    10 /* 10 BYTE ,必须根据GPS模块修改*/
//<5> 11407.1374:经度dddmm.mmmm(度分)格式(前导位数不足补0)
#define IS_EASTERN_HEMISPHERE_6     1 /* 1 BYTE,这里没有用*/
//<6> E:经度半球E(东经)或W(西经)
#define  NAVIGATIONAL_SPEED_7     5 /* 5 BYTE*,这里没有用*/
//<7> 0.00:对地航速,单位:Knots,范围:000.0-999.9
#define  NAVIGATIONAL_DIRECTION_8     6 /* 6 BYTE,这里没有用*/
//<8> 244.71:对地航向,单位:度,以真北为参考基准,二维方向指向,相当于二维罗盘
#define  UTC_DATE_9                   6 /* 6 BYTE*/
//<9> 080522:定位到UTC日期,格式:ddmmyy(日月年)
#define  MAGNETIC_DECLINATION_10     3 /* 3 BYTE,这里没有用*/
//<10> :磁偏角,单位:度,范围:000-180
#define  MAGNETIC_DECLINATION_ORIENT_11     1 /* 1 BYTE,这里没有用*/
//<11> :磁偏角方向,E:东,W:西
#define  LOCATION_MODE_12     1 /* 1 BYTE,这里没有用*/
//<12> A:定位模式标志,A:自主模式,E:估算模式,N:数据无效,D:差分模式
#define  PILOT_STATE_13     1 /* 1 BYTE,这里没有用*/
//<13> V:导航状态标志,V表示系统不输出导航状态信息
#define  CHECK_SUM_14     2 /* 2 BYTE,这里没有用*/
//<CS> *0A:校验和
// $GNRMC 6, 逗号 10, 各参数数据BYTES
#define RMC_FOLLOW_USED_BYTES  6+10+UTC_TIME_1+LOCATE_STATE_2+LONGITUDE_5+IS_NORTHERN_HEMISPHERE_4+LATITUDE_3+IS_EASTERN_HEMISPHERE_6+NAVIGATIONAL_SPEED_7+NAVIGATIONAL_DIRECTION_8+UTC_DATE_9 
uint8_t gps_get_rmc(uint32_t timeout)//经纬度,时间
{
      uint8_t *pframe = NULL;
      uint8_t ubRet;
      uint16_t len;
      char *frame_start;
      char *p_start;
      char *p_end;
    gps_uart_rx_restart();
    if (timeout == 0)
    { 
        return 2;
    }
    else
    {
            receive:
            ubRet = gps_take_sem(timeout);//已接收帧,但串口没有接收到新的字节。等待这种情况
            if( ubRet ==0){//从通信帧中查找ack
                pframe = gps_uart_rx_get_frame();
                if (pframe == NULL){// ret没有字符串
                    gps_uart_rx_restart();//清零RX缓冲
                    return  2;
                }    
                
              frame_start=strstr((const char *)pframe , "RMC"); 
                if (frame_start== NULL)//在ret字符串中查找 "GNRMC"
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }    
                
                if(gps_uart_rx_get_frame_len()< RMC_FOLLOW_USED_BYTES)
                    goto receive;//通信帧中没有足够的字符,再次接收新的帧
                                
                //<1> 132506.000:定位点的UTC时间,hhmmss.sss(时分秒.毫秒)格式  10BYTE
        p_start = strstr((const char *)frame_start, ","); 
                if (p_start== NULL)//在ret字符串中查找 "," 
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }    
        p_end = strstr((const char *)(p_start+1), ","); 
                if (p_end== NULL)//在ret字符串中查找 ","
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }                    
        len = p_end - (    p_start+1 ) ;                
        if(len!= UTC_TIME_1) 
                {
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧                    
                }                        
                memcpy(gps_rmc.UTC_time, p_start+1, UTC_TIME_1);
                gps_rmc.UTC_time[UTC_TIME_1] ='\0';                        
                
        //<2> A: 定位状态,A=定位,V=未定位                
        p_start = strstr((const char *)(p_end), ","); 
                if (p_start== NULL)//在ret字符串中查找 "," 
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }    
        p_end = strstr((const char *)(p_start+1), ","); 
                if (p_end== NULL)//在ret字符串中查找 ","
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }                    
        len = p_end - (    p_start+1 ) ;                
        if(len!= LOCATE_STATE_2) 
                {
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧                    
                }                        
                memcpy(gps_rmc.validPosition, p_start+1, LOCATE_STATE_2);
                gps_rmc.validPosition[LOCATE_STATE_2] ='\0';                            
                
                //<3> 2233.8743:纬度ddmm.mmmm(度分)格式(前导位数不足补0)
        p_start = strstr((const char *)(p_end), ","); 
                if (p_start== NULL)//在ret字符串中查找 "," 
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }    
        p_end = strstr((const char *)(p_start+1), ","); 
                if (p_end== NULL)//在ret字符串中查找 ","
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }                    
        len = p_end - (    p_start+1 ) ;                
        if(len!= LATITUDE_3) 
                {
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧                    
                }                        
                memcpy(gps_rmc.latitude, p_start+1,LATITUDE_3);
                gps_rmc.latitude[LATITUDE_3] ='\0';                

                //<4> N:纬度半球N(北半球)或S(南半球)
        p_start = strstr((const char *)(p_end), ","); 
                if (p_start== NULL)//在ret字符串中查找 "," 
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }    
        p_end = strstr((const char *)(p_start+1), ","); 
                if (p_end== NULL)//在ret字符串中查找 ","
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }                    
        len = p_end - (    p_start+1 ) ;                
        if(len!= IS_NORTHERN_HEMISPHERE_4) 
                {
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧                    
                }                        
                memcpy(gps_rmc.north, p_start+1,IS_NORTHERN_HEMISPHERE_4);
                gps_rmc.north[IS_NORTHERN_HEMISPHERE_4] ='\0';                        

                //<5> 11407.13740:经度dddmm.mmmm(度分)格式(前导位数不足补0)
        p_start = strstr((const char *)(p_end), ","); 
                if (p_start== NULL)//在ret字符串中查找 "," 
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }    
        p_end = strstr((const char *)(p_start+1), ","); 
                if (p_end== NULL)//在ret字符串中查找 ","
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }                    
        len = p_end - (    p_start+1 ) ;                
        if(len!= LONGITUDE_5) 
                {
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧                    
                }                        
                memcpy(gps_rmc.longitude, p_start+1,LONGITUDE_5);
                gps_rmc.longitude[LONGITUDE_5] ='\0';        
                
                //<6> E:经度半球E(东经)或W(西经)                
        p_start = strstr((const char *)(p_end), ","); 
                if (p_start== NULL)//在ret字符串中查找 "," 
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }    
        p_end = strstr((const char *)(p_start+1), ","); 
                if (p_end== NULL)//在ret字符串中查找 ","
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }                    
        len = p_end - (    p_start+1 ) ;                
        if(len!= IS_EASTERN_HEMISPHERE_6) 
                {
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧                    
                }                        
                memcpy(gps_rmc.east, p_start+1,IS_EASTERN_HEMISPHERE_6);
                gps_rmc.east[IS_EASTERN_HEMISPHERE_6] ='\0';                        
                
                //<7> 0.00:对地航速,单位:Knots,范围:000.0-999.9
        p_start = strstr((const char *)(p_end), ","); 
                if (p_start== NULL)//在ret字符串中查找 "," 
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }    
        p_end = strstr((const char *)(p_start+1), ","); 
                if (p_end== NULL)//在ret字符串中查找 ","
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }                    
        len = p_end - (    p_start+1 ) ;                
        if(len!= NAVIGATIONAL_SPEED_7) 
                {
                     //gps_uart_rx_restart();//清零RX缓冲
                     //goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧                    
                }                        
                //memcpy(gps_rmc.east, p_start+1,NAVIGATIONAL_SPEED_7);
                //gps_rmc.east[NAVIGATIONAL_SPEED_7] ='\0';                    
                
                //<8> 244.71:对地航向,单位:度,以真北为参考基准,二维方向指向,相当于二维罗盘
        p_start = strstr((const char *)(p_end), ","); 
                if (p_start== NULL)//在ret字符串中查找 "," 
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }    
        p_end = strstr((const char *)(p_start+1), ","); 
                if (p_end== NULL)//在ret字符串中查找 ","
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }                    
        len = p_end - (    p_start+1 ) ;                
        if(len!= NAVIGATIONAL_DIRECTION_8) 
                {
                     //gps_uart_rx_restart();//清零RX缓冲
                     //goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧                    
                }                        
                //memcpy(gps_rmc.east, p_start+1,NAVIGATIONAL_DIRECTION_8);
                //gps_rmc.east[NAVIGATIONAL_DIRECTION_8] ='\0';                    
                
                //<9> 080522:定位到UTC日期,格式:ddmmyy(日月年)
        p_start = strstr((const char *)(p_end), ","); 
                if (p_start== NULL)//在ret字符串中查找 "," 
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }    
        p_end = strstr((const char *)(p_start+1), ","); 
                if (p_end== NULL)//在ret字符串中查找 ","
                {                                        
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧
                }                    
        len = p_end - (    p_start+1 ) ;                
        if(len!= UTC_DATE_9) 
                {
                     gps_uart_rx_restart();//清零RX缓冲
                     goto receive;//从通信帧中没有找到目标字符串,再次接收新的帧                    
                }                        
                memcpy(gps_rmc.UTC_date, p_start+1,UTC_DATE_9);
                gps_rmc.UTC_date[UTC_DATE_9] ='\0';    
                
        return 0;                
            }
            if(ubRet ==1){
                /*超时,没有接收数据*/
                return 1;        
            }
            if(ubRet ==2){
              /*错误,没有接收数据*/
              return 2;            
            }
            return 0;
    }
}
复制代码

 

测试算例:

在串口助手ASCII格式,发送: $GNRMC,095554.000,A,2318.1327,N,11359.7252,E,000.0,005.7,040315,,,A*73

 

posted @   辛河  阅读(426)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示