Как отмечалось на geektimes, микрокомпьютер BBC micro:bit ещё этой весной начали рассылать британским школьникам, а пару месяцев назад он поступил в свободную продажу по цене от £13 за штуку.
Предположим, micro:bit приобретён; что с ним делать дальше? Я решил сделать из него часы, потому что мои наручные как раз сломались.

Инструкция по использованию micro:bit со старой версией mbed OS есть на сайте Ланкастерского университета; но ARM две недели назад выпустила новую версию mbed OS 5, и с этой новой версией библиотека поддержки microbit-dal «из коробки» не работает.
Насколько я понимаю, даже в самом ARM никто ещё не пытался использовать mbed OS 5 на micro:bit; мне хотелось стать первым.
Для начала работы нужно установить среду разработки mbed CLI. Она написана на Python (для работы требуется версия 2.7.6+), и распространяется посредством PyPI:
Либо, если мы работаем на машине без прав root, и даже без
Кроме этого, нужно установить компилятор GNU ARM Embedded. Если tarball с компилятором распакован в
Теперь создаём для нашего проекта рабочее окружение:
Следующий шаг — добавим в наше рабочее окружение библиотеки поддержки micro:bit:
В составе ланкастерской библиотеки есть ассемблерный файл
Кроме этого, с ланкастерской библиотекой есть ещё несколько проблем:
Первые две проблемы исправлены в моём форке ланкастерской библиотеки; для третьей предлагается следующий низкотехнологичный воркараунд:
Последняя проблема решается настройками компиляции: чтобы кучи хватило для работы microbit-dal, системный стек должен быть размером 512 байт или меньше. (Обработчикам прерываний, которые им пользуются, хватает и половины этого.)
Теперь самое интересное — собственно реализация часов. В ней всего два нетривиальных момента:
Когда этот код сохранён (скажем, в файл
Готово!
При желании, для получившихся часов можно смастерить защитный корпус из баночки из-под витаминов и термоклея:

Предположим, micro:bit приобретён; что с ним делать дальше? Я решил сделать из него часы, потому что мои наручные как раз сломались.

Инструкция по использованию micro:bit со старой версией mbed OS есть на сайте Ланкастерского университета; но ARM две недели назад выпустила новую версию mbed OS 5, и с этой новой версией библиотека поддержки microbit-dal «из коробки» не работает.
Насколько я понимаю, даже в самом ARM никто ещё не пытался использовать mbed OS 5 на micro:bit; мне хотелось стать первым.
Для начала работы нужно установить среду разработки mbed CLI. Она написана на Python (для работы требуется версия 2.7.6+), и распространяется посредством PyPI:
$ sudo pip install mbed-cli
Либо, если мы работаем на машине без прав root, и даже без
pip:$ wget http://bootstrap.pypa.io/ez_setup.py $ python ez_setup.py --user $ ~/.local/bin/pip install virtualenv --user $ ~/.local/bin/virtualenv venv $ source venv/bin/activate (venv) $ pip install mbed-cli
Кроме этого, нужно установить компилятор GNU ARM Embedded. Если tarball с компилятором распакован в
/work/gcc-arm-none-eabi-5_4-2016q2/, то он регистрируется в mbed CLI командой $ mbed config --global GCC_ARM_PATH /work/gcc-arm-none-eabi-5_4-2016q2/bin/
Теперь создаём для нашего проекта рабочее окружение:
$ mbed new mb_clock $ cd mb_clock $ mbed target NRF51_MICROBIT $ mbed toolchain GCC_ARM
Если командаmbed newвыполняется из-под root и/или внутри venv, то она сама доустановит в систему необходимые модули Python. В противном случае, она попросит выполнить
$ sudo pip install -r mbed-os/requirements.txt
Следующий шаг — добавим в наше рабочее окружение библиотеки поддержки micro:bit:
$ mbed add https://github.com/lancaster-university/microbit # первая ланкастерская библиотека $ mbed add https://github.com/tyomitch/microbit-dal # мой форк второй ланкастерской библиотеки
В составе ланкастерской библиотеки есть ассемблерный файл
CortexContextSwitch.s, который поставляется в двух вариантах: для GNU as и для armasm. Библиотека для mbed OS 3 включала файл CMakeLists.txt, в котором был прописан автоматический выбор нужного варианта. Увы, mbed OS 5 игнорирует CMakeLists.txt, так что вариант для GNU as придётся выбрать вручную: $ cp microbit-dal/source/asm/CortexContextSwitch.s.gcc microbit-dal/source/asm/CortexContextSwitch.s
Кроме этого, с ланкастерской библиотекой есть ещё несколько проблем:
- mbed OS 5 включает поддержку многопоточности, поэтому код microbit-dal теперь выполняется с «пользовательским» стеком (PSP), а не с «системным» (MSP), как в предыдущих версиях mbed OS;
- системный API для работы с BLE в mbed OS 5 изменился;
- поддержка BLE в mbed OS 5 занимает слишком много памяти, и на micro:bit с его 16 КБ RAM она просто не влезает;
- размер стека по умолчанию (2 КБ) слишком велик: с таким стеком в системе не остаётся свободной памяти для динамического выделения («куча»).
Первые две проблемы исправлены в моём форке ланкастерской библиотеки; для третьей предлагается следующий низкотехнологичный воркараунд:
$ rm mbed-os/features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_MCU_NRF51822/source/nRF5xn.cpp
Последняя проблема решается настройками компиляции: чтобы кучи хватило для работы microbit-dal, системный стек должен быть размером 512 байт или меньше. (Обработчикам прерываний, которые им пользуются, хватает и половины этого.)
Теперь самое интересное — собственно реализация часов. В ней всего два нетривиальных момента:
- Для того, чтобы отображать на дисплее micro:bit размером 5х5 светодиодов по две цифры одновременно, пришлось творчески подойти к созданию шрифта. Ноль я решил сделать в виде точки посередине знакоместа (похожим образом он выглядит �� арабских цифрах), восьмёрку — в виде двоеточия, по логике «два ноля один над другим». Все остальные цифры узнаются безо всякого затруднения.
- У micro:bit нет энергонезависимых «часов реального времени», поэтому время отсчитывается от начальной загрузки. Начальное отображаемое значение задаётся в момент компиляции; после запуска, при помощи двух кнопок micro:bit, отображаемое время можно корректировать в ту или другую сторону, с шагом в одну минуту.
Листинг
#include "MicroBit.h" MicroBit uBit; const uint8_t digit_bits[10][10] = { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }, { 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 }, { 1, 1, 0, 1, 1, 1, 1, 0, 1, 1 }, { 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 }, { 0, 1, 1, 1, 1, 1, 0, 1, 0, 1 }, { 1, 1, 1, 0, 1, 1, 0, 1, 1, 1 }, { 0, 1, 1, 0, 1, 0, 1, 1, 1, 1 }, { 1, 1, 0, 1, 0, 1, 1, 0, 1, 0 }, { 0, 0, 0, 1, 0, 0, 0, 1, 0, 0 }, { 1, 1, 1, 1, 0, 1, 0, 1, 1, 0 } }; MicroBitImage digits[] = { MicroBitImage(2,5,digit_bits[0]), MicroBitImage(2,5,digit_bits[1]), MicroBitImage(2,5,digit_bits[2]), MicroBitImage(2,5,digit_bits[3]), MicroBitImage(2,5,digit_bits[4]), MicroBitImage(2,5,digit_bits[5]), MicroBitImage(2,5,digit_bits[6]), MicroBitImage(2,5,digit_bits[7]), MicroBitImage(2,5,digit_bits[8]), MicroBitImage(2,5,digit_bits[9]) }; int started_at = 18*60+53; void onButtonAClick(MicroBitEvent evt) { started_at--; } void onButtonBClick(MicroBitEvent evt) { started_at++; } int main() { uBit.init(); uBit.messageBus.listen(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, onButtonAClick); uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick); while(1) { int cur_time = (started_at + uBit.systemTime() / 60000L) % (24*60); int hours = cur_time / 60; int minutes = cur_time % 60; uBit.display.image.paste(digits[hours/10],0,0,0); uBit.display.image.paste(digits[hours%10],3,0,0); uBit.sleep(300); uBit.display.image.paste(digits[minutes/10],0,0,0); uBit.display.image.paste(digits[minutes%10],3,0,0); uBit.sleep(300); uBit.display.clear(); uBit.sleep(600); } }
Когда этот код сохранён (скажем, в файл
mb_clock.cpp), весь проект можно скомпилировать, и загрузить на устройство:$ mbed compile -D __STACK_SIZE=512 -D ISR_STACK_SIZE=512 -D MICROBIT_BLE_ENABLED=0 $ cp ./.build/NRF51_MICROBIT/GCC_ARM/mb_clock.hex /media/MICROBIT
Готово!
При желании, для получившихся часов можно смастерить защитный корпус из баночки из-под витаминов и термоклея:

