单片机启动方式
-
从Flash启动,将Flash地址0x08000000映射到0x00000000
-
从SRAM启动,将SRAM地址0x20000000映射到0x00000000
-
从系统存储器启动,将系统存储器地址0x1FFFF000映射到0x00000000,通过uart1烧录程序,boot0置0,boot1置1
指针SP指向哪里
启动方式不同,指向地址不同,从Flash启动,SP指向0X0800 0000,从SRAM启动SP指向0X2000 0000
数组和链表的区别
数组和链表是两种常见的数据结构,它们在存储和访问数据上有一些区别。
-
存储方式:数组是一种连续存储的数据结构,它将元素存储在一段连续的内存空间中,而链表则是通过每个节点中保存下一个节点的地址来实现存储。
-
插入和删除操作:对于数组,插入和删除操作可能需要移动其他元素以保持连续性,这样的操作时间复杂度为O(n);而对于链表,插入和删除操作只需要调整节点的指针指向,时间复杂度为O(1)。
-
访问元素:数组可以通过索引直接访问元素,时间复杂度为O(1);而链表需要从头节点开始遍历,直到找到目标节点,时间复杂度为O(n)。
-
空间复杂度:数组的空间复杂度是固定的,与元素个数成正比;而链表的空间复杂度是动态的,根据实际需要分配。
-
扩容:对于数组,如果存储空间不够,需要重新分配更大的空间,并将原有元素复制到新的空间中;而链表可以动态地分配节点,不需要连续的内存空间。
综上所述,数组适合随机访问元素,但插入和删除操作较慢;链表适合频繁插入和删除操作,但访问元素较慢。在实际应用中,根据具体需求选择合适的数据结构。
存储区域介绍
区域 | 作用 |
---|---|
内存栈区 | 存放局部变量名 |
内存堆区 | 存放new或者malloc出来的对象 |
常数区 | 存放局部变量或者全局变量的值 |
静态区 | 用于存放全局变量或者静态变量 |
代码区 | 二进制代码 |
全局区(静态区) | 已初始化的全局变量 |
通信协议
TTL电平
-
全双工
-
5V系统,逻辑1:2.4v-5v
-
逻辑0:0v-0.5v
-
-
信号0对应0v,信号1对应3.3v或者5v,与单片机、SOC的IO电平兼容
-
通信距离较短——大概为几米
同步
两个通信的机器时钟频率一致
USART
-
异步通信
-
全双工
-
低速(115200bps=14.4kb/s,9600bps=1.2kb/s)
-
TTL电平
-
经典电路
IIC
-
同步通信
-
半双工
-
低速(标准模式S=100kbps=12.5kb/s,快速模式F=400kpbs=50kb/s,高速模式HS=3.4Mbps=435kb/s,超快速模式=5Mbps=525kb/s)
-
通信距离——理想距离15米,实际一般就几米,通常板间通信
-
TTL电平
-
经典电路
SPI通信
-
同步
-
高速(50Mbps=5.9mb/s)
-
通信距离——一般15cm以下
-
全双工
-
TTL电平
-
经典电路
为什么SPI比IIC快?
-
因为SPI协议更简单,没有仲裁,总线等判断
-
SPI通信距离更短一些,硬件决定可以把时钟速度拉高
RS232
-
异步
-
低速(50b/s,75b/s,110b/s,150b/s,300b/s,600b/s,1200b/s,2400b/s,4800b/s,9600b/s,19200b/s)
-
通信距离——RS232理论距离最大为几十米
-
节点数量——1对1
-
全双工
-
RSR232电平
-
规定逻辑1电平:-15v~-5v,逻辑0电平为+5v~+15v,选用该电气标准的目的在于提高抗干扰能力,增大通信距离。RS232的噪声容限为2V,接收器将能识别高至+3V的信号作为逻辑“0”,将低到-3V的信号作为逻辑“1”
-
由于RS232采用串行传送方式,并且将微机的TTL电平转换为RS-232C电平,其传送距离一般可达30m。若采用光电隔离20mA的电流环进行传送,其传送距 离可以达到1000m。
-
-
经典电路
RS485
-
异步
-
低速——RS485的最高数据传输速率为10Mbps=1250kb/s
-
长距离-RS485的最大通信距离为约1200m
-
半双工
-
节点数量——RS-485在总线上是允许连接多达128个收发器。
-
485电平
-
(逻辑1:+2V~+6V 逻辑0:-6V~2V)这里的电平指AB两线间的电压差。
-
RS485是一种串口接口标准,为了长距离传输采用差分方式传输,传输的是差分信号,即通过AB两根线的电压差作为电平信号。差分信号能有效地抵御外界因素的干扰,因为干扰对两根线影响是一样的,两根线的电压差不变,信号传递也就不会受干扰。
-
通信差分电压是A高B低
-
经典电路
-
CAN
-
ISO11898标准
-
低速——最高数据传输速率1Mbps=128kb/s
-
超长距离——CAN是半双工的。收发数据要分时进行。不管CAN网络上挂多少设备,在同一时刻只能有1个发送数据。如果有多个需要同时发送则只有优先级别高的先发送,其它等待。
-
节点数量
-
理论上最多有127个节点 总线两端120R电阻
-
-
CAN总线电平
-
逻辑1是隐性电平,CAN_High和CAN_Low都是2.5V,电位差为0V, 逻辑0是显性电平,CAN_Hight为3.5V,CAN_Low为1.5V,电位差是2V
-
传输的是差分信号,即通过CAN_H、CAN_L两根线的电压差作为电平信号。
-
-
经典电路
linux——相关基础概念
Linux内核的架构和主要组成部分
Linux内核是操作系统的核心部分,它负责管理计算机硬件和提供基本的系统功能。下面是Linux内核的一些主要架构和组成部分:
-
进程管理:Linux内核负责管理系统中的进程。它负责创建、销毁和调度进程,以及为进程分配资源和处理进程间的通信。
-
内存管理:Linux内核负责管理系统的内存资源。它跟踪系统中的内存使用情况,并为进程分配内存空间。内存管理的一部分是虚拟内存系统,它允许每个进程拥有独立的虚拟地址空间。
-
文件系统:Linux内核支持各种文件系统,包括常见的ext4、NTFS等。它负责管理文件和目录,以及处理文件的读写操作。
-
设备驱动程序:Linux内核通过设备驱动程序与硬件设备进行通信。每个设备都有对应的驱动程序,负责与设备进行交互和控制。
-
网络协议栈:Linux内核支持各种网络协议,如TCP/IP协议栈。它负责处理网络通信,包括数据包的传输、路由和网络连接的管理。
-
系统调用接口:Linux内核提供系统调用接口,允许用户空间程序与内核进行交互。通过系统调用,用户程序可以请求内核执行特定的操作,如文件读写、进程创建等。
以上只是Linux内核的一部分架构和组成部分,它还包含许多其他功能和模块,如中断处理、调度器、文件系统驱动等。Linux内核是一个开源项目,具有可扩展性和灵活性,可以根据需求进行定制和修改。
什么是设备树(Device Tree)?它在嵌入式Linux开发中的作用是什么?
设备树(Device Tree)是一种用来描述硬件设备信息的数据结构,广泛应用于嵌入式Linux系统的开发中。它的作用是将硬件设备的信息和配置与操作系统内核分离开来,使得内核在不同的硬件平台上能够灵活适配和配置。
在传统的嵌入式系统中,硬件设备的信息通常是通过编译内核时直接硬编码进内核代码中的。这样的设计存在一些问题,例如:
-
不利于跨平台移植:硬件信息的硬编码使得内核无法自动适配不同的硬件平台,需要针对每个平台进行修改和重新编译。
-
不利于硬件配置的修改:如果要对硬件配置进行修改,需要重新编译和烧录内核,这样的过程较为繁琐。
-
不利于设备的动态添加和移除:硬编码的硬件信息不适合处理设备的动态添加和移除,需要手动修改内核代码。
为了解决以上问题,设备树被引入到嵌入式Linux开发中。设备树是一个描述硬件设备的层次结构的文本文件,它将硬件设备的信息、配置和连接关系以一种结构化的方式进行描述。设备树并不包含设备的驱动程序代码,而是提供了一个通用的描述设备的方式。
嵌入式Linux系统在启动过程中,会从固定的位置(通常是存储器中)加载设备树文件,并将设备树传递给内核。内核在启动时会解析设备树,并根据设备树的描述来动态地进行硬件设备的初始化和配置。这样,内核就可以在不同的硬件平台上运行,而无需修改内核代码,只需要使用适当的设备树文件。
设备树除了描述硬件设备的信息外,还可以描述中断控制器、时钟、电源管理等系统级别的信息。它可以帮助系统开发人员更好地理解和配置硬件平台,提高系统的可移植性和灵活性。同时,设备树也为硬件厂商提供了一种标准的、与操作系统独立的硬件描述方式,方便供应链管理和系统集成。
总之,设备树在嵌入式Linux开发中的作用是将硬件设备的信息和配置与操作系统内核分离开来,提高系统的可移植性和灵活性。它是一个通用的描述硬件的方式,使得内核可以在不同的硬件平台上进行适配和配置。
如何编写一个简单的设备树文件来描述一个GPIO设备?
编写设备树文件描述一个GPIO设备需要以下步骤:
-
打开文本编辑器,创建一个新的设备树文件,以
.dts
或.dtsi
为扩展名。 -
在文件开头,添加设备树文件的描述信息,例如:
/dts-v1/;
/include/ "path/to/include/file.dtsi";
/ {
compatible = "manufacturer,model";
description = "GPIO device example";
};
这里的manufacturer
和model
可以替换为相应的设备制造商和型号。
-
添加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
可以替换为设备制造商的名称。
-
解释上述节点的各个属性:
-
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电平是否为低电平触发。
-
-
保存设备树文件并编译生成设备树二进制文件(
.dtb
)。
以上是一个简单的GPIO设备的设备树文件示例。根据实际需求,你可能需要添加更多属性或节点来描述设备的其他特性。
详细描述一下嵌入式系统的启动过程
嵌入式系统启动过程主要包括硬件初始化、引导加载、内核启动和用户空间启动等多个阶段。下面是嵌入式系统启动过程的详细描述:
-
硬件初始化:此阶段负责对硬件进行初始化,包括CPU、内存、外设等。其中,最重要的硬件初始化包括时钟初始化、内存控制器初始化、串口初始化和Flash初始化等。在此阶段完成后,CPU进入到其默认的复位状态。
-
引导加载:此阶段负责启动加载程序(Boot Loader)从非易失性存储器(如SPI Flash、NOR Flash或MMC/SD卡等)中加载内核镜像到RAM中,并将控制权交给内核启动代码执行。其中,引导加载可以通过串口、USB、以太网或无线网络接口等多种方式进行。
-
内核启动:此阶段负责解压缩内核镜像、初始化内核数据结构、初始化驱动程序、建立页表等基本操作。其中,内核启动流程包括以下几个步骤:
-
解压缩内核镜像:内核镜像一般被压缩为gzip、bzip2或xz等格式,此阶段需要对内核镜像进行解压缩。
-
初始化内核数据结构:此阶段负责初始化内核的数据结构,包括进程列表、文件系统、网络协议栈等。
-
初始化驱动程序:此阶段负责初始化各种硬件驱动程序,包括串口、网卡、USB、存储设备等。
-
建立页表:此阶段负责建立页表,为虚拟地址和物理地址之间建立映射关系,从而使内核能够访问物理内存。
-
-
用户空间启动:此阶段负责启动用户空间应用程序,包括系统服务、用户应用程序等。其中,用户空间应用程序需要通过init进程启动,并依次启动各个服务和应用程序。
-
用户交互:此阶段负责与用户进行交互,包括启动桌面、提供终端命令行界面等。在此阶段,用户可以使用各种工具和应用程序,从而满足自己的需求。
以上是嵌入式系统启动过程的详细描述。有了对于嵌入式系统启动流程的充分掌握,可以加强对于嵌入式系统工作原理的理解从而更好地实现它。
Linux——常见linux命令行工具
Linux命令行工具是Linux操作系统的核心,主要用于与操作系统进行交互和管理。下面列举一些常用的Linux命令行工具:
-
ls:查看当前目录下的文件列表。
$ ls
-
cd:切换当前目录到指定目录。
$ cd /path/to/directory
-
pwd:显示当前所在的目录。
$ pwd
-
cp:复制文件或目录。
$ cp source_file destination_directory
-
mv:移动文件或目录,也可用于重命名文件或目录。
$ mv old_file_name new_file_name
-
rm:删除文件或目录。
$ rm file_name
-
mkdir:创建一个新的目录。
$ mkdir new_directory
-
rmdir:删除指定的空目录。
$ rmdir empty_directory
-
touch:创建一个新的空文件或改变现有文件的时间戳。
$ touch file_name
-
cat:查看文件内容。
$ cat file_name
-
less:分页显示文件内容。
$ less file_name
-
grep:搜索文件中的文本。
$ grep "search_text" file_name
-
find:按照指定条件查找文件。
$ find /path/to/search -name "file_name_pattern"
-
ps:列出当前运行的进程。
$ ps -ef
-
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单片机中,中断处理的过程如下:
-
中断向量表初始化:在程序运行之前,需要将中断向量表(Interrupt Vector Table)初始化。中断向量表是一个存储器区域,其中记录了各个中断源对应的中断服务子程序的入口地址。
-
中断源配置和使能:通过设置相应的寄存器,选择要使能的中断源,并设置中断触发条件,例如上升沿触发、下降沿触发等。
-
中断服务子程序编写:为每个中断源编写对应的中断服务子程序(ISR,Interrupt Service Routine)。中断服务子程序是一段特殊的代码,用于具体处理中断事件。
-
中断优先级设置:为不同的中断源设置优先级,以确定中断优先级的高低顺序。在多个中断同时发生时,会根据优先级的设定确定先后顺序。
-
中断发生:当一个中断事件发生时,CPU会暂停当前执行的指令,保存相关的寄存器状态并跳转到对应中断源的中断服务子程序。
-
中断服务子程序执行:CPU开始执行中断服务子程序,处理中断事件。在执行完中断服务子程序后,可以根据需要进行相关的操作,例如读取数据、清除中断标志等。
-
中断结束:中断服务子程序执行完毕后,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的精度和噪声水平。