控制/MCU
在单片机系统中,如果并行口的IO资源不够,那么我们可以使用74LS164来扩展并行IO口,节约单片机IO资源。74LS164是一个串行输入并行输出的移位寄存器,并带有清除端。
时序分析,单片机串行口时序单片机串行口作显示端口时应工作在方式发送状态,其时序如图所示。从时序图中可以看出,当一个数据输入到串行口发送缓冲器SBUF时,串行口将8位数据以Fosc/12的波特率从低位到高位(即从D0~D7)依次通过RXD引脚输出,当位发送完以后,中断标志TI置“1”,结束一个发送过程。这里应当注意的是串行口物出一个字节数字的次序,它是单片机串行口与74LS164正确配合的关键所在。
图2所示为8位串行输入、并行输出移位寄存器74LS164的工作时序。从图2中我们可以看出,使能端B(引脚2)为低电平时,寄存器禁止输入数据,当B为高电平时,数据可以从A端(引脚1)输人。图2中表明,先输入的数据首先Qa从(引脚3)输出。当寄存器74LS164接收完8位数据时,先输入的1位移至Q(引脚13)输出,而Qa端输出的则是最后接收到的1位。
从以上对单片机串行口及74LS164的时序分析可知,移位寄存器首先块收到单片机串行口输出的最低位D0。,最后接收到该字节的最高位D7。因此,当接收完8位数据后,移位寄存器QH端输出的是D0。,而QA输出的是D7。以上谈到的两文中所出现的错误原因,就是忽视了这一重要特点。他们给出的硬件电路如图3所示。字形代码及真值表如表所示。
从表1可以看出,字形代码把段码a当作D0位,把段码h当作D7位依次对应。在移位寄存器接收到一个完整的字形码时,QA输出的不是段码a,而是段码h(h为小数点,也可不接。)这样处理的结果,使输出的段码次序刚好相反,所以,显示出的字形就不对了。表1右侧列出了(h连小数点时的)错显字形。若要显示正确字形,在使用图3所示电路时字形码应做调整,调整后的字形代码和真值表如表2所示。这种字形码输入到74LS164内时,在其输出脚QA~QH上依次输出段码a~h,从而正确显示出要显示的内容。
在某些场合,设计者仍想使用表1所列的字形码,那么只需把显示器与74LS164的连接做一调整即可。硬件电路如图4所示
通过对单片机串行口和74LS164时序的分析,给出了两种正确的软件与硬件组合方法。笔者在研制成功的智能离心开关断开测速仪中利用表2所列的字形码与图3所示硬件相配合,使用结果令人满意。另外,有的设计中也可能遇到8位并入串行输出移位寄存器74LS165与单片机串行口的配合间题,其研究方法仍然应从时序入手,与本文所介绍方法类同,此不赘述
#include《reg51.h》
#define HIGH 1
#define LOW 0
#define SEG_PORT P0
sbit DATA = P0^4;
sbit CLK = P0^5;
unsigned char Timer0IRQEvent = 0;
unsigned char Time1SecEvent = 0;
unsigned int TimeCount = 0;
unsigned char SegCurPosition = 0;
code unsigned char SegCode[10] = {~0x3F,~0x06,~0x5B,~0x4F,~0x66,~0x6D,~0x7D,~0x07,~0x7F,~0x6F};
code unsigned char SegPosition[4] = {0xFE,0xFD,0xFB,0xF7};
unsigned char SegBuf[4] = {0};
void LS164_DATA(unsigned char x)
{
if(x)
{
DATA = 1;
}
else
{
DATA = 0;
}
}
void LS164_CLK(unsigned char x)
{
if(x)
{
CLK = 1;
}
else
{
CLK = 0;
}
}
/**********************************************************
*函数名称:LS164Send
*输 入:byte单个字节
*输 出:无
*功 能:74LS164发送单个字节
***********************************************************/
void LS164Send(unsigned char byte)
{
unsigned char j;
for(j=0;j《=7;j++)
{
if(byte&(1《《(7-j)))
{
LS164_DATA(HIGH);
}
else
{
LS164_DATA(LOW);
}
LS164_CLK(LOW);
LS164_CLK(HIGH);
}
}
/**********************************************************
*函数名称:SegRefreshDisplayBuf
*输 入:无
*输 出:无
*功 能:数码管刷新显示缓存
***********************************************************/
void SegRefreshDisplayBuf(void)
{
SegBuf[0] = TimeCount%10;
SegBuf[1] = TimeCount/10%10;
SegBuf[2] = TimeCount/100%10;
SegBuf[3] = TimeCount/1000%10;
}
/**********************************************************
*函数名称:SegDisplay
*输 入:无
*输 出:无
*功 能:数码管显示数据
***********************************************************/
void SegDisplay(void)
{
unsigned char t;
SEG_PORT = 0x0F;
t = SegCode[SegBuf[SegCurPosition]];
LS164Send(t);
SEG_PORT = SegPosition[SegCurPosition];
if(++SegCurPosition 》= 4)
{
SegCurPosition = 0;
}
}
/**********************************************************
*函数名称:TimerInit
*输 入:无
*输 出:无
*功 能:定时器初始化
***********************************************************/
void TimerInit(void)
{
TH0 = (65536 - 5000)/256;
TL0 = (65536 - 5000)%256;
TMOD = 0x01;
}
/**********************************************************
*函数名称:Timer0Start
*输 入:无
*输 出:无
*功 能:定时器启动
***********************************************************/
void Timer0Start(void)
{
TR0 = 1;
ET0 = 1;
}
/**********************************************************
*函数名称:PortInit
*输 入:无
*输 出:无
*功 能:I/O初始化
***********************************************************/
void PortInit(void)
{
P0 = P1 = P2 = P3 = 0xFF;
}
/**********************************************************
*函数名称:main
*输 入:无
*输 出:无
*功 能:函数主题
***********************************************************/
void main(void)
{
PortInit();
TimerInit();
Timer0Start();
SegRefreshDisplayBuf();
EA = 1;
while(1)
{
if(Timer0IRQEvent)
{
Timer0IRQEvent = 0;
if(Time1SecEvent)
{
Time1SecEvent = 0;
if(++TimeCount 》= 9999)
{
TimeCount = 0;
}
SegRefreshDisplayBuf();
}
SegDisplay();
}
}
}
/**********************************************************
*函数名称:Timer0IRQ
*输 入:无
*输 出:无
*功 能:定时器中断函数
***********************************************************/
void Timer0IRQ(void) interrupt 1
{
static unsigned int cnt = 0;
TH0 = (65536 - 5000)/256;
TL0 = (65536 - 5000)%256;
Timer0IRQEvent = 1;
if(++cnt 》= 200)
{
cnt = 0;
Time1SecEvent = 1;
}
}
全部0条评论
快来发表一下你的评论吧 !