Всем привет

В этой статье я хочу показать, насколько в Embox легко перенести существующий проект на новую платформу. За основу возьмем уже описанный ранее демонстрационный проект с ModBus и расскажем, как портировать наш проект на новую плату на примере STM32 NUCLEO-F2207ZG.

В статье про ModBus уже было показано, как важны для уменьшения “времени выхода на рынок” (time-to-market), переносимость ПО и удобство разработки и отладки. А теперь предположим, что мы хотим сделать новую версию нашего устройства на новой аппаратной платформе, более дешевой, менее энергопотребляющей или просто доступной на рынке.


Портирование на новую плату

Итак мы хотим выпустить новую версию нашего устройства на плате NUCLEO-F207ZG, которая не поддерживается в Embox. Более того, не поддержаны никакие платы серии F2.

Начнём с поддержки STM32Cube для серии F2, его мы используем для упрощения поддержки различной периферии как описали в статье.

Получилось, что мы можем продемонстрировать весь процесс добавления новой STM32 платы. В коде она видна в нашем Pull Request на github.

Процесс добавления поддержки новой платы можно описать следующими шагами:

  1. Добавление поддержки STM32Cube

  2. Добавление платформо-зависимой части

  3. Добавление базового темплейта (конфигурации сборки)

  4. Добавление описания устройств

  5. Добавление поддержки драйверов

    1. UART

    2. GPIO

    3. I2C

    4. SPI

    5. FLASH (In-chip)

    6. Ethernet

  6. Расширение темплейта драйверами и другими модулями

Поддержка 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", PB, PIN_9, AF7),
   			 PIN("RX", PB, 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)

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

Подготовка платы

  1. Настраиваем наш проект как внешний в embox
    make ext_conf EXT_PROJECT_PATH=<your projects path>

  2. Конфигурируем проект для нашей платы
    make confload-ext_project/modbus/nucleo_f207zg_demo

  3. Настраиваем нужные сетевые настройки в файле ‘conf/rootfs/network

  4. Собираем
    make

  5. Прошиваем плату с помощью 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.

