当前位置: 首页 > news >正文

嵌入式企业面经实战合集(持续更新)

面经实战汇总

对于项目的拷打,会问项目中的一些外设,如:项目的adc位数,通信速率是多少,等等进行展开。

CVTE_1

1、sizeof 是对什么进行操作的?

sizeof是关键字/运算符,对变量进行操作,用于计算变量占用的字节大小。


2、u8 a【100】,这个数组的大小,u16 b【100】,这个数组的字节数

3、一个指针p指向数组b,sizeof(p)的大小

p的大小就是指针类型的大小。指针的大小和CPU位数有关。

指针变量的本质是 “存储内存地址”,而地址的长度由 CPU 位数决定。

关键注意点

  1. 与数组无关:哪怕数组bint b[100](400 字节)或char b[1](1 字节),p作为指针只存数组首元素的地址,sizeof(p)仍由架构决定,与数组长度 / 类型无关;
  2. 与指针类型无关:无论是int *pchar *p还是float *p,只要在同一架构下,所有指针变量的大小都相同(因为地址长度一致)。


4、结构体和联合体(共用体)的区别,改变他们成员的值,其他成员会不会改变

联合体里的所有变量使用的都是同一块内存,一般是共用体中数据成员占用内存最多的变量内存。

共用体在使用的时候只有一个变量有效。初始化变量时只需要初始化一个。

联合体改变一个成员的值,其他成员会改变。结构体的话则不会。


5、static修饰的全局变量的作用域,如果想在其他文件访问static修饰的全局变量,应该使用什么方式?

方法一:include 目标static函数的.c文件

方法二:通过函数包住static函数,另一文件调这个函数

方法三:通过static的地址调用(利用函数指针)

6、GPIO的八种工作模式,推挽和开漏输出有什么区别他们输出的值是多少(0和1还是电平)

输出分为推挽输出,开漏输出,复用推挽输出,复用开漏输出。复用功能:GPIO引脚用于外设功能,如UART,SPI,PWM等。

输入分为上拉输入,下拉输入,浮空输入,模拟输入。


7、中断的执行流程

保护现场;跳转到中断服务程序,执行中断服务程序;恢复现场,将栈顶的值回送给PC;跳转到到被中断的位置开始执行下一条指令。
8、串口通讯,如何保证串口发送的10个字节全部发出和收到

利用数据包发送,定义好数据包的格式,帧头,帧尾等。接收端可以采用串口DMA+空闲中断+环形缓冲区的方式接收数据。


9、main函数和中断函数都要对一个变量修改,该怎么办

这个其实是考察互斥访问。对于这种可能会被不同对象同时修改的变量,都需要加一个关键字volatile。

如果是裸机环境,就使用”关中断保护“。

RTOS 环境下,使用”信号量 / 互斥锁“。


10、手撕代码,交换两个变量的值,不使用第三个临时变量

//假设a = 2, b = 3。现在要交互a和b的值
int a = 2;
int b = 3;
a = a + b;
b = a - b;//b = a + b - b ,所以b = a 
a = a - b;//a = a + b - a ,所以a = b

11、static修饰的变量存储在哪里?

static修饰过的局部变量存储在静态存储区。(和全局变量位置相同)


 1). 一个参数既可以是const还可以是volatile吗?解释为什么。 

是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。 

  2). 一个指针可以是volatile 吗?解释为什么。 

 是的。尽管这并不很常见。一个例子是当一个中服务子程序修改一个指向一个buffer的指针时。 

12、RAM和ROM的区别

ROM数据断电不丢失。只能进行读取,用于存储程序代码、常量数据。

RAM数据断电丢失。存储运行时临时数据(如变量、堆栈、动态内存)。

单片机中的ROM一般是Flash,也叫闪存。Flash英文也有闪的意思,因此叫闪存。ROM存储用户写好编译好的程序,

单片机中RAM一般是SRAMRAM是用来存放数据的。

ROM和RAM的协同工作方式是:ROM存储程序,运行时CPU从ROM中读取一条一条的指令执行,指令运行过程中产生的临时数据放在RAM中。


拓竹科技

结构体中数据类型对齐方式

结构体数据对齐是为了提高 CPU 访问内存的效率。默认情况下,编译器会按照成员中最大基本数据类型的大小来对齐结构体。例如,若结构体中有int(4 字节)、char(1 字节)成员,结构体的对齐方式通常为 4 字节对齐。

堆栈区别

:由编译器自动分配和释放,用于存储函数的局部变量、函数参数、返回地址等。空间是连续的,大小通常固定(由系统或编译器决定),生长方向是从高地址到低地址。

:由程序员手动分配(如mallocnew)和释放(如freedelete),用于动态内存分配。空间不连续,大小相对灵活,生长方向是从低地址到高地址。

指针传参与结构体传参的区别

指针传参:传递的是结构体变量的地址,在函数内部通过指针间接操作结构体数据。优点是不需要拷贝结构体的所有数据,节省内存和时间,尤其是结构体较大时效率高;缺点是如果操作不当,可能会修改原结构体的数据。

结构体传参:传递的是结构体变量的拷贝,函数内部对结构体的修改不会影响原结构体。优点是安全,不会意外修改原数据;缺点是当结构体较大时,拷贝操作会消耗较多的内存和时间,效率较低。

单片机启动流程

以常见的 ARM Cortex - M 系列单片机为例,启动流程大致如下:

  1. 硬件复位:当单片机的复位引脚被触发或系统上电时,进入复位状态。
  2. 获取栈顶指针:从程序存储器的起始地址(向量表的第一个位置)读取栈顶指针的值,初始化堆栈。
  3. 跳转到复位处理函数:从向量表的第二个位置读取复位处理函数的入口地址,跳转到该函数执行。在复位处理函数中,通常会进行系统时钟初始化、外设初始化等操作,然后调用main函数,进入应用程序执行阶段。

中断处理过程

中断处理过程主要包括:

  1. 中断请求:外设或其他中断源产生中断请求,向 CPU 发送中断信号。
  2. 中断响应:CPU 在满足响应条件(如中断使能、当前指令执行完成等)时,保存当前程序的上下文(如 PC、寄存器等),并根据中断向量表找到对应的中断服务函数(ISR)入口地址。
  3. 执行中断服务函数:CPU 跳转到中断服务函数,执行相应的中断处理任务,如数据读取、状态清除等。
  4. 中断返回:中断服务函数执行完成后,恢复之前保存的上下文,返回被中断的程序继续执行。

中断优先级

中断优先级用于决定当多个中断同时发生时,CPU 先响应哪个中断。通常分为抢占优先级和子优先级(或响应优先级)。抢占优先级高的中断可以打断正在执行的低抢占优先级的中断服务函数;当抢占优先级相同时,子优先级高的中断先被响应。在单片机中,可通过配置中断优先级寄存器来设置每个中断的优先级。

mcu 运行过程中,全局变量如何读入

在 MCU 运行过程中,全局变量存储在指定的内存区域(如 RAM 中)。当程序需要访问全局变量时,CPU 通过地址总线找到该全局变量在内存中的地址,然后通过数据总线读取该地址中的数据。由于全局变量的作用域是整个程序,所以在程序的任何地方,只要知道其地址(由编译器分配确定),就可以对其进行读取操作。

DMA,(关键字 volatile,信号量互斥量)

  • DMA:直接内存访问,允许外设与内存之间直接进行数据传输,而无需 CPU 参与。这样可以减轻 CPU 的负担,提高数据传输效率,常用于高速数据传输场景,如大规模数据的串口收发、ADC 数据采集等。
  • volatile:是 C 语言中的一个关键字,用于告诉编译器,被修饰的变量可能会被意外地修改(如被硬件、中断服务函数等修改),因此编译器在优化时,不会对该变量的读取和写入进行过度优化,每次都从内存中实际读取或写入该变量的值。
  • 信号量:是一种用于实现进程或任务间同步与互斥的机制。分为二进制信号量(值为 0 或 1,用于互斥或简单同步)和计数信号量(值为非负整数,用于资源计数等场景)。任务可以通过获取信号量来请求资源,通过释放信号量来归还资源。
  • 互斥量:是一种特殊的二进制信号量,用于实现临界区的互斥访问,确保在同一时间只有一个任务可以访问共享资源,防止数据竞争问题。与信号量相比,互斥量还具有所有权和递归访问等特性。

freertos 启动流程

FreeRTOS 的启动流程大致如下:

  1. 系统初始化:调用vTaskStartScheduler()函数,该函数会进行一系列初始化操作,如创建空闲任务(Idle Task),用于在没有其他任务运行时执行;如果配置了定时器任务(Timer Task),也会创建定时器任务。
  2. 启动调度器:初始化任务调度相关的变量和数据结构,设置系统滴答定时器(SysTick),用于产生任务调度的时钟节拍。然后,将 CPU 的控制权交给调度器,调度器根据任务的优先级等信息,选择一个最高优先级的就绪任务开始执行。

项目中如何使用 freertos,任务调度 / 内存管理

  • 任务调度:在 FreeRTOS 中,任务具有不同的优先级,调度器采用基于优先级的抢占式调度算法。创建任务时,为每个任务分配优先级,高优先级的任务可以抢占低优先级任务的 CPU 使用权。例如,在一个多传感器数据采集项目中,可创建多个任务,如传感器数据采集任务(高优先级,确保及时采集数据)、数据处理任务(中优先级)、数据上传任务(低优先级)等。调度器会根据任务的优先级和状态(就绪、运行、阻塞等),决定哪个任务获得 CPU 时间。
  • 内存管理:FreeRTOS 提供了多种内存管理方案。可以使用 FreeRTOS 自带的内存管理函数,如pvPortMalloc()vPortFree(),这些函数基于内存池或堆的方式进行内存分配和释放。在项目中,根据内存使用的需求,选择合适的内存管理策略,确保任务在动态创建、删除或申请内存时,内存的合理分配和释放,避免内存泄漏等问题。例如,在创建任务时,需要为任务的栈空间分配内存;在使用队列、信号量等组件时,也需要分配相应的内存资源。

介绍简历中项目,如何处理读取多传感器数据

在我参与的 [项目名称] 中,需要读取多个传感器(如温度传感器、湿度传感器、加速度传感器等)的数据。处理方式如下:

  1. 任务划分:使用 FreeRTOS 创建多个传感器数据采集任务,每个任务负责一种或几种传感器的数据读取,根据传感器的重要性和数据更新频率设置不同的任务优先级。
  2. 数据缓存:为每个传感器创建对应的队列(FreeRTOS 的队列组件),采集任务将读取到的传感器数据放入相应队列,供后续的数据处理任务读取。
  3. 同步机制:利用信号量或互斥量来保证对传感器硬件访问的互斥性,防止多个任务同时访问同一传感器硬件导致的冲突;同时,通过定时器或传感器的中断信号来触发数据采集任务的执行,确保数据采集的及时性。

避免卡死,卡死时如何处理,对性能如何分析调整,多任务调度如何实现

  • 避免卡死
    • 确保任务间的同步与互斥机制正确,如使用互斥量保护共享资源,避免死锁(如避免嵌套持有互斥量、确保互斥量的获取和释放顺序一致)。
    • 合理设置任务的阻塞时间,避免任务长时间占用 CPU 而不释放,影响其他任务执行。
    • 对硬件操作(如传感器读取、外设通信)设置超时机制,防止因硬件故障导致任务一直等待。
  • 卡死时的处理
    • 可使用 FreeRTOS 的任务监控功能,如创建一个看门狗任务,定期检查其他任务的运行状态。如果发现某个任务长时间没有正常运行(如超过设定的最大执行时间),则采取相应措施,如重启该任务、记录故障日志或复位系统。
    • 利用调试工具(如 J - Link 配合调试软件),在系统卡死时,查看各任务的状态、堆栈使用情况以及寄存器的值,分析卡死的原因。
  • 性能分析调整
    • 使用 FreeRTOS 提供的钩子函数(Hook Functions)或性能分析工具,统计各任务的执行时间、CPU 利用率、堆栈使用情况等。
    • 根据性能统计结果,调整任务的优先级、栈大小、阻塞时间等参数。例如,如果某个任务执行时间过长,导致其他高优先级任务无法及时执行,可优化该任务的代码逻辑,减少执行时间;如果堆栈溢出,可适当增大栈的大小。
  • 多任务调度的实现:FreeRTOS 的多任务调度是基于任务控制块(TCB)和调度器实现的。每个任务都有一个 TCB,其中包含任务的状态、优先级、堆栈指针等信息。调度器通过遍历所有任务的 TCB,根据任务的优先级和状态,选择优先级最高的就绪任务,将 CPU 的上下文切换到该任务,使其开始执行。系统滴答定时器定期产生中断,触发调度器进行任务调度,确保任务的公平性和实时性。


影石嵌入式驱动

问题 1:C 语言的编译过程原理

C 语言编译过程主要分为四个阶段:

  • 预处理:处理以#开头的预处理指令,如#include(将头文件内容插入)、#define(宏替换)、#ifdef等条件编译指令,生成.i文件。

  • 编译:把预处理后的文件翻译成汇编语言,进行语法、语义检查,生成.s文件。

  • 汇编:将汇编代码转换为机器码,生成目标文件(.o文件)。

  • 链接:把多个目标文件和库文件组合起来,解决符号引用等问题,生成可执行文件。

问题 2:void*类型指针作用是什么,对比char*int*等有什么优势

void*是无类型指针,可指向任意类型数据,常作为通用接口的参数或返回值,用于在不知道具体数据类型时传递指针。优势在于通用性,能适配不同类型指针的传递,无需提前确定类型;而char*主要针对字符型,int*针对整型,类型固定,灵活性不如void*

问题 3:大端模式和小端模式,内存对齐(charintchar的结构体 32 位系统中占用多少个字节)

  • 大小端模式:大端是高字节存低地址,小端是低字节存低地址。

  • 内存对齐:32 位系统中,char占 1 字节,int占 4 字节。结构体中,第一个char占 1 字节,int要对齐到 4 字节地址,所以中间空 3 字节,第二个char占 1 字节,又空 3 字节(为了整体对齐到 4 字节),总共占用 1+3+4+1+3=12 字节。

问题 4:SPI 的四种模式,SPI 如何和从机通信(通信协议帧)、如何实现一主多从(多条 SS 片选线)

  • 四种模式:由时钟极性(CPOL)和时钟相位(CPHA)组合,CPOL 为 0 表示空闲时钟低,为 1 则高;CPHA 为 0 表示数据在时钟第一个沿采样,为 1 则第二个沿。四种组合对应四种模式。

  • 与从机通信:主设备通过 SS 选通从机,然后在 SCK 时钟下,主从通过 MOSI(主发从收)、MISO(从发主收)传输数据,数据帧一般是 8 位或 16 位等,按时钟节拍传输。

  • 一主多从:给每个从机分配独立的 SS 片选线,主设备通过控制不同 SS 线的电平(拉低选中,拉高取消选中),实现和不同从机通信。

问题 5:I2C 通信协议数据帧(指定地址写、当前地址读)、硬件如何连接

  • 指定地址写:起始信号后,发送从机地址(含写标志),然后发送要写入的寄存器地址,再发送数据,最后停止信号。

  • 当前地址读:起始信号后,发送从机地址(含读标志),从机返回数据,主设备发送应答(或非应答),最后停止信号。

  • 硬件连接:I2C 有 SDA(数据线)和 SCL(时钟线),两个线都要接上拉电阻(一般 1 - 10kΩ)到电源,主从设备都连接到这两根线上。

问题 6:I2C 通信挂死如何进行排查(软件、硬件两方面)

  • 软件方面检查 I2C 初始化是否正确,如时钟地址配置;通信时序是否符合协议,比如起始、停止信号,应答是否正确;是否有死循环等待应答等情况

  • 硬件方面:检查 SDA、SCL 线路是否短路、断路;上拉电阻是否合适,有无虚焊;从机设备是否正常供电,是否存在硬件故障导致总线被占用。

问题 7:I2C 上拉电阻选择?如果通信过程出现低电平 0.4V 高电平 2.6V 等中间电平原因是什么?

  • 上拉电阻选择:通常根据总线电容、通信速率选择,一般在 1 - 10kΩ 之间,速率高、电容大时选小电阻,反之选大电阻,保证信号能快速上升到高电平。

  • 中间电平原因:可能是上拉电阻阻值过大,导致总线在高电平时拉不上去;或者总线存在较大负载、短路情况,使电平处于中间状态,不满足 I2C 高 / 低电平的要求(一般高电平接近电源电压,低电平接近地)。

问题 8:I2C 多主多从模式介绍,多主多从如何总线仲裁

  • 多主多从模式:多个主设备可同时尝试控制总线,多个从设备挂在总线上,主设备通过地址访问从设备。

  • 总线仲裁:当多个主设备同时发送数据时,逐位比较发送的位,若某主设备发送的位为高,而其他主设备发送的为低,则发送高电平的主设备失去仲裁权,停止发送,由发送低电平且后续能保持优势的主设备获得总线控制权。总结下就是多个主设备发送数据时,谁先发低电平,谁就优先。

问题 9:单链表和数组的区别,单链表如何删除和增加节点?对应时间复杂度是?对比数组,链表在内存中排布规律?

  • 区别:数组是连续内存空间,元素类型相同,随机访问快;单链表是通过指针连接的非连续节点,插入、删除方便随机访问慢

  • 单链表增删节点:增加节点,找到插入位置,修改前后节点指针;删除节点,找到待删节点,修改前一节点指针指向后一节点。

  • 时间复杂度:单链表增删(已知位置)是 O(1)(主要是指针操作),查找插入 / 删除位置是 O(n);数组增删(中间位置)是 O(n)(需移动元素)。

  • 内存排布:数组是连续的内存块;单链表节点在内存中是分散的,通过指针链接。

