1 / 35

第四章 ARM 嵌入式系统的编程

第四章 ARM 嵌入式系统的编程. 4.1 ARM 汇编语言伪操作、宏指令和伪指令( P173 ). 一、基本概念. 1 、 伪操作: 为完成汇编程序做各种准备工作,只在汇编过程中起作用,一旦汇编结束,它的作用也随之结束。. 2 、宏指令: 是一段独立的程序代码,可插在源程序中。与子程序相似,但有本质不同。. 3 、伪指令: 在汇编时将被合适的指令代替。. 伪 操作、宏指令一般与编译器有关,常用 ARM 编译开发环境有 2 种: GNU (基于 Embest IDE 环境)和 ADS ( ARM 公司提供). 二、 GNU 编译环境下的常用 ARM 伪操作和宏指令.

annona
Download Presentation

第四章 ARM 嵌入式系统的编程

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第四章 ARM嵌入式系统的编程 4.1 ARM汇编语言伪操作、宏指令和伪指令(P173) 一、基本概念 1、伪操作:为完成汇编程序做各种准备工作,只在汇编过程中起作用,一旦汇编结束,它的作用也随之结束。 2、宏指令:是一段独立的程序代码,可插在源程序中。与子程序相似,但有本质不同。 3、伪指令:在汇编时将被合适的指令代替。 伪操作、宏指令一般与编译器有关,常用ARM编译开发环境有2种:GNU(基于Embest IDE环境)和ADS(ARM公司提供)

  2. 二、GNU编译环境下的常用ARM伪操作和宏指令 1、常量编译控制伪操作(P201) .byte分配一段字节内存单元(字节对齐) 例:.byte 21, 48, 89, 0x13, 0xFF 类似的有: .hword 分配一段字节内存单元(半字对齐) .word、 .long、 .int 分配一段字节内存单元(字对齐)

  3. 2、字符编译控制伪操作(204) (1).equ 为数字常量、基于寄存器的值和程序中的标号定义一个字符名称。 例:.equ num , 20 (2) .global声明一个全局变量(声明一个能被其他文件引用的符号) 例:.global _start

  4. 3、汇编程序代码控制伪操作(P205) .text代码段开始 .data数据段开始 .bss bss段开始(未初始化的全局变量) .code 16| 32 .thumb .arm .end标记汇编文件结束 .include将一个源文件包含到当前文件中

  5. 4、宏编译控制伪操作(P207) 5、条件编译控制伪操作(P209) 三、ARM汇编语言的伪指令(P211) 1、小范围的地址读取伪指令ADR 2、中等范围的地址读取伪指令ADRL 3、大范围的地址读取伪指令LDR(P214) 示例89(233)ADS和GNU编译环境下程序比较

  6. 4.2 嵌入式系统汇编程序举例 1、单简的ARM指令程序GUN与ADS环境下编程比较(P233) .global_start .text _start: MOV R0, #10 MOV R1, #3 ADD R0,R0,R1 stop: MOV R0, #0x18 LDR R1, =0X20026 SWI 0X123456 .end AREA ARMex,CODE,READONLY ENTRY start MOV R0, #10 MOV R1, #3 ADD R0,R0,R1 stop MOV R0, #0x18 LDR R1, =0X20026 SWI 0X123456 END

  7. src dst 8 8 2、示例90 数据块复制(P234)

  8. 3、示例91利用跳转表实现程序跳转(P236) BL Func … Func: … ADR R3,JTable LDR PC,[R3,R0,LSL,#2] JTable: DCD DoAdd /*4字节子程序入口地址*/ DCD DoSub /*4字节子程序入口地址*/ DoAdd: … DoSub: …

  9. 4.3 嵌入式C语言程序设计基础 • C语言“预处理伪指令” • 嵌入式程序设计中的函数及函数库 • 嵌入式程序设计中常用的C语言语句 • 嵌入式程序设计中C语言的变量、数组、结构、联合

  10. 一、C语言“预处理伪指令” “预处理命令”可以改进程序设计的环境,提高编程效率,一般以#号打头 ,可分为以下三种 : 文件包含伪指令的格式(P239) #include<头文件名.h> ;标准头文件 #include“头文件名.h” ;自定义头文件 #include 宏标识符 • 文件包含 • 宏定义 • 条件编译 简单宏: # define宏标识符 宏体 参数宏:# define 宏标识符(形式参数表) 宏体 条件宏定义:(P242) #ifdef宏标识符 [#undef 宏标识符] #define 宏标识符 宏体 #else #define 宏标识符 宏体 #endif #if(条件表达式1) (P244) … #elif(条件表达式2) … #elif(条件表达式n) … #else … #endif

  11. 二、嵌入式程序设计中的函数及函数库 函数是C语言程序设计的核心。一个较大的C语言程序一般是由一个主函数和若干个子函数组成,每个函数完成一个特定的功能。函数之间也可以相互调用。 函数的格式: 定义性说明格式 :(P245) [存储类说明符] 类型说明符 [修饰符] 标识符 (参数表) {函数体} 原型说明格式 : extern 类型说明符[修饰符] 标识符(参数表){函数体}

  12. 三、嵌入式程序设计中常用的C语言语句 C语言语句格式为: [标号:] 语句[;] C语言语句很多,常用到的有以下几种: 条件语句 swith语句 循环语句

  13. 四、嵌入式程序设计中C语言的变量、数组、结构、联合四、嵌入式程序设计中C语言的变量、数组、结构、联合 变量(P252) [存储类型] 类型说明符 [修饰符] 标识符 [=初值] [,标识符[=初值]]…; 数组(P255) 一维数组: 类型说明符 标识符 [常量表达式][={初值,初值,…}]; char 标识符[ ] =“字符串”; 二维数组: 类型说明符 标识符[m][n] [={{初值表},{初值表}…}]; 指针数组和数组指针 类型说明符 *标志符 [常量表达式] [={地址,地址,…}]; 类型说明符 (*标志符)[ ][=数组标识符];

  14. 结构说明 [存储类说明符]struct [结构原型名] { 类型说明标识符[,标识符…]; 类型说明标识符[,标识符…]; … }标识符[={初值表}[,标识符[={初值表}]…];

  15. 4.4 嵌入式C语言程序设计技巧 一、C程序编译后的汇编代码 二、变量定义 1、在变量声明的时候,最好把所有相同类型的变量放在一起定义,这样可以优化存储器布局。由下例可以看出: 2、对于局部变量类型的定义,使用short或char来定义变量并不是总能节省存储空间。有时使用32位int或unsinged int局部变量更有效率一些,如下图所示: 3、变量定义中,为了精简程序,程序员总是竭力避免使用冗余变量。但有时使用冗余变量可以减少存储器访问的次数这可以提高系统性能。

  16. 三、参数传递 为了使单独编译的C语言程序和汇编程序能够互相调用,定义了统一的函数过程调用标准ATPCS。ATPCS定义了寄存器组中的{R0~R3}作为参数传递和结果返回寄存器,如果参数数目超过四个,则使用堆栈进行传递。 内部寄存器的访问速度是远远大于存储器的,所以要尽量使参数传递在寄存器里面进行,即应尽量把函数的参数控制在四个以下。

  17. 四、循环条件 计数循环是程序中十分常用的流程控制结构,一般有以下两种形式: for (loop=1;loop<=limit;loop++) for (loop=limit;loop!=0;loop--) 这两种循环形式在逻辑上并没有效率差异,但是映射到具体的体系结构中时,就产生了很大的不同,如下图所示。

  18. 4.5 C与汇编语言混合编程 一、ATPCS介绍 ATPCS(ARM-Thumb Produce Call Standard)是ARM程序和Thumb程序中子程序调用的基本规则,目的是为了使单独编译的C语言程序和汇编程序之间能够相互调用。这些基本规则包括子程序调用过程中寄存器的使用规则、数据栈的使用规则和参数的传递规则。

  19. 寄存器 别名 特殊名 使用规则 R0 a1 参数/结果/scratch寄存器1 R1 a2 参数/结果/scratch寄存器2 R2 a3 参数/结果/scratch寄存器3 R3 a4 参数/结果/scratch寄存器4 R4 v1 ARM状态局部变量寄存器1 R5 v2 ARM状态局部变量寄存器2 R6 v3 ARM状态局部变量寄存器3 R7 v4 wr ARM状态局部变量寄存器4 Thumb状态工作寄存器 R8 v5 ARM状态局部变量寄存器5 R9 v6 sb ARM状态局部变量寄存器6, 在支持RWPI的ATPCS中为静态基址寄存器 R10 v7 sl ARM状态局部变量寄存器7, 在支持数据栈检查的ATPCS中为数据栈限制指针 R11 v8 fp ARM状态局部变量寄存器8/帧指针 R12 ip 子程序内部调用的scratch寄存器 R13 sp 数据栈指针 R14 lr 连接寄存器 R15 pc 程序计数器 寄存器的使用规则

  20. 数据栈的使用规则 根据堆栈指针指向位置的不同 和增长方向的不同可以分为以下4种数据栈 : FD (Full Descending) 满递减 ED (Empty Descending) 空递减 FA (Full Ascending) 满递增 EA (Empty Ascending) 空递增 ATPCS规定数据栈为FD(满递减)类型,并且对数据栈的操作是8字节对齐的。

  21. 参数的传递规则 参数个数固定的子程序参数传递规则: 第一个整数参数,通过寄存器R0~R3来传递。其他参数通过数据栈传递。 参数个数可变的子程序参数传递规则: 当参数不超过4个时,可以使用寄存器R0~R3来传递参数;当参数超过4个时,还可以使用数据栈来传递参数 子程序结果返回规则 结果为一个32位的整数时,可以通过寄存器R0返回;结果为一个64位整数时,可以通过寄存器R0和R1返回,依次类推。

  22. 二、内嵌汇编 在C程序中嵌入汇编程序可以实现一些高级语言没有的功能,并可以提高执行效率。armcc和armcpp内嵌汇编器支持完整的ARM指令集;tcc和tcpp用于Thumb指集。 内嵌的汇编指令包括大部分的ARM指令和Thumb指令,但是不能直接引用C的变量定义,数据交换必须通过ATPCS进行。嵌入式汇编在形式上表现为独立定义的函数体。

  23. 内嵌汇编指令的语法格式 __asm(“指令[;指令]”); ARM C汇编器使用关键字“__asm"。如果有多条汇编指令需要嵌入,可以用“{}”将它们归为一条语句。如: __asm { 指令[;指令] … [指令] } 需要特别注意的是__asm是两个下划线。

  24. 三、C和ARM汇编程序间相互调用 在C和ARM汇编程序之间相互调用必须遵守ATPCS(ARM-Thumb Procedure Call Standard)规则。 当参数不超过4个时,可以使用寄存器R0~R3来传递参数;当参数超过4个时,还可以使用数据栈来传递参数 。 子程序结果返回规则 结果为一个32位的整数时,可以通过寄存器R0返回;结果为一个64位整数时,可以通过寄存器R0和R1返回,依次类推。 • C和汇编之间的相互调用可以从以下这三方面来介绍: • 汇编程序对C全局变量的访问 • 在C语言程序中调用汇编程序 • 在汇编程序中调用C语言程序

  25. 汇编程序访问全局C变量 汇编程序可以通过地址间接访问在C语言程序中声明的全局变量。通过使用IMPORT关键词引人全局变量,并利用LDR和STR指令根据全局变量的地址可以访问它们。 对于不同类型的变量,采用的LDR和STR指令,如下 unsigned char LDRB/STRB unsigned short LDRH/STRH unsigned int LDR/STR char LDRSB/STRSB short LDRSH/STRSH

  26. 在C语言程序中调用汇编程序 汇编程序的设计要遵守ATPCS。在汇编程序中使用EXPORT伪操作来声明,使得本程序可以被其它程序调用。 在C程序调用该汇编程序之前使用extern关键词来声明该汇编程序。 在汇编程序中调用C语言程序 汇编程序的设计要遵守ATPCS。在汇编程序调用该C程序之前使用IMPORT伪操作来声明该C程序,通过BL指令来调用子程序。 在C程序中不需要使用任何关键字来声明将被汇编语言调用的C程序

  27. 四、嵌入式系统C语言程序设计实例(P261) 1、包含头文件#include 2、函数声明 3、各类型变量、数组定义 4、本文件中各函数定义 5、执行者

  28. 4.6 基于Embest IDE for ARM 环境的软件开发 一、嵌入式软件开发流程 编写代码仅是嵌入式软件开发的第一步,还要学会在具体的编程环境下完成工程创建、源文件的编写、编译、链接、调试、固化等一系列工作。这就涉及到程序编译、链接、调试的具体配置以及开发板上硬件的初始化、程序下载等问题。 在Embest IDE for ARM开发环境下的实际开发例程,

  29. 工程文件 源代码文件 功能文件 模块、库文件 编译、汇编器 输出调试文件 程序编译 程序调试 软件开发步骤 .cs、.map、.ld

  30. 调试符号文件 调试配置 软件功能 RAM/ Flash调试 断点、标记 调试窗口 程序调试 软件开发步骤 Go、Step etc.

  31. 文件类型 工程文件 源代码文件 功能文件 模块、库文件 • 功能文件 (*.cs、*.map、*.ld) • 工程文件 (*.ews、*.pjf、*.opt) • Embest IDE for ARM使用的GCC标准文件 • 用户参考相关资料并跟据实际硬件编写 • Embest IDE for ARM的工程配置文件 • 用户一般不可更改 • 模块、库文件( *.lib、*.a etc.) • 源代码文件( *.c、*.cpp、*.s etc.) • Embest IDE for ARM直接支持的标准库文件 • 可由Embest IDE for ARM或其他编译软件生成 • ANSI C语言程序(*.c) • C++语言程序(*.C、*.cpp) • 汇编语言程序(*.S)

  32. 链接脚本文件 赋当前地址,可能为RAM或Flash的访问地址 只读区域基地址,启动程序中使用的符号 代码段,在这里标识开始放置程序代码 只读区域长度,启动程序中使用的符号 读写区域基地址,启动程序中使用的符号 数据段, 程序中已初始化的全局变量放在该段 只读数据段,程序中静态全局变量等固定值放在该段 清零区域基地址, 启动程序中使用的符号 包含未初始化的全局可用数据, 如未初始化全局变量 清零区域长度, 启动程序中使用的符号 读写区域基长度, 启动程序中使用的符号 调 试 信 息 SECTIONS { . = 0x0C000000; Image_RO_Base = .; .text: { *(.text) }; Image_RO_Limit = .; Image_RW_Base = .; .data : { *(.data) }; .rodata : { *(.rodata) }; Image_ZI_Base = .; .bss : { *(.bss) }; Image_ZI_Limit = .; Image_RW_Limit = .; __bss_start__ = .; __bss_end__ = .; __EH_FRAME_BEGIN__ = .; __EH_FRAME_END__ = .; PROVIDE (__stack = .); end = .; _end = .; .debug_info 0 : { *(.debug_info) } .debug_line 0 : { *(.debug_line) } .debug_abbrev 0 : { *(.debug_abbrev)} .debug_frame 0 : { *(.debug_frame) } } • 在系统级别的嵌入式开发中需要使用链接定位文件,该文件描述代码链接定位的有关信息,包括代码段,数据段,地址段等,链接器必须使用该文件对整个系统的代码做正确的定位,该文件称为链接脚本文件(*.ld)

  33. int A1; int A2 =5; const int A3 = 10; void main() { int A4; register int A5; A4 = A3; } 变量A1作为未初始化的变量将保存在 .bss 段中 变量A2 作为已初始化的变量将保存在 .data 段中 常量A3保存在只读数据断 .rodata 段中 main函数对应的代码保存在 .text 段中 局部变量A4当程序执行到main函数时 存放在main函数对应的函数栈中 寄存器变量A5直接保存在ARM的一个寄存器中 .text : { *(.text) }; 代码段,在这里标识从0开始放置程序代码 .rodata : { *(.rodata) };只读数据段,程序中静态全局变量等固定值放在该段 .data : { *(.data) }; 数据段, 程序中已初始化的全局变量放在该段 .bss : { *(.bss) }; 包含未初始化的全局可用数据, 如未初始化全局变量 链接文件示例

  34. MEMWRITE – 存储区写 语 法: memwrite [–e] 地址 数值 说 明: 向存储区指定地址写入数值 地址 要写入数值的存储区地址 数值 待写数值 选 项: -e 大印第安方式写入 示例: memwrite 0x1000 0x5A 向地址0x1000处写入数值0x5A memwrite -e 0x2000000 0x22334455 等效于memwrite 0x2000000 0x55443322 • 在集成环境与目标连接时、软件调试过程中以及目标板复位后,有时需要集成环境自动完成一些特定的操作,比如复位目标板、清除看门狗、屏蔽中断寄存器、存储区映射等。这些操作可以通过执行一组命令序列来完成,保存一组命令序列的文本文件称为命令脚本文件(*.cs) • 命令脚本文件中各行以“;”号开始作为注释内容,分号前是一条命令。凡是可以在调试命令窗口使用的命令,都可以在脚本文件中使用,包括执行脚本文件命令“SCRIPT” 命令脚本文件 调试命令列表 BKPTCLEAR –清除断点 BKPTDATA –设置数据断点 BKPTINST –设置指令断点 BKPTLIST –断点列表 DISASM –反汇编 DOWNLOAD –文件下载 GO –执行程序 HELP –显示帮助信息 MEMREAD –存储区读 MEMWRITE –存储区写 REFRESH –刷新窗口 REGLIST –寄存器列表 REGREAD –寄存器读 REGWRITE –寄存器写 RESET –复位目标设备 SCRIPT –执行脚本文件 STEP –单步执行程序 STOP –停止执行程序 SYMBOL –载入符号文件 命令脚本的执行方法 方 法 一 在Embest IDE for ARM工程设置对话框 DEBUG菜单 ->Action After Connect 输入文件及路径名 方 法 二 在Embest IDE for ARM连接目标板后 Command子窗口里输入 Script 文件名

  35. 命令脚本文件示例

More Related