关于51单片机P2口做地址总线和一般IO口的问题(P2):

miaozhendaoren 2011-11-10 03:48:26
路过的大神们,进来看下!
main 函数(0,1,2,3)在3楼,1602函数在1楼,2楼是一个工程头文件
问题描述:

#include <reg52.h>
#define uchar unsigned char

uchar xdata volatile TEST _at_ 0xff00

void main(void)
{
P2 = 0x20;
TEST = 0x00; //TEST _at_ 0xff00
//上面一句执行完后P2口的输出是多少? 0xff or 0x20 ?
//如果是 0x20,是怎么实现的?执行 TEST = 0x00的时候 MOV DPH,#0ffh ;MOV DPL,#00h,
// CLR A,然后,MOVX @DPTR,A,执行 MOVX @DPTR,A的时候 P2输出为0xff
//但是执行完后怎么又成了 0x20了? 0x20是我用proteus 仿真的结果,不解?
//要使这样的话,P2口可以即作地址总线,同时做IO口,比如控制1602的引脚RS,WR,
//只是中间 会有数据晃一下 ,只要保证1602的 E (用非P2口控制)引脚在执行MOVX @DPTR,A前后一直
//都为低电平 就行 ?可以这样吗?

while(1);
}



基于上面的问题,我想寻求一个好的方法:
我的单片机想接较多 的外设,需要地址扩展, 我想用P2的低3位通过38译码器来选择不同的片,(有8255,ADC0809,LCD1602等等,还有其他的片),P0经373锁存器 选择各个片内部的通道(如,8255的的几个口,ADC的 0~7 8个通道)但是 有一个问题:1602 有E RS RW 三个引脚, 1602读写时要求先 设置好RS RW 引脚,再将E引脚置为高电平

所以我 用 51的WR,RD引脚和P2低三位经38译码器输出的CS_LCD引脚 逻辑组合产生E引脚信号,E为高电平时(即CS_LCD为低,读写信号为低),RS RW 引脚已由 P0口经373锁存器的低两位(A0,A1)输出, 这样连接行吗?



还有什么好的统一编址 的方法吗?

想 清爽一点,写程序 可以直接定义 uchar xdata volatile LCDcmdWR _at_ 0xf800(高字节低三位 译码后 与WR,RD 选择LCD_E引脚,低字节低两位00(RS,RW) 即指令寄存器写操作),这样 往1602写命令的时候就可以直接 LCDcmdWR = 0xXX; 了,但是 程序写出来 1602却没有显示, 是1602初始化 有问题吗?程序在下面!是不是E引脚时间不够啊?波形图如下:



程序在下面:
...全文
2316 13 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
miaozhendaoren 2011-11-17
  • 打赏
  • 举报
回复
木有人了吗?木有我就要结贴了!
schlafenhamster 2011-11-13
  • 打赏
  • 举报
回复
通常P2口的高地址线只在ALE有效时才输出,过了ALE就恢复到IO口的值。
bjtea 2011-11-13
  • 打赏
  • 举报
回复
1)扩充片选,通常使用P2口的高3位地址线,外接74138。这样,将64K空间分成8个8K,138的每一条输出对应8K空间,且每一个8K的地址是连续的(用P2的低3位,8K空间就不连续了)。
2)P2要么作为地址线用,要么作为I/O口用,混用的话,很容易相互干扰。
3)上述(1)的做法是译码法做片选。还有一种做法是线选,还是用P2的最高几位,例如用高6位做片选。当P2.2~P2.7某一线输出高,通过反相器,可以选中某一片外设。一共可选6片外设,每条片选的地址区域是2^10=1K。
miaozhendaoren 2011-11-13
  • 打赏
  • 举报
回复
谢谢夸奖,好多人都认为这是一个简单的问题,其实这是一个很容易被忽视的问题,看了标题没想太多的肯定没注意过这个问题,猜猜?51IO口
codesnail 2011-11-10
  • 打赏
  • 举报
回复
虽然不想看lz的问题。
但是lz的态度值得表扬,程序、图片排版很好,赞下!
miaozhendaoren 2011-11-10
  • 打赏
  • 举报
回复
问题已解决,求开头那个小例子的解释,下面的一大堆就不用管了。
另外分享一点感悟:
试图把问题描述清的过程就是解决问题的开始

我程序其他的都没有错,那个波形有的时候是齿状的有的时候是高低脉冲 ,不知道怎么回事!

开头的例子还是弄不明白,求大神!
miaozhendaoren 2011-11-10
  • 打赏
  • 举报
回复
刚刚有显示了,我想把1602像8255啊之类的其他的外设一样都统一编址,不用再RS=0;RW = 0; E = 1 ; P0 = 0xXX;E = 0;那样了,那样太麻烦了, 还有什么统一编址的比较好的方法吗?
yinsirjeff 2011-11-10
  • 打赏
  • 举报
