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

先说启动文件,st官方已结给我们做好了,但是jz2440开发板没有统一的启动文件,需要自己编写,那么,基础的arm汇编就得有所熟悉,在之后的学习中,遇到一个指令就学习一个。

(汇编)指令是CPU机器指令的助记符,经过编译后会得到一串1、0组成的机器码,可以由CPU读取执行。 (汇编)伪指令本质上不是指令(只是和指令一起写在代码中),它是编译器环境提供的,目的是用来指导编译过程,经过编译后伪指令最终不会生成机器码。

ARM官方的ARM汇编风格:指令一般用大写、Windows中IDE开发环境(如ADS、MDK等)常用。如: LDR R0, [R1] GNU风格的ARM汇编:指令一般用小写字母、linux中常用。如:ldr r0, [r1]

ARM采用RISC架构,CPU本身不能直接读取内存,而需要先将内存中内容加载入CPU中通用寄存器中才能被CPU处理。 ldr(load register)指令将内存内容加载入通用寄存器。 str(store register)指令将寄存器内容存入内存空间中。 ldr/str组合用来实现 ARM CPU和内存数据交换。

先看流水灯的一段启动代码:

  1. @******************************************************************************
  2. @ Filecrt0.S
  3. @ 功能:通过它转入C程序
  4. @******************************************************************************
  5.  
  6. .text
  7. .global _start
  8. _start:
  9. ldr r0, =0x53000000 @ WATCHDOG寄存器地址
  10. mov r1, #0x0
  11. str r1, [r0] @ 写入0,禁止WATCHDOG,否则CPU会不断重启
  12.  
  13. ldr sp, =* @ 设置堆栈,注意:不能大于4k, 因为现在可用的内存只有4K
  14. @ nand flash中的代码在复位后会移到内部ram中,此ram只有4K
  15. bl main @ 调用C程序中的main函数
  16. halt_loop:
  17. b halt_loop

预备知识:摘自 http://blog.csdn.net/qq506124204/article/details/7952966

mov   r1, #0x53000000   //立即数寻址方式 
mov   r2, #0x0 
str   r2, [r1]        
立即数寻址方式,立即数要求以“#”作前缀,对于十六进制的数,还要求在#后面加上0x或者&。STR是比较重要的指令了,跟它对应的是LDR。 ARM指令集是加载/存储型的,也就是说它只处理在寄存器中的数据。那么对于系统存储器的访问就经常用到STR和LDR了。STR是把寄存器上的数据传输 到指定地址的存储器上。它的格式我个人认为很特殊:
STR(条件) 源寄存器,<存储器地址>
比如 STR R0, [R1] ,意思是R0-> [R1],它把源寄存器写在前面,跟MOV、LDR都相反。
LDR应该是非常常见了。LDR就是把数据从存储器传输到寄存器上。而且有个伪指令也是LDR,因此我有个百思不得其解的问题。看这段代码:
mov r1, #GPIO_CTL_BASE 
add   r1, r1, #oGPIO_F 
ldr   r2,=0x55aa   // 0x55aa是个立即数啊,前面加个=干什么? 
对于当中的ldr 那句,我就不明白了,如果你把=去掉,是不能通过编译的。我查了一些资料,个人感觉知道了原因:这个=应该表示LDR不是ARM指令,而是伪指令。作为伪指令的时候,LDR的格式如下:
LDR 寄存器, =数字常量/Label
它的作用是把一个32位的地址或者常量调入寄存器。嗬嗬,那大家可能会问,
“MOV r2,#0x55aa”也可以啊。应该是这样的。不过,LDR是伪指令啊,也就是说编译时编译器会处理它的。怎么处理的呢?——规则如下:如果该数字常量 在MOV指令范围内,汇编器会把这个指令作为MOV。如果不在MOV范围中,汇编器把该常量放在程序后面,用LDR来读取,PC和该常量的偏移量不能超过 4KB。

然后说一下跳转指令。ARM有两种跳转方式。
(1) mov pc <跳转地址〉
这种向程序计数器PC直接写跳转地址,能在4GB连续空间内任意跳转。
(2)通过 B BL BLX BX 可以完成在当前指令向前或者向后32MB的地址空间的跳转(为什么是32MB呢?寄存器是32位的,此时的值是24位有符号数,所以32MB)。
B是最简单的跳转指令。要注意的是,跳转指令的实际值不是绝对地址,而是相对地址——是相对当前PC值的一个偏移量,它的值由汇编器计算得出。
BL非常常用。它在跳转之前会在寄存器LR(R14)中保存PC的当前内容。

为什么要先关闭看门狗?因为板子上电,看门狗是硬件打开的,不关闭会一直复位。

