logo资料库

基于单片机简易数字示波器设计.pdf

第1页 / 共42页
第2页 / 共42页
第3页 / 共42页
第4页 / 共42页
第5页 / 共42页
第6页 / 共42页
第7页 / 共42页
第8页 / 共42页
资料共42页,剩余部分请下载后查看
数字示波器的仿真设计 丁 林 基于新型 51 单片机 STC12C5A60S2 硬件资源,想构思一个数字示波器的方案,不过一个稍微 复杂一些的想法是要时间来实现的,所幸现在正处于暑假期间,于是利用proteus 仿真完成了一个基 于 LCD12864 显示的数字示波器设计。在此声明,这个教程是写给初学者看的,我会从简单到复杂 一步一步详细介绍设计过程,甚至是调试的过程,还包括一些经验总结,特别是提供了完整的 keil 工程附件。希望读者立足示波器项目,学到更多关于软硬件开发的一些经验技巧。 1 简易数字示波器原理 数字示波器基本原理可以简单理解为:数据采集+图形显示,该过程循环进行,如图 1-1 所示。 图 1-1 简易数字示波器原理图 首先是数据采集,这里我们直接用 STC12C5A60S2 单片机上的 ADC 来进行数据的采集。(如果 你没有 ADC,也可能没有信号发生器,但后面会介绍一种正弦表调试方法。) 注:由于 keil C51 中没有 STC12C5A60S2,所以这里的参考程序均改自 STC89C52,两款单片机是兼容的,只 是资源和速度(大约是 STC89C52 的 8-12 倍)不同而已。本文档所有程序均位于灰色方框中 宏晶公司所给驱动函数 INT8U get_AD_result(INT8U channel)。 清 0 高 5 位 //INT8U 是 typedef unsigned char INT8U; 即已重定义的一种类型 INT8U get_AD_result(INT8U channel) { INT8U AD_finished=0; // 存储 A/D 转换标志 ADC_RES = 0; ADC_RESL = 0; channel &= 0x07; //0000,0111 ADC_CONTR = AD_SPEED; _nop_(); ADC_CONTR |= channel; // 选择 A/D 当前通道 _nop_(); ADC_CONTR |= 0x80; // 启动 A/D 电源 delay(1); // ADC_CONTR |= 0x08; //0000,1000 令 ADCS = 1, 启动 A/D 转换, AD_finished = 0; while (AD_finished ==0 ) // 等待 A/D 转换结束 { AD_finished = (ADC_CONTR & 0x10); //0001,0000 测试 A/D 转换结束否 } ADC_CONTR &= 0xE7; //1111,0111 使输入电压达到稳定 清 ADC_FLAG 位, 关闭 A/D 转换, 第 1 页,共 42 页
return (ADC_RES); // } 返回 A/D 高 8 位转换结果 以上是宏晶公司所给的与 STC12C5A60S2 片上 ADC 配套的数据采集驱动函数,使用该函数需 要注意的是,两次调用该函数之间的间隔要超过 ADC 的采样时间,具体可参考 STC12C5A60S2 的 数据手册(datasheet),AD 转换是要一段时间的,在高速系统中时间控制尤其关键。STC12C5A60S2 单片机在 11.0592M 晶振时钟频率下运行,连续两次 AD 采集数据并将数据写入 RAM 变量缓冲区, 之间的时间间隔肯定小于 21us 的,需要适当延时。这在高速档数据采集时增加了一定延时等待就是 这个原因。 图形显示有很多种,LCD 显示稍难,ADC 得到的结果如何在 LCD 上描点,这确实是一个难点, 涉及 LCD 驱动问题,需要花费很大篇幅才能完成。 最初调试我们可以选用串口来做,借助他人现成的工具软件。下面介绍基于串口和上位机工具 软件的波形显示程序设计。 串口初始化函数 void initiate_RS232 (void)。 波特率 //--------------------------------------------------------------------- #define Fosc 11059200 #define BAUD 115200 // #define RELOAD_115200 (256 - (Fosc/16*10/BAUD+5)/10 ) //1T 模式, 波特率加倍 #define BRTx12_enable() AUXR |= 0x04 //BRT 独立波特率发生器的溢出率快 12 倍 #define BRT_start() AUXR |= 0x10 // 启动独立波特率发生器 BRT 计数。 void initiate_RS232 (void) // 串口初始化 { ES = 0; // SCON = 0x50; // AUXR |= 0x01; // PCON |= 0x80; // BRTx12_enable(); //BRT BRT = RELOAD_115200; // 设置独立波特率发生器 BRT 的自动重装数 BRT_start(); // ES = 1; } 禁止串口中断 可变波特率. 8 位无奇偶校验 使用独立波特率发生器 波特率加倍 独立波特率发生器的溢出率快 12 倍 启动独立波特率发生器 BRT 计数。 往串口写 1 字节函数 void Send_Byte(INT8U one_byte)。 //--------------------------------------------------------------------- void Send_Byte(INT8U one_byte) // 发送一个字节 { TI = 0; // SBUF = one_byte; while (TI == 0); TI = 0; // 清零串口发送中断标志 清零串口发送中断标志 第 2 页,共 42 页
} 从串口读 1 字节函数 unsigned char uart_get_uchar()。 unsigned char uart_get_uchar() //从串口读 1 字节无符号数据 { } while(!RI); RI = 0; return SBUF; 以上这几个函数是学单片机的人一定要掌握的,能够随手拿来就用,通过串口调试程序,很方 便。有了以上 4 个函数,再建一个 keil 工程,添加一个主函数,就可以演示了。 initiate_RS232 (); // 串口初始化 for(i=0;i<128;i++){ #include #include "STC_NEW_8051.H" /// 与 STC12C5A60S2 单片机有关的参数定义 void main() { ADC_result = get_AD_result(7);//P1.7 为 A/D 当前通道, 测量并发送结果 Send_Byte(ADC_result); } } } 在主程序循环中,读取 ADC 数据并发送一次,在串口调试助手(例如 SSCOM)里,设置相关 端口和波特率后,就可以看到 ADC 的结果,如图 1-2 所示。 第 3 页,共 42 页
图 1-2 串口调试 ADC 调试 ADC 还有一种更方便的方法,结果直接在数码管上显示出来,不管你对数码管硬件熟悉不 熟悉,只要使用模板程序提供的数码管驱动函数 led_disp(uint number)即可。 数码管驱动函数 led_disp(unsigned int)。 { void led_disp(uint number) //Mini51板数码管显示函数,传入整数 0~9999 { unsigned char code tab1[20]= {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,}; unsigned char temp,flag=0; if(number < 10000) SEG_B = 0xff;//数码管熄灭 flag = 0; SEG_B = tab1[temp]; flag = 1; SEG_Q = tab1[temp]; flag = 1; SEG_Q = 0xff; //数码管熄灭 flag = 0; temp = number/1000%10; //千位数码管 if (temp) { } else { } temp = number/100%10; //百位数码管 if(flag | temp) } else { } temp = number/10%10; //十位数码管 if(flag | temp) SEG_S = tab1[temp]; else SEG_S = 0xff; // 数码管熄灭 temp = number%10; // 个位数码管 { 第 4 页,共 42 页
SEG_G = tab1[temp]; SEG_Q = 0xbf;//"-" SEG_B = 0xbf; SEG_S = 0xbf; SEG_G = 0xbf; } else { } } 1以上演示源程序 keil 工程请参考附件【串口调试 1】 这里再介绍两款串口绘图软件【MyOsc】和【ComCalWave】,可以直接把串口接收到的数据按 X-Y 轴绘图,显示结果更直观。 主程序这样改: initiate_RS232 (); // 串口初始化 for(i=0;i<128;i++){ #include #include "STC_NEW_8051.H" /// 与 STC12C5A60S2 单片机有关的参数定义 void main() { ADC_result = get_AD_result(7);//P1.7 为 A/D 当前通道, 测量并发送结果 Send_Byte(ADC_result); } } } 运行【MyOsc】,设置串口和波特率后“OPEN”,适当调节输入信号频率后可以看到如图 1-3 所 示的图案。 第 5 页,共 42 页
图 1-3 串口绘图软件示例 运行【ComCalWave】,选择正确的串口号和波特率,“Open COM”,再设置“Wave Show”,看 到了什么?图形!在图形窗口尝试用鼠标右键操作,还可选择特定范围显示,如图 1-4 所示。 图 1-4 串口绘图软件示例 2 要看到以上漂亮的波形,还有一些硬件连接要做,需要将信号发生器和最小系统板上面的 P1.7 口相连接,注意,本 ADC 只能进行 0 到 5V 之间的信号转换,你还需要调整信号发生器,产生满足 条件的信号才行。 1以上演示源程序 keil 工程请参考附件【串口调试 2】 第 6 页,共 42 页
实际在调试程序中,缺少必要硬件设备(如使用普通的 51 单片机且没有 ADC),还可以用正弦表 代替实际 ADC,这里再介绍一款正弦表生成器软件【正弦表发生器】,软件界面如图 1-5 所示。 图 1-5 正弦表发生器 【量化阶数】就是 ADC 位数,例如 STC12C5A60S2ADC 是 10 阶,ADC0809 是 8 阶; 【采样点数】就是在一个正弦周期内,均匀分布多少个采样点,例如在 128 点的 lcd 上显示 2 个以上周期的话,采样点数要小于 64 点,这里选用 30 点数来举例,源程序如下。 0x80,0x9a,0xb4,0xcb,0xdf,0xee,0xf9,0xff,0xff,0xf9, 0xee,0xdf,0xcb,0xb4,0x9a,0x80,0x65,0x4c,0x34,0x21, 0x11,0x6,0x0,0x0,0x6, 0x10,0x20,0x34,0x4b,0x65, #include "mini51b.h" // 所有与硬件相关的接口函数定义 #include "uart.h" unsigned char code dot[30]={ // 正弦表,注意数据类型是“code”,存放在 rom 当中 }; void main() { } unsigned char i; rs232_port_init(); delay_ms(1); while(1) { } for(i=0;i<128;i++) { } uart_put_uchar(dot[i%30]); delay_ms(1);//此处延时当于调节了采样率 用以上调试软件同样可以看到漂亮的正弦信号图形。 以上调试成功后,感觉应该还可以吧,如果你是第一次亲自完成 ADC 将数据采集,再用软件绘 图显示还原信号波形图,一定是一件特别令人激动的事情(Dare to Create and Enjoy)。 第 7 页,共 42 页
2 图形液晶 LCD12864 绘图驱动设计基础(以下均用仿真来代替) 下面我们学习如何在 LCD12864 上显示同样的正弦波形。 关于 LCD 的硬件接口电路,在前面的教程中有详细介绍,涉及单片机总线知识和 CPLD 内部电 路,需要专门学习,这里我们借助现成的驱动函数,重点讲解 LCD 绘图程序设计。 LCD12864 的电路接口在【STC51.h】头文件中定义。 #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 的编程操作都是基于以上接口定义(总线编址)进行的读写操作。 首先来看 LCD 点阵结构图,这里以不带字库的 LCD12864 来讲解,如图 2-1 所示。 图 2-1 LCD 点阵分布结构图 此 LCD 屏由水平 128 列,垂直 64 行组成。水平 128 列分左右各 64 列两个半屏构成。垂直 64 行又分 8 页,每页 8 行(1 列 8 点刚好 1 字节)。程序每次对 LCD 的绘图操作就是以最小单位 1 字 节进行操作的。理解这点至关重要。也就是每次只能针对 8 点进行操作,而不是 1 点进行操作。左 右屏由单独地址线控制(前面的接口定义就是分左右屏定义的)。实际打点只需往指定“位置”写入 数据,“1”亮,“0”暗(在前面的教程里我已经说过,对 LCD 编程实际上就是在对相应的 RAM 单元 进行写“1”操作)。 LCD 驱动函数:忙检测函数 void loop_lcd12864_is_busy(unsigned char right)。 void loop_lcd12864_is_busy(unsigned char right) { 第 8 页,共 42 页
分享到:
收藏