C语言嵌入式面试

指针
1. 数组指针 与 指针数组,函数指针 与 指针函数 区别?
答:
函数指针指向函数的指针变量,即本质是一个变量。
指针函数是指返回值是指针的函数,即本质是一个函数。
数组指针是指向数组首元素的地址的指针,其本质为指针。(这个指针存放的是数组首地址的地址,相当于2级指针,这个指针不可移动)
指针数组是数组元素为指针的数组,其本质为数组。

2. 用变量a给出下面的定义
a) 一个整型数?
b) 一个指向整型数的指针?
c) 一个指向指针的的指针,它指向的指针是指向一个整型数?
d) 一个有10个整型数的数组?
e) 一个有10个指针的数组,该指针是指向一个整型数的?
f) 一个指向有10个整型数数组的指针?
g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数?
h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数?
答:
a) int a;
b) int *a;
c) int * * a;
d) int a[10];
e) int *a[10];
f) int (*a)[10];
g) int (*a)(int );
h) int (*a[10])(int );

3.什么叫 野指针, 产生原因,如何规避
答:野指针就是 指针指向的位置是不可知的。
原因:
1、指针变量未初始化
2、指针释放后之后未置空(指针所指向的变量 在指针之前被销毁)
3、指针操作超越变量作用域
规避:
1、初始化时置 NULL
2、释放时置 NULL

4.简述数组与指针的区别?
答:
数组 在静态存储区被创建(如全局数组),或 在栈上被创建。
指针 可以随时指向任意类型的内存块。
(1)修改内容上的差别

char a[] = “hello”;
a[0] = ‘X’;
char *p = “world”; // 注意p 指向常量字符串
p[0] = ‘X’; // 编译器不能发现该错误,运行时错误
1
2
3
4
(2) sizeof(数组名),计算出数组的容量(字节数);
sizeof(指针变量),计算出指针变量的字节数,而不是所指的内存容量。

static
1. 关键字static的作用是什么?
答:
static对 局部变量 改变生存期;
static对 全局变量 改变作用域;
static对 函数 改变作用域;

const
const的意思是只读,不可改变的。

1关键字 const 是什么含意?
答:
作用:定义常量、修饰函数参数、修饰函数返回值三个作用。
定义:const修饰的数据类型是指常类型,常类型的变量或对象的值是不能被更新的。
目的:const 推出的初始目的是为了取代预编译指令,消除它的缺点,同时继承它的优点。

2.使用 const 理由
答:
1.const 修饰只读变量、数组,防止被修改。
2.节省空间。const 修饰只读变量常数只有一个拷贝,不是像#define给的是立即数,有多个拷贝。
3.使用const修饰的形参函数能够处理const和非const实参。

3.下面 const 声明都是什么意思?
答:

const int a; //a是一个 常整型数
int const a; //a是一个 常整型数
const int *a; //a是一个指向 常整型数 的指针(整型数是不可修改,但指针可以)
int * const a; //a是一个指向整型数的 常指针(指针指向的整型数是可以修改的,但指针是不可修改的
const int * const a; //a是一个指向 常整型数的 常指针(指针指向的整型数 和 指针 都不可修改)
1
2
3
4
5
4.指向字符串常量的指针,指向字符串的常量指针(const)
答:

const char * p = "hello"; //指向 "字符串常量"
p[0] = 'X'; //错误! 想要修改字符串的第一个字符. 但是常量不允许修改
p = p2; //正确! 让p指向另外一个指针.

char * const p = "hello"; //指向字符串的" 常量的指针"
p[0] = 'X'; //正确! 允许修改字符串, 因为该字符串不是常量
p = p2; //错误! 指针是常量, 不许修改p的指向

char const * 和 const char * 是一样的,const 的位置在char左边还是右边都一样.
常量指针的const应当写在 * 星号的右边.
指向常量字符串的常量指针的写法是 const char * const p = "xx"; 要2个const
1
2
3
4
5
6
7
8
9
10
11
volatile
1.概念作用
volatile(英译:易变的)是一个特征修饰符关键字,防止编译器对修饰的变量相关代码进行优化,每次使用都重新读取变量的值,而不是使用寄存器里的备份。
volatile字面意思不太好理解,其实它是提醒编译器这个变量是易变的,不要去优化它!

2.一个参数既可以是 const 还可以是 volatile 吗?解释为什么。
答: 是的。
例如是只读的状态寄存器。
volatile 是不让编译器去优化它。const 是不让程序去修改它。

3.一个指针可以是 volatile 吗?解释为什么。
答: 是的。例如当一个中断服务子程序修改一个指向一个buffer的指针时。

4.volatile 用在如下的几个地方:
1)、中断服务程序中修改的供其它程序检测的变量需要加volatile;
2)、多任务环境下各任务间共享的标志应该加volatile;
3)、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能有不同意义;

5.以上几种情况还要同时考虑数据的完整性(比如标志读了一半被打断了重写),volatile 是不具备原子性的,所以要考虑临界资源的访问冲突问题。
1)中可以通过关中断来实现,或其他方法实现;
2)中可以禁止任务调度,或其他方法实现;
3)中则只能依靠硬件的良好设计了。

