gpio接口编程实例

一、GPIO
    gpio(general purpose ports)通用输入/输出端口
    gpio的操作是所有硬件操作的基础,这是底层开发人员必须掌握的
    
    以三星公司的s3c2410/s3c2440为例做一下简要说明
    s3c2410有117个i/o        分为A~H 8组
    s3c2440有130个i/o        分为A~J 9组
    
二、通过操作寄存器来控制gpio,说明如下
    寄存器类别            中文名称            功能描述
    GPxCON                配置寄存器            配置端口的功能,如作为输入口(input)、输出口(output)以及特殊功能口(如中断信号)
    GPxUP                上拉寄存器            上、下拉电阻的使能/失能(注:上拉电阻作用为当I/O PORTS被定义为input口时,为了避免信号干                                                    扰产生不正确的值,通常会使用上拉电阻。)
    GPxDAT                数据寄存器            只作为普通i/o时用于交互数据
    GPxDRV                驱动能级寄存器        配置引脚的推拉电流能力
    
    注:不同的处理器一般由不同的位数来控制一个pin,这需要查看数据手册才能正确配置,
    关于GPxDRV寄存器有的处理器是没有这组寄存器的,关于GPA一般用于接外部存储器而不作为
    普通的i/o,使用时应当注意这几点。
    
三、上、下拉电阻的作用
    1、钳位电平
    2、抗干扰
    3、提高引脚的推拉电流能力
    
四、引脚的操作步骤
    引脚的操作无外乎3种:输出高低电平,检测引脚状态,中断。
    操作步骤概括如下:
    1、查看引脚对应寄存器地址
    2、配置引脚的功能及上下拉电阻
    3、操作数据寄存器(只限用作普通i/o时)
    注:有的引脚会通过缓冲器与外设相连,缓冲器的作用主要是提高驱动能力,隔离前后级信号。
    
五、裸机程序的设计步骤
    编写源程序、生成可执行文件、烧写程序、运行测试
    
    这个过程中要注意一些问题:
    1、C语言程序在编译器编译生成可执行程序的时会在其中加入几个启动文件代码---ctrl.o、crti.o、
    crtend.o    crtn.o等,它们是标准库文件,用来设置堆栈,然后调用main函数,它们依赖于操作系
    统,在裸机中这些代码无法执行,所以需要自己写一个启动代码。
    2、在这个启动代码中需要做以下几件事
        @1   关看门狗,防止cpu一直自动重启
        @2     设置栈指针,设置sp时候注意其值不能大于内部RAM(IRAM)的空间大小
        @3     调用c程序中的main函数
    3、没有操作系统的裸板上运行程序,只能通过JTAG烧写
    
