Обработка ошибок

Язык Си напрямую не поддерживает обработку ошибок, в нём нет понятия исключений (англ. exception), хотя их и можно на нём реализовать. Стандартная библиотека предлагает другой способ — через глобальный макрос errno (модуль <errno.h>), который ведёт себя по сути как переменная: при инициализации программы туда записывается 0. Если при работе функции произошла какая-нибудь ошибка, её код помещается туда (справедливо для стандартной библиотеки).

Согласно стандарту, библиотека должна определять только три возможных ошибки (EDOM, EILSEQ и ERANGE), но она расширяется стандартом POSIX. Все эти дополнительные ошибки полезны при работе с файловой системой, сетью и всем тем, что есть в полноценной системе, но не во встраиваемых системах. Поэтому имеет смысл определить собственное перечисление и работать с ним через значение возврата функции, как это сделано, например, в библиотеке HAL.

typedef enum {
    SUCCESS = 0U,
    ERROR = !SUCCESS
} ErrorStatus;
// ...
ErrorStatus LL_TIM_Init(TIM_TypeDef *TIMx, LL_TIM_InitTypeDef *TIM_InitStruct) { /* ... */ }

Железо

Не все ошибки могут быть напрямую связаны с кодом программы. Когда вы пишете драйвер для какого-нибудь датчика, то вы скорее всего не думаете о том, что его можно физически оторвать от платы. Если создаваемая вами система принимает решение на основе данных с датчика, то позволять устройству выходить в рабочий режим при его отсутствии неправильно и скорее всего небезопасно. Запуск атомного реактора без обратной связи почти наверняка приведёт к аварии, как и отказ каких-либо датчиков прямо во время работы.

Космос — достаточно агрессивная среда. Высокоэнергетические частицы из другой галактики (ха-ха) запросто прошьют насквозь микросхему и устроят там короткое замыкание.

Например, популярный температурный датчик DS18B20, если он присутствует на шине, перед приёмом команды отправляет импульс приветствия. Для простоты реализации драйвера проверку присутствия можно опустить, но это совершенно недопустимо для критически важных систем.

Во всех критических системах, будь то атомный реактор, самолёт или космический аппарат, применяют резервирование по мажоритарной схеме. То есть используется не один датчик, а скажем, три. При этом решение принимается на основании суммы выходов: если показания двух из трёх датчиков совпадают, то таким данным можно доверять. Резервирование можно встретить и в биологических системах: у вас два глаза, уха, лёгких и две почки.

DS18B20_STATUS_t ds18b20_get_temperature(uin32_t* temp);
// ...
if (ds18b20_read_temperature(temperature) == DS18B20_PRESENT) {
    // everithing is O.K.
} else {
    // handle it somehow
}

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

Не доверяйте показаниям датчиков, доверяйте физическим принципам.

Железо может сбоить и программа должна это учитывать. Проверяйте входные данные — если датчик выдаёт показания которые за пределами его паспортных значений, то наверняка что-то идёт не так. Если значения противоречат физическим принципам, то таким данным доверять нельзя.

В октябре 2018 года потерпел крушение Boeing 737 MAX8, 189 человек погибли. Через несколько месяцев, в марте 2019, самолёт той же модели унёс жизни ещё 157 человек. Причин было несколько – от экономических, до архитектурных и программных. Airbus обновил свой A320 (A320NEO) поставив более экономичный двигатель (потреблял на 15% меньше топлива) и продажи Boeing стали падать, нужно было дать ответ. Но Boeing не мог установить новый двигатель просто так – не было достаточно места под фюзеляжем. Инженеры приняли решение поднять двигатель выше уровня крыла, из-за чего изменились полётные характеристики – нос самолёта стал опасно задираться при взлёте. Для решения этой проблемы Boeing внёс изменения в программный модуль MCAS, который принудительно уменьшал угол атаки опираясь всего на один датчик AOA (Angle-of-Attack). В обоих случаях AOA показывал неверные значения. Не смотря на то, что использование всего одного датчика вместо мажоритарной системы плохо, из ситуации можно было выйти, опираясь на данные с других датчиков (скорости, ускорения) верифицируя реалистичность показаний AOA. // How MCAS was Born 737 max 8 — Prof Simon


Изменено: