1. 任務需求
- 量程:直流電壓0~20V
- 三檔:0~200mV,200mV~2V,2V~20V
- 精度:0.01,顯示穩定,無閃爍
- 誤差:0.2V擋位≤10%,2V和20V擋≤1%
2. 需求分析
- 直流電壓表(0~20V):利用數碼管,通過數字方式顯示測定直流電壓值,其范圍為0~20V
- 自動量程轉換:0~20V直流電壓分為三檔:0~0.2V,0.2V~2V,2V~20V。且能根據0~20V測試電壓的具體值來自動切換量程
3. 設計方案
3.1. 總概述
首先,我們了解到測試電壓為0~20V的直流電壓。現我們使用的ADC0809芯片只能處理0~5V電壓。故,我們先需要將0~20V電壓5分壓至0~4V,即滿足了ADC0809芯片處理范圍。然后STC89C52接收到數字量后,在程序中判斷當前量程是否合適。若是,則將電壓值輸出到數碼管上顯示。否,則反饋調節數據選擇器的輸出口去調節擋位,直到量程合適。總體設計圖如3-1
圖3-1
3.2. 電壓放大部分
這一部分屬於信號預處理部分,實際上電壓表的誤差主要出現在這里。首先,我了解到ADC0809為八位A/D轉換芯片,量化模擬電壓值范圍為0~5V。我們測定電壓范圍為0~20V,故我首先要將其5分壓至0~4V以便滿足其需求。而為了精確測定,我需要將5分壓后較小的兩個量程0~0.2、0.2~2分別放大100倍、10倍。此處,我借助同相比例放大器。三個擋位增益由小到大根據圖3-2分別為1、1+R10/R9、1+R11/R9。同時,為了滿足自動轉換這一需求,我借助74HC4051芯片——八選一數據選擇器。將其數據地址口A、B分別與單片機P1.4、P1.5相連。在單片機內部程序中,根據ADC0809送入數字量判斷當前量程是否合適,若不合適改變數據選擇器的地址輸入,即可完成自動量程轉換這一需求。
圖3-2
3.3. 模數轉換部分
主要元器件為ADC0809與74LS74。這一部分屬於整個電路中最最為關鍵的部分,但是實際上設計與操作來說,並不是特別復雜。因為ADC0809芯片集成度很高,我只需要將其啟動轉換端口,轉換完成標志端口等將其與單片機STC89C52相連接,並且在程序內編寫相關語句就可以完成對ADC0809芯片的控制,即可完成A/D轉換的處理。而74LS74芯片是一個雙D觸發器。由於ADC0809內部並沒有晶振電路,所以,其需要時鍾信號,並且要求范圍在0~640Khz之間。這次我使用的STC89C52芯片晶振頻率為6Mhz,其ALE口輸出為1Mhz,故使用D觸發器進行二分頻可得到500Khz時鍾信號,即完成ADC0809芯片的正常運轉。
圖3-3
3.4. 數碼管顯示部分
在位選部分我選擇了P0口,值得注意的一點是:P0口在作為通用I/O使用時,需要接入上拉電阻並且手動先置高電平。並且,我使用了共陰極七段數碼管,所以在位選個個端口要接入非門。在段選部分,我使用P2.0~3,使用了4511譯碼器,即可完成段選數據的輸入。小數點則單獨使用了P2.7口,在單片機程序內判斷是否點亮它。
圖3-4
4. 仿真
2~20V:測試用量5V
0.2~2V:測試用量1.5V
0~0.2:測試用量0.1V
5. 調試與測試
5.1. 幾個問題
問題一:如何降低同相比例放大器處的誤差?(此處誤差為電壓表誤差最主要之處)
問題二:如何保證數碼管處焊接無錯誤(此處為電路中最難焊接之處)
問題三:如何確保ADC0809芯片處於正常工作狀態
5.2. 問題分析
問題一:由於定值電阻並不是完全准確,所以在同相比例放大器處放大倍數並不是在仿真中那樣完全理想。故會產生較大的誤差。
問題二:數碼管處在段選部分,我們使用單個I/O控制,故此我們需要將每一個數碼管對應的引腳並連。線路並不復雜,但極易出現短路、短路、鏈接錯誤等情況。
問題三:ADC0809正常工作有比較多的需求。首先,需要有0~640kHZ的時鍾信號,其次,單片機內部程序要對其正確的操作。
5.3. 解決方法
問題一:采用電位器,在整個電路焊接好之后,調節電位器來減小誤差。
問題二:編寫測試文件,即單片機程序送出一個固定的數字,若數碼管顯示數字及小數點符合預期要求,即數碼管處連接正確。反之,則有錯誤。
問題三:實際上來說這個問題並不能直接調試,因為假定寫入完整的程序,數碼管是固定的數字,並無法保證是其出錯。我的方法是:寫入完整的程序后,給定1V測試電壓。在預期方案中1V屬於0.2~2V量程,則此刻4051選擇通道2,則此時其數據地址A為高,B為低。若是如此,則ADC0809完成了正常的數據采集操作,因為A之所以為高,B之所以為低是單片機程序作用的結果,而單片機只有接收到了正確的值才會做這個操作。故反推ADC0809是正常工作的。
6. 參考文獻
汪文、陳林. 單片機原理及應用[M]. 湖北:華中科技大學出版社.
7. 附錄1(測量數據)
8. 附錄2(源碼)
1 #include<reg52.h> 2 sbit start = P1^0; //啟動A/D轉換 3 sbit eoc = P1^1; //轉換結束信號輸出端 4 sbit oe = P1^2; //輸出允許 5 sbit ale = P1^3; //地址鎖存允許 6 7 //自動擋位選擇輸入 8 sbit a = P1^4; 9 sbit b = P1^5; 10 11 sbit point = P2^7; 12 13 14 unsigned int W_temp_data[4] = {0x08, 0x04, 0x02, 0x01};//位選數據 15 unsigned int D_temp_data[8] = {0,0,0,0,0,0,0,0}; //段選數據 16 17 unsigned long int temp_data = 0; //ADC0809輸出數字量 18 19 /********************* 20 * 軟件延時 * 21 *********************/ 22 void deley(unsigned int x) 23 { 24 unsigned int i,j; 25 for(i=0; i<x; i++) 26 { 27 for(j=0; j<100; j++) 28 { 29 ; 30 } 31 } 32 } 33 34 /********************* 35 * 顯示函數 * 36 *********************/ 37 void display(unsigned int x) 38 { 39 unsigned int i, j; 40 for(j = 0; temp_data ; j ++) 41 //將數字量每一位取出放入D_temp_data數組中 42 { 43 D_temp_data[j] = temp_data % 10; 44 temp_data = temp_data / 10; 45 } 46 for(i = 0; i < 4; i ++) 47 { 48 49 //前導零消除 50 if(x == 0 && i == 3 && D_temp_data[i] == 0) 51 { 52 break; 53 } 54 if(x == 2) 55 { 56 if(D_temp_data[i] == 0 && i == 2 && D_temp_data[3] == 0) 57 { 58 break; 59 } 60 if(D_temp_data[i] == 0 && i == 3) 61 { 62 break; 63 } 64 } 65 P0 = W_temp_data[i]; 66 P2 = D_temp_data[i]; 67 //小數點顯示 68 if(x == 0) 69 { 70 if(i == 2) 71 { 72 point = 1; 73 } 74 } 75 else if(x == 1) 76 { 77 if(i == 3) 78 { 79 point = 1; 80 } 81 } 82 else if(x == 2) 83 { 84 if(i == 1) 85 { 86 point = 1; 87 } 88 } 89 deley(1); 90 } 91 } 92 93 /********************* 94 * 擋位選擇 * 95 * x: 0 *1擋 * 96 * 1 *10擋 * 97 * 2 *100擋 * 98 *********************/ 99 void choose(unsigned int x) 100 { 101 switch(x) 102 { 103 case 0: 104 a = 0; 105 b = 0; 106 break; 107 case 1: 108 a = 1; 109 b = 0; 110 break; 111 case 2: 112 a = 0; 113 b = 1; 114 break; 115 default: 116 ; 117 } 118 } 119 120 void main() 121 { 122 unsigned int x = 0; //量程控制 123 while(1) 124 { 125 ale = 1; 126 deley(1); 127 ale = 0; 128 choose(x); 129 start = 1; 130 deley(1); 131 start = 0; //下降沿啟動A/D轉換 132 while(eoc == 0) 133 //轉換結束 134 { 135 ; 136 } 137 oe = 1; //輸出允許 138 temp_data = P3; 139 oe = 0; //輸出阻塞 140 if(x == 0 && temp_data < 21) 141 //量程過高,切換至0.2-2V 142 { 143 x = 1; 144 continue; 145 } 146 if(x == 1 && temp_data < 21) 147 { 148 //量程過高,切換至0-0.2V 149 x = 2; 150 continue; 151 } 152 if(x ==1 && temp_data > 204) 153 //量程過低,切換至2-20V 154 { 155 x = 0; 156 continue; 157 } 158 if(x == 2 && temp_data > 204) 159 //量程過低,切換至0.2-2V 160 { 161 x = 1; 162 continue; 163 } 164 temp_data = temp_data * 100 * 5 /51; 165 display(x); 166 } 167 }