Линейная программа на главном цикле

Программа на главном цикле проста в реализации и хорошо подходит в тех случаях, когда функциональность устройства сильно ограничена. Допустим, устройство собирает показания с датчиков и отправляет их на некоторое центральное устройство, которое, в свою очередь, принимает решение. Городить сложную прошивку (англ. firmware) с применением операционной системы не имеет практического смысла — программа линейна, а ее разработка и отладка не займет много времени. К тому же в простых устройствах целесообразно использовать дешевые микроконтроллеры, у которых объем памяти незначителен, а добавление лишнего кода приведет к тому, что прошивка может не поместиться в память. Как следствие, дополнительный код скажется на производительности системы (а значит, и на энергопотреблении), что вкупе с низкой тактовой частотой может оказаться критичным фактором.

Устройство закончит свою работу, как только будет выполнена последняя операция в функции main(). Перезапустить программу в таком случае можно лишь ножкой сброса или манипуляцией с питанием.

// прошивка самого глупого устройства
#define LED_ON_TIME      1000
#define LED_OFF_TIME     1000
int main(void) {
    led_on();
    delay_ms(LED_ON_TIME);
    led_off();
    delay_ms(LED_OFF_TIME);
    return 0;
}

Структура программы подразумевает использование главного цикла — он-то и обеспечивает непрерывную работу. Перепишем код так, чтобы работа программы не завершалась.

int main(void) {
    init_mcu();
    
    while(1) {
        led_on();
        delay_ms(LED_ON_TIME);
        led_off();
        delay_ms(LED_OFF_TIME);
    }
}

Программа будет работать бесконечно долго (пока есть питание), а светодиод загораться с периодичностью в две секунды.

Но как обрабатывать событие, например, нажатия кнопки? Допустим, когда кнопка не нажата, напряжение на входе (определенной ножке МК) близко к 0 В, а когда нажата — к 3,3 В. В таком случае для обработки нажатия достаточно считать состояние соответствующего регистра (англ. pooling) и отреагировать на его изменение (т.е. на событие).

#define IS_PRESSED      ((GPIOA->IDR & GPIO_IDR_1) == GPIO_IDR_1)
// ...
if (IS_PRESSED) {       // is the button pressed?
    while(IS_PRESSED);  // waiting until button is released
    led_on();
} else {
    led_off();
}

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

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

В микроконтроллере имеется такая сущность, как прерывания — мы уже говорили о модуле, отвечающем за них, в разделе про архитектуру ARM. Самое время рассмотреть другой подход.


Изменено: