程序项目代做,有需求私信(vue、React、Java、爬虫、电路板设计、嵌入式linux等)

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芯片手册:

英文版<S3C2440芯片手册>

中文版<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引脚输出低电平需要两个步骤:

  1. 配置控制寄存器GPBCON的bit[11:10]=01,使GPB5引脚为输出模式;

  2. 配置数据寄存器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点亮流水灯

[2] 学习笔记]ARM9-mini2440之点亮第一颗LED

[3]Mini2440上的第一个程序——点亮Led

posted @ 2021-06-08 22:26  大奥特曼打小怪兽  阅读(910)  评论(0编辑  收藏  举报
如果有任何技术小问题,欢迎大家交流沟通,共同进步