Для работы с периферией и самой платой нужны драйвера и другой системный код. Данный код можно взять из примеров предоставляемых самим производителем чипа. В нашем случае производитель предлагает использовать SimplifyStudio. Есть также открытый репозиторий на GitHub). Этот код и будем использовать.
В Embox есть механизмы позволяющие использовать BSP производителя при создании драйверов. Для этого нужно скачать BSP и собрать его как библиотеку в Embox. При этом можно указать различные пути и флаги, необходимые для использования данной библиотеки в драйверах.
Пример Makefile для скачивания BSP
PKG_NAME := Gecko_SDK
PKG_VER := v5.1.2
PKG_ARCHIVE_NAME := $(PKG_NAME)-$(PKG_VER).tar.gz
PKG_SOURCES := https://github.com/SiliconLabs/$(PKG_NAME)/archive/v5.1.2.tar.gz
PKG_MD5 := 0de78b48a8da80931af1a53d401e74f5
include $(EXTBLD_LIB)
Mybuild для сборки BSP
package platform.efm32
...
@BuildArtifactPath(cppflags="-I$(EXTERNAL_BUILD_DIR)/platform/efm32/bsp_get/Gecko_SDK-5.1.2/hardware/kit/common/bsp/")
module bsp_get { }
@BuildDepends(bsp_get)
@BuildDepends(efm32_conf)
static module bsp extends embox.arch.arm.cmsis {
…
source "platform/emlib/src/em_timer.c",
"platform/emlib/src/em_adc.c",
…
depends bsp_get
depends efm32_conf
}
Mybuild для платы EFM32ZG_STK3200
package platform.efm32.efm32zg_stk3200
@BuildArtifactPath(cppflags="-I$(EXTERNAL_BUILD_DIR)/platform/efm32/bsp_get/Gecko_SDK-5.1.2/platform/Device/SiliconLabs/EFM32ZG/Include")
@BuildArtifactPath(cppflags="-I$(EXTERNAL_BUILD_DIR)/platform/efm32/bsp_get/Gecko_SDK-5.1.2/hardware/kit/EFM32ZG_STK3200/config")
...
@BuildArtifactPath(cppflags="-D__CORTEX_SC=0")
@BuildArtifactPath(cppflags="-DUART_COUNT=0")
@BuildArtifactPath(cppflags="-DEFM32ZG222F32=1")
module efm32zg_stk3200_conf extends platform.efm32.efm32_conf {
source "efm32_conf.h"
}
@BuildDepends(platform.efm32.bsp)
@BuildDepends(efm32zg_stk3200_conf)
static module bsp extends platform.efm32.efm32_bsp {
@DefineMacro("DOXY_DOC_ONLY=0")
@AddPrefix("^BUILD/extbld/platform/efm32/bsp_get/Gecko_SDK-5.1.2/")
source
"platform/Device/SiliconLabs/EFM32ZG/Source/system_efm32zg.c",
"hardware/kit/common/drivers/displayls013b7dh03.c",
...
}
После таких достаточно простых действий можно использовать код от производителя. Прежде чем приступить к работе с драйверами необходимо разобраться со средствами разработки и архитектурными частями. В Embox используются обычные средства разработки gcc, gdb, openocd. При запуске openocd нужно указать что мы используем платформу efm32:
sudo openocd -f /usr/share/openocd/scripts/board/efm32.cfg
Для нашей платки нет каких-то специальных архитектурных частей, только специфика cortex-m0+. Это задается компилятором. Поэтому мы можем задать общий код для cotrex-m0 отключив все лишнее, например, работу с плавающей точкой.
@Runlevel(0) include embox.arch.generic.arch
include embox.arch.arm.libarch
@Runlevel(0) include embox.arch.arm.armmlib.locore
@Runlevel(0) include embox.arch.system(core_freq=8000000)
@Runlevel(0) include embox.arch.arm.armmlib.exception_entry(irq_stack_size=256)
@Runlevel(0) include embox.kernel.stack(stack_size=1024,alignment=4)
@Runlevel(0) include embox.arch.arm.fpu.fpu_stub
После этого можно попробовать скомпилить Embox и походить с помощью отладчика по шагам, проверив тем самым правильно ли мы задали параметры в линкер скрипте
/* region (origin, length) */
ROM (0x00000000, 32K)
RAM (0x20000000, 4K)
/* section (region[, lma_region]) */
text (ROM)
rodata (ROM)
data (RAM, ROM)
bss (RAM)
Первым драйвером реализуемым для поддержки какой-нибудь платы в Embox обычно является UART. На нашей плате есть LEUART. Для драйвера достаточно реализовать несколько функций. При этом мы можем использовать функции из BSP.
static int efm32_uart_putc(struct uart *dev, int ch) {
LEUART_Tx((void *) dev->base_addr, ch);
return 0;
}
static int efm32_uart_hasrx(struct uart *dev) {
...
}
static int efm32_uart_getc(struct uart *dev) {
return LEUART_Rx((void *) dev->base_addr);
}
static int efm32_uart_setup(struct uart *dev, const struct uart_params *params) {
LEUART_TypeDef *leuart = (void *) dev->base_addr;
LEUART_Init_TypeDef init = LEUART_INIT_DEFAULT;
/* Enable CORE LE clock in order to access LE modules */
CMU_ClockEnable(cmuClock_HFPER, true);
...
/* Finally enable it */
LEUART_Enable(leuart, leuartEnable);
return 0;
}
...
DIAG_SERIAL_DEF(&efm32_uart0, &uart_defparams);
Для того чтобы функции BSP были доступны нужно просто указать это в описании драйвера, файл Mybuild
package embox.driver.serial
@BuildDepends(platform.efm32.efm32_bsp)
module efm32_leuart extends embox.driver.diag.diag_api {
option number baud_rate
source "efm32_leuart.c"
@NoRuntime depends platform.efm32.efm32_bsp
depends core
depends diag
}
После реализации драйвера UART вам доступны не только вывод, но и консоль где вы можете вызвать свои пользовательские команды. Для этого вам достаточно добавить в конфигурационный файл Embox маленький командный интерпретатор:
include embox.cmd.help
include embox.cmd.sys.version
include embox.lib.Tokenizer
include embox.init.setup_tty_diag
@Runlevel(2) include embox.cmd.shell
@Runlevel(3) include embox.init.start_script(shell_name="diag_shell")
А также указать, что нужно использовать не полноценный tty доступный через devfs, а заглушку, которая позволяет обращаться к заданному устройству. Устройство задается также в конфигурационном файле mods.conf
@Runlevel(1) include embox.driver.serial.efm32_leuart
@Runlevel(1) include embox.driver.diag(impl="embox__driver__serial__efm32_leuart")
include embox.driver.serial.core_notty
Еще один очень простой драйвер это GPIO. Для его реализации мы также можем воспользоваться вызовами из BSP. Для этого в описании драйвера укажем что он зависит от BSP
package embox.driver.gpio
@BuildDepends(platform.efm32.efm32_bsp)
module efm32_gpio extends api {
option number log_level = 0
option number gpio_chip_id = 0
option number gpio_ports_number = 2
source "efm32_gpio.c"
depends embox.driver.gpio.core
@NoRuntime depends platform.efm32.efm32_bsp
}
Сама реализация
static int efm32_gpio_setup_mode(unsigned char port, gpio_mask_t pins, int mode) {
...
}
static void efm32_gpio_set(unsigned char port, gpio_mask_t pins, char level) {
if (level) {
GPIO_PortOutSet(port, pins);
} else {
GPIO_PortOutClear(port, pins);
}
}
static gpio_mask_t efm32_gpio_get(unsigned char port, gpio_mask_t pins) {
return GPIO_PortOutGet(port) & pins;
}
...
static int efm32_gpio_init(void) {
#if (_SILICON_LABS_32B_SERIES < 2)
CMU_ClockEnable(cmuClock_HFPER, true);
#endif
#if (_SILICON_LABS_32B_SERIES < 2)
|| defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)
CMU_ClockEnable(cmuClock_GPIO, true);
#endif
return gpio_register_chip((struct gpio_chip *)&efm32_gpio_chip, EFM32_GPIO_CHIP_ID);
}
Этого достаточно чтобы использовать команду ‘pin’ из Embox. Данная команда позволяет управлять GPIO. И в частности, может использоваться для проверки мигания светодиодом.
Добавляем саму команду в mods.conf
include embox.cmd.hardware.pin
И сделаем так, чтобы она запускалась при старте. Для этого в конфигурационном файле start_sctpt.inc добавим одну из строчек
<source”>«pin GPIOC 10 blink»,
Или
"pin GPIOC 11 blink",
Команды одинаковые, просто номера светодиодов разные.
Попробуем запустить еще и дисплей. Сначала все просто. Ведь мы опять можем использовать вызовы BSP. Для этого нам нужно только добавить их в описание драйвера фреймбуфера
package embox.driver.video
@BuildDepends(platform.efm32.efm32_bsp)
module efm32_lcd {
...
source "efm32_lcd.c"
@NoRuntime depends platform.efm32.efm32_bsp
}
Но как только мы делаем любой вызов связанный с дисплеем например DISPLAY_Init у нас секция .bss увеличивается больше чем на 2 kB, при размерах RAM 4 kB, это очень существенно. После изучения данного вопроса, выяснилось, что в самом BSP выделен фреймбуфер размером под дисплей. То есть 128x128x1 бит или 2048 байт.
В этот момент я даже хотел остановиться на достигнутом, ведь уместить в 4kB RAM вызов пользовательских команд с каким-то простым командным интерпретатором само по себе достижение. Но все-таки решил попробовать.
Первым я убрал командный интерпретатор и оставил только вызов уже упомянутой команды pin. Для этого я изменил конфигурационный файл mods.conf следующим образом
//@Runlevel(2) include embox.cmd.shell
//@Runlevel(3) include embox.init.start_script(shell_name="diag_shell")
@Runlevel(3) include embox.init.system_start_service(cmd_max_len=32, cmd_max_argv=6)
Поскольку я использовал другой модуль для пользовательского старта, я перенес запуск команд в другой конфигурационный файл. Вместо start_script.inc использовал system_start.inc.
Затем, поскольку уже не требовалось использовать индексные дескрипторы в командном интерпретаторе, а также таймеры, я с помощью опций в mods.config, избавился и от них
include embox.driver.common(device_name_len=1, max_dev_module_count=0)
include embox.compat.libc.stdio.file_pool(file_quantity=0)
…
include embox.kernel.task.resource.idesc_table(idesc_table_size=3)
include embox.kernel.task.task_no_table
@Runlevel(1) include embox.kernel.timer.sys_timer(timer_quantity=1)
...
@Runlevel(1) include embox.kernel.timer.itimer(itimer_quantity=0)
Поскольку я вызывал команды напрямую, а не через командный интерпретатор, я смог уменьшить размер стека
@Runlevel(0) include embox.arch.arm.armmlib.exception_entry(irq_stack_size=224)
@Runlevel(0) include embox.kernel.stack(stack_size=448,alignment=4)
Наконец, у меня собралось и запустилось мигание светодиодом, и при этом внутри был вызов инициализации дисплея.
Мне хотелось вывести что нибудь на дисплейю Я подумал, что логотип Embox будет показателен. По хорошему нужно использовать полноценный драйвер фреймбуфера и выводить изображение из файла, ведь все это есть в Embox. Но места совсем не хватало. И для демонстрации я решил вывести логотип прямо в функции инициализации драйвера фреймбуфера. Причем данные конвертировав напрямую в битовый массив. Таким образом мне потребовалось ровно 2048 байт в ROM.
Сам код, как и ранее, использует BSP
extern const uint8_t demo_image_mono_128x128[128][16];
static int efm_lcd_init(void) {
DISPLAY_Device_t displayDevice;
EMSTATUS status;
DISPLAY_PixelMatrix_t pixelMatrixBuffer;
/* Initialize the DISPLAY module. */
status = DISPLAY_Init();
if (DISPLAY_EMSTATUS_OK != status) {
return status;
}
/* Retrieve the properties of the DISPLAY. */
status = DISPLAY_DeviceGet(DISPLAY_DEVICE_NO, &displayDevice);
if (DISPLAY_EMSTATUS_OK != status) {
return status;
}
/* Allocate a framebuffer from the DISPLAY device driver. */
displayDevice.pPixelMatrixAllocate(&displayDevice,
displayDevice.geometry.width,
displayDevice.geometry.height,
&pixelMatrixBuffer);
#if START_WITH_LOGO
memcpy(pixelMatrixBuffer, demo_image_mono_128x128,
displayDevice.geometry.width * displayDevice.geometry.height / 8 );
status = displayDevice.pPixelMatrixDraw(&displayDevice,
pixelMatrixBuffer,
0,
displayDevice.geometry.width,
0,
displayDevice.geometry.height);
#endif
return 0;
}
Собственно все. На коротком видео можно увидеть результат.
Весь код доступен на GitHub. Если есть плата, то же самое можно воспроизвести на ней с помощью инструкции описанной на wiki.
Результат превзошел мои ожидания. Ведь удалось запустить Embox по сути на 2kB RAM. Это означает, что с помощью опций в Embox накладные расходы на использование ОС можно свести к минимуму. При этом в системе присутствует многозадачность. Пусть даже она и кооперативная. Ведь обработчики таймеров вызываются не напрямую в контексте прерывания, а из собственного контекста. Что естественно является плюсом использования ОС. Конечно, данный пример во многом искусственный. Ведь при столь ограниченных ресурсах и функциональность будет ограниченной. Преимущества Embox начинают сказываться на более мощных платформах. Но в то же время это можно считать предельным случаем работы Embox.
Apple возобновила переговоры с OpenAI о возможности внедрения ИИ-технологий в iOS 18, на основе данной операционной системы будут работать новые…
Конкурсный управляющий российской «дочки» Google подготовил 23 иска к участникам рекламного рынка. Общая сумма исков составляет 16 млрд рублей –…
Google завершил обновление основного алгоритма March 2024 Core Update. Раскатка обновлений была завершена 19 апреля, но сообщил об этом поисковик…
У частных продавцов на Авито появилась возможность составлять текст объявлений с помощью нейросети. Новый функционал доступен в категории «Обувь, одежда,…
24 апреля 2024 года в Москве состоялась церемония вручения наград международного конкурса Workspace Digital Awards. В этом году участниками стали…
27 июня Яндекс проведет гик-фестиваль Young Con для студентов и молодых специалистов, которые интересуются технологиями и хотят работать в IT.…