题目:
第一题:
关于通讯的逻辑,在这里面时序的概念十分重要。很明显,时序包括了两个概念。第一是时间的问题。在这里时间指的是通讯的时候,每位传输的时间,即时间为波特率分之一。其次为“序”,就是在通讯的时候信息传递的顺序。例如,在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; //设置字节发送完成标志
}
}