wwwxxx国产_337p日本欧洲亚洲大胆张筱雨_免费在线看成人av_日本黄色不卡视频_国产精品成熟老女人_99视频一区_亚洲精品97久久中文字幕_免费精品视频在线_亚洲色图欧美视频_欧美一区二三区

標(biāo)題: 第12章 指針的基礎(chǔ)與1602液晶的初步認(rèn)識(shí) [打印本頁(yè)]

作者: admin    時(shí)間: 2013-9-28 15:03
標(biāo)題: 第12章 指針的基礎(chǔ)與1602液晶的初步認(rèn)識(shí)
鄭重聲明:
本教材現(xiàn)以連載的方式由網(wǎng)絡(luò)發(fā)布,并將于2014年由清華大學(xué)出版社出版最終完整版,版權(quán)歸作者和清華大學(xué)出版社所有。本著開源、分享的理念,本教材可以自由傳播及學(xué)習(xí)使用,但是務(wù)必請(qǐng)注明出處來(lái)自www.kingst.org,未經(jīng)作者同意不得用于任何商業(yè)目的。最終解釋權(quán)歸金沙灘工作室所有.

我們?cè)谏?font face="Times New Roman">C語(yǔ)言課的時(shí)候,學(xué)到指針,每一位教C語(yǔ)言的老師都會(huì)告訴我們一句:指針是C語(yǔ)言的靈魂。由此可見,指針是否學(xué)會(huì)是判斷一個(gè)人是否真正學(xué)會(huì)C語(yǔ)言的重要指標(biāo)之一,但是很多同學(xué)只知道其重要性,卻沒學(xué)會(huì)其靈活性。
簡(jiǎn)單的程序,100來(lái)行代碼,不需要指針我們可以輕松搞定,但是當(dāng)代碼寫到幾千上萬(wàn)行甚至更多的時(shí)候,利用指針就可以直接而快速的處理內(nèi)存中的各種數(shù)據(jù)結(jié)構(gòu)中的數(shù)據(jù),特別是數(shù)組、字符串和內(nèi)存的動(dòng)態(tài)分配等,它為函數(shù)之間各類數(shù)據(jù)傳遞提供了簡(jiǎn)潔便利的方法。說(shuō)了這么多作用估計(jì)大家沒用過(guò)指針也體會(huì)不到,但這里就是表達(dá)這樣一個(gè)意思,指針很重要,必須要學(xué)會(huì)。
指針相對(duì)其他知識(shí)點(diǎn)來(lái)說(shuō)比較難講,主要在于例子不好舉。簡(jiǎn)單的程序用指針去做會(huì)把簡(jiǎn)單的程序搞復(fù)雜,復(fù)雜的程序用指針去寫牽扯的知識(shí)太多可能又不好理解。從一個(gè)角度講,沒學(xué)會(huì)指針就等于沒學(xué)會(huì)C語(yǔ)言,所以再難也不是我們學(xué)不好的理由。這節(jié)課我就從我對(duì)指針的理解盡可能的把指針形象的介紹給大家,幫大家啃下這塊硬骨頭,同學(xué)們學(xué)習(xí)這節(jié)課內(nèi)容也要打起十二分精神,集中注意力認(rèn)真去學(xué),爭(zhēng)取拿下指針。
12.1 指針的基本概念和指針變量的聲明12.1.1 變量的地址
要研究指針,我們得先來(lái)深入理解內(nèi)存地址這個(gè)概念。打個(gè)比方:整個(gè)內(nèi)存就相當(dāng)于一個(gè)擁有很多房間的大樓,每個(gè)房間都有房間號(hào),比如從101102103直到NNN,我們可以說(shuō)這些房間號(hào)就是房間的地址,相應(yīng)的內(nèi)存中的每個(gè)單元也都有自己的編號(hào),比如從0x000x010x02直到0xNN,我們同樣可以說(shuō)這些編號(hào)就是內(nèi)存單元的地址。房間里可以住人,對(duì)應(yīng)的內(nèi)存單元里就可以“住進(jìn)”變量了:假如一位名字叫A的人住在101房間,我們可以說(shuō)A的住址就是101,或者101就是A的住址;對(duì)應(yīng)的,假如一個(gè)名為x的變量住在編號(hào)為0x00的這個(gè)內(nèi)存單元中,那么我們可以說(shuō)變量x的內(nèi)存地址就是0x00,或者0x00就是變量x的地址。
基本的內(nèi)存單元是字節(jié),英文單詞為Byte,我們所使用的STC89C52RC單片機(jī)共有512字節(jié)的RAM,就是我們所謂的內(nèi)存,但它分為內(nèi)部256字節(jié)和外部256字節(jié),我們僅以內(nèi)部的256字節(jié)為例,很明顯其地址的編號(hào)從0開始就是0x000xFF。我們用C語(yǔ)言定義的各種變量就存在0x000xFF的地址范圍內(nèi),而不同類型的變量會(huì)占用不同數(shù)量的內(nèi)存單元,即字節(jié),可以結(jié)合前面講過(guò)的C語(yǔ)言變量類型深入理解。現(xiàn)在,假如我們現(xiàn)在定義了unsigned char a = 1;  unsigned char b = 2;  unsigned int c = 3;  unsigned long d = 4; 這樣4個(gè)變量,我們把這4個(gè)變量分別放到內(nèi)存中,就會(huì)是表12-1中所列的樣子,我們先來(lái)大概了解一下他們的存儲(chǔ)方式。
12-1 變量存儲(chǔ)方式
內(nèi)存地址
存儲(chǔ)的數(shù)據(jù)
0xFF
... ...
... ...
... ...
0x07
d
0x06
d
0x05
d
0x04
d
0x03
c
0x02
c
0x01
b
0x00
a
    變量abcd之間的變量類型不同,因此在內(nèi)存中所占的存儲(chǔ)單元也不一樣,ab都占一個(gè)字節(jié),c占了2個(gè)字節(jié),而d占了4個(gè)字節(jié)。那么,a的地址就是0x00b的地址就是0x01c的地址就是0x02d的地址就是0x04,它們的地址的表達(dá)方式可以寫成:&a&b&c&d。這樣就代表了相應(yīng)變量的地址,C語(yǔ)言中變量前加一個(gè)&表示取這個(gè)變量的地址,&這個(gè)符號(hào)就叫做“取址符”。
講到這里,有一點(diǎn)延伸內(nèi)容,大家可以了解下:比如變量cunsigned int類型的,占了2個(gè)字節(jié),存儲(chǔ)在了0x020x03這兩個(gè)內(nèi)存地址上,那么0x02是他的低字節(jié)還是高字節(jié)呢?這個(gè)問題由所用的C編譯器與CPU架構(gòu)共同決定,單片機(jī)類型不同就有可能不同,大家知道這么回事即可。比如:在我們使用的keil+51單片機(jī)的環(huán)境下,0x02存的是高字節(jié),0x03存的是低字節(jié)。這是編譯底層實(shí)現(xiàn)上的細(xì)節(jié)問題,并不影響上層的應(yīng)用,如下這兩種情況在應(yīng)用上就絲毫不受這個(gè)細(xì)節(jié)的影響:強(qiáng)制類型轉(zhuǎn)換——b = (unsigned char) c,那么b的值一定是c的低字節(jié);取地址——&c,則得到一定是0x02,這都是C語(yǔ)言本身所決定的規(guī)則,不因單片機(jī)編譯器的不同而有所改變。
實(shí)際生活中,我們要尋找一個(gè)人有兩種方式,一種方式是通過(guò)它的名字來(lái)找人,還有第二種方式就是通過(guò)它的住宅地址來(lái)找人。我們?cè)谂沙鏊膽艏芾硐到y(tǒng)的信息輸入方框內(nèi),輸入小明的家庭住址,系統(tǒng)會(huì)自動(dòng)指向小明的相關(guān)信息,輸入小剛的家庭住址,系統(tǒng)會(huì)自動(dòng)指向小剛的相關(guān)信息。這個(gè)供我們輸入地址的這個(gè)方框,在戶籍管理系統(tǒng)叫做“地址輸入框”。
那么,在C語(yǔ)言中,我們要訪問一個(gè)變量,同樣有兩種方式:一種是通過(guò)變量名來(lái)訪問,另一種自然就是通過(guò)變量的地址來(lái)訪問了。在C語(yǔ)言中,地址就等同于指針,變量的地址就是變量的指針。我們要把地址送到上邊那個(gè)所謂的“地址輸入框”內(nèi),這個(gè)“地址輸入框”既可以輸入x的指針,又可以輸入y的指針,所以相當(dāng)于一個(gè)特殊的變量——保存指針的變量,因此稱之為指針變量,簡(jiǎn)稱為指針,而通常我們說(shuō)的指針就是指指針變量。
地址輸入框輸入誰(shuí)的地址,指向的就是這個(gè)人的信息,而給指針變量輸入哪個(gè)普通變量的地址,它自然就指向了這個(gè)變量的內(nèi)容,通常的說(shuō)法就是指針指向了該變量。
12.1.2 指針變量的聲明
C語(yǔ)言中,變量的地址往往都是編譯系統(tǒng)自動(dòng)分配的,對(duì)我們用戶來(lái)說(shuō),我們是不知道某個(gè)變量的具體地址的。所以我們定義一個(gè)指針變量p,把普通變量a的地址直接送給指針變量p就是p = &a;這樣的寫法。
對(duì)于指針變量p的定義和初始化,一般有兩種方式,這兩種方式,初學(xué)者很容易混淆,因此這個(gè)地方?jīng)]別的方法,就是死記硬背,記住即可。
方法1:定義時(shí)直接進(jìn)行初始化賦值。
       unsigned   char   a;
       unsigned   char   *p = &a;
方法2:定義后再進(jìn)行賦值。
       unsigned   char   a;
       unsigned   char  *p;
       p = &a;
大家仔細(xì)看會(huì)看出來(lái)這兩種寫法的區(qū)別,它們都是正確的。我們?cè)诙x的指針變量前邊加了個(gè)*,這個(gè)*p就代表了這個(gè)p是個(gè)指針變量,不是個(gè)普通的變量,它是專門用來(lái)存放變量地址的。此外,我們定義*p的時(shí)候,用了unsigned char來(lái)定義,這里表示的是這個(gè)指針指向的變量類型是unsigned char型的。
指針變量似乎比較好理解,大家也能很容易就聽明白。但是為什么很多人弄不明白指針呢?因?yàn)樵?font face="Times New Roman">C語(yǔ)言中,有一些運(yùn)算和定義,他們是有區(qū)別的,很多同學(xué)就是沒弄明白他們的區(qū)別,指針就始終學(xué)不好。這里我要重點(diǎn)強(qiáng)調(diào)兩個(gè)區(qū)別,只要把這兩個(gè)區(qū)別弄明白了,起碼指針變量這部分就不是問題了。這兩個(gè)重點(diǎn)現(xiàn)在大家死記硬背,直接記住即可,靠理解有可能混淆概念。
第一個(gè)重要區(qū)別:指針變量p和普通變量a的區(qū)別。
我們定義一個(gè)變量a,同時(shí)也可以給變量a賦值a = 1,也可以賦值a = 2
我們定義一個(gè)指針變量p,另外還定義了一個(gè)普通變量a=1,普通變量b=2,那么這個(gè)指針變量可以指向a的地址,也可以指向b的地址,可以寫成p = &a,也可以寫成p = &b,但是就不能寫成p = 1或者p = 2或者p = a,這三種表達(dá)方式都是錯(cuò)的。
因此這個(gè)地方,不要看到定義*p的時(shí)候前邊有個(gè)unsigned char型,就錯(cuò)誤的賦值p=1,這個(gè)只是說(shuō)明p指向的變量是這個(gè)unsigned char類型的,而p本身,是指針變量,不可以給他賦值普通的值或者變量,后邊我們會(huì)直接把指針變量稱之為指針,大家要注意一下這個(gè)小細(xì)節(jié)。
    前邊這個(gè)區(qū)別似乎比較好理解,還有第二個(gè)重要區(qū)別,一定要記清楚。
第二個(gè)重要區(qū)別:定義指針變量*p和取值運(yùn)算*p的區(qū)別。
*”這個(gè)符號(hào),在我們的C語(yǔ)言有三個(gè)用法,第一個(gè)用法很簡(jiǎn)單,乘法操作就是用這個(gè)符號(hào),這里就不講了。
第二個(gè)用法,是定義指針變量的時(shí)候用的,比如unsigned char *p,這個(gè)地方使用“*”代表的意思是p是一個(gè)指針變量,而非普通的變量。
還有第三種用法,就是取值運(yùn)算,和定義指針變量是完全兩碼事,比如:
unsigned   char   a = 1;
unsigned   char   b = 2;
unsigned   char  *p;
p = &a;
b = *p;
這樣兩步運(yùn)算完了之后,b的值就成了1了。在指針這塊,&a表示取a這個(gè)變量的地址,把這個(gè)地址送給p之后,再用*p運(yùn)算表示的是取指針變量p指向的地址的變量的值,又把這個(gè)值送給了b,最終的結(jié)果相當(dāng)于b=a。同樣是*p,放在定義的位置就是定義指針變量,放在程序中就是取值運(yùn)算。
這兩個(gè)重要區(qū)別,大家可以反復(fù)閱讀三四遍,把這兩個(gè)重要區(qū)別弄明白,指針的大門就順利的踏進(jìn)去一只腳了。至于詳細(xì)的用法,我們后邊用得多了就會(huì)慢慢熟悉起來(lái)了。
12.1.3 指針的簡(jiǎn)易應(yīng)用程序
前邊我們提到了,指針的意義往往在小程序里是體現(xiàn)不出來(lái)的,對(duì)于簡(jiǎn)易程序來(lái)說(shuō),有時(shí)候用了指針,反而可能比沒用指針還麻煩,但是為了讓大家鞏固一下指針的用法,我還是寫了個(gè)使用指針的流水燈程序,目的是讓大家從簡(jiǎn)單程序開始了解指針,當(dāng)程序復(fù)雜的時(shí)候不至于手足無(wú)措。

#include <reg52.h>

sbit  ADDR0 = P1^0;
sbit  ADDR1 = P1^1;
sbit  ADDR2 = P1^2;
sbit  ADDR3 = P1^3;
sbit  ENLED = P1^4;

void ShiftLeft(unsigned char *p);

void main(void)
{
    unsigned int  i = 0;
    unsigned char buf = 0x01;

    ADDR0 = 0;   //選擇獨(dú)立LED
    ADDR1 = 1;
    ADDR2 = 1;
    ADDR3 = 1;
    ENLED = 0;   //LED總使能

    while(1)
    {
        P0 = ~buf;               //緩沖值取反送到P0
        for (i=0; i<20000; i++); //延時(shí)
        ShiftLeft(&buf);         //緩沖值左移一位
        if (buf == 0)            //如移位后為0則重賦初值
        {
            buf = 0x01;
        }
    }
}

void ShiftLeft(unsigned char *p)
{
    *p = *p << 1;  //利用指針變量可以向函數(shù)外輸出運(yùn)算結(jié)果
}

這是一個(gè)使用指針實(shí)現(xiàn)流水燈的例子,純粹是為了講指針而寫這樣一段程序,程序中傳遞的是buf的地址,把這個(gè)地址值直接傳遞給函數(shù)ShiftLeft的形參指針變量p,也就是p指向了buf。對(duì)比之前的函數(shù)調(diào)用,大家是否看明白,如果是普通變量傳遞,只能單向的,也就是說(shuō),主函數(shù)傳遞給子函數(shù)的值,子函數(shù)只能使用卻不能改變。而現(xiàn)在我們傳遞的是指針,不僅僅我們的子函數(shù)可以使用buf里邊的值,而且還可以對(duì)buf里邊的值進(jìn)行改變。
此外再?gòu)?qiáng)調(diào)一句,只要是*p前邊帶了變量類型如unsigned char,就是表示定義了一個(gè)指針變量p,程序中的*p,是指p所指向的內(nèi)容。
通過(guò)理論的學(xué)習(xí)和這樣一個(gè)例程,我想大家對(duì)指針應(yīng)該有概念了,至于它的靈活應(yīng)用,需要我們?cè)诤筮叺某绦蛑腥ンw會(huì),理論上就不再過(guò)多贅述了。
[size=14.0000pt]12.2 [size=14.0000pt]指向數(shù)組元素的指針[size=14.0000pt]12.2.1 [size=14.0000pt]指向數(shù)組元素的指針的介紹和運(yùn)算法則
所謂指向數(shù)組元素的指針,其本質(zhì)還是變量的指針。因?yàn)槲覀兊臄?shù)組里的每個(gè)元素,其實(shí)都可以直接看成是一個(gè)變量,所以指向數(shù)組元素的指針,也就是變量的指針。
指向數(shù)組元素的指針不難,但很常用。我們用程序來(lái)解釋會(huì)比較直觀一些。
unsigned  char  number[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
unsigned  char *p;
如果我們寫p = &number[0];那么指針p就指向了number的第0號(hào)元素,也就是把number[0]的地址賦值給了p,同理,如果寫p =&number[1];p就指向了數(shù)組number的第1號(hào)元素,p=&number[x];其中x的取值范圍是0<=x<=9,表示p指向了數(shù)組number的第x號(hào)元素。
指針本身,也可以進(jìn)行幾種簡(jiǎn)單的運(yùn)算,這幾種運(yùn)算對(duì)于數(shù)組元素的指針來(lái)說(shuō)應(yīng)用最多。
1、比較運(yùn)算。比較的前提是兩個(gè)指針指向同種類型的對(duì)象,比如兩個(gè)指針變量pq他們指向了具有同種數(shù)據(jù)類型的數(shù)組,那他們可以進(jìn)行<>>=<===等關(guān)系運(yùn)算。如果p==q為真的話,表示這兩個(gè)指針指向的是同一個(gè)元素。
2、指針和整數(shù)可以直接進(jìn)行加減運(yùn)算。比如還是上邊我們那個(gè)指針p和數(shù)組number,如果p = &number[0],那么p+1就指向了number[1]p+9就指向了number[9]。當(dāng)然了,如果p = &number[9]p-9也就指向了number[0]
3、兩個(gè)指針變量在一定條件下可以進(jìn)行減法運(yùn)算。如p = &number[0]; q = &number[9];那么q-p的結(jié)果就是9。但是這個(gè)地方大家要特別注意,這個(gè)9代表的是元素的個(gè)數(shù),而不是真正的地址差值。如果我們的number的變量類型是unsigned int型,占2個(gè)字節(jié),q-p的結(jié)果依然是9,他代表的是數(shù)組元素的個(gè)數(shù)。
在數(shù)組元素指針這里還有一種情況,就是我們的數(shù)組名字代表了數(shù)組元素的首地址,也就是說(shuō)
p = &number[0];
p = number;
這兩種表達(dá)方式是等價(jià)的,因此以下幾種表達(dá)形式和內(nèi)容需要大家格外注意一下。
1、根據(jù)指針的運(yùn)算規(guī)則,p+x代表的是number[x]的地址,那么number+x代表的也是number[x]的地址。或者說(shuō),他們指向的都是number數(shù)組的第x號(hào)元素。
2、*(p+x)*(number+x)都表示number[x]
3、指向數(shù)組元素的指針也可以表示成數(shù)組的形式,也就是說(shuō),允許指針變量帶下標(biāo),即p[ i]*(p+i)等價(jià)。但是為了避免混淆與規(guī)范起見,這里我們建議大家不要寫成前者,而一律采用后者的寫法。但如果看到別人那么寫,也知道是怎么回事即可。
二維數(shù)組元素的指針和一維數(shù)組類似,需要介紹的內(nèi)容不多。假如現(xiàn)在一個(gè)指針變量p和一個(gè)二維數(shù)組number[3][4],它的地址的表達(dá)方式也就是p=&number[0][0],有一個(gè)地方要注意,既然數(shù)組名代表了數(shù)組元素的首地址,那么也就是說(shuō)pnumber都是指數(shù)組的首地址。對(duì)二維數(shù)組來(lái)說(shuō),number[0]number[1]number[2]都可以看成是一維數(shù)組的數(shù)組名字,所以number[0]等價(jià)于&number[0][0]number[1]等價(jià)于&number[1][0]number[2]等價(jià)于&number[2][0]。加減運(yùn)算和一維數(shù)組是類似的,不再詳述。
12.2.2 指向數(shù)組元素的指針應(yīng)用例程
在我們的C語(yǔ)言里邊,sizeof()可以用來(lái)獲取括號(hào)內(nèi)的對(duì)象所占用的內(nèi)存字節(jié)數(shù),雖然它寫作函數(shù)的形式,但它并不是一個(gè)函數(shù),而是C語(yǔ)言的一個(gè)關(guān)鍵字,sizeof()整體在程序代碼中就相當(dāng)于一個(gè)常量,也就是說(shuō)這個(gè)獲取操作是在程序編譯的時(shí)候進(jìn)行的,而不是在程序運(yùn)行的時(shí)候進(jìn)行。這是一個(gè)實(shí)際編程中很有用的關(guān)鍵字,靈活運(yùn)用它可以為程序帶來(lái)更好可讀性、易維護(hù)性和可移植性,在后續(xù)的例程學(xué)習(xí)中將會(huì)慢慢有所體會(huì)的。
sizeof()括號(hào)中的可以是變量名,也可以是變量類型,其結(jié)果是等效的。而其更大的用處是與數(shù)組名搭配使用,這樣可以獲取整個(gè)數(shù)組占用的字節(jié)數(shù),就不用自己動(dòng)手計(jì)算了。
下面我們提供了一個(gè)簡(jiǎn)單的串口演示例程,可以體驗(yàn)一下指針和sizeof()的用法。例程首先接收上位機(jī)下發(fā)的命令,根據(jù)命令值分別把不同數(shù)據(jù)的數(shù)據(jù)回發(fā)給上位機(jī),程序還用到了指針的自增運(yùn)算,也就是+1運(yùn)算,大家可以認(rèn)真考慮一下指針ptrTxd在串口發(fā)送的過(guò)程中的指向是如何變化的。在上位機(jī)串口調(diào)試助手中分別下發(fā)1,2,3,4,就會(huì)得到不同的數(shù)組回發(fā),注意這里都用十六進(jìn)制發(fā)送和十六進(jìn)制顯示。
此外,這個(gè)程序還應(yīng)用到一個(gè)小技巧,這里大家要學(xué)會(huì)使用。我們前邊講了串口發(fā)送中斷標(biāo)志位TI是硬件置位,軟件清零的。通常來(lái)講,我們想一次發(fā)送多個(gè)數(shù)據(jù)的時(shí)候,就需要把第一個(gè)字節(jié)寫入SBUF,然后在等待發(fā)送中斷,再在后續(xù)中斷中在發(fā)送剩余的數(shù)據(jù),這樣我們的數(shù)據(jù)發(fā)送過(guò)程就被拆分到了兩個(gè)地方——主循環(huán)內(nèi)和中斷服務(wù)函數(shù)內(nèi),無(wú)疑就使得程序結(jié)構(gòu)變得零散了。這個(gè)時(shí)候,為了使程序結(jié)構(gòu)盡量緊湊,在啟動(dòng)發(fā)送的時(shí)候,不是向SBUF中寫入第一個(gè)待發(fā)的字節(jié),而是直接讓TI=1,注意,這時(shí)候會(huì)馬上進(jìn)入串口中斷,因?yàn)橹袛鄻?biāo)志位置1了,但是串口線上并沒有發(fā)送任何數(shù)據(jù),于是,我們所有的數(shù)據(jù)發(fā)送都可以在中斷中進(jìn)行,而不用再分為兩部分了。大家可以在程序中體會(huì)一下這個(gè)技巧的好處。

#include <reg52.h>

bit cmdArrived = 0;   //命令到達(dá)標(biāo)志,即接收到上位機(jī)下發(fā)的命令
unsigned char cmdIndex = 0; //命令索引,即與上位機(jī)約定好的數(shù)組編號(hào)
unsigned char cntTxd = 0;   //串口發(fā)送計(jì)數(shù)器
unsigned char *ptrTxd = 0;  //串口發(fā)送指針

unsigned char array1[1] = {1};
unsigned char array2[2] = {1,2};
unsigned char array3[4] = {1,2,3,4};
unsigned char array4[8] = {1,2,3,4,5,6,7,8};

void ConfigUART(unsigned int baud);

void main ()
{
    ConfigUART(9600);  //配置波特率為9600
    EA = 1;  //開總中斷

    while(1)
    {
        if (cmdArrived)
        {
            cmdArrived = 0;
            switch (cmdIndex)
            {
                case 1:
                    ptrTxd = array1;         //數(shù)組1的首地址賦值給發(fā)送指針
                    cntTxd = sizeof(array1); //數(shù)組1的長(zhǎng)度賦值給發(fā)送計(jì)數(shù)器
                    TI = 1;                //手動(dòng)方式啟動(dòng)發(fā)送中斷,處理數(shù)據(jù)發(fā)送
                    break;
                case 2:
                    ptrTxd = array2;
                    cntTxd = sizeof(array2);
                    TI = 1;
                    break;
                case 3:
                    ptrTxd = array3;
                    cntTxd = sizeof(array3);
                    TI = 1;
                    break;
                case 4:
                    ptrTxd = array4;
                    cntTxd = sizeof(array4);
                    TI = 1;
                    break;
                default:
                    break;
            }
        }
    }
}

void ConfigUART(unsigned int baud)  //串口配置函數(shù),baud為波特率
{
    SCON = 0x50;   //配置串口為模式1
    TMOD &= 0x0F;  //清零T1的控制位
    TMOD |= 0x20;  //配置T1為模式2
    TH1 = 256 - (11059200/12/32) / baud;  //計(jì)算T1重載值
    TL1 = TH1;     //初值等于重載值
    ET1 = 0;       //禁止T1中斷
    ES  = 1;       //使能串口中斷
    TR1 = 1;       //啟動(dòng)T1
}


void InterruptUART() interrupt 4
{
    if (RI)  //接收到字節(jié)
    {
        RI = 0;           //手動(dòng)清零接收中斷標(biāo)志位
        cmdIndex = SBUF;  //接收到的數(shù)據(jù)保存到命令索引中
        cmdArrived = 1;   //設(shè)置命令到達(dá)標(biāo)志
    }
    if (TI)  //字節(jié)發(fā)送完畢
    {
        TI = 0;    //手動(dòng)清零發(fā)送中斷標(biāo)志位
        if (cntTxd > 0)  //有待發(fā)送數(shù)據(jù)時(shí),繼續(xù)發(fā)送后續(xù)字節(jié)
        {
            SBUF = *ptrTxd;  //發(fā)出指針指向的數(shù)據(jù)
            cntTxd--;        //發(fā)送計(jì)數(shù)器遞減
            ptrTxd++;        //發(fā)送指針遞增
        }
    }
}
12.3 字符數(shù)組和字符指針12.3.1 常量和符號(hào)常量
在程序運(yùn)行過(guò)程中,其值不能被改變的量稱之為常量。常量分為不同的類型,有整型常量如123100等;浮點(diǎn)型常量3.140.56-4.8;字符型常量’a’,’b’,’0’;字符串常量”a”,”abc”,”1234”,”1234abcd”等。
細(xì)心的同學(xué)會(huì)發(fā)現(xiàn),整型和浮點(diǎn)型常量我們直接寫的數(shù)字,而字符型常量用單引號(hào)來(lái)表示一個(gè)字符,用雙引號(hào)來(lái)表示一個(gè)字符串,尤其大家要注意’a’和”a”是不一樣的,這個(gè)等會(huì)我們要詳細(xì)介紹。
常量一般有2種表現(xiàn)形式。
1、直接常量:直接以值的形式表示的常量稱之為直接常量。上述舉例這些都是直接常量,直接寫出來(lái)了。
2、符號(hào)常量:用標(biāo)識(shí)符命名的常量稱之為符號(hào)常量,就是為上面的直接常量再取一個(gè)名字。使用符號(hào)常量一是方便理解,提高程序可讀性,更重要的是方便程序的后續(xù)維護(hù),習(xí)慣上符號(hào)常量我們都用大寫字母和下劃線來(lái)命名。
比如,我們可以把3.14取名為PI(即π)。再比如,我們上節(jié)課的串口程序,我們用的波特率是9600,如果用符號(hào)常量來(lái)進(jìn)行提前聲明的話,那我們要修改成其他速率的話,就不用在程序中找9600修改了,直接修改聲明處就可以了,兩種方法舉例說(shuō)明。
1、用const聲明。比如我們?cè)诔绦蜷_始位置定義一個(gè)符號(hào)常量BAUD
    定義形式是   const   類型   符號(hào)常量名字=常量值;
const  unsigned  int  BAUD = 9600;       /*注意結(jié)尾有個(gè)分號(hào)*/
我們就可以在程序中直接把9600改成BAUD,這樣我們?nèi)绻牟ㄌ芈实脑挘苯釉诔绦蜷_頭位置改一下這個(gè)值就可以了。
2、用預(yù)處理命令#define來(lái)完成,預(yù)處理命令我們先來(lái)認(rèn)識(shí)#define
    定義形式是  #define   符號(hào)常量名   常量值
