float bytes2float(char bytes[4]) { ... }
void Recall_DataSet(void) { ... }
void Sensor_init(void) { ... }
void uC_Sleep(void) { ... }
В таком порядке функции были объявлены в файле с исходным кодом. А теперь разделим их и сгруппируем по решаемым задачам:
○ функции, реализующие предметную логику:
• float calc_RPM(void) { ... }
• void Do_Average(void) { ... }
• void Get_Next_Measurement(void) { ... }
• void Zero_Sensor_1(void) { ... }
• void Zero_Sensor_2(void) { ... }
○ функции, обслуживающие аппаратную платформу:
• ISR(TIMER1_vect) { ... }*
• ISR(INT2_vect) { ... }
• void uC_Sleep(void) { ... }
○ функции, реагирующие на нажатия кнопок:
• void btn_Handler(void) { ... }
• void Dev_Control(char Activation) { ... }
○ функция, читающая данные из аппаратного аналогово-цифрового преобразователя:
• static char Read_RawData(void) { ... }
○ функции, записывающие значения в долговременное хранилище:
• char Load_FLASH_Setup(void) { ... }
• void Save_FLASH_Setup(void) { ... }
• void Store_DataSet(void) { ... }
• float bytes2float(char bytes[4]) { ... }
• void Recall_DataSet(void) { ... }
○ функция, которая не делает того, что подразумевает ее имя:
• void Sensor_init(void) { ... }
Заглянув в другие файлы этого приложения, я нашел множество препятствий, мешающих пониманию кода. Также я обнаружил, что организация файлов подразумевает единственный способ тестирования этого кода — непосредственно внутри целевого устройства. Практически каждый бит этого кода знает, что относится к специализированной микропроцессорной архитектуре, используя «расширенные» конструкции языка C[58], привязывающие код к конкретному набору инструментов и микропроцессору. У этого кода нет ни малейшего шанса служить долго, если будет решено перенести продукт на другую аппаратную платформу.
Приложение работает: инженер прошел тест на профпригодность. Но нельзя сказать, что оно имеет чистую встраиваемую архитектуру.
Привязка к оборудованию — узкое место
Существует масса особых проблем, с которыми приходится сталкиваться разработчикам встраиваемых систем и не знакомых разработчикам обычного программного обеспечения. Например, ограниченный объем памяти, ограничения по времени выполнения операций, ограниченные возможности ввода/вывода, нетрадиционные пользовательские интерфейсы, а также наличие датчиков и контактов, соединяющих с внешним миром. В большинстве случаев аппаратное обеспечение развивается параллельно с программным обеспечением и микропрограммами. У вас, как инженера, разрабатывающего код для такого рода систем, может не быть места для запуска кода. Но это еще не самое худшее — полученное оборудование может иметь собственные недостатки, что замедляет разработку программного обеспечения больше, чем обычно.
Да, встраиваемое программное обеспечение имеет свои особенности, и инженеры встраиваемых систем — особые люди. Но разработка встраиваемых систем не настолько особенная, чтобы принципы, описываемые в этой книге, нельзя было применить к встраиваемым системам.
Одна из особых проблем встраиваемых систем —
Чистая встраиваемая архитектура — архитектура, поддерживающая тестирование
Давайте посмотрим, как применяются некоторые архитектурные принципы к встраиваемому программному обеспечению и микропрограммам и как они помогают избавиться от тесной привязки к оборудованию.
Деление на уровни можно произвести разными способами. Начнем с трехуровневой организации, изображенной на рис. 29.1. Внизу находится уровень оборудования. Как предупреждает Дуг, вследствие совершенствования технологий и согласно закону Мура оборудование будет изменяться. Одни компоненты устаревают, и на смену им приходят новые компоненты, потребляющие меньше электроэнергии, или имеющие более высокую производительность, или стоящие дешевле. Независимо от причин я, как инженер встраиваемых систем, не хотел бы делать больше, чем необходимо, когда неизбежное изменение оборудования наконец произойдет.
Рис. 29.1. Три уровня