Статья про микроконтроллер EFM32ZG110F32
Так уж вышло, что у нас на складе оказалось довольно много микроконтроллеров EFM32ZG110F32, это серия Zero Gecko от компании SiLabs. Контроллеры классные, но пока не особенно популярны, потому я и пишу эту статью.
На правах рекламы мы предлагаем вот такой набор: 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, проще всего судить по вот такой схеме.
Цветом для каждого блока обозначен режим энергопотребления, вплоть до которого блок может использоваться:
- 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, а на отладочной плате находится более «старший» микроконтроллер той же серии — 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), появится главное меню программы.
Все отобразившиеся инструменты активны именно для подключенного микроконтроллера. Если бы был выбран, например, 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, содержащий готовые прошивки для микроконтроллера.
Нужно просто выбрать один из проектов и нажать кнопку Finish. Контроллер будет запрограммирован готовым бинарным файлом, на плате запустится выбранная программа, а на экране компьютера появится график изменения уровня потребляемого тока.
Если всё прошло успешно, то можно начинать разработку собственных программ.
Все демо-программы доступны и в виде проектов для каждой из поддерживаемых сред разработки. Соответствующие файлы после установки Simplicity Studio находятся в директории …\SiliconLabs\SimplicityStudio\v3\developer\sdks\efm32\v2\kits\EFM32ZG_STK3200\examples
Для каждого примера доступны:
- проект для 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. Для их использования необходимо добавить в проект соответствующие файлы, их можно найти в директории …\SimplicityStudio\v3\developer\sdks\efm32\v2\emlib после установки Simplicity Studio.
В приведенном примере также используется пакет BSP (Board Support Package), включающий готовые функции, упрощающие работу с отладочными платами для EFM32. Эти функции позволяют не задумываться о топологии платы при работе с установленными на ней модулями (например, BSP_LedInit () и BSP_LedToggle (1)). Библиотеки BSP не рассчитаны ни на работу с другими платами, ни даже на работу с фирменной платой, запитанной от батарейки, а не отладочного USB. Однако в пробном проекте вполне можно себе такое позволить.
И EFM32 API, и BSP доступны для всех IDE, поддерживающих микроконтроллеры EFM32.
Что касается подключения по LEUART к передающему устройству, то в соответствии с программной настройкой LEUART (см. функцию initLeuart () из приведенного ниже листинга), требуется
а) запитать плату от отладочного USB-интерфейса,
б) подключить линию D5 и землю к устройству-передатчику.
Первый вариант программной реализации
Для наглядности эксперимента, на первой итерации написания программы притворимся школьником и забудем о существовании прерываний. Пишем программу, выполняющую бесконечный цикл опроса последовательного интерфейса:
//....
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, доступное в выпадающем меню.
При настройке конфигурации должны быть указаны исполняемый файл и настройки bild-а, соответствующие используемой IDE.
Многие настройки в Profile Configurations также связаны с функциями Code Correlation. Code Correlation — это привязка результатов измерений энергопотребления к исполняемому коду (каждой точке на графике ставится в соответствие строка в листинге). Именно благодаря этой опции мы и говорим о профилировании, а не просто об измерении энергопотребления. Звучит, конечно, здорово, но для микроконтроллеров серии Zero Gecko функция Code Correlation недоступна. Для того чтобы соотнести измерения и текст программы, с платы должно дополнительно сниматься значение счетчика команд. Такие данные передаются по линии SWO, которой на микроконтроллерах на базе Cortex-M0 не предусмотрено. Поэтому мы будем довольствоваться только графиком изменения тока, строящимся по ходу исполнения программы, что тоже весьма неплохо.
Начинаем профилирование первого варианта реализации программы.
Действительно, раз в 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++;
}
}
#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 мА.
Ожидание посылки микроконтроллер проводит в режиме EM2, это самый глубокий сон, который мы можем себе позволить для сохранения функциональности LEUART. Обработку результатов изменять в общем-то некуда, значит дальнейшая оптимизация программы возможна только для процесса получения посылки. Посмотрим на соответствующий участок графика ближе.
По приходу каждого символа строки { '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 мкА.
Исчезли «зубчики», соответствующие обработке прерываний по каждому пришедшему символу. За счет этого заметно снизились и энергопотребление за прием/обработку данных, и среднее значение.
Мне не хотелось бы вдаваться в сравнение конкретных цифр (строка могла бы, например, быть подлиннее, а посылки почаще), но думаю что принцип снижения потребления с использованием low energy UART и energy profiler описан достаточно понятно.
Пример #3 Работа с аппаратным счетчиком импульсов PCNT и конфигуратором кристалла
Рассмотрим другой интересный инструмент разработки, конфигуратор периферии и линий ввода/вывода. Выбрав соответствующий пункт главного меню Simplicity Studio и указав используемый part number, можно начать создание нового проекта в графической среде настройки контроллера.
Для микроконтроллера EFM32ZG110F32 (да-да, всё ещё $0.96 за штуку) пользователю доступен вот такой интерфейс:
Окно настройки линий ввода/вывода
Окно настройки периферии
Общий принцип работы в Configurator таков: выбирается необходимая периферия, для каждого блока настраивается режим работы, после этого назначаются необходимые для работы блоков линии ввода/вывода, для каждой линии задается конфигурация. После настройки запускается генерация заготовки проекта для вашей среды разработки.
Конфигуратор позволяет упростить начальный этап программирования — вместо выуживания настроек периферии из документации используется графический интерфейс. К тому же, эта утилита позволяет быстро оценить возможности выбранного кристалла и сделать простой пробный проект. Подобный конфигуратор есть у большинства производителей микроконтроллеров.
С использованием конфигуратора познакомимся с 16-разрядным счетчиком импульсов. Также как LEUART, этот модуль редко можно найти на микроконтроллерах, и, также как LEUART, счетчик импульсов позволяет снизить энергопотребление устройства.
Итак, в меню настройки периферии микроконтроллера EFM32ZG110F32 ставим галочку на PCNT0 и оглядываем доступные настройки.
Модуль может быть настроен на подсчет количества импульсов, приходящих на один из портов кристалла, в этом случае прерывание генерируется по достижении заданного количества импульсов. Другой вариант работы модуля — это режим квардатурного энкодера с прерыванием по изменению направления счета (по часовой стрелке / против часовой стрелки).
Счетчик 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 относятся к режиму квадратурного энкодера, поэтому на них не смотрим вовсе.
Увидев наши настройки, конфигуратор справедливо замечает, что раз уж подсчитываются импульсы, приходящие на одну из линий ввода/вывода, то эту линию нужно выбрать и настроить.
По клику на сообщение об ошибке переходим в окно настройки линий ввода/вывода, где для линии S0IN доступно две локации:
Допустим, что вторая локация, т.е. линия PC0, нам подходит больше. Снова смотрим в окно ошибок.
И, опять соглашаясь с конфигуратором, изменяем режим работы ножки контроллера с Disabled на Input.
Теперь можно генерировать исходный код для заданной конфигурации микроконтроллера 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;
}
/**************************************************************************//**
* @file
* @brief Empty Project
* @author Energy Micro AS
* @version 3.20.2
******************************************************************************
* @section License
* (C) Copyright 2014 Silicon Labs, http://www.silabs.com
*******************************************************************************
*
* 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) {
}
}
Файл InitD