在应用液晶屏做仪器控制界面的时候常常会遇到绘制测量曲线的问题,综合分析这些问题大体可以分为两类:
一. 波形显示很糟糕,曲线的周围出现不可预计的白点
二. 因为曲线数据量的离散,使波形看上去不是连续的曲线,采用了补点的办法却使波形相对于实际的测量结果严重失真。
第一种问题出在对液晶屏的显示RAM和实际显示点阵的关系理解不透测,而第二种问题是因为没有从数学和美观的角度综合分析曲线的趋势,找出合理的补点算法。
下面就根据这两个问题结合现在市面上流行的单色液晶控制器Sed1335,针对320*240的点阵液晶 具体说明这两种问题的解决办法,并提供简单的C51模块程序,实际上这种方法对所有的液晶和曲线都是通用,希望能给刚刚从事液晶屏开发的朋友一点启发。
SED1330/SED1335是日本SEIKOEPSON公司出品的液晶显示控制器,笔者认为其最大的优点就是:它可以以图形和文本方式分层显示,这给界面菜单和曲线的绘制都带来了很大的方便。
在文本显示的方式下显示位为FX*FY点阵块,在图形方式下显示位为8*1点阵块 。
图1 液晶绘制的曲线
笔者建议画曲线的时候最好在图形方式下( 文本方式下,相对复杂的多 ),其中显示RAM是从左向右从上到下地址递增的,而每一个byte对应8个点。如果我们采用的是8位单片机(这里以51系列为例) 。每次访问一次显示RAM必须是一个字节,那么要轻松的在屏幕的任意一个位置画一个点就必须要对显的RAM进行位操作。Sed1335的指令集中提供了对显示RAM读写的操作指令,因此我们只要在每次写入显示RAM的时候读取原来的状态再根据具体的需要对RAM进行操作就很容易的可以在屏幕的任意一个位置画一个点同时不会删除画点之前这个点周围的点,这样我们的第一个问题就解决了。
再来看第二个问题,我们分析如图1的曲线 ( 这是一个RC电路中电感在高压脉冲经过以后出现的振荡波形 ) ,我们把曲线的走势分类,设Q(t) 是曲线在时间t时刻的导数,那么可以把曲线的趋势分成五类 :
(1) Q(t) > 1 快速递增
(2) 0 < Q(t) ≤ 1 缓慢递增
(3) Q(t) < -1 快速递减
(4) 0 > Q(t) ≥ -1 缓慢递减
(5) Q(t) = 0 幅值不便
对于需要绘制实时曲线的系统来说,几乎所有采样系统都是按照系统时间的推移来采样的,同样我们要绘制的曲线也必须时按照同样的规律变化。因此们有必要把液晶屏幕上曲线的X轴等价于实际的时间轴 为了能充分的反应出被测对象的特性。我们把液晶屏幕X轴方向每一个点的变化。定义为一次AD采样。这样的曲线该是个什么样子呢,我们可以想象如果我们的AD采样一直不变的情况下这个曲线刚好是一个连续的水平直线(因为X坐标是连续的)。但是如果AD采样的值发生变化以后,Y值(应该反应被测对象的幅值特性)就会发生变化,而这样的变化是必使曲线呈现离散的状态 。
下面我们就根据我们把曲线(指实际的曲线,可以理解为AD数据的变化趋势)分类好的趋势来分5种情况对曲线进行补点。因为我们的X坐标对于点阵来说是连续的因此我们只要讨论Y变化后的补点就可以了。
左图是一对典型的 Q(t) > 1的离散点。为了满足快速递增的特性我们只要按照右图的方法补点就可以了 。
(1) Q(t) > 1 快速递增
我们同样保持这个缓慢递增的特性
(2) 0<Q(t)≤1 缓慢递增(如果X轴连续这个情况不用考虑)
(3) Q(t)<-1 快速递减
(4) 0>Q(t)≥-1 缓慢递增(如果X轴连续这个情况不用考虑)
(5) Q(t)=0 幅值不变(如果X轴连续这个情况不用考虑)
笔者给出部分程序供参考。
extern void drawdots_e(int dot_x,int dot_y,Byte layer);
是在屏幕上画点的函数
extern void draw_wave(Byte value1,float yamplify,Word value2,Byte value3,Byte layer,Bool value4);
是在屏幕上画波形的函数,包括了横纵坐标的放大倍数
extern void wave_lineto(int x1,int y1,int x2,int y2,Byte layer);
是连线函数
- void wave_lineto(int x1,int y1,int x2,int y2,Byte layer){
- int d_x,d_y;
- float k;
- d_x=x2-x1;
- d_y=y2-y1;
- k=d_y/d_x;
- if(k==0){
- for(x1=x1+1;x1<=x2;x1++){
- drawdots_e(x1,y1,layer);
- }
- }
- if((k>0)&&(k<=1)){
- for(x1=x1+1;x1<=x2;x1++){
- y1++;
- drawdots_e(x1,y1,layer);
- }
- }
- if(k>1){
- x1++;
- for(y1=y1+1;y1<=y2;y1++){
- drawdots_e(x1,y1,layer);
- }
- }
- if((k<0)&&(k>=-1)){
- for(x1=x1+1;x1<=x2;x1++){
- y1--;
- drawdots_e(x1,y1,layer);
- }
- }
- if(k<-1){
- for(y1=y1-1;y1>y2;y1--){
- drawdots_e(x1,y1,layer);
- }
- x1++;
- drawdots_e(x1,y1,layer);
- }
- }
- void draw_wave(Byte value1,float value5,Word value2,Byte value3,Byte layer,Bool
- value4){
- int x1,y1,x2,y2;
- int pdata temp;
- dot_lineflag=FALSE;
- dot_line=value4;
- //处理X轴平移的问题
- x1=0;
- y1=sram_r(value2)-value3;
- drawdots_e(x1,y1,layer);
- for(x2=1;x2<300;x2++){
- temp=value2+(value1+1)*x2;
- y2=sram_r(temp)-value3;
- if(temp>0x7f00)
- break;
- y2=y2*value5;
- wave_lineto(x1,y1,x2,y2,layer);
- x1=x2;
- y1=y2;
- }
- dot_lineflag=FALSE;
- dot_line=FALSE;
- }
- void drawdots_e(int dot_x,int dot_y,Byte layer){
- int x,y;
- Byte temp,dotbyte,dotbyteago;
- long zuobiao1,zuobiao2;
- Byte OH,OL;
- if(dot_line){
- dot_lineflag=~dot_lineflag;
- }
- if(!dot_lineflag){
- temp=0x80;
- dot_x+=20;
- x=dot_x/8;
- y=dot_y;
- y=112-y;
- dotbyte=dot_x%8;
- temp=temp>>dotbyte;
- dotbyte=temp;
- zuobiao1=y*40+x;
- zuobiao2=zuobiao1;
- OH=zuobiao1>>8;
- OL=zuobiao2;
- //comm_w(0x4c);
- comm_w(0x46);
- data_w(OL);
- data_w(OH+layer);
- comm_w(0x43);
- dotbyteago=0;
- dotbyteago=data_r();
- _nop_();
- _nop_();
- dotbyte=dotbyte|dotbyteago;
- //comm_w(0x4c);
- comm_w(0x46);
- data_w(OL);
- data_w(OH+layer);
- comm_w(0x42);
- data_w(dotbyte);
- }
- }
经过这样的补点,可以和模拟或数字的示波器相比较。