内存
1.程序的内存分配
答:
1)栈区(stack)----由编译器自动分配释放,存放函数的参数值,局部变量等。
2)堆区(heap)----一般由程序员分配释放。分配使用new和malloc,释放使用deleted和free
3)全局区(静态区)(static)----全局变量和静态变量是存放在一块的。初始化的在一块区域,未初始化存放在另一块区域(BSS)。
4)常量区----存放常量字符串。
5)程序代码区----存放函数体的二进制代码。
例子程序:

int a=0; //全局初始化区
char *p1; //全局未初始化区
main()
{
int b;栈
char s[]=”abc”; //栈
char *p2; //栈
char *p3=”123456″; //123456\\0在常量区,p3在栈上。
static int c=0; //全局(静态)初始化区
p1 = (char*)malloc(10);
p2 = (char*)malloc(20); //分配得来得10和20字节的区域就在堆区。
strcpy(p1,”123456″); //123456\\0放在常量区,编译器可能会将它与p3所向”123456″优化成一个地方。
}
1
2
3
4
5
6
7
8
9
10
11
12
13
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w5O7qfi0-1661091706346)(…/_resources/2757d7fd2adcd72691003742e297ed6b.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nb1qBwNT-1661091706348)(…/_resources/e90512f0aac4bb3bebe77497f8e5b7ba.png)]

2.访问固定的内存位置。绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器,写代码去完成这一任务。
int *ptr;
ptr = (int *)0x67a9;

*ptr = 0xaa55;
1
2
3
4
一个较晦涩的方法是:
*(int * const)(0x67a9) = 0xaa55;
即使你的品味更接近第二种方案,但我建议你在面试时使用第一种方案。

3.队列和栈有什么区别?
答:队列先进先出,栈后进先出

4.解释 堆(heap)和 栈(stack) 的区别
答:
1)申请方式
stack:由系统自动分配。
例如:声明在函数中一个局部变量int b; 系统自动在栈中为b开辟空间
heap:需要程序员自己申请,并指明大小。在c中malloc函数
如:p1 = (char*)malloc(10); //在C++中用new运算符。注意p1本身是在栈中的。
2)申请效率的比较
栈: 由系统自动分配,速度较快。但程序员是无法控制的。
堆: 是由malloc/new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.
3)申请大小的限制
栈:系统预先规定好的.事先配置好。栈获得的空间较小。
堆:程序员自己定义堆获得的空间比较灵活,也比较大。
4)堆和栈中的存储内容
栈:存储函数 局部临时变量、现场的保护等。
堆:堆中的具体内容由程序员安排。

5.堆栈溢出一般是由什么原因导致的?
答:
1.没有回收垃圾资源;
2.层次太深的递归调用

6.全局变量 和 局部变量 在内存中是否有区别?如果有,是什么区别?
答:
1、全局变量储存在静态数据区,局部变量在堆栈中。
2、全局变量的作用域是整个函数,局部变量的作用域是声明该变量的函数

7.全局变量 可不可以定义在可被多个.C文件包含的头文件中?为什么?
答:可以,在不同的C文件中以static形式来声明同名全局变量。只能有一个C文件中对此变量赋初值,此时连接不会出错。

8.堆栈区别
答:

堆 栈(通常说堆栈)
管理方式不同 程序员手工分配/释放 编译器自动管理
作用不同 程序员使用的大块空间 保护运行环境现场(中断、函数调用、任务切换)
生长方向不同 向上生长 向下生长
其他
1.嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码,第一个设置a的bit 3,第二个清除a 的bit 3。在以上两个操作中,要保持其它位不变。
答:

#define BITS_CLR(v, n) v &= ~(1u << n)
#define BITS_SET(v, n) v |= (1u << n)
static int a;
BTIS_SET(a, 3);
BTIS_CLR(a, 3);
1
2
3
4
5
2.不能做 switch() 的参数类型
答:实型

3.当表达式中存在 有符号类型 和 无符号类型 时,所有的操作数是啥类型?
答:无符号类型。有符号类型将自动转换为无符号类型。

4.sizeof与strlen的区别
答:
strlen(…) 是函数,计算字符串的长度,以’\0’ 结束(不包含’\0’)
sizeof(…) 是运算符,不是函数。计算内存空间大小。在编译时就计算好了,因此sizeof不能用来返回动态分配的内存空间的大小。

常见算法
1、冒泡排序算法?
原理:比较相邻元素进行比较,交换。

