硬件图中,找到LED灯对应的GPIO
从二极管方向看出,端口输出低电平时,电流经过,LED点亮。
再到数据手册查找对应的寄存器
GPBCON:模式选择,GPIO配置可以有3中模式,输入、输出、DAC
GPBDAT:数据位
GPBUP:上拉禁能标志
LED1对应的是GPB5
(1)配置GPB5为输出引脚,给GPBCON寄存器写入(1 << 10)
(2)设置GPB5输出低电平,给GPBDAT寄存器写入(1 << 5)
(3)GPBUP保持默认
/*
* LED.S
* LED1:GPB5
*/
.text
.global _start
_start:
/* 配置GPF4为输出引脚
* 把0x400写到地址0x56000010
*/
ldr r1, =0x56000010 /* 0x56000010比较大,使用伪指令ldr,将0x56000010写入r1 */
ldr r0, =0x400 /* mov r0, #0x400 带#表示立即数 */
str r0, [r1] /* r1作为地址,将r0的数据存放到r1地址对应位置 */
/* 设置GPB5输出低电平
* 把0写到地址0x56000014
*/
ldr r1, =0x56000014
ldr r0, =0 /* mov r0, #0 */
str r0, [r1]
/* 死循环 */
halt:
b halt
编译常用的命令在gcc/ld/objcopy/objdump命令中整理。
在终端中输入命令:
jw@pc:~/w/02-led$ arm-linux-gcc -c -o led_on.o LED.S
jw@pc:~/w/02-led$ arm-linux-ld -Ttext 0 led_on.o -o led_on.elf
jw@pc:~/w/02-led$ arm-linux-objcopy -O binary -S led_on.elf led_on.bin
jw@pc:~/w/02-led$ ls -l
总用量 20
-rwxrwxr-x 1 jw jw 36 5月 18 22:06 led_on.bin
-rwxrwxr-x 1 jw jw 33502 5月 18 22:05 led_on.elf
-rw-rw-r-- 1 jw jw 671 5月 18 22:05 led_on.o
-rwxrw-rw- 1 jw jw 522 5月 18 22:00 LED.S
也可以使用makefile:
all:
arm-linux-gcc -c -o led_on.o LED.S
arm-linux-ld -Ttext 0 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
使用JLink往开发板的NOR FLASH中烧写这个程序
烧写过程与上一篇文章(一)MINI2440–恢复出厂相同。
烧写成功后,重启,led1被点亮了。
ARM是RISC结构,在ARM架构下, 数据从内存到CPU之间的移动只能通过LDR/STR指令来完成. 而MOV只能在寄存器之间移动数据,或者把立即数移动到寄存器中,并且数据的长度不能超过8位
比如想把数据从RAM中某处读取到CPU寄存器中,只能使用ldr
ldr r0,=label 用于加载立即数或一个地址值到指定寄存器中
如果label是立即数: ldr r0,=0X123 ;将0X123存入r0中
如果name是个标识符: ldr r0,=label_1 ;将label_1所指向的地址值存入r0中
ldr r0,[r1] ;将R1地址中的值存到r0中
ldr r1,[r2,#16] ;将(r2+16)地址中的内容存到r1中
ldr r1,[r2],#4 ;将r2地址中的内容存到r1中,同时r2=r2+4
str r1,[r2] ; 将r1中的值存到r2所指定的地址中
str r1,[r2,#4] ;将r1中的值存到r2+4所指定的地址中
str r1,[r2],#4 ;将r1中的值存到r2所指定的地址中, 同时r2=r2+4
如果要使用C语言,则需要为C程序搭建环境,主要是配置堆栈,然后调用main函数。
而堆栈的栈顶位置随着Nand启动、非Nand启动而有所不同,对于MINI2440的内存空间,可以参考:
s3c2440启动过程分析
s3c2440启动过程详解 (主要讲启动时nand nor的地址映射)
汇编程序,主要为C程序搭建运行环境和调用C:
/* Start.S */
.text
.global _start
_start:
/* 关闭看门狗 */
ldr r0, =0x53000000
ldr r1, =0
str r1, [r0]
/* 设置内存: sp 栈 */
/* 分辨是nor/nand启动
* 写0到0地址, 再读出来
* 如果得到0, 表示0地址上的内容被修改了
* 说明0地址对应片内sram,因为片内sram不用经过我们配置就可以被读写, 这就是nand启动模式
* 否则就是nor启动
*/
mov r1, #0
ldr r0, [r1] /* 读出原来的值备份 */
str r1, [r1] /* 0->[0] */
ldr r2, [r1] /* r2=[0] */
cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */
ldr sp, =0x40000000+4096 /* 先假设是nor启动 */
moveq sp, #4096 /* nand启动 */
streq r0, [r1] /* 恢复原来的值 */
/* 调用main */
bl main
halt:
b halt
C程序操作寄存器:
/* led.c */
int main()
{
unsigned int *pGPBCON = (unsigned int *)0x56000010;
unsigned int *pGPBDAT = (unsigned int *)0x56000014;
/* 配置GPB5为输出引脚 */
*pGPFCON = 0x400;
/* 设置GPB5输出0 */
*pGPFDAT = 0;
return 0;
}
Makefile
all:
arm-linux-gcc -c -o led.o led.c
arm-linux-gcc -c -o start.o start.S
arm-linux-ld -Ttext 0 start.o led.o -o led.elf
arm-linux-objcopy -O binary -S led.elf led.bin
arm-linux-objdump -D led.elf > led.dis
clean:
rm *.bin *.o *.elf *.dis