1.系统时钟计算公式:SYSCLK = HSE*N/(M*P)

2.定时器中断/PWM频率计算公式:TIMXPWM/INT = TIMXCNT/ARR

3.中断优先级:数字越小,优先级越高,具有高先占式优先级的中断可以在具有低先占式优先级的中断处理过程中被响应,即中断嵌套。当两个中断源的先占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。

如果这两个中断同时到达,则中断控制器根据他们的从优先级高低来决定先处理哪一个;

如他们的先占式优先级从优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。

4.NVIC_PRIORITYGROUP_4:4位抢占优先级,0位响应优先级

5.任务优先级:数字越大,优先级越高

6.定时器:PWM模式1下,TIMx_CCR1大时有效;PWM模式2下,TIMx_CCR1小有效。默认向上计数,高电平有效。CCRX的值决定占空比,CCRX的值越大,占空比越大。定时器功能强大,用于全局定时,用于PWM控制电机、LED、蜂鸣器,用于传感器固定周期读取数据,用于捕获遥控信号

7.任务状态:运行<-就绪,就绪<-挂起,就绪<-阻塞

8.任务调度:新创建的任务默认就是就绪态的,如果当前没有比它更高优先级的任务运行那么此任务就会立即进入运行态开始运行。任务进入阻塞有多种方式,有延时阻塞,有获取信号量阻塞,有等待通知阻塞。调度器总是选择所有能够进入运行态的任务中具有最高优先级的任务。一个高优先级但不能够运行的任务意味着不会被调度器选中,而代之以另一个优先级虽然更低但能够运行的任务。大多数应用程序中都不会用到挂起状态。高优先级的任务一旦就绪就能及时运行。

9.中断:ISR不改变当前任务的状态。尽管IRQ发生以后,当前运行着的任务执行被暂停,CPU转而执行ISR的代码,但当前任务的状态仍然是Running,并不是变成其它状态——这与任务被抢占明显不同。

尽量减少中断程序运行的时间

10.信号量:使用信号量的目的是为了保护资源,创建一个信号量相当于创建一个队列,创建一个队列相当于分配内存。不管是创建信号量还是释放信号量本质上都是对队列进行操作。主要用于任务之间的资源管理。信号量(semaphores)是 20 世纪 60 年代中期 Edgser Dijkstra 发明的。使用信号量的最初目的是为了给共享资源建立一个标志,该标志表示该共享资源被占用情况。这样,当一个任务在访问共享资源之前,就可以先对这个标志进行查询,从而在了解资源被占用的情况之后,再来决定自己的行为。

互斥信号量:与一般信号量不同的是互斥信号量可以改变优先级,使得高优先级也无法打断低优先级的任务

递归互斥信号量:递归互斥信号量可以看作是一个特殊的互斥信号量,已经获取了互斥信号量的任务就不能再次获取这个互斥信号量,但是递归互斥信号量不同,已经获取了递归互斥信号量的任务可以再次获取这个递归互斥信号量,而且次数不限!一个任务使用函数 xSemaphoreTakeRecursive()成功的获取了多少次递归互斥信号量就得使用函数 xSemaphoreGiveRecursive()释放多少次!比如某个任务成功的获取了 5 次递归信号量,那么这个任务也得同样的释放 5 次递归信号量。

11.缓冲区:StreamBuffer也是一种数据结构,类似队列,在使用外设与外界进行通信时,不管是发送还是接收,数据都需要经过缓冲区,为什么需要缓冲区?因为数据是不停发送和接收的,程序不应随数据进行改变,而是在它想进行处理时就从缓冲区里拿出来进行处理

12.USB:USB也是一种外设,用于通信,类似串口

13.文件系统:文件系统是为了存储和管理数据,而在存储介质上建立的一种组织结构,这些结构主要包括系统引导区,目录和文件。在使用文件系统前,需要先对存储介质进行格式化,格式化之后,存储介质会创建一个文件分配表和目录。这样,文件系统就可以记录数据存放的物理地址和剩余空间。使用文件系统时,数据都以文件的形式存储。写入新文件时,先在目录中创建一个文件索引,它指示了文件存放的物理地址,再把数据存储到该地址中。当需要读取数据时,可以从该目录中找到该文件的索引,进而在相应的地址中读取数据。文件系统的存在使我们在存储数据时,不再是简单的向某个物理地址直接读写,而是要遵循它的读写格式。如经过逻辑转换,一个完整的文件可能被分成多段并存储到不连续的物理地址,以及使用目录或链表的方式来获知下一段的位置。在SPI方式读写SD卡的试验中只完成了向物理地址读写数据的工作,而根据文件系统格式的逻辑转换部分则需要额外的代码来完成。实质上,这个转换部分可以理解为,当我们需要写入一段数据时,由它来求解向什么物理地址写入数据、以什么格式写入,以及写入一些原始数据以外的信息(如目录)。这个逻辑转换部分代码我们也习惯称之为文件系统

14.SPI:传感器采用SPI通信,SPI支持一主多从模式,通过片选管脚进行从机选择

15.static:

面向过程:

静态局部变量:该变量在全局数据区分配内存;静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化;静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为 0;它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束。

静态全局变量:全局静态变量是显式用 static 修饰的全局变量,作用域是声明此变量所在的文件,其他的文件即使用 extern 声明也不能使用。对于编程来说,使用静态全局变量的一个优点就是:本变量只能在本文件里面进行修改,不用担心在别的文件中有操作

静态函数:使用static用于函数定义时,对函数的连接方式产生影响,使得函数只在本文件内部有效,对其他文件是不可见的。这样的函数又叫作静态函数。使用静态函数的好处是,不用担心与其他文件的同名函数产生干扰,另外也是对函数本身的一种保护机制。static函数是不能直接被其他文件直接调用的

面向对象:

静态成员变量:静态成员变量是该类的所有对象所共有的。对于普通成员变量,每个类对象都有自己的一份拷贝。而静态成员变量一共就一份,无论这个类的对象被定义了多少个,静态成员变量只分配一次内存,由该类的所有对象共享访问。所以,静态数据成员的值对每个对象都是一样的,它的值可以更新;

静态成员函数:静态成员函数为类服务而不是为某一个类的具体对象服务。静态成员函数与静态成员变量一样,都是类的内部实现,属于类定义的一部分。普通成员函数必须具体作用于某个对象,而静态成员函数并不具体作用于某个对象。与普通函数相比,静态成员函数属于类本身,而不作用于对象,因此它不具有this指针。

什么时候用?C++里如果需要调一个在类里,但跟类的实例无关的函数,就需要

keil里全局搜索,如果是static的变量不会纳入搜索范围

16.inline:函数调用是有时间和空间开销的。程序在执行一个函数之前需要做一些准备工作,要将实参、局部变量、返回地址以及若干寄存器都压入栈中,然后才能执行函数体中的代码;函数体中的代码执行完毕后还要清理现场,将之前压入栈中的数据都出栈,才能接着执行函数调用位置以后的代码。

如果函数体代码比较多,需要较长的执行时间,那么函数调用机制占用的时间可以忽略;如果函数只有一两条语句,那么大部分的时间都会花费在函数调用机制上,这种时间开销就就不容忽视。

