十年專注單片機方案開發的方案公司英銳恩,分享從一個簡單的A/D轉換程序(C語言)想開去。英銳恩現提供服務產品涉及主控芯片:8位單片機、16位單片機、32位單片機及各類運算放大器等。
從一個簡單的A/D轉換程序(C語言)想開去
從一個簡單的A/D轉換程序(C語言)想開去以下是一個A/D轉換程序,所用芯片是16F72 unsigned char adc(void) //使用RA0,VREF=RA3 { unsigned char i; ADCON1=0x05; ADCON0=0x40; ADON=1; i=0x20; while(i--) //等待一段時間(A/D采樣) ADGO=1; //啟動A/D轉換 while(ADGO) //等待A/D轉換完成 return(ADRES); //取結果返回 } 第一個問題:網上經常有人問“會C語言后還要學匯編嗎?” 第二個問題:這個程序是否還有問題?要回答第一個問題,先看一下編譯后的程序,特別是while(i--)這一句 14: unsigned char adc(void) //使用RA0,VREF=RA3 15: { unsigned char i; 0004ED 3005 MOVLW 0x5 16: ADCON1=0x05; 0004EE 1683 BSF 0x3, 0x5 0004EF 1303 BCF 0x3, 0x6 0004F0 09F MOVWF 0x1f 17: ADCON0=0x40; 0004F1 3040 MOVLW 0x40 0004F2 1283 BCF 0x3, 0x5 0004F3 09F MOVWF 0x1f 18: ADON=1; 0004F4 141F BSF 0x1f, 0 19: i=0x20; 0004F5 3020 MOVLW 0x20 0004F6 0A1 MOVWF 0x21 20: while(i--) //等待一段時間(A/D采樣) 0004F7 2CFB GOTO 0x4fb 0004FB 1283 BCF 0x3, 0x5 0004FC 1303 BCF 0x3, 0x6 0004FD 3A1 DECF 0x21, F 0004FE F21 INCFSZ 0x21, W 0004FF 2D01 GOTO 0x501 000500 2D02 GOTO 0x502 000501 2CF8 GOTO 0x4f8 21: ADGO=1; //啟動A/D轉換 0004F8 1283 BCF 0x3, 0x5 0004F9 1303 BCF 0x3, 0x6 0004FA 151F BSF 0x1f, 0x2 22: while(ADGO) //等待A/D轉換完成 000502 2D07 GOTO 0x507 000507 1283 BCF 0x3, 0x5 000508 1303 BCF 0x3, 0x6 000509 191F BTFSC 0x1f, 0x2 00050A 2D0C GOTO 0x50c 00050B 2D0D GOTO 0x50d 00050C 2D03 GOTO 0x503 23: return(ADRES); //取結果返回 000503 1283 BCF 0x3, 0x5 000504 1303 BCF 0x3, 0x6 000505 81E MOVF 0x1e, W 000506 2D0D GOTO 0x50d 24: } 00050D 1303 BCF 0x3, 0x6
把while(i--)改成while(--i)后再編譯 20: while(--i) //等待一段時間(A/D采樣) 0004F8 2CFC GOTO 0x4fc 0004FC 1283 BCF 0x3, 0x5 0004FD 1303 BCF 0x3, 0x6 0004FE BA1 DECFSZ 0x21, F 0004FF 2D01 GOTO 0x501 000500 2D02 GOTO 0x502 000501 2CF9 GOTO 0x4f9 21: ADGO=1; //啟動A/D轉換 0004F9 1283 BCF 0x3, 0x5 0004FA 1303 BCF 0x3, 0x6 0004FB 151F BSF 0x1f, 0x2 22: while(ADGO) //等待A/D轉換完成 000502 2D07 GOTO 0x507 編譯后程序更簡單更易理解,從這個例子可看出熟悉匯編可以編出更高效的C
第二個問題,其實本程序是有問題的,最大問題出在while(--i)和while(ADGO) 應改成如下寫法: unsigned char adc(void) //使用RA0,VREF=RA3 { unsigned char i; ADCON1=0x05; ADCON0=0x40; ADON=1; i=0x20; while(--i){} //等待一段時間(A/D采樣) ADGO=1; //啟動A/D轉換 while(ADGO){} //等待A/D轉換完成 return(ADRES); //取結果返回 }
20: while(--i){} //等待一段時間(A/D采樣) 0004F9 2CFA GOTO 0x4fa 0004FA 1283 BCF 0x3, 0x5 0004FB BA1 DECFSZ 0x21, F 0004FC 2CFE GOTO 0x4fe 0004FD 2CFF GOTO 0x4ff 0004FE 2CFA GOTO 0x4fa 21: ADGO=1; //啟動A/D轉換 0004FF 1283 BCF 0x3, 0x5 000500 1303 BCF 0x3, 0x6 000501 151F BSF 0x1f, 0x2 22: while(ADGO){} //等待A/D轉換完成 000502 2D03 GOTO 0x503 000503 1283 BCF 0x3, 0x5 000504
1303 BCF 0x3, 0x6 000505 191F BTFSC 0x1f, 0x2 000506 2D08 GOTO 0x508 000507 2D09 GOTO 0x509 000508 2D03 GOTO 0x503 23: return(ADRES); //取結果返回 000509 1283 BCF 0x3, 0x5 00050A 1303 BCF 0x3, 0x6 00050B 81E MOVF 0x1e, W 00050C 2D0D GOTO 0x50d 24: } 00050D 1303 BCF 0x3, 0x6
第三個問題,為什么在判斷語句后連著有三個GOTO?而我們用匯編編寫時只一個GOTO就夠了。?
給你看看我編譯的結果
1: #include 2: 3: unsigned char adc(void) //使用RA0,VREF=RA3 4: { unsigned char i; 0007F0 3005 MOVLW 0x5 5: ADCON1=0x05; 0007F1 1683 BSF 0x3, 0x5 0007F2 1303 BCF 0x3, 0x6 0007F3 09F MOVWF 0x1f 6: ADCON0=0x40; 0007F4 3040 MOVLW 0x40 0007F5 1283 BCF 0x3, 0x5 0007F6 09F MOVWF 0x1f 7: ADON=1; 0007F7 141F BSF 0x1f, 0 8: i=0x20; 0007F8 3020 MOVLW 0x20 0007F9 0A0 MOVWF 0x20 9: while(--i); //等待一段時間(A/D采樣) 0007FA BA0 DECFSZ 0x20, F 0007FB 2FFA GOTO 0x7fa 10: ADGO=1; //啟動A/D轉換 0007FC 151F BSF 0x1f, 0x2 11: while(ADGO); //等待A/D轉換完成 0007FC 191F BTFSC 0x1f, 0x2 0007FD 2FFC GOTO 0x7fc 12: return(ADRES); //取結果返回 0007FE 81E MOVF 0x1e, W 13: } 0007FF 008 RETURN
你把優化關掉了,打開的步驟: Project -> Build Options -> Project 在Build Options窗口里點PICC Compiler 將Enable assembler optimization選中,然后再編譯。不要小看PICC哦!另,你程序里的明顯錯誤,while(i--)后面要加分號,while(ADGO)后面也要加分號。
優化的結果居然與我寫的匯編一樣,一條都不多,一條都不少。為什么while(--i)后面未加分號時編譯,鏈接都通過?PICC應該提示我呀, 1:while(--i)后面未加分號時,編譯沒錯誤,但程序的邏輯是錯的。 2:改成while(--i){}時,程序的邏輯是正確的 3:while(--i);表示什么?我遇到這種情況總寫成 while(--i){} 4:再次感謝
{} ;都可以的
沒有操作的時候全部是空操作,所以編譯后就兩條。很多說c效率低的都是因為結構差,對自己寫的源碼優化差導致容量變大??梢赃@么說,不熟練的使用者用兩種方式變出來的代碼差不多大小,c可能要略小一些,也就是對數據結構和MCU特點一無所知。中等熟練的,也就是對數據結構比較清楚,比較熟悉MCU特點,代碼會很接近。只有真正的匯編高手,也就是說用了多年匯編,非常熟悉MCU本身,對算法和數據結構很清楚才可能做到??!不信大家可以比一比,當然硬要摳那種三五個字節的沒辦法比。 10%以上誤差很難。
又,不要認為c編譯器弱智, i=j/256;在c中一樣會編譯成直接被賦成高字節或者右移8位!類似的例子太多了,編譯了看看就知道。