Прерывание занимает больше времени, чем ожидалось
Я пытаюсь написать функцию задержки на языке Си, которая должна занять 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 сек. выкл.). Я не понимаю, почему.
1 ответ
Лучший ответ:
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
.- Если у вас действительно мало места в коде, вероятно, лучше не полагаться на предыдущее состояние аппаратных регистров таймера.
- ATmega328P - проблема с использованием таймера 2 для генерации тона
- Точность синхронизации Arduino nano
- Интервальный таймер на Arduino: Сомнения по поводу библиотеки TimerOne
- Можно ли отсоединить прерывание на определенное время
- Заставить TCNT оставаться ниже OCRxA на ATmega328P
- Проблема с таймером 0
- Эмуляция Arduino Uno с помощью QEMU: прерывания не работают
- Безопасное расширение TCNT1/ICR1 до 24 бит с помощью TOV1
Извините за , возможно, тривиальный вопрос, но я всегда думал, что 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