为了消除函数调用的时空开销,C++ 提供一种提高效率的方法,即在编译时将函数调用处用函数体替换,类似于C语言中的宏展开。这种在函数调用处直接嵌入函数体的函数称为内联函数(Inline Function),又称内嵌函数或者内置函数。

当函数比较复杂时,函数调用的时空开销可以忽略,大部分的 CPU 时间都会花费在执行函数体代码上,所以我们一般是将非常短小的函数声明为内联函数。

使用内联函数的缺点也是非常明显的,编译后的程序会存在多份相同的函数拷贝,如果被声明为内联函数的函数体非常大,那么编译后的程序体积也将会变得很大,所以再次强调,一般只将那些短小的、频繁调用的函数声明为内联函数。

我觉得内联函数之所以能work,是因为它本身是内联于它所在的cpp文件的,编译的时候不会去链接外部

如果你想inline某个函数,这个函数可以是写在cpp里面,但是如果我想在别的cpp里面实现这个函数呢?我可以用头文件声明去做吗?

编译是对一个cpp文件讲的,编译过程你的函数都不在cpp上面怎么inline,那就是在头文件上面写函数了,才能Inline

总结:inline必须是通过头文件的方式做

首先要说明的是一个inline函数具有静态链接(static linkage),不会被文件以外者看到。

18.MAVLink:Mavlink是为小型飞行器和地面站(或者其他飞行器)通讯时常常用到的那些数据制定一种发送和接收的规则并加入了校验(checksum)功能,本质上是一个通讯协议。

大家可以在Mavlink的官网上找到它们的定义。简单的说MavLink协议上传输的都是各种消息(Message),消息其实就是一种数据包格式,是为了通信而准备的,消息的格式多种多样。而命令(Command)是一种数据,格式完全统一,通过命令号来解释不同的意义,这也就方便在飞行任务中定义、存储、解释、执行。

19.函数指针:本质上是一个指针,指向函数的指针。函数存放在内存的代码区域内,它们同样有地址。如果我们有一个函数,那么,它的地址就是函数的名字,这一点如同数组一样,数组的名字就是数组的起始地址。函数指针的定义方法为:函数类型 (*指针变量名)(形参列表)。调用方式与一般函数一样。

int (*p) (int x);

这里定义了一个函数指针,指针变量名是p

函数指针数组:函数指针数组是一个其元素是函数指针的数组。那么也就是说,此数据结构是是一个数组,且其元素是一个指向函数入口地址的指针。调用方式与一般函数一样。

结构体调用函数的方式:Xiao_Ming.dance();

20.vTaskDelayUntil:用于以固定周期去执行任务,调用时会阻塞任务。任务中第一次调用函数vTaskDelayUntil的话需要将 pxPreviousWakeTime初始化进入任务的 while()循环体的时间点值。在以后的运行中函数vTaskDelayUntil()会自动更新 pxPreviousWakeTime。

应用例子:

vTaskDelayUntil( &xLastWakeTime, 0.01*configTICK_RATE_HZ ); //延时10ms

void vTaskDelayUntil( TickType_t *pxPreviousWakeTime, const TickType_t xTimeIncrement );

参数 xTimeIncrement 用于设置延迟的时钟节拍个数 

第二个参数是定时节拍数

21.如何退出当前任务?一般使用延时函数将任务阻塞掉,让出CPU使用权。

22.如何进入别的任务:延时时间到后,任务进入就绪态,系统根据优先级选择任务执行。

23.空闲任务:空闲任务的优先级是最低的,系统空闲的时候才会执行。空闲任务的主要工作是释放内存,被删除任务的堆栈和TCB立即释放。

24.构造函数:构造函数是类的一种特殊成员函数,构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。创建类类型对象时由编译器自动调用,保证每个数据成员都有 一个合适的初始值,并且在对象的生命周期内只调用一次。

在子类的构造函数中,加上一个冒号(:),然后加上父类的带参构造函数,这就是父类构造函数的显式调用。这种一般声明在类的外面

子类::子类构造函数:父类构造函数(参数)

构造函数使用:Line line(10.0);

C++中没有显式定义构造和析构函数时,编译器会自动添加一个构造函数和一个析构函数

C++中类的默认构造函数是一个空函数,什么也不做,如果用户在类中声明了构造函数,默认构造函数就不再起作用了

默认构造函数和初始化构造函数在定义类的对象的时候,完成对象的初始化工作。

25.数据类型:

unsigned char 8位 一个字节 255

unsigned short int 16位 两个字节 65535

unsigned int 32位 四个字节 4294967295

unsigned long long int 64位 八个字节

float 四个字节 double 八个字节 范围很大很大就是了。。。

不同数据类型强制转换有可能会导致数据精度丢失

26.函数重载:函数可以根据参数进行重载,构造函数也可以重载,使用哪个取决于参数

27.运算符重载:可以对运算符进行重载,通常用于对象的运算,什么时候使用重载的运算符也是取决于参数,运算符连同操作数可以看成一个函数调用。

28.双冒号运算符:用于引用类的静态函数成员,调用命名空间里的成员

29.静态成员数据:静态成员数据并不属于任何一个对象,它属于类,因此引用它时要用类去用

30.静态函数成员:和静态成员数据是一样的,不属于任何一个对象,属于类,引用时要用双冒号运算符

31.联合体:联合体的成员共用一块内存空间,当对某个成员赋值时,别的成员的值会被覆盖掉

32.this:this是一个指针,指向当前对象,this存放的是当前对象的地址。this一般写于类的成员函数里面,此时代表的就是当前对象,this->a,就是对该对象的成员a进行操作

33.延时:延时有好多方法,如果是在系统任务里可以直接使用系统的延时API,但这样会把任务给阻塞掉,系统延时API最小延时时间应为一个节拍数,一般freertos的时钟频率为1000HZ,就是最小延时1ms,如果延时要小于1ms,那就不能使用系统API,这时候考虑定时器延时,通过获取定时器计数值就能进行精准延时。

34.DMA:直接存储器访问,DMA传输无需经过CPU直接控制传输,减轻CPU负担。

使用DMA的好处,传统使用串口收发需要频繁进入中断进行缓冲区读写操作,理论上有多少个数据就要进入多少次中断,如果使用DMA就应该只需进入一次中断?

35.类访问修饰符:public公有成员,随意访问;private,外部不可访问,而且派生类都不能访问,只能在原本的类里面操作,默认是private;protected,和private一样外部不可访问,不一样的是派生类可以访问。

之所以有这么多修饰符也是使程序更加健壮,比如说有些函数就是不能被随便被外界使用的就应该用protected保护起来,以免程序出现错误

36.virtual:虚函数,可在派生类中对函数进行重写

37.__DSB():汇编指令,目前没搞懂它的具体作用,对于MCU是空指令?

38.Cache:高速缓存,I-Cache是指令高速缓冲存储器,D-Cache是数据高速缓冲存储器,没体会到Cache的作用

高速缓存就是高速存储器块,包括地址信息和相关联的数据,它的目的主要是为了提高对存储器的平均访问速度。

