0%

起因

今天再看rtt手册的时候看到一个smp方案,就是多核操作调度,其实想想也是,mcu只是普遍是单核,也不能这样说,esp32就是RISC-v双核MCU呢。

SMP

这个是多核,注意了,这个多核,指的是全部架构和功能一样的多核,就像cpu多核一样那种,此时整个系统都是一个rtos,就是一个rtos控制所有核心,每一个核心都可以运行一个任务和中断等操作。此时,就得注意了,高优先级和低优先级就能一起触发运行了。因为此时是一个cpu干一件事这件事包括时间片,所以,如果有就绪列表中有任务的话同优先级的任务就会运行在同一个核心上面,经过时间片调度,一起运行。此时优先级低的任务就可以分到一个cpu来处理同优先级的任务了。这点是要注意的。至于实现就是比较复杂了,根据官方的操作去移植吧,这里篇幅不够。反正注意是一个系统控制所有资源和核心,有点linux的味道了,但是linux还有mmu等诸多资源管理。

AMP

怎么可能总是一样的架构芯片?随便拿一个手机芯片,你看有多少个大核,小核。arm开发板也是一样的啊 STM32H745I就是M4和M7双核。所以一个arm-A系列,也常常有A53和A7混合啊。这明显就是非对称核心了。那么此时怎么办?rt-thread的文档没有给方案!一般厂商再出厂的时候,就会为自己的soc写好rtos的代码,直接调用api就好了。但是如果没有怎么办?此时,咱们的老大哥,freertos有方案,就是AMP方案,就是每一个核心都运行一个rtos,然后通过一个流\信号缓冲区来实现,控制和函数调用。具体·流程思路如下:大核心,一般都是主控主核,这点大家都是默认的。就是大核心就是一只告高速运行的,此时可以通过调用api的方法,向rtos中的缓冲区中发送函数指令,特定的核心是时刻处于接收状态的,一旦识别到指令就去执行,因为他们的空间是共享的,所以就会直接去调用函数。此时大核就完成任务和发布。这其实就有点dma的味道,就是告诉dma该干嘛,dma就去干了,完事之后,也往缓冲区中发送执行完毕
的命令此时就实现一个简单的多核操作。

note:多核心是多核心,不是多板子,是一个芯片里面多个核心,是芯片内部的数据、控制等处理。不是说一个板子上面接上两个mcu就是多核心了,这只是两个mcu之间的通信,就跟串口那样的,所以就不是多核心。多核心,它们内部可能有共享内存、线路、指令等,很复杂的走线。

多核心烧录

多核心怎么烧录呢?我们一般烧写代码,都是mcu那种单核代码烧写或者直接就是linux内核那种多核系统下载进去了,然后uboot引导开机。对于非对称核心的,就是要烧录多次, STM32H745I一个M4和M7核心,但是它们有着独立的空间和数据。所以,需要分别烧录才行。还有使用uboot或者bootloader那种进行代码的下载。这个时候就只需要下载一次,此时就是把程序按照特定的规则来打包,然后再引导程序中解包。对于对称多核,因为它们是共享内存和数据空间的,所以,往往烧写一次就足够了。对于不同版本,不同厂商的mcu处理方法有所不同,但是大致的思路是正确的。

其实,如果是arm架构的单片机,我们要知道一件事,此时它们是在一块内存上面的,因为我们对cube的探索发现gpio同一个时刻只能给一个核心使用,而且两个核心都是有引脚的控制权,所以,它们内存也是统一编址的,我们就可以使用keil分别烧录,把M7烧录到0x08000000 把m4烧录到0x08100000 此时就可以两者工作了,注意了,烧录的时候,不要清空flash了。也可以使用一些cubeide等诸多软件来进行烧录。此时是两份代码都是通过一个jtag进行烧录的哦。

结语

今天,我问我的同事,多核怎么烧录,他思考一会儿,对我说,用不到得,这东西没搞得的必要,就像我之前问他,觉得mcu动态调用实现,手机那种app的操作,他也觉得没必要。我们的求知欲会渐渐消退,变得越来越现实。但是我内心的….还是不甘啊,我只是在为了我糟糕的人生赎罪。

起因

我在想办法写代码的时候,想降低耦合,增加内聚能力。这个口号天天喊,模块化天天说,但是什么是才算内聚什么才算耦合?平时我们都是经验主义和习惯主义,脑子中没有一个准确的定义和概念。

内聚

偶然内聚 :就是一个模板内,所有元素互不干扰独立的

逻辑内聚 :就是通过传参来影响模块内部的逻辑功能,就是一般的传参操作 fun(temp);

时间内聚 :同时执行的行为和动作组合起来形成的模块,比如一键实现界面登录和打开主页

过程内聚 :指一个模块完成多个任务,这些任务必须按指定的过程执行。 例如:先写姓名 → 电话 → 家庭住址

通信内聚 : 在逻辑内聚的基础上,通过传数据结构来实现操作,数组、结构体、链表等

顺序内聚 : 一个模块内,前一个元素所产生的影响会影响后一个元素的操作

功能内聚 :一个模板内,所有元素完成一个功能,缺一个不可

耦合

无直接耦合 :就是模块没有联系,就是主模块来调用两个独立模块

数据耦合 : 有联系但不多,就是传参一样的操作

标记耦合 : 这个就是在上面基础,传数据结构

控制耦合 : 这个就不是传参副本了,而是直接修改源数据了。比如传地址

外部耦合 : 就是几个模块共用一堆数据区(全局变量),但是这些数据区是只读不修改的

公共耦合 : 多个模块公用一个数据集合,而且可以修改

内容耦合 : 这个是最糟糕的耦合了,就是一个模块调用另一模块内部的属性,比如使用另一个模块的static变量,类变量等等

结语

内聚和耦合是不可避免的,一些耦合度很高的模块,就可以选择合并到一起,或者编译成库等都是可以的
我们作为开发人员,就需要尽可能高内聚低耦合,模块化哦!

起因

为什么突然想起来这个属性呢,因为最近在看嵌入式系统设计的课程。看到抢占式系统不能直接使用不可重入函数,一下子我就茅塞顿开了。

什么是可重入和不可重入

你可以认为就是该函数是否独立,是否是模块化的!
如下:

1
2
3
4
5
6
7
8
9
volatile uint32_t staticData = 100;

uint32_t testFun()
{
if(staticData>=101)
return 0;

return staticData++;
}

由上面函数可以发现,第一次调用的时候是可以正常返回staticData的数据的,但是以后的调用都是返回0,也就是无效的,因为它使用全局的静态变量,所以它是非独立的。假如我任务一是低优先级,此时调用这个函数,但是任务二立刻发生了抢占但是也调用了这个函数,那么任务二是正常调用并得到了数据,此时回到任务一调用就得到了0,是无效的。这个时候,彼此之间发生了干扰,这是不合理的!对系统的稳定和安全有大问题,尤其是项目大的时候。

如何分辨是否是可重入

1.是否调用全局变量,注意了还有类中的static变量也是全局变量
2.是否有内部的static变量
3.是否发生了跳转 如setjmp和longjmp
4.是否有调用了malloc和new的变量
5.是否有标准 I/O 函数

note:传参导致的函数返回错误值,这不算不可重入函数,这是你因为外部原因导致数据错误。函数只是正常的反馈数据错误,此时是合理正确的。不要搞混了!!!

如何避免或者解决带来的问题

1.一个不可重入函数,只给一个任务来使用
2.减少不可重入函数的定义,选择使用传参来进行判断和数据处理,这样就可以在任务进行判断,而并非来自函数来进行判断,这样就减少不可预知的错误
3.多个标志位来做标记,比如上面的任务二使用之后,就在一个自定义的系统属性结构体中进行使用过的痕迹(标志位)标记,然后在不可重入函数中进行判断和处理等,非常繁琐,别用。

结语

OS是非常考验设计师和工程师的水准的,对于每个资源的调度和处理,每一个流程都要了如指掌才行,得知道这样得代价是什么,很多时候,其实我们都不会犯错误。因为,这些技术和知识,其实就在我们的日常习惯中,大家都不会乱用goto,都不会乱使用一些繁琐的东西,大家都知道这样不好。所以很多时候,没出现错误和bug!但是出现错误了,记得总结为什么出错了,才能真正有效的进步。不要把习惯当成标准!你只是一时的好习惯拯救了自己,但是你却是在不经意和没有思考之中调用了自己的固有思维罢了。

起因

​ 今天,我的哥们又在上班摸鱼,他发了一些极性码的博客给我,他真的牛逼,都准备搞5G协议栈了,当然只是看着玩的,学学思路而已,这东西不是一个人可以搞定的,然后他问我,线程和进程怎么区分啊?我一时间懵了,因为我之前学linux的时候,就是调用api啊,fork thread就完了。线程最小单位,进程是一个车间啥的,线程共享资源,进程独立资源,脑子就只有这个概念。我本来想要回答的,它来一个博客,就是关于线程和进程认识。举个例子,如果是mcu,那么此时就只有一个进程,那么是不是可以认为,控制程序控制块指针就是控制一个进程呢?如果我们对rtos上系统,是不是就是多任务(线程)了呢?我跟他说了一下观点。我的思路和观点是比较简单的,这个系统就不是一个人可以实现的!

CPU核心数和线程数

打开电脑的资源管理器就可以看到几内核,多少逻辑处理器(线程)。还有那些云服务器啥的,也写了参数。
比如: 8核16线程 32核32线程
首先,我们所谓的cpu核心数量,不是这样算的,是一个大cpu里面多个小cpu,每个小cpu有单核,双核,多核。
单核就是同时只能执行一个线程或者进程,双核就是两个,以此类推。
所以此时的核心就是 cpu (核心)
此时的线程就是 cpu * n *几核心 (线程) 这里是使用超线程技术哦。

进程和线程

进程这个概念在mcu中是没有的,就一个单片机,搞个鬼的进程,多任务还差不多。所以,我们就来思考linux的进程和线程,首先,少不了的结构体,就是进程控制块,内部有id、父id、内部数据、线程列表、内存大小等。然后就得去找mmu要内存,因为每个进程都是独立的。然后线程列表,共用这个进程中的内存数据,线程id,线程优先级等。自然这些结构体里面还得有信号处理,还有互斥锁,信号量等东西。cpu的最小运行单位就是线程,那么进程是怎么处理的呢?
我们要知道每次生成一个进程的时候,一定是有一个主线程,就是进程自身。
所以,cpu哪怕是调用进程,也是运行进程中的线程。我们可以这样认为:
运行一个线程的时候就是裸机。运行一个进程的时候就是一个rtos。

CPU和系统

​ 我们学linux的时候,发现线程可以申请非常大,甚至百万千万上亿都是可以的,但是进程是受限制的。
​ 我们通过上面的思考,可以知道因为进程还会申请空间,还会做很多进程通信的配置,所以是非常耗资源的,加上系统自身还有很多守护进程在,所以,这是进程的申请有限,看配置,而且会越来越卡,因为资源终究有限。可是我的cpu只有8核16线程啊?再怎么样也比不过系统的进程和线程数啊?
​ 这里就要分清楚了os的并发和硬件cpu的并发不一样。
​ 我们用windows来思考,首先就是bios启动开机,此时就得到了cpu的线程数和核心数,然后告诉系统了,系统就知道了目前可用的设备和处理器有那些了。它内部的线程和进程就全部添加到列表中,linux里面也有状态,挂起、运行、暂停、就绪、僵尸、死亡。这些一个个对应着列表,就绪状态的,会被系统调度,然后经过系统的算法和处理,分配cpu线程给到此时的线程和进程,就实现了cpu处理了,所以,本来就不是一一对应的关系。是类似于时间片轮询的操作来实现一个cpu多个线程和进程来使用。
​ 我们可以使用python来申请特定的核心来作为进程,就可以发现,cpu是可以被单独用来执行特定目标的,所以,
系统只是经过算法然后就调度到可用的cpu来进行操作了。我们在linux中,比如编译openwrt,此时就需要使用多线程编译,反正都是生成.o文件,所以,彼此之前不怕干扰,编译就完事了,此时就是linux把可用的cpu丢给编译。我们还可以把进程绑定给一个特定的cpu核心上面,和上面一样的操作就可以实现对核心的操作啦。当然对于我这个初学者,我是把它当成多个单片机,只不过中间有很多数据总线,地址总线、控制总线等

结语

​ 这个博客,是我对于os的内部的原理的初步认识、分析和学习,自然实际中的os,不可能那么简单的啊,就连普通的freertos和rtt都没那么简单的,这个博客,等以后我变强了,再回来修改错误和增添内容哦!

起因

为什么突然开始写这个博客呢,因为上班摸鱼的时候,看到一个国产芯片功能结构图。里面有啥IPU(AI视觉那种的芯片)、GPIO、USB、flash、I2S等众多功能嘛,我还记得我刚学嵌入式的时候,看到这个芯片图都是懵的,现在好了一点。里面突然看到一个OTP,我就想到了OTA,然后去看了一下,不是一个东西啊。这竟然是只能烧录一次的存储空间One Time Programming,烧录之后就会熔丝,就不能再次更改了。星宸科技的芯片功能图如下:

芯片方案链接

存储空间

