瑞萨单片机论坛
直播中

jf_07365693

2年用户 380经验值
擅长:嵌入式技术 控制/MCU
私信 关注
[讨论]

【RA4E2开发板评测】串口读取 GPS

【RA-Eco-RA4E2-64PIN-V1.0 开发板】串口读取 GPS

本文介绍了 RA-Eco-RA4E2-64PIN-V1.0 通过串口通信接口 UART 读取 GPS 模块,并串口发送原始数据和解析数据的项目设计。

项目介绍

  • 硬件连接:GPS 模块通过串口与开发板的 UART0 对应引脚连接;
  • 工程创建:使用 e2 studio 软件实现空白工程创建;
  • 工程配置:包括GPIO引脚、时钟树、堆栈、串口重定向等配置;
  • 工程代码:包括主程序代码和 GPS 函数定义头文件和源文件;
  • 效果演示:串口打印读取到的 GPS 原始数据和解析数据。

硬件连接

GPS 模块与开发板连接方式如下

GPS module RA4E2 Note
RXD TXD0 (P101) Receive data
TXD RXD0 (P100) Transmit data
VCC 3V3 Power
GND GND Ground

实物图

gps-connect.jpg

工程创建

  • 打开 e^2^ studio 软件;
  • 依次点击 文件 - 新建 - 瑞萨 C/C++ 项目 - Renesas RA
  • 依次进行工程命名,路径设置,FSP版本,目标开发板选择,Device 选择 R7FA4E2B93CFM ,工具链选择 GNU ARM Embedded ,调试器选择 J-Link ,完成工程创建 ;

工程配置

包括 Pins、Clock、Stacks 等配置。

GPIO 引脚

  • 进入 FSP 配置界面,打开 Pins 标签页,根据原理图或开发板丝印,将 P109 和 P110 引脚分别配置为 TXD9 和 RXD9 串口模式;
  • 同理,将 P101 和 P100 引脚分别配置为 TXD0 和 RXD0 串口模式;

gps-uart-gpio-config.jpg

UART 堆栈

  • 新建串口通信堆栈 New Stack - Connectivity - UART (r_sci_uart)
  • 串口属性配置,General 标签下的 Channel 改为 9,名称改为 g_uart9,中断回调函数命名为 user_uart9_callback
  • 再次新建串口通信堆栈 New Stack - Connectivity - UART (r_sci_uart)
  • 串口属性配置,General 标签下的 Channel 改为 0,名称改为 g_uart0,中断回调函数命名为 user_uart0_callback,注意波特率需要修改为 9600 并与 GPS 模块匹配;

gps-uart-config.jpg

  • 进入 BSP 标签页,配置 RA Common 属性,RA Common 标签下的 Heap size 改为 0x2000
  • 点击 Generate Project Content 按钮,生成工程代码。

串口重定向

  • 右键项目进入属性界面,选择 C/C++ 构建 - 设置 - GNU Arm Cross C Linker - Miscellaneous,勾选 printf 、scanf 以及syscalls 选项。
    printf-config.jpg

流程图

flowchart_gps-uart.jpg

工程代码

.../src 目录下新建源文件 gps_parser.c 和头文件 gps_parser.h 用于配置 GPS 相关解析函数。

hal_entry.c

#include "hal_data.h"
#include "gps_parser.h"
#include "stdio.h"
#include <string.h>

FSP_CPP_HEADER
void R_BSP_WarmStart(bsp_warm_start_event_t event);
FSP_CPP_FOOTER

fsp_err_t err = FSP_SUCCESS;
volatile bool uart_send_complete_flag = false;
void user_uart9_callback (uart_callback_args_t * p_args)
{
    if(p_args->event == UART_EVENT_TX_COMPLETE)
    {
        uart_send_complete_flag = true;
    }
}

/*------------- 串口重定向 -------------*/
#ifdef __GNUC__
    #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else

#endif

PUTCHAR_PROTOTYPE
{
        err = R_SCI_UART_Write(&g_uart9_ctrl, (uint8_t *)&ch, 1);
        if(FSP_SUCCESS != err) __BKPT();
        while(uart_send_complete_flag == false){}
        uart_send_complete_flag = false;
        return ch;
}

int _write(int fd,char *pBuffer,int size)
{
    for(int i=0;i<size;i++)
    {
        __io_putchar(*pBuffer++);
    }
    return size;
}

// 全局变量
volatile uint8_t gps_rx_buffer[256];
volatile uint16_t gps_rx_index = 0;
volatile bool gps_line_ready = false;

gps_data_t current_gps_data;