问题 10:UART 通信数据帧、奇偶校验、硬件流控、异步 or 同步?

  • 数据帧:起始位(低电平)、数据位(5 - 9 位,通常 8 位)、奇偶校验位(可选)、停止位(1 - 2 位,通常 1 位)。

  • 奇偶校验:用于检错,奇校验要求数据位和校验位总 1 的个数为奇数,偶校验为偶数。

  • 硬件流控:常用 RTS(请求发送)和 CTS(清除发送),主设备通过 RTS 请求发送,从设备用 CTS 响应,控制数据传输节奏。

  • 异步同步:UART 是异步通信,无需统一时钟,靠起始位同步每帧数据。

问题 11:UART 误码率是什么,出现通信错误的原因排查(软件、硬件两方面)

  • 误码率:接收错误的比特数与总接收比特数的比值。

  • 软件方面:波特率设置是否与发送方一致;数据位、停止位、奇偶校验配置是否匹配;中断处理或轮询读取是否及时,有无数据溢出。

  • 硬件方面:通信线路是否有干扰(如电磁干扰);电平转换电路(如 RS232 转换)是否正常;收发设备的驱动能力是否足够,线路是否过长导致信号衰减。

问题 12:UART 对应的三种电平协议(TTL、RS232、RS485),以及他们的特点、分别适用于哪些应用场景?

  • TTL 电平:高电平为 5V(或 3.3V 等),低电平为 0V,传输距离短(几米内),抗干扰弱,适用于板内或近距离设备间通信。

  • RS232 电平:采用负逻辑,-15V ~ -3V 为高电平,+3V ~ +15V 为低电平,传输距离稍远(几十米),抗干扰比 TTL 强,适用于计算机与外设(如串口设备)间短距离通信。

  • RS485 电平:差分传输,抗干扰能力强,传输距离远(几百米到上千米),支持多节点(32 个以上),适用于工业现场、长距离多设备通信场景,如楼宇自控、工业控制总线。

问题 13:CAN 通信一主多从的总线仲裁(线与逻辑)

CAN 总线采用非破坏性总线仲裁,基于 “线与” 逻辑(即多个节点同时发送时,电平为各节点发送电平的 “与” 结果)。每个节点发送的是显性(低电平)或隐性(高电平)位,显性位优先级高于隐性位。仲裁时,各节点逐位比较发送的位,若某节点发送隐性位,而其他节点发送显性位,则该节点退出仲裁,最终只有发送最高优先级(显性位多或 ID 值小)的节点获得总线控制权,继续发送数据。

问题 14:volatile关键字、RTOS 中volatile应用场景

  • volatile关键字:告诉编译器,变量的值可能会被意外修改(如被硬件、中断服务程序等修改),因此每次使用该变量时都要从内存中读取,而不是使用寄存器中的缓存值。

  • RTOS 中应用场景:用于多任务共享的全局变量,防止任务切换时编译器优化导致变量值错误;也用于中断服务程序和任务之间共享的变量,确保中断修改变量后,任务能获取到最新值。比如任务中判断一个由中断修改的标志位,该标志位就需要用volatile修饰。

  • 总结一下其实就是要被频繁修改的共享全局变量。

经纬恒润

问题 1:RTOS 的调度机制

RTOS(实时操作系统)常见的调度机制有抢占式调度、合作式调度、时间片轮转

  • 抢占式调度:优先级高的任务可以在任何时候抢占优先级低的任务的 CPU 使用权。比如,当一个高优先级任务就绪时,不管低优先级任务是否在运行,都会立即切换到高优先级任务执行。

  • 合作式调度:任务需要主动放弃 CPU 使用权,才能让其他任务运行。只有当当前任务主动释放 CPU 时,调度器才会选择下一个任务。这种方式对任务的编写要求较高,需要任务合理安排放弃 CPU 的时机。

  • 部分 RTOS 也采用时间片轮转调度,同优先级任务轮流执行固定时间片。

问题 2:stm32 启动流程

STM32 的启动流程大致如下:

  • 首先,系统复位后,CPU 从复位向量(通常是 Flash 的起始地址)获取栈顶指针(SP)的值,加载到堆栈指针寄存器中。

  • 然后,从复位向量的下一个地址获取复位处理程序(Reset_Handler)的入口地址,跳转到该程序执行。

  • 在 Reset_Handler 中,会进行一些初始化操作,比如初始化系统时钟、配置外设等,之后再调用main函数,进入应用程序的主逻辑。

问题 3:CAN 的负载率计算

CAN 的负载率计算公式为:计算公式为:负载率 =(总数据帧占用总线时间 / 统计时间)× 100%。。总数据帧占用的总线时间包括帧起始、仲裁场、控制场、数据场、CRC 场、应答场和帧结束等部分的时间总和。通过统计在一定观测时间内,CAN 总线用于传输数据帧的时间占比,就能得到负载率,它反映了 CAN 总线的繁忙程度。

问题 4:SPI、IIC、CAN 的地址管理区别

  • SPI:没有专门的地址管理机制,通过片选(SS)线来选择从设备,主设备要和哪个从设备通信,就拉低对应的 SS 线。

  • IIC:通过设备地址来识别从设备,主设备在通信时,先发送从设备的地址(7 位或 10 位地址),总线上的从设备根据自身地址是否匹配来决定是否响应。

  • CAN:利用 ID 来进行地址相关的管理,CAN 的 ID 不仅用于标识不同的消息类型,也可以在一定程度上起到地址的作用,不同的节点通过发送带有特定 ID 的消息,其他节点根据 ID 来判断是否需要接收和处理该消息。

问题 5:串口 115200 波特率是一秒传输几个字节数据

串口通信中,通常一帧数据包括 1 个起始位、8 个数据位、1 个停止位(无校验位情况),那么一帧数据的位数是 1+8+1=10 位。波特率是 115200bps(位每秒),所以每秒传输的字节数为 10115200​=11520 字节。

问题 6:RTOS 中带 ISR 和不带 ISR 的函数区别

  • 带 ISR(中断服务程序)的函数:运行在中断上下文,优先级通常很高,用于快速响应硬件中断事件,处理时间要尽可能短,不能进行阻塞性操作(如等待信号量、延时等),否则会影响系统的实时性。

  • 不带 ISR 的函数:运行在任务上下文,优先级由任务的优先级决定,可以执行各种操作,包括阻塞性操作,用于实现应用程序的主要逻辑。

问题 7:作为项目管理是怎么安排项目的

首先,会明确项目的目标和需求,将项目分解为多个任务模块。然后,根据任务的难度、依赖关系等,为每个任务分配合适的人员,并制定详细的时间计划,设置里程碑节点。在项目进行过程中,会定期召开例会,跟踪任务的进展情况,及时解决出现的问题,调整资源分配,确保项目按照计划推进,同时还要进行风险管理,提前识别可能出现的风险并制定应对措施。

不知名中小厂

1. volatile关键字的作用

volatile用于告诉编译器,变量值可能被意外修改(如硬件、中断等),每次使用都要从内存读取,而非用寄存器缓存值,防止编译器优化导致错误。