/**
******************************************************************************
* @brief 冒泡排序加强版 函数
* @param *ary 数据指针
* @param len 数据长度
* @param dir 排序方向(1--降序;0--升序)
* @return None
* @note 升降排列通用
******************************************************************************
*/
#define BubbleType int //元素类型
void BubbleSortPlus(BubbleType *ary, unsigned char len, unsigned char dir)
{
BubbleType tmp;
unsigned char i, j;

len--; //自减1。注意不能少
for(i=0; i<len; i++) //外循环为排序趟数,len个数进行len-1趟
{
for(j=0; j<len-i; j++) //内循环为每趟比较的次数,第i趟比较len-i次
{
if((ary[j] < ary[j+1]) ^ dir) //相邻元素比较,若逆序则交换(升序为左大于右,降序反之)
{
tmp = ary[j];
ary[j] = ary[j+1];
ary[j+1] = tmp;
}
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2.二分查找,数组起始下标为1,则查找A[2]的比较序列的下标为(B)
int BinSearch(const int *Array, int low, int high, int target)
{
int mid;
while(low <= high)
{
mid = low + (high - low) / 2;
if(target == Array[mid]) return mid;
else if(target < Array[mid]) high = mid - 1;
else if(target > Array[mid]) low = mid + 1;
}
return -1;
}
1
2
3
4
5
6
7
8
9
10
11
12
3.PID 作用
答:

名称 作用 缺点
P 比例控制 控制对象以线性的方式增加,在一个常量比例下,动态输出。 会产生稳态误差
I 积分控制 用来消除稳态误差 会增加超调
D 微分控制 减弱超调,加大惯性响应速度 对噪音敏感,容易出现过操作
3.PID 调参方法
答:
1,先调节比例。从小往大调,使系统在目标值上下波动,且波动足够小。
2,再调节积分。从大往小调,使系统稳定。

曲线振荡很频繁,比例度盘要放大
曲线漂浮绕大湾,比例度盘往小扳

曲线偏离回复慢,积分时间往下降
曲线波动周期长,积分时间再加长

MCU/STM32 部分
1.什么单片机?有哪些组成?各有什么功能?
答:
单片机是单芯片微型计算机。把CPU、RAM、ROM、I/O口、中断系统、定时器等功能集成到一块硅片上构成的的微型计算机系统。
单片机组成由CPU、RAM、ROM、I/O口、中断系统、定时器等功能。

CPU:中央处理器。计算机系统的运算和控制核心。
RAM:随机存取存储器。用来暂时存储程序、数据和中间结果
ROM:只读存储器。存放不需要更改的程序。
I/O口:输入/输出端口。
中断系统:处理紧急事件。也是实现多道程序设计的必要条件。
定时器:计时。
2.ARM v7的体系结构可以分为哪几个子版本(款式),分别应用在什么领域?
答:A,R,M三种。
Contex-A系列面向密集型系统的应用处理器内核。顶级主控,在手机,平板,GPS普遍应用,移动设备芯片90%都是使用arm。与arm9和arm11相对应,都是可以跑操作系统系统的如linux等。
Contex-R系列面向实时应用的高性能内核。主要应用于对实时性较高的场合,如硬盘控制器、车载控制产品等。
Contex-M系列面向各类嵌入式应用的微控制器内核(相当于高级单片机)。主要应用工业、消费领域。与arm7相似,不能跑大操作系统(只能跑类似uCos2实时操作系统)

3.Cortex-M3的处理器有那两种工作模式和状态?如何进行工作模式和状态的切换?
答:
ARM状态,32位,ARM状态执行字对齐的32位ARM指令。
Thumb状态,16位,执行半字对齐的16位指令。

4.简述嵌入式系统的定义、应用、特点、构成?
答:
嵌入式系统定义:以应用为中心,以现代计算机技术为基础,能够根据用户需求(功能、可靠性、成本、体积、功耗、环境等)灵活裁剪软硬件模块的专用计算机系统。
嵌入式系统应用:应用于军事设备、信息终端、汽车电子、制造工业、航天航空等领域。
嵌入式系统特点:专用性、可裁性、实时性好、可靠性高、功耗低。
嵌入式系统构成:由 硬件层、中间层、系统软件层、应用软件层 组成。

5.你用过STM32有哪些型号?内部资源情况?价格?
答:下面自己根据情况填写

型号 内核 ROM(Flash) RAM(SRAM) 时钟 封装(IO) 功能 价格(美元)
STM32F103C8Tx M3 64K 20K 72MHz LQFP48(37IO) 2
STM32F105RBT6 M3 64K 64K 72MHz LQFP64(51IO) USB(OTG主从) 2.5
STM32F373VCx M4 256K 32K 72MHz LQFP100 16B ADC、DAC 3.2
STM32F411CEUx M4 512K 128K 100MHz UFQFPN48 2.8
STM32F412VETx M4 512K 256K 100MHz LQFP100 USB(HOST) 3.6
STM32F407VGT6 M4 1M 192K 168MHz LQFP100(82IO) 5.6
STM32F429IGTx M4 1M 256K 180MHz LQFP176 6.8
STM32H743ZIT6 M7 2M 1M 480MHz LQFP144 7
STM32H743IITx M7 2M 1M 480MHz LQFP176 7.2
6.STM32F1和F4的区别?
系列 内核 性能 特点
STM32F0 Cortex-M0 入门级MCU 适合成本敏感型应用
STM32F1 Cortex-M3 基础型MCU 高性能(外设、低功耗、低压操作),价格可接受。适合工业、医疗和消费类
STM32F3 Cortex-M4 混合信号MCU(附带DSP和FPU) 16位ADC、12位DAC、可编程增益运算,72MHz主频
STM32F4 Cortex-M4 高性能MCU(附带DSP和FPU) 180MHz主频、高性能
STM32F7 Cortex-M7 高性能MCU 216MHz主频、高性能。
STM32H7 Cortex-M7 超高性能MCU 480MHz主频、高性能
答:参看STM32开发–STM32初识
内核不同:F1是Cortex-M3内核,F4是Cortex-M4内核;
主频不同:F1主频一般多为72MHz,F4主频一般多为168MHz左右;
浮点运算:F1无浮点运算单位,F4有;
功能性能:F4外设比F1丰富且功能更强大,比如GPIO翻转速率、上下拉电阻配置、ADC精度
等;
SRAM大小: F1范围从4K~80K,F4范围64K~256K。
Flash大小:F1范围16K~1024K,F4范围128K~2048K。

7.STM32的启动流程?
答:
①上电后硬件设置SP、跳转到 Reset_Hander
②设置系统时钟(SystemInit)
③软件设置SP
④加载.data、.bss,并初始化栈区(__main)
⑤跳转到C文件的main函数

Code:代表程序代码段
RO_DATA:代表只读数据段
RW_DATA:代表已经初始化全局数据
ZI_DATA:代表未初始化全局数据

RO_SIZE = Code + RO_DATA(占用 Flash)
RW_DATA = RW_DATA + ZI_DATA(占用 SRAM)
ROM_SIZE = Code + RO_DATA + RW_DATA (烧写到 FLASH 大小空间)

8.STM32有几个时钟源?
答:
STM32 有5个时钟源:HSI、HSE、LSI、LSE、PLL。
①、HSI是高速内部时钟,RC振荡器,频率为8MHz,精度不高。
②、HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz。
③、LSI是低速内部时钟,RC振荡器,频率为40kHz,提供低功耗时钟。 
④、LSE是低速外部时钟,接频率为32.768kHz的石英晶体。
⑤、PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。

9.函数调用 和 中断服务函数执行 区别
答:
不同点:函数调用发生条件已知,中断发生条件未知;函数调用可能有返回值,中断没有返回值。
相同点:都需要CPU去执行,都需要入栈和出栈

10.ARM的中断概述
答:
STM32中断由 NVIC中断控制器 + 模块级中断使能 构成。
NVIC中断控制器:属于内核,是一个管理中断机制的模块。由ARM公司设计。
模块级中断使能:属于片上外设,外设中断使能,由芯片厂家设计

NVIC中断优先级分为两类:抢占优先级、响应优先级;中断中还有自己规定的自然优先级。
8位寄存器来设置 抢占优先级 与 响应优先级,例如:3个位来设置抢占优先级,则剩下5个位来设置响应优先级

抢占优先级:高优先级的事件发生时,能够打断低优先级的事件的执行过程。高优先级的事件能够抢到CPU资源。----处理中断嵌套问题。
响应优先级:高优先级的事件发生时,不能够打断低优先级的事件的执行过程。只能是两个优先级的事件同时发生时,先执行优先级高的事件。----抢占优先级相同时,同时中断,谁先执行的问题。
自然优先级:中断入口地址固定,则优先级固定。----以上优先级相同,同时中断,按自然优先级执行。
抢占优先级>响应优先级>自然优先级。优先级中:数值越小,优先级越高。

如:串口1(中断源): 抢占优先级2,响应优先级3,自然优先级42
定时器2(中断源):抢占优先级2,响应优先级3,自然优先级50
中断响应 对比规则:
首先比较抢占优先级,如果抢占优先级一样,才会去比较响应优先级,如果响应优先级一样,才会去比较自然优先级。

举例抢占优先级:
事件A的抢占优先级为2,响应优先级为3
事件B的抢占优先级为1,响应优先级为4
1.当同时发送异常事件时,如何执行? CPU先执行执行事件B
2.当执行事件A的过程中发生了事件B,如何执行? CPU先去执行B,执行完B后,回到事件A
3.当执行事件B的过程中发生了事件A,如何执行? CPU继续执行B,执行完B后,再执行A

举例响应优先级:
事件A:抢占优先级2 响应优先级3
事件B:抢占优先级2 响应优先级4
1.当同时发送异常事件时,如何执行? CPU执行A
2.当执行事件A的过程中发生了事件B,如何执行? CPU继续执行A,执行完A后,再执行B
3.当执行事件B的过程中发生了事件A,如何执行? CPU继续执行B,执行完B后,再执行A

举例自然优先级:
事件A:抢占优先级2 响应优先级3 自然优先级 10
事件B:抢占优先级2 响应优先级3 自然优先级 8
1.当同时发送异常事件时,如何执行? 先执行B
2.当执行事件A的过程中发生了事件B,如何执行? 继续执行A,执行完A后,再执行B
3.当执行事件B的过程中发生了事件A,如何执行? 继续执行B,执行完B后,再执行A

总结:
1.只要抢占优先级一样,就不会发生中断嵌套问题。
2.抢占优先级和响应优先级都是用户自定义。自然优先级由厂家固定。
3.两个中断之间,抢占优先级和响应优先级可以设置成一样,但是自然优先级不可能一样。

11.ADC转换器的主要技术指标( B )
答:分辨率、转换速率、量化误差

12.DMA 概念、作用
答:DMA译为 直接存储器访问。主要就是搬运数据,不需要CUP参与。

DMA控制器可编程的数据传输数目最大为(65536 )。
STM32中,1个DMA请求占用至少(2)个周期的CPU访问系统总线时间。
13.DMA搬运的方向只有三种:
答:
1.外设 到 存储器
2.存储器 到 外设
3.存储器 到 存储器

14.DMA FIFO模式 与 直接模式
答:
FIFO模式:用于在 源数据 传输到 目标 之前临时存储这些数据。源和目标的传输的数据宽度可以不一致。
直接模式:用于在 源数据 直接传输到 目标,数据宽度要一致。存储器 到 存储器 传输时不得使用直接模式。

15.大端模式 与 小端模式
答:
1)大端模式(Big-Endian): 高位字节 排放在内存的 低地址,低位字节排放在内存的高地址。
2)小端模式(Little-Endian):低位字节 排放在内存的 低地址,高位字节排放在内存的高地址。

例如:0x87 65 43 21 在 0x20000000 存储方式。

内存地址 0x20000000 0x20000001 0x20000002 0x20000003
大端模式存储 0x87 0x65 0x43 0x21
小端模式存储 0x21 0x43 0x65 0x87
大端:是符合人的顺序直观思维。常见 C51单片机、通讯协议。
小端:适合字节的存储方式,强制转换不用判断。常见 STM32单片机、BMP、操作系统。

宏实现大小端转换

#define BIG_LITTLE_SWAP16(x) ( (((*(short int *)&x) & 0xff00) >> 8) | \
(((*(short int *)&x) & 0x00ff) << 8) )
1
2
函数实现大小端转换

void BigLittleEndianSwap(unsigned char *p, unsigned char size)
{
unsigned char i;
unsigned char tmp;
unsigned char num = size/2;
size--;
for(i=0; i<num; i++)
{
tmp = p[i];
p[i] = p[size-i];
p[size-i] = tmp;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
16.常用串行通信 对比(UART、IIC、SPI)
UART IIC SPI
特征总结 异步 串行 全双工 同步 串行 半双工 同步 串行 全双工
时钟 异步 同步 同步
数据 串行 串行 串行
通信方向 RS485半双工、RS232全双工、全双工 半双工 全双工
接口 TX、RX SCL、SDA NSS、SCK、MOSI、MISO
速度(单位bps) 9.6K、…、1.152M(高) 100K(标)、400K(快)、3.4M(高) 25M、80M、100M…Mhz级以上
数据帧格式 空闲位(1)+起始位(0)+数据位+校验位+停止位(1) 起始条件+数据位+应答+停止条件
起始条件:SCL–1, SDA 1–>0
停止条件:SCL–1, SDA 0–>1 低片选+发数据位+收数据位+高片选。
四种MODE0 - 3
时钟高/低电平时发送数据位;
时钟下降/上升沿时接收数据位;
GPIO端口模式设置 TX推挽输出
RX浮空输入 SCL推挽输出
硬件模式:SDA开漏输出、既不上拉也不下拉。
软件模拟:SDA推挽输出 或 配置上拉输入。 MISO 主机输入,从机输出。
MOSI 主机输出,从机输入。
SCLK 时钟信号,由主机产生。
CS 从机片选信号,由主机控制。
主从设备通讯 没有主从之分 有主从之分 有主从之分
总线结构 一对一 一对多 一对多
I2C仲裁机制?

简单说,它遵循“低电平优先”的原则,即谁先发送低电平谁就会掌握对总线的控制权。
SPI通信的四种模式,就是SCLK极性 与 相位 组合的四种情况。

SCLK极性 就是 空闲时0或1
SCLK相位 就是 上升沿或下降沿
I2C、SPI 通讯速率?
答:I2C小百K (100K\400K); SPI在十几到几十M

17.CAN总结介绍一下?
CAN控制器根据 CAN_H 和 CAN_L 上的电位差来判断总线电平。总线电平分为显性电平(差为高电平)和隐性电平(差为低电平)。发送方通过使总线电平发生变化,将消息发送给接收方。

18.CAN初始化配置步骤?
(1)配置相关引脚的复用功能,使能CAN时钟
(2)设置CAN工作模式及波特率等(CAN初始化环回模式,波特率500Kbps )
(3)设置滤波器

TCP/IP
1.TCP与UDP区别
TCP UDP
是否连接 面向连接,多次握手,类似打电话 无需连接,不用握手,类似发短信
是否可靠 传输可靠 传输不可靠
连接对象 点对点连接 可以一对一,一对多,多对一和多对多连接
首部开销 开销大,20~60字节 开销小,8字节
传输速度 传输慢 传输快
传输方式 字节流 报文
适用场景 可靠传输,如文件 实时应用,视频,直播等
程序上标记 有监听 没有监听
2.TCP 服务端 与 客户端 区别
TCP 服务端 TCP 客户端
主被动 被动角色 主动角色
类似 10086客服 手机主人
连接请求 等待来自客户端的连接请求 发送连接请求
执行情况 处理请求并回传结果 等待服务器的响应
客户端侧在配置TCP连接时,必须设置服务器IP地址及端口号,自身可设备自动分配,或设定好。
服务器侧在配置TCP连接时,必须设置服务器使用的端口号,客户端IP地址及端口号为可选项。

IP地址:网络协议地址。类似电话号码
端口号:用于区分不同服务的逻辑编号,通常使用2000~5000范围内端口。类似分机号

3.子网掩码作用
1、屏蔽部分IP地址,区分 网络标识 和 主机标识,解释 IP地址 是在 局域网上 还是在 远程网络上。
2、将一个大的IP网络划分为几个小的子网络。

4.网关(Gateway)
网关称为网间连接器。我们知道从一个房间走到另一个房间,必然要经过一扇门。同样,从一个网络向另一个网络发送信息,也必须经过一道“关口”,这道关口就是网关,具有路由功能,如路由器。网关也是有IP地址的。

Bootloader
规划 bootloader 和 app 的空间

1.Bootloader 程序部分
1.在mdk中设置 bootloader 空间大小
2.擦写APP FLASH
APP固件在Bootloader中接收的 情况
1)、检查是否要升级
2)、接收app固件文件
3)、擦 APP FLASH
4)、写 APP FLASH
APP固件已存储的 情况
1)、检查存储区是否有app固件
2)、擦 APP FLASH
3)、写 APP FLASH
4)、擦 升级标志
3.实现跳转
1)、检测 APP 栈顶地址是否合法。
2)、设置 MSP和PSP 栈指针 APP
3)、关闭所有中断(若关闭了总中断,app程序中要开启)
4)、跳转到 APP 复位地址处。(地址强转成函数指针,调用)