// SCI0回调函数(GPS数据接收)
void user_uart0_callback(uart_callback_args_t * p_args)
{
    if (p_args->event == UART_EVENT_RX_CHAR)
    {
        uint8_t rx_char = (uint8_t)p_args->data;

        // 存储接收到的字符
        if (gps_rx_index < (sizeof(gps_rx_buffer) - 1))
        {
            gps_rx_buffer[gps_rx_index++] = rx_char;

            // 检查是否收到换行符(一行结束)
            if (rx_char == '\n')
            {
                gps_rx_buffer[gps_rx_index] = '\0';
                gps_line_ready = true;
                gps_rx_index = 0;
            }
        }
        else
        {
            // 缓冲区溢出,重置
            gps_rx_index = 0;
        }
    }
}

// 初始化函数
void init_system(void)
{
    fsp_err_t err;

    printf("=== RA4E2 GPS Receiver ===\r\n");
    printf("Initializing system...\r\n");

    // 初始化GPS解析器
    gps_parser_init();

    // 打开SCI9(printf输出)
    err = R_SCI_UART_Open(&g_uart9_ctrl, &g_uart9_cfg);
    if (FSP_SUCCESS != err)
    {
        while(1); // 初始化失败
    }
    printf("SCI9 (printf) initialized at 115200 baud\r\n");

    // 打开SCI0(GPS数据接收)
    err = R_SCI_UART_Open(&g_uart0_ctrl, &g_uart0_cfg);
    if (FSP_SUCCESS != err)
    {
        printf("Error: Failed to initialize SCI0 for GPS\r\n");
        while(1);
    }
    printf("SCI0 (GPS) initialized at 9600 baud\r\n");
    printf("Waiting for GPS data...\r\n\r\n");
}

// 处理GPS数据
void process_gps_data(void)
{
    if (gps_line_ready)
    {
        // 禁用中断以防数据被修改
        __disable_irq();

        // 复制数据到本地缓冲区
        char nmea_line[256];
        strncpy(nmea_line, (char *)gps_rx_buffer, sizeof(nmea_line));
        nmea_line[sizeof(nmea_line) - 1] = '\0';

        gps_line_ready = false;

        // 使能中断
        __enable_irq();

        // 打印原始NMEA数据
        printf("RAW: %s", nmea_line);

        // 解析GPGGA语句
        if (strstr(nmea_line, "$GPGGA"))
        {
            if (parse_gpgga(nmea_line, &current_gps_data))
            {
                print_gps_data(&current_gps_data);
            }
            else
            {
                printf("Failed to parse GPGGA data\r\n");
            }
        }
        // 可以添加其他NMEA语句的解析
        else if (strstr(nmea_line, "$GPRMC"))
        {
            gps_time_t rmc_time;
            if (parse_gprmc(nmea_line, &current_gps_data, &rmc_time))
            {
                // 如果GPGGA没有提供时间,使用GPRMC的时间
                if (current_gps_data.time.hour == 0 &&
                    current_gps_data.time.minute == 0 &&
                    current_gps_data.time.second == 0)
                {
                    current_gps_data.time = rmc_time;
                }
            }
            //printf("GPRMC sentence received\r\n");
        }

        printf("\r\n");
    }
}

// 打印解析后的GPS数据
void print_parsed_gps_data(void)
{
    static uint32_t last_print_time = 0;
    static uint32_t current_time = 0;

    current_time++;

    // 每2秒打印一次解析结果
    if (current_time - last_print_time >= 200)
    {
        last_print_time = current_time;

        printf("\r\n=== PARSED GPS DATA ===\r\n");

        if (current_gps_data.is_valid)
        {
            // 打印时间信息
            print_gps_time(&current_gps_data.time);
            printf("\r\n");
            // 打印位置信息
            printf("Latitude:  %.6f°\r\n", current_gps_data.latitude);
            printf("Longitude: %.6f°\r\n", current_gps_data.longitude);
            printf("Altitude:  %.1f m\r\n", current_gps_data.altitude);
            printf("Satellites: %d\r\n", current_gps_data.satellites);
            printf("Fix Quality: %d\r\n", current_gps_data.fix_quality);

            // 根据定位质量显示状态
            switch (current_gps_data.fix_quality)
            {
                case 0: printf("Status: Invalid fix\r\n"); break;
                case 1: printf("Status: GPS fix\r\n"); break;
                case 2: printf("Status: DGPS fix\r\n"); break;
                default: printf("Status: Other fix (%d)\r\n", current_gps_data.fix_quality); break;
            }
        }
        else
        {
            // 即使没有有效定位,也显示时间和卫星信息
            if (current_gps_data.time.hour != 0 ||
                current_gps_data.time.minute != 0 ||
                current_gps_data.time.second != 0)
            {
                print_gps_time(&current_gps_data.time);
                printf("\r\n");
            }

            printf("No valid GPS fix\r\n");
            printf("Satellites in view: %d\r\n", current_gps_data.satellites);
            printf("Waiting for satellite lock...\r\n");
        }

        printf("==========================\r\n");
    }
}

