Статья про микроконтроллер EFM32ZG110F32

в 8:17, , рубрики: EFM32, SiLabs, Блог компании ЭФО, программирование микроконтроллеров, продам микроконтроллер, разработка, Разработка для интернета вещей, системное программирование, энергосбережение

Так уж вышло, что у нас на складе оказалось довольно много микроконтроллеров EFM32ZG110F32, это серия Zero Gecko от компании SiLabs. Контроллеры классные, но пока не особенно популярны, потому я и пишу эту статью.

Статья про микроконтроллер EFM32ZG110F32 - 1
На правах рекламы мы предлагаем вот такой набор: ARM Cortex-M0+, 32 Кбайт Flash, 4 Кбайт ОЗУ, DMA, I2C, UART, USART, 12-разрядный АЦП, токовый ЦАП, компаратор, аппаратный счетчик импульсов, часы реального времени и разные штуки для снижения энергопотребления в корпусе QFN-24 за $0.96.

Под катом длинный пост с подробным обзором кристалла и отладочной платы, описанием доступных средств программирования и отладки. Приведены примеры работы с различными периферийными блоками кристалла, используются фирменные средства разработки и платформа mbed от ARM.

Обзор аппаратной части


EFM32 — это 32-разрядные маолопотребляющие микроконтроллеры на базе процессорных ядер ARM Cortex-M. Их главной особенностью является поддержка разнообразных программных и аппаратных технологий для оптимизации энергопотребления, но вместе с этим предоставляется и стандартный набор периферийных устройств.

Герой настоящей статьи — микроконтроллер EFM32ZG110F32 — принадлежит к самой младшей серии семейства EFM32, она называется EFM32 Zero Gecko. Это самые простые и самые дешевые контроллеры под брендом EFM32, они построены на базе ядра Cortex-M0+ и различаются между собой по набору поддерживаемых периферийных устройств и корпусам. О том, что представляет собой EFM32ZG110F32, проще всего судить по вот такой схеме.

Статья про микроконтроллер EFM32ZG110F32 - 2

Цветом для каждого блока обозначен режим энергопотребления, вплоть до которого блок может использоваться:

  • EM0 — активный режим, заявленное производителем энергопотребление — 114 мкА / МГц
  • EM1 — режим с отключенным процессорным ядром, заявленное энергопотребление — 48 мкА / МГц
    Современные малопотребляющие микроконтроллеры как правило поддерживают технологии автономной работы периферии. Каждый производитель называет их по своему, для EFM32 технология взаимодействия периферии без участия процессорного ядра называется Peripheral Reflex System (PRS). С использованием PRS и DMA можно организовать довольно сложные сценарии работы кристалла. Например, такой: по переполнению таймера выполняется преобразование на АЦП, результаты преобразования через DMA записываются в ОЗУ; эти действия повторяются двадцать раз и только после сохранения двадцати результатов генерируется прерывание, по которому просыпается процессорное ядро и начинается обработка полученного массива данных.
  • EM2 — это режим, в котором отключаются высокочастотные тактовые генераторы и доступными остаются только низкочастотные и асинхронные периферийные модули. Заявленное производителем энергопотребление — 0.9 мкА
    Чаще всего именно EM2 используется в качестве режима «сна». В отличие от EM1, этот режим позволяет обеспечить длительное питание от батарейки, но с сохранением поддержки DMA, последовательного интерфейса, I2C, счетчика импульсов, компаратора и других блоков, позволяющих детектировать события и обмениваться данными со сторонними устройствами.
  • EM3 — режим, в котором отключаются не только высокочастотные, но и низкочастотные тактовые генераторы. Соответственно, становятся недоступны LEUART, часы реального времени и DMA. Заявленное производителем энергопотребление режима EM3 — 0.5 мкА.
  • В режиме EM4 микроконтроллер фактически выключен и потребляет около 20 нА. Выйти из такой «глубокой спячки» можно только по reset, который, впрочем, может генерироваться не только по линии сброса, но и от другой линии ввода/вывода, предварительно настроенной соответствующим образом.

Чтобы оценить описанный микроконтроллер, удобнее всего использовать отладочную плату и в этой статье возможности кристалла будут демонстироваться именно с её помощью. Кит для микроконтроллеров серии EFM32 Zero Gecko называется EFM32ZG-STK3200 и приобретается за 42 доллара, обращайтесь. Набор состоит из USB-кабеля, батарейки и вот такой платы.

Статья про микроконтроллер EFM32ZG110F32 - 3

Здесь важно заметить вот что: статья посвящена микросхеме EFM32ZG110F32, а на отладочной плате находится более «старший» микроконтроллер той же серии — EFM32ZG222F32. Первый контроллер выполнен в корпусе QFN-24, второй — в QPF-48. Как следствие, на EFM32ZG110F32 доступно только 17 линий ввода/вывода, из которых до пяти можно использовать как каналы АЦП, а на EFM32ZG222F32 уже 37 линий, из которых только два потенциальных канала для АЦП. В остальном кристаллы полностью идентичны, поэтому рассмотренные ниже примеры можно смело относить как к EFM32ZG222F32, так и к EFM32ZG110F32.

Теперь вернемся к отладочной плате и рассмотрим доступные на ней модули:

  • J-Link — полноценный отладочный интерфейс для программирования и отладки установленного на плате микроконтроллера. Плата EFM32ZG-STK3200 может также использоваться в качестве программатора для сторонней платы, для этого нужно сконфигурировать EFM32ZG-STK3200 в качестве отладчика и подключиться к сторонней плате определенным образом. Об этом расскажу об этом ниже.
  • Переключатель питания позволяет выбрать в качестве источника либо отладочный USB-интерфейс, либо трехвольтовую батарейку, соответственно у переключателя два положения — DBG и BAT. В первом случае обеспечивается не только питание контроллера и отладочный интерфейса. При подключению к компьютеру через USB-кабель возможна работа с блоком Advanced Energy Monitor. Это измерительный модуль, который установлен на плате для измерения энергопотребления кристалла. Данные, которые можно получить с Advanced Energy Monitor, используются программой профилирования энергопотребления, о которой также будет рассказано чуть позже.
  • Основное устройство вывода, установленное на плате — это так называемый memory LCD, подключенный к контроллеру через интерфейс SPI. Memory LCD — это монохромный дисплей, имеюий собственную память, в которой хранится отображающееся изображение. При смене обновлении изображения перерисовываются только изменившиеся пиксели, таким образом обеспечивается низкое энергопотребление и сравнительно высокое быстродействие дисплея. Memory LCD дисплей, установленный на EFM32ZG-STK3200 настолько хорош, что одну плату у нас купили только ради дисплея. Было странно.
  • По светодиодам и механическим кнопкам никаких пояснений, думаю, не требуется. Но они есть, по две штуки.
  • Сенсорные кнопки представляют собой емкостные сенсоры и построены на популярной схеме, использующей RC-цепочку и интегрированные в микроконтроллер аналоговый компаратор и таймер. Если коротко, то на RC-цепочке и компараторе строится генератор частоты, частота которого зависит от емкости, которая в свою очередь увеличивается при прикосновении пальца к контактной площадке. Прикосновение детектируется при через измерение частоты, выполняемое, как правило, на таймерах.
  • Контактные площадки, на которых доступны все линии ввода/вывода, расположены в два ряда по верхнему и нижнему краю платы и на 20-выводном разъеме по правому краю.

Если ваше приложение рассчитано на задачу, в которой важно энергопотребление, то EFM32ZG-STK3200 стопроцентно вам пригодится (в первую очередь из-за упомянутого измерительного модуля Advanced Energy Monitor). Если же EFM32 будет использоваться в устройстве не с батарейным питанием, и вам почему-то совсем не хочется использовать эту плату, то всё равно держите ссылку на Zero Gecko Starter Kit Schematics. Вполне может пригодиться при проектировании.

От обзора микроконтроллера и отладочной платы перейдем к обзору доступных программных средств разработки.

Обзор программного обеспечения


Для разработки можно использовать как программное обеспечение от Silicon Labs, так и общеупотребимые gcc или Keil, IAR и др.

Платформа от Silicon Labs называется Simplicity Studio. Она включает в себя IDE на базе eclipse, несколько утилит для разработки и отладки проекта, примеры программ, всю документацию и другие компоненты. Имеет смысл использовать либо только Simplicity Studio, либо привычную вам среду разработки (Keil, IAR, Atollic, Rowley или Sourcery) или gcc вместе с утилитами из Simplicity Studio.

Почему стоит скачать Simplicity Studio вне зависимости от предпочитаемого компилятора и IDE?
Во-первых, это ничего не стоит. Как и любое другое ПО от производителей микроконтроллеров, Simplicity Studio — бесплатная среда. Во-вторых, дистрибутивы доступны для Mac и Ubuntu. В-третьих, при установке Simplicity Studio для выбранного семейства микроконтроллеров (в нашем случае для 32-разрядных EFM32) вы сразу получите полный комплект средств разработки, документации и полезных ссылок, которые будут автоматически обновляться. Это удобно.

Итак, после установки Simplicity Studio и подключения отладочной платы (на которой должно быть выбрано питание от USB), появится главное меню программы.

Статья про микроконтроллер EFM32ZG110F32 - 4

Все отобразившиеся инструменты активны именно для подключенного микроконтроллера. Если бы был выбран, например, Zigbee-модуль, то получился бы совсем другой набор иконок. Для отладки программы на EFM32ZGxxx доступны следующие опции:

  • Среда разработки. По умолчанию это Simplicity IDE, однако в качестве предпочитаемой IDE может быть выбрана и другая среда, в этом случае иконка «Simplicity IDE» будет заменена.
  • Профилирование энергопотребления — это утилита, которая работает с установленным на отладочной плате измерительным модулем Advanced Energy Monitor и строит красивый график Current / Time, который обновляется по ходу исполнения программы.
  • Конфигуратор — это графический интерфейс для настройки линий ввода/вывода и периферийных устройств, позволяющий сгенерировать проект программы с соответствующими функциями инициализации.
  • С демо-примерами и утилитой для программирования кристалла, думаю, всё понятно.
  • В настройках отладочной платы можно выбрать режим отладки. Доступно три режима:
    1. Отладка МК, расположенного на плате, через J-Link, расположенный на плате
    2. Отладка МК, расположенного на плате, через внешний отладчик
    3. Использование платы в качестве отладчика для подключенной платы.
    В этом же разделе можно обновить прошивку платы, подробнее об этом см. в примере #5 .
  • Как SWO-терминал попал в меню для контроллера EFM32 Zero Gecko я не знаю — линия SWO не предусмотрена на микроконтроллерах на базе ядра Cortex-M0+. Будем считать это случайностью.
  • Эмуляция работы устройства от батарейки — это утилита, которой на вход задаются серия микроконтроллера, его состояния (режимы энергопотребления и активные периферийные блоки), количество и тип батарей питания. Для описанной конфигурации кристалла и питания рассчитывается срок автономной работы контроллера.
  • В разделе Software Examples доступны готовые программы для выбранной серии микроконтроллеров — подключил плату, выбрал программу, запустил. За полезными примерами стоит также заглянуть в меню Application Notes. К каждому документу, описывающему особенности работы с периферией, прилагаются иллюстрирующие проекты.
  • Вся документация на подключенную плату или выбранный микроконтроллер доступна в один клик — при установке Simplicity Studio скачиваются все доступные материалы для выбранного семейства микросхем, а при обновлении Simplicity Studio подтянется актуальная документация.
  • Документация на программное обеспечение микроконтроллеров — библиотеки, драйверы, все регистры и структуры данных — доступна в разделе Software Documentation или по ссылке.
  • Настройка взаимодействия со сторонним ПО — это меню для выбора предпочитаемой IDE, проверки и установки требуемых драйверов и т.п.

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

К слову об обновлениях. Первая Simplicity Studio, разработанная ещё компанией energy micro, была великолепна в своей простоте. Следующую версию выпустили уже в Silicon Labs. Simplicity Studio 2.0 обладала гораздо более широким набором функций, содержала IDE и поддерживала микроконтроллеры C8051Fxxx, но получилась сильно тормознутой. К нынешней версии (3.2) ситуация полностью выровнялась и работать в Simplicity Studio снова весьма приятно.

Чтобы познакомить читателя с возможностями микроконтроллера EFM32ZG110F32, рассмотрим несколько примеров работы с различными средствами разработки и отладки.

Пример #1 Проверка готовности


Если в ваших руках первый раз оказалась отладочная плата и вы только-только запустили Simplicity Studio, то имеет смысл запустить демо-пример и убедиться что весь этот программно-аппаратный комплекс работает нормально.
После включения платы в режиме DBG убедитесь, что плата определилась (см. левый нижний угол главного меню Simplicity Studio). Для платы EFM32ZG-STK3200 в первом ряду будет доступен пункт Demo, содержащий готовые прошивки для микроконтроллера.

Статья про микроконтроллер EFM32ZG110F32 - 5

Нужно просто выбрать один из проектов и нажать кнопку Finish. Контроллер будет запрограммирован готовым бинарным файлом, на плате запустится выбранная программа, а на экране компьютера появится график изменения уровня потребляемого тока.
Если всё прошло успешно, то можно начинать разработку собственных программ.

Все демо-программы доступны и в виде проектов для каждой из поддерживаемых сред разработки. Соответствующие файлы после установки Simplicity Studio находятся в директории ...SiliconLabsSimplicityStudiov3developersdksefm32v2kitsEFM32ZG_STK3200examples

Для каждого примера доступны:

  • проект для Keil — MDK-ARM (папка arm),
  • makefile для gcc (папка armgcc),
  • проект для Atollic TrueSTUDIO (папка atollic),
  • проект для IAR Embedded Workbench for ARM (папка iar),
  • проект для Rowley Associates — CrossWorks for ARM (папка rowley),
  • проект для Simplicity IDE — CrossWorks for ARM (папка SimplicityStudio),
  • готовые файлы .bin, .hex и .out (папка bin).

Пример #2 Работа с интерфейсом Low Energy UART и утилитой energy profiler


Начнем разбираться с отдельными блоками микроконтроллера. Вернувшись к схеме периферийных блоков микроконтроллера EFM32ZG110F32, можно заметить среди коммуникационных интерфейсов модуль LEUART.
LEUART или Low Energy UART — это последовательный интерфейс, сохраняющий функциональность в режимах сна до EM2 включительно. Поскольку в режиме EM2 для тактирования доступны только низкочастотные генераторы на 32.768 кГц, интерфейс LEUART поддерживает только самую низкую скорость работы из стандартных скоростей UART — 9600 бод.

Кроме работы с минимальным энергозатратами, этот интерфейс предоставляет две необычные для UART функции. Для LEUART может быть назначен это либо стартовый фрейм start frame, до прихода которого посылки не принимаются, либо signal frame, только по приходу которого генерируется прерывание. В обоих случаях «анализ» входных сигналов проводится контроллером автоматически и без участия ядра.

Поскольку LEUART — это блок, призванный снизить уровень энергопотребления кристалла, логично демонстрировать его возможности вместе с программным средством профилирования энергопотребления — утилитой energy profiler.

Суть эксперимента

От внешнего источника на последовательный интерфейс приходят посылки — массивы символов. Каждый символ должен быть принят и сохранен, а по окончании приема строки контроллер должен произвести некие вычисления. Программа у нас тестовая, а значит довольно простая и практически бесполезная.
Строка, которая приходит со стороннего устройства передается дважды в секунду и имеет один и тот же вид вид:

 char hello[] = { 'H', 'E', 'L', 'L', 'O', ' ', 'H', 'A', 'B', 'R', '!', 0, 'r'  };

Символом окончания строки будем считать возврат каретки 'r', а принятые символы записывать в массив rxbuf[]. Вычисления, необходимые для «обработки» полученной строки, сведем к исполнению пустого цикла for (j = 0; j < 1000; j++), на время исполнения которого включается LED1.
Целью эксперимента будем считать контроль и оптимизацию энергопотребления кристалла при исполнении задачи приема и обработки данных.

Настройка проекта, подключение платы

В проекте для работы low energy UART используются функции из EFM32 API — библиотек, предоставленных SilLabs-ом и работающих «поверх» ARM-овских CMSIS. Для их использования необходимо добавить в проект соответствующие файлы, их можно найти в директории ...SimplicityStudiov3developersdksefm32v2emlib после установки Simplicity Studio.

Статья про микроконтроллер EFM32ZG110F32 - 6

В приведенном примере также используется пакет BSP (Board Support Package), включающий готовые функции, упрощающие работу с отладочными платами для EFM32. Эти функции позволяют не задумываться о топологии платы при работе с установленными на ней модулями (например, BSP_LedInit() и BSP_LedToggle(1)). Библиотеки BSP не рассчитаны ни на работу с другими платами, ни даже на работу с фирменной платой, запитанной от батарейки, а не отладочного USB. Однако в пробном проекте вполне можно себе такое позволить.

И EFM32 API, и BSP доступны для всех IDE, поддерживающих микроконтроллеры EFM32.

Что касается подключения по LEUART к передающему устройству, то в соответствии с программной настройкой LEUART (см. функцию initLeuart() из приведенного ниже листинга), требуется
а) запитать плату от отладочного USB-интерфейса,
б) подключить линию D5 и землю к устройству-передатчику.

Статья про микроконтроллер EFM32ZG110F32 - 7

Первый вариант программной реализации

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

//....
  while (1)
  {
	  rx_char = LEUART_Rx(LEUART0);
	  if(rx_char == 'r') {
		  rxbuf[i] = rx_char;
		  i = 0;
		  BSP_LedToggle(1);
		  for (j = 0; j < 1000; j++);
		  BSP_LedToggle(1);
	  }
	  else {
		  rxbuf[i] = rx_char;
		  i++;
	  }
  }
}

Полный текст программы

#include "em_chip.h"
#include "em_device.h"
#include "em_cmu.h"
#include "em_emu.h"
#include "em_leuart.h"
#include "em_gpio.h"
#include "bsp.h"

char rx_char;
int i, j;
char rxbuf[13];

LEUART_Init_TypeDef LEUART0Init =
{
  .enable   = leuartEnableRx,    
  .refFreq  = 0,                          
  .baudrate = 9600,                   
  .databits = leuartDatabits8,     
  .parity   = leuartNoParity,         
  .stopbits = leuartStopbits2,       
};

void initLeuart(void)
{
  LEUART_Reset(LEUART0);
  LEUART_Init(LEUART0, &LEUART0Init);

  LEUART0->ROUTE = LEUART_ROUTE_RXPEN |
                   LEUART_ROUTE_LOCATION_LOC0;
  GPIO_PinModeSet(gpioPortD,
                  5,
                  gpioModeInputPull,
                  1);
}


int main(void)
{
  CHIP_Init();

  CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFXO);
  CMU_ClockSelectSet(cmuClock_LFB, cmuSelect_LFXO);

  CMU_ClockEnable(cmuClock_CORELE, true);
  CMU_ClockEnable(cmuClock_GPIO, true);
  CMU_ClockEnable(cmuClock_LEUART0, true);

  initLeuart();
  BSP_LedsInit();

  while (1)
  {
	  rx_char = LEUART_Rx(LEUART0);
	  if(rx_char == 'r') {
		  rxbuf[i] = rx_char;
		  i = 0;
		  BSP_LedToggle(1);
		  for (j = 0; j < 1000; j++);
		  BSP_LedToggle(1);
	  }
	  else {
		  rxbuf[i] = rx_char;
		  i++;
	  }
  }
}

С позволения публики, я не буду ни приводить пошаговую инструкцию по созданию проекта, ни построчно пояснять приведенный код. Для широких масс привожу ссылку на инструкцию по созданию пустого проекта в Simplicity IDE, оттуда по кнопке NEXT можно перейти к миганию светодиодом и на следующие несколько десятков уроков.

Итак, чтобы измерить энергопотребление программы с бесконечным опросом флага приема данных, следует воспользоваться утилитой energy profiler, входящей в состав Simplicity Studio. Если проект создан в Simplicity IDE, то для запуска профилирования достаточно найти вот такую иконку в верхнем меню. В случае, если вы программируете в другой среде или хотите покопаться в настройках конфигураций профилирования, то вам прямой путь в окно настроек Profile Configurations, доступное в выпадающем меню.

Статья про микроконтроллер EFM32ZG110F32 - 8

При настройке конфигурации должны быть указаны исполняемый файл и настройки bild-а, соответствующие используемой IDE.
Многие настройки в Profile Configurations также связаны с функциями Code Correlation. Code Correlation — это привязка результатов измерений энергопотребления к исполняемому коду (каждой точке на графике ставится в соответствие строка в листинге). Именно благодаря этой опции мы и говорим о профилировании, а не просто об измерении энергопотребления. Звучит, конечно, здорово, но для микроконтроллеров серии Zero Gecko функция Code Correlation недоступна. Для того чтобы соотнести измерения и текст программы, с платы должно дополнительно сниматься значение счетчика команд. Такие данные передаются по линии SWO, которой на микроконтроллерах на базе Cortex-M0 не предусмотрено. Поэтому мы будем довольствоваться только графиком изменения тока, строящимся по ходу исполнения программы, что тоже весьма неплохо.

Начинаем профилирование первого варианта реализации программы.

Статья про микроконтроллер EFM32ZG110F32 - 9

Действительно, раз в 500 мс микроконтроллер принимает посылку и обрабатывает её. Подтверждая данные с графика, светодиод на плате подмигивает дважды в секунду.
В среднем при постоянном опросе флага потребляется примерно 1.6 мА. Это много, поэтому программу нужно оптимизировать.

Второй вариант программной реализации

Очевидный вариант оптимизации — это использование прерываний от последовательного интерфейса и «усыпение» контроллера на время ожидания прерывания. Исполнять пустой цикл и включать LED1 будем уже в обработчике прерывания.

Фрагмент функции main()


int main(void)
{
  CHIP_Init();
  i = 0;

...

  while (1)
  {
    EMU_EnterEM2(true);
  }
}

Настройка прерываний


void setupLeuart(void)
{
  LEUART_IntEnable(LEUART0, LEUART_IEN_RXDATAV);

  NVIC_EnableIRQ(LEUART0_IRQn);
  LEUART0->CTRL = LEUART_CTRL_RXDMAWU;
}

Обработка прерываний


void LEUART0_IRQHandler(void)
{
  leuartif = LEUART_IntGet(LEUART0);
  LEUART_IntClear(LEUART0, leuartif);
  rx_char = LEUART0->RXDATA;

  if (rx_char == 'r')
  {
	  rxbuf[i] = rx_char;
	  i = 0;
	  BSP_LedToggle(1);
	  for (j=0; j<1000; j++);
	  BSP_LedToggle(1);
  }
  else
  {
	  rxbuf[i] = rx_char;
	  i++;
  }
}
Полный текст программы
Программа почти полностью повторяет пример работы с LEUART, доступный в Simplicity Studio


#include "em_chip.h"
#include "em_device.h"
#include "em_cmu.h"
#include "em_emu.h"

#include "em_leuart.h"
#include "em_dma.h"

char rx_char;
int i, j;
char rxbuf[12];

uint32_t leuartif;
uint32_t len;

LEUART_Init_TypeDef LEUART0Init =
{
  .enable   = leuartEnableRx,     
  .refFreq  = 0,                 
  .baudrate = 9600,               
  .databits = leuartDatabits8,    
  .parity   = leuartNoParity,   
  .stopbits = leuartStopbits2,    
};

void LEUART0_IRQHandler(void)
{
  leuartif = LEUART_IntGet(LEUART0);
  LEUART_IntClear(LEUART0, leuartif);

  rx_char = LEUART0->RXDATA;

  if (rx_char == 'r')
  {
	  rxbuf[i] = rx_char;
	  i = 0;
	  BSP_LedToggle(1);
	  for (j=0; j<1000; j++);
	  BSP_LedToggle(1);
  }
  else
  {
	  rxbuf[i] = rx_char;
	  i++;
  }
}

void initLeuart(void)
{
  LEUART_Reset(LEUART0);
  LEUART_Init(LEUART0, &LEUART0Init);

  LEUART0->ROUTE = LEUART_ROUTE_RXPEN |
                   LEUART_ROUTE_LOCATION_LOC0;

  GPIO_PinModeSet(gpioPortD,        
                  5,                    
                  gpioModeInputPull,    
                  1);                   
}


void setupLeuart(void)
{
  LEUART_IntEnable(LEUART0, LEUART_IEN_RXDATAV);
  NVIC_EnableIRQ(LEUART0_IRQn);
}


int main(void)
{
  CHIP_Init();
  i = 0;

  CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFXO);
  CMU_ClockSelectSet(cmuClock_LFB, cmuSelect_LFXO);

  CMU_ClockEnable(cmuClock_CORELE, true);     /* Enable CORELE clock */
  CMU_ClockEnable(cmuClock_GPIO, true);       /* Enable GPIO clock */
  CMU_ClockEnable(cmuClock_LEUART0, true);    /* Enable LEUART0 clock */

  initLeuart();
  setupLeuartDma();
  BSP_LedsInit();
  while (1)
  {
    EMU_EnterEM2(true);
  }
}

После изменения кода достаточно один раз нажать на иконку profiler, программа будет скомпилирована, контроллер запрограммирован, а результаты измерений будут выведены на экран. По результатам видно, что потребление многократно снизилось — в среднем 224 мкА вместо 1.6 мА.

Статья про микроконтроллер EFM32ZG110F32 - 10

Ожидание посылки микроконтроллер проводит в режиме EM2, это самый глубокий сон, который мы можем себе позволить для сохранения функциональности LEUART. Обработку результатов изменять в общем-то некуда, значит дальнейшая оптимизация программы возможна только для процесса получения посылки. Посмотрим на соответствующий участок графика ближе.

Статья про микроконтроллер EFM32ZG110F32 - 11

По приходу каждого символа строки { 'H', 'E', 'L', 'L', 'O', ' ', 'H', 'A', 'B', 'R', '!', 0, 'r' } вызывается обработчик прерывания, в котором вновь пришедший символ сравнивается с 'r'. Вспомнив, что перед нами не обычный последовательный интерфейс, а LEUART, можно использовать не прерывание по приходу символа, а прерывание по приходу SIGFRAME. Попробуем.

Третий вариант программной реализации

По условиям задачи, в память контроллера должен заноситься каждый пришедший символ строки. Если бы просто изменить тип прерывания, то сохранить сделать это будет невозможно. Решением проблемы будет использование DMA, по нулевому каналу которого данные будут «переправляться» с LEUART без выхода из энергосберегающего режима EM2.

Настройка прерываний


void setupLeuartDma(void)
{
  DMA_Init(&dmaInit);
  DMA_CfgChannel(DMA_CHANNEL, &chnlCfg);
  DMA_CfgDescr(DMA_CHANNEL, true, &descrCfg);

  DMA_ActivateBasic(DMA_CHANNEL,
                    true,
                    false,
                    (void *) &rxbuf,
                    (void *) &LEUART0->RXDATA,
                    BUF_MAX-1);

//------------------------------- разрешение прерывания по приходу символа 'r' --------------------------------//
  LEUART0->SIGFRAME = 'r';
  LEUART_IntEnable(LEUART0, LEUART_IEN_SIGF);
//-----------------------------------------------------------------------------------------------------------//

  NVIC_EnableIRQ(LEUART0_IRQn);
  LEUART0->CTRL = LEUART_CTRL_RXDMAWU;
}

Обработка прерываний


void LEUART0_IRQHandler(void)
{
  leuartif = LEUART_IntGet(LEUART0);
  LEUART_IntClear(LEUART0, leuartif);

  if (leuartif & LEUART_IF_SIGF)
  {
    DMA_ActivateBasic(DMA_CHANNEL, true, false, NULL, NULL, BUF_MAX-1);
	BSP_LedToggle(1);
	for (j = 0; j < 1000; j++);
	BSP_LedToggle(1);
  }
}
Полный текст программы

#include "em_chip.h"
#include "em_device.h"
#include "em_cmu.h"
#include "em_emu.h"

