ARM Developer Suite(ADS)作为ARM官方推出的集成开发环境,至今仍是许多传统嵌入式项目的参考标准。在实际项目选型时,我们需要综合考虑以下要素:
经验分享:在量产项目中,我们通常会维护两套工程配置——一套用于模拟器快速验证,一套用于实际硬件调试。两者的scatter-loading内存映射文件需要分别配置。
以ADS 1.2为例,环境搭建的关键步骤:
bash复制set PATH=%PATH%;C:\ADSv1_2\Bin
set ARMINC=C:\ADSv1_2\Include
makefile复制CC = armcc -cpu ARM7TDMI -littleend -apcs /noswstackcheck
AS = armasm -cpu ARM7TDMI -li
LD = armlink -info totals -map -symbols
-dwarf2参数生成标准调试信息-fk选项指定汇编器包含路径-noautoinline避免优化干扰ARM-Thumb Procedure Call Standard定义了函数调用时的寄存器使用规则:
| 寄存器 | 用途 | 保存责任 |
|---|---|---|
| R0-R3 | 参数传递/返回值 | 调用者保存 |
| R4-R8 | 变量寄存器 | 被调者保存 |
| R9 | 平台特定(通常为SB) | 视情况而定 |
| R10 | 栈限制指针(SL) | 被调者保存 |
| R11 | 帧指针(FP) | 被调者保存 |
| R12 | 临时寄存器(IP) | 调用者保存 |
| R13 | 栈指针(SP) | 被调者保存 |
| R14 | 链接寄存器(LR) | 调用者保存 |
| R15 | 程序计数器(PC) | 系统管理 |
关键实现细节:
ARM/Thumb代码交互的三种实现方式:
armasm复制; ARM调用Thumb函数的veneers
CODE32
call_thumb_func
LDR PC, =thumb_func+1 ; +1表示Thumb状态
CODE16
thumb_func
BX LR ; 返回ARM状态
c复制#pragma thumb
void thumb_function() { /*...*/ }
#pragma arm
void arm_function() { /*...*/ }
c复制__attribute__((naked)) void mode_switch() {
__asm("BX R0"); // R0最低位决定处理器状态
}
典型嵌入式系统的内存布局示例:
code复制ROM_LOAD 0x00000000 0x00400000
{
ROM_EXEC 0x00000000 0x00200000
{
startup.o (RESET, +First)
* (+RO)
}
RAM_EXEC 0x20000000 0x00200000
{
* (+RW, +ZI)
}
ARM_LIB_HEAP 0x20200000 EMPTY 0x00080000 {}
ARM_LIB_STACK 0x20280000 EMPTY -0x00004000 {}
}
关键技巧:
+First确保复位向量位于起始位置EMPTY创建未初始化内存区域ALIGN指令保证特定对齐要求OVERLAY实现代码覆盖技术ROPI(只读位置无关)实现方案:
armasm复制LDR R0, =_start ; 绝对地址加载
ADR R1, _start ; 相对PC的地址
编译器配置参数:
code复制armcc --ropi --rwpi # 启用位置无关特性
armlink --ropi --rwpi # 链接时保持特性
动态加载注意事项:
调试信息生成配置对比:
| 选项 | 优点 | 缺点 |
|---|---|---|
| -g | 完整调试信息 | 显著增大文件体积 |
| -g -dwarf2 | 标准格式 | 需要调试器支持 |
| -g -compress | 减小体积 | 增加调试器加载时间 |
| --debug=line | 仅保留行号信息 | 无法查看变量 |
实用调试命令:
bash复制fromelf --text -c image.axf > disasm.txt # 反汇编
fromelf --symbols image.axf > syms.txt # 符号表导出
实现自定义semihosting的示例:
c复制void _sys_exit(int code) {
__asm {
MOV R0, #0x18 // SYS_EXIT编号
LDR R1, =code
SWI 0x123456
}
while(1);
}
常用semihosting调用编号:
| 服务号 | 功能 | 参数约定 |
|---|---|---|
| 0x01 | SYS_OPEN | R1=文件名指针 |
| 0x02 | SYS_CLOSE | R1=文件句柄 |
| 0x03 | SYS_WRITE | R1=文件句柄 |
| 0x04 | SYS_READ | R2=数据长度 |
| 0x05 | SYS_SEEK | R1=文件句柄 |
| 0x06 | SYS_FLEN | R1=文件句柄 |
在ADS中集成RTOS的典型步骤:
armasm复制IMPORT __main
IMPORT rtos_entry
Reset_Handler
BL hardware_init
BL rtos_entry ; 跳转到RTOS入口
BL __main ; 传统应用入口
c复制#pragma import(__use_realtime_heap)
void *rtos_malloc(size_t size) {
return __rt_alloc_realtime_heap(size);
}
c复制#define IRQ_PRIORITY 0xC0 // 优先级位[7:6]
void configure_interrupts() {
__asm {
MRS R0, CPSR
BIC R0, R0, #0xC0
ORR R0, R0, #IRQ_PRIORITY
MSR CPSR_c, R0
}
}
关键优化策略:
-Otime -Oinline优化代码大小__pure属性声明__irq规范c复制#pragma pack(push, 4)
typedef struct {
uint32_t id;
uint16_t data[4];
} __packed sensor_frame;
#pragma pack(pop)
在长期项目维护中,我们发现ARM7TDMI内核的写缓冲只有16字节,因此DMA传输配置为16字节倍数时性能最佳。这个经验也适用于后续Cortex-M系列处理器。