CVTE 等公司的嵌入式面试

CVTE 等公司的嵌入式面试

1.自我介绍

我叫。。。优势。。。。做过啥设计

2.const 关键字的作用

1.保护定义的变量防止意外的修改,增强程序的健壮性

为什么会被意外的修改呢?有可能时在别的.c文件使用的时候,修改了。

怎么修改,通过指针修改

程序维护,升级的重要性

https://blog.csdn.net/zhanshen112/article/details/80783258

这里的23例题,讲解了static局部变量如何防止被修改。

看到const 的一个反应是它只读。

4

https://blog.csdn.net/silently_frog/article/details/96737764?ops_request_misc=%25257B%252522request%25255Fid%252522%25253A%252522161175906016780255239714%252522%25252C%252522scm%252522%25253A%25252220140713.130102334.pc%25255Fall.%252522%25257D&request_id=161175906016780255239714&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_v2~rank_v29_name-3-96737764.pc_search_result_hbase_insert&utm_term=const+%25E5%2586%2585%25E5%25AD%2598 关于const 内存分配的知识

3.extern 关键字的作用

说明这个变量在别的C文件已经被声明了。

不能重复定义 ,定义只能允许一次!!

volatile 关键字的作用

被设计用来修饰被不同线程访问和修改的变量

因为不同的线程、硬件,或者操作系统访问的时候是会改变值的

确保指令不会因编译器的优化而省略

既每次都从变量的地址中读取数据

volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。

static 关键字的作用

存储于进程的全局数据区 (有点类似全局变量)

static 主要是让自己定义的变量,或者函数只能在这个C文件中使用, 你定义的时候不需要考虑别的文件是否由同样的变量,就算有不怕。

注意:全局变量和静态全局变量的区别

1)全局变量的话,一般指的是没有用static所定义的变量,它可以在其它文件被调用,只要在变量前面加上extern就可以了。

2)全局静态变量的话,前面加上static,表示只能在这个文件内使用了,当然了,这也是有好处的,这样不用管其它文件是否定义了相同的变量。即使加了extern 也是不行的

https://baike.baidu.com/item/static/9598919?fr=aladdin#2

static 变量, 静态,因为只能在本文件被调用,所以在程序运行的一开始就被初始化,也是唯一一次初始化

https://blog.csdn.net/zhanshen112/article/details/80783258

这里的23例题,讲解了static局部变量如何防止同文件中其它函数被修改。

i2c总线最多可以挂多少个ic

i2c总线最多可以挂多少个ic?有哪些因素决定?
1.由IIC地址决定,8位地址,减去1位广播地址,是7位地址,2^7=128,但是地址0x00不用,那就是127个地址, 所以理论上可以挂127个从器件。
2.还受总线电容 400pf的限制,一端测地,一端测线,不能超过400pf

STM32GPIO口中的推挽输出和开漏输出的问题

具体看这里 “https://www.firebbs.cn/thread-12702-1-1.html”

推挽输出和开漏输出,两个都是配置输出用的,好像没啥不同,但是确实不一样

开漏出最大不同就是把它置1 的时候,它呈现的是高阻态。

如果需要让它置1的时候也要输出高电平。

怎么办呢?

那就是给个上拉电阻。这个上拉电阻很妙,我们可以让它拉3.3v 也可以让它拉5V。

拉5V就是说要让它输出电平为5V。

为什么有时候要拉5V呢?

因为STM32的IO驱动能力只有3.3V ,而当我们需要连到5V的模块的话,

就得配置为开漏输出,上拉电阻接5V

有什么是5V的模块呢? 多了, 例如有些5V 的lcd1602

第二点比较重要的是它的“线与”特性

通常是用在IIC通信上,(IIC通信上需要把引脚配置为开漏输出)

线与功能有什么用呢?

线与的特性就是当总线上有一个设备输出低电平时,整条总线便处于低电平状态,这时候的总线被称为占用状态。

https://blog.csdn.net/jiangdf/article/details/72779046?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control

C语言程序运行的时候,内存中有多少个区?

有常量区, 静态存储区, 代码区, 堆和栈区。

常量区主要是const 的,已经初始化字符串的常量

静态存储区主要是全局变量以及 static的变量

堆区主要是人为申请分配和释放的区

栈区主要是编译器自动分配的,局部变量就放在这

堆和栈的效率问题

申请效率

栈比堆快, 系统自动分配,但无法控制

存储效率

栈比堆快? 不太懂,网上资料较少

小结

堆和栈的区别可以用如下的比喻来看出:
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自 由度小。

使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由 度大。 (经典!)

指针

1.指针中*p 和 p

int a;

int *P      
然后 int* p; 这样也可以,测试照常打印, int *p 和 int* p 是一样的
int* p,a;  表示的是定义一个指针变量p,和整形变量a。 而不是说一个p指针和一个a指针

p是指针, 可以存某个变量的地址

例如我要存 a 的地址, 所以 p=&a; (注意这两个数据类型要一致,int型, 否则要强制转换)

然后要打印 a的值的话, 可以这样 :

printf("%d",a);

或者 printf("%d",*p);
这里的*p表示取出指针所指内存地址的值

如果是

printf("%d",p); 这样就变成了打印a 的地址。

2.指针和字符串的赋值

	char *s;
	s="hello";
	printf("%s",s);  //能够正常打印
	char *s;
	*s="hello";
	printf("%s",s);  //不能够正常打印

3.指针和数组的赋值

4.指针的类型和它赋值的类型

int a=7;
char *p1;
p1=&a;   //!!!这里请注意,char型指针,我指向了int型的
//所以在以下程序中,我赋值必须是整形赋值
scanf("%d",p1);   
printf("%d",*p1);

错误示例:
scanf("%s",p1);   
printf("%s",*p1);
			
%s是输入字符串,  %c是输入字符,对于 char    %x是输入16进制数

如果要定义一个字符串,就是char s[]="hello"; 这样定义可读可写

char *a0 与 char a1[]的区别

char *a0 = "hello";    hello 放在常量区 ,只能读写
char a1[]="hello" ;    hello 放在栈,可以通过指针去访问和修改数组内容

scanf("%s",a0);  // 这样是不允许的,
scanf("%s",a1);  //这样是可以的。
#include 
#include "windows.h"
void main()
{
	char *a;
	scanf("%d",&a);
	printf("%d",a);
	
}
这种情况下又是可以的, 但是注意,这里的话是只能输入数字,输入字符的话,结果总是输出1
    
更新:以上是本人知识薄弱的见解,因为我定义了一个指针,但是没有指向,所以a是野指针。

浮空和高阻态的区别

1.浮空输入在正点代码中用于 触摸按键,以及触摸屏的使用。(正点原子代码)

高阻态是在总线中使用的,例如在那个IIC中,实现线与功能,或者外加上拉电阻接别的模块

浮空输入:对于浮空输入,一直没找到很权威的解释,只好从以下图中去理解了

由于浮空输入一般多用于外部按键输入,结合图上的输入部分电路,我理解为浮空输入状态下,IO的电平状态是不确定的,完全由外部输入决定,如果在该引脚悬空的情况下,读取该端口的电平是不确定的。

3.电路分析时高阻态可做开路理解。你可以把它看作输出(输入)电阻非常大。它的极限状态可以认为悬空(开路)。也就是说理论上高阻态不是悬空,它是对地或对电源电阻极大的状态。而实际应用上与引脚的悬空几乎是一样的。

  1. 高阻态可以看成接一个无限大电阻再接地。

STM32代码测试

串口IO脚配置测试

经过测试, 把STM32串口PA9 PA10脚该为浮空输入是不能运作的

​ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//输入功能

​ GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //浮空

然后, PA9 PA10脚改为 复用功能, 然后浮空,是可以运作的

​ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能

​ GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //浮空

总结,在STM32F4中,模式一定要是复用功能才可以,上下拉浮空的话哪个都行

IIC

时序图

SCL线总是为高, SDA线由1到0 开始 SDA线由0到1 结束。

看野火的视频 第24讲 读写EEPROM(第六节-代码讲解-初始化IIC)

在视频中,他给的代码是硬件IIC例程, 其中IO口配置为开漏输出。

其中解释:因为开漏输出逻辑1的时候是高阻态, 这样不会造成短路。

假如是推挽输出,逻辑1的时候高电平, 输出逻辑0的时候低电平,

然后总线上挂载多个模块的时候,会造成短路。

你可能会说,外面上拉电阻不会造成短路吗?不会的,是因为有个电阻嘛,傻

程序Debug方式:

微观方式:

1.硬件上是否无错误,晶振是否起振。2.使用printf函数打印 3.使用assert parrm来 判断参数

4.仿真调试 5.使用示波器来查看引脚信号电平

宏观方式

1.跟同事讨论,讲解一下你处理的流程,2.写文章记录下来(让你在梳理的过程发现错误点)

变量的生命周期和作用域

全局变量和静态变量,生命周期是程序的编译到程序的运行结束为止。 (运行结束可以理解为停下来了,不再执行语句了,或者死机了?)

局部变量的生命周期是程序运行的某一个时刻开始

img

全局变量

全局变量没有初始化的话,默认为0

本地变量(生存期在函数里头,进去才有,离开就没有了)也叫做局部变量

1.静态本地变量(实际上是特殊的全局变量)

1.加上static

  1. 当函数离开的时候,静态本地变量会继续存在并保持其值
  2. 静态本地变量的初始化

动态内存分配

为什么需要malloc? 因为在编程的时候不能确定数组应该定义多大,在程序运行时要根据需要从系统中动态地获取内存空间

不断地申请,如果不释放的话,程序不一定会崩溃

到时候它是返回空指针,也即是不让你去申请了。

结构体的使用

1.为什么需要结构体

当我们使用函数的时候,往往都带有参数,当参数越来越多的时候,如果要增加参数,那么整个函数定义,声明都要改,而使用结构体作为形参,只要增加就可以了,函数的参数时不需要改的。

2.结构体的声明方式
1.第一种
struct {
    
member-list;  //成员列表,例如人的身高,年龄,性别
    
} variable-list; //变量列表
这种的话,每次声明变量都要做出一个成员列表这样

2.第二种
struct tag {   //带了一个tag

member-list;

};  //取消了变量列表

这种的话,带了一个tag,下次需要声明的时候,直接 struct tag var;

3.第三种
typedef struct{
    member-list;
}tag;
这种的话, 连struct 都不需要了, 直接tag var;
在STM32中最常见

结构体指针的话,多数用->

结构体的话,多数用 .作下标

const和宏定义的区别

1)就起作用的阶段而言: #define是在编译的预处理阶段起作用,而const是在 编译、运行的时候起作用。
(2)就起作用的方式而言: #define只是简单的字符串替换,没有类型检查。而const有对应的数据类型,是要进行判断的,可以避免一些低级的错误。
(3)就存储方式而言:#define只是进行展开,有多少地方使用,就替换多少次,它定义的宏常量在内存中有若干个备份;const定义的只读变量在程序运行过程中只有一份备份。
(4)从代码调试的方便程度而言: const常量可以进行调试的,define是不能进行调试的,因为在预编译阶段就已经替换掉了。

编程题

1.当时我做的两道编程题是 类似字符串的比较 和一个根据按键输入判断密码是否正确。

2.当时抽到的是求一个数的质因子。(算法题)

3.手写一个判断字符串是否对称的函数。

4.给定一个字符串,如何高效地将内部个空格替换成%20

5.strcpy实现原理。

6.翻转单链表

7.完成一个符号数字字母字符串的排序。

8数组排序,以及字符串题目

51单片机的IO口和STM32的IO口

51单片机的除了P0脚是开漏输出模式外。

其它IO都是双向IO,

什么是双向IO,就是直接读写,不用配置输入输出模式

但是STM32的io口需要配置输入输出模式这样来读写。

但是STM32如何配置双向IO呢,方法是:

将STM32的IO配置成开漏输出,然后外接上拉,就实现了双向IO。
注意:读取IO数据时需要使用GPIO_ReadInputDataBit();

计算字符串长度的几种方法

char *s;

1.strlen(s)

2.while(*s!='\0')
    {
        count++;
    }
3.  for(i=0;t[i]!='\0';i++){
  	 count++;
  }

心得

1.有时候我们需要注意操作结构体,以及操作结构体里面的成员变量,这里需要区分,例如结构体指针,但是里面有成员是int类型,所以scanf要取地址,以及另一种我们定义普通结构体,普通结构体里面含有指针变量,free的时候结构体里面的指针变量,不是结构体本身。

你可能感兴趣的:(嵌入式,软件工程)