Прямые манипуляции с портом по какой-то причине выполняются за 300 наносекунд.

У меня есть этот код

#include <Arduino.h>




void setup() {
  
  DDRD  = B00001000;

}
void delayNanoSeconds(int time){
    int ans = (float)time / 62.5;
    for(int i = 0; i < ans; i++){
        _NOP();


    }


}
#define delay250 _NOP(); _NOP();_NOP(); _NOP();
#define delay1000 delay250 delay250 delay250 delay250
void loop() {
  while(1){
       PORTD ^= B00001000;
       delay1000; delay1000;
  }                
 
  }

но я получаю задержку 2300 us вот картинка логического анализатора и когда я изменяю задержку на 250 или 1000, я все еще получаю эти дополнительные 300 нс, так почему же

, 👍3


1 ответ


6

Помните: все, что делает ЦП, требует времени. Вот разборка вашего loop():

    ldi  r25, 0b00001000 ; r25 = B00001000
1:  in   r24, PORTD      ; r24 = PORTD
    eor  r24, r25        ; r24 ^= r25
    out  PORTD, r24      ; PORTD = r24
    nop
    nop
    ...
    nop
    rjmp 1b              ; goto previous label 1

Как видите, кроме набора nop, в цикл:

  • in, eor и out занимают по одному циклу каждый
  • rjmp занимает два цикла.

Общие накладные расходы составляют 5 циклов ЦП или 312,5 нс при работает на частоте 16 МГц. Довольно близко к тому, что вы видите.

Если вам нужны точные тайминги, вам придется дизассемблировать код и подсчитайте накладные циклы, как я сделал здесь. Тогда я предлагаю это проще подход:

#define OVERHEAD_CYCLES 5

void loop() {
    for (;;) {
        PORTD ^= _BV(PD3);
        _delay_us(2 - OVERHEAD_CYCLES / (F_CPU / 1e6));
    }
}
,

Ок спасибо за помощь, @Abdrlrahman

Кстати как вы увидели ассемблерный код, @Abdrlrahman

@Abdrlrahman: «Как вы увидели код сборки»: avr-objdump -SCz sketch.elf > sketch.lss. Вы должны увидеть расположение файла ELF, если вы включите подробную компиляцию. Инструмент avr-objdump находится где-то в вашей установке Arduino, но точное местоположение может зависеть от вашей ОС и установленной версии Arduino. Возможно, вам придется поискать его., @Edgar Bonet

Кстати не должно быть ` _delay_us(2 - OVERHEAD_CYCLES * (F_CPU / 1e6)); ` вместо /, @Abdrlrahman

@Abdrlrahman: Нет, это действительно разделение. В итоге получается 2 − 5/16,0 = 1,6875 мкс., @Edgar Bonet

2 - OVERHEAD_CYCLES/(F_CPU/1e6), конечно, должен оцениваться во время компиляции. Таким образом, 2 нельзя добавить во время выполнения. Интересно, что _delay_us имеет параметр с плавающей точкой, по сравнению с delayMicroseconds(unsigned int) в Arduino., @DataFiddler