第81节:液晶屏显示串口发送过来的任意汉字和字符

发布者:星辰古泉最新更新时间:2016-03-16 来源: eefocus关键字:液晶屏  串口发送 手机看文章 扫描二维码
随时随地手机看文章
开场白:

通过上一节的学习,我们发现汉字的识别本质是机内码,字符的识别本质是ASCII码。不管是机内码还是ASCII码,这些都是16进制的数字,也就是我们手机平时接收和发送的信息本质都是这些数字编码,但是机内码是2个字节,ASCII码是1个字节,如果在一串随机的信息中,同时包含汉字和字符两种数字信息,我们的程序又该如何能筛选和识别它们,会不会把机内码和ASCII码搞混乱了?这一节要教大家三个知识点:

第一个:ASCII码与汉字机内码不一样的规律是,ASCII码都是小于128(0x80)的,根据这个特点可以编程序把它们区分开来。

第二个:当任意一串信息中既包含汉字机内码,又包含字符ASCII码时,并且当ASCII码左右相邻个数是以奇数存在的时候,如何巧妙地插入填充空格字符0x20使它们能够符合一个坐标点显示2个字符的要求。

第三个:本节程序串口部分是在第39节内容基础上移植修改而成,本节程序中多添加了如何通过结束标志0x0D 0x0A来提取有效数据的内容,读者可以学习一下其中的框架。

具体内容,请看源代码讲解。

(1)硬件平台:基于朱兆祺51单片机学习板。

(2)实现功能:

开机上电后,液晶屏第1行显示“请发送信息”。 任意时刻,从电脑“串口调试助手”根据以下协议要求,发送一串不超过24个汉字或者字符的信息,液晶屏就实时把这些信息显示在第2,3,4行。并且蜂鸣器会鸣叫一声表示数据接收正确。

波特率是:9600 。

通讯协议:EB 00 55 XX XX XX XX …XX XX 0D 0A

最前面3个字节EB 00 55 表示数据头。

最后面2个字节0D 0A表示信息的结束标志。

中间的XX是机内码和ASCII码信息。比如:要发送“曹健1人学习51单片机”的信息,它们对应的指令是:

EB 00 55 B2 DC BD A1 31 C8 CB D1 A7 CF B0 35 31 B5 A5 C6 AC BB FA 0D 0A

(3)源代码讲解如下:

#include "REG52.H"

/* 注释一:

* 本程序的串口那部分内容是从《第三十九节:判断数据头来接收一串数据的串口通用程序框架。》

* 移植过来的,但是以下要把接收缓冲区的数据从10改成60.同时,协议后面多增加了数据结束标志0x0d 0x0a。

*/

#define const_rc_size 60 //接收串口中断数据的缓冲区数组大小

#define const_receive_time 5 //如果超过这个时间没有串口数据过来,就认为一串数据已经全部接收完,这个时间根据实际情况来调整大小

#define const_voice_short 40 //蜂鸣器短叫的持续时间

sbit LCDCS_dr = P1^6; //片选线

sbit LCDSID_dr = P1^7; //串行数据线

sbit LCDCLK_dr = P3^2; //串行时钟线

sbit LCDRST_dr = P3^4; //复位线

sbit beep_dr=P2^7; //蜂鸣器的驱动IO口

void initial_myself(void);

void initial_peripheral(void);

void delay_long(unsigned int uiDelaylong);

void T0_time(void); //定时中断函数

void usart_receive(void); //串口接收中断函数

void usart_service(void); //串口服务程序,在main函数里

void display_service(void); //显示服务程序,在main函数里

void empty_diaplay_buffer(void); //把显示缓冲区全部填充空格字符0x20

void diaplay_all_buffer(void); //显示第2,3,4行全部缓冲区的内容

void SendByteToLcd(unsigned char ucData); //发送一个字节数据到液晶模块

void SPIWrite(unsigned char ucWData, unsigned char ucWRS); //模拟SPI发送一个字节的命令或者数据给液晶模块的底层驱动

void WriteCommand(unsigned char ucCommand); //发送一个字节的命令给液晶模块

void LCDWriteData(unsigned char ucData); //发送一个字节的数据给液晶模块

void LCDInit(void); //初始化 函数内部包括液晶模块的复位

void display_clear(void); // 清屏。4行8列的坐标点全部显示2个空字符相当于清屏了。

void display_double_code(unsigned int x,unsigned int y,const unsigned char ucArray1,const unsigned char ucArray2); //在一个坐标点显示1个汉字或者2个字符的函数

void delay_short(unsigned int uiDelayshort); //延时

code unsigned char ucAddrTable[]= //调用内部字库时,液晶屏的坐标体系,位置编码,是驱动内容,读者可以不用深究它的含义。

{

0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,

0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,

0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,

0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,

};

code unsigned char JN1616_qing[]= //机内码 请

{

0xC7,0xEB, //请

};

code unsigned char JN1616_fa[]= //机内码 发

{

0xB7,0xA2,

};

code unsigned char JN1616_song[]= //机内码 送

{

0xCB,0xCD,

};

code unsigned char JN1616_xin[]= //机内码 信

{

0xD0,0xC5,

};

code unsigned char JN1616_xi[]= //机内码 息

{

0xCF,0xA2,

};

unsigned int uiSendCnt=0; //用来识别串口是否接收完一串数据的计时器

unsigned char ucSendLock=1; //串口服务程序的自锁变量,每次接收完一串数据只处理一次

unsigned int uiRcregTotal=0; //代表当前缓冲区已经接收了多少个数据

unsigned char ucRcregBuf[const_rc_size]; //接收串口中断数据的缓冲区数组

unsigned int uiRcMoveIndex=0; //用来解析数据协议的中间变量

unsigned int uiVoiceCnt=0; //蜂鸣器鸣叫的持续时间计数器

unsigned char ucWd1Update=1; //窗口1的整屏更新显示变量 1代表更新显示,响应函数内部会清零

unsigned char ucWd1Part1Update=0; //窗口1的第1个局部更新显示变量 1代表更新显示,响应函数内部会清零

unsigned char ucDispplayBuffer[48]; //第2,3,4行显示内容的缓冲区

void main()

{

initial_myself();

delay_long(100);

initial_peripheral();

while(1)

{

usart_service(); //串口服务程序

display_service(); //显示服务程序

}

}

/* 注释二:在一个坐标点显示1个汉字或者2个字符的函数

* 第1,2个参数x,y是坐标体系。x的范围是0至8,y的范围是0至3.

* 第3个参数ucArray1是第1个汉字机内码或者ASCII码。

* 第4个参数ucArray2是第2个汉字机内码或者ASCII码。

*/

void display_double_code(unsigned int x,unsigned int y,const unsigned char ucArray1,const unsigned char ucArray2)

{

WriteCommand(0x30); //基本指令集

WriteCommand(ucAddrTable[8*y+x]); //起始位置

LCDWriteData(ucArray1);

LCDWriteData(ucArray2);

}

void display_clear(void) // 清屏。4行8列的坐标点全部显示2个空字符相当于清屏了。

{

unsigned int i,j;

for(i=0;i<4;i++)

{

for(j=0;j<8;j++)

{

display_double_code(j,i,0x20,0x20); //0x20是空格的ASCII码

}

}

}

void SendByteToLcd(unsigned char ucData) //发送一个字节数据到液晶模块

{

unsigned char i;

for ( i = 0; i < 8; i++ )

{

if ( (ucData << i) & 0x80 )

{

LCDSID_dr = 1;

}

else

{

LCDSID_dr = 0;

}

LCDCLK_dr = 0;

LCDCLK_dr = 1;

}

}

void SPIWrite(unsigned char ucWData, unsigned char ucWRS) //模拟SPI发送一个字节的命令或者数据给液晶模块的底层驱动

{

SendByteToLcd( 0xf8 + (ucWRS << 1) );

SendByteToLcd( ucWData & 0xf0 );

SendByteToLcd( (ucWData << 4) & 0xf0);

}

