我們如何才能大致估算上述代碼的最大脈衝頻率
能正確讀取狀態嗎?
它將始終正確讀取狀態。我想您想問的問題是,它可以“測量”而不丟失任何高點或低點的最大頻率是什麼。
我們應該找出它使用了多少個時鐘週期,然後乘以
時鐘頻率?
基本上是。重要的因素是每次讀取端口之間的時間。請注意,根據機器代碼的不同,這可能並不總是相同的,因此應在兩次讀取之間使用最長時間。
如果是的話,我該怎麼做呢?
您可以反彙編代碼,計算出每條指令要花費多少時間,或者在模擬器中逐步執行,或者在實際的ATmega328p中運行代碼並監視物理輸出(例如,切換輸出引腳或在LCD屏幕上顯示頻率)。
請注意,結果在很大程度上取決於編譯器生成的機器代碼。通過對不影響輸出的任何變量進行優化,可以將其優化,其他看似微不足道的更改可能會對所生成的代碼量產生很大影響。因此,唯一可以保證測試代碼準確的方法是全部。運行隔離代碼的小片段可能會給最終應用程序帶來非常誤導的性能提示。
例如,這是您的問題代碼清單:-
int main(void){
86:89 e1 ldi r24,0x19; 25
88:90 e0 ldi r25,0x00; 0
uint8_t val;
對於(int i = 0; i<25; i ++){
數據<< = 1;
PORTD & =〜(1 << 5);
8a:5d 98 cbi 0x0b,5; 11
// _delay_us(2);
PORTD | =(1 << 5);
8c:5d 9a sbi 0x0b,5; 11
// _delay_us(2);
如果((PIND &(1 << PIND6))==((1 << PIND6)){
8e:r18中的29 b1,0x09; 9
90:01 97旋轉r24,0x01; 1個
而(1){
uint8_t val;
對於(int i = 0; i<25; i ++){
92:d9 f7 brne .-10; 0x8a <main + 0xa>
94:f8 cf rjmp .-16; 0x86 <main + 0x6>
不會為 val
和 data
生成任何代碼,並且內部循環只有5條指令佔用9個週期。使用16 MHz時鐘時,內部環路時間為62.5 ns * 9 = 562.5 ns,這應該能夠滿足〜888 kHz的輸入頻率。
接下來,我將 data
輸出到PORTD,這將迫使編譯器為其生成代碼:-
而(1){
uint8_t val;
對於(int i = 0; i<25; i ++){
數據<< = 1;
90:88 0f添加r24,r24
92:99 1f adc r25,r25
94:aa 1f adc r26,r26
96:bb 1f adc r27,r27
PORTD & =〜(1 << 5);
98:5d 98 cbi 0x0b,5; 11
// _delay_us(2);
PORTD | =(1 << 5);
9a:5d 9a sbi 0x0b,5; 11
// _delay_us(2);
如果((PIND &(1 << PIND6))==((1 << PIND6)){
9c:r20中的49 b1,0x09; 9
}
其他{
val = 0;
}
數據| = val;
9e:46 fb bst r20、6
a0:44 27 eor r20,r20
a2:40 f9 bld r20、0
a4:84 2b或r24,r20
a6:21 50 subi r18,0x01; 1個
a8:31 09 sbc r19,r1
而(1){
uint8_t val;
對於(int i = 0; i<25; i ++){
aa:91 f7 brne .-28; 0x90 <main + 0x10>
}
數據| = val;
}
PORTD =(uint8_t)數據;
ac:8b b9 out 0x0b,r24; 11
//其餘代碼
}
ae:ee cf rjmp .-36; 0x8c <main + 0xc>
內部循環現在有14條指令需要17個週期,並且它可以精確跟踪的最大頻率幾乎減半。
最後,我將 data
設為靜態,以強制編譯器將其存儲在內存中(這對於更複雜的程序可能是必需的):-
而(1){
uint8_t val;
對於(int i = 0; i<25; i ++){
數據<< = 1;
9a:40 91 00 01 lds r20,0x0100; 0x800100 <_edata>
9e:50 91 01 01 lds r21,0x0101; 0x800101 <_edata + 0x1>
a2:60 91 02 01 lds r22,0x0102; 0x800102 <_edata + 0x2>
a6:70 91 03 01 lds r23,0x0103; 0x800103 <_edata + 0x3>
aa:44 0f加r20,r20
交流電:55 1f adc r21,r21
AE:66 1F ADC R22,R22
b0:77 1f adc r23,r23
b2:40 93 00 01 sts 0x0100,r20; 0x800100 <_edata>
b6:50 93 01 01 sts 0x0101,r21; 0x800101 <_edata + 0x1>
ba:60 93 02 01 sts 0x0102,r22; 0x800102 <_edata + 0x2>
是:70 93 03 01 sts 0x0103,r23; 0x800103 <_edata + 0x3>
PORTD & =〜(1 << 5);
c2:5d 98 cbi 0x0b,5; 11
// _delay_us(2);
PORTD | =(1 << 5);
c4:5d 9a sbi 0x0b,5; 11
// _delay_us(2);
如果((PIND &(1 << PIND6))==((1 << PIND6)){
c6:r18中的29 b1,0x09; 9
}
其他{
val = 0;
}
數據| = val;
c8:26 fb bst r18、6
ca:22 27 eor r18,r18
抄送:20 f9 bld r18,0
ce:40 91 00 01 lds r20,0x0100; 0x800100 <_edata>
d2:50 91 01 01 lds r21,0x0101; 0x800101 <_edata + 0x1>
d6:60 91 02 01 lds r22,0x0102; 0x800102 <_edata + 0x2>
da:70 91 03 01 lds r23,0x0103; 0x800103 <_edata + 0x3>
de:42 2b或r20,r18
e0:40 93 00 01 sts 0x0100,r20; 0x800100 <_edata>
e4:50 93 01 01 sts 0x0101,r21; 0x800101 <_edata + 0x1>
e8:60 93 02 01 sts 0x0102,r22; 0x800102 <_edata + 0x2>
ec:70 93 03 01 sts 0x0103,r23; 0x800103 <_edata + 0x3>
f0:01 97旋轉r24,0x01; 1個
而(1){
uint8_t val;
對於(int i = 0; i<25; i ++){
f2:99 f6 brne .-90; 0x9a <main + 0xa>
f4:d0 cf rjmp .-96; 0x96 <main + 0x6>
內部循環代碼現在已膨脹為29條指令,耗時49個週期,從而將最大可測量頻率降低至〜163 kHz。只需添加 static
關鍵字就足以使其慢5倍。但是,這是在較大的應用程序中使用代碼時可能期望的實際速度。
如果您需要盡可能快的速度而不受編譯器怪癖的影響,則可以使用3個選項:-
-
編寫精巧的彙編代碼,以最有效的方式使用每條指令(其他非關鍵代碼仍可以用C編寫)。
-
使用外圍硬件,如定時器/計數器或SPI。
-
添加一個外部芯片,例如預分頻器以分頻,或者添加一個移位寄存器(例如 CD4031)以捕獲波形。
ol>