1.简介
本案例使用gps模块获取gps数据,同时将数据显示在oled屏幕上。gps模块每一秒会通过串口发送数据包,通过haas506上的串口接收并处理该数据,获得经纬度、UTC时间等信息。
注意:由于haas506只有一个TTL串口,而TTL又被作为下载口和默认的repl口,所以在board.json中需要重定向repl口以及在下载程序的时候不能将gps模块pin脚接入到串口上。本案例将repl口重定向到RS232。
串口接收到的数据,若是完整的包如下所示:
bytearray(b'
$GNGGA,052538.119,,,,,0,00,25.5,,,,,,*7A\r\n
$GNGLL,,,,,052538.119,V,N*64\r\n
$GNGSA,A,1,,,,,,,,,,,,,25.5,25.5,25.5,1*01\r\n
$GNGSA,A,1,,,,,,,,,,,,,25.5,25.5,25.5,4*04\r\n
$GPGSV,3,1,09,07,,,23,08,,,31,10,,,20,14,,,21,0*67\r\n
$GPGSV,3,2,09,21,,,25,27,,,26,30,,,24,34,,,24,0*6C\r\n
$GPGSV,3,3,09,35,,,24,0*6C\r\n
$BDGSV,1,1,00,0*74\r\n
$GNRMC,075622.000,A,3116.56922,N,12044.12475,E,0.00,0.00,020422,,,A,V*01\r\n
$GNVTG,,,,,,,,,N*2E\r\n
$GNZDA,052538.119,,,,,*48\r\n
$GPTXT,01,01,01,ANTENNA OPEN*25\r\n')
然而无法获得完整的数据包,所以在代码中针对该情况修改了一下代码,即判断$GNRMC是否存在,
若存在就将$GNRMC所在的一行数据去除","变为列表。变为列表后,判断该列表的长度是否为14,
若是,则从该列表中取出经纬度、utc时间等信息。
----------------------------------------------------------------------
#gps相关信息可以从$GNRMC中获取,例如:
$GNRMC,075622.000,A,3116.56922,N,12044.12475,E,0.00,0.00,020422,,,A,V*01
# 字段0---语句的id:$GPRMC :,推荐最小定位信息
# 字段1---UTC时间: 格式为hhmmss.ssss(时分秒.毫秒)
# 字段2---状态:A=定位 ,V=未定位
# 字段3---纬度: 格式ddmm.mmmmm若前导数不足,则用0填充
# 字段4---纬度: N(北纬)/S(南纬)
# 字段5---经度 :格式dddmm.mmmmm 若前导数不足,则用0填充
# 字段6---经度: E(东经)/W(西经)
# 字段7---速度,节,knots 1.852KM/h
# 字段8---方位角,度 (二位方向,等效于二位罗盘)
# 字段9---UTC日期,DDMMYY格式(天月年)
# 字段10---磁偏角(000-180)度 若前导数不足,则用0填充
# 字段11---磁偏角方向 E=东,W=西
# 字段12---模式:A=自动,D=差分,E=估计,AND=无效数据
# 字段13---校验值 ,"*"是校验和识别符,后面的两位数为校验和
# 十进制北纬度数 = dd + mm.mmmmm/60;
# 十进制南纬度数 = -(dd + mm.mmmmm/60);
# 十进制东经度数 = ddd + mm.mmmmm/60;
# 十进制西经度数 = -(ddd + mm.mmmmm/60);
2.硬件
- 硬件模块
gps模块:中科微-ATGM336H-5N系列模块
oled模块:尺寸为128x64


- 硬件接线图

3.测试代码
- main.py
from driver import UART
import utime as time
from ssd1306 import SSD1306_128_64
#数据显示
def disInfo(msg1,msg2):
#显示字符串 6*8(宽x高)点阵
#两行数据,分别为latitude,longitude
disp.oled_showStr(0,0,msg1,1)
disp.oled_showStr(0,1,msg2,1)
#时间
def utcDateTime(dateStr, timeStr, timeZone=8):
if dateStr == '' or timeStr == '':
return None
day = dateStr[0:2]
month = dateStr[2:4]
year = dateStr[4:6]
hr = int(timeStr[0:2])+timeZone
if hr>=24:
hr=str(hr-24)
else:
hr=str(hr)
min = timeStr[2:4]
sec = timeStr[4:6]
t ="20"+year+"-"+month+"-"+day+" "+hr+":"+min+":"+sec
return t
#纬度
def latitude(d,h):
if d=="":
return 0
hemi="" if h=="N" else "-"
#度
deg=int(d[0:2])
#分
min=str(float(d[2:])/60)[1:]
return hemi +str(deg)+min
#经度
def longitude(d,h):
if d=="":
return 0
hemi="" if h=="E" else "-"
#度
deg=int(d[0:3])
#分
min=str(float(d[3:])/60)[1:]
return hemi +str(deg)+min
def main():
while True:
#串口读
size=gps_module.read(readBuf)
data=readBuf
#将字节数据转化成字符串数据
data_str=data.decode()
# 判断是否有数据 且数据中是否包含"$GNRMC"
if size!=0 and "$GNRMC" in data_str and "$GNVTG" in data_str:
print(readBuf)
print("------------0-----------------------")
#删除"\r\n"后,字符串变为列表
data_list=data_str.split('\r\n')
print(data_list)
print("------------------1------------------")
for i in range(len(data_list)):
if "$GNRMC" in data_list[i]:
print(data_list[i])
#删除","
result=data_list[i].split(',')
#$GNRMC,075622.000,A,3116.56922,N,12044.12475,E,0.00,0.00,020422,,,A,V*01
#['$GNRMC', '075622.000', 'A', '3116.56922', 'N', '12044.12475', 'E', '0.00', '0.00', '020422', '', '', 'A', 'V*01']
# 在GNRMC中取数据
if len(result)==14:
lat=latitude(result[3],result[4])
long=longitude(result[5],result[6])
tim=utcDateTime(result[9],result[1][:6],8)
print("lat:",lat)
print("long:",long)
print(tim)
#显示屏显示数据
disInfo("lat:"+str(lat)[:9],"lon:"+str(long)[:10])
print("---------------------2-------------------")
time.sleep(1)
if __name__=="__main__":
#创建一个串口实例
gps_module=UART()
#打开
gps_module.open("serial1")
#波特率可以在board.json中设置,也可以在代码中设置
#gps模块的波特率是9600
gps_module.setBaudRate(9600)
# 创建一个字节数组,用于接受串口数据
readBuf=bytearray(512)
#创建一个oled实例
disp=SSD1306_128_64()
# 初始化
disp.begin()
#清屏
disp.clear()
#主函数
main()
- ssd1306.py
import utime as time
import codetab
# Constants
SSD1306_I2C_ADDRESS = 0x3C # 011110+SA0+RW - 0x3C or 0x3D
SSD1306_SETCONTRAST = 0x81
SSD1306_DISPLAYALLON_RESUME = 0xA4
SSD1306_DISPLAYALLON = 0xA5
SSD1306_NORMALDISPLAY = 0xA6
SSD1306_INVERTDISPLAY = 0xA7
SSD1306_DISPLAYOFF = 0xAE
SSD1306_DISPLAYON = 0xAF
SSD1306_SETDISPLAYOFFSET = 0xD3
SSD1306_SETCOMPINS = 0xDA
SSD1306_SETVCOMDETECT = 0xDB
SSD1306_SETDISPLAYCLOCKDIV = 0xD5
SSD1306_SETPRECHARGE = 0xD9
SSD1306_SETMULTIPLEX = 0xA8
SSD1306_SETLOWCOLUMN = 0x00
SSD1306_SETHIGHCOLUMN = 0x10
SSD1306_SETSTARTLINE = 0x40
SSD1306_MEMORYMODE = 0x20
SSD1306_COLUMNADDR = 0x21
SSD1306_PAGEADDR = 0x22
SSD1306_COMSCANINC = 0xC0
SSD1306_COMSCANDEC = 0xC8
SSD1306_SEGREMAP = 0xA0
SSD1306_CHARGEPUMP = 0x8D
SSD1306_EXTERNALVCC = 0x1
SSD1306_SWITCHCAPVCC = 0x2
# Scrolling constants
SSD1306_ACTIVATE_SCROLL = 0x2F
SSD1306_DEACTIVATE_SCROLL = 0x2E
SSD1306_SET_VERTICAL_SCROLL_AREA = 0xA3
SSD1306_RIGHT_HORIZONTAL_SCROLL = 0x26
SSD1306_LEFT_HORIZONTAL_SCROLL = 0x27
SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = 0x29
SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = 0x2A
class SSD1306Base(object):
def __init__(self, width, height):
self.width = width
self.height = height
self._pages = height//8
self._buffer = [0]*(width*self._pages)
# Handle hardware I2C
from driver import I2C
self._i2c=I2C()
self._i2c.open('OLED')
def _initialize(self):
raise NotImplementedError
def writeCmd(self, command):
"""Send command byte to display."""
# I2C write.
control = 0x00 # Co = 0, DC = 0
writeBuf=bytearray(2)
writeBuf[0]=control
writeBuf[1]=command
self._i2c.write(writeBuf,2)
def writeDat(self, data):
"""Send byte of data to display."""
# I2C write.
control = 0x40 # Co = 0, DC = 0
writeBuf=bytearray(2)
writeBuf[0]=control
writeBuf[1]=data
self._i2c.write(writeBuf,2)
def begin(self, vccstate=SSD1306_SWITCHCAPVCC):
"""Initialize display."""
# Save vcc state.
self._vccstate = vccstate
# Reset and initialize display.
# self.reset()
self._initialize()
# Turn on the display.
self.writeCmd(SSD1306_DISPLAYON)
# --------------------------------------------------------------
# Prototype : oled_fill(fill_data)
# Parameters : fill_data,范围0x00-0xff
# Description : 全屏填充,例如 0x00-全黑,0xff全亮
# --------------------------------------------------------------
def oled_fill(self,fill_data):
for i in range(8):
#page0-page1
self.writeCmd(0xb0+i)
# low colum start address
self.writeCmd(0x00)
#high colum start address
self.writeCmd(0x10)
for i in range(128*64):
self.writeDat(fill_data)
# --------------------------------------------------------------
# Prototype : clear()
# Parameters : none
# Description : 全黑
# --------------------------------------------------------------
def clear(self):
self.oled_fill(0x00)
# --------------------------------------------------------------
# Prototype : oled_setPos(x,y)
# Parameters : x,y -- 起始点坐标(x:0~127, y:0~7)
# Description : 设置起始坐标
# --------------------------------------------------------------
def oled_setPos(self,x,y):
self.writeCmd(0xb0+y)
self.writeCmd(((x&0xf0)>>4)|0x10)
self.writeCmd((x&0x0f)|0x01)
# --------------------------------------------------------------
# Prototype : oled_showCN(x,y,n)
# Parameters : x,y -- 起始点坐标(x:0~127, y:0~7); N:汉字在codetab.h中的索引
# Description : 显示codetab.py中的汉字,16*16点阵
# --------------------------------------------------------------
def oled_showCN(self,x,y,n):
self.oled_setPos(x,y)
adder=32*n
for i in range(16):
self.writeDat(codetab.F1[adder])
adder+=1
self.oled_setPos(x,y+1)
for i in range(16):
self.writeDat(codetab.F1[adder])
adder+=1
# --------------------------------------------------------------
# Prototype : oled_showStr(x,y,ch,TextSize)
# Parameters : x,y -- 起始点坐标(x:0~127, y:0~7); ch[] -- 要显示的字符串; TextSize -- 字符大小(1:6*8 ; 2:8*16)
# Description : 显示codetab.py中的ASCII字符,有6*8和8*16可选择
# --------------------------------------------------------------
def oled_showStr(self,x,y,ch,TextSize):
c=0
j=0
if TextSize==1:
while ch[j]!='\0':
#ord()将字符转换成十进制,如'a'->97
c=ord(ch[j])-32
if x>126:
x=0
y+=1
self.oled_setPos(x,y)
for i in range(6):
self.writeDat(codetab.F6x8[c][i])
x+=6
j+=1
#防止index out of range
if j==len(ch):
break
if TextSize==2:
while ch[j]!='\0':
#ord()将字符转换成十进制,如'a'->97
c=ord(ch[j])-32
if x>120:
x=0
y+=1
self.oled_setPos(x,y)
for i in range(8):
self.writeDat(codetab.F8X16[c*16+i])
self.oled_setPos(x,y+1)
for i in range(8):
self.writeDat(codetab.F8X16[c*16+i+8])
x+=8
j+=1
#防止index out of range
if j==len(ch):
break
# --------------------------------------------------------------
# Prototype : oled_showPicture(x0,y0,x1,y1,BMP)
# Parameters : x0,y0 -- 起始点坐标(x0:0~127, y0:0~7); x1,y1 -- 起点对角线(结束点)的坐标(x1:1~128,y1:128)
# Description : 显示BMP位图
# --------------------------------------------------------------
def oled_showPicture(self,x0,y0,x1,y1,BMP):
i=0
if y1%8==0:
y=y1/8
else:
y=y1/8+1
for y in range(y0,y1):
self.oled_setPos(x0,y)
for x in range(x0,x1):
self.writeDat(BMP[i])
i+=1
if i==len(BMP) :
break
# --------------------------------------------------------------
# Prototype : set_contrast(contrast)
# Parameters : coontrast,取值范围为0-255
# Description : 对比度/亮度调节
# --------------------------------------------------------------
def set_contrast(self, contrast):
if contrast < 0 or contrast > 255:
raise ValueError('Contrast must be a value from 0 to 255 (inclusive).')
self.writeCmd(SSD1306_SETCONTRAST)
self.writeCmd(contrast)
class SSD1306_128_64(SSD1306Base):
def __init__(self):
super(SSD1306_128_64, self).__init__(128, 64)
def _initialize(self):
# 128x64 pixel specific initialization.
self.writeCmd(SSD1306_DISPLAYOFF) # 0xAE
self.writeCmd(SSD1306_SETDISPLAYCLOCKDIV) # 0xD5
self.writeCmd(0x80) # the suggested ratio 0x80
self.writeCmd(SSD1306_SETMULTIPLEX) # 0xA8
self.writeCmd(0x3F)
self.writeCmd(SSD1306_SETDISPLAYOFFSET) # 0xD3
self.writeCmd(0x0) # no offset
self.writeCmd(SSD1306_SETSTARTLINE | 0x0) # line #0
self.writeCmd(SSD1306_CHARGEPUMP) # 0x8D
if self._vccstate == SSD1306_EXTERNALVCC:
self.writeCmd(0x10)
else:
self.writeCmd(0x14)
self.writeCmd(SSD1306_MEMORYMODE) # 0x20
self.writeCmd(0x00) # 0x0 act like ks0108
self.writeCmd(SSD1306_SEGREMAP | 0x1)
self.writeCmd(SSD1306_COMSCANDEC)
self.writeCmd(SSD1306_SETCOMPINS) # 0xDA
self.writeCmd(0x12)
self.writeCmd(SSD1306_SETCONTRAST) # 0x81
if self._vccstate == SSD1306_EXTERNALVCC:
self.writeCmd(0x9F)
else:
self.writeCmd(0xCF)
self.writeCmd(SSD1306_SETPRECHARGE) # 0xd9
if self._vccstate == SSD1306_EXTERNALVCC:
self.writeCmd(0x22)
else:
self.writeCmd(0xF1)
self.writeCmd(SSD1306_SETVCOMDETECT) # 0xDB
self.writeCmd(0x40)
self.writeCmd(SSD1306_DISPLAYALLON_RESUME) # 0xA4
self.writeCmd(SSD1306_NORMALDISPLAY) # 0xA6
class SSD1306_128_32(SSD1306Base):
def __init__(self):
super(SSD1306_128_32, self).__init__(128, 32)
def _initialize(self):
self.command(SSD1306_DISPLAYOFF) # 0xAE
self.command(SSD1306_SETDISPLAYCLOCKDIV) # 0xD5
self.command(0x80) # the suggested ratio 0x80
self.command(SSD1306_SETMULTIPLEX) # 0xA8
self.command(0x1F)
self.command(SSD1306_SETDISPLAYOFFSET) # 0xD3
self.command(0x0) # no offset
self.command(SSD1306_SETSTARTLINE | 0x0) # line #0
self.command(SSD1306_CHARGEPUMP) # 0x8D
if self._vccstate == SSD1306_EXTERNALVCC:
self.command(0x10)
else:
self.command(0x14)
self.command(SSD1306_MEMORYMODE) # 0x20
self.command(0x00) # 0x0 act like ks0108
self.command(SSD1306_SEGREMAP | 0x1)
self.command(SSD1306_COMSCANDEC)
self.command(SSD1306_SETCOMPINS) # 0xDA
self.command(0x02)
self.command(SSD1306_SETCONTRAST) # 0x81
self.command(0x8F)
self.command(SSD1306_SETPRECHARGE) # 0xd9
if self._vccstate == SSD1306_EXTERNALVCC:
self.command(0x22)
else:
self.command(0xF1)
self.command(SSD1306_SETVCOMDETECT) # 0xDB
self.command(0x40)
self.command(SSD1306_DISPLAYALLON_RESUME) # 0xA4
self.command(SSD1306_NORMALDISPLAY) # 0xA6
class SSD1306_96_16(SSD1306Base):
def __init__(self):
super(SSD1306_96_16, self).__init__(96, 16)
def _initialize(self):
self.command(SSD1306_DISPLAYOFF) # 0xAE
self.command(SSD1306_SETDISPLAYCLOCKDIV) # 0xD5
self.command(0x60) # the suggested ratio 0x60
self.command(SSD1306_SETMULTIPLEX) # 0xA8
self.command(0x0F)
self.command(SSD1306_SETDISPLAYOFFSET) # 0xD3
self.command(0x0) # no offset
self.command(SSD1306_SETSTARTLINE | 0x0) # line #0
self.command(SSD1306_CHARGEPUMP) # 0x8D
if self._vccstate == SSD1306_EXTERNALVCC:
self.command(0x10)
else:
self.command(0x14)
self.command(SSD1306_MEMORYMODE) # 0x20
self.command(0x00) # 0x0 act like ks0108
self.command(SSD1306_SEGREMAP | 0x1)
self.command(SSD1306_COMSCANDEC)
self.command(SSD1306_SETCOMPINS) # 0xDA
self.command(0x02)
self.command(SSD1306_SETCONTRAST) # 0x81
self.command(0x8F)
self.command(SSD1306_SETPRECHARGE) # 0xd9
if self._vccstate == SSD1306_EXTERNALVCC:
self.command(

项目采用ATGM336H-5N GPS模块获取定位信息,并通过Haas506板载TTL串口传送至OLED屏幕显示。代码实现筛选$GNRMC数据包,解析经纬度、UTC时间等关键信息,确保即使数据包不完整也能正确显示。
最低0.47元/天 解锁文章
16

被折叠的 条评论
为什么被折叠?



