本文作者:Edward
本文已在《無線電》2012年12期發表。轉載請注明出處。這里發表的是原文,和雜志上略有不同。
這個制作以前發過一次帖子:《最近完工的智能型ICL7135四位半表頭》這次在這里將詳細內容發上來,是想在網絡上留個印記,方便自己引用,也是希望和大家分享一下。
正文開始:
最近需要一個精度比較高的電壓表頭,但網上賣的表頭多是三位半的,精度不夠,也有一些四位半表頭但性價比不高。正好手里有幾片ICL7135,以前也有做過四位半表頭的嘗試,而且去年也學習了單片機,于是就有了做一個智能型ICL7135四位半表頭的想法。歷經2個月的學習、設計和修改,最終做出了這樣一款表頭。
表頭使用ICL7135作為ADC,其是一款高精度的單片4½位ADC,擁有多路復用的BCD輸出以及與單片機兼容的控制信號接口,可以很容易的與單片機連接,實現智能化。生產其兼容芯片的廠家有很多,我手里是Maxim和TI生產的。ICL7135應用電路由模擬部分和數字部分組成,表頭整體設計思路是:模擬部分采用典型電路和推薦元件參數,數字部分采用單片機提供ICL7135所需時鐘、采集輸出、驅動數碼管。后來,考慮到只做一個簡單功能的表頭實在是無趣,所以就將單片機的部分IO口和串口引出以便實現與上位機通信或自動控制等功能。
確定好整體思路和預期功能后就開始進行電路設計了。ICL7135模擬部分典型電路如圖1所示。
174436gwdtd7vdgngfgguf.png (45.07 KB, 下載次數: 216)
下載附件
2017-7-28 17:17 上傳
圖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電位器)等這類精密多圈電位器,以保證精確性和穩定性。
174437zt2m1cpo1ichpw0n.png (37.46 KB, 下載次數: 193)
下載附件
2017-7-28 17:17 上傳
圖2 基準電壓電路
傳統的ICL7135表頭很多是使用4MHz晶振經CD4060分頻獲得125kHz頻率,BCD輸出用74LS48之類譯碼,最后再用三極管或達林頓驅動器驅動數碼管,電路復雜不說,成本還高。既然要使用單片機,那么就要讓其完成所有功能。STC10F04XE是增強型的8051單片機,擁有4kB的ROM,512B的RAM,5kB的EEPROM以及獨立波特率發生器, IO口可設置為多種輸出模式;可以實現時鐘信號、數據讀取、數碼管驅動、狀態指示、按鍵控制和串口通信等全部功能。其引腳定義如圖3所示。
174438hqvavmeeppqmhcvy.png (56.01 KB, 下載次數: 193)
下載附件
2017-7-28 17:17 上傳
圖3 STC10F04XE引腳圖
考慮程序的復雜度,決定采用比較簡單的讀取BCD方式采集ICL7135數據。以前我也嘗試使用Busy信號來采集數據,但并不成功,所以這次并不打算采用這種方式。ICL7135輸出數據有5位數字,D5-D1端按順序分別輸出高電平脈沖進行不間斷的掃描。B8、B4、B2、B1端輸出當前位的BCD值。當一次正常的數據轉換結束后,D5-D1重新開始不斷掃描(超量程時不是)。每次數據轉換后的D5-D1的第一遍掃描過程中的每個脈沖的中間,
185631qi2x9x29rkbyirtd.png (2.09 KB, 下載次數: 207)
下載附件
2017-7-28 17:17 上傳
端都會有一個很短的低電平脈沖,之后直到下次轉換結束前都沒有額外的脈沖。該脈沖能幫助及時采集ADC結果,防止重復采集,簡化程序。上述過程的時序如圖4所示。
184151fh5ff9y1yddrn5tp.png (25.52 KB, 下載次數: 196)
下載附件
2017-7-28 17:17 上傳
圖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供電輸入上,幸好當時沒偷懶,保護電路也焊上了,不然芯片肯定不保了。
180239zbusquo43qs22363.png (183.26 KB, 下載次數: 289)
下載附件
2017-7-28 17:17 上傳
圖5 整機電路(清晰大圖請下載本貼附件)
整機電路圖見圖5。將電路畫好檢查無誤之后就開始進行電路板布線了。布線主要看個人的學習和經驗了。各元件優先選擇貼片元件以減小體積和成本,為了便于更換積分電容,我特意制作了能兼容多種腳距的封裝。布線時要注意元件的合理布局,數字模擬要分開,模擬地和數字地要單點接地。如果想使用洞洞板搭建電路,那就要注意不要使用紙基的洞洞板,紙基的洞洞板很容易受潮,漏電大,會嚴重影響ICL7135模擬電路的工作。我曾經使用過紙基板搭電路,工作很不穩定。這也是我選擇PCB打樣的原因之一。經歷兩次打樣和修改后,最終的PCB如下:
174443ff1zkuc8zfkf1yfd.jpg (87.4 KB, 下載次數: 195)
下載附件
2017-7-28 17:17 上傳
174447ms520ujupoqqajq5.jpg (95.31 KB, 下載次數: 187)
下載附件
2017-7-28 17:17 上傳
圖6 PCB空板
焊接元件時要注意首先將所有貼片元件焊接好,仔細檢查已焊接元件有無錯誤,LED、1N4148和穩壓二極管的極性不能接反。然后將除數碼管、ICL7135和排針以外的直插元件焊接好,再次檢查已焊接元件有無錯誤。最后依次焊接ICL7135、數碼管和排針,數碼管和ICL7135方向一定不要裝反。如果想日后能夠更換積分電容,可使用28Pin的IC插座來安裝ICL7135。元件焊接好后,可以通電試一下機,如果沒有下載過程序,LED1-8會不斷閃爍。將元件焊接好之后是這樣的:
174450ebjvb1bdbjau2mzf.jpg (69.35 KB, 下載次數: 189)
下載附件
2017-7-28 17:17 上傳
174453m3rork66ms3srmzt.jpg (76.58 KB, 下載次數: 187)
下載附件
2017-7-28 17:17 上傳
圖7 焊接好元件之后
設計工作的最后一步自然是程序設計了。其實程序設計、測試和電路板打樣、修改是交互進行的。第一次打樣出的PCB很可能有很多問題,如設計不合理等等。在寫程序和測試時發現問題就要修改電路板,然后再編程再打樣再測試,如此反復,最后設計出成品。可以說,程序是整個作品的靈魂,因為表頭的全部功能都體現在程序上,程序的好壞直接決定了作品的成敗。我花了很長時間來設計這個程序,也嘗試使用了一些以前未曾用過的程序結構,得到了比較好的效果。整個程序最關鍵的自然是ADC數據的讀取和顯示。最初我沒有使用信號和中斷,結果數據讀取不及時,有時還會丟字或者是重復讀取,而且還不好與顯示函數協調。經過反復嘗試和研讀ICL7135手冊,最后利用的電平脈沖信號產生中斷來讀取和更新數據。讀取ADC數據后結束中斷返回正常運行,由于讀取數據過程很短,能夠不影響顯示正常的顯示、通信和控制,同時也節省了單片機資源,簡化了程序。把主要的做好,其他的功能逐步添加就可以了。由于單片機的功能很強,同時也有EEPROM,可以方便的存儲數據,所以我設計了一個菜單來方便功能的設置,設置數據存儲在EEPROM中實現掉電不丟失。現在功能菜單也有了,就可以完全任憑自己的想象力來發揮了,因為MCU無限可能。我目前實現的功能有數據保持、串口發送測量值設定、工作頻率設定、小數點位置設定、菜單超時退出和還原出廠設置等。未來要實現的功能有:開關短按功能設置,電壓超閾值報警,平均值計算,差值計算,軟件校準,自動控制等等。板子上只設計了一個輕觸開關,基本夠用了。長按按鍵進入設置菜單,短按按鍵數據保持。以后要增加短按功能設置,可以更改短按的功能。這個程序也是我第一次使用定時器來進行按鍵按檢測。我沒有使用常用的軟件延時和中斷檢測法。因為單片機要不斷掃描數碼管,不能打斷掃描太長時間,不然數字顯示會閃爍或中斷。不使用中斷的原因是單片機外部中斷資源比較有限,我想把中斷1端口預留給以后控制使用;另外程序編寫得循環很快,每次循環只掃描一個數碼管,這樣一次循環的時間就很短,不會影響到按鍵的判斷。程序也有向上位機發送測量結果的功能如果設置打開了串口發送數據,那么每次測量結束就會將測量結果以BCD碼通過串口發送出去。多參考單片機的數據手冊,再開動腦筋就可以做出很多很有意思的功能。編寫程序的時候要注意IO口的工作模式,數據接收要設為開漏,數據發送和發送接收口要設為準雙向口,數碼管段驅動口要根據選取的數碼管類型(共陰或共陽)設為推挽或開漏,數碼管位驅動口同樣,要設為開漏或推挽。
當程序編寫調試完成后,在投入使用前最后一個重要工作就是對表頭進行校準,以保證測量的準確性。原則上應該使用4½及以上位數的數字電壓表或萬用表,將其輸入端與表頭輸入端并聯,并輸入一個1V左右穩定的電壓,調節電位器使二者顯示一致的方式來調整。但是考慮可能沒有4½或以上位數的儀表,也可以用精度良好的3½或3¾位萬用表來簡單調整。方法是使用萬用表200.0mV或400.0mV量程電壓檔,將其輸入端與本機輸入端并聯,并輸入一個100-200mV穩定的電壓,調節電位器,調整使二者顯示一致即可。
最后就可以將表頭投入使用了。由于我沒有更好的積分電容,所以表頭精度略差,實際滿量程誤差是正負十幾個字左右。整機耗電在30mA左右。目前工作正常,與上位機通信也很穩定。
174457uuu5r0i2iuquraro.jpg (88.56 KB, 下載次數: 184)
下載附件
2017-7-28 17:17 上傳
圖8 工作中的表頭
電路圖大圖下載:
05.png (778.67 KB, 下載次數: 275)
下載附件
2017-7-28 17:16 上傳
單片機的源程序:
4.5Digital Voltmeter MCU Program V1.0.rar
(5.91 KB, 下載次數: 408)
2017-7-28 17:16 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
另外再補充一段工作時的視頻:(數據保持忘了拍了,是短按按鍵,藍色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提供下載:
|