第13章 1602液晶与串口应用实例 练习题

题目:

 第一题:

        关于通讯的逻辑,在这里面时序的概念十分重要。很明显,时序包括了两个概念。第一是时间的问题。在这里时间指的是通讯的时候,每位传输的时间,即时间为波特率分之一。其次为“序”,就是在通讯的时候信息传递的顺序。例如,在UART通讯的时候,先发送一位的低电平让单片机进行检测,之后再进行传送8位的数据,最后再发送一位的高电平。也就是说,传送一个字节(8位)的数据,实际上单片机收到了10位的信息。

        关于器件的时序图,本书中是以LCD1602来进行举例子的。通过阅读LCD的时序图,便于我们理解好关于对LCD的操作。其中,对于时序中的每个电平和电平变化的时间都有一些时间的要求,具体可以参考相应的LCD时序图。因为STC89C52单片机相对来说。它的时钟周期相对来说比较慢,一些时序图里面要求的时间,不需要进行软件的延迟,就可以满足要求。

第二题:

        将程序改写为LCD显示屏目文字向右移动的代码如下:

#include <reg52.h>

#define LCD1602_DB P0
sbit LCD1602_RS=P1^0;
sbit LCD1602_RW=P1^1;
sbit LCD1602_E=P1^5;

bit flag500ms=0;
unsigned char T0RH=0;
unsigned char T0RL=0;
unsigned char code str1[]="oidutS tsgniK";
unsigned char code str2[]="...evom s'teL";

void ConfigTimer0(unsigned int ms);
void InitLcd1602();
void LcdShowStr(unsigned char x,unsigned char y,unsigned char *str,unsigned char len);

void main()
{
	unsigned char i;
	signed char index=0;
	unsigned char pdata bufMove1[16+sizeof(str1)+16];
	unsigned char pdata bufMove2[16+sizeof(str2)+16];
	
	EA=1;
	ConfigTimer0(10);
	InitLcd1602();
	for(i=0;i<16;i++){
		bufMove1[i]=' ';
		bufMove2[i]=' ';
	}
	for(i=0;i<(sizeof(str1)-1);i++)
	{
	   bufMove1[16+i]=str1[i];
	   bufMove2[16+i]=str2[i];
	}
	for(i=(16+sizeof(str1)-1);i<sizeof(bufMove1);i++)
	{
		bufMove1[i]=' ';
		bufMove2[i]=' ';
	}
	index=16+sizeof(str1)-1;
	while(1){
	   if(flag500ms){
		   flag500ms=0;
		   LcdShowStr(0,0,bufMove1+index,16);
		   LcdShowStr(0,1,bufMove2+index,16);
		   index--;
		   if(index==-1) index=16+sizeof(str1)-1;
	   }
	}
}
void ConfigTimer0(unsigned int ms)
{
	 unsigned long tmp;

	 tmp=11059200/12;
	 tmp=(tmp*ms)/1000;
	 tmp=65536-tmp;
	 tmp+=12;
	 T0RH=(unsigned char)(tmp>>8);
	 T0RL=(unsigned char)tmp;
	 TMOD&=0XF0;
	 TMOD|=0X01;
	 TH0=T0RH;
	 TL0=T0RL;
	 ET0=1;
	 TR0=1;
}
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);
}
void LcdWriteCmd(unsigned char cmd)
{
	LcdWaitReady();
	LCD1602_RS=0;
	LCD1602_RW=0;
	LCD1602_DB=cmd;
	LCD1602_E=1;
	LCD1602_E=0;
}
void LcdWriteDat(unsigned char dat)
{
	LcdWaitReady();
	LCD1602_RS=1;
	LCD1602_RW=0;
	LCD1602_DB=dat;
	LCD1602_E=1;
	LCD1602_E=0;
}
void LcdSetCursor(unsigned char x,unsigned char y)
{
	  unsigned char addr;
	  if(y==0)
	  	   addr=0x00+x;
	  else
	       addr=0x40+x;
	  LcdWriteCmd(addr|0x80);
}
void LcdShowStr(unsigned char x,unsigned char y,unsigned char *str,unsigned char len)
{
		LcdSetCursor(x,y);
		while(len--){
		     LcdWriteDat(*str++);
		}
}
void InitLcd1602()
{
		LcdWriteCmd(0x38);
		LcdWriteCmd(0x0c);
		LcdWriteCmd(0x06);
		LcdWriteCmd(0x01);
}
void ZD1() interrupt 1
{
	 static unsigned char tmr500ms=0;

	 TH0=T0RH;
	 TL0=T0RL;
	 tmr500ms++;
	 if(tmr500ms>=50){
	 	tmr500ms=0;
		flag500ms=1;
	 }
}

        因为本人开发板上面LCD显示存在点问题,所以就不展示这段程序的实际效果了。本程序基本的思路就是构造一个字符数组缓存区,然后利用for循环进行遍历,实现右移显示的效果。

第三题:

        对于多.C文件的使用,毫无疑问,使用多.c文件的优势是毫无疑问的。可以将单片机的功能封装起来,然后进行调用。在进行多文件编程的同时,将编写好的文件加入到工程之中即可。

第四题:

        本多.C文件,利用到了实用的串口通讯机制程序。该程序的算法是基于用户的命令都是在一段时间内很快就会发送的,所以我们需要判断在30ms的时间内,判断单片机串口的接收的部分有没有变化。即如果单片机串口的接收端在30ms内电位没有发生变化,则认为已经结束了发送。相关的代码如下:

/*
*******************************************************************************
*                     《手把手教你学51单片机(C语言版)》
*                    配套 KST-51 单片机开发板 示例源代码
*
*         (c) 版权所有 2014 金沙滩工作室/清华大学出版社 保留所有权利
*                 获取更多资料请访问:http://www.kingst.org
*
* 文件名:Uart.c
* 描  述:基于帧模式的UART串口驱动模块
* 版本号:v1.0.0
* 备  注:
*******************************************************************************
*/

#include <reg52.h>

bit flagFrame = 0;  //帧接收完成标志,即接收到一帧新数据
bit flagTxd = 0;    //单字节发送完成标志,用来替代TXD中断标志位
unsigned char cntRxd = 0;   //接收字节计数器
unsigned char pdata bufRxd[64];  //接收字节缓冲区

extern void UartAction(unsigned char *buf, unsigned char len);

/* 串口配置函数,baud-通信波特率 */
void ConfigUART(unsigned int baud)
{
    SCON  = 0x50;  //配置串口为模式1
    TMOD &= 0x0F;  //清零T1的控制位
    TMOD |= 0x20;  //配置T1为模式2
    TH1 = 256 - (11059200/12/32)/baud;  //计算T1重载值
    TL1 = TH1;     //初值等于重载值
    ET1 = 0;       //禁止T1中断
    ES  = 1;       //使能串口中断
    TR1 = 1;       //启动T1
}
/* 串口数据写入,即串口发送函数,buf-待发送数据的指针,len-指定的发送长度 */
void UartWrite(unsigned char *buf, unsigned char len)
{
    while (len--)  //循环发送所有字节
    {
        flagTxd = 0;      //清零发送标志
        SBUF = *buf++;    //发送一个字节数据
        while (!flagTxd); //等待该字节发送完成
    }
}
/* 串口数据读取函数,buf-接收指针,len-指定的读取长度,返回值-实际读到的长度 */
unsigned char UartRead(unsigned char *buf, unsigned char len)
{
    unsigned char i;
    
    if (len > cntRxd)  //指定读取长度大于实际接收到的数据长度时,
    {                  //读取长度设置为实际接收到的数据长度
        len = cntRxd;
    }
    for (i=0; i<len; i++)  //拷贝接收到的数据到接收指针上
    {
        *buf++ = bufRxd[i];
    }
    cntRxd = 0;  //接收计数器清零
    
    return len;  //返回实际读取长度
}
/* 串口接收监控,由空闲时间判定帧结束,需在定时中断中调用,ms-定时间隔 */
void UartRxMonitor(unsigned char ms)
{
    static unsigned char cntbkp = 0;
    static unsigned char idletmr = 0;

    if (cntRxd > 0)  //接收计数器大于零时,监控总线空闲时间
    {
        if (cntbkp != cntRxd)  //接收计数器改变,即刚接收到数据时,清零空闲计时
        {
            cntbkp = cntRxd;
            idletmr = 0;
        }
        else                   //接收计数器未改变,即总线空闲时,累积空闲时间
        {
            if (idletmr < 30)  //空闲计时小于30ms时,持续累加
            {
                idletmr += ms;
                if (idletmr >= 30)  //空闲时间达到30ms时,即判定为一帧接收完毕
                {
                    flagFrame = 1;  //设置帧接收完成标志
                }
            }
        }
    }
    else
    {
        cntbkp = 0;
    }
}
/* 串口驱动函数,监测数据帧的接收,调度功能函数,需在主循环中调用 */
void UartDriver()
{
    unsigned char len;
    unsigned char pdata buf[40];

    if (flagFrame) //有命令到达时,读取处理该命令
    {
        flagFrame = 0;
        len = UartRead(buf, sizeof(buf));  //将接收到的命令读取到缓冲区中
        UartAction(buf, len);  //传递数据帧,调用动作执行函数
    }
}
/* 串口中断服务函数 */
void InterruptUART() interrupt 4
{
    if (RI)  //接收到新字节
    {
        RI = 0;  //清零接收中断标志位
        if (cntRxd < sizeof(bufRxd)) //接收缓冲区尚未用完时,
        {                            //保存接收字节,并递增计数器
            bufRxd[cntRxd++] = SBUF;
        }
    }
    if (TI)  //字节发送完毕
    {
        TI = 0;   //清零发送中断标志位
        flagTxd = 1;  //设置字节发送完成标志
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值