#endif
Использование stdint.h во встраиваемом программном обеспечении и микропрограммах поможет вам сохранить код чистым и переносимым. Всякое программное обеспечение должно быть независимым от типа процессора, но этот совет годится не для всякой микропрограммы. В следующем фрагменте кода используются особые расширения языка C, позволяющие коду обращаться к периферийным устройствам в микроконтроллере. Продукт может быть оснащен этим микроконтроллером, поэтому вы можете использовать интегрированные в него периферийные устройства. Следующая функция выводит в последовательный порт строку с текстом "hi". (Этот пример основан на реальном коде.)
void say_hi()
{
IE = 0b11000000;
SBUF0 = (0x68);
while(TI_0 == 0);
TI_0 = 0;
SBUF0 = (0x69);
while(TI_0 == 0);
TI_0 = 0;
SBUF0 = (0x0a);
while(TI_0 == 0);
TI_0 = 0;
SBUF0 = (0x0d);
while(TI_0 == 0);
TI_0 = 0;
IE = 0b11010000;
}
Эта маленькая функция страдает множеством проблем. Первое, что бросается в глаза, — присутствие последовательности символов 0b11000000. Такая форма записи двоичных чисел очень удобна, но поддерживается ли она стандартным языком C? К сожалению, нет. Еще несколько проблем проистекают непосредственно из использования нестандартных расширений:
• IE: устанавливает биты разрешения прерываний.
• SBUF0: буфер вывода последовательного порта.
• TI_0: прерывание опустошения буфера передачи последовательного порта. Если операция чтения возвращает 1, это указывает, что буфер пуст.
Переменные с именами, состоящими из букв верхнего регистра, в действительности представляют механизмы доступа к встроенной периферии микроконтроллера. Если программе понадобится управлять прерываниями и выводить символы, вам придется использовать эту периферию. Да, это удобно, но это уже не язык C.
Чистая архитектура будет напрямую использовать эти средства доступа к периферии лишь в нескольких местах и только в пределах микропрограммы. Любой код, знающий о существовании регистров, автоматически превращается в микропрограмму и, соответственно, оказывается тесно связанным с конкретным оборудованием. Тесная связь кода с оборудованием мешает получить действующий код до получения стабильно работающего оборудования. Она также будет мешать переносить встраиваемое приложение на новый процессор.
Если вы используете микроконтроллер, подобный этому, ваша программа могла бы спрятать эти низкоуровневые функции за слоем абстракций процессора (Processor Abstraction Layer; PAL). Часть микропрограммы, находящаяся над слоем PAL, могла бы проверять платформу, на которой выполняется, и таким способом ослабить жесткость кода.
Операционная система — это деталь
Слой аппаратных абстракций (HAL) является насущной необходимостью, но достаточно ли его? Во встраиваемых системах, где отсутствует другое программное окружение, слоя HAL более чем достаточно, чтобы оградить код от избыточной зависимости от операционной среды. Но что, если встраиваемая система использует некоторую операционную систему реального времени (RealTime Operating System; RTOS) или встраиваемую версию Linux или Windows?
Чтобы дать коду шанс служить долго, операционную систему следует рассматривать как деталь и защищаться от зависимостей, связывающих с ней.
Программное обеспечение обращается к операционному окружению посредством операционной системы (ОС). ОС — это слой, отделяющий программное обеспечение от микропрограмм (рис. 29.5). Прямое использование механизмов ОС может стать источником проблем. Например, представьте,
Рис. 29.5. Дополнительный слой операционной системы
что производителя вашей RTOS купила другая компания и из-за этого выросла стоимость системы или упало ее качество. Или ваши потребности изменились, а используемая вами RTOS не обладает необходимыми возможностями. Вам придется изменить много кода. И это будут не просто синтаксические изменения, обусловленные сменой API, скорее всего, вам придется приспосабливать семантику кода к различным механизмам и примитивам новой ОС.
Чистая встраиваемая архитектура изолирует программное обеспечение от операционной системы, реализуя слой абстракции операционной системы (Operating System Abstraction Layer; OSAL), как показано на рис. 29.6. В некоторых случаях этот слой может иметь очень простую реализацию, выражающуюся в простой подмене имен функций. Но иногда может потребоваться полное обертывание некоторых функций.
Если вам доводилось переносить программное обеспечение с одной RTOS на другую, вы знаете, насколько трудно это дается. Если ваше программное обеспечение зависит только от слоя OSAL, но не зависит от ОС, вам потребуется только написать новый слой OSAL, совместимый с прежним. Что бы вы предпочли: изменить кучу сложного кода или написать новый код, определяющий интерфейс и поведение? Это даже не спорный вопрос. Я выбираю последнее.
Вас может обеспокоить проблема разбухания кода. Однако вам не о чем беспокоиться. Большая часть повторяющегося кода, обусловленного использованием операционной системы, будет сосредоточена в слое абстракции. Такой повторяющийся код не будет вызывать больших накладных расходов.
Рис. 29.6. Слой абстракции операционной системы
Определяя слой OSAL, вы также получаете возможность привести свои приложения к некоторой общей структуре. Например, вы могли бы реализовать механизмы передачи сообщений и отказаться от ручного управления конкурентным выполнением в каждом потоке.
Слой OSAL может помочь создать точки для тестирования, чтобы прикладной программный код можно было тестировать без использования целевого оборудования и ОС. Программное обеспечение с чистой встраиваемой архитектурой поддерживает возможность тестирования вне целевой операционной системы. Удачно спроектированный слой OSAL предоставляет тот шов, или набор, точек подстановки, которые облегчат тестирование вне целевой среды.
Программирование с применением интерфейсов и возможность подстановки