2.App 程序部分
1、在mdk中设置 App 空间起始地址和大小
2、中断向量表重定向,修改偏移地址(系统初始函数最后)
3、生成bin文件

RTOS共同部分
1.RTOS操作系统 与 裸机开发(前后系统)区别
RTOS:人为的为任务切换CPU资源,并对运行环境进行保护管理,相当虚拟了多个CPU。每个任务都是一个死循环。
裸机开发(前后系统):每个任务只能顺序获得CPU资源。一个任务运行一遍了才轮到下个任务。

2.RTOS 任务优先级 特点
答:
FreeRTOS 任务优先级 数值越小,优先级越低。
uCOSII 任务优先级 数值越小,优先级越大。

3.回调函数 与 钩子函数 的区别
答:
回调函数:用户定义、编写的函数,注册给系统调用的函数。
钩子函数:系统定义、实现了部分函数功能,剩下部分需要用户实现的函数。

4.什么是不可剥夺型内核?
答:各个任务彼此合作共享一个CPU 内核要求每个任务自我放弃CPU的所有权。

5.什么是可剥夺型内核?
答:根据优先级可占用当前CPU的使用权。

FreeRTOS 部分
1.FreeRTOS 移植过程
答:

STM32CubeMX移植FreeRTSO

官方示例移植

