- #include "hal_data.h"
- #include "FreeRTOS.h"
- #include "task.h"
- #include "queue.h"
- #include <string.h>
- #include <stdio.h>
- // 宏定义
- #define UART_GNSS_BAUDRATE 115200 // UM982通信波特率
- #define UART_DIFF_BAUDRATE 115200 // 差分数据传输波特率
- #define MAX_GNSS_BUF_LEN 512 // GNSS数据缓冲区大小
- #define RTK_FIXED_STATE 3 // 固定解状态码(参考UM982手册)
- #define PPS_GPIO_PORT BSP_IO_PORT_01 // PPS信号引脚(示例)
- // UM982 UBX帧结构(简化)
- typedef struct {
- uint8_t class; // 消息类(如0x01=NAV)
- uint8_t id; // 消息ID(如0x02=POSLLH)
- uint16_t len; // payload长度
- uint8_t payload[256];// 数据载荷
- uint16_t checksum; // 校验和
- } ubx_frame_t;
- // RTK状态信息
- typedef struct {
- uint8_t rtk_stat; // 0=单点解, 1=浮点解, 3=固定解
- double lat; // 纬度(度)
- double lon; // 经度(度)
- double alt; // 高程(米)
- } rtk_info_t;
- // 全局变量
- QueueHandle_t g_gnss_queue; // GNSS数据队列
- rtk_info_t g_rtk_info = {0}; // RTK状态全局变量
- void hardware_init(void) {
- // 初始化GPIO(PPS信号输入)
- R_IOPORT_Open(&g_ioport_ctrl, g_ioport.p_cfg);
- R_IOPORT_PinCfg(&g_ioport_ctrl, PPS_GPIO_PORT, IOPORT_CFG_PORT_DIRECTION_INPUT);
- // 初始化GNSS UART(连接UM982)
- g_uart0.p_api->open(g_uart0.p_ctrl, g_uart0.p_cfg);
- g_uart0.p_api->baudSet(g_uart0.p_ctrl, UART_GNSS_BAUDRATE, NULL);
- // 初始化差分数据UART(传输RTCM)
- g_uart1.p_api->open(g_uart1.p_ctrl, g_uart1.p_cfg);
- g_uart1.p_api->baudSet(g_uart1.p_ctrl, UART_DIFF_BAUDRATE, NULL);
- // 创建FreeRTOS队列(用于传递GNSS数据)
- g_gnss_queue = xQueueCreate(10, MAX_GNSS_BUF_LEN);
- if (g_gnss_queue == NULL) {
- // 队列创建失败处理
- while(1);
- }
- }
- // UART0中断回调(接收UM982输出的UBX/RTCM数据)
- void uart0_callback(uart_callback_args_t *p_args) {
- static uint8_t gnss_buf[MAX_GNSS_BUF_LEN] = {0};
- static uint16_t buf_idx = 0;
- if (p_args->event == UART_EVENT_RX_CHAR) {
- // 接收单字节数据
- gnss_buf[buf_idx++] = (uint8_t)p_args->data;
- // 缓冲区满或检测到UBX帧尾(0x1B)时,发送到队列
- if (buf_idx >= MAX_GNSS_BUF_LEN || gnss_buf[buf_idx-1] == 0x1B) {
- xQueueSendFromISR(g_gnss_queue, gnss_buf, NULL);
- buf_idx = 0;
- memset(gnss_buf, 0, MAX_GNSS_BUF_LEN);
- }
- }
- }
- // 校验UBX帧校验和
- static bool ubx_checksum_valid(ubx_frame_t *frame) {
- uint8_t ck_a = 0, ck_b = 0;
- // 计算校验和(覆盖class、id、len、payload)
- ck_a += frame->class;
- ck_b += ck_a;
- ck_a += frame->id;
- ck_b += ck_a;
- ck_a += (frame->len >> 8) & 0xFF;
- ck_b += ck_a;
- ck_a += frame->len & 0xFF;
- ck_b += ck_a;
- for (int i=0; i<frame->len; i++) {
- ck_a += frame->payload[i];
- ck_b += ck_a;
- }
- // 对比帧中校验和
- return (ck_a == (frame->checksum >> 8)) && (ck_b == (frame->checksum & 0xFF));
- }
- // 解析UBX-NAV-RTK帧(获取RTK状态)
- static void parse_ubx_nav_rtk(uint8_t *data, uint16_t len) {
- if (len < 4) return; // 最小长度检查
- // RTK状态码位于payload第3字节(参考UM982手册)
- g_rtk_info.rtk_stat = data[3];
- // 打印状态(调试用)
- switch(g_rtk_info.rtk_stat) {
- case 0: printf("RTK状态:单点解\n"); break;
- case 1: printf("RTK状态:浮点解\n"); break;
- case 3: printf("RTK状态:固定解\n"); break;
- default: printf("RTK状态:未知\n");
- }
- }
- // 解析UBX-NAV-POSLLH帧(获取经纬度)
- static void parse_ubx_nav_posllh(uint8_t *data, uint16_t len) {
- if (len < 28) return; // 确保数据完整
- // 纬度(1e-7度):payload[8-11]为int32_t
- int32_t lat_raw = *(int32_t*)(data + 8);
- g_rtk_info.lat = lat_raw / 1e7;
- // 经度(1e-7度):payload[12-15]
- int32_t lon_raw = *(int32_t*)(data + 12);
- g_rtk_info.lon = lon_raw / 1e7;
- // 高程(毫米):payload[20-23]
- int32_t alt_raw = *(int32_t*)(data + 20);
- g_rtk_info.alt = alt_raw / 1000.0;
- printf("位置:%.8f, %.8f, %.2f米\n", g_rtk_info.lat, g_rtk_info.lon, g_rtk_info.alt);
- }
- // 主解析函数
- void parse_gnss_data(uint8_t *buf, uint16_t len) {
- ubx_frame_t frame;
- // 查找UBX帧头(0xB5, 0x62)
- for (int i=0; i<len-2; i++) {
- if (buf[i] == 0xB5 && buf[i+1] == 0x62) {
- // 解析帧结构
- frame.class = buf[i+2];
- frame.id = buf[i+3];
- frame.len = (buf[i+4] << 8) | buf[i+5];
- memcpy(frame.payload, &buf[i+6], frame.len);
- frame.checksum = (buf[i+6+frame.len] << 8) | buf[i+7+frame.len];
-
- // 校验并解析特定帧
- if (ubx_checksum_valid(&frame)) {
- if (frame.class == 0x01 && frame.id == 0x39) { // NAV-RTK帧
- parse_ubx_nav_rtk(frame.payload, frame.len);
- } else if (frame.class == 0x01 && frame.id == 0x02) { // NAV-POSLLH帧
- parse_ubx_nav_posllh(frame.payload, frame.len);
- }
- }
- break;
- }
- }
- }
- // 任务1:接收GNSS数据并解析
- void gnss_parse_task(void *pvParameters) {
- uint8_t rx_buf[MAX_GNSS_BUF_LEN];
- while(1) {
- // 从队列读取数据
- if (xQueueReceive(g_gnss_queue, rx_buf, portMAX_DELAY) == pdPASS) {
- parse_gnss_data(rx_buf, MAX_GNSS_BUF_LEN);
- memset(rx_buf, 0, MAX_GNSS_BUF_LEN);
- }
- }
- }
- // 任务2:基站模式下发送RTCM差分数据
- void base_station_task(void *pvParameters) {
- uint8_t rtcm_buf[MAX_GNSS_BUF_LEN];
- while(1) {
- // 若为基站,将UM982生成的RTCM数据转发给流动站
- // (实际应用中需过滤RTCM帧,此处简化为直接转发)
- if (xQueueReceive(g_gnss_queue, rtcm_buf, portMAX_DELAY) == pdPASS) {
- // 检查是否为RTCM帧(帧头0xD3)
- if (rtcm_buf[0] == 0xD3) {
- g_uart1.p_api->write(g_uart1.p_ctrl, rtcm_buf, MAX_GNSS_BUF_LEN, NULL);
- }
- memset(rtcm_buf, 0, MAX_GNSS_BUF_LEN);
- }
- vTaskDelay(pdMS_TO_TICKS(10)); // 10ms间隔
- }
- }
- // 任务3:LED状态指示(固定解亮绿灯,否则闪烁)
- void led_indicator_task(void *pvParameters) {
- while(1) {
- if (g_rtk_info.rtk_stat == RTK_FIXED_STATE) {
- // 固定解:绿灯常亮
- R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_02, BSP_IO_LEVEL_HIGH);
- vTaskDelay(pdMS_TO_TICKS(500));
- } else {
- // 非固定解:绿灯闪烁
- R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_02, BSP_IO_LEVEL_HIGH);
- vTaskDelay(pdMS_TO_TICKS(200));
- R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_02, BSP_IO_LEVEL_LOW);
- vTaskDelay(pdMS_TO_TICKS(200));
- }
- }
- }
3. 系统功能模块
配置管理:通过串口指令或Web服务器(基于RA8D1的Ethernet)配置GNSS参数(如星座选择、更新率)、差分数据传输协议(TCP/UDP端口)。
日志记录:将定位结果(时间、经纬度、高程、解状态)按格式写入Flash/SD卡,支持后期数据导出分析。
异常处理:检测GNSS信号丢失、差分链路中断等故障,触发重试机制或报警(如蜂鸣器)。
成果展示:
GPS Count 当前接收到有效信号的 GPS 卫星数量充足,GPS Lock3D RTK 固定解、HDOP/VDOP 数值小,说明当前定位精度高、可靠性强;Course Over Ground 则用于指示移动方向,适合导航或轨迹记录场景。
四、创新点
1. 时间同步:利用UM982的PPS信号与RA8D1的定时器同步,确保基站与流动站观测数据的时间戳误差小于1ms(RTK解算精度关键)。
2. 差分数据完整性:在无线传输中加入校验(如CRC),避免数据丢包导致解算失败;实现数据缓存机制,应对短时间链路中断。
3. 低功耗优化:通过RA8D1的睡眠模式(如STOP模式),在GNSS信号稳定时降低CPU主频,关闭闲置外设(如Ethernet),延长户外设备续航。
4. 抗干扰设计:硬件上增加电源滤波电容、信号屏蔽层;软件上采用数据平滑算法(如卡尔曼滤波),减少多路径效应影响。
5.多通信接口输出,依靠瑞萨RA1丰富的通信接口,输出RS232.485,CANFD协议,RJ45,
五、应用场景
精准农业:流动站安装在农机上,通过RTK定位实现厘米级作业路径控制。
工程测量:基站固定于已知点,流动站手持或车载,快速获取测点精确坐标。
智能驾驶:通过RA8D1的CAN接口连接车载总线,或添加IMU(如BNO055)实现GNSS/IMU组合导航,提升遮挡场景下的定位连续性。