在單片機系統中,如果并行口的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與單片機串行口的配合間題,其研究方法仍然應從時序入手,與本文所介紹方法類同,此不贅述
proteus仿真圖和代碼附上。
#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;
}
}
評論