Browse Source
This adds support for the LEGO Hub No. 7, aka LEGO Technic Small hub, aka LEGO SPIKE Essential hub. This board is largely similar to Hub No. 6: - Same MCU (STM32F413 - different packaging with fewer pins). - Same Bluetooth chip (TI CC2564). - Same IMU chip. - Similar external flash chip - 4MiB instead of 32MiB. - 2 I/O ports instead of 6. - No display - only status and battery LEDs. - Different LED driver chip. - Only 1 button which is also the power button. - No speaker. Signed-off-by: David Lechner <david@pybricks.com>pull/8761/merge
David Lechner
2 years ago
committed by
Damien George
13 changed files with 910 additions and 0 deletions
@ -0,0 +1,135 @@ |
|||
LEGO Hub No.7 |
|||
============= |
|||
|
|||
This board definition is for the LEGO Hub No. 7, a LEGO control unit with 1 button, |
|||
2 RGB LEDs, 2 Powered Up ports, 6-DOF sensor, Bluetooth, USB, 4MiB external SPI |
|||
flash storage, and a rechargeable battery. |
|||
|
|||
Features that are currently supported: |
|||
- standard MicroPython |
|||
- machine and bluetooth modules |
|||
- filesystem |
|||
- USB VCP, MSC and HID |
|||
|
|||
The Hub has a bootloader preinstalled at 0x08000000 (which is 32kiB in size) which |
|||
cannot be erased. This bootloader is entered by holding down the button for 5 seconds, |
|||
at which point the USB DFU device appears. If the battery is installed then the |
|||
RGB LED will flash purple. If the battery is not installed, the LED will flash orange |
|||
briefly and then the hub will turn off (so having the battery installed is required). |
|||
When this bootloader is active, the flash from 0x08008000 and up can be erased |
|||
and programmed via USB DFU. |
|||
|
|||
The built-in bootloader has some drawbacks: it cannot be entered programmatically, |
|||
and it does not keep the Hub powered up when running from battery (which requires |
|||
keeping BAT_PWR_EN high). As such, this board is configured to work with mboot as |
|||
a secondary bootloader: mboot is placed at 0x08008000 and the main application |
|||
firmware at 0x08010000. When mboot is installed it can be entered programatically |
|||
via machine.bootloader(). |
|||
|
|||
Backing up original Hub firmware |
|||
-------------------------------- |
|||
|
|||
Before installing MicroPython it is advised to backup the original LEGO firmware that |
|||
the Hub comes installed with. To do this, enter the built-in bootloader by holding |
|||
down the power button for 5 seconds while powering up the Hub via USB (you may |
|||
need to take out the battery and disconnect USB to power off the Hub first). Then |
|||
run the following command from the root of this repository: |
|||
|
|||
$ cd ports/stm32 |
|||
$ make BOARD=LEGO_HUB_NO7 backup-hub-firmware |
|||
|
|||
This will create a file called `lego_hub_firmware.dfu`. Put this file in a safe |
|||
location. To restore it, enter the built-in bootloader again and run: |
|||
|
|||
$ make BOARD=LEGO_HUB_NO7 restore-hub-firmware |
|||
|
|||
This will restore the original firmware but not the filesystem. To recreate the |
|||
original filesystem the Hub must be updated using the appropriate LEGO PC |
|||
application. |
|||
|
|||
Installing MicroPython |
|||
---------------------- |
|||
|
|||
You first need to build and install mboot, which only needs to be done once. From |
|||
the root of this repository run: |
|||
|
|||
$ cd ports/stm32/mboot |
|||
$ make BOARD=LEGO_HUB_NO7 |
|||
|
|||
Now enter the built-in bootloader by holding down the power button for 5 |
|||
seconds while powering up the Hub via USB (you may need to take out the battery |
|||
and disconnect USB to power off the Hub first). Then run: |
|||
|
|||
$ make BOARD=LEGO_HUB_NO7 deploy |
|||
|
|||
mboot should now be installed. To enter mboot, remove USB and the battery. |
|||
Connect the USB cable (the other end of the USB cable must be connected to |
|||
something that provides power). The status light should start cycling through |
|||
different colors. Replace the battery (the button will not work without the |
|||
battery present). Press the button to activate the desired boot mode: |
|||
|
|||
- Status light is red - run application (normal boot). |
|||
- Status light is green - run application in factory file system mode. |
|||
- Status light is blue - run application in safe mode. |
|||
- Status light is white - start DFU on the USB port. |
|||
|
|||
|
|||
Now build MicroPython (start at the root of this repository): |
|||
|
|||
$ cd mpy-cross |
|||
$ make |
|||
$ cd ../ports/stm32 |
|||
$ make submodules |
|||
$ make BOARD=LEGO_HUB_NO7 |
|||
|
|||
And deploy to the Hub (making sure mboot DFU is active, the center button is |
|||
blinking red): |
|||
|
|||
$ make BOARD=LEGO_HUB_NO7 deploy |
|||
|
|||
If successful, the Hub should now appear as a USB serial and mass storage device. |
|||
|
|||
Using MicroPython on the Hub |
|||
---------------------------- |
|||
|
|||
Access the MicroPython REPL using mpremote (pip install mpremote), or with any |
|||
serial terminal program. |
|||
|
|||
To scan for BLE devices: |
|||
|
|||
>>> import bluetooth |
|||
>>> ble = bluetooth.BLE() |
|||
>>> ble.irq(lambda *x: print(*x)) |
|||
>>> ble.active(1) |
|||
>>> ble.gap_scan(2000, 625, 625) |
|||
|
|||
Use help("modules") to see available built-in modules. |
|||
|
|||
Updating MicroPython from the Hub's filesystem |
|||
---------------------------------------------- |
|||
|
|||
You can update the MicroPython application firmware using the instructions above |
|||
for installing the firmware for the first time. The Hub also supports updating |
|||
the application firmware from within MicroPython itself, using the on-board |
|||
filesystem. |
|||
|
|||
To use this feature, build the firmware (see above for details) then gzip it and |
|||
copy the resulting file to the Hub (eg using mpremote): |
|||
|
|||
$ make BOARD=LEGO_HUB_NO7 |
|||
$ gzip build-LEGO_HUB_NO7/firmware.dfu |
|||
$ mpremote cp build-LEGO_HUB_NO7/firmware.dfu.gz : |
|||
|
|||
Then get a REPL on the Hub and execute: |
|||
|
|||
>>> import appupdate |
|||
>>> appupdate.update_app("firmware.dfu.gz") |
|||
|
|||
You can alternatively run this REPL command using mpremote: |
|||
|
|||
$ mpremote exec --no-follow "import appupdate; appupdate.update_app('firmware.dfu.gz')" |
|||
|
|||
At that point the Hub should restart and the LED on the central button will flash |
|||
different colours. Once the update is complete the LED will stop flashing and the |
|||
Hub will appear again as a USB device. The application firmware is now updated |
|||
and you can remove the firmware.dfu.gz file if desired. |
@ -0,0 +1,3 @@ |
|||
// LEGO_HUB_NO7 is identical to LEGO_HUB_NO6 in this regard.
|
|||
|
|||
#include "../LEGO_HUB_NO6/bdev.c" |
@ -0,0 +1,3 @@ |
|||
// LEGO_HUB_NO7 is identical to LEGO_HUB_NO6 in this regard.
|
|||
|
|||
#include "../LEGO_HUB_NO6/bluetooth_init_cc2564C_1.5.c" |
@ -0,0 +1,13 @@ |
|||
{ |
|||
"deploy": [ |
|||
"../deploy.md" |
|||
], |
|||
"docs": "", |
|||
"features": [], |
|||
"images": [], |
|||
"mcu": "stm32f4", |
|||
"product": "Hub No.7", |
|||
"thumbnail": "", |
|||
"url": "", |
|||
"vendor": "LEGO" |
|||
} |
@ -0,0 +1,212 @@ |
|||
/*
|
|||
* This file is part of the MicroPython project, http://micropython.org/
|
|||
* |
|||
* The MIT License (MIT) |
|||
* |
|||
* Copyright (c) 2021-2022 Damien P. George |
|||
* Copyright (c) 2022 David Lechner <david@pybricks.com> |
|||
* |
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to deal |
|||
* in the Software without restriction, including without limitation the rights |
|||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
* copies of the Software, and to permit persons to whom the Software is |
|||
* furnished to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in |
|||
* all copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
* THE SOFTWARE. |
|||
*/ |
|||
|
|||
#include "py/mphal.h" |
|||
#include "irq.h" |
|||
|
|||
void board_init(void) { |
|||
if (query_irq() == IRQ_STATE_DISABLED) { |
|||
enable_irq(IRQ_STATE_ENABLED); |
|||
} |
|||
|
|||
// Enable 3V3 for all ports
|
|||
mp_hal_pin_output(pyb_pin_PORT_3V3_EN); |
|||
mp_hal_pin_high(pyb_pin_PORT_3V3_EN); |
|||
|
|||
// Port A
|
|||
// Enable RX/TX buffer
|
|||
mp_hal_pin_output(pyb_pin_PORTA_EN); |
|||
mp_hal_pin_low(pyb_pin_PORTA_EN); |
|||
|
|||
// Port B
|
|||
// Enable RX/TX buffer
|
|||
mp_hal_pin_output(pyb_pin_PORTB_EN); |
|||
mp_hal_pin_low(pyb_pin_PORTB_EN); |
|||
} |
|||
|
|||
#if BUILDING_MBOOT |
|||
|
|||
#include "drivers/memory/spiflash.h" |
|||
#include "mboot/mboot.h" |
|||
#include "boardctrl.h" |
|||
#include "adc.h" |
|||
#include "hub_display.h" |
|||
|
|||
#define RESET_MODE_NUM_STATES (4) |
|||
#define RESET_MODE_TIMEOUT_CYCLES (8) |
|||
|
|||
// Location and value for the SPI flash update key. If this key exists at the defined
|
|||
// location then mboot will attempt to do a filesystem-load update of the main firmware.
|
|||
// This makes the update robust to power failures: if the update does not complete then
|
|||
// it will be restarted the next time it powers up. Only when it fully completes will
|
|||
// this key be erased, and then the application can run.
|
|||
#define SPIFLASH_UPDATE_KEY_ADDR (1020 * 1024) |
|||
#define SPIFLASH_UPDATE_KEY_VALUE (0x12345678) |
|||
|
|||
static void board_led_pattern(int reset_mode, uint16_t brightness) { |
|||
switch (reset_mode) { |
|||
case BOARDCTRL_RESET_MODE_NORMAL: |
|||
// set status light to red
|
|||
hub_display_set(3, brightness); |
|||
hub_display_set(4, 0); |
|||
hub_display_set(5, 0); |
|||
break; |
|||
case BOARDCTRL_RESET_MODE_SAFE_MODE: |
|||
// set status light to green
|
|||
hub_display_set(3, 0); |
|||
hub_display_set(4, brightness); |
|||
hub_display_set(5, 0); |
|||
break; |
|||
case BOARDCTRL_RESET_MODE_FACTORY_FILESYSTEM: |
|||
// set status light to blue
|
|||
hub_display_set(3, 0); |
|||
hub_display_set(4, 0); |
|||
hub_display_set(5, brightness); |
|||
break; |
|||
case BOARDCTRL_RESET_MODE_BOOTLOADER: |
|||
// set status light to white
|
|||
hub_display_set(3, brightness); |
|||
hub_display_set(4, brightness); |
|||
hub_display_set(5, brightness); |
|||
break; |
|||
} |
|||
|
|||
hub_display_update(); |
|||
} |
|||
|
|||
static void board_battery_init(void) { |
|||
mp_hal_pin_config(pyb_pin_BAT_VMON_ADC, MP_HAL_PIN_MODE_ADC, MP_HAL_PIN_PULL_NONE, 0); |
|||
adc_config(ADC1, 12); |
|||
} |
|||
|
|||
// returns true if the battery is pressed, otherwise false
|
|||
static int board_battery_state(void) { |
|||
uint16_t value = adc_config_and_read_u16(ADC1, 6, ADC_SAMPLETIME_15CYCLES); |
|||
// If battery voltage is above USB voltage, then we consider the battery
|
|||
// to be present.
|
|||
return value > 41100; // 41100 is approx 5.5V
|
|||
} |
|||
|
|||
static void board_button_init(void) { |
|||
mp_hal_pin_input(pyb_pin_BUTTON); |
|||
} |
|||
|
|||
// returns true if the button is pressed, otherwise false
|
|||
static int board_button_state(void) { |
|||
// button is active low
|
|||
return !mp_hal_pin_read(pyb_pin_BUTTON); |
|||
} |
|||
|
|||
void board_mboot_cleanup(int reset_mode) { |
|||
board_led_pattern(0, 0); |
|||
hub_display_off(); |
|||
} |
|||
|
|||
void board_mboot_led_init(void) { |
|||
hub_display_on(); |
|||
} |
|||
|
|||
void board_mboot_led_state(int led, int state) { |
|||
if (state) { |
|||
hub_display_set(3 + led, 0x7fff); |
|||
} else { |
|||
hub_display_set(3 + led, 0); |
|||
} |
|||
|
|||
hub_display_update(); |
|||
} |
|||
|
|||
int board_mboot_get_reset_mode(uint32_t *initial_r0) { |
|||
board_battery_init(); |
|||
board_button_init(); |
|||
int reset_mode = BOARDCTRL_RESET_MODE_NORMAL; |
|||
|
|||
if (board_battery_state()) { |
|||
// Battery is present, check flash for update key and start an update if the key exists.
|
|||
// Otherwise, boot normally.
|
|||
|
|||
// Initialise the external SPI flash.
|
|||
MBOOT_SPIFLASH_SPIFLASH->config = MBOOT_SPIFLASH_CONFIG; |
|||
mp_spiflash_init(MBOOT_SPIFLASH_SPIFLASH); |
|||
|
|||
// Read in the key.
|
|||
uint32_t buf; |
|||
mp_spiflash_read(MBOOT_SPIFLASH_SPIFLASH, SPIFLASH_UPDATE_KEY_ADDR, 4, (uint8_t *)&buf); |
|||
|
|||
if (buf == SPIFLASH_UPDATE_KEY_VALUE) { |
|||
// The key has the correct value, so read in the FS-load elements and enter the bootloader.
|
|||
mp_spiflash_read(MBOOT_SPIFLASH_SPIFLASH, SPIFLASH_UPDATE_KEY_ADDR + 4, ELEM_DATA_SIZE, ELEM_DATA_START); |
|||
*initial_r0 = MBOOT_INITIAL_R0_KEY_FSLOAD; |
|||
reset_mode = BOARDCTRL_RESET_MODE_BOOTLOADER; |
|||
} |
|||
} else { |
|||
// Battery is not present. Cycle through reset modes until button is pressed.
|
|||
systick_init(); |
|||
hub_display_on(); |
|||
reset_mode = 0; |
|||
for (int i = 0; i < (RESET_MODE_NUM_STATES * RESET_MODE_TIMEOUT_CYCLES + 1) * 64; i++) { |
|||
if (i % 64 == 0) { |
|||
if (++reset_mode > RESET_MODE_NUM_STATES) { |
|||
reset_mode = BOARDCTRL_RESET_MODE_NORMAL; |
|||
} |
|||
board_led_pattern(reset_mode, 0x7fff); |
|||
} |
|||
|
|||
if (board_button_state()) { |
|||
break; |
|||
} |
|||
|
|||
mp_hal_delay_ms(19); |
|||
} |
|||
|
|||
// Flash the selected reset mode.
|
|||
for (int i = 0; i < 6; i++) { |
|||
board_led_pattern(reset_mode, 0x0fff); |
|||
mp_hal_delay_ms(50); |
|||
board_led_pattern(reset_mode, 0x7fff); |
|||
mp_hal_delay_ms(50); |
|||
} |
|||
|
|||
mp_hal_delay_ms(300); |
|||
} |
|||
|
|||
board_led_pattern(0, 0); |
|||
return reset_mode; |
|||
} |
|||
|
|||
void board_mboot_state_change(int state, uint32_t arg) { |
|||
if (state == MBOOT_STATE_FSLOAD_END) { |
|||
// The FS-load update completed (either with success or failure), so erase the
|
|||
// update key and write the result of the FS-load operation into flash.
|
|||
mp_spiflash_erase_block(MBOOT_SPIFLASH_SPIFLASH, SPIFLASH_UPDATE_KEY_ADDR); |
|||
mp_spiflash_write(MBOOT_SPIFLASH_SPIFLASH, SPIFLASH_UPDATE_KEY_ADDR + 4, 4, (const uint8_t *)&arg); |
|||
} |
|||
|
|||
mboot_state_change_default(state, arg); |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,6 @@ |
|||
// LEGO_HUB_NO7 is nearly identical to LEGO_HUB_NO6 in this regard.
|
|||
|
|||
#define CC2564_TIMER_BT_SLOWCLOCK_TIM 2 |
|||
#define CC2564_TIMER_BT_SLOWCLOCK_TIM_CH 2 |
|||
|
|||
#include "../LEGO_HUB_NO6/cc2564.c" |
@ -0,0 +1,190 @@ |
|||
/*
|
|||
* This file is part of the MicroPython project, http://micropython.org/
|
|||
* |
|||
* The MIT License (MIT) |
|||
* |
|||
* Copyright (c) 2021 Damien P. George |
|||
* Copyright (c) 2022 David Lechner <david@pybricks.com> |
|||
* |
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to deal |
|||
* in the Software without restriction, including without limitation the rights |
|||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
* copies of the Software, and to permit persons to whom the Software is |
|||
* furnished to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in |
|||
* all copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
* THE SOFTWARE. |
|||
*/ |
|||
|
|||
#include "py/mphal.h" |
|||
#include "hub_display.h" |
|||
|
|||
#include STM32_HAL_H |
|||
|
|||
#define I2C_ADDR 0x28 |
|||
|
|||
// Registers
|
|||
#define DEVICE_CONFIG0 0x00 |
|||
#define DEVICE_CONFIG1 0x01 |
|||
#define LED_CONFIG0 0x02 |
|||
#define BANK_BRIGHTNESS 0x03 |
|||
#define BANK_A_COLOR 0x04 |
|||
#define BANK_B_COLOR 0x05 |
|||
#define BANK_C_COLOR 0x06 |
|||
#define LED0_BRIGHTNESS 0x07 |
|||
#define LED1_BRIGHTNESS 0x08 |
|||
#define LED2_BRIGHTNESS 0x09 |
|||
#define LED3_BRIGHTNESS 0x0A |
|||
#define OUT0_COLOR 0x0B |
|||
#define OUT1_COLOR 0x0C |
|||
#define OUT2_COLOR 0x0D |
|||
#define OUT3_COLOR 0x0E |
|||
#define OUT4_COLOR 0x0F |
|||
#define OUT5_COLOR 0x10 |
|||
#define OUT6_COLOR 0x11 |
|||
#define OUT7_COLOR 0x12 |
|||
#define OUT8_COLOR 0x13 |
|||
#define OUT9_COLOR 0x14 |
|||
#define OUT10_COLOR 0x15 |
|||
#define OUT11_COLOR 0x16 |
|||
#define RESET 0x17 |
|||
|
|||
// Flags
|
|||
#define DEVICE_CONFIG0_CHIP_EN (1 << 6) |
|||
#define DEVICE_CONFIG1_LOG_SCALE_EN (1 << 5) |
|||
#define DEVICE_CONFIG1_POWER_SAVE_EN (1 << 4) |
|||
#define DEVICE_CONFIG1_AUTO_INCR_EN (1 << 3) |
|||
#define DEVICE_CONFIG1_PWM_DITHERING_EN (1 << 2) |
|||
#define DEVICE_CONFIG1_MAX_CURRENT_OPTION (1 << 1) |
|||
#define DEVICE_CONFIG1_LED_GLOBAL_OFF (1 << 0) |
|||
#define LED_CONFIG0_LED3_BANK_EN (1 << 3) |
|||
#define LED_CONFIG0_LED2_BANK_EN (1 << 2) |
|||
#define LED_CONFIG0_LED1_BANK_EN (1 << 1) |
|||
#define LED_CONFIG0_LED0_BANK_EN (1 << 0) |
|||
|
|||
#define LP50XX_NUM_CH 6 |
|||
|
|||
// channel mapping:
|
|||
// CH 0 = battery LED - red
|
|||
// CH 1 = battery LED - green
|
|||
// CH 2 = battery LED - blue
|
|||
// CH 3 = status LED - red
|
|||
// CH 4 = status LED - green
|
|||
// CH 5 = status LED - blue
|
|||
|
|||
#define FMPI2C_CONVERT_TIMINGS(PRESC, SCLDEL, SDADEL, SCLH, SCLL) \ |
|||
(((PRESC) << FMPI2C_TIMINGR_PRESC_Pos) | \ |
|||
((SCLDEL) << FMPI2C_TIMINGR_SCLDEL_Pos) | \ |
|||
((SDADEL) << FMPI2C_TIMINGR_SDADEL_Pos) | \ |
|||
((SCLH) << FMPI2C_TIMINGR_SCLH_Pos) | \ |
|||
((SCLL) << FMPI2C_TIMINGR_SCLL_Pos)) |
|||
|
|||
static FMPI2C_HandleTypeDef hub_display_i2c; |
|||
static bool hub_display_init; |
|||
|
|||
static struct { |
|||
uint8_t reg; |
|||
uint8_t values[LP50XX_NUM_CH]; |
|||
} __attribute__((packed)) hub_display_data = { |
|||
.reg = OUT0_COLOR, |
|||
}; |
|||
|
|||
void HAL_FMPI2C_MspInit(FMPI2C_HandleTypeDef *hfmpi2c) { |
|||
__HAL_RCC_FMPI2C1_CLK_ENABLE(); |
|||
mp_hal_pin_config(pyb_pin_LED_SCL, MP_HAL_PIN_MODE_ALT_OPEN_DRAIN, MP_HAL_PIN_PULL_NONE, 4); |
|||
mp_hal_pin_config(pyb_pin_LED_SDA, MP_HAL_PIN_MODE_ALT_OPEN_DRAIN, MP_HAL_PIN_PULL_NONE, 4); |
|||
} |
|||
|
|||
void HAL_FMPI2C_MspDeInit(FMPI2C_HandleTypeDef *hfmpi2c) { |
|||
__HAL_RCC_FMPI2C1_CLK_DISABLE(); |
|||
__HAL_RCC_FMPI2C1_FORCE_RESET(); |
|||
__HAL_RCC_FMPI2C1_RELEASE_RESET(); |
|||
mp_hal_pin_config(pyb_pin_LED_SCL, MP_HAL_PIN_MODE_ANALOG, MP_HAL_PIN_PULL_NONE, 0); |
|||
mp_hal_pin_config(pyb_pin_LED_SDA, MP_HAL_PIN_MODE_ANALOG, MP_HAL_PIN_PULL_NONE, 0); |
|||
} |
|||
|
|||
static void hub_display_i2c_init(void) { |
|||
hub_display_i2c.Instance = FMPI2C1; |
|||
hub_display_i2c.Init.Timing = FMPI2C_CONVERT_TIMINGS(0, 4, 0, 19, 28); |
|||
hub_display_i2c.Init.OwnAddress1 = 0; |
|||
hub_display_i2c.Init.AddressingMode = FMPI2C_ADDRESSINGMODE_7BIT; |
|||
hub_display_i2c.Init.DualAddressMode = FMPI2C_DUALADDRESS_DISABLE; |
|||
hub_display_i2c.Init.OwnAddress2 = 0; |
|||
hub_display_i2c.Init.OwnAddress2Masks = FMPI2C_OA2_NOMASK; |
|||
hub_display_i2c.Init.GeneralCallMode = FMPI2C_GENERALCALL_DISABLE; |
|||
hub_display_i2c.Init.NoStretchMode = FMPI2C_NOSTRETCH_DISABLE; |
|||
HAL_FMPI2C_Init(&hub_display_i2c); |
|||
} |
|||
|
|||
void hub_display_set(uint8_t led, uint16_t value) { |
|||
if (led >= LP50XX_NUM_CH) { |
|||
return; |
|||
} |
|||
|
|||
hub_display_data.values[led] = value >> 8; |
|||
} |
|||
|
|||
void hub_display_update(void) { |
|||
if (!hub_display_init) { |
|||
return; |
|||
} |
|||
|
|||
HAL_FMPI2C_Master_Transmit(&hub_display_i2c, I2C_ADDR, (uint8_t *)&hub_display_data, |
|||
sizeof(hub_display_data), HAL_MAX_DELAY); |
|||
} |
|||
|
|||
void hub_display_on(void) { |
|||
if (hub_display_init) { |
|||
return; |
|||
} |
|||
|
|||
hub_display_i2c_init(); |
|||
mp_hal_pin_output(pyb_pin_LED_EN); |
|||
mp_hal_pin_high(pyb_pin_LED_EN); |
|||
|
|||
static const struct { |
|||
uint8_t reg; |
|||
uint8_t values[11]; |
|||
} __attribute__((packed)) init_data = { |
|||
.reg = DEVICE_CONFIG0, |
|||
.values = { |
|||
[DEVICE_CONFIG0] = DEVICE_CONFIG0_CHIP_EN, |
|||
[DEVICE_CONFIG1] = DEVICE_CONFIG1_POWER_SAVE_EN | DEVICE_CONFIG1_PWM_DITHERING_EN | DEVICE_CONFIG1_AUTO_INCR_EN, |
|||
[LED_CONFIG0] = 0, |
|||
[BANK_BRIGHTNESS] = 0, |
|||
[BANK_A_COLOR] = 0, |
|||
[BANK_B_COLOR] = 0, |
|||
[BANK_C_COLOR] = 0, |
|||
[LED0_BRIGHTNESS] = 51, // battery LED
|
|||
[LED1_BRIGHTNESS] = 38, // status LED
|
|||
[LED2_BRIGHTNESS] = 0, |
|||
[LED3_BRIGHTNESS] = 0, |
|||
} |
|||
}; |
|||
|
|||
HAL_FMPI2C_Master_Transmit(&hub_display_i2c, I2C_ADDR, (uint8_t *)&init_data, |
|||
sizeof(init_data), HAL_MAX_DELAY); |
|||
|
|||
hub_display_init = true; |
|||
} |
|||
|
|||
void hub_display_off(void) { |
|||
if (!hub_display_init) { |
|||
return; |
|||
} |
|||
|
|||
HAL_FMPI2C_DeInit(&hub_display_i2c); |
|||
|
|||
mp_hal_pin_config(pyb_pin_LED_EN, MP_HAL_PIN_MODE_ANALOG, MP_HAL_PIN_PULL_NONE, 0); |
|||
|
|||
hub_display_init = false; |
|||
} |
@ -0,0 +1,4 @@ |
|||
void hub_display_on(void); |
|||
void hub_display_off(void); |
|||
void hub_display_update(void); |
|||
void hub_display_set(uint8_t led, uint16_t value); |
@ -0,0 +1,5 @@ |
|||
include("$(PORT_DIR)/boards/manifest.py") |
|||
|
|||
# Modules for application firmware update. |
|||
freeze("$(PORT_DIR)/mboot", "fwupdate.py", opt=3) |
|||
freeze("$(PORT_DIR)/boards/LEGO_HUB_NO6", ("spiflash.py", "appupdate.py"), opt=3) |
@ -0,0 +1,152 @@ |
|||
/*
|
|||
* This file is part of the MicroPython project, http://micropython.org/
|
|||
* The MIT License (MIT) |
|||
* Copyright (c) 2021 Damien P. George |
|||
*/ |
|||
|
|||
#include <stdint.h> |
|||
|
|||
#define MICROPY_HW_BOARD_NAME "LEGO Technic Hub No.7" |
|||
#define MICROPY_HW_MCU_NAME "STM32F413" |
|||
|
|||
#define MICROPY_HW_HAS_SWITCH (0) |
|||
#define MICROPY_HW_HAS_FLASH (1) |
|||
#define MICROPY_PY_PYB_LEGACY (0) |
|||
#define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0) |
|||
#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0) |
|||
#define MICROPY_HW_ENABLE_RTC (1) |
|||
#define MICROPY_HW_ENABLE_RNG (1) |
|||
#define MICROPY_HW_ENABLE_DAC (1) |
|||
#define MICROPY_HW_ENABLE_USB (1) |
|||
#define MICROPY_HW_FLASH_FS_LABEL "HUB_NO7" |
|||
|
|||
// HSE is 16MHz, CPU freq set to 100MHz, buses at maximum freq
|
|||
#define MICROPY_HW_CLK_PLLM (16) |
|||
#define MICROPY_HW_CLK_PLLN (200) |
|||
#define MICROPY_HW_CLK_PLLP (RCC_PLLP_DIV2) |
|||
#define MICROPY_HW_CLK_PLLQ (4) |
|||
#define MICROPY_HW_CLK_AHB_DIV (RCC_SYSCLK_DIV1) |
|||
#define MICROPY_HW_CLK_APB1_DIV (RCC_HCLK_DIV2) |
|||
#define MICROPY_HW_CLK_APB2_DIV (RCC_HCLK_DIV1) |
|||
|
|||
// For 2.7 to 3.6 V, 75 to 100 MHz: 3 wait states.
|
|||
#define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_3 |
|||
|
|||
// UART buses
|
|||
// Bluetooth HCI
|
|||
#define MICROPY_HW_UART2_CTS (pyb_pin_BT_CTS) |
|||
#define MICROPY_HW_UART2_RTS (pyb_pin_BT_RTS) |
|||
#define MICROPY_HW_UART2_TX (pyb_pin_BT_TX) |
|||
#define MICROPY_HW_UART2_RX (pyb_pin_BT_RX) |
|||
// Port B
|
|||
#define MICROPY_HW_UART3_TX (pyb_pin_PORTB_TX) |
|||
#define MICROPY_HW_UART3_RX (pyb_pin_PORTB_RX) |
|||
// Port A
|
|||
#define MICROPY_HW_UART5_TX (pyb_pin_PORTA_TX) |
|||
#define MICROPY_HW_UART5_RX (pyb_pin_PORTA_RX) |
|||
|
|||
// SPI buses
|
|||
#define MICROPY_HW_SPI2_NSS (pyb_pin_FLASH_NSS) |
|||
#define MICROPY_HW_SPI2_SCK (pyb_pin_FLASH_SCK) |
|||
#define MICROPY_HW_SPI2_MISO (pyb_pin_FLASH_MISO) |
|||
#define MICROPY_HW_SPI2_MOSI (pyb_pin_FLASH_MOSI) |
|||
|
|||
// USB config
|
|||
#define MICROPY_HW_USB_VBUS_DETECT_PIN (pyb_pin_USB_VBUS) |
|||
#define MICROPY_HW_USB_FS (1) |
|||
#define MICROPY_HW_USB_MSC (1) |
|||
|
|||
// Bluetooth config
|
|||
#define MICROPY_HW_BLE_UART_ID (PYB_UART_2) |
|||
#define MICROPY_HW_BLE_UART_BAUDRATE (115200) |
|||
#define MICROPY_HW_BLE_UART_BAUDRATE_SECONDARY (921600) |
|||
#define MICROPY_HW_BLE_BTSTACK_CHIPSET_INSTANCE btstack_chipset_cc256x_instance() |
|||
|
|||
// SPI flash, for R/W storage
|
|||
// The first 1MiB is skipped because it's used by the built-in bootloader
|
|||
// Note: MICROPY_HW_SPIFLASH_OFFSET_BYTES must be a multiple of MP_SPIFLASH_ERASE_BLOCK_SIZE
|
|||
#define MICROPY_HW_SPIFLASH_OFFSET_BYTES (1024 * 1024) |
|||
#define MICROPY_HW_SPIFLASH_BLOCKMAP(bl) ((bl) + MICROPY_HW_SPIFLASH_OFFSET_BYTES / FLASH_BLOCK_SIZE) |
|||
#define MICROPY_HW_SPIFLASH_BLOCKMAP_EXT(bl) ((bl) + MICROPY_HW_SPIFLASH_OFFSET_BYTES / MP_SPIFLASH_ERASE_BLOCK_SIZE) |
|||
#define MICROPY_HW_SPIFLASH_ENABLE_CACHE (1) |
|||
#define MICROPY_HW_SPIFLASH_SIZE_BITS (32 * 1024 * 1024 - MICROPY_HW_SPIFLASH_OFFSET_BYTES * 8) |
|||
#define MICROPY_HW_SPIFLASH_CS (MICROPY_HW_SPI2_NSS) |
|||
#define MICROPY_HW_SPIFLASH_SCK (MICROPY_HW_SPI2_SCK) |
|||
#define MICROPY_HW_SPIFLASH_MISO (MICROPY_HW_SPI2_MISO) |
|||
#define MICROPY_HW_SPIFLASH_MOSI (MICROPY_HW_SPI2_MOSI) |
|||
|
|||
// SPI flash, block device config
|
|||
#define MICROPY_HW_BDEV_IOCTL(op, arg) ( \ |
|||
(op) == BDEV_IOCTL_NUM_BLOCKS ? (MICROPY_HW_SPIFLASH_SIZE_BITS / 8 / FLASH_BLOCK_SIZE) : \ |
|||
(op) == BDEV_IOCTL_INIT ? spi_bdev_ioctl(&spi_bdev, (op), (uint32_t)&spiflash_config) : \ |
|||
spi_bdev_ioctl(&spi_bdev, (op), (arg)) \ |
|||
) |
|||
|
|||
// Configuration for stardard block protocol (block size FLASH_BLOCK_SIZE).
|
|||
#define MICROPY_HW_BDEV_READBLOCKS(dest, bl, n) \ |
|||
spi_bdev_readblocks(&spi_bdev, (dest), MICROPY_HW_SPIFLASH_BLOCKMAP(bl), (n)) |
|||
#define MICROPY_HW_BDEV_WRITEBLOCKS(src, bl, n) \ |
|||
spi_bdev_writeblocks(&spi_bdev, (src), MICROPY_HW_SPIFLASH_BLOCKMAP(bl), (n)) |
|||
|
|||
// Configuration for extended block protocol (block size MP_SPIFLASH_ERASE_BLOCK_SIZE).
|
|||
#define MICROPY_HW_BDEV_BLOCKSIZE_EXT (MP_SPIFLASH_ERASE_BLOCK_SIZE) |
|||
#define MICROPY_HW_BDEV_READBLOCKS_EXT(dest, bl, off, len) \ |
|||
(spi_bdev_readblocks_raw(&spi_bdev, (dest), MICROPY_HW_SPIFLASH_BLOCKMAP_EXT(bl), (off), (len))) |
|||
#define MICROPY_HW_BDEV_WRITEBLOCKS_EXT(src, bl, off, len) \ |
|||
(spi_bdev_writeblocks_raw(&spi_bdev, (src), MICROPY_HW_SPIFLASH_BLOCKMAP_EXT(bl), (off), (len))) |
|||
#define MICROPY_HW_BDEV_ERASEBLOCKS_EXT(bl, len) \ |
|||
(spi_bdev_eraseblocks_raw(&spi_bdev, MICROPY_HW_SPIFLASH_BLOCKMAP_EXT(bl), (len))) |
|||
|
|||
// Board control config
|
|||
#define MICROPY_BOARD_STARTUP board_init |
|||
|
|||
/******************************************************************************/ |
|||
// Bootloader configuration
|
|||
|
|||
// Configure CPU frequency to 96MHz, to make updates from SPI flash faster
|
|||
#define MBOOT_CLK_PLLM (MICROPY_HW_CLK_VALUE / 1000000) |
|||
#define MBOOT_CLK_PLLN (192) |
|||
#define MBOOT_CLK_PLLP (RCC_PLLP_DIV2) |
|||
#define MBOOT_CLK_PLLQ (4) |
|||
#define MBOOT_CLK_AHB_DIV (RCC_SYSCLK_DIV1) |
|||
#define MBOOT_CLK_APB1_DIV (RCC_HCLK_DIV4) |
|||
#define MBOOT_CLK_APB2_DIV (RCC_HCLK_DIV2) |
|||
#define MBOOT_FLASH_LATENCY FLASH_LATENCY_3 |
|||
|
|||
#define MBOOT_FSLOAD (1) |
|||
#define MBOOT_VFS_FAT (1) |
|||
#define MBOOT_LEAVE_BOOTLOADER_VIA_RESET (0) |
|||
|
|||
#define MBOOT_SPIFLASH_ADDR (0x80000000) |
|||
#define MBOOT_SPIFLASH_BYTE_SIZE (4 * 1024 * 1024) |
|||
#define MBOOT_SPIFLASH_LAYOUT "/0x80000000/1024*4Kg" |
|||
#define MBOOT_SPIFLASH_ERASE_BLOCKS_PER_PAGE (1) |
|||
#define MBOOT_SPIFLASH_SPIFLASH (&board_mboot_spiflash) |
|||
#define MBOOT_SPIFLASH_CONFIG (&board_mboot_spiflash_config) |
|||
|
|||
#define MBOOT_LED1 0 |
|||
#define MBOOT_LED2 1 |
|||
#define MBOOT_LED3 2 |
|||
#define MBOOT_BOARD_LED_INIT board_mboot_led_init |
|||
#define MBOOT_BOARD_LED_STATE board_mboot_led_state |
|||
|
|||
#define MBOOT_BOARD_EARLY_INIT(initial_r0) board_init() |
|||
#define MBOOT_BOARD_CLEANUP board_mboot_cleanup |
|||
#define MBOOT_BOARD_GET_RESET_MODE board_mboot_get_reset_mode |
|||
#define MBOOT_BOARD_STATE_CHANGE board_mboot_state_change |
|||
|
|||
/******************************************************************************/ |
|||
// Function declarations
|
|||
|
|||
extern const struct _mp_spiflash_config_t spiflash_config; |
|||
extern struct _spi_bdev_t spi_bdev; |
|||
extern const struct _mp_spiflash_config_t board_mboot_spiflash_config; |
|||
extern struct _mp_spiflash_t board_mboot_spiflash; |
|||
|
|||
void board_init(void); |
|||
void board_mboot_cleanup(int reset_mode); |
|||
void board_mboot_led_init(void); |
|||
void board_mboot_led_state(int led, int state); |
|||
int board_mboot_get_reset_mode(uint32_t *initial_r0); |
|||
void board_mboot_state_change(int state, uint32_t arg); |
|||
void *btstack_chipset_cc256x_instance(void); |
@ -0,0 +1,50 @@ |
|||
MCU_SERIES = f4 |
|||
CMSIS_MCU = STM32F413xx |
|||
AF_FILE = boards/stm32f413_af.csv |
|||
LD_FILES = boards/LEGO_HUB_NO6/stm32f413xg.ld boards/common_bl.ld |
|||
TEXT0_ADDR = 0x08010000 |
|||
|
|||
BOOTLOADER_DFU_USB_VID ?= 0x0694 |
|||
BOOTLOADER_DFU_USB_PID ?= 0x000C |
|||
|
|||
# MicroPython settings
|
|||
MICROPY_PY_BLUETOOTH ?= 1 |
|||
MICROPY_BLUETOOTH_NIMBLE ?= 0 |
|||
MICROPY_BLUETOOTH_BTSTACK ?= 1 |
|||
MICROPY_VFS_LFS2 ?= 1 |
|||
|
|||
# Board specific frozen modules
|
|||
FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py |
|||
|
|||
SRC_HAL += $(STM32LIB_HAL_BASE)/Src/stm32f4xx_hal_fmpi2c.c |
|||
|
|||
ifneq ($(BUILDING_MBOOT),1) |
|||
LIB_SRC_C += lib/btstack/chipset/cc256x/btstack_chipset_cc256x.c |
|||
endif |
|||
|
|||
# Bootloader settings
|
|||
MBOOT_TEXT0_ADDR = 0x08008000 |
|||
MBOOT_LD_FILES = ../boards/LEGO_HUB_NO6/mboot_memory.ld stm32_sections.ld |
|||
|
|||
# Backup/restore original Hub firmware
|
|||
|
|||
HUB_FIRMWARE = lego_hub_firmware.dfu |
|||
HUB_FIRMWARE_ADDR = $(MBOOT_TEXT0_ADDR) |
|||
HUB_FIRMWARE_SIZE = 0xf8000 |
|||
|
|||
backup-hub-firmware: |
|||
$(Q)$(DFU_UTIL) -a 0 \
|
|||
-d $(BOOTLOADER_DFU_USB_VID):$(BOOTLOADER_DFU_USB_PID) \
|
|||
-U $(HUB_FIRMWARE).bin \
|
|||
-s $(HUB_FIRMWARE_ADDR):$(HUB_FIRMWARE_SIZE) |
|||
$(Q)$(PYTHON) $(DFU) \
|
|||
-b $(HUB_FIRMWARE_ADDR):$(HUB_FIRMWARE).bin \
|
|||
-D $(BOOTLOADER_DFU_USB_VID):$(BOOTLOADER_DFU_USB_PID) \
|
|||
$(HUB_FIRMWARE) |
|||
$(Q)$(RM) $(HUB_FIRMWARE).bin |
|||
$(ECHO) "Backup created in $(HUB_FIRMWARE)" |
|||
|
|||
restore-hub-firmware: |
|||
$(Q)$(DFU_UTIL) -a 0 \
|
|||
-d $(BOOTLOADER_DFU_USB_VID):$(BOOTLOADER_DFU_USB_PID) \
|
|||
-D $(HUB_FIRMWARE) |
|
@ -0,0 +1,23 @@ |
|||
/* This file is part of the MicroPython project, http://micropython.org/
|
|||
* The MIT License (MIT) |
|||
* Copyright (c) 2019 Damien P. George |
|||
*/ |
|||
#ifndef MICROPY_INCLUDED_STM32F4XX_HAL_CONF_H |
|||
#define MICROPY_INCLUDED_STM32F4XX_HAL_CONF_H |
|||
|
|||
#include "boards/stm32f4xx_hal_conf_base.h" |
|||
|
|||
#include "stm32f4xx_hal_fmpi2c.h" |
|||
|
|||
#define HAL_FMPI2C_MODULE_ENABLED |
|||
|
|||
// Oscillator values in Hz
|
|||
#define HSE_VALUE (16000000) |
|||
#define LSE_VALUE (32768) |
|||
#define EXTERNAL_CLOCK_VALUE (12288000) |
|||
|
|||
// Oscillator timeouts in ms
|
|||
#define HSE_STARTUP_TIMEOUT (100) |
|||
#define LSE_STARTUP_TIMEOUT (5000) |
|||
|
|||
#endif // MICROPY_INCLUDED_STM32F4XX_HAL_CONF_H
|
Loading…
Reference in new issue