C语言
1.从源码到可执行文件会经历怎样的过程?
- 预编译 :处理预处理指令(#define #include #ifdef #if 等),去掉空格注释,生成干净的源代码
- 编译: 将源代码翻译成汇编代码,生成汇编文件。
- 汇编: 将汇编代码翻译成机器码,生成目标文件(二进制文件)。
- 链接: 将目标文件和程序所依赖的库连接成最终的可执行文件。
2.介绍一下C语言程序的内存模型
3.什么是标识符?标识符有什么命名规范?
- 变量、函数、数组、结构体等要素命名时使用的字符序列,称为标识符。
- 由小写或大写英文字母,0-9 或 _ 组成。
- 不能以数字开头。
- 不可以是关键字。
- 下划线拼接
- 驼峰命名
4.32位系统,地址用多少个字节表示? 64位系统,地址用多少字节表示?
- 32位系统,地址用4个字节表示。
- 64位系统,地址用8个字节表示。
5.C语言变量的声明和定义有什么区别?
- 声明是告诉编译器某个变量的类型和名称,但不指定其值
- 定义是告诉编译器某个变量的类型和名称,并指定其值
6.变量和常量有什么区别?
- 变量的值在程序运行期间可以修改,常量的值不能修改
7.C 语言中有哪些定义常量的方式?有什么区别?
- const: C99新增,定义时需指定数据类型,编译时会类型检查,有作用域,更安全。
- #define : 预处理阶段文本替换,没有类型检查,没有作用域,更灵活。
8.全局变量和局部变量有什么区别?
- 作用域: 全局变量-整个程序 局部变量-声明该变量的函数或者代码块内
- 生命周期: 全局变量-整个程序运行期间 局部变量-函数或代码块的执行期间
- 存储位置: 全局变量-全局变量存储在数据区 局部变量-局部变量存储在栈区
- 默认初始值: 全局变量-有默认值,为0 局部变量-没无默认值,随机
9.假设有变量num,表达式num ++和++num有什么不同?
- 先取值再运算
- 先运算再取值
10.请说几个你经常用到的预处理命令?
- #include #define、#undef、#ifdef、#ifndef #if、#else、#elif、#endif
11.如何取消宏定义?
- #undef
12.什么是条件编译,有哪些应用场景?
- 条件编译(Conditional Compilation)是指在程序编译过程中,根据某些条件的不同,决定是否编译某段代码的技术。通常,条件编译通过预处理指令实现,最常见的指令是 #if、#ifdef、#ifndef、#else 和 #endif。
- 应用场景:跨平台 调试代码 功能开关
13.说说你知道的C语言数据类型?
- 整数 char、short、int、long、long long
- 浮点数 float、double、long double
14.整数和浮点数在存储原理上有什么区别?
- 整数: 以二进制补码的形式存储
- 浮点数:C语言中的浮点数存储遵循IEEE 754标准,采用科学计数法表示。浮点数由三部分组成:符号位:占1位,表示浮点数的正负。 指数部分:占8位(float)或者11位(double),表示数值的规模,采用偏移量表示(通常为127或1023)。 尾数部分:占23位(float)或者52位(double),表示有效数字。
15.char类型的本质
- 长度为1个字节(8位)的整数。实际存储的是字符对应的ascii码编码值。
16.数据类型的自动转换遵循什么原则?
- 整数类型之间: 窄字节类型转为宽字节类型,有符号类型转为无符号类型
- 浮点数之间: 精度小的类型转为精度大的类型
- 整数和浮点数之间: 整数转为浮点数
17.枚举有哪些应用场景?使用枚举类型的好处是什么?
- 应用场景:状态机中的各个状态可用枚举定义,通用的方法返回值(TRUE,FALSE,SUCCESS、ERROR等)
- 好处:提高代码的可读性 增强类型安全
18.typedef 和 #define 定义的类型别名有什么不同?
- typedef:用于创建类型别名,它具有作用域,具有类型安全检查。
- #define:只是在预处理阶段进行文本替换,不具有作用域,不具有类型安全检查。
19.switch的参数数据类型有什么要求?
- 必须是整型或者枚举类型
20.break和continue有什么区别?
- break:break可以结束掉循环,并可以跳出switch中的case。
- continue:continue以跳出本次循环,循环并未结束继续下一次。
21.C语言数组有什么特点,如何计算数组长度
- 特点:数组中的元素在内存中是依次紧密排列的且有序的。初始化完成长度就是确定的,不能修改。可直接通过索引(下标)获取指定位置的元素,速度很快。
- 长度计算:sizeof(arr) / sizeof(int)
22.C 语言中字符串的本质是什么
- 字符数组,需要特别注意的是所有的字符串都以’\0’(ASCII值为0)结尾
23.现有字符串 char str[]= “hello world”,使用sizeof() 计算的字符串长度和strlen()计算的字符串长度有什么区别
- sizeof:返回的是字符数组(包括结尾的空字符\0)的长度
- strlen:返回的是实际的字符个数,不包括结尾的空字符\0
24.什么是多维数组,内存的存储形式是怎样的
- 数组的元素也是数组就是多维数组。
- 存储形式
25.两个同类型指针相减会得到什么结果
- 同类型的指针相减会得到两个指针之间元素的个数,而不是它们的地址差。
26.指针加减整数会得到什么结果
- 指针与整数的加减运算,表示指针所指向的内存地址的移动,指针每次+1(或-1)后移动多少,取决于指针指向的数据类型
27.指针能否比较大小
- 可以,比较的各自指向的内存地址的大小
28.数组名是指针吗,和指针有什么不同?
- 数组名本质:数组实体的标识符,但在有些场景下,会被隐式的转为指向第一个元素的指针,数组本身 sizeof,&取地址,指向第一个元素的指针 其余情况
- 数组名和指向第一个元素的指针的区别
29.指针数组和数组指针有什么区别?
- 指针数组:是一个数组,其中每个元素都是指针。char *strs[2] = {“hello”, “world”};
- 数组指针: 数组指针是一个指针,它指向一个数组。int arr[5] = {1, 2, 3, 4, 5};int (*p_arr)[5] = &arr;
30.指针函数和函数指针有什么不同
- 指针函数:是一个函数,其返回值为指针类型 int* fun(intx,int y);
- 函数指针:值一个指针,其指向的是一个函数 int(p_fun)(int,int);
31.常量指针和指针常量有什么不同?
- 常量指针(pointer to const):是一个指针,其指向一个常量,其指向的值不可修改const int* ptr;
- 指针常量(const pointer):只一个常量,其类型是指针,该指针的值本身不可修改 int* const ptr;
32.用字符数组和字符指针表示字符串有什么不同?
33.什么是空指针
- 空指针是一个不指向任何有效内存地址的指针,在C 语言中用NULL 表示。
34.什么是悬空指针
- 悬空指针(Dangling Pointer)是指一个指针变量指向的内存地址已经被释放或者不再有效,但该指针仍然持有该地址的值,悬空指针会导致程序访问无效内存。
35.什么是野指针,如何避免野指针
- 野指针就是指针指向的位置是不可知
- 未初始化的指针
- 悬空指针
- 越界访问的指针
36.指针和引用的区别和用法
- 引用是C++中的概念,C语言中没有。引用相当于是对已有的变量取别名。
37.使用结构体变量访问成员和使用结构体指针访问成员有什么不同
- 结构体变量: . 运算符
- 结构体指针: -> 运算符
38.结构体的长度如何计算
39.结构体和共用体有什么不同
- 两者最大的区别在于内存的使用
- 结构体各成员拥有自己的内存,各自使用且互不干涉,遵循内存对齐原则。
- 联合体所有成员共用一块内存空间,并且同时只有一个成员可以得到这块内存的使用权。一个联合体变量的总长度应至少能容纳最大的成员变量
40.形参和实参有什么区别
- 形参:形参是函数定义中声明的参数,用于接收从函数调用中传递的值。形参本质上是局部变量
- 实参:实参是函数调用时传递给函数的具体值或变量。
41.C 语言主函数的参数和返回值有什么规则
42.什么是函数原型
- 就是函数声明。包括函数名称、返回类型和参数列表,但不包括函数体
43.说出几个你常用的 C 语言系统函数
- printf strlen、strcat、strcmp malloc 、free、memcpy、memset abs、sin、cos、tan
44.传递指针给函数和传递值给函数有什么区别
- 传递值:实参的值被复制到函数的形参中,函数内部对形参的修改不会影响到实参 用于基本类型参数
- 传递指针:实参的地址被传递给函数,函数可以通过该指针访问和修改实参指向的数据,对指针指向的数据的修改在函数内外都是可见的 用于数组、结构体等较大的数据,以避免复制整个数据块
45.什么是回调函数
- 定义:举例说明:调用一个函数A时,将一个自定义的函数B作为参数传入,这个自定义函数B会在某些特定时机被调用,函数B就是一个回调函数。
- 意义:解耦
46.带参宏定义和函数有什么区别
47.printf()、sprintf()、fprintf() 三个函数有什么不同
- printf() 函数用于将格式化的数据输出到标准输出设备(通常是控制台)。
- sprintf() 函数用于将格式化的数据输出到一个字符串中,而不是标准输出设备。
- fprintf() 函数用于将格式化的数据输出到指定的文件流中。
48.static 关键字有什么作用
- 声明静态局部变量:在函数调用结束后不会销毁,生命周期延长至整个程序的执行期间,会存储在内存的全局静态区。
- 声明静态全局变量:它使得变量的作用域仅限于声明它的源文件,不可以被其他源文件访问。
- 声明静态函数:它使得函数的作用域仅限于声明它的源文件,不可以被其他源文件调用。
49.extern关键字有什么作用
- extern 关键字在 C 语言中用于声明一个变量或函数在其他文件中定义,它告诉编译器该变量或函数在其他地方已经定义过,不需要再次定义。通常,extern 用于跨文件共享全局变量或函数。
50.volatile关键字有什么作用?
- 当你声明一个变量为 volatile 时,编译器会确保CPU每次访问该变量时都直接从内存中读取或写入,而不是使用寄存器缓存的值。以此确保CPU每次都能访问到变量的最新值。
51.在1G内存的计算机中能否malloc(1.2G)?为什么?
- 如果操作系统支持虚拟内存,就可以,否则不可以
52.什么是内存泄漏
- 内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
53.什么是内存溢出
- 定义:内存溢出(Out Of Memory)是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于系统能提供的最大内存。此时程序无法运行,系统提示内存溢出。
- 出现原因:内存泄漏的堆积最终导致内存溢出。需要保存多个耗用内存过大的对象或加载单个超大的对象时,其大小超过了当前剩余的可用内存空间。
54.堆和栈有什么区别
- 管理方式:栈内存由编译器自动分配和释放,堆内存需要程序员手动管理
- 效率:栈的访问效率要高于堆。栈的特点是后进先出,因此内存的分配和释放非常简单;而堆是动态分配的,因此多次分配和释放后,会导致空闲内存不连续,这样就会增加内存分配的复杂度。
- 方向:
55.堆栈溢出一般是由什么原因导致的?
- 堆溢出:内存泄漏
- 栈溢出:函数调用层级过深/递归层次过深 分配了过大的局部变量
56.C语言如何进行动态内存分配,使用哪些函数,有哪些注意事项
- 函数:malloc(),calloc(),realloc()和free()
- 注意事项:避免分配大量的小内存块。分配堆上的内存有一些系统开销,所以分配许多小的内存块比分配几个大内存块的系统开销大。仅在需要时分配内存。只要使用完堆上的内存块,就需要及时释放它,否则可能出现内存泄漏。总是确保释放已分配的内存。在编写分配内存的代码时,就要确定好在代码的什么地方释放内存。
57.C语言的库使用过哪些?
- stdio、math、stdlib、string
STM32
1.常见的指令集架构?
- CISC(Complex Instruction Set Computing,复杂指令集)X86 Intel开发
- RISC(Reduced Instruction Set Computing,精简指令集) ARM RISC-V
2.CPU内部结构?
- 算术逻辑单元(Arithmetic logic unit,ALU) 执行算术和逻辑运算
- 控制单元(Control Unit,CU) 从内存中获取指令,并根据指令类型调度ALU、寄存器、内存等组件的工作
- 寄存器 用于存储CPU在处理数据时需要的临时数据 程序计数器(PC)指向下一条待执行的指令 指令寄存器(IR)存储当前正在执行的指令 通用寄存器用于临时存储数据
- 时钟 提供同步信号,确保CPU内部各个组件按时工作。
3.CPU和内存、虚拟内存、硬盘的关系?
- 硬盘 用于持久化存储程序和数据。
- 内存 用于临时存储程序和数据,硬盘上的系统、程序和数据,加载到内存中,才能被CPU读写。
- 虚拟内存 虚拟内存是操作系统的一种的内存管理机制,它为每个进程提供一个独立的虚拟内存地址空间,然后通过MMU将虚拟地址映射到物理地址。
4.ARM结构处理器可分为哪几类?
- Cortex-A系列 主打高性能 手机,平板,智能电视等
- Cortex-R系列 主打实时 汽车,工业控制,医疗设备等
- Cortex-M系列 主打嵌入式 传感器、智能家居等
5.嵌入式系统中ROM、RAM、Register的概念和作用是什么?
- ROM 非易失性存储器 持久化存储程序和数据
- RAM 易失性存储器 临时存储程序和数据,支持快速读写
- Register 寄存器 CPU 内部的超快速存储单元,直接参与运算和执行,存储中间计算结果
6.SRAM、DRAM、SDRAM的区别?
- SRAM 静态随机存取内存(Static Random Access Memory) 使用锁存器保存数据。所谓的“静态”,是指这种存储器只要保持通电,里面储存的数据就可以恒常保持。
- DRAM 动态随机存取内存(Dynamic Random Access Memory) 利用电容内存储电荷的多少来代表1或者0。由于电容存储的电荷会有所流失,因此为保持数据,DRAM需要进行周期性的刷新。
- SDRAM 同步动态随机存取内存(Synchronous Dynamic Random Access Memory) SDRAM的工作原理基于DRAM,但是它与CPU的时钟信号同步,允许更快速的数据传输
7.什么叫字节序?都有哪些字节序?
- 多字节数据在内存中的存储顺序,包含大端序和小端序,ARM芯片默认为小端序
8.C语言代码判断大小端?
9.STM32最小系统?
- STM32单片机能工作的最小外围电路就叫最小系统,最小系统通常包括:STM32芯片、电源、时钟、下载调试和复位5部分组成。
10.解释下什么叫嵌入式系统
- 目前流行的嵌入式系统定义是:嵌入式系统是以应用为中心,以计算机技术为基础,并且软硬件可裁剪,适用于应用系统对功能、可靠性、成本、体积、功耗有严格要求的专用计算机系统。由嵌入式微处理器、外围硬件设备、嵌入式操作系统以及用户的应用程序等四个部分组成。
11.说说你常用的STM32的外设有哪些?
12.什么是中断,解释一下中断向量表和中断嵌套
- 中断:中断是单片机处理紧急事件的一种机制,当事件发生时,CPU会暂停当前任务,转而去处理紧急事件,处理完,在恢复之前的任务。
- 中断向量表:中断向量表指的就是各中断源的中断服务程序(ISR)的地址集合,当中断发生时,CPU会根据中断类型查找相应的ISR的地址,来执行对应的中断服务程序。
- 中断嵌套:高优先级中断可以打断正在执行的低优先级中断
13.STM32如何进入中断? / 简述处理器中断处理的过程
14.简述一下STM32中断有哪些?
- 外部中断、定时器中断、DMA中断、UART中断、SPI中断、I2C中断、USB中断、ADC中断
15.STM32中断优先级如何分组?
- 通过4个bit进行分组,4个bit可分为两部分,一部分用于设置抢占优先级,一部分用于设置响应优先级
16.相比于正常函数,中断服务函数有什么特点和需要注意的地方?
- 没有参数和返回值。
- 需要配置相应的寄存器来开启中断。
- 触发条件满足时,硬件自动调用。
- 内部不能递归调用自己。
- 尽量不要执行耗时操作。
17.如果中断服务函数执行时间过长,你该如何优化?
中断推迟处理
- 裸机开发 使用标志位
- FreeRTOS 使用信号量、消息队列、任务通知等手段,实现ISR和Task之间的同步
18.介绍一下STM32 GPIO
- 功能:GPIO是通用输入输出,STM32的一种重要的外设,可以用于控制数字输入输出。
- 工作模式:输入模式 浮空输入、带上拉输入、带下拉输入、模拟输入 输出模式 开漏输出、推挽输出、开漏复用输出、推挽复用输出
- 常见用途:普通GPIO 输入模式下,可通过输入电平来读取外部传感器、按键等信号;输出模式下,可以控制外部电路元件的开关状态。 引脚复用 可以通过选择不同的复用功能,将GPIO引脚作为其他外设的输入输出端口,如ADC、TIM、USART等。中断模式 可以配置GPIO的中断模式来实现外部中断、事件捕捉等功能。
19.STM32 IO口开漏输出和推挽输出有什么区别?
- 推挽输出:既能输出低电平也能输出高电平,有比较强的电流驱动能力
- 开漏输出:只能输出低电平,不能输出高电平。如果要输出高电平,需要外部接入上拉电阻。
20.谈一下I2C总线
- 基本概念:是一种半双工、同步、串行通信协议
- 传输速度:标准模式 100kbps 快速模式 400kbps 高速模式 3.4Mbps
- 信号线:有两根信号线,分别是SDA和SCL,SDA负责数据传输,SCL负责数据同步
- 主从模式:支持主从模式,一个主设备可连接多个从设备,主设备通常是单片机,从设备是各种传感器。主设备负责发起请求,从设备负责响应
- 设备地址 每个从设备都有一个地址,每次通信,主设备都要指定从设备地址
- 传输协议 每次通信,都要以一个起始信号开始,以一个终止信号结束,中间的数据以字节为单位进行发送,没发送一个字节,都要接收一个确认信号。
- 协议细节
21.为什么I2C中要用开漏输出?
- 避免多设备冲突 I2C总线的核心特点之一是允许多个设备共享同一条总线(SDA和SCL)。采用开漏输出设计可以确保不同设备之间不会产生冲突。如果采用推挽输出,当一个设备输出高电平,另一个设备输出低电平,就可能造成短路。
- 实现多主仲裁 I2C支持多个主设备,但同一时刻只能有一个主设备控制总线。开漏输出保证总线空闲时处于高电平,便于主设备之间进行仲裁。
22.IIC驱动怎么验证?
- 读写从设备寄存器
- 读取从设备ID
- 逻辑分析仪
23.IIC通讯接一个多大的电阻,会影响速率吗
- 大小 一般1-10k,通常4.7k
- 速率 100kHz 4.7kΩ至10kΩ 400kHz及以上 1kΩ至4.7kΩ
24.谈一下SPI总线
- 工作模式 根据数据的采样时机,共分为四种模式
25.模拟SPI和硬件SPI的区别是什么?
- 硬件 SPI是芯片的外设功能,而模拟的SPI是用GPIO实现的。
- 硬件SPI的CPU占用少于模拟SPI。
- 模拟SPI的移植性高于硬件SPI。
26.uart如何保证数据传输的正确性?
- 在数据位的两端添加了起始位、奇偶校验位、停止位等用于数据的同步和校验
27.在串口通讯中,如何接收不定长数据?
- 固定格式:收发双方约定好数据开始和结束格式, 比如数据用AB开头,用BA就结束。一旦收到AB表示新数据要来了,一旦收到BA表示数据传输结束。
- 接收中断+超时判断:通常来讲,两帧数据之间,会有个时间间隔。因此,我们可以使用一个计时器,如果在一个固定的时间点里没接收到新的字符,则认为一帧数据接收完成了。
- 空闲中断:当串口一段时间没有接收到数据,则会触发空闲中断,可以认为一帧数据已经传输完毕。空闲中断并不是所有的 MCU 都具备,一般高端一点的 MCU 才有,低端一些的 MCU 并没有空闲中断。
28.介绍一下RS232、RS485?
29.如何用一个串口去操控6个温湿度传感器
- 使用RS485多点通信
- 使用串口多路复用器(如CD74HC4067)
30.解释一下异步和同步通信的区别
- 异步通信:通信双方不使用共同的时钟信号,而是通过在数据中添加起始位和停止位来标识数据的开始和结束。接收方根据预先约定的波特率(即每秒传输的比特数)来接收数据。
- 同步通信:通信双方使用共同的时钟信号进行数据传输,发送方和接收方在时钟的节拍下同步进行。例如,发送方在时钟上升沿发送一位数据,接收方在相同的时钟上升沿接收该数据。
31.请说明总线接口UART、USB、SPI、IIC的异同点
32.CAN通信协议的特点(禾望电器)
- CAN(Controller Area Network)是一种常用于工业自动化和汽车领域的串行通信总线,允许多个节点同时通信,支持广播和点对点通信模式。
- 高可靠性:采用差分信号传输,可以抵抗电磁干扰。
- 高实时性:采用优先级机制,具有快速响应和实时性高的特点。
- 高速性:传输速率高达1Mbps。
- 数据帧格式固定:数据帧格式是固定的,包括帧头、数据、校验等字段。
- 支持多节点:可以同时连接多个设备进行通信,具有较强的扩展性和兼容性。
33.介绍嵌入式系统中常用的通信接口及其适用场景
- UART:适用于简单的点对点通信,例如与外部设备(传感器、显示器等)进行数据交换。
- SPI:适用于需要高速数据传输和与多个外部设备通信的场景,例如存储器读写、传感器数据采集等。
- I2C:适用于连接多个从设备、数据传输速率不是特别高的场景,例如传感器网络、外设控制等。
- CAN:适用于需要长距离传输、高可靠性、抗干扰能力强的场景,例如车辆网络、工业自动化等。
- Ethernet(以太网):适用于需要大数据传输、远程控制、网络通信等场景,例如智能家居、远程监控等。
34.你们用的WIFI芯片是什么?与STM32是如何通讯的?(禾望电器)
- 我们用的是ESP32-C3芯片,这个芯片即提供了WIFI功能,也提供了低功耗蓝牙功能。他们与STM32是通过串口进行通信。STM32通过发送AT指令来控制ESP32-C3芯片。
35.无线模块怎么选型?分别适用什么场景?
36.请描述下你对定时器的理解?
- 工作原理:定时器的核心是一个计数器,可以按照时钟源递增或递减计数,计数频率可通过预分频器进行调整。该计数器可设置一个上限值或者下限值(ARR),计数器到达该值后,可以触发中断。
- 功能:基于该计数器,定时器可以完成基本的定时功能,以及一系列其他高级功能。周期性任务计数+中断 PWM生成 计数+捕获/比较寄存器(CCR) 输入信号测量 计数+捕获/比较寄存器(CCR)
37.STM32定时器有什么作用和优势?
38.pwm怎么实现的?
- 通过ccr(捕获比较寄存器)的值来控制占空比:占空比 (%) = (CCR / (ARR + 1)) * 100%
- 定时器的计数值小于这个值,输出为高电平; 定时器的计数值大于等于这个值,输出为低电平
39.DMA有什么作用?
- 可以在外设和内存之间直接交换数据,而无需CPU干预,从而减轻CPU的负担。
40.ADC采集的电压做什么处理,直接STM32读取吗?
- 低于参考电压可直接读取,高于参考电压,可使用串联电阻分压。
41.STM32单片机如何降低功耗?
- 不用的外设关闭相应的时钟。
- 根据实际情况,适度降低时钟频率。
- 使用低功耗模式:休眠模式、停机模式、待机模式。
42.在嵌入式开发中,你常用的调式手段有哪些?
- 调试器(Debugger):使用仿真器可以在开发板上进行源代码级的调试,包括设置断点、单步执行、观察变量值等。
- 串口调试:通过串口,输出调试信息、变量值、错误信息等。
- printf调试:在代码中使用printf函数输出各种调试信息。
- LED指示灯: LED灯来表示程序的执行状态、某些事件的发生或者错误的状态。
- 逻辑分析仪(Logic Analyzer):捕获和分析数字信号,查看高低电平方波。
- 示波器(Oscilloscope):观察和分析模拟信号,例如传感器输出、模拟电路等。
43.单片机上电后没有运转,说说你的解决思路?
44.复位后,在main函数执行之前单片机做什么?
- 初始化栈指针(Stack Pointer)
- 初始化程序计数器(PC),另其指向Reset_Handler
- 配置中断向量表
- 初始化系统时钟
- 调用 __main 函数进行 C 运行时环境的初始化
- 执行main()函数
45.看门狗的作用,底层是怎么执行的?
- 作用:监控系统是否失常,防止死循环或程序崩溃,提高系统可靠性
- 底层原理:看门狗通常是一个硬件计数器,用户可配置计数频率以及溢出值。当计数器到达溢出值就会触发复位。用户需定期重置看门狗计数器(喂狗),如果程序运行正常,则看门狗就不会复位,如果程序运行异常,耽误了喂狗,就会触发复位操作。
FreeRTOS
1.简述一下什么是RTOS系统,与普通操作系统有什么区别.
- 定义:RTOS为实时操作系统,用于处理实时任务,其目标是取保系统能够以最快的速度对外部事件做出响应。
- 与普通操作系统的区别
2.RTOS和FreeRTOS有什么关系?
- RTOS是一种通用的术语,用于指代各种实时操作系统。
- FreeRTOS是其中一种具体的实时操作系统,是RTOS中的一种。
3.FreeRTOS的主要优点是什么
- 免费开源,在商业和个人项目中自由使用和修改。
- 轻量级设计,不占用过多内存和处理器资源。
- 可移植性强,支持多种硬件平台和处理器架构。
- 应用广泛,使用多、可参考资料多。
- 功能丰富易用:多任务调度、通信、同步等功能,API简单好上手。
- 支持低功耗:有效降低系统的功耗,延长电池寿命。
4.什么是RTOS的裁剪?为什么要进行裁剪?
- 定义:RTOS的裁剪是指根据项目的需求和目标,选择性地包含或排除RTOS的功能模块。提供了用户级配置文件FreeRTOSConfig.h,可以设置一些功能开关、函数使能。
- 意义:目的是满足功能需求的同时,尽可能减小系统的资源占用 减小内存占用。降低系统开销。简化配置和维护。
4.FreeRTOS都需要配置哪些,需要注意什么?
- 调度策略:抢占式调度、时间片调度。
- 时钟频率:CPU主频、FreeRTOS系统时钟频率。
- 定义PendSV、SVC、Systick中断服务函数。
- 内存设置:heap大小(参考芯片SRAM大小)、空闲任务的栈大小。
- 中断设置 PendSV、SVC中断优先级(一般最低) 临界区屏蔽中断的范围
- 其他:需要的功能,比如信号量、队列等。
5.FreeRTOS中的上下文切换是怎么实现的?
- 核心逻辑:保存当前任务的CPU寄存器状态,恢复下一个任务的CPU寄存器状态。
- 具体操作:FreeRTOS中,每个任务都有一个独立的栈空间,发生上下文切换时,调度器会将当前任务的CPU中的各个寄存器(PC、SP、通用寄存器)的状态保存至该任务的栈空间。然后将下一个任务的CPU寄存器的状态从起栈中恢复到CPU。
6.FreeRTOS的任务调度器是如何工作的?
- FreeRTOS使用基于优先级的抢占式任务调度策略:
- 抢占式调度:高优先级的任务在任何时刻抢占正在执行的低优先级任务。
- 时间片轮转:优先级相同的任务,每个任务轮流执行一个时间片。
7.在 FreeRTOS 中,什么是任务堵塞(Task Blocking)?它的作用是什么?请举例说明任务堵塞的情况。
- 定义:阻塞是FreeRTOS定义的四个任务状态之一,当任务需要延时、或者等待信号量、消息队列、任务通知等等时就会进入阻塞状态。
- 意义:任务进入阻塞状态后,调度器会将其从调度队列移除,因此该任务不会占用CPU资源,从而提高CPU的利用率。
8.FreeRTOS为什么要设计空闲任务?
- 释放已删除任务占用的内存空间
- 实现低功耗相关逻辑
9.FreeRTOS有多少个优先级?任务优先级和系统优先级是什么关系?
- FreeRTOS的优先级级数可通过宏进行配置,32位硬件最大32.
- 系统优先级(中断优先级)和任务优先级没有直接关系
10.什么是优先级翻转? 如何处理优先级翻转?
- 定义 高优先级任务等待低优先级持有的共享资源而进入非预期的长久阻塞状态的问题。
- 解决思路:互斥信号量 优先级继承机制 低优先级任务持有高优先级任务需要的共享资源时,优先级被临时提高到高优先级任务的优先级。释放共享资源后,优先级恢复。
11.任务优先级的设置,是怎么考虑的?
- 任务的重要性:例如:传感器数据的读取>指示灯、显示屏
- 资源分配:例如:优先级分配的不合理,有可能导致低优先级任务无法执行,此时可以考虑提高低优先级任务的优先级
12.什么是临界区?FreeRTOS如何实现临界区?
- 定义:不会被其他任务打断的代码段称为临界区
- 实现原理:FreeRTOS通过禁用中断来实现临界区,由于任务的上下文切换需要依赖Systick和PendSV中断,因此禁用中断后,系统便无法进行上下文切换了,所以就能保证临界区的代码不被其他任务打断了。
- STM32平台:STM32所使用的Cortex M3内核可以使用BASEPRI寄存器屏蔽一个优先级范围内的中断。
13.FreeRTOS的任务与ISR的关系?
- 可利用信号量、队列等实现ISR和任务之间的同步
14.任务间如何进行数据的传输?
15.阐述信号量的作用,信号量的类型
- 二值信号量 常用于两个任务之间的同步
- 互斥信号量 常用于任务互斥
- 计数型信号量 盘点事件 资源管理
16.在FreeRTOS中,二值信号量和互斥量的区别?
- 互斥信号量是包含优先级继承机制的二值信号量,可以用于缓解优先级翻转问题。
17.在FreeRTOS中,任务通知的运行机制是怎么样的?
- 每个任务的TCB中都有一个通知数组,该数组用于接收通知。每个通知信息,都会占用5个字节,其中一个字节用于挂起通知,其余四个字节用于保存通知值。
- 任务向任务发送通知,不需要中间对象,直接往对方任务TCB内的任务通知数组对应位置写入通知值。
- 通知值支持多种模式,包括递增、置位等等,因此使用任务通知机制,也可以实现信号量和事件标志组的功能。
- 发送通知不支持阻塞。
- 不能在ISR中接收任务通知。
18.FreeRTOS任务通信与同步常见方式与区别?
19.在RTOS中,什么是死锁(Deadlock)?它可能导致的问题是什么?
- 死锁是指系统中的多个任务或进程之间互相等待对方所持有的资源而无法继续执行的情况。 比如:Task1和Task2,都要获取两个资源A和B才继续执行。Task1先获取了A,Task2先获取了B,此时Task1一直等B,Task2一直等A。
20.介绍FreeRTOS中的内存管理机制?
- heap_1:最简单,不允许释放内存。
- heap_2:允许释放内存,但不会合并相邻的空闲块,申请内存时,采用best fit算法。
- heap_3:简单包装了标准库中的malloc()和free(),以保证线程安全。
- heap_4:允许释放内存,并且会合并相邻的空闲块以避免碎片化。申请内存时采用first fit算法。
- heap_5:如同 heap_4,能够跨越多个不相邻内存区域的堆。
21.在FreeRTOS中,什么是任务堆栈溢出(Stack Overflow)?它可能导致的问题是什么?该如何解决任务堆栈溢出问题?
- 原因:任务堆栈溢出是指任务的堆栈空间不足,无法容纳任务执行过程中的局部变量、函数调用信息等数据,导致数据覆盖或堆栈破坏。
- 危害:任务堆栈溢出可能导致任务异常终止或系统崩溃,影响系统的稳定性和可靠性。
- 解决:开发测试时,开启栈溢出检测:configCHECK_FOR_STACK_OVERFLOW。优化代码逻辑:变量的数量和类型、减少函数调用层次、慎用递归等。增加任务栈大小。
22.如何跟踪任务运行情况?
- FreeRTOS提供了跟踪任务状态和任务时间统计的功能,可以很方便追踪任务运行情况。
- 任务状态查询:优先级、任务数量、任务堆栈剩余最小值、状态等。
- 任务时间统计:各个任务占用CPU的时间比例。
23.FreeRTOS的低功耗是怎么实现的?
- 以STM32为例,FreeRTOS的低功耗依赖于硬件的支持(睡眠模式),FreeRTOS会在所有用户任务都处在阻塞或者挂起状态时,也就是只有空闲任务运行时,计算睡眠时长,若满最小时长要求,就会调整Systick频率,另其在睡眠时间结束后发生中断。之后通过wfi指令,令STM32进入睡眠模式。睡眠期间发生任意中断,STM32都会被唤醒(若没发生其他中断,最后也会发生Systick中断)。唤醒后,FreeRTOS会补偿睡眠期间缺失的滴答值。