#define  BAUD   9600                   /*注意結(jié)尾沒有分號(hào)*/
這樣定義以后,只要在程序中出現(xiàn)BAUD的話,意思就是完全替代了后邊的9600這個(gè)數(shù)字。
不知大家是否記得,我們之前在數(shù)碼管真值表,用了一個(gè)code關(guān)鍵字
    unsigned char code LedChar[] = {   //用數(shù)組來(lái)表示數(shù)碼管真值表
        0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
        0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8e
};
我們當(dāng)時(shí)說(shuō)加了code之后,這個(gè)真值表的數(shù)據(jù)只能被使用,不能被改變,如果我們直接寫LedChar[0] = 1;這樣就錯(cuò)了。實(shí)際上code這個(gè)關(guān)鍵字是51單片機(jī)特有的,如果是其他型號(hào)的單片機(jī)我們只需要寫成const unsigned char LedChar[]={}就可以了,自動(dòng)保存到FLASH里,而51單片機(jī)只用const而不加code的話,這個(gè)數(shù)組會(huì)保存到RAM中,而不會(huì)保存到FLAHS中,鑒于此,在51這個(gè)體系下,const反倒變得不那么重要了,它的作用被code取代了,這里大家知道這么回事即可。
我們來(lái)對(duì)各種類型的常量做進(jìn)一步說(shuō)明。
整型常量和浮點(diǎn)型常量沒太多內(nèi)容在里邊了,之前我們應(yīng)用都很熟練了,整型直接寫數(shù)字就是十進(jìn)制如128,前邊0x開頭的表示是十六進(jìn)制0x80,浮點(diǎn)型直接寫帶小數(shù)點(diǎn)的數(shù)據(jù)就可以了。
字符型常量是由一對(duì)單引號(hào)括起來(lái)的單個(gè)字符。它分為兩種形式,一種是普通字符,一種是轉(zhuǎn)義字符。
1、普通字符就是那些我們可以直接書寫直接看到的有形的字符,比如阿拉伯?dāng)?shù)字09,英文字符Az,以及標(biāo)點(diǎn)符號(hào)等。它們都是ASCII碼表中的字符,而它們?cè)趩纹瑱C(jī)中都占用一個(gè)字節(jié)的空間,其值就是對(duì)應(yīng)的ASCII碼值。比如’a’的值是97’A’的值是65,0’的值是48,如果定義一個(gè)變量unsigned char a = ’a’,那么變量a的值就是97
2、除了上述這些字符之外,還有一些特殊字符,它們一些是無(wú)形的,像回車符、換行符這些都是看不到的,還有一些像’、”這類字符它們已經(jīng)有特殊用途了,想象一下如果寫’’’你覺得編譯器會(huì)怎么取解釋。針對(duì)這些特殊符號(hào),為了可以讓它們正常進(jìn)入到我們的程序代碼中,C語(yǔ)言就規(guī)定了轉(zhuǎn)義字符,它是以反斜杠(\)開頭的特定字符序列,讓它們來(lái)表示這些特殊字符,比如我們用\n來(lái)代表?yè)Q行,這塊內(nèi)容我們后邊有程序說(shuō)明。我們用一個(gè)簡(jiǎn)單表格來(lái)說(shuō)明一下常用的轉(zhuǎn)義字符的意思,如表12-2所示。
12-2 常用轉(zhuǎn)義字符及含義
字符形式
含義
\n
換行
\t
橫向跳格(相當(dāng)于Tab)
\v
豎向跳格
\b
退格
\r
光標(biāo)移到行首
\\
反斜杠字符’\’
\’
單引號(hào)字符
\”
雙引號(hào)字符
\f
走紙換頁(yè)
\0
空值
    表格不需要大家記住,用到了,過(guò)來(lái)查就可以了。
字符串常量是用雙引號(hào)括起來(lái)的字符序列,一般我們都稱之字符串。如”a”,”1234”,”welcome to www.kingst.org”等都是字符串常量。字符串常量在內(nèi)存中按順序逐個(gè)存儲(chǔ)字符串中的字符的ASCII碼值,并且特別注意,最后還有一個(gè)字符’\0’,’0’字符的ASCII碼值是0,它是字符串結(jié)束標(biāo)志,在寫字符串的時(shí)候,這個(gè)‘\0’是隱藏的,我們看不到,但是實(shí)際卻是存在的。所以”a”就比’a’多了一個(gè)’\0’,”a”的就占了2個(gè)字節(jié),而’a’只占一個(gè)字節(jié)。
還有一個(gè)地方要注意,就是字符串中的空格,也是一個(gè)字符,比如”welcome to www.kingst.org”一共占了26個(gè)字節(jié)的空間。其中21個(gè)字母,2個(gè)’.’,2個(gè)‘ ’(空格字符)以及一個(gè)’\0’。
[size=14.0000pt]12.3.2 [size=14.0000pt]字符和字符串?dāng)?shù)組應(yīng)用例程
為了對(duì)比字符串,字符數(shù)組,常量數(shù)組的區(qū)別,我們寫個(gè)了簡(jiǎn)單的演示程序,定義了4個(gè)數(shù)組分別是:
unsigned char array1[] = "1-Hello!\r\n";
    unsigned char array2[] = {'2', '-', 'H', 'e', 'l', 'l', 'o', '!', '\r', '\n'};
    unsigned char array3[] = {51,  45,  72,  101, 108, 108, 111, 33,  13,   10};
unsigned char array4[] = "4-Hello!\r\n";
在串口調(diào)試助手下,發(fā)送十六進(jìn)制的1234,使用字符形式顯示的話,會(huì)分別往電腦上送這4個(gè)數(shù)組中對(duì)應(yīng)的那個(gè)數(shù)組。我們只是在起始位置做了區(qū)分,其他均沒做區(qū)分。大家可以比較一下效果。
此外還要說(shuō)明一點(diǎn),數(shù)組1和數(shù)組4,數(shù)組1我們是發(fā)完整的字符串,而數(shù)組4我們僅僅發(fā)送數(shù)組中的字符,沒有發(fā)結(jié)束符號(hào)。串口調(diào)試助手用字符形式顯示是沒有區(qū)別的,但是大家如果改用十六進(jìn)制顯示,大家會(huì)發(fā)現(xiàn)數(shù)組1比數(shù)組4多了一個(gè)字節(jié)’\0’的ASCII00

#include <reg52.h>

bit cmdArrived = 0;   //命令到達(dá)標(biāo)志,即接收到上位機(jī)下發(fā)的命令
unsigned char cmdIndex = 0; //命令索引,即與上位機(jī)約定好的數(shù)組編號(hào)
unsigned char cntTxd = 0;   //串口發(fā)送計(jì)數(shù)器
unsigned char *ptrTxd = 0;  //串口發(fā)送指針

unsigned char array1[] = "1-Hello!\r\n";
unsigned char array2[] = {'2', '-', 'H', 'e', 'l', 'l', 'o', '!', '\r', '\n'};
unsigned char array3[] = {51,  45,  72,  101, 108, 108, 111, 33,  13,   10};
unsigned char array4[] = "4-Hello!\r\n";

void ConfigUART(unsigned int baud);

void main ()
{
    ConfigUART(9600);  //配置波特率為9600
    EA = 1;  //開總中斷

    while(1)
    {
        if (cmdArrived)
        {
            cmdArrived = 0;
            switch (cmdIndex)
            {
                case 1:
                    ptrTxd = array1;         //數(shù)組1的首地址賦值給發(fā)送指針
                    cntTxd = sizeof(array1); //數(shù)組1的長(zhǎng)度賦值給發(fā)送計(jì)數(shù)器
                    TI = 1;                //手動(dòng)方式啟動(dòng)發(fā)送中斷,處理數(shù)據(jù)發(fā)送
                    break;
                case 2:
                    ptrTxd = array2;
                    cntTxd = sizeof(array2);
                    TI = 1;
                    break;
                case 3:
                    ptrTxd = array3;
                    cntTxd = sizeof(array3);
                    TI = 1;
                    break;
                case 4:
                    ptrTxd = array4;
                    cntTxd = sizeof(array4) - 1;//字符串長(zhǎng)度為數(shù)組長(zhǎng)度減1
                    TI = 1;
                    break;
                default:
                    break;
            }
        }
    }
}

void ConfigUART(unsigned int baud)  //串口配置函數(shù),baud為波特率
{
    SCON = 0x50;   //配置串口為模式1
    TMOD &= 0x0F;  //清零T1的控制位
    TMOD |= 0x20;  //配置T1為模式2
    TH1 = 256 - (11059200/12/32) / baud;  //計(jì)算T1重載值
    TL1 = TH1;     //初值等于重載值
    ET1 = 0;       //禁止T1中斷
    ES  = 1;       //使能串口中斷
    TR1 = 1;       //啟動(dòng)T1
}

void InterruptUART() interrupt 4
{
    if (RI)  //接收到字節(jié)
    {
        RI = 0;           //手動(dòng)清零接收中斷標(biāo)志位
        cmdIndex = SBUF;  //接收到的數(shù)據(jù)保存到命令索引中
        cmdArrived = 1;   //設(shè)置命令到達(dá)標(biāo)志
    }
    if (TI)  //字節(jié)發(fā)送完畢
    {
        TI = 0;    //手動(dòng)清零發(fā)送中斷標(biāo)志位
        if (cntTxd > 0)  //有待發(fā)送數(shù)據(jù)時(shí),繼續(xù)發(fā)送后續(xù)字節(jié)
        {
            SBUF = *ptrTxd;  //發(fā)出指針指向的數(shù)據(jù)
            cntTxd--;        //發(fā)送計(jì)數(shù)器遞減
            ptrTxd++;        //發(fā)送指針遞增
        }
    }
}

12.4 1602液晶的認(rèn)識(shí)12.4.1 1602液晶的硬件接口介紹
[size=14.0000pt]
前邊我們講的流水燈、數(shù)碼管、LED點(diǎn)陣這三種都是LED設(shè)備,這節(jié)課我們來(lái)學(xué)習(xí)一下LCD顯示設(shè)備——1602液晶。那個(gè)大大的,平時(shí)第一行顯示16個(gè)小黑塊,第二行什么都不顯示的東西就是1602液晶,是不是早就注意到它了呢?
大家學(xué)習(xí)這些電子器件,頭腦中要逐漸形成一種意識(shí),不管是我們的單片機(jī),還是74HC138,甚至我們的三極管等等,都是有數(shù)據(jù)手冊(cè)的。不管是設(shè)計(jì)電路還是編寫程序,器件的數(shù)據(jù)手冊(cè)是我們最好的參考資料,那我們今天來(lái)學(xué)習(xí)1602,首先就要看它的數(shù)據(jù)手冊(cè)。手冊(cè)大家可以在網(wǎng)上找到,這里我講的時(shí)候只挑手冊(cè)的重點(diǎn)講。
首先我們來(lái)看一個(gè)主要技術(shù)參數(shù)表格,如表12-3所示。
12-3 1602液晶主要技術(shù)參數(shù)
顯示容量
16 x 2個(gè)字符
芯片工作電壓
4.5~5.5V
工作電流
2.0mA(5.0V)
模塊最佳工作電壓
5.0V
字符尺寸
2.95 x 4.35mm (寬乘高)
1602液晶,從它的名字我們就可以理解他的顯示容量,就是可以顯示2行,每行16個(gè)字符的液晶。他的工作電壓4.5V5.5V,這個(gè)我們?cè)O(shè)計(jì)電路的時(shí)候,直接按照5V系統(tǒng)設(shè)計(jì),但是保證我們的5V系統(tǒng)最低不能低于4.5V。在5V工作電壓下測(cè)量它的工作電流是2mA,大家注意,這個(gè)2mA僅僅是指液晶,而它的黃綠背光都是用LED做的,所以功耗不會(huì)太小的,一二十毫安還是有的。
1602液晶一共16個(gè)引腳,每個(gè)引腳的功能,我們都可以在它的數(shù)據(jù)手冊(cè)上獲得。而這些基本的信息,在我們?cè)O(shè)計(jì)電路和編寫代碼之前,必須先看明白,如表12-4所示。
12-4 1602液晶引腳功能
編號(hào)
符號(hào)
引腳說(shuō)明
編號(hào)
符號(hào)
引腳說(shuō)明
1
VSS
電源地
9
D2
Data  I/O
2
VDD
電源正極
10
D3
Data  I/O
3
VL
液晶顯示偏壓信號(hào)
11
D4
Data  I/O
4
RS
數(shù)據(jù)/命令選擇端(H/L)
12
D5
Data  I/O
5
R/W
/寫選擇端(H/L)
13
D6
Data  I/O
6
E
使能信號(hào)
14
D7
Data  I/O
7
D0
Data  I/O
15
BLA
背光源正極
8
D1
Data  I/O
16
BLK
背光源負(fù)極
液晶的電源12腳以及背光電源1516腳,不用多說(shuō),正常接就可以了。
3腳叫做液晶顯示偏壓信號(hào),大家注意到小黑塊沒有,當(dāng)我們要顯示一個(gè)字符的時(shí)候,有的黑點(diǎn)顯示,有的黑點(diǎn)就不能顯示,這樣就可以實(shí)現(xiàn)我們想要的字符了。我們這個(gè)3腳就是用來(lái)調(diào)整顯示的黑點(diǎn)和不顯示的之間的對(duì)比度,調(diào)整好了對(duì)比度,就可以讓我們的顯示更加清晰一些。在進(jìn)行電路設(shè)計(jì)實(shí)驗(yàn)的時(shí)候,通常的辦法是在這個(gè)引腳上接個(gè)電位器,也就是我們初中學(xué)過(guò)的滑動(dòng)變阻器,是一個(gè)東西。通過(guò)調(diào)整電位器的分壓值,來(lái)調(diào)整3腳的電壓。而當(dāng)產(chǎn)品批量生產(chǎn)的時(shí)候,我們可以把我們調(diào)整好的這個(gè)值直接用簡(jiǎn)單電路來(lái)實(shí)現(xiàn),就如同在我們板子上,我們直接使用的是一個(gè)18歐的下拉電阻,市面上有的1602的下拉電阻大概11.5k也是比較合適的值。
4腳是數(shù)據(jù)命令選擇端。我們的液晶,有時(shí)候我們要發(fā)送一些命令,讓他實(shí)現(xiàn)我們想要的一些狀態(tài),有時(shí)候我們要發(fā)給他一些數(shù)據(jù),讓他顯示出來(lái),液晶就通過(guò)這個(gè)引腳來(lái)判斷接收到的是數(shù)據(jù)還是命令,這個(gè)引腳我們接到了ADDR0上,通過(guò)跳線帽和P1.0連接在一起。大家注意學(xué)會(huì)讀手冊(cè),看到我們這個(gè)引腳描述里:數(shù)據(jù)/命令選擇端,而后跟了括號(hào)(H/L),他的意思就是當(dāng)這個(gè)引腳是H(High)高電平的時(shí)候,是數(shù)據(jù),當(dāng)這個(gè)引腳是L(Low)低電平的時(shí)候,是命令。
5腳和4腳用法類似,只是功能是讀寫選擇端。我們既可以寫給液晶數(shù)據(jù)或者命令,也可以讀取液晶內(nèi)部的數(shù)據(jù),就是控制這個(gè)引腳。因?yàn)橐壕П旧韮?nèi)部有RAM,實(shí)際上我們送給液晶的命令或者數(shù)據(jù),液晶需要先保存在緩存里,然后再寫到內(nèi)部的寄存器或者RAM中,這個(gè)就需要一定的時(shí)間。所以我們?cè)龠M(jìn)行讀寫操作之前,首先要讀一下液晶當(dāng)前狀態(tài),是不是在“忙”,如果不忙,我們可以讀寫數(shù)據(jù),如果在“忙”,我們需要等待液晶忙完了,再進(jìn)行操作。讀狀態(tài)是常用的,不過(guò)讀液晶數(shù)據(jù)我接觸的場(chǎng)合沒怎么用過(guò),大家了解這個(gè)功能即可。這個(gè)引腳我們接到了ADDR1上,通過(guò)跳線帽和P1.1連接在一起。
6腳是使能信號(hào),很關(guān)鍵,液晶的讀寫命令和數(shù)據(jù),都要靠它才能正常讀寫,我們后邊詳細(xì)講這個(gè)引腳怎么用。這個(gè)引腳我們通過(guò)跳線帽接到了ENLCD上,這個(gè)位置的跳線帽是為了和我們將來(lái)以后另外一個(gè)12864液晶的切換使用的。
714引腳就是8個(gè)數(shù)據(jù)引腳了,我們就是通過(guò)這8個(gè)引腳讀寫數(shù)據(jù)和命令的。我們統(tǒng)一接到了P0總線上了。我們來(lái)看一下我們開發(fā)板上的1602的原理圖,如圖12-1所示。
圖12-1 1602液晶原理圖
12.4.2 1602液晶的讀寫時(shí)序介紹[size=14.0000pt]
1602液晶內(nèi)部帶了80個(gè)字節(jié)的RAM,用來(lái)存儲(chǔ)我們發(fā)送的數(shù)據(jù),他的結(jié)構(gòu)如圖12-2所示。

圖12-2 1602內(nèi)部RAM結(jié)構(gòu)圖
第一行的地址是00H27H,第二行的地址從40H67H,其中第一行00H0FH是和液晶上第一行16個(gè)字符顯示位置相互對(duì)應(yīng),第二行40H4FH是和第二行16個(gè)字符顯示位置相互對(duì)應(yīng)的。而每行都多出來(lái)一部分,是為了顯示移動(dòng)字幕設(shè)置的。1602字符液晶是顯示字符的,因此他是跟ASCII字符表是對(duì)應(yīng)的。比如我們給00H這個(gè)地址寫一個(gè)’a’,也就是10進(jìn)制的97,液晶的最左上方的那個(gè)小塊就會(huì)顯示一個(gè)a。此外,我們本章學(xué)過(guò)指針了,液晶內(nèi)部有個(gè)數(shù)據(jù)指針,它指向哪里,我們寫的那個(gè)數(shù)據(jù)就會(huì)送到相應(yīng)的那個(gè)地址里。
液晶有一個(gè)狀態(tài)字字節(jié),我們可以通過(guò)了解這個(gè)狀態(tài)字的內(nèi)容,就可以知道1602液晶的一些內(nèi)部情況,如表12-5所示。
STA0-6
當(dāng)前數(shù)據(jù)的指針的值
STA7
讀寫操作使能
1:禁止   0:允許
這個(gè)狀態(tài)字節(jié)的8位,最高位表示了當(dāng)前液晶是不是“忙”,如果這個(gè)狀態(tài)字是1表示液晶正“忙”,禁止我們讀寫數(shù)據(jù)或者命令,如果是0,則可以進(jìn)行讀寫。而低7位就表示了當(dāng)前數(shù)據(jù)地址指針的位置。
1602的基本操作時(shí)序,一共有4個(gè),這些大家都不需要記住,但是都需要理解,因?yàn)槲覀儸F(xiàn)在不是為了應(yīng)付考試了,所以不需要你把手冊(cè)背的滾瓜爛熟,但是你寫程序的時(shí)候,打開手冊(cè)能看懂如何操作,還要再提醒一句,單片機(jī)讀外部狀態(tài)前,必須先保證自己是高電平哦。
我們這里要做1602液晶程序,因此我們我們的聲明寫成:
#define LCD1602_DB   P0
sbit LCD1602_RS = P1^0;
sbit LCD1602_RW = P1^1;
sbit LCD1602_E  = P1^5;
1、讀狀態(tài):RS=LR/W=HE=H。這是個(gè)很簡(jiǎn)單的邏輯,就是說(shuō),我們直接寫
    LCD1602_DB = 0xFF;
    LCD1602_RS = 0;
LCD1602_RW = 1;
LCD1602_E = 1;
sta = LCD1602_DB;
這樣就把當(dāng)前液晶的狀態(tài)字讀到了sta這個(gè)變量中,我們可以通過(guò)判斷sta的最高位來(lái)了解當(dāng)前液晶是否處于“忙”狀態(tài),也可以得知當(dāng)前數(shù)據(jù)的指針位置。兩個(gè)問題,問題一是如果我們當(dāng)前讀到的狀態(tài)是“不忙”,那么我們程序可以進(jìn)行讀寫操作,如果當(dāng)前狀態(tài)是“忙”,那么我們還得繼續(xù)等待重新判斷液晶的狀態(tài);問題二,大家可以看我們的原理圖,我們的流水燈、數(shù)碼管、點(diǎn)陣,1602液晶都用到了P0口總線,我們讀完了液晶狀態(tài)繼續(xù)保持LCD1602_E 是高電平的話,1602液晶會(huì)繼續(xù)輸出它的狀態(tài)值,輸出的這個(gè)值會(huì)占據(jù)了P0總線,干擾到流水燈數(shù)碼管等其他外設(shè),所以我們讀完了狀態(tài),通常要把這個(gè)引腳拉低來(lái)釋放總線,這里我們用了一個(gè)do...while循環(huán)語(yǔ)句來(lái)實(shí)現(xiàn)。
    LCD1602_DB = 0xFF;
    LCD1602_RS = 0;
    LCD1602_RW = 1;
    do   // do...while循環(huán)語(yǔ)句
    {
        LCD1602_E = 1;
        sta = LCD1602_DB; //讀取狀態(tài)字
        LCD1602_E = 0;  //讀完了要關(guān)閉使能,防止液晶輸出數(shù)據(jù)干擾總線
} while (sta & 0x80); //bit7等于1表示液晶正忙,重復(fù)檢測(cè)直到其等于0為止
2、讀數(shù)據(jù):RS=HR/W=LE=H。這個(gè)邏輯也很簡(jiǎn)單,但是讀數(shù)據(jù)不常用,大家了解一下就可以了,這里就不詳細(xì)解釋了。
3、寫指令:RS=LR/W=LD0~D7=指令碼,E=高脈沖。
        這個(gè)在邏輯上沒什么難的,只是E=高脈沖這個(gè)問題要解釋一下。這個(gè)指令一共有4條語(yǔ)句,其中前三條語(yǔ)句順序無(wú)所謂,但是E=高脈沖這一句很關(guān)鍵。實(shí)際上流程是這樣的:因?yàn)槲覀儸F(xiàn)在是寫數(shù)據(jù),所以我們首先要保證我們的E引腳是低電平狀態(tài),而前三句不管我們?cè)趺磳懀?/font>1602液晶只要沒有接收到E引腳的使能控制,它都不會(huì)來(lái)讀總線上的信號(hào)的。當(dāng)通過(guò)前三句準(zhǔn)備好數(shù)據(jù)之后,E使能引腳從低電平到高電平變化,然后E使能引腳再?gòu)母唠娖降降碗娖匠霈F(xiàn)一個(gè)下降沿,1602液晶內(nèi)部一旦檢測(cè)到這個(gè)下降沿后,并且檢測(cè)到RS=LR/W=L,就馬上來(lái)讀取D0~D7的數(shù)據(jù),完成單片機(jī)寫1602指令過(guò)程。歸納總結(jié)我們寫了個(gè)E=高脈沖,就是E使能引腳先從低拉高,再?gòu)母呃停纬梢粋(gè)高脈沖,就是這個(gè)意思。
4、寫數(shù)據(jù):RS=HR/W=LD0~D7=數(shù)據(jù),E=高脈沖。
寫數(shù)據(jù)和寫指令是類似的,就是把RS改成H,把總線改成數(shù)據(jù)即可。
此外,這里要提一句,液晶1602所使用的通信時(shí)序是摩托羅拉公司所創(chuàng)立的6800時(shí)序,大家知道這么回事即可。
   
這里還要說(shuō)明一個(gè)問題,就是從這4個(gè)時(shí)序大家可以看出來(lái),我們的LCD1602的使能引腳E,高電平的時(shí)候是有效,低電平的時(shí)候是無(wú)效。如果這個(gè)引腳是個(gè)高電平的話,那么就可能被液晶認(rèn)為是讀指令或者讀數(shù)據(jù),1602就會(huì)占用P0口往外送數(shù)據(jù)。由于P0口同時(shí)控制了LED小燈、數(shù)碼管、點(diǎn)陣LED以及1602液晶,如果1602往外送數(shù)據(jù),P0口就會(huì)干擾到其他外設(shè)。因此我們?nèi)绻G闆r下,用單片機(jī)IO口直接連到1602的這個(gè)E引腳上,只要我們用LED小燈、數(shù)碼管、點(diǎn)陣,那么我們程序上來(lái)就寫一句LCD1602_E=0,就可以避免1602干擾到其他外設(shè)。我們之前的程序沒有加這句,是因?yàn)槲覀儼遄釉谶@個(gè)引腳上加了一個(gè)15K的下拉電阻,這個(gè)下拉電阻就可以保證這個(gè)引腳上電默認(rèn)后是低電平,如圖12-3所示。

12-3 液晶下拉電阻
    如果不加這個(gè)下拉電阻,剛開始講點(diǎn)亮LED小燈的時(shí)候,我們就得寫一句:LCD1602_E=0,可能很多初學(xué)者容易弄不明白,所以我們才加了這樣一個(gè)電路。但是在實(shí)際開發(fā)過(guò)程中,就不必要這樣了。如果這是個(gè)實(shí)際產(chǎn)品,能用軟件去處理的,我們就不會(huì)用硬件去實(shí)現(xiàn),所以大家在做實(shí)際產(chǎn)品的時(shí)候,這塊電路可以直接去掉,只需要在程序中最開始多加一條語(yǔ)句即可。


12.4.3 1602液晶的指令介紹
[size=14.0000pt]
和單片機(jī)寄存器的用法類似,1602液晶在使用的時(shí)候,我們首先要進(jìn)行初始化的功能配置,1602液晶有以下幾個(gè)指令需要了解。
1、顯示模式設(shè)置。
寫指令0x38,設(shè)置16x2顯示,5x7點(diǎn)陣,8位數(shù)據(jù)接口。這條指令對(duì)我們這個(gè)液晶來(lái)說(shuō)是固定的,必須寫0x38,大家仔細(xì)看會(huì)發(fā)現(xiàn)我們的液晶實(shí)際上內(nèi)部點(diǎn)陣是5x8的,還有一些1602液晶還兼容串行通信,用2個(gè)IO口即可,但是速度慢,我們這個(gè)液晶固定的0x38的模式。
2、顯示開/關(guān)以及光標(biāo)設(shè)置指令。
這里有2條指令,第一條指令一個(gè)字節(jié)中8位,其中高5位是固定的00001,低3位我們假設(shè)是DCB從高到底表示,D=1表示開顯示,D=0表示關(guān)顯示;C=1表示顯示光標(biāo),C=0表示不顯示光標(biāo);B=1表示光標(biāo)閃爍,B=0表示光標(biāo)不閃爍。
第二條指令高6位是固定的000001,低2位我們分別用NS從高到底表示,其中N=1表示讀或者寫一個(gè)字符后,指針自動(dòng)加1,光標(biāo)自動(dòng)加1N=0表示讀或者寫一個(gè)字符后指針自動(dòng)減1,光標(biāo)自動(dòng)減1S=1表示寫一個(gè)字符后,整屏顯示左移(N=1)或右移(N=0),以達(dá)到光標(biāo)不移動(dòng)而屏幕移動(dòng)的效果,如同我們的計(jì)算器輸入一樣的效果,而S=0表示寫一個(gè)字符后,整屏顯示不移動(dòng)。
3、清屏指令。
固定的,寫入01H表示顯示清屏,其中包含了數(shù)據(jù)指針清零,所有的顯示清零。寫入02H后,僅僅是數(shù)據(jù)指針清零,顯示不清零。
4RAM地址設(shè)置指令
該指令碼的最高位為1,低7位為RAM的地址,RAM地址與液晶上字符的關(guān)系如上圖12-2 所示。通常,我們?cè)谧x寫數(shù)據(jù)之前都要先設(shè)置好地址,然后再進(jìn)行數(shù)據(jù)的讀寫操作。
12.4.4 1602液晶簡(jiǎn)易程序例程[size=14.0000pt]
1602液晶手冊(cè)提供了一個(gè)初始化過(guò)程,但是它寫的比較復(fù)雜,我們這邊總結(jié)了一個(gè)更加簡(jiǎn)易方便的過(guò)程提供給大家,手冊(cè)上描述的那個(gè),大家僅僅作為了解就可以了,下面我把程序?qū)懗鰜?lái)大家看下,我們的初始化只用了4條語(yǔ)句,沒有像手冊(cè)介紹的那么繁瑣。

#include <reg52.h>

#define LCD1602_DB   P0

sbit LCD1602_RS = P1^0;
sbit LCD1602_RW = P1^1;
sbit LCD1602_E  = P1^5;

void LcdInit();
void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);

void main ()
{
    unsigned char str[] = "Kingst Studio";

    LcdInit();
    LcdShowStr(2, 0, str);
    LcdShowStr(0, 1, "Welcome to KST51");

    while(1)
    {}
}

void LcdWaitReady()  //讀取“忙”表示,等待液晶準(zhǔn)備好
{
    unsigned char sta;

    LCD1602_DB = 0xFF;
    LCD1602_RS = 0;
    LCD1602_RW = 1;
    do   // do...while循環(huán)語(yǔ)句
    {
        LCD1602_E = 1;
        sta = LCD1602_DB; //讀取狀態(tài)字
        LCD1602_E = 0;  //讀完了要關(guān)閉使能,防止液晶輸出數(shù)據(jù)干擾總線
    } while (sta & 0x80); //bit7等于1表示液晶正忙,重復(fù)檢測(cè)直到其等于0為止
}
void LcdWriteCmd(unsigned char cmd)  //寫入命令函數(shù)
{
    LcdWaitReady();
    LCD1602_RS = 0;
    LCD1602_RW = 0;
    LCD1602_DB = cmd;
    LCD1602_E  = 1;
    LCD1602_E  = 0;
}
void LcdWriteDat(unsigned char dat)  //寫入數(shù)據(jù)函數(shù)
{
    LcdWaitReady();
    LCD1602_RS = 1;
    LCD1602_RW = 0;
    LCD1602_DB = dat;  
    LCD1602_E  = 1;
    LCD1602_E  = 0;
}
void LcdInit()  //液晶初始化函數(shù)
{
    LcdWriteCmd(0x38);  //16*2顯示,5*7點(diǎn)陣,8位數(shù)據(jù)接口
    LcdWriteCmd(0x0C);  //顯示器開,光標(biāo)關(guān)閉
    LcdWriteCmd(0x06);  //文字不動(dòng),地址自動(dòng)加1
    LcdWriteCmd(0x01);  //清屏
}
void LcdShowStr(unsigned char x, unsigned char y,  unsigned char *str)  //顯示字符串,屏幕起始坐標(biāo)(x,y),字符串指針str
{
    unsigned char addr;

    //由輸入的顯示坐標(biāo)計(jì)算顯示RAM的地址
    if (y == 0)
    {
        addr = 0x00 + x; //第一行字符地址從0x00起始
    }
    else
    {
        addr = 0x40 + x; //第二行字符地址從0x40起始
    }

    //由起始顯示RAM地址連續(xù)寫入字符串
    LcdWriteCmd(addr | 0x80); //寫入起始地址
    while (*str != '\0')      //連續(xù)寫入字符串?dāng)?shù)據(jù),直到檢測(cè)到結(jié)束符
    {
        LcdWriteDat(*str);    //注意*str就是這個(gè)指針指向的內(nèi)容
        str++;
    }
}
程序我注釋的已經(jīng)很詳細(xì)了,有幾個(gè)地方再說(shuō)兩句。首先,我們把程序所有的功能都使用函數(shù)模塊化了,這樣非常有利于我們程序的維護(hù),不管要寫一個(gè)什么樣的功能,只要調(diào)用相應(yīng)的函數(shù)就可以了,大家注意學(xué)習(xí)這個(gè)技巧。
其次,我們使用液晶的習(xí)慣,也是喜歡用數(shù)學(xué)上的X,Y坐標(biāo)來(lái)進(jìn)行定位,而和數(shù)學(xué)不同的是,液晶的左上角的坐標(biāo)是x=0y=0,往右邊是x+偏移,下邊是y+偏移。
第三,第一次接觸多個(gè)參數(shù)傳遞的函數(shù),稍微注意熟悉一下。
第四,讀寫數(shù)據(jù)和指令程序,每次都必須進(jìn)行“忙”判斷。
第五,領(lǐng)略一下指針在這個(gè)地方的巧妙用法,你可以嘗試不用指針改寫程序試試,感受一下指針的優(yōu)勢(shì)。
12.5 作業(yè)
1、把本節(jié)課的指針相關(guān)內(nèi)容,反復(fù)學(xué)習(xí)35遍,徹底弄懂了指針是怎么回事,不知道如何用沒關(guān)系,即使是背,也得把這部分背下來(lái),等到后邊我們用的時(shí)候,就可以實(shí)現(xiàn)頓悟。學(xué)會(huì)指針,就是突破了C語(yǔ)言的一道重要的堡壘。
2、把1602所有的指令功能都應(yīng)用一遍,能夠靈活使用1602液晶顯示任意字符串。
[size=12.0000pt]3、嘗試通過(guò)串口調(diào)試助手下發(fā)字符在1602液晶上顯示出來(lái)。
作者: aaaaabbbbb    時(shí)間: 2014-12-16 18:58
挺好挺好,學(xué)會(huì)了不少東西
作者: liuxingg    時(shí)間: 2015-3-24 21:18
講的很詳細(xì)透徹
作者: yesonjob    時(shí)間: 2015-11-19 08:11
真的寫得很好,很適合初學(xué)者。
作者: 俠客行者    時(shí)間: 2017-8-27 14:30
仔細(xì)看了這篇教程 我也在開發(fā)板上試了 是存在問題的  就是 字符的 重復(fù)顯示 我改了一下  現(xiàn)在 編譯通過(guò)且成功 特地分享
/*問題 1602重復(fù)顯示*/
#include<reg52.h>
#define LCD1602_DB  P0                //1602 IO口
sbit LCD1602_RD=P1^2;        // 數(shù)據(jù)命令選擇端  H DATA  L CMD
sbit LCD1602_RW=P1^3;   //  讀寫端    H  READ  L WRITE                                                                                                                     
sbit LCD1602_E=P1^4;        //   使能
void readbusy();      //判忙 1忙 0空
void write_data(unsigned char dat);
void write_cmd(unsigned char cmd);
void LCD1602_INIT();
unsigned char code word1[]={"cjb study 1602"};
void LCDShowstr(unsigned char addr_start,unsigned char *p);   //x  y表示坐標(biāo) p表示指針變量 即數(shù)組word的首地址
void main()
{

        LCD1602_INIT();
        LCDShowstr(0x80,word1);
        while(1){}
}
void LCD1602_INIT()                  //1602INIT
{
        write_cmd(0x38);           //顯示模式設(shè)置  1602 固定命令
        write_cmd(0x0c);                //開顯示器 關(guān)閉光標(biāo)   0000 1DCB   D=1開顯示 C=1顯示光標(biāo) B=1 光標(biāo)閃爍
        write_cmd(0x06);                 //文字不動(dòng)  地址自動(dòng)加1   0000 01NS N=1 寫入一個(gè)字符后 地址自動(dòng)加1 S=0 寫入一個(gè)字符 整屏不移動(dòng)
        write_cmd(0x01);                //清屏
}
/*1602每次的寫入都要確保其處于不忙的狀態(tài)  狀態(tài)字的最高位D7代表其是否忙碌1 禁止寫入 0 應(yīng)許寫入*/
void readbusy()
{
        unsigned char sta;
        LCD1602_DB=0XFF;    //P0置位,判斷D7是不是1602拉低的
        LCD1602_RD=0;
        LCD1602_RW=1;    //讀D7狀態(tài)           RD  RW 不能寫錯(cuò)
        do{
        LCD1602_E=1;      //使能
        sta=LCD1602_DB;
        }while(sta&0x80);                //等到sta 首位為0 時(shí) 退出循環(huán)  此時(shí)1602處于空閑狀態(tài)
        LCD1602_E=0;                  //讀完了關(guān)閉使能  液晶輸出數(shù)據(jù)對(duì)總線的干擾
}
void write_data(unsigned char dat)                 //寫數(shù)據(jù)
{
    readbusy();
        LCD1602_RD=1;                           //參考時(shí)序圖
        LCD1602_RW=0;
        LCD1602_DB=dat;
        LCD1602_E=1;
        LCD1602_E=0;
}
void write_cmd(unsigned char cmd)        //寫命令
{
    readbusy();
        LCD1602_RD=0;                                   //參考時(shí)序圖
        LCD1602_RW=0;
    LCD1602_DB=cmd;
        LCD1602_E=1;
        LCD1602_E=0;
}
void LCDShowstr(unsigned char addr_start,unsigned char *p)
{
        write_cmd(addr_start);
        while(*p!='\0')
        {
                write_data(*p++);
        }
}
至于問題出在哪里 我認(rèn)為 是坐標(biāo)哪里有問題  希望大神求解
作者: yyfpy    時(shí)間: 2017-10-12 16:32
耐心學(xué)習(xí)!謝謝樓主
作者: wohhhde    時(shí)間: 2017-11-1 10:36
絕世好貼!
作者: 15677446238    時(shí)間: 2018-1-4 12:09
指針的使用最為靈活也最麻煩,這降解真心好,理解好的畫用的非常好
作者: 332405177    時(shí)間: 2018-1-16 11:59
最近在學(xué)指針,非常棒的文章。因?yàn)樽x的比較仔細(xì),所以看到流水燈例程時(shí)發(fā)現(xiàn)個(gè)小錯(cuò)誤,應(yīng)該是P1=~buf
作者: 五行缺錢    時(shí)間: 2018-3-20 16:23
液晶屏“讀數(shù)據(jù)”操作,應(yīng)該是R/W = H吧?
作者: keneng    時(shí)間: 2018-7-12 13:21
學(xué)習(xí)內(nèi)容越來(lái)越難了,努力中
作者: qq23230233    時(shí)間: 2018-8-2 02:23
為什么不直接出個(gè)新版講解單片機(jī)視頻呢,主頁(yè)推薦的郭天祥的視頻第一模糊,第二視頻播放速度和聲音速度延遲8S左右,雖然我可以通過(guò)下載視頻通過(guò)軟件吧延遲擬補(bǔ)回來(lái)但是真的太麻煩了,而且郭視頻也需要新一代更新更新呀




歡迎光臨 (http://www.izizhuan.cn/bbs/) Powered by Discuz! X3.1
作爱视频免费观看视频在线播放激情网| 国内揄拍国内精品少妇国语| 欧美乱大交xxxxx| 久草热久草热线频97精品| 午夜精品久久久内射近拍高清 | 久久精品美女视频网站| 麻豆一区区三区四区产品精品蜜桃| 少妇性l交大片| 国内免费精品视频| 在线播放黄网| 97超碰在线公开在线看免费| 亚洲a级精品| 国产馆精品极品| 欧美日韩第一区日日骚| 国产精品九九久久久久久久| 人体内射精一区二区三区| 日日碰狠狠添天天爽| 另类综合图区| av在线女优影院| 精品产国自在拍| 日本不卡高清视频| 在线影视一区二区三区| 国产亚洲人成a一在线v站| 国产一区二区三区色淫影院 | 久久成人综合视频| 影音先锋在线亚洲| 成年人午夜剧场| 美女在线不卡| 91国内外精品自在线播放| 日本视频中文字幕一区二区三区| 精品视频999| 亚洲free性xxxx护士白浆| 久久久国产精品久久久| 婷婷丁香花五月天| 黄色网在线免费观看| 婷婷综合社区| 亚洲高清视频的网址| 国产成人中文字幕| 中文字幕永久免费| 影音先锋日韩av| 电影在线观看一区| 国产在线视频一区二区三区| 日韩不卡在线观看| 妞干网这里只有精品| 丁香六月婷婷综合| 天堂在线一二区| 五月开心六月丁香综合色啪| 精品久久香蕉国产线看观看gif| 国产精品入口免费视| 中文在线观看免费视频| 国产浴室偷窥在线播放| 日韩电影av| 国产美女主播视频一区| 亚洲人成网站777色婷婷| 穿情趣内衣被c到高潮视频| 久久久夜色精品| 天天摸天天操天天干| 91精品啪在线观看国产爱臀 | 欧美日韩在线精品一区二区三区激情综 | 天天干天天插天天操| 高清毛片在线看| 91精品久久久久久综合五月天| 国产亚洲成年网址在线观看| 久久精品国产精品亚洲| 少妇一晚三次一区二区三区| 日韩欧美大片在线观看| 国产主播色在线| 麻豆精品av| 国产农村妇女毛片精品久久麻豆| www.久久久久| 久久久久久久中文| 在线观看中文字幕码| 污视频在线看操| 日韩在线精品| 亚洲成av人片在www色猫咪| 欧美在线国产精品| 国产精品免费观看久久| 色偷偷在线观看| 136福利第一导航国产在线| 日日夜夜一区二区| 精品国产一二三| 色噜噜狠狠一区二区三区| 久草网视频在线观看| 九色视频网站| 精品国产视频| 亚洲18色成人| 91福利视频导航| 国产午夜福利一区| jizzjizz16| 日韩av影院| 国产日韩高清在线| 国产精品香蕉国产| 亚洲精品午夜视频| a天堂在线观看| 日韩理论电影中文字幕| 亚洲精品欧美二区三区中文字幕| 欧美又大又粗又长| 影音先锋人妻啪啪av资源网站| 精品福利影院| 粉嫩一区二区三区在线观看| 久久久久久久久久久久久女国产乱| 欧美激情在线观看| aaaaa黄色片| 羞羞影视羞羞在线| 女人抽搐喷水高潮国产精品| 国产精品美女久久久久久久| 成人黄色大片在线免费观看| 9.1片黄在线观看| 成人免费淫片| 亚洲乱码精品| 日韩网站在线看片你懂的| 天堂av一区二区| 亚洲日本韩国在线| 国产日本在线| 裸体一区二区| 一区二区中文字幕| 国产又大又黄又粗又爽| 欧美video巨大粗暴18| 日韩精品一区二区三区中文 | 国产精品福利网| 成年人的黄色片| 成人狠狠色综合| 久久精品免费一区二区三区| 91精选在线观看| 欧美另类videosbestsex日本| 一二三区中文字幕| 成人动漫一区| 国产清纯在线一区二区www| 国产成人精品视频在线| 91狠狠综合久久久| 欧美一区二区少妇| 狠狠色狠狠色合久久伊人| 美女av一区二区| 成人免费看片载| av三级影院| 日韩天天综合| 色av吧综合网| youjizz.com日本| 中国麻豆视频| 奇米888四色在线精品| 欧美成人性生活| 中出视频在线观看| 一级毛片免费看| 日韩精品一二三| 欧美激情国内偷拍| 性猛交ⅹxxx富婆video| 三级国产三级在线| 久久精品国产成人一区二区三区| 久久这里有精品视频| 欧美 变态 另类 人妖| 欧美18一12sex性处hd| 日本成人在线不卡视频| 69av在线播放| 538精品在线视频| 日本激情在线观看| 91免费视频大全| 91精品国产91久久久久青草| 潘金莲一级淫片aaaaaa播放| 中文不卡1区2区3区| 一区二区三区在线观看动漫| 欧美日韩精品久久| 亚洲精品久久久久avwww潮水| 成人在线视频区| 亚洲va韩国va欧美va| koreanbj精品视频一区| 桃花视频大全不卡免费观看网站 | av日韩在线免费| 丝瓜av网站精品一区二区 | 免费观看羞羞视频网站| 国内精品久久久久影院薰衣草| 97视频免费看| 国产一级片久久| 国产精品蜜芽在线观看| 一区二区三区四区不卡视频 | 综合久久久久久| 蜜桃av色综合| 老熟妇高潮一区二区高清视频| 国产+成+人+亚洲欧洲在线| 欧美一区二区视频在线观看2022| 亚洲 欧美 另类人妖| 俄罗斯xxxx性全过程| 青椒成人免费视频| 国产精品久久久久久久app| 日本视频免费在线| 69堂免费精品视频在线播放| 午夜精品福利视频网站| 欧美啪啪免费视频| 天天干天天草| 精品一区二区三区香蕉蜜桃 | 亚洲欧洲另类国产综合| 亚洲一区二区精品在线| 欧美激情性xxxxx| 午夜亚洲福利| 性欧美亚洲xxxx乳在线观看| 成人免费看片98欧美| 制服诱惑亚洲| 精品久久久久久亚洲国产300| 黄色在线视频网| 在线免费视频福利| 久久九九久久九九| 亚洲一区三区视频在线观看| 国产精品外围在线观看| 久久高清一区| 国产精品视频26uuu| 国产精品国产三级国产aⅴ| 西野翔中文久久精品国产| 亚洲欧美精品一区| 娇妻被老王脔到高潮失禁视频| 精品自拍一区| 午夜视频一区在线观看| 亚洲精品一二三四五区| 四虎在线视频| 亚洲欧洲精品一区二区精品久久久| 正在播放国产精品| h视频在线观看免费网站| 国产成人亚洲综合a∨猫咪| 久久一区二区精品| 69堂视频在线观看国产| 免费国产亚洲视频| 高清不卡一区二区三区| 国产一二三区精品| 亚洲一二av| 中文字幕九色91在线| 久久久精品视频免费观看| 日日狠狠久久| 日韩精品视频在线免费观看| 麻豆传媒在线看| 八戒八戒神马在线电影| 欧美图区在线视频| 国产高清自拍视频| 欧美羞羞视频| 亚洲电影免费观看高清完整版在线| 国产调教在线观看| 黄色软件在线| 欧洲一区二区三区在线| 中国一级特黄录像播放| 欧美男人天堂| 日韩免费性生活视频播放| 久久日免费视频| 欧美日韩伦理一区二区| 亚洲欧美日韩区| 91香蕉在线视频| 伊人久久大香线蕉| 久久久久久久亚洲精品| 国产精品一区二区黑人巨大| 综合国产在线| 成人激情视频在线| 美女免费黄视频网站| 国产精品美女久久久久久| 在线视频观看一区二区| 自拍偷拍亚洲视频| 亚洲剧情一区二区| 7799精品视频天天看| 国产调教精品| 国产精品久久久久久久午夜 | 久久99伊人| 精品久久久久久一区| 美女视频黄的免费| 国产无一区二区| 国产精品69页| 国产一二三区在线观看| 欧美夫妻性生活| 蜜桃传媒一区二区亚洲| 亚洲动漫精品| 日本乱人伦a精品| 日本一区精品久久久久影院| 国产高清视频一区| 国产 日韩 亚洲 欧美| 大地资源中文在线观看免费版| 欧美亚洲日本一区| xxxxxx国产| 国产精品女主播一区二区三区 | 日本一卡二卡四卡精品| 在线观看网站黄不卡| 精品无码一区二区三区电影桃花| 沈樵精品国产成av片| 国产欧美一区二区三区四区| 国产成人香蕉在线视频网站| 91在线观看免费视频| 欧美亚洲日本在线观看| 成人免费观看在线观看| 亚洲毛茸茸少妇高潮呻吟| 国产91视频在线| 麻豆91精品视频| 91亚洲精品国产| 日本视频在线观看| 亚洲第一色在线| 波多野结衣毛片| 国产一区二区三区的电影 | 无码专区aaaaaa免费视频| 老司机99精品99| 亚洲黄色有码视频| 性欧美videos另类hd| 久久成人久久爱| 成熟丰满熟妇高潮xxxxx视频| 日本孕妇大胆孕交无码| 伊人激情综合网| 亚洲精品久久久蜜桃动漫 | 无码人妻一区二区三区一| 国产精品久久久久77777丨| 久久99精品久久久久久琪琪| 亚洲 欧美 自拍偷拍| 丁香激情综合国产| 激情网站五月天| 日韩精品av| 不卡中文字幕av| 天堂中文www在线| 91网页版在线| 女人扒开双腿让男人捅| 国产亚洲精aa在线看| 国产精品美女999| japanesexxxxfree在线观看| 亚洲va国产天堂va久久en| 国产91丝袜美女在线播放| 欧洲杯什么时候开赛| 国产女主播一区二区| 一级二级三级在线观看| 精品美女一区二区| a天堂在线观看视频| 成人性生交大片免费看视频在线 | 亚欧洲乱码视频| 日韩影视高清在线观看| 日韩免费中文字幕| eeuss影院www在线观看手机| 欧美午夜精品久久久久久浪潮| 午夜激情福利电影| 欧美精品1区| 欧美日韩dvd| 人狥杂交一区欧美二区| 97人洗澡人人免费公开视频碰碰碰| 青青草视频导航| 欧洲一区二区av| 亚洲永久精品视频| 成人天堂资源www在线| 亚洲精品久久一区二区三区777| 亚洲桃色综合影院| 日韩电影大全在线观看| 免费网站免费进入在线| 日韩欧美一级在线播放| 亚洲男人的天堂成人| 国产精品日韩成人| 亚洲人做受高潮| 国产情侣一区| 精品www久久久久奶水| 欧美视频三区| 国产精品一 二 三| 欧美成年黄网站色视频| 九九九久久久久久| 天堂男人av| 日韩亚洲欧美中文高清在线| 青青青视频在线| 5566中文字幕一区二区电影 | 中文字幕欧美日韩一区二区三区 | 久久久久久欧美精品色一二三四| 日本不卡三区| 夜夜嗨av一区二区三区网页| 五月婷婷一区二区| 国产传媒日韩欧美成人| 在线精品一区二区三区| 红桃视频亚洲| 青青草精品视频在线观看| 四虎884aa成人精品最新| 亚洲人成人77777线观看| 国产亚av手机在线观看| 99精品国产高清一区二区| 成人三级网址| 欧美专区第一页| 欧美日韩在线中文字幕| 久久这里只有精品99| 人人澡人人爽| 亚洲男人第一av网站| av电影在线免费观看| 国产亚洲免费的视频看| a视频免费看| 欧美三级免费观看| 天堂中文在线看| 日本乱人伦aⅴ精品| 亚洲av片一区二区三区| 欧美亚洲国产一卡| 久热中文字幕精品视频在线| 日韩欧美一卡二卡| 九色蝌蚪视频在线| 亚洲天堂影视av| 免费h片在线| 精品久久久av| 日韩一区二区三区中文字幕| 精品自在线视频| 在线观看一级片| 欧美激情在线播放| eeuss在线观看| 欧美在线观看视频| 91这里只有精品| 国产精品wwwwww| 老司机99精品99| 成人黄色av网| 日韩欧美精品电影| 亚洲v国产v| 免费看一区二区三区| 中文字幕第一页亚洲| 高清日韩中文字幕|