void WriteCommand(unsigned char ucCommand) //发送一个字节的命令给液晶模块

{

LCDCS_dr = 0;

LCDCS_dr = 1;

SPIWrite(ucCommand, 0);

delay_short(90);

}

void LCDWriteData(unsigned char ucData) //发送一个字节的数据给液晶模块

{

LCDCS_dr = 0;

LCDCS_dr = 1;

SPIWrite(ucData, 1);

}

void LCDInit(void) //初始化 函数内部包括液晶模块的复位

{

LCDRST_dr = 1; //复位

LCDRST_dr = 0;

LCDRST_dr = 1;

}

void empty_diaplay_buffer(void) //把显示缓冲区全部填充空格字符0x20

{

unsigned int i;

for(i=0;i<48;i++)

{

ucDispplayBuffer[i]=0x20; //第2,3,4行显示内容的缓冲区全部填充0x20空格字符

}

}

void diaplay_all_buffer(void) //显示第2,3,4行全部缓冲区的内容

{

unsigned int i,j;

for(i=0;i<3;i++) //i代表行数

{

for(j=0;j<8;j++) //j代表某行的某个坐标在第几列

{

display_double_code(j,i+1,ucDispplayBuffer[i*16+j*2],ucDispplayBuffer[i*16+j*2+1]); //这里的16代表一行可以显示16个字符

}

}

}

void display_service(void) //显示服务程序,在main函数里

{

if(ucWd1Update==1) //窗口1整屏更新,里面只放那些不用经常刷新显示的内容

{

ucWd1Update=0; //及时清零,避免一直更新

ucWd1Part1Update=1; //激活窗口1的第1个局部更新显示变量

display_clear(); // 清屏。4行8列的坐标点全部显示2个空字符相当于清屏了。

//显示第一行固定的内容:请发送信息

display_double_code(1,0,JN1616_qing[0],JN1616_qing[1]); //请

display_double_code(2,0,JN1616_fa[0],JN1616_fa[1]); //发

display_double_code(3,0,JN1616_song[0],JN1616_song[1]); //送

display_double_code(4,0,JN1616_xin[0],JN1616_xin[1]); //信

display_double_code(5,0,JN1616_xi[0],JN1616_xi[1]); //息

}

if(ucWd1Part1Update==1) //窗口1的第1个局部更新显示变量,里面放一些经常需要刷新显示的内容

{

ucWd1Part1Update=0; //及时清零,避免一直更新

diaplay_all_buffer(); //显示第2,3,4行全部缓冲区的内容

}

}

/* 注释三:

* 以下有效信息截取和如何判断机内码与ASCII码是本程序的核心,请仔细看讲解。

* 凡是ASCII码都是小于0x80(128)的,根据这个特点可以把ASCII码和机内码分离出来,

* 同时,由于液晶屏的1个坐标必须显示2个编码,对于单个存在的ASCII码,我们要在

* 它的右边多插入一个空格字符0x20。至于如何插入空格0x20字符,请看以下代码。

*/

void usart_service(void) //串口服务程序,在main函数里

{

unsigned int i;

unsigned int uiCodeCnt; //统计接收的有效编码数量

unsigned int uiCodeYu; //对uiCodeCnt求2的余数,方便识别是否是1个ASCII码相邻

if(uiSendCnt>=const_receive_time&&ucSendLock==1) //说明超过了一定的时间内,再也没有新数据从串口来

{

ucSendLock=0; //处理一次就锁起来,不用每次都进来,除非有新接收的数据

uiRcMoveIndex=0; //由于是判断数据头,所以下标移动变量从数组的0开始向最尾端移动 这个变量是用来抗干扰处理的

while(uiRcregTotal>=6&&uiRcMoveIndex<=(uiRcregTotal-6)) //这里的6表示有3个字节的数据头,至少1个有效数据,2个数据结束标志0x0d 0x0a

{

if(ucRcregBuf[uiRcMoveIndex+0]==0xeb&&ucRcregBuf[uiRcMoveIndex+1]==0x00&&ucRcregBuf[uiRcMoveIndex+2]==0x55) //数据头eb 00 55的判断

{

empty_diaplay_buffer(); //把显示缓冲区全部填充空格字符0x20

uiCodeCnt=0; //统计接收的有效编码数量清零

for(i=0;i<(uiRcregTotal-uiRcMoveIndex-3)&&i<48;i++)//这里的3表示有3个字节的数据头。48表示最大只能接收24个汉字,一共48个字节的机内码.

{

if(ucRcregBuf[uiRcMoveIndex+3+i]==0x0d&&ucRcregBuf[uiRcMoveIndex+4+i]==0x0a) //结束标志0x0d 0x0a的判断

{

uiVoiceCnt=const_voice_short; //蜂鸣器发出声音,表示数据接收正确完毕

ucWd1Part1Update=1; //及时更新显示第2,3,4行内容的信息

break; //退出for循环

}

else //收集有效信息编码进入显示缓冲区

{

uiCodeYu=uiCodeCnt%2; //对2求余数,用来识别相信的2个是否是机内码,否则要进行插入填充0x20处理

if(uiCodeYu==1)

{

if(ucRcregBuf[uiRcMoveIndex+3+i]>=0x80&&ucRcregBuf[uiRcMoveIndex+3+i-1]<0x80) //如果当前的是机内码,而上一个不是机内码

{

ucDispplayBuffer[uiCodeCnt]=0x20; //当前的先填充插入空格字符0x20

uiCodeCnt++; //统计接收的有效编码数量

}

}

ucDispplayBuffer[uiCodeCnt]=ucRcregBuf[uiRcMoveIndex+3+i]; //收集有效信息编码进入显示缓冲区

uiCodeCnt++; //统计接收的有效编码数量

}

}

break; //退出while循环

}

uiRcMoveIndex++; //因为是判断数据头,游标向着数组最尾端的方向移动

}

uiRcregTotal=0; //清空缓冲的下标,方便下次重新从0下标开始接受新数据

}

}

void T0_time(void) interrupt 1 //定时中断