要明白,只有外行才会叫存储空间为内存,内存其实就是ram,这类东西,就是用来暂时存放变量的。存储空间就是rom,这个是掉电非易丢失。
prom 这个就是只可以烧写一次的rom空间,就是说烧坏了,就没了,这个是好的,防止固件丢失等奇奇怪怪的情况,前提是代码得对,而且对后还得兼容才行。这个是比较考虑水准的。

eprom 这个就是在全新的生产工艺,利用那些射线来清空rom中数据,然后再烧录写。这个是非常耗时耗力的,而且也只能烧写十来次的东西,还得把注意保护不要受光照影响等。

eeprom 这个就是划时代的东西了,这个就可以多次烧写,甚至对每一个位随心所欲的进行操作。这因为是针对每一个位的读写,所以就会导致效率低下。主要是iic来读写,而且因为是对位进行操作,所以它的线路设计的非常复杂,导致了价格高,而且体积大不了。一般都是几十kb,百来kb

因此flash就出来了,flash的特点就是写0可以按位,但是想要重新写的话,就要整页,整块,整扇区的清空才行,你可以认为他们pnp类型中,有部分全部集中在一个集上,就像全部串联起来一刷新就全刷新。再mcu中flash主要是依靠spi进行读写的,注意力spi可不止一种。

烧写办法

这里要介绍一下boot0 和boot1这两个兄弟了
boot0接地,就会默认是烧写到0x08000000中,最常用。
boot0 = 1 boot1 接地,就会是内部ISP引导程序来操作了,就是从0x1FF00000开始了
boot0 = 1 boot1 = 1,此时就是下载到sram中,就是从0x20000000开始了掉电就丢失,适合于测试。

1.ICP(In Circuit Programing)在电路编程
PC上运行的软件(ICP编程工具)通过SWD的接口更新芯片内部APROM、LDROM、数据闪存(DataFlash)和目标用户配置字(Config)

2.IAP(In applicating Programing)在应用编程
IAP就是通过软件实现在线电擦除和编程的方法。IAP技术是从结构上将Flash存储器映射为两个存储体,当运行一个存储体上的用户程序时,可对另一个存储体重新编程,之后将程序从一个存储体转向另一个。此时就是用到了内部的引导程序了

3.ISp(In System Programing)在系统编程
目标芯片通过USB/UART/SPI/I²C/RS-485/CAN等周边接口的LDROM引导代码去更新芯片的内部APROM、数据闪存(DataFlash)和用户配置字(Config)。这个需要用户提前先写入bootloader程序,这个不是内部程序,而是自己写的引导程序,用来接收来自接口的数据,自己进行选择升级,或者跳转。

常见的就是 SWD+J-flash烧录,只要4根线还能debug调试,自然会还有jtag(但是多几根线),而且debug还没swd好、串口烧录、配合软件比如flyMCU STC51等IAP下载方法。

烧写重启

如果是keil中的话,是需要勾选自动下载完毕复位的。否则就要我们来手动点击一下复位键才能运行代码,为什么不能下载好就继续运行代码呢?因为上电了,cpu就开始工作了,但是啥都没有的cpu是根据arm设定的架构规则就开始运行就是从0开始跑代码,但是代码有地址映射的,所以是0x08000000为代码地址,也可以实现代码运行行了,这个算是arm的取巧了。所以,代码下载好了,此时运行是老代码,而且也不知道那些空间可以覆盖,cpu跑没边了,硬件配置也对不上,还容易出现错误,溢出等诸多bug。只有重新上电才会重新加载sram。所以直接复位才是最好的选择。

OS是什么

OS就是operating system,就是操作系统。系统就是为了资源的充分利用,这个定义是非常含糊的,因为fat32文件系统,系统内核,电源管理系统,tcp\ip协议栈等,都可以算是资源的利用和管理,所以,嵌入式系统是需要裁剪和定制的。系统没有高低贵贱之分,只是是否合适,是否满足。当然,驾驭更高级的系统,肯定是要求比驾驭低级的系统需要更多知识点和技术的,需要知道更多原理和技术。
但是两者的本质和使命都是一样,能完成需求的系统就是好系统。虽然系统本身也会消耗资源,但是这是为了更好的分配和高效使用

OS的属性

OS有可抢占和非可抢占两种,可抢占就是,可以直接打断当前的任务,把状态保持到寄存器中去执行优先级高的任务。这就是抢占式的OS系统,实际上,大部分的RTOS都是抢占式系统,设置都没有响应优先级,全都是抢占优先级为主。因为实时真的太重要了。

