网站建设的概念,网站开发团队 组建,个人网站域名所有权,抖音91ARM的一些常见问题
ARM 体系结构的主要特点是什么#xff1f;
精简指令集 (RISC)#xff1a;ARM 采用 RISC 结构#xff0c;指令集较小且简单#xff0c;执行效率高。相比于复杂指令集 (CISC)#xff0c;RISC 更强调每条指令的执行速度。低功耗设计#xff1a;ARM 处理…ARM的一些常见问题
ARM 体系结构的主要特点是什么
精简指令集 (RISC)ARM 采用 RISC 结构指令集较小且简单执行效率高。相比于复杂指令集 (CISC)RISC 更强调每条指令的执行速度。低功耗设计ARM 处理器通过简化指令集和减少每条指令的处理步骤达到了低功耗的效果适用于移动设备和嵌入式系统。Thumb 和 Thumb-2 指令集ARM 支持 16 位 Thumb 指令集它能够提高代码密度特别适合对内存有限的嵌入式系统。多核和对称多处理 (SMP)现代 ARM 处理器支持多核架构并且多核处理器可以并行处理多个任务。向量浮点单元 (VFP) 和 NEONARM 处理器通常集成 VFP向量浮点运算单元和 NEON 技术来加速多媒体和信号处理任务。
ARM 的中断处理机制是怎样的 ARM 的中断机制是通过中断向量表来处理的不同的中断类型有不同的优先级。主要的中断处理流程如下
中断请求 (IRQ)常规外设或定时器产生的中断请求。快速中断 (FIQ)高优先级的中断请求常用于紧急任务处理。FIQ 有独立的寄存器组来加快中断响应速度。中断向量表中断向量表存储在一个固定的地址如地址 0x00000000 或 0xFFFF0000每种中断类型有一个固定的入口地址处理器通过跳转到相应的中断向量表位置来响应中断。嵌套中断在 ARM 架构中通过管理中断屏蔽和优先级可以支持嵌套中断即在处理中断的过程中可以响应更高优先级的中断。
ARM Cortex-M 系列的主要特点是什么 ARM Cortex-M 系列是专为嵌入式系统设计的主要特点包括
基于 ARMv7-M 或 ARMv8-M 指令集。低功耗非常适合电池供电的嵌入式设备。支持快速中断响应通过 NVIC嵌套向量中断控制器实现高效的中断管理最多支持 240 个中断通道。内置调试支持如 DWT数据观察点和跟踪和 ITM指令跟踪宏单元。浮点运算单元 (FPU)Cortex-M4 和 Cortex-M7 支持硬件浮点运算适合需要大量数值计算的应用。
ARM 处理器的总线接口有哪些 ARM 处理器主要使用以下总线接口技术
AMBA (Advanced Microcontroller Bus Architecture)AMBA 是 ARM 提出的总线协议常用的总线类型包括AHB (Advanced High-performance Bus)主要用于高性能外围设备和高速数据传输。APB (Advanced Peripheral Bus)用于低速外围设备如 GPIO、串口等。AXI (Advanced eXtensible Interface)用于高性能内存和外设之间的数据传输支持并行和流水线操作。
如何在 ARM 体系结构中实现内存映射I/O 内存映射I/O 是 ARM 处理器中外设与内存地址空间共享的一种机制具体步骤如下
内存映射每个外设都分配有固定的内存地址处理器可以通过读写这些内存地址来控制外设。地址分配不同外设的地址范围通常在芯片手册中指定。例如GPIO 的控制寄存器可能映射到一个特定的地址段。访问外设通过普通的 ldr 和 str 指令ARM 处理器可以访问这些内存映射的外设。
ARM中的异常处理机制是什么 ARM 处理器的异常处理机制与中断类似但处理的是软件错误或其他需要特殊处理的事件。常见的异常包括
复位异常 (Reset)设备复位时触发。未定义指令异常 (Undefined Instruction)执行未定义的指令时触发。数据预取异常 (Data Abort/Prefetch Abort)发生内存访问错误或非法地址访问时触发。软件中断 (SWI)用于用户态调用内核服务类似于系统调用。
ARM 的虚拟内存管理是如何实现的 ARM 处理器支持虚拟内存管理通过以下方式实现
MMU (Memory Management Unit)用于将虚拟地址转换为物理地址并提供访问控制。分页机制ARM 使用分页来管理内存将内存划分为固定大小的页虚拟地址通过页表映射到物理地- 址。TLB (Translation Lookaside Buffer)TLB 用于缓存最近使用的页表项减少内存访问时的转换开销。
安装交叉编译工具
拷贝 gcc-4.6.4.tar.xz 到 Linux 环境并解压
tar -xvf gcc-4.6.4.tar.xz将整个 GCC 目录安装到系统 方法将 gcc-4.6.4/bin 目录添加到 PATH 环境变量修改 ~/.bashrc 文件添加以下内容在文件底部添加 export PATH$PATH:/home/lsf/source/gcc-4.6.4/binsource ~/.bashrc 使修改立即生效 测试安装 终端中输入 arm-none- 并使用 Tab 键自动补齐检查工具是否可用注意 若是 64 位系统可能需要安装 32 位支持库
sudo apt-get install lib32ncurses5 lib32z1接口技术
CPU: 狭义上指的是 核, 广义上指的是 整个芯片 狭义上的 CPU指处理器的 核心 (core) 部分负责执行指令、处理数据和控制计算过程。它包括了运算逻辑单元 (ALU)、寄存器、控制单元等核心的任务是执行软件的指令集。广义上的 CPU可以包括整个芯片除了核心以外还包括缓存 (cache)、总线控制器、内存管理单元 (MMU) 等部件。它不仅仅是执行指令的逻辑单元还包括一些周边的控制逻辑和接口。 SOC : system on chip 片上系统, 核接口 SoC 可以看作是一个集成了 CPU、接口以及其他外设的单芯片系统适合于嵌入式系统和移动设备应用 裸机编程: 没有操作系统的编程,比如 单片机 STM32基于 ARM Cortex-M 核心广泛应用于工业控制和消费电子。51 单片机经典的8位单片机适合学习与小型应用。
裸机编程示例图 - 核心流程 ldr 指令CPU 使用 ldrload register指令从存储器或外设的寄存器读取数据到寄存器。 str 指令CPU 使用 strstore register指令将寄存器的数据写回存储器或外设的寄存器中。 - 硬件,分为两部分 核心板外设板 - 核心板能让CPU正常运行的最小系统主要组件CPU,flash,RAM,电源,串口,晶振 - 外设板: 外设板通常包含了许多外部设备这些设备通过各种接口与核心板的 CPU 连接用于扩展系统的功能。例如扬声器、SD卡接口、ADC、HDMI - 核心板和外设板通过接口如 GPIO、I2C、SPI、UART 等进行连接和通信。
操作硬件一般步骤以 LED 为例
从电路板上找到该硬件 找到你想控制的硬件设备。例如LED2记下该设备的标号如 LED2方便后续在电路图和芯片手册中查找相关信息 打开电路图找到该硬件的连接信息 打开硬件的 电路原理图找到 LED2 相关的部分在电路图中查找 LED2 的位置并查看该 LED 是如何连接到 CPU 的管脚上。例如你可能会发现 LED2 连接到 CPU 的 GPX2_7 引脚 阅读芯片手册 (datasheet)找到相关控制寄存器 打开对应的 芯片手册 (datasheet)找到涉及控制 GPX2_7 引脚的章节通常是在 GPIO通用输入输出控制器章节中GPX2_7 表示 GPIO X 组的第 7 个引脚你需要找到该引脚的寄存器配置方式 寄存器的相关配置
GPX2CON引脚配置寄存器配置该引脚的模式输入/输出。
寄存器地址0x11000C40。
操作说明设置 [31:28] 位为 0001表示将 GPX2_7 设置为输出模式。
GPX2DAT数据寄存器用于控制引脚的高低电平控制开/关。
寄存器地址0x11000C44。
操作说明[7] 位控制 GPX2_7 的输出状态。设置该位为 1 则为高电平LED 点亮设置为 0 则为低电平LED 熄灭。编写代码进行编程和测试 代码在start.s中写通过Makefile文件编译取bin文件烧录进去开发板即可
.text通过 ARM 裸机编程直接控制硬件具体任务是通过按键控制 LED 的亮灭即当按键按下时点亮 LED松开按键时熄灭 LED。配置 GPX2_7 为输出模式
ldr r0,0x11000c40 加载 GPX2CON 地址
ldr r2,[r0] 读取 GPX2CON 内容到 r2
bic r2,r2,#0xF0000000 清除 GPX2_7 的控制位
orr r2,r2,#0x10000000 设置 GPX2_7 为输出模式 (0001)
str r2,[r0] 将修改后的值写回 GPX2CON 配置 K2 为输入模式 (GPX1_1)
ldr r0,0x11000c20 加载 GPX1CON 地址
ldr r2,[r0] 读取 GPX1CON 内容到 r2
bic r2,r2,#0xF0 清除 GPX1_1 的控制位
str r2,[r0] 直接清除后默认是输入模式 (0000)loop:ldr r0,0x11000c24 加载 GPX1DAT 地址 (检测按键输入)ldr r2,[r0] 读取 GPX1DATand r2,r2,#2 检查 GPX1_1 状态检查按键是否按下cmp r2,#0bleq light_led_fun 如果按下按键调用点亮 LED 函数blne off_led_fun 如果没有按下调用熄灭 LED 函数bl sdelay 延时b loop 继续循环 点亮 LED
light_led_fun:ldr r0,0x11000c44 加载 GPX2DAT 地址ldr r2,[r0] 读取 GPX2DATorr r2,r2,#0x80 设置 GPX2_7 为高电平点亮 LEDstr r2,[r0] 写回 GPX2DATmov pc,lr 返回 熄灭 LED
off_led_fun:ldr r0,0x11000c44 加载 GPX2DAT 地址ldr r2,[r0] 读取 GPX2DATbic r2,r2,#0x80 清除 GPX2_7 高电平熄灭 LEDstr r2,[r0] 写回 GPX2DATmov pc,lr 返回 延时函数
sdelay:ldr r10,0xFFFF 加载一个较大的值用于延时
delay_loop:sub r10,r10,#1 自减cmp r10,#0 检查是否达到 0bgt delay_loop 如果没有到 0继续延时mov pc,lr 返回
.end GPIO章节
概述
GPIOGeneral Purpose Input/Output通用输入输出端口 是嵌入式系统中用于控制和管理芯片外部管脚的模块。GPIO 模块允许用户根据需要将芯片的管脚配置为输入或输出并通过这些管脚与外部设备进行交互例如控制 LED、读取传感器信号等。多功能引脚GPIO 引脚可以配置为多个功能不仅限于通用输入输出还可以配置为串口、USB、I2C、SPI 等特定功能。
GPIO 管脚的分组管理
为了有效管理芯片上众多管脚GPIO 管脚通常按组分类不同组的 GPIO 管脚通过寄存器进行独立控制。例如 GPA组 A 管脚如 GPA_0, GPA_1。 GPB组 B 管脚。 GPX1, GPX2组 X 的第 1 和第 2 组管脚。 GPD组 D 管脚依次类推。每组 GPIO 通常包含 16 个引脚可以独立配置为输入或输出每组都有自己的控制寄存器。
GPIO 的基本功能
输出功能输入功能
GPIO的其他功能
串口UART用于串行通信。USB 接口用于外设通信。I2C/SPI 接口用于与传感器或其他外部设备的同步通信。中断引脚部分 GPIO 可以配置为中断引脚当管脚电平变化时触发中断信号通知处理器做出响应。
使用示例在资源文件里面开发板为fs4412
串口通信
串口Serial是一种常见的通信方式它通过串行传输数据即一次一位地发送或接收数据。相比之下并口Parallel一次可以发送多个数据位但由于并口需要更多的引脚所以在嵌入式系统中较少使用。
基本概念
串口控制器负责管理串口通信的硬件模块控制数据的发送与接收。串行通信数据按位顺序依次传输通常用于设备之间的数据交换常见于嵌入式系统、传感器等应用。并行通信数据一次性发送多位数据虽然速度快但需要更多的引脚和布线较少在现代嵌入式系统中使用。
缺点
速度较慢相比并行通信串行通信速度较慢因为它一次只能传输一位数据。数据量较小串口通信一次发送的数据量较小通常是字节级别的数据。误码率问题由于信号在传输过程中可能受到噪声、干扰等影响导致数据误码。同步问题串行通信需要发送方和接收方的时钟保持同步如果不同步可能会导致数据丢失。
串口通信代码编写流程
找到板子上的串口硬件 在电路板上找到串口接口硬件例如 COM2 或 CON7这些端口通常连接到处理器的串行通信接口上用于发送和接收数据 在电路图中查找对应的 GPIO 引脚 根据硬件端口的编号如 COM2/CON7打开电路原理图查找该端口连接到芯片的哪些 GPIO 管脚。 例如在电路图中查找到 COM2 连接到芯片的 GPA1_0 和 GPA1_1 引脚。 将 GPIO 配置为串口模式 打开芯片的 datasheet数据手册查找 GPIO 章节找到对应的管脚配置寄存器并将这些 GPIO 管脚配置为串口模式。例如对于 GPA1_0 和 GPA1_1通过配置寄存器将其设置为串口的 TX发送和 RX接收引脚。
GPA1CON 0x11400020; // GPA1CON 寄存器地址
GPA1CON ~(0xFF); // 清除 GPA1_0 和 GPA1_1 的原有配置
GPA1CON | (0x22); // 配置 GPA1_0 和 GPA1_1 为串口模式 (0x2)配置串口寄存器 在 UART 串口章节 中找到串口控制器的相关寄存器配置波特率、停止位、数据位等参数。配置串口寄存器的步骤 ULCON2配置数据宽度、停止位、校验方式。UCON2配置接收和发送的模式轮询模式或中断模式。UTRSTAT2用于检测发送和接收状态判断是否可以继续发送或接收数据。UTXH2 和 URXH2发送和接收数据的寄存器。UBRDIV2 和 UFRACVAL2配置波特率。 波特率计算 根据波特率和系统时钟SCLK_UART需要配置波特率寄存器 UBRDIV2 和 UFRACVAL2。波特率公式 D I V _ V A L ( S C L K _ U A R T / ( 波特率 × 16 ) ) – 1 DIV\_VAL (SCLK\_UART / (波特率 × 16)) – 1 DIV_VAL(SCLK_UART/(波特率×16))–1 例如波特率为 115200 bpsSCLK_UART 为 100 MHz
DIV_VAL (100000000 / (115200 × 16)) - 1 54.253 - 1 53.253
UBRDIVn 53 // 整数部分
UFRACVALn 4 // 小数部分0.253 × 16 4串口与PC通信代码的编写
编写main.c
//嵌入式系统中发送 hello world 字符串
/*## GPIO章节 中将管脚配置为串口模式 GPA1CON 0x11400020 [3:0]0x2 [7:4]0x2## UART串口章节, 配置寄存器 波特率 停止位 数据值 校验位 ..... ULCON2 0x13820000 [1:0] 0x3 8bit data width[2] 0 1 stop bit[5:3]000 no parity[6] 0 no IRUCON2 0x13820004 [1:0]0x01 poll for recv [3:2]0x01 poll for sendUTRSTAT2 0x13820010 x[0] 1,接收buf有了数据 0-nodatax[1] ,1 可以继续发送了 0-数据还在发送中UTXH2 0x13820020 [7:0]data;URXH2 0x13820024 data[7:0];UBRDIV2 0x13820028 53UFRACVAL2 0x1382002C 4
*/// GPIO 和 UART 寄存器的地址定义
#define GPA1CON *(volatile long*)0x11400020 // GPA1CON GPIO 引脚控制寄存器
#define ULCON2 *(volatile long*)0x13820000 // ULCON2 串口控制寄存器设置数据宽度、校验位等
#define UCON2 *(volatile long*)0x13820004 // UCON2 串口控制寄存器设置发送和接收模式
#define UTRSTAT2 *(volatile long*)0x13820010 // UTRSTAT2 串口状态寄存器检查是否可以发送和接收
#define UTXH2 *(volatile long*)0x13820020 // UTXH2 串口发送数据寄存器
#define URXH2 *(volatile long*)0x13820024 // URXH2 串口接收数据寄存器#define UBRDIV2 *(volatile long*)0x13820028 // 波特率整数部分寄存器
#define UFRACVAL2 *(volatile long*)0x1382002C // 波特率小数部分寄存器// 初始化 UART 串口
void uart_init(void)
{// 配置 GPA1_0 和 GPA1_1 为串口模式GPA1CON GPA1CON ~0xF; // 清除 GPA1_0 的原有配置GPA1CON GPA1CON | (11); // 设置 GPA1_0 为 UART TX (串口发送)GPA1CON GPA1CON ~(0xF4); // 清除 GPA1_1 的原有配置GPA1CON GPA1CON | (15); // 设置 GPA1_1 为 UART RX (串口接收)// 配置 ULCON2设置为 8 位数据宽度、1 位停止位无校验ULCON2 ULCON2 | 0x3; // 设置数据宽度为 8 位ULCON2 ULCON2 ~(12); // 设置 1 个停止位ULCON2 ULCON2 ~(73); // 禁用校验位 (no parity)ULCON2 ULCON2 ~(16); // 禁用红外模式 (no IR)// 配置 UCON2设置为轮询模式UCON2 ~(3); // 清除原有配置UCON2 | 10; // 设置为轮询接收模式UCON2 ~(32); // 清除原有发送配置UCON2 | 12; // 设置为轮询发送模式// 设置波特率为 115200UBRDIV2 53; // 设置波特率的整数部分UFRACVAL2 4; // 设置波特率的小数部分
}// 发送单个字符
void putc(char ch)
{// 等待直到发送缓冲区为空while( ( UTRSTAT2 (11) ) 0 );// 向发送寄存器中写入数据UTXH2 ch;
}// 发送字符串
void puts(char *s)
{int i 0;// 循环发送字符串中的每个字符while(s[i]) {putc(s[i]); // 发送单个字符i;}
}// 简单的延时函数延时 ms 毫秒
void mysleep(int ms)
{while(ms--) {int num 0x1FFF/2; // 简单的延时循环while(num--);}
}// 主函数
void main(void)
{uart_init(); // 初始化 UART/*在 Linux 系统中\n 表示回车和换行在 Windows 系统中\n 表示换行\r 表示回到行首*/while(1) {puts(hello world\r\n); // 通过串口发送 hello world 字符串并换行mysleep(200); // 延时 200 毫秒}return;
}
编写start.S
目的是为嵌入式系统中的主程序如串口代码提供一个基本的运行环境。启动代码的作用是对处理器和堆栈等进行初始化并最终跳转到主程序的入口点。
.global delay1s 全局定义 delay1s 延时函数
.text 定义文本段代码段
.global _start 全局定义 _start 入口函数_start: 程序入口点程序从此开始执行b reset 跳转到 reset 处复位处理ldr pc, _undefined_instruction 加载 undefined_instruction 异常处理器的地址ldr pc, _software_interrupt 加载 software_interrupt 异常处理器的地址ldr pc, _prefetch_abort 加载 prefetch_abort 异常处理器的地址ldr pc, _data_abort 加载 data_abort 异常处理器的地址ldr pc, _not_used 加载未使用异常的处理器地址ldr pc, _irq 加载中断请求 (IRQ) 异常处理器的地址ldr pc, _fiq 加载快速中断 (FIQ) 异常处理器的地址_undefined_instruction: .word _undefined_instruction 定义未定义指令异常处理器的地址
_software_interrupt: .word _software_interrupt 定义软件中断异常处理器的地址
_prefetch_abort: .word _prefetch_abort 定义指令预取异常处理器的地址
_data_abort: .word _data_abort 定义数据访问异常处理器的地址
_not_used: .word _not_used 定义未使用异常处理器的地址
_irq: .word _irq 定义 IRQ 中断异常处理器的地址
_fiq: .word _fiq 定义 FIQ 快速中断异常处理器的地址reset:ldr r0,0x40008000 设置异常向量表基地址为 0x40008000mcr p15, 0, r0, c12, c0, 0 写入 Vector Base Address Register (VBAR)重新定位异常向量表init_stack:ldr r0,stacktop 将栈顶地址加载到 r0/******** svc 模式栈 ********/mov sp,r0 将栈顶指针加载到 spsvc 模式的栈sub r0,#128*4 减少 512 字节空间用于 IRQ 模式栈/******** irq 模式栈 ********/msr cpsr,#0xd2 切换到 IRQ 模式CPSR 位设置mov sp,r0 设置 IRQ 模式的栈指针sub r0,#128*4 减少 512 字节空间用于 FIQ 模式栈/******** fiq 模式栈 ********/msr cpsr,#0xd1 切换到 FIQ 模式mov sp,r0 设置 FIQ 模式的栈指针sub r0,#0 FIQ 模式使用 0 字节的栈空间/******** abort 模式栈 ********/msr cpsr,#0xd7 切换到 Abort 模式mov sp,r0 设置 Abort 模式的栈指针sub r0,#0 Abort 模式使用 0 字节的栈空间/******** undefined 模式栈 ********/msr cpsr,#0xdb 切换到 Undefined 模式mov sp,r0 设置 Undefined 模式的栈指针sub r0,#0 Undefined 模式使用 0 字节的栈空间/******** sys 和 usr 模式栈 ********/msr cpsr,#0x10 切换到用户模式 (SYS/USR)mov sp,r0 设置用户模式的栈指针 为用户模式分配 1024 字节的栈空间b main 跳转到 main 函数进入主程序delay1s: 定义 1 秒延时函数ldr r4,0x1ffffff 将一个大数加载到 r4用作计数器
delay1s_loop:sub r4,r4,#1 每次循环 r4 减 1cmp r4,#0 检查 r4 是否为 0bne delay1s_loop 如果 r4 不为 0继续循环mov pc,lr 返回调用函数.align 4 地址对齐/**** swi_interrupt handler ****/ 异常处理中断的处理器可以在此处实现.data 定义数据段stack:.space 4*512 为栈分配 512 字节的空间
stacktop:.word stack4*512 定义栈顶指针的初始位置.end 程序结束标志编写 Makefile 定义编译链接规则和清除不必要的东西 用于编译 ARM 裸机程序。它使用了 arm-none-linux-gnueabi 工具链来编译汇编和 C 代码链接生成 ELF 可执行文件并生成二进制文件。
# 目标名称 all 是默认目标当执行 make 时会首先执行该目标中的指令
all:# 使用 arm-none-linux-gnueabi-gcc 编译 start.S 文件为目标文件 start.o# -fno-builtin: 禁用 GCC 的内置函数避免使用标准库函数# -nostdinc: 不使用标准头文件适合裸机编程# -c: 仅编译为目标文件不进行链接arm-none-linux-gnueabi-gcc -fno-builtin -nostdinc -c -o start.o start.S# 使用 arm-none-linux-gnueabi-gcc 编译 main.c 文件为目标文件 main.o# 这里同样不使用内置函数和标准头文件适合裸机编程arm-none-linux-gnueabi-gcc -fno-builtin -nostdinc -c -o main.o main.c# 链接 start.o 和 main.o 目标文件生成 main.elf 可执行文件# -Tmap.lds: 使用自定义的链接脚本 map.lds 定义内存布局# main.elf: 最终生成的可执行文件arm-none-linux-gnueabi-ld start.o main.o -Tmap.lds -o main.elf# 使用 arm-none-linux-gnueabi-objcopy 将 ELF 文件转换为纯二进制文件 main.bin# -O binary: 指定输出格式为二进制文件arm-none-linux-gnueabi-objcopy -O binary main.elf main.bin# 使用 arm-none-linux-gnueabi-objdump 反汇编 ELF 文件生成汇编代码文件 main.dis# -D: 反汇编整个 ELF 文件# 反汇编的输出被保存到 main.dis 文件中便于阅读arm-none-linux-gnueabi-objdump -D main.elf main.dis# clean 目标用于清理生成的中间文件和最终文件
clean:# 删除所有备份文件、目标文件、ELF 文件、二进制文件和反汇编文件# -f: 忽略文件不存在的错误# *.bak: 删除所有扩展名为 .bak 的备份文件# *.o: 删除所有目标文件# main.elf: 删除生成的 ELF 可执行文件# main.bin: 删除生成的二进制文件# main.dis: 删除生成的反汇编文件rm -rf *.bak start.o main.o main.elf main.bin main.dis
加入map.lds map.lds 链接脚本 定义了程序在内存中的布局指定了代码段、数据段和未初始化数据段的起始地址和内容来源。这份脚本非常适用于嵌入式系统开发特别是当你需要精确控制程序的各个部分在内存中的位置时。代码如下
OUTPUT_FORMAT(elf32-littlearm, elf32-littlearm, elf32-littlearm)
# 指定生成文件的输出格式为 elf32-littlearm适用于 32 位 ARM 的小端格式
# elf32-littlearm 出现在三个位置分别对应目标文件、二进制文件和目标机器的 ELF 格式OUTPUT_ARCH(arm)
# 指定目标架构为 ARM确保链接器知道处理的是 ARM 处理器的指令集ENTRY(_start)
# 指定程序的入口地址为 _start这是程序执行的起始点通常定义在汇编文件 start.S 中SECTIONS
{. 0x40008000; # 设置程序在内存中的起始地址为 0x40008000通常是裸机编程中设置的 RAM 基地址# 所有代码段、数据段都从这个地址开始布局. ALIGN(4);# 将当前地址对齐到 4 字节边界以确保对齐要求满足 ARM 处理器的要求# 对齐有助于提高内存访问效率避免非对齐访问引发错误.text :{start.o(.text)# 将 start.o 文件中的 .text 段代码段加载到当前地址处*(.text)# 加载所有目标文件中的 .text 段代码段确保代码在合适的内存位置}. ALIGN(4);# 再次对齐到 4 字节边界以确保段之间的正确对齐.data : { *(.data) }# 将所有目标文件中的 .data 段数据段加载到当前地址# .data 段通常包含已初始化的全局变量和静态变量. ALIGN(4);# 再次对齐到 4 字节边界确保 bss 段的正确对齐.bss :{ *(.bss) }# 将所有目标文件中的 .bss 段未初始化的全局变量和静态变量加载到当前地址# .bss 段通常不占用文件大小它只分配未初始化数据所需的空间
}make生成bin二进制文件烧录进入开发板即可成功时会发送hello world 给PC