在几个月前看2440视频的时候,发现太多知识欠缺,购买开发板期间补习makefile,linux,arm汇编和arm构架之后,现在重新开始学习。

先看板子LED硬件连接图:

可以看到LED 1,2,4连接GPF4,5,6。故,查看芯片手册GPF IO口:

有寄存器地址,有对应位的功能,剩下的就是我们的编程了。

写在前面:基本汇编拾遗

Load/Store 内存访问指令:

LDR:字 数据读取指令

LDRB:字节读取指令

LDR R0,[R1];把内存单元R1中的数据读取到R0寄存器中

。。。LDR,STR还有很多后缀的指令不详举例了,以后用到再说明

STR:字数据写入指令

STRB:字节数据写入指令

STR R0,[R1,#0X100];把R0中的数据保存到内存单元(R1+0X100)中

B:跳转

MOV:移动 MOV R1,R0;把R0的值赋值给R1

伪指令:LDR(上面一个LDR的用法是汇编指令,但是LDR也可以是伪指令)

LDR R0,=0X1234;表示把0x1234赋值给R0,你可能会想,MOV不就可以达到效果了吗?

但是,MOV只能处理立即数,当我们不能保证一个数是否是立即数的时候,请使用LDR伪指令。

关于什么样的数才能算是立即数,可以参考ARM体系结构 P24对立即数的叙述。

简单介绍一下:

每个立即数由一个8位的常数循环右移偶数位得到,循环右移的偶数由一个4位二进制数的两倍表示记作rotate_imm,8位常数记作immed_8,立即数记作<immediate>;

<immediate>=immed_8循环右移(2*rotate_imm)

现在开始编写汇编指令,linux与windows开发最大的不同就是linux一切靠自己,不用IDE。

/*
* 点亮LED1: gpf4
*/ .text/*文本段*/
.global _start/*全局标号_start*/ _start: /* 配置GPF4为输出引脚
* 把0x100写到地址0x56000050
*/
ldr r1, =0x56000050/*为什么不使用mov,因为立即数不是一眼就能看出来的*/
ldr r0, =0x100 /* mov r0, #0x100 */
str r0, [r1] /* 设置GPF4输出低电平
* 把0写到地址0x56000054
*/
ldr r1, =0x56000054
ldr r0, = /* mov r0, # */
str r0, [r1] /* 死循环 ,因为flash后面的数据不可预知,只能停留在这里*/
halt:
b halt

这里使用c/c++中的注释方式,其实汇编中应该使用分号; 代表注释。

上述代码只是简单粗暴的完成了任务,但是破坏了寄存器中其他位的数据,作为第一个程序,暂且先这样。

需要使用到上篇随笔安装的两个工具,交叉编译器arm-linux-gcc和samba服务器,这里简单说明一下makefile:

all:
arm-linux-gcc -c -o led_on.o led_on.S
arm-linux-ld -Ttext led_on.o -o led_on.elf
arm-linux-objcopy -O binary -S led_on.elf led_on.bin
arm-linux-objdump -D led_on.elf > led_on.dis
clean:
rm *.bin *.o *.elf

这个makefile显然只能是拿来入门使用,关于其中的工具链用法,在linux系列随笔中已经有过讲解。

是不是这样就完了呢?显然不是,还要进一步分析反汇编:

第一列表示地址,第二列表示机器码,第三列表示汇编码。

ARM9采用三级流水线工作方式,可以大大提高系统效率。三级流水线,取址,译码,执行。

当cpu在执行一条指令时,已经开始对下一条指令进行译码,对下下一条指令进行取址了。这也就是我们常说的PC值等于当前指令地址加上8的原因。

分析第一条汇编,r1=[pc+20];此时pc=当前指令地址(0)+8+20=28=0x1c

可以看到在地址1c处,存放的是0x56000050,这就相当于把[pc+20]地址的内容赋值给r1,此时就r1=0x56000050。

第二条指令把256即0x100赋值给r0;

第三条指令,把r0的值写在r1的地址内,即把0x100写进地址0x56000050。后面的以此类推。

分析了反汇编之后,我们就可以通过更改bin文件,即直接更改二进制文件达到点亮led灯的效果。

刚才我们点亮了GPF4,现在点亮GPF 5,只用把上面的0x100改成0x400,其他不变(后面说注意事项)。

我们知道上面的第二列表示机器码之后,需要达到直接写一串机器码,实现MOV R0,#0X400的功能,此时我们需要查看

MOV指令机器码:

上面生成的二进制文件,可以使用sublime text 或者Uedit32打开。

先看上面我们生成的MOV RO,#256机器码:

e3a00c01

对照上面的MOV机器码,做几点说明,12-15位表示Rd,此时我们使用的是R0,这四个位全为0,表示寄存器R0,最后0-11位(立即数)就是我们需要修改的了。

关于立即数,上面做了说明:

<immediate>=immed_8循环右移(2*rotate_imm)

这里的0-11位高4位就是rotate_imm,低八位就是8位常数immed_8。

0x100的表示如上图,

公式:<immediate>=immed_8循环右移(2*rotate_imm)

高4位12,低八位为1,1循环右移2*12,刚好等于0x100.

那么0x400的呢?

可以想到,0x400,二进制0x1后面10个0,前面就需要22位,所以,我们可以采用1循环右移22位。

/* 中途插播 */

NOTE:

右移本来就是不确定的,左移是确定的,但是,右移的不确定是根据cpu的具体实现来决定的,就是不同的cpu可以有不同的处理,在我们这里,就是特定这个cpu S3C2440,所以我们的右移补位方式这里就是对特定CPU的。这个符合我们的需求,但是在其他硬件平台上,右移如何补位,要查看它的参考资料。

/* 插播 完毕*/

所以我们0x400的立即数就可以表示为:高四位为11,低八位位1,这样就可以切合。有公式完成这个就不再赘述了。

最后得出:

现在我们更改之前的bin文件,把上面MOV r0,#256的机器码改成上图的e3a00b01

烧写进入单板,观察是否点亮GPF 5.

之前的bin文件做一点说明:

可以看到我们的e59f1014在图1和图2上似乎顺序不同,一个是反的,一个正的。这是因为我们采取单板默认的小端模式进行的开发,小端模式,高位在高地址,地位在低地址。

所以只用把图1中第五列的0c改成0b即可,下载进入,观看现象。当然是被点亮了。

最后,再说明一点,这个更改机器码确实麻烦,我们已经过去了那个写机器码的年代,但是知道这些,总会有好处的,

上面的改写0x400是因为我刚好的写入的是一个立即数,要是例如0x123这样的非立即数数据,是不会反汇编mov指令的,下面测试:

反汇编:

通过这些学习,我们可以和cpu的距离变得更近。

新的开始——LED灯汇编机器码的点亮方式的更多相关文章

  1. 第7章 使用寄存器点亮LED灯

    第7章     使用寄存器点亮LED灯 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/fir ...

  2. 第7章 使用寄存器点亮LED灯—零死角玩转STM32-F429系列

    第7章     使用寄存器点亮LED灯 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/fir ...

  3. 灵动微电子ARM Cortex M0 MM32F0010 GPIO 的配置驱动LED灯

    灵动微电子ARM Cortex M0 MM32F0010 GPIO的配置 目录: 1.前言 2.学习方法简要说明 3.要点提示 4.注意事项 5.MM32F0010系统时钟的配置 6.MM32F001 ...

  4. 30个物联网传感器小实验:三行代码点亮LED灯

    30个物联网传感器小实验:三行代码点亮LED灯 三行代码点亮LED灯 LED灯闪烁 LED灯调亮度 LED淡入淡出 不写一行代码点亮LED灯 全彩RGB灯 面包板 30个物联网传感器小实验:三行代码点 ...

  5. linux IMX6 汇编点亮一个LED灯

    驱动Linux引脚与驱动STM32其实是一样的,都是在操作寄存器,在相应的寄存器上附上相应的值即可驱动. IMX6U手册上有各个管脚的命名,跟STM32不同,IOMUXC_SW_MUC_CTL_PAD ...

  6. AT89S52汇编实现l通过按键中断切换led灯的四种闪烁模式(单灯左移,单灯右移,双灯左移,双灯右移)

    ;通过P1口控制8路LED的四种闪烁模式,单独LED灯左移,单独LED灯右移,相邻两个灯左移,相邻两个灯右移;通过一个外部中断0来检测按键的跳变沿来切换闪烁模式,第一次按键按下弹起,灯的闪烁状态由单独 ...

  7. 25个led灯新玩法

    Microbit板子的25个led灯,是5X5的阵列,led(lights emitting diodes)中文叫发光二极管,有单向导电性,还发光,有各种颜色的,红,蓝,黄等等.mPython可以让你 ...

  8. C语言版——点亮LED灯,深入到栈

    在上一篇进行了汇编语言的编写之后,我们采用C语言来编写程序,毕竟C语言才是我们使用最多的语言. 仅仅是点亮LED灯显然太过于简单,我们需要分析最后的反汇编,了解函数调用栈,深入C语言骨髓去分析代码,并 ...

  9. 起航,第一个程序——还是LED灯

    如同学基本语言一样,helloworld是很多语言的第一个程序.在嵌入式开发中,点亮LED灯也是各种架构和开发板的第一个程序,其中很多东西是和单片机例如stm32是类似的,只是,现在我们没有了库函数, ...

随机推荐

  1. C语言stat()函数:获取文件状态

    相关函数:fstat, lstat, chmod, chown, readlink, utime 头文件:#include<sys/stat.h>  #include<uninstd ...

  2. JVM调优总结(一)-- 堆和栈的基本概念

    数据类型 Java虚拟机中,数据类型可以分为两类:基本类型和引用类型.基本类型的变量保存原始值,即:他代表的值就是数值本身:而引用类型的变量保存引用值.“引用值”代表了某个对象的引用,而不是对象本身, ...

  3. jQuery 自定义网页滚动条样式插件 mCustomScrollbar 的介绍和使用方法(转)

    系统默认的滚动条样式,真的已经看的够恶心了.试想一下,如果在一个很有特色和创意的网页中,出现了一根系统中默认的滚动条样式,会有多么的别扭. 为了自己定义网页中的滚动条的方法,我真的已经找了很久了,就目 ...

  4. NPM Node.js 包管理

    1.NPM 简介 1.1 NPM Node.js® 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,可方便地构建快速,可扩展的网络应用程序的平台.Node.js 使用事件驱动, ...

  5. Android lrucache 实现与使用(Android内存优化)

    什么是LruCache? LruCache实现原理是什么? 这两个问题其实可以作为一个问题来回答,知道了什么是 LruCache,就只然而然的知道 LruCache 的实现原理:Lru的全称是Leas ...

  6. 向量运算 与 JavaScript

    二维向量都包含两个值:方向(direction)及大小(magnitude)   这两个值可以表达出各种各样的物理特性来,比如力和运动.如两个物体间的碰撞检测.   向量的大小   虽说二维向量是对大 ...

  7. django ---- models继承

    django 中各个models之前可以有继承关系.这种继承关系又可以分成三种情况: 1.简单继承 2.抽象继承 3.代理 一.简单继承: model定义 from django.db import ...

  8. Logstash 报错 An unexpected error occurred! :error => bad URI(is not URI?,是因为路径c:\program files\logstash\logstash.bat 中有空格

    I am trying to run logstash under c:\program filesbut I get an error An unexpected error occurred! : ...

  9. Java操作XML的JAXB工具

    在java中操作XML的工作中中,比较方便的工具是JAXB(Java Architecture for XML Binding). 利用这个工具很方便生成XML的tag和Java类的对应关系.参照网上 ...

  10. JDK1.6新特性,基础类库篇,IO支持

    1. JDK1.6中提供了java.io.Console类 JDK1.6中提供了java.io.Console 类专用来访问基于字符的控制台设备.你的程序如果要与Windows下的cmd或者Linux ...