Pull to refresh

Лучшее время для изучения микроконтроллеров

Reading time12 min
Views212K
image
Признайтесь, как часто вы думали о том, чтоб освоить азы программирования микроконтроллеров? Наверняка у вас есть в голове несколько идей потенциальных проектов, но воплощать их в жизнь вы так и не взялись. Так вот: лучше времени для старта, чем сейчас просто не найти.

Почему я решил, что этот вопрос интересен аудитории Хабра? Достаточно проанализировать количество добавления в избранное некоторых постов и выводы напрашиваются сами собой.

С другой стороны, в моём текущем окружении очень много программистов, но почти нет имбедеров. Когда я разговариваю с ними на тему микроконтроллеров, у меня создаётся впечатление, что мнение о них у многих осталось на уровне 10-летней давности.

Не смотря на то, что писать на asm’е для микроконтроллеров проще, чем под x86 архитектуру, многие его боятся и это служит для них преградой на пути к встраиваемым системам. Друзья, для того, чтоб сейчас запустить микроконтроллер, не обязательно, даже, досконально читать даташиты, не говоря уже о том, чтоб знать его инструкции. Конечно, если это – ваша профессия, то тут уровень погружения гораздо выше, но, скажите мне, как часто вы, вне зависимости от того, профессионалом в каком деле вы являетесь, при создании продуктов стараетесь не нарушать принципа инкапсуляции до последнего и нет нет да и заглядываете в исходники используемых библиотек? Сейчас вы поймёте, что я имею в виду.

Я смутно помню те времена, когда я не программировал микроконтроллеры. Я начинал писать на asm’е, не только потому, что это делали все, но и потому, что нормальных инструментов практически не было. Популярность 8-битных контроллеров от AVR я объясняю тем, что для них создавались очень простые в использовании библиотеки, позволяющие создать новый проект, написать десяток строчек кода и получить вполне себе рабочую программу (адреса регистров периферийных устройств и векторы прерываний любезно заполнены создателями библиотек). Я не проводил исследования, это из моих личных воспоминаний — я уверен, что более толковые библиотеки для других контроллеров существовали ещё раньше, но тогда мы об этом не знали.

Первый действительно массовый 32-битный микроконтроллер создала французская контора STM. Именно в тот момент многие любители познакомились с архитектурой Cortex-M3 и начали её широко использовать. Помню, мне одновременно в руки попало 2 контроллера – STM32F103 и LPC1768. Мне надо было как можно быстрее сделать прототип устройства на одном из них. Естественно, мой выбор пал на первый: французы выпустили библиотеку для периферии под названием Standard Peripherals Library и всё, что мне оставалось сделать – это запустить FreeRTOS, подключить необходимую периферию и на основе созданного скелета собирать проект уже на следующем уровне абстракции, не отвлекаясь больше на работу с регистрами. Скелет я использовал и в дальнейшем, часто перегибая и засовывая 32-х битный Cortex туда, где хватило бы и самой маленькой ATtiny, но чаще всего цена позволяла (а там, где не позволяла, либо нужно было пониженное энергопотребление, можно было использовать дешёвые MSP430 и STM8, но случалось это редко).

Конечно, я слукавлю, если скажу, что мне так и не пришлось выучить полностью архитектуру Cortex-M3 и скурить даташит F103 – конечно же, пришлось и тут моё увлечение библиотекой CMSIS и StdPeriph_Lib мне скорее помешало, чем помогло, но скорость вхождения в новое для меня семейство поразила и уже тогда я понял, что мир контроллеров меняется и становится одновременно и проще и сложнее.

И вот мы плавно подобрались к тому, о чём я и хотел вам рассказать. Дело в том, что популярность всяких Arduino сборок долго не давала покоя ребятам из Texas Instruments. Они выпускали лаунчпады на основе MSP430 и продавали их дешевле себестоимости и бесплатной доставкой, они запускали сообщество, в котором можно было выкладывать свои проекты, они создавали Energia – форк Arduino, они выпускали лаунчпады Stellaris, а затем переименовывали их в Tiva C (хотя тут речь идёт о более глобальном ребрендинге и добавлении некоторых новых функций, суть не поменялась). О последнем я и хочу поговорить.

Купить лаунчпад EK-TM4C123GXL можно за 12.99$ вместе с доставкой FedEx (то есть получите вы его достаточно скоро). Плата не изобилует различной электроникой, как, например, Discovery F4 (на борту которой находятся акселерометр, звуковой сенсор, ЦАП, куча светодиодов) – всё, что вы найдёте на Tiva C Launchpad – это RGB диод и 2 кнопки, но его сила заключается не в дополнительных устройствах на плате.

image

Сила EK-TM4C123GXL в библиотеках, доступных для скачивания с сайта TI под названием TivaWare. Дело в том, что библиотеки для своих контроллеров сейчас пишут все, но многие из них, к сожалению, качеством не особо отличаются и являются скорее традиционными примерами, чем полноценными библиотеками, которые не стыдно использовать в своих проектах (для упомянутого чуть выше LPC1768, NXP написали свою библиотеку почти одновременно с STM, но качеством она тогда не особо отличалась). Библиотека для Tiva C удивляет своей стандартизированностью, документированностью и многообразием.

Чуть позже я предложу вам скачать TivaWare и, если вам не будет лень, то после установки вы сможете наблюдать следующие каталоги:
  • Driverlib – тут находятся непосредственно драйверы для периферийных устройств, таких как adc, gpio и так далее (в том числе и заголовочный файл с макросами для вызова функций из rom – об этом далее)
  • Examples – ну тут, понятно, примеры. Делятся на boards, peripherals и project. В первой, естественно, проекты для конкретных отладочных плат, во второй примеры использования периферийных устройств и в третьей пример чистого проекта для различных сред разработки (IAR, Keil, CCS) – файлы доступны под лицензией BSD.
  • Inc – различные заголовочные файлы с макросами, в том числе и файл tm4c123gh6pm, который пригодится нам для создания простейшего проекта
  • Docs – вот это отличительная черта TI — простая и понятная документация. Внутри находятся несколько User Guide, относящиеся к описанным тут компонентам – DriverLib, Examples, Bootloader, IQmath и так далее, а так-же юзергайд непосредственно к используемому нами лаунчпаду.
  • Sensorlib – вот это то, что меня действительно удивило: тут находятся драйвера для различных сенсоров сторонних производителей, таких как sht21, lsm303d, MPU6050 и так далее. Я люблю использовать последний (это такая MEMS сборка из акселерометров и гироскопов на одном кристале) и раньше я всегда подключал выдранную из примеров Invensense либу, так что весьма аккуратно написанные исходники от TI меня прям порадовали (кроме того, тут же я нашёл сырцы для работы с квантерионами).
  • IQmath – библиотека алгоритмов, оптимизированных для работы с числами с плавающей запятой на Stellaris (Tiva C) устройствах.
  • Utils – часто используемые утилиты, для работы с командной строкой, последовательным портом, планировщик и многое другое.
  • Оставшиеся каталоги содержат bootloader, сырцы сторонних производителей (FreeRTOS, например), библиотеку для работы с USB, драйвера для Windows и так далее.

Для того, чтоб запустить любой пример на вашем лаунчпаде, достаточно открыть проект из папки examples/boards/ ek-tm4c123gxl в вашей любимой IDE – там всё уже готово (я использую IAR, по этому я открыл ek-tm4c123gxl.eww и он мне загрузил уже настроенный workspace ). Но, знаете, такие настроенные примеры есть сейчас почти у каждого микроконтроллера, а настоящие трудности начинаются, когда мы пытаемся создать что-то своё.

Ок, давайте откажемся не только от примеров но и от настроенного скелета проекта – запустим всё с нуля (естественно с использованием файл из библиотек). Сразу дисклеймер: работать с лаунчпадом я начал только сегодня, так что я пока не знаю почти ничего ни о нём, ни об исходниках с которыми я буду работать. Это и есть основной лейтмотив всей статьи – человек видит Tiva C первый раз и сразу пытается работать с ней.

Я хотел написать сразу какое-нибудь сложное и интересное приложение, но потом понял, что это займёт некоторое время, а публиковать статью надо срочно и, поэтому я решил сделать простенький пример, а потом, если будут желающие, мы сделаем что-нибудь поинтереснее.

