• GPIO


    一、什么是GPIO?
     
        首先应该理解什么是GPIO。GPIO,英文全称为General-Purpose IO ports,也就是通用IO口。在嵌入式系统中经常有数量众多,可是结构却比較简单的外部设备/电路,对这些设备/电路有的须要CPU为之提供控制手段有的则须要被CPU用作输入信号并且,很多这种设备/电路仅仅要求一位,即仅仅要有开/关两种状态就够了,比方灯亮与灭。对这些设备/电路的控制,使用传统的串行口或并行口都不合适。所以在微控制器芯片上一般都会提供一个“通用可编程IO接口”,即GPIO。接口至少有两个寄存器,即“通用IO控制寄存器”与“通用IO数据寄存器”数据寄存器的各位都直接引到芯片外部,而对数据寄存器中每一位的作用,即每一位的信号流通方向时输入还是输出,则能够通过控制寄存器中相应位独立的加以设置。这样,有无GPIO接口也就成为微控制器差别于微处理器的一个特征。
     
        在实际的MCU中,GPIO是有多种形式的。比方,有的数据寄存器能够依照位寻址,有些却不能依照位寻址,这在编程时就要区分了。比方传统的8051系列,就区分成可位寻址和不可位寻址两种寄存器。另外,为了使用的方便,非常多mcu把glue logic等集成到芯片内部,增强了系统的稳定性能,比方GPIO接口除去两个标准寄存器必须具备外,还提供上拉寄存器,能够设置IO的输出模式是高阻,还是带上拉的电平输出,或者不带上拉的电平输出。这在电路设计中,外围电路就能够简化不少。
     
        明确了这个道理,不同的MCU,提供的GPIO口的数目不同,可选择的glue logic也不同。所以,在了解共性的基础上去了解个性。
     
        另外须要注意的是,对于不同的计算机体系结构,设备可能是port映射,也可能是内存映射的。假设系统结构支持独立的IO地址空间,而且是port映射,就必须使用汇编语言完毕实际对设备的控制,由于C语言并没有提供真正的“port”的概念。假设是内存映射,那就方便的多了。举个样例,比方像寄存器A(地址假定为0x48000000)写入数据0x01,那么就能够这样设置了。
     

    #define A (*(volatile unsigned long *)0x48000000)

    ...

        A = 0x01;

    ...

     
        这实际上就是内存映射机制的方便性了。当中volatilekeyword是嵌入式系统开发的一个重要特点。这个就不再这里总结了。上述表达式拆开来分析,首先(volatile unsigned long *)0x48000000的意思是把0x48000000强制转换成volatile unsigned long类型的指针,暂记为p,那么就是#define A *p,即A为P指针指向位置的内容了。这里就是通过内存寻址訪问到寄存器A,能够读/写操作。
    我们在这里就来看看通常在嵌入式c编程中是怎样来操作这些可内存寻址的寄存器:
    #define CTL_REG_READ(addr)  (*(volatile unsigned long *)(addr))
    #define CTL_REG_WRITE(addr, val) (*(volatile unsigned long *)(addr)=(var))
     
    二、S3C2410的GPIO的特点
     
        首先看看introduction。
     

    · 117-bit general purpose I/O ports / 24-ch external interrupt source

     
        可见,s3c2410的GPIO有117pin,以下应该到9 IO ports看看具体部分了。
     

    The S3C2410X has 117 multi-functional input/output port pins. The ports are:
    — Port A (GPA): 23-output port
    — Port B (GPB): 11-input/output port
    — Port C (GPC): 16-input/output port
    — Port D (GPD): 16-input/output port
    — Port E (GPE): 16-input/output port
    — Port F (GPF): 8-input/output port
    — Port G (GPG): 16-input/output port
    — Port H (GPH): 11-input/output port

     
        这么多的IO口,相当于把117个io port划分为8个组,每一个组也叫一个Port,每一个Port控制对应数量个port,事实上非常多是复合功能的,既能够作为普通的IO口使用,也能够作为特殊外设接口。在程序设计时,要对总体的资源有所规划,初始化时就应该把全部资源安排合理。这样才会避免出现故障。当然,只做一个最简单的led灯实验,倒是省去了非常多步骤。
     
        如今的8个port,针对于每一个port都存在上面提到的两个寄存器,其寄存器是类似的。除了两个通用寄存器GPxCON、GPxDAT外,还提供了GPxUP用于确定是否使用内部上拉电阻(当中x为A-H,须要注意的是没有GPAUP)。应用的主要步骤就是:
     
        ·设置GPIO控制寄存器GPxCON
        ·设置GPIO上拉寄存器GPxUP
     
        初始化完毕后,就能够通过对GPxDAT的操作来实现对应的应用了。当中,PORT A与PORT B-H在功能选择方面有所不同GPACON的每一位对应一根引脚(共23pin有效)。当某位设为0,对应引脚为输出引脚,由于Port A控制的23个pin仅仅能进行输出,所以也就没有输入的控制此时往GPADAT对应的位中写0/1,能够让引脚输出低电平/高电平当某位设为1,则对应引脚为地址线,或者用于地址控制,此时GPADAT没实用了。一般而言,GPACON通常全设为1,以便訪问外部存储器件。PORT B-H在寄存器操作方面全然同样。GPxCON中每两位控制一根引脚00表示输入,01表示输出,10表示特殊功能,11保留。GPxDAT用于读/写引脚:当引脚设为输入时,读此寄存器可知对应引脚状态是高/低;当引脚设为输出时,写此寄存器对应位能够使对应引脚输出低电平或高电平。GPxUP:某位设为0,对应引脚无内部上拉;为1,对应引脚使用内部上拉。关于特殊功能,那就得结合特殊外设来进行设置了。
     
        这算是最简单的部分。完毕一个led灯实验,能够用来做兴许实验的调试手段。
     
    基本实验一:LED灯循环点亮
     
        在EDUKIT-III实验箱上,有四个LED灯,与IO口的相应关系为GPF[7:4]----LED[4:1]。当IO引脚输出为低电平的时候,LED灯被点亮。仅仅须要关注三个寄存器GPFCONGPFDATGPFUP。因为硬件电路的关系,设置上拉电阻与否并不影响LED灯的点亮,所以GPFUP能够不必考虑。剩下的就是GPFCON和GPFDAT。
     
        我參考了《S3C2410全然开发》和vivi源码,对前者的源码进行了完好和修正,形成了两个版本号。版本号1是採用ARM汇编语言完毕,版本号2採用C语言完毕。版本号1练习了宏定义函数,子程序等,相对而言比較简单。版本号2重点练习了软件架构,尽管短小,可是仍然模仿了vivi的软件架构。仅仅是没有必要写复杂的Makefile,所以仅仅写了比較简单的Makefile。在编写过程中,发现自己对ld,objcopy,和一些细节没有非常好的把握,经过查看资料,已经基本掌握,兴许工作须要就这些工具进行深入的学习,目标是可以熟练掌握。
     
    ARM汇编版本号:
    @ register address
    .equ WTCON, 0x53000000
    .equ GPFCON, 0x56000050
    @ offset value
    .equ oGPFDAT, 0x04
    @ macro defination LED_ON
    @ you should initial IO pins about leds in order to use this macro
    .macro LED_ON led_value //注意 在这里定义了一段宏,相当于函数,在以下能够直接通过宏名+參数值来调用该宏
     ldr r1, =/led_value
     str r1, [r0, #oGPFDAT] //将r1中的值赋给0x5300000054寄存器中,也就是GPFDAT寄存器
     bl delay //调用延时子程序
    .endm
    .text
    .global _start @ specified by GNU ld. Here is
      @ ultimately 0x00000000,and you
      @ can fine this in Makefile.
    _start:
     @ disable watch dog timer
     @ otherwise mcu will reset at fixed interval, and
     @ you will find led on and off in abnormal way.
     mov r0, #WTCON
     mov r1, #0x00
     str r1, [r0]
     @ initial IO pins: GPF[7:4]
     @ please read datasheet //開始初始化pin相应的Port控制寄存器和上拉寄存器
     ldr r0, =GPFCON
     ldr r1, =0x5500  //为什么要将GPFCON寄存器设置为0x5500就要一位位的来分析GPFCON寄存器,由于4个LED灯与IO口的相应关系为:GPF[7:4]----LED[4:1],我们知道在Port F中每两位来控制一个引脚,并且要将引脚设置为输出才干控制LED,所以GPF7也就是[15:14]=01,GPF6也就是[13:12]=01,同理GPF5也就是[11:10]=01,GPF4也就是[9:8]=01,所以GPFCON要被设置为0x5500
     str r1, [r0]
     @ start to light the leds
     @ led on when low voltage
    1:  //循环的依次点亮4个LED灯,也就是依次将以下的值设置给GPFDAT寄存器,通过以下的0xd0,0x70,0xe0,0xb0能够知道在GPFDAT寄存器中的[7:4]来控制LED的亮灭。
     LED_ON 0xd0
     LED_ON 0x70
     LED_ON 0xe0
     LED_ON 0xb0
     
     b 1b
     @ meaningless but readable
    stop:
     b stop
    @
    @ SUB Routine
    @
    @ not accurately, but good effect
    delay:
     mov r2, #0x10000
    2:
     subs r2, r2, #0x1
     bne 2b
     mov pc, lr
    .end
        (1)我採用了延时子程序,可是上电复位后,WDT默认是打开的。所以在程序的開始要禁用WDT。
        (2)关于ARM的跳转指令B、BL、BX要区分开。B一般用于本段内的指令跳转,而BL用于子程序调用,BX用于ARM和THUMB状态的切换。特别地说,BL指令会将下一条指令的地址复制到LR中,然后跳转到指定的地址执行程序。所以,子程序调用的模型为:
        bl delay
        ...
     
    delay:
        ...
        mov pc, lr
     
        明白了这两条,程序就不难理解了。源码见上传的附件。
     
    C语言版本号:
     
        (1)软件架构仿照了vivi,也能够说是Linux Kernel。当然,只写这么小的程序用不到这么麻烦,可是能够训练这样的架构,为写中型大型程序打好基础。
        (2)注意C语言下实现寄存器读写的(*(volatile unsigned long *)(addr))。事实上就是要掌握volatile和指针的使用方法,明确在嵌入式环境中,为什么要这样操作。
        (3)写c时,要注意头文件怎样处理。写Makefile时,要注意是否採用隐含规则,假设不採用,就要自定义明白规则,就像vivi里面的Rules.make。在这里,由于仅仅是涉及到.s的编译不採用隐含规则,所以没有把Rules.make单独拿出,其实能够单独写为Rules.make,然后在Makefile后增加include Rules.make就能够了。
        (4)要调用C子程序,必须分配堆栈空间。由于子程序调用时,要进行入栈出栈处理。又由于从nand flash启动,而nand flash在S3C2410下的特点规定堆栈不能超过4K。
    先来看看利用C语言时对寄存器的操作:
    #define GPIO_CTL_BASE 0x56000000  //定义GPIO控制寄存器的起始地址也就是GPACON的地址
    #define bGPIO(p, o) CTL_REG_READ(GPIO_CTL_BASE + (p) + (o))
    /* offset */
    #define oGPIO_CON 0x0
    #define oGPIO_DAT 0x4
    #define oGPIO_UP 0x8
     
    #define oGPIO_F  0x50  //GPFCON寄存器相相应与GPADAT的偏移
    /* registers */
    #define GPFCON  bGPIO(oGPIO_F, oGPIO_CON)
    #define GPFDAT  bGPIO(oGPIO_F, oGPIO_DAT)
    #define GPFUP  bGPIO(oGPIO_F, oGPIO_UP)
    然后来看看C代码:
    #define DELAYTIME 0x5000
    void delay(unsigned long n);
    int main(void)
    {
     unsigned char i;
     unsigned long led[4] = {0xd0, 0x70, 0xe0, 0xb0};
     GPFCON = vGPFCON; //相同vGPFCON已经初始化为0x00005500,将这个值赋值给GPFCON
     while (1) {
      for (i=0; i<4; i++) {
       GPFDAT = led[i];
       delay(DELAYTIME);
      }
     }
     return 0;
    }
    /* delay some time */
    void delay(unsigned long n)
    {
     while (n--) {
      ;
     }
    }
  • 相关阅读:
    实现Vector对象的序列化的例子
    BigDecimal
    java.io.Serializable引发的问题——什么是序列化?在什么情况下将类序列化?
    删除表中重复记录的方法
    使用PreparedStatement为不同的数据库编写可移植的数据库存取方法
    hsqldb介绍
    ant管理项目
    在jsp中点击按钮,在bean中把已经查出的数据,生成csv文件,然后在ie中自动打开
    用JAVA操作日期类型
    ORACLE默认用户的问题?
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/4511090.html
Copyright © 2020-2023  润新知