一,概述

GNSS芯片选用了ublox的 UBX-M8030 系列,有3个型号:

可以到官网去下载相关资料,文档还挺齐的:
https://www.u-blox.com/zh/product/ubx-m8030-series#tab-product-selection
比较重要的几个文档有:

ReceiverDescrProtSpec 芯片通讯协议配置相关,软件开发较关心
DataSheet 不用说了吧
HardwareIntegrationManual 硬件设计详解
PowerManagement_AppNote 电源及低功耗
GNSS-Antennas_AppNote 天线相关

把选型相关和我比较关心的一些特性列一下:
最多可并发接收 3 个 GNSS(GPS、伽利略、GLONASS、北斗)
定位精度2米
-148 dBm 到 -167 dBm 灵敏度
冷启动26秒定位时间,热启动只要1.5s
1个串口,默认9600,8n1,本项目只用这个通讯口,其它的USB, I2C, SPI不关心
尺寸封装:QFN, 5x5
支持低电压,支持低功耗模式(这个对我们很重要)

简单来说,如果硬件没问题,天线调试可以,那样芯片会自动每秒发送一次信息,如果已定位成功,就有位置信息了,软件只要解析报文就可以。状态机如下图,acquisition我们叫搜星,tracking就是跟踪。

Ublox还专门提供了一个调试用的软件,叫u-center,官网可下载。安装到PC上,通过串口与芯片通讯,分析芯片发过来的位置信息报文,图文展示卫星等信息,也支持下发配置,非常方便。

二,硬件

先说明,本人软件工程师,硬件水平渣。如果之前没有研发过GPS类芯片的硬件工程师可能要好好把文档看懂才动手,尤其是天线部分,后面会专门分析该部分。芯片主体框图如下:

我们项目中原理图如下(天线部分后面提供):

2.1 电源

上图把几个重要的外围接口都列了出来,芯片有几个供电pin 脚,硬件要怎么供电取决于你的需求,上图的Main Supply实际上有两路电源输入,V_CORE, 和 V_DCDC,几个电源接口有着不同的功能。
V_BCKP,给RTC,RAM供电,用来保存星历,下次定位更快,功耗只有15uA
V_CORE, 搜星时功耗,只有GPS时 33.6 mA, GNSS 43.2 mA
V_DCDC_IN, 只有GPS时18.3 mA, GNSS 24.2mA
VDD_IO,通讯pin 脚供电

详细功能看官方文档,这里只截个图:

2.2 低功耗

低功耗与性能是个矛盾关系,各人根据需求去配置,例如硬件上有以下做法可以省电:
主电源1.4v供电
使用晶振(Crystal)而不是TCXO
用UART and DDC接口通讯,而不是USB, SPI
不使用SQI Flash(ROM only version)
无源天线(性能很受影响)

在软件上芯片有两种工作模式,连续模式(Continuous Mode)和省电模式(Power Save Mode,PSM)。连续模式就是使用全部性能,全部通道去搜星,星历都下载OK后进入跟踪引擎,这时功耗会降低。

如上图最开始阶段,芯片(acquisition engine)尽最大能力搜星,星历下载完成,得到初始位置信息后,芯片关闭acquisition engine进入跟踪状态。检测到新的卫星信号又重新启动acquisition engine,也就是上图的fix阶段。很明显,要省电最好是加长”Update Period”的时间,也就是说省电模式只在跟踪阶段。
省电模式又分两种模式:”Cyclic tracking” 和 “ON/OFF operation”,从下图可以看出,只有设置”Update Period”超过10s “ON/OFF operation”才有省电优势,这也会影响到性能,看用户怎么平衡了。

要想进入低功耗模式,要满足以下条件:
不要使用USB接口
有RTC,或者使用"single crystal"模式
使用GPS-only 模式

芯片配置步骤:
1. disable Glonass, 只要GPS。 UBX-CFG-GNSS
2. UBX-CFG-PM2 或者 UBX-CFG-PMS,一般推荐使用Cyclic tracking
3. UBX-CFG-RXM, set the power mode to "1 - Power Save Mode"

2.3 时钟

从前面系统框图可知,这个芯片有两路时钟输入,分别是TCXO(或晶振)和RTC时钟,参考框图如下:

