zoukankan      html  css  js  c++  java
  • 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--) {
      ;
     }
    }
  • 相关阅读:
    【PAT甲级】1043 Is It a Binary Search Tree (25 分)(判断是否为BST的先序遍历并输出后序遍历)
    Educational Codeforces Round 73 (Rated for Div. 2)F(线段树,扫描线)
    【PAT甲级】1042 Shuffling Machine (20 分)
    【PAT甲级】1041 Be Unique (20 分)(多重集)
    【PAT甲级】1040 Longest Symmetric String (25 分)(cin.getline(s,1007))
    【PAT甲级】1039 Course List for Student (25 分)(vector嵌套于map,段错误原因未知)
    Codeforces Round #588 (Div. 2)E(DFS,思维,__gcd,树)
    2017-3-9 SQL server 数据库
    2017-3-8 学生信息展示习题
    2017-3-5 C#基础 函数--递归
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/4511090.html
Copyright © 2011-2022 走看看