GNSS RTK(Global Navigation Satellite System Real-Time Kinematic,全球导航卫星系统实时动态差分)是一种高精度定位技术,通过基站与流动站的协同工作,可实现厘米级甚至毫米级的实时定位精度,广泛应用于测绘、精准农业、自动驾驶、工程施工等领域 。 基于瑞萨RA8D1微控制器设计GNSS RTK接收机,依靠瑞萨RA8D1单片机的高性能处理能力、丰富外设接口及低功耗特性,搭配GNSS模组(如UM982)实现实时动态差分定位。
一、系统设计目标
支持RTK差分定位(固定解精度达厘米级)。实现基站与流动站数据交互(如RTCM数据传输)。支持RS232,RS485,CAN,RJ45多种输出,支持ROS系统。具备高效数据处理、存储及外设扩展能力。满足低功耗、小型化及工业级可靠性要求。
二、硬件架构设计
设计框图:
1. 核心控制器:CPKCOR-RA8D1基于瑞萨RA8D1高性能单片机。
核心优势:基于ARM CortexM85内核(最高240MHz主频),支持单精度浮点运算(FPU),适合RTK解算辅助处理;内置丰富外设(UART、SPI、Ethernet、CAN、ADC等),满足多模组互联需求;支持安全加密(TrustZoneM),适合户外设备安全防护。支持单 / 双精度 FPU 和 DSP 指令,可辅助 RTK 解算中的浮点运算(如载波相位差分、基线解算),可以快速解析复杂的 RTCM3.x 帧结构。RA 系列内置多达 8 个 UART 接口,支持硬件流控(RTS/CTS)和 IrDA 模式,可直接通过外部电平转换芯片(如 MAX232/MAX485)实现 RS232/RS485 通信。UART 波特率最高可达 3Mbps,满足 RTK 差分数据(如 RTCM3.2)的高速传输需求;支持中断 / DMA 接收,避免数据丢包(关键于 RTK 固定解稳定性)。内置 2-6 个 CAN FD 控制器(兼容传统 CAN 2.0),支持最高 8Mbps 速率,具备错误检测、自动重传功能,适合工业环境下的多节点通信(如车载 RTK 与自动驾驶系统互联)。内置 10/100Mbps 以太网 MAC+PHY,支持 IEEE 1588 PTP 精确时间同步,可通过 TCP/UDP 协议传输海量差分数据(如基站向多个流动站广播 RTCM)。
2. GNSS模组接口(以UM982为例)
通信接口: 主UART(如UART0):连接UM982的TX/RX,传输原始观测数据(如UBX格式)和配置指令,波特率设为115200bps。 PPS同步:UM982的PPS(秒脉冲)输出连接RA8D1的GPIO(如P100),用于时间同步,确保基站与流动站时钟对齐 ( RTK解算关键)。
天线接口:UM982外接高增益GNSS天线(支持多星座:GPS、北斗、GLONASS、Galileo),通过SMA接口连接,需匹配低噪声放大器(LNA)减少信号损耗。
3. 差分数据传输模块
基站→流动站数据链路:
无线传输:若为移动场景,通过RA8D1的UART1连接4G/5G模组(如移远EC20)或LoRa模组(如Semtech SX1276),传输RTCM3.2差分数据(波特率9600115200bps)。
有线传输:固定场景可通过RA8D1的Ethernet接口(内置MAC+PHY)连接局域网,基于TCP/UDP协议传输差分数据。
接口保护:在UART/Ethernet接口处添加TVS管(如SMBJ6.5A)和ESD保护电路,增强户外抗干扰能力。
4. 存储模块
数据记录:通过SPI1连接NOR Flash(W25Q128)存储配置参数、日志及历史定位数据;或通过SDIO接口连接SD卡,存储大容量原始观测数据(供后处理分析)。
实时时钟(RTC):外接低功耗RTC芯片,在GNSS信号丢失时提供时间基准,通过I2C接口与RA8D1通信。
5. 电源与外围电路
宽压输入:支持9-36V直流输入(适应车载/工业电源),通过DC-DC转换器(如LM2596)转换为5V,再经LDO输出3.3V给RA8D1、GNSS模组等。
指示灯:通过RA8D1的GPIO控制LED,指示系统供电、GNSS信号强度、RTK固定解状态(如固定解时绿灯常亮,浮点解时闪烁)。
三、软件系统设计
1. 开发环境与底层驱动
开发工具:使用瑞萨e² studio IDE,基于FreeRTOS实时操作系统(RA8D1官方支持),实现多任务调度。
外设驱动:
配置UART中断接收GNSS数据(UM982的UBX/RTCM帧),通过DMA减少CPU占用。
实现SPI/I2C驱动,控制Flash、RTC等外设。
开发网络驱动(Ethernet/LoRa/4G),封装TCP/UDP协议栈(如lwIP)用于差分数据传输。
- #include "hal_data.h"
- #include "FreeRTOS.h"
- #include "task.h"
- #include "queue.h"
- #include
- #include
- // 宏定义
- #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);
- }
- }
复制代码
2. GNSS数据处理流程
数据解析:在FreeRTOS任务中解析UM982输出的UBX格式数据(如导航电文、原始伪距、载波相位),提取卫星信息、时间戳、位置初值。
RTK解算辅助:
若作为基站:RA8D1接收UM982的原始数据,结合已知基站坐标生成RTCM3.2差分电文(如1005、1077帧),通过传输模块发送给流动站。
若作为流动站:接收基站RTCM数据,与本地UM982的观测数据融合,通过RA8D1的FPU加速差分算法(或调用UM982内置RTK解算功能,直接获取固定解结果)。
解状态判断:解析UM982的RTK状态帧(如UBXNAVRTK),获取解类型(固定解/浮点解/单点解),通过LED或串口上报。
- // 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);
- }
- }
- }
复制代码
[code]// 校验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; ilen; i++) {
ck_a += frame->payload;
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
|