用c语言写一个程序:通过串口UART2和PPS2从北斗模块接收$GNRMC数据,读取并解析其中的时间和年月日信息,转换为北京时间并设置为系统时间
时间: 2024-03-12 07:46:51 浏览: 131
好的,这是一个比较复杂的程序,需要用到串口通信、数据解析和时间转换等知识。我会尽可能详细地解释每一步的实现方法,帮助你完成这个程序。
首先,需要包含一些头文件,定义一些宏和变量:
```c
#include <p32xxxx.h> // 包含芯片头文件
#include <stdio.h> // 标准输入输出库
#include <string.h> // 字符串库
#include <time.h> // 时间库
#define SYS_FREQ 80000000L // 系统时钟频率
#define PB_DIV 2 // 外设时钟分频系数
#define PB_FREQ SYS_FREQ/PB_DIV // 外设时钟频率
char gprmc[100]; // 存储接收到的$GNRMC数据
int gprmc_len = 0; // $GNRMC数据长度
```
接下来,需要初始化串口UART2和PPS2,以便从北斗模块接收数据:
```c
void init_uart2() {
U2MODEbits.STSEL = 0; // 1位停止位
U2MODEbits.PDSEL = 0; // 8位数据位,无奇偶校验
U2MODEbits.BRGH = 0; // 标准波特率模式
U2BRG = PB_FREQ / 16 / 9600 - 1; // 设置波特率为9600
U2STAbits.UTXISEL0 = 0; // 发送缓冲区空时中断
U2STAbits.UTXISEL1 = 0;
U2STAbits.URXISEL = 0; // 接收缓冲区有数据时中断
IFS1bits.U2RXIF = 0; // 清除接收中断标志位
IEC1bits.U2RXIE = 1; // 打开接收中断
U2MODEbits.ON = 1; // 打开UART2模块
}
void init_pps2() {
CFGCONbits.IOLOCK = 0; // 解锁IO口配置
U2RXR = 0b0100; // 把U2RX引脚映射到RP4上
CFGCONbits.IOLOCK = 1; // 锁定IO口配置
}
```
接着,需要编写UART2的中断处理函数,用于接收$GNRMC数据:
```c
void __attribute__((interrupt(ipl2), vector(_UART2_RX_VECTOR))) uart2_rx_isr() {
char data = U2RXREG;
if (data == '$') { // 开始接收$GNRMC数据
gprmc_len = 0;
} else if (gprmc_len < 100) { // 接收$GNRMC数据
gprmc[gprmc_len++] = data;
if (data == '\n') { // 接收完毕,开始解析数据
parse_gprmc(gprmc);
}
}
IFS1bits.U2RXIF = 0; // 清除接收中断标志位
}
```
在中断处理函数中,首先判断接收到的数据是否为'$',如果是,则表示开始接收$GNRMC数据,将数据长度清零。如果接收到的数据不是'$',则将其存储到gprmc数组中,直到接收到'\n'为止,表示$GNRMC数据接收完毕,调用parse_gprmc函数进行解析。
接下来,需要编写parse_gprmc函数,用于解析$GNRMC数据并设置系统时间:
```c
void parse_gprmc(char *data) {
char *p = strtok(data, ","); // 以逗号为分隔符分割字符串
int count = 0;
int time, year, month, day;
while (p != NULL) {
switch (count) {
case 1: // 时间
time = atoi(p);
break;
case 9: // 日期
sscanf(p, "%2d%2d%2d", &day, &month, &year);
break;
}
count++;
p = strtok(NULL, ",");
}
// 转换为北京时间
time += 80000;
if (time >= 240000) {
time -= 240000;
day++;
if (day > 31 || (day == 31 && (month == 2 || month == 4 || month == 6 || month == 9 || month == 11))) {
day = 1;
month++;
if (month > 12) {
month = 1;
year++;
}
}
}
// 设置系统时间
struct tm new_time = {0};
new_time.tm_year = year + 100; // 年份需要加上100
new_time.tm_mon = month - 1; // 月份从0开始
new_time.tm_mday = day;
new_time.tm_hour = time / 100;
new_time.tm_min = time % 100;
new_time.tm_sec = 0;
time_t t = mktime(&new_time);
t -= 8 * 3600; // 转换为UTC时间
_CP0_SET_COUNT(0); // 计时器清零
__builtin_disable_interrupts(); // 关闭中断
_CP0_SET_COMPARE(SYS_FREQ / 2 / 1000); // 设置计时器比较值为500ms
while (_CP0_GET_COUNT() < SYS_FREQ / 2 / 1000); // 延时500ms
_CP0_SET_COMPARE(SYS_FREQ / 2); // 设置计时器比较值为1s
while (_CP0_GET_COUNT() < SYS_FREQ / 2); // 延时1s
_CP0_SET_COUNT(0); // 计时器清零
__builtin_enable_interrupts(); // 开启中断
__builtin_mtc0(12, 0, t); // 设置系统时间
}
```
在parse_gprmc函数中,首先使用strtok函数以逗号为分隔符分割字符串,依次读取时间和日期信息,并进行转换。将时间加上8小时,如果超过24点,则日期加1,并进行月份和年份的调整。最后,使用mktime函数将时间转换为time_t类型,再将其转换为UTC时间,并使用__builtin_mtc0函数设置系统时间。
最后,在main函数中,调用init_uart2和init_pps2函数进行初始化,并进入死循环:
```c
int main() {
init_uart2();
init_pps2();
while (1);
return 0;
}
```
好了,这个程序就完成了。需要注意的是,由于北斗模块输出的$GNRMC数据是ASCII码,因此需要进行字符转换、字符串分割和数值转换等处理,程序中的时间转换算法也只是一个简单的实现,可能存在一些误差。如果需要更精确的时间同步,可以使用GPS模块输出的NMEA0183协议数据,或者连接NTP服务器进行同步。
阅读全文