Merge pull request #2350 from hierophect/stm32-neopixel-fix

Fix for neopixels on <100MHz STM32 boards
This commit is contained in:
Scott Shawcroft 2019-12-06 10:40:50 -08:00 committed by GitHub
commit 840f88b8f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 14 additions and 8 deletions

View File

@ -47,7 +47,7 @@ digitalinout_result_t common_hal_digitalio_digitalinout_construct(
GPIO_InitStruct.Pin = pin_mask(self->pin->number); GPIO_InitStruct.Pin = pin_mask(self->pin->number);
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(pin_port(self->pin->port), &GPIO_InitStruct); HAL_GPIO_Init(pin_port(self->pin->port), &GPIO_InitStruct);
return DIGITALINOUT_OK; return DIGITALINOUT_OK;
@ -73,7 +73,7 @@ void common_hal_digitalio_digitalinout_switch_to_input(
GPIO_InitStruct.Pin = pin_mask(self->pin->number); GPIO_InitStruct.Pin = pin_mask(self->pin->number);
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(pin_port(self->pin->port), &GPIO_InitStruct); HAL_GPIO_Init(pin_port(self->pin->port), &GPIO_InitStruct);
common_hal_digitalio_digitalinout_set_pull(self, pull); common_hal_digitalio_digitalinout_set_pull(self, pull);
@ -114,7 +114,7 @@ void common_hal_digitalio_digitalinout_set_drive_mode(
GPIO_InitStruct.Mode = (drive_mode == DRIVE_MODE_OPEN_DRAIN ? GPIO_InitStruct.Mode = (drive_mode == DRIVE_MODE_OPEN_DRAIN ?
GPIO_MODE_OUTPUT_OD : GPIO_MODE_OUTPUT_PP); GPIO_MODE_OUTPUT_OD : GPIO_MODE_OUTPUT_PP);
GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(pin_port(self->pin->port), &GPIO_InitStruct); HAL_GPIO_Init(pin_port(self->pin->port), &GPIO_InitStruct);
} }

View File

@ -28,6 +28,8 @@
#include "shared-bindings/neopixel_write/__init__.h" #include "shared-bindings/neopixel_write/__init__.h"
#include "tick.h" #include "tick.h"
#include "py/mperrno.h"
#include "py/runtime.h"
#include "common-hal/microcontroller/Pin.h" #include "common-hal/microcontroller/Pin.h"
#include "stm32f4xx_hal.h" #include "stm32f4xx_hal.h"
#include "stm32f4xx_ll_gpio.h" #include "stm32f4xx_ll_gpio.h"
@ -40,6 +42,9 @@ uint32_t next_start_tick_us = 1000;
#define MAGIC_800_T0H 2800000 // ~0.36 us -> 0.44 field #define MAGIC_800_T0H 2800000 // ~0.36 us -> 0.44 field
#define MAGIC_800_T1H 1350000 // ~0.74 us -> 0.84 field #define MAGIC_800_T1H 1350000 // ~0.74 us -> 0.84 field
#pragma GCC push_options
#pragma GCC optimize ("Os")
void common_hal_neopixel_write (const digitalio_digitalinout_obj_t* digitalinout, uint8_t *pixels, void common_hal_neopixel_write (const digitalio_digitalinout_obj_t* digitalinout, uint8_t *pixels,
uint32_t numBytes) { uint32_t numBytes) {
uint8_t *p = pixels, *end = p + numBytes, pix = *p++, mask = 0x80; uint8_t *p = pixels, *end = p + numBytes, pix = *p++, mask = 0x80;
@ -48,7 +53,7 @@ void common_hal_neopixel_write (const digitalio_digitalinout_obj_t* digitalinout
//assumes 800_000Hz frequency //assumes 800_000Hz frequency
//Theoretical values here are 800_000 -> 1.25us, 2500000->0.4us, 1250000->0.8us //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 //TODO: try to get dynamic weighting working again
uint32_t sys_freq = HAL_RCC_GetSysClockFreq(); uint32_t sys_freq = HAL_RCC_GetSysClockFreq();
uint32_t interval = sys_freq/MAGIC_800_INT; uint32_t interval = sys_freq/MAGIC_800_INT;
uint32_t t0 = (sys_freq/MAGIC_800_T0H); uint32_t t0 = (sys_freq/MAGIC_800_T0H);
@ -63,23 +68,22 @@ void common_hal_neopixel_write (const digitalio_digitalinout_obj_t* digitalinout
__disable_irq(); __disable_irq();
// Enable DWT in debug core. Useable when interrupts disabled, as opposed to Systick->VAL // Enable DWT in debug core. Useable when interrupts disabled, as opposed to Systick->VAL
//ITM->LAR = 0xC5ACCE55; //this should be required but isn't
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
DWT->CYCCNT = 0; DWT->CYCCNT = 0;
for(;;) { for(;;) {
cyc = (pix & mask) ? t1 : t0;
start = DWT->CYCCNT; start = DWT->CYCCNT;
LL_GPIO_SetOutputPin(p_port, p_mask); LL_GPIO_SetOutputPin(p_port, p_mask);
cyc = (pix & mask) ? t1 : t0; while((DWT->CYCCNT - start) < cyc);
while(DWT->CYCCNT - start < cyc);
LL_GPIO_ResetOutputPin(p_port, p_mask); LL_GPIO_ResetOutputPin(p_port, p_mask);
while((DWT->CYCCNT - start) < interval);
if(!(mask >>= 1)) { if(!(mask >>= 1)) {
if(p >= end) break; if(p >= end) break;
pix = *p++; pix = *p++;
mask = 0x80; mask = 0x80;
} }
while(DWT->CYCCNT - start < interval); //wait for interval to finish
} }
// Enable interrupts again // Enable interrupts again
@ -94,3 +98,5 @@ void common_hal_neopixel_write (const digitalio_digitalinout_obj_t* digitalinout
next_start_tick_us -= 100; next_start_tick_us -= 100;
} }
} }
#pragma GCC pop_options