跳至主要内容

【转】vivi开发笔记(五):GPIO实验

文章说明:calmarrow(lqm)原创

文章引自:http://piaoxiang.cublog.cn

 
一、什么是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也不同。所以,在了解共性的基础上去了解个性。
 
    另外需要注意的是,对于不同的计算机体系结构,设备可能是端口映射,也可能是内存映射的。如果系统结构支持独立的IO地址空间,并且是端口映射,就必须使用汇编语言完成实际对设备的控制,因为C语言并没有提供真正的"端口"的概念。如果是内存映射,那就方便的多了。举个例子,比如像寄存器A(地址假定为0x48000000)写入数据0x01,那么就可以这样设置了。
 

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

...

    A = 0x01;

...

 
    这实际上就是内存映射机制的方便性了。其中volatile关键字是嵌入式系统开发的一个重要特点。这个就不再这里总结了。上述表达式拆开来分析,首先(volatile unsigned long *)0x48000000的意思是把0x48000000强制转换成volatile unsigned long类型的指针,暂记为p,那么就是#define A *p,即A为P指针指向位置的内容了。这里就是通过内存寻址访问到寄存器A,可以读/写操作。
 
二、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口,其实很多是复合功能的,既可以作为普通的IO口使用,也可以作为特殊外设接口。在程序设计时,要对整体的资源有所规划,初始化时就应该把所有资源安排合理。这样才会避免出现问题。当然,仅仅做一个最简单的led灯实验,倒是省去了很多步骤。
 
    现在的8个端口,其寄存器是相似的。除了两个通用寄存器GPxCON、GPxDAT外,还提供了GPxUP用于确定是否使用内部上拉电阻(其中x为A-H,需要注意的是没有GPAUP)。应用的主要步骤就是:
 
    ・设置GPIO控制寄存器GPxCON
    ・设置GPIO上拉寄存器GPxUP
 
    初始化完成后,就可以通过对GPxDAT的操作来实现相应的应用了。其中,PORT A与PORT B-H在功能选择方面有所不同,GPACON的每一位对应一根引脚(共23pin有效)。当某位设为0,相应引脚为输出引脚,此时往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灯被点亮。只需要关注三个寄存器GPFCON、GPFDAT、GPFUP。由于硬件电路的关系,设置上拉电阻与否并不影响LED灯的点亮,所以GPFUP可以不必考虑。剩下的就是GPFCON和GPFDAT。
 
    我参考了《S3C2410完全开发》和vivi源代码,对前者的源代码进行了完善和修正,形成了两个版本。版本1是采用ARM汇编语言完成,版本2采用C语言完成。版本1练习了宏定义函数,子程序等,相对而言比较简单。版本2重点练习了软件架构,虽然短小,但是仍然模仿了vivi的软件架构。只是没有必要写复杂的Makefile,所以只写了比较简单的Makefile。在编写过程中,发现自己对ld,objcopy,和一些细节没有很好的把握,经过查看资料,已经基本掌握,后续工作需要就这些工具进行深入的学习,目标是能够熟练掌握。
 
