【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 |
实物图

动态效果见顶部视频。
工程创建
- 打开 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 ;

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

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 按钮,生成工程代码。

流程图

工程代码
在 .../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;
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_parser_init();
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");
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");
}
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();
printf("RAW: %s", nmea_line);
if (strstr(nmea_line, "$GPGGA"))
{
if (parse_gpgga(nmea_line, ¤t_gps_data))
{
print_gps_data(¤t_gps_data);
}
else
{
printf("Failed to parse GPGGA data\r\n");
}
}
else if (strstr(nmea_line, "$GPRMC"))
{
gps_time_t rmc_time;
if (parse_gprmc(nmea_line, ¤t_gps_data, &rmc_time))
{
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("\r\n");
}
}
void print_parsed_gps_data(void)
{
static uint32_t last_print_time = 0;
static uint32_t current_time = 0;
current_time++;
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(¤t_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(¤t_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)
{
i2c_event = p_args->event;
}
}
int timeout_ms = 100;
void hal_entry(void)
{
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;
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);
R_BSP_SoftwareDelay(200, BSP_DELAY_UNITS_MILLISECONDS);
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_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();
print_parsed_gps_data();
status_counter++;
if (status_counter >= 1000)
{
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
R_BSP_NonSecureEnter();
#endif
}
汉字取模
- 下载 并运行 PC2LCD2018 软件 ;
- 菜单栏模式设置为
字符模式 ;
- 点击工具栏中的
设置 按钮,配置字模选项:列行式取模、低位在前等,设置完成后点击 确定 保存配置;

- 在文字输入选框输入目标汉字,点击
生成字模 ;
- 选中复制或保存字模,代码添加至
oledfont.h 文件。
保存代码,构建工程、调试工程。
效果演示

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

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

定位结果与实际位置 (121.45,31.03) 较为接近。
动态效果见底部视频。
总结
本文介绍了 RA4M2-SENSOR 通过 IIC 协议实现 OLED 显示,并结合串口通信读取 GPS 模块数据,实现便携式卫星定位器的项目设计,为该产品的相关开发设计与快速应用提供了参考。