比如freertos,我们一般都是设为全抢占为主的,但是其实是可以设为响应的,这也有好处,有些任务确实优先级同级,但是不好中断。

还有FCFS 先来先服务的OS,就是谁先到就绪列表,下一个就运行谁,这是有点前后台的味道了。这个就是开触发顺序来进行的任务排序,作为一些特殊的输入设备来说,可能有用吧。

短任务有限算法,这个就是提前制定好所有任务的时间消耗,在当前任务运行完毕之后,在就绪列表中,找到耗时最短的任务有限调用。

时间片调度,这个是作用于同优先级之间的,但是要注意切换中间也是会消耗大量资源的,所有想要权衡调度和时间之间的关系,看都运行都公平,真的值得消耗资源吗?

OS的种类

OS一般有分时系统,实时系统,批处理系统。

分时系统就是多个用户,比如linux,windows。
实时系统就是rt-thread,ucos,freertos
批处理就是集群,大批量的服务器的OS。

一般使用分时和实时系统,如下所示:

以下是我个人觉得的难易程度进行分类
1.裸机
一般有MCU SOC 那些的裸机的开发,这些只适合简单,单一线程的工作

2.RTOS
就连cube都支持直接使用freertos一键生成,还有ucos thread-x那些,本质还是调用API,是rtos接管了很多寄存器和上下文切换的那些中断操作等

3.rt-thread
这个就有点难度了,它可以像freertos那样简单的就调用api实现线程,钩子,空间申请等。但是它有它们没有的,第一个就是BSP,第二个ENV第三个类linux系统,第四个串口操作这个BSP还是有点含金量的,很规范,对于移植来说,就是锦上添花的好东西啊。ENV,这个一下子就让整个编译BSP变得高大上了,还有menucofing呢类linux驱动开发,因为还有一堆宏操作,就是使用宏来把函数添加到列表中的操作等,对于没有接触过驱动开发的人来说,甚至没看懂这些宏定义为什么可以把函数添加进入模块。还有那些文件操作集那些。不得了,不得了,你使用rtt还能使用命令行操作,就是把上述模块添加之后,可以在打开特定连接的打印串口,实现串口输入指令来调用模块函数,这真的有操作的

4.linux 主板OS(电脑主板,识别设备,调度起来) 树莓派
这个难度就非常吓人了,在rtt的基础上面还得学习设备树,字符设备,平台设备,混杂设备,主设备号,次设备号。而且你会发现一件事,rtt是没人写驱动因为驱动cube已经生成了,写好kconfig文件就可以了。但是linux不一样了,驱动得人来写才行得。rt-thread一般都是写好了国内大部分芯片的驱动的,照猫画虎就好了,设备树那些就只有smart版本才有了。

5.IOS 安卓 鸿蒙 CUDE(GPU的那个系统)
可没说让你去搞应用开发,是去理解和写驱动那些,不同机型,底层得io,设备啥的都是不一样的,这个一般都是芯片厂商写的,如果以后进了芯片厂,这些就得要掌握了

6.windows Mac Ubuntu等
这个别想了,多年高强度迭代、一个人不可能掌握,能熟练使用就非常了不起了,而且还有好多没开源,开源学习成本太高了,回报基本为0.

OS的选择

一定要明白一点,OS没有高低贵贱之分,在嵌入式里面没有优越,只有适合,只有满足即可,一切为了需求,一切够用即可。
MCU不是一定要上系统,难道只有几个功能,还要几个任务相互抢?浪费空间,这个适合都可以上51了。

就算是SOC,去看厂商给的代码,大概率都是有一个rtos的demo,为什么?因为不见得一定要上类linux1或者linux那些系统啊,rtos就够用了啊!有的时候,想要的就是快速的无条件的实时响应!所以适合简单而好用的小设备,大设备因为太多硬件的,光用rtos很难
进行完美的资源控制和调度了,开发也困难。

至于那些复杂,多应用的,这个时候就要利用mmu,来给空间进行更多操作,底层全部丢给系统,这样上层才可以随心所欲实现应用切换,应用下载的功能。虽然mcu也是可以实现动态加载模组(就是写一个可以控制sram的程序,把app下载到flash,使用的时候
,把代码拷贝到空余的sram空间,这就想手机一样,可以使用空间,可以释放空间,而且平时不占据sram,sram只有一个sram控制系统),但是,太复杂了,而且引脚躲起来了,完全就不是开发了,这点厂商也想到了。