执行代码的时候CPU每次都要去访问FLASH,而我们知道FLASH的读取速度是远远低于CPU的主频的,所以需要设置一个等待周期来保证能够正确地从FLASH中把数据读出来。

有了Cache之后,第一次访问FLASH读取出需要的指令和数据之后,可以把指令和数据先放到Cache里,当下次再需要这部分内容的时候就不需要再去访问FLASH,而是直接从Cache中把这部分内容读出来,这样就可以提高存储器的平均访问速度和程序的执行速度。

如果每次CPU要读写SRAM区的数据,都能够在Cache里面进行,自然是最好的,实现了200MHz到400MHz的飞跃,实际是做不到的,因为数据Cache只有16KB大小,总有用完的时候。

因为DMA是直接与SRAM交换数据的,而CPU与SRAM之间隔了一个Cache,如果DMA更新了某个数据到SRAM,CPU要去访问,而恰好Cache中有,那么CPU就不会去SRAM中拿,就会拿到Cache中已经过时的数据。

因此使用了DMA的内存区要配置为无Cache或者拿数据前清一次Cache。

39.MPU:内存保护单元,保护不需要用到Cache的内存

40.RTC:实质是一个掉电后还继续运行的定时器,要有后备电池给stm32供电

41.__packed:__packed 限定符将所有有效类型的对齐边界设置为 1。这就意味着:

1、不会插入填充以对齐压缩对象

2、使用未对齐的访问读取或写入压缩类型的对象。

使用 __packed 限定符声明结构或联合后,__packed 将应用于该结构或联合的所有成员。成员之间或结构末尾均没有填充。必须使用 __packed 声明压缩结构的所有子结构。

若要将结构映射到外部数据结构或访问未对齐数据,__packed 限定符非常有用;

42.template<typename T>:模板T,定义模板的固定格式,比如你想求2个int float 或double型变量的值,只需要定义一个函数就可以了

43.HardFault_Handler:硬件错误中断函数,STM32出现硬件错误可能有以下原因:(1)数组越界操作;(2)内存溢出,访问越界;(3)堆栈溢出,程序跑飞;(4)中断处理错误;

44.using namespace std:C++标准程序库中的所有标识符都被定义于一个名为std的namespace中。使用STL需要添加这句语句。

45.指针:指针初始化赋值为0表示其不指向任何地址,*ptr表示指针ptr指向的值,&i表示i的地址,对于结构体类型的指针,想引用其成员,使用指针变量名->成员名。

指针的作用非常大,在程序里面我们只需要记录变量或函数的地址就可以进行操作了

如果一个指针定义了没有赋值,那就是个野指针,不知指向何处,如果赋值为0,那就是空指针,不指向任何变量

函数参数是指针的时候要传地址,而不是变量本身

46.信号量获取:

函数原型:BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xBlockTime)

参数:xSemaphore :要获取的信号量句柄。xBlockTime: 阻塞时间。

返回值:pdTRUE: 获取信号量成功。 pdFALSE: 超时,获取信号量失败。

在嵌入式操作系统中互斥型信号量是任务间资源保护的重要手段。调用函数xSemaphoreTake获取信号量资源,如果信号量没有被任务占用,将直接获取资源。如果信号量被占用,任务将由运行态转到阻塞状态,等待资源可用。一旦获取了资源并使用完毕后会通过函数xSemaphoreGive释放掉资源。

一般我们会设置阻塞时间,如果资源不可用,任务会进入阻塞态,相当于延时。如果阻塞时间设为0,那就直接返回,不会阻塞。在阻塞时间内,当资源可用时,系统会有中断触发,任务会进入就绪态,返回pdTRUE,超过阻塞时间,任务也会进入就绪态,返回pdFALSE

47.xTimerPendFunctionCallFromISR():函数将一个普通函数作为参数“提交”给系统服务,让系统自带的Daemon Task执行这个函数。提交时一并指定两个参数传递给这个函数。Daemon Task 受调度器管理,它的任务优先级由 configTIMER_TASK_PRIORITY 指定。Daemon Task 何时执行提交的函数,就要看系统是否空闲了,当它获得执行机会时,就会从命令队列里面取出要执行的函数入口地址和参数去执行。

什么时候会用到这个函数?集中处理中断延迟

所谓集中处理中断延迟,是使用xTimerPendFunctionCallFromISR()API将接下来的处理函数作为回调函数提供给RTOS的内核守护任务。通过传递处理函数的指针,在中断服务函数退出后,相应的处理函数会得到执行。从中断退出,到相应的处理函数得到执行,在时间上是连续的,好像这个函数直接由中断调用一样。

48.new:new int[5];//仅仅是分配了空间,并没有生成对象。开辟一个存放整数的存储空间,返回一个指向该存储空间的地址(即指针),可以类比其他变量类型使用

new int[5] 仅仅分配了空间, 但是 new A(),不仅仅为对象obj在队上分配了空间, 而且还调用了 A的构造函数,生成了这个对象。

49.map:map是C++标准库里的一个类,也叫做容器,它提供搜索和插入的数据结构,有一一对应的功能。从数据结构来说,map是标准库里写好的一个类。使用时要#include <map>。C++中map提供的是一种键值对容器,里面的数据都是成对出现的。每一对中的第一个值称之为关键字(key),每个关键字只能在map中出现一次;第二个称之为该关键字的对应值。

从直觉上看,map像是一个表

map的基本操作函数:

begin()               返回指向map头部的迭代器

end()                  返回指向容器尾元素的下一个位置的迭代器,也就是说end指示的是一个不存在的元素

find(k)                找到一个任意一个键为k的元素,返回该元素的迭代器,如果没有键为k的元素,则返回end()

insert( pair<>()) 插入元素 

erase(it)             删除元素

50.delete:delete ptr 代表用来释放内存,且只用来释放ptr指向的内存。

51.时间片调度:这个在freertos里面是默认打开的,而抢占式调度需要我们用户自己开启,一般在freertosconfig.h中使能。所谓时间片调度就是每个任务都有其最大的运行时间,这个时间为系统节拍,一般的任务会在时间片里面主动进行切换,如果运行时间超过系统节拍就会被动发生任务切换。

52.const:一个关键字,如果你需要这样一个变量(暂且称它变量),在声明时就赋一个初始值。之后程序在其它任何处都不会再去重新对它赋值,用const 。

用const修饰函数的参数:如果输入参数采用“指针传递”,那么加const 修饰可以防止意外地改动该指针,起到保护作用。

在函数后加const的意义:我们定义的类的成员函数中,常常有一些成员函数不改变类的数据成员,也就是说,这些函数是"只读"函数,而有一些函数要修改类数据成员的值。已定义成const的成员函数,一旦企图修改数据成员的值,则编译器按错误处理。

为什么要这么处理?这是让程序员一眼就能看出这个函数的特点,他的类成员无法修改

在C++中,const 全局变量被用来替换一般常量宏定义。因为虽然 const 变量的值不能改变,但是依然是变量,使用时依然会进行类型检查,要比宏定义的直接替换方法更严格一些

53.Iterator:迭代器,迭代器是一种检查容器内元素并遍历元素的数据类型。迭代器(Iterator)是指针(pointer)的泛化,它允许程序员用相同的方式处理不同的数据结构(容器)。

