From 6cce2d6d145896e02e78fffa26edb60f7d0d6a69 Mon Sep 17 00:00:00 2001 From: Hierophect Date: Fri, 15 Nov 2019 12:28:16 -0500 Subject: [PATCH 1/4] Implement Neopixel write, add defaults to f405 --- .../feather_stm32f405_express/mpconfigboard.h | 2 + .../boards/feather_stm32f405_express/pins.c | 1 + .../stm32f4/common-hal/microcontroller/Pin.h | 4 + .../common-hal/neopixel_write/__init__.c | 89 +++++++++++++++++++ ports/stm32f4/mpconfigport.mk | 1 + 5 files changed, 97 insertions(+) create mode 100644 ports/stm32f4/common-hal/neopixel_write/__init__.c diff --git a/ports/stm32f4/boards/feather_stm32f405_express/mpconfigboard.h b/ports/stm32f4/boards/feather_stm32f405_express/mpconfigboard.h index 67ddc885ae..e2295dc5e7 100644 --- a/ports/stm32f4/boards/feather_stm32f405_express/mpconfigboard.h +++ b/ports/stm32f4/boards/feather_stm32f405_express/mpconfigboard.h @@ -35,6 +35,8 @@ #define AUTORESET_DELAY_MS 500 #define BOARD_FLASH_SIZE (FLASH_SIZE - 0x4000) +#define MICROPY_HW_NEOPIXEL &pin_PC00 + // On-board flash #define SPI_FLASH_MOSI_PIN &pin_PB05 #define SPI_FLASH_MISO_PIN &pin_PB04 diff --git a/ports/stm32f4/boards/feather_stm32f405_express/pins.c b/ports/stm32f4/boards/feather_stm32f405_express/pins.c index 4aa1f362ad..180fdd0937 100644 --- a/ports/stm32f4/boards/feather_stm32f405_express/pins.c +++ b/ports/stm32f4/boards/feather_stm32f405_express/pins.c @@ -26,6 +26,7 @@ STATIC const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_PB10) }, { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_PB11) }, + { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_PC00) }, { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, diff --git a/ports/stm32f4/common-hal/microcontroller/Pin.h b/ports/stm32f4/common-hal/microcontroller/Pin.h index 0c3fb375f7..c42b0801fc 100644 --- a/ports/stm32f4/common-hal/microcontroller/Pin.h +++ b/ports/stm32f4/common-hal/microcontroller/Pin.h @@ -31,6 +31,10 @@ #include "peripherals/stm32f4/pins.h" +#ifdef MICROPY_HW_NEOPIXEL +extern bool neopixel_in_use; +#endif + void reset_all_pins(void); // reset_pin_number takes the pin number instead of the pointer so that objects don't // need to store a full pointer. diff --git a/ports/stm32f4/common-hal/neopixel_write/__init__.c b/ports/stm32f4/common-hal/neopixel_write/__init__.c new file mode 100644 index 0000000000..c5d810ae9b --- /dev/null +++ b/ports/stm32f4/common-hal/neopixel_write/__init__.c @@ -0,0 +1,89 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Lucian Copeland for Adafruit Industries + * + * 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 "shared-bindings/neopixel_write/__init__.h" + +#include "tick.h" +#include "common-hal/microcontroller/Pin.h" +#include "stm32f4xx_hal.h" +#include "stm32f4xx_ll_gpio.h" + +uint64_t next_start_tick_ms = 0; +uint32_t next_start_tick_us = 1000; + +void common_hal_neopixel_write (const digitalio_digitalinout_obj_t* digitalinout, uint8_t *pixels, + uint32_t numBytes) { + uint8_t *p = pixels, *end = p + numBytes, pix = *p++, mask = 0x80; + uint32_t start = 0; + uint32_t cyc = 0; + + //assumes 800_000Hz frequency + uint32_t sys_freq = HAL_RCC_GetSysClockFreq(); + uint32_t interval = sys_freq/800000; // cycles per interval (1.25 us). 210@168MHz + uint32_t t0 = interval - (sys_freq/2500000); // 0.4 us + uint32_t t1 = interval - (sys_freq/1250000); // 0.8 us + + // This must be called while interrupts are on in case we're waiting for a + // future ms tick. + wait_until(next_start_tick_ms, next_start_tick_us); + + __disable_irq(); + // Enable DWT in debug core. Useable when interrupts disabled, as opposed to Systick->VAL + //ITM->LAR = 0xC5ACCE55; //is this required? + CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; + DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; + DWT->CYCCNT = 0; + + GPIO_TypeDef * p_port = pin_port(digitalinout->pin->port); + uint32_t p_mask = pin_mask(digitalinout->pin->number); + + for(;;) { + LL_GPIO_SetOutputPin(p_port, p_mask); + cyc = (pix & mask) ? t1 : t0; + start = DWT->CYCCNT; + while(DWT->CYCCNT - start < cyc); + LL_GPIO_ResetOutputPin(p_port, p_mask); + if(!(mask >>= 1)) { //max has shifted all the way + if(p >= end) break; + pix = *p++; + mask = 0x80; + } + while(DWT->CYCCNT - start < interval); //wait for interval to finish + } + + // Enable interrupts again + __enable_irq(); + + // Update the next start. + current_tick(&next_start_tick_ms, &next_start_tick_us); + if (next_start_tick_us < 100) { + next_start_tick_ms += 1; + next_start_tick_us = 100 - next_start_tick_us; + } else { + next_start_tick_us -= 100; + } +} diff --git a/ports/stm32f4/mpconfigport.mk b/ports/stm32f4/mpconfigport.mk index bce68f99f4..928d53e67c 100644 --- a/ports/stm32f4/mpconfigport.mk +++ b/ports/stm32f4/mpconfigport.mk @@ -26,6 +26,7 @@ CIRCUITPY_STORAGE = 1 CIRCUITPY_RANDOM = 1 CIRCUITPY_USB_HID = 1 CIRCUITPY_USB_MIDI = 1 +CIRCUITPY_NEOPIXEL_WRITE = 1 #ifeq ($(MCU_SUB_VARIANT), stm32f412zx) #endif From 98fd372d5b602e29b89102c500e4d8e79f9c4d28 Mon Sep 17 00:00:00 2001 From: Hierophect Date: Fri, 15 Nov 2019 13:19:37 -0500 Subject: [PATCH 2/4] Add missing define --- ports/stm32f4/common-hal/microcontroller/Pin.c | 11 +++++++++++ ports/stm32f4/common-hal/microcontroller/Pin.h | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/ports/stm32f4/common-hal/microcontroller/Pin.c b/ports/stm32f4/common-hal/microcontroller/Pin.c index 7e737e21b4..9380bf139a 100644 --- a/ports/stm32f4/common-hal/microcontroller/Pin.c +++ b/ports/stm32f4/common-hal/microcontroller/Pin.c @@ -31,6 +31,17 @@ #include "stm32f4/pins.h" #include "stm32f4xx_hal.h" +#ifdef MICROPY_HW_NEOPIXEL +bool neopixel_in_use; +#endif +#ifdef MICROPY_HW_APA102_MOSI +bool apa102_sck_in_use; +bool apa102_mosi_in_use; +#endif +#ifdef SPEAKER_ENABLE_PIN +bool speaker_enable_in_use; +#endif + #if MCU_PACKAGE == 144 #define GPIO_PORT_COUNT 7 GPIO_TypeDef * ports[GPIO_PORT_COUNT] = {GPIOA, GPIOB, GPIOC, GPIOD, GPIOE, GPIOF, GPIOG}; diff --git a/ports/stm32f4/common-hal/microcontroller/Pin.h b/ports/stm32f4/common-hal/microcontroller/Pin.h index c42b0801fc..c4c9be6a81 100644 --- a/ports/stm32f4/common-hal/microcontroller/Pin.h +++ b/ports/stm32f4/common-hal/microcontroller/Pin.h @@ -34,6 +34,10 @@ #ifdef MICROPY_HW_NEOPIXEL extern bool neopixel_in_use; #endif +#ifdef MICROPY_HW_APA102_MOSI +extern bool apa102_sck_in_use; +extern bool apa102_mosi_in_use; +#endif void reset_all_pins(void); // reset_pin_number takes the pin number instead of the pointer so that objects don't From 51078cc38f9ae1eb1bd7044ea3a0da1fbac3f3d6 Mon Sep 17 00:00:00 2001 From: Hierophect Date: Fri, 15 Nov 2019 17:17:05 -0500 Subject: [PATCH 3/4] timing tweaks with testing --- .../common-hal/neopixel_write/__init__.c | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/ports/stm32f4/common-hal/neopixel_write/__init__.c b/ports/stm32f4/common-hal/neopixel_write/__init__.c index c5d810ae9b..fd46825d03 100644 --- a/ports/stm32f4/common-hal/neopixel_write/__init__.c +++ b/ports/stm32f4/common-hal/neopixel_write/__init__.c @@ -35,6 +35,11 @@ uint64_t next_start_tick_ms = 0; uint32_t next_start_tick_us = 1000; +//sysclock divisors +#define MAGIC_800_INT 900000 // ~1.11 us -> 1.2 field +#define MAGIC_800_T0H 2800000 // ~0.36 us -> 0.44 field +#define MAGIC_800_T1H 1350000 // ~0.74 us -> 0.84 field + void common_hal_neopixel_write (const digitalio_digitalinout_obj_t* digitalinout, uint8_t *pixels, uint32_t numBytes) { uint8_t *p = pixels, *end = p + numBytes, pix = *p++, mask = 0x80; @@ -42,32 +47,34 @@ void common_hal_neopixel_write (const digitalio_digitalinout_obj_t* digitalinout uint32_t cyc = 0; //assumes 800_000Hz frequency + //Theoretical values here are 800_000 -> 1.25us, 2500000->0.4us, 1250000->0.8us + //But they don't work, possibly due to bad optimization? Use tested magic values instead uint32_t sys_freq = HAL_RCC_GetSysClockFreq(); - uint32_t interval = sys_freq/800000; // cycles per interval (1.25 us). 210@168MHz - uint32_t t0 = interval - (sys_freq/2500000); // 0.4 us - uint32_t t1 = interval - (sys_freq/1250000); // 0.8 us + uint32_t interval = sys_freq/MAGIC_800_INT; + uint32_t t0 = (sys_freq/MAGIC_800_T0H); + uint32_t t1 = (sys_freq/MAGIC_800_T1H); // This must be called while interrupts are on in case we're waiting for a // future ms tick. wait_until(next_start_tick_ms, next_start_tick_us); + GPIO_TypeDef * p_port = pin_port(digitalinout->pin->port); + uint32_t p_mask = pin_mask(digitalinout->pin->number); + __disable_irq(); // Enable DWT in debug core. Useable when interrupts disabled, as opposed to Systick->VAL - //ITM->LAR = 0xC5ACCE55; //is this required? + //ITM->LAR = 0xC5ACCE55; //this should be required but isn't CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; DWT->CYCCNT = 0; - GPIO_TypeDef * p_port = pin_port(digitalinout->pin->port); - uint32_t p_mask = pin_mask(digitalinout->pin->number); - for(;;) { + start = DWT->CYCCNT; LL_GPIO_SetOutputPin(p_port, p_mask); cyc = (pix & mask) ? t1 : t0; - start = DWT->CYCCNT; while(DWT->CYCCNT - start < cyc); LL_GPIO_ResetOutputPin(p_port, p_mask); - if(!(mask >>= 1)) { //max has shifted all the way + if(!(mask >>= 1)) { if(p >= end) break; pix = *p++; mask = 0x80; From a4797327cd4ea9fd53e00bf2ecfc82c30e413cec Mon Sep 17 00:00:00 2001 From: Hierophect Date: Mon, 18 Nov 2019 13:51:59 -0500 Subject: [PATCH 4/4] add microcontroller toggles for status LED --- .../feather_stm32f405_express/mpconfigboard.h | 10 ++--- .../stm32f4/common-hal/microcontroller/Pin.c | 38 +++++++++++++------ 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/ports/stm32f4/boards/feather_stm32f405_express/mpconfigboard.h b/ports/stm32f4/boards/feather_stm32f405_express/mpconfigboard.h index e2295dc5e7..dc88f695a9 100644 --- a/ports/stm32f4/boards/feather_stm32f405_express/mpconfigboard.h +++ b/ports/stm32f4/boards/feather_stm32f405_express/mpconfigboard.h @@ -35,13 +35,13 @@ #define AUTORESET_DELAY_MS 500 #define BOARD_FLASH_SIZE (FLASH_SIZE - 0x4000) -#define MICROPY_HW_NEOPIXEL &pin_PC00 +#define MICROPY_HW_NEOPIXEL (&pin_PC00) // On-board flash -#define SPI_FLASH_MOSI_PIN &pin_PB05 -#define SPI_FLASH_MISO_PIN &pin_PB04 -#define SPI_FLASH_SCK_PIN &pin_PB03 -#define SPI_FLASH_CS_PIN &pin_PA15 +#define SPI_FLASH_MOSI_PIN (&pin_PB05) +#define SPI_FLASH_MISO_PIN (&pin_PB04) +#define SPI_FLASH_SCK_PIN (&pin_PB03) +#define SPI_FLASH_CS_PIN (&pin_PA15) #define DEFAULT_I2C_BUS_SCL (&pin_PB06) #define DEFAULT_I2C_BUS_SDA (&pin_PB07) diff --git a/ports/stm32f4/common-hal/microcontroller/Pin.c b/ports/stm32f4/common-hal/microcontroller/Pin.c index 9380bf139a..21290e03c9 100644 --- a/ports/stm32f4/common-hal/microcontroller/Pin.c +++ b/ports/stm32f4/common-hal/microcontroller/Pin.c @@ -26,6 +26,8 @@ */ #include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "supervisor/shared/rgb_led_status.h" #include "py/mphal.h" #include "stm32f4/pins.h" @@ -34,13 +36,6 @@ #ifdef MICROPY_HW_NEOPIXEL bool neopixel_in_use; #endif -#ifdef MICROPY_HW_APA102_MOSI -bool apa102_sck_in_use; -bool apa102_mosi_in_use; -#endif -#ifdef SPEAKER_ENABLE_PIN -bool speaker_enable_in_use; -#endif #if MCU_PACKAGE == 144 #define GPIO_PORT_COUNT 7 @@ -64,6 +59,10 @@ void reset_all_pins(void) { for (uint8_t i = 0; i < GPIO_PORT_COUNT; i++) { HAL_GPIO_DeInit(ports[i], ~never_reset_pins[i]); } + + #ifdef MICROPY_HW_NEOPIXEL + neopixel_in_use = false; + #endif } // Mark pin as free and return it to a quiescent state. @@ -71,13 +70,18 @@ void reset_pin_number(uint8_t pin_port, uint8_t pin_number) { if (pin_port == 0x0F) { return; } - - // Clear claimed bit. + // Clear claimed bit & reset claimed_pins[pin_port] &= ~(1<port && pin_number == MICROPY_HW_NEOPIXEL->number) { + neopixel_in_use = false; + rgb_led_status_init(); + return; + } + #endif +} void never_reset_pin_number(uint8_t pin_port, uint8_t pin_number) { never_reset_pins[pin_port] |= 1<port] |= 1<number; + + #ifdef MICROPY_HW_NEOPIXEL + if (pin == MICROPY_HW_NEOPIXEL) { + neopixel_in_use = true; + } + #endif } bool pin_number_is_free(uint8_t pin_port, uint8_t pin_number) { @@ -101,6 +111,12 @@ bool pin_number_is_free(uint8_t pin_port, uint8_t pin_number) { } bool common_hal_mcu_pin_is_free(const mcu_pin_obj_t *pin) { + #ifdef MICROPY_HW_NEOPIXEL + if (pin == MICROPY_HW_NEOPIXEL) { + return !neopixel_in_use; + } + #endif + return pin_number_is_free(pin->port, pin->number); }