Mini2440裸机开发之点亮LED
在之前的章节我们已经介绍了嵌入式linux开发的准备工作,主要包括:
如果你已经具备了这些知识,那么就可以直接上手开始开发了。今天我将带领大家去点亮Mini2440开发板的LED。
一、Mini2440 LED电路图
下图是Mini2440开发板LED接线图,一共有4个LED。
Mini2440主芯片引脚图:
nlED_x与CPU接线如下表:
nLED_1 | nLED_2 | nLED_3 | nLED_4 | |
GPIO | GPB5 | GPB6 | GPB7 | GPB8 |
可复用为 | nXBACK | nXREQ | nXDACK1 | nDREQ1 |
使能 | 低电平 | 低电平 | 低电平 | 低电平 |
这里我们以点亮nLED_1为例。有原理图我们知道nLED_1对应主控芯片的GPB5引脚,且当GPB5输出低电平时,nLED_1才会被点亮。而引脚是通过寄存器控制的,因此我们需要对GPB5引脚相关寄存器进行控制。
二、GPIO控制
这里为大家准备了两份S3C2440芯片手册:
2.1 GPIO资源
关于GPI相关的内容位于手册第九章,S3C2440共有24个外部中断端口,130个多功能输入/输出引脚。具体如下:
-
端口A(GPA):25 位输出端口;
-
端口B(GPB):11 位输入/输出端口 ;
-
端口C(GPC):16 位输入/输出端口;
-
端口D(GPD):16 位输入/输出端口;
-
端口E(GPE):16 位输入/输出端口 ;
-
端口F(GPF):8 位输入/输出端口;
-
端口G(GPG):16 位输入/输出端口;
-
端口H(GPH):9 位输入/输出端口;
-
端口J(GPJ):13 位输入/输出端口;
2.2 GPIO寄存器
(1) 端口配置寄存器(GPACON 至GPJCON)
S3C2440A 中,大多数端口为复用引脚。因此要决定每个引脚选择哪项功能。PnCON(引脚控制寄存器)决 定了每个引脚使用哪项功能。
如果在掉电模式中PE0 至PE7 用于唤醒信号,这些端口必须配置为输入模式。
(2) 端口数据寄存器(GPADAT 至GPJDAT)
如果端口配置为输出端口,可以写入数据到PnDAT 的相应位。如果端口配置为输入端口,可以从PnDAT 的相 应位读取数据。
(3) 端口上拉寄存器(GPBUP 至GPJUP)
端口上拉寄存器控制每个端口组的使能/禁止上拉电阻。当相应位为0 时使能引脚的上拉电阻。当为1 时禁止 上拉电阻。
如果使能了上拉电阻,那么上拉电阻与引脚的功能设置无关(输入、输出、DATAn、EINTn 等等) 杂项控制寄存器 此寄存器控制睡眠模式,USB 引脚和CLKOUT 选择的数据端口上拉电阻。
(4) 杂项控制寄存器
此寄存器控制睡眠模式,USB 引脚和CLKOUT 选择的数据端口上拉电阻。
(5) 外部中断控制寄存器
24 个外部中断由各种信号方式触发。EXTINT 寄存器为外部中断请求配置信号触发方式为低电平触发、高电平 触发、下降沿触发、上升沿触发或双边沿触发。
由于每个外部中断引脚包含一个数字滤波器,中断控制可以确认请求信号是否长于3 个时钟。 EINT[15:0]用于唤醒源。
2.3 端口B寄存器使用 (GPBCON、GPBDAT、GPBUP)
由于nLED_1对应的引脚为GPB5,因此这里只介绍端口B相关寄存器的相关信息。
寄存器 | 地址 | R/W | 描述 |
GPBCON | 0x56000010 | R/W | 配置端口B的引脚 |
GPBDAT | 0x56000014 | R/W | 配置B的数据寄存器 |
GPBUP | 0x56000018 | R/W | 端口B的上拉使能寄存器 |
保留 | 0x5600001C | - | 保留 |
(1) GPBCON
GPBCON | 位 | 描述 | 初始状态 |
GPB10 | [21:20] | 00:输入 01:输出 10:nxDREQ0 11:保留 | 0 |
GPB9 | [19:18] | 00:输入 01:输出 10:nxDACK0 11:保留 | 0 |
GPB8 | [17:16] | 00:输入 01:输出 10:nxDREQ1 11:保留 | 0 |
GPB7 | [15:14] | 00:输入 01:输出 10:nxDACK1 11:保留 | 0 |
GPB6 | [13:12] | 00:输入 01:输出 10:nxBREQ 11:保留 | 0 |
GPB5 | [11:10] | 00:输入 01:输出 10:nxBACK 11:保留 | 0 |
GPB4 | [9:8] | 00:输入 01:输出 10:TCLK[0] 11:保留 | 0 |
GPB3 | [7:6] | 00:输入 01:输出 10:TOUT3 11:保留 | 0 |
GPB2 | [5:4] | 00:输入 01:输出 10:TOUT2 11:保留 | 0 |
GPB1 | [3:2] | 00:输入 01:输出 10:TOUT1 11:保留 | 0 |
GPB2 | [1:0] | 00:输入 01:输出 10:TOUT0 11:保留 | 0 |
由上表可知,B端口的控制寄存器可以将每个引脚配置为四种模式:
- 00:输入模式
- 01:输出模式
- 10:功能扩展模式
- 11:保留模式
若要使GPB5引脚输出低电平,须将GPB5配置为输出模式,即将Bit[11:10]配置为01。 GPBCON的地址为0x56000010,因此将0x400写入地址0x56000010即可:
# write GPBCON
ldr r1,=0x56000010
mov r0,#0x400
str r0,[r1]
(2) GPBDAT
GPBDAT | 位 | 描述 |
GPB[10:0] | [10:0] |
当端口配置为输入端口时,相应位为引脚状态。当端口配置为输出端口时,引脚状态将与相应位相同。当端口配置为功能引脚,将读取到未定义值。 |
由表可知,引脚的状态与对应的位的状态相同,因而可以通过配置GPBDAT每一位的状态从而配置引脚的状态。 将数据寄存器的bit5配置为0即可把引脚GPB5配置为低电平:
# write GPBDAT
ldr r1,=0x56000014
mov r0,#0x00
str r0,[r1]
(3) GPBUP
GPBUP | 位 | 描述 |
GPB[10:0] | [10:0] |
0:使能附加上拉功能到相应端口引脚 1:禁止附加上拉功能到相应端口引脚 |
当引脚的驱动能力不足时,需要配置上拉寄存器。由于引脚输出低电平即可点亮led,因此上拉寄存器无需配置。
2.4 总结
以上可知,配置GPB5引脚输出低电平需要两个步骤:
-
配置控制寄存器GPBCON的bit[11:10]=01,使GPB5引脚为输出模式;
-
配置数据寄存器GPBDAT的bit5=0,使GPB5引脚输出低电平;
三、编写代码
我们在samba共享文件夹下创建一个project文件夹,专门用来放置我们的测试代码。
3.1 纯汇编
.startS:
# nLED_1 GPB5=0
.text
.global _start
_start:
b light_led
light_led:
# write GPBCON
ldr r1,=0x56000010
mov r0,#0x400
str r0,[r1]
# write GPBDAT
ldr r1,=0x56000014
mov r0,#0x00
str r0,[r1]
b halt
halt:
b halt
Makefile:
all:start.o
# 链接
arm-linux-ld -Ttext 0x30000000 -o start.elf $^
# 转为bin -S 不从源文件中复制重定位信息和符号信息到目标文件中
arm-linux-objcopy -O binary -S start.elf start.bin
# 反汇编 -D反汇编所有段
arm-linux-objdump -D start.elf > start.dis
%.o : %.S
arm-linux-gcc -g -c $^
%.o : %.c
arm-linux-gcc -g -c $^
.PHONY: clean
clean:
rm *.o *.elf *.bin *.dis
注意:arm-linux-ld -Ttext 0x30000000将目标文件start.elf文件中的.text代码段链接到起始地址0x30000000,0x30000000是S3C2440 的SDRAM起始地址。
进入目标文件夹,使用make命令进行编译,编译成功后,我们通过MiniTools工具进行程序烧录。烧录有两种方式;
- 直接下载到RAM中,下载地址为0x30000000,下载后程序会直接运行。
- 将start.bin烧录到NAND FLASH中,下载到NAND FLASH,这样程序可以保存下来,程序烧录完成后,将Mini2440开发板调至NAND FLASH启动,然后给开发板上电,观察nLED_1是否已经点亮:
通过上图这种方式烧录到NAND FLASH是不能运行的,Mini2440官方手册给的例子是同时也要将SuperBoot烧进去,但是我尝试了一下,断电之后NAND启动还是有问题的,所以不要通过上面这种方式烧录NAND。
我们可以把我们的程序当做uboot程序,通过Linux裸机进行烧录,这样烧录后的程序可以正常运行:
这里烧录的程序有两点需要注意:
- 程序好像不能大于300kb,我尝试了400多kb时会烧录失败;
- 确保从NAND启动时,这个程序是可以正常运行的,不要跑飞了(如果程序在不停的重启运行,估计就是代码跑飞了);,
这里顺带提一下,如果我们从NAND启动,分为两种情况:
1. 程序小于4kb的时候,如果代码中都是位置无关码,我们链接地址为0x00,0x30000000都可以。NAND启动的时候会将NAND前4kb代码复制到片内SRAM中,然后从0x00地址开始运行,因为是位置无关码,所以都可以运行。但是如果代码中有位置相关码,链接地址只可以是0x00.
2. 程序大于4kb的时候,那就只能在SDRAM去跑了,这就需要满足下面条件:
-
- SDRAM必须要初始化;
- 有一段代码把NAND里面的程序搬到SDRAM里面去运行;
上面的两个条件都需要运行程序,就只能在SRAM里面运行,所以程序的前面4kb的代码必须完成这两项工作,在完成了这两项工作后从SRAM跳转到SDRAM里面执行余下的工作;这样就实现了程序从NAND启动了。但程序是如何从SRAM跳转到SDRAM接着去执行的呢?以下面代码为例:
@************************************************************************* @ File:head.S @ 功能:设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行 @************************************************************************* .equ MEM_CTL_BASE, 0x48000000 .equ SDRAM_BASE, 0x30000000 .text .global _start _start: bl disable_watch_dog @ 关闭WATCHDOG bl memsetup @ 设置存储控制器 bl copy_nand_to_sdram @ 复制NAND代码到SDRAM中 ldr pc, =on_sdram @ 跳到SDRAM中继续执行 on_sdram: ldr sp, =0x34000000 @ 设置堆栈 bl main halt_loop: b halt_loop
这里就要求在代码跳转到SDRAM之前的代码必须是位置无关码,跳转的时候使用位置相关码。
- bl :位置无关码 -- 跳转位置与启动代码的链接地址无关;
- ldr:位置相关码 --跳转位置与链接地址相关;
S3C2440的SDRAM起始地址为0x30000000,所以启动代码的链接地址为0x30000000,这个地址是在编译链接操作的时候指定的,在makefile里面;
当执行bl的时候,跳转地址跟SDRAM链接地址无关(当前指令在SRAM内地址),这个地址还是本来的地址范围,SRAM的范围,所以还是在SRAM里面跳转。
当执行ldr的时候,跳转地址跟SDRAM连接地址有关,这里跳转到0x30000000+当前指令在SRAM内地址,这个地址已经是SDRAM的地址范围了,所以完成跳转。需要注意的是在跳转到SDRAM运行之前的代码,一定不可以跳转到超出SRAM的大小,不然程序会跑飞掉。
这篇博客重点不是介绍这块内容,感兴趣的可以先阅读学习笔记----S3C2440 从NANDFLASH启动的设计原理与过程。
3.2 汇编、c混合
代码分为两个部分,一个是汇编文件,一个是c文件:
start.S
.text
.global _start
_start:
# 设置内存: sp 栈
ldr sp, =4096 /* nand启动,4K片内SRAM起始地址0x00 */
#ldr sp, =0x40000000+4096 /* nor启动,4k片内SRAM起始地址0x40000000 */
# 调用main
bl main
halt:
b halt
led.c:
int main() { unsigned int *p_con =(unsigned int *) 0x56000010; unsigned int *p_dat =(unsigned int *) 0x56000014; /* 配置GPB5引脚为输出模式 */ *p_con = 0x400; while(1) { /* 配置GPB5引脚输出低电平*/ *p_dat = 0; } return 0; }
Makefile:
all:start.o led.o # 链接 arm-linux-ld -Ttext 0x30000000 -o led.elf $^ # 转为bin -S 不从源文件中复制重定位信息和符号信息到目标文件中 arm-linux-objcopy -O binary -S led.elf led.bin # 反汇编 -D反汇编所有段 arm-linux-objdump -D led.elf > led.dis %.o : %.S arm-linux-gcc -g -c $^ %.o : %.c arm-linux-gcc -g -c $^ .PHONY: clean clean: rm *.o *.elf *.bin *.dis
注意:arm-linux-ld -Ttext 0x30000000将目标文件start.elf文件中的.text代码段链接到起始地址0x30000000,进入目标文件夹,使用make命令进行编译,编译成功后,烧录程序的过程同上,这里就不重复介绍了。
这里arm-linux-gcc 并没有指定--march=armv4t参数,主要是因为我使用的arm-linux-gcc-4.3.2编译时默认采用的就是armv4t架构。
四、代码下载
Young / s3c2440_project【1.led_asm】
Young / s3c2440_project【2.led_c】
参考文章:
[1] 二.mini2440点亮流水灯