Часть 19: Оптимизация задержек — от _delay_ms() к ассемблеру

Стандартная функция _delay_ms() использует циклы, которые считают такты. Но она блокирует процессор и не позволяет делать ничего другого.

Хак: Используем прерывания таймера для создания неблокирующих задержек.

Код на ассемблере для таймера 0:

#define __SFR_OFFSET 0x00
#include <avr/io.h>

.global timer0_init
.global timer0_reset

timer0_ms_counter: .byte 1

timer0_init:
    ; Настраиваем таймер 0 на прерывание каждые 1 мс
    LDI R16, 249           ; OCR0A = 249 (16 МГц / 64 / 250 = 1000 Гц)
    OUT OCR0A, R16
    
    LDI R16, (1 << WGM01)  ; Режим CTC
    OUT TCCR0A, R16
    
    LDI R16, (1 << CS01) | (1 << CS00)  ; Прескалер 64
    OUT TCCR0B, R16
    
    LDI R16, (1 << OCIE0A) ; Включаем прерывание по совпадению
    OUT TIMSK0, R16
    
    CLR R16
    STS timer0_ms_counter, R16
    RET

; Обработчик прерывания TIMER0_COMPA_vect
.global __vector_TIMER0_COMPA
__vector_TIMER0_COMPA:
    PUSH R16
    LDS R16, timer0_ms_counter
    INC R16
    STS timer0_ms_counter, R16
    POP R16
    RETI

timer0_reset:
    CLR R16
    STS timer0_ms_counter, R16
    RET

; Функция получения миллисекунд
.global timer0_get_ms
timer0_get_ms:
    LDS R16, timer0_ms_counter
    RET

Использование в C++:

extern "C" {
    void timer0_init();
    void timer0_reset();
    uint8_t timer0_get_ms();
}

void setup() {
    Serial.begin(115200);
    timer0_init();
    sei();
    timer0_reset();
}

void loop() {
    if (timer0_get_ms() >= 1000) {
        Serial.println("Прошла 1 секунда!");
        timer0_reset();
    }
    // CPU свободен для других задач!
}

Вам также может понравиться

About the Author: ардуинчиков

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *