在數邏的課程中,已經學習過AD轉換的概念:將模擬信號采樣、量化、編碼后轉換為數字信號。但是未學習過通過單片機編程,顯示結果。
編碼分有舍有入、只舍不入兩種,量化誤差前者更小。=2Vm/(2^n+1 - 1 )
注意,為了達到精確度高、穩定性好的目的,最好將所有器件的模擬地和數字分別連接,最后將模擬地和數字地僅在一點相連。
此處,使用的是STC12C5A60S2內部的AD轉換。
1 /* 功能:使用12C5A60S2內部AD讀取外部電壓,顯示在1602上 */ 2 3 #include "STC12C5A60S2.H" 4 #include <intrins.h> 5 sbit RS = P2^6; //1602定義口 //HZ:EN=P2.2 RS=P2.0 RW=P2.1 6 sbit RW = P2^5; 7 sbit EN = P2^7; 8 #define uchar unsigned char; 9 #define uint unsigned int; 10 11 #define RS_CLR RS=0 12 #define RS_SET RS=1 13 #define RW_CLR RW=0 14 #define RW_SET RW=1 15 #define EN_CLR EN=0 16 #define EN_SET EN=1 17 18 #define DataPort P0 //連接1602數據口 P0 19 20 uchar da1=0,da2=0,da3=0; 21 double Data,c; 22 char a[5]=""; 23 uchar ADC_Chanul_Turn=0; 24 25 26 void DelayUs2x(unsigned char t) 27 { 28 while(--t); 29 } 30 31 32 void DelayMs(unsigned char t) 33 { 34 35 while(t--) 36 { 37 //大致延時1mS 38 DelayUs2x(245); 39 DelayUs2x(245); 40 } 41 } 42 /*------------------------------------------------ 43 判忙函數 44 ------------------------------------------------*/ 45 bit LCD_Check_Busy(void) 46 { 47 DataPort= 0xFF; 48 RS_CLR; 49 RW_SET; 50 EN_CLR; 51 _nop_(); 52 EN_SET; 53 return (bit)(DataPort & 0x80); 54 } 55 /*--------- 56 ------------------------------------------------*/ 57 void LCD_Write_Com(unsigned char com) 58 { 59 while(LCD_Check_Busy()); //忙則等待 60 RS_CLR; 61 RW_CLR; 62 EN_SET; 63 DataPort= com; // 64 _nop_(); 65 EN_CLR; 66 } 67 /*------------------------------------------------ 68 寫入數據函數 69 ------------------------------------------------*/ 70 void LCD_Write_Data(unsigned char Data) 71 { 72 while(LCD_Check_Busy()); //忙則等待 73 RS_SET; 74 RW_CLR; 75 EN_SET; 76 DataPort= Data; 77 _nop_(); 78 EN_CLR; 79 } 80 81 /*------------------------------------------------ 82 清屏函數 83 ------------------------------------------------*/ 84 void LCD_Clear(void) 85 { 86 LCD_Write_Com(0x01); 87 DelayMs(5); 88 } 89 /*------------------------------------------------ 90 寫入字符串函數 91 ------------------------------------------------*/ 92 void LCD_Write_String(unsigned char x,unsigned char y,unsigned char *s)//y為行數,x為橫坐標,最后一個是字符 93 { 94 if (y == 0) 95 { 96 LCD_Write_Com(0x80 + x); //表示第一行 97 } 98 else 99 { 100 LCD_Write_Com(0xC0 + x); //表示第二行 101 } 102 while (*s) 103 { 104 LCD_Write_Data( *s); 105 s ++; 106 } 107 } 108 /*------------------------------------------------ 109 寫入字符函數 110 ------------------------------------------------*/ 111 void LCD_Write_Char(unsigned char x,unsigned char y,unsigned char Data) 112 { 113 if (y == 0) 114 { 115 LCD_Write_Com(0x80 + x); 116 } 117 else 118 { 119 LCD_Write_Com(0xC0 + x); 120 } 121 LCD_Write_Data( Data); 122 } 123 /*------------------------------------------------ 124 初始化函數 125 ------------------------------------------------*/ 126 void LCD_Init(void) 127 { 128 LCD_Write_Com(0x38); /*顯示模式設置*/ 129 DelayMs(5); 130 LCD_Write_Com(0x38); 131 DelayMs(5); 132 LCD_Write_Com(0x38); 133 DelayMs(5); 134 LCD_Write_Com(0x38); 135 LCD_Write_Com(0x08); /*顯示關閉*/ 136 LCD_Write_Com(0x01); /*顯示清屏*/ 137 LCD_Write_Com(0x06); /*顯示光標移動設置*/ 138 DelayMs(5); 139 LCD_Write_Com(0x0C); /*顯示開及光標設置*/ 140 } 141 142 /*------------------------------------------------ 143 ADC函數 144 ------------------------------------------------*/ 145 void InitADC()//初始AD寄存器 146 { 147 P1ASF=0x03; //0xff設置P1口全部為ADC通道,P10 P11為輸出口 148 ADC_RES=0x00; //清除高8位緩沖數據 149 if(ADC_Chanul_Turn%2==0) 150 { 151 ADC_CONTR=0xF0; //P10口 152 _nop_(); 153 _nop_(); 154 _nop_(); 155 _nop_(); 156 ADC_CONTR=0xE8; 157 158 } 159 if(ADC_Chanul_Turn%2==1) //P11口 160 { 161 ADC_CONTR=0xF1; 162 _nop_(); 163 _nop_(); 164 _nop_(); 165 _nop_(); 166 ADC_CONTR=0xE9; 167 } 168 } 169 170 void timer0() interrupt 1 //interrupt 1: 定時器0,interrupt3:定時器3 171 { 172 TH0=(65536-20000)/256; //高八位,(需要表示Xms的定時,計數器由65536-X數到65536,由於16位,只能分高低位) 173 TL0=(65536-20000)%256; //低八位 174 InitADC(); 175 176 } 177 178 void adc_isr() interrupt 5 //FLAG標志位置位觸發中斷,沒有設優先級,但是同優先級下定時器0更高 179 { 180 181 //V_5REF=V_1REF*256/da_ref; 182 if(ADC_Chanul_Turn%3==0) //外部基准電壓 183 { 184 da1=ADC_RES; //獲取轉換結果 185 Data=((double)da1/256)*5; //取八位計算基准電壓Data, 186 c =Data; 187 } 188 if(ADC_Chanul_Turn%3==1) 189 { 190 da2=ADC_RES; //獲取轉換結果 191 Data=((double)da2/256)*5; //取八位計算實際值Data, 192 c =Data; 193 } 194 if(ADC_Chanul_Turn%3==2) 195 { 196 da3=ADC_RES; //獲取轉換結果 197 Data=((double)da3/256)*5; //取八位計算實際值Data, 198 c =Data; 199 } 200 201 a[0]=((int)c%10+0x30);//個位(電壓<5,僅有個) //0x30: ASCAI碼里代表“0”,必須轉換成字符存在字符型數組里才可以在1602液晶屏上顯示 202 a[1]=0x2e; //小數點 203 a[2]=((int)(c*10)%10+0x30); // 十分位 204 a[3]=((int)(c*100)%10+0x30);// 百分位 205 a[4]='\0'; // 加了串尾符才成了字符串哦 206 207 if(ADC_Chanul_Turn%3==0) LCD_Write_String(0,0,a); 208 if(ADC_Chanul_Turn%3==1) LCD_Write_String(5,0,a); 209 if(ADC_Chanul_Turn%3==2) LCD_Write_String(0,1,a); 210 211 ADC_CONTR&=0xEF; //標志位清零 212 213 ADC_Chanul_Turn++; 214 if(ADC_Chanul_Turn==252) 215 ADC_Chanul_Turn=0; 216 } 217 void main() 218 { 219 LCD_Init(); 220 LCD_Clear(); //清屏 221 DelayMs(255); 222 TH0=(65536-20000)/256; //開定時器0 223 TL0=(65536-20000)%256; 224 EA=1; //開全局中斷 225 ET0=1; //允許定時器零中斷 226 EADC=1; //允許ADC中斷 227 TR0=1; 228 while(1); 229 }