54.break:C++ 中 break 语句有以下两种用法:

  1. 当 break 语句出现在一个循环内时,循环会立即终止,且程序流将继续执行紧接着循环的下一条语句。
  2. 它可用于终止 switch 语句中的一个 case。

如果您使用的是嵌套循环(即一个循环内嵌套另一个循环),break 语句会停止执行最内层的循环,然后开始执行该块之后的下一行代码。

55.i++ 与 ++i 的主要区别有两个:
1、 i++ 返回原来的值,++i 返回加1后的值。
2、 i++ 不能作为左值,而++i 可以。

56.心跳包:心跳包由6个数据组成

第一个参数是占一个字节的飞行器类型数据(type),这个数据表示了当前发消息的是什么飞行器,比如四旋翼,固定翼等等。

第二个参数是自驾仪类型(autopilot),比如apm,ppz,Pixhawk等飞控,具体定义查找和之前查找飞行器类型时的方法一样。

第三个参数是基本模式(base mode),是指飞控现在处在哪个基本模式,对于发心跳包的地面站来说没有意义,对于发送心跳包的飞控来说是有意义的。

第四个参数是用户模式(custom mode)

第五个是系统状态(system status)

第六个是mavlink版本(mavlink version),现在是“3”版本。

57.定义:只要变量定义了就会分配内存空间,对于编程来说,定义是第一步,首先定义你的研究对象

58.memset:char型初始化函数,头文件:<string.h> 或 <memory.h>,函数原型:void *memset(void *s , int ch , size_t  n )

memset(结构体/数组名 , 用于替换的ASCII码对应字符 , 前n个字符 );

函数解释:将s中的前n个字节用ch替换并且返回s

函数作用:在一段内存块中填充某一个给定的值,常用于较大的对结构体和数组的清零操作。

59.拷贝构造函数:classname (const classname &obj) { // 构造函数的主体 },拷贝构造函数通常用于:(1)复制对象把它作为参数传递给函数。(2)通过使用另一个同类型的对象来初始化新创建的对象。

60.类的赋值运算符:拷贝构造函数和赋值运算符的行为比较相似,都是将一个对象的值复制给另一个对象。调用的是拷贝构造函数还是赋值运算符,主要是看是否有新的对象实例产生。如果产生了新的对象实例,那调用的就是拷贝构造函数;如果没有,那就是对已有的对象赋值,调用的是赋值运算符

61.strcat:C 库函数 char *strcat(char *dest, const char *src) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。

strcat(str1,str2)会先去掉str1的结束符后再把str2接连到str1末尾,这样很符合一个字符串只能有一个结束符的规定。

62.FATFS_LinkDriver:在进行Flash和SD卡初始化的时候均使用了这个函数,从函数名可以看出这个函数用于注册磁盘驱动。

函数原型:uint8_t FATFS_LinkDriver(const Diskio_drvTypeDef *drv, char *path),第一个参数是一个结构体指针,如果是使用Flash,那就是Flash_Driver;如果是使用SD卡,那就是SD_Driver。两个结构体指针都是底层驱动写好的,直接使用即可。第二个参数是char型指针,函数实际上对这个char型指针指向的数组的内容进行了修改,也就是初始化了路径。

63.f_mount:计算机上的盘符是C: D: E: FatFS中的盘符是0: 1: 2:....f_mount是给磁盘分配盘符的

裸机上挂载文件系统之前需要进行格式化操作,格式化完成即会擦除原来的数据,并在存储介质上新建一个文件索引和目录索引,以便于文件系统可以记录数据存储的状态信息,如物理地址、空间等。

64.memcpy:

void *memcpy(void *dest, const void *src, size_t n);
从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中,是用指针进行操作的。

65.字节对齐:

现代计算机中内存空间都是按照byte划分的,牺牲空间以换取时间的效率。为了使CPU能够对变量进行快速的访问,变量的起始地址应该具有某些特性,即所谓的”对齐”.。比如4字节的int型,其起始地址应该位于4字节的边界上,即起始地址能够被4整除。

__attribute__((aligned(4))) 4字节对齐

66.DMA中断:当设置DMA_BufferSize后,只有当传送完所设的大小后,才会产生一个DMA_IT_TC中断。

67.std::numeric_limits<T>::min/max:取类型的最小值,最大值

68.xTaskNotify:任务通知是一个事件,假如某个任务通知的接收任务因为等待任务通知而阻塞的话,向这个接收任务发送任务通知以后就会解除这个任务的阻塞状态。任务通知的发送使用函数 xTaskNotify()或者 xTaskNotifyGive()(还有此函数的中断版本)来
完成,这个通知值会一直被保存着,直到接收任务调用函数 xTaskNotifyWait()或者ulTaskNotifyTake()来获取这个通知值。假如接收任务因为等待任务通知而阻塞的话那么在接收到任务通知以后就会解除阻塞态。

69.全局变量:定义在函数外面的变量是全局变量,要想在别的文件里使用这个变量要用extern声明。

变量前有extern不一定就是声明,而变量前无extern就只能是定义。

一般不会在头文件里用extern,一般直接写在cpp文件里面,目的是提醒自己这个东西是别的文件里的全局变量,不然突然冒出个变量不知道哪定义看起来很奇怪

70.信息获取:先定义一个变量,然后用&运算把变量的地址交给接口函数,接口函数可以用*运算把信息赋值给这个变量

71.#ifndef #endif:防止头文件的重复包含和编译

72.TickType_t:它可以是16-bit或者32-bit两种类型,由FreeRTOSConfig.h中的configUSE_16_BIT_TICKS决定,定义为1表示为uint16_t,定义为0表示为uint32_t。在8-bit和16-bit处理上使用uint16_t;在32-bit上使用uint32_t。

73.时间片调度:在 FreeRTOS 中允许一个任务运行一个时间片(一个时钟节拍的长度)后让出 CPU 的使用权,让拥有同优先级的下一个任务运行

74.volatile:编译器会对带有volatile关键字的变量禁用优化

75.TIMx_CCRx:占空比是由TIMx_CCRx来确定的,PWM模式1下当TIMx_CNT<TIMx_CCRx,输出OCiREF有效电平,TIMx_CNT>=TIMx_CCRx时,输出OCiREF无效电平。至于有效电平是0还是1,要设置TIMx_CCERx

76.pow():C 库函数 double pow(double x, double y) 返回 x 的 y 次幂。

可以求平方,平方根,次幂

77.TCB:Task control block

78.portYIELD_FROM_ISR:种断服务程序中使用portYIELD_FROM_ISR()强制任务切换,实质是使能一个PendSV中断,在PendSV中断服务程序中,找到最高优先级的就绪任务,然后让这个任务获得CPU运行权,从而完成任务切换。

79.NULL和0:在C语言中,可以说NULL就是0,两者是完全等价的。只不过NULL用在指针和对象,0多用于数值。

指针初始化时,采用 pointer = NULL 和 pointer = 0 都是可以的。

80.资源保护:资源的保护是指,如何保护资源不被多个任务同时操作。如果一个保存数据的全局变量在被一个任务操作的过程中,又被多个更高优先级的任务抢占并处理,那么最后这个支离破碎的数据将会毫无意义,破坏了数据的独立完整性;如果一个任务在使用uart串口发送数据的时候,又被多个更高优先级的任务抢占串口资源并发送数据,那么最后串口发送的数据将只是一堆乱码毫无意义,造成了系统的不稳定性。

一个例子:

在上图中,有个低优先级的任务(LP)和高优先级的任务(HP),两个任务都可以对一个资源进行操作。为了对资源进行保护采用了信号量的机制。LP首先获得信号量可以对资源进行操作,在时刻1,HP因为优先级高在内核调度中抢占了LP,在时刻2,HP想获得信号量但失败因此进入了阻塞状态。之后LP可以继续对资源操作,在时刻3执行完毕后归还了信号量。在时刻4,HP因为优先级高在内核调度中抢占了LP并获取了信号量可以对资源可以操作。

81.STM332程序的起始地址一般在0x08000000。所以BootLoader程序是在0x08000000

82.sct文件:简单来说就是将特定的代码和变量放到指定的地方

83.存储器组织结构:程序存储器、数据存储器、寄存器和 I/O 端口排列在同一个线性(即地址连续)的 4 GB 地址空间内。

84.*.o (RESET, +First):合起来就是在所有的“.o”文件中找到含有“RESET”字段的内容放到“ER_IROM1"这块存储区域内

85.DTCM:DTCM(Data TCM)是cortex内核中数据传输总线,是一种高速缓存,据说是被直接集成在CPU芯片中。

86.__attribute__((section("section_name"))):其作用是将作用的函数或数据放入指定名为"section_name"对应的段中。

87.asin():反正弦函数,返回-PI/2 到 PI/2 之间的计算结果。返回值是弧度,变量类型是double

88.double atan2(double y, double x) :反正切函数,表示的 y/x 的反正切,返回-PI 到 PI 之间的计算结果。返回值是弧度,变量类型是double

atan2和atan的区别在于值域,atan2的值域是-pi到pi,atan的值域是-pi/2到pi/2

89.xTaskCreate:此函数用来创建一个任务,任务需要 RAM 来保存与任务有关的状态信息(任务控制块),任务也需要一定的 RAM 来作为任务堆栈。如果使用函数 xTaskCreate()来创建任务的话那么这些所需的 RAM 就会自动的从 FreeRTOS 的堆中分配,因此必须提供内存管理文件,默认我们使用heap_4.c 这个内存管理文件。新创建的任务默认就是就绪态的,如果当前没有比它更高优先级的任务运行那么此任务就会立即进入运行态开始运行,不管在任务调度器启动前还是启动后,都可以创建任务。此函数也是我们以后经常用到的,本教程所有例程均用此函数来创建任务

90.位运算:所谓位运算,就是对一个比特(Bit)位进行操作。

91.typedef:

typedef是类型定义的意思。typedef struct 是为了使用这个结构体方便。

具体区别在于: 
若struct node{ }这样来定义结构体的话。在定义 node 的结构体变量时,需要这样写:struct node n; 
若用typedef,可以这样写:typedef struct node{}NODE; 。在申请变量时就可以这样写:NODE n;就是自定义了一种数据类型。比如整型,用的时候是int age=10;int是数据类型,age是整型变量。同样typedef就是一种可以自定义类型的关键字,这样你就可以自定义类型了。

92.define:#define MAXNUM 99999,通常写在函数外面,写在函数里面也没毛病

93.void *:无类型指针

94.TaskHandle_t:是一个指针

95.变量类型:除了基本的变量类型,枚举、指针、数组、引用、数据结构、类等等这些都是变量类型

类也是一种数据类型,c++独有的,用户可以根据自己的需求写自己的数据类型

96.函数默认参数:函数的默认参数值,即在定义参数的时候同时给它一个初始值。在调用函数的时候,我们可以省略含有默认值的参数。也就是说,如果用户指定了参数值,则使用用户指定的值,否则使用默认参数的值。

通常我们都将默认参数放在函数声明中

97.SCB_InvalidateDCache_by_Addr :用于将数据Cache无效化,无效化的意思是将Cache Line标记为无效,等同于删除操作。这样Cache空间就都腾出来了,可以加载新的数据。

98.ADC:使用ADC进行可进行芯片基准电压采集和cpu温度监控

99.STL:标准模板库Standard Template LibrarySTL),是一个C++软件库,大量影响C++标准程序库但并非是其的一部分。其中包含4个组件,分别为算法、容器、函数、迭代器。模板是C++程序设计语言中的一个重要特征,而标准模板库正是基于此特征。标准模板库使得C++编程语言在有了同Java一样强大的类库的同时,保有了更大的可扩展性。

100.USART:串口,每次传输一个字节的数据,使串口工作在FIFO模式下,能在中断中一次性处理缓冲区的数据

101.Sbus:一种通信协议

字节位 byte1      byte2-23                                          byte24                                         byte25
类型    开始字节 通道数据字节(含16个脉宽通道) 标志位字节(含2个数字通道)   结束字节
数据    0x0F       通道数据范围11Bits = [0,2047]        2个数字通道位+2个状态位         0x00

总共有22个数据字节表示16个通道的通道值,databytes = 22bytes = 22 x 8Bits = 16 x 11Bits(CH1-16)

每个数据有11位数据进行表示,采集完通道值将其转化为0-1000的范围值

102.vPortEnterCritical:进入临界段

103.bool:在编译器里,本质是unsigned char,但其输出只有0和非0

默认值一般根据编译器的实现情况来定,有的相同程序不同调试版本也不同,所以,自己写程序还是要初始化,这样比较保险,免得出了bug不知道怎么找。

我目前编程默认是true

C语言不是没有布尔类型,只能说在C99标准之前没有。现在只要在源文件中包含stdbool.h这个头文件,就可以在C语言里像C++那样使用bool类型了。

104.字符串:字符串最后一个字符是结束字符‘\0’或者直接0

105.继承:class<派生类名>:<继承方式><基类名>

我们几乎不使用 protected 或 private 继承,通常使用 public 继承。

公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有保护成员来访问。

派生类在基类的基础上增加了其特有的属性和方法。

106.return:用于退出函数,并返回值

107.条件运算符:

Exp1 ? Exp2 : Exp3;

其中,Exp1、Exp2 和 Exp3 是表达式。请注意冒号的使用和位置。? : 表达式的值取决于 Exp1 的计算结果。如果 Exp1 为真,则计算 Exp2 的值,且 Exp2 的计算结果则为整个 ? : 表达式的值。如果 Exp1 为假,则计算 Exp3 的值,且 Exp3 的计算结果则为整个 ? : 表达式的值。

108.指向类的指针:

一个指向 C++ 类的指针与指向结构的指针类似,访问指向类的指针的成员,需要使用成员访问运算符 ->,就像访问指向结构的指针一样。与所有的指针一样,您必须在使用指针之前,对指针进行初始化。

109.消息队列:在计算机科学中,消息队列(英语:Message queue)是一种进程间通信或同一进程的不同线程间的通信方式,软件的贮列用来处理一系列的输入,通常是来自用户。消息队列提供了异步的通信协议,每一个贮列中的纪录包含详细说明的资料,包含发生的时间,输入设备的种类,以及特定的输入参数,也就是说:消息的发送者和接收者不需要同时与消息队列交互。消息会保存在队列中,直到接收者取回它。

110.sizeof():sizeof是C/C++中的一个操作符(operator),简单的说其作用就是返回一个对象或者类型所占的内存字节数。

111.sizeof(void*):sizeof(void*)的含义就是获取一个指针的大小。

112.cpu使用率:操作系统运行时是不断在不同的任务间进行切换,而驱动这一调度过程是通过系统tick来驱动的,即每产生一次系统tick则检查一下当前正在运行的任务的环境判断是否需要切换任务,即调度,如果需要,则触发PendSV,通过在PendSV中断调用vTaskSwitchContext()函数来实现任务的调度。CPU使用率算法是通过在一定时间内(1000个时间片内),计算空闲任务所占用的时间片总量,100减去空闲任务所占百分比则为工作任务所占百分比,即CPU使用率。

113.矩阵:在机器人程序里描述一个矩阵,也就是数组,多维矩阵也可以用一维数组来描述,只是个数多了点

114.命名:程序里面命名不能随便命名,需要让程序员从名字里推理出这个函数或者变量的作用

以字母或下划线开始,不能以数字开始,区分大小写

115.数组名:数组名不是指针,有类似指针的作用

error: cannot convert 'float (*)[2]' to 'float*'

116.枚举:

枚举类型(enumeration)是 C++ 中的一种派生数据类型,它是由用户定义的若干枚举常量的集合。

枚举变量的值只能取枚举常量表中所列的值,就是整型数的一个子集。

枚举常量代表该枚举类型的变量可能取的值,编译系统为每个枚举常量指定一个整数值,默认状态下,这个整数就是所列举元素的序号,序号从0开始。 可以在定义枚举类型时为部分或全部枚举常量指定整数值,在指定值之前的枚举常量仍按默认方式取值,而指定值之后的枚举常量按依次加1的原则取值。 各枚举常量的值可以重复。

typedef enum

{

    Gimbal_yaw_roll_pitch = 0,

    Gimbal_yaw_pitch = 1,

} gimbal_type_e;

117.函数参数:C++函数参数传递三种方式

一. 值传递:void Fun(int a)

二. 指针传递(地址传递):void swap(int *a, int *b)

三. 传递引用:void Fun(int &a)

118.引用:引用只是别名,不是实体类型(也就是说c++编译器不为引用单独分配内存空间),对一个对象的引用,就是直接对这个对象的操作。

函数参数为引用时,可以直接传变量名进去,这样会直接修改实际变量的大小

119.常量:常量可以是任何的基本数据类型,可分为整型数字、浮点数字、字符、字符串和布尔值。

实型 就是 浮点型。默认状态,实型常量 是 double类型,所以不用加任何后缀

120.赋值运算符与返回引用:

1、赋值返回引用 x = y = z 先执行y = z,返回y的引用,执行x = y

2、赋值不返回引用 x = y = z 先执行y = z,返回用y初始化的临时对象(注意临时对象都是常对象),再执行x = y的临时对象(要求operator=(const X&) ),返回用x初始化的临时对象(此处要求拷贝构造函数必须为X(const X&) )。

所以也并非必须返回引用,返回引用的好处既可以于赋值的原始语义已知,又可避免拷贝构造函数和析构函数的调用。

121.拷贝:浅拷贝:调用系统默认的拷贝构造函数,不再新分配资源内存。深拷贝:调用自己的拷贝构造函数,分配新的资源内存。

拷贝构造函数用已存在的对象创建一个相同的新对象。而赋值运算符用已存在的对象赋予一个已存在的同类对象。

122.同名:同名问题关键是要确定变量的作用域,同一个作用域的变量只能有一个名字,位于不同作用域的变量自然可以取相同的名字,采用“就近原则”,下面总结一下不同情况的同名:

1. 局部变量:不同函数的局部变量可以同名

2. 静态局部变量:不同函数的静态局部变量可以同名

3. 不同文件的全局变量可以同名,只允许一个定义时赋初始值。

4. 全局变量和局部变量可以同名

5. 不同文件的静态全局变量可以同名

6. 静态全局变量和静态局部变量可以同名

123.#pragma once:让所在的文件在一个单独的编译中只被包含一次,来避免重复包含,归根结底是为了遵循One Definition Rule, 也就是说在一个编译单元(translation unit)中某个类型(或对象,函数)不能有多个定义。注意是“一个编译单元”。一般写hpp的时候都会用上这句话。

124.头文件可以写啥?

头文件中可以写内联函数(inline)的定义。

因为inline函数是需要编译器在遇到它的地方根据它的定义把它内联展开的,而并非是普通函数那样可以先声明再链接的(内联函数不会链接),所以编译器就需要在编译时看到内联函数的完整定义才行。

如果内联函数像普通函数一样只能定义一次的话,这事儿就难办了。

因为在一个文件中还好,我可以把内联函数的定义写在最开始,这样可以保证后面使用的时候都可以见到定义;

但是,如果我在其他的文件中还使用到了这个函数那怎么办呢?

这几乎没什么太好的解决办法,因此 C++ 规定,内联函数可以在程序中定义多次,只要内联函数在一个 .cpp 文件中只出现一次,并且在所有的 .cpp 文件中,这个内联函数的定义是一样的,就能通过编译。

那么显然,把内联函数的定义放进一个头文件中是非常明智的做法。

头文件中可以写类(class)的定义。

因为在程序中创建一个类的对象时,编译器只有在这个类的定义完全可见的情况下,才能知道这个类的对象应该如何布局,所以,关于类的定义的要求,跟内联函数是基本一样的。

所以把类的定义放进头文件,在使用到这个类的 .cpp 文件中去包含这个头文件,是一个很好的做法。

在 C++ 的类中,如果函数成员在类的定义体中被定义,那么编译器会视这个函数为内联的。

注意一下,如果把函数成员的定义写在类定义的头文件中,而没有写进类定义中,这是不合法的,因为这个函数成员此时就不是内联的了。

一旦头文件被两个或两个以上的 .cpp 文件包含,这个函数成员就被重定义了。

总结一下:inline、clase、const、static都可以在头文件定义,因为他们都可以满足在不同文件中多次定义

c125.#include:

  • 系统自带的头文件用尖括号括起来,这样编译器会在系统文件目录下查找。
  • 用户自定义的文件用双引号括起来,编译器首先会在用户目录下查找,然后在到 C++ 安装目录(比如 VC 中可以指定和修改库文件查找路径,Unix 和 Linux 中可以通过环境变量来设定)中查找,最后在系统文件中查找。

126.编译过程:

译的时候,并不会去找 b.cpp 文件中的函数实现,只有在 link 的时候才进行这个工作。

我们在 b.cpp 或 c.cpp 中用 #include "a.h" 实际上是引入相关声明,使得编译可以通过,程序并不关心实现是在哪里,是怎么实现的。

源文件编译后成生了目标文件(.o 或 .obj 文件),目标文件中,这些函数和变量就视作一个个符号。

在 link 的时候,需要在 makefile 里面说明需要连接哪个 .o 或 .obj 文件(在这里是 b.cpp 生成的 .o 或 .obj 文件),此时,连接器会去这个 .o 或 .obj 文件中找在 b.cpp 中实现的函数,再把他们 build 到 makefile 中指定的那个可以执行文件中。

在 VC 中,一帮情况下不需要自己写 makefile,只需要将需要的文件都包括在 project中,VC 会自动帮你把 makefile 写好。

通常,C++ 编译器会在每个 .o 或 .obj 文件中都去找一下所需要的符号,而不是只在某个文件中找或者说找到一个就不找了。

因此,如果在几个不同文件中实现了同一个函数,或者定义了同一个全局变量,链接的时候就会提示 "redefined"。

编译都是对一个cpp文件来讲的

编译是分模块进行的(不同的编译模块指不同的文件),使用#include把类的头文件包含进来,在预处理阶段会进行文本替换。

如果此时在.h文件中定义了类的函数,那么包含了inlcude的文件在编译的时候就会把函数定义写入该编译模块目标文件中的.symtab符号表中。

现在,如果.h类中的函数定义不是inline的话,.symtab中的函数符号符号便是向外公开的。

127.编译单元:简单地,一个cpp文件就是一个编译单元。

定义:当一个c或者cpp文件在编译时,预处理器首先递归包含头文件,形成一个含有所有必要信息的单个源文件,这个源文件就是一个编译单元。