回复
你想做什么?不明白....
miaozhendaoren 2011-11-10
  • 打赏
  • 举报
回复
/*******main文件**********/

#include "smallPLCmaster.h"

void main(void)
{
uchar idata test[] = "myname";

while(busyP0P2);
init1602();
putString1602(0,&test);
while(1);
}


miaozhendaoren 2011-11-10
  • 打赏
  • 举报
回复
/******主头文件(用于全局变量的定义等)*********/

#include <reg52.h>
#define uchar unsigned char

/*************************全局变量定义*************************/
bit busyP0P2; //P0P2口总线状态标志,使用总线前必须先检测此标志
//此标志位0,方可使用,此标志在使用到总线的子函数中进入处置1,结束处清0


/******************************end*****************************/




/****************************functions define********************/
#include "LCD1602.h"
#include "IO8255.h"
#include "ADC0808.h"
#include "uart.h"
/*******************************end******************************/

miaozhendaoren 2011-11-10
  • 打赏
  • 举报
回复
/*****LCD1602.h********/

/****************************P0P2口地址定义**************************/
uchar volatile xdata LCDbusyRd _at_ 0xf8fe; //1602忙碌状态读取地址 ,高位f8为LCD片选
uchar volatile xdata LCDcmdWr _at_ 0xf8fc; //1602写命令 地址
uchar volatile xdata LCDdatWr _at_ 0xf8fd; //1602写数据 地址
uchar volatile xdata LCDdatRd _at_ 0xf8f8; //1602数据读 地址
/********************************end**********************************/

/***************************1602命令定义******************************/
#define cmd0 0x08 //
#define cmd0DISPLAYOFFand 0xfb //显示关
#define cmd0DISPLAYONor 0x04 //显示开
#define cmd0CURSOROFFand 0xfd //光标关
#define cmd0CURSORONor 0x02 //光标开
#define cmd0CURSORFLASHOFFand 0xfe //光标不闪烁
#define cmd0CURSORFLASHONor 0x01 //光标闪烁

#define cmd1 0x40
#define cmd1SCREENROLLor 0x08 //屏幕滚动
#define cmd1CURSORROLLand 0xf7 //光标滚动
#define cmd1ROLLRIGHTor 0x04 //向右滚动
#define cmd1ROLLLEFTand 0xfb //向左滚动

#define cmd2 0x00
#define cmd2ACUPor 0x02 //AC自动加1
#define cmd2ACDOWNand 0xfd //AC自动减1

#define cmd3CLEAR 0x01 //清除屏幕
#define cmd4ACRESET 0x02 //AC归0

#define addDDRAMBASE 0x80 //DDRAM基地址
/*********************************end**********************************/


/************************1602***functions******************************/
//delay5ms() 1602等待busy flag 变为0需要5ms
void delay5ms(void)
{
uchar i;
uchar j;
i = 255;
while(i--){
j = 20;
while(j--);
}
}/********end delay5ms()*/

//BusyRd1602() 1602忙碌状态读取,返回0时不忙碌
uchar BusyRd1602(void) //没有操作P0P2总线标志,不可在main中调用
{
LCDbusyRd = 0xff;
return LCDbusyRd & 0x80;
}/*******end BusyRd1602()***/


//init1602
void init1602(void)
{

busyP0P2 = 1; //将P0P2总线置为忙碌

LCDcmdWr = 0x38;
delay5ms();
LCDcmdWr = 0x38;
delay5ms();
LCDcmdWr = 0x38;
delay5ms();

LCDcmdWr = 0x38;
delay5ms();
LCDcmdWr = cmd0 & cmd0DISPLAYOFFand; //关闭显示
delay5ms();
LCDcmdWr = cmd3CLEAR; //清除屏幕
delay5ms();
LCDcmdWr = cmd0 | cmd0DISPLAYONor &cmd0CURSOROFFand;//显示开,光标关

busyP0P2 = 0; //释放P0P2总线
}/******end init1602()****/


//putString1602() 在1602屏幕上显示以'\0'结尾的字符串
//IN:startPos,开始位置0~31
//IN: uchar idata *pString,字符指针
void putString1602(uchar startPos,uchar idata *pString)
{
busyP0P2 = 1; //总线忙碌置标

LCDcmdWr = addDDRAMBASE + startPos; //写第一行地址
for( ; *pString != '\0'; pString++,startPos++)
{
if(startPos > 15)
break;
LCDdatWr = *pString;
delay5ms();
}

LCDcmdWr = addDDRAMBASE + (startPos-16) + 0x40;//写第二行地址
for(; *pString != '\0'; pString++, startPos++)
{
if(startPos > 31)
break;
LCDdatWr = *pString;
delay5ms();
}

busyP0P2 = 0; //总线忙碌清除
}/***********end putString1602()***************/

27,508

社区成员

发帖
与我相关
我的任务
社区描述
硬件/嵌入开发 单片机/工控
社区管理员
  • 单片机/工控社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