最近面了几场试,整理一下面试问题。面试问题中有一些能在网上比较方便的找到答案,我就不写如何回答了,对于一些非固定答案的问题,我会写一写我的理解,供大家参考。
1. μC/OS中的Timer是如何实现的?
2. 讲一下μC/OS中的消息队列和邮箱的原理和实现方法。
3. 互斥量与信号量有什么区别?
互斥量只能被申请一次,也就是只能有一个任务申请,但信号量可以定义一个初始值,比如定义为2,那么就允许有2个任务同时去申请,更加灵活。
5. 移植μC/OS最主要or最难的工作是什么?
对于单核RTOS的移植,最主要的工作就是任务的切换,如何保存和恢复上下文环境,这部分通常是用汇编来写的,不同架构的单片机差别也很大。还可以着重说一下如何触发任务切换,通常是用中断或Trap等机制。对于多核RTOS,可以再说一下任务调度算法方面的问题,都有哪些调度算法,各自的效率如何,有什么优缺点,最后为什么选择某一种,具体的可以看我之前写的移植μC/OS的第四篇文章,说那两种算法就可以了。
6. μC/OS会在哪些时刻发生任务切换?
这个在μC/OS的手册中可以找到更为详细的内容,简单来说有以下几个时间点:系统时钟中断、退出其它中断、等待信号量、发送信号量、调用系统延时函数等时间点。这些时刻系统会查询就绪任务优先级表,判断是否有任务更高的优先级就绪,如有则触发任务切换。
7. 多核RTOS和单核RTOS对于临界段代码的处理有什么不同?
对于临界段代码,单核RTOS只需要关闭中断,即可保证这段代码的执行不被打断或打扰,但多核RTOS只关闭中断的话并不能阻止其他核访问该段代码,所以要采用互斥量的方法进行限制。在μC/OS中,具体的操作就是,在OS_CRITICAL_ENTER宏定义中添加申请互斥量的操作,OS_CRITICAL_EXTI宏定义中添加释放互斥量的操作。
8. 多核RTOS的系统时钟怎么实现的?
我移植的时候,就只在主核中设置了系统时钟,OS_Tick任务只在主核上运行。触发系统时钟中断的时候,主核会判断所有核的就绪任务队列,任一核有更高优先级的任务就绪,就会通过核间中断的方式触发其它核的任务切换中断,最后再判断主核是否要执行任务切换。这种方法的好处是副核不用设置系统时钟,负载率低,且不用担心多核之间时钟同步的问题,缺点是主核负载较高,在核数少的单片机上更适用,在任务分配的时候也可以考虑任务特性来决定分配到主核还是副核。
面试官在这里分享了AUTOSAR OS的系统时钟实现,它是在每个核上都有一个系统时钟,各个核之间比较平等,没有明显的主核副核之分。大部分情况下多核之间是不需要同步时钟的,只有执行一些对时间很敏感的任务时,通过核间中断同步一下时钟就好。
1. BootLoader的流程?用到了哪些服务?
需要的同学可以参考我之前关于UDS BootLoader的文章。
2. 单片机在进入Main函数之前执行了哪些操作?
3. 在更新程序的时候,如果要求程序刷写失败,可以回滚到原来的应用程序,应该如何设计?
可以把存储应用程序的Flash区域划分成两块,有效的应用程序存在其中的一块,当更新应用程序的时候,先把新的应用程序写入另一块区域,等写完且校验成功后,将这一块的应用程序置为有效,下次更新应用程序的时候写入另外一块,这样就可以保证MCU中总是存在有效的应用程序,即使更新失败,也可以使用原来的区域的程序。
4. BootLoader开发的时候,中断向量怎么处理?如果没有硬件中断向量基地址寄存器,该如何实现中断重映射?
BootLoader程序和应用程序要分别拥有一套自己的中断向量表,BootLoader占用默认中断向量表,跳转到应用程序前,需要对向量表进行重映射,对于有硬件向量基地址寄存器的单片机,可以直接改基址寄存器的值,而对于没有基址寄存器的单片机,就需要用软件跳转,也就是在中断服务程序中判断当前程序是BootLoader还是应用程序,从而跳转到不同的中断处理函数。
5. BootLoader开发中最重要的点是什么?
第一个就是Flash操作,毕竟BootLoader的功能就是把旧的应用程序擦除,再写入新的应用程序;第二个是BootLoader和应用程序之间的通信和跳转,以及中断向量表的重映射;第三个是数据校验和加密,包括应用程序数据传输的加密,和程序传输正确性和有效性的校验等。
6. 大概说一下诊断服务都有哪些?
7. 写诊断程序的时候有没有分层?
大概分为底层CAN驱动层、网络层和应用层,分别对应的ISO 11898/15765/14229。
1. Tricore的CSA节点有多少字节?怎样来确定CSA链表节点的个数?
CSA节点有16个DWORD,即16*4=64字节。确定CSA节点个数需要考虑系统中的任务个数,以及任务中的函数嵌套层级数,任务数和函数嵌套层级数越多,需要的CSA节点个数越多。
2. CSA节点中的栈指针和μC/OS中的栈顶指针什么关系?
指向同一位置,具体怎么使用的可以参考我之前移植μC/OS的文章。
3. Tricore中的Trap都包含哪些?有什么作用?
参考Tricore架构用户手册,作用简单来说就是芯片发生异常的时候可以直接进入Trap,并记录触发Trap的CORE ID、程序位置等,我们可以结合Trap类型和子类进行程序错误分析。
4. Tricore上多核RTOS核间通信怎么实现的?怎么保证多核对外设的访问不冲突?
我只说了一种,就是共享内存,其实就是定义一个全局数组,需要注意的就是数据的一致性,用互斥量来保证每次只有一个核来访问这块区域,即访问内存之前申请互斥量,如果申请失败则等待一小段时间再申请,访问完毕后释放互斥量。对重要的外设可以采用同样的方法保证每次只有一个核访问外设。
5. Tricore的互斥量怎么实现的?为什么用一条汇编语言来实现而不是一个uint8?
在Tricore的iLLD库中有互斥量的函数接口,是使用汇编指令 cmpswapw 实现的。用uint8其实读取或写入的时候也会生成几条汇编指令,并不能实现原子操作。
1. 有一个SOC核一个MCU核,两个之间要传输CAN数据,如何进行通讯?
可以使用SPI,因为SPI是全双工,且速率较高,能满足传输速度的要求。
2. 追问1:上一题用SPI的话哪个核做master比较好?
用MCU做master比较好,因为SOC通常用于一些复杂运算,如图像处理声音处理等,MCU更适合做底层控制、通信相关的工作,各种外设比较齐全,且通常MCU上运行的RTOS拥有更好的实时性。
3. 追问2:如果slave想向master发送数据,应该如何实现?
方法一:master采用轮询的方式,定时向slave询问是否有数据要发送。
方法二:占用一个IO口,当slave想要发送数据的时候,通过该IO口向master发送一个请求信号,触发master开始传输数据。
4. SPI的四种工作模式分别是什么?
5. 如何解析CAN信号的?
我现在都是手动解析的,有没有什么更好的、可配置的方法,我现在还不太清除。
6. 如果CAN邮箱只有10个,但想要接收100个不同id的报文,且不能丢帧,应该如何做?对中断服务程序有什么要求?
可以在RAM中分配一块存储区,做一个FIFO,收到报文后直接从邮箱中取出存入FIFO,等待程序处理。这样就可以不受单片机邮箱数量的限制。当需要接收的报文比较多的时候中断服务程序就要尽可能快的执行完毕,代码量越少越好,以防丢帧。
7. 在调试程序的时候,如果遇到了crash或reset,通常有哪些原因?如何来调试?
导致crash的原因通常有:堆栈溢出、数组访问越界、指针目标对象不可用(未初始化、指向空值等)、参数错误、变量未初始化就使用等。
导致reset的原因通常有:程序跑飞导致看门狗复位、电压波动幅度过大导致供电不稳定、外部硬件复位信号被误触发等。
调试方法通常有:OCDS、通过CAN/UART等输出系统运行信息并定位crash位置等。
8. CAN和CAN FD的区别?
最主要的区别就是数据场通讯速率和长度都提高了,从而大大提升数据运载能力。
9. CAN和CAN FD节点可以共存于一个网络中吗?
这个要看具体节点的性能,通常来说CAN FD是兼容CAN的。
10. 车载以太网和传统以太网主要区别?
车上节点比较少、对实时性要求比较高、对以太网芯片功耗比较敏感。
11. 测试中遇到的最多的问题是什么?能否举一个详细的例子?
12. 有没有过使用外部Flash的经验?
13. DMA的工作原理?如何使用?
14. 函数指针怎么定义和使用?
15. ROS的节点之间如何进行通信?
1.输入:I love reading books 输出:books reading love I。
2. 实现一个ASCII到int的类型转换函数atoi()(要求负数也可以转换)。
3. 手写一个队列,用数组实现。
编程题都要注意一下,如果函数的输入参数是指针,要判断一下指针是否为空,防止程序挂掉,能略微体现一下编程习惯。再有就是涉及到算法的题目可以先问问面试官更关注时间复杂度还是空间复杂度。
4. 以下程序会输出什么?
这个题主要考的是数组指针,最终结果应该是:2 5,原因是*(a+1) = a[1] = 2,但(&a+1)是先取a的地址,这个时候&a是一个数组指针,&a+1是数组指针向后移动一个数组的位置,即地址增加(sizeof(int)5),所以ptr是指向整个数组的后一个地址处,此时(ptr-1)是向前移动一个int,正好指向数组的最后一个元素。
5. 以下程序会输出什么?
这个题考察后置++是先用再把值加一,而不是先加一再用。所以,第七行先执行*,指了一下地址p,没有实际意义,然后把p加一,指向a的后一个地址;第八行是先取指针p指向的值赋给b,再把p加一,所以最后b的值为a后一个地址位置的值,不能确定。但如果是单核操作系统,是指向main函数堆栈的下一个位置,所以通常是0.