第13章-1602液晶與串口的應(yīng)用實例
代碼分模塊組織,多個文件,反而不好往出粘貼了
keil那個寫代碼的,真垃圾,破玩意,一點也不智能






//課后習(xí)題,問題像右移動
#include <reg52.h>
#define LCD1602_DB?? P0
sbit LCD1602_RS =? P1^0;
sbit LCD1602_RW =? P1^1;
sbit LCD1602_E? =? P1^5;
/* 等待液晶準(zhǔn)備好 */
void LcdWaitReady()
{
?? ?unsigned char sta;
?? ?LCD1602_DB = 0xFF;
?? ?LCD1602_RS = 0;
?? ?LCD1602_RW = 1;
?? ?do {
?? ??? ?LCD1602_E = 1;
?? ??? ?sta = LCD1602_DB; //讀取狀態(tài)字
?? ??? ?LCD1602_E = 0;
?? ?} while (sta & 0x80); //bit7等于1表示液晶正忙,重復(fù)檢測直到其等于0為止
}
/* 向LCD1602液晶寫入一字節(jié)命令,cmd-待寫入命令值 */
void LcdWriteCmd(unsigned char cmd)
{
?? ?LcdWaitReady();
?? ?LCD1602_RS = 0;
?? ?LCD1602_RW = 0;
?? ?LCD1602_DB = cmd;
?? ?LCD1602_E? = 1;
?? ?LCD1602_E? = 0;
}
/* 向LCD1602液晶寫入一字節(jié)數(shù)據(jù),dat-待寫入數(shù)據(jù)值 */
void LcdWriteDat(unsigned char dat)
{
?? ?LcdWaitReady();
?? ?LCD1602_RS = 1;
?? ?LCD1602_RW = 0;
?? ?LCD1602_DB = dat;
?? ?LCD1602_E? = 1;
?? ?LCD1602_E? = 0;
}
void LcdSetCursorHL(unsigned char hang, unsigned char lie)
{
?? ?unsigned char addr;
?? ?if (hang == 0)? //由輸入的屏幕坐標(biāo)計算顯示RAM的地址
?? ??? ?addr = 0x00 + lie;? //第一行字符地址從0x00起始
?? ?else
?? ??? ?addr = 0x40 + lie;? //第二行字符地址從0x40起始
?? ?LcdWriteCmd(addr | 0x80);? //設(shè)置RAM地址
}
void LcdShowStrHL(unsigned char hang, unsigned char lie, unsigned char *str)
{
?? ?LcdSetCursorHL(hang, lie);?? //設(shè)置起始地址
?? ?while (*str != '\0')? //連續(xù)寫入字符串?dāng)?shù)據(jù),直到檢測到結(jié)束符
?? ?{
?? ??? ?LcdWriteDat(*str++);
?? ?}
}
/* 初始化1602液晶 */
void InitLcd1602()
{
?? ?LcdWriteCmd(0x38);? //16*2顯示,5*7點陣,8位數(shù)據(jù)接口
?? ?LcdWriteCmd(0x0C);? //顯示器開,光標(biāo)關(guān)閉
?? ?LcdWriteCmd(0x06);? //文字不動,地址自動+1
?? ?LcdWriteCmd(0x01);? //清屏
}
unsigned char TH0RL,TL0RL;
bit flag500ms=0;
void main()
{
?? ?unsigned char? i,cnt,strlen;
?? ?char str1[]="www.bilibili.com";;
?? ?char str2[]="www.qdkingst.com";
?? ?EA = 1;?????????? //開總中斷
?? ?ConfigTimer0(1);? //配置T0定時1ms
?? ?InitLcd1602();??? //初始化液晶
?? ?//顯示字符串
?? ?LcdShowStrHL(0,0,str1);
?? ?LcdShowStrHL(1,0,str2);
?? ?//16個空白,字符串,剩下的空白
?? ?strlen=sizeof(str1);
?? ?LcdWriteCmd(0x02);
?? ?for(i=0;i<strlen;i++)
?? ?{
?? ??? ?LcdWriteCmd(0x18);//提前移動
?? ?}
?? ?flag500ms=0;
?? ?cnt=0;
?? ?while (1)
?? ?{
?? ??? ?if(flag500ms)
?? ??? ?{
?? ??? ??? ?flag500ms=0;
?? ??? ??? ?LcdWriteCmd(0x1C);//像右移動
?? ??? ??? ?cnt++;
?? ??? ??? ?if(cnt==strlen*2)
?? ??? ??? ?{
?? ??? ??? ??? ?cnt=0;
?? ??? ??? ??? ?LcdWriteCmd(0x02);
?? ??? ??? ??? ?for(i=0;i<strlen;i++)
?? ??? ??? ??? ?{
?? ??? ??? ??? ??? ?LcdWriteCmd(0x18);//提前移動
?? ??? ??? ??? ?}
?? ??? ??? ?}
?? ??? ?}
?? ?}
}
/* 配置并啟動T0,ms-T0定時時間 */
void ConfigTimer0(unsigned int ms)
{
?? ?unsigned int tmp= 65536 - 11059200/12*ms/1000;
?? ?TMOD =(TMOD & 0xF0)|0x01;
?? ?TH0=TH0RL = (unsigned char)(tmp>>8);? //定時器重載值拆分為高低字節(jié)
?? ?TL0=TL0RL = (unsigned char)tmp;
?? ?
?? ?ET0=TR0 = 1;??????? //啟動T0
}
/* T0中斷服務(wù)函數(shù),執(zhí)行按鍵掃描 */
void Timer0_ISR() interrupt 1
{
?? ?static unsigned int cnt=0;
?? ?
?? ?TH0 = TH0RL;? //重新加載重載值
?? ?TL0 = TL0RL;
?? ?
?? ?if(++cnt>=500)
?? ?{
?? ??? ?flag500ms=1;
?? ??? ?cnt=0;
?? ?}
}
//課后第5題,老師的看不太懂,用自己的思路實現(xiàn)
//直接在老師的代碼上修改,懶得寫了,組織的亂七八糟
#include <stdio.h>
#include <string.h>
//Lcd1602部分
#include <reg52.h>
#define LCD1602_DB?? P0
sbit LCD1602_RS =? P1^0;
sbit LCD1602_RW =? P1^1;
sbit LCD1602_E? =? P1^5;
/* 等待液晶準(zhǔn)備好 */
void LcdWaitReady()
{
?? ?unsigned char sta;
?? ?LCD1602_DB = 0xFF;
?? ?LCD1602_RS = 0;
?? ?LCD1602_RW = 1;
?? ?do
?? ?{
?? ??? ?LCD1602_E = 1;
?? ??? ?sta = LCD1602_DB; //讀取狀態(tài)字
?? ??? ?LCD1602_E = 0;
?? ?}
?? ?while (sta & 0x80);?? //bit7等于1表示液晶正忙,重復(fù)檢測直到其等于0為止
}
/* 向LCD1602液晶寫入一字節(jié)命令,cmd-待寫入命令值 */
void LcdWriteCmd(unsigned char cmd)
{
?? ?LcdWaitReady();
?? ?LCD1602_RS = 0;
?? ?LCD1602_RW = 0;
?? ?LCD1602_DB = cmd;
?? ?LCD1602_E? = 1;
?? ?LCD1602_E? = 0;
}
/* 向LCD1602液晶寫入一字節(jié)數(shù)據(jù),dat-待寫入數(shù)據(jù)值 */
void LcdWriteDat(unsigned char dat)
{
?? ?LcdWaitReady();
?? ?LCD1602_RS = 1;
?? ?LCD1602_RW = 0;
?? ?LCD1602_DB = dat;
?? ?LCD1602_E? = 1;
?? ?LCD1602_E? = 0;
}
void LcdSetCursorHL(unsigned char hang, unsigned char lie)
{
?? ?unsigned char addr;
?? ?if (hang == 0)? //由輸入的屏幕坐標(biāo)計算顯示RAM的地址
?? ??? ?addr = 0x00 + lie;? //第一行字符地址從0x00起始
?? ?else
?? ??? ?addr = 0x40 + lie;? //第二行字符地址從0x40起始
?? ?LcdWriteCmd(addr | 0x80);? //設(shè)置RAM地址
}
void LcdShowStrHL(unsigned char hang, unsigned char lie, unsigned char *str)
{
?? ?LcdSetCursorHL(hang, lie);?? //設(shè)置起始地址
?? ?while (*str != '\0')? //連續(xù)寫入字符串?dāng)?shù)據(jù),直到檢測到結(jié)束符
?? ?{
?? ??? ?LcdWriteDat(*str++);
?? ?}
}
/* 初始化1602液晶 */
void InitLcd1602()
{
?? ?LcdWriteCmd(0x38);? //16*2顯示,5*7點陣,8位數(shù)據(jù)接口
?? ?LcdWriteCmd(0x0C);? //顯示器開,光標(biāo)關(guān)閉
?? ?LcdWriteCmd(0x06);? //文字不動,地址自動+1
?? ?LcdWriteCmd(0x01);? //清屏
}
//Uart部分
unsigned char UartRecBuff[32];//接收緩沖區(qū)
unsigned char UartRecCnt=0;//接收的字節(jié)數(shù)
bit flagUartRecOK=0;//是否一幀結(jié)束---結(jié)束就可以讀取數(shù)據(jù)了
unsigned char UartMsCnt=0;//判斷一幀結(jié)束的計數(shù)器
bit flagUartMsCntEn=0;//是否開啟判斷一幀結(jié)束的計數(shù)器
bit flagSentBusy=0;//是否uart正在發(fā)送
sbit BUZZ = P1^6;? //蜂鳴器控制引腳
bit flagBuzzOn = 0;?? //蜂鳴器啟動標(biāo)志
/* 串口數(shù)據(jù)讀取函數(shù),buf-接收指針,len-指定的讀取長度,返回值-實際讀到的長度 */
unsigned char UartRead(unsigned char *buf, unsigned char len)
{
?? ?unsigned char i;
?? ?unsigned char retCnt= (UartRecCnt<=len)?UartRecCnt:len;//取小值
?? ?for(i=0; i<retCnt; i++)
?? ?{
?? ??? ?buf[i]=UartRecBuff[i];
?? ??? ?UartRecCnt--;
?? ?}
?? ?if(UartRecCnt==0)
?? ?{
?? ??? ?flagUartRecOK=0;//數(shù)據(jù)取完了,
?? ?}
?? ?return retCnt;
}
// 串口數(shù)據(jù)寫入,阻塞式發(fā)送數(shù)據(jù)
void UartSent(unsigned char *buf, unsigned char len)
{
?? ?while (len--)? //循環(huán)發(fā)送所有字節(jié)
?? ?{
?? ??? ?flagSentBusy = 1;????? //清零發(fā)送標(biāo)志
?? ??? ?SBUF = *buf++;??? //發(fā)送一個字節(jié)數(shù)據(jù)
?? ??? ?while (flagSentBusy); //等待該字節(jié)發(fā)送完成
?? ?}
}
/* 串口配置函數(shù),baud-通信波特率 */
void ConfigUART(unsigned int baud)
{
?? ?SCON? = 0x50;? //配置串口為模式1
?? ?ES=1;
?? ?TMOD =(TMOD&0x0F)| 0x20;? //配置T1為模式2
?? ?TH1 =TL1= 256 - (11059200/12/32)/baud;? //計算T1重載值
?? ?ET1 = 0;?????? //禁止T1中斷
?? ?TR1 = 1;?????? //啟動T1
}
void UartAction(unsigned char * buf,unsigned char len)
{
?? ?unsigned char i;
?? ?unsigned char * cmd[]= {"buzz on","buzz off","showstr "};
?? ?for(i=0; i<3; i++)
?? ?{
?? ??? ?unsigned char cmdLen=strlen(cmd[i]);
?? ??? ?if(memcmp(buf,cmd[i],cmdLen)==0)
?? ??? ?{
?? ??? ??? ?if(i==0)
?? ??? ??? ?{
?? ??? ??? ??? ?flagBuzzOn=1;
?? ??? ??? ?}
?? ??? ??? ?else if(i==1)
?? ??? ??? ?{
?? ??? ??? ??? ?flagBuzzOn=0;
?? ??? ??? ?}
?? ??? ??? ?else if(i==2)
?? ??? ??? ?{
?? ??? ??? ??? ?unsigned char pdata tmp[32]= {0};
?? ??? ??? ??? ?memcpy(tmp,buf+cmdLen,len-cmdLen);
?? ??? ??? ??? ?LcdWriteCmd(0x01);
?? ??? ??? ??? ?LcdShowStrHL(0,0,tmp);
?? ??? ??? ?}
?? ??? ??? ?UartSent(buf,len);
?? ??? ??? ?return;
?? ??? ?}
?? ?}
?? ?UartSent("Bad Common: ",strlen("Bad Common: "));
?? ?UartSent(buf,len);
}
/* 串口驅(qū)動函數(shù),監(jiān)測數(shù)據(jù)幀的接收,調(diào)度功能函數(shù),需在主循環(huán)中調(diào)用 */
void UartDriver()
{
?? ?unsigned char pdata buf[32];
?? ?if (flagUartRecOK) //有命令到達(dá)時,讀取處理該命令
?? ?{
?? ??? ?unsigned char len = UartRead(buf, sizeof(buf));? //將接收到的命令讀取到緩沖區(qū)中
?? ??? ?UartAction(buf, len);? //傳遞數(shù)據(jù)幀,調(diào)用動作執(zhí)行函數(shù)
?? ?}
}
/* 串口中斷服務(wù)函數(shù) */
void UART_ISR() interrupt 4
{
?? ?if (RI)? //接收到新字節(jié)
?? ?{
?? ??? ?RI = 0;? //清零接收中斷標(biāo)志位
?? ??? ?if (UartRecCnt < sizeof(UartRecBuff)) //接收緩沖區(qū)尚未用完時,
?? ??? ?{
?? ??? ??? ?//保存接收字節(jié),從0開始計數(shù)
?? ??? ??? ?UartRecBuff[UartRecCnt++] = SBUF;
?? ??? ??? ?UartMsCnt=0;//計數(shù)清零
?? ??? ??? ?flagUartMsCntEn=1;//開啟計數(shù)
?? ??? ?}
?? ??? ?else //數(shù)據(jù)太多,接收滿了
?? ??? ?{
?? ??? ??? ?flagUartMsCntEn=0;//不用計數(shù)了
?? ??? ??? ?flagUartRecOK=1;//直接結(jié)束接收
?? ??? ?}
?? ?}
?? ?if (TI)? //字節(jié)發(fā)送完畢
?? ?{
?? ??? ?TI = 0;?? //清零發(fā)送中斷標(biāo)志位
?? ??? ?flagSentBusy = 0;? //設(shè)置字節(jié)發(fā)送完成標(biāo)志
?? ?}
}
void int_1ms_uart_task(void)
{
?? ?if(flagUartMsCntEn)//是否需要計數(shù)
?? ?{
?? ??? ?if(++UartMsCnt>=50)//50ms
?? ??? ?{
?? ??? ??? ?flagUartMsCntEn=0;//不用計數(shù)了
?? ??? ??? ?flagUartRecOK=1;//直接結(jié)束接收
?? ??? ?}
?? ?}
}
unsigned char TH0RL,TL0RL;
/* 配置并啟動T0,ms-T0定時時間 */
void ConfigTimer0(unsigned int ms)
{
?? ?unsigned int tmp= 65536 - 11059200/12*ms/1000;
?? ?TMOD =(TMOD & 0xF0)|0x01;
?? ?TH0=TH0RL = (unsigned char)(tmp>>8);? //定時器重載值拆分為高低字節(jié)
?? ?TL0=TL0RL = (unsigned char)tmp;
?? ?ET0=TR0 = 1;??????? //啟動T0
}
/* T0中斷服務(wù)函數(shù),執(zhí)行按鍵掃描 */
void Timer0_ISR() interrupt 1
{
?? ?TH0 = TH0RL;? //重新加載重載值
?? ?TL0 = TL0RL;
?? ?if(flagBuzzOn)
?? ??? ?BUZZ=!BUZZ;
?? ?else
?? ??? ?BUZZ=1;
?? ?int_1ms_uart_task();
}
void main ()
{
?? ?EA = 1;??????????? //開總中斷
?? ?ConfigTimer0(1);?? //配置T0定時1ms
?? ?ConfigUART(9600);? //配置波特率為9600
?? ?InitLcd1602();???? //初始化液晶
?? ?while (1)
?? ?{
?? ??? ?UartDriver();? //調(diào)用串口驅(qū)動
?? ?}
}