文针对LCD12864 特性,完成了数字示波器显示必须的绘图驱动程序设计,这个教程定位给初学者使用,立足从简单到复杂一步一步介绍设计过程,甚至是调试的过程,还包括一些经验总结,特别是提供了完整的keil 工程附件。希望读者立足示波器项目,学到更多软硬件设计经验技巧。
一、简易数字示波器原理
数字示波器基本原理可以简单理解为:数据采集+ 图形显示,该过程循环进行,如图1 所示。
图1 简易数字示波器流程图
LCD 图形显示需要根据LCD 特性设计,不同LCD驱动程序不同,本篇将结合不带字库的LCD12864 设计显示程序。
二、图形液晶LCD12864绘图驱动设计基础
关于LCD 的硬件接口电路,在其他教程中有详细介绍,涉及单片机总线知识和CPLD 内部电路,需要认真学习,这里借助现成的驱动函数,重点讲解LCD绘图程序设计。
LCD12864 的电路接口在头文件中定义:
#define LCD_LCW XBYTE[0xf4ea]
// 左屏命令写入
#define LCD_LDW XBYTE[0xf5ea]
// 左屏数据写入
#define LCD_LCR XBYTE[0xf6ea]
// 左屏命令读出
#define LCD_LDR XBYTE[0xf7ea]
// 左屏数据读出
#define LCD_RCW XBYTE[0xf8ea]
// 右屏命令写入
#define LCD_RDW XBYTE[0xf9ea]
// 右屏数据写入
#define LCD_RCR XBYTE[0xfaea]
// 右屏命令读出
#define LCD_RDR XBYTE[0xfbea]
// 右屏数据读出
后面所有对LCD 的编程操作都是基于以上接口定义进行的各种读写操作。
首先来看LCD12864 的点阵结构图,如图2 所示。
图2 LCD点阵分布结构图
此LCD 屏由水平128 列,垂直64 行组成。水平128 列分左右各64 列两个半屏构成。垂直64 行又分8 页,每页8 行(1 列8 点刚好1 字节)。程序每次对LCD 的绘图操作就是以最小单位1 字节进行操作的。
理解这点至关重要。也就是每次只能针对8 点进行操作,而不是1 点进行操作。左右屏由单独地址线控制(前面的接口定义就是分左右屏定义的)。实际打点只需往指定“位置”写入数据,“1”亮,“0”暗。
LCD 驱动忙检测函数void loop_lcd12864_is_busy(unsigned char right)。
void loop_lcd12864_is_busy(unsigned char right)
{
unsigned char tmp,counter=0;
do {
if(right) tmp = LCD_RCR;
else tmp = LCD_LCR;
if(counter++>50) break; // 超时跳出
}
while ((tmp|0x7f)==0xff); //bit7 为1 则表示LCD 内部执行命令,处于“忙”状态
}
对LCD 进行读写操作时,需要进行“忙”检测,LCD 内部也是由控制器来完成一系列刷屏操作的,执行各种操作都是需要一定的时间,也就是说不是任何时候外部控制器都可以对LCD 发操作指令的,只有LCD为空闲状态时才可以操作,忙检测就是循环读取LCD状态标志位,判断是否空闲,关于命令的细节请参考数据手册。
命令写入函数void lcd_cmd_wr(unsigned char cmd,right)。
void lcd_cmd_wr(unsigned char cmd, right)
{
loop_lcd12864_is_busy(right); // 忙检测
if(right) LCD_RCW = cmd; // 右屏命令写入
else LCD_LCW = cmd; // 左屏命令写入
}
数据写入函数void lcd_dat_wr(unsigned char data,right)。
void lcd_dat_wr(unsigned char data,right)
{
loop_lcd12864_is_busy(right);
if(right) LCD_RDW = data;
else LCD_LDW = data;
}
lcd_cmd_wr() 和lcd_dat_wr() 两个函数分别是给LCD 写命令和写数据函数,通过写命令函数设定地址。每个函数都分左右屏,“right”参数选择,“0”选左屏,“非0”选右屏。
读数据函数unsigned char lcd_dat_rd(unsigned char right)。
unsigned char lcd_dat_rd(unsigned char right)
{
loop_LCD12864_is_busy(right);
六、数字示波器几个参数
1. 数据采样率与时间轴定标
数据采样率也就是每秒钟连续采集到的数据个数,或者说两个有效数据之间的时间间隔。为了在LCD 上还原波形图,时间轴与采样率之间要有严格的换算关系。
为了还原波形图,采样率与被测量信号频率之间有一定的关联,不同的采样率,对应不同频段的信号,高采样率适合高频信号,低采样率适合变化缓慢的低频信号。
从显示效果来看,为了在屏幕上再现信号的频率特征,根据Nyquist 频率特征定理,一般要保证信号的每个周期内至少有2 点,用LCD 再现,2 点效果已经很差了。最高采样率一般由ADC 器件决定,这里用的TLC1549 最快只能做到88μs 采集一点,按2 点每周期计算的话,可以做到对5.6K(1000000/88/2) 信号采样,实际效果已经很差了,1K 以内效果最好。
实际在设计采样率档位时,最快档按100μs 每点,屏幕每5 点(500μs)一格,第一档就是0.5ms/div,这一档位需要连续采样,用少量延时控制采样率为10k,第二档就是1ms/div,第三档2ms/div,第四档5ms/div,第五档10ms/div,后面四档分别用定时器控制完成。
2. 电压轴分档定标
由于这版没做模拟电路,没有信号电调理电路,所谓电压档定标是纯软件算法实现,屏幕同样取5 点一格,共10 格,当输入信号从0 到5V 变化时,LCD 刚好满屏显示,每格0.5V,另外还设计了0.1v/div,0.2v/div,1v/div,2v/div,5v/div。共6 档。
3. 交流直流切换
功能完善的示波器应该从硬件上实现交直流切换,本版先从软件上实现交流直流显示,设计思想是根据采样得到的数据的最大值和最小值确定交流信号幅度和中值,把直流部分减掉,显示只显示交流部分,这就是交流档,直流档就是不做去直流处理。
4. 运行停止切换
该功能实现起来很容易,所谓停止,就是停止新的数据采集,重复显示同一帧数据,显示的效果就是波形稳定无抖动,便于对信号电压和频率进行测量。
5. 电压测量
根据当前电压档位(每格表示多少伏),计算出两测量线之间的电压值,在LCD 上显示。
6. 频率测量
根据当前时间轴档位(每格表示多长时间),计算出两测量线之间的时间,假定两测量线之间刚好是一个周期,转换成频率值在LCD 上显示。
7. 波形平移
用变量控制波形在LCD 上显示的相对位置,实现波形上下左右平移。
8. 帧同步
我们使用的数字示波器,对有规律的周期信号能够稳定显示,要想实现稳定显示,需要在起始点显示信号的不同周期的同一点,如何做到这点呢?
程序设计思想是逐点比较过滤,我们取中值,如图7-1 中第5 点所示,信号从小于中值到大于中值的上升沿为起点,不同帧信号可能从1、2、3、4 任何一点开始,这就要用程序判断把设定的起点5 以前的数据丢掉。
程序是这样的:
while(da_buffer > dam) if(++i > (DATA_SIZE/2)) break;
while(da_buffer <= dam) if(++i > (DATA_SIZE/2)) break;
while(da_buffer > dam) if(++i > (DATA_SIZE/2)) break;
这里的dam 就是信号交流部分的中值。
程序用几个while 语句,看上去很呆板,实际运行效果很好喔。第一句把比设定值大的数据过滤,如图10 中的1、2、3 点,如果数据是从4 开始的,第一句会直接跳过;第二句把比设定值小的数据跳过,找出过零点的上升沿5 点。
用户377235 2015-10-25 21:54
用户419124 2012-5-11 19:46
飞言走笔 2012-5-3 08:20
用户377235 2012-4-29 11:37