Комментарии (10)


  1. BARSRAB
    28.11.2023 05:47

    Что-то я так и не понял, в чем преимущество Embox по отношению к той же FreeRTOS (да и к другим ОСРВ), которая при грамотном написании драйверов, переносится на другой МК вообще без проблем, просто с правкой конфига и заменой драйверов периферии, если это требуется. Причем без танцев с бубном и калокуба и сам перенос занимает, ну час. ОС в принципе мало зависит от МК. Да и сами STM нынче малопригодны для использования, сейчас в моде китайские МК.


    1. DungeonLords
      28.11.2023 05:47

      Удачи вам там с модными китайскими чипами! И вот сразу три причины, из-за которых контроллер GD32F450 теряет UDP пакеты


      1. BARSRAB
        28.11.2023 05:47

        Удачи, не удачи, а альтернатив на текущий момент нет и не предвидится. Да и реализовывать программный стек на любых МК занятие такое себе. Куда проще поставить сетевой контроллер, типа W5500 и не знать вообще никаких проблем в работе TCP/UDP. А по стоимости она не сильно отличается от PHY. Тем более проблема то оказалась по большей части именно в PHY, для которого не поставили отдельный генератор. А проблема чек суммы описана в Errata

        Да, сейчас STM32 как-то продаются, даже ценник упал до более-менее приемлемых величин. Но что будет завтра, никто не скажет, а китаезные чипы как продавались, так и будут продаваться.


    1. abondarev Автор
      28.11.2023 05:47
      +1

      Что-то я так и не понял, в чем преимущество Embox по отношению к той же FreeRTOS (да и к другим ОСРВ)

      Спасибо за вопрос. На самом деле преимущество не в драйверах, да мы их совершенствуем, развиваем dev-tree, но одно из наших слабых мест, это как раз количество поддерживаемых платформ. Для FreeRTOS вы скорее всего возьмете готовое решение. Так как пока популярность проекта далека от FreeRTOS, количество разработчиков драйверов сильно ограничено, и про разные платформы и драйвера у нас часто спрашивают. Эта статья показывает, что вы сами легко можете добавить новую платформу. постепенно компании начинают добавлять поддержку нужных платформ.
      Поскольку по сравнению с различными RTOS (тем более FreeRTOS) у нас огромное преимущество при разработке вашей прикладной системы на Линукс, а уже затем перенос на железо, причем любое. Это отмечено в заключении

      Как показано в статье, разрабатывать и отлаживать прикладные задачи под Embox очень удобно.

      Да и сами STM нынче малопригодны для использования, сейчас в моде китайские МК.

      STM ки просто пример, и да, у нас их часто спрашивают. GD мы сейчас рекомендуем как альтернативу, и там описание будет такое же. А на самом деле, в РФ вот вот появятся собственые МК, правда не уверен что по цене китайских.



      1. BARSRAB
        28.11.2023 05:47

        Для FreeRTOS вы скорее всего возьмете готовое решение. 

        Ну как готовое. Если ядро остается тем же, то я просто перенесу сборку с одного МК на другой, заменив при необходимости драйвера периферии. Если же ядро другое, то возьму сборку RTOS под требуемое ядро и на нее перенесу уже все остальное. Ну как сборку, пару файлов исходников.

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

        Платформа - имеется в виду конкретная плата с МК? Тогда вообще непонятно, зачем добавлять их поддержку в RTOS. Ведь для запуска именно ядра ОС, этой самой ОС требуется только знать, на каком ядре МК она запускается, и все. Периферия же к самой ОС отношения не имеет.

        А на самом деле, в РФ вот вот появятся собственые МК, правда не уверен что по цене китайских.

        Даааа, присылали нам на работу предложение ознакомиться с МК из РФ. Вот только даташит почему-то от GD32 был...


        1. abondarev Автор
          28.11.2023 05:47

          Даааа, присылали нам на работу предложение ознакомиться с МК из РФ. Вот только даташит почему-то от GD32 был...

          Не знаю может Вы и правы, но у нас другой опыт https://habr.com/ru/articles/777302/

          Ведь для запуска именно ядра ОС, этой самой ОС требуется только знать,
          на каком ядре МК она запускается, и все. Периферия же к самой ОС
          отношения не имеет.

          Не хочется спорить. Коротко, если вы не сталкивались с подобными проблемами которые мы пытаемся решать (довольно успешно), я не смогу этого объяснить


          1. BARSRAB
            28.11.2023 05:47

            но у нас другой опыт https://habr.com/ru/articles/777302/

            Да, фирма у нас была другая.

            Коротко, если вы не сталкивались с подобными проблемами которые мы пытаемся решать (довольно успешно), я не смогу этого объяснить

            Это с какими? Портирование ОС под какие-то эвал борды? А в чем смысл? Это тоже самое, что делать сборку ОС под каждый конкретный ПК. ОС для встраиваемых систем поставляется в виде ядра, больше от нее ничего не нужно. Периферия все равно в каждом проекте будет использована своя. Так какой смысл заниматься созданием ОС под конкретные платы? Пока я вижу ваши проблемы в войне с ветряными мельницами. Хотя вы даже не особо в курсе, как к МК подключиться

            Подключите плату с помощью jtag (сигналы (PA2 SWCLK, PA3 SWDIO, GND) 

            Открою страшную тайну, у JTAG совершенно другие сигнальные линии. А это SWD интерфейс.

            И в статье про модбас вы пишите:

             Ведь в случае с RTOS ами обычно просто делается новая библиотека под конкретную платформу, ну или берется уже написаная и самостоятельно прикручивается. 

            Ну начнем с того, что отдельная либа под модбас нужна только в том случае, если используется весь его функционал. На практике же используется буквально 3-4 типа запроса. Плюс даже если этого отдельная либа, то прикручивается к RTOS она куда проще и быстрее вашего варианта. Единственное, что там потребуется отредактировать - карта регистров под конкретное устройство. Зачем что-то для этого делать под линуксом, а потом портировать на МК? Особенно если учесть, что для этого нужен сам Линукс. Много каких-то лишних телодвижений, Telnet, веб морда и т.п. барахло, которое в 99% случаем не требуется и только без толку жрет процессорное время. Плюс я так и не нашел нигде сравнения скорости работы вашей RTOS с хотя бы одной не вашей.


            1. abondarev Автор
              28.11.2023 05:47

              Хотя вы даже не особо в курсе, как к МК подключиться

              Приплыли! :)

              Пока я вижу ваши проблемы в войне с ветряными мельницами.

              Повторяю, не нужно тратить свое время на всякую чепуху, ну мало ли кто чего понаписал, в самом то деле :)


              1. BARSRAB
                28.11.2023 05:47

                Приплыли! :)

                Приплыли. Не уметь отличить интерфейсы друг от друга, это нонсенс для разработчика.

                 не нужно тратить свое время на всякую чепуху

                Это уже мне решать, куда и как тратить время) Пока я вижу, что вы делаете работу ради работы. По сравнению с той же FreeRTOS у вас какие-то сплошные костыли и грабли. И вам об этом не раз писали в комментариях. Проекту вашему больше 10 лет, а вы до сих пор не можете внятно объяснить, зачем он нужен и чем лучше других RTOS. Даже сравнение быстродействия до сих пор нет. Да и судя по статьям, все, что он делает - это работа на различных отладках.


                1. abondarev Автор
                  28.11.2023 05:47

                  Это уже мне решать, куда и как тратить время) Пока я вижу, что вы делаете работу ради работы.

                  Не видите случайно двойных стандартов в ваших идущих подрят двух фразах?