#include "em_leuart.h"
#include "em_dma.h"
#include "em_gpio.h"

#define DMA_CHANNEL    0
#define BUF_MAX        1023

char rx_char;
int i, j;

/* DMA control block, must be aligned to 256. */
#if defined (__ICCARM__)
#pragma data_alignment=256
DMA_DESCRIPTOR_TypeDef dmaControlBlock[DMA_CHAN_COUNT * 2];
#elif defined (__CC_ARM)
DMA_DESCRIPTOR_TypeDef dmaControlBlock[DMA_CHAN_COUNT * 2] __attribute__ ((aligned(256)));
#elif defined (__GNUC__)
DMA_DESCRIPTOR_TypeDef dmaControlBlock[DMA_CHAN_COUNT * 2] __attribute__ ((aligned(256)));
#else
#error Undefined toolkit, need to define alignment
#endif

uint32_t leuartif;
uint32_t len;

/* Defining the LEUART0 initialization data */
LEUART_Init_TypeDef LEUART0Init =
{
  .enable   = leuartEnableRx,     /* Activate data reception on LEUn_RX pin. */
  .refFreq  = 0,                  /* Inherit the clock frequenzy from the LEUART clock source */
  .baudrate = 9600,               /* Baudrate = 9600 bps */
  .databits = leuartDatabits8,    /* Each LEUART frame containes 8 databits */
  .parity   = leuartNoParity,     /* No parity bits in use */
  .stopbits = leuartStopbits2,    /* Setting the number of stop bits in a frame to 2 bitperiods */
};

/* DMA init structure */
DMA_Init_TypeDef dmaInit =
{
  .hprot        = 0,                  /* No descriptor protection */
  .controlBlock = dmaControlBlock,    /* DMA control block alligned to 256 */
};

/* Setting up channel */
DMA_CfgChannel_TypeDef chnlCfg =
{
  .highPri   = false,                     /* Normal priority */
  .enableInt = false,                     /* No interupt enabled for callback functions */
  .select    = DMAREQ_LEUART0_RXDATAV,    /* Set LEUART0 RX data avalible as source of DMA signals */
  .cb        = NULL,                      /* No callback funtion */
};

/* Setting up channel descriptor */
DMA_CfgDescr_TypeDef descrCfg =
{
  .dstInc  = dmaDataInc1,       /* Increment destination address by one byte */
  .srcInc  = dmaDataIncNone,    /* Do no increment source address  */
  .size    = dmaDataSize1,      /* Data size is one byte */
  .arbRate = dmaArbitrate1,     /* Rearbitrate for each byte recieved*/
  .hprot   = 0,                 /* No read/write source protection */
};

void LEUART0_IRQHandler(void)
{
  leuartif = LEUART_IntGet(LEUART0);
  LEUART_IntClear(LEUART0, leuartif);

  if (leuartif & LEUART_IF_SIGF)
  {
    DMA_ActivateBasic(DMA_CHANNEL, true, false, NULL, NULL, BUF_MAX-1);
	BSP_LedToggle(1);
	for (j = 0; j < 1000; j++);
	BSP_LedToggle(1);
  }
}

void initLeuart(void)
{
  LEUART_Reset(LEUART0);
  LEUART_Init(LEUART0, &LEUART0Init);

  LEUART0->ROUTE = LEUART_ROUTE_RXPEN |
                   LEUART_ROUTE_LOCATION_LOC0;

  GPIO_PinModeSet(gpioPortD,            
                  5,                    
                  gpioModeInputPull,    
                  1);                  
}


void setupLeuartDma(void)
{
  DMA_Init(&dmaInit);
  DMA_CfgChannel(DMA_CHANNEL, &chnlCfg);
  DMA_CfgDescr(DMA_CHANNEL, true, &descrCfg);

  DMA_ActivateBasic(DMA_CHANNEL,
                    true,
                    false,
                    (void *) &rxbuf,
                    (void *) &LEUART0->RXDATA,
                    BUF_MAX-1);

  LEUART0->SIGFRAME = 'r';
  LEUART_IntEnable(LEUART0, LEUART_IEN_SIGF);

  NVIC_EnableIRQ(LEUART0_IRQn);
  LEUART0->CTRL = LEUART_CTRL_RXDMAWU;
}


int main(void)
{
  CHIP_Init();
  i = 0;

  CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFXO);
  CMU_ClockSelectSet(cmuClock_LFB, cmuSelect_LFXO);

  CMU_ClockEnable(cmuClock_CORELE, true);     
  CMU_ClockEnable(cmuClock_DMA, true);        
  CMU_ClockEnable(cmuClock_GPIO, true);      
  CMU_ClockEnable(cmuClock_LEUART0, true);   

  initLeuart();
  setupLeuartDma();
  BSP_LedsInit();

  while (1)
  {
    EMU_EnterEM2(true);
  }

}

Запускаем профилирование третий раз и получаем доказательство того, что проведенная оптимизация имеет смысл — потребление снизилось с 224 до 139 мкА.

Статья про микроконтроллер EFM32ZG110F32 - 12

Исчезли «зубчики», соответствующие обработке прерываний по каждому пришедшему символу. За счет этого заметно снизились и энергопотребление за прием/обработку данных, и среднее значение.

Мне не хотелось бы вдаваться в сравнение конкретных цифр (строка могла бы, например, быть подлиннее, а посылки почаще), но думаю что принцип снижения потребления с использованием low energy UART и energy profiler описан достаточно понятно.

Пример #3 Работа с аппаратным счетчиком импульсов PCNT и конфигуратором кристалла


Рассмотрим другой интересный инструмент разработки, конфигуратор периферии и линий ввода/вывода. Выбрав соответствующий пункт главного меню Simplicity Studio и указав используемый part number, можно начать создание нового проекта в графической среде настройки контроллера.

Для микроконтроллера EFM32ZG110F32 (да-да, всё ещё $0.96 за штуку) пользователю доступен вот такой интерфейс:

Окно настройки линий ввода/вывода

Статья про микроконтроллер EFM32ZG110F32 - 13

Окно настройки периферии

Статья про микроконтроллер EFM32ZG110F32 - 14

Общий принцип работы в Configurator таков: выбирается необходимая периферия, для каждого блока настраивается режим работы, после этого назначаются необходимые для работы блоков линии ввода/вывода, для каждой линии задается конфигурация. После настройки запускается генерация заготовки проекта для вашей среды разработки.
Конфигуратор позволяет упростить начальный этап программирования — вместо выуживания настроек периферии из документации используется графический интерфейс. К тому же, эта утилита позволяет быстро оценить возможности выбранного кристалла и сделать простой пробный проект. Подобный конфигуратор есть у большинства производителей микроконтроллеров.

С использованием конфигуратора познакомимся с 16-разрядным счетчиком импульсов. Также как LEUART, этот модуль редко можно найти на микроконтроллерах, и, также как LEUART, счетчик импульсов позволяет снизить энергопотребление устройства.

Итак, в меню настройки периферии микроконтроллера EFM32ZG110F32 ставим галочку на PCNT0 и оглядываем доступные настройки.

Статья про микроконтроллер EFM32ZG110F32 - 15

