这是一个系列文章《如何从零开始实现TDOA技术的 UWB 精确定位系统》第3部分。

重要提示(劝退说明):

Q:做这个定位系统需要基础么?
A:文章不是写给小白看的,需要有电子技术和软件编程的基础
Q:你的这些硬件/软件是开源的吗?
A:不是开源的。这一系列文章是授人以“渔”,而不是授人以“鱼”。文章中我会介绍怎么实现UWB定位系统,告诉你如何克服难点,但不会直接把PCB的Gerber文件给你去做板子,不会把软件的源代码给你,不会把编译好的固件给你。我不会给你任何直接的结果,我只是告诉你方法。
Q:我个人对UWB定位很兴趣,可不可以做出一个定位系统?
A:如果是有很强的硬件/软件背景,并且有大量的时间,当然可以做得出来。文章就是写给你看的!
Q:我是商业公司,我想把UWB定位系统搞成一个商业产品。
A:当然可以。这文章也是写给你看的。如果你想自己从头构建整个系统,看了我的文章后,只需要画电路打板;构思软件结构再编码。就这样,所有的难点我都会在文中提到,并介绍了解决方法。你不需要招人来做算法研究。如果你想省事省时间,可以直接购买我们的电路图(AD工程文件),购买我们的软件源代码,然后快速进入生产环节。(网站: https://uwbhome.top

前一篇文章介绍了时钟同步的方法,是为了尽快拿点干货出来,免得读者觉得我只写一些泛泛而谈的大路货,没技术含量。下一步慢慢写吧。

基站固件设计-Flash布局

给MCU写固件的时候,如果是初学者,可能一上来直接就开始写应用程序了。老手都会先考虑布局,都要做些什么事,它们之间的关系是什么等等,要考虑周全。

基站的Flash分为4块:Bootloader、Application1、Application2、Data。

基站固件设计-在线升级

产品的固件可以现场升级很重要。现场升级是指不用把已经安装好的产品拆下,直接通过局域网之类的,在客户现场就可以升级固件。要知道,当产品已经安装在几千公里外的客户现场,突然发现固件中有个bug,你怎么办?如果能现场升级,在办公室把代码修正后,生成新固件,发给现场的客户,在现场升级固件。在这个过程中,你根本不需要离开你的办公室。

如果你的产品不能现场升级固件,对不起,发现bug后,只能请客户把产品拆下,寄回公司,你刷入新固件,再寄给客户......想起来都头大。

基站固件设计-Bootloader

Bootloader是启动代码,Application1/Application2是应用程序,Data放配置数据。

上电的时候,Bootloader中的程序先被执行,它判断Application1/Application2哪个有效,就跳转到有效的那个Application去执行。在Data区有两个变量: Application1Valid和Application2Valid,如果为True表示对应的Application有效。

在生产阶段,负责生产的工人只负责刷入Bootloader,这时,Application1Valid和Application2Valid都是False。

上电后,如果找不到有效的Application,Bootloader会启动网络部分的代码: DHCP Client/TCP Client/DNS Client/UDP Server/UDP Client等。如果它连接在网络,会一直在网络中发出一个UDP包,请求初始化。

出厂初始化程序收到请求初始化的UDP包后,会登记新基站的基本信息,如MCU ID等,然后给它分配一个 EUI64 的地址,这个地址以后就是这个基站的ID了。还会分配一个系列号。然后还要写入缺省的配置数据。出厂初始化程序接下来开始发送固件给新基站。Bootloader收到固件后,写入到一个无效的Application区(刚开始,Application1区肯定是无效的)。固件会被分很多个包发送。固件接收完成后,Bootloader会置Applicaiton1Valid为True,然后重启。

再次上电后,Bootloader发现Application1有效,就跳转到Application1去执行。

这就完成了新固件的启动。

其实,Bootloader中的网络相关代码和固件接收代码,只在出厂初始化的时候执行一次,以后就不会再被执行到。因为在出厂之后,Applicaiton1和Application2总有一个是有效的。

如果Bootloader发现两个区都有效,会把第二个区置为无效。只允许一个区有效,无效的那个区用保留给新的升级固件使用。

基站固件设计-固件升级

在固件的Application中,也有类似前述Bootloader的固件升级代码。只是它要判断哪个区无效,把收到的新固件保存到无效的那个区,并置对应的变量为有效,把正在运行的这个区对应的变量置为无效。下次上电时,Bootloader就会跳转到新固件去执行。

基站固件设计-程序结构

现在介绍Application怎么写。

因为基站的功能还是有点多,好几个功能需要“并发”执行。这里的“并发”是指逻辑上的,因为STM32不支持抢先式执行/不支持多线程/只有一个核,所有代码都只能顺序执行,并不会有真正的并发。

所以,MCU不能裸奔,得使用RTOS。当初,因为比较熟悉uCOS,所以就用它了。如果是现在选择,大概率会用FreeRTOS。

  • 时钟同步部分,如果作为时钟源,需要定期发送UWB时钟同步包;
  • UWB芯片处理,负责DW1000的状态管理,以及UWB数据包的收发。
  • 网络处理,包括DHCP Client/DNS Client/UDP Server/UDP Client等。
  • 看门狗任务

先从简单的部分开始介绍。

看门狗任务

STM32有看门狗,如果不及时调用喂狗函数,MCU就会认为程序挂了,它会自动复位。我们的程序分为很多个任务,每一个任务都有挂掉的可能,所以,我们搞一个看门狗任务。每个任务有一个看门狗变量,由各个任务及时把它置0,看门狗任务循环给这些变量加1,并判断这些变量的值,如果超过某个界限,就判断对应的任务挂了,就复位MCU。看门狗任务还负责喂狗。

总之,我们要保证各个任务都能正常工作,当任务挂掉后,立即复位。

DW1000处理任务

考虑到将来可能会在一个基站内置多个DW1000,所以我们把DW1000相关数据定义为一个结构:

typedef struct __DW1000_MODULE__
{
uint16_t Module_SPI_Prescaler;
SPI_TypeDef* SPIn;
GPIO_TypeDef* Module_SPI_Port;
uint16_t Module_SPI_SCK_Pin;
uint16_t Module_SPI_MISO_Pin;
uint16_t Module_SPI_MOSI_Pin;
GPIO_TypeDef* Module_SPI_CS_Port;
uint16_t Module_SPI_CS_Pin;
GPIO_TypeDef* Module_RSTn_Port;
uint16_t Module_RSTn_Pin;
GPIO_TypeDef* Module_RST_IRQ_Port;
uint16_t Module_RST_IRQ_Pin;
uint32_t Module_RST_IRQ_EXTI_Line;
uint8_t Module_RST_IRQ_EXTI_PortSource;
uint8_t Module_RST_IRQ_EXTI_PinSource;
IRQn_Type Module_RST_IRQ_EXTI_IRQn;
GPIO_TypeDef* Module_IRQ_Port;
uint16_t Module_IRQ_Pin;
uint32_t Module_IRQ_EXTI_Line;
uint8_t Module_IRQ_EXTI_PortSource;
uint8_t Module_IRQ_EXTI_PinSource;
IRQn_Type Module_IRQ_EXTI_IRQn;
uint8_t Module_IRQ_EXTI_USEIRQ;
uint8_t Module_RST_IRQ_EXTI_USEIRQ;
uint8_t crystal_oscillator_trim;
uint8_t rx_buffer[RX_BUF_LEN];
DW_EVENT DWEvents[MAX_EVENT_NUMBER];
uint8_t DWEventIdxIn, DWEventIdxOut;
DW_EVENT dw_event_g;
CLOCKSOURCE_SYNC_INFO clockSourceSyncInfo[CLOCKSOURCE_SYNC_INFO_NUM];
SEEN_CLOCK_SOURCE seenClockSources[SEEN_CLOCK_SOURCE_NUM];
dwt_local_data_t dw1000Local;
} DW1000_MODULE;
typedef DW1000_MODULE* PDW1000_MODULE;

这样的话,有些代码就可以复用了,程序处理起来会灵活一些。

在这里要吐槽一下DecaWave,DW1000的工作情况并不很稳定。有时会出现一些奇怪的现象。所以,我们还要经常判断DW1000状态,如果不在期望的状态,判定死了,我们要及时对DW1000进行复位。当然,这种情况并不太多,但是只要有,我们就得处理。否则在客户现场,基站活着、固件活着,但是DW1000死了,无法收发数据,就很尴尬了。

基站的主要工作就是接收UWB数据包,为了保证收到的数据包能及时处理,不影响后续的UWB包的接收,我们搞了一个队列来做缓存。DW1000任务只管收UWB包,收到后立即放入队列,然后又处理后续的UWB包。队列中的包由其他任务来处理。

网络任务

网络任务的工作比较杂乱。

它最重要的一个工作就是把收到的UWB重新组装成一个新的数据包,发给定位引擎。

其它的工作就是辅助性的了。

DHCP客户端。很多时候,IP地址的管理很麻烦,如果每个基站都一一配置IP地址,是一件比较辛苦的事。一般来说,除非客户要求每台设置都要配置指定的IP地址,否则使用DHCP来分配IP地址是比较方便的。W5500芯片有DHCP客户的代码例子,直接复制过来改改就可以了。它的代码在处理上不太完整,DHCP的IP地址合约到期后的续签,还有如果有IP地址冲突怎么处理等等,我们对代码做了一些完善工作。

DNS客户端。其实可以不用的。如果定位引擎不在局域网内,而是在很远的地方,例如Internet上的云主机。使用DNS指定定位引擎的地址会好一些,万一定位引擎的IP地址变了,但是域名可以不变。我们没有使用DNS,之前的客户基本都在局域网内,把定位引擎放在Internet上的只有为数不多的几个。

UDP。我们使用UDP来实现定位引擎自动发现。如果配置为定位引擎自动发现,网络任务会检查是否与定位引擎建立起TCP连接,如果没有,它会广播一个UDP包(定位引擎发现包),要求定位引擎给回复。定位引擎收到UDP包后,会回复自己的IP地址之类的信息。基站就会与定位引擎建立起TCP连接。

基站配置。在早期的版本中,我们使用UDP来传送基站配置信息。使用UDP的好处是可以广播,缺点是UDP是无连接的,有丢失的可能。后来我们统一改成使用TCP来传送基站配置信息。基站配置程序先通过UDP广播,查找局域网内都有哪些基站,然后与它们建立TCP连接,双向传送配置信息。

基站固件设计-杂项功能

恢复出厂设置。为了保证数据安全,基站设置有密码,基站配置程序必须使用正确的用户名和密码才能对基站进行配置。既然是密码,就有忘记的可能。另外也有可能用户配置基站,把数据弄乱了,自己懒得一个一个的改,所以我们设计了一个恢复出厂设置功能。

// RS232端口 GPIOA (被挪用于检测是否恢复出厂设置)
#define USART1_TX PA9 // out
#define USART1_RX PA10 // in
#define USART1_PORT GPIOA if ((!GPIO_ReadInputDataBit(USART1_PORT, USART1_TX)) || (!GPIO_ReadInputDataBit(USART1_PORT, USART1_RX))) {
//== 恢复出厂设置
RestoreToFactoryConfig();
HardReset();
}

USART1_TX和USART1_RX在出厂的产品上没用,对应的是PA9和PA10被挪用于检测是否恢复出厂设置。需要恢复出厂设置时,把这两个引脚之一与GND短接,重新上电就可以了。

报警功能。有个客户打算把我们的系统用在煤矿(重要提醒,产品需要本安认证才用在煤矿),需要增加一个报警功能。当发生险情时,系统可以发报警信息给标签,让携带标签的工人可以得时得到撤离信号。流程大致是这样,应用程序调用定位引擎的接口,定位引擎通过网络把数据包发给时钟源(或扮演时钟名的基站),时钟源在发送时钟同步数据包的任务中发出UWB报警数据包,标签收到UWB报警数据包后,立即报警(红灯,或震动)。报警消息有两种:广播给所有人,指定ID的标签。

基站固件设计-可能会遇到的坑

第一个坑是DW1000的状态。前面已经说过了。我们在系统中要定期检查DW1000的工作状态,如果不正常,要及时对DW1000进行复位。

第二坑是W5500的晶振。我们第一次量产基站时,W5500使用的是无源晶振,这是很正常的电路设计,我们之前的测试板都是这样设计的。我们在之前的一些产品中用到W5500也是使用无源晶振。但是基站生产完成,收到货后测试发现有很多基站的W5500的晶振不起振。当时想了各种办法来找原因,换pad电容,重新焊接等等。这个起振问题,似乎处于一个不确定状态,有些上电一段时间会自己起来,有些就总是不起振,有些甚至开始起振了后面会自己停。以至于我们只好在固件初始化的时候判断W5500的状态,看它状态正常不,还在网络任务中经常性的判断W5500的状态。后来我们的硬件设计改用有源晶振,就一切OK了。再之后,我们其他用到W5500的产品中,都使用有源晶振。