再高级一点的设备就上安卓,鸿蒙了,澎湃OS等了。再高级一点就是主机和pc端了啊,上linux,上windows等

然后就是服务器了,这个不用考虑了,只能是linux系统了,可以Ubuntu,kail等

note:优先级也是有动静态之分的,一般我们使用的rtos最好都是固定好优先级,不想那种linux高级os可以动态修改优先级,动态释放和调用资源,因为rtos本身就是为了高效、实时和稳定。功能越复杂,可能的问题就越多,这是无法避免的

结语

千万不要神话系统和技术,觉得越高级系统就越神圣,其实不是的,我们一切都是为了需求,没有任何一个系统是完美的,都是在自己所在领域非常好用,作为嵌入式开发人员,也不要为此而产生过度的优越感,这样可能会让自己止步不前,从而顾此失彼,看不起其他技术。这一点就要牢牢铭记,沿用一段文章内容。如下

git 管理

git有啥东西?不就是add、commit、push 、 pull 、reset 、log 、reflog 、status 、tag、 branch 、checkout….
常用也就是这些了,但是,还有很多其他的操作,这些操作是更加面向团队和管理的首先,pr,还有分支管理和分支保护,管理员,fork,ssh

git ssh

ssh就是建立一个安全的下载链接罢了,但是如果是下载私有仓库,那还是得进
行github登录才能实现下载,不是说有ssh就可以直接下载了

git fork

我们经常可以看到github上面好多代码贡献的,比如linux,这个提交量
非常大,而且fork数量也非常多。
fork不等于clone,更不等于zip下载。

zip下载就是下载一个版本或者当前版本,常见就是Tag进行版本管理,这些是没有.git。

clone和ssh就是把整个开发的过程的git都下载过来,这个,就是直接把开源库复制一份到自己的分布式主机上面。此时是无法通过push提交到原仓库的。那为什么gitee那些又可以拉人,又可以下载和提交呢,那是因为所有clone都是默认为不可push,只有管理员去修改我的访问和提交权限,我才能提交和修改。

fork 这个就是不一样了,是自己生成一个仓库副本,就是在github上面直接生成这个这个仓库,此时,就要切换为一个新分支,然后,正常提交,然后push给新分支,然后选择pr就可以提交到原仓库中,然后等待原仓库管理员去解决冲突和选择、审批等。如下操作:
1.首先打开一个项目,然后直接fork,点击创造fork。这样就会在我的账户下面生成一个fork副本。
2.此时可以使用git clone 或者ssh来下载。
3.git remote add upstream https://github.com/original-author/project-name.git
4.新建分支 git checkout -b feature-or-bugfix
5.修改和提交代码
6.推送分支 git push -u origin feature-or-bugfix
7.创建Pull Request (PR) 跟提交分支一样的操作是
8.等待原仓库管理人进行处理

如果不想要fork,就进入到fork仓库,点击settings中的delete仓库,对,就是删库就好了。

git branch保护

为什么要分支保护啊?我们在看gitee一些项目,可以看到main分支上面有一把锁,这个时候就是管理员把main分支进行上锁,就是变为可读模式,此时就是无法修改。使用的是Lock branch这个一般是短期无需修改才会选择这个状态,这个是管理员随时可以修改得状态。

一般常用就是Require a pull request before merging,就是只能通过pr合并来实现main分支的更新,不允许直接push提交,这样就可以让整个结构和流程更加简洁和方便管理,防止胡乱提交导致版本错误等诸多问题。

此时还可以Require approvals,这个就是添加审核人,审核人必须发言和评价才能合并。

使用Do not allow bypassing the above settings就是管理员和用户一视同仁,这个不建议勾选,因为管理员就是为了快速方便管理分支和仓库,只要管理员认真点,仔细点就行了,只要分支保护做的好,都是ok的

git 令牌 & git 小组

git 令牌
这个就是把我的私有仓库通过令牌的方式,给与他人使用权限。这个就是在个人然后点击settings,然后点击Developer settings,
然后选择token,两个都可以new和classic,无脑选择new就行了,然后就是权限限制读写操作,然后就仓库限制,时间天数限制。然后点击确认就是生成令牌。使用git 令牌下载只能使用https下载不能使用ssh下载。下载的时候,直接git clone 仓库,然后写上我的名字,然后就输入这个生成的token就可以下载,分享啦。

git 小组
就是拉人到自己的仓库,一起创作进入仓库,点击settings,然后点击collaborators,添加用户的邮箱,然后进入即可,然后就是小组啦自然还有那些企业版啥的,我目前还没用过,我感觉大差不差应该都是这个原理,企业版就是,可以把所有人拉到一个开发组,然后方便拉近仓库和管理。

