Chinaunix首页 | 论坛 | 博客
  • 博客访问: 119314
  • 博文数量: 29
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 242
  • 用 户 组: 普通用户
  • 注册时间: 2014-07-17 13:36
文章分类

全部博文(29)

文章存档

2015年(29)

我的朋友

分类: 嵌入式

2015-04-12 20:50:40

趣讲51单片机P0端口

一、P0

P0口的结构如下图所示

其中输入缓冲器是一个三态门,三态门和二极管的区别在于,三态门比二极管多一个控制端,所以三态门作为输出时,有3中状态,输出高电平、低电平和高阻态。

下面来分析当控制信号为0时,此时,V1为断开状态,引脚P0.x为开漏输出。

 

AP0口作为GPIO输入

CPU通过控制读锁存器控制端和读引脚控制端来控制输入到内部总线上的数据来源。

如果总线数据从锁存器上得到,叫做读端口;如果总线数据从引脚上得到,叫做读引脚。

 

读端口的操作是由单片机自动完成的,直白点说,就是读锁存器的控制对于程序员是透明的,这个控制过程在51内核的指令集中没有提供接口。

CPU何时使读锁存器有效呢,即CPU何时读端口?8051内核单片机对于P0-P3口的输入有如下约定:凡是属于-修改-方式的指令,从锁存器读入信号,其他指令则从端口引脚线上读入信号。

 

那么,什么是-修改-方式的指令?

-修改-指令的特点是,从端口输入信号(读),在单片机上加以运算(修改),最后输出到该端口(写);比如:

ANL P0,#立即数;  P0与立即数相与后,回传到P0

ORL P0,A;                P0A寄存器中的值相与后,回传到P0

INC P1;                     P0自增

DEC P3;                    P3自减

CPL P2;                     P2按位取反

可以看出,这些操作都是将P0端口看做一个寄存器在操作,将P0做处理后,重新传给P0

那为什么在这些操作中就不可以读引脚的值呢?

那只有一种情况,就是读引脚的值可能和读端口的值存在不同。

举个例子,当P0口作为普通IO口使用,P0口某个引脚先输出高电平,MOSV2处于断开状态,由于上拉电阻的作用,此时,输出引脚为高电平,然而,由于外部电路也可能将该引脚的电平拉低,所以,此时,引脚上的电平为低,而锁存器输出的电平Q为高,这是很有可能发生的,别忘了,P0-P3作为GPIO口时,是准双向口,在读取这些端口的引脚数据时,首先要对这些位写1

还不信,好吧,下面给出给位看官想要的证据:

Keil中编写下列程序,够简单的吧:

#include

int main()

{

       unsigned char a;

       P0 = 0x0f;

       a = P0;

       P0++;

}

下面进行单步调试,

上图所示,先解释一下右侧的对话框是怎么出来的,点击菜单栏”Peripherals(外设)“->”I/0-Ports(IO端口)”->”Port0”,看这个对话框,很有意思哈,由对话框标题可以知道P0口是一个并行口,P0其实就是代表了端口的值,而ins,我自己的理解是inputs,就是引脚输入,如果你点击P0后面的复选框,ins的值也会跟着变化,而如果你点击ins复选框,P0是不会随之变化的,这和我们的工作原理图就对应起来了。

如下图

你可以将输出为1 的端口拉低,P0作为普通输入时我们就是这么干的。

但是,如果你打算将输出为低电平拉高,如上图,你点击insbit4-bit7中的任意一个,很不幸,会出现下列错误:

单片机的意思很明显,如果输出为0V2为导通状态,引脚直接和GND相连,此时,如果你打算输入高电平,直接和GND短路,单片机发出抗议,你丫懂不懂,是不是想烧死我?这也进一步说明,如果要输入,先要往P0口写1

Ok,继续下一步,执行完a = P0,结果如下图所示:

很明显,如果是内存中某个变量需要得到P0的输入值,它会去取引脚上的值,因为,这种语句并不是-修改-写指令

Ok,继续下去指令,在执行完P0++后,结果如下图所示:

可以看出,P0口以端口中存储的值作为原值进行操作,并进行了对引脚的写操作。

分析完了,那还有一个问题,如果第三句是”a = P0++”呢,ok,我们来试试,其实学习的过程就是探索的过程,从书本上得来的太容易了,容易得来的东西,大家就不会珍惜,所以,相信一句话纸上得来终觉浅 绝知此事要躬行

在执行完”a = P0++”后,结果如下图:

现在a的值为3,也就是说,a也是先得到P0引脚上的值,然后P0再进行自增操作的。即”a = P0++;”等价于“a=P0;P0+=1;”

现在,基本上将端口输入引脚输入讲清楚了,再来总结一下:

总结:端口输入,P0的值改变时,使用的是端口输入,如果是内存中的变量希望得到P0或者进过P0运算后得到的值,使用的是引脚输入。

其实,这些码农们都不用关心,因为,你根本不能控制读锁存器读引脚这两个控制端口,ok,你说C语言不行,那我用汇编总行吧;很抱歉,汇编也不行,因为干这个是IC电路设计人员,除非您老打算自己出个芯片,或者用FPGA写个IP核,那所有的控制器都对您老开放,你可以为所欲为,哈哈。

讲到这里,给大家普及一下CPU的层次,有兴趣的同学可以去看《大话处理器》这本书,非常之不错。

如下图所示:

其中,ISA是软件与硬件的交接处。而ISA通常是由CPU厂商以汇编指令的形式提供,为什么说汇编语言与硬件关系密切,移植行差,就是因为它所处的分层模型的位置比较特殊。ISA定义了该CPU有哪些功能,比如,有add功能,sub功能等等,但是这些加操作和减操作如何实现,那就是下一层微构架的事了,微构架是对指令集功能如何实现的设计,设计完成后,就可以由硬件厂商去生产了,也就是IC的物理实现过程,总的说来,对微构架的设计比较费脑,而物理的实现对环境与精度的要求不是一般的高,本科时候学微电子时候,虽然吊儿郎当,这制片过程中要求超净的环境还是记忆深刻的。多的不说了,有兴趣的强烈建议看看《大话处理器》,当小说茶余饭后看也挺好的。

 

BP0口作为GPIO的输出

P0口作为IO线输出时,其电路通路如下图所示:

此时,控制信号为0,内部总线为0P0.x直接接地,能够输出低电平,如果内部总线为1P0.x为悬空状态,此时,需要一个上拉电阻将引脚电平拉高,所以,P0作为GPIO使用时,还是需要在外围电路中配置一个上拉电阻,输出比较简单,就说这些吧。

 

CP0口作为外部存储器的扩展

当控制信号为1时,输出端组成一个推挽输出的结构,此时,P0口为真正的双向口,作为地址的输出和数据的输入输出。在外部扩展RAMROM时,就需要使用P0作为数据和地址进行数据传递,8051256Byte的内部RAM4K的内部ROM80528K的内部ROM

那么问题来了,控制信号到底是由谁发出的?

先来介绍一下与扩展存储器有关的3个引脚:

/PSEN29pin):全称为程序存储器允许输出控制端(Program Store Enable,在读外部程序存储器时,/PSEN低电平有效,以实现外部程序存储器单元的读操作。注意,该端口为51处理器对外输出端,就是给外部ROM的信号,与ROMOE脚相接。

 

/EA31pin):该端口为输入端口,用来控制使用内部还是外部的ROM,当/EA接高电平时,单片机读取内部程序存储器,例如8051的片内ROM4KB,超过4K的部分,处理器自动到外部ROM上取,此时,可以说内部ROM和外部ROM统一编址,外部ROM的前一部分空间被内部ROM所屏蔽;当/EA0时,使用外部ROM,即程序开始时,就跳到外部ROM0x00地址进行执行,此时,片内ROM就成为了摆设。

总结:/EA控制是否使用外部ROM,在读取内部ROM时,因为没外部ROM什么事,所以和外部ROMOE端相接的/PSEN无动作;当进行外部ROM读取时,每个机器周期都会有两次动作,也就是一个周期进行两次外部ROM取指令。

 

ALE/PROG30pin):在单片机外部扩展存储器时,ALE用于控制把P0口输出的低8位地址送入锁存器锁存起来,以实现低位地址和数据的隔离。

由上面可以知道,外部ROM是通过/EA和实际的地址值确定是访问外部ROM还是内部ROM,那RAM由谁决定访问对象呢?

是由伟大的程序员决定的,单片机默认用“MOVX @dptr,A”实现对外部ARM的写数据,用“MOVX A,@dptr”实现外部RAM写;MOV由于访问内部RAM。另外,MOVC是访问ROM的指令。

那在C中如何表示?

1.C51中的存储器类型 (单片机原理及应用 王景景 第78页)

 data      直接寻址访问的片内RAM的低128B,访问速度快

bdata     片内RAM的可寻址区(20H~2FH),允许字节和位混合访问

idata      间接寻址访问片内RAM,允许访问全部片内RAM

pdata     用Ri间接访问的片内RAM的低256B

xdata    用DPTR间接访问的片外RAM,允许访问全部64K片外RAM

code    程序存储器ROM的64KB空间

2.C51中的预定义宏指令(81页)

   放在#include   头文件中

CBYTE   以字节形式对code区寻址

DBYTE  以字节形式对data区寻址

PBYTE  以字节形式对pdata区寻址

XBYTE 以字节形式对xdata区寻址

CWORD 以字形式对code区寻址

DWORD 以字形式对data区寻址

PWORD 以字形式对pdata区寻址

XWORD 以字形式对xdata区寻址

使用方法是:宏名[地址值];

下图为P0作为地址/数据时的结构图:


再次总结:P0口的控制信号是由处理器自动给出的,如果你用了外部存储器,并在程序中使用了读写操作,P0就会使上图中的控制信号为1。也就是说,如果你用来外部存储器,那么,您老就不要使用P0口为GPIO功能了。

功能复用,我的理解是可以在不同的时间可以转变不同的功能,而不是在同一时间使用双功能,而读指令是每个机器周期都是存在的,所以P0口的地址/数据线功能始终被占用,所以,GPIO功能就用不了了。

OKP0口终于讲完了。

 

 

 

阅读(10270) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~