前言
2023-4-6 20:26:54
以下内容源自《【嵌入式系统】》
仅供学习交流使用
推荐
Keil 4 安装教程及简单使用【嵌入式系统】
ARM简单程序设计
创建项目
新建工程xxx
芯片:ARM7 (Little Endian)
设置工程属性
Build结果必须是0Error的
如果是下图看配置是否正确
注意这个:
注意事项
1.每一次修改代码就需要重写Build
2.READWRITE区变量初始化为0的解决
格式:
AREA xxx, CODE, READONLY ; 代码段的名字 symbol
ENTRY ; 程序的入口
CODE32
start
xxx
stop
MOV R0, #0x18
LDR R1, =0x20026
SWI 0x123456
AREA Data0, DATA, READONLY ;放到READONLY 注意段名字和下面不一样 Data0 Data
x DCD -25
AREA Data, DATA, READWRITE
y DCD 0
END
顺序结构程序
两数之和
ARMex.s
例4- 1试编制一程序,完成10+3的操作
AREA ARMex, CODE, READONLY ; 代码段名ARMex
ENTRY ; 程序的入口
CODE 32
start
MOV R0, #10 ; 将立即数10存入寄存器R0
MOV R1, #3 ; 将立即数3存入寄存器R1
ADD R0, R0, R1 ; R0 = R0 + R1
stop
MOV R0, #0x18 ; 这三条指令是ADS调试环境特约
LDR R1, =0x20026 ;程序运行结束返回编译器调试环境
SWI 0x123456
END ; 结束
结果如下
(R0)=13
分支结构程序
符号函数
例4-2给定以下符号函数:
{ 1 x>0
y= { 0 x=0
{ -1 x<0
任意给定值,假定为-25,存放在x单元,函数值存放在y单元;要求根据x中的值来确定y的值。
symbol.s
AREA symbol, CODE, READONLY ; 代码段的名字 symbol
ENTRY ; 程序的入口
CODE32
start
LDR R0, =x ; 加载数据段中的变量x地址,存入R0
LDR R1, =y ; 加载数据段中的变量y地址,存入R1
LDR R2, [R0] ; 加载变量x的值,存入R2
compare
CMP R2, #0 ; 将R2的值与0作比较
BEQ ZERO ; 如果R2等于0,那么转向标号ZERO处
BGT PLUS ; 如果R2大于0,那么转向标号PLUS处
MOV R3, #-1 ; 否则,R2小于0,将-1存入R3中
B stop
ZERO
MOV R3, #0 ; R2等于0,将0存入R3中
B stop
PLUS
MOV R3, #1 ; R2大于0,将1存入R3中
stop
STR R3,[R1]
MOV R0, #0x18
LDR R1, =0x20026
SWI 0x123456
AREA Data0, DATA, READONLY
x DCD -25
AREA Data, DATA, READWRITE
y DCD 0
END
结果如下
R0=&x=0x00000048
R1=&y=0x40000000
R2=x=-25
R3=y=-1
循环结构程序
已知循环次数
例 4‑4 从x单元开始的30个连续字单元中存放有30个无符号数,从中找出最大者送入y单元中。
分析:
根据题意,把第一个数先送入Rx寄存器,将Rx中的数与后面的29个数逐个进行比较,如果Rx中的数较小,则将该较大的数送入Rx ;继续与余下的数据逐个比较。在比较过程中, Rx中始终保持较大的数,共计比较29次,则最终Rx中保留了最大数,最后把Rx中的数(最大者)送入y单元。
max.s
AREA max, CODE, READONLY ; 代码段的名字 max
ENTRY ; 程序的入口
CODE32
num EQU 29 ; 比较的次数
start
LDR R0, =x ; R0指向源数据块x
LDR R1, =y ; R1指向单元y
LDR R2, =num ; R2作为计数器
LDR R3, [R0] ; 将源数据块x中第一个数加载到R3中
compare
ADD R0, R0, #4 ; 每进行一次比较,将R0指针地址加4
LDR R4, [R0],#4 ; 依次将源数据块x中下一个数加载到R4中
CMP R3, R4 ; 比较R3和R4中数的大小
MOVCC R3, R4 ; 如果R3小于R4,则将较大的数送入R3中
SUBS R2, R2, #1 ; 计数器值减1
BNE compare ; 如果不为0,那么继续跳到compare执行
STREQ R3, [R1] ; 如果为0,那么循环比较结束,R3是最大的数
; 并且将R3中的数加载到R1指向的单元(即y)中
stop
MOV r0, #0x18
LDR r1, =0x20026
SWI 0x123456
AREA Data0, DATA, READONLY
x DCD 73,59,61,34,81,107,225,231,54,43
DCD 100,35,1,42,222,254,34,71,100,31
DCD 33,119,13,44,18,147,55,244,97,3
AREA Data, DATA, READWRITE
y DCD 0
END
说明
结果如下
R0=&x
R1=&y=0x40000000
R3=0x000000FE=254
未知循环次数
例 4‑5 从自然数1开始累加,直到累加和大于1000为止,统计被累加的自然数的个数,并把统计的个数送入n单元,把累加和送入sum单元。
分析:
根据题意,被累加的自然数的个数事先未知,因此不能用计数方法控制循环。但题目中给定一个条件,即累加和大于1000则停止累加,因此,可以根据这一条件控制循环。我们用R3寄存器放累加和,用R4寄存器放每次取得的自然数,其中它的值也是统计自然数的个数。
SUM.s
AREA SUM, CODE, READONLY ; 代码段的名字 SUM
ENTRY ; 程序的入口
CODE32
start
LDR R0, =n ; 将数据段中自然数的个数n的地址加载到R0寄存器
LDR R1, =sum ; 将数据段中自然数的累加和sum的地址加载到R1寄存器
LDR R3, =0 ; R3存放自然数的累加和
LDR R4, =0 ; R4用于循环个数的统计/每次取得的自然数
LDR R5, =1000 ; R5用于循环结束的界限值
continue
ADD R4, R4, #1 ; 取下一个自然数
ADD R3, R3, R4 ; 累加自然数
CMP R3, R5 ; 比较累加和是否超过了1000
BCC continue ; 如果小于1000,那么跳到compare执行
STRCS R3, [R1] ; 如果大于1000,那么将累加和存储到R1所指向的单元中
STRCS R4, [R0] ; 如果大于1000,那么将已累加的自然数个数值存储
; 到R0所指向的单元中
stop
MOV r0, #0x18
LDR r1, =0x20026
SWI 0x123456
AREA Data, DATA, READWRITE
n DCD 0 ; 定义累加的自然数的个数
sum DCD 0 ; 定义自然数的累加和
END
结果如下
R0=&n=0x40000000
R1=&sum=0x40000004
R3=0x000004DB=1035
R4=0x0000002D=45
R5=0x000003E8=1000
两重循环
冒泡排序
Bibble.s
AREA Bibble, CODE, READONLY
ENTRY
start
;想实现数组拷贝 需要输入原数组 数组长度 输出目的数组 即可
;这里借用字符串拷贝 要求源数组最后加一个无效元素0 中间不能出现0
LDR R1, =src0 ; R1指向数据区的源字符串
LDR R0, =src ; R0指向数据区的目的字符串
BL strcopy ; 调用子程序strcopy,完成字符串拷贝
LDR R1,=src ; (右指针)R1起初指向src第一个单元
MOV R2, #0 ; 用于外层循环控制计数器,并初始化为0
LDR R4, num ; R4中是数据区中待排序数据个数
SUB R4, R4, #1 ;
ADD R1, R4,LSL #2 ;(右指针)R1指向src最后一个单元 R1=R1+R4*4,
outer ; 外层循环
LDR R0, =src ;(左指针)R0指向数据区src单元
inner ; 内层循环
LDR R5, [R0] ; 将R0所指向单元的数加载到R5中
LDR R6,[R0, #4] ; 将相邻单元的数加载到R6中
CMP R5, R6 ; 比较相邻两单元中的数
STRGT R6, [R0] ;如果前者大于后者,那么两个数交换
STRGT R5, [R0, #4] ; 内层循环修改、控制部分
ADD R0, R0, #4 ; 地址指针向下拨移4个字节
CMP R0, R1 ; 是否扫描了一遍
BNE inner ; 没有完成一遍,继续内循环
; 外层循环修改、控制部分,表示已经完成了一遍,
ADD R2, R2, #1 ; 外层循环控制计数器加1
SUB R1, R1, #4 ; 右指针R1向左指针方向移动4字节
CMP R2, R4 ; 是否全部扫描
BNE outer ; 没有完成全部扫描,继续外循环
stop
MOV R0, #0x18 ; 程序结束返回编译器调试环境
LDR R1, =0x20026
SWI 0x123456
strcopy
LDR R2, [R1], #4 ; 将R1指向的单元内容加载到R2中
STR R2, [R0], #4 ; 将R2中的数存储到R0指向的单元中
CMP R2, #0 ; 检查R0的值是否等于0
BNE strcopy ; 如果不等于0,那么转到strcopy处执行
MOV PC, LR ; 子程序返回
AREA BlockData0, DATA, READONLY
src0 DCD 18,4,2,35,3,20,1,23,12,21,0
num DCD 10
AREA BlockData, DATA, READWRITE
src DCD 18,4,2,35,3,20,1,23,12,21,0 ;Keil会把此数组初始化为0
END
说明
因为待排序数组直接放到READWITER,会有初始化为0的问题
所以就需把其放到READONLY下
但是,这就排不了序了
所以需要把其拷贝到一个READWITER下的数组,对其进行排序
这里我借用了字符串拷贝
真正的数组拷贝的设计:需要输入原数组 数组长度 输出目的数组 即可
另外
右指针的初始化
; (右指针)R1起初指向src第一个单元
LDR R1,=src ; 0x40000000
; R4中是数据区中待排序数据个数
LDR R4, num ;10
SUB R4, R4, #1 ;9
;(右指针)R1指向src最后一个单元 R1=R1+R4*4,用左移两位实现 一个元素占4个字节
ADD R1, R4,LSL #2 ;0x400000000+9*4=0x40000032
结果如下
1.完成数组拷贝
末尾的0是无效的
2.完成初始化
R0左指针->第一个
R1右指针->倒数的一个
3.第一遍循环
R0左指针->第一个
R1右指针->倒数的二个
倒数第一个,为最大的树
4.最终结果
R0左指针
R1右指针
R2=9 循环次数
R4=9 循环次数
R5,R6 是最后交换的两个数
子程序设计
①寄存器传递参数方式
StrCopy.s
AREA StrCopy, CODE, READONLY
ENTRY
CODE32
start
LDR R1, =srcstr ; R1指向数据区的源字符串
LDR R0, =dststr ; R0指向数据区的目的字符串
BL strcopy ; 调用子程序strcopy,完成字符串拷贝
stop
MOV R0, #0x18 ; 程序结束返回编译器调试环境
LDR R1, =0x20026
SWI 0x123456
strcopy
LDRB R2, [R1], #1 ; 将R1指向的单元内容加载到R2中
STRB R2, [R0], #1 ; 将R2中的数存储到R0指向的单元中
CMP R2, #0 ; 检查R0的值是否等于0
BNE strcopy ; 如果不等于0,那么转到strcopy处执行
MOV PC, LR ; 子程序返回
AREA Strings, DATA, READWRITE
srcstr DCB "First string - source",0 ; 源字符串
dststr DCB "Second string - destination",0 ; 目的字符串
END
结果如下
R0=&dststr=0x40000000
R1=&srcstr=0x00000038
②存储区域传递参数方式
Jump.s
AREA Jump, CODE, READONLY
num EQU 4 ; 函数地址表内容的个数
ENTRY
CODE32
start
LDR R0, =choice ; R0指向存储区的choice单元
LDR R0, [R0] ; 设置第一个参数:选择执行哪一个函数
MOV R1, #16 ; 设置第1个操作数
MOV R2, #2 ; 设置第2个操作数
BL arithfunc ; 调用子程序arithfunc
stop
MOV R0, #0x18 ; 程序结束返回编译器调试环境
LDR R1, =0x20026
SWI 0x123456
arithfunc
CMP R0, #num ; 比较R0的值是否超过函数地址表的个数
MOVHS PC, LR ; 如果大于,那么就返回到标号stop处
ADR R3, JumpTable ; 将函数地址表的地址作为基地址
LDR PC, [R3, R0, LSL #2] ; 根据R0参数进入对应的子程序
JumpTable ; 函数地址表的入口基地址
DCD DoAdd ; 加法子程序
DCD DoSub ; 减法子程序
DCD DoMul ; 乘法子程序
DCD DoDiv ; 除法子程序
DoAdd
ADD R0, R1, R2 ; R0 = R1 + R2
MOV PC, LR ; 返回
DoSub
SUB R0, R1, R2 ; R0 = R1 - R2
MOV PC, LR ; 返回
DoMul
MOV R0, R1, LSL R2 ; R0 = R1 << R2
MOV PC, LR ; 返回
DoDiv
MOV R0, R1, LSR R2 ; R0 = R1 >> R2
MOV PC, LR ; 返回
AREA NUM, DATA, READWRITE
choice DCD 3 ; 0:表示选择加法子程序 1:表示选择减法子程序
; 2:表示选择乘法子程序 3:表示选择除法子程序
END
说明
类似:函数指针的用法
ADR R3, JumpTable ; 将函数地址表的地址作为基地址
LDR PC, [R3, R0, LSL #2] ; 根据R0参数进入对应的子程序
结果如下
R0=16/1<<2=4
③ 堆栈传递参数方式
略
最后
2023-4-6 22:29:06
2023-4-11 19:17:12文章来源:https://www.toymoban.com/news/detail-418997.html
祝大家逢考必过
点赞收藏关注哦文章来源地址https://www.toymoban.com/news/detail-418997.html
到了这里,关于ARM简单程序设计【嵌入式系统】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!