diff --git a/software/library/boards/board.build b/software/library/boards/board.build
index 1d50ddf7297d8838020457b5485761527e17b3d2..d90d2423569927d33f6c34499334730121ee15ee 100644
--- a/software/library/boards/board.build
+++ b/software/library/boards/board.build
@@ -1,3 +1,9 @@
+%section bmaaa-brain
+ %inherit stm32-f107vc
+
+ CONFIG_BMAAA
+ CONFIG_BMAAA_BOARD brain
+
%section bmaaa-pinky
%inherit nrf51-xxac
diff --git a/software/library/boards/brain.c b/software/library/boards/brain.c
new file mode 100644
index 0000000000000000000000000000000000000000..af6c0dafe0c400a62d8270d3a9305124e3f21c64
--- /dev/null
+++ b/software/library/boards/brain.c
@@ -0,0 +1,340 @@
+/*
+ This file is part of MutekH.
+
+ MutekH is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; version 2.1 of the
+ License.
+
+ MutekH is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program. If not, see
+ .
+
+ Copyright Julien Peeters (c) 2016
+*/
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+/* CPU */
+
+DEV_DECLARE_STATIC(cpu_dev, "cpu", DEVICE_FLAG_CPU, arm32m_drv,
+ DEV_STATIC_RES_ID(0, 0),
+
+ DEV_STATIC_RES_FREQ(72000000, 1)
+);
+
+/* GPIO A..E. */
+DEV_DECLARE_STATIC(gpio_dev, "gpio", 0, stm32_gpio_drv,
+ DEV_STATIC_RES_DEV_ICU("/cpu"),
+ DEV_STATIC_RES_IRQ(0, STM32_IRQ_EXTI0, DEV_IRQ_SENSE_HIGH_LEVEL, 0, 0x1),
+ DEV_STATIC_RES_IRQ(1, STM32_IRQ_EXTI1, DEV_IRQ_SENSE_HIGH_LEVEL, 0, 0x1),
+ DEV_STATIC_RES_IRQ(2, STM32_IRQ_EXTI2, DEV_IRQ_SENSE_HIGH_LEVEL, 0, 0x1),
+ DEV_STATIC_RES_IRQ(3, STM32_IRQ_EXTI3, DEV_IRQ_SENSE_HIGH_LEVEL, 0, 0x1),
+ DEV_STATIC_RES_IRQ(4, STM32_IRQ_EXTI4, DEV_IRQ_SENSE_HIGH_LEVEL, 0, 0x1),
+ DEV_STATIC_RES_IRQ(5, STM32_IRQ_EXTI9_5, DEV_IRQ_SENSE_HIGH_LEVEL, 0, 0x1),
+ DEV_STATIC_RES_IRQ(6, STM32_IRQ_EXTI15_10, DEV_IRQ_SENSE_HIGH_LEVEL, 0, 0x1)
+);
+
+/* UART 4 */
+DEV_DECLARE_STATIC(uart2_dev, "uart4", 0, stm32_usart_drv,
+ DEV_STATIC_RES_MEM(STM32_UART4_ADDR, STM32_UART4_ADDR + STM32_UART4_SIZE),
+
+ DEV_STATIC_RES_FREQ(72000000, 1),
+
+ DEV_STATIC_RES_DEV_ICU("/cpu"),
+ DEV_STATIC_RES_IRQ(0, STM32_IRQ_UART4, DEV_IRQ_SENSE_HIGH_LEVEL, 0, 0x1),
+
+ DEV_STATIC_RES_DEV_PARAM("iomux", "/gpio"),
+ DEV_STATIC_RES_IOMUX("tx", 0, STM32_PC10, 0, 0),
+ DEV_STATIC_RES_IOMUX("rx", 0, STM32_PC11, 0, 0),
+
+ DEV_STATIC_RES_UART(19200, 8, DEV_UART_PARITY_NONE, 1, 0, 0)
+);
+
+/* UART 4 */
+DEV_DECLARE_STATIC(timer4_dev, "timer4", 0, stm32_timer_drv,
+ DEV_STATIC_RES_MEM(STM32_TIM4_ADDR, STM32_TIM4_ADDR + STM32_TIM4_SIZE),
+
+ DEV_STATIC_RES_FREQ(72000000, 1),
+
+ DEV_STATIC_RES_DEV_ICU("/cpu"),
+ DEV_STATIC_RES_IRQ(0, STM32_IRQ_TIM4, DEV_IRQ_SENSE_HIGH_LEVEL, 0, 0x1),
+);
+
+#include
+#include
+#include
+#include
+
+#define __IO volatile
+
+struct stm32f1xx_flash_dev_s
+{
+ __IO uint32_t FLASH_ACR;
+ __IO uint32_t FLASH_KEYR;
+ __IO uint32_t FLASH_OPTKEYR;
+ __IO uint32_t FLASH_SR;
+ __IO uint32_t FLASH_CR;
+ __IO uint32_t FLASH_AR;
+ __IO uint32_t __reserved0;
+ __IO uint32_t FLASH_OBR;
+ __IO uint32_t FLASH_WRPR;
+} __attribute__ ((packed));
+
+#define STM32_FLASH_LAT_WS(n) ((n) & 0x7)
+
+void stm32_clock_init(void)
+{
+ uintptr_t a;
+ uint32_t x;
+
+ __IO struct stm32f1xx_flash_dev_s *flash_dev =
+ (struct stm32f1xx_flash_dev_s *) 0x40022000;
+
+ /* Disable interrupts. */
+ cpu_mem_write_32(STM32_RCC_ADDR + STM32_RCC_CIR_ADDR, 0);
+
+ /* PLL OFF, PLLI2S OFF, HSE ON, HSI OFF. */
+ cpu_mem_write_32(STM32_RCC_ADDR + STM32_RCC_CR_ADDR,
+ endian_le32(STM32_RCC_CR_HSEON));
+
+ /* Wait for hse to stabilize. */
+ do
+ {
+ x = endian_le32(cpu_mem_read_32(STM32_RCC_ADDR + STM32_RCC_CR_ADDR));
+ }
+ while (!(x & STM32_RCC_CR_HSERDY));
+
+ /* Set PLL2 @ 40MHz and PLL3 @ 50MHz */
+ x = 0;
+ STM32_RCC_CFGR2_PREDIV2_SET(x, 0x4 /* / 5 */);
+ STM32_RCC_CFGR2_PLL2MUL_SET(x, 0x6 /* x 8 */);
+ STM32_RCC_CFGR2_PLL3MUL_SET(x, 0x8 /* x 10 */);
+ cpu_mem_write_32(STM32_RCC_ADDR + STM32_RCC_CFGR2_ADDR, endian_le32(x));
+
+ /* Enable PLL2 @ 40MHz */
+ a = STM32_RCC_ADDR + STM32_RCC_CR_ADDR;
+ x = endian_le32(cpu_mem_read_32(a));
+ STM32_RCC_CR_PLL2ON_SET(x, 1);
+ cpu_mem_write_32(a, endian_le32(x));
+
+ /* Wait for PLL2 to lock */
+ do
+ {
+ x = endian_le32(cpu_mem_read_32(a));
+ }
+ while (!(x & STM32_RCC_CR_PLL2RDY));
+
+ /* Enable PLL3 @ 50MHz */
+ STM32_RCC_CR_PLL3ON_SET(x, 1);
+ cpu_mem_write_32(a, endian_le32(x));
+
+ /* Wait for PLL2 to lock */
+ do
+ {
+ x = endian_le32(cpu_mem_read_32(a));
+ }
+ while (!(x & STM32_RCC_CR_PLL3RDY));
+
+ /* Set PLL @ 72MHz as sink of PLL2 */
+ x = endian_le32(cpu_mem_read_32(STM32_RCC_ADDR + STM32_RCC_CFGR2_ADDR));
+ STM32_RCC_CFGR2_PREDIV1SRC_SET(x, 0x1 /* PLL2 */);
+ STM32_RCC_CFGR2_PREDIV1_SET(x, 0x4 /* / 5 */);
+ cpu_mem_write_32(STM32_RCC_ADDR + STM32_RCC_CFGR2_ADDR, endian_le32(x));
+
+ x = endian_le32(cpu_mem_read_32(STM32_RCC_ADDR + STM32_RCC_CFGR_ADDR));
+ STM32_RCC_CFGR_PLLSRC_SET(x, 0x1 /* PREDIV1 */);
+ STM32_RCC_CFGR_PLLMUL_SET(x, 0x7 /* x 9 */);
+ cpu_mem_write_32(STM32_RCC_ADDR + STM32_RCC_CFGR_ADDR, endian_le32(x));
+
+ /* Enable PLL */
+ a = STM32_RCC_ADDR + STM32_RCC_CR_ADDR;
+ x = endian_le32(cpu_mem_read_32(a));
+ STM32_RCC_CR_PLLON_SET(x, 1);
+ cpu_mem_write_32(a, endian_le32(x));
+
+ /* Wait for PLL to lock */
+ do
+ {
+ x = endian_le32(cpu_mem_read_32(a));
+ }
+ while (!(x & STM32_RCC_CR_PLLRDY));
+
+ /* set wait cycles for the flash. */
+ flash_dev->FLASH_ACR = STM32_FLASH_LAT_WS(2);
+
+ /* Configure prescalers. */
+ a = STM32_RCC_ADDR + STM32_RCC_CFGR_ADDR;
+ x = endian_le32(cpu_mem_read_32(a));
+ STM32_RCC_CFGR_HPRE_SET(x, 0x0 /* / 1 */); /* 72MHZ */
+ STM32_RCC_CFGR_PPRE1_SET(x, 0x1 /* / 2 */); /* 36MHZ */
+ STM32_RCC_CFGR_PPRE2_SET(x, 0x0 /* / 1 */); /* 72MHz */
+
+ /* Use PLL-based clock path for the system clock. */
+ STM32_RCC_CFGR_SW_SET(x, 0x2 /* PLL */);
+
+ /* Set MCO clock source */
+ STM32_RCC_CFGR_MCO_SET(x, 0x6 /* HSE */);
+ cpu_mem_write_32(a, endian_le32(x));
+
+ /* Wait for system clock to be sourced by the PLL. */
+ do
+ {
+ x = endian_le32(cpu_mem_read_32(a));
+ }
+ while (!(x & STM32_RCC_CFGR_SWS_PLL));
+
+ /* Enable all clock gates. */
+ cpu_mem_write_32(STM32_RCC_ADDR + STM32_RCC_AHBENR_ADDR, -1);
+ cpu_mem_write_32(STM32_RCC_ADDR + STM32_RCC_APB1ENR_ADDR, -1);
+ cpu_mem_write_32(STM32_RCC_ADDR + STM32_RCC_APB2ENR_ADDR, -1);
+
+ cpu_mem_write_32(STM32_RCC_ADDR + STM32_RCC_AHBRSTR_ADDR, -1);
+ cpu_mem_write_32(STM32_RCC_ADDR + STM32_RCC_AHBRSTR_ADDR, 0);
+}
+
+#include
+#include
+
+enum shell_otoroshi_opts_e
+{
+ OTOROSHI_OPT_RELAY = 0x1,
+ OTOROSHI_OPT_STATE = 0x2,
+ OTOROSHI_OPT_CLOSED = 0x4,
+ OTOROSHI_OPT_OPEN = 0x8,
+ OTOROSHI_OPT_PULSE = 0x10,
+ OTOROSHI_OPT_DELAY = 0x20,
+};
+
+struct termui_optctx_otoroshi_opts
+{
+ uint8_t relay;
+ bool_t state;
+ size_t delay;
+};
+
+static TERMUI_CON_COMMAND_PROTOTYPE(shell_otoroshi_relay)
+{
+ struct termui_optctx_otoroshi_opts * c = ctx;
+
+ struct device_timer_s timer;
+ dev_timer_delay_t msec;
+
+ struct device_gpio_s gpio;
+
+ error_t err = 0;
+
+ err = device_get_accessor_by_path(&gpio.base, NULL, "/gpio", DRIVER_CLASS_GPIO);
+ if (err)
+ return err;
+
+ err = device_get_accessor_by_path(&timer.base, NULL, "/timer*", DRIVER_CLASS_TIMER);
+ if (err)
+ goto gpio_cleanup;
+
+ err = dev_timer_init_sec(&timer, &msec, 0, 1, 1000);
+ if (err)
+ goto timer_cleanup;
+
+ DEVICE_OP(&gpio, set_output, STM32_PE8, STM32_PE15, dev_gpio_mask0, dev_gpio_mask0);
+ DEVICE_OP(&gpio, set_mode, STM32_PE8, STM32_PE15, dev_gpio_mask1, DEV_PIN_PUSHPULL);
+
+ if (used & OTOROSHI_OPT_PULSE)
+ {
+ uint8_t id = STM32_PE8 + c->relay * 2;
+
+ static uint8_t door_open_mask[1] = { 0x2 };
+ static uint8_t door_close_mask[1] = { 0x1 };
+
+ DEVICE_OP(&gpio, set_output, id, id + 1, door_open_mask, door_open_mask);
+ dev_timer_wait_delay(&timer, msec * 100, 0);
+ DEVICE_OP(&gpio, set_output, id, id, dev_gpio_mask0, dev_gpio_mask0);
+
+ dev_timer_wait_delay(&timer, msec * c->delay, 0);
+
+ DEVICE_OP(&gpio, set_output, id, id + 1, door_close_mask, door_close_mask);
+ dev_timer_wait_delay(&timer, msec * 100, 0);
+ DEVICE_OP(&gpio, set_output, id, id, dev_gpio_mask0, dev_gpio_mask0);
+ }
+ else
+ {
+ bool_t state = 0;
+
+ if (used & OTOROSHI_OPT_STATE)
+ state = c->state;
+ else if (used & OTOROSHI_OPT_CLOSED)
+ state = 0;
+ else if (used & OTOROSHI_OPT_OPEN)
+ state = 1;
+
+ uint8_t id = STM32_PE8 + c->relay * 2 + state;
+ DEVICE_OP(&gpio, set_output, id, id, dev_gpio_mask1, dev_gpio_mask1);
+ dev_timer_wait_delay(&timer, msec * 100, 0);
+ DEVICE_OP(&gpio, set_output, id, id, dev_gpio_mask0, dev_gpio_mask0);
+ }
+
+timer_cleanup:
+ device_put_accessor(&timer.base);
+gpio_cleanup:
+ device_put_accessor(&gpio.base);
+ return err;
+}
+
+static TERMUI_CON_OPT_DECL(otoroshi_opts) =
+{
+ TERMUI_CON_OPT_INTEGER_RANGE_ENTRY("-r", "--relay", OTOROSHI_OPT_RELAY,
+ struct termui_optctx_otoroshi_opts, relay, 1, 0, 3,
+ TERMUI_CON_OPT_CONSTRAINTS(OTOROSHI_OPT_RELAY, 0))
+
+ TERMUI_CON_OPT_INTEGER_RANGE_ENTRY("-s", "--state", OTOROSHI_OPT_STATE,
+ struct termui_optctx_otoroshi_opts, state, 1, 0, 1,
+ TERMUI_CON_OPT_CONSTRAINTS(OTOROSHI_OPT_STATE, OTOROSHI_OPT_RELAY))
+
+ TERMUI_CON_OPT_ENTRY("-c", "--closed", OTOROSHI_OPT_CLOSED,
+ TERMUI_CON_OPT_CONSTRAINTS(OTOROSHI_OPT_CLOSED | OTOROSHI_OPT_OPEN | OTOROSHI_OPT_STATE,
+ OTOROSHI_OPT_RELAY))
+
+ TERMUI_CON_OPT_ENTRY("-o", "--open", OTOROSHI_OPT_OPEN,
+ TERMUI_CON_OPT_CONSTRAINTS(OTOROSHI_OPT_CLOSED | OTOROSHI_OPT_OPEN | OTOROSHI_OPT_STATE,
+ OTOROSHI_OPT_RELAY))
+
+ TERMUI_CON_OPT_ENTRY("-p", "--pulse", OTOROSHI_OPT_PULSE,
+ TERMUI_CON_OPT_CONSTRAINTS(OTOROSHI_OPT_CLOSED | OTOROSHI_OPT_OPEN | OTOROSHI_OPT_STATE | OTOROSHI_OPT_PULSE,
+ OTOROSHI_OPT_RELAY))
+
+ TERMUI_CON_OPT_INTEGER_ENTRY("-d", "--delay", OTOROSHI_OPT_DELAY,
+ struct termui_optctx_otoroshi_opts, delay, 1,
+ TERMUI_CON_OPT_CONSTRAINTS(OTOROSHI_OPT_CLOSED | OTOROSHI_OPT_OPEN | OTOROSHI_OPT_STATE | OTOROSHI_OPT_DELAY,
+ OTOROSHI_OPT_RELAY | OTOROSHI_OPT_PULSE))
+
+ TERMUI_CON_LIST_END
+};
+
+static TERMUI_CON_GROUP_DECL(shell_otoroshi_subgroup) =
+{
+ TERMUI_CON_ENTRY(shell_otoroshi_relay, "relay",
+ TERMUI_CON_OPTS_CTX(otoroshi_opts, OTOROSHI_OPT_RELAY,
+ OTOROSHI_OPT_STATE | OTOROSHI_OPT_CLOSED | OTOROSHI_OPT_OPEN
+ | OTOROSHI_OPT_PULSE | OTOROSHI_OPT_DELAY, NULL))
+
+ TERMUI_CON_LIST_END
+};
+
+MUTEK_SHELL_ROOT_GROUP(shell_otoroshi_subgroup, "otoroshi");
diff --git a/software/tests/brain_relay/Makefile b/software/tests/brain_relay/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..5e5dfd33e332a8d267c1d5a3de8d39b77a17aa09
--- /dev/null
+++ b/software/tests/brain_relay/Makefile
@@ -0,0 +1,2 @@
+
+objs += main.o
diff --git a/software/tests/brain_relay/config b/software/tests/brain_relay/config
new file mode 100644
index 0000000000000000000000000000000000000000..e201545cfa567318c47c10461260f60850086382
--- /dev/null
+++ b/software/tests/brain_relay/config
@@ -0,0 +1,9 @@
+ %set OUTPUT_NAME bmaaa-brain
+ %append MODULES $(OUTPUT_NAME):$(CONFIGPATH)
+
+ CONFIG_LICENSE_APP_BSD
+
+ %include bmaaa.build
+
+ CONFIG_PTHREAD
+ CONFIG_PTHREAD_MAIN
diff --git a/software/tests/brain_relay/main.c b/software/tests/brain_relay/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..789286b7427aba39f553129fff4d342036367b33
--- /dev/null
+++ b/software/tests/brain_relay/main.c
@@ -0,0 +1,30 @@
+#include
+#include
+
+#include
+
+#define FIRST STM32_PE8
+#define LAST STM32_PE15
+
+static struct device_gpio_s gpio;
+static struct device_timer_s timer;
+
+static dev_timer_delay_t msec;
+
+static uint8_t brain_gpio_init_mask[] = { 0x55, 0x55 };
+
+void main()
+{
+ printk("bmaaa: brain firmware started!\n");
+
+ ensure(!device_get_accessor_by_path(&gpio.base, NULL, "/gpio", DRIVER_CLASS_GPIO));
+ ensure(!device_get_accessor_by_path(&timer.base, NULL, "/timer4", DRIVER_CLASS_TIMER));
+
+ ensure(!dev_timer_init_sec(&timer, &msec, 0, 1, 1000));
+
+ DEVICE_OP(&gpio, set_output, FIRST, LAST, brain_gpio_init_mask, dev_gpio_mask0);
+ DEVICE_OP(&gpio, set_mode, FIRST, LAST, dev_gpio_mask1, DEV_PIN_PUSHPULL);
+ dev_timer_wait_delay(&timer, msec * 100, 0);
+ DEVICE_OP(&gpio, set_output, FIRST, LAST, dev_gpio_mask0, dev_gpio_mask0);
+}
+