Итак, первое, что нам нужно — это скачать и установить библиотеку (если вы не сделали этого до сих пор). Кроме этого, вам необходимо установить IDE и флэшер.

Где качать?
Скачать можно по этой ссылке.Если трафик позволяет — качайте DK-TM4C123G Kit Full Installer — он содержит библиотеку TivaWare, документацию и инсталяторы IDE (около 3.5Gb).
IDE и Flash Programmer находятся в папке Tools.
Если нет — можете скачать отдельно TivaWare for Tiva C Series, LM Flash Programmer и IDE.


В качестве IDE я буду использовать IAR (следовательно и примеры будут с ним), однако вы можете использовать любую из поддерживаемых.

Кто не знает, у IAR есть 2 варианта бесплатного использования — с ограничением по времени и с ограничением по размеру кода. И, естественно, есть другие варианты, если вам не подходят эти 2 (сами знаете какие). Описывать процедуру установки и регистрации IAR я не буду — там всё просто, но, если у вас возникнут трудности — не стесняйтесь задавать вопросы в комментариях.

Итак, первым делом создадим новый проект. В свойствах проекта изменим некоторые настройки.

Настройки проекта
Настроек, на самом деле, не так много. Откроем опции проекта и выберем правильный камень. На вкладе General Options -> Target.

image

В C/C++ Compiler, на вкладке Preprocessor укажем путь к папке TivaWare. На этой же вкладке чуть позже мы добавим некоторые константы.

image

В Debugger выберем TI Stellaris.

image

Более детальные настройки (если нужно), можно посомотреть в примерах (например там используется другой файл конфигурации линковщика, не тот, что предложен IAR'ом).


Теперь нам необходимо настроить структуру проекта. Я предлагаю придерживаться структуры, предложенной TI (как в примерах), но с некоторыми отличиями.

В папке (на самом деле «в группе», но папки — както привычнее) Src у нас располагаются наши исходники. В примерах туда добавляют и исходники библиотечных файлов, но я считаю, что это только запутает проект.

В папке Library хранятся файлы из DriverLib. TI добавляют туда уже скомпилированный файл (в случае с IAR это файл driverlib/ewarm/Exe/driverlib.a), вы можете сделать так же, но я бы предложил добавлять вместо этого исходники, причём только в случае необходимости — так проще запомнить где что находится, да и смотреть исходный код полезно. Если вы планируете добавлять в эту же папку файлы из других библиотек (Utils, например), то лучше создать ещё один уровень иерархии.

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

Главное не забывать, что вот эти папки — это только для удобства пользователя, они ничего общего с размещением файлов на диске не имеют и никак не влияют на компиляцию.

Итак, в папке Src создадим файл main.c

В папку Library добавим startup_ewarm.c — он нужен для правильной инициализации вектора прерываний. Взять можете его из project_0 (это в examples) проекта, например.

Начнём нашу программу с функции main. Так как к порту F нашего лаунчпада подключен RGB светодиод, то им мы и поморгаем.

void main(void)
{
    volatile uint32_t ui32Counter;
  
    //Инициализация периферии

    while(1)
    {

        //Тут код для переключения пинов

        for(ui32Counter = 0; ui32Counter < 1000000; ui32Counter++)
        {
        }

        //Тут код для переключения пинов

        for(ui32Counter = 0; ui32Counter < 1000000; ui32Counter++)
        {
        }
    }
}


Думаю всё понятно: использовать типы вроде uint32_t — хорошая привычка, это помогает решить неоднозначность с размерами переменных на различных контроллерах (кроме того, это соответствует стандартам MISRA-C); volatile — сообщает компилятору, что эту переменную не стоит оптимизировать (потому-что мы её будем использовать для, в общем-то, бесполезной операции). Дальше — беконечный цикл (как учат студентов — программа на микроконтроллерах не должна никогда кончаться) и 2 счётчика для задержек.
Чтоб этот код компилировался, добавим в начало файла.

#include <stdint.h>


Теперь мы приступим непосредственно к миганию светодиодами.

Скачиваем со стрницы лаунчпада файл Tiva C Series TM4C123G LaunchPad Evaluation Kit User's Manual и читаем в разделе User Switches and RGB User LED о том, что диод подключен к пинам PF1 (red), PF2 (blue) и PF3 (green).
Теперь открываем SW-TM4C-DRL-UG (в папке docs TivaWare) и просматриваем раздел GPIO. Из введения мы понимаем, что сперва пины должны быть сконфигурированны (конфигураций, естественно, множество) на выход. Тут же читаем, что одни из самых полезный функций — GPIOPinRead() и GPIOPinWrite(). Что они делают — понятно, осталось глянуть в их описание для уточнения списка параметров. Тут же находим, что для регистрации пинов на вывод используется функция GPIOPinTypeGPIOOutput(). Итак, меняем комментарии, в нашем коде на:

GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1|GPIO_PIN_2);  //для инициализации 
//и
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0xFF);
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0);
//и
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0);
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0xFF);