这是基站固件设计的第二篇文章,以后再回头修改修改充实一些细节吧。

顺便做个找工作的广告。因为经营上的原因,公司散伙了。本人30+年工作经验,顺手的语言C/C++/Java/Delphi,这个UWB定位系统在初期硬件软件都是我一个人弄出来的,产品成型之后才增加人手组团队。Base贵阳,或远程。如果有工作机会,请联系我要详细的简历。

如何从零开始实现TDOA技术的 UWB 精确定位系统(3)的更多相关文章

  1. 关于精准UWB人员定位系统解决方案

    WB技术, 目前主要应用在室内定位.人员定位系统等定位领域.近年来被应用在无线定位和雷达测距应用中,因此作为民用雷达和民用测距取得了较快的发展.而今天,我们主要要来介绍的产品就是UWB技术的芯片DW1 ...

  2. 浅谈UWB(超宽带)室内定位技术(转载)

    技术背景 随着无线通信技术的发展和数据处理能力的提高,基于位置的服务成为最有前途的互联网业务之一.无论移动在室内还是室外环境下,快速准确地获得移动终端的位置信息和提供位置服务的需求变得日益迫切.通信和 ...

  3. [转帖]新iPhone的黑科技:UWB技术揭秘

    新iPhone的黑科技:UWB技术揭秘 http://blog.nsfocus.net/iphone-black-technology-uwb-technology-revealed/    阅读:  ...

  4. 基于UWB技术的DW1000芯片简单解析

    近些年来随着物联网和机器人技术的大发展,精确定位技术的热度也随之攀升.目前精确定位的技术有很多,如基于wifi.RFID.zigbee.超声波.UWB等技术都可以实现精准定位.由于技术的不同,精度也不 ...

  5. 【硬件模块】UWB介绍

    From: https://liudongdong1.github.io/ UWB超宽带定位技术属于无线定位技术的一种.无线定位技术是指用来判定移动用户位置的测量方法和计算方法,即定位算法.目前最常用 ...

  6. 位置指纹(LF)定位技术简介-室内定位

        信号的多径传播对环境具有依赖性,呈现出非常强的特殊性.对于每个位置而言,该位置上信道的多径结构是惟一的,终端发射的无线电渡经过反射和折射,产生与周围环境密切相关的特定模式的多径信号,这样的多径 ...

  7. 一个「学渣」从零开始的Web前端自学之路

    从 13 年专科毕业开始,一路跌跌撞撞走了很多弯路,做过餐厅服务员,进过工厂干过流水线,做过客服,干过电话销售可以说经历相当的“丰富”. 最后的机缘巧合下,走上了前端开发之路,作为一个非计算机专业且低 ...

  8. Linux基础介绍【第九篇】

    服务器添加3块磁盘的体系结构 [root@oldboylinux test]# free -m              total used free shared buffers cached M ...

  9. OpenCASCADE Conic to BSpline Curves-Circle

    OpenCASCADE Conic to BSpline Curves-Circle eryar@163.com Abstract. The conic sections and circles pl ...

  10. css精灵

    ○ css 精灵(Sprites)技术利用photoshop将图片整合,然后用background-images,background-position,background-repeat技术,对图片 ...

