本文作者:Edward
本文已在《无线电》2012年12期发表。转载请注明出处。这里发表的是原文,和杂志上略有不同。
这个制作以前发过一次帖子:《最近完工的智能型ICL7135四位半表头》这次在这里将详细内容发上来,是想在网络上留个印记,方便自己引用,也是希望和大家分享一下。
正文开始:
最近需要一个精度比较高的电压表头,但网上卖的表头多是三位半的,精度不够,也有一些四位半表头但性价比不高。正好手里有几片ICL7135,以前也有做过四位半表头的尝试,而且去年也学习了单片机,于是就有了做一个智能型ICL7135四位半表头的想法。历经2个月的学习、设计和修改,最终做出了这样一款表头。
表头使用ICL7135作为ADC,其是一款高精度的单片4½位ADC,拥有多路复用的BCD输出以及与单片机兼容的控制信号接口,可以很容易的与单片机连接,实现智能化。生产其兼容芯片的厂家有很多,我手里是Maxim和TI生产的。ICL7135应用电路由模拟部分和数字部分组成,表头整体设计思路是:模拟部分采用典型电路和推荐元件参数,数字部分采用单片机提供ICL7135所需时钟、采集输出、驱动数码管。后来,考虑到只做一个简单功能的表头实在是无趣,所以就将单片机的部分IO口和串口引出以便实现与上位机通信或自动控制等功能。
确定好整体思路和预期功能后就开始进行电路设计了。ICL7135模拟部分典型电路如图1所示。
图1 ICL7135电路模拟部分
模拟部分元件没什么可说的,基本都采用推荐值。其中比较关键的是积分电容的选择和基准电压的提供。积分电容对于ICL7135这类双积分ADC是至关重要的,它直接影响积分非线性、翻转、比例误差,即它的好坏直接决定了表头的准确性。而ICL7135对积分电容的要求尤为严格。积分电容必须具有低的电介质吸收特性(亦称浸润或电介质迟滞)。将基准输入(REF)接至IN HI可以检测积分电容介质吸收(此法一般称为自检),良好的积分电容读数将会是9999,与这个读数之间的任何偏差都可能是由于电介质吸收引起的。一般来说,特氟龙(聚四氟乙烯)、聚苯乙烯和聚丙烯的电介质吸收特性低至0.02%,一般陶瓷和聚碳酸酯电容的典型值为0.2%,银云母和钽电容为1.0%-5.0%,铝电解电容高达10%或以上。ICL7135手册上建议积分电容最好使用特氟龙和聚丙烯电容,要求不高时可以使用聚苯乙烯和聚碳酸酯电容。在实际选择时,优质高耐压的CBB21或CBB22电容效果不错,一般自检读数能够达到9996或以上,但是买到能有9999自检读数的电容是挺不容易的。我挑选了几批电容测试,读数从9994-9996都有,就是没有9997以上的,比较遗憾。
开始的时候,想使用LM385作为基准,但是LM385本身参数并不很好,不同厂家的温漂从20~150ppm/℃都有,而且买到正品LM385不太容易。后来在网上搜索了一段时间,发现拆机的Linear公司产电压基准LT1009很好。LT1009是具有0.2%初始精度,15ppm典型温漂,25ppm最大温漂的2.5V并联型基准,它有一个调节引脚,可以在±5%范围内调整输出电压。LT1009的性能超过了一般的LM385,而且拆机件能够保证芯片是真品也廉价。基准电压电路如图2所示,Rref1和Rref2将LT1009输出的电压分压,通过调节电位器Rref3将分压之后的电压调整到准确的1.000V。1.000V电压的微调还有另一种比较常见的方式,即只微调分压电阻,由于种种原因我没选择这种方式。调整电位器要使用如3/8寸Square Trimpot.®微调电位器(3296电位器)等这类精密多圈电位器,以保证精确性和稳定性。
图2 基准电压电路
传统的ICL7135表头很多是使用4MHz晶振经CD4060分频获得125kHz频率,BCD输出用74LS48之类译码,最后再用三极管或达林顿驱动器驱动数码管,电路复杂不说,成本还高。既然要使用单片机,那么就要让其完成所有功能。STC10F04XE是增强型的8051单片机,拥有4kB的ROM,512B的RAM,5kB的EEPROM以及独立波特率发生器, IO口可设置为多种输出模式;可以实现时钟信号、数据读取、数码管驱动、状态指示、按键控制和串口通信等全部功能。其引脚定义如图3所示。
图3 STC10F04XE引脚图
考虑程序的复杂度,决定采用比较简单的读取BCD方式采集ICL7135数据。以前我也尝试使用Busy信号来采集数据,但并不成功,所以这次并不打算采用这种方式。ICL7135输出数据有5位数字,D5-D1端按顺序分别输出高电平脉冲进行不间断的扫描。B8、B4、B2、B1端输出当前位的BCD值。当一次正常的数据转换结束后,D5-D1重新开始不断扫描(超量程时不是)。每次数据转换后的D5-D1的第一遍扫描过程中的每个脉冲的中间,
端都会有一个很短的低电平脉冲,之后直到下次转换结束前都没有额外的脉冲。该脉冲能帮助及时采集ADC结果,防止重复采集,简化程序。上述过程的时序如图4所示。
图4 数字扫描和输出时序
将BCD输出端和D1-D4按顺序连接在单片机的P0口(D5悬空即可),用于采集ICL7135数据,端接到单片机中断0端(P3.2)。单片机的P1.0口是独立波特率发生器的可编程输出时钟端口,用该端口输出稳定的时钟信号给ICL7135。为了良好的抑制50Hz工频干扰,ICL7135的信号积分阶段周期应是工频周期的整数倍,信号积分阶段周期为10000个时钟周期,则最佳时钟频率=50×10000/N,N为整数。所以可选的时钟频率可为100kHz、125kHz等。STC10F04XE是1T单片机,所以可以选择4MHz、6Mhz等较低频率的晶振以减小功耗和干扰,但选择晶振频率要注意保证P1.0口能输出所需的时钟频率。使用6MHz的晶振能产生100kHz、120kHz、125kHz等多种频率,但4MHz晶振无法产生120kHz等频率。本来我是想选择4MHz晶振的,但是考虑到程序中要能设定多种时钟频率,所以最终使用了6MHz晶振。为了能够直接驱动数码管和8个LED,所以选择了这种IO口可设为推挽输出的单片机。数码管各段需要用电阻限流,以防烧坏数码管和单片机。ICL7135的其余端口根据PCB布线的方便性接在单片机的不同IO口上,在P4.0口设置一轻触开关,同时单片机引出串口、中断1、部分IO口以供通信、控制之用。
ICL7135通常需要±5V供电,提供双电源实在是不方便,所以采用手册推荐的ICL7660电荷泵负压电路,简单稳定。供电电路还额外增加了AMS1117-5.0稳压芯片,除使用5V外还可以使用6V-15V电压供电,扩展了供电范围,同时还设计了超压保护和反接保护电路,以保护芯片安全。不要小看这个超压保护和反接保护,在平时做一些实验调试时,各种线会很多很乱,各种电压也会有很多,接错线是很正常的事,如果没有这些保护,接错的后果往往很严重。我在修改测试这个表头的过程中,就有过将13V电源误接入5V供电输入上,幸好当时没偷懒,保护电路也焊上了,不然芯片肯定不保了。
图5 整机电路(清晰大图请下载本贴附件)
整机电路图见图5。将电路画好检查无误之后就开始进行电路板布线了。布线主要看个人的学习和经验了。各元件优先选择贴片元件以减小体积和成本,为了便于更换积分电容,我特意制作了能兼容多种脚距的封装。布线时要注意元件的合理布局,数字模拟要分开,模拟地和数字地要单点接地。如果想使用洞洞板搭建电路,那就要注意不要使用纸基的洞洞板,纸基的洞洞板很容易受潮,漏电大,会严重影响ICL7135模拟电路的工作。我曾经使用过纸基板搭电路,工作很不稳定。这也是我选择PCB打样的原因之一。经历两次打样和修改后,最终的PCB如下:
图6 PCB空板
焊接元件时要注意首先将所有贴片元件焊接好,仔细检查已焊接元件有无错误,LED、1N4148和稳压二极管的极性不能接反。然后将除数码管、ICL7135和排针以外的直插元件焊接好,再次检查已焊接元件有无错误。最后依次焊接ICL7135、数码管和排针,数码管和ICL7135方向一定不要装反。如果想日后能够更换积分电容,可使用28Pin的IC插座来安装ICL7135。元件焊接好后,可以通电试一下机,如果没有下载过程序,LED1-8会不断闪烁。将元件焊接好之后是这样的:
图7 焊接好元件之后
设计工作的最后一步自然是程序设计了。其实程序设计、测试和电路板打样、修改是交互进行的。第一次打样出的PCB很可能有很多问题,如设计不合理等等。在写程序和测试时发现问题就要修改电路板,然后再编程再打样再测试,如此反复,最后设计出成品。可以说,程序是整个作品的灵魂,因为表头的全部功能都体现在程序上,程序的好坏直接决定了作品的成败。我花了很长时间来设计这个程序,也尝试使用了一些以前未曾用过的程序结构,得到了比较好的效果。整个程序最关键的自然是ADC数据的读取和显示。最初我没有使用信号和中断,结果数据读取不及时,有时还会丢字或者是重复读取,而且还不好与显示函数协调。经过反复尝试和研读ICL7135手册,最后利用的电平脉冲信号产生中断来读取和更新数据。读取ADC数据后结束中断返回正常运行,由于读取数据过程很短,能够不影响显示正常的显示、通信和控制,同时也节省了单片机资源,简化了程序。把主要的做好,其他的功能逐步添加就可以了。由于单片机的功能很强,同时也有EEPROM,可以方便的存储数据,所以我设计了一个菜单来方便功能的设置,设置数据存储在EEPROM中实现掉电不丢失。现在功能菜单也有了,就可以完全任凭自己的想象力来发挥了,因为MCU无限可能。我目前实现的功能有数据保持、串口发送测量值设定、工作频率设定、小数点位置设定、菜单超时退出和还原出厂设置等。未来要实现的功能有:开关短按功能设置,电压超阈值报警,平均值计算,差值计算,软件校准,自动控制等等。板子上只设计了一个轻触开关,基本够用了。长按按键进入设置菜单,短按按键数据保持。以后要增加短按功能设置,可以更改短按的功能。这个程序也是我第一次使用定时器来进行按键按检测。我没有使用常用的软件延时和中断检测法。因为单片机要不断扫描数码管,不能打断扫描太长时间,不然数字显示会闪烁或中断。不使用中断的原因是单片机外部中断资源比较有限,我想把中断1端口预留给以后控制使用;另外程序编写得循环很快,每次循环只扫描一个数码管,这样一次循环的时间就很短,不会影响到按键的判断。程序也有向上位机发送测量结果的功能如果设置打开了串口发送数据,那么每次测量结束就会将测量结果以BCD码通过串口发送出去。多参考单片机的数据手册,再开动脑筋就可以做出很多很有意思的功能。编写程序的时候要注意IO口的工作模式,数据接收要设为开漏,数据发送和发送接收口要设为准双向口,数码管段驱动口要根据选取的数码管类型(共阴或共阳)设为推挽或开漏,数码管位驱动口同样,要设为开漏或推挽。
当程序编写调试完成后,在投入使用前最后一个重要工作就是对表头进行校准,以保证测量的准确性。原则上应该使用4½及以上位数的数字电压表或万用表,将其输入端与表头输入端并联,并输入一个1V左右稳定的电压,调节电位器使二者显示一致的方式来调整。但是考虑可能没有4½或以上位数的仪表,也可以用精度良好的3½或3¾位万用表来简单调整。方法是使用万用表200.0mV或400.0mV量程电压档,将其输入端与本机输入端并联,并输入一个100-200mV稳定的电压,调节电位器,调整使二者显示一致即可。
最后就可以将表头投入使用了。由于我没有更好的积分电容,所以表头精度略差,实际满量程误差是正负十几个字左右。整机耗电在30mA左右。目前工作正常,与上位机通信也很稳定。
图8 工作中的表头
电路图大图下载:
单片机的源程序:
4.5Digital Voltmeter MCU Program V1.0.rar
(5.91 KB, 下载次数: 408)
另外再补充一段工作时的视频:(数据保持忘了拍了,是短按按键,蓝色LED会亮。为了方便拍摄,是使用串口控制的表头,如果按键,正常显示时短按就是数据保持,长按就是进入菜单,长按生效时,全部LED都会亮起。)
单片机源程序如下:
- /**************************************************
- 4 1/2 Digital Voltmeter MCU Program
- Using MCU STC10F04XE with 6.000MHz Crystal
- With PCB v1.01
- UART Command
- 0xF1 Key Long
- 0xF2 Ket Short
- 0xF3 Erase EEPROM 0x0800
- 0xF4 Reboot(choosen)
- 0x00 Reboot
- Created By Edward
- Creation Date 14-Apr-2012
- Latest Version 27-May-2012
- Copyright 2012 Edward All rights reserved.
- **************************************************/
- #include
- #include
- /*Declare SFR associated*/
- sfr CLK_DIV = 0x97;//System clock divide
- sfr P4 = 0xC0;//P4 Port
- sfr P4SW = 0xBB;//P4SW Register
- sfr P0M0 = 0x94;//IO Port Type Register
- sfr P0M1 = 0x93;
- sfr P1M0 = 0x92;
- sfr P1M1 = 0x91;
- sfr P2M0 = 0x96;
- sfr P2M1 = 0x95;
- sfr P3M0 = 0xB2;
- sfr P3M1 = 0xB1;
- sfr P4M0 = 0xB4;
- sfr P4M1 = 0xB3;
- sfr AUXR = 0x8E;//Auxiliary register
- sfr BRT = 0x9C;//Baud-Rate Timer register
- sfr WAKE_CLKO=0x8F;//Clock output and Power-down Wakeup Control register
- sfr IAP_CTRL = 0xC7;//ISP/IAP Control register
- /*Declare SFR associated with the IAP */
- sfr IAP_DATA = 0xC2; //Flash data register
- sfr IAP_ADDRH= 0xC3; //Flash address HIGH
- sfr IAP_ADDRL= 0xC4; //Flash address LOW
- sfr IAP_CMD = 0xC5; //Flash command register
- sfr IAP_TRIG = 0xC6; //Flash command trigger
- sfr IAP_CONTR= 0xC7; //Flash control register
- /*Define ISP/IAP/EEPROM command*/
- #define CMD_IDLE 0 //Stand-By
- #define CMD_READ 1 //unsigned char-Read
- #define CMD_PROGRAM 2 //unsigned char-Program
- #define CMD_ERASE 3 //Sector-Erase
- /*Define ISP/IAP/EEPROM operation const for IAP_CONTR*/
- #define ENABLE_IAP 0x84 //SYSCLK<6MHz
- /*Define Which Command that Received to Reboot System*/
- #define REBOOT_CMD 0xF4
- //#define REBOOT_CMD 0x00 //STC-ISP Default Download Command
- /*Define if the Sysytem will Reboot after Sent Command*/
- #define REBOOT_CTRL 1 //Rebot
- //#define REBOOT_CTRL 0 //not reboot
- /*Define the Different ways to reboot*/
- #define REBOOT_WAY 0x60 //From ISP
- //#define REBOOT_WAY 0x20 //
- sbit S1 = P4^0;//Key1
- sbit CLK = P1^0;//ICL7135 Clock
- sbit BSY = P1^1;//ICL7135 BUSY signal
- sbit POL = P1^2;//ICL7135 Polarity output
- sbit RH = P1^3;//ICL7135 Run/Hold
- sbit STB = P3^2;//ICL7135 STROBE signal
- sbit OVR = P1^4;//ICL7135 Over Range signal
- sbit UNR = P1^5;//ICL7135 Under Range signal
- sbit LED = P1^6;//LEDs
- sbit D1 = P3^6;//Seven-Segment LED, the right the first
- sbit D2 = P3^7;
- sbit D3 = P3^5;
- sbit D4 = P3^4;
- sbit D5 = P1^7;
- /*For Seven segment LED display*/
- unsigned char code Led_Dsp[]={
- 0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x27,//0-7
- 0x7F,0x6F,0x77,0x7C,0x58,0x5E,0x79,0x71,//8-F
- 0x39,0x3D,0x74,0x76,0x30,//0x39-C,0x3D-G,0x74-h,0x76-H,0x30-I
- //20
- 0x0E,0x38,0x54,0x5C,0x73,//0x0E-J,0x38-L,0x54-n,0x5C-o,0x73-P
- 0x67,0x50,0x6D,0x78,0x1C,//0x67-q,0x50-r,0x6D-S,0x78-t,0x1C-u
- //30
- 0x3E,0x6E,0x00,0x40,0x0C},//0x3E-U,0x6E-y,0x00-,0x40--,0x0C-
- /*Menu Display data*/
- MenuDat[][6]={
- 0x00,27,14,11,24,29,//rEbot LEDx 0
- 0x01,28,14,23,13,33,//SEnd LED1 1
- 0x03,28, 1,28,14,29,//S1SEt LED1-2 2
- 0x07,13,25,28,14,29,//dpSEt LED1-3 3
- 0x0F,15,26,28,14,29,//FqSEt LED1-4 4
- 0x1F,28,34,28,14,29,//S-SEt LED1-5 5
- 0x3F,26,31,20,29,33,//qUIt LED1-6 6
- 0x7F,27,14,28,14,29,//rESEt LED1-7 7
- 0xAA,33, 0,15,15,33,// OFF LED2,4,6,8 8
- 0x55,33, 0,23,33,33,// On LED1,3,5,7 9
- 0x10,0x20,0x40,0x80,0,0x01,//For LED 10
- 0x11,15,33,33,33, 4,//F 4 LED1,5 11
- 0x22,15,33,33, 1, 0,//F 10 LED2,6
- 0x33,15,33,33, 5, 0,//F 50 LED
- 0x44,15,33, 1, 0, 0,//F 100 LED
- 0x55,15,33, 1, 2, 0,
- 0x66,15,33, 1, 2, 5,
- 0x77,15,33, 1, 5, 0,
- 0x88,15,33, 2, 5, 0,
- 0x99,15,33, 3, 0, 0,
- 0xAA,15,33, 5, 0, 0,
- 0xBB,15,33, 6, 0, 0,
- 0xCC,15, 1, 0, 0, 0,
- 0xDD,15, 1, 5, 0, 0,//F1500 LED 23
- 0xFF,28,30,12,12,14,//SuccE LED1-8 24
- 0xA5,14,27,27,24,27 //Error LED1,3,6,8 25
- };
- unsigned char
- Dat[]={0,1,2,3,4,5,0},//ICL7135 Digital output with Polatity
- //Data formate: POL MSD ... LSD LED
- DatSend[]={0xFF,0xEE,0xEE,0xEE,0x00,0xAA},//Data for send
- //Data format:0xff H=POL/OVR L-MSB+0x0E ... L-LSB CRC8 0xAA
- DatRom[]={0,0,0},//Data for EEPROM
- //Data format H-Send,L-S1;H-Dp,L-CLK;CRC
- Dp,//Decimal point setting,0 is the left
- ADCLK,//ICL7135 Clock Settings
- i,
- countdwn0,//Use for timing or countdown
- sendjud,//Send Data judgment and countdown
- Keyjud,//For key and menu
- dispmod,//Display Mode
- dispcnt,//For display
- S1Func;//S1 Func
- bit
- readjud,//Read ICL7135 data judgment
- KeyScanjud,//Key scan judgment
- KeyLong, //Key
- KeyShort,
- sendctrl,//Send data control
- sndctldsp,//Send data control for display
- readadctrl;//Read AD Status control
- /*---------------------------------------------
- Software Delay Function
- Input char for different delay time
- Output void
- ---------------------------------------------*/
- void Delay(char xx)
- {
- unsigned int y;
- switch (xx)
- {
- case 0: //For Init Function and Key scan
- {
- for(xx=0;xx++;xx<255)
- for(y=0;y++;y<255);
- }break;
- case 1: //For LED Display
- {
- xx=1;
- while(xx--)
- {
- y=65500;
- while(++y);
- }
- }break;
- case 2: //For Reboot
- {
- xx=8;
- while(xx--)
- {
- y=0;
- while(++y);
- }
- }break;
- }
- }
- /*----------------------------
- Disable ISP/IAP/EEPROM function
- Make MCU in a safe state
- ----------------------------*/
- void IapIdle()
- {
- IAP_CONTR = 0; //Close IAP function
- IAP_CMD = 0; //Clear command to standby
- IAP_TRIG = 0; //Clear trigger register
- IAP_ADDRH = 0x80; //Data ptr point to non-EEPROM area
- IAP_ADDRL = 0; //Clear IAP address to prevent misuse
- }
- /*----------------------------
- Read one unsigned char from ISP/IAP/EEPROM area
- Input: addr (ISP/IAP/EEPROM address)
- Output:Flash data
- ----------------------------*/
- unsigned char IapReadByte(unsigned int addr)
- {
- unsigned int dat1; //Data buffer
- IAP_CONTR = ENABLE_IAP; //Open IAP function, and set wait time
- IAP_CMD = CMD_READ; //Set ISP/IAP/EEPROM READ command
- IAP_ADDRL = addr; //Set ISP/IAP/EEPROM address low
- IAP_ADDRH = addr >> 8; //Set ISP/IAP/EEPROM address high
- IAP_TRIG = 0x5a; //Send trigger command1 (0x5a)
- IAP_TRIG = 0xa5; //Send trigger command2 (0xa5)
- _nop_(); //MCU will hold here until ISP/IAP/EEPROM
- //operation complete
- dat1 = IAP_DATA; //Read ISP/IAP/EEPROM data
- IapIdle(); //Close ISP/IAP/EEPROM function
- return dat1; //Return Flash data
- }
- /*----------------------------
- Program one unsigned char to ISP/IAP/EEPROM area
- Input: addr (ISP/IAP/EEPROM address)
- dat (ISP/IAP/EEPROM data)
- Output: void
- ----------------------------*/
- void IapProgramByte(unsigned int addr, unsigned char dat2)
- {
- IAP_CONTR = ENABLE_IAP; //Open IAP function, and set wait time
- IAP_CMD = CMD_PROGRAM; //Set ISP/IAP/EEPROM PROGRAM command
- IAP_ADDRL = addr; //Set ISP/IAP/EEPROM address low
- IAP_ADDRH = addr >> 8; //Set ISP/IAP/EEPROM address high
- IAP_DATA = dat2; //Write ISP/IAP/EEPROM data
- IAP_TRIG = 0x5a; //Send trigger command1 (0x5a)
- IAP_TRIG = 0xa5; //Send trigger command2 (0xa5)
- _nop_(); //MCU will hold here until ISP/IAP/EEPROM
- //operation complete
- IapIdle();
- }
- /*----------------------------
- Erase one sector area
- Input: addr (ISP/IAP/EEPROM address)
- Output:void
- ----------------------------*/
- void IapEraseSector(unsigned int addr)
- {
- IAP_CONTR = ENABLE_IAP; //Open IAP function, and set wait time
- IAP_CMD = CMD_ERASE; //Set ISP/IAP/EEPROM ERASE command
- IAP_ADDRL = addr; //Set ISP/IAP/EEPROM address low
- IAP_ADDRH = addr >> 8; //Set ISP/IAP/EEPROM address high
- IAP_TRIG = 0x5a; //Send trigger command1 (0x5a)
- IAP_TRIG = 0xa5; //Send trigger command2 (0xa5)
- _nop_(); //MCU will hold here until ISP/IAP/EEPROM
- //operation complete
- IapIdle();
- }
- /*----------------------------
- CRC sector area
- Input: *p len
- Output:CRC
- ----------------------------*/
- unsigned char CRC8(unsigned char *ptr,unsigned char len)
- {
- unsigned char crc=0;
- while(len--!=0)
- {
- for(i=0x80;i!=0;i=i>>1)
- {
- if((crc&0x80)!=0)
- {
- crc=crc<<1;
- crc^=0x5E;
- } /* 余式CRC 乘以2 再求CRC */
- else crc=crc<<1;
- if((*ptr&i)!=0)crc^=0x5E; /* 再加上本位的CRC */
- }
- ptr++;
- }
- return crc;
- }
- /*---------------------------------------------
- Reboot Function
- Reboot the system
- Input void
- Output void
- ---------------------------------------------*/
- void Reboot(void)
- {
- P2=0x40;
- D1=D3=D5=D2=D4=0;
- Delay(2);
- IAP_CTRL=REBOOT_WAY;
- }
- /*---------------------------------------------
- Read AD Status Function
- Read Status for ICL7135, then write to Dat[]
- Input unsigned char
- Output void
- ---------------------------------------------*/
- void ReadAD(bit yy)
- {
- if (yy)
- {
- Dat[0]=POL;
- if(BSY)Dat[6]=Dat[6]|0x01;
- else Dat[6]=Dat[6]&0xFE;
- if(OVR) //Over Range
- {
- Dat[6]=Dat[6]|0x10;
- DatSend[1]|=0xE0;
- dispmod=2;
- }
- else
- {
- Dat[6]=Dat[6]&0xEF;
- DatSend[1]&=0x0F;
- dispmod=0;
- }
- if(UNR)Dat[6]=Dat[6]|0x20; //Under Range
- else Dat[6]=Dat[6]&0xDF;
- if(!RH)Dat[6]=Dat[6]|0x02; //Run/Hold
- else Dat[6]=Dat[6]&0xFD;
- }
- }
- /*---------------------------------------------
- Data Convert Function
- Convert ICL7135 output to an int number
- Directly use Dat[]
- Input void
- Output int
- ---------------------------------------------*/
- void DataConvt(void)
- {
- DatSend[1]=Dat[0]<<4;
- DatSend[1]=DatSend[1]+Dat[1]+0x0E;
- if(OVR)DatSend[1]|=0xE0; //Over Range
- DatSend[2]=Dat[2]<<4;
- DatSend[2]=DatSend[2]+Dat[3];
- DatSend[3]=Dat[4]<<4;
- DatSend[3]=DatSend[3]+Dat[5];
- DatSend[4]=CRC8(&DatSend[0],4);
- }
- /*---------------------------------------------
- ICL7135 Clock Set Function
- MCU should run at 6MHz
- Input unsigned char
- Output void
- ---------------------------------------------*/
- void AD_Clock(unsigned char clk)
- {
- AUXR&=0xEF;//Stop the Independent Baud Rate Generator
- AUXR|=0x04;//SysCLK divide by 1
- switch (clk)
- {
- case 1://4kHz
- {
- AUXR&=0xF3;//SysCLK divide by 12
- BRT=0xFA;//250
- }break;
- case 2://10kHz
- {
- AUXR&=0xFB;//SysCLK divide by 12
- BRT=0xE7;//231
- }break;
- case 3:BRT=0xC4;break;// 50kHz 196
- case 4:BRT=0xE2;break;// 100kHz 226
- case 5:BRT=0xE7;break;// 120kHz 231
- case 6:BRT=0xE8;break;// 125kHz 232
- case 7:BRT=0xEC;break;// 150kHz 236
- case 8:BRT=0xF4;break;// 250kHz 244
- case 9:BRT=0xF6;break;// 300kHz 246
- case 10:BRT=0xFA;break;// 500kHz 250
- case 11:BRT=0xFB;break;// 600kHz 251
- case 12:BRT=0xFD;break;//1,000kHz 253
- case 13:BRT=0xFE;break;//1,500kHz 254
- }
- AUXR|=0x10;//Run the Independent Baud Rate Generator
- }
- /*---------------------------------------------
- Display Function
- Dynamic scanning of the digital
- Input char display mode
- Output void
- ---------------------------------------------*/
- void Display(char k)
- {
- D5=D4=D3=D2=D1=LED=1;
- switch (k)
- {
- case 0://Normal display
- {
- P2=Led_Dsp[Dat[dispcnt]];
- switch (dispcnt)
- {
- case 0:
- {
- P2=Dat[6];//LED
- LED=0;
- }break;
- case 1:
- {
- if(!Dat[1])P2=0x00;//The Left Digit
- if(!Dat[0])P2=P2+0x40;
- D5=0;
- }break;
- case 2:D4=0;break;
- case 3:D3=0;break;
- case 4:D2=0;break;
- case 5:D1=0;break;
- case 6:
- {
- P2=0x80; //Decimal point
- switch (Dp)
- {
- case 0:D5=0;break;
- case 1:D4=0;break;
- case 2:D3=0;break;
- case 3:D2=0;break;
- case 4:D1=0;break;
- }
- }break;
- }
- }break;
- case 1://Menu display
- {
- if(countdwn0>80)Reboot();//Quite Menu if timeout
- P2=Led_Dsp[Dat[dispcnt]];
- if(!dispcnt)P2=Dat[0];
- switch (dispcnt)
- {
- case 0:LED=0;break;
- case 1:D5=0;break;
- case 2:D4=0;break;
- case 3:D3=0;break;
- case 4:D2=0;break;
- case 5:D1=0;break;
- case 6:
- {
- P2=0x80; //Decimal point
- switch (Keyjud)
- {
- case 1:D2=0;break;
- case 2:D4=0;break;
- case 3:D4=0;break;
- case 4:D4=0;break;
- case 5:D5=0;break;
- case 6:D2=0;break;
- case 7:D1=0;break;
- case 0xF3:
- {
- switch (Dp)
- {
- case 0:D5=0;break;
- case 1:D4=0;break;
- case 2:D3=0;break;
- case 3:D2=0;break;
- case 4:D1=0;break;
- }
- }break;
- case 0xF4:D5=0;break;
- }
- }break;
- }
- }break;
- case 2://O.L. display
- {
- switch(dispcnt)
- {
- case 1:
- {
- P2=Dat[6];
- LED=0;
- }break;
- case 2:
- {
- P2=Led_Dsp[0]+0x80;
- D2=0;
- }break;
- case 3:
- {
- P2=Led_Dsp[22]+0x80;
- D1=0;
- dispcnt=0;
- }break;
- }
- }break;
- }
- Delay(1);
- dispcnt++;
- if(dispcnt>6)dispcnt=0;
- }
- /*---------------------------------------------
- Display Menu Function
- Write Menu display data to Dat[]
- Input void
- Output void
- ---------------------------------------------*/
- void DispMenu(void)
- {
- if(Keyjud>0&&Keyjud<0x10)for(i=0;i<7;i++)Dat[i]=MenuDat[Keyjud][i];//For Display
- else
- {
- switch (Keyjud)
- {
- case 0xF1://Send or not
- {
- for(i=0;i<7;i++)
- {
- if(sndctldsp)Dat[i]=MenuDat[9][i];
- else Dat[i]=MenuDat[8][i];
- }
- }break;
- case 0xF2:;break;//S1.Set Func is reserved for future use
- case 0xF3://Dp position
- {
- for(i=0;i<7;i++)Dat[i]=33;
- Dat[Dp+1]=35;
- Dat[0]=MenuDat[10][Dp];
- }break;
- case 0xF4:for(i=0;i<7;i++)Dat[i]=MenuDat[ADCLK+10][i];break;//CLK
- }
- }
- }
- /*---------------------------------------------
- Serial communication Function
- Send One Byte Data using UART
- Input unsigned char
- Output void
- ---------------------------------------------*/
- void SendOneByte(unsigned char xx)
- {
- SBUF=xx;
- while(!TI);
- TI=0;
- }
- /*---------------------------------------------
- Data Send Function
- Send AD Data using UART
- Input void
- Output void
- ---------------------------------------------*/
- void SendData(bit y)
- {
- if(y)
- {
- if(!BSY&&sendjud)
- {
- DataConvt();
- SendOneByte(DatSend[sendjud-1]);
- sendjud++; //send only one byte per scan
- if(sendjud>6)sendjud=0;
- }
- if(BSY)sendjud=1;
- if(!RH&&!dispcnt)SendOneByte(0xF0);
- }
- }
- /*---------------------------------------------
- Reset Settings Function
- Just reset the settings in EEPROM
- Input unsigned char MenuDat[?][]
- Output void
- ---------------------------------------------*/
- void Reset(unsigned char y)
- {
- countdwn0=0;
- IapEraseSector(0x0800);
- DatRom[0]=0x01;//Default not Send data, S1Func reserved for future use
- DatRom[1]=0x04;//Default decimal point at the most significant digit
- //ICL7135 default run at 100kHz
- DatRom[2]=CRC8(&DatRom[0],2);
- for(i=0;i<4;i++)IapProgramByte(0x0800+i,DatRom[i]);
- for(i=0;i<7;i++)Dat[i]=MenuDat[y][i];
- Keyjud=7;
- while(countdwn0<20)
- {
- Display(1);
- }
- Reboot();
- }
- /*---------------------------------------------
- Settings Function
- Read, check, Save or reset settings
- or reboot(Quit Menu) the System
- Input unsigned char
- Output void
- ---------------------------------------------*/
- void Settings(unsigned char yy)
- {
- switch (yy)
- {
- case 0://Check settings while Init
- {
- for(i=0;i<3;i++)DatRom[i]=IapReadByte(0x0800+i);
- if(DatRom[2]!=CRC8(&DatRom[0],2))
- {
- Reset(25);
- }
- else
- {
- S1Func=DatRom[0]&0x0F;
- sendctrl=DatRom[0]>>4;
- ADCLK=DatRom[1]&0x0F;
- Dp=DatRom[1]>>4;
- }
- }break;
- case 1://Save settings
- {
- countdwn0=0;
- IapEraseSector(0x0800);
- if(sndctldsp)DatRom[0]=0x10;
- else DatRom[0]=0x00;
- DatRom[0]|=S1Func;
- DatRom[1]=Dp<<4;
- DatRom[1]|=ADCLK;
- DatRom[2]=CRC8(&DatRom[0],2);
- for(i=0;i<4;i++)IapProgramByte(0x0800+i,DatRom[i]);
- for(i=0;i<7;i++)Dat[i]=MenuDat[24][i];
- Keyjud=7;
- while(countdwn0<20)
- {
- Display(1);
- }
- Reboot();
- }break;
- case 2://Quit(Reboot Sysytem)
- {
- Reboot();
- }break;
- case 3://Reset settings
- {
- Reset(24);
- }break;
- }
- }
- /*---------------------------------------------
- Key Scan Function
- Input void
- Output void
- ---------------------------------------------*/
- void KeyScan(void)
- {
- if(!S1)
- {
- Delay(0);
- if(!S1) //>2s Long time
- {
- if(!KeyScanjud)
- {
- countdwn0=0;
- KeyScanjud=1;
- }
- }
- if(countdwn0>12) //Light a LED
- {
- switch (dispmod)
- {
- case 0:Dat[6]=0xFF;break;
- case 1:Dat[0]=0xFF;break;
- }
- }
- }
- else //<2S Short time
- {
- if(KeyScanjud)
- {
- if(countdwn0>12)KeyLong=1;
- else KeyShort=1;
- KeyScanjud=0;
- }
- }
- }
- /*---------------------------------------------
- Key Function
- Input void
- Output void
- ---------------------------------------------*/
- void KeyFunc(void)
- {
- if(KeyLong) //For Long Key
- {
- if(!Keyjud)
- {
- Keyjud=1;
- EX0=0;//STOP read data from AD
- readadctrl=0;//Stop read status from AD
- dispmod=1;//Change Display Mode to Menu
- sndctldsp=sendctrl;
- sendctrl=0;
- }
- else
- {
- if(Keyjud<5)Keyjud|=0xF0;
- else if(Keyjud>0xF0){if(Keyjud<0xF5)Keyjud&=0x0F;}
- else if(Keyjud<0x10)Settings(Keyjud-4);
- if(Keyjud==0xF2)Keyjud=0x02;//S1.Set Func is reserved for future use
- }
- DispMenu();
- KeyLong=0;
- }
-
- if(KeyShort) //For Short Key
- {
- if(!Keyjud)RH=!RH;
- else if(Keyjud<0x10)
- {
- Keyjud++;
- if(Keyjud>7)Keyjud=1;
- }
- else if(Keyjud>0xF0)
- {
- switch (Keyjud)
- {
- case 0xF1:sndctldsp=!sndctldsp;break;//Send dat or not
- case 0xF2:;break;//S1.Set Func is reserved for future use
- case 0xF3:{Dp++;if(Dp>4)Dp=0;}break;//Dp position
- case 0xF4:{ADCLK++;if(ADCLK>13)ADCLK=1;}break;//AD CLK
- }
- }
- DispMenu();
- KeyShort=0;
- }
- }
- /*---------------------------------------------
- Initialization Function
- Input void
- Output void
- ---------------------------------------------*/
- void Init(void)
- {
- CLK_DIV=0x00;//System run at 6MHz
- WAKE_CLKO=0x04;//Set the P1.0 pin as the independent Baud Rate Generator clock output
- P4SW =0x70;//Set P4.4 P4.5 P4.6 to IO Prot
- P0M1 =0xFF;//Set P0 Prot to HZ input
- P0M0 =0x00;
- P1M1 =0XF6;//Set P1.0 P1.3 to IO, P1.6-P1.7 to Open Drain, others to HZ
- P1M0 =0XC0;
- P2M1 =0x00;//Set P2 Port to Pull Push Output
- P2M0 =0xFF;
- P3M1 =0xF4;//Set P3.4-P3.7 to Open Drain, P3.2 to HZ, others to IO
- P3M0 =0xF0;
- P4M1 =0xE6;//Set P1.0 P4.3 P4.4 Prot to IO, others to HZ
- ……………………
- …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码
所有资料51hei提供下载:
|