Всем привет
В этой статье я хочу показать, насколько в Embox легко перенести существующий проект на новую платформу. За основу возьмем уже описанный ранее демонстрационный проект с ModBus и расскажем, как портировать наш проект на новую плату на примере STM32 NUCLEO-F2207ZG.
В статье про ModBus уже было показано, как важны для уменьшения “времени выхода на рынок” (time-to-market), переносимость ПО и удобство разработки и отладки. А теперь предположим, что мы хотим сделать новую версию нашего устройства на новой аппаратной платформе, более дешевой, менее энергопотребляющей или просто доступной на рынке.
Портирование на новую плату
Итак мы хотим выпустить новую версию нашего устройства на плате NUCLEO-F207ZG, которая не поддерживается в Embox. Более того, не поддержаны никакие платы серии F2.
Начнём с поддержки STM32Cube для серии F2, его мы используем для упрощения поддержки различной периферии как описали в статье.
Получилось, что мы можем продемонстрировать весь процесс добавления новой STM32 платы. В коде она видна в нашем Pull Request на github.
Процесс добавления поддержки новой платы можно описать следующими шагами:
Добавление поддержки STM32Cube
Добавление платформо-зависимой части
Добавление базового темплейта (конфигурации сборки)
Добавление описания устройств
Добавление поддержки драйверов
UART
GPIO
I2C
SPI
FLASH (In-chip)
Ethernet
Расширение темплейта драйверами и другими модулями
Поддержка STM32Cube
На сегодняшний день большинство драйверов в Embox для STM32 использует STM32Cube для обеспечения переносимости драйверов. Поэтому нам пришлось добавить поддержку SMT32Cube для серии F2.
Посмотреть код можно в папке third-party/bsp/stm32f2cube
Там всего несколько файлов.
./Mybuild - объявляет абстрактный модуль для построения на основе него различных плат.
package third_party.bsp.stmf2cube
abstract module stm32f2_conf {
}
./cube/stm32cube_hal.h - заголовочный файл для включения в драйверах чтобы не зависить от серии Cube.
#ifndef THIRD_PARTY_BSP_STMF2CUBE_CUBE_STM32CUBE_HAL_H_
#define THIRD_PARTY_BSP_STMF2CUBE_CUBE_STM32CUBE_HAL_H_
#include "stm32f2xx_hal.h"
#endif /* THIRD_PARTY_BSP_STMF2CUBE_CUBE_STM32CUBE_HAL_H_ */
./cube/Makefile - makefile для скачивания внешних проктов.
PKG_NAME := stm32cubef2
PKG_VER := v1.9.4
PKG_SOURCES := https://www.github.com/STMicroelectronics/STM32CubeF2/archive/$(PKG_VER).zip
PKG_MD5 := 741de186164780de6e15314b79a72f9e
include $(EXTBLD_LIB)
./cube/Mybuild описаваем модули для Embox.
модуль cpp_flags нужен для указания флагов и путей, мы будем его использовать для модулей которые зависят от нашего Cube.
package third_party.bsp.stmf2cube
@BuildArtifactPath(cppflags="-DUSE_HAL_DRIVER -DSTM32F2_CUBE")
@BuildArtifactPath(cppflags="$(addprefix -I$(EXTERNAL_BUILD_DIR)/third_party/bsp/stmf2cube/cube/STM32CubeF2-1.9.4/, Drivers/STM32F2xx_HAL_Driver/Inc Drivers/CMSIS/Device/ST/STM32F2xx/Include Drivers/CMSIS/Include)")
module cube_cppflags {
}
Модуль cube
@Build(stage=1,script="$(EXTERNAL_MAKE) download extract patch")
@BuildDepends(cube_cppflags)
@BuildDepends(third_party.bsp.stmf2cube.stm32f2_conf)
static module cube {
option number eth_rx_packet_count = 4
option number eth_tx_packet_count = 2
@IncludeExport(path="bsp")
source "stm32cube_hal.h"
@AddPrefix("^BUILD/extbld/^MOD_PATH/STM32CubeF2-1.9.4/Drivers/STM32F2xx_HAL_Driver/Src")
source
"stm32f2xx_hal.c",
"stm32f2xx_hal_cortex.c",
"stm32f2xx_hal_adc.c",
…
@NoRuntime depends third_party.bsp.stmf2cube.stm32f2_conf
@NoRuntime depends cube_cppflags
}
В аннотации @Build
указывается, что модуль собирается на первом шаге сборки (есть еще нулевой, когда собирается база Embox, и второй, когда собираются пакеты, зависящие от базовых). А также что будет вызван Makefile для сборки внешнего проекта.
Аннотация @BuildDepends
добавляет при сборке нужные флаги компиляции, например только что описанные в cube_cppflags.
Аннотация @IncludePath
указывает, что наш независимый от серии cube заголовочный файл следует поместить в папку bsp, доступную всем при сборке.
Аннотация @AddPrefix
добавляет путь к исходникам, указанным в следующем за ним команде source.
Поддержка платы
Для поддержки новой платы нужно настроить некоторые особенности, специфичные для конкретной платы, например задать тактовые частоты. Код, отвечающий за данную часть, можно посмотреть в папке platform/stm32/f2/nucleo_f207zg.
./stm32cube_compat.c -- вспомогательный файл, содержащий функцию HAL_GetTick(), необходимую для корректной работы STM32Cube.
uint32_t HAL_GetTick(void) {
return clock_sys_ticks();
}
./arch.c -- файл реализующий интерфейс arch в Embox -- файл с настройками для платы из оригинального Cube, но в него внесено изменение, позволяющее управлять количеством буферов для сетевой карты с помощью опций Mybuild.
/* Definition of the Ethernet driver buffers size and count */
#define ETH_RX_BUF_SIZE ETH_MAX_PACKET_SIZE /* buffer size for receive */
#define ETH_TX_BUF_SIZE ETH_MAX_PACKET_SIZE /* buffer size for transmit */
#if defined(__EMBOX__)
#include <framework/mod/options.h>
#include <module/third_party/bsp/stmf2cube/cube.h>
#define ETH_RXBUFNB \
OPTION_MODULE_GET(third_party__bsp__stmf2cube__cube, NUMBER, eth_rx_packet_count)
#define ETH_TXBUFNB \
OPTION_MODULE_GET(third_party__bsp__stmf2cube__cube, NUMBER, eth_tx_packet_count)
#else
#define ETH_RXBUFNB 5U /* 5 Rx buffers of size ETH_RX_BUF_SIZE */
#define ETH_TXBUFNB 5U /* 5 Tx buffers of size ETH_TX_BUF_SIZE */
#endif /*__EMBOX__*/
./arch.c -- файл реализующий интерфейс arch в Embox.
void arch_init(void) {
ipl_t ipl = ipl_save();
SystemInit();
HAL_Init();
SystemClock_Config();
ipl_restore(ipl);
}
void arch_idle(void) {
}
void arch_shutdown(arch_shutdown_mode_t mode) {
switch (mode) {
case ARCH_SHUTDOWN_MODE_HALT:
case ARCH_SHUTDOWN_MODE_REBOOT:
case ARCH_SHUTDOWN_MODE_ABORT:
default:
HAL_NVIC_SystemReset();
break;
}
/* NOTREACHED */
while(1) {
}
}
Настройка системных клоков (SystemClock_Config) была также взята из Cube.
В файле ./Mybuild описаны модули, необходимые для данной платы.
package platform.stm32.f2.nucleo_f207zg
@Build(stage=1)
@BuildArtifactPath(cppflags="-DSTM32F207xx -DUSE_STM32F2XX_NUCLEO_144")
static module nucleo_f207zg_conf extends third_party.bsp.stmf2cube.stm32f2_conf {
@IncludeExport(path="")
source "stm32f2xx_hal_conf.h"
}
модуль nucleo_f207zg_conf наследуется от описанного выше абстрактного модуля stm32f2_conf, и он будет использован при сборке самого Cube. В нем указаны флаги, с которыми собирается STM32Cube, а также необходимый заголовочный файл stm32f2xx_hal_conf.h.
@BuildDepends(nucleo_f207zg_conf)
@BuildDepends(third_party.bsp.stmf2cube.cube)
module arch extends embox.arch.arch {
source "arch.c"
source "stm32cube_compat.c"
@AddPrefix("^BUILD/extbld/third_party/bsp/stmf2cube/cube/STM32CubeF2-1.9.4/")
source "Projects/NUCLEO-F207ZG/Templates/Src/system_stm32f2xx.c"
}
Модуль arch, необходимый для сборки Embox. В его исходниках должны быть реализованы три функции (arch_init(). arch_idle , arch_shutdown), которые находятся в файле arch.c. Также используется файл из STM32Cube system_stm32f2xx.c, содержащий функцию SystemInit(). И вспомогательный файл stm32cube_compat.c, содержащий функцию HAL_GetTick() необходимую для корректной работы STM32Cube.
@Build(stage=1)
@BuildDepends(nucleo_f207zg_conf)
@BuildDepends(third_party.bsp.stmf2cube.cube)
@BuildArtifactPath(cppflags="-I$(EXTERNAL_BUILD_DIR)/third_party/bsp/stmf2cube/cube/STM32CubeF2-1.9.4/Drivers/BSP/STM32F2xx_Nucleo_144")
static module bsp extends third_party.bsp.st_bsp_api {
…
@NoRuntime depends third_party.bsp.stmf2cube.cube
@NoRuntime depends nucleo_f207zg_conf
@NoRuntime depends arch
}
Конечный модуль, описывающий BSP для поддержки данной платы, будет требоваться для различных драйверов и содержать все необходимые флаги компиляции.
Базовый темплейт
После того, как мы добавили поддержку платы, мы можем собрать и запустить Embox, правда, пока что без периферийных устройств, даже без UART. Поэтому проверить работу мы сможем только с помощью gdb.
Для сборки Embox нужны минимум три файла конфигурации. Посмотреть пример минимальной конфигурации можно в папке templates/arm/minimal. В нашем случае мы добавим настройки для нашей платы.
Наш темплейт положим в папку для платы platform/stm32/templates/f2/nucleo_f207zg.
./lds.conf содержит описание карты памяти.
/* region (origin, length) */
ROM (0x08000000, 1024K)
RAM (0x20000000, 128K)
/* section (region[, lma_region]) */
text (ROM)
rodata (ROM)
data (RAM, ROM)
bss (RAM)
./build.conf содержит описание компилятора и флагов для сборки.
TARGET = embox
PLATFORM = nucleo_f207zg
ARCH = arm
CROSS_COMPILE = arm-none-eabi-
CFLAGS += -O0 -g
CFLAGS += -mthumb -mlittle-endian
CFLAGS += -march=armv7-m -mtune=cortex-m3
CFLAGS += -ffreestanding
./mods.conf содержит описание системы (требования к конечной системе).
package genconfig
configuration conf {
include embox.arch.system(core_freq=120000000)
@Runlevel(0) include embox.arch.arm.cortexm3.bundle
include embox.arch.arm.armmlib.locore
include embox.arch.arm.libarch
include embox.arch.arm.vfork
include platform.stm32.f2.nucleo_f207zg.bsp
@Runlevel(1) include embox.driver.interrupt.cortexm_nvic
@Runlevel(1) include embox.driver.clock.cortexm_systick
include embox.kernel.time.jiffies(cs_name="cortexm_systick")
…
}
Здесь мы уже задали системную частоту, наш новый BSP для платы, а также таймер и контроллер прерываний, поскольку они не зависят от платформы.
Описание устройств
Не так давно у нас появился аналог device tree (Linux или ZephyrOS). Данная технология позволяет удобно описывать конфигурацию устройств, доступных на аппаратной платформе. В отличие от Linux, нам нужно статическое представление. Файлы описания у нас храняться в папке board_config.
Добавим файл nucleo_f207zg.conf.h с описанием периферии.
#include <gen_board_conf.h>
#include <stm32.h>
struct uart_conf uarts[] = {
[1] = {
.status = DISABLED,
.name = "USART1",
.dev = {
.irqs = {
VAL("", 37),
},
.pins = {
PIN("TX", PA, PIN_9, AF7),
PIN("RX", PA, PIN_10, AF7),
},
.clocks = {
VAL("TX", CLK_GPIOA),
VAL("RX", CLK_GPIOA),
VAL("UART", CLK_USART1),
}
},
.baudrate = 115200,
},
…
};
…
struct spi_conf spis[] = {
[1] = {
.status = DISABLED,
.name = "SPI1",
.dev = {
.pins = {
PIN("SCK", PA, PIN_5, AF5),
PIN("MISO", PA, PIN_6, AF5),
PIN("MOSI", PA, PIN_7, AF5),
PIN("CS", PD, PIN_14, NOAF),
},
.clocks = {
VAL("SCK", CLK_GPIOA),
VAL("MISO", CLK_GPIOA),
VAL("MOSI", CLK_GPIOA),
VAL("CS", CLK_GPIOD),
VAL("SPI", CLK_SPI1),
}
},
},
};
struct i2c_conf i2cs[] = {
[1] = {
.status = ENABLED,
.name = "I2C1",
.dev = {
.irqs = {
VAL("EVENT_IRQ", 31),
VAL("ERROR_IRQ", 32),
},
.pins = {
PIN("SCL", GPIO_PORT_B, PIN_6, AF4),
PIN("SDA", GPIO_PORT_B, PIN_9, AF4),
},
.clocks = {
VAL("SCL", CLK_GPIOB),
VAL("SDA", CLK_GPIOB),
VAL("I2C", CLK_I2C1),
}
},
},
…
};
EXPORT_CONFIG(UART(uarts), LED(leds), SPI(spis), I2C(i2cs))
Поддержка драйверов
Базовые драйвера в Embox уже были, но нужно было добавить поддержку для серии F2.
UART
Драйвер находится в папке ./src/drivers/serial/stm32cube_usart . Драйвер использует описание board_config. Для добавления поддержки необходимо добавить заголовочный файл, содержащий специфичные для серии особенности, и описание в файл Mybuild.
@BuildDepends(third_party.bsp.st_bsp_api)
module stm_usart_f2 extends stm32_usart_ops {
@IncludeExport(path="drivers/serial", target_name="stm_usart.h")
source "stm32_usart_conf_f2.h"
source "stm_hal_msp.c"
source "stm_usart.c"
depends embox.driver.serial.core
depends third_party.bsp.st_bsp_api
}
Он зависит от нашего BSP и реализует интерфейс stm32_usart_ops.
./stm32_usart_conf_f2.h
#include <bsp/stm32cube_hal.h>
#define STM32_USART_FLAGS(uart) uart->SR
#define STM32_USART_RXDATA(uart) uart->DR
#define STM32_USART_TXDATA(uart) uart->DR
#define STM32_USART_CLEAR_ORE(uart) \
do { \
} while (0)
GPIO
Драйвер находится в папке ./src/drivers/gpio/stm32cube. Драйвер оказался независимым от серии (есть отличия для серии f1, там нет альтернативных функций для GPIO).
I2C
Драйвер находится в папке ./src/drivers/i2c/adapters/stm32cube_i2c и тоже не потребовал доработок для новой серии.
SPI
Драйвер находится в папке ./src/drivers/spi/stm32cube_spi и тоже не потребовал доработок для новой серии.
FLASH (In-chip)
Под данным устройством подразумевается блочное устройство, которое мы создаем, используя несколько блоков внутренней flash-памяти внутри микроконтроллера. Используем мы это устройство для организации простой файловой системы для хранения логов настроек, и ещё какой то заранее известной информации. Это описано в нашей статье.
Драйвер хранится в папке ./src/drivers/flash/stm32cube_flash.
Драйвер потребовал следующей доработки:
Добавить заголовочный файл, описывающий специфику данной серии, например, размер блока in-chip flash.
#include <framework/mod/options.h>
#define STM32_FLASH_FLASH_SIZE OPTION_GET(NUMBER,flash_size)
#define STM32_ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000)
/* First 4 sectors of STM32F4-Discovery flash are 16Kb */
#define STM32_FLASH_SECTOR_SIZE (16 * 1024)
/* We use only first 4 16Kb sectors */
#define STM32_FLASH_SECTORS_COUNT 4
#define STM32_FLASH_WORD (4)
#ifndef __ASSEMBLER__
#include <stm32f2xx_hal.h>
#include <string.h>
static inline void stm32_fill_flash_erase_struct(
FLASH_EraseInitTypeDef *erase_struct,
unsigned int block) {
memset(erase_struct, 0, sizeof *erase_struct);
erase_struct->TypeErase = FLASH_TYPEERASE_SECTORS;
erase_struct->Sector = block;
erase_struct->NbSectors = 1;
}
#endif
И добавить описание модуля для данной серии.
@BuildDepends(third_party.bsp.st_bsp_api)
module stm32f2cube {
option number log_level = 1
option number flash_size=0xc000
source "stm32_flash.c"
source "stm32_flash.lds.S"
@IncludeExport(path="drivers/block_dev/flash",target_name="stm32flash.h")
source "stm32f2flash.h"
depends third_party.bsp.st_bsp_api
depends core
}
Ethernet
Драйвер находится в папке ./src/drivers/net/stm32cube.
Для поддержки новой платы потребовалось задать функцию HAL_MSP_Init в фйле stm32f2cube_eth_msp.c короторой происходит настройка правильных контактов для работы с PHY.
void HAL_ETH_MspInit(ETH_HandleTypeDef *heth)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable GPIOs clocks */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
/* Ethernet pins configuration ************************************************/
/*
RMII_REF_CLK ----------------------> PA1
RMII_MDIO -------------------------> PA2
RMII_MDC --------------------------> PC1
RMII_MII_CRS_DV -------------------> PA7
RMII_MII_RXD0 ---------------------> PC4
RMII_MII_RXD1 ---------------------> PC5
RMII_MII_RXER ---------------------> PG2
RMII_MII_TX_EN --------------------> PG11
RMII_MII_TXD0 ---------------------> PG13
RMII_MII_TXD1 ---------------------> PB13
*/
/* Configure PA1, PA2 and PA7 */
GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure PB13 */
GPIO_InitStructure.Pin = GPIO_PIN_13;
HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Configure PC1, PC4 and PC5 */
GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5;
HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
/* Configure PG2, PG11, PG13 and PG14 */
GPIO_InitStructure.Pin = GPIO_PIN_2 | GPIO_PIN_11 | GPIO_PIN_13;
HAL_GPIO_Init(GPIOG, &GPIO_InitStructure);
/* Enable ETHERNET clock */
__HAL_RCC_ETH_CLK_ENABLE();
}
Добавить заголовочный файл stm32f2cube_eth.h, в котором только определен PHY адрес.
#include <bsp/stm32cube_hal.h>
#define PHY_ADDRESS LAN8742A_PHY_ADDRESS
И добавить описание модуля в Mybuild.
@BuildDepends(third_party.bsp.st_bsp_api)
module stm32f2cube_eth {
option number irq = 61
source "stm32cube_eth.c"
source "stm32f2cube_eth_msp.c"
@IncludeExport(path="drivers/net", target_name="stm32cube_eth.h")
source "stm32f2cube_eth.h"
option number log_level=1
…
}
Расширение темплейта
После добавления драйверов расширим наш базовый темплейт, чтобы проверить что добавленные драйвера работают.
Прежде всего, нам потребуется файл с настройками нашей периферии, который будет будет инициализировать то, что указано в файле с описанием устройств. Файл board.conf.h.
#include <nucleo_f207zg.conf.h>
CONFIG {
uarts[1].status = ENABLED;
uarts[2].status = DISABLED;
uarts[3].status = ENABLED;
spis[1].status = ENABLED;
}
В файл mods.conf добавим нашу переферию.
@Runlevel(1) include embox.driver.serial.stm_usart_f2
@Runlevel(1) include embox.driver.serial.stm_diag(baud_rate=115200, usartx=3)
@Runlevel(1) include embox.driver.diag(impl="embox__driver__serial__stm_diag")
@Runlevel(1) include embox.driver.serial.stm_ttyS0(baud_rate=115200, usartx=3)
include embox.driver.gpio.stm32cube_gpio
@Runlevel(1) include embox.driver.input.button.stm32cube_button(pin_port=2, pin_num=13)
include embox.driver.spi.core
include embox.driver.spi.stm32cube_spi(log_level=0)
include embox.driver.spi.stm32cube_spi1(log_level=0)
include embox.driver.i2c.stm32cube_i2c
include embox.driver.i2c.stm32cube_i2c1
include embox.driver.flash.flash_cache_block
include embox.driver.flash.stm32f2cube
include embox.driver.flash.flash_fs
@Runlevel(2) include embox.driver.net.stm32f2cube_eth
Добавим командный интерпретатор и несколько полезных утилит, например.
@Runlevel(3) include embox.init.system_start_service(log_level=3, tty_dev="ttyS0")
include embox.cmd.sh.tish(
builtin_commands = "exit logout service"
)
include embox.cmd.service(services_count=2)
include embox.cmd.sys.version
include embox.cmd.help
include embox.cmd.fs.dd
include embox.cmd.fs.cat
include embox.cmd.fs.ls
include embox.cmd.fs.rm
include embox.cmd.fs.mount
include embox.cmd.fs.umount
include embox.cmd.fs.stat
include embox.cmd.fs.echo
include embox.cmd.fs.touch
include embox.cmd.fs.mkdir
include embox.cmd.net.ifconfig
include embox.cmd.net.route
include embox.cmd.net.ping
include embox.cmd.net.bootpc
include embox.cmd.net.telnetd
include embox.cmd.net.netmanager
include embox.cmd.testing.ticker
include embox.cmd.testing.block_dev_test
include embox.cmd.testing.input.input_test
include embox.cmd.testing.input.button_test
include embox.cmd.hardware.pin
include embox.cmd.hardware.spi
include embox.cmd.hw.lsblk
include embox.cmd.hw.input
include embox.cmd.i2c_tools.i2cdetect
include embox.cmd.i2c_tools.i2cdump
include embox.cmd.i2c_tools.i2cget
include embox.cmd.i2c_tools.i2cset
include embox.cmd.net.httpd
Естественно, нужно еще расширить конфигурацию такими вещами, как настройка ядра (многозадачность, время и так далее) и другие системные штуки. Но не будут останавливаться на этом в данной статье.
Отмечу, что поскольку мы использовали system_start_service, нам нужно ещё добавить в темплейт файл system_start.inc/ Например с таким содержанием.
"mkdir -v /conf",
"mount -t DumbFS /dev/stm32flash0 /conf",
"conf_setup",
"netmanager",
"service httpd /http_admin",
"service telnetd",
"tish",
Поскольку мы для настройки сети используем netmanager, нам нужно добавить в корневую файловую система файл network с нужными настройками. Просто создадим папку rootfs в нашем темплейте и положим туда наш файл.
iface eth0 inet static
address 192.168.2.128
netmask 255.255.255.0
gateway 192.168.2.1
hwaddress aa:bb:cc:dd:ee:02
#iface eth0 inet dhcp
Проверка демонстрационного темплейта
После этого собираем наш темплетейт (make confload-platform/stm32/f2/nucleo_f207zg && make), заливаем образ на плату и убеждаемся, что всё работает.
По умолчанию стартуют telnetd и httpd, как можно видеть в стартовом скрипте.
Заходим телнетом
Заходим браузером
Всё работает, причём сам процесс переноса у меня занял меньше двух дней. Да, часть драйверов у нас ещё требует доработки, и в будущем мы будем это улучшать :)
Запуск проекта на новой плате
Напоминаю, проект у нас внешний и найти его можно на github в отдельном репозитории .
После добавления поддержки платы попробуем запустить демонстрационный проект на новой плате, указав, что у нас есть два светодиода которыми мы хотим управлять с помощью строчки в mods.conf.
include iocontrol.modbus.lib.libleddrv(leds_quantity=2)
Приведу diff, из которого видно, что изменения которые я внёс, касаются только некоторых аппаратных особенностей платформ
diff stm32f4_discovery_demo/mods.conf nucleo_f207zg_demo/mods.conf
4c4
< include embox.arch.system(core_freq=144000000)
---
> include embox.arch.system(core_freq=120000000)
6c6
< include platform.stm32.f4.stm32f4_discovery.bsp
---
> include platform.stm32.f2.nucleo_f207zg.bsp
13,14c13,14
< @Runlevel(0) include embox.arch.arm.fpu.cortex_m4_fp
< @Runlevel(0) include embox.arch.arm.fpu.fpv5(log_level=3)
---
> //@Runlevel(0) include embox.arch.arm.fpu.cortex_m4_fp
> //@Runlevel(0) include embox.arch.arm.fpu.fpv5(log_level=3)
20,21c20,21
< @Runlevel(1) include embox.driver.serial.stm_usart_f4
< @Runlevel(1) include embox.driver.serial.stm_diag(baud_rate=115200, usartx=6)
---
> @Runlevel(1) include embox.driver.serial.stm_usart_f2
> @Runlevel(1) include embox.driver.serial.stm_diag(baud_rate=115200, usartx=3)
23,24c23
< @Runlevel(1) include embox.driver.serial.stm_ttyS1(baud_rate=57600, usartx=2)
< @Runlevel(1) include embox.driver.serial.stm_ttyS0(baud_rate=115200, usartx=6)
---
> @Runlevel(1) include embox.driver.serial.stm_ttyS0(baud_rate=115200, usartx=3)
27c26
< @Runlevel(2) include embox.driver.flash.stm32f4cube
---
> @Runlevel(2) include embox.driver.flash.stm32f2cube
32c31
< @Runlevel(2) include embox.driver.net.stm32f4cube_eth
---
> @Runlevel(2) include embox.driver.net.stm32f2cube_eth
133c132
< include iocontrol.modbus.lib.libleddrv(leds_quantity=4)
---
> include iocontrol.modbus.lib.libleddrv(leds_quantity=2)
Теперь просто соберём и запустим проект на новой плате. На всякий случай приведу всю инструкцию, она достаточно короткая, чтобы желающие могли воспроизвести ситуацию у себя.
Подготовка платы
Настраиваем наш проект как внешний в embox
make ext_conf EXT_PROJECT_PATH=<your projects path>Конфигурируем проект для нашей платы
make confload-ext_project/modbus/nucleo_f207zg_demoНастраиваем нужные сетевые настройки в файле ‘conf/rootfs/network’
Собираем
makeПрошиваем плату с помощью openocd как указано на wiki (https://github.com/embox/embox/wiki/NUCLEO-F207ZG)
Подготовка modbus-клиента
Заходим в папку ‘./modbus/host_cmds’ во внешнем проекте и собираем клиент с помощью команды make
Запуск и тестирование
Запускаем плату, подключив ethernet-кабель.
С помощью собранного modbus-клиента управляем светодиодами.
./led-client -a 192.168.2.128 set 0 # включает красный (0) светодиод на плате.
./led-client -a 192.168.2.128 clr 0 # выключает его.
192.168.2.128 -- адрес платы.
Синий (1) светодиод также управляется с помощью этой команды.
Заходим браузером на адрес нашей платы
И с помощью нажатия на соответствующую кнопку включаем или выключаем нужный светодиод. Состояние можно сохранить во flash-памяти, оно будет восстановлено при перезагрузке.
Можно посмотреть как это работает на этом видео
Заключение
Как показано в статье, разрабатывать и отлаживать прикладные задачи под Embox очень удобно. А, как видно из данной статьи, добавление новой платформы и перенос на нее существующего проекта, не составляет труда. Это и позволяет делать жизненный цикл устройства при разработке под Embox очень технологичным и позволяет существенно экономить на поддержке и сопровождении устройства, а также минимизирует параметр time-to-market.