1,下载官方示例文件
Demo: 对应芯片系列和编译器名称文件夹下的 FreeRTOSConfig
FreeRTOS 源码文件:源码文件、头文件夹、移植文件(内存管理、编译器文件下的 内核接口文件)
2,官方工程编译修改正确
3,添加到自己对应芯片工程里,并修改FreeRTOSConfig系统时钟参数
2.队列、事件标志组、信号量、任务通知 总结
功能 区别列表
消息队列 (需要传递消息时使用) 在任务与任务间、 中断和任务间传递信息,可以数据传输
事件标志组 (多个事件同步,不需要传递消息时使用) 实现任务与任务间、 中断和任务间的同步,无数据传输。 可实现一对多、多对多的同步, 可选择是 “ 逻辑或 ” 触发还 是 “ 逻辑与 ”触发。即 一 个任务可以等待多个事件的发生。
信号量 (单个事件同步,不需要传递消息时使用)
#二值信号量 类似一个标志位。仅”空“(0)和”非空“(1)两种状态
#计数信号量 用来 事件计数 和 资源管理
#互斥信号量 (中断中无法使用)拥有优先级继承的二值信号量
任务通知 可以在一定场合下替代 FreeRTOS 的信号量, 队列、事件组等
1.任务
1.什么是任务
任务: 运行的函数
句柄: 结构体指针

2.任务状态
就绪(Ready): 随时可以运行的任务,但还没轮到它。新创建的任务为就绪态。
运行(Running): 任务正在执行,此时占用处理器。例如,汽车正在行驶。
阻塞(Blocked): 等待某个事件(时间延时,事件等)。例如,汽车等待绿灯。
暂停(Suspended)(挂起): 不等待,直接去休息了。
3.任务优先级
高优先级的任务,优先执行,可抢占低优先级任务
高优先级的任务不停止,低优先级任务永远无法执行
同等优先级任务,轮流执行(或叫时间片轮转)

优先级有关配置:
是否 可抢占
是否 时间片轮转
是否 空闲任务让步

4.任务怎么管理
FreeRTOS通过三种链表(就绪任务链表、阻塞任务链表、暂停任务链表)来管理任务。就绪任务链表 有多少个优先级就有多少个 就绪任务链表;阻塞任务链表、暂停任务链表 各1个。

1、从高优先级 就绪任务链表 向低优先级 就绪任务链表 中找到就绪任务,获取任务句柄给到运行指针, 运行它。
2、若有同级任务,排队轮流执行,链表前面的先运行,运行1个tick后排到队伍后面。
3、若某个任务阻塞,则将该任务从 就绪任务链表移到 阻塞任务链表 中;阻塞解除,则反之。
4、若某个任务暂停,则将该任务从 就绪任务链表移到 暂停任务链表 中;解除暂停,则反之。

5. 怎么进行任务调度
在Tick中断函数中会进行任务切换(链表种取出合适的任务运行)。每次中断会检查有哪些延时任务到期,将其从延时任务列表里移除并加入到就绪列表里。如果就绪任务的优先级都相同,如果开启时间片轮询,就会每个tick执行一个任务,轮询执行。

Tick中断做了什么:

1)、找到就绪链表中最高优先级,且排在最前面的的绪任务。
2)、保存当前任务的信息,并将其插入就绪任务链表末端
3)、恢复新的任务
6.创建任务所需参数
任务函数、设置任务栈大小(1000)、优先级、参数、传出的任务句柄

