Большое разнообразие ответов здесь ... в основном решение проблемы различными способами.
Более 25 лет я пишу встроенное низкоуровневое программное обеспечение и прошивки на разных языках - в основном на C (но с путями в Ada, Occam2, PL / M и множестве ассемблеров).
После долгих размышлений, проб и ошибок я остановился на методе, который довольно быстро получает результаты и довольно легко создает тестовые обертки и жгуты (где они ДОБАВЛЯЮТ ЗНАЧЕНИЕ!)
Метод идет примерно так:
Напишите модуль кода драйвера или аппаратной абстракции для каждого основного периферийного устройства, которое вы хотите использовать. Также напишите один, чтобы инициализировать процессор и настроить все (это создает дружественную среду). Как правило, на небольших встроенных процессорах (например, ваш AVR) может быть от 10 до 20 таких устройств, все маленькие. Это могут быть единицы для инициализации, аналого-цифрового преобразования в немасштабированные буферы памяти, побитовый вывод, ввод с помощью кнопки (без выборки, только выборка), драйверы широтно-импульсной модуляции, UART / простые последовательные драйверы, использующие прерывания и небольшие буферы ввода / вывода. Может быть еще несколько - например, драйверы I2C или SPI для EEPROM, EPROM или других устройств I2C / SPI.
Затем для каждого из модулей аппаратной абстракции (HAL) / драйвера я пишу тестовую программу. Это зависит от последовательного порта (UART) и инициализации процессора - поэтому первая тестовая программа использует только эти 2 модуля и выполняет только некоторые основные операции ввода и вывода. Это позволяет мне проверить, что я могу запустить процессор и что у меня есть базовая поддержка последовательного ввода-вывода поддержки отладки. Как только это сработает (и только тогда), могу ли я затем разработать другие тестовые программы HAL, построив их поверх известных хороших модулей UART и INIT. Поэтому у меня могут быть тестовые программы для чтения побитовых входных данных и отображения их в удобной форме (шестнадцатеричной, десятичной или любой другой) на моем терминале последовательной отладки. Затем я могу перейти к более крупным и более сложным вещам, таким как тестовые программы EEPROM или EPROM - я делаю большинство этих меню управляемыми, чтобы выбрать тест для запуска, запустить его и увидеть результат. Я не могу СКАРИТЬ это, но обычно я не
После того, как все мои HAL запущены, я нахожу способ получить регулярный таймер. Это обычно со скоростью от 4 до 20 мс. Это должно быть регулярно, генерируется в прерывании. Как правило, это можно сделать с помощью опрокидывания / переполнения счетчиков. Затем обработчик прерываний увеличивает размер семафора в байтах. На этом этапе вы также можете возиться с управлением питанием, если вам нужно. Идея семафора состоит в том, что если его значение> 0, вам нужно запустить «основной цикл».
EXECUTIVE запускает основной цикл. Он просто ожидает, что этот семафор станет ненулевым (я абстрагирую эту деталь). На этом этапе вы можете поиграть со счетчиками, чтобы подсчитать эти тики (потому что вы знаете частоту тиков), и поэтому вы можете установить флаги, показывающие, является ли текущий тик руководителя интервалом в 1 секунду, 1 минуту и другие общие интервалы, которые вы используете. Возможно, захотите использовать. Как только руководитель узнает, что семафор> 0, он выполняет один проход через каждую функцию «обновления» процессов «приложения».
Процессы приложения эффективно расположены рядом друг с другом и регулярно запускаются с помощью галочки «обновление». Это просто функция, вызываемая исполнительной властью. Это, по сути, многозадачность для бедняков с очень простой домашней ОСРВ, которая опирается на все приложения, входящие, выполняющие небольшую часть работы и выходящие. Приложения должны поддерживать свои собственные переменные состояния и не могут выполнять долгосрочные вычисления, потому что нет приоритетной операционной системы для обеспечения справедливости. Очевидно, что время выполнения приложений (совокупно) должно быть меньше, чем период основного тика.
Вышеупомянутый подход легко расширяется, поэтому вы можете добавить такие вещи, как стеки связи, которые работают асинхронно, и затем сообщения приложений могут быть доставлены приложениям (вы добавляете новую функцию для каждого, которая называется rx_message_handler), и вы пишете диспетчер сообщений, который показывает из какого приложения отправлять).
Этот подход работает практически для любой коммуникационной системы, которую вы хотите назвать - он может (и уже работал) для многих проприетарных систем, систем связи с открытыми стандартами, он даже работает для стеков TCP / IP.
Он также имеет преимущество в том, что он состоит из модульных элементов с четко определенными интерфейсами. Вы можете вставлять и вынимать части в любое время, заменяя их разными. В каждой точке пути вы можете добавить тестовые жгуты или манипуляторы, которые опираются на известные хорошие детали нижнего слоя (материал ниже). Я обнаружил, что примерно от 30% до 50% дизайна могут получить пользу от добавления специально написанных модульных тестов, которые обычно довольно легко добавляются.
Я сделал еще один шаг вперед (идея, которую я получил от кого-то еще, кто сделал это) и заменил слой HAL аналогом для ПК. Так, например, вы можете использовать C / C ++ и winforms или аналогичные на ПК и, написав код ВНИМАТЕЛЬНО, вы можете эмулировать каждый интерфейс (например, EEPROM = файл диска, считываемый в память ПК), а затем запускать все встроенное приложение на ПК. Возможность использования дружественной среды отладки может сэкономить огромное количество времени и усилий. Только действительно большие проекты могут оправдать такое количество усилий.
Вышеприведенное описание не является уникальным для того, как я работаю на встроенных платформах - я сталкивался с многочисленными коммерческими организациями, которые делают подобное. То, как это делается, обычно сильно отличается в реализации, но принципы часто во многом совпадают.
Я надеюсь, что вышеизложенное дает некоторую изюминку ... этот подход работает для небольших встроенных систем, которые работают в нескольких килобайтах с агрессивным управлением батареями, вплоть до монстров 100К или более линий источников, которые работают постоянно. Если вы используете «встроенный» в большой ОС, такой как Windows CE или около того, то все вышеперечисленное совершенно несущественно. Но это не РЕАЛЬНОЕ встроенное программирование, во всяком случае.