ARM简单程序设计【嵌入式系统】

简介: ARM简单程序设计【嵌入式系统】

前言

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

祝大家逢考必过

点赞收藏关注哦

相关文章
|
6月前
|
消息中间件 网络安全 数据安全/隐私保护
麒麟系统ARM安装rabbitmq
麒麟系统ARM安装rabbitmq
|
6月前
|
Ubuntu Windows
【Ubuntu/Arm】Ubuntu 系统如何链接有线网络(非虚拟机)?
【Ubuntu/Arm】Ubuntu 系统如何链接有线网络(非虚拟机)?
|
6月前
|
监控 网络协议 安全
验证嵌入式ARM32环境中4G模块的有效方法
验证嵌入式ARM32环境中4G模块的有效方法
149 0
|
3月前
|
Ubuntu Linux
查看Linux系统架构的命令,查看linux系统是哪种架构:AMD、ARM、x86、x86_64、pcc 或 查看Ubuntu的版本号
查看Linux系统架构的命令,查看linux系统是哪种架构:AMD、ARM、x86、x86_64、pcc 或 查看Ubuntu的版本号
875 3
|
1月前
|
Ubuntu Shell API
Ubuntu 64系统编译android arm64-v8a 的openssl静态库libssl.a和libcrypto.a
Ubuntu 64系统编译android arm64-v8a 的openssl静态库libssl.a和libcrypto.a
|
1月前
|
数据处理
基于ARM的嵌入式原理与应用:ALU的功能与特点
基于ARM的嵌入式原理与应用:ALU的功能与特点
|
3月前
|
Linux 网络安全 开发工具
内核实验(二):自定义一个迷你Linux ARM系统,基于Kernel v5.15.102, Busybox,Qemu
本文介绍了如何基于Linux Kernel 5.15.102版本和BusyBox创建一个自定义的迷你Linux ARM系统,并使用QEMU进行启动和调试,包括内核和BusyBox的编译配置、根文件系统的制作以及运行QEMU时的命令和参数设置。
290 0
内核实验(二):自定义一个迷你Linux ARM系统,基于Kernel v5.15.102, Busybox,Qemu
|
5月前
|
存储 Ubuntu 编译器
合肥中科深谷嵌入式项目实战——基于ARM语音识别的智能家居系统(三)
合肥中科深谷嵌入式项目实战——基于ARM语音识别的智能家居系统(三)
合肥中科深谷嵌入式项目实战——基于ARM语音识别的智能家居系统(三)
|
3月前
|
Ubuntu Windows
ARM架构安装ubuntu系统
8月更文挑战第19天
1168 0
|
5月前
|
Ubuntu Unix Linux
合肥中科深谷嵌入式项目实战——基于ARM语音识别的智能家居系统(一)
合肥中科深谷嵌入式项目实战——基于ARM语音识别的智能家居系统(一)
下一篇
无影云桌面