主时钟是一定要有的,而且对搜星性能有影响,温漂要控制在范围内。可以根据需求,成本,性能分析是使用TCXO还是晶振,频率是26 MHz,” HardwareIntegrationManual”文档会有专门的分析,这个文档很细,把外围电路各种器件的参考物料都列了出来,买文档推荐的物料肯定没错了。
主时钟是供信号采集用的,RTC是用来备份和热保持,当芯片主电源意外掉电,芯片热启动时会用到,频率是32.768 kHz。芯片也支持单晶振(Single Crystal)模式,就是主晶振也可以作RTC用,这需要配置芯片。这样V_BCKP电源也会给主晶振供电,主电源掉的时候主晶振依然能工作。这么做能省物料成本,但是增加了功耗。

2.4 IO配置

UBX-M8030芯片有17个IO口,从PIO0到PIO16,都是由VDD_IO供电,而且电平也跟它一致。PIO0到PIO5可以连SQI Flash,SQI Flash的作用是升级芯片固件,保存配置,保存log,保留辅助定位信息。这几个pin脚也可以通过配置作为LDO输出的功能。我们项目没用SQI Flash,也没用LDO输出,所以都悬空了。

三,天线

天线部分我们硬件参考了” HardwareIntegrationManual”文档里面的最佳性能电路,如下图:

我们的原理图:

如上图所示,最左边BWGNSCNX16-6W是天线座,U11的MAX2659是一个LNA(Low Noise Amplifier,低噪声放大器)芯片,用来放大信号。U9的SAFEB1G57KE0F00R15是一个声表面波滤波器(SAW),用来滤波,特别是我们的项目中用到了lte cat.1芯片,怕信号会干扰。经过这些简单处理后信号被送到芯片,整个过程如下图:

要求硬件把外围电路处理好,然后重点就是天线的调试了。Ublox有专门文档介绍天线相关知识(GNSS-Antennas_AppNote),建议先看看。其实如果硬件工程师射频相关经验不多的话,最好是找天线厂帮忙,我们就因为这个浪费了一,两星期的时间。GPS的信号很弱,-100dBm以上的,天线比2.4G,433M之类的天线要难搞,还是要请专业人士保险。本人这一块也薄弱,只提供几个建议:
天线分陶瓷天线和FPC天线,增益强度,指向,体积都不一样,看产品需求选用。
天线对PCB有要求,例如净空,铺地,阻抗之类的,最好先让射频工程师评估。
串口输出的报文要能看懂,u-center最好会使用,能了解和推算出信号强弱。
产品外壳也很重要,天线的位置会有关系
最好还是有专业人员参与

四,u-center

u-center 软件的功能非常强大,我只使用了其中小部分功能,主界面如下:

上图左边部分用来查看串口输出数据流,配置界面等,这些界面都可以通过菜单栏的view子菜单打开。右边会把数据解析后的实时结果展示出来,包括当前搜到的星,信号强度,如果定位到的话会有位置,海拔,速度等信息。几个view的作用:
Text console 打印串口上报的报文,如上图左边部分。
Messages View 上报的报文通过NMEA协议解析出来,是字符格式,可以直接查看。下发配置报文一般用UBX协议,是二进制格式,而且有窗口显示要发送的报文内容。因为我下发的配置都是固定的,我一般在这里把要配置的内容测试OK,然后直接把二进制内容复制到代码去,省去组织报文的代码,特别是检验码是要计算出来的。

另外还有configuration view 和 statistic view用得多一点。建议调试时先把硬件调OK了再写功能代码。

五,软件

直接贴别人开源项目的代码,侵删:

ublox.h