Модуль может быть настроен на подсчет количества импульсов, приходящих на один из портов кристалла, в этом случае прерывание генерируется по достижении заданного количества импульсов. Другой вариант работы модуля — это режим квардатурного энкодера с прерыванием по изменению направления счета (по часовой стрелке / против часовой стрелки).
Счетчик PCNT может использовать один или два входных сигнала. В режимах подсчета импульсов «Single input, LFACLK oversampling» и «Single input, externally clocked» используется только линия S0, а сигнал на S1 игнорируется. В режиме «Quadrature decoder mode, externally clocked» используются оба входных сигнала.

Счетчик PCNT доступен в режимах сна вплоть до EM3, однако в режиме подсчета импульсов «Single input, LFACLK oversampling» используется внутренний, а не внешний источник тактирования, поэтому возможно использование режимов энергопотребления не ниже чем EM2. Работа блока в режиме EM3 также невозможна, если в качестве входов блока PCNT используются не линии ввода/вывода микроконтроллера, а каналы Peripheral Reflex System. С другой стороны, EM2 — это энергопотребление в единицы микроампер, что вполне себе приемлемо для энкодера.

Зададим через конфигуратор настройки самого простого режима PCNT. Пусть детектируется пятый импульс на одной из линий GPIO.
Выставляем режим работы в «Single input, LFACLK oversampling», значение Initial top value в «5», оставляем настройки счета (подсчет фронтов «Count positive edges» и направление счета «Count up») по умолчанию, включаем фильтр «Filter out pulses shorter then 5 clockcycles» для защиты от дребезга. Настройки входных каналов остаются по умолчанию, т.к. второй канал в режиме Single input игнорируется, а для первого используется ножка контроллера, а не канал PRS. Остальные свойства меню Pulse Counter относятся к режиму квадратурного энкодера, поэтому на них не смотрим вовсе.

Статья про микроконтроллер EFM32ZG110F32 - 16

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

Статья про микроконтроллер EFM32ZG110F32 - 17

По клику на сообщение об ошибке переходим в окно настройки линий ввода/вывода, где для линии S0IN доступно две локации:

Статья про микроконтроллер EFM32ZG110F32 - 18

Допустим, что вторая локация, т.е. линия PC0, нам подходит больше. Снова смотрим в окно ошибок.

Статья про микроконтроллер EFM32ZG110F32 - 19

И, опять соглашаясь с конфигуратором, изменяем режим работы ножки контроллера с Disabled на Input.

Статья про микроконтроллер EFM32ZG110F32 - 20

Теперь можно генерировать исходный код для заданной конфигурации микроконтроллера EFM32ZG110F32. В выпадающем по правому клику мыши меню находим команду Generate Source и получаем готовый проект. Проект содержит функции инициализации кристалла — настройку тактирования, счетчика импульсов и GPIO, а также пустой цикл while (1) в функции main().


extern void enter_DefaultMode_from_RESET(void) 
{
	CMU_enter_DefaultMode_from_RESET();
	PCNT0_enter_DefaultMode_from_RESET();
	PORTIO_enter_DefaultMode_from_RESET();
}

extern void CMU_enter_DefaultMode_from_RESET(void) {
	CMU_OscillatorEnable(cmuOsc_LFRCO, true, true);
	CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFRCO);
	CMU_ClockSelectSet(cmuClock_HF, cmuSelect_HFRCO);
	CMU_ClockEnable(cmuClock_CORELE, true);
	CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFRCO);
	CMU_ClockEnable(cmuClock_PCNT0, true);
	CMU_ClockEnable(cmuClock_GPIO, true);
}

extern void PCNT0_enter_DefaultMode_from_RESET(void) {
	PCNT_Init_TypeDef init = PCNT_INIT_DEFAULT;

	init.counter = 0;
	init.top = 5;
	init.negEdge = 0;
	init.countDown = 0;
	init.filter = 1;
	init.hyst = 0;
	init.s1CntDir = 0;
	init.cntEvent = pcntCntEventUp;
	init.auxCntEvent = pcntCntEventNone;
	init.s0PRS = pcntPRSCh0;
	init.s1PRS = pcntPRSCh0;

	PCNT_Init(PCNT0, &init);
	PCNT_Enable(PCNT0, pcntModeOvsSingle);
}

extern void PORTIO_enter_DefaultMode_from_RESET(void) {
	/* Pin PC0 is configured to Input enabled */
	GPIO->P[2].MODEL = (GPIO->P[2].MODEL & ~_GPIO_P_MODEL_MODE0_MASK)
			| GPIO_P_MODEL_MODE0_INPUT;
	/* Module PCNT0 is configured to location 2 */
	PCNT0->ROUTE = (PCNT0->ROUTE & ~_PCNT_ROUTE_LOCATION_MASK)
			| PCNT_ROUTE_LOCATION_LOC2;
}
Полный текст программы
Файл main.c


/**************************************************************************//**
 * @file
 * @brief Empty Project
 * @author Energy Micro AS
 * @version 3.20.2
 ******************************************************************************
 * @section License
 * <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b>
 *******************************************************************************
 *
 * This file is licensed under the Silicon Labs Software License Agreement. See 
 * "http://developer.silabs.com/legal/version/v11/Silicon_Labs_Software_License_Agreement.txt"  
 * for details. Before using this software for any purpose, you must agree to the 
 * terms of that agreement.
 *
 ******************************************************************************/
#include "em_device.h"
#include "em_chip.h"

/**************************************************************************//**
 * @brief  Main function
 *****************************************************************************/
int main(void)
{
  /* Chip errata */
  CHIP_Init();

  /* Infinite loop */
  while (1) {
  }
}

Файл InitDevice.c


//=========================================================
// src/InitDevice.c: generated by Hardware Configurator
//
// This file will be regenerated when saving a document.
// leave the sections inside the "$[...]" comment tags alone
// or they will be overwritten!
//=========================================================

// USER INCLUDES
#include "InitDevice.h"

// USER PROTOTYPES
// USER FUNCTIONS

// $[Library includes]
#include "em_system.h"
#include "em_emu.h"
#include "em_cmu.h"
#include "em_device.h"
#include "em_chip.h"
#include "em_pcnt.h"
// [Library includes]$

//==============================================================================
// enter_DefaultMode_from_RESET
//==============================================================================
extern void enter_DefaultMode_from_RESET(void) {
	// $[Config Calls]
	CMU_enter_DefaultMode_from_RESET();
	PCNT0_enter_DefaultMode_from_RESET();
	PORTIO_enter_DefaultMode_from_RESET();
	// [Config Calls]$

}

//================================================================================
// HFXO_enter_DefaultMode_from_RESET
//================================================================================
extern void HFXO_enter_DefaultMode_from_RESET(void) {
	// $[HFXO]
	// [HFXO]$

}

//================================================================================
// LFXO_enter_DefaultMode_from_RESET
//================================================================================
extern void LFXO_enter_DefaultMode_from_RESET(void) {
	// $[Use oscillator source]
	// [Use oscillator source]$

	// $[LFXO Boost Percent]
	// [LFXO Boost Percent]$

	// $[REDLFXO Boost]
	// [REDLFXO Boost]$

}