2.同步与互斥
同步: 一种依赖关系,A任务运行完了,B任务才能运行。
互斥: 访问同一个资源时,同一时刻只能有一个获得,一个使用完后才能轮到下一个。

3. 队列
种类:队列、消息邮箱(长度为1)

1. 队列
队列是为了任务与任务、任务与中断之间传递不定长消息。FreeRTOS采用是复制消息方式。

特性:
读写队列支持超时机制。
消息支持先进先出原则(FIFO),但是也支持后进先出原则(LIFO)。
可以允许不同长度的任意类型消息(不超过队列节点最大值)。
一个任务能够从任意一个消息队列接收和发送消息。
多个任务能够从同一个消息队列接收和发送消息。
当队列使用结束后,可以通过删除队列函数进行删除。

2. 消息邮箱
消息队列长度为 1 的情况。

4. 信号量
信号量其实就是只有一个队列项的队列,该队列项不是用来传递消息,而是用来计数。
作用:资源管理、任务同步、互斥访问。

种类:计数信号量、二值信号量、互斥信号量、递归信号量

1.计数信号量
作用:事件计数 或 资源数量的管理。
优先级翻转现象: 只有一个资源时,低优先级任务 正在占用 资源,高优先级任务 只能等待 低优先级任务 释放资源。这种现象称为“优先级翻转”。

2.二值信号量
概念:计数值只有 0 和 1 信号量。

3.互斥信号量
概念:具有优先级继承的二值信号量。
优先级继承:低优先级任务 在使用 互斥信号量时,这时候 高优先级任务 也想用互斥信号量,高优先级任务 把优先级暂时继承给 低优先级任务 减少阻塞时间,让赶快用完。

互斥信号量 不能用于 中断服务函数,原因如下:
1、有 优先级继承 机制。
2、中断服务函数 不能设置阻塞时间。

4.递归信号量
概念:其实就是 互斥信号量 嵌套。

5. 事件组
概念:就是一个 32 位的标志集合。(0 表示该事件类型未发生、1 表示该事件类型已经发生)。支持事件等待超时机制。
作用:多个事件同步,不需要传递消息时使用。

特性:

仅用于同步,不提供数据传输。
多次向任务设置同一事件(如果任务还未来得及读走),等效于 只设置一次。
允许 多个任务 对 同一事件 进行 读写操作。
支持 事件等待 超时机制。
6. 任务通知
要明确发给那个任务。

任务通知可以实现:消息邮箱,计数信号量,二值信号量,事件标志组。
不同点:

使用 队列,信号量,事件标志组 前 ,必须先创建。
在创建任务的时候,系统已经创建了任务通知, 任务通知处理更快,RAM 开销更小。
任务通知限制:

只有一个任务等待信号量。
不支持超时等待,
几种方式发送通知给任务 :
1、如果有通知未读,不覆盖通知值。
2、直接覆盖通知值。
3、设置通知值的一个或者多个位 ,可以当做事件组来使用。
4、递增通知值,可以当做计数信号量使用
7.综合问答
FreeRTOS 有几种任务状态?
就绪(Ready): 随时可以运行的任务,但还没轮到它。新创建的任务为就绪态。
运行(Running): 任务正在执行,此时占用处理器。例如,汽车正在行驶。
阻塞(Blocked): 等待某个事件(时间延时,事件等)。例如,汽车等待绿灯。
暂停(Suspended)(挂起): 不等待,直接去休息了。
FreeRTOS 优先级?
答:FreeRTOS 任务优先级 数值越小,优先级越低。

FreeRTOS 调度机制(调度算法/调度算法)有几种?
答:三种调度方式:时间片、抢占式、合作式。
时间片调度(不同优先级):每个任务都有相同的优先级,任务会运行固定的时间片个数或者遇到阻塞式的API函数,比如vTaskDelay,才会执行同优先级任务之间的任务切换。
抢占式调度(相同优先级):每个任务都有不同的优先级,任务会一直运行直到被高优先级任务抢占或者遇到阻塞式的API函数,比如vTaskDelay。
合作式调度:用到的很少。相同优先级时间片,不同优先级抢占式调度。

FreeRTOS 中,互斥信号量和二值信号量的区别?二者能不能中断中调用,说明原因?
答:
互斥信号量
1.有优先级继承。
2.中断中不调用。

二值信号量
1.无优先级继承。
2.允许在中断中调用。

互斥信号量 不能用于 中断服务函数,原因如下:
1、有 优先级继承 机制。
2、中断服务函数 不能设置阻塞时间。

单片机裸机和 rtos 开发过程中,如何保证全局变量在中断和主循环中读写的正确性?
答:这个变量是临界区资源,无论裸机还是rtos,都要保证临界资源的可见性和原子性。对于变量可见性用volatile修饰,原子性在rots就要访问变量前设置进入临界状态,访问完毕退出临界状态;裸机就要用锁或关闭中断等方法,若嵌套的话还要注意锁或中断状态的保存。

UART是全双工还是半双工?485是全双工还是半双工?
答:UART/RS232是全双工;485是半双工。

你这个项目485采用是啥接法?
答:采用一条总线将各个节点串接起来,不支持环形 或星型网络。分支多会造成阻抗不连续,会产生大量的驻波和反射。
阻抗匹配电阻120R。

你这个项目485主从机如何地址码如何实现,通讯如何识别?
答:目标地址(收件人地址)+本机地址(寄件人地址)。

LCD屏是多大?啥通讯协议?
答:4.3寸(240x320 RGB),用的是8080通讯协议。支持I2C、SPI。

8080、6800 通讯有几个线,通讯协议是啥?有啥区别?
答:8个双向数据线(DB0~DB7),5个控制线,共计13个。8080、6800通讯类似。

控制线:

复位(/RES)
数据/命令 选择(D/I)
片选(CS)
1). 8080: 读使能(/RD); 写使能(/WR)
2). 6800: 总使能(E); 读/写选择(R/W)
8080 通讯协议

1.选择 数据/命令。
2.读、写 禁止。
3.片选。
4.读/写 使能。
5.读/写 数据。
6.读/写 禁止。
7.片选 取消。
6800 通讯协议

1.选择 数据/命令。
2.读/写 选择。
3.片选。
4.使能。
5.读/写 数据。
6.禁止。
7.片选 取消。
I2C通讯有几个线,通讯协议是啥?
答:2线

SPI通讯有几个线,通讯协议是啥?
答:SPI有3/4线(区别 有无 主输入线)
四种模式,时钟极性、相位两种情况。

NorFlash 与 NandFlash 有啥区别?如何选型?
答:

区别项 NOR Flash NAND FLASH
擦写速度 慢 快
耐用性 擦次数少(10万次) 擦次数多(100万次)
容量和成本 容量小(1MB~32MB),贵 容量大(16MB~512MB),便宜
易用性 接口和RAM一样,一次可读一个字节,可直接在上面运行代码 接口使用I/O口来串行访问,一次读512字节,不可直接运行代码,必须先写入驱动程序,才可以继续执行其他的操作。
主要用途 常用于 保存代码 和 关键数据 用于 保存数据
根据用途选型。

MCU 如何选型?
统计有项目哪些外设
确定外设的供电范围及通讯电平
确定外设IO的种类(中断、PWM、ADC、ADC、通讯)
确定外设的IO数量
统计外设占用的内存和flash大小
再依据成本、交货周期、采购难易、流通性
MOS管 如何选型?
确定N、P沟道的选择
当MOS管接地,负载连接到干线时,构成了低压侧开关,应采用 N沟道MOS管。
当MOS管连接到干线,负载接地时,构成了高压侧开关,常采用 P沟道MOS管。
确定电压(1.5倍)
确定电流
确定开关性能
确定热要求(RDS(ON))
封装因素考量
品牌(品牌代表性能差异、流通性、成本等等)
N管:2N7002
P管:AO3401A
三极管 如何选型?
根据电路确定 N管、P管
确定电压(1.5倍)
确定电流
确定开关性能
确定热要求(RDS(ON))
封装因素考量
品牌(品牌代表性能差异、流通性、成本等等)
N管:8050(40V)、3904
P管:8550(40V)、3906
可控硅管 如何选型?
ARM Cortex-M3/M4 内核有多少 寄存器?
答:16个寄存器(R0‐R15)。其中 13个 通用寄存器(R0‐R12),3个 特殊用途寄存器( R13(SP)、R14(LR)、R15(PC) )

堆栈指针 R13(SP)
CM4 处理器内核中共有两个堆栈指针,共用R13,同一时刻只能用一个。

主堆栈指针(MSP),或写作 SP_main。这是缺省的堆栈指针,它由 OS 内核、异常服务例程以及所有需要特权访问的应用程序代码来使用。
进程堆栈指针(PSP),或写作 SP_process。用于常规的应用程序代码(不处于异常服用例程中时)。
连接寄存器 R14(LR)
用于在调用子程序时存储返回地址。只有一级调用就不用入栈。

程序计数器 R15(PC)
R15 是 程序计数器,是用于存放下一条指令所在的地址。

ARM Cortex-M3/M4 内核有多少 特殊功能寄存器组?
5个

程序状态 寄存器组(PSRs 或曰 xPSR)
中断屏蔽 寄存器组(PRIMASK, FAULTMASK,以及 BASEPRI)
控制 寄存器(CONTROL)
HardFault死机如何查找?如何调试?
答:
1、堆栈溢出
2、内存溢出或访问越界

第一种方法,在线调试,通过MDK窗口查找发生死机的位置。
Step1:找出SP地址。
“CPU register”窗口查看LR寄存器B2值(0-MSP;1-PSP),判断SP是MSP(主)还是PSP(进程),复制SP地址。
Step2:找出PC地址。
在memory窗口,输入SP的地址,找到死机前压栈的8个寄存器(压栈依次为 xPSR、PC、LR、R12以及 R3~R0),当前SP地址往大数第7个是死机前PC地址。
Step3:找出代码行数。
在反汇编窗口,输入PC地址,就能找到死机前代码的位置。

第二种方法,离线调试,增加代码,打印发生死机的位置。
目前没用过。

如果在客户现场出现BUG,不拆机如何查找解决问题?
答:要有整体局部意识。
1)、分析问题触发条件和环境;
2)、复现问题;
3)、根据问题现象和环境条件,确认分类类型(比如:电磁环境,硬件问题,软件问题);
4)、根据根据问题类别制定查找方案,并找到问题;
5)、解决问题,并测试;
6)、还原问题,验证问题点和解决方案是对的;

posted @ 2023-08-03 10:21  丁培飞  阅读(124)  评论(0编辑  收藏  举报