среда, 17 апреля 2013 г.

STM32F407(STM32F4-DISCOVERY) - Нестандартный подход - Базовый проект

С этой статьи я начинаю приводить практические примеры. И первый из них будет - создание базового проекта. Для чего я это делаю? Ответ прост: чтобы упростить сборку нужной мне программы. Структура этого проекта рассчитана на полную автономность по отношению к другим заголовочным файлам и библиотекам, поэтому он достаточно переносим.
Также следует заметить, что в том решении которое я выбрал, есть два больших плюса:
  1. Проект расширяем, т.е. вы можете легко добавить в него как простой код, так и целые библиотеки.
  2. Изменением минимального количества файлов можно изменять модель МК (чуть позднее я проработаю этот момент получше, чтобы можно было менять семейства МК).
Рассмотрим поподробнее этот проект, а также некоторые технические моменты.
Все исходные коды которые относятся к этому циклу статей (да и другое чем не жалко поделиться) я храню в системе GitHub. Поэтому для читателя важно понимать как работает система контроля версий git, или хотя бы уметь ею пользоваться.

Файлы проекта постоянно обновляются, поэтому тут я покажу лишь значимые части своего кода. Полную версию можно получить из репозитория stm32f407.

Базовый проект называется base (КО). Вот его структура:
  • inc/ - общие заголовочные файлы
  • lib/ - всё, что касается готовых библиотек
  • src/ - собственно исходные коды
  • Makefile - основной файл для сборки, очистки и загрузки бинарной версии программы в МК.
  • stm32f4xxxg_flash.ld - скрипт для задания регионов памяти, нужен сборщику (ld), для того чтобы правильно собрать бинарную версию программы.
Рассмотрим поподробней каждый из этих элементов в порядке иерархии.

Makefile
Для управления сборкой, очисткой и загрузкой проекта используется система make. Перейдя по ссылке можно узнать как она работает, я же сконцентрирую внимание на некоторых особенностях моей версии системы сборки:
  1. # Project root path
    ROOTDIR=$(realpath .) - данная опция указывает что считать корнем проекта, очень удобно дабы не плодить в каждом звене иерархии проекта тучу относительных путей. Применяется для включения заголовочных файлов и указания путей библиотек.
  2. # Include common config
    include lib/make/Makefile.common - включает общий конфигурационный файл проекта (ниже я опишу его устройство чуть подробнее). Позволяет быстро добавлять глобальные флаги и пути.
  3. # Making libs
    lib:
    $(MAKE) -C lib ROOTDIR=$(ROOTDIR- при сборке библиотек я передаю путь корня проекта во все нижележащие Makefile.
  4. # Rules for write firmware to mcu
    write: firmware.bin
     qstlink2 -cewV ./firmware.bin - для записи я использую приложение QSTLink о котором рассказывал в начале.
Остальные конструкции кода этого Makefile вполне стандартны. Поэтому я не буду их описывать, однако в комментариях готов ответить на любые вопросы связанные с этим, равно как и с другими интересующими вас особенностями.

stm32f4xxxg_flash.lg
Скрипт построения бинарного образа программы с экспортом абсолютных адресов регионов для инициализации. Скопирован из стандартного скрипта сборки, с добавлением некоторых опций, а также изменений активных областей.

inc/
Общие заголовочные файлы проекта. Собственно помимо заголовочного файла для каждого файла исходного кода в src, есть директории в которых находятся специфичные для проекта определения. Вот краткое описание содержимого этой директорииx:
  • base/ - содержит базовые определения и синтаксические конструкции
  • core_support/ - как видно из названия, определения связанные с особенностями ядра, которые не возможно описать средствами языка (специфичные регистры, команды процессорного ядра и сопроцессоров, и т.п.)
  • device_support/ - содержит аналогичные определения но только для периферии МК.
  • reset-handler.h - заголовочный файл для кода вектора сброса.
  • vectors.h - заголовочный файла для таблицы векторов прерываний, ниже я объясню некоторые особенности её построения.
  • work.h - заголовочный файл для рабочего кода проекта 
Собственно сюда вы можете добавлять свои заголовочные файлы, однако я бы на вашем месте потрудился дать каждой группе файлов директорию, чтобы не возникало кучи непонятных файлов.

lib/
В этой директории хранятся все библиотеки проекта.

Также здесь расположен общий - Makefile.common, Makefile для указания основных настроек проекта используемых при сборке. В них входят текущие библиотеки, опции компилятора, расположения заголовочных файлов, библиотек и прочих важных компонентов. этот файл расположен в поддиректории make/.

Общий Makefile, для сборки этой директории содержит правила для нижележащих библиотек:
  1. all:
    $(MAKE) -C std_periph - для сборки библиотеки
  2. clean:
    $(MAKE) clean -C std_periph - для её очистки
Т.е по сути в этом файле на каждую новую библиотеку заводится всего 2 строчки кода, что упрощает добавление новых библиотек в проект.

Также, в этой директории присутствует std_periph/ - библиотека стандартной периферии, о которой я расскажу в следующей статье.

src/
"Рабочая" директория проекта. Здесь расположены следующие основные файлы:
  • reset_handler.c - обработчик вектора сброса, помимо вызова функции work() -     /* call working code */    work();, выполняет роль инициализатора переменных проекта, как описанных так и определённых:
    uint32_t * src;
    uint32_t * dest;
    
    src = &__text_end;
    dest = &__data_start;
    
    if (src != dest)
        while (dest < &__data_end)
            *(dest++) = *(src++);
    
    dest = &__bss_start;
    
    while (dest < &__bss_end)
        *(dest++) = 0;
    
    
    
  •  vectors.c - таблица векторов прерываний МК. Также первое 32х битное поле это не вектор а указатель на начало стека, которое автоматически используется процессорным ядром МК после сброса. Каждое прерывание имеет уже фактическое название:
    /* ARM CortexCore interrupts */
        reset_handler, /*!#0 cortex-m4 reset interrupt begin code of this */
    А благодаря следующему определению в заголовочном файле:
    /* prototypes of functions with weak defenition */
    void reset_handler(void) __attribute__ ((weak, alias ("default_handler")));
    В случае не нахождения компилятором ссылки на действительную функцию переопределяется на default_handler(), представляющий из себя себя простой бесконечный цикл:
    /* default(weak) handler */
    
    void default_handler(void) {
    
        while (1);
    
    }
    
  • work.c - рабочий код проекта, он же основной цикл. В настоящее время тут только бесконечный цикл:
    /* main work function */
    
    void work(void) {
    
        /* infinity loop */
        while (1);
    
    }
    
На этом, я заканчиваю описание основных опций базового проекта, в последующих статьях я подробно опишу каждую его часть.