/*Andrea ToscanoU-BLOX NEO M8M Parser
*/#ifndef UBLOX_H_INCLUDED
#define UBLOX_H_INCLUDED#include <Arduino.h>#include <stdint.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>class Ublox
{
public:class Tokeniser{public:Tokeniser(char* str, char token);bool next(char* out, int len);private:char* str;char token;};struct satellite{uint8_t prn;int16_t elevation;int16_t azimuth;uint8_t snr; //signal to noise ratio};struct _datetime{uint8_t day, month, year;uint8_t hours, minutes, seconds;uint16_t millis;bool valid; //1 = yes, 0 = no};enum _fixtype { FIX_TYPE_NONE, FIX_TYPE_GPS, FIX_TYPE_DIFF };enum _fix { FIX_NONE = 1, FIX_2D, FIX_3D };enum _op_mode { MODE_MANUAL, MODE_AUTOMATIC };bool encode(char c);float latitude, longitude, altitude, vert_speed;int latlng_age, alt_age;//these units are in hundredths//so a speed of 5260 means 52.60km/huint16_t speed, course, knots;int speed_age, course_age, knots_age;_fixtype fixtype; //0 = no fix, 1 = satellite only, 2 = differential fixint fixtype_age;_fix fix;int fix_age;float pdop, hdop, vdop; //positional, horizontal and vertical dilution of precisionint dop_age;int8_t sats_in_use;int8_t sats_in_view;satellite sats[12];int sats_age;_datetime datetime;int time_age, date_age;_op_mode op_mode;private:bool check_checksum();uint8_t parse_hex(char h);bool process_buf();char buf[120];uint8_t pos;void read_gga();void read_gsa();void read_gsv();void read_rmc();void read_vtg();};//extern Ublox gps;#endif // UBLOX_H_INCLUDED

ublox.c

#include "Ublox.h"Ublox::Tokeniser::Tokeniser(char* _str, char _token)
{str = _str;token = _token;
}bool Ublox::Tokeniser::next(char* out, int len)
{uint8_t count = 0;if(str[0] == 0)return false;while(true){if(str[count] == '\0'){out[count] = '\0';str = &str[count];return true;}if(str[count] == token){out[count] = '\0';count++;str = &str[count];return true;}if(count < len)out[count] = str[count];count++;}return false;
}bool Ublox::encode(char c)
{buf[pos] = c;pos++;if(c == '\n') //linefeed{bool ret = process_buf();memset(buf, '\0', 120);pos = 0;return ret;}if(pos >= 120) //avoid a buffer overrun{memset(buf, '\0', 120);pos = 0;}return false;
}bool Ublox::process_buf()
{if(!check_checksum()) //if checksum is bad{return false; //return}//otherwise, what sort of message is itif(strncmp(buf, "$GNGGA", 6) == 0){read_gga();}if(strncmp(buf, "$GNGSA", 6) == 0){read_gsa();}if(strncmp(buf, "$GPGSV", 6) == 0){read_gsv();}if(strncmp(buf, "$GNRMC", 6) == 0){read_rmc();}if(strncmp(buf, "$GNVTG", 6) == 0){read_vtg();}return true;
}// GNGGA
void Ublox::read_gga()
{int counter = 0;char token[20];Tokeniser tok(buf, ',');while(tok.next(token, 20)){switch(counter){case 1: //time{float time = atof(token);int hms = int(time);datetime.millis = time - hms;datetime.seconds = fmod(hms, 100);hms /= 100;datetime.minutes = fmod(hms, 100);hms /= 100;datetime.hours = hms;time_age = millis();}break;case 2: //latitude{float llat = atof(token);int ilat = llat/100;double mins = fmod(llat, 100);latitude = ilat + (mins/60);}break;case 3: //north/south{if(token[0] == 'S')latitude = -latitude;}break;case 4: //longitude{float llong = atof(token);int ilat = llong/100;double mins = fmod(llong, 100);longitude = ilat + (mins/60);}break;case 5: //east/west{if(token[0] == 'W')longitude = -longitude;latlng_age = millis();}break;case 6:{fixtype = _fixtype(atoi(token));}break;case 7:{sats_in_use = atoi(token);}break;case 8:{hdop = atoi(token);}break;case 9:{float new_alt = atof(token);vert_speed = (new_alt - altitude)/((millis()-alt_age)/1000.0);altitude = atof(token);alt_age = millis();}break;}counter++;}
}void Ublox::read_gsa()
{int counter = 0;char token[20];Tokeniser tok(buf, ',');while(tok.next(token, 20)){switch(counter){case 1: //operating mode{if(token[0] == 'A')op_mode = MODE_AUTOMATIC;if(token[0] == 'M')op_mode = MODE_MANUAL;}break;case 2:{fix = _fix(atoi(token));fix_age = millis();}break;case 14:{pdop = atof(token);}break;case 15:{hdop = atof(token);}break;case 16:{vdop = atof(token);dop_age = millis();}break;}counter++;}
}void Ublox::read_gsv()
{char token[20];Tokeniser tok(buf, ',');tok.next(token, 20);tok.next(token, 20);tok.next(token, 20);int mn = atoi(token); //msg numbertok.next(token, 20);sats_in_view = atoi(token); //number of satsint8_t j = (mn-1) * 4;int8_t i;for(i = 0; i <= 3; i++){tok.next(token, 20);sats[j+i].prn = atoi(token);tok.next(token, 20);sats[j+i].elevation = atoi(token);tok.next(token, 20);sats[j+i].azimuth = atoi(token);tok.next(token, 20);sats[j+i].snr = atoi(token);}sats_age = millis();
}void Ublox::read_rmc()
{int counter = 0;char token[20];Tokeniser tok(buf, ',');while(tok.next(token, 20)){switch(counter){case 1: //time{float time = atof(token);int hms = int(time);datetime.millis = time - hms;datetime.seconds = fmod(hms, 100);hms /= 100;datetime.minutes = fmod(hms, 100);hms /= 100;datetime.hours = hms;time_age = millis();}break;case 2:{if(token[0] == 'A')datetime.valid = true;if(token[0] == 'V')datetime.valid = false;}break;/*case 3:{float llat = atof(token);int ilat = llat/100;double latmins = fmod(llat, 100);latitude = ilat + (latmins/60);}break;case 4:{if(token[0] == 'S')latitude = -latitude;}break;case 5:{float llong = atof(token);float ilat = llong/100;double lonmins = fmod(llong, 100);longitude = ilat + (lonmins/60);}break;case 6:{if(token[0] == 'W')longitude = -longitude;latlng_age = millis();}break;*/case 8:{course = atof(token);course_age = millis();}break;case 9:{uint32_t date = atoi(token);datetime.year = fmod(date, 100);date /= 100;datetime.month = fmod(date, 100);datetime.day = date / 100;date_age = millis();}break;}counter++;}
}void Ublox::read_vtg()
{int counter = 0;char token[20];Tokeniser tok(buf, ',');while(tok.next(token, 20)){switch(counter){case 1:{course = (atof(token)*100);course_age = millis();}break;case 5:{knots = (atof(token)*100);knots_age = millis();}break;case 7:{speed = (atof(token)*100);speed_age = millis();}break;}counter++;}
}bool Ublox::check_checksum()
{if (buf[strlen(buf)-5] == '*'){uint16_t sum = parse_hex(buf[strlen(buf)-4]) * 16;sum += parse_hex(buf[strlen(buf)-3]);for (uint8_t i=1; i < (strlen(buf)-5); i++)sum ^= buf[i];if (sum != 0)return false;return true;}return false;
}uint8_t Ublox::parse_hex(char c)
{if (c < '0')return 0;if (c <= '9')return c - '0';if (c < 'A')return 0;if (c <= 'F')return (c - 'A')+10;return 0;
}

代码是c++写的,单片机不好支持,我修改为c了。代码的使用较简单,GPS串口发过来的每一个字节都丢到encode函数去,如果检测到换行符'\n',会调用process_buf函数解析,解析后得到时间,经纬度等值。

end

智工运维定位器之ublox_m8030_gps芯片开发相关推荐

  1. 智工运维定位器之ublox

    一,概述 GNSS芯片选用了ublox的 UBX-M8030 系列,有3个型号: 可以到官网去下载相关资料,文档还挺齐的: https://www.u-blox.com/zh/product/ubx- ...

  2. 智工运维定位器技术方案及选型

    背景 工卡式定位器在市场上比较少见,这个产品最初是一个客户的定制需求,配合我们公司园区管理系统使用.客户是一家大型工厂,厂区面积比较大,希望在门岗给来访的人员及车辆发一个定位器,园区管理系统可以监控到 ...

  3. 企业运维容器之 docker仓库

    企业运维容器之 docker 仓库 1. 什么是仓库? 2. Docker hub 3. Registry 工作原理 4. 配置镜像加速器 5. 搭建私有仓库 5. 总结 1. 什么是仓库? Dock ...

  4. 智象运维 | 如何利用snmp trap监控交换机端口状态(Up/Down)

    原理:在智象智能运维平台系统配置安装过程中,利用snmp协议在交换机上设置trap陷阱,当端口状态发生改变时,通知监控主机,监控主机配置snmptrapd进行接收,然后告警给用户. 参考文献:http ...

  5. NC运维人员拓展知识 之 开发工具入门(一)

    对于NC系统运维人员,通常情况下接触到较多的是NC前端问题处理,问题集中于业务之上.但是有时也会出现系统报错,例如,"****Exception",甚至出现"未知的错误& ...

  6. directx最终用户运行时_运维定位服务故障时,前5分钟都在忙啥?

    遇到服务器故障,问题出现的原因很少可以一下就想到.我们基本上都会从以下步骤入手,这些也是绝大多数运维工程师在定位故障时前几分钟的主要排查点: 一.尽可能搞清楚问题的前因后果 不要一下子就扎到服务器前面 ...

  7. Java,php,运维工程师转型大数据开发怎么样?你属于哪一类?

    一:java转型大数据 "2019年可能会是过去十年里最差的一年,但却是未来十年里最好的一年".市场发展的受限,不仅波及了各个行业的从业者,就连IT领域也受到了影响,很多IT人开始 ...

  8. linux运维排查常用命令(开发专享)

    cd: 进入到某个目录下 cd hikvision ll:详细展示该目录下有的文件 ll su 用户名:切换用户名 例子: su root 根据字符串在文件中查找信息:Grep –a –i 字符串 文 ...

  9. 2021 IT运维巡展北京站圆满落幕,北京智和信通荣获IT运维样板工程

    10月21日,以"数智转型 运维赋能"为主题的"2021(第十二届)IT运维巡展北京站"圆满落幕.会上行业专家.企业代表以及用户代表等共聚一堂,探讨数智时代下I ...

最新文章

  1. php取当前是pc还是手机号,利用PHP判断是手机移动端还是PC端访问的函数示例
  2. Hibernate3.X实现基于CLOB字段类型的注解方式:
  3. 装机之MBR和GPT
  4. 电脑版剪映v0.6.9内测版
  5. android读写缓存文件路径,Android app-cache-Path的 缓存图片、缓存文件的路径包名路径 和外路径比较...
  6. 不同浏览器input file样式不一样
  7. 对DataGridView中的DataGridViewComboBoxColumn有了一点点体会
  8. datetime数据类型_当pandas遇上数据类型问题
  9. Atitit 圣爱提拉克斯在会议上 2019年10月中旬的一天,有过这样的事。 这天,地方上风大。出乎人们意料之外,到东海岸一个地方访问的亲爱的领导者圣爱提拉克斯要同人们一道开会。主持人着了慌,
  10. win10默认壁纸_小白个人系统安装美化(二)win10系统美化设置篇
  11. CText类使用例程
  12. 断舍离------活成自己喜欢的样子
  13. leetcode-954. 二倍数对数组
  14. 后浪小萌新Python --- 字典
  15. 知名APP(支付宝、微信、花瓣等)首页设计技巧及原型实例讲解
  16. office运行时错误,部分系统文件可能丢失或已损坏(错误代码:0x80040154)
  17. Hadoop/Spark集群搭建图文全攻略
  18. 笔记本电脑开机到登入页面扩展显示器和电脑突然黑屏很久才显示
  19. 【数据产品案例】阿里生意参谋-竞争情报
  20. RGB与Ycbcr空间的互相转换

热门文章

  1. 清除无效的隧道适配器
  2. 《思维风暴》收获表述
  3. 计算机毕设Python+Vue幼儿园管理系统(程序+LW+部署)
  4. 数据的含义还没懂?就别做数据分析了
  5. 招沿实业理财投资的亮点
  6. opencv_contrib安装教程
  7. 凝思linux多网卡配置_linux(凝思)网卡多IP设置
  8. Ract Native笔记
  9. 【程序设计】布尔逻辑
  10. fastadmin表格