Прерывание занимает больше времени, чем ожидалось

Я пытаюсь написать функцию задержки на языке Си, которая должна занять 0,5 секунды, а затем продолжить. Я знаю, что для этой цели существуют библиотеки, я хочу сделать это сам.

#include <avr/interrupt.h>
#include <avr/io.h>
#define bitset(var,bitno) ((var) |= (1 << (bitno)))

int volatile time_unit;

ISR(TIMER0_COMPA_vect) {

  time_unit += 1;
}

void wait_func(){
  TCNT0 = 0;
  time_unit = 0;
  while (time_unit < 31);
}


int main(void){
  DDRD = 1;
   cli();
  bitset(TCCR0A, WGM01);
  bitset(TCCR0B, CS00);
  bitset(TCCR0B, CS02);
  OCR0A = 0xFF;
  bitset(TIMSK0, OCIE0A);
  sei();

  while(1){
    PIND = 0;
    wait_func();
    PIND = 1;
    wait_func();
  };
}

Таймер находится в режиме 1024 прескалера в режиме CTC, отсчет времени 31*255 (OCR0A = 0xFF) должен дать около 0,5 секунды при базовой тактовой частоте 16 МГц Arduino Atmega328p. Код работает, но он включает и выключает светодиод со скоростью 1 секунда (1 сек вкл. 1 сек. выкл.). Я не понимаю, почему.

, 👍2


1 ответ


Лучший ответ:

3
  • PIND = 1; переключает состояние бита 0 портаD.
  • PIND = 0; вообще ничего не делает; никакого переключения каких-либо контактов.

Так что вы просто переключаетесь не так часто, как вам кажется. В остальном ваша задержка сама по себе прекрасна. Измените значение PIN = 0; на значение PIN = 1; и оно будет работать так, как ожидалось. Сделав это, вы можете избавиться от одного набора вызовов PIND = 1; и wait_func ();.

В качестве альтернативы, если вы хотите следовать текущей форме кода, установите и очистите бит непосредственно в PORTD:

while(1){
  PORTD &= ~1; // or &= 0xFE
  wait_func();
  PORTD |=  1; 
  wait_func();
}

Как бы то ни было, это того стоит:

  • Точка с запятой после вашего цикла while ничего не делает.
  • time_unit, вероятно, должен быть unsigned char / uint8_t.
  • Если у вас действительно мало места в коде, вероятно, лучше не полагаться на предыдущее состояние аппаратных регистров таймера.
,

Извините за , возможно, тривиальный вопрос, но я всегда думал, что PIN=1 загружает бит 0b00000001 для регистрации PIN, почему он переключается?, @ibroketheinternet

Именно так спроектированы регистры AVR GPIO. Это позволяет немного изменить одну запись, а не выполнять набор инструкций по чтению, изменению и записи. ПИНДназывает в духе чтения из порта., @timemage

Верно ли это для каждого регистра GPIO?, @ibroketheinternet

Я думаю, что да, если я понимаю, о чем вы спрашиваете. Для других портов GPIO на ATMege328P запись " 1 "в битовом индексе "PINx" приводит к переключению бита с тем же индексом в соответствующем регистре "PORTx". Многие (не все) другие части AVR следуют той же схеме для своих портов., @timemage

См. Страницу 60 в [ATmega328P datasheet](https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf)., @StarCat