玳瑁的嵌入式日记---0923(ARM)
1.链接脚本的作用?各个段存放什么类型数据
SECTIONS
{. = 0x87800000;.text : {obj/start.o*(.text)}.rodata ALIGN(4) : {*(.rodata*)}.data ALIGN(4) : {*(.data)}__bss_start = .;.bss ALIGN(4) : {*(.bss) *(COMMON)}__bss_end = .;
}
链接脚本的整体作用
这个链接脚本定义了程序在内存中的布局,指定了各代码段和数据段的加载地址、排列顺序及对齐方式,最终指导链接器将多个目标文件合并为一个可执行文件。具体来说,它完成了以下工作:
- 设定程序的起始加载地址为
0x87800000
(通常是嵌入式系统中的 RAM 或 Flash 地址) - 规定了各段(.text、.rodata、.data、.bss)的排列顺序和对齐要求
- 定义了
__bss_start
和__bss_end
符号,用于标记.bss 段的起止位置(方便运行时初始化)
各段的具体含义及存储数据类型
.text 段
- 起始地址:
0x87800000
(通过. = 0x87800000
设定) - 包含内容:
- 优先存放
obj/start.o
(通常是启动文件,包含程序入口、中断向量表等关键代码) - 随后合并所有目标文件的
.text
段(*(.text)
)
- 优先存放
- 存储数据:程序的可执行指令(机器码),如函数实现、汇编指令等,只读可执行
- 起始地址:
.rodata 段
- 特性:
ALIGN(4)
表示按 4 字节对齐(提高访问效率) - 包含内容:
*(.rodata*)
表示合并所有目标文件的只读数据段 - 存储数据:只读常量,如字符串常量(
"hello"
)、const
修饰的全局变量等,只读不可修改
- 特性:
.data 段
- 特性:
ALIGN(4)
按 4 字节对齐 - 包含内容:
*(.data)
合并所有目标文件的已初始化数据段 - 存储数据:已初始化的全局变量和静态变量,如
int a = 10;
,可读可写
- 特性:
.bss 段
- 特性:
ALIGN(4)
按 4 字节对齐 - 包含内容:
*(.bss)
合并所有目标文件的未初始化数据段*(COMMON)
处理未初始化的全局变量(C 语言中未显式初始化的全局变量默认放在 COMMON 块)
- 存储数据:未初始化的全局变量和静态变量(如
int b;
),运行时会被自动初始化为 0,可读可写 - 特殊符号:
__bss_start = .;
标记.bss 段的起始地址__bss_end = .;
标记.bss 段的结束地址(这两个符号通常在启动代码中用于实现.bss 段的清零初始化)
- 特性:
总结
该脚本典型用于嵌入式系统(如 ARM 架构开发板),通过固定起始地址和规范段布局,确保程序能在指定内存空间正确加载和运行。各段按 .text
→.rodata
→.data
→.bss
的顺序排列,符合程序执行时的内存使用逻辑(代码→只读数据→已初始化数据→未初始化数据)。
2.编译过程需要哪些工具,分别什么作用?
交叉编译器工具链 (arm-linux-gnueabihf-)这是针对 ARM 架构的交叉编译工具链,用于在 x86 等主机平台上编译能在 ARM 架构设备上运行的程序。
gcc (交叉编译版本:arm-linux-gnueabihf-gcc)
- 作用:C 语言编译器
- 在这里负责将 C 源代码 (.c) 和汇编源代码 (.S) 编译为目标文件 (.o)
- 使用了
-Wall
(显示所有警告)、-nostdlib
(不链接标准库)、-c
(只编译不链接)等选项
ld (交叉编译版本:arm-linux-gnueabihf-ld)
- 作用:链接器
- 负责将所有目标文件 (.o) 链接成可执行的 ELF 格式文件 (.elf)
- 通过
-Timx6ull.lds
指定链接脚本,控制链接过程和内存布局
objcopy (交叉编译版本:arm-linux-gnueabihf-objcopy)
- 作用:目标文件转换工具
- 在这里将 ELF 格式的可执行文件 (.elf) 转换为二进制文件 (.bin)
- 使用
-O binary
指定输出格式为二进制,-S
移除符号表和重定位信息,-g
移除调试信息
objdump (交叉编译版本:arm-linux-gnueabihf-objdump)
- 作用:目标文件反汇编工具
- 用于将 ELF 格式文件反汇编,生成汇编代码文本文件 (.dis)
- 通过
-D
选项对所有段进行反汇编,方便调试和分析
这些工具共同完成了从源代码到可在 ARM 设备上运行的二进制文件的完整编译流程:源代码编译→目标文件链接→格式转换→反汇编分析。
3.led点灯程序需要进行哪些步骤?
一、硬件基础准备
- 硬件连接确认
- 确定 LED 与 MCU 引脚的连接:代码中使用
GPIO1_IO03
引脚(通过GPIO1->DR
操作第 3 位),需确保硬件上 LED 通过此引脚控制(通常 LED 串联限流电阻后连接到该引脚和地 / 电源)。 - 确认电路逻辑:代码中
led_on()
通过清除DR
寄存器第 3 位(输出低电平)点亮 LED,说明硬件采用 “低电平有效”(LED 另一端接电源)。
- 确定 LED 与 MCU 引脚的连接:代码中使用
二、软件实现步骤
1. 系统时钟初始化(clock_init()
)
- 作用:使能外设时钟,确保 GPIO 等模块能正常工作。
- 代码中通过设置
CCM
(时钟控制模块)的CCGR0~CCGR6
寄存器为0xFFFFFFFF
,全使能所有外设的时钟(简化操作,实际可按需使能)。
2. GPIO 引脚初始化(led_init()
)
- ① 配置引脚复用功能通过
IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0)
将物理引脚复用为 GPIO 功能(而非其他外设功能如 UART、SPI)。 - ② 配置引脚电气属性通过
IOMUXC_SetPinConfig(...)
设置驱动强度(代码中0x10B0
包含驱动强度等参数)、上下拉电阻、速度等硬件特性。 - ③ 配置 GPIO 为输出模式通过
GPIO1->GDIR |= (1 << 3)
设置GPIO1
的第 3 位为输出模式(GDIR
寄存器:1 = 输出,0 = 输入)。 - ④ 初始状态设置调用
led_off()
初始化 LED 为熄灭状态(设置GPIO1->DR
第 3 位为 1,输出高电平)。
3. 实现 LED 控制逻辑
- 亮灯(
led_on()
):GPIO1->DR &= ~(1 << 3)
清除第 3 位,输出低电平(根据硬件逻辑点亮 LED)。 - 灭灯(
led_off()
):GPIO1->DR |= (1 << 3)
置位第 3 位,输出高电平(LED 熄灭)。 - 闪烁(
led_toggle()
):GPIO1->DR ^= (1 << 3)
通过异或操作翻转第 3 位电平,实现亮灭切换。
4. 主程序循环(main()
)
- 初始化流程:先初始化时钟(
clock_init()
),再初始化 LED(led_init()
)和蜂鸣器(beep_init()
)。 - 主循环:通过
led_toggle()
和beep_toggle()
实现 LED 与蜂鸣器同步闪烁 / 鸣叫,led_delay()
提供简单延时控制频率。
三、编译与运行
- 交叉编译:使用
arm-linux-gnueabihf-gcc
等工具链编译,结合链接脚本(如imx6ull.lds
)生成可执行文件。 - 烧录运行:通过 J-Link 或 SD 卡等方式将程序烧录到 i.MX6ULL 开发板,复位后 LED 即按设定逻辑工作。
核心逻辑总结
LED 点灯的本质是 **“配置 GPIO 引脚为输出模式,并通过寄存器控制其高低电平”**,步骤可简化为:使能时钟 → 配置引脚复用 → 设定电气属性 → 配置为输出 → 控制电平高低
。代码中通过寄存器操作直接控制硬件,体现了嵌入式开发中 “直接操作底层外设” 的特点。