{

TF0=0; //清除中断标志

TR0=0; //关中断

if(uiSendCnt

{

uiSendCnt++; //表面上这个数据不断累加,但是在串口中断里,每接收一个字节它都会被清零,除非这个中间没有串口数据过来

ucSendLock=1; //开自锁标志

}

if(uiVoiceCnt!=0)

{

uiVoiceCnt--; //每次进入定时中断都自减1,直到等于零为止。才停止鸣叫

beep_dr=0; //蜂鸣器是PNP三极管控制,低电平就开始鸣叫。

}

else

{

; //此处多加一个空指令,想维持跟if括号语句的数量对称,都是两条指令。不加也可以。

beep_dr=1; //蜂鸣器是PNP三极管控制,高电平就停止鸣叫。

}

TH0=0xfe; //重装初始值(65535-500)=65035=0xfe0b

TL0=0x0b;

TR0=1; //开中断

}

void usart_receive(void) interrupt 4 //串口接收数据中断

{

if(RI==1)

{

RI = 0;

++uiRcregTotal;

if(uiRcregTotal>const_rc_size) //超过缓冲区

{

uiRcregTotal=const_rc_size;

}

ucRcregBuf[uiRcregTotal-1]=SBUF; //将串口接收到的数据缓存到接收缓冲区里

uiSendCnt=0; //及时喂狗,虽然main函数那边不断在累加,但是只要串口的数据还没发送完毕,那么它永远也长不大,因为每个中断都被清零。

}

else //我在其它单片机上都不用else这段代码的,可能在51单片机上多增加" TI = 0;"稳定性会更好吧。

{

TI = 0;

}

}

void delay_short(unsigned int uiDelayShort)

{

unsigned int i;

for(i=0;i

{

;

}

}

void delay_long(unsigned int uiDelayLong)

{

unsigned int i;

unsigned int j;

for(i=0;i

{

for(j=0;j<500;j++) //内嵌循环的空指令数量

{

; //一个分号相当于执行一条空语句

}

}

}

void initial_myself(void) //第一区 初始化单片机

{

beep_dr=1; //用PNP三极管控制蜂鸣器,输出高电平时不叫。

//配置定时器

TMOD=0x01; //设置定时器0为工作方式1

TH0=0xfe; //重装初始值(65535-500)=65035=0xfe0b

TL0=0x0b;

//配置串口

SCON=0x50;

TMOD=0X21;

IP =0x10; //把串口中断设置为最高优先级,必须的。

TH1=TL1=-(11059200L/12/32/9600); //这段配置代码具体是什么意思,我也不太清楚,反正是跟串口波特率有关。

TR1=1;

}

void initial_peripheral(void) //第二区 初始化外围

{

EA=1; //开总中断

ES=1; //允许串口中断

ET0=1; //允许定时中断

TR0=1; //启动定时中断

LCDInit(); //初始化12864 内部包含液晶模块的复位

WriteCommand(0x0C); //命令字0x0c表示用内部字库模式。命令字0x36表示用自构字库模式。

empty_diaplay_buffer(); //把显示缓冲区全部填充空格字符0x20

}

总结陈词:

我们现在是调用液晶屏内部字库来显示内容,如果要某行内容反显或者光标闪烁改怎么编程?欲知详情,请听下回分解-----如何在调用液晶屏内部字库时让某行内容反显或者光标闪烁。

关键字:液晶屏  串口发送 引用地址:第81节:液晶屏显示串口发送过来的任意汉字和字符

上一篇:第80节:调液晶屏内部字库显示汉字或字符的坐标体系和本质
下一篇:第82节:调用液晶屏内部字库把一个任意数值的变量显示出来

推荐阅读最新更新时间:2024-03-16 14:47

STM8L151C8单片机学习例程(7)——ADC采集,串口发送电压值
STM8L_7_ADC: 1.User:工程及main文件 2.Hardware:ADC,USART,CLK,LED,Delay 3.STM8L15x_StdPeriph_Driver:STM8自带库文件 4.Debug:hex文件存放于EXE文件夹 5.Function: ADC采集,串口发送电压值 /** ****************************************************************************** * @file main.c * @author Alex——小白 * @version V1.0 * @date 20
[单片机]
STM8L151C8单片机学习例程(7)——ADC采集,<font color='red'>串口</font><font color='red'>发送</font>电压值
51单片机控制LCD12864液晶屏显示图形菜单
想了很久,每次做菜单都受限于12864字符模式的各种限制,比如字体大小,不能显示图标等等,没有任何效果,这次就来个彻底的解脱,写一个单函数就能完成所有功能的函数,只需要2个图片就能完成任意菜单内容的菜单,可以有2的8次方=256个选择结果,应该能满足所有的要求了吧?菜单的内容就是图片的内容,因此可以显示任意大小的图形文字混合的菜单条目,比如使用字符模式,一屏只能显示4行菜单(64/16=4),且只能显示汉字和字符等,显示位置等等的都受限,而这个方法可以显示比如12*12(64/12=5行)的字符,8*8(64/8=8行)的字符,甚至不是字符,可以是图标+文字+任意其他的图像,只要是图片能显示的,都可以。。哈哈,比较复杂,现在只写了一
[单片机]
基于Sitsang平台的汽车黑匣子开发
引言 2020年中国民用数量将飙升至1.4亿辆,全世界汽车保有量以每年3000万辆的速度增长,由此带来的交通安全事故数量也逐年增长。在处理交通事故时,执法人员无法了解整个事故发生前后状况,供提取证据的事故现场一般被严重破坏,可能会导致一些误判。而汽车一旦装上本系统后,在处理交通事故的程序上,由于有录像和数字证据,提高了执法的公平性和公证性。 本系统设计的汽车实现了视频取证,通过摄像头采集事故发生前后车前的录像,可以真实地再现事故发生的全过程。此外,汽车黑匣子还具有防盗功能,可以在一定的条件下启动语音传输功能,通过移动电话网络把车内的语音传输到指定的接收中心,也可以通过GPS全球定位系统确定汽车的位置。 基于Sitsan
[嵌入式]
关于单片机串口发送和接收的问题
串口初始化过程:     1、确定T/C1的工作方式--TMOD     2、设置T/C初始值----------------------------TH1、TH2     3、启动T/C-----------------------------------TR1     4、串行口的控制------------------------------SCON     5、串口在中断方式下,需要开CPU和源中断----IE      总结:不一定需要使用中断函数,可以在main函数里面的while里面判断RI和TI是否等于1, 如果不使用中断函数interrupt,可以不用开源中断ES和EA。
[单片机]
关于单片机<font color='red'>串口</font><font color='red'>发送</font>和接收的问题
基于ds1302的电子日历系统
  基于ds1302的电子日历系统在硬件选择上主要采用AT89S51作为主控制系统;DS1302提供时钟;DS18B20作为数字式温度传感器;LCD1602液晶屏作为显示。   AT89C51单片机   单片机最小系统   AT89C51单片机采用Flash ROM,内部具有4KB ROM存储空间,相对于本设计而言程序空间完全够用。能于3V的超低压工作,而且与MCS-51系列单片机完全兼容,而且运用于电路设计中时具备ISP在线编程技术,当在对电路进行调试时,由于程序的错误修改或对程序的新增功能需要烧入程序时,避免芯片的多次拔插对芯片造成的损坏。   ds1302   DS1302与单片机的连接   DS130是美国DA
[单片机]
基于ds1302的电子日历系统
51单片机12864大液晶屏俄罗斯方块
参考源代码: //************************************************************************************************* //************************************************************************************************* // 程序名 :俄罗斯方块游戏 ** // 版本说明 :此版是第一版,在LCD1608第4版基础上改进而来,目前只可以绘制游戏区域的边框。 ** // 完成时间 :2007年8月28日 ** // 作者 :从零开始单片
[单片机]
51单片机12864大<font color='red'>液晶屏</font>俄罗斯方块
STM32 FSMC 驱动80总线液晶屏的方法的解释
这里先看TFT液晶屏所谓的80时序图: 当我们对比之后发现STM32的FSMC的SRAM时序和80总线很相近,也可以套用: 在这里只要把地址线当成RS就可以相当于80时序了,具体实现方法是这样的: 把A0连接到RS,那么当我对0地址进行读写方式的时候A0将会输出0,这也就相当于命令的读写,当我地1地址进行读写的时候,那就相当于对数据的读写,这样就巧妙的避开了地址的问题,因为液晶是地址数据复用的,我们在代码里也可以看到这个结构体: typedef struct { vu16 LCD_REG; vu16 LCD_RAM; } LCD_TypeDef; //使用NOR/SRAM的 Bank1.sector4,地址位HAD
[单片机]
武汉华星光电获得TüV莱茵国内首张液晶屏低蓝光认证证书
据武汉晚报报道,武汉华星光电技术有限公司获得德国莱茵TüV集团(简称TüV莱茵)国内首张液晶屏低蓝光认证证书。 TüV莱茵武汉分公司经理李璇介绍,TüV莱茵与武汉华星光电,在手机液晶显示产品蓝光管理方面展开了系列合作,对其最新的手机液晶屏幕进行低蓝光认证。 在电子屏幕时代,手机、电脑、液晶电视等各式电子产品充斥着人们的生活,由显示屏产生的高能可见蓝光危害也随之凸显,对眼睛的伤害已经引起各方关注。“什么样标准的低蓝光,既能让图像不失真,又能减少对眼睛伤害?”作为国际领先的检验检测认证机构——TüV莱茵在显示行业,率先引入低蓝光检测认证,并于2018年9月,与Healthe合作制定了全球首套数码设备屏幕配件蓝光过滤认证标
[嵌入式]
武汉华星光电获得TüV莱茵国内首张<font color='red'>液晶屏</font>低蓝光认证证书
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved