藍僑杯單片機編程筆記 一、 IO口編程 二、 數碼管動態掃描和定時器 三、 矩陣鍵盤 四、 串口通訊和串口中斷 五、 外部中斷的使用 六、 實時時鐘DS1302的使用 七、 PCF8591與IIC總線的使用 八、 DS18B20溫度芯片的使用 九、 超聲波傳感器的使用 十、 步進電機與直流電機的使用 十一、 擴展:宏定義編程方法(推薦) 十二、 注意事項(常見編程錯誤) IO編程,該開發板使用了573鎖存器,通過P2口的5,6,7位連接3-8譯碼器,擴展出了8個口,其中4個口分別連接4個573鎖存器,這里以LED的鎖存器來舉例: 原理圖573: 分析代碼: P2=((P2&0x1f)|0x80); 其中0x1f=0001 1111,P2與0x1f進行與運算,高三位清零,其余位保持原來狀態,不改變,即把控制3-8譯碼器的高三位留出來: 接著再或上0x80;容易發現0x80=1000 0000;或運算,與1或結果為1,與0或結果不變,所以或上0x80只需看P2的高三位,則高三位為100,對應3-8譯碼器的話,P2^7=1;P2^6=0;P2^5=0; 所以輸出Y4=0;Y4再經過與非運算,看下圖示: 則輸出Y4C=1;即LED對應的鎖存器的片選信號被選中,鎖存器打通,接下來就可以對P0口進行操作,操作完之后, P2=P2&0x1f;P2高三位直接清零,此時Y4C=0,則把鎖存器鎖上了。 類似的方法,數碼管、蜂鳴器等都是如此操作, 選中鎖存器代碼: P2=((P2&0x1f)|(這里填對應鎖存器的位移號))。 數碼管顯示分為段選和位選, 數碼管定義和顯示函數: code unsigned char tab[] = { 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; unsigned char dspbuf[]={10,10,10,10,10,10,10,10}; unsigned char dspcom=0; void display() { //段選,消隱 P2=((P2&0x1f)|0xe0); P0=0xff; P2=P2&0x1f; //位選 P2=((P2&0x1f)|0xc0); P0=(1<<dspcom); P2=P2&0x1f; //段碼輸入 P2=((P2&0x1f)|0xe0); P0=tab[dspbuf[dspcom]]; P2=P2&0x1f; if(++dspcom==8) dspcom=0; }注意:這里1左移dspcom位,剛開始dspcom=0,則1左移dspcom位依舊為1,接著dspcom每次自增1,1對應二進制0000 0001,即把1每次向左移,每次都比上一次多移一位,直至8位移完,對應8個數碼管。 定時器配置: 這里只需記住定時器的配置,知道怎么使用就可以了。首先有兩個定時器,T0和T1,(也有的單片機有T2),定時器有4種工作方式0,1,2,3;其中最常用的是方式1(16位),其次是方式2(8位自動重裝,串口通訊中斷會用到)。 定時器需要配置:TMOD |=0x01;配置成使用定時器0,工作方式為1;同理使用定時器1工作方式1:TMOD |=0x10;則同時使用兩個定時器且工作方式為1,那么可以:TMOD |=0x11; 定時器1配置成工作方式2:TMOD |=0x20; 接著配置(以定時器0舉例): TH0=(65535-2000)/256;//配置初值 TL0=(65535-2000)%256; ET0=1; TR0=1;//定時0中斷 EA =1;//總中斷 定時器1也是同理的,只不過0要改成1. 接著定時中斷函數和優先級: 定時器0 void isr_timer_0(void) interrupt 1 //默認中斷優先級 1 { TH0 = (65536-2000)/256; TL0 = (65536-2000)%256; //定時器重載 display(); } 定時器1: void isr_timer_1(void) interrupt 3 //默認中斷優先級 3 { TH0 = (65536-2000)/256; TL0 = (65536-2000)%256; //定時器重載 display(); } 注意:定時器0優先級為1,定時器1為3,串口中斷優先級為4,總共有5個中斷源,后面還會介紹外部中斷和串口中斷。 數碼管動態掃描,顯示函數放在定時中斷函數里面,2ms掃一次是最穩定的!! 矩陣鍵盤需要死記了!這里不再講獨立鍵盤。 第二種單片機鍵盤掃描代碼(沒有消抖): sfr P4^4=0xC0; //鍵盤定義 sbit r1=P3^0; //4行 sbit r2=P3^1; sbit r3=P3^2; sbit r4=P3^3; //4列 sbit c1=P4^4; sbit c2=P4^2; sbit c3=P3^5; sbit c4=P3^4; //讀取矩陣鍵盤鍵值 unsigned char key_scan() { unsigned char key_value; r1=0; r2=r3=r4=1; c1=c2=c3=c4=1; if(!c1) key_value=0; else if(!c2) key_value=1; else if(!c3) key_value=2; else if(!c4) key_value=3; r2=0; r1=r3=r4=1; c1=c2=c3=c4=1; if(!c1) key_value=4; else if(!c2) key_value=5; else if(!c3) key_value=6; else if(!c4) key_value=7; r3=0; r2=r1=r4=1; c1=c2=c3=c4=1; if(!c1) key_value=8; else if(!c2) key_value=9; else if(!c3) key_value=10; else if(!c4) key_value=11; r4=0; r2=r3=r1=1; c1=c2=c3=c4=1; if(!c1) key_value=12; else if(!c2) key_value=13; else if(!c3) key_value=14; else if(!c4) key_value=15; return key_value; } 串口中斷配置只需記住幾個寄存器就行了, 初始化: SCON =0x50; //串口配置成模式1 TMOD |=0x20;//定時器1,方式2,8位自動重裝 TH1=256-(unsigbedchar)(SYSTEMCLOK/BAUDRATE/384+0.5);//定時初值 ES=1; //串口中斷打開 TR1=1; //啟動定時器1 EA=1; //總中斷打開 這里必須使用定時器1,不能用定時器0. 下面是模塊化的函數: void Uart_Init() { SCON = 0x50; TMOD |=0x20; TH1=256-(SYSREMCLOCK/BAUDRATE/384+0.5); ES=1; TR1=1; EA=1; } void UartSend(unsigned char*pBuff,int length) { unsigned char c; int i=0; for(i=0;i<length;i++) { c=pBuff[ i]; SBUF=c; while(TI==0); TI=0; } } 接收數據可以這樣寫: 定義全局變量: unsigned char uart_buf[100];//串口緩沖區 unsigned int uart_Count=0;//串口數據長度 void uart_inte() interrupt 4 { unsigned char c; if(RI) { RI=0; c=SBUF; uart_buf[uart_Count]=c; uart_Count++; } } 如果可以指定的接收,可以這樣寫 //串口中斷服務函數 void isr_uart(void) interrupt 4{ if(RI){ RI = 0; //清除接收標志位 rxbuf[rxcnt] = SBUF; if(rxbuf[rxcnt] == '\n'){ rxcnt = 0; rx_over = 1; ES = 0; //回車為接收結束標志,檢測到回車符后,關閉串口中斷 } else{ rxcnt++; } } } 當接收完一幀數據時關閉串口中斷,設一個標志位,處理完之后再打開。 #include "reg51.h" #include "intrins.h" typedef unsigned char BYTE; typedef unsigned int WORD; BYTE code_tab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff}; char arry[10]="I CAN PLAY"; unsigned char x; #define FOSC 11059200//12000000L //系統頻率 #define BAUD 115200 //串口波特率 #define NONE_PARITY 0 //無校驗 #define ODD_PARITY 1 //奇校驗 #define EVEN_PARITY 2 //偶校驗 #define MARK_PARITY 3 //標記校驗 #define SPACE_PARITY 4 //空白校驗 #define PARITYBIT NONE_PARITY //定義校驗位 sfr AUXR = 0x8e; //輔助寄存器 sfr P_SW1 = 0xA2; //外設功能切換寄存器1 #define S1_S0 0x40 //P_SW1.6 #define S1_S1 0x80 //P_SW1.7 sbit P22 = P2^2; bit busy; void SendData(BYTE dat); void SendString(char *s); void main() { ACC = P_SW1; ACC &= ~(S1_S0 | S1_S1); //S1_S0=0 S1_S1=0 P_SW1 = ACC; //(P3.0/RxD, P3.1/TxD) // ACC = P_SW1; // ACC &= ~(S1_S0 | S1_S1); //S1_S0=1 S1_S1=0 // ACC |= S1_S0; //(P3.6/RxD_2, P3.7/TxD_2) // P_SW1 = ACC; // // ACC = P_SW1; // ACC &= ~(S1_S0 | S1_S1); //S1_S0=0 S1_S1=1 // ACC |= S1_S1; //(P1.6/RxD_3, P1.7/TxD_3) // P_SW1 = ACC; //#if (PARITYBIT == NONE_PARITY) SCON = 0x50; //8位可變波特率 //#elif (PARITYBIT == ODD_PARITY) || (PARITYBIT == EVEN_PARITY) || (PARITYBIT == MARK_PARITY) // SCON = 0xda; //9位可變波特率,校驗位初始為1 //#elif (PARITYBIT == SPACE_PARITY) // SCON = 0xd2; //9位可變波特率,校驗位初始為0 //#endif AUXR = 0x40; //定時器1為1T模式 TMOD = 0x20; //定時器1為模式2(8位自動重載) TL1 = (256 - (FOSC/32/BAUD)); //設置波特率重裝值 TH1 = (256 - (FOSC/32/BAUD)); TR1 = 1; //定時器1開始工作 ES = 1; //使能串口中斷 EA = 1; while(1) { // SendString(arry); SendString("I CAN PLAY~~\r\n");//上位機顯示接收文本模式 // SendData(x); } } /*---------------------------- UART 中斷服務程序 -----------------------------*/ void Uart() interrupt 4 using 1 { if (RI)//單片機接收數據,發送數字0~9,可在數碼管上顯示,發送hex模式 { RI = 0; //清除RI位 // P0 = SBUF; x=SBUF;//將緩存器的數據賦值給x P0=0xff; //消隱 P2|=0xe0; P2&=0x1f; P0=code_tab[x]; //段選 P2|=0xe0; P2&=0x1f; P0=0x01; //位選第一位 P2|=0xc0; P2&=0x3f; } if (TI) { TI = 0; //清除TI位 busy = 0; //清忙標志 } } /*---------------------------- 發送串口數據 ----------------------------*/ void SendData(BYTE dat) { while (busy); //等待前面的數據發送完成 ACC = dat; //獲取校驗位P (PSW.0) if (P) //根據P來設置校驗位 { #if (PARITYBIT == ODD_PARITY) TB8 = 0; //設置校驗位為0 #elif (PARITYBIT == EVEN_PARITY) TB8 = 1; //設置校驗位為1 #endif } else { #if (PARITYBIT == ODD_PARITY) TB8 = 1; //設置校驗位為1 #elif (PARITYBIT == EVEN_PARITY) TB8 = 0; //設置校驗位為0 #endif } busy = 1; SBUF = ACC; //寫數據到UART數據寄存器 } /*---------------------------- 發送字符串 ----------------------------*/ void SendString(char *s) { while (*s) //檢測字符串結束標志 { SendData(*s++); //發送當前字符 } } 記不住可以看手冊!! #include "reg51.h" #include "intrins.h" typedef unsigned char BYTE; typedef unsigned int WORD; #define FOSC 11059200L #define BAUD 115200 sfr AUXR=0x8e; //輔助寄存器 sbit P22=P2^2; bit busy; void SendData(BYTE dat); void SendString(char *s); void main() { SCON=0x50; AUXR=0x40; //設置定時器T1為1T,即一個機器周期模式 TMOD=0x20; TL1=(256-(FOSC/32/BAUD)); TH1=(256-(FOSC/32/BAUD)); TR1=1; ES=1; EA=1; SendString("Hello"); while(1); } void Uart() interrupt 4 using 1 { if(RI) { RI=0; P0=SBUF; } if(TI) { TI=0; busy=0; } } void SendData(BYTE dat) { while(busy); busy=1; SBUF=dat; } void SendString(char *s) { while(*s) { SendData(*s++); } } #include <reg52.h> sbit L1=P0^0; int main(){ IT0=1; //IT0=1,下降沿觸發外部中斷0,IT0=0邊沿觸發 EX0=1;//使用外部中斷0 EA=1; while(1){ } } void Ex_int0() interrupt 0 //外部中斷優先級最高 { P2=((P2&0x1f)|0x80); L1=~L1; P2=(P2&0x1f); } 其中,外部中斷的引腳控制是P3^2,P3^3,即對應獨立按鍵的S5,S4。 藍橋杯提供函數,解釋為: 里面的命令和寫入的數據可以看芯片手冊: 左側的READ、WRITE分別是讀寫的命令,BIT7-BIT0是要寫入的數據,根據需要進行配置。DS1302只需記住這兩個函數即可:Write_Ds1302( , )與Read_Ds1302(x),配置看手冊。 重點:芯片表說明:第一行:秒->因為秒的范圍是0-59,所以6,5,4位表示秒的十位,3,2,1,0表示個位,十位最大是5,所以三位即可。 第二行:跟上面一樣; 第三行:7位:1為12小時制,0為24小時制;5位:12小時制時為0表示上午,1表示下午,24小時制時,和4位一起表示小時的十位; 其余的時間一樣的表示。 倒數第二行:只看7位:為1時禁止寫數據,所以開始寫數據時必須置0; 讀數時: !!需要加“寫操作這一行代碼”。 讀的話直接按照命令讀即可。 DS1302進階(BCD碼轉換):解決之前60秒不能進位的問題。 例:寫入時間->17:58:50 Ds1302_Single_Byte_Write(0x8e, 0x00);//寫操作 Ds1302_Single_Byte_Write(0x85, ((17/10)<<4 | (17%10)));//寫時 Ds1302_Single_Byte_Write(0x83, ((58/10)<<4 | (58%10)));//寫分 Ds1302_Single_Byte_Write(0x81, ((50/10)<<4 | (50%10)));//寫秒 Ds1302_Single_Byte_Write(0x8e, 0x80);//寫保護 即轉換的公式是:((Value/10)<<4 | (Value%10)),可以寫一個settime()函數。 2 ) 讀數:讀回來的數要進行轉換成十進制數 ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); 八進制轉十進制-> ReadValue=Ds1302_Single_Byte_Read(0x85); hour=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); !!(這句一定不要省) Ds1302_Single_Byte_Write(0x00, 0x00);//寫操作 ReadValue=Ds1302_Single_Byte_Read(0x83); minute=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); Ds1302_Single_Byte_Write(0x00, 0x00);//寫操作 ReadValue=Ds1302_Single_Byte_Read(0x81); sec=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); Ds1302_Single_Byte_Write(0x00, 0x00);//寫操作 顯示: dspbuf[0]=hour/10; dspbuf[1]=hour%10; dspbuf[2]=minute/10; dspbuf[3]=minute%10; dspbuf[4]=sec/10; dspbuf[5]=sec%10; 比賽提供了IIC的兩個庫文件,IIC.h;IIC.c,其中需要注意的函數是: 其中,該函數是初始化的,當使用AD轉換的時候需要在main函數開始時調用,該函數內部只需看這句代碼即可:i2c_sendbyte(0x03);//ADC通道3,板上有4個模擬輸入口,分別為0,1,2,3;設置哪一個模擬輸入口就是根據這句代碼,0x03表示通道3,這是根據芯片手冊配置的,如圖: 8位前6位不用管,都為0,最后兩位就是配置選擇哪一個通道的。 第二個函數: 讀取AD轉換后的數值,這個函數直接調用就可以了,函數內部如何實現不用管,但是需要注意的是:該函數掃描調用最好是100ms。 第三個函數,上面的都是AD轉換,即模擬信號轉數字信號,下面這個函數是DA轉換,數字信號轉換成模擬信號,就是單片機輸出數字信號,用萬能表去量單片機引出的引腳,量一下電壓大小,這個估計比賽不會考,不過預防萬一: 該函數和上面兩個函數分離開來的,一、二函數是要在一起使用,初始化后之后才能調用,第三個加入頭文件,直接調用即可,比較簡單!! 上面說法有誤,A/D轉換的初始化函數和讀取轉換后的數值都需要自己寫。 這里了解一下PCF8591只需根據時序格式發送地址字節和控制字節: ,這是地址字節,其中A2,A1,A0硬件已經接地,故都為0,最低位表示的是你要從IIC總線上讀數還是寫數據,1表示讀,0表示寫,即讀數據發的地址是:0x91;寫數據發的地址是0x90; 控制字節: 由芯片資料知,控制字節有8位,有兩位固定是0,除了第0、1位需要自己設置,其他的我們都設為0,那些位都是一些具體的功能,我們暫時用不著,不用管先,第0、1位是模擬通道選擇,PCF8591上提供了4路模擬通道,根據需求進行選擇,如選擇通道3即發送控制字節:0x03; 地址字節和控制字節都明白了,接下來根據時序要求進行配置,A/D轉換需要一個初始化函數:Init_ADpcf8591();和一個獲得AD轉換后的數值的函數:adc_pcf8591(); 其中初始化函數的作用是發送AD轉換的控制字節;adc_pcf8591()發送讀取得地址并讀回數據,先寫指令才能讀;格式如下: 這個是初始化的協議:分別是startIIC、(地址寫)發送0x90、等待應答、發送控制字節(AD這里是選擇通道的指令,如選擇通道3,0x03)0x03、等待應答、(達到目的,沒有后續的操作,直接停止總線)StopIIC. 初始化的函數就是如此寫; adc_pcf8591的協議: 依次是:startIIc、發送讀地址0x91、等待應答、讀回AD轉換后的數值、讀回后發送應答給PCF8591,表示收到,并且不需要再返回應答,要傳參數1,如圖紅圈示,即函數Ack(1);、最后stopIIC總線。 D/A轉換(其實挺麻煩,先前太自信了,哈): 所謂D/A轉換其實就是把數字信號轉換成模擬信號輸出,用單片機發數字通過D/A轉換成電壓輸出,檢測的方法可以用電壓表測量。 配置的方法跟A/D類似,先發地址字節,再發控制字節,然后把數字發出去(AD這里是接收模擬信號,是相反的機制)。 控制字節: 如圖示,控制字節的第6位是1的話是模擬輸出模式,其余位全為0,發送格式跟AD一樣: 代碼如一開始圖示。 - EEPROM的使用,AT24C02,可以掉電依舊保存上一次操作的數據,下次上電后接著運行。
需要注意兩個函數,一個是寫進EEPROM里面保存,再次上電再從里面讀回來: 其中寫函數需要指定AT24C02的地址以及需要寫入的數據,讀函數要想取回寫進的數據,需要從相同的地址里面讀: 其中AT24C02的存儲地址是0x00,可以是其他地址,如0x02,但是讀和寫的地址必須一致。 寫與讀的協議與AD或DA相同, 由芯片資料及原理圖知EEPROM(AT24C02)的寫地址為0xa0;讀地址為0xa1;注意:讀數的時候讀出一個數之后發送一個應答信號,若ACK(0)表示還想繼續讀下一個字節,若ACK(1);則不想再讀數,讓EEPROM停止發送。 比賽有提供代碼,只需記住這個函數: 讀取溫度值,整數(其中,提示EA總中斷要打開、關閉,也可以不用)。 浮點數的表示。 注意,只有提供函數,沒有提供讀取溫度的函數,即上面的那個,只有下面: 這幾個函數。 編寫讀取溫度的函數需要記住DS13B20的三條指令,0xCC,跳過ROM檢測;然后啟動溫度轉換:0x44;轉換需要時間,這里精確延時Delay_OneWire(200);然后再次初始化,再次執行跳過,然后讀取溫度指令:0xBE.;注意讀出的溫度是低字節先,然后才是高字節,分別用兩個變量保存還要通過公式轉換成我們需要的整數或浮點數。完整代碼如上圖示。 #include "reg52.h" //定義51單片機特殊功能寄存器 #include "intrins.h" #include "absacc.h" //12M用這個 /*#define somenop {_nop_();_nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();}*/ //11.0592用這個 #define somenop {_nop_();_nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();} sbit TX = P1^0; //發射引腳 sbit RX = P1^1; //接收引腳 code unsigned char tab[] = { 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,\ 0xff}; unsigned char dspbuf[8] = {10,10,10,10,10,10,10,10}; //顯示緩沖區 unsigned char dspcom = 0; unsigned int intr = 0; bit s_flag; unsigned int t = 0; void send_wave(void); void display(void); void main(void) { unsigned int distance; TMOD |= 0x11; //配置定時器工作模式 TH0 = (65536-2000)/256; TL0 = (65536-2000)%256; TH1 = 0; TL1 = 0; EA = 1; ET0 = 1; //打開定時器0中斷 TR0 = 1; //啟動定時器 while(1){ /** 200毫秒更新一次數據 */ if(s_flag) { s_flag = 0; /** 關閉定時器0中斷:計算超聲波發送到返回的時間 send_wave(); //發送方波信號 TR1 = 1; //啟動計時 while((RX == 1) && (TF1 == 0)); //等待收到脈沖 TR1 = 0; //關閉計時 //發生溢出 if(TF1 == 1) { TF1 = 0; distance = 9999; //無返回 } else { /** 計算時間 */ t = TH1; t <<= 8; t |= TL1; distance = (unsigned int)(t*0.017); //計算距離 } TH1 = 0; TL1 = 0; } /** 數據處理 */ dspbuf[5] = distance/100; dspbuf[6] = distance%100/10; dspbuf[7] = distance%10; } } //定時器0中斷服務函數 void isr_timer_0(void) interrupt 1 //默認中斷優先級 1 { TH0 = (65536-2000)/256; TL0 = (65536-2000)%256; //定時器重載 display(); //2ms執行一次 if(++intr == 200){ s_flag = 1; intr = 0; } } //顯示函數 void display(void){ XBYTE[0xE000] = 0xff; //去除鬼影 XBYTE[0xC000] = (1<<dspcom); XBYTE[0xE000] = tab[dspbuf[dspcom]]; if(++dspcom == 8){ dspcom = 0; } } //TX引腳發送40KHz方波信號驅動超聲波發送探頭 void send_wave(void) { unsigned char i = 8; //發送8個脈沖 do { TX = 1; somenop; TX = 0; somenop; } while(i--); } 必要時還可以加個看門狗: WDT_CONTR=0x34; 參考代碼如下: #include <reg52.h> sbit A1=P1^4; //定義步進電機連接端口 sbit B1=P1^3; sbit C1=P1^2; sbit D1=P1^1; void qudong1(); #define Dy_A1 {A1=1;B1=0;C1=0;D1=0;}//A相通電,其他相斷電 #define Dy_B1 {A1=0;B1=1;C1=0;D1=0;}//B相通電,其他相斷電 #define Dy_C1 {A1=0;B1=0;C1=1;D1=0;}//C相通電,其他相斷電 #define Dy_D1 {A1=0;B1=0;C1=0;D1=1;}//D相通電,其他相斷電 //采用1相勵磁 #define Dy_OFF {A1=0;B1=0;C1=0;D1=0;}//全部斷電 unsigned char Speed,Speed1; /*------------------------------------------------ uS延時函數,含有輸入參數 unsigned char t,無返回值 unsigned char 是定義無符號字符變量,其值的范圍是 0~255 這里使用晶振12M,精確延時請使用匯編,大致延時 長度如下 T=tx2+5 uS ------------------------------------------------*/ void DelayUs2x(unsigned char t) { while(--t); } /*------------------------------------------------ mS延時函數,含有輸入參數 unsigned char t,無返回值 unsigned char 是定義無符號字符變量,其值的范圍是 0~255 ------------------------------------------------*/ void DelayMs(unsigned char t) { while(t--) { //大致延時1mS DelayUs2x(245); DelayUs2x(245); } } /*------------------------------------------------ 主函數 ------------------------------------------------*/ main() { Dy_OFF; for(;;) { qudong1(); } } void qudong1() { unsigned int i=470;//旋轉一周時間 Speed=5; while(i--) //正向 { Dy_A1 //遇到Coil_A1 用{A1=1;B1=0;C1=0;D1=0;}代替 DelayMs(Speed); //改變這個參數可以調整電機轉速 , //數字越小,轉速越大,力矩越小 Dy_B1 //順序從A1--D1相通電如果為正轉,那么順序從D1--A1相通電則為反轉 DelayMs(Speed); Dy_C1 DelayMs(Speed); Dy_D1 DelayMs(Speed); } Dy_OFF i=512; while(i--)//反向 { Dy_D1 //遇到Coil_A1 用{A1=1;B1=0;C1=0;D1=0;}代替 DelayMs(Speed); //改變這個參數可以調整電機轉速 , //數字越小,轉速越大,力矩越小 Dy_C1 DelayMs(Speed); Dy_B1 DelayMs(Speed); Dy_A1 DelayMs(Speed); } } 直流電機: #include<reg52.h> #define uint unsigned int void drive(); void delay(uint); sbit dj1=P1^0; //電機,1引腳 uint set; //set為電機轉角標志位 unsigned char angle,angle1; //angle為電機PWM變化打角 void Time0_Init() //中斷初始化 { TMOD=0X01; IE=0X82; TH0=(65536-58)/256; TL0=(65536-58)%256; TR0=1; } void main() //主函數 { set=0; angle=62; // 改變angle的值可以改變電機的占空比 Time0_Init(); for(;;) { } } void T0_time()interrupt 1 { TH0=(65536-58)/256; TL0=(65536-58)%256; if(set<angle) dj1=1; else dj1=0; set++; if(set>165) set=0; //電機占空比 } 常用的可以宏定義,省時,下面以數碼管為例: #define rst573 P2&=0x1f #define Y6C P2= ((P2&0x1f)|0xc0) #define Y7C P2= ((P2&0x1f)|0xe0) code unsigned char tab[] = { 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; unsigned char dspbuf[8] = {10,10,10,10,10,10,10,10}; //顯示緩沖區 unsigned char dspcom; unsigned char flag; void display() { Y7C; P0=0xff; rst573; Y6C; P0=1<<dspcom; rst573; Y7C; P0=tab[dspbuf[dspcom]]; rst573; if(++dspcom==8) dspcom=0; } - 設置標志位flag,常在定時的時候使用,一般我們常用的數據類型是unsigned char類型,有時候我們這樣判斷if(flag==500){flag=0; … };這是十分明顯的錯誤,unsigned char是8位,范圍是0-255,500已經超出其最大值的范圍了,但是編譯的時候是不會報錯的,我們可以設成unsigned int 類型,16位,范圍很大。
- 模擬題中led閃爍的頻率隨著溫度值value與設定的范圍值比較而改變,這里困擾了我很久,終于在前人的代碼中找到解決方法了,這樣:設置的標志位flag不要直接在while循環里直接如:if(flag==100){flag=0;…}不要這樣寫。應該在定時中斷新定義一個變量,如為標志位flag_100ms(假設進入一次定時中斷2ms):
void time0() interrupt 1 { unsigned int i; if(++i==50) { i=0; flag_100ms=1; } } 然后在需要用到的位置判斷標志位是否為1即可。 注意:也可以設靜態變量,建議用: void time0() interrupt 1 { TH0=(65535-2000)/256; TL0=(65535-2000)%256; static unsigned int i=0; static unsigned int j=0; if(++i==100) { i=0; flag_200ms=1; } if(++j==200) { j=0; flag_400ms=1; } } 3、注意DS18b20溫度傳感器,因為使用的單片機芯片不一樣,晶振頻率不一樣,而DS18b20對時序的要求又很嚴格,所以這里記住兩種單片機的延時程序://單總線延時函數 #ifndef STC12 void Delay_OneWire(unsigned int t) //STC89C52RC { while(t--); } #else void Delay_OneWire(unsigned int t) //STC12C5260S2 { unsigned char i; while(t--){ for(i=0;i<12;i++); } } #endif 4、使用串口通訊傳一個數組給PC機,如果是文本模式顯示字符,需要將字符的ASII碼轉換成文本字符,還需在數組末尾加一個結束符‘\0’: Val[0]='{'; Val[1]=(hour/10)+48; 。 。 。 Val[19]='H'; Val[20]='}'; Val[21]='\0'; 5、串口通訊波特率設置及初始化函數: sfr AUXR=0x8e; //宏定義區 #define BAUD 1200//波特率;需要設置哪個波特率只需在這里更改即可 #define FOSC 11059200L //晶振頻率 void InitSerial() { SCON=0x50; TMOD |=0x00;//使用16位自動重載方式,選擇T1 AUXR=0x40;//設置定時器為一個周期,即1T TL1=(65536-(FOSC/4/BAUD)); TH1=(65536-(FOSC/4/BAUD))>>8; TR1=1; ES=1; } 6、串口中斷函數及發送、結束函數: void Uart() interrupt 4 using 1 { if(RI){ RI=0; value=SBUF; } if(TI){ TI=0; busy=0; } } void SendData(unsigned char dat) { while(busy); busy=1; SBUF=dat; } void SendString(unsigned char *s) { while(*s){ SendData(*s++); } } 7、簡單的電路設計知識點: SOURCE CURRENT 電流源
SOURCE VOLTAGE 電壓源 定值無極性電容;CAP
定值有極性電容;CAP
繼電器:RELAY. LIB 運放:OPAMP
BATTERY 直流電源
DIODE 二極管
DIODE SCHOTTKY 穩壓二極管
NPN NPN三極管
NAND 與非門
NOR 或非門
NOT 非門 4013 D 觸發器
4027 JK 觸發器
8、LED閃爍控制程序參考: static unsigned char LED=0xfe; XBYTE[0x8000]=LED=LED^0x01;(和0x01進行異或運算,L1閃爍!!) 9、數碼管的碼值(牢記): tab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0xbf};
完整的Word格式文檔51黑下載地址:
藍僑杯單片機編程筆記新.docx
(976.4 KB, 下載次數: 9)
2018-3-19 08:53 上傳
點擊文件名下載附件
|