编译每个编译单元时互相独立,即每个cpp文件之间不知道对方的存在(一般不这样写:#include“xxx.cpp”),编译器会分别将每个编译单元进行编译,生成相应的obj文件,然后生成最终的可执行文件。

128.内部链接和外部链接:

内部链接-如果一个名称对编译单元来说是局部的,在链接的时候其他编译单元无法链接到它且不会与其他编译单元中的同样名称相冲突。(例如被关键字static,inline标识)

外部链接-如果一个名称对编译单元来说不是局部的,而在链接的时候其他的编译单元可以访问它,也就是说它可以和别的编译单元交互。

129.类:C++程序设计允许程序员使用(class)定义特定程序中的数据类型。这些数据类型的实例被称为对象,这些实例可以包含程序员定义的成员变量常量成员函数,以及重载的运算符。语法上,类似C中结构体(struct)的扩展,C中结构体不能包含函数以及重载的运算符。在 C++ 中,结构体 是由关键词 struct 定义的一种数据类型。他的成员和基类默认为公有的(public)。由关键词 class 定义的成员和基类默认为私有的(private)。这是C++中结构体和类仅有的区别。C++ 的结构体和类具有他们自己的成员。这些成员包括变量(包括其他结构体和类),被看做方法的函数(特定的标示符或重载的运算符),构造函数以及析构函数。成员被声明成为公共或私有使用说明符public:private:来区分。说明符后出现的任何成员会获得相应的访问权限直到下一个说明符的出现。对于继承的类能够使用protected:说明符。成员函数是C++ 的类和结构体的一个重要特性。这些数据类型可以包含作为其成员的函数。成员函数分为静态成员函数与非静态成员函数。静态成员函数只能访问该数据类型的对象的静态成员。而非静态成员函数能够访问对象的所有成员。在非静态成员函数的函数体内,关键词this指向了调用该函数的对象。这通常是通过thiscall调用协议,将对象的地址作为隐含的第一个参数传递给成员函数。

130.浮点常数通常以 double 类型存储在内存中。如果需要强制将这类常数存储为 float 类型,则可以将 F 或 f 字母附加到其末尾。

131.KEIL工程.c文件上面有‘*’和‘-’标志:这是因为当前文件Options for File->Properties中Include in Target Build没有选中,表示当前文件不编译(注;默认状态是选中)。

132.常用c库:stdint、stdlib、stdbool、string、stdio

stdint包括了常见的数据类型的定义宏定义

133.hal库:HAL库是st公司为了更方便地进行stm32之间的移植而开发的库,通用性很强,在不同的两款stm32芯片之间的移植基本上不需要修改。

134.BSP:Board Support Package是板级支持包,内核中针对具体的一款电路板和元器件所添加的代码,可以认为所有针对设备的代码都是驱动,但上下两层,上层的更关注设备的功能实现,具备一定的通用性,也就是我们前面说的驱动。下层就是BSP,这部分代码都是完全依附于某一款特定的电路板和元器件,不可以在不同板子之间通用。BSP的开发也要依赖具体的操作系统类型,为具体某款内核操作系统的子系统服务,适配具体的硬件体系架构。BSP作为内核能够运行于一款特定的硬件设备板子的支撑是必不可少的,必须和内核子系统静态链接。

135.端口复用:STM32有很多的内置外设,这些外设的外部引脚都是与GPIO复用的。也就是说,一个GPIO如果可以复用为内置外设的功能引脚,那么当这个GPIO作为内置外设使用的时候,就叫做复用。简单易懂的说,就是某些普通的外设引脚也可以用来其它功能。比如说串口1 的发送接收引脚是PA9,PA10,当我们把PA9,PA10不用作普通的GPIO口,而用做复用功能串口1的发送接收引脚的时候,叫端口复用。

136.重映射:我们知道每个内置外设都有若干个输入输出引脚,一般这些引脚的输出脚位都是固定不变的,为了让设计工程师可以更好地安排引脚的走向和功能,在STM32中引入了外设引脚重映射的概念,即一个外设的引脚除了具有默认的脚位外,还可以通过设置重映射寄存器的方式,把这个外设的引脚映射到其它的脚位。

137.GPIO的8种工作模式:
typedefenum
{
    GPIO_Mode_AIN = 0x0,           /* 模拟输入 */
    GPIO_Mode_IN_FLOATING = 0x04,  /* 浮空输入,复位后的状态 */
    GPIO_Mode_IPD = 0x28,          /* 下拉输入,这里的下拉是指单片机内部的电阻 */
    GPIO_Mode_IPU = 0x48,          /* 上拉输入,这里的上拉是指单片机内部的电阻 */
    GPIO_Mode_Out_OD = 0x14,       /* 开漏输出 */
    GPIO_Mode_Out_PP = 0x10,       /* 推挽输出 */
    GPIO_Mode_AF_OD = 0x1C,        /* 复用开漏输出 */
    GPIO_Mode_AF_PP = 0x18         /* 复用推挽输出 */
}GPIOMode_TypeDef;

138.不管是大端还是小端模式,我们在读取和存储数据的时候一定都是从内存的低地址依次向高地址读取或写入。

139.数据输出格式

%X:%X代表输出为大写十六进制,在里面加了个04,是输出四个字节

%m.nf:m表示要输出这个数的宽度,包括小数点,如果实际数值的宽度大于m,则以实际的数据宽度为准,如果实际数值宽度小于m,那么默认右对齐,前面补空格。n表示小数点后面数据的位数。

%mf:m表示要输出这个数的宽度,包括小数点,如果实际数值的宽度大于m,则以实际的数据宽度为准,如果实际数值宽度小于m,那么默认右对齐,前面补空格。

%f:输出浮点数,7位有效数字

使用printf时记得要写对输出格式,不然会输出错误的结果

140.push:push与push_back是STL中常见的方法,都是向数据结构中添加元素。

141.float和double的区别:float的运算要比double快不少,如果算法执行周期很短,double可能会超时

142.__weak:__weak是一个宏,和__packed是同一种东西都是gcc的扩展属性:
#define __packed __attribute__((packed))
#define __weak __attribute__((weak))

如果这个关键字用在函数定义上面,一般情况下和一般函数没有两样。但是当有一个同名函数但是不带__weak被定义时,所有对这个函数的调用都是指向后者(不带__weak那个), 如果有两个一样的函数都用了__weak,那么真正调用那个,就要看连接器了。

143.rebuild:build只编译改动过的文件,没改动过的就不需要重新编译了,rebuild,不管你有没有改动过,它都会重新把所有文件再编译一遍。如果环境配置的宏有改动,只build是不会将宏修改编译进去

144.watch窗口:在debug状态下可以移除窗口变量,在运行状态没法移除窗口变量,要切换16进制和十进制要在总变量处修改(结构体层),如果没有结构体就是直接修改

145.keil没有自动对齐代码,要加插件

146.没有定义:头文件明明包含了定义却报错说没有,头文件全部展开后有可能有顺序问题,也就是定义在使用后面

147.ceres优化库:给出残差(误差)到residuals

148.elseif:从上到下依次检测判断条件,当某个判断条件成立时,则执行其对应的语句块,然后跳到整个 if else 语句之外继续执行其他代码。如果所有判断条件都不成立,则执行语句块n,然后继续执行后续代码。也就是说,一旦遇到能够成立的判断条件,则不再执行其他的语句块,所以最终只能有一个语句块被执行。

149.三目运算符:<表达式1> ? <表达式2> : <表达式3>

返回值:先求表达式 1 的值,如果为真,则执行表达式 2,并返回表达式 2 的结果;如果表达式 1 的值为假,则执行表达式 3,并返回表达式 3 的结果。

150.malloc:动态内存分配。在非常重视安全(safety-critical)的嵌入式C语言程序开发中,动态内存分配广泛被认为是禁忌。使用C语言的malloc和free库函数可能会带来灾难性的副作用,例如内存泄漏或者碎片。此外,malloc常常会表现出极其不可预测的特性,这使其成为在多核系统上进行多线程C语言程序开发的瓶颈。

151.命令行:

g++ .\xx.c 编译生成exe文件

.\a 执行程序a

152.flash与ram

RAM相当于内存,Flash相当于硬盘。

keil编译信息(单位是字节B):

Code:是程序中代码所占字节大小;

RO-data:程序只读的变量,也就是带const的,和已初始化的字符串等;

RW-data:已初始化的可读写全局/静态变量;

ZI-data:未初始化的可读写全局/静态变量;

程序所占Flash空间大小=Code+RO data+RW data=生成的bin文件大小。

程序固定占用RAM大小=RW data+ZI data。

153.不管是boot还是app,都有启动代码,进入启动代码会初始化栈顶指针,因此boot用到的ram和app用到的ram不是加起来算的,而是取最大值,以在.map文件中查看堆栈的大小和基地址

154.栈的主要作用是用于局部变量、函数调用、函数形参的开销大小应小于内部RAM大小,考虑到局部变量的需求,防止栈溢出。

1、Stack_Size:若工程中使用的局部变量较多,定义的数据长度较大时,若不调整栈的空间大小,则会导致程序出现栈溢出,程序运行结果与预期的不符或程序跑飞。这时我们就需要手动的调整栈的大小。

2、Heap_Size:当工程中使用了malloc动态分配内存空间时,这时分配的空间就为堆的空间。所以若默认的堆空间大小不满足工程需求时,就需要手动调整堆空间的大小。

155.keil的查找功能:在文件中查找只在当前编辑器的文件中查找,在工程中查找是在整个工程中查找,如果文件没有添加进工程是不会纳入查找范围

156.如果一个有返回值类型的函数没有写返回值,编译器也会返回东西的,具体返回什么要看编译器的处理

157.git暂存区:对于暂存区,简单来讲是对文件的修改,与对修改的提交两者之间的一个过渡阶段:将修改过的文件add到暂存区,然后根据暂存区内容进行commit操作。

158.vscode批量修改变量名:全选目标变量名或函数名光标左击选中某个变量,然后CTRL+Shift+L

159.freemaster:

示波器采样时间,右键示波器属性,看周期

采集数据,点Toggle Data Capture按钮,开始记录,再次点击保存数据

160.git建仓库:先在云端建立仓库,然后拉取到本地,将文件移到仓库中再commit和push

161.if __name__ == '__main__':if __name__ == 'main': 下的代码只有在文件作为脚本直接执行时才会被执行,而 import 到其他脚本中是不会被执行的。

162.c++冒号:类名冒号后面的是用来定义类的继承

class 派生类名 : 继承方式 基类名

{

派生类的成员

};

继承方式:public、private和protected,默认处理是public。

163.fscanf()函数是格式化读写函数。它读取的对象是磁盘文件

函数原型:

int fscanf(FILE * fp,char * format,...);

其中fp为文件指针,format为C字符串,...为参数列表,返回参数列表中被成功赋值的参数个数。

fscanf函数会从文件输入流中读入数据,存储到format中,遇到空格和换行时结束

fscanf在遇到不符合格式的地方即停止,指针即停在非法字符的开始处

164.继承:如果父类的虚函数为空,子类需要实现父类的虚函数

165.vscode的几个json文件的作用:

c_cpp_properties.json:插件c/c++的配置(比如编译器的路径)

launch.json:运行c/c++的配置(配置调试器)

tasks.json:如何编译生成可执行程序(用cmake还需要这个?)

相比于不用 CMake 的调试配置,CMake 无需 task.json 手动配置编译命令及选项。但是 CMakeLists.txt 中可能定义多个目标,因此需要在 VSCode 底部状态栏 Build [all] 点击 [all] 切换调试目标

166..ros同时运行多个可执行文件(节点),节点之间可传递消息,节点相当于进程

ros的基本组成单元是pkg,src文件夹内可以包括多个pkg,catkin_make会编译src里面所有的pkg

注意每打开一个终端都要source一下

167.ubuntu加入中文输入法,语言需要加入中文,选智能拼音那个

168.变量不初始化不一定是0,所以最好都初始化一下

169.vscode批量替换变量:shift + ctrl + L

鼠标点击变量,然后按快捷键,即可同时编辑多处

170.hardfault:最常见是指针操作有问题,访问/修改了某些未知变量的内容,导致访问出错

171.波特率:串口传输速率为115200bps,每秒可传输多少字节?

起始位:1

数据位:8

停止位:1

校验位:0

传输1字节数据,需要传输10bit,因此:

115200 ÷ 10 = 11520Byte

也就是1秒能传11520个字节,速度为11520Byte/s

假设1包数据需要传32个字节,需要多久时间能把这包数据传完,32/11520 = 0.0027,需要2.7ms

172.git删除不必要文件的记录和跟踪

git rm -r --cached *.           //不删除本地文件
git rm -r --f *.                   //删除本地文件

173.keil仿真watch窗口数组不能展开的问题,不要右键添加,直接在watch窗口添加

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