六、实验实例:实验平台mini2440单板

    1、汇编程序(led_on.S):点亮GPB5~8的四个led灯    
    
    .text                                    @代码段从此处开始
    .global _start                            @将_start标号定义为全局可见
    _start:                                 @程序入口
                LDR     R0,=0x56000010        @将GPBCON寄存器地址值装入R0 
                MOV     R1,#0x00015400        @配置5、6、7、8管脚为输出
                STR     R1,[R0]         
                LDR     R0,=0x56000014        @将GPBDAT寄存器地址值装入R0
                MOV     R1,#0x00000000        @让5、6、7、8管脚为输出低电平
                STR     R1,[R0]     
    MAIN_LOOP:
                B       MAIN_LOOP
    工程Makefile
    
    led_on.bin : led_on.S
        arm-linux-gcc -g -c  led_on.S -o led_on.o
        arm-linux-ld -Ttext 0x0000000 -g led_on.o -o led_on_elf
        arm-linux-objcopy -O binary -S led_on_elf led_on.bin
    clean:
        rm -f   led_on.bin led_on_elf *.o
    
    2、C语言程序(led_on.c)点亮GPB5、7、8的三个led灯
    
    #define GPBCON                (*(volatile unsigned long *)0x56000010)
    #define GPBDAT                (*(volatile unsigned long *)0x56000014)
    #define GPBCON_5678_OUTPUT  0x1<<5*2 | 0x1<<6*2 | 0x1<<7*2 | 0x1<<8*2
    #define GPBDAT_5678_LOW        0x0<<5*1 | 0x0<<6*1 | 0x0<<7*1 | 0x0<<8*1

    int main(int argc, char *argv[])
    {
        GPBCON &= ~(0x3<<5*2 | 0x3<<6*2 | 0x3<<7*2 | 0x3<<8*2);
        GPBCON |= GPBCON_5678_OUTPUT;

        GPBDAT &= ~(0x1<<5*1 | 0x1<<6*1 | 0x1<<7*1 | 0x1<<8*1);
        GPBDAT |= GPBDAT_5678_LOW;    

        GPBDAT |= 0x1<<6*1;

        return 0;
    }
    
    由于前面说过需要自己编写启动代码,若是没有启动代码,交叉编译器会报告缺少
    _start入口,所以必须自己编写一个启动代码
    
    .text
    .global _start
    _start:
            
    halt:
        b    halt
        
    ???这不是一个死循环吗?坑爹!这样led灯肯定不亮啊!!!
    好吧,先不管那么多,我们先用下面的Makefile编译,试试看灯到底亮不?
    
    led_on_test.bin:start.S led_on.c
        arm-linux-gcc -g -c start.S -o start.o
        arm-linux-gcc -g -c led_on.c -o led_on.o
        arm-linux-ld -Ttext 0x00000000 -g led_on.o start.o -o led_test_elf   #@要点1
        arm-linux-objcopy -O binary -S led_test_elf led_test.bin
        arm-linux-objdump -D -m arm led_test_elf > led_on.dis 
    clean:
        @rm *.o *.dis *elf *.bin
    
        二进制文件生成了,先不管把led_test.bin烧入板子,重启板子。看一看,我靠狗日的,
    竟然led灯神奇的亮了,更加坑爹了啊?!什么原因,且待后面继续探索。
        下面是另一个正确的启动代码,按部就班的操作吧!
        
    .text
    .global _start
    _start:
        ldr r0, =0x53000000
        ldr r1, =0x0
        str r1, [r0]

        ldr sp, =1024*4

        bl  main    

    halt:
        b    halt

    下面是Makefile
    led_on.bin:start.S led_on.c
        arm-linux-gcc -g -c start.S -o start.o
        arm-linux-gcc -g -c led_on.c -o led_on.o
        arm-linux-ld -Ttext 0x00000000 -g start.o led_on.o -o led_on_elf         #@要点2
        arm-linux-objcopy -O binary -S led_on_elf led_on.bin
        arm-linux-objdump -D -m arm led_on_elf > led_on.dis
            
    clean:
        @rm *.o *.dis *elf *.bin
        
        我们先使用make执行第一个目标生成led_on.bin ,烧入led_on.bin,然后重启板子,
    看看吧!发现可以正常点亮led灯。靠!当然这不用怀疑了,正确的东西怎能不行呢!
    可是为什么前面的那个坑爹的启动代码竟然可以点亮led灯呢???这的确有点让人费解
    仔细观察下:
    !!!请注意Makefile中的标注要点1 和 要点2  不同点在于链接时候顺序不一样呗!
    链接器在链接时候是以一种从左到右的顺序链接的。就算是这样又怎么了,不是说程序启动时候
    的入口应该都是_start吗???
    要不改下上面的链接顺序, 重新试一下吧!发现还真是这回上面的错误启动代码,不能将led点亮了
    ,虽然现象是正常了(与我们的理论推测结果一致),可是为什么呢?要不看看两次生成的反
    汇编代码吧!
    
    为交换链接顺序时错误的启动代码情况时反汇编如下(只看核心代码部分):
    00000000 <main>:
       0:    e52db004     push    {fp}        ; (str fp, [sp, #-4]!)
       4:    e28db000     add    fp, sp, #0    ; 0x0
       8:    e24dd00c     sub    sp, sp, #12    ; 0xc
       c:    e50b0008     str    r0, [fp, #-8]
      10:    e50b100c     str    r1, [fp, #-12]
      14:    e3a02456     mov    r2, #1442840576    ; 0x56000000
      18:    e2822010     add    r2, r2, #16    ; 0x10
      1c:    e3a03456     mov    r3, #1442840576    ; 0x56000000
      20:    e2833010     add    r3, r3, #16    ; 0x10
      24:    e5933000     ldr    r3, [r3]
      28:    e3c33bff     bic    r3, r3, #261120    ; 0x3fc00
      2c:    e5823000     str    r3, [r2]
      30:    e3a02456     mov    r2, #1442840576    ; 0x56000000
      34:    e2822010     add    r2, r2, #16    ; 0x10
            …………………………中间省略n行………………………………
      a0:    e1a00003     mov    r0, r3
      a4:    e28bd000     add    sp, fp, #0    ; 0x0
      a8:    e8bd0800     pop    {fp}
      ac:    e12fff1e     bx    lr

    000000b0 <_start>:
      b0:    eafffffe     b    b0 <_start>
    交换链接顺序后错误的启动代码情况时反汇编如下:
    00000000 <_start>:
       0:    eafffffe     b    0 <_start>

    00000004 <aa>:
       4:    e52db004     push    {fp}        ; (str fp, [sp, #-4]!)
       8:    e28db000     add    fp, sp, #0    ; 0x0
       c:    e24dd00c     sub    sp, sp, #12    ; 0xc
      10:    e50b0008     str    r0, [fp, #-8]
      14:    e50b100c     str    r1, [fp, #-12]
      18:    e3a02456     mov    r2, #1442840576    ; 0x56000000
      1c:    e2822010     add    r2, r2, #16    ; 0x10
      20:    e3a03456     mov    r3, #1442840576    ; 0x56000000
      24:    e2833010     add    r3, r3, #16    ; 0x10
      28:    e5933000     ldr    r3, [r3]
      2c:    e3c33bff     bic    r3, r3, #261120    ; 0x3fc00
      30:    e5823000     str    r3, [r2]
      34:    e3a02456     mov    r2, #1442840576    ; 0x56000000
            …………………………中间省略n行………………………………
      a0:    e3a03000     mov    r3, #0    ; 0x0
      a4:    e1a00003     mov    r0, r3
      a8:    e28bd000     add    sp, fp, #0    ; 0x0
      ac:    e8bd0800     pop    {fp}
      b0:    e12fff1e     bx    lr
      
    反汇编代码中依次含义如下
        0:        e52db004         push    {fp}
     地址         机器码            指令助记码
     
    其中地址指的是这条指令的存储位置,指令十六进制码(机器码)是这条指令在计算
    机中实际存储的数据形式, 指令助记码其实是指令十六进制码一种通俗一点的
    表达符号,目的是让程序员更容易看懂,试想一直让你看机器码,你是不是会疯掉啊。
    
    比较两者的反汇编代码不难知道,原来是“eafffffe     b    0 <_start>”这一条指令
    被存放在了不同的地址,才导致截然不同的结果,程序在运行时候依次由低地址往
    高地址依次取指令顺序顺序执行,所以前者在没有进入_start入口直接进入了main函数
    执行所以才出现了令人诧异的结果。
    
        总结后一定牢记:链接器在链接时候是以一种从左到右的顺序链接的,最好把启动代码
    作为第一个链接对象放在最左边,使其最先链接。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值