git 小组踢人
就是在collaborators中,点击删除就好了

git 小组主动退出
点击右上角的头像,点击setting,点击仓库,就可以看到共同工作的仓库,里面有leave按键,然后确认就好了

git总结

git一句话分布式管理,这个本质就是为了团队,大家一起工作,小公司可能用不到git,但是为了项目管理,为了整体发展git的重要性非常高。git只是自己一个人用,那么就是失去了分布式的意义了,这不就是变成了一个同步了吗?没用的!!!

起因

我本人是不喜欢用这些什么库的,代码就是要开源才能大家一起进步,但是,这些终究只是极客幻想。现在还是打工仔呢,等日后有机会开工作室、独立研发或者商单的时候,在考虑这种事情吧。那么公司的代码,肯定就是要保密啦。保密的办法就是转为库

a静态库

静态库windows是.lib linux是.a文件,这两种静态库的二进制格式不一样,所以不能相互使用。

a静态库生成
1
2
3
gcc –o mylib.o –c mylib.c
ar rcs libmylib.a mylib.o
#此时生成了libmylib.a
a静态库使用

这个就非常简单了,就是把头文件添加到项目中,然后添加库文件到项目中,然后#include “mylib.h”即可。然后根据头文件中的数据和函数进行调用即可

so动态库

linux和UNIX的动态库一般是.so,这个好处就是,不用编进代码之中,缺点就是想要终端或者平台提前准备好动态库文件哦。#动态库库生成 #可以考虑写成makefile文件或者shell文件
windows的动态库一般是dll。