ARM汇编版本:
    (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。
 
    经过测试,两个程序都可以很好的运行。四个灯循环点亮^_^
 
基本实验二:蜂鸣器
 
    这个实验比较简单,本来《S3C2410完全开发》下个实验是IO ports,用按键控制灯亮灭,但是我的开发板没有相应的硬件功能部分。所以就以buzzer实验代替。
 
    这里应该弄清楚蜂鸣器的工作原理,并且知道硬件电路应该怎么设计。从百度上搜索了一篇文章,讲解得比较清晰。网址如下: http://www.ourmpu.com/mcujx/fmqsy.htm
 
【整理部分资料放在这里:
 
    蜂鸣器是一种一体化结构的电子讯响器,广泛应用于计算机、打印机、复印机、报警器、电子玩具、汽车电子设备、电话机、定时器等电子产品中作发声器件;蜂鸣器主要分为压电式蜂鸣器和电磁式蜂鸣器两种类型。
 
    压电式蜂鸣器主要由多谐振荡器、压电蜂鸣片、阻抗匹配器及共鸣箱、外壳等组成。多谐振荡器由晶体管或集成电路构成,当接通电源后(1.5~15V直流工作电压),多谐振荡器起振,输出1.5~2.5kHZ的音频信号,阻抗匹配器推动压电蜂鸣片发声。
                      
    电磁式蜂鸣器由振荡器、电磁线圈、磁铁、振动膜片及外壳等组成。接通电源后,振荡器产生的音频信号电流通过电磁线圈,使电磁线圈产生磁场,振动膜片在电磁线圈和磁铁的相互作用下,周期性地振动发声。
 
    电磁式蜂鸣器驱动原理
                      
    蜂鸣器发声原理是电流通过电磁线圈,使电磁线圈产生磁场来驱动振动膜发声的,因此需要一定的电流才能驱动它,单片机IO引脚输出的电流较小,单片机输出的TTL电平基本上驱动不了蜂鸣器,因此需要增加一个电流放大的电路。S51增强型单片机实验板通过一个三极管C8550来放大驱动蜂鸣器,原理图见下图:

    如图所示,蜂鸣器的正极接到VCC(+5V)电源上面,蜂鸣器的负极接到三极管的发射极E,三极管的基级B经过限流电阻R1后由单片机的P3.7引脚控制,当P3.7输出高电平时,三极管T1截止,没有电流流过线圈,蜂鸣器不发声;当P3.7输出低电平时,三极管导通,这样蜂鸣器的电流形成回路,发出声音。因此,我们可以通过程序控制P3.7脚的电平来使蜂鸣器发出声音和关闭。程序中改变单片机P3.7引脚输出波形的频率,就可以调整控制蜂鸣器音调,产生各种不同音色、音调的声音。另外,改变P3.7输出电平的高低电平占空比,则可以控制蜂鸣器的声音大小,这些我们都可以通过编程实验来验证。】
 
-------------------------
 
实验源代码如下(做了简单的注释)
 
文件: gpio.tar.gz
大小: 3KB
下载: 下载
 
【注:如在windows下下载,可下载后重命名为led_asm.tar.gz。然后用winrar解压。下同】

评论

此博客中的热门博文

【转】AMBA、AHB、APB总线简介

AMBA 简介 随着深亚微米工艺技术日益成熟,集成电路芯片的规模越来越大。数字IC从基于时序驱动的设计方法,发展到基于IP复用的设计方法,并在SOC设计中得到了广泛应用。在基于IP复用的SoC设计中,片上总线设计是最关键的问题。为此,业界出现了很多片上总线标准。其中,由ARM公司推出的AMBA片上总线受到了广大IP开发商和SoC系统集成者的青睐,已成为一种流行的工业标准片上结构。AMBA规范主要包括了AHB(Advanced High performance Bus)系统总线和APB(Advanced Peripheral Bus)外围总线。   AMBA 片上总线        AMBA 2.0 规范包括四个部分:AHB、ASB、APB和Test Methodology。AHB的相互连接采用了传统的带有主模块和从模块的共享总线,接口与互连功能分离,这对芯片上模块之间的互连具有重要意义。AMBA已不仅是一种总线,更是一种带有接口模块的互连体系。下面将简要介绍比较重要的AHB和APB总线。 基于 AMBA 的片上系统        一个典型的基于AMBA总线的系统框图如图3所示。        大多数挂在总线上的模块(包括处理器)只是单一属性的功能模块:主模块或者从模块。主模块是向从模块发出读写操作的模块,如CPU,DSP等;从模块是接受命令并做出反应的模块,如片上的RAM,AHB/APB 桥等。另外,还有一些模块同时具有两种属性,例如直接存储器存取(DMA)在被编程时是从模块,但在系统读传输数据时必须是主模块。如果总线上存在多个主模块,就需要仲裁器来决定如何控制各种主模块对总线的访问。虽然仲裁规范是AMBA总线规范中的一部分,但具体使用的算法由RTL设计工程师决定,其中两个最常用的算法是固定优先级算法和循环制算法。AHB总线上最多可以有16个主模块和任意多个从模块,如果主模块数目大于16,则需再加一层结构(具体参阅ARM公司推出的Multi-layer AHB规范)。APB 桥既是APB总线上唯一的主模块,也是AHB系统总线上的从模块。其主要功能是锁存来自AHB系统总...

【转】GPIO编程模拟I2C入门

ARM编程:ARM普通GPIO口线模拟I2C  请教个问题: 因为需要很多EEPROM进行点对点控制,所以我现在要用ARM的GPIO模拟I2C,管脚方向我设 置的是向外的。我用网上的RW24C08的万能程序修改了一下,先进行两根线的模拟,SDA6, SCL6,但是读出来的数不对。我做了一个简单的实验,模拟SDA6,SCL6输出方波,在示波 器上看到正确方波,也就是说,我的输出控制是没问题的。 哪位大哥能指点一下,是否在接收时管脚方向要设为向内?(不过IOPIN不管什么方向都可 以读出当前状态值的阿) 附修改的RW24C08()程序: #define  SomeNOP() delay(300); /**/ /* *********************************  RW24C08   **************************************** */ /**/ /* ----------------------------------------------------------------------------- ---  调用方式:void I2CInit(void)   函数说明:私有函数,I2C专用 ------------------------------------------------------------------------------- -- */ void  I2CInit( void ) ... {  IO0CLR  =  SCL6;      // 初始状态关闭总线  SomeNOP();  // 延时   I2CStop();  // 确保初始化,此时数据线是高电平 }   /**/ /* ---------------------------------------------------------------------------- ----  调用方式:void I2CSta...

【转】cs8900网卡的移植至基于linux2.6内核的s3c2410平台

cs8900网卡的移植至基于linux2.6内核的s3c2410平台(转) 2008-03-11 20:58 硬件环境:SBC-2410X开发板(CPU:S3C2410X) 内核版本:2.6.11.1 运行环境:Debian2.6.8 交叉编译环境:gcc-3.3.4-glibc-2.3.3 第一部分 网卡CS8900A驱动程序的移植 一、从网上将Linux内核源代码下载到本机上,并将其解压: #tar jxf linux-2.6.11.1.tar.bz2 二、打开内核顶层目录中的Makefile文件,这个文件中需要修改的内容包括以下两个方面。 (1)指定目标平台。 移植前:         ARCH?= $(SUBARCH) 移植后: ARCH            :=arm (2)指定交叉编译器。 移植前: CROSS_COMPILE ?= 移植后: CROSS_COMPILE   :=/opt/crosstool/arm-s3c2410-linux-gnu/gcc-3.3.4-glibc-2.3.3/bin/arm-s3c2410-linux-gnu- 注:这里假设编译器就放在本机的那个目录下。 三、添加驱动程序源代码,这涉及到以下几个方面。(1)、从网上下载了cs8900.c和cs8900.h两个针对2.6.7的内核的驱动程序源代码,将其放在drivers/net/arm/目录下面。 #cp cs8900.c ./drivers/net/arm/ #cp cs8900.h ./drivers/net/arm/ 并在cs8900_probe()函数中,memset (&priv,0,sizeof (cs8900_t));函数之后添加如下两条语句: __raw_writel(0x2211d110,S3C2410_BWSCON); __raw_writel(0x1f7c,S3C2410_BANKCON3); 注:其原因在"第二部分"解释。 (2)、修改drivers/net/arm/目录下的Kconfig文件,在最后添加如...