- 打卡等级:以坛为家I
- 打卡总天数:366
- 最近打卡:2026-05-30 00:01:31
已绑定手机
中级会员
- 积分
- 281
|
用几块钱的材料动手做了一个GPS时钟。
主要的代码由豆包生成。
#include <STC8H.H>
#include <intrins.h>
#include <string.h>
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
// 硬件引脚
sbit LCD_CLK = P3^6;
sbit LCD_DAT = P3^7;
sbit LCD_RS = P3^5;
sbit LCD_RST = P3^4;
#define FOSC 35000000L
// 时钟变量
u8 year=26, mon=5, day=10;
u8 hour=17, min=22, sec=0;
u8 week=0;
u16 ms_cnt = 0;
bit gps_valid = 0;
bit gps_synced = 0;
u8 last_display_sec = 0xFF;
u8 slow_refresh_cnt = 0;
// GPS定位信息变量
u32 latitude = 0; // 纬度,格式:DDMM.MMMM,实际存储为度数*1000000
u32 longitude = 0; // 经度,格式:DDDMM.MMMM,实际存储为度数*1000000
u16 altitude = 0;
u8 satellite = 0;
u16 speed = 0,speed_kmh=0;
char lat_dir = 'N'; // 纬度方向 N/S
char lon_dir = 'E'; // 经度方向 E/W
u8 gps_display_buf[256]; // 用于显示的GPS数据缓冲区
bit gps_data_ready = 0; // GPS数据更新标志
u8 last_valid_lat_lon[50]; // 保存最后一次有效的经纬度字符串
// 显示模式
bit disp_mode = 0;
u8 disp_cnt = 0;
code u8 week_table[7][4]={"SUN","MON","TUE","WED","THU","FRI","SAT"};
code u8 ASCII[][5] = {
// 数字 '0' - 索引0
{0x3E,0x41,0x41,0x3E,0x00}, // 0x3E=00111110, 0x41=01000001, 0x41=01000001, 0x3E=00111110 -> 显示0
// 数字 '1' - 索引1
{0x00,0x42,0x7F,0x40,0x00}, // 0x7F=01111111(全亮竖线), 0x42=01000010, 0x40=01000000 -> 显示1
// 数字 '2' - 索引2
{0x62,0x51,0x49,0x46,0x00}, // 01100010,01010001,01001001,01000110 -> 显示2
// 数字 '3' - 索引3
{0x42,0x49,0x49,0x36,0x00}, // 01000010,01001001,01001001,00110110 -> 显示3
// 数字 '4' - 索引4
{0x1C,0x12,0x7F,0x10,0x00}, // 00011100,00010010,01111111,00010000 -> 显示4
// 数字 '5' - 索引5
{0x4F,0x49,0x49,0x31,0x00}, // 01001111,01001001,01001001,00110001 -> 显示5
// 数字 '6' - 索引6
{0x3E,0x49,0x49,0x30,0x00}, // 00111110,01001001,01001001,00110000 -> 显示6
// 数字 '7' - 索引7
{0x00,0x03,0x71,0x09,0x07}, // 00000000,00000011,01110001,00001001,00000111 -> 显示7
// 数字 '8' - 索引8
{0x36,0x49,0x49,0x36,0x00}, // 00110110,01001001,01001001,00110110 -> 显示8
// 数字 '9' - 索引9
{0x06,0x49,0x49,0x3E,0x00}, // 00000110,01001001,01001001,00111110 -> 显示9
// 空格 ' ' - 索引10
{0x00,0x00,0x00,0x00,0x00}, // 全0,空白字符
// 短横线 '-' - 索引11
{0x08,0x08,0x08,0x08,0x08}, // 00001000重复5次 -> 显示一条水平线
// 冒号 ':' - 索引12
{0x00,0x00,0x24,0x00,0x00}, // 00000000,00000000,00100100,00000000,00000000 -> 显示上下两个点
// 字母 'A' - 索引13
{0x7C,0x12,0x11,0x7C,0x00}, // 01111100,00010010,00010001,01111100 -> 显示A
// 字母 'B' - 索引14
{0x7F,0x49,0x49,0x36,0x00}, // 01111111,01001001,01001001,00110110 -> 显示B
// 字母 'C' - 索引15
{0x3E,0x41,0x41,0x22,0x00}, // 00111110,01000001,01000001,00100010 -> 显示C
// 字母 'D' - 索引16
{0x7F,0x41,0x41,0x3E,0x00}, // 01111111,01000001,01000001,00111110 -> 显示D
// 字母 'E' - 索引17
{0x7F,0x49,0x49,0x41,0x00}, // 01111111,01001001,01001001,01000001 -> 显示E
// 字母 'F' - 索引18
{0x7F,0x09,0x09,0x01,0x00}, // 01111111,00001001,00001001,00000001 -> 显示F
// 字母 'G' - 索引19
{0x3E,0x41,0x49,0x72,0x00}, // 00111110,01000001,01001001,01110010 -> 显示G
// 字母 'H' - 索引20
{0x7F,0x08,0x08,0x7F,0x00}, // 01111111,00001000,00001000,01111111 -> 显示H
// 字母 'I' - 索引21
{0x00,0x7F,0x00,0x00,0x00}, // 00000000,01111111,00000000,00000000,00000000 -> 显示I(竖线)
// 字母 'J' - 索引22
{0x40,0x40,0x3F,0x00,0x00}, // 01000000,01000000,00111111,00000000,00000000 -> 显示J
// 字母 'K' - 索引23
{0x7F,0x08,0x14,0x63,0x00}, // 01111111,00001000,00010100,01100011 -> 显示K
// 字母 'L' - 索引24
{0x7F,0x40,0x40,0x40,0x00}, // 01111111,01000000,01000000,01000000 -> 显示L
// 字母 'M' - 索引25
{0x7F,0x02,0x04,0x02,0x7F}, // 01111111,00000010,00000100,00000010,01111111 -> 显示M
// 字母 'N' - 索引26
{0x7F,0x04,0x08,0x10,0x7F}, // 01111111,00000100,00001000,00010000,01111111 -> 显示N
// 字母 'O' - 索引27
{0x3E,0x41,0x41,0x41,0x3E}, // 00111110,01000001,01000001,01000001,00111110 -> 显示O
// 字母 'P' - 索引28
{0x7F,0x09,0x09,0x06,0x00}, // 01111111,00001001,00001001,00000110,00000000 -> 显示P
// 字母 'Q' - 索引29
{0x3E,0x41,0x51,0x7E,0x00}, // 00111110,01000001,01010001,01111110,00000000 -> 显示Q
// 字母 'R' - 索引30
{0x7F,0x09,0x19,0x66,0x00}, // 01111111,00001001,00011001,01100110,00000000 -> 显示R
// 字母 'S' - 索引31
{0x26,0x49,0x49,0x32,0x00}, // 00100110,01001001,01001001,00110010,00000000 -> 显示S
// 字母 'T' - 索引32
{0x01,0x01,0x7F,0x01,0x01}, // 00000001,00000001,01111111,00000001,00000001 -> 显示T
// 字母 'U' - 索引33
{0x3F,0x40,0x40,0x3F,0x00}, // 00111111,01000000,01000000,00111111,00000000 -> 显示U
// 字母 'V' - 索引34
{0x07,0x18,0x60,0x18,0x07}, // 00000111,00011000,01100000,00011000,00000111 -> 显示V
// 字母 'W' - 索引35
{0x7F,0x20,0x10,0x20,0x7F}, // 01111111,00100000,00010000,00100000,01111111 -> 显示W
// 字母 'X' - 索引36
{0x63,0x14,0x08,0x14,0x63}, // 01100011,00010100,00001000,00010100,01100011 -> 显示X
// 字母 'Y' - 索引37
{0x03,0x0C,0x70,0x0C,0x03}, // 00000011,00001100,01110000,00001100,00000011 -> 显示Y
// 字母 'Z' - 索引38
{0x61,0x51,0x49,0x45,0x43}, // 01100001,01010001,01001001,01000101,01000011 -> 显示Z
{0x00,0x0C,0x12,0x0C,0x00}, // 39: °
//{0x00,0x00,0x10,0x20,0x00} // 40: '
//{0x04,0x00,0x00,0x00,0x00} // 底部
//{0x01,0x02,0x00,0x00,0x00}, // 两个点斜向右下
{0x02,0x01,0x00,0x00,0x00} // 两个点斜向左下
};
u8 uart2_buf[256];
u8 uart2_cnt = 0;
bit uart2_done = 0;
// 函数声明
void GPIO_Init(void);
void delayms(u16 ms);
void Lcd_WriteByte(u8 dat);
void Lcd_WriteCmd(u8 cmd);
void Lcd_WriteData(u8 dat);
void Lcd_SetPos(u8 x, u8 y);
void Lcd_Clear(void);
void Lcd_Init(void);
void LCD_ShowChar(u8 x, u8 y, u8 ch);
void LCD_ShowStr(u8 x, u8 y, u8 *str);
void LCD_ShowNum(u8 x, u8 y, u32 num, u8 len);
void ShowDate(void);
void ShowTime(void);
void UART2_Init(void);
void UART2_ISR(void);
void GPS_Update(void);
void ParseGPGGA(void);
void Local_Time(void);
void ShowAllInfo(void);
void ShowLatLon(void);
u8 ZellerWeek(u8 year, u8 mon, u8 day); // 蔡勒公式计算星期函数声明
//====================================================================
// 蔡勒公式计算星期
// 输入:年(0-99实际为2000-2099),月(1-12),日(1-31)
// 输出:0-6 对应星期日到星期六
//====================================================================
u8 ZellerWeek(u8 year, u8 mon, u8 day)
{
u16 y, m, c;
u16 week;
// 将月份转换为蔡勒公式要求的格式(3月=1,4月=2,...,1月=13,2月=14)
if (mon == 1 || mon == 2) {
m = mon + 12; // 1月=13,2月=14
y = (year - 1 + 2000) % 100; // 年份减1
c = (year - 1 + 2000) / 100; // 世纪数
} else {
m = mon;
y = (year + 2000) % 100;
c = (year + 2000) / 100;
}
// 蔡勒公式:week = (y + y/4 + c/4 - 2*c + 26*(m+1)/10 + day - 1) mod 7
week = (y + y/4 + c/4 - 2*c + (26*(m+1))/10 + day - 1) % 7;
// 转换为0-6,0=星期日,1=星期一,...,6=星期六
return (u8)week;
}
//====================================================================
// LCD 驱动
//====================================================================
void GPIO_Init(void)
{
P3M0 |= 0xF8;
P3M1 &= 0x07;
}
void delayms(u16 ms)
{
u16 i,j;
for(i=ms;i>0;i--)
for(j=1100;j>0;j--);
}
void Lcd_WriteByte(u8 dat)
{
u8 i;
for(i=0;i<8;i++)
{
LCD_CLK=0;
LCD_DAT=(dat&0x80);
dat<<=1;
LCD_CLK=1;
}
}
void Lcd_WriteCmd(u8 cmd) { LCD_RS=0; Lcd_WriteByte(cmd); }
void Lcd_WriteData(u8 dat){ LCD_RS=1; Lcd_WriteByte(dat); }
void Lcd_SetPos(u8 x,u8 y)
{
y = (y+4)%8;
Lcd_WriteCmd(0xB0 | y);
Lcd_WriteCmd(0x00 | (x&0x0F));
Lcd_WriteCmd(0x10 | (x>>4));
}
void Lcd_Clear(void)
{
u8 i,j;
for(i=0;i<8;i++)
{
Lcd_SetPos(0,i);
for(j=0;j<132;j++) Lcd_WriteData(0x00);
}
}
void Lcd_Init(void)
{
LCD_RST=0; delayms(200);
LCD_RST=1; delayms(100);
Lcd_WriteCmd(0xE2); delayms(10);
Lcd_WriteCmd(0xAE);
Lcd_WriteCmd(0xA2);
Lcd_WriteCmd(0xA0);
Lcd_WriteCmd(0xC8);
Lcd_WriteCmd(0x2F);
Lcd_WriteCmd(0x81);
Lcd_WriteCmd(0x20);
Lcd_WriteCmd(0xAF);
}
void LCD_ShowChar(u8 x,u8 y,u8 ch)
{
u8 i,idx=10;
if(ch>='0'&&ch<='9') idx=ch-'0';
else if(ch=='-') idx=11;
else if(ch==':') idx=12;
else if(ch>='A'&&ch<='Z') idx=13+ch-'A';
else if(ch == 0xDF) idx = 39;
else if(ch == 0x27) idx = 40;
else if(ch==' ') idx=10;
Lcd_SetPos(x,y);
for(i=0;i<5;i++) Lcd_WriteData(ASCII[idx][i]);
}
void LCD_ShowStr(u8 x,u8 y,u8 *str)
{
while(*str) { LCD_ShowChar(x,y,*str); x+=6; str++; }
}
void LCD_ShowNum(u8 x, u8 y, u32 num, u8 len)
{
u8 buf[12];
u8 i;
buf[len] = 0;
for(i=len; i>0; i--)
{
buf[i-1] = (num % 10) + '0';
num /= 10;
}
LCD_ShowStr(x, y, buf);
}
void ShowDate(void)
{
u8 buf[16];
buf[0]='2'; buf[1]='0';
buf[2]=(year/10)+'0';
buf[3]=(year%10)+'0';
buf[4]='-';
buf[5]=(mon/10)+'0';
buf[6]=(mon%10)+'0';
buf[7]='-';
buf[8]=(day/10)+'0';
buf[9]=(day%10)+'0';
buf[10]=0;
LCD_ShowStr(10,2,buf);
LCD_ShowStr(76,2,(u8*)week_table[week]);
if(gps_valid)
LCD_ShowStr(100,2,(u8*)"A");
else
LCD_ShowStr(100,2,(u8*)"-");
}
void ShowTime(void)
{
u8 buf[16];
buf[0]=hour/10+'0'; buf[1]=hour%10+'0'; buf[2]=':';
buf[3]=min/10+'0'; buf[4]=min%10+'0'; buf[5]=':';
buf[6]=sec/10+'0'; buf[7]=sec%10+'0'; buf[8]=0;
LCD_ShowStr(20,3,buf);
}
//====================================================================
// 串口2
//====================================================================
void UART2_Init(void)
{
P1M0 &= ~0x02;
P1M1 |= 0x02;
S2CON = 0x50;
AUXR |= 0x04;
T2L = 0x71;
T2H = 0xFC;
AUXR |= 0x10;
IE2 |= 0x01;
EA = 1;
}
void UART2_ISR(void) interrupt 8
{
u8 ch;
if(S2CON & 0x01)
{
S2CON &= ~0x01;
ch = S2BUF;
if(uart2_cnt < sizeof(uart2_buf) - 2)
{
uart2_buf[uart2_cnt++] = ch;
}
// ? 只在遇到换行符时标记一帧
if(ch == '\n')
{
uart2_buf[uart2_cnt] = 0;
uart2_done = 1;
}
}
}
//====================================================================
// GPS解析(更新版,添加经纬度解析)
//====================================================================
void ParseGPGGA(void)
{
u8 *p;
u8 temp_sat = 0;
u16 temp_alt = 0;
u8 cnt = 0;
u8 i;
p = strstr((char*)uart2_buf, "$GPGGA");
if(p == 0)
{
p = strstr((char*)uart2_buf, "$GNGGA");
if(p == 0) return;
}
/* ---------- 卫星数(第7个逗号) ---------- */
cnt = 0;
for(i = 0; p[i] && p[i]!='*'; i++)
{
if(p[i] == ',')
{
cnt++;
if(cnt == 7)
{
i++;
while(p[i] >= '0' && p[i] <= '9')
{
temp_sat = temp_sat * 10 + (p[i] - '0');
i++;
}
satellite = temp_sat; // ? 允许 0
break;
}
}
}
/* ---------- 海拔(第9个逗号) ---------- */
cnt = 0;
for(i = 0; p[i] && p[i]!='*'; i++)
{
if(p[i] == ',')
{
cnt++;
if(cnt == 9)
{
i++;
while((p[i] >= '0' && p[i] <= '9') || p[i] == '.')
{
if(p[i] == '.') { i++; break; }
temp_alt = temp_alt * 10 + (p[i] - '0');
i++;
}
if(temp_alt < 5000)
altitude = temp_alt;
break;
}
}
}
}
void GPS_Update(void)
{
u8 *p;
u8 hh,mm,ss,dd,mmd,yy;
u8 status;
u16 temp_hour;
u8 i;
p = strstr((char*)uart2_buf, "$GPRMC");
if(p == 0) return;
p += 7;
if(p[0] < '0' || p[0] > '9') return;
hh = (p[0] - '0') * 10 + (p[1] - '0');
mm = (p[2] - '0') * 10 + (p[3] - '0');
ss = (p[4] - '0') * 10 + (p[5] - '0');
p = strchr(p, ',');
if(p == 0) return;
p++;
status = *p;
gps_valid = (status == 'A') ? 1 : 0;
if(!gps_valid) return;
// 保存有效的经纬度数据到显示缓冲区
memcpy(gps_display_buf, uart2_buf, uart2_cnt + 1);
gps_data_ready = 1;
// 解析速度
// 跳到第 7 个逗号(速度字段)
for(i=0; i<6; i++)
{
p = strchr(p, ',');
if(p == 0) return;
p++;
}
speed = 0;
while((*p >= '0' && *p <= '9') || *p == '.')
{
if(*p == '.')
{
p++;
break;
}
speed = speed * 10 + (*p - '0');
p++;
}
speed_kmh = speed * 1852 /1000;
// 跳转到日期
for(i=0; i<1; i++) {
p = strchr(p, ',');
if(p == 0) return;
p++;
}
if(p[0] < '0' || p[0] > '9') return;
dd = (p[0] - '0') * 10 + (p[1] - '0');
mmd = (p[2] - '0') * 10 + (p[3] - '0');
yy = (p[4] - '0') * 10 + (p[5] - '0');
temp_hour = hh + 8;
if(temp_hour >= 24)
{
temp_hour -= 24;
dd++;
if(dd > 31) { dd = 1; mmd++; }
if(mmd > 12) { mmd = 1; yy++; }
}
hour = (u8)temp_hour;
min = mm;
sec = ss;
day = dd;
mon = mmd;
year = yy;
// 使用蔡勒公式计算星期
week = ZellerWeek(year, mon, day);
gps_synced = 1;
}
void Local_Time(void)
{
static u16 local_ms = 0;
local_ms++;
if(local_ms >= 1000)
{
local_ms = 0;
if(!gps_valid) // ? 只有在 GPS 无效时才自增
{
sec++;
if(sec >= 60)
{
sec = 0;
min++;
if(min >= 60)
{
min = 0;
hour++;
if(hour >= 24) hour = 0;
}
}
}
}
}
//====================================================================
// 主函数
//====================================================================
// 修改主函数中的while循环部分
void main(void)
{
u8 display_counter = 0;
// 初始化时使用蔡勒公式计算初始星期
week = ZellerWeek(year, mon, day);
GPIO_Init();
UART2_Init();
Lcd_Init();
Lcd_Clear();
delayms(500);
// 初始显示
ShowAllInfo();
while(1)
{
ms_cnt++;
if(ms_cnt >= 20)
{
ms_cnt = 0;
Local_Time();
}
if(uart2_done)
{
if(strstr((char*)uart2_buf, "$GPRMC") ||
strstr((char*)uart2_buf, "$GNRMC"))
{
GPS_Update();
}
if(strstr((char*)uart2_buf, "$GPGGA") ||
strstr((char*)uart2_buf, "$GNGGA"))
{
ParseGPGGA();
}
uart2_cnt = 0;
uart2_done = 0;
}
// 每100ms刷新一次显示
display_counter++;
if(display_counter >= 2) // 约200ms刷新一次
{
display_counter = 0;
ShowAllInfo();
}
if(strstr((char*)uart2_buf, "$GPGGA"))
delayms(100);
}
}
// 新增函数:在一屏内显示所有信息(包含经纬度)
void ShowAllInfo(void)
{
u8 buf[20];
// 第0行:卫星数和GPS状态
buf[0] = 'S';
buf[1] = 'A';
buf[2] = 'T';
buf[3] = ':';
buf[4] = (satellite/10)+'0';
buf[5] = (satellite%10)+'0';
if(gps_valid)
{
buf[6] = 'A';
buf[7] = 0;
}
else
{
buf[6] = '-';
buf[7] = 0;
}
LCD_ShowStr(0, 0, buf);
// 第8行:海拔
buf[0] = 'A';
buf[1] = 'L';
buf[2] = 'T';
buf[3] = ':';
buf[4] = (altitude/1000)+'0';
buf[5] = ((altitude%1000)/100)+'0';
buf[6] = ((altitude%100)/10)+'0';
buf[7] = (altitude%10)+'0';
buf[8] = 'm';
buf[9] = 0;
LCD_ShowStr(0, 7, buf);
// 第2行:速度
buf[0] = 'S';
buf[1] = 'P';
buf[2] = 'D';
buf[3] = ':';
buf[4] = (speed_kmh/100)? (speed_kmh/100)%10+'0' : ' ';
buf[5] = (speed_kmh/10)? (speed_kmh/10)%10+'0' : ' ';
buf[6] = (speed_kmh%10)+'0';
buf[7] = 'K';
buf[8] = 'M';
buf[9] = 'H';
buf[10] = 0;
LCD_ShowStr(0, 2, buf);
// 第8行:日期
buf[0]='2'; buf[1]='0';
buf[2]=(year/10)+'0';
buf[3]=(year%10)+'0';
buf[4]='-';
buf[5]=(mon/10)+'0';
buf[6]=(mon%10)+'0';
buf[7]='-';
buf[8]=(day/10)+'0';
buf[9]=(day%10)+'0';
buf[10]=0;
LCD_ShowStr(55, 7, buf);
// 星期
LCD_ShowStr(116, 7, (u8*)week_table[week]);
// 第4行:时间
buf[0]=hour/10+'0'; buf[1]=hour%10+'0'; buf[2]=':';
buf[3]=min/10+'0'; buf[4]=min%10+'0'; buf[5]=':';
buf[6]=sec/10+'0'; buf[7]=sec%10+'0'; buf[8]=0;
LCD_ShowStr(40, 5, buf);
// 第5-6行:经纬度
ShowLatLon();
}
// 新增:显示经纬度函数
void ShowLatLon(void)
{
u8 buf[30];
u8 *p;
u8 lat_deg, lat_min;
u8 lon_deg, lon_min;
u8 lat_dir_char, lon_dir_char; // 所有变量声明在开头
if(gps_data_ready && gps_valid)
{
p = strstr((char*)gps_display_buf, "$GPRMC");
if(p)
{
p += 7;
p = strchr(p, ','); if(p) p++;
p = strchr(p, ','); if(p) p++;
if(p && p[0] >= '0' && p[0] <= '9')
{
lat_deg = (p[0] - '0') * 10 + (p[1] - '0');
lat_min = (p[2] - '0') * 10 + (p[3] - '0');
p = strchr(p, ',');
if(p) p++;
lat_dir_char = *p;
// 手动拼接纬度字符串
buf[0] = lat_dir_char;
buf[1] = lat_deg/10 + '0';
buf[2] = lat_deg%10 + '0';
buf[3] = 0xDF;
buf[4] = lat_min/10 + '0';
buf[5] = lat_min%10 + '0';
buf[6] = 0x27;
buf[7] = 0;
LCD_ShowStr(45, 0, buf);
// 跳转到经度
p = strchr(p, ',');
if(p) p++;
if(p && p[0] >= '0' && p[0] <= '9')
{
lon_deg = (p[0] - '0') * 100 + (p[1] - '0') * 10 + (p[2] - '0');
lon_min = (p[3] - '0') * 10 + (p[4] - '0');
p = strchr(p, ',');
if(p) p++;
lon_dir_char = *p;
// 手动拼接经度字符串(包含分钟符号)
// buf[0] = 'L'; buf[1] = 'O'; buf[2] = 'N'; buf[3] = ':';
buf[1] = lon_deg/100 + '0';
buf[2] = (lon_deg%100)/10 + '0';
buf[3] = lon_deg%10 + '0';
buf[4] = 0xDF;
buf[5] = lon_min/10 + '0';
buf[6] = lon_min%10 + '0';
buf[7] = 0x27; // 分钟符号
buf[0] = lon_dir_char; // 方向字母
buf[8] = 0; // 结束符
LCD_ShowStr(88, 0, buf);
}
}
}
}
else
{
// LCD_ShowStr(0, 5, (u8*)"LAT:--- --' ");
// LCD_ShowStr(0, 6, (u8*)"LON:--- --' ");
}
}
|
-
|