0%

source insight

Sourceinsight(以下简称SI)是良许使用过的最好用,最顺手,最强大的编辑器,没有之一!它几乎支持所有的语言,包括:C,C++,ASM,HTML等等,能够自动创建并维护它自己高性能的符号数据库,包括函数、method、全局变量、结构、类和工程源文件里定义的其它类型的符号,对于大工程的源码阅读非常方便。

但是,作为Linux程序员,我们的代码一般放在Linux电脑里。Linux里也有一些好用的代码查看工具,比如sublime,以及著名的Vim。SI什么都好,但就是没有Linux版。如果我们一定要用前文介绍过的共享文件夹来实现。

为什么不用vs code?

vscode就是出了名的好用,这个好用不是说它特别厉害,而是特别好上手,好上手不代表它好用。有些东西是需要适应的,不是说,不好用就逃避,尤其看linux内核源码的时候,不用这个的话,真的会绝望的。

VScode和SourceInsight,到底哪个看源码爽?_vscode和source那个好用-CSDN博客

source insight用法

根据下面这个博客就可以配置啦,还可以得到学习版本哦

嵌入式开发神器—SourceInsight 4的使用教程(附安装包)_sourceinsight4-CSDN博客

总结

vscode是最好用的”农具“,source insight是最好用的”字典“。

.bss

BSS段(bss segment)通常是指用来存放程序中未初始化的或者初始值为0的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。

.data

数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配

.txt

代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。

堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新 分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)

栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变 量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进后出特点,所以 栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

自定义段

其实根据上面这些东西,你就会发现,其实,他们都是段,我们可以打开一个mcu的map文件,就会发现。

.data 是存在rom的,但是他们运行的时候会被加载到sram之中,会被映射load过去。

.bss 是直接在ram空间的。

txt是写在rom,注意了所有函数都是写在rom的,因为函数是不变的,只是一个输入输出的过程,所以它是不变的。

堆栈这两个,是可以直接手动修改的,看map也知道它们的起始地址和大小

所以,我们可以自己使用section来定义属于自己的段

shell

cli命令行,mcu和linux想要实现cmd的话,都是使用section来开辟属于自己的地址,这个初始化在函数执行之前,开辟一个shell结构体,然后调用入口函数和出口函数,就可以实现链表的插入,这个结构体内部就是,一个string,一个函数指针,一些状态。使用方法,比如usart中断触发的时候,就可以在进行轮询判断,然后执行对应的函数指针。

OS_malloc

有些os是有自己的malloc函数,这不是c库的,其实c库函数就是从那个堆空间选一段空闲连续的空间。所以,我们也可以在sram中选择一个起始地址,选择大小,然后就可以编写一个OS_malloc,指向我说开辟的section地址,这样就可以实现c库的malloc功能。

总结

其实,这些段,就是最基础的编译需求,也是留给我们开发人员很多空闲的部分,搞嵌入式,对于sram和rom是要非常了解的才行。注意了自己开辟的地址,不要冲突了,比如写mcu,自己的代码100kb,然后你用一个section指向一个0x8019000以内的,那就冲突了,程序都会有问题的,还有就是sram的地址也不要冲突了

起因

我在和一个朋友讨论bootloader的时候,他提出了他的设计思路,也就是状态判断的一个设计,这个设计,确实是我没想到的,也就是bootloader得到上一次复位的原因,是掉电复位,看门狗,还是软件复位

复位寄存器

这个寄存器不会因为断电而丢失,STM32中,RCC的寄存器和功能因芯片的不同而不同,但RCC_CSR寄存器是众多STM32种都具有的一个。

但是,rcc_csr寄存器也是在sram中的数据,掉电就会丢失,因为需要有备用电源,就是一般有那种备用电源的设备上面。其实也可以理解,一般有稳定电源的mcu也不会担心掉电问题,备用电源(电池)就是因为要低功耗啊,才可能出现主电池没电问题。

作用

开机的时候,可以根据上一次的行为来知道为什么复位。

  • 可以利用看门狗喂狗的功能:跳转app,如果app没喂狗就说明了app有问题,直接复位失败,几次之后就立起来标志位,并发送给云端,说明当前固件错误的问题。
  • 利用掉电复位来报警,因为电池的供电本来就是一个不稳的情况,一般mcu在2.2v就能启动了,1.8v就不能工作了,一般都是有pvd中断的,可以结合pvd+掉电复位来报警提醒电量不够。
  • 软件复位,可以直接用于ota,软件层面接收到数据,bootloader接收到是软件复位就开始了升级了,也是一个设计。
  • 监控mcu状态是否合理,多次复位,肯定是有问题的,可以把复位原因每次都发送云端,这样方便修改和调试,找到问题。

当然功能还是以查询状态,然后针对这个状态,做出一些措施而已。有联网的措施,有本地的措施,有程序自检的措施,主要是看产品设计和定位。

总结

我个人认为还是物联网方面需要这个功能,如果只是一些普通的项目,一般都是没问题的,只要通过了测试,很多问题都是可以的,反而因为需要远程4g或者wifi的fota,这个很可能出现网络问题,传输问题等。而且物联网总是需要实时监控的,有这个功能,也方便管理和及时ota来修复应用层问题。

BSDiff

BSDiff是一种可执行文件的二进制差异构建和应用修补工具。