Для включения и выключения диодов.

Естественно, не забываем добавить файл driverlib/gpio.c в папку Library, а так же

#include "driverlib/gpio.h" 

в заголовки.

Кроме того, уже в процессе компиляци замечаем, что необходимо добавить ещё 2 заголовочных файла:
#include <stdbool.h> // Для поддержки типа bool, который используется в gpio
#include "inc/hw_memmap.h" //Для поддержки деклараций пинов.


Теперь наша программа выглядит вот так:
#include <stdint.h>
#include <stdbool.h>
#include "driverlib/gpio.h"
#include "inc/hw_memmap.h"


void main(void)
{
    volatile uint32_t ui32Counter;
  
    GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1);

    while(1)
    {

        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0xFF);
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0);

        for(ui32Counter = 0; ui32Counter < 2000000; ui32Counter++)
        {
        }


        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0);
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0xFF);

        for(ui32Counter = 0; ui32Counter < 2000000; ui32Counter++)
        {
        }
    }
}


И, если вы её скомпилируете и запустите — сможете наблюдать поочерёдную смену цвета диода.

Но моргание светодиодом — слишком просто для нас. Давайте пойдём чуть дальше и добавим поддержку порта ввода-вывода.
Действия — все те же. Находим к каким портам подключен UART, читаем про конфигурацию модуля в UserGuide, конфигурируем, используем функции для записи в и чтения из uart.

Моя функция инициализации uart получилась похожа на функцию из примера, но с одним интересным отличием. Вот так выглядит инициализация из примера:

void ConfigureUART(void)
{
    //
    // Enable the GPIO Peripheral used by the UART.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

    //
    // Enable UART0
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);

    //
    // Configure GPIO Pins for UART mode.
    //
    ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
    ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
    ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    //
    // Use the internal 16MHz oscillator as the UART clock source.
    //
    UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);

    //
    // Initialize the UART for console I/O.
    //
    UARTStdioConfig(0, 115200, 16000000);
}


Как видите, тут используются странные функции с префиксом ROM_ — это специальные функции, которые уже сохранены в ROM'е микроконтроллера лаунчпада. Прочитать о них можно в всё том же UserGuide для DRL. Созданны они для того, чтоб уменьшить размер кода в Flash памяти. Нужно оно вам или нет — решать вам, мне идея понравилась (раз уж я всё-равно использую Peripheral Driver Library). Кстати, если вы не знаете, будет код использвоаться на устройстве с кусками библиотеки в ROM или нет — вы можете использовать Mapped ROM Calls. Тогда будет использоваться код из ROM, если он есть и компилироваться, если его нет.

Для работы с ROM необходимо настроить несколько констант: в опциях проекта, в C/C++ Compiler, на вкладке Preprocessor добавить константу TARGET_IS_BLIZZARD_RB1 в поле Defined Symbols. Туда же сразу добавьте PART_TM4C123GH6PM и ewarm — они нужны для успешной компиляции фалов библиотеки.

Скрин
image


Кроме того, необходимо добавить в дерево проекта недостающие файлы:

image

Итак, всё, что нам осталось, это вывести что-то в порт (можете использовать для чтения любой эмулатор терминала. Например, для Windows я использовал Real Term). Затем, я предлагаю считать букву из порта, проверить, принадлежит ли она к одному из цветов (r, g, b) и поменять состояние соответствующего пина.