void hal_entry(void)
{
    /* TODO: add your own code here */
    err = R_SCI_UART_Open(&g_uart9_ctrl, &g_uart9_cfg);
    assert(FSP_SUCCESS == err);
    printf("RA4M2 GPS Reader Started\r\n");
    // 系统初始化
    init_system();

    uint32_t status_counter = 0;
    while(1){
        //printf("hello world!\n");
        //R_BSP_SoftwareDelay (500, BSP_DELAY_UNITS_MILLISECONDS);
        // 原始GPS数据
        process_gps_data();
        // 处理GPS数据
        print_parsed_gps_data();

        // 每隔一段时间打印状态
        status_counter++;
        if (status_counter >= 1000) // 约每10秒
        {
            printf("[Status] System running...\r\n");
            if (current_gps_data.is_valid)
            {
                printf("[Status] GPS fix acquired\r\n");
            }
            else
            {
                printf("[Status] Waiting for GPS fix\r\n");
            }
            status_counter = 0;
        }

        // 短暂延时
        R_BSP_SoftwareDelay(10, BSP_DELAY_UNITS_MILLISECONDS);
    }
#if BSP_TZ_SECURE_BUILD
    /* Enter non-secure code */
    R_BSP_NonSecureEnter();
#endif
}

gps_parser.c

#include "gps_parser.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

// 初始化GPS解析器
void gps_parser_init(void)
{
    // 可以在这里初始化任何需要的变量
}

// 解析GPGGA语句
bool parse_gpgga(const char *nmea_sentence, gps_data_t *gps_data)
{
    if (!nmea_sentence || !gps_data || strncmp(nmea_sentence, "$GPGGA", 6) != 0)
    {
        return false;
    }

    // 初始化数据结构
    memset(gps_data, 0, sizeof(gps_data_t));
    gps_data->is_valid = false;

    char sentence_copy[256];
    strncpy(sentence_copy, nmea_sentence, sizeof(sentence_copy) - 1);
    sentence_copy[sizeof(sentence_copy) - 1] = '\0';

    char *token = strtok(sentence_copy, ",");
    int field_index = 0;
    double latitude = 0.0, longitude = 0.0;
    char lat_dir = 'N', lon_dir = 'E';

    while (token != NULL)
    {
        switch (field_index)
        {
            case 1: // UTC时间 HHMMSS.SSS
                if (strlen(token) >= 6)
                {
                    // 解析小时、分钟、秒
                    sscanf(token, "%2hhu%2hhu%2hhu",
                           &gps_data->time.hour,
                           &gps_data->time.minute,
                           &gps_data->time.second);

                    // 解析毫秒部分
                    char *dot = strchr(token, '.');
                    if (dot && strlen(dot) > 1)
                    {
                        gps_data->time.millisecond = atoi(dot + 1);
                    }
                }
                break;

            case 2: // 纬度
                latitude = atof(token);
                break;

            case 3: // 纬度方向
                lat_dir = token[0];
                break;

            case 4: // 经度
                longitude = atof(token);
                break;

            case 5: // 经度方向
                lon_dir = token[0];
                break;

            case 6: // 定位质量
                gps_data->fix_quality = atoi(token);
                break;

            case 7: // 卫星数量
                gps_data->satellites = atoi(token);
                break;

            case 9: // 海拔高度
                gps_data->altitude = atof(token);
                break;

            default:
                break;
        }

        token = strtok(NULL, ",");
        field_index++;
    }

    // 转换度分格式为十进制度
    if (latitude > 0)
    {
        double lat_deg = (int)(latitude / 100);
        double lat_min = latitude - (lat_deg * 100);
        gps_data->latitude = lat_deg + (lat_min / 60.0);
        if (lat_dir == 'S') gps_data->latitude = -gps_data->latitude;
    }

    if (longitude > 0)
    {
        double lon_deg = (int)(longitude / 100);
        double lon_min = longitude - (lon_deg * 100);
        gps_data->longitude = lon_deg + (lon_min / 60.0);
        if (lon_dir == 'W') gps_data->longitude = -gps_data->longitude;
    }

    // 检查数据有效性
    gps_data->is_valid = (gps_data->fix_quality > 0);

    return gps_data->is_valid;
}