作者为Colin Percival,早在2003年就已经写好了这个工具。官网是Binary diff,不过看来已经下载不到资源,处于不维护的状态。这个可以说是非常非常老的东西了,不过已经稳定的东西,就不用修改了,这世上的屎山可不少,维护的人却是越来越少了,只要它是稳定独立的、模块化的,大家也就都不会去动他。

增量更新BSDiff算法溯源和原理解析

就是类似于linux、windows的那些补丁一样,linux本身也是有利用补丁来实现替换,这样就不用修改整个文件,只需要修改差异化部分进行替换就好了。

为什么要BSDiff

我们常见的OTA,都是把整个bin都传过去,或者自己的一些压缩算法,帧头帧位,组包,校验、MD5等方法,保证整个文件的完全传输,有的时候固件很大很大,上百上千KB的时候,不论是用tcp还是udp传输都是有大小传输限制的,大概也就是1000个字节左右,所以,就会有多个包,还要加上自定义的协议等操作,中间只要出现网络的问题,很可能就会导致下载失败,然后bootloader就直接丢弃了下载数据。

总的来就是,尽量减少对外通信的时间,不要过度依赖外部网络,因为物联网设备工作在什么场景都可能的,不是说在实验室和公司的那种环境,所以,传输的数据越短越少,就越稳定越可靠,然后通过主控、处理器自己去处理这个差异补丁,反过来得到固件。在主控、处理器内部进行拷贝覆盖原本应用,校验、测试处理等。

MCU实现

我也是参考了下面这个博客才有的想法,确实是一个很好的开源项目,总的来说,原理就是调用BSDiff算法库(就算是源码,我估计你也不愿意看的,稳定的东西,用就好了,肯定涉及很多内存方面的东西,估计跟FS有的一比),对更新好的数据,进行处理。

在STM32中使用bsdiff算法实现差分升级(bootloader)_差分升级算法-CSDN博客

linux实现

linux补丁指令入门

Linux patch命令教程:如何在Linux中应用和创建补丁(附实例详解和注意事项)-CSDN博客

这个实现可以查考下面这个博客,不过就是一些命令行操作了,就更见简单了,其实就是文件系统那一套设计思路,进行对应数据修改+索引追加和指向,然后最后重新生成差分包,算法得到这个生成的差分包,就知道该怎么修改了,有点簇的味道了。

增量更新BSDiff算法溯源和原理解析_bsdiff原理-CSDN博客

总结

BSDiff的原理和技术非常成熟,可以用于apk的差分,不过在mcu之中也是可以使用这种方法,把这个功能写到引导之中,通过BSDiff算来就可以做到,只下载差异化部分,然后自动更新固件的功能。自然不可能只有BSDiff,如下博客,还有其他的方法,本质都是差不多的原理,通过差异化来生成文件,进行替换。

Xdelta3 bsdiff Courgette三种差分算法比较-CSDN博客

DNS

RootFS

根文件系统首先是内核启动时所mount的第一个文件系统,内核代码映像文件保存在根文件系统中,而系统引导启动程序会在根文件系统挂载之后从中把一些基本的初始化脚本和服务等加载到内存中去运行。

系统引导启动程序会在根文件系统挂载之后从中把一些初始化脚本(如rcS,inittab)和服务加载到内存中去运行。

RootFS作用和限制
RootFS添加应用程序
BulidRoot
RootFS与VFS
RootFS使用
总结

模块

模块化,有蓝牙和WiFi、电机、传感器、显示屏等模块,这些模块都是由官方提供的api或者sdk,可以高效的开发,当初我第一次玩esp32开发板的时候,看到esp32+摄像头二合一的模块,这个代码没想到官方库都有提供,直接使用arduion就可以创建实例,可以快速实现拍照和上传,这就是模块化的好处了。搞嵌入式,最常说就是模块化编程了,就是高内聚,低耦合,通过内核来分配资源。

函数指针

可以用纯c写一个结构体,里面全都是函数指针,这样就可以实现api,方便其他程序调用,而且也可以通过外部来进行修改指向,这个是一个非常好用的技巧了。

对了,这里分享一个,我开发遇到的有意思的东西,我用c写了一个函数指针,去指向一个类实例里面的函数,发现是不能指向,提示要static的类方法才能被指向,这个是为什么呢?

非静态成员函数依赖于具体的类实例,它们有一个隐含的this指针指向类的实例。因此,它们在底层的函数签名上是不同的,无法直接用普通的函数指针去指向它们。静态成员函数与类实例无关,它们不依赖于任何this指针,因此它们在行为上与普通的全局函数相似。所以,可以用普通的函数指针指向静态成员函数。

git子模块

一定要知道有一个主模块,然后再添加git submodule add ,这样就可以在主工程中添加子模块,这样的开发其实非常有利于修改和开发的,不过对于一个模块的要求还是很高的。尤其是为了给客户使用,很多厂商linux内核和芯片编译都要连接外网,然后git拉下来仓库进行编译,这个过程就可以用这个子模块,然后子模块中只有静态库文件,这样就非常保密了,而且规范了开发流程。还有一点就是这个子模块问题就是版本更新的问题,有些外国人不讲武德,动不动就删分支,所以make就会失败,这个时候就得手动版本回溯,或者自己手动先下载好老版本的分支哦

总结

模块,高内聚低耦合,一个代码的风格和框架是可以看出来一个人的经验和水平的,不过很多人都是凭感觉来写的,感兴趣的可以看一下前面的博客,有关于内核和耦合的,也可以自行搜索。模块的制作确实有点麻烦,不过制作好之后就非常简单了。