Функция для инициализации UART у вас уже есть. Изменяем инициализацию портов, чтоб добавить третий пин (раньше мы настроили только 2 на выход). Вывести строку в терминал можно с помощью функции UARTprintf(); из библиотеки utils/uartstdio.c (естественно, этот необходимо добавить к проекту и подключить заголовчный файл).

Считываем символ функцией UARTCharGet(). Она входит в цикл до тех пор, пока в уарт не придёт символ. После этого совершаем действия над пинами и возвращаемся в начало цикла.

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/gpio.h"
#include "driverlib/rom.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"
#include "driverlib/pin_map.h"
#include "utils/uartstdio.h"

void ConfigureUART(void)
{
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);


    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);

    ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
    ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
    ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);

    UARTStdioConfig(0, 115200, 16000000);
}

void main(void)
{
    volatile uint32_t ui32Loop;
    uint32_t ui32Color;
    uint8_t ui8Red = 0xFF;
    uint8_t ui8Green = 0xFF;
    uint8_t ui8Blue = 0xFF;
    
    GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3);
    
    ConfigureUART();

    UARTprintf("Hello HabraHabr!\n");
    
    while (1) 
    {
    UARTprintf("Please, enter color(r,g,b) \n");
    
    ui32Color = UARTCharGet(UART0_BASE);
    switch (ui32Color)
        {
        case 'r': 
            GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, ui8Red);
            ui8Red = ~ui8Red;
            break;
        case 'b': 
            GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, ui8Blue);
            ui8Blue = ~ui8Blue;
            break;
        case 'g': 
            GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, ui8Green);
            ui8Green = ~ui8Green;
            break;
        default:
            UARTprintf("Incorrect color! \n");
            break;
        }
    }
}


В TivaWare я нашёл интересную либу – utils/cmdline. Она позволяет создавать и обрабатывать команды введённые из командной строки. Я хотел сделать интересный проект с использованием её и ещё нескольких интересных библиотек, но время поджимает, так что я могу написать об этом позже, если будет интерес (а так же о прерываниях, которые тут даже не упомянуты и о FreeRTOS).
Ну, а теперь о том, почему я несколько раз написал о том, что время поджимает и почему из всех благоприятных времён для начала изучений контроллеров сейчас самое благоприятное: 22 января на edX стартуют курсы Embedded Systems — Shape The World.



Регистрация бесплатная, но, если вы хотите получить сретификат, то необходимо заплатить взнос (минимум 50$). Лично я заплатил 50$ — не из-за сертификата, а просто из любви к подобным курсам и в их поддержку.

Для участия вам надо купить Tiva C лаунчпад и различную рассыпуху. Последнюю вы можете купить на любом радиорынке, а вот с лаунчпадом придётся поторопиться: TI обычно отправляют через FedEx, но это может занять до 10 дней, в то время, как до начала курса осталась неделя.

Но переживать не стоит: не думаю, что на первом уроке вам сразу понадобится работать с железом, возможно вы сможете просимулировать вашу программу.

Итак, немного выводов. Использование подобных библиотек – палка о двух концах. С одной стороны она упрощает разработку, понижает порог входа, с другой стороны создаёт уровень абстракции, который усложняет понимание основ (во всей статье нет ни одной отсылки к даташиту, и это – неправильно: даташит надо смотреть всегда и это является обязательным условием певращения из любителя в профессионала). Но у подобных библиотек в отличии от ардуино (кстати, Energia поддерживает описанный лаунчпад), есть одно преимущество: они не создают у вас ложного понимания реальности. Если вы используете библиотеку, вы понимаете, что за абстракцией кроется вполне реальное устройство и параллели между функциями и реальными регистрами проследить совсем не сложно.

Я надеюсь, что данный материал подтолкнёт вас к покупке и изучению (пусть и столь поверхностному) этого замечательного устройства. Если у вас есть идеи проектов, которые вы могли бы реализовать на TM4C123G, но возникли сложности в реализации – не стесняйтесь писать об этом в комментариях: будем разбираться вместе.
Tags:
Hubs:
+83
Comments77

Articles