//================================================================================
// CMU_enter_DefaultMode_from_RESET
//================================================================================
extern void CMU_enter_DefaultMode_from_RESET(void) {
	// $[LFXO enable]
	// [LFXO enable]$

	// $[HFXO enable]
	// [HFXO enable]$

	// $[LFACLK Setup]
	/* Enable LFRCO oscillator */
	CMU_OscillatorEnable(cmuOsc_LFRCO, true, true);

	/* Select LFRCO as clock source for LFACLK */
	CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFRCO);

	// [LFACLK Setup]$

	// $[High Frequency Clock select]
	/* Using HFRCO at 14MHz as high frequency clock, HFCLK */
	CMU_ClockSelectSet(cmuClock_HF, cmuSelect_HFRCO);
	// [High Frequency Clock select]$

	// $[LF clock tree setup]
	/* Enable LF clocks */
	CMU_ClockEnable(cmuClock_CORELE, true);
	CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFRCO);
	// [LF clock tree setup]$
	// $[Peripheral Clock enables]
	/* Enable clock for PCNT0 */
	CMU_ClockEnable(cmuClock_PCNT0, true);

	/* Enable clock for GPIO by default */
	CMU_ClockEnable(cmuClock_GPIO, true);

	// [Peripheral Clock enables]$

}

//================================================================================
// ADC0_enter_DefaultMode_from_RESET
//================================================================================
extern void ADC0_enter_DefaultMode_from_RESET(void) {
	// $[ADC_Init]
	// [ADC_Init]$

	// $[ADC_InitSingle]
	// [ADC_InitSingle]$

	// $[ADC_InitScan]
	// [ADC_InitScan]$

}

//================================================================================
// ACMP0_enter_DefaultMode_from_RESET
//================================================================================
extern void ACMP0_enter_DefaultMode_from_RESET(void) {
	// $[ACMP Initialization]
	// [ACMP Initialization]$

	// $[ACMP Channel config]
	// [ACMP Channel config]$

}

//================================================================================
// IDAC0_enter_DefaultMode_from_RESET
//================================================================================
extern void IDAC0_enter_DefaultMode_from_RESET(void) {
	// $[IDAC Initialization]
	// [IDAC Initialization]$

	// $[IDAC optional configurations]
	// [IDAC optional configurations]$

	// $[IDAC enable]
	// [IDAC enable]$

}

//================================================================================
// RTC_enter_DefaultMode_from_RESET
//================================================================================
extern void RTC_enter_DefaultMode_from_RESET(void) {
	// $[RTC_Init]
	// [RTC_Init]$

}

//================================================================================
// USART1_enter_DefaultMode_from_RESET
//================================================================================
extern void USART1_enter_DefaultMode_from_RESET(void) {
	// $[USART_InitAsync]
	// [USART_InitAsync]$

	// $[USART_InitSync]
	// [USART_InitSync]$

	// $[USART_InitPrsTrigger]
	// [USART_InitPrsTrigger]$

}

//================================================================================
// LEUART0_enter_DefaultMode_from_RESET
//================================================================================
extern void LEUART0_enter_DefaultMode_from_RESET(void) {
	// $[LEUART0 initialization]
	// [LEUART0 initialization]$

}

//================================================================================
// VCMP_enter_DefaultMode_from_RESET
//================================================================================
extern void VCMP_enter_DefaultMode_from_RESET(void) {
	// $[VCMP_Init]
	// [VCMP_Init]$

}

//================================================================================
// WDOG_enter_DefaultMode_from_RESET
//================================================================================
extern void WDOG_enter_DefaultMode_from_RESET(void) {
	// $[CMU_ClockEnable]
	// [CMU_ClockEnable]$

	// $[CMU_OscillatorEnable]
	// [CMU_OscillatorEnable]$

	// $[WDOG_Init]
	// [WDOG_Init]$

}

//================================================================================
// I2C0_enter_DefaultMode_from_RESET
//================================================================================
extern void I2C0_enter_DefaultMode_from_RESET(void) {
	// $[I2C0 initialization]
	// [I2C0 initialization]$

}

//================================================================================
// TIMER0_enter_DefaultMode_from_RESET
//================================================================================
extern void TIMER0_enter_DefaultMode_from_RESET(void) {
	// $[TIMER0 initialization]
	// [TIMER0 initialization]$

	// $[TIMER0 CC0 init]
	// [TIMER0 CC0 init]$

	// $[TIMER0 CC1 init]
	// [TIMER0 CC1 init]$

	// $[TIMER0 CC2 init]
	// [TIMER0 CC2 init]$

}

//================================================================================
// TIMER1_enter_DefaultMode_from_RESET
//================================================================================
extern void TIMER1_enter_DefaultMode_from_RESET(void) {
	// $[TIMER1 initialization]
	// [TIMER1 initialization]$

	// $[TIMER1 CC0 init]
	// [TIMER1 CC0 init]$

	// $[TIMER1 CC1 init]
	// [TIMER1 CC1 init]$

	// $[TIMER1 CC2 init]
	// [TIMER1 CC2 init]$

}

//================================================================================
// PCNT0_enter_DefaultMode_from_RESET
//================================================================================
extern void PCNT0_enter_DefaultMode_from_RESET(void) {
	// $[PCNT0 initialization]
	PCNT_Init_TypeDef init = PCNT_INIT_DEFAULT;

	init.counter = 0;
	init.top = 5;
	init.negEdge = 0;
	init.countDown = 0;
	init.filter = 1;
	init.hyst = 0;
	init.s1CntDir = 0;
	init.cntEvent = pcntCntEventUp;
	init.auxCntEvent = pcntCntEventNone;
	init.s0PRS = pcntPRSCh0;
	init.s1PRS = pcntPRSCh0;
	PCNT_Init(PCNT0, &init);

	/* Activating PCNT0 */
	PCNT_Enable(PCNT0, pcntModeOvsSingle);
	// [PCNT0 initialization]$

}

//================================================================================
// PRS_enter_DefaultMode_from_RESET
//================================================================================
extern void PRS_enter_DefaultMode_from_RESET(void) {
	// $[PRS initialization]
	// [PRS initialization]$

}

extern void PORTIO_enter_DefaultMode_from_RESET(void) {

	// $[Port A Configuration]
	// [Port A Configuration]$

	// $[Port B Configuration]
	// [Port B Configuration]$

	// $[Port C Configuration]

	/* Pin PC0 is configured to Input enabled */
	GPIO->P[2].MODEL = (GPIO->P[2].MODEL & ~_GPIO_P_MODEL_MODE0_MASK)
			| GPIO_P_MODEL_MODE0_INPUT;
	// [Port C Configuration]$

	// $[Port D Configuration]
	// [Port D Configuration]$

	// $[Port E Configuration]
	// [Port E Configuration]$

	// $[Port F Configuration]
	// [Port F Configuration]$

	// $[Route Configuration]

	/* Module PCNT0 is configured to location 2 */
	PCNT0->ROUTE = (PCNT0->ROUTE & ~_PCNT_ROUTE_LOCATION_MASK)
			| PCNT_ROUTE_LOCATION_LOC2;
	// [Route Configuration]$

}

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