随机推荐

  1. 低代码引擎 TinyEngine 正式发布!

    在当今数字化飞速发展的时代,企业对高效.敏捷的应用程序需求日益旺盛.为了满足这一需求,越来越多的低代码开发平台开始涌现.这些平台通过提供简单易用的开发工具和优化后的开发流程,帮助开发者快速构建高质量. ...

  2. open与fopen的区别

    1. 来源 从来源的角度看,两者能很好的区分开,这也是两者最显而易见的区别: open是UNIX系统调用函数(包括LINUX等),返回的是文件描述符(File Descriptor),它是文件在文件描 ...

  3. 【ASP.NET Core】在 Mini-API 中注入服务

    经过版本更新,Mini API 的功能逐步完善,早期支持得不太好的 mini API 现在许多特性都可以用了,比如灰常重要的依赖注入. 咱们先来个相当简单的注入测试.来,定义一个服务类,为了偷懒,老周 ...

  4. 小米手机抓取Log教程

    当小米手机遇到任何系统下的较为严重的故障时(即此时系统还是基本正常运行的,只是某些功能实现出了问题),例如软件自动退出,SD卡自动卸载,电话无法拨出等等.可以请用户协助抓取log即系统运行日志,来发送 ...

  5. Chapter 6. Build Script Basics

    Chapter 6. Build Script Basics 6.1. Projects and tasks Everything in Gradle sits on top of two basic ...

  6. 记一次 .NET某新能源检测系统 崩溃分析

    一:背景 1. 讲故事 前几天有位朋友微信上找到我,说他的程序会偶发性崩溃,一直找不到原因,让我帮忙看一下怎么回事,对于这种崩溃类的程序,最好的办法就是丢dump过来看一下便知,话不多说,上windb ...

  7. 【原型链污染】Python与Js

    [原型链污染]Python与Js 一.背景 最近在TSCTF的比赛题中遇到了Python的原型链污染题目,所以借此机会学习一下.说到原型链,最多的还是在Js中,所以就一并学习一下.(因为是菜鸡所以文章 ...

  8. Go 函数的健壮性、panic异常处理、defer 机制

    Go 函数的健壮性.panic异常处理.defer 机制 目录 Go 函数的健壮性.panic异常处理.defer 机制 一.函数健壮性的"三不要"原则 1.1 原则一:不要相信任 ...

  9. [Python急救站课程]Hello Kitty的绘制

    我女朋友喜欢Hello Kitty,当她知道我会用Python画图,让我给画一个,怎么说呢,当然要安排上啦!请看程序 import math import turtle as t # 计算长度.角度 ...

  10. JPA中@ElementCollection使用

    转载请注明出处: 在JPA中,@ElementCollection注解主要用于映射集合属性,例如List.Set或数组等集合属性,以及Map结构的集合属性,每个属性值都有对应的key映射.这个注解可以 ...