瑞萨单片机论坛
直播中

jf_07365693

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

【RA4M2-SENSOR】便携 GPS 定位器

gps_oled

【RA4M2-SENSOR】便携 GPS 定位器

本文介绍了 RA4M2-SENSOR 通过 IIC 协议实现 OLED 显示,并结合串口通信读取 GPS 模块数据,实现便携式 GPS 定位器的项目设计。

项目介绍

  • 硬件连接:GPS 模块和 OLED 分别与开发板的 UART0 和 SCI2-IIC 对应引脚连接;
  • 工程创建:使用 e^2^ studio 软件实现空白工程创建;
  • 工程配置:包括 GPIO 引脚、时钟树、UART堆栈、IIC 堆栈等配置;
  • 工程代码:包括流程图、主程序代码、 GPS 解析代码和 OLED 驱动代码;
  • 效果演示:OLED 显示实时卫星定位经纬度信息、串口打印读取到的 GPS 原始数据和解析数据。

硬件连接

OLED 与开发板连接方式如下

OLED RA4M2 Note
SDA SDA2 (P302) Serial data
SCL SCL2 (P301) Serial clock
VCC 3V3 Power
GND GND Ground

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

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

实物图

gps-oled-connect.jpg

动态效果见顶部视频。

工程创建

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

工程配置

包括 Pins、Clock、Stacks 等配置。

GPIO 引脚

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

  • 同理,将 P101 和 P100 引脚分别配置为 TXD0 和 RXD0 串口模式;

  • 将 SCI2 配置为 Simple I2C 模式,引脚定义为 P302 和 P301 分别对应 SDA2 和 SCL2 ;

    sci-i2c-config.jpg

时钟树

  • 进入 Clock 时钟标签页,将 XTAL 外部高速晶振修改为 12MHzFCLK Div 设置为 2 分频;

clock_tree.png

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 模块匹配;

IIC 堆栈

  • 新建 IIC 通信堆栈

    • Stacks - New Stack - Connectivity - I2C Master (r_sci_i2c)
  • 选中 iic 堆栈方框,打开属性标签,配置 IIC 参数;

  • 属性设置: 属性 - Module g_i2c_master0 I2C Master (r_iic_master)

  • Slave Address 设置为 0x3C

  • 回调函数默认为 sci_i2c_master_callback

  • 点击 Generate Project Content 按钮,生成工程代码。

sci-i2c-stack-config.jpg

流程图

flowchart_gps-oled-uart.jpg

工程代码

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

hal_entry.c

#include "hal_data.h"
#include "oled.h"
#include "bmp.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("=== RA4M2 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); // 纬度
            OLED_ShowDecimal(48, 6, current_gps_data.latitude, 2, 4, 16, 0);
            printf("Longitude: %.6f°\r\n", current_gps_data.longitude); //经度
            OLED_ShowDecimal(48, 3, current_gps_data.longitude, 3, 4, 16, 0);
            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");
            OLED_ShowString(60,6,"None",16,0);
            OLED_ShowString(60,3,"None",16,0);
            printf("Satellites in view: %d\r\n", current_gps_data.satellites);
            printf("Waiting for satellite lock...\r\n");
        }

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

i2c_master_event_t i2c_event = I2C_MASTER_EVENT_ABORTED;
void sci_i2c_master_callback(i2c_master_callback_args_t *p_args)
{
    i2c_event = I2C_MASTER_EVENT_ABORTED;
    if (NULL != p_args)
    {
        /* capture callback event for validating the i2c transfer event*/
        i2c_event = p_args->event;
    }
}
//fsp_err_t err = FSP_SUCCESS;
int  timeout_ms = 100;

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;
    /* Initialize IIC OLED */
            err = R_SCI_I2C_Open(&g_i2c2_ctrl, &g_i2c2_cfg);
            assert(FSP_SUCCESS == err);
            OLED_Init();
            OLED_Clear();
            OLED_DrawBMP(0,0,128,8,BMP1,0); // initialized page -> Renesas logo
            R_BSP_SoftwareDelay(200, BSP_DELAY_UNITS_MILLISECONDS);

            /*----------- Frame ------------*/
            OLED_Clear();
            OLED_ShowString(0,0,"   GPS ",16,1);
            OLED_ShowCHinese(56,0,0,1);//定
            OLED_ShowCHinese(72,0,1,1);//位
            OLED_ShowCHinese(88,0,2,1);//器
            OLED_ShowString(104,0,"   ",16,1);

            //OLED_ShowString(4,3,"ADC",16,0);
            OLED_ShowCHinese(0,3,3,0);//经
            OLED_ShowCHinese(16,3,5,0);//度
            OLED_ShowChar(32,3,':',16,0);
            OLED_ShowCHinese(112,3,6,0); // °

            OLED_ShowCHinese(0,6,4,0);//纬
            OLED_ShowCHinese(16,6,5,0);//度
            OLED_ShowChar(32,6,':',16,0);
            OLED_ShowCHinese(112,6,6,0); // °
     
    while(1){
                process_gps_data(); // 原始GPS数据
                print_parsed_gps_data(); // 解析GPS数据

                // 每隔一段时间打印状态
                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
}

汉字取模

  • 下载 并运行 PC2LCD2018 软件 ;
  • 菜单栏模式设置为 字符模式
  • 点击工具栏中的 设置 按钮,配置字模选项:列行式取模、低位在前等,设置完成后点击 确定 保存配置;

font_mode_setting.jpg

  • 在文字输入选框输入目标汉字,点击 生成字模
  • 选中复制或保存字模,代码添加至 oledfont.h 文件。

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

效果演示

  • 串口调试助手接收 GPS 原始数据和解析数据;

gps-data.jpg

  • 使用移动电源供电,实现便携式 GPS 定位器设计;
  • OLED 显示实时卫星定位经纬度信息

gps-test-oled.jpg

将上述采集得到的经纬坐标 (121,31) 输入网址 经纬度定位 ,点击 查询 按钮,可获得对应的地理位置

gps_local.jpg

定位结果与实际位置 (121.45,31.03) 较为接近。

动态效果见底部视频。

总结

本文介绍了 RA4M2-SENSOR 通过 IIC 协议实现 OLED 显示,并结合串口通信读取 GPS 模块数据,实现便携式卫星定位器的项目设计,为该产品的相关开发设计与快速应用提供了参考。

gps-position

回帖(1)

jf_07365693

2025-9-8 14:15:24
移动电源或者3.7V锂电池供电,可实现GPS卫星定位,在户外作业、旅游探险、安防监控、农林畜牧等领域具有广泛应用~
举报

更多回帖

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