![]() |
發(fā)布時間: 2014-10-6 22:36
正文摘要:本帖最后由 codenew 于 2014-10-6 22:42 編輯 下面程序是我從別的地方搞來的,最后發(fā)現(xiàn)很多錯誤。為了避免誤人子弟,特地作了一下分析,開頭部分for分析是我寫的,程序中紅色的部分我 ... |
神仙打架 |
阻塞式延時都是垃圾,,哈哈 |
欽佩做事認真的人,因為曾有偉人說過,“世界上怕就怕認真二字”,科學來不得半點馬虎。 |
![]() |
![]() |
樓主人好,技術不錯。 |
18b20有時候跟硬件關系很大,不同硬件程序跑出來不一樣,還有中斷也會影響到它的測量 |
有沒有完整的對的程序u |
好的,謝謝分享了。。。 |
本帖最后由 codenew 于 2014-10-9 09:58 編輯 //下面的time在源程序中都是全局變量! /***************************************************** 函數(shù)功能:將DS18B20傳感器初始化,讀取應答信號 出口參數(shù):flag ***************************************************/ bit Init_DS18B20(void) { bitflag; //儲存DS18B20是否存在的標志,flag=0,表示存在;flag=1,表示不存在 DQ= 1; //先將數(shù)據線拉高 for(time=0;time<2;time++);//略微延時約6微秒。實際是延時10us,局部變量時為8us,不是注釋所說的6us。 DQ= 0; //再將數(shù)據線從高拉低,要求保持480~960us for(time=0;time<200;time++);//略微延時約600微秒,以向DS18B20發(fā)出一持續(xù)480~960us的低電平復位脈沖。實際是延時802us,time為局部變量時才為602us,即約600us。這點作者明顯誤撞了,幸好802us還在范圍內。 DQ= 1; //釋放數(shù)據線(將數(shù)據線拉高) for(time=0;time<10;time++);//延時約30us(釋放總線后需等待15~60us讓DS18B20輸出存在脈沖)。實際是延時42us,time為局部變量時才為 32us,即約32us。明顯作者又錯了,把time當作局部變量了,幸好 42us還在范圍內。實際最好延時60us,確保能讀到存在脈沖。 flag=DQ; //讓單片機檢測是否輸出了存在脈沖(DQ=0表示存在) for(time=0;time<200;time++); //延時足夠長時間,等待存在脈沖輸出。延時802us。 return(flag); //返回檢測成功標志 } /***************************************************** 函數(shù)功能:從DS18B20讀取一個字節(jié)數(shù)據 出口參數(shù):dat ***************************************************/ unsigned char ReadOneChar(void) { unsignedchar i=0; unsignedchar dat; //儲存讀出的一個字節(jié)數(shù)據 for(i=0;i<8;i++) { DQ=1; //先將數(shù)據線拉高 _nop_(); //等待一個機器周期 DQ= 0; //單片機從DS18B20讀書據時,將數(shù)據線從高拉低即啟動讀時序 dat>>=1; _nop_(); //等待一個機器周期 DQ= 1; //將數(shù)據線"人為"拉高,為單片機檢測DS18B20的輸出電平作準備 for(time=0;time<2;time++);//延時約6us,使主機在15us內采樣。實際是延時10us,局部變量時為8us,都不是注釋的6us。 if(DQ==1) dat|=0x80; //如果讀到的數(shù)據是1,則將1存入dat else dat|=0x00;/如果讀到的數(shù)據是0,則將0存入dat,將單片機檢測到的電平信號DQ、r for(time=0;time<8;time++);//延時3us,兩個讀時序之間必須有大于1us的恢復期。實際是延時34us,與3us差很多。 } return(dat); //返回讀出的十進制數(shù)據 } /***************************************************** 函數(shù)功能:向DS18B20寫入一個字節(jié)數(shù)據 入口參數(shù):dat ***************************************************/ WriteOneChar(unsigned char dat) { unsignedchar i=0; for(i=0; i<8; i++) { DQ=1; // 先將數(shù)據線拉高 _nop_(); //等待一個機器周期 DQ=0; //將數(shù)據線從高拉低時即啟動寫時序 DQ=dat&0x01; //利用與運算取出要寫的某位二進制數(shù)據,并將其送到數(shù)據線上等待DS18B20采樣 for(time=0;time<10;time++);//延時約30us,DS18B20在拉低后的約15~60us期間從數(shù)據線上采樣。實際是延時42us,局部變量時才為32us。 DQ=1; //釋放數(shù)據線 for(time=0;time<1;time++);//延時3us,兩個寫時序間至少需要1us的恢復期 。實際是延時6us,局部變量時才為5us。 dat>>=1; //將dat中的各二進制位數(shù)據右移1位 } for(time=0;time<4;time++);//稍作延時,給硬件一點反應時間。延時18us。 } /***************************************************** 函數(shù)功能:做好讀溫度的準備 ***************************************************/ void ReadyReadTemp(void) { Init_DS18B20(); //將DS18B20初始化。既然Init_DS18B20();有返回值flag,不判斷其值是否成功就寫命令了,又筆誤。 WriteOneChar(0xCC); // 跳過讀序號列號的操作 WriteOneChar(0x44); // 啟動溫度轉換 for(time=0;time<100;time++);//溫度轉換需要一點時間。延時402us。 Init_DS18B20(); //將DS18B20初始化 WriteOneChar(0xCC);//跳過讀序號列號的操作 WriteOneChar(0xBE);//讀取溫度寄存器,前兩個分別是溫度的低位和高位 } /***************************************************** 函數(shù)功能:延時若干毫秒 入口參數(shù):n ***************************************************/ voiddelaynms(unsigned char n) { unsigned char i; for(i=0;i<n;i++) delay1ms(); } // 照理說void delaynms(unsigned char n),參數(shù)n的取值范圍是0~255,因是是無符號數(shù)。但在主函數(shù)中卻調用delaynms(1000),明顯錯誤,超出取值范圍。/***************************************************** 函數(shù)功能:延時1ms (3j+2)*i=(3×33+2)×10=1010(微秒),可以認為是1毫秒 ***************************************************/ void delay1ms() { unsigned char i,j; for(i=0;i<4;i++) for(j=0;j<33;j++); } //實際延時422us,代碼優(yōu)化為0時才接近1ms。 |
沒看懂的說![]() |
本帖最后由 明白 于 2014-10-8 00:42 編輯 你篡改了別人原c語言程序的本意, 到此為止,你也應該知道源程序哪里被你改了吧 |
本帖最后由 明白 于 2014-10-8 00:37 編輯 delay1ms()延時函數(shù): void delay1ms() { unsigned char i,j; for(i=0;i<4;i++) for(j=0;j<33;j++); } 834-412=422 看看delay1ms(),跟我上面精度計算結果也是422,一模一樣。 |
函數(shù)調用結束.jpg (18.06 KB, 下載次數(shù): 196)
函數(shù)調用開始.jpg (26.17 KB, 下載次數(shù): 167)
函數(shù)調用延時機器碼.jpg (32.77 KB, 下載次數(shù): 183)
本帖最后由 明白 于 2014-10-8 00:25 編輯 for(time=0;time<2;time++);的話: 399-391=8個機器周期 |
參數(shù)2結束.png (3.08 KB, 下載次數(shù): 206)
參數(shù)2開始.png (3.43 KB, 下載次數(shù): 204)
原地延時機器碼.jpg (16.78 KB, 下載次數(shù): 168)
本帖最后由 明白 于 2014-10-7 23:59 編輯 剛才我也仿真一下: for(time=0;time<1;time++);的話: 396-391=5個機器周期 |
參數(shù)1結束.png (3.38 KB, 下載次數(shù): 183)
參數(shù)1開始.png (4.2 KB, 下載次數(shù): 216)
我靠 太牛了 ,樓主的這個帖子對我的幫助非常大 ,佩服得五體投地 阿 |
for(time=0;time<1;time++); //15us for(time=0;time<2;time++); //23us for(time=0;time<3;time++); //31us for(time=0;time<4;time++); //39us for(time=0;time<5;time++); //47us for(time=0;time<6;time++); //55us for(time=0;time<7;time++); //63us for(time=0;time<8;time++); //71us for(time=0;time<9;time++); //79us for(time=0;time<10;time++); //87us for(time=0;time<20;time++); //167us for(time=0;time<60ime++); //487us for(time=0;time<70ime++); //567us for(time=0;time<80ime++); //647us for(time=0;time<100;time++); //807us for(time=0;time<200;time++); //1607us 這些原地延時是沒有可變性的(當然是排除中斷的騷擾), 編譯出來的機器碼是有固定模式的: START: CLR A //1T MOV R7,A //1T LOOP: INC R7 //1T CJNE R7,#02H,LOOP //2T 所以延時時間:2T+time*(1T+2T)=2T+time*3T=(2+time*3)T 如果一個機器周期為:1us an=am+8*(n-m)。這跟: =2+3*time。差別大不大? for(time=0;time<1;time++); //15us-------5us for(time=0;time<2;time++); //23us-------8us for(time=0;time<3;time++); //31us-------11us for(time=0;time<4;time++); //39us-------14us for(time=0;time<5;time++); //47us-------17us for(time=0;time<6;time++); //55us-------20us for(time=0;time<7;time++); //63us-------23us for(time=0;time<8;time++); //71us-------26us for(time=0;time<9;time++); //79us-------29us for(time=0;time<10;time++); //87us-------32us for(time=0;time<20;time++); //167us-------62us for(time=0;time<60ime++); //487us-------182us for(time=0;time<70ime++); //567us-------212us for(time=0;time<80ime++); //647us-------242us for(time=0;time<100;time++); //807us-------302us for(time=0;time<200;time++); //1607us-------602us |
你延時這樣計算就有錯誤 看看 delay1ms() 編譯出來的內碼,精算一下: delay1ms: CLR A //1T MOV R7,A //1T D1: CLR A //1T MOV R6,A //1T D2: INC R6 //1T CJNE R6,#21H,D2 //2T INC R7 //1T CJNE R7,#04H,D1 //2T RET //2T 2T + [( 2T + 33 * 3T) + 1T + 2T ] * 4 + 2T = 2T + [ 101T + 1T + 2T ] * 4 + 2T = 2T + [ 104 T] * 4 + 2T = 2T + [ 104 T] * 4 + 2T = 420T 再加上調用delay1ms()時間,需要的LCALL兩個機器周期,就是: 422us |
codenew 發(fā)表于 2014-10-7 11:13 如果晶振是12Mhz。那么 void delay1ms() { unsigned char i,j; for(i=0;i<4;i++) for(j=0;j<33;j++); } 這個就不能認為是1ms,相差不只一倍 |
codenew 發(fā)表于 2014-10-7 10:54 DS18B20的讀寫操作需要一段時間,是不能短于時順規(guī)定的時間,如果延時長了,只是浪費時間而已。 可以長,但是不能短 |
這要動腦再想想哪程序錯了,為什么還能正常用,的確運氣很好。例如源程序的另一個延時程序又是錯的,其注釋思路肯定不對,但其實際效果還是接近1ms的: /***************************************************** 函數(shù)功能:延時1ms (3j+2)*i=(3×33+2)×10=1010(微秒),可以認為是1毫秒 ***************************************************/ void delay1ms() { unsigned char i,j; for(i=0;i<4;i++) for(j=0;j<33;j++); } |
樓上,我敢保證我的想法是對的。因為把源程序原封不動寫入芯片,測溫是正常顯示的,晶振是12Mhz。當然引腳也是對應的。 |
程序的注釋,不是完全對。 程序通過返復調整得到正確代碼,這是每一個程序員編程過程, 在這過程中不可能總是修改注釋, 這樣就導致注釋的不同步。 延時函數(shù)一般情況下是線性。 原程序的運行機器周期可能不是1us |