仿真圖
電路圖
原理圖
PCB圖
實物圖
C程序
1 #include <reg52.h> 2 #include <intrins.h> 3 4 #define uchar unsigned char // 以后unsigned char就可以用uchar代替 5 #define uint unsigned int // 以后unsigned int 就可以用uint 代替 6 7 sfr ISP_DATA = 0xe2; // 數據寄存器 8 sfr ISP_ADDRH = 0xe3; // 地址寄存器高八位 9 sfr ISP_ADDRL = 0xe4; // 地址寄存器低八位 10 sfr ISP_CMD = 0xe5; // 命令寄存器 11 sfr ISP_TRIG = 0xe6; // 命令觸發寄存器 12 sfr ISP_CONTR = 0xe7; // 命令寄存器 13 14 sbit LcdRs_P = P1^1; // 1602液晶的RS管腳 15 sbit LcdRw_P = P1^2; // 1602液晶的RW管腳 16 sbit LcdEn_P = P1^3; // 1602液晶的EN管腳 17 18 sbit Trig1_P = P3^2; // 超聲波模塊1的Trig管腳 19 sbit Echo1_P = P3^3; // 超聲波模塊1的Echo管腳 20 21 sbit KeySet_P = P2^2; // 設置按鍵的管腳 22 sbit KeyDown_P = P2^1; // 減按鍵的管腳 23 sbit KeyUp_P = P2^0; // 加按鍵的管腳 24 25 sbit Buzzer_P = P2^3; // 蜂鳴器的管腳 26 sbit Led1_P = P3^4; // 傳感器1報警燈 27 28 uint gAlarm; // 報警距離變量 29 30 31 32 /*********************************************************/ 33 // 單片機內部EEPROM不使能 34 /*********************************************************/ 35 void ISP_Disable() 36 { 37 ISP_CONTR = 0; 38 ISP_ADDRH = 0; 39 ISP_ADDRL = 0; 40 } 41 42 43 /*********************************************************/ 44 // 從單片機內部EEPROM讀一個字節,從0x2000地址開始 45 /*********************************************************/ 46 unsigned char EEPROM_Read(unsigned int add) 47 { 48 ISP_DATA = 0x00; 49 ISP_CONTR = 0x83; 50 ISP_CMD = 0x01; 51 ISP_ADDRH = (unsigned char)(add>>8); 52 ISP_ADDRL = (unsigned char)(add&0xff); 53 // 對STC89C51系列來說,每次要寫入0x46,再寫入0xB9,ISP/IAP才會生效 54 ISP_TRIG = 0x46; 55 ISP_TRIG = 0xB9; 56 _nop_(); 57 ISP_Disable(); 58 return (ISP_DATA); 59 } 60 61 62 /*********************************************************/ 63 // 往單片機內部EEPROM寫一個字節,從0x2000地址開始 64 /*********************************************************/ 65 void EEPROM_Write(unsigned int add,unsigned char ch) 66 { 67 ISP_CONTR = 0x83; 68 ISP_CMD = 0x02; 69 ISP_ADDRH = (unsigned char)(add>>8); 70 ISP_ADDRL = (unsigned char)(add&0xff); 71 ISP_DATA = ch; 72 ISP_TRIG = 0x46; 73 ISP_TRIG = 0xB9; 74 _nop_(); 75 ISP_Disable(); 76 } 77 78 79 /*********************************************************/ 80 // 擦除單片機內部EEPROM的一個扇區 81 // 寫8個扇區中隨便一個的地址,便擦除該扇區,寫入前要先擦除 82 /*********************************************************/ 83 void Sector_Erase(unsigned int add) 84 { 85 ISP_CONTR = 0x83; 86 ISP_CMD = 0x03; 87 ISP_ADDRH = (unsigned char)(add>>8); 88 ISP_ADDRL = (unsigned char)(add&0xff); 89 ISP_TRIG = 0x46; 90 ISP_TRIG = 0xB9; 91 _nop_(); 92 ISP_Disable(); 93 } 94 95 96 97 /*********************************************************/ 98 // 毫秒級的延時函數,time是要延時的毫秒數 99 /*********************************************************/ 100 void DelayMs(uint time) 101 { 102 uint i,j; 103 for(i=0;i<time;i++) 104 for(j=0;j<112;j++); 105 } 106 107 108 /*********************************************************/ 109 // 1602液晶寫命令函數,cmd就是要寫入的命令 110 /*********************************************************/ 111 void LcdWriteCmd(uchar cmd) 112 { 113 LcdRs_P = 0; 114 LcdRw_P = 0; 115 LcdEn_P = 0; 116 P0=cmd; 117 DelayMs(2); 118 LcdEn_P = 1; 119 DelayMs(2); 120 LcdEn_P = 0; 121 } 122 123 124 /*********************************************************/ 125 // 1602液晶寫數據函數,dat就是要寫入的數據 126 /*********************************************************/ 127 void LcdWriteData(uchar dat) 128 { 129 LcdRs_P = 1; 130 LcdRw_P = 0; 131 LcdEn_P = 0; 132 P0=dat; 133 DelayMs(2); 134 LcdEn_P = 1; 135 DelayMs(2); 136 LcdEn_P = 0; 137 } 138 139 140 /*********************************************************/ 141 // 液晶光標定位函數 142 /*********************************************************/ 143 void LcdGotoXY(uchar line,uchar column) 144 { 145 // 第一行 146 if(line==0) 147 LcdWriteCmd(0x80+column); 148 // 第二行 149 if(line==1) 150 LcdWriteCmd(0x80+0x40+column); 151 } 152 153 154 155 /*********************************************************/ 156 // 液晶輸出字符串函數 157 /*********************************************************/ 158 void LcdPrintStr(uchar *str) 159 { 160 while(*str!='\0') 161 LcdWriteData(*str++); 162 } 163 164 165 /*********************************************************/ 166 // 液晶輸出數字 167 /*********************************************************/ 168 void LcdPrintNum(uint num) 169 { 170 LcdWriteData(num/100+0x30); // 百位 171 LcdWriteData(num%100/10+0x30); // 十位 172 LcdWriteData(num%10+0x30); // 個位 173 } 174 175 176 /*********************************************************/ 177 // 1602液晶功能初始化 178 /*********************************************************/ 179 void LcdInit() 180 { 181 LcdWriteCmd(0x38); // 16*2顯示,5*7點陣,8位數據口 182 LcdWriteCmd(0x0C); // 開顯示,不顯示光標 183 LcdWriteCmd(0x06); // 地址加1,當寫入數據后光標右移 184 LcdWriteCmd(0x01); // 清屏 185 } 186 187 188 189 /*********************************************************/ 190 // 1602液晶顯示內容初始化 191 /*********************************************************/ 192 void LcdShowInit() 193 { 194 LcdGotoXY(0,0); // 定位到第0行第0列 195 LcdPrintStr("D: cm "); // 第0行顯示"D: " 196 } 197 198 199 /*********************************************************/ 200 // 計算傳感器1測量到的距離 201 /*********************************************************/ 202 203 uint GetDistance1(void) 204 { 205 uint ss; // 用於記錄測得的距離 206 207 TH0=0; 208 TL0=0; 209 210 Trig1_P=1; // 給超聲波模塊1一個開始脈沖 211 DelayMs(1); 212 Trig1_P=0; 213 214 while(!Echo1_P); // 等待超聲波模塊1的返回脈沖 215 TR0=1; // 啟動定時器,開始計時 216 while(Echo1_P); // 等待超聲波模塊1的返回脈沖結束 217 TR0=0; // 停止定時器,停止計時 218 219 ss=((TH0*256+TL0)*0.034)/2; // 距離cm=(時間us * 速度cm/us)/2 220 return ss; 221 } 222 223 /*********************************************************/ 224 // 按鍵掃描 225 /*********************************************************/ 226 void KeyScanf() 227 { 228 if(KeySet_P==0) // 判斷是否有按鍵按下 229 { 230 LcdGotoXY(0,0); // 光標定位 231 LcdPrintStr(" Alarm Set "); // 第0行顯示“ Alarm Set ” 232 LcdGotoXY(1,0); // 光標定位 233 LcdPrintStr(" alarm= cm "); // 第1行顯示“ alarm= cm ” 234 LcdGotoXY(1,8); // 光標定位 235 LcdPrintNum(gAlarm); // 顯示當前的報警值 236 237 DelayMs(10); // 消除按鍵按下的抖動 238 while(!KeySet_P); // 等待按鍵釋放 239 DelayMs(10); // 消除按鍵松開的抖動 240 241 while(1) 242 { 243 /* 報警值減的處理 */ 244 if(KeyDown_P==0) 245 { 246 if(gAlarm>2) // 報警值大於2才能減1 247 gAlarm--; // 報警值減1 248 LcdGotoXY(1,8); // 光標定位 249 LcdPrintNum(gAlarm); // 刷新修改后的報警值 250 DelayMs(300); // 延時 251 } 252 253 /* 報警值加的處理 */ 254 if(KeyUp_P==0) 255 { 256 if(gAlarm<400) // 報警值小於400才能加1 257 gAlarm++; // 報警值加1 258 LcdGotoXY(1,8); // 光標定位 259 LcdPrintNum(gAlarm); // 刷新修改后的報警值 260 DelayMs(300); // 延時 261 } 262 263 /* 退出報警值設置 */ 264 if(KeySet_P==0) 265 { 266 break; // 退出while循環 267 } 268 } 269 270 LcdShowInit(); // 液晶恢復測量到測量界面 271 DelayMs(10); // 消除按鍵按下的抖動 272 while(!KeySet_P); // 等待按鍵釋放 273 DelayMs(10); // 消除按鍵松開的抖動 274 275 Sector_Erase(0x2000); // 保存報警距離 276 EEPROM_Write(0x2000,gAlarm/100); 277 EEPROM_Write(0x2001,gAlarm%100); 278 } 279 } 280 281 282 /*********************************************************/ 283 // 傳感器1報警判斷 284 /*********************************************************/ 285 void AlarmJudge1(uint ss) 286 { 287 288 if(ss<gAlarm) // LED燈判斷 289 { 290 Led1_P=0; 291 Buzzer_P=1; 292 DelayMs(10); 293 Buzzer_P=0; 294 DelayMs(10); 295 } 296 else 297 { 298 Led1_P=1; 299 Buzzer_P=0; 300 } 301 302 303 } 304 305 /*********************************************************/ 306 // 報警值初始化 307 /*********************************************************/ 308 void AlarmInit() 309 { 310 gAlarm=EEPROM_Read(0x2000)*100+EEPROM_Read(0x2001); // 從EEPROM讀取報警值 311 312 if((gAlarm==0)||(gAlarm>400)) // 如果讀取到的報警值異常(等於0或大於400則認為異常) 313 { 314 gAlarm=15; // 重新賦值報警值為15 315 } 316 } 317 318 319 /*********************************************************/ 320 // 主函數 321 /*********************************************************/ 322 void main() 323 { 324 uchar i; // 循環變量 325 uint dist; // 保存測量結果 326 327 LcdInit(); // 液晶功能初始化 328 LcdShowInit(); // 液晶顯示內容初始化 329 AlarmInit(); // 報警值初始化 330 331 TMOD = 0x01; // 選擇定時器0,並且確定是工作方式1(為了超聲波模塊測量距離計時用的) 332 333 Trig1_P=0; // 初始化觸發引腳為低電平 334 335 while(1) 336 { 337 /*傳感器1*/ 338 dist=GetDistance1(); // 讀取超聲波模塊1測量到的距離 339 LcdGotoXY(0,7); // 光標定位 340 LcdPrintNum(dist); // 顯示傳感器1測量到的距離 341 AlarmJudge1(dist); // 判斷傳感器1的測量距離是否需要報警 342 343 /*延時並掃描按鍵*/ 344 for(i=0;i<15;i++) 345 { 346 KeyScanf(); 347 DelayMs(10); 348 } 349 350 351 } 352 }