预热篇——
入门篇——
进阶篇——
外设篇——
在嵌入式系统开发中,通用输入输出(GPIO)口的调用是一项重要的基本功。GPIO口它为开发者提供了与外部设备进行交互的桥梁。通过GPIO口,开发者可以控制和监视外部设备的状态,同时也能接收来自外部设备的信号。
本文将深入探讨51单片机GPIO口的调用方法。我们将从GPIO口的结构开始,逐步介绍其功能、寄存器操作方法,以及如何在C语言中进行编程调用。我们还将通过实例代码,展示“点灯大法”,以帮助读者更好地理解GPIO口的使用。
下面我们将介绍它的结构(只需要简单了解即可),以及关于它的使用方法(要求熟练掌握),在这里我们主要介绍单用和复用这两种情况。
GPIO全拼叫General Purpose Input Output(通用输入输出)简称IO口也叫总线扩展器,GPIO口是由引脚,功能寄存器组成,不同的架构中的GPIO封装不同,所使用的引脚数与寄存器数不同,具体可以参考芯片手册里的GPIO篇。
51单片机的GPIO口,它的结构较为简单,工作模式比较简单,不需要专门进行初始化配置,通常我们只使用它的输入与输出这两个功能。虽然在日常开发中我们只是简单的去调用,不过我们还是要去了解它的结构,为我们日后的进一步学习打下基础。
在实际的工程中,我们通常会调用gpIO口去输出一个控制信号以及用它来做一个接收外界数据的信号。同时,我们也会用它来模拟一些总线,比如IIC,SPI等用来做通讯信号。同时呢,我们也会用它来进行一些脉冲调制,比如PWM。
如果要学习他GPIO口的内部结构需要一定的模电知识和数电知识,在这里呢,为了方便初学者们能入们,防止劝退,我们不多涉及这方面,只是作为一个简单的拓展介绍
在这幅图呢,他分别用到了一些逻辑门和MOS管以及施密特触发器等数字电路中的器件,如果想要进一步了解呢同学们可以去看《电子技术基础·模拟部分》和《数字电子技术基础》
不过可以知道的是,他的gpIO口不需要进行软件初始化配置,并且当它输出为低电平,也就是0的时候,它的驱动外设的能力非常强,容易使得外设进入正常工作状态。但是当它输出为高电平的时候,也就是1,它的驱动能力较弱。
然后就是在51单片机中比较特殊的p0口,一般在我们的开发当中,我们都会给p0管脚添加一个10k到4.7k的上拉电阻,这是因为我们要将它作为IO口使用。而当我们不添加上拉电阻的时候,它上电复位后处于开漏模式,是用作地址/数据复用总线使用。
最后呢,就是关于IO口应用的注意事项:
1. IO口的**损坏**:没有加限流电阻。
例如:LED
2. IO口的**假死**:执行速度过快(1T的时候),或者程序逻辑有误。
3. IO口的**驱动能力**:高电平驱动能力较弱,有时候需要NPN外围放大电路放大驱动电流。
例如:蜂鸣器,人体红外
40Pin
在这里呢,我们不讲汇编,我们讲C语言版本的gpIO口的单用
首先呢关于51单片机它的头文件常用的有这么两种REGX52.H和REG52.H
他们的区别呢,就在于前者对单用的引脚物理地址进行了特殊的位声明,从而我们可以直接进行使用
REG52X.H
而后者则没有进行这样的声明,如果要进行单用,需要用户自己进行特殊位声明
REG52.H
MAIN.C
那么关于复用呢?分为两种情况,一种是集体使用,另一种是选择性使用。
集体性使用
选择性使用
那么接下来就是我们的重头戏,如何去在实践中应用我们的理论知识?那么首先呢?我们为了方便大家入门,我们只介绍gpIO口的输出控制信号和接收输入信号方面的应用。
相信每一位电子爱好者,接触到嵌入式,接触到单片机的第一节课,就是学会点灯!!!哈哈。
那么我们在这里不点无用之灯。我们说一些在实际中常用到的,比如手机闪光灯,他会常亮,也会关闭,也会闪烁;还有,手机上的指示灯,他会“呼吸”(我们后面再学如何“呼吸”)
那么首先呢,就是要搭建一个正确的电路
紧接着,我们就在这个硬件电路上进行编程,首先第一种情况是长灭和常亮
在实际工程的应用当中,我们经常会用一个指示灯来表示电源的通断或者一个模组状态的好坏;
比如现在有这么一个情况:我现在要用长亮来表示一个模组A的工作状态为正常,长灭代表整个系统S未进入初始化;
如图
那么在这里,我们不引入这些模组,我们把它简化成一个变量——一个数即标志位来代替
#include
//项目情况,这里简化为一个
#define FLAG_SYS_NORMAL 1//此标志位代表系统正常
#define FLAG_DEV_A 2//此标志位代表模组A正常
//端口声明
sbit LED = P1^0;
#define LED_ON 0
#define LED_OFF 1
//主函数
void main()
{
for(;;)//死循环
{
if(FLAG_SYS_NORMAL)//如果系统正常
{
if(FLAG_DEV_A )//则去判断模组A是否正常
{
LED=LED_ON ;
//....其余情况不考虑,其余省略
}
//。。。其余情况不考虑,省略
}
else
{
LED=LED_OFF;
//....其余情况不考虑,其余省略
}
}
}
如果你理解了上述代码,那么下面我们进行下一步就是关于它的闪烁(其实就是多了一个判断和运算)
如图
#include
//项目情况,这里简化为一个
#define FLAG_SYS_NORMAL 1//此标志位代表系统正常
#define FLAG_DEV_A 2//此标志位代表模组A正常
#define FLAG_DEV_A_no 2//此标志位代表模组A不正常⭐
//端口声明
sbit LED = P1^0;
#define LED_ON 0
#define LED_OFF 1
//主函数
void main()
{
for(;;)//死循环
{
if(FLAG_SYS_NORMAL)//如果系统正常
{ //则去判断模组是否正常
if(FLAG_DEV_A )//若A正常
{
LED=LED_ON ;
//....其余情况先不考虑,其余省略
}
else if(FLAG_DEV_A_no)//若A不正常
{
LED=~LED;delay_ms(500);//每次不正常,改变状态,适当延时,方便人眼观察闪烁,
//....其余情况先不考虑,其余省略
}
//。。。其余情况先不考虑,省略
}
else
{
LED=LED_OFF;
//....其余情况先不考虑,其余省略
}
}
}
【单位显示】
首先我们先定义数组,
uchar code DSY_CODE[]=
{//为了方便计算我们默认1为亮段,0为灭段
0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0x82,0xf8,0x80,0x90,0xff
};
然后调用我们的数据,对P0口进行赋值即可(道理同点灯一样,如果掌握了复用GPIO口对此很好理解)
P0=~DSY_CODE[1];//在这里进行了取反操作,因为是共阴极,0是点亮,反逻辑,所以取反
然后加入延时和变量,让他们依次动态显示,效果如下
void main()
{
uchar i=0;
P0=0x00;
while(1)
{
P0=~DSY_CODE[i];
i=(i+1)%10;/*显示0-9*/
DelayMS(200);
}
}
通过上现象描述,我们不难发现,其实数码管只是一些变形的LED的集合,原理图如下,有共阴极和共阳极之分
共阴极(CommonCathode即CC)
共阳极(CommonAnthode即CA)
码值转换
在这里我们只举一例(其中P07是接小数点那一段的,但在这里我们没有,我们只管数字)
希望大家自行理解,如有疑问可在评论区交流,或者网上查阅相关资料——二进制与十六进制的转换
也可以用计算器辅助学习
【闪烁显示】
闪烁显示数字的思想和让LED闪烁的思想是一样的,只不过这里是让一群形状不一样,队列特殊的LED进行闪烁;
也就是说让他们先选择性的亮起,形成数字,一段时间后再全部灭掉,就形成了闪烁的效果。
void main()
{
uchar k,m;
P0=0xff;
while(1)
{
for(m=0;m<8;m++)
{
P0=DSY_CODE[m];
DelayMS(2);
}
DelayMS(1000);
}
}
【滚动显示】
细心的同学可以发现,在刚才我们用到了多位数码管,那么下面我们就着多位数码管说一下滚动显示
【多位数码管原理讲解】
关于多位数码管的原理讲解呢,我之前有介绍,大家可以看【降落伞】10:00左右这段
那么在刚才的讲解中呢?我们可以知道多位数码管每次只能选择一位进行显示,
如何进行选择——根据数码管是共阴极还是共阳级进行位选择,(效果雷同点灯,由此可见点灯的重要性,哈哈哈),电路图如下
要达到滚动的效果,就要依次显示,也可以进行移位运算,在这里用了一个库函数
void main()
{
uchar i,k=0x80;
while(1)
{
for(i=8;i>0;i--)
{
P2=0xff;//共阴极,位选清零
k=_crol_(k,1);//循环左移1位
P0=DSY_CODE[8-i];//段选
P2=k;//位选
DelayMS(3);
}
}
}
这就是我们的滚动显示,那么现在大家不妨设想一下,如果我们的滚动显示越来越快它会出现什么现象呢?会是多重影?
打个趣。现在给大家科普一下
【仿真容易,实物焊接难】
感兴趣的同学可以自行体会实践一下…
关于按键的我们有单击按键和双击按键以及短按和长按这几种情况
硬件特性
对于机械开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开,所以在开关闭合及断开的瞬间会伴随一连串的抖动,因此我们要进行消抖。
硬件消抖
要消除杂波,就应该对杂波进行吸收
软件消抖
不过一般我们用软件编程中的延时函数去跳过这段抖动时间(10ms左右)
单击按键
void DelayMS(uint x)//子函数,
{
uchar i;
while(x--)
{
for(i=200;i>0;i--);
}
}
void Key_scan()//独立按键函数
{
if(KEY_1==0)//检测电平
{
DelayMS(10);//防抖
if(KEY_1==0)//确认
{
//按键按下后执行的内容★★★
}
while(!KEY_1);
}
}
多种情况
在这里,由于包含多种状态,我们采用结构体进行编程
首先呢,我们要列举出案件的所有状态
//按键处理阶段
typedef enum
{
KEY_PRESSED ,//按键按下
KEY_REALSE ,//按键松开
KEY_REALSE_WOBBLE ,//确认松开的消抖状态
KEY_PRESS_WOBBLE ,//确认按下的消抖状态
}KEY_STAGEn;
然后就是去判断他是短按还是长按,如果是短按的话那么他是单击还是多击。具体操作呢,它涉及到了定时器,那么由于我们还没有学到,所以说在这里我们先不做解释,在初学状态,我们只需要了解单击即可。
实际应用
那么,关于按键的介绍就这么些,他在实战中的应用也非常简单,只不过需要在刚才加注释的地方换成我们需要执行的内容即可,具体呢可以参考刚才的内容
下面是一个小展示,大家只需要把前面点灯的内容搬到按键处理里面即可
【什么是重点,什么是难点,】
1. 点灯:能够让灯进行闪烁,并且可以把这段内容打包,移植到别的地方,同样能够闪烁,起到指示灯的作用
2. 数码管:这个呢,只是作为一个拓展,实际应用中用的频率可能不多。不过也是要求掌握,能够加深对嵌入式的理解。
3. 按键:能够编写按键处理函数,教程中只是一个范例,可以有其他写法,
但核心思想是不变的,消抖,状态检测,状态内容执行,松手检测
关于按键的进阶使用,我们在学习完定时器后再做介绍。
如有疑问,欢迎再来评论区留言!!!