// 解析GPRMC语句
bool parse_gprmc(const char *nmea_sentence, gps_data_t *gps_data, gps_time_t *time_data)
{
    // 简化的GPRMC解析,主要解析时间
    if (!nmea_sentence || !gps_data || !time_data || strncmp(nmea_sentence, "$GPRMC", 6) != 0)
    {
        return false;
    }

    char sentence_copy[256];
    strncpy(sentence_copy, nmea_sentence, sizeof(sentence_copy) - 1);
    sentence_copy[sizeof(sentence_copy) - 1] = '\0';

    char *token = strtok(sentence_copy, ",");
    int field_index = 0;

    while (token != NULL)
    {
        switch (field_index)
        {
            case 1: // UTC时间 HHMMSS.SSS
                if (strlen(token) >= 6)
                {
                    sscanf(token, "%2hhu%2hhu%2hhu",
                           &time_data->hour,
                           &time_data->minute,
                           &time_data->second);

                    // 解析毫秒部分
                    char *dot = strchr(token, '.');
                    if (dot && strlen(dot) > 1)
                    {
                        time_data->millisecond = atoi(dot + 1);
                    }
                }
                break;

            default:
                break;
        }

        token = strtok(NULL, ",");
        field_index++;
    }

    return true;
}

// 打印GPS数据(包含时间)
void print_gps_data(const gps_data_t *gps_data)
{
    if (!gps_data || !gps_data->is_valid)
    {
        printf("GPS Data: No valid fix\r\n");
        return;
    }

    printf("=== GPS Information ===\r\n");

    // 打印时间
    printf("Time: %02d:%02d:%02d",
           gps_data->time.hour,
           gps_data->time.minute,
           gps_data->time.second);

    if (gps_data->time.millisecond > 0)
    {
        printf(".%03d", gps_data->time.millisecond);
    }
    printf(" UTC\r\n");

    // 打印位置信息
    printf("Latitude:  %.6f°\r\n", gps_data->latitude);
    printf("Longitude: %.6f°\r\n", gps_data->longitude);
    printf("Altitude:  %.1f m\r\n", gps_data->altitude);
    printf("Satellites: %d\r\n", gps_data->satellites);

    // 显示定位质量状态
    printf("Fix Quality: %d (", gps_data->fix_quality);
    switch (gps_data->fix_quality)
    {
        case 0: printf("Invalid"); break;
        case 1: printf("GPS Fix"); break;
        case 2: printf("DGPS Fix"); break;
        case 3: printf("PPS Fix"); break;
        case 4: printf("RTK Fixed"); break;
        case 5: printf("RTK Float"); break;
        case 6: printf("Estimated"); break;
        case 7: printf("Manual"); break;
        case 8: printf("Simulation"); break;
        default: printf("Unknown"); break;
    }
    printf(")\r\n");

    printf("========================\r\n");
}

// 打印时间数据
void print_gps_time(const gps_time_t *time_data)
{
    if (time_data)
    {
        printf("GPS Time: %02d:%02d:%02d",
               time_data->hour, time_data->minute, time_data->second);

        if (time_data->millisecond > 0)
        {
            printf(".%03d", time_data->millisecond);
        }
        printf(" UTC\r\n");
    }
    else
    {
        printf("Time: No data\r\n");
    }
}

gps_parser.h

#ifndef GPS_PARSER_H
#define GPS_PARSER_H

#include <stdint.h>
#include <stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif

// 时间数据结构
typedef struct {
    uint8_t hour;
    uint8_t minute;
    uint8_t second;
    uint16_t millisecond;
} gps_time_t;

// GPS位置数据结构
typedef struct {
    double latitude;      // 纬度(十进制度)
    double longitude;     // 经度(十进制度)
    float altitude;       // 海拔高度(米)
    uint8_t satellites;   // 卫星数量
    uint8_t fix_quality;  // 定位质量
    bool is_valid;        // 数据是否有效
    gps_time_t time;      // 时间数据
} gps_data_t;

// 函数声明
void gps_parser_init(void);
bool parse_gpgga(const char *nmea_sentence, gps_data_t *gps_data);
bool parse_gprmc(const char *nmea_sentence, gps_data_t *gps_data, gps_time_t *time_data);
void print_gps_data(const gps_data_t *gps_data);
void print_gps_time(const gps_time_t *time_data);

#ifdef __cplusplus
}
#endif

#endif // GPS_PARSER_H

保存代码,构建工程、调试工程。

效果演示

  • TypeC - USB 数据线连接开发板串口和电脑;
  • 打开串口调试助手,连接并打开板载串口 9,配置对应的波特率等参数;
  • 打开串口,即可接收读取到的 GPS 原始数据和解析数据;

gps-raw-data.jpg

解析数据包括时间、日期、经纬度、速度等信息。

动态效果见底部视频。

总结

本文介绍了 RA-Eco-RA4E2-64PIN-V1.0 通过串口通信接口 UART 读取 GPS 模块,并串口发送原始数据和解析数据的项目设计,为该产品的相关开发设计与快速应用提供了参考。

gps_uart

更多回帖

发帖
×
20
完善资料,
赚取积分