嵌入式软件开发面试题

单片机启动方式

  1. 从Flash启动,将Flash地址0x08000000映射到0x00000000

  2. 从SRAM启动,将SRAM地址0x20000000映射到0x00000000

  3. 从系统存储器启动,将系统存储器地址0x1FFFF000映射到0x00000000,通过uart1烧录程序,boot0置0,boot1置1

指针SP指向哪里

启动方式不同,指向地址不同,从Flash启动,SP指向0X0800 0000,从SRAM启动SP指向0X2000 0000

数组和链表的区别

数组和链表是两种常见的数据结构,它们在存储和访问数据上有一些区别。

  1. 存储方式:数组是一种连续存储的数据结构,它将元素存储在一段连续的内存空间中,而链表则是通过每个节点中保存下一个节点的地址来实现存储。

  2. 插入和删除操作:对于数组,插入和删除操作可能需要移动其他元素以保持连续性,这样的操作时间复杂度为O(n);而对于链表,插入和删除操作只需要调整节点的指针指向,时间复杂度为O(1)。

  3. 访问元素:数组可以通过索引直接访问元素,时间复杂度为O(1);而链表需要从头节点开始遍历,直到找到目标节点,时间复杂度为O(n)。

  4. 空间复杂度:数组的空间复杂度是固定的,与元素个数成正比;而链表的空间复杂度是动态的,根据实际需要分配。

  5. 扩容:对于数组,如果存储空间不够,需要重新分配更大的空间,并将原有元素复制到新的空间中;而链表可以动态地分配节点,不需要连续的内存空间。

综上所述,数组适合随机访问元素,但插入和删除操作较慢;链表适合频繁插入和删除操作,但访问元素较慢。在实际应用中,根据具体需求选择合适的数据结构。

存储区域介绍

区域作用
内存栈区存放局部变量名
内存堆区存放new或者malloc出来的对象
常数区存放局部变量或者全局变量的值
静态区用于存放全局变量或者静态变量
代码区二进制代码
全局区(静态区)已初始化的全局变量

通信协议

TTL电平

  1. 全双工

    1. 5V系统,逻辑1:2.4v-5v

    2. 逻辑0:0v-0.5v

  2. 信号0对应0v,信号1对应3.3v或者5v,与单片机、SOC的IO电平兼容

  3. 通信距离较短——大概为几米

同步

两个通信的机器时钟频率一致

USART

  1. 异步通信

  2. 全双工

  3. 低速(115200bps=14.4kb/s,9600bps=1.2kb/s)

  4. TTL电平

  5. 经典电路

IIC

  1. 同步通信

  2. 半双工

  3. 低速(标准模式S=100kbps=12.5kb/s,快速模式F=400kpbs=50kb/s,高速模式HS=3.4Mbps=435kb/s,超快速模式=5Mbps=525kb/s)

  4. 通信距离——理想距离15米,实际一般就几米,通常板间通信

  5. TTL电平

  6. 经典电路

SPI通信

  1. 同步

  2. 高速(50Mbps=5.9mb/s)

  3. 通信距离——一般15cm以下

  4. 全双工

  5. TTL电平

  6. 经典电路

为什么SPI比IIC快?

  1. 因为SPI协议更简单,没有仲裁,总线等判断

  2. SPI通信距离更短一些,硬件决定可以把时钟速度拉高

RS232

  1. 异步

  2. 低速(50b/s,75b/s,110b/s,150b/s,300b/s,600b/s,1200b/s,2400b/s,4800b/s,9600b/s,19200b/s)

  3. 通信距离——RS232理论距离最大为几十米

  4. 节点数量——1对1

  5. 全双工

  6. RSR232电平

    1. 规定逻辑1电平:-15v~-5v,逻辑0电平为+5v~+15v,选用该电气标准的目的在于提高抗干扰能力,增大通信距离。RS232的噪声容限为2V,接收器将能识别高至+3V的信号作为逻辑“0”,将低到-3V的信号作为逻辑“1”

    2. 由于RS232采用串行传送方式,并且将微机的TTL电平转换为RS-232C电平,其传送距离一般可达30m。若采用光电隔离20mA的电流环进行传送,其传送距 离可以达到1000m。

  7. 经典电路