so动态库生成
1
2
3
4
gcc –fPIC -o mylib.o -c mylib.c		 	#(生成符号表)
gcc –shared -o libmylibs.so mylib.o #(防止重复加载)
#或者
gcc –fPIC –shared –o libmylibs.so mylib.c (可以省略 -c 和 .o后缀文件名)
so动态库使用
1
2
#方法一
gcc –o test test.c -lmylibs #后面就是接上动态库所在文件夹即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#方法二
#使用系统函数来使用动态链接库
#include <stdioh>
#include <dlfcn.h>
int main (void)
{
void *handle;
char *error;
void (*testFunction)(); //要变成指针函数来用(由符号表引出的指针)
if ((handle = dlopen(“/usr/lib/libmylibs.so”, RTLD_LAZY) == NULL)
{
printf (“dlopen error\n”);
exit(1);
}
testFunction = dlsym(handle, "testFunction");
if ((error = dlerror()) != NULL)
{
printf (“dlsym error \n”);
exit(1);
}
testFunction ();
dlclose (handle);
exit(0);
}
lib静态库
1
2
3
4
5
6
7
8
9
#方法一 打开cmd
gcc -c test.c
ar -cr libadd.a add.o
#编译
gcc main.c libadd.a -o test.exe
#或者
gcc main.c -ladd -L./ -o test.exe
#进行测试
test.exe

#方法二 打开keil 直接勾选output中的生成lib文件,生成库的时候不要把main和h文件添加进来,main是最好不要,因为可能冲突。h文件可有可无。然后点击编译即可。
使用方法和.a文件是一样的

dll动态库

我们一般使用vs或者devC++那些ide就可以生成了,虽然使用gcc命令行也可以实现编译,但是毕竟别人ide做了适配啥的,还是一键搞定方便。用vs举例子,点击生成新的动态库项目,然后在解决方案中的源文件和头文件目录中,添加需要的代码。然后选择X64模式,release版本,编译就可以了。然后就生成了dll文件lib文件,把这两个+代码头文件复制到新的项目所在的目录中。新的项目可以使用vs的控制台模式项目,然后在解决方案的资源文件中添加lib文件,不是dll文件哦,再把头文件添加到头文件目录中。然后声明头文件,调用即可。

cpp文件编写

1
2
3
4
5
6
7
8
#include "pch.h"    //必要的头文件
#include "myDll1.h" //自己的头文件

//一个简单的函数
int funTest(int a, int b)
{
//....
}

h文件编写

1
2
3
4
5
6
7
8
#pragma once  //规定只编译一次

//宏定义,增加可读性
#define DATAEXCHANGEDLL_API __declspec(dllexport)

//按C进行编译,声明.cpp中的函数
extern "C" DATAEXCHANGEDLL_API int funTest(int a, int b);
//为了兼容c,如果全是cpp文件的话,那么就无所谓了

对了vs还可以选择静态库项目,也是生成lib文件,使用的话,就是在解决方案的资源文件
中添加lib文件就可以了。自然也是可以gcc生成了,谁没事找事呢(doge)

结语

有些公司会选择visual GDB来进行开发stm32,这个时候就是linux的静态库,自然keil才是开发stm32的主流,所以还是使用的.lib文件,自然两者都会终究是好的

本人菜鸡,没想到因为代码写的太乱了,导致出现头文件内容错误bug

起因

因为我在bsp代码中使用了,一个其他h文件中的枚举作为参数。一开始枚举定义在bsp,所以,所有都正常,但是,我想集中和降低耦合,然后就出现问题了,bsp.h中的函数和一些变量显示没定义。

原因

因为存在依赖,因为c和cpp很多语言都是顺序编译的,很少有那种就是先不报错,等待延后依赖的。所以,一旦发现没定义就会报错,停止编译了,这个其实也是很合理的,如果你是linux的话?编译了几小时,然后依赖真的没有,那完蛋了,真的浪费时间了,所以,就需要人为控制和人为降低耦合来实现

例子

这是一个非常经典的错误,就是后面头文件对前面的头文件存在非常明显的依赖。如果日后,遇到这种问题就说明你写的和我一样是一坨,哈哈哈。就是耦合度太高了,还是建议降低耦合,尽量模块化,独立接口啥的

结语

低耦合高内聚模块化,天天喊,天天叫。哎~~ 如果真的非常依赖bsp的话,可以考虑这样写哦。因为#ifndef #define #endif和 #pragma one的原因,那些头文件只能加载一次,但是如果内部又加载其他的头文件就可以导致,下面的没执行完毕,导致很多数据丢失,编译失败。

错误原因:因为调用头文件,成环了,所以,导致缺失。 头文件有先后顺序才能执行。

这些本质都是因为这些文件关联了其他文件,没实现线性树状的结构导致的高耦合错误。

开始填坑

这里就直接使用arduion ide进行开发,在芯片库中搜索esp32就可以找到esp32的所有资源和下载。我们要知道ESP32是搞物联网的,如果你说你要用esp32来当纯主控,那确实不太好的,因为引脚少,功能有限制。比如有一些引脚,是只有only read pin,因为他和arm的架构不一样,而且是双核,所以使用的时候,需要注意的。甚至有一些同时支持wifi和蓝牙。

如果是用PlatformIO或者espressif idf的话,自己去vscode上面下载这个插件就可以了,其实也是大差不差的。但是arduion ide一键操作,无脑啊,还是非常方便就是界面有点老。其实都是一样的啊

你甚至可以espressif idf直接安装本地,然后使用命令行编译,所以他们其实都是调用这个来作为编译链的,不同的ide,不同界面,本质还是选板子,选串口,选择实例,烧录下载仿真。

使用microPython的话,可以查考如下的博客。

MicroPython开发ESP32

arduion ide

使用arduion,有一个很明显的特点,第一就是cpp,第二两个函数 setup和loop,有点hal的意思了我们使用esp32的目的,肯定是为了一件事,就是联网和io控制。所以点击实例中的esp32实例,修改一下连接的wifi密码和名称,设模式为用户找到一个联网的模板进行修改即可,修改添加一个mqtt模块,添加一个url功能,就能搞定大部分工作了。剩下的都是主控的操作了,什么io输出啊,iic那些

代码编译和烧录

编译就是找到上面的按键进行编译就是了。烧录的话,是通过usb口的iap下载烧录的,所以你得选择开发板和串口,编译成功就是烧录下载了此时还可以进入调试界面来看出现运行状态

总结

开发esp32非常简单的,因为很多东西都是模块化好的库,使用就好了,因为物联网发展的非常好,而且使用的是cpp开发,而且使用的cpp语法也是比较简单的,所以,一般都是找一个模板就可以开始操作了,联网成功之后,剩下就是本地主控操作了。就是一个mcu主控+wifi模块罢了。只不过开发arm mcu的时候,是通过at指令来控制wifi的连接和收发,但是本质还是通过主控去对数据进行处理。此时就是把wifi和主控合二而一,但是放心。厂商已经写好硬件驱动,直接调用api就可以连接和收发了。还可以进行分频和扩频,蓝牙和wifi复用功能,这些都是调用厂商的api,找一个demo就好了

对了,还有一件事,esp32是可以上rtos的哦,rtt和freertos那些都是可以的。