第13章-1602液晶与串口的应用实例
代码分模块组织,多个文件,反而不好往出粘贴了
keil那个写代码的,真垃圾,破玩意,一点也不智能






//课后习题,问题像右移动
#include <reg52.h>
#define LCD1602_DB P0
sbit LCD1602_RS = P1^0;
sbit LCD1602_RW = P1^1;
sbit LCD1602_E = P1^5;
/* 等待液晶准备好 */
void LcdWaitReady()
{
unsigned char sta;
LCD1602_DB = 0xFF;
LCD1602_RS = 0;
LCD1602_RW = 1;
do {
LCD1602_E = 1;
sta = LCD1602_DB; //读取状态字
LCD1602_E = 0;
} while (sta & 0x80); //bit7等于1表示液晶正忙,重复检测直到其等于0为止
}
/* 向LCD1602液晶写入一字节命令,cmd-待写入命令值 */
void LcdWriteCmd(unsigned char cmd)
{
LcdWaitReady();
LCD1602_RS = 0;
LCD1602_RW = 0;
LCD1602_DB = cmd;
LCD1602_E = 1;
LCD1602_E = 0;
}
/* 向LCD1602液晶写入一字节数据,dat-待写入数据值 */
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) //由输入的屏幕坐标计算显示RAM的地址
addr = 0x00 + lie; //第一行字符地址从0x00起始
else
addr = 0x40 + lie; //第二行字符地址从0x40起始
LcdWriteCmd(addr | 0x80); //设置RAM地址
}
void LcdShowStrHL(unsigned char hang, unsigned char lie, unsigned char *str)
{
LcdSetCursorHL(hang, lie); //设置起始地址
while (*str != '\0') //连续写入字符串数据,直到检测到结束符
{
LcdWriteDat(*str++);
}
}
/* 初始化1602液晶 */
void InitLcd1602()
{
LcdWriteCmd(0x38); //16*2显示,5*7点阵,8位数据接口
LcdWriteCmd(0x0C); //显示器开,光标关闭
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); //定时器重载值拆分为高低字节
TL0=TL0RL = (unsigned char)tmp;
ET0=TR0 = 1; //启动T0
}
/* T0中断服务函数,执行按键扫描 */
void Timer0_ISR() interrupt 1
{
static unsigned int cnt=0;
TH0 = TH0RL; //重新加载重载值
TL0 = TL0RL;
if(++cnt>=500)
{
flag500ms=1;
cnt=0;
}
}
//课后第5题,老师的看不太懂,用自己的思路实现
//直接在老师的代码上修改,懒得写了,组织的乱七八糟
#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;
/* 等待液晶准备好 */
void LcdWaitReady()
{
unsigned char sta;
LCD1602_DB = 0xFF;
LCD1602_RS = 0;
LCD1602_RW = 1;
do
{
LCD1602_E = 1;
sta = LCD1602_DB; //读取状态字
LCD1602_E = 0;
}
while (sta & 0x80); //bit7等于1表示液晶正忙,重复检测直到其等于0为止
}
/* 向LCD1602液晶写入一字节命令,cmd-待写入命令值 */
void LcdWriteCmd(unsigned char cmd)
{
LcdWaitReady();
LCD1602_RS = 0;
LCD1602_RW = 0;
LCD1602_DB = cmd;
LCD1602_E = 1;
LCD1602_E = 0;
}
/* 向LCD1602液晶写入一字节数据,dat-待写入数据值 */
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) //由输入的屏幕坐标计算显示RAM的地址
addr = 0x00 + lie; //第一行字符地址从0x00起始
else
addr = 0x40 + lie; //第二行字符地址从0x40起始
LcdWriteCmd(addr | 0x80); //设置RAM地址
}
void LcdShowStrHL(unsigned char hang, unsigned char lie, unsigned char *str)
{
LcdSetCursorHL(hang, lie); //设置起始地址
while (*str != '\0') //连续写入字符串数据,直到检测到结束符
{
LcdWriteDat(*str++);
}
}
/* 初始化1602液晶 */
void InitLcd1602()
{
LcdWriteCmd(0x38); //16*2显示,5*7点阵,8位数据接口
LcdWriteCmd(0x0C); //显示器开,光标关闭
LcdWriteCmd(0x06); //文字不动,地址自动+1
LcdWriteCmd(0x01); //清屏
}
//Uart部分
unsigned char UartRecBuff[32];//接收缓冲区
unsigned char UartRecCnt=0;//接收的字节数
bit flagUartRecOK=0;//是否一帧结束---结束就可以读取数据了
unsigned char UartMsCnt=0;//判断一帧结束的计数器
bit flagUartMsCntEn=0;//是否开启判断一帧结束的计数器
bit flagSentBusy=0;//是否uart正在发送
sbit BUZZ = P1^6; //蜂鸣器控制引脚
bit flagBuzzOn = 0; //蜂鸣器启动标志
/* 串口数据读取函数,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;//数据取完了,
}
return retCnt;
}
// 串口数据写入,阻塞式发送数据
void UartSent(unsigned char *buf, unsigned char len)
{
while (len--) //循环发送所有字节
{
flagSentBusy = 1; //清零发送标志
SBUF = *buf++; //发送一个字节数据
while (flagSentBusy); //等待该字节发送完成
}
}
/* 串口配置函数,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);
}
/* 串口驱动函数,监测数据帧的接收,调度功能函数,需在主循环中调用 */
void UartDriver()
{
unsigned char pdata buf[32];
if (flagUartRecOK) //有命令到达时,读取处理该命令
{
unsigned char len = UartRead(buf, sizeof(buf)); //将接收到的命令读取到缓冲区中
UartAction(buf, len); //传递数据帧,调用动作执行函数
}
}
/* 串口中断服务函数 */
void UART_ISR() interrupt 4
{
if (RI) //接收到新字节
{
RI = 0; //清零接收中断标志位
if (UartRecCnt < sizeof(UartRecBuff)) //接收缓冲区尚未用完时,
{
//保存接收字节,从0开始计数
UartRecBuff[UartRecCnt++] = SBUF;
UartMsCnt=0;//计数清零
flagUartMsCntEn=1;//开启计数
}
else //数据太多,接收满了
{
flagUartMsCntEn=0;//不用计数了
flagUartRecOK=1;//直接结束接收
}
}
if (TI) //字节发送完毕
{
TI = 0; //清零发送中断标志位
flagSentBusy = 0; //设置字节发送完成标志
}
}
void int_1ms_uart_task(void)
{
if(flagUartMsCntEn)//是否需要计数
{
if(++UartMsCnt>=50)//50ms
{
flagUartMsCntEn=0;//不用计数了
flagUartRecOK=1;//直接结束接收
}
}
}
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); //定时器重载值拆分为高低字节
TL0=TL0RL = (unsigned char)tmp;
ET0=TR0 = 1; //启动T0
}
/* T0中断服务函数,执行按键扫描 */
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(); //调用串口驱动
}
}