🧄🐳由于需要面试,所根据自身情况整理的面试题目,分享给大家,涵盖C语言、C++、单片机等。
一、C语言
1、gcc的编译过程?
🍏gcc编译过程分为4个阶段:预处理、编译、汇编、链接。
- 预处理:头⽂件包含、宏替换、条件编译、删除注释
- 编译:主要进⾏词法、语法、语义分析等,检查⽆误后将预处理好的⽂件编译成汇编⽂件。
- 汇编:将汇编⽂件转换成 ⼆进制⽬标⽂件
- 链接:将项⽬中的各个⼆进制⽂件+所需的库+启动代码链接成可执⾏⽂件
1、static的用法
🍊修饰局部变量
——称为静态局部变量
static修饰局部变量时,会改变局部变量的存储位置,从而使得局部变量的生命周期变长。
其本质是:
一个局部变量是存储在栈区的,但是被static修饰后就存储在静态区了,因为存储类型的变化,生命周期就跟着变化了。
注意:static修饰局部变量只改变生命周期,不改变作用域!
🍌修饰全局变量
——称为静态全局变量
首先我们要知道全局变量是具有 外部链接属性 的
全局变量被static修饰后,它的外部链接属性就变成内部链接属性,即这个全局变量只能在自己的.c文件内被使用,其他文件看不到也无法使用,相当于作用域缩小了。
🍐修饰函数
——称为静态函数
本质和全局变量很像:
函数本身也是有外部链接属性的;
被static修饰后,函数的外部链接属性被修改成内部链接属性,使得这个函数只能在自己的源文件内被使用,因此函数的作用域就变小了。
2、变量/函数的声明和定义之间有什么区别
🍅函数的声明和定义
1.1)函数的声明是函数的原型
声明部分的作用:
对有关标识符的属性进行说明。
标识符例如变量、函数体、结构体、共用体等。一般在头文件中声明
1.2)函数的定义是函数功能的确立
函数的声明在声明部分,也可以在定义部分。
函数的定义肯定不在函数的声明部分,它是一个文件中的独立模块。一般在.C文件中定义
🥒变量的声明和定义(难)
2.1)变量的声明
声明部分的变量有两种:
1)需要建立存储空间的变量(如 int a;)——定义性声明(定义),即可说是声明,也可说是定义。
2)不需要建立存储空间的变量(如extern int a;)——引用性声明,是声明,不是定义
不需要建立存储空间的声明称为声明。
所谓声明,其作用就是向编译系统发出一个信息,声明该变量是一个在后面定义的外部变量,仅仅是为了提前引用该变量而作的声明。
2.2)变量的定义
需要建立存储空间的声明称为定义。
- 系统根据外部变量的定义分配存储单元,而不是根据声明分配存储单元。
- 外部变量的初始化只能在定义时进行,不能在声明中进行。
- extern 只用作声明,不用于定义。
3、C语言的内存模型
🧄C语言的内存模型分为5个区:代码区,BSS段,数据段,堆区和栈区。
1. 代码区
可执行代码、字符串常量
2.BSS段
- 未初始化全局变量,未初始化全局静态变量
3.数据段
- 已初始化全局变量、已初始化全局静态变量、局部静态变量、常量数据
4.堆区(Heap)
存放程序员创建的变量的区域,是一块不连续的区域,一般使用new,alloc,calloc,malloc关键字创建的变量,这些变量需要程序员调用delete,release,free关键字才能释放。若没有调用,则会造成内存泄露。
5.栈区(Stack)
存放函数的参数,局部变量的区域,由编译器自动分配和释放。通常在作用范围域外就会释放变量。形式上类似于数据结构中栈。因为在CPU的指令集中分配内存,所以操作快且效率高。但是空间就很有限。
4、解释堆和栈的区别
1.1 栈简介
栈由操作系统自动分配释放 ,用于存放函数的参数值、局部变量等,其操作方式类似于数据结构中的栈。
1.2 堆简介
堆由开发人员分配和释放, 若开发人员不释放,程序结束时由 OS 回收,分配方式类似于链表。
1.3 堆与栈区别
(1)管理方式不同
栈由操作系统自动分配释放,无需我们手动控制;堆的申请和释放工作由程序员控制,容易产生内存泄漏;
(2)空间大小不同
每个进程拥有的栈大小要远远小于堆大小。理论上,进程可申请的堆大小为虚拟内存大小,进程栈的大小 64bits 的 Windows 默认 1MB,64bits 的 Linux 默认 10MB;
(3)生长方向不同
堆的生长方向向上,内存地址由低到高;栈的生长方向向下,内存地址由高到低。
(4)数据结构不同
堆:一般使用数据结构中的树来管理。
栈:一般使用先进后出的队列来管理。
(5)存放内容不同
堆通常用于存储动态分配的对象,如数组、类对象和结构体等。而栈通常用于存储局部变量、函数参数、返回值和函数调用堆栈信息等。
(6)分配效率不同
- 栈由操作系统自动分配,在CPU的指令集中完成分配并且有硬件层的支持(专门寄存器负责存储,专门指令负责压栈出栈)。
- 堆是由系统内核API接口来完成申请与管理,实现机制较为复杂,并且频繁的内存申请容易产生内存碎片。这样栈的分配效率比堆高多了。
5、指针问题
🍉指针常量与常量指针
🍌1.1常量指针
语法:const 类型* 变量名;
- const修饰指针
- 指针的指向可改变(指向的地址可改变),指向的变量的值不可改变,
🍌 1.2指针常量
语法: 类型* const 变量名;
- const修饰变量
- 指针指向的值可以改变,指针指向不可以改变
🥬1.3const即修饰指针,又修饰变量
语法: const 类型* const 变量名;
- 指针的指向和指针指向的值都不可以改
🎴指针函数和函数指针
1.1 指针函数
指针函数: 顾名思义,它的本质是一个函数,不过它的返回值是一个指针。
1.2 函数指针
与指针函数不同,函数指针 的本质是一个指针,该指针的地址指向了一个函数,所以它是指向函数的指针。
我们知道,函数的定义是存在于代码段,因此,每个函数在代码段中,也有着自己的入口地址,函数指针就是指向代码段中函数入口地址的指针。
📫 指针数组和数组指针
1.1 指针数组 *p[n]
指针数组:是数组——装着指针的数组。
1.2 数组指针 (*p)[n]
数组指针:是指针——指向数组的指针。
🀄函数指针数组
//函数指针
int (*p)(int,int);
//函数指针数组
int (*pfArr[])(int,int);
//对比
int* p;//整形指针
int* arr[];//整形指针数组
🔫指向函数指针数组的指针
//函数指针
int (*pf)(int,int);
//函数指针数组
int (*pfArr[4])(int,int);
//指向函数指针数组的指针
int (*(*ptr)[4])(int,int) = &pfArr;
6、C语言参数传递方式
- 传值,就是把你的变量的值传递给函数的形式参数,实际就是用变量的值来新生成一个形式参数,因而在函数里对形参的改变不会影响到函数外的变量的值。
- 传址,就是传变量的地址赋给函数里形式参数的指针,使指针指向真实的变量的地址,因为对指针所指地址的内容的改变能反映到函数外,也就是能改变函数外的变量的值。
- 传引用,实际是通过指针来实现的,能达到使用的效果如传址,可是使用方式如传值。
说几点建议:
- 如果传值的话,会生成新的对象,花费时间和空间,而在退出函数的时候,又会销毁该对象,花费时间和空间。
- 因而如果int,char等固有类型,而是你自己定义的类或结构等,都建议传指针或引用,因为他们不会创建新的对象。
7、#include<> 与#include ""的区别?
- #include<>到系统指定⽬录寻找头⽂件,
- #include ""先到项⽬所在⽬录寻找头⽂件,如果没有找再到系统指定的⽬录下寻找
8、#define 宏定义
预处理指令,用于在编译之前将标识符替换为特定的值或代码片段,宏缺乏类型检查
与typedef相比:
typedef 编译阶段会检查错误,#define 预处理阶段不检查错误。
9、ifndef/define/endif 的作用?
防止头文件被重复包含和编译。 头文件重复包含会增大程序大小,重复编译增加编译时间。
10、c语⾔中有符号和⽆符号的区别?
- 有符号:数据的最⾼位为符号位,0表示正数,1表示负数
- ⽆符号:数据的最⾼位不是符号位,⽽是数据的⼀部分,只有正数,所以范围更大
11、violate关键字
volatile是一个类型修饰符(type specifier), 防止编译器对代码进行优化。
12、sizeof 和 strlen 的区别
1 sizeof 是一个操作符,strlen 是库函数
2 sizeof 的参数可以是数据的类型,也可以是变量,而 strlen 只能以结尾为‘\0‘的字符串作参数。
3 编译器在编译时就计算出了 sizeof 的结果。而 strlen 函数必须在运行时才能计算出来。并且 sizeof 计算的是数据类型占内存的大小,而 strlen 计算的是字符串实际的长度。
4 数组做 sizeof 的参数不退化,传递给 strlen 就退化为指针了。
13、设置地址为 0x67a9 的整型变量的值为 0xaa66
int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa66;
14、常见的排序和思路
🚃冒泡排序
基本思路
冒泡排序是一个非常好理解的排序,顾名思义——冒泡,此时将要排序的数据从上至下排列,从最上面的数(第一个数据)开始对相邻的两个数据进行比较,较小的数据往上浮,较大的数据往下沉,达到排序的效果。
(1)对每一对相邻的元素进行比较,若第一个比第二个大,则调换这两个元素的位置,依次两两比较直到数据的最后一对,此为一轮操作。
(2)重复n轮此操作(n为元素的个数),不过每轮结束后的最后一个元素不用参与下一轮的比较,因为经历一轮排序后,最后一个元素一定比前面所有的元素都要大。
(3)因此每一轮需要比较的元素都在减少,一直到没有数可比较为止。(不过为了减少比较次数,可以记录每轮是否有数据的交换,如果没有交换,表明当前数据已经按从小到大的顺序拍好了,可直接跳出循环)
🚃快速排序
基本思路:
快速排序的重点在于找一个基准值,将数列分为两部分——大于基准值的放在右边,小于基准值的 放在左边。然后分别对这两部分重复次操作,一分为二,二分为四······直到每个元素自成一部分。
1.将数据的中间元素设为基准值,初始化令 ii 指向最左边个元素,令 jj 指向最右边个元素,通过i++i++从左往右找一个大于基准数的数,通过j−−j−−从右往左找一个小于基准数的数,交换两数的位置,直到i=ji=j。
2.如此不断的细分递归,达到排序的目的
🚃插入排序
将数据分为两组——一组是有序的,一组是无序的,将无序数据中的元素依次插入到有序数据中,从而将整个数据变为有序的(这里的分组是潜意识的,实际上并不会用两个数组来分)
1.初始时,将第一个元素分为有序组(因为只有一个元素,所以认为它是排好序的),其余元素分为无序组
2.因此只需从第二个元素开始,依次在有序组中找到自己的位置,插入即可,直到最后一个元素。
3.但这并不意味着只需要一次循环,因为在“找自己的位置”的过程中,需要将自己与前面的元素相比较,若是自己较小,则将那个元素后移一位;若是自己较大,则将自己插入到上一次比较的位置
二、C++区域
😀C和C++的区别
- C是面向过程的语言,是一个结构化的语言,考虑如何通过一个过程对输入进行处理得到输出
- C++是面向对象的语言,主要特征是“封装、继承和多态”
- C和C++动态管理内存的方法不一样,C是使用malloc/free,而C++除此之外还有new/delete关键字。
- C++中有引用,C中不存在引用的概念
😁new、delete、malloc、free之间的关系
new/delete,malloc/free都是动态分配内存的方式
1)malloc对开辟的空间大小严格指定,而new只需要对象名
2)new为对象分配空间时,调用对象的构造函数,delete调用对象的析构函数
3)malloc/free是库函数,new/delete是C++运算符。
😃构造函数和析构函数
1.1构造函数
构造函数在创建对象时自动执行,往往用来做一些初始化工作,例如对成员变量赋值、预先打开文件等,当然你也可以让构造函数什么也不做,或者做一些提示工作。
1.2析构函数
创建对象时系统会自动调用构造函数进行初始化工作,销毁对象时系统也会自动调用析构函数来进行清理工作,例如释放分配的内存、关闭打开的文件等。
特点:
- 无类型
- 无返回值
- 名字与类名相同
- 不带参数,不可重载,析构函数只有一个!
- 析构函数前“~” (取反符,表示逆构造函数)
😅虚函数、纯虚函数
1.1虚函数
在基类中,通过virtual关键字修饰的函数叫做虚函数,核心理念就是通过基类访问派生类定义的函数,是C++中多态性的一个重要体现。利用基类指针访问派生类中的虚函数,这种情况下采用的是动态绑定技术。
1.2纯虚函数
- 纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“=0”.纯虚函数不能实例化对象。
- 纯虚函数是只有声明没有实现的虚函数,是对子类的约束,是接口继承
- 包含纯虚函数的类是抽象类,它不能被实例化,只有实现了这个纯虚函数的子类才能生成对象
😳typdef和define区别
- #define是预处理命令,在预处理是执行简单的替换,不做正确性的检查
- typedef是在编译时处理的,它是在自己的作用域内给已经存在的类型一个别名
😖 什么是野指针
野指针不是NULL指针,是未初始化或者未清零的指针,它指向的内存地址不是程序员所期望的,可能指向了受限的内存。
成因:
1)指针变量没有被初始化
2)指针指向的内存被释放了,但是指针没有置NULL
3)指针超过了变量了的作用范围,比如b[10],指针b+11
😞 线程与进程的区别
1. 根本区别:进程是操作系统进行资源分配的最小单元,线程是操作系统进行运算调度的最小单元。
2. 从属关系不同:进程中包含了线程,线程属于进程。
3. 开销不同:进程的创建、销毁和切换的开销都远大于线程。
4. 拥有资源不同:每个进程有自己的内存和资源,一个进程中的线程会共享这些内存和资源。
5. 控制和影响能力不同:子进程无法影响父进程,而子线程可以影响父线程,如果主线程发生异常会影响其所在进程和子线程。
6. CPU利用率不同:进程的CPU利用率较低,因为上下文切换开销较大,而线程的CPU的利用率较高,上下文的切换速度快。
7. 操纵者不同:进程的操纵者一般是操作系统,线程的操纵者一般是编程人员。
🍏 main 函数执行以前,还会执行什么代码?
全局对象的构造函数会在main 函数之前执行
三、单片机区域
🍊STM32 启动过程
1.初始化堆栈指针 SP=_initial_sp
2.初始化 PC 指针=Reset_Handler
3.初始化中断向量表
4.配置系统时钟
5.调用 C 库函数_main 初始化用户堆栈,然后进入 main 函数。
💊32位单片机中的32指什么
这里位指的是数据处理一次能的够处理的数据的位宽,32位单片机,就是指一次能够处理的数据的位宽是32个比特位的单片机,对应内部寄存器的大小是32位的
📫上下拉电阻
上拉电阻和下拉电阻通常用于I/O口的输入端,它们的作用是在没有外部信号输入时,通过引入一个预定的电平来确保输入端的电平状态。上拉电阻连接到电源电压,下拉电阻连接到地。
当外部设备没有输出信号时,上拉或下拉电阻可以确保输入端的电平稳定,避免出现浮动状态。这样可以有效防止干扰和误操作,提高系统的稳定性和可靠性
🍐介绍一下GPIO模式
GPIO 8种工作模式(gpio_init.GPIO_Mode):
- GPIO_Mode_AIN 模拟输入
- GPIO_Mode_IN_FLOATING 浮空输入
- GPIO_Mode_IPD 下拉输入
- GPIO_Mode_IPU 上拉输入
- GPIO_Mode_Out_OD 开漏输出
- GPIO_Mode_Out_PP 推挽输出
- GPIO_Mode_AF_OD 复用开漏输出
- GPIO_Mode_AF_PP 复用推挽输出
🥒单片机中的中断
中断是处理器中的一种机制,用于响应和处理突发事件或紧急事件。当发生中断时,当前正在执行的程序会被暂时中止,处理器会跳转到中断处理程序(也称为中断服务例程),对中断事件进行处理。处理完中断后,处理器再返回到被中断的程序继续执行。
中断向量表(Interrupt Vector Table):中断向量表是一个存储中断处理程序地址的表格。每个中断都有一个特定的向量,当中断发生时,微控制器会根据中断向量表中相应中断的地址跳转到对应的中断处理程序。
🍋UART的介绍
双线双向全双工异步通信,有两根线,(接收线)RX 和 (发送线)TX ,数据帧是1位起始位,8位数据位,无奇偶校验位,一位停止位,使用UART串口协议传输数据时,需要规定双方的传输速率(波特率)一致。
- 波特率(Baud Rate)单位bps是用于衡量串口通信速度的单位,它表示每秒钟发送的比特数。如果一个串口的波特率为9600,就表示该串口在一秒钟内可以发送9600个比特的数据。
🍉I2C协议
同步半双工协议,有两根条信号线,一条是双向的串行数据线,一条是时钟线, 每个连接到IIC总线上的器件都有一个唯一的器件地址,通过寻址的方式选择从机,
IIC产生四种信号,
开始信号(SCL高电平期间,SDA由高电平到低电平),
结束信号(SCL高电平期间,SDA由低电平到高电平),
应答信号(SCL高电平期间,SDA拉低),
非应答信号(SCL高电平期间,SDA拉高),且总线空闲时都处于高电平状态,
写数据(开始信号,发送7位器件地址+一个读写位,0是写 1是读,等待回应即可开始传输数据)
读数据(开始信号,发送7位器件地址+一个读写位。从设备回应,即可开始传输数据)
时钟线拉低---交换数据
时钟线拉高--稳定状态-读取数据
🍇🍇SPI
高速同步全双工,总线上包括四条逻辑线,
MISO(主机输入从机输出)--MOSI(主机输出从机输出),
SCLK(时钟线),CS(片选线),内部通过移位寄存器进行数据进出,且有数据缓冲区来控制读写同时进行,每次发送一个字节的数据,通过拉低片选线来控制从机,SPI根据时钟极性和时钟相位通常有4种工作模式。
🍈ADC
模数转换器,ADC的作用就是将连续变化的模拟信号转换为离散的数字信号,
12位ADC是一个逐次逼近型模数转换器。
它有多达19个多路通道,允许它测量来自16个外部源和3个内部源(温度传感、内部参考电压、外部电池)的信号。
各种通道的A/D转换可以在单次、连续、扫描或间断的模式下进行。
ADC的结果被存储在一个向左对齐或向右对齐的16位数据寄存器中。
ADC的精度计算:参考电压/精度
🍓DMA
📣直接存储器访问,
DMA用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。DMA的作用就是实现数据的直接传输,而去掉了传统数据传输需要CPU寄存器参与的环节。给CPU节省资源,使CPU的工作效率提高,
DMA传输将数据从一个地址空间复制到另一个地址空间
提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。
🥦定时器
可以用来定时、计数、PWM产生、输入捕获以及定时器中断等,分高级定时器、基本定时器、通用定时器,内部通过计数器实现的多种功能。
🚩 看门狗
一种用于监视和维护系统正常运行的硬件计时器,,它的作用是在系统出现故障、死锁或其他异常情况时,对系统进行重启,以使系统恢复到一个可控状态。
看门狗通常由一个定时器和一个计数器组成。在系统正常运行时,软件需要周期性地喂狗(即向看门狗喂送信号),以防止看门狗计时器溢出。如果系统出现故障或软件停止响应,导致无法喂狗,计时器将会溢出,触发看门狗复位,强制系统重新启动,从而恢复到一个可控状态。
通过看门狗,可以提高系统的稳定性和可靠性,防止因软件死锁、死循环或其他异常情况导致系统无法正常工作。
🧅485通信
半双工通信,差分信号传输,更加稳定,RS485内部的物理结构,
采用的是平衡驱动器和差分接收器的组合,抗干扰能力大大增加,
数据最高传输速率为10Mbps传输距离最远可达到1200米左右,最多支持32个设备,
三线制,A,B线 还有一“使能”端,拉高发送数据,拉低读取数据
逻辑“1”以AB两线间的电压差为+(2~6)V表示;逻辑“0”以AB两线间的电压差为-(2~6)V表示,
🥗MODBUS协议
Modbus是一主多从的通信协议-----
Modbus通信中只有一个设备可以发送请求。其他从设备接收主机发送的数据来进行响应,从机是任何外围设备,从站处理信息和使用Modbus将其数据发送给主站。
也就是说,Modbus不能同步进行通信,主机在同一时间内只能向一个从机发送请求,总线上每次只有一个数据进行传输,即主机发送,从机应答,主机不发送,总线上就没有数据通信。
Modbus消息帧
- 帧结构 = 从机地址 + 功能吗 + 数据 + 校验
🥗MQTT协议
一种基于发布/订阅(publish/subscribe)模式的“轻量级”通讯协议
实现MQTT协议需要:客户端和服务器端
MQTT协议中有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。
MQTT传输的消息分为:主题(Topic)和负载(payload)两部分
Topic,可以理解为消息的类型,订阅者订阅(Subscribe)后,就会收到该主题的消息内容(payload)
payload,可以理解为消息的内容,是指订阅者具体要使用的内容
🍇 CAN总线
CAN总线通信系统是串行通信的一种,要优于RS485总线,是目前比较常用的一种工业总线,如汽车的电气部分就采用CAN总线实现通信。
- 异步半双工通讯,分为两条线CAN高线、CAN低线
CAN节点的差分电平 :总线可以具有两种逻辑状态之一:隐性(recessive )或显性(dominant)
两线压差小于0.5V 时候为隐性的,逻辑信号表现为"逻辑1",即高电平。
两线压差大于0.9V 时候为显性的,逻辑信号表现为"逻辑0",即低电平
🍇 TCP/IP协议
- 应用层、传输层、网络层、数据链路层
- 应用层:负责处理网络应用程序和用户之间的交互,例如Web浏览器、电子邮件客户端等。常见的应用层协议有HTTP、FTP、SMTP等。
- 传输层:负责提供端到端的可靠数据传输服务,可以通过TCP或UDP协议实现。TCP协议提供面向连接的可靠数据传输,确保数据的完整性和有序性;而UDP协议则提供无连接的快速传输,但不保证数据的可靠性和有序性。
- 网络层:负责路由选择和数据包转发,保证不同网络之间的互通性。常见的网络层协议有IP、ICMP、ARP等。
- 链路层:负责通过物理介质(例如光纤、网线)传输数据,保证数据在物理层面上的正确传输。常见的链路层协议有以太网协议、无线局域网协议等。
TCP/IP协议三次握手和四次挥手
TCP/IP协议中,三次握手和四次挥手是指建立和断开网络连接时的一些约定。
三次握手是指客户端和服务器之间进行连接时,需要经过三次握手才能建立连接。具体流程如下:
客户端向服务器发送一个请求(SYN)。
服务器收到请求后回复一个确认(ACK)。
客户端再次向服务器发送一个确认(ACK),建立连接。
四次挥手是指在断开连接时,需要经过四次挥手才能断开连接。具体流程如下:
客户端向服务器发送一个FIN信号,表示不再发送数据给服务器。
服务器收到FIN信号后,发送一个ACK确认信号给客户端。
服务器再向客户端发送一个FIN信号,表示不再向客户端发送数据。
客户端收到服务器的FIN信号后,发送一个ACK确认信号给服务器,此时连接断开。
需要注意的是,三次握手是建立连接,四次挥手是断开连接。在实际应用中,连接的建立和断开都需要采用这样的方式来保证数据传输的准确性和完整性。
四、RTOS实时操作系统
🍓什么是FreeRTOS?
FreeRTOS是一个流行的实时操作系统(RTOS),用于嵌入式设备。它是开源的,因此可以免费使用和修改。FreeRTOS提供了多任务处理、时间管理、中断管理等功能,非常适合资源受限的微控制器和小型处理器。
🍈FreeRTOS中的调度策略是什么?
FreeRTOS使用抢占式调度策略。这意味着如果有更高优先级的任务变得可运行(例如,它刚刚完成了一个等待操作),当前运行的任务将被中断,调度器将切换到更高优先级的任务。这确保了关键任务能够及时响应。
🍒FreeRTOS中的中断如何处理
在FreeRTOS中,中断处理程序应该尽可能快速地执行并返回,以最小化中断服务例程(ISR)的执行时间。在ISR中,应避免执行复杂的操作或阻塞调用。如果需要执行长时间运行的任务,应将任务推迟到普通的任务上下文中执行。此外,FreeRTOS提供了一种机制,允许从中断上下文中安全地调用任务
🍑FreeRTOS中的队列是什么?它们有什么用途?
队列是FreeRTOS中的一种中间件,它允许任务之间通过发送和接收消息来通信。队列是一种先进先出(FIFO)的数据结构,任务可以向队列发送数据,其他任务则可以从队列中取出数据。队列对于任务解耦和同步非常有用,因为它们允许任务以异步的方式交换信息。
🥭FreeRTOS中的信号量有哪些类型?
FreeRTOS中有三种主要类型的信号量:二值信号量、计数信号量和递归信号量。
- 二值信号量用于锁定资源,类似于互斥锁,它们只能有两个状态:获取或释放。
- 计数信号量可以用于管理多个相同的资源,或者用来同步多个任务。
- 递归信号量是计数信号量的一种特殊形式,允许同一个任务多次获取同一个资源。
🔓在启动FreeRTOS系统时,我们需要进行哪些配置?
配置FreeRTOS内核通常包括定义堆栈大小、设置时钟滴答心跳、定义任务优先级、选择调度策略等。
📦FreeRTOS中的软件定时器是什么?它们如何工作?
软件定时器是FreeRTOS提供的一种机制,允许任务在将来的某个时间点被唤醒。软件定时器不是真正的硬件定时器,而是使用FreeRTOS的调度器和时钟滴答来模拟的。任务可以创建一个软件定时器,并将其与任务函数关联。当定时器到期时,关联的任务将被添加到就绪列表中,以便在下一个调度点执行。
🍉FreeRTOS如何处理任务间通信?
FreeRTOS支持多种任务间通信机制。最常见的包括使用队列、信号量、事件组和流缓冲区。队列用于发送和接收消息;信号量用于同步和互斥;事件组用于表示一组事件的状态;流缓冲区则允许任务通过类似环形缓冲区的结构发送和接收数据流。这些机制可以根据应用需求灵活选择和组合。
📋请解释FreeRTOS中的任务挂起和恢复机制
任务挂起是指将任务从就绪状态移除,使其暂时不参与调度。
📌FreeRTOS四种任务状态
🍪Running—运行态
当任务处于实际运行状态被称之为运行态,即 CPU 的使用权被这个任务占用。
🔔Ready—就绪态
处于就绪态的任务是指那些能够运行(没有被阻塞和挂起),但是当前没有运行的任务,因为同优先级或更高优先级的任务正在运行。
🌯Blocked—阻塞态
由于等待信号量,消息队列,事件标志组等而处于的状态被称之为阻塞态,另外任务调用延迟函数也会处于阻塞态。
🧆Suspended—挂起态
类似阻塞态,通过调用函数 vTaskSuspend()对指定任务进行挂起,挂起后这个任务将不被执行,只有调用函数 xTaskResume()才可以将这个任务从挂起态恢复。
电路相关知识
🥙该部分主要是考察应聘者是否能够读懂电路图,了解常见的元器件作用
💤电阻和电容有哪些作用
1.1 电阻
分流、分压、限流、滤波(与电容组成RC滤波器)、上下拉、假负载作用、跳线作用
1.2 电容
特性:隔直流通交流,通高频阻低频
主要用于电源滤波、信号滤波、信号耦合、谐振、滤波、充补偿放电、储能、旁路、隔直等电路中。
三极管的导通方式与使用
三极管(晶体管)是通过控制基极电流来控制集电极和发射极之间的电流流动的。具体来说:
- NPN三极管导通:NPN三极管的基极为高电平时三极管导通。
- PNP三极管导通:PNP三极管的基极为低电平时三极管导通
NPN和PNP三极管的使用如下:
NPN三极管:常用于低电平控制高电平的电路中,例如开关、放大器等电路中。在NPN三极管中,基极与发射极之间施加正向电压时,三极管导通,否则截止。
PNP三极管:常用于高电平控制低电平的电路中,例如开关、放大器等电路中。在PNP三极管中,基极与发射极之间施加负向电压时,三极管导通,否则截止。
MOS管的用法
MOS管(Metal-Oxide-Semiconductor Field-Effect Transistor)是一种场效应晶体管,常用于电子设备中。其功能和用法包括:
放大器应用:MOS管可以用于放大音频信号、射频信号等。
开关应用:MOS管可以用于电源管理、驱动器、电机控制等领域。
数字逻辑应用:MOS管可以用于构建逻辑门、存储器单元、微处理器等数字电路。
📢PCB布线
- 信号引线:避免过长的信号线,因为长的线路会增加信号的传输延迟和损耗。尽量使用直线或45度角的线路走向,减少锐角和拐弯,以减少信号反射和串扰的可能性。
- 在多层PCB中,合理规划信号和电源地等走线的层间转换
- 电源线以及地线的大小需要合理控制