2. struct字节对齐(假设结构体含char a; int b; short c;

32 位系统下,char占 1 字节,int占 4 字节(需对齐到 4 字节地址,所以a后空 3 字节),short占 2 字节(对齐到 2 字节地址),总大小为 1+3+4+2=10 字节,再整体对齐到 4 字节,最终为 12 字节。字节对齐是为了提升 CPU 访问效率,遵循 “数据类型自身大小为对齐模数” 规则。

3. static局部变量存储位置

存储在静态存储区(数据段),程序运行期间一直存在,作用域仅限定义它的函数内,多次调用函数时,其值会保留上次调用后的结果。

4. 数组越界的危害

可能修改相邻内存数据,导致程序逻辑错误、崩溃,甚至被恶意利用(如缓冲区溢出攻击),破坏系统稳定性与安全性。

5. 栈泄漏的危害

栈用于存储函数局部变量、调用栈等,栈泄漏(如递归过深、局部数组过大)会导致栈空间耗尽,引发程序栈溢出,使程序崩溃。

6. 递归在单片机中使用的问题

单片机栈空间有限,递归深度过深易导致栈溢出;且递归调用有额外的栈开销(保存返回地址、寄存器等),会降低程序执行效率,实时性要求高的场景不适用。

7. GPIO 推挽和开漏输出的区别

  • 推挽输出:可输出高、低电平,驱动能力强,能直接驱动小负载(如 LED)。

  • 开漏输出:只能输出低电平或高阻态,高电平需外部上拉电阻提供,可实现线与逻辑(多个开漏输出引脚接同一上拉电阻,实现 “与” 功能)。

8. 熟悉哪些操作系统?

熟悉 FreeRTOS。它是轻量级 RTOS,支持任务调度、信号量、消息队列等,适合单片机等资源受限的嵌入式系统,能有效管理多任务,提升系统实时性。

还有RT-Thread。

9. ePWM 的设置(以实现占空比 50% 的 PWM 为例)

配置时,需设置时基模块(确定周期,如设置计数器周期值)、比较模块(设置比较值为周期值的一半,以得到 50% 占空比)、输出模块(选择输出引脚、配置输出极性等)。手动实现的话,可通过定时器定时翻转引脚电平,周期内高电平时间占比为目标占空比。

10. 上升沿下降沿触发

是数字电路中信号变化的触发方式,上升沿触发指信号从低电平跳变到高电平时触发(如中断触发、触发器翻转);下降沿触发则是信号从高电平跳变到低电平时触发。

11. 数码管扫描频率?共阴极和共阳极的区别?

  • 扫描频率:一般每秒 25 - 100 次,保证视觉暂留,无闪烁感。

  • 区别:共阴极数码管,公共端接低电平,段码高电平点亮对应段;共阳极数码管,公共端接高电平,段码低电平点亮对应段。

12. AD 采样位数代表什么?什么因素影响 ADC 精度和转换速率?有什么滤波算法?

  • 采样位数:代表 ADC 对模拟信号的量化精度,如 12 位 ADC,可将模拟信号量化为 2^12=4096 个等级。

  • 影响因素:参考电压精度、采样时钟稳定性影响精度;转换时钟频率、ADC 内部结构(如逐次逼近型、Σ - Δ 型)影响转换速率。

  • 滤波算法:有滑动平均滤波(取连续若干次采样的平均值)、中值滤波(取采样值的中值,抗脉冲干扰)等。

13. 中断如何设置?中断中任务过多有什么危害?

  • 设置:配置中断触发方式(如上升沿、下降沿、电平),使能中断源,配置中断优先级,编写中断服务程序(ISR)。

  • 危害:中断优先级高,若 ISR 中任务过多(如执行复杂运算、长时间阻塞),会占用 CPU 过多时间,影响其他低优先级中断或任务的响应,破坏系统实时性。

14. 波特率是什么?

波特率是串行通信中,每秒传输的码元数,反映数据传输的速率。在 UART 等串口通信中,波特率决定了数据位的传输节奏。

15. 串口通信起始位如何判定?I2C 总线仲裁?

  • 串口起始位判定:串口数据帧以起始位(低电平)开始,通过检测线路从高电平跳变到低电平,且持续一定时间(根据波特率计算的位时间),判定为起始位。

  • I2C 总线仲裁:多主设备同时通信时,逐位比较发送的位,若某主设备发送隐性位(高电平),而其他主设备发送显性位(低电平),则发送隐性位的主设备失去仲裁权,最终发送显性位且后续更有优势的主设备获得总线控制权。

芯海嵌软

1. 自我介绍

我是 [姓名],学嵌入式专业,参与过 [项目名称],做过硬件驱动和程序开发,想在贵司继续深耕嵌入式领域。

2. 对岗位的了解

嵌入式软件工程师要基于硬件,用代码实现功能,还要保障软件实时、稳定,得懂系统和硬件交互。

3. 过往经历中印象比较深刻的项目及主要工作

做过智能温湿度监测设备的嵌入式开发,负责传感器驱动、数据处理和基于 FreeRTOS 的任务调度,让设备能精准采集并上传数据。

4. FreeRTOS 的原理

FreeRTOS 是一个轻量级的实时操作系统内核,它支持多任务处理,通过任务调度器来管理多个任务的执行。它基于抢占式调度算法,能让优先级高的任务优先获得 CPU 资源运行。FreeRTOS 为每个任务维护独立的任务控制块(TCB),其中包含任务的状态、优先级、堆栈指针等信息。同时,它还提供了丰富的系统服务,如任务创建、删除、挂起、恢复,以及信号量、消息队列、互斥量等同步与通信机制,方便开发者进行多任务应用程序的开发。

5. FreeRTOS 任务之间切换的原理

FreeRTOS 任务切换基于上下文切换机制。当发生任务切换时,当前运行任务的上下文(包括 CPU 寄存器的值、程序计数器等)会被保存到该任务的堆栈中;然后,调度器会选择下一个要运行的任务(通常是优先级最高且处于就绪态的任务),并从该任务的堆栈中恢复其上下文,使 CPU 寄存器等恢复到该任务上次被切换出时的状态,从而让该任务继续执行,实现任务之间的切换。

6. 两个任务运行时间是否一样

在 FreeRTOS 中,两个任务运行的时间不一定一样。任务的运行时间受到多种因素影响,比如任务的优先级,优先级高的任务能抢占 CPU 资源,运行时间可能更长;任务自身的工作量,若任务一需要处理的数据或执行的操作更多,其运行时间会比任务二长;还有系统中其他任务的调度情况,若有更高优先级任务插入,也会影响当前任务的运行时长。

7. 任务通过哪个中断触发切换

在 FreeRTOS 中,任务切换通常由 SysTick 中断触发。SysTick 是一个系统定时器,会按照设定的时间间隔产生中断,FreeRTOS 的任务调度器会在 SysTick 中断服务程序中进行任务调度决策,判断是否需要进行任务切换。此外,也可能由其他外部中断触发,当外部中断发生且满足任务切换条件(如唤醒了更高优先级的任务)时,也会触发任务切换。

8. SPI 和 IIC 的从机是否能主动向主机发数据

  • SPI:从机通常不能主动向主机发送数据,SPI 通信是由主机发起的,主机通过产生时钟信号来控制数据的传输,从机在主机的时钟控制下,被动地接收主机发送的数据并向主机发送数据(在主机读取从机数据的阶段),从机无法主动发起数据传输。

  • IIC:从机可以主动向主机发送数据,当从机有数据要发送时,可以通过产生时钟信号(在主机允许的情况下,或者在特定的通信模式下,如从机模式下的中断触发等),向主机发起数据传输。

9. UART、IIC、SPI 的区别

通信协议

总线类型

通信方式

同步 / 异步

引脚数量

速率

主从模式

UART

异步串行总线

全双工,通过 TX(发送)和 RX(接收)引脚传输数据

异步,无需时钟线,依靠波特率同步

至少 2 根(TX、RX)

速率相对较低

无严格主从,点对点通信

IIC

同步串行总线

半双工,通过 SDA(数据线)和 SCL(时钟线)传输数据

同步,由时钟线 SCL 同步

2 根(SDA、SCL)

速率中等,有标准模式、快速模式等

多主多从,通过地址识别设备

SPI

同步串行总线

全双工,通过 MOSI(主机发从机收)、MISO(从机发主机收)、SCK(时钟)、CS(片选)等引脚传输数据

同步,由 SCK 时钟线同步

至少 4 根(MOSI、MISO、SCK、CS)

速率较高

一主多从,通过 CS 选择从机

10. 两个 MCU 之间通过 SPI 通讯,从机有数据要向主机发的设计

可以采用以下设计方式:首先,在 SPI 通信的基础上,增加一根 GPIO 线,当从机有数据要发送给主机时,从机通过该 GPIO 线向主机发送一个中断信号;主机接收到中断信号后,发起一次 SPI 读操作,从机在这次读操作过程中,将需要发送的数据通过 MISO 线传输给主机;另外,也可以利用 SPI 通信的特性,在主机向从机写数据的同时,从机把要发送的数据通过 MISO 线传回给主机,实现双向的数据传输,不过这需要对数据传输的协议进行详细设计,确保主机和从机对数据的收发逻辑清晰。

11. 嵌入式当中 malloc 的原理

在嵌入式系统中,malloc 函数用于动态分配内存,其原理通常是基于内存池的概念。系统会预先划分出一块连续的内存区域作为内存池,当调用 malloc 时,malloc 函数会在内存池中查找满足大小要求空闲内存块。为了管理这些内存块,通常会采用链表结构,每个内存块的头部包含该块的大小、下一个空闲块的指针等信息。找到合适的空闲块后,会将其标记为已分配,并返回该块的起始地址。当调用 free 函数释放内存时,会将对应的内存块重新标记为空闲,并尝试与相邻的空闲块合并,以减少内存碎片。不过,嵌入式系统中由于资源有限,malloc 的实现可能会根据具体系统进行简化或优化,且动态内存分配可能会带来内存碎片、分配失败等问题,所以在一些对可靠性要求极高的嵌入式场景中,会尽量避免过度使用动态内存分配。

12. 所用 BLE 形式及协议

BLE(蓝牙低功耗)通常采用中心 - 外围(Central - Peripheral)形式。在协议方面,使用的是蓝牙低功耗协议栈,基于蓝牙核心规范。它包含物理层(PHY)、链路层(LL)、主机控制器接口(HCI)、主机层(包含逻辑链路控制与适配协议 L2CAP、安全管理协议 SMP、属性协议 ATT、通用属性配置文件 GATT 等),通过这些协议层的协作,实现低功耗、短距离的无线通信,满足嵌入式设备在功耗和通信距离上的需求。

13. 为什么选择我们公司

贵公司在嵌入式领域有着出色的成绩,推出了很多具有创新性的产品,在行业内口碑很好。而且贵公司的技术研发团队实力雄厚,我了解到贵公司在 [某嵌入式技术方向,如智能硬件、物联网嵌入式解决方案等] 方面有深入的研究,这与我个人的职业发展方向高度契合。我相信在贵公司,我能接触到前沿的技术和项目,不断提升自己的专业能力,同时也能为公司的发展贡献自己的力量。

14. 未来 3 - 5 年的职业规划

在未来 3 - 5 年,我希望能从一名初级嵌入式软件工程师成长为中级乃至高级工程师。首先,在入职后的 1 - 2 年内,我会专注于熟悉公司的产品、技术栈和开发流程,不断提升自己的代码编写能力和问题解决能力,能够独立承担中小型嵌入式软件模块的开发任务。然后,在 3 - 5 年内,我希望能...

欣旺达

1. 自我介绍

面试官好,我叫 [你的名字],毕业于 [学校名称],所学专业是 [专业名称]。在校期间,我对嵌入式软件开发等领域充满兴趣,也积累了一些相关知识。同时,我有过 [实习公司名称] 的实习经历,参与了 [具体项目名称],在项目中负责 [具体工作内容],这让我对软件开发流程以及相关技术有了更深入的实践认知。我性格开朗,善于沟通协作,也具备较强的学习能力,希望能有机会加入欣旺达,在软件相关岗位上发挥自己的能力,不断成长。

2. 实习、项目介绍

我之前在 [实习公司] 参与过一个基于 STM32 的智能硬件项目。这个项目主要是为了实现对设备的精准控制和数据采集。我在项目中负责软件部分的开发,包括使用 FreeRTOS 进行任务调度的设计与实现,以及基于 STM32 的驱动开发等工作。在任务调度方面,我根据不同任务的优先级和执行周期,合理配置 FreeRTOS 的任务参数,确保各个任务能够高效、有序地运行。在 STM32 相关开发中,我也深入了解了 STM32 的启动流程等,保证程序能够正常初始化并运行。通过这个项目,我不仅提升了自己的技术能力,也锻炼了团队协作和问题解决的能力。

3. FreeRTOS 任务调度

FreeRTOS 的任务调度是基于优先级的抢占式调度。每个任务都有一个优先级,当系统中有更高优先级的任务处于就绪状态时,当前运行的低优先级任务会被暂停,高优先级任务获得 CPU 使用权开始执行。同时,FreeRTOS 也支持时间片轮转调度,当多个同优先级的任务就绪时,它们会按照时间片轮流执行,每个任务执行一段固定的时间后,就会切换到下一个同优先级任务。这样的调度机制能够很好地满足实时系统对任务响应的要求,确保高优先级任务及时得到处理。

4. STM32 启动流程

STM32 的启动流程大致如下:首先,系统复位后,会从复位向量处获取初始栈指针(MSP)的值,然后获取复位处理程序(Reset_Handler)的入口地址。接着,程序跳转到 Reset_Handler 执行,在 Reset_Handler 中,会进行一些初始化操作,比如初始化系统时钟、配置相关外设的时钟使能等。之后,会调用 C 库的初始化函数(如__main),完成全局变量的初始化(包括.data 段的初始化和.bss 段的清零)。最后,跳转到 main 函数,开始执行用户的应用程序。整个启动流程确保了 STM32 从硬件复位到软件运行的顺利过渡。

5. IIC 和 SPI、数组链表区别

  • IIC 和 SPI

    • IIC:是一种两线式(SDA 和 SCL)的串行总线,支持多主多从模式,通信时需要进行地址寻址,速率相对较低,一般在几 Mbps 以内。它的接线简单,只需要两根线,适合设备较多、对通信速率要求不是特别高的场景,比如一些传感器与主控芯片的通信。

    • SPI:是一种四线式(MOSI、MISO、SCK、CS)的串行总线,通常是主从模式,通信速率较高,可达到几十 Mbps 甚至更高。它的通信过程相对简单直接,没有地址寻址的过程,通过片选信号(CS)来选择从设备,适合对通信速率要求较高的场景,像高速数据传输的存储设备等。

  • 数组和链表

    • 数组:是在内存中连续存储的相同类型元素的集合,它的优点是可以通过下标快速访问元素,时间复杂度为 O (1);但缺点是插入和删除元素时,需要移动大量的元素,效率较低时间复杂度为 O (n),而且数组的大小在初始化时一般是固定的,不容易动态扩展。

    • 链表:是通过指针将分散在内存中的节点连接起来的一种数据结构,节点包含数据和指向下一个节点(或上一个节点,双向链表)的指针。它的优点是插入和删除元素很方便,只需要修改指针即可,时间复杂度为 O (1);但缺点是访问元素时需要从表头开始遍历,时间复杂度为 O (n),而且每个节点需要额外的空间存储指针,内存开销较大。

6. 对公司了解吗

我了解到欣旺达是在电池领域以及相关电子设备领域很有影响力的企业,在软件方面也有不少的业务方向,比如嵌入式软件开发、智能硬件软件设计等,这些方向都与我所学的专业以及我的兴趣和职业规划相契合,我也很期待能在这些方向上贡献自己的力量,与公司共同发展。

7. 更倾向于哪个方向

我更倾向于嵌入式软件开发方向。因为我在学校学习以及实习过程中,在这方面积累了较多的知识和实践经验,比如对 FreeRTOS、STM32 等相关技术的运用,而且我对通过软件来控制硬件设备,实现各种智能功能很感兴趣,所以希望能在这个方向上深入发展。

鼎桥嵌入式

  1. 自我介绍:面试官好,我叫 [姓名],来自 [学校],所学专业是 [专业]。在校期间,我专注于嵌入式系统方向的学习,成绩优异,还积极参与相关项目实践。曾在 [公司 / 实验室] 参与过 [项目名称],负责 [具体工作],锻炼了我的专业技能和解决问题的能力。我对嵌入式软件开发充满热情,希望能在贵公司的相关岗位上发挥自己的所学,不断成长。
  2. 研究生相对于本科最大提升在哪方面:研究生阶段,我在科研思维和解决复杂问题的能力上有了很大提升。本科更多是学习基础理论和知识,而研究生期间,通过参与科研项目,我学会了如何从实际问题中提炼科学问题,运用更系统、深入的方法去研究和解决。比如在 [项目] 中,面对 [具体复杂问题],我需要查阅大量文献,设计实验方案,不断优化算法,这个过程让我在逻辑推理、创新能力以及对专业知识的深度理解上都有了质的飞跃。
  3. 讲一下项目开发流程:通常项目开发流程包括需求分析、设计、编码、测试和维护等阶段。首先是需求分析,与客户或相关方沟通,明确项目要实现的功能和性能要求;然后进行设计,包括架构设计、模块划分等,确定系统的整体框架;接着进入编码阶段,根据设计文档编写代码;之后是测试,包括单元测试、集成测试、系统测试等,确保软件功能正确、稳定;最后是维护阶段,在项目上线后,处理用户反馈的问题,进行功能迭代和优化。
  4. FreeRTOS 移植过程:FreeRTOS 移植主要包括以下步骤。首先,获取 FreeRTOS 源码,包含核心文件和与硬件相关的接口文件。然后,根据目标硬件(如特定的 MCU),实现 FreeRTOS 的底层接口,像任务切换所需的上下文切换代码(通常涉及汇编)、时钟节拍中断的配置等。接着,配置 FreeRTOS 的相关宏,比如任务栈大小、优先级数量等。之后,编写初始化代码,创建任务并启动调度器。最后,进行测试,确保 FreeRTOS 在目标硬件上能正常运行任务调度等功能。
  5. FreeRTOS 调度方式,任务怎么切换:FreeRTOS 采用基于优先级的抢占式调度方式,同时也支持时间片轮转调度。当有更高优先级的任务进入就绪态时,当前运行的低优先级任务会被抢占,高优先级任务获得 CPU 使用权。任务切换主要通过上下文切换实现,在任务切换时,会保存当前任务的上下文(如寄存器值等),然后恢复下一个要运行任务的上下文,从而实现任务的切换。
  6. 讲一下实习期间的收获:实习期间,我收获颇丰。在专业技能上,我更熟练地掌握了 [具体技术,如某款 MCU 的开发、特定通信协议的应用等],参与了 [实习项目],将学校学到的理论知识应用到实际工作中,提升了自己的动手能力。在工作方法和团队协作方面,我学会了如何在团队中高效沟通,与同事配合完成项目任务,也了解了企业的开发流程和规范,培养了自己的职业素养。
  7. FreeRTOS 中的中断配置:在 FreeRTOS 中,中断配置需要考虑中断优先级和 FreeRTOS 的中断管理。首先,要合理设置硬件中断的优先级,确保关键中断能及时响应。FreeRTOS 提供了相关的 API 来管理中断,比如portDISABLE_INTERRUPTS()portENABLE_INTERRUPTS()用于关闭和开启中断,还有专门的中断服务例程(ISR)中使用的 API,如xHigherPriorityTaskWoken来标记在中断中是否有更高优先级的任务需要调度,在中断退出前进行任务切换判断。
  8. 驱动的作用是什么:驱动程序是操作系统与硬件设备之间的桥梁。它的主要作用是将操作系统的请求传输,转化为特定物理设备控制器能够理解的命令。通过驱动程序,操作系统可以对硬件设备进行初始化、配置、读写等操作,使硬件设备能够正常工作,为上层应用程序提供操作硬件的接口。
  9. 驱动程序和用户态软件的区别是什么:驱动程序运行在内核态,具有对硬件的直接访问权限,能够执行一些特权操作,比如直接操作硬件寄存器等,其主要负责与硬件交互。而用户态软件运行在用户空间,受到操作系统的保护,不能直接访问硬件,需要通过系统调用等方式请求内核态的驱动程序来完成对硬件的操作。驱动程序更接近硬件,用户态软件则更偏向于实现应用功能。
  10. 内存操作中驱动和用户态的区别:在内存操作方面,驱动程序在内存管理上有更大的权限,可以直接进行物理内存的操作,比如内存映射(MMU 相关操作)等,用于与硬件设备进行数据交互的内存操作。而用户态软件只能操作自己的虚拟地址空间,对内存的分配、释放等操作由操作系统进行管理,通过系统调用(如 malloc 等函数,底层依赖操作系统的内存管理)来获取和使用内存,不能直接访问物理内存的特定区域(除非通过驱动提供的接口)。
  11. 驱动的加载与卸载函数:在 Linux 驱动中,模块的加载函数是module_init()指定的函数,当驱动模块被加载时,该函数会被调用,主要完成驱动的初始化工作,比如设备注册、中断申请等。模块的卸载函数是module_exit()指定的函数,当驱动模块被卸载时,该函数会被调用,用于释放驱动占用的资源,如注销设备、释放中断等。
  12. C 语言中用到过哪些数据结构:在 C 语言中,常用的数据结构有数组、链表(单链表、双链表)、栈、队列、树(如二叉树)、哈希表等。比如在实现一些需要动态存储数据的场景时,会用到链表;在进行表达式求值等操作时,会用到栈;在需要快速查找数据时,可能会用到哈希表。
  13. 链表适合存储什么数据:链表适合存储需要频繁进行插入、删除操作,且数据量不固定、不需要随机访问的数据。因为链表的插入和删除操作只需要修改指针,时间复杂度为 O (1),而不需要像数组那样移动大量元素。例如,在实现一个动态的任务队列时,使用链表可以方便地添加和移除任务。
  14. 宏定义有哪些用法,与内联函数的区别:宏定义的用法主要有定义常量(如#define PI 3.14159)、定义带参数的宏(如#define MAX(a,b) ((a)>(b)?(a):(b)))等。宏是在预处理阶段进行文本替换,没有类型检查,也不会产生函数调用的开销,但可能会导致代码膨胀等问题。内联函数是在编译阶段进行处理,会进行类型检查,它像函数一样有参数传递和返回值,同时又避免了函数调用的开销(将函数体代码嵌入到调用处)。内联函数更安全、灵活,而宏在某些简单场景下使用更简洁,但要注意使用规范避免错误。
  15. 遇到最大困难是什么:在之前参与的 [项目名称] 中,我遇到的最大困难是 [具体困难,如某个功能模块的性能不达标,或者与其他模块的兼容性问题等]。当时,这个问题导致项目进度受到影响,我通过 [采取的措施,如查阅大量资料、与团队成员反复讨论、进行多次实验和调试等],最终解决了问题,也让我对相关技术有了更深入的理解。
  16. 项目任务划分:在项目中,任务划分通常会根据功能模块和团队成员的技能来进行。比如将项目分为硬件驱动模块、应用逻辑模块、通信模块等,然后根据每个成员在相应领域的专长,分配具体的任务。同时,会明确每个任务的 deadlines 和交付物,确保项目各部分能协调推进。
  17. 怎么实现线程池的:实现线程池通常需要以下步骤。首先,创建一定数量的工作线程,并让它们处于等待状态。然后,维护一个任务队列,当有新的任务到来时,将任务加入队列。工作线程从队列中获取任务并执行。还需要考虑线程同步和互斥,比如使用锁来保护任务队列的操作,避免竞争条件。另外,还可以设置线程池的参数,如线程数量、队列大小等,以适应不同的应用场景。
  18. 怎么定位死锁问题:定位死锁问题可以通过分析系统的资源分配情况和线程的执行路径。首先,查看是否存在循环等待资源的情况,即多个线程互相等待对方持有的资源。可以使用工具(如调试器、系统监控工具)来查看线程的状态、持有的锁以及等待的锁。也可以在代码中添加日志,记录线程获取和释放锁的过程,从而追踪死锁发生的原因。
  19. 团队合作里最重要的是什么:团队合作里最重要的是有效的沟通和相互信任。有效的沟通能确保团队成员之间信息畅通,大家对项目目标、任务分工等有清晰的认识,避免因误解导致的问题。相互信任则能让成员们放心地将工作交给队友,在遇到困难时能互相支持,共同为项目成功努力。
  20. 团队沟通注重什么:团队沟通注重清晰、及时和尊重。清晰的沟通能让信息准确传达,避免歧义;及时沟通能确保问题得到及时反馈和解决,不影响项目进度;尊重的沟通能营造良好的团队氛围,让每个成员都能积极参与交流,发表自己的观点。
  21. 反问:请问贵公司在嵌入式软件开发方面,目前主要的项目方向和技术栈是怎样的?团队未来的发展规划是怎样的?

小鹏嵌入式

  1. freertos 项目中的任务有哪些?如何分配的优先级?:在我参与的 FreeRTOS 项目中,有数据采集任务、数据处理任务、通信任务以及用户交互任务等。优先级分配是根据任务的紧急程度和重要性来确定的,比如数据采集任务需要实时获取数据,所以分配了较高的优先级;通信任务用于与外部设备交互,优先级次之;数据处理任务在获取到数据后进行处理,优先级稍低;用户交互任务优先级相对最低,因为对实时性要求不是特别高。
  2. 介绍如何实现的低功耗?:实现低功耗主要从几个方面入手。首先,合理配置 FreeRTOS 的任务,在不需要执行任务时,让任务进入阻塞状态,减少 CPU 的运行时间。其次,利用 MCU 的低功耗模式,在系统空闲时,让 MCU 进入休眠模式,如停止模式或待机模式,降低功耗。另外,对外设进行管理,在不使用外设时,关闭外设的时钟或电源,减少不必要的能耗。
  3. 堆和栈是用来干嘛的?:栈主要用于存储函数调用时的局部变量、函数参数以及返回地址等,它的内存分配是由编译器自动管理的,遵循 “先进后出” 的原则,空间较小且是连续的。堆则用于动态内存分配,程序可以通过malloc等函数在堆上申请内存,用于存储需要长期存在或大小不固定的数据,堆的内存管理相对灵活,但需要程序员手动分配和释放(在一些高级语言或环境中可能有自动回收机制),空间通常比栈大,且不一定是连续的。
  4. 会看汇编吗?:会一些基础的汇编查看,能够通过汇编代码来辅助调试程序,比如查看函数的调用过程、寄存器的使用情况等,这对理解程序的底层运行和解决一些疑难问题有帮助。
  5. 用过锁吗?:用过,在多任务环境下,为了保护共享资源,防止多个任务同时访问导致数据不一致的问题,会使用锁,比如 FreeRTOS 中的互斥量(mutex)、信号量(semaphore)等,来实现对共享资源的互斥访问。
  6. 任务之间怎么实现的同步?:任务之间的同步可以通过 FreeRTOS 提供的同步机制来实现,比如使用信号量,当一个任务完成某个操作后,发送信号量,另一个等待该信号量的任务就会被唤醒,继续执行;也可以使用事件组,多个任务可以等待多个事件的组合,当事件发生时,任务被同步唤醒。
  7. iic?spi?:IIC(Inter-Integrated Circuit)是一种两线式串行总线,支持多主多从模式,通信速率相对较低,常用于短距离、低速的设备通信,比如传感器与 MCU 之间的通信。SPI(Serial Peripheral Interface)是一种四线式串行总线,通常是主从模式,通信速率较高,用于高速数据传输的场景,如与 Flash 存储设备、高速 AD/DA 等的通信。
  8. 未来的职业规划?:短期内,我希望能在嵌入式软件开发领域不断提升自己的专业技能,深入掌握相关技术,成为一名能够独立负责模块开发的工程师。长期来看,希望能在技术管理或架构设计方面有所发展,带领团队完成更复杂的嵌入式系统项目。
  9. iic 与 spi 的区别?:如前面所述,IIC 是两线(SDA、SCL),多主多从,地址寻址,速率低;SPI 是四线(MOSI、MISO、SCK、CS),主从,无地址寻址(通过 CS 选择从设备),速率高。此外,IIC 的总线仲裁机制相对复杂,SPI 则更简单直接。
  10. rtos 中任务同步的方式?:RTOS 中任务同步的方式有信号量、互斥量、事件组、消息队列等。信号量用于控制对共享资源的访问或任务间的同步;互斥量主要用于互斥访问共享资源,还能解决优先级反转问题;事件组用于多个事件的组合同步;消息队列用于任务间传递消息,实现同步和数据交互。
  11. ros 中通信和 freertos 中通信的区别?:ROS是面向机器人的操作系统,其通信机制更复杂,支持多种通信方式,如话题(Topic)、服务(Service)、动作(Action)等,主要用于分布式系统中不同节点之间的通信,强调灵活性和扩展性,适用于复杂的机器人系统。而 FreeRTOS 中的通信主要是在同一个 MCU 内的多任务之间,通信方式相对简单,如队列、信号量等,用于任务间的同步和数据传递,更侧重于实时性和资源有限的嵌入式系统。
  12. 中断延时测量过吗?:测量过,通过在中断服务程序的入口和出口处添加时间标记,比如使用高精度的定时器,记录进入中断和退出中断的时间差,从而测量中断延时,这对于评估系统的实时性很重要。
  13. 优先级反转了解过吗?:了解过,优先级反转是指当一个低优先级任务持有一个高优先级任务需要的资源时,高优先级任务会被阻塞,而此时如果有一个中优先级任务可运行,就会先于高优先级任务运行,导致高优先级任务的响应时间变长。FreeRTOS 中的互斥量可以解决优先级反转问题,通过优先级继承机制,让持有互斥量的低优先级任务临时提升优先级,直到释放互斥量。
  14. 对岗位了解吗?:我了解到这个嵌入式软件开发岗位,主要负责嵌入式系统的软件设计、开发和维护,涉及到 FreeRTOS 等实时操作系统的应用、驱动开发以及与硬件的交互等工作。需要具备扎实的 C 语言基础、嵌入式系统知识以及相关的开发经验,这与我的专业背景和职业规划非常契合。

CVTE_2

  1. 自我介绍:面试官您好,我是 [姓名],毕业于 [学校名称],专业是 [专业名称]。在校期间,我专注于嵌入式软件开发方向的学习与实践,积累了较为扎实的理论基础和项目经验。曾参与过 [具体项目名称],在项目中负责 [具体工作内容],涉及 FreeRTOS 任务调度、驱动开发等方面,锻炼了我的问题解决能力和团队协作能力。我对嵌入式领域充满热情,希望能在贵公司的嵌入式软件开发岗位上贡献自己的力量。
  2. 碰到的最难的问题:在之前参与的一个嵌入式设备通信项目中,设备间通过自定义协议进行数据传输时,频繁出现数据丢包和乱序的问题。经过仔细排查,发现是由于通信双方的时序匹配存在误差,且在数据处理时没有完善的校验机制。我查阅了大量通信协议相关资料,重新优化了时序控制逻辑,并引入了 CRC 校验算法,最终解决了数据传输的稳定性问题。这个过程让我深刻认识到细节把控和严谨校验在嵌入式开发中的重要性。
  3. 裸机和 RTOS,如何挑选,包括 Linux 驱动:选择裸机还是 RTOS,主要依据项目需求。若项目功能简单,任务单一,对实时性要求不高,裸机开发更为直接高效,资源占用少。当项目复杂,存在多任务并行需求,且对实时性要求严格时,RTOS(如 FreeRTOS)更合适,它能提供良好的任务调度和管理机制。对于需要丰富功能、复杂驱动支持的大型嵌入式系统,Linux 则是更好的选择,其成熟的驱动框架和庞大的生态系统,能支持多种硬件设备和复杂应用。
  4. RTOS 如何保证实时性:RTOS 通过基于优先级的抢占式调度策略来保证实时性。高优先级的任务可以随时抢占低优先级任务的 CPU 使用权,确保紧急任务能够及时得到处理。同时,RTOS 的上下文切换时间极短,中断响应也经过优化,能够快速对外部事件做出反应,从而满足实时系统的要求。
  5. FreeRTOS 源码如何实现任务调度:FreeRTOS 的任务调度主要通过vTaskSwitchContext函数实现。该函数会遍历所有就绪任务的任务控制块(TCB),找到当前优先级最高的就绪任务,然后进行上下文切换,将 CPU 的执行权交给该任务。在任务创建时,会将任务的 TCB 添加到相应的优先级就绪列表中,方便调度器进行查找和管理。
  6. FreeRTOS 如何实现任务切换:任务切换过程涉及上下文的保存与恢复。当发生任务切换时,FreeRTOS 会将当前任务的寄存器状态等上下文信息保存到该任务的栈中,然后从下一个要运行的任务的栈中恢复其上下文信息,包括寄存器值、程序计数器等,从而实现任务的切换。这一过程通常通过汇编代码来完成,以保证切换的高效性。
  7. 怎么判断能驱动几路电机:要判断能驱动几路电机,需要综合考虑多个因素。首先是 MCU 的硬件资源,如可用的 PWM 通道数量,因为电机控制通常需要 PWM 信号;其次是 GPIO 的驱动能力,包括输出电流等参数,要确保 GPIO 能为电机提供足够的驱动电流;另外,还需考虑电源的供电能力,能否为所有电机提供稳定且足够的功率。通过对这些因素进行分析和计算,才能确定 MCU 能够驱动的电机数量。
  8. 如何判断主频能带动多少电机:判断主频能带动多少电机,需要评估控制单个电机所需的计算量和时间。首先分析控制电机的算法复杂度,如 PID 控制算法的计算步骤;然后根据 MCU 的主频,计算出单位时间内 MCU 能够进行的指令数。通过将控制单个电机的计算量与 MCU 的计算能力进行对比,可大致估算出在该主频下能够同时稳定控制的电机数量。
  9. 摄像头驱动底层,如何检测驱动错误:在摄像头驱动底层检测错误,可从多个方面入手。一是通过读取摄像头的状态寄存器,获取摄像头的工作状态信息,判断是否存在硬件层面的错误;二是在数据传输过程中,对传输的数据进行校验,如检查数据的帧格式、校验和等,若不符合预期,则说明驱动存在错误;此外,还可以通过打印调试信息,跟踪驱动的执行流程,定位出现错误的具体环节。
  10. 如果打印日志,如何根据日志排查 I2C 驱动:当打印 I2C 驱动的日志后,可根据日志中记录的 I2C 通信过程来排查错误。查看日志中 I2C 的起始信号、地址传输、数据传输、停止信号等各个阶段的信息是否正常。例如,若起始信号没有正确产生,或者地址传输时出现不匹配的情况,都能从日志中发现异常,进而定位到 I2C 驱动在相应阶段的问题。
  11. I2C 驱动的时序:I2C 通信有时钟线(SCL)和数据线(SDA)两条线。起始条件是当 SCL 为高电平时,SDA 从高电平跳变为低电平;停止条件是当 SCL 为高电平时,SDA 从低电平跳变为高电平。在数据传输过程中,SCL 为高电平时,SDA 上的数据必须保持稳定,数据在 SCL 的低电平期间进行变化。地址和数据都是以字节为单位进行传输,且每个字节传输后都需要有一个应答位。
  12. 为什么需要头文件:头文件的主要作用是实现代码的模块化和复用。它用于声明函数、宏定义、数据结构等,使得这些声明可以被多个源文件(.c 文件)共享。这样,在不同的源文件中就不需要重复编写相同的声明,提高了代码的可维护性和开发效率,同时也便于对接口进行统一管理。
  13. 头文件分尖括号和双引号,应该如何写引入的顺序:在引入头文件时,通常先使用双引号引入用户自定义的头文件(属于项目内部的头文件),然后再使用尖括号引入系统提供的头文件(如标准库头文件)。
  14. 就是要先写双引号再写尖括号,为什么:这样做的原因是编译器会先在当前项目的目录下查找双引号指定的头文件,如果找不到,再到系统的头文件目录中查找尖括号指定的头文件。这种顺序可以确保项目内部的头文件优先被找到和使用,避免因系统头文件与项目头文件重名而导致的冲突问题,保证项目代码的正确性。
  15. float 可以移位吗:在 C 语言中,移位操作符(<<>>)只能用于整数类型(如intlong等),float类型是浮点类型,不能直接进行移位操作。因为移位操作是基于二进制位的整数运算,而float类型的存储格式是按照 IEEE 754 标准的浮点格式,与整数的存储格式不同。
  16. 就是要 float 移位怎么办 (可以用指针强转):如果需要对float类型的数据进行类似移位的操作,可以通过指针强制转换来实现。将float类型的指针转换为unsigned int类型的指针,然后对unsigned int类型的数据进行移位操作,这样就可以对float类型数据的二进制位进行操作。但需要注意的是,这种操作会直接修改float数据的二进制表示,需要清楚float的存储格式,否则容易导致数据错误。
  17. new 和 mallocnew是 C++ 中的操作符,它不仅会在堆上分配内存,还会调用对象的构造函数来初始化对象;而malloc是 C 语言中的函数,它只是在堆上分配指定大小的内存,不会进行初始化操作,返回的是void*类型的指针,需要进行类型转换。此外,new分配内存失败时会抛出异常,malloc失败时会返回NULL
  18. 进程间、线程间通讯,管道的优势:在进程间通信中,管道(特别是匿名管道)具有简单易用的优势,它可以在父子进程之间方便地传递数据,不需要复杂的设置和协议。对于线程间通信,虽然线程共享进程的地址空间,有更高效的通信方式(如共享内存、互斥量等),但管道在某些简单的线程间数据传输场景下,也能提供一种相对独立和有序的通信渠道,并且实现较为简单。
  19. 同进程多线程,可以 socket 通讯吗:同进程内的多线程是可以使用 socket 进行通信的,但通常没有必要。因为同进程的线程共享进程的地址空间,它们可以通过共享内存、全局变量、线程安全的队列等方式进行更高效、更直接的通信。socket 通信主要用于不同进程之间或网络通信场景,其通信开销相对较大,在同进程线程间通信时,使用共享内存等方式更为合适。
  20. 怎么学习:我的学习方法主要包括理论学习和实践相结合。首先通过教材、在线课程等学习嵌入式开发的理论知识,如 C 语言、操作系统、硬件原理等。然后通过实际项目来巩固和应用这些知识,在项目中遇到问题时,通过查阅文档、论坛,与同行交流等方式解决问题,不断总结经验,提升自己的技能。
  21. 经典反问:请问贵公司在嵌入式软件开发方面,目前主要的项目方向和技术难点是什么?团队在这些项目中采用的技术栈是怎样的?

小米嵌入式实习

  1. 自我介绍,问了一点项目:面试官您好,我是来自东南大学的 [姓名],专业是嵌入式软件开发。在项目方面,我参与过基于 FreeRTOS 的智能设备控制系统开发,负责系统的任务调度模块和部分硬件驱动的开发工作。通过这个项目,我深入理解了 FreeRTOS 的工作机制以及嵌入式系统中软硬件交互的过程,也提升了自己的代码编写和问题解决能力。
  2. FreeRTOS 操作系统:FreeRTOS 是一款轻量级的实时操作系统,适用于嵌入式系统。它支持多任务并发执行,通过任务调度器对不同优先级的任务进行管理,能够保证高优先级任务的实时性。同时,它还提供了丰富的 API,用于任务创建、同步、通信等操作,方便开发者进行嵌入式系统的开发。
  3. static 关键字static关键字有多种用法。在修饰局部变量时,该变量会在程序的整个生命周期内存在,不会随着函数的调用结束而销毁,且仅在定义它的函数内可见;修饰全局变量或函数时,会将其作用域限制在当前文件内,外部文件无法访问,起到了隐藏标识符的作用,避免了命名冲突。
  4. 指针函数和函数指针:指针函数是指返回值为指针类型的函数,其形式如int *func(int a);函数指针是指向函数的指针变量,它可以存储函数的入口地址,通过函数指针可以调用相应的函数,形式如int (*pFunc)(int a),其中pFunc就是一个函数指针,可指向返回值为int、参数为int的函数。
  5. 野指针:野指针是指指向的地址不确定或无效的指针。它可能是由于指针未初始化、指针指向的内存被释放后未及时置空等原因产生的。使用野指针进行操作会导致不可预测的结果,如程序崩溃、数据损坏等,因此在编程中要避免野指针的出现,养成及时初始化指针和释放内存后将指针置空的习惯。
  6. 如何防止重复引用头文件:为了防止头文件被重复引用,可以使用预处理指令#ifndef#define#endif来对每个头文件进行保护。具体做法是在头文件的开头使用#ifndef判断一个唯一的标识符是否未定义,如果是,则通过#define定义该标识符,并包含头文件的内容;如果已经定义,则跳过该头文件的内容,这样就可以避免头文件被多次包含。
  7. 如何预防变量的重定义:预防变量重定义的方法主要有两种。一是将变量定义为static类型(如果该变量仅在当前文件中使用),这样可以限制变量的作用域在当前文件内,避免与其他文件中的变量重名;二是使用extern关键字在一个文件中声明变量,而在另一个文件中定义该变量,确保变量只在一个文件中进行定义,其他文件通过声明来使用。
  8. 数据结构用过哪些:在嵌入式开发中,我用过的常见数据结构有数组、链表、栈、队列等。数组用于存储一组相同类型的数据,访问速度快;链表用于动态存储数据,插入和删除操作方便;栈遵循 “先进后出” 的原则,常用于函数调用、表达式求值等场景;队列遵循 “先进先出” 的原则,适用于任务调度、数据缓冲等场景。
  9. C 文件的预编译过程:C 文件的预编译过程主要包括以下几个步骤。首先,处理宏定义,将所有的#define指令进行替换;其次,处理头文件包含,将#include指令指定的头文件内容插入到当前文件中;然后,处理条件编译指令(如#if#ifdef等),根据条件决定哪些代码段被包含或排除;最后,生成经过预处理的.i文件,供后续的编译阶段使用。
  10. 堆和栈的区别以及怎么管理堆区的:堆和栈的区别主要体现在以下几个方面。栈是由编译器自动管理的,用于存储函数的局部变量、函数参数、返回地址等,其内存空间是连续的,大小固定,遵循 “先进后出” 的原则;堆是用于动态内存分配的区域,由程序员手动管理(通过mallocfree等函数),内存空间不连续,大小不固定。堆区的管理通常采用一些算法,如空闲链表法,将空闲的内存块组织成链表,当需要分配内存时,从链表中找到合适的内存块进行分配;当释放内存时,将内存块重新插入到空闲链表中。
  11. 最后手撕合并有序链表:合并两个有序链表的基本思路是创建一个新的链表,然后同时遍历两个待合并的有序链表,比较当前节点的值,将较小的节点添加到新链表的末尾,直到其中一个链表遍历完毕,再将另一个链表中剩余的节点添加到新链表的末尾。以下是实现代码:
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) {// 创建哑节点,方便处理头节点struct ListNode* dummy = (struct ListNode*)malloc(sizeof(struct ListNode));struct ListNode* current = dummy;// 同时遍历两个链表while (l1 != NULL && l2 != NULL) {if (l1->val < l2->val) {current->next = l1;l1 = l1->next;} else {current->next = l2;l2 = l2->next;}current = current->next;}// 将剩余的节点连接到新链表current->next = l1 != NULL ? l1 : l2;// 保存新链表的头节点(哑节点的下一个节点)struct ListNode* result = dummy->next;// 释放哑节点的内存free(dummy);return result;
}

http://www.dtcms.com/a/467232.html

相关文章:

  • package.xml文件的作用
  • 萍乡网站建设哪家好哦小项目加盟
  • 爱妮微如何做网站链接的网址一起做网站下载数据包
  • C语言入门教程(第5讲):数组详解——一次性搞懂一维数组、二维数组与内存布局
  • c++ static_cast用法
  • 广东省建设工程总监扣分查询网站无极网络
  • 龙华建设网站公司wordpress 3.7
  • 淘宝客怎么做自己的网站学习做网站的网站
  • 手机百度网站证书过期外贸网站推广方法之一
  • 网站登录后不显示内容试玩网站设计建设
  • Visual Basic.NET 关键词
  • 数据结构--------树二叉树
  • 郑州网站开发培训班o2o网站系统建设
  • Lampiao渗透项目学习记录
  • 新功能来袭——支持导出MIDI文件,AI音乐从此进入新篇章
  • 网站推广活动方案权威网站排名
  • Google 智能体设计模式:工具使用(函数调用)
  • 网站开发的技术类型有哪些网络服务代码1001
  • Redis-string
  • 网站网页的收录数量赤峰建设厅官方网站
  • 做灯箱的网站wordpress nginx apache
  • (7)100天python从入门到拿捏《迭代器和生成器》
  • 花卉网站建设策划书核酸二维码
  • 00--VSCode配置
  • 光明区建设局网站抖音代运营成功案例
  • 宁波论坛建站模板珠海市建设工程信息网
  • 温州做网站价格外贸推广软件
  • 自己建的网站也要注册域名吗电子商务网站建设与维护试卷答案
  • Java项目:基于SSM框架实现的连锁干洗店管理系统(ssm+B/S架构+源码+数据库+毕业论文)
  • 国外装饰公司网站郑州网站seo厂家