1. В сообществе нашего форума Вконтакте создан раздел по продаже электронных компонентов.
    Каждый может продать в нем свои залежавшиеся детали. Подробности здесь.

Туториал ChibiOS/HAL: объектно-ориентированный подход

Тема в разделе "ChibiOS/RTOS", создана пользователем radioengineer, 2 май 2017.

  1. radioengineer

    radioengineer Администратор Команда форума

    Сообщения:
    3.559
    Симпатии:
    354
    Адрес:
    Алматы
    1. HAL означает Hardware Abstraction Layer (Слой абстракции от железа)

    Мы уже познакомились с ChibiOS/HAL в более раннем туториале "Знакомство с ChibiOS" и далее мы будем пользоваться HAL во всех статьях по ChibiOS. Довольно часто мы игнорируем то, как этот слой абстракции работает изнутри, ведь, чтобы с ним работать достаточно просто прочесть документацию. Эта статья будет полезна тем, кто хочет немного углубиться в детали реализации и узнать как эта "штука" работает. Дополнение: начиная с версии ChibiOS 3.0, ChibiOS/HAL стал по сути отдельным продуктом, который не привязан жестко к самой ОС и может использоваться как на голом железе, так и в связке с любыми другими ОСРВ.
    ChibiOS/HAL стремится предоставить аппаратную абстракцию, позволяющую очень гибко использовать периферию контроллера без ущерба функциональности. По словам Giovanni Di Sirio, разработчика проекта ChibiOS, HAL претерпел множество изменений в следствие запросов сообщества. После нескольких лет разработки и исправления багов, он достиг высокого уровня стабильности, хорошего уровня абстракции и, несомненно, превосходного уровня гибкости.
    Для достижения всего этого HAL имеет:
    - Сокрытие сложной реализации драйверов, позволяющей тем не менее производить аппаратно-зависимую настройку, таким образом пользователь может адаптировать драйвер для целей своего приложения;
    - Обеспечивает аппаратно-независимый высокоуровневый API, сохраняя код приложения настолько переносимым насколько это возможно;
    - Существенная оптимизация и неблокирующий API. Эти вещи делают HAL отличным решением для применения в системах реального времени;
    - Возможность работать в режим stand-alone, без ОС;
    - Поддержка наиболее часто используемой периферии: ADC, CAN, DAC, GPIO, ICU, PWM, SPI, TIM, UART, USB и многое другое.

    2. Достижение абстракции за счет многоуровневой архитектуры

    HAL организован многоуровневым образом, а точнее имеет два уровня. Можно представить эту архитектуру как стену из LEGO, доступ к которой мы можем получить только с верхнего кубика. Так как все кубики связаны, то косвенно мы можем получить доступ и к более нижним уровням.
    В то время как верхний уровень API остается всегда постоянным, меняется второй нижний уровень абстракции (взаимодействия с железом). Так драйвер может работать с совершенно различным аппаратным обеспечением с постоянным API. Для того, чтобы понять эти изменения на нижних уровнях абстракции, давайте внимательно взглянем на иерархию HAL (рисунок 1 - ChibiOS/HAL иерархия).
    [​IMG]
    Верхний уровень HAL содержится в папках include и src: первая содержит заголовочные файлы acd.h, spi.h, hal.h и т.д., вторая файлы исходных текстов acd.c, spi.c, hal.c и т.д. Нижний уровень содержится в папке ports и сгруппирован в подпапках по архитектурам: они и представляют низкоуровневый драйвер.
    Изучив содержимое папки ports, мы можем понять как работает многоуровневая архитектура. Для примера откроем папку STM32 и видим внутри папки для каждой платформы. Посмотрим в две подпапки STM32F3xx и STM32F4xx (рисунок 2 - STM32F3 и STM32F4 LLD папки).
    [​IMG]
    Обе папки содержат файлы, названные таким же образом. Открывая файлы, можем обнаружить что LLD файлы имеют идентичное API, таким образом уровень выше может быть реализован без дополнительных осложнений. Очевидно, что за исключением API реализация LLD различна для разных типов платформ. Переключение между LLD осуществляется за счет различных файлов platform.mk.
    Добавление поддержки новой архитектуры означает создание поддиректории в папке port и создание LLD совместимого файла. Обратите внимание, что в папке STM32/LLD содержатся файлы общие для каждой архитектуры STM32.
    ChibiOS/HAL может использоваться отдельно без ОС. Все потому что в версии 3.0 нет вызовов к API ядра, все вызовы заменены на вызовы к OSAL (Operating System Abstraction Layer) API. Проект ChibiOS обеспечивает уровень абстракции для NIL и RT в папке osal. Разработав свой OSAL API можно интегрировать HAL в любую другую ОСРВ.
    В папке templates находится шаблон для создания нового драйвера. Этот шаблон представляет из себя минимальную структуру, которая является стандартом для всех драйверов.
    В предыдущей версии ChibiOS эти шаблоны назывались meta-template. Существует высокоуровневый meta-template и низкоуровневый meta-template (смотри вложение).
    Для дополнительной информации можно прочесть о хорошо организованной иерархии в статье "Программируем на С для встраиваемых приложений: советы и подсказки"

    3. Преимущества объектно-ориентированного подхода

    Несмотря на отсутствие объектов в языке С ChibiOS/HAL дизайн можно считать объектно-ориентированным: драйвера представлены структурой и каждый API вызов требует указатель для доступа к драйверу. Поскольку эта структура содержит почти всю информацию о драйвере, то для API не требуется длинный список параметров.
    В качестве примера рассмотрим модуль SPI. В файле spi_lld.h находим определение структуры SPIDriver, которая представляет SPI драйвер. Ниже можно найти несколько строк с объявлением внешних переменных, которые звучат очень знакомо:
    PHP:
    #if STM32_SPI_USE_SPI1 && !defined(__DOXYGEN__)
    extern SPIDriver SPID1;
    #endif

    #if STM32_SPI_USE_SPI2 && !defined(__DOXYGEN__)
    extern SPIDriver SPID2;
    #endif

    #if STM32_SPI_USE_SPI3 && !defined(__DOXYGEN__)
    extern SPIDriver SPID3;
    #endif
    Обратите внимание что STM32_SPI_USE_SPIX одно из определений, которое объявляется в mcuconf.h. Сам драйвер реализован в соответствующем файле spi_lld.c. Аппаратно-зависимые конфигурации также сгруппированы в структурах, созданных как объекты, например, SPIConfig в файле spi_lld.h. Среди других полей драйвера есть переменные типа xxxstate_t (в нашем случае spistate_t). Это потому что HAL спроектирован как автомат с конечными состояниями, а это дает большое преимущество, т.к. мы можем знать в каком состоянии находится наш драйвер.
    [​IMG]
    Взглянем на конечные состояния SPI (Рисунок 3 - SPI FSM диаграмма), мы не можем вызвать функцию spiStart() на SPID1, если драйвер в состоянии SPI_UNINIT. Каждый драйвер должен быть проинициализирован входом в состояние XXX_STOP: эта операция выполняется функцией xxxInit(). Эта функция инициализации для каждого драйвера и она вызывается другой функцией, которая должна быть знакома всем кто использует ChibiOS: halInit().
    PHP:
    /**
    * @brief   HAL initialization.
    * @details This function invokes the low level initialization code then
    *          initializes all the drivers enabled in the HAL. Finally the
    *          board-specific initialization is performed by invoking
    *          @p boardInit() (usually defined in @p board.c).
    *
    * @init
    */
    void halInit(void) {

      
    /* Initializes the OS Abstraction Layer.*/
      
    osalInit();

      
    /* Platform low level initializations.*/
      
    hal_lld_init();

    #if (HAL_USE_PAL == TRUE) || defined(__DOXYGEN__)
      
    palInit(&pal_default_config);
    #endif
    #if (HAL_USE_ADC == TRUE) || defined(__DOXYGEN__)
      
    adcInit();
    #endif
    #if (HAL_USE_CAN == TRUE) || defined(__DOXYGEN__)
      
    canInit();
    #endif
    #if (HAL_USE_DAC == TRUE) || defined(__DOXYGEN__)
      
    dacInit();
    #endif
    #if (HAL_USE_EXT == TRUE) || defined(__DOXYGEN__)
      
    extInit();
    #endif
    #if (HAL_USE_GPT == TRUE) || defined(__DOXYGEN__)
      
    gptInit();
    #endif
    #if (HAL_USE_I2C == TRUE) || defined(__DOXYGEN__)
      
    i2cInit();
    #endif
    #if (HAL_USE_I2S == TRUE) || defined(__DOXYGEN__)
      
    i2sInit();
    #endif
    #if (HAL_USE_ICU == TRUE) || defined(__DOXYGEN__)
      
    icuInit();
    #endif
    #if (HAL_USE_MAC == TRUE) || defined(__DOXYGEN__)
      
    macInit();
    #endif
    #if (HAL_USE_PWM == TRUE) || defined(__DOXYGEN__)
      
    pwmInit();
    #endif
    #if (HAL_USE_SERIAL == TRUE) || defined(__DOXYGEN__)
      
    sdInit();
    #endif
    #if (HAL_USE_SDC == TRUE) || defined(__DOXYGEN__)
      
    sdcInit();
    #endif
    #if (HAL_USE_SPI == TRUE) || defined(__DOXYGEN__)
      
    spiInit();
    #endif
    #if (HAL_USE_UART == TRUE) || defined(__DOXYGEN__)
      
    uartInit();
    #endif
    #if (HAL_USE_USB == TRUE) || defined(__DOXYGEN__)
      
    usbInit();
    #endif
    #if (HAL_USE_MMC_SPI == TRUE) || defined(__DOXYGEN__)
      
    mmcInit();
    #endif
    #if (HAL_USE_SERIAL_USB == TRUE) || defined(__DOXYGEN__)
      
    sduInit();
    #endif
    #if (HAL_USE_RTC == TRUE) || defined(__DOXYGEN__)
      
    rtcInit();
    #endif

      /* Community driver overlay initialization.*/
    #if defined(HAL_USE_COMMUNITY) || defined(__DOXYGEN__)
    #if (HAL_USE_COMMUNITY == TRUE) || defined(__DOXYGEN__)
      
    halCommunityInit();
    #endif
    #endif

      /* Board specific initialization.*/
      
    boardInit();

    /*
    *  The ST driver is a special case, it is only initialized if the OSAL is
    *  configured to require it.
    */
    #if OSAL_ST_MODE != OSAL_ST_MODE_NONE
      
    stInit();
    #endif
    }
    Вспоминая как мы используем драйвера HAL, мы можем сказать, что нам обычно нужно объявить структуру конфигурации драйвера и передать указатель на нее в качестве параметра для функции xxxStart(). Адрес структуры конфигурации далее передается процедурам, которые позволяют нам работать с драйвером. Некоторая периферия требует повторения полного цикла конфигурации, это объясняет почему в ChibiOS нам нужно останавливать иногда драйвер и снова его конфигурировать.
    Инициализируя драйвер мы занимаем часть пространства в памяти, но это не увеличивает энергопотребление. Поэтому очень важно останавливать драйвер, если он уже не используется. Управление питанием производится через хорошо спроектированные драйвера HAL.
    Кроме того, ChibiOS/HAL уменьшает используемую память, убирая из скомпилированного кода каждый неиспользуемый драйвер, запрещая использование драйвера в halconf.h мы отключаем весь связанный с этим драйвером код, несмотря на директивы препроцессора.

    Вложения:

    Yuri нравится это.
     

Поделиться этой страницей