任何单片机的学习都是从了解GPIO开始的,GPIO是英文General Purpose Input Output (通用输入/输出)的输出,实际上就是微处理器的功能管脚。我们知道,现实世界中的信号有两种,一种是连续变化的模拟信号,如声音;另外一种,则是离散变化的数字信号,如电流的通断,微处理器为了能够处理这两种信号,它的GPIO管脚也有两类,一类是处理数字信号的数字管脚,另外一类则是处理模拟信号的模拟管脚。一般来说,微处理的所有管脚都可以当数字管脚使用,而只有其中的部分管脚可以复用成模拟管脚用于处理模拟信号。
对于模拟管脚来说,它处理的信号是连续变化的,我们需要关心的是信号的电压范围,不同的微处理器其能处理的电压范围并不相同,具体的数值会在其DATASHEET中进行说明,如LPC1768的电压范围在0到Vdda之间,而Vdda它要求和LPC1768的供电电压VDD相同,推荐值为3.3V。
对于模拟管脚来说,它处理的信号是离散变化的,即只有高低两种电平对应着逻辑1与0,具体高低电平对应的电压范围是多少也会由相应的微处理器进行说明,对于LPC1768来说,对于输入而言,任何低于0.3*VDD(即0.3*3.3)的信号都是低点平,即逻辑0,任何高于0.7*VDD(即0.7*3.3)的信号都是高点平,即逻辑1。对于输出而言,LPC1768的逻辑0为输出0V,逻辑1为输出VDD。
mbed数字输入输出API我们在前面的HelloWorld程序中已经使用了mbed的数字输出功能,LED的点亮就是通过mbed输出高电平来实现的。在mbed中,一共有三个类和数字输入输出相关,分别是DigitalIn(数字管脚输入类)、DigitalOut(数字管脚输出类)、DigitalInOut(数字管脚输入输出类),各个类的公用方法描述如下:
类名 | | |
| | |
| |
| 设定管脚的工作模式,可以是PullUp(上拉), PullDown(下拉), PullNone(无), OpenDrain(开漏输出)等状态 |
| |
| | |
| |
| |
DigitalOut& operator= (int value) | |
DigitalOut& operator= (DigitalOut& rhs) | |
| |
| DigitalInOut(PinName pin) | |
| |
| |
| |
| |
| 设定管脚的工作模式,可以是PullUp(上拉), PullDown(下拉), PullNone(无), OpenDrain(开漏输出)等状态 |
DigitalOut& operator= (int value) | |
DigitalOut& operator= (DigitalOut& rhs) | |
在上面的方法中,比较难以理解的就是操作符重载,如下面的代码片断,其中的led1=1就相当于led1.write(1),而led2=led1就相当于led2.write(led1.read())。
DigitalOut led1(LED1);
DigitalOut led2(LED2);
led1=1;
led2=led1;
那么具体的管脚名称,如这里的LED1,LED2是在哪里定义的呢?一般来说,mbed的管脚定义会出现在hal目录对应MCU下的PinNames.h中,如LPC1768的定义文件,所有的管脚名称都被定义成枚举常量,其中的有些是管脚的实际名称,有些是别名。
typedefenum {
// LPC Pin Names
P0_0 = LPC_GPIO0_BASE,
P0_1, P0_2, P0_3, P0_4, P0_5, P0_6, P0_7, P0_8, P0_9, P0_10,
P0_11, P0_12, P0_13, P0_14, P0_15, P0_16, P0_17, P0_18, P0_19,
P0_20, P0_21, P0_22, P0_23, P0_24, P0_25, P0_26, P0_27, P0_28,
P0_29, P0_30, P0_31,
……
// mbed DIP Pin Names
p5 = P0_9,
p6 = P0_8,
p7 = P0_7,
p8 = P0_6,
p9 = P0_0,
……
// Not connected
NC = (int)0xFFFFFFFF
} PinName;
mbed数字输入输出初步应用
一般情况下,我们用LED(发光二极管)来体现管脚数字输出的效果,用按钮来体现数字数入的效果。LED具有单向导通的特性,而且必须超过一定的电压才能导通也即点亮。在具体的应用上,LED有两种方式,一种是低电平导通,另外一种则是高电平导通,具体的连接示意图如下:
Xbed LPC1768采用的是前者,即高电平导通方式,但这样的方式有个潜在的问题,一般来说,MCU在启动时各个管脚的状态是不确定的,如LPC1768起动时其管脚电压在2.0V左右,而LED的导通电压在1.7到2.2V之间,不同的LED之间有差异,所以LPC1768在启动时会直接把某些LED点成半亮,导致用户认知错误。而对于后者来说,由于3.3-2.0=1.3V远小于LED的导通电压,而MCU管脚输出高电平是,LED两端的电压为0,低电压时两端的电压为3.3,所以可靠性更高。
为了实现数字电平的输入,我们需要用到按钮,也就是开关,现实世界中常用的电子开发有单刀单掷(SPST)和单刀双掷(SPDT)两类,为了保证管脚在一个确定的状态(高电平或低电平),电子开关一般要和电阻配合使用,如下面的三种连接方式。
在xbed LPC1768种,UBT按钮和p2_8管脚相连,采用的是第2种方式,也就是说默认情况下是高电平,用户按下是低电平。这样一样,我们就可以用以下代码完成受控的LED变换过程,即用户按下按钮时LED亮,其余时间LED灭,其代码如下:
DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);
DigitalOut led4(LED4);
DigitalOut led5(P2_6);
DigitalIn btn(P2_8);
int main()
{
led1=0;
led2=0;
led3=0;
led4=0;
led5=0;
while (1)
{
led1=!btn;
}
}
当然,有的应用希望是用户每按下一次按钮,灯就变换一次,这时,我们需要考虑的就不能是管脚的一个状态,而是管脚的一个变化过程,下面的代码只对用户按下这个动作,即管脚由高到低做出反应,关键代码如下:
while (1)
{
if (btn==0 )
{
wait_ms(10);
if (btn==0 )
{
led1=!led1;
while (btn==0);
}
}
}
在这里,我们需要强调的是,微处理器单个GPIO的输出电流是有限制的,如LPC1768单个GPIO管脚输出或输入的最大电流在40ma左右,如果我们要驱动一个更大电流的器件,就必须使用三极管或场效应管这样的驱动器件来实现了,具体的用法请参见相应器件的使用手册,下面是一个简单的应用示例。