Последние два примера описывают дополнительные возможности при работе с отладочной платой EFM32ZG-STK3200.

Пример #4 Использование отладочной платы в качестве программатора-отладчика


Как было сказано выше, отладчик J-Link, встроенный на EFM32ZG-STK3200 может использоваться не только для программирования распаенного на плате кристалла, но и для программирования и отладки «сторонних» микроконтроллеров EFM32. Важно, что такой способ отладки позволяет в том числе измерять энергопотребления подключенной к EFM32ZG-STK3200 платы и работать в energy profiler.
Чтобы использовать EFM32ZG-STK3200 как отладчик нужно выполнить следующие действия.

1. Конфигурирование платы

Подключив EFM32ZG-STK3200 через USB интерфейс, нужно через главное меню Simplicity Studio перейти в Kit Manager. В открывшемся окне всего лишь изменить Debug Mode с MCU на Out.

Статья про микроконтроллер EFM32ZG110F32 - 21

2. Подключение платы

В правом верхнем углу платы EFM32ZG-STK3200 расположен стандартный для Cortex-ов 20-выводный отладочный разъем SWD (Serial Wire Debug). Через этот разъем нужно подключиться к отлаживаемой плате, причем из 20 выводов понадобятся пять.

Статья про микроконтроллер EFM32ZG110F32 - 22

Сигнал SWO не является обязательным для программирования и отладки. В микроконтроллерах EFM32 Zero Gecko, на и в любых других микроконтроллерах Cortex-M0/-M0+, линии SWO не предусмотрено, однако если через встроенный на плату EFM32ZG-STK3200 J-Link вы программируете кристалл на базе Cortex-M3 и выше, подключение SWO позволит использовать для отладки данные трассировки.

Сигнал Reset формально также не является обязательным, но, цитирую документацию, highly recommended. Если при выполнении программы на целевом контроллере отключатся источники тактирования или порты, использующиеся для отладки, то потом до отлаживаемого контроллера можно будет не достучаться.

Также нужно заметить, что целевой контроллер не запитывается по линии VMCU. Сигнал с VMCU служит для измерения энергопотребления, что позволяет использовать energy profiler для оптимизации энергопотребления кристалла, расположенного на другой плате.

3. Настройка IDE

В Simplicity IDE процесс отладки при использовании платы EFM32ZG-STK3200 в режиме Debug Out не отличается от стандартного. При использовании Keil или IAR лучше свериться с настройками J-Link, описанными в документе AN0043 — EFM32 Debug and Trace.

Пример #5 Работа с EFM32 в среде mbed


В последнем примере хотелось бы описать принцип разработки программ для микроконтроллеров EFM32 Zero Gecko с использованием платформы mbed от компании ARM.
mbed — это масштабный проект, который должен полностью покрыть потребности разработчиков IoT-приложений. В составе платформы и mbed OS для систем на базе ядер Cortex-M, и онлайн-IDE, и библиотеки для поддержки аппаратных модулей разного рода (интерфейсы, датчики, память, приводы и т.д.), облачные сервисы и бог знает что ещё. Платформа постепенно развивается и пока оставляет у разработчиков противоречивые впечатления.
Воздержусь от оценки mbed и обзора всех её функций и ограничусь небольшим руководством по быстрому старту работы в mbed с использованием микроконтроллеров EFM32 Zero Gecko.

SiLabs — это один из десятка производителей микроконтроллеров, чьи контроллеры поддерживаются в mbed. В разделе, посвященном EFM32ZG пока не так много разработок, но всё же есть три готовых программы, по которым можно понять что к чему и опробовать онлайн-компилятор. Этим и займемся.

Отладочная плата должна быть сконфигурирована стандартный режим отладки (см. Debug Mode: MCU в примере #4) и подключена к компьютеру через USB-интерфейс. Нужно также убедиться что питание платы осуществляется по отладочному интерфейсу (переключатель питания в положении DBG), а прошивка платы обновлена до последней версии. Прошивка платы не имеет никакого отношения к прошивке микроконтроллера, всё что нужно о ней знать — это то что существует два варианта: прошивка без поддержки mbed и прошивка с поддержкой mbed. На новых платах поддержка mbed есть по умолчанию, а для выпущенных год-два-три назад китах обновиться нужно вручную. Для этого достаточно зайти в Kit Manager из главного меню Simplicity Studio. Если обновление необходимо, но Kit Manager сразу предложит это сделать. Останется нажать Ок и подождать пару секунд.

Разработка с помощью mbed полностью проходит онлайн. Для начала на сайте developer.mbed.org нужно зарегистрироваться, после этого нужно перейти в раздел Platforms и найти плату EFM32ZG-STK3200.
Статья про микроконтроллер EFM32ZG110F32 - 23
Статья про микроконтроллер EFM32ZG110F32 - 24

Каждый зарегистрированный в mbed пользователь волен добавлять в свой онлайн-компилятор сколь угодно плат от разных производителей. Добавляем EFM32ZG-STK3200, и переходим в окно IDE.
Статья про микроконтроллер EFM32ZG110F32 - 25
Статья про микроконтроллер EFM32ZG110F32 - 26

Далее предлагается выбрать пример для загрузки. Пример с выводом на ЖКИ стандартной картинки и часов в формате часы: минуты: секунды подойдет.
Статья про микроконтроллер EFM32ZG110F32 - 27

Готовый пример можно скомпилировать, не внося изменений в код. После компиляции бинарный файл автоматически загружается на компьютер.
Статья про микроконтроллер EFM32ZG110F32 - 28

А теперь интересное. Все отладочные платы для микроконтроллеров EFM32, обладающие поддержкой mbed, определяются компьютером как Mass Storage Device.

Статья про микроконтроллер EFM32ZG110F32 - 29

Загруженный с developer.mbed.org файл достаточно скопировать на внешний диск STK3200, в ту же секунду контроллер будет запрограммирован и на для ЖКИ будет выведено время в заданном формате.

Статья про микроконтроллер EFM32ZG110F32 - 30

Словом, начать разработку в mbed довольно просто. Процесс разработки программ также стараются упростить и унифицировать, весь среди основных целей mbed понижение порога вхождения в разработку встраиваемых систем. Хорошее дело.

Для примера также привожу код mbed для мигания светодиодом. За исключением функции sleep(), код универсален и может быть запущен на отладочных платах от других партнеров mbed. Поскольку главной особенностью EFM32 является низкое энергопотребление, для mbed написан sleep API, позволяющий использовать режимы энергопотребления даже при разработке средствами ARM mbed.


#include "mbed.h"
 
DigitalOut myled(LED1);
LowPowerTicker toggleTicker;

void ledToggler(void) {
    myled = !myled;
}
 
int main() {
    toggleTicker.attach(&ledToggler, 0.2f);
    while(1) {
        sleep();
    }
}

Заключение


Спасибо за внимание. Хочется верить, что кристалл EFM32ZG110F32 и вся серия EFM32 заинтересовала уважаемого читателя. Если вы хотите взять на пробу микроконтроллер или отладочную плату, то проще всего написать мне здесь или на email, указанный в профиле.

Автор: ЭФО

Источник

Поделиться новостью

* - обязательные к заполнению поля