再看Makefile文件:

  1. CFLAGS := -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -ffreestanding
  2. leds.bin : crt0.S leds.c
  3. arm-linux-gcc $(CFLAGS) -c -o crt0.o crt0.S
  4. arm-linux-gcc $(CFLAGS) -c -o leds.o leds.c
  5. arm-linux-ld -Ttext 0x0000000 crt0.o leds.o -o leds_elf
  6. # arm-linux-ld -Tleds.lds crt0.o leds.o -o leds_elf
  7. arm-linux-objcopy -O binary -S leds_elf leds.bin
  8. arm-linux-objdump -D -m arm leds_elf > leds.dis
  9. clean:
  10. rm -f leds.dis leds.bin leds_elf *.o

这里用到了arm的交叉编译器,-ld,是链接指令,-Ttext是指明程序段,即程序存储的地方,为什么是从0地址开始,这是因为在我使用的2440中,是以nand flash启动的,nand启动会复制nand flash中4K的内容到2440芯片的SRAM,这4K内容从地址0开始取址启动,nor启动就不需要复制内容到SARM,可以直接启动。前面-c选项,表示只编译不链接,而后面ld,表示把那些.o文件链接起来构成*elf文件,然后通过objcopy,把上面编译链接之后的目标文件转化成二进制文件即.bin文件。objcopy把一种目标文件中的内容复制到另一种类型的目标文件中. -S 表示移出所有的标志及重定位信息 ;
-O binary xyb xyb.bin 表示由xyb生成二进制文件xyb.bin。

objdump指令,-D代表反汇编所有的段,-m代表的是反汇编目标文件使用的构架,这里是arm构架,>重定向成*.dis文件。

关于Makefile中的几个编译选项:参考链接:http://blog.chinaunix.net/uid-20737871-id-1881211.html
-Wall 开启所有警告

-Wstrict-prototypes 如果函数的声明或定义没有指出参数类型,编译器就发出警告。

-O2 优化等级2

-fomit-frame-pointer选项是发布产品时经常会用到的优化选项,它可以优化汇编函数中用edp协助获取堆栈中函数参数的部分,不使用edp,而是通过计算,全部使用esp来完成。参考链接:http://www.cnblogs.com/yamadie/p/3363567.html

-ffreestanding按独立环境编译,该环境可以没有标准库,且对main()函数没有要求。最典型的例子就是操作系统内核。参考链接:http://blog.csdn.net/eroswang/article/details/1966640

终于介绍完了预备知识,这些东西有的需要理解记忆,有的只用了解熟悉即可。现在开始编写程序。就流水灯的C语言程序来说,其实和单片机一样,属于简单的。但是arm版之所以很多人买了之后就吃灰了,就是因为对只是体系的构架不熟悉,还有基本技能的缺失。这也是为什么很多人说,韦老师的课程不适合新手吧。

  1. #define GPFCON (*(volatile unsigned long *)0x56000050)
  2. #define GPFDAT (*(volatile unsigned long *)0x56000054)
  3.  
  4. #define GPF4_out (1<<(4*2))
  5. #define GPF5_out (1<<(5*2))
  6. #define GPF6_out (1<<(6*2))
  7.  
  8. void wait(volatile unsigned long dly)
  9. {
  10. for(; dly > ; dly--);
  11. }
  12.  
  13. int main(void)
  14. {
  15. unsigned long i = ;
  16.  
  17. GPFCON = GPF4_out|GPF5_out|GPF6_out; // 将LED1,2,4对应的GPF4/5/6三个引脚设为输出
  18.  
  19. while(){
  20. wait();
  21. GPFDAT = (~(i<<)); // 根据i的值,点亮LED1,2,4
  22. if(++i == )
  23. i = ;
  24. }
  25.  
  26. return ;
  27. }

这是韦老师的参考代码,硬件上是低电平LED亮。

IO口分别对应GPIO_F  4,5,6。虽然老师的代码很好地实现了流水灯,但我觉得这个代码写成一个函数更友好,可移植性也更高,要是我的LED等编程GPI0_F 0,3,7,要实现流水灯就要大改代码了,所以自己做了更改,模仿st的库函数写法,但是由于才接触2440,对它的寄存器还不熟悉,所以先不急于封装,等学了韦老师资料之后再去封装。

起航,第一个程序——还是LED灯的更多相关文章

  1. 嵌入式学习笔记(综合提高篇 第一章) -- 利用串口点亮/关闭LED灯

    1      前言 从踏入嵌入式行业到现在已经过去了4年多,参与开发过的产品不少,有交换机.光端机以及光纤收发器,停车场出入缴费系统,二维码扫码枪,智能指纹锁以及数字IC芯片开发等; 涉及产品中中既有 ...

  2. (二)重拾单片机 第一天 LED灯

    由图知道 低电平 亮,高电平 灭 控制第一个 LED1 亮灭程序代码,如下 #include<reg52.h> #define uchar8 unsigned char #define u ...

  3. 《RT-Thread Studio开发STM32》第一章~第一节《配置STM32H743XIH6点亮LED灯》

    安装RT-Thread Studio后添加相关芯片库文件,打开软件 打开SDK管理界面,安装相关的库文件,本次采用STM32H743XIH6芯片,野火开发板. 新建工程并设置相关的参数 先编译下下载到 ...

  4. 《STM32CubeMX配置STM32H743XI工程》第一讲《初始化UART,重定义printf函数,点亮一个LED灯》

    1.打开STM32CubeMX软件->新建一个工程(软件自行到ST官网下载安装) 2.输入对应的芯片型号(本次基于野火STM32H743XI Pro 开发板)点击Start Project生成项 ...

  5. 嵌入式Linux学习入门:控制LED灯

    记录自己linux学习过程,让自己能够一直坚持下去 1.原理图分析: nLED_1, nLED_2, nLED_4, 给低电平则对应LED灯亮,高电平则对应LED灯灭, S3C2440芯片GPF4-G ...

  6. 用LED灯和按键来模拟工业自动化设备的运动控制

    开场白: 前面三节讲了独立按键控制跑马灯的各种状态,这一节我们要做一个机械手控制程序,这个机械手可以左右移动,最左边有一个开关感应器,最右边也有一个开关感应器.它也可以上下移动,最下面有一个开关感应器 ...

  7. Tiny4412之蜂鸣器驱动与led灯驱动

    一:LED驱动编写 要编写LED驱动,首先的知道开发板的构造:开发板分为核心板与底板:编写驱动的第一步就是要看开发板,找到LED灯在开发板上的位置及所对应的名字:第一步就要查看核心板电路图,以及底板电 ...

  8. 嵌入式linux——点亮led灯(二)

    刚才在jz2440板子上写了一个点亮中间led的程序,前前后后十几分钟才好.最终代码 本节内容: 1. 汇编点灯 2. C点灯 3. 参数选择点灯 4. 按键点灯 1. 汇编点灯 .text .glo ...

  9. 51单片机学习笔记(郭天祥版)(1)——单片机基础和点亮LED灯

    关于单片机型号的介绍: STC89C52RC40C-PDIP 0721CV4336..... STC:STC公司 89:89系列 C:COMS 52(还有51,54,55,58,516,):2表示存储 ...

随机推荐

  1. 科普:UTF-8 GBK UTF8 GB2312 之间的区别和关系

    UTF-8:Unicode TransformationFormat-8bit,允许含BOM,但通常不含BOM.是用以解决国际上字符的一种多字节编码,它对英文使用8位(即一个字节),中文使用24为(三 ...

  2. java学习笔记15--多线程编程基础2

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note15.html,转载请注明源地址. 线程的生命周期 1.线程的生命周期 线程从产生到消亡 ...

  3. spring autowired还需要在xml中申明bean ?

    如果未自动扫描spring管理的类,则需要在xml中申明.如果自动扫描包下的类,则不需要 (如果配置了自动扫描,还是不行还需要进行手动在xml中声明,则就是工程建立的有问题,包的路径等问题)

  4. xcode的svn和git使用方法

    1.创建版本库:(参考:http://blog.csdn.net/itianyi/article/details/8601183) 方法:直接在windows服务器安装VisualSVN Server ...

  5. tomcat启动报错,找不到相应的 queue,从而引发内存泄漏

    tomcat启动报错,无法创建 bean listenerStatusChangeDealHandler, no queue 'STOCK.NOTIFY_CHANGE.INTER.CACHE.QUEU ...

  6. vue 表单 验证 async-validator

    1.使用插件async-validator async-validator 地址:https://github.com/yiminghe/async-validator 2.示例(vue+elemen ...

  7. npm发包流程

    1.注册npm 账号 https://www.npmjs.com/signup 2.初始化npm项目 npm init 根据发的包进行填写: { "name": "wen ...

  8. HttpClient 解说 (1) 基础

    前言 超文本传输协议(HTTP)或许是当今互联网上使用的最重要的协议了. Web服务,有网络功能的设备和网络计算的发展,都持续扩展了HTTP协议的角色,超越了用户使用的Web浏览器范畴.同一时候,也添 ...

  9. SlidingMenu(一)

    我们一般称之为侧边栏,今天下倒腾了一下,留点笔记... 源码来自:https://github.com/jfeinstein10/SlidingMenu 来张图把: 代码API注释看看这个吧 http ...

  10. 查看tomcat启动文件都干点啥

    以下所写的都是基于Windows 操作系统,tomcat7.0版本.一直在使用tomcat但是老实说对于tomcat本身并没有一个系统的掌握,今天饶有兴致的随便看了看,做了一点笔记,写一点心得,我本人 ...