RS485

  1. 异步

  2. 低速——RS485的最高数据传输速率为10Mbps=1250kb/s

  3. 长距离-RS485的最大通信距离为约1200m

  4. 半双工

  5. 节点数量——RS-485在总线上是允许连接多达128个收发器。

  6. 485电平

    1. (逻辑1:+2V~+6V 逻辑0:-6V~2V)这里的电平指AB两线间的电压差。

    2. RS485是一种串口接口标准,为了长距离传输采用差分方式传输,传输的是差分信号,即通过AB两根线的电压差作为电平信号。差分信号能有效地抵御外界因素的干扰,因为干扰对两根线影响是一样的,两根线的电压差不变,信号传递也就不会受干扰。

    3. 通信差分电压是A高B低

    4. 经典电路

CAN

  1. ISO11898标准

  2. 低速——最高数据传输速率1Mbps=128kb/s

  3. 超长距离——CAN是半双工的。收发数据要分时进行。不管CAN网络上挂多少设备,在同一时刻只能有1个发送数据。如果有多个需要同时发送则只有优先级别高的先发送,其它等待。

  4. 节点数量

    1. 理论上最多有127个节点 总线两端120R电阻

  5. CAN总线电平

    1. 逻辑1是隐性电平,CAN_High和CAN_Low都是2.5V,电位差为0V, 逻辑0是显性电平,CAN_Hight为3.5V,CAN_Low为1.5V,电位差是2V

    2. 传输的是差分信号,即通过CAN_H、CAN_L两根线的电压差作为电平信号。

  6. 经典电路

linux——相关基础概念

Linux内核的架构和主要组成部分

Linux内核是操作系统的核心部分,它负责管理计算机硬件和提供基本的系统功能。下面是Linux内核的一些主要架构和组成部分:

  1. 进程管理:Linux内核负责管理系统中的进程。它负责创建、销毁和调度进程,以及为进程分配资源和处理进程间的通信。

  2. 内存管理:Linux内核负责管理系统的内存资源。它跟踪系统中的内存使用情况,并为进程分配内存空间。内存管理的一部分是虚拟内存系统,它允许每个进程拥有独立的虚拟地址空间。

  3. 文件系统:Linux内核支持各种文件系统,包括常见的ext4、NTFS等。它负责管理文件和目录,以及处理文件的读写操作。

  4. 设备驱动程序:Linux内核通过设备驱动程序与硬件设备进行通信。每个设备都有对应的驱动程序,负责与设备进行交互和控制。

  5. 网络协议栈:Linux内核支持各种网络协议,如TCP/IP协议栈。它负责处理网络通信,包括数据包的传输、路由和网络连接的管理。

  6. 系统调用接口:Linux内核提供系统调用接口,允许用户空间程序与内核进行交互。通过系统调用,用户程序可以请求内核执行特定的操作,如文件读写、进程创建等。

以上只是Linux内核的一部分架构和组成部分,它还包含许多其他功能和模块,如中断处理、调度器、文件系统驱动等。Linux内核是一个开源项目,具有可扩展性和灵活性,可以根据需求进行定制和修改。

什么是设备树(Device Tree)?它在嵌入式Linux开发中的作用是什么?

设备树(Device Tree)是一种用来描述硬件设备信息的数据结构,广泛应用于嵌入式Linux系统的开发中。它的作用是将硬件设备的信息和配置与操作系统内核分离开来,使得内核在不同的硬件平台上能够灵活适配和配置。

在传统的嵌入式系统中,硬件设备的信息通常是通过编译内核时直接硬编码进内核代码中的。这样的设计存在一些问题,例如:

  1. 不利于跨平台移植:硬件信息的硬编码使得内核无法自动适配不同的硬件平台,需要针对每个平台进行修改和重新编译。

  2. 不利于硬件配置的修改:如果要对硬件配置进行修改,需要重新编译和烧录内核,这样的过程较为繁琐。

  3. 不利于设备的动态添加和移除:硬编码的硬件信息不适合处理设备的动态添加和移除,需要手动修改内核代码。

为了解决以上问题,设备树被引入到嵌入式Linux开发中。设备树是一个描述硬件设备的层次结构的文本文件,它将硬件设备的信息、配置和连接关系以一种结构化的方式进行描述。设备树并不包含设备的驱动程序代码,而是提供了一个通用的描述设备的方式。

嵌入式Linux系统在启动过程中,会从固定的位置(通常是存储器中)加载设备树文件,并将设备树传递给内核。内核在启动时会解析设备树,并根据设备树的描述来动态地进行硬件设备的初始化和配置。这样,内核就可以在不同的硬件平台上运行,而无需修改内核代码,只需要使用适当的设备树文件。

设备树除了描述硬件设备的信息外,还可以描述中断控制器、时钟、电源管理等系统级别的信息。它可以帮助系统开发人员更好地理解和配置硬件平台,提高系统的可移植性和灵活性。同时,设备树也为硬件厂商提供了一种标准的、与操作系统独立的硬件描述方式,方便供应链管理和系统集成。

总之,设备树在嵌入式Linux开发中的作用是将硬件设备的信息和配置与操作系统内核分离开来,提高系统的可移植性和灵活性。它是一个通用的描述硬件的方式,使得内核可以在不同的硬件平台上进行适配和配置。

如何编写一个简单的设备树文件来描述一个GPIO设备?

编写设备树文件描述一个GPIO设备需要以下步骤:

  1. 打开文本编辑器,创建一个新的设备树文件,以.dts.dtsi为扩展名。

  2. 在文件开头,添加设备树文件的描述信息,例如:

/dts-v1/;
/include/ "path/to/include/file.dtsi";
​
/ {
    compatible = "manufacturer,model";
    description = "GPIO device example";
};

这里的manufacturermodel可以替换为相应的设备制造商和型号。

  1. 添加GPIO设备的节点,例如:

&gpio {
    status = "okay";
    
    gpio-device {
        compatible = "manufacturer,gpio-example";
        gpio-controller;
        
        #gpio-cells = <2>;
        gpio-name = "my_gpio";
        
        gpio-pin = <0>;
        gpio-direction = "out"; // 或者 "in"
        gpio-active-low;
    };
};

这里的manufacturer可以替换为设备制造商的名称。

  1. 解释上述节点的各个属性:

    • compatible:用于指定设备的兼容性字符串。

    • status:用于指定设备的状态,"okay"表示设备可使用。

    • gpio-device:自定义名称,可以替换为识别设备的特定名称。

    • gpio-controller:用于指示该节点是GPIO控制器。

    • #gpio-cells:用于指定GPIO单元格的数量,一般为2。

    • gpio-name:自定义GPIO的名称。

    • gpio-pin:指定GPIO引脚的编号。

    • gpio-direction:指定GPIO引脚的方向,"out"表示输出,"in"表示输入。

    • gpio-active-low:可选属性,用于指示GPIO电平是否为低电平触发。

  2. 保存设备树文件并编译生成设备树二进制文件(.dtb)。

以上是一个简单的GPIO设备的设备树文件示例。根据实际需求,你可能需要添加更多属性或节点来描述设备的其他特性。

详细描述一下嵌入式系统的启动过程

嵌入式系统启动过程主要包括硬件初始化、引导加载、内核启动和用户空间启动等多个阶段。下面是嵌入式系统启动过程的详细描述:

  1. 硬件初始化:此阶段负责对硬件进行初始化,包括CPU、内存、外设等。其中,最重要的硬件初始化包括时钟初始化、内存控制器初始化、串口初始化和Flash初始化等。在此阶段完成后,CPU进入到其默认的复位状态。

  2. 引导加载:此阶段负责启动加载程序(Boot Loader)从非易失性存储器(如SPI Flash、NOR Flash或MMC/SD卡等)中加载内核镜像到RAM中,并将控制权交给内核启动代码执行。其中,引导加载可以通过串口、USB、以太网或无线网络接口等多种方式进行。

  3. 内核启动:此阶段负责解压缩内核镜像、初始化内核数据结构、初始化驱动程序、建立页表等基本操作。其中,内核启动流程包括以下几个步骤:

    • 解压缩内核镜像:内核镜像一般被压缩为gzip、bzip2或xz等格式,此阶段需要对内核镜像进行解压缩。

    • 初始化内核数据结构:此阶段负责初始化内核的数据结构,包括进程列表、文件系统、网络协议栈等。

    • 初始化驱动程序:此阶段负责初始化各种硬件驱动程序,包括串口、网卡、USB、存储设备等。

    • 建立页表:此阶段负责建立页表,为虚拟地址和物理地址之间建立映射关系,从而使内核能够访问物理内存。

  4. 用户空间启动:此阶段负责启动用户空间应用程序,包括系统服务、用户应用程序等。其中,用户空间应用程序需要通过init进程启动,并依次启动各个服务和应用程序。

  5. 用户交互:此阶段负责与用户进行交互,包括启动桌面、提供终端命令行界面等。在此阶段,用户可以使用各种工具和应用程序,从而满足自己的需求。

以上是嵌入式系统启动过程的详细描述。有了对于嵌入式系统启动流程的充分掌握,可以加强对于嵌入式系统工作原理的理解从而更好地实现它。

Linux——常见linux命令行工具

Linux命令行工具是Linux操作系统的核心,主要用于与操作系统进行交互和管理。下面列举一些常用的Linux命令行工具:

  1. ls:查看当前目录下的文件列表。

$ ls
  1. cd:切换当前目录到指定目录。

$ cd /path/to/directory
  1. pwd:显示当前所在的目录。

$ pwd
  1. cp:复制文件或目录。

$ cp source_file destination_directory
  1. mv:移动文件或目录,也可用于重命名文件或目录。

$ mv old_file_name new_file_name
  1. rm:删除文件或目录。

$ rm file_name
  1. mkdir:创建一个新的目录。

$ mkdir new_directory
  1. rmdir:删除指定的空目录。

$ rmdir empty_directory
  1. touch:创建一个新的空文件或改变现有文件的时间戳。

$ touch file_name
  1. cat:查看文件内容。

$ cat file_name
  1. less:分页显示文件内容。

$ less file_name
  1. grep:搜索文件中的文本。

$ grep "search_text" file_name
  1. find:按照指定条件查找文件。

$ find /path/to/search -name "file_name_pattern"
  1. ps:列出当前运行的进程。

$ ps -ef
  1. top:实时显示进程的运行情况。

$ top

以上是一些常用的Linux命令行工具,它们可以大大提高Linux系统管理员和开发人员的工作效率。

Linux——Makefile

Makefile概念

Makefile是一种常用的构建工具,用于描述和管理软件项目的编译、链接和安装等过程。

Makefile示例

objs = start.o main.o 

ledc.bin : $(objs)
	arm-linux-gnueabihf-ld -Timx6u.lds $^ -o ledc.elf
	arm-linux-gnueabihf-objcopy -O binary -S ledc.elf $@		//将"ledc.elf"文件转换为二进制格式,并保留符号信息。
	arm-linux-gnueabihf-objdump -D -m arm ledc.elf >ledc.dis	//反汇编

%.o : %.c
	arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<

%.o : %.s
	arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<

clean:
	rm -rf *.o ledc.bin ledc.elf ledc.dis

单片机

单片机IO口的4种输入和输出

输入输出
上拉输入开漏输出
下拉输入推挽输出
模拟输入复用开漏输出
浮空输入复用推挽输出

单片机IO口开漏输出和推挽输出有什么区别?

开漏输出:在开漏输出模式下,IO口只能拉低,而不能主动拉高。当IO口处于高电平状态时,是通过外部上拉电阻将电平拉高的。

推挽输出:在推挽输出模式下,IO口既可以拉高,也可以拉低。当IO口处于高电平状态时,IO口输出高电平信号,当IO口处于低电平状态时,IO口输出低电平信号。

单片机里定时器有什么作用和优势?

精确计时、时间戳、PWM控制、任务调度、节省CPU资源

简述通过I2C接口读取设备X的寄存器Y的值的过程

初始化I2C总线、发送起始信号、发送设备地址和写命令、发送寄存器地址、重新启动信号、发送设备地址和读命令、读取寄存器值、发送停止信号、解析和处理数据

unsigned char Ad_Read(unsigned char addr)
{
	unsigned char temp;
	I2CStart();
	I2CSendByte(0x90);
	I2CWaitAck();
	I2CSendByte(addr);
	I2CWaitAck();
	
	I2CStart();
	I2CSendByte(0x91);
	I2CWaitAck();
	temp=I2CReceiveByte();
	I2CSendAck(1);
	I2CStop();	
	return temp;
}

中断是什么?请简述STM32单片机中断处理的过程

中断是指在程序运行过程中,当发生某种事先定义好的特定事件时,CPU会终止当前执行的指令,立即转去处理该事件,并在处理完毕后返回到原来的执行位置继续执行。

在STM32单片机中,中断处理的过程如下:

  1. 中断向量表初始化:在程序运行之前,需要将中断向量表(Interrupt Vector Table)初始化。中断向量表是一个存储器区域,其中记录了各个中断源对应的中断服务子程序的入口地址。

  2. 中断源配置和使能:通过设置相应的寄存器,选择要使能的中断源,并设置中断触发条件,例如上升沿触发、下降沿触发等。

  3. 中断服务子程序编写:为每个中断源编写对应的中断服务子程序(ISR,Interrupt Service Routine)。中断服务子程序是一段特殊的代码,用于具体处理中断事件。

  4. 中断优先级设置:为不同的中断源设置优先级,以确定中断优先级的高低顺序。在多个中断同时发生时,会根据优先级的设定确定先后顺序。

  5. 中断发生:当一个中断事件发生时,CPU会暂停当前执行的指令,保存相关的寄存器状态并跳转到对应中断源的中断服务子程序。

  6. 中断服务子程序执行:CPU开始执行中断服务子程序,处理中断事件。在执行完中断服务子程序后,可以根据需要进行相关的操作,例如读取数据、清除中断标志等。

  7. 中断结束:中断服务子程序执行完毕后,CPU会恢复原来的执行位置,并恢复相关的寄存器状态。此时,中断处理结束,程序继续执行原来的指令。

需要注意的是,中断处理过程中,为了避免出现冲突或数据损坏,通常会在中断服务子程序中禁用其他中断,直到当前中断处理完毕再重新使能其他中断。这样可以确保中断处理的稳定性和可靠性。

对于char型变量 a,写两段代码分别将 a的 bit 6置1和置0

a |= (1 << 6);
//该代码使用了位运算中的按位或操作符 | 和左移位操作符 <<。首先,将数字 1 左移 6 位,得到一个只有第 6 位为 1、其余位均为 0 的数。然后,通过按位或操作符 | 将 a 的第 6 位设置为 1。
​
a &= ~(1 << 6);
//该代码使用了位运算中的按位与操作符 &、取反操作符 ~ 和左移位操作符 <<。首先,将数字 1 左移 6 位,得到一个只有第 6 位为 1、其余位均为 0 的数。然后,通过取反操作符 ~ 对这个数进行取反,得到一个只有第 6 位为 0、其余位均为 1 的数。最后,通过按位与操作符 & 将 a 的第 6 位设置为 0。

请写一段代码,可以将输入为”0.0.0.0”—“255.255.255.255”的字符串转换为int型整数数组。 输入:”255.255.255.255” 输出:255 255 255 255

#include <stdio.h>
char str[] = "255.255.255.255";
int num[4] = {0};

int main()
{
	int i = 0;
	scanf("%s.%s.%s.%s",str);
	sscanf(str,"%d.%d.%d.%d", &num[0], &num[1], &num[2], &num[3]);
	for (i = 0; i < 4; i++)
	{
		printf("%d\n", num[i]);
	}
	return 0;
}

已知单片机内置12位精度的ADC,单片机工作电压3.3V,ADC基准电压3V,请计算ADC采样的电压最小分辨率是多少?如果单片机ADC输入口电压为1V,则采样得到的值是多少?

ADC的最小分辨率取决于ADC的分辨率和参考电压。在这里,ADC的分辨率为12位,即可以表示212=4096212=4096个离散的电平值。参考电压为3V,因此ADC采样的电压最小分辨率为:

3/2^12=0.732mV

如果单片机ADC输入口电压为1V,则采样得到的值为:

1/3.3×2^12≈1240

注意,这是一个近似值,因为实际上ADC采样的电压值可能略微高于1V或略微低于1V,取决于ADC的精度和噪声水平。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值