总体来说,汇顶科技的固件开发工程师的笔试卷并不算难,中规中矩的一份试卷。试卷12条单选题、2条多选题、2条填空题、1条编程题、3条简答题。题目从考察的知识点上来讲,C语言基础(没有C++)、单片机基础为主,也不是很有难度,只要基础还不错的话应该是没有什么问题的。
由于题目也记得不是很清晰,就大体上挑自己记得的内容来写了。
需要注意的是:汇顶科技的编程题不允许使用本地的IDE编译器,必须使用牛客网的编译器进行调试,也就是说,笔试过程中不允许跳出考试界面。
1、单片机8051的寻址方式
解答:这个知识点在整个试卷上出现的次数太多太多了(单选、多选、填空都有),来来回回地考,特别重要!
寻址就是寻找指令中操作数或操作数所在的地址。所谓寻址方式,就是如何找到存放操作数的地址,把操作数提取出来的方法。通常指源操作数的寻址方式。MCS-51系列单片机寻址方式共有七种:寄存器寻址、直接寻址、立即数寻址、寄存器间接寻址、变址寻址、相对寻址、位寻址。
8051的寻址方式寻址方式 | 含义 | 寻址范围 | 汇编 |
立即数寻址 |
指令操作码后面紧跟的是一字节或两字节操作数,用 # 号表示,以区别直接地址。 |
程序存储器ROM |
MOV A, #data 该指令将立即数data传送到累加器A中,双字节指令。 |
直接寻址 | 指在指令中直接给出操作数所在的存储单元的地址 | 片内RAM低128B,特殊功能寄存器(SFR) |
MOV A, direct 该指令的功能是将片内RAM地址direct单元中的内容传送到累加器A中,双字节指令。 |
寄存器寻址 | 指操作数存放在某一寄存器中,指令中给出寄存器名,就能得到操作数。 | 工作寄存器R0~R7,A,C,DPTR,AB |
MOV A, Rn ; n=0~7 该指令将Rn中的内容传送到累加器A中,单字节指令。 |
寄存器间接寻址 | 寄存器中的内容是一个地址,由该地址单元寻址到所需的操作数。 | 片内RAM低128B,片外RAM |
MOV A, @Ri ; i=0、1 该指令是将Ri中的内容作为地址,再将该地址中的内容传送到累加器A中,单字节指令。 |
变址寻址 |
基址寄存器+变址寄存器间接寻址, 以16位的地址指针寄存器DPTR或16位的PC寄存器为基址寄存器, 以累加器 A 为变址寄存器, 两者中的“内容”形成一个16位的“地址”,该“地址”所指的存储单元中的内容才是操作数。 |
程序存储器ROM (@A+DPTR, @A+PC) |
MOVC A, @A+DPTR 该指令将DPTR中的内容加上A中的内容作为地址,再将该地址中的内容传送到累加器A中,单字节指令。 |
相对寻址 |
相对寻址只出现在相对转移指令中。相对转移指令执行时,是以当前的PC值加上指令中规定的偏移量rel而形成实际的转移地址。 目的地址=源地址+2(相对转移指令字节数)+rel |
程序存储器ROM(相对寻址指令的下一指令PC值加-128~+127) |
SJMP rel 汇编语言相对寻址指令中的”rel”往往是一个标号地址,表示ROM中某转移目标地址。 |
位寻址 | 指令中给出的是位地址,是片内RAM某个单元中的某一位的地址。 | 片内RAM的20H~2FH字节地址中所有的位,可位寻址的SFR |
ANL C, bit 该指令将bit(位地址)中的内容(0或1)与C中的内容进行与操作,再将结果传送到PSW中的进位标志C中。 |
参考文章:单片机的几种寻址方式、51系列单片机的寻址方式。
2、movc指令访问的是哪种空间?
解答:区分一下mov、movc、movx:
- mov:采用多种寻址方式(如直接寻址,立即寻址,寄存器间接寻址……)寻址内部数据RAM中;
- movc:采用变址寻址来寻址ROM程序存储器;
- movx:采用间接寻址来寻址外部数据存储器中,且传送数据的大小为一字节(所有的外部RAM的数据必须通过累加器读入读取)。
3、与非门的逻辑图?
解答:逻辑符号是逻辑学中用以表示逻辑形式和逻辑运算的各种人工语言符号。常用的逻辑符号如下:
4、下面计算的结果与其他三个不同的是:
#include <stdio.h> int main() { int a = 3, b = 7, c = 8, d = 9; printf("%d\n", (b + c)*d / a); printf("%d\n", (b / a + c / a)*d); printf("%d\n", (b + c) / a*d); printf("%d\n", b*d / a + c*d / a); return 0; }
解答:这四句的输出分别为45、36、45、45。显而易见,与其他三个不同的是第二个。
5、已知某个函数fun,请定义指向这个函数的函数指针p。
int fun(int a, int b){ ... }
解答:按照函数指针的定义直接写就是了:
int (*p)(int, int);
6、请简述一下vloatile的作用、使用情景和缺点。
解答:一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
- 存储器映射的硬件寄存器通常也要加voliate,因为每次对它的读写都可能有不同意义;
- 在中断函数中的交互变量,一定要加上volatile关键字修饰,这样每次读取非自动存储类型的值(全局变量,静态变量)都是在其内存地址中读取的,确保是我们想要的数据;
- 多任务环境下各任务间共享的标志应该加volatile。
缺点:频繁更改、改变或写入volatile字段 有可能导致性能问题(读取volatile字段时没有性能问题的)。
7、找出unsigned int类型的数bit 1出现的次数(2种方式)。
解答:可以采用移位的方式,与0x01依次求与操作,比较最后一位是否为bit 1:
#include <stdio.h> int main() { int num, count = 0; scanf_s("%d", &num); while (num) { if(num&0x01==1) count++; num >>= 1; } printf("%d\n", count); return 0; }
或者使用num &= (num-1)的方式,消去最后一个bit 1:
#include <stdio.h> int main() { int num, count = 0; scanf_s("%d", &num); while (num) { count++; num &= (num - 1); } printf("%d\n", count); return 0; }
8、一个整型数组里除了一个数字只出现过一次之外,其他的数字都出现了两次。请写程序找出这个只出现一次的数字。
解答:这道题没有什么难度,可以采用计数排序的方式直接计数,当然如果没有什么太高的算法要求等,可以直接两个循环解决,只要多判断一步即可:
#include <stdio.h> int main() { int a[1000]; int n, i, j; bool flag = false; scanf_s("%d", &n); for (int i = 0; i < n; i++) scanf_s("%d", &a[i]); for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { if (a[i] == a[j] && i != j) flag = true; } if (flag) flag = false; else break; } printf("%d\n", a[i]); return 0; }
9、从团结、价值、结果导向等方面介绍一下自己。
解答:这道题就没什么可说的了,介绍一下自己,从各个方面(最好结合例子)讲述自己的优势等等。