From 23d9c6a0fd4c125abd56fe6828084b05d6d897dd Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 5 Jul 2019 17:24:59 +1000 Subject: [PATCH] stm32: Add initial support for STM32L0xx MCUs. --- ports/stm32/Makefile | 16 ++++- ports/stm32/dma.c | 106 ++++++++++++++++++++++++++--- ports/stm32/dma.h | 15 ++++ ports/stm32/extint.c | 6 +- ports/stm32/flash.c | 9 ++- ports/stm32/machine_uart.c | 2 +- ports/stm32/modmachine.c | 7 +- ports/stm32/mpconfigboard_common.h | 9 +++ ports/stm32/mphalport.c | 3 + ports/stm32/powerctrl.c | 32 +++++++-- ports/stm32/rtc.c | 15 +++- ports/stm32/timer.c | 70 +++++++++++++++---- ports/stm32/uart.c | 9 ++- 13 files changed, 264 insertions(+), 35 deletions(-) diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 3967e63117..3d39f0c94a 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -64,7 +64,7 @@ CFLAGS_CORTEX_M = -mthumb ifeq ($(CMSIS_MCU),$(filter $(CMSIS_MCU),STM32F765xx STM32F767xx STM32F769xx STM32H743xx)) CFLAGS_CORTEX_M += -mfpu=fpv5-d16 -mfloat-abi=hard else -ifeq ($(MCU_SERIES),f0) +ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f0 l0)) CFLAGS_CORTEX_M += -msoft-float else CFLAGS_CORTEX_M += -mfpu=fpv4-sp-d16 -mfloat-abi=hard @@ -75,6 +75,7 @@ endif CFLAGS_MCU_f0 = $(CFLAGS_CORTEX_M) -mtune=cortex-m0 -mcpu=cortex-m0 CFLAGS_MCU_f4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 CFLAGS_MCU_f7 = $(CFLAGS_CORTEX_M) -mtune=cortex-m7 -mcpu=cortex-m7 +CFLAGS_MCU_l0 = $(CFLAGS_CORTEX_M) -mtune=cortex-m0plus -mcpu=cortex-m0plus CFLAGS_MCU_l4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 CFLAGS_MCU_h7 = $(CFLAGS_CORTEX_M) -mtune=cortex-m7 -mcpu=cortex-m7 @@ -200,7 +201,7 @@ SRC_LIBM = $(addprefix lib/libm/,\ wf_lgamma.c \ wf_tgamma.c \ ) -ifeq ($(MCU_SERIES),f0) +ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f0 l0)) SRC_LIBM += lib/libm/ef_sqrt.c else SRC_LIBM += lib/libm/thumb_vfp_sqrtf.c @@ -289,12 +290,21 @@ SRC_O = \ resethandler_m0.o \ lib/utils/gchelper_m0.o else +ifeq ($(MCU_SERIES),l0) +CSUPEROPT = -Os # save some code space +SRC_O = \ + $(STARTUP_FILE) \ + lib/stm32lib/CMSIS/STM32$(MCU_SERIES_UPPER)xx/Source/Templates/system_stm32$(MCU_SERIES)xx.o \ + resethandler_m0.o \ + lib/utils/gchelper_m0.o +else SRC_O = \ $(STARTUP_FILE) \ system_stm32.o \ resethandler.o \ lib/utils/gchelper_m3.o endif +endif SRC_HAL = $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_,\ hal.c \ @@ -341,8 +351,10 @@ endif ifeq ($(CMSIS_MCU),$(filter $(CMSIS_MCU),STM32H743xx)) SRC_HAL += $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_, hal_fdcan.c) else +ifneq ($(MCU_SERIES),$(filter $(MCU_SERIES),l0)) SRC_HAL += $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_, hal_can.c) endif +endif SRC_USBDEV = $(addprefix $(USBDEV_DIR)/,\ core/src/usbd_core.c \ diff --git a/ports/stm32/dma.c b/ports/stm32/dma.c index a1dd40c352..32b9a74d70 100644 --- a/ports/stm32/dma.c +++ b/ports/stm32/dma.c @@ -70,7 +70,7 @@ typedef union { struct _dma_descr_t { #if defined(STM32F4) || defined(STM32F7) || defined(STM32H7) DMA_Stream_TypeDef *instance; - #elif defined(STM32F0) || defined(STM32L4) + #elif defined(STM32F0) || defined(STM32L0) || defined(STM32L4) DMA_Channel_TypeDef *instance; #else #error "Unsupported Processor" @@ -310,6 +310,47 @@ static const uint8_t dma_irqn[NSTREAM] = { DMA2_Stream7_IRQn, }; +#elif defined(STM32L0) + +#define NCONTROLLERS (1) +#define NSTREAMS_PER_CONTROLLER (7) +#define NSTREAM (NCONTROLLERS * NSTREAMS_PER_CONTROLLER) + +#define DMA_SUB_INSTANCE_AS_UINT8(dma_request) (dma_request) + +#define DMA1_ENABLE_MASK (0x007f) // Bits in dma_enable_mask corresponding to DMA1 + +// These descriptors are ordered by DMAx_Channel number, and within a channel by request +// number. The duplicate streams are ok as long as they aren't used at the same time. + +// DMA1 streams +const dma_descr_t dma_SPI_1_RX = { DMA1_Channel2, DMA_REQUEST_1, dma_id_1, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_I2C_3_TX = { DMA1_Channel2, DMA_REQUEST_3, dma_id_1, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_SPI_1_TX = { DMA1_Channel3, DMA_REQUEST_1, dma_id_2, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_I2C_3_RX = { DMA1_Channel3, DMA_REQUEST_3, dma_id_2, &dma_init_struct_spi_i2c }; +#if MICROPY_HW_ENABLE_DAC +const dma_descr_t dma_DAC_1_TX = { DMA1_Channel3, DMA_REQUEST_6, dma_id_2, &dma_init_struct_dac }; +#endif +const dma_descr_t dma_SPI_2_RX = { DMA1_Channel4, DMA_REQUEST_1, dma_id_3, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_I2C_2_TX = { DMA1_Channel4, DMA_REQUEST_3, dma_id_3, &dma_init_struct_spi_i2c }; +#if MICROPY_HW_ENABLE_DAC +const dma_descr_t dma_DAC_2_TX = { DMA1_Channel4, DMA_REQUEST_5, dma_id_3, &dma_init_struct_dac }; +#endif +const dma_descr_t dma_SPI_2_TX = { DMA1_Channel5, DMA_REQUEST_1, dma_id_4, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_I2C_2_RX = { DMA1_Channel5, DMA_REQUEST_3, dma_id_4, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_I2C_1_TX = { DMA1_Channel6, DMA_REQUEST_3, dma_id_5, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_I2C_1_RX = { DMA1_Channel7, DMA_REQUEST_3, dma_id_6, &dma_init_struct_spi_i2c }; + +static const uint8_t dma_irqn[NSTREAM] = { + DMA1_Channel1_IRQn, + DMA1_Channel2_3_IRQn, + DMA1_Channel4_5_6_7_IRQn, + 0, + 0, + 0, + 0, +}; + #elif defined(STM32L4) #define NCONTROLLERS (2) @@ -459,9 +500,11 @@ volatile dma_idle_count_t dma_idle; #define DMA_INVALID_CHANNEL 0xff // Value stored in dma_last_channel which means invalid -#if defined(STM32F0) +#if defined(STM32F0) || defined(STM32L0) #define DMA1_IS_CLK_ENABLED() ((RCC->AHBENR & RCC_AHBENR_DMA1EN) != 0) +#if defined(DMA2) #define DMA2_IS_CLK_ENABLED() ((RCC->AHBENR & RCC_AHBENR_DMA2EN) != 0) +#endif #else #define DMA1_IS_CLK_ENABLED() ((RCC->AHB1ENR & RCC_AHB1ENR_DMA1EN) != 0) #define DMA2_IS_CLK_ENABLED() ((RCC->AHB1ENR & RCC_AHB1ENR_DMA2EN) != 0) @@ -526,6 +569,44 @@ void DMA2_Stream5_IRQHandler(void) { IRQ_ENTER(DMA2_Stream5_IRQn); if (dma_handl void DMA2_Stream6_IRQHandler(void) { IRQ_ENTER(DMA2_Stream6_IRQn); if (dma_handle[dma_id_14] != NULL) { HAL_DMA_IRQHandler(dma_handle[dma_id_14]); } IRQ_EXIT(DMA2_Stream6_IRQn); } void DMA2_Stream7_IRQHandler(void) { IRQ_ENTER(DMA2_Stream7_IRQn); if (dma_handle[dma_id_15] != NULL) { HAL_DMA_IRQHandler(dma_handle[dma_id_15]); } IRQ_EXIT(DMA2_Stream7_IRQn); } +#elif defined(STM32L0) + +void DMA1_Channel1_IRQHandler(void) { + IRQ_ENTER(DMA1_Channel1_IRQn); + if (dma_handle[dma_id_0] != NULL) { + HAL_DMA_IRQHandler(dma_handle[dma_id_0]); + } + IRQ_EXIT(DMA1_Channel1_IRQn); +} + +void DMA1_Channel2_3_IRQHandler(void) { + IRQ_ENTER(DMA1_Channel2_3_IRQn); + if (dma_handle[dma_id_1] != NULL) { + HAL_DMA_IRQHandler(dma_handle[dma_id_1]); + } + if (dma_handle[dma_id_2] != NULL) { + HAL_DMA_IRQHandler(dma_handle[dma_id_2]); + } + IRQ_EXIT(DMA1_Channel2_3_IRQn); +} + +void DMA1_Channel4_5_6_7_IRQHandler(void) { + IRQ_ENTER(DMA1_Channel4_5_6_7_IRQn); + if (dma_handle[dma_id_3] != NULL) { + HAL_DMA_IRQHandler(dma_handle[dma_id_3]); + } + if (dma_handle[dma_id_4] != NULL) { + HAL_DMA_IRQHandler(dma_handle[dma_id_4]); + } + if (dma_handle[dma_id_5] != NULL) { + HAL_DMA_IRQHandler(dma_handle[dma_id_5]); + } + if (dma_handle[dma_id_6] != NULL) { + HAL_DMA_IRQHandler(dma_handle[dma_id_6]); + } + IRQ_EXIT(DMA1_Channel4_5_6_7_IRQn); +} + #elif defined(STM32L4) void DMA1_Channel1_IRQHandler(void) { IRQ_ENTER(DMA1_Channel1_IRQn); if (dma_handle[dma_id_0] != NULL) { HAL_DMA_IRQHandler(dma_handle[dma_id_0]); } IRQ_EXIT(DMA1_Channel1_IRQn); } @@ -572,18 +653,21 @@ static void dma_enable_clock(dma_id_t dma_id) { dma_last_sub_instance[channel] = DMA_INVALID_CHANNEL; } } - } else { + } + #if defined(DMA2) + else { if (((old_enable_mask & DMA2_ENABLE_MASK) == 0) && !DMA2_IS_CLK_ENABLED()) { __HAL_RCC_DMA2_CLK_ENABLE(); // We just turned on the clock. This means that anything stored - // in dma_last_channel (for DMA1) needs to be invalidated. + // in dma_last_channel (for DMA2) needs to be invalidated. for (int channel = NSTREAMS_PER_CONTROLLER; channel < NSTREAM; channel++) { dma_last_sub_instance[channel] = DMA_INVALID_CHANNEL; } } } + #endif } static void dma_disable_clock(dma_id_t dma_id) { @@ -599,7 +683,7 @@ void dma_init_handle(DMA_HandleTypeDef *dma, const dma_descr_t *dma_descr, uint3 dma->Instance = dma_descr->instance; dma->Init = *dma_descr->init; dma->Init.Direction = dir; - #if defined(STM32L4) || defined(STM32H7) + #if defined(STM32L0) || defined(STM32L4) || defined(STM32H7) dma->Init.Request = dma_descr->sub_instance; #else #if !defined(STM32F0) @@ -705,7 +789,10 @@ static void dma_idle_handler(uint32_t tick) { } static const uint32_t controller_mask[] = { - DMA1_ENABLE_MASK, DMA2_ENABLE_MASK + DMA1_ENABLE_MASK, + #if defined(DMA2) + DMA2_ENABLE_MASK, + #endif }; { int controller = (tick >> DMA_SYSTICK_LOG2) & 1; @@ -719,9 +806,12 @@ static void dma_idle_handler(uint32_t tick) { dma_idle.counter[controller] = 0; if (controller == 0) { __HAL_RCC_DMA1_CLK_DISABLE(); - } else { + } + #if defined(DMA2) + else { __HAL_RCC_DMA2_CLK_DISABLE(); } + #endif } else { // Something is still active, but the counter never got // reset, so we'll reset the counter here. @@ -731,7 +821,7 @@ static void dma_idle_handler(uint32_t tick) { } } -#if defined(STM32F0) || defined(STM32L4) +#if defined(STM32F0) || defined(STM32L0) || defined(STM32L4) void dma_nohal_init(const dma_descr_t *descr, uint32_t config) { DMA_Channel_TypeDef *dma = descr->instance; diff --git a/ports/stm32/dma.h b/ports/stm32/dma.h index 7b74a73993..bedabe7f89 100644 --- a/ports/stm32/dma.h +++ b/ports/stm32/dma.h @@ -58,6 +58,21 @@ extern const dma_descr_t dma_SPI_6_RX; extern const dma_descr_t dma_SDIO_0; extern const dma_descr_t dma_DCMI_0; +#elif defined(STM32L0) + +extern const dma_descr_t dma_SPI_1_RX; +extern const dma_descr_t dma_I2C_3_TX; +extern const dma_descr_t dma_SPI_1_TX; +extern const dma_descr_t dma_I2C_3_RX; +extern const dma_descr_t dma_DAC_1_TX; +extern const dma_descr_t dma_SPI_2_RX; +extern const dma_descr_t dma_I2C_2_TX; +extern const dma_descr_t dma_DAC_2_TX; +extern const dma_descr_t dma_SPI_2_TX; +extern const dma_descr_t dma_I2C_2_RX; +extern const dma_descr_t dma_I2C_1_TX; +extern const dma_descr_t dma_I2C_1_RX; + #elif defined(STM32L4) extern const dma_descr_t dma_ADC_1_RX; diff --git a/ports/stm32/extint.c b/ports/stm32/extint.c index 0b1ba8eb09..9a7295995a 100644 --- a/ports/stm32/extint.c +++ b/ports/stm32/extint.c @@ -139,12 +139,16 @@ STATIC mp_obj_t pyb_extint_callback_arg[EXTI_NUM_VECTORS]; #endif STATIC const uint8_t nvic_irq_channel[EXTI_NUM_VECTORS] = { - #if defined(STM32F0) + #if defined(STM32F0) || defined(STM32L0) EXTI0_1_IRQn, EXTI0_1_IRQn, EXTI2_3_IRQn, EXTI2_3_IRQn, EXTI4_15_IRQn, EXTI4_15_IRQn, EXTI4_15_IRQn, EXTI4_15_IRQn, EXTI4_15_IRQn, EXTI4_15_IRQn, EXTI4_15_IRQn, EXTI4_15_IRQn, EXTI4_15_IRQn, EXTI4_15_IRQn, EXTI4_15_IRQn, EXTI4_15_IRQn, + #if defined(STM32L0) + PVD_IRQn, + #else PVD_VDDIO2_IRQn, + #endif RTC_IRQn, 0, // internal USB wakeup event RTC_IRQn, diff --git a/ports/stm32/flash.c b/ports/stm32/flash.c index 56896a7037..aff85f7e37 100644 --- a/ports/stm32/flash.c +++ b/ports/stm32/flash.c @@ -68,7 +68,7 @@ static const flash_layout_t flash_layout[] = { { 0x08040000, 0x40000, 3 }, }; -#elif defined(STM32L4) +#elif defined(STM32L0) || defined(STM32L4) static const flash_layout_t flash_layout[] = { { (uint32_t)FLASH_BASE, (uint32_t)FLASH_PAGE_SIZE, 512 }, @@ -170,7 +170,12 @@ void flash_erase(uint32_t flash_dest, uint32_t num_word32) { EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; EraseInitStruct.PageAddress = flash_dest; EraseInitStruct.NbPages = (4 * num_word32 + FLASH_PAGE_SIZE - 4) / FLASH_PAGE_SIZE; - #elif (defined(STM32L4) && !defined(SYSCFG_MEMRMP_FB_MODE)) + #elif defined(STM32L0) + __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR); + EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; + EraseInitStruct.PageAddress = flash_dest; + EraseInitStruct.NbPages = (4 * num_word32 + FLASH_PAGE_SIZE - 4) / FLASH_PAGE_SIZE; + #elif defined(STM32L4) && !defined(SYSCFG_MEMRMP_FB_MODE) __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS); EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; EraseInitStruct.Page = get_page(flash_dest); diff --git a/ports/stm32/machine_uart.c b/ports/stm32/machine_uart.c index 29369c0c14..92343ba6d7 100644 --- a/ports/stm32/machine_uart.c +++ b/ports/stm32/machine_uart.c @@ -489,7 +489,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_uart_readchar_obj, pyb_uart_readchar); // uart.sendbreak() STATIC mp_obj_t pyb_uart_sendbreak(mp_obj_t self_in) { pyb_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); - #if defined(STM32F0) || defined(STM32F7) || defined(STM32L4) || defined(STM32H7) + #if defined(STM32F0) || defined(STM32F7) || defined(STM32L0) || defined(STM32L4) || defined(STM32H7) self->uartx->RQR = USART_RQR_SBKRQ; // write-only register #else self->uartx->CR1 |= USART_CR1_SBK; diff --git a/ports/stm32/modmachine.c b/ports/stm32/modmachine.c index ba0ee28471..8d6dbf4fec 100644 --- a/ports/stm32/modmachine.c +++ b/ports/stm32/modmachine.c @@ -56,6 +56,11 @@ #include "uart.h" #include "wdt.h" +#if defined(STM32L0) +// L0 does not have a BOR, so use POR instead +#define RCC_CSR_BORRSTF RCC_CSR_PORRSTF +#endif + #if defined(STM32L4) // L4 does not have a POR, so use BOR instead #define RCC_CSR_PORRSTF RCC_CSR_BORRSTF @@ -300,7 +305,7 @@ STATIC mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) { return mp_obj_new_tuple(MP_ARRAY_SIZE(tuple), tuple); } else { // set - #if defined(STM32F0) || defined(STM32L4) + #if defined(STM32F0) || defined(STM32L0) || defined(STM32L4) mp_raise_NotImplementedError("machine.freq set not supported yet"); #else mp_int_t sysclk = mp_obj_get_int(args[0]); diff --git a/ports/stm32/mpconfigboard_common.h b/ports/stm32/mpconfigboard_common.h index 036e1f6ccd..94a86ca92a 100644 --- a/ports/stm32/mpconfigboard_common.h +++ b/ports/stm32/mpconfigboard_common.h @@ -184,6 +184,15 @@ #define MICROPY_HW_MAX_TIMER (17) #define MICROPY_HW_MAX_UART (8) +// Configuration for STM32L0 series +#elif defined(STM32L0) + +#define MP_HAL_UNIQUE_ID_ADDRESS (0x1FF80050) +#define PYB_EXTI_NUM_VECTORS (30) // TODO (22 configurable, 7 direct) +#define MICROPY_HW_MAX_I2C (3) +#define MICROPY_HW_MAX_TIMER (22) +#define MICROPY_HW_MAX_UART (4) + // Configuration for STM32L4 series #elif defined(STM32L4) diff --git a/ports/stm32/mphalport.c b/ports/stm32/mphalport.c index 79b28bd3de..c5786e7409 100644 --- a/ports/stm32/mphalport.c +++ b/ports/stm32/mphalport.c @@ -119,6 +119,9 @@ void mp_hal_gpio_clock_enable(GPIO_TypeDef *gpio) { #elif defined(STM32H7) #define AHBxENR AHB4ENR #define AHBxENR_GPIOAEN_Pos RCC_AHB4ENR_GPIOAEN_Pos + #elif defined(STM32L0) + #define AHBxENR IOPENR + #define AHBxENR_GPIOAEN_Pos RCC_IOPENR_GPIOAEN #elif defined(STM32L4) #define AHBxENR AHB2ENR #define AHBxENR_GPIOAEN_Pos RCC_AHB2ENR_GPIOAEN_Pos diff --git a/ports/stm32/powerctrl.c b/ports/stm32/powerctrl.c index e3ad20039c..cf2445c7d9 100644 --- a/ports/stm32/powerctrl.c +++ b/ports/stm32/powerctrl.c @@ -84,7 +84,31 @@ void powerctrl_check_enter_bootloader(void) { } } -#if !defined(STM32F0) +#if defined(STM32L0) +void SystemClock_Config(void) { + // Enable power control peripheral + __HAL_RCC_PWR_CLK_ENABLE(); + + // Use the 16MHz internal oscillator + RCC->CR |= RCC_CR_HSION; + while (!(RCC->CR & RCC_CR_HSIRDY)) { + } + const uint32_t sysclk_src = 1; + + // Select SYSCLK source + RCC->CFGR |= sysclk_src << RCC_CFGR_SW_Pos; + while (((RCC->CFGR >> RCC_CFGR_SWS_Pos) & 0x3) != sysclk_src) { + // Wait for SYSCLK source to change + } + + SystemCoreClockUpdate(); + + HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / 1000); + HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); +} +#endif + +#if !defined(STM32F0) && !defined(STM32L0) // Assumes that PLL is used as the SYSCLK source int powerctrl_rcc_clock_config_pll(RCC_ClkInitTypeDef *rcc_init, uint32_t sysclk_mhz, bool need_pllsai) { @@ -158,7 +182,7 @@ int powerctrl_rcc_clock_config_pll(RCC_ClkInitTypeDef *rcc_init, uint32_t sysclk #endif -#if !(defined(STM32F0) || defined(STM32L4)) +#if !defined(STM32F0) && !defined(STM32L0) && !defined(STM32L4) STATIC uint32_t calc_ahb_div(uint32_t wanted_div) { if (wanted_div <= 1) { return RCC_SYSCLK_DIV1; } @@ -333,7 +357,7 @@ void powerctrl_enter_stop_mode(void) { __HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_MSI); #endif - #if !defined(STM32F0) && !defined(STM32L4) + #if !defined(STM32F0) && !defined(STM32L0) && !defined(STM32L4) // takes longer to wake but reduces stop current HAL_PWREx_EnableFlashPowerDown(); #endif @@ -426,7 +450,7 @@ void powerctrl_enter_standby_mode(void) { // Note: we only support RTC ALRA, ALRB, WUT and TS. // TODO support TAMP and WKUP (PA0 external pin). - #if defined(STM32F0) + #if defined(STM32F0) || defined(STM32L0) #define CR_BITS (RTC_CR_ALRAIE | RTC_CR_WUTIE | RTC_CR_TSIE) #define ISR_BITS (RTC_ISR_ALRAF | RTC_ISR_WUTF | RTC_ISR_TSF) #else diff --git a/ports/stm32/rtc.c b/ports/stm32/rtc.c index a7c3f2068b..d0f444c216 100644 --- a/ports/stm32/rtc.c +++ b/ports/stm32/rtc.c @@ -73,6 +73,17 @@ STATIC bool rtc_use_lse = false; STATIC uint32_t rtc_startup_tick; STATIC bool rtc_need_init_finalise = false; +#if defined(STM32L0) +#define BDCR CSR +#define RCC_BDCR_RTCEN RCC_CSR_RTCEN +#define RCC_BDCR_RTCSEL RCC_CSR_RTCSEL +#define RCC_BDCR_RTCSEL_0 RCC_CSR_RTCSEL_0 +#define RCC_BDCR_RTCSEL_1 RCC_CSR_RTCSEL_1 +#define RCC_BDCR_LSEON RCC_CSR_LSEON +#define RCC_BDCR_LSERDY RCC_CSR_LSERDY +#define RCC_BDCR_LSEBYP RCC_CSR_LSEBYP +#endif + void rtc_init_start(bool force_init) { RTCHandle.Instance = RTC; @@ -287,7 +298,7 @@ STATIC HAL_StatusTypeDef PYB_RTC_Init(RTC_HandleTypeDef *hrtc) { // Exit Initialization mode hrtc->Instance->ISR &= (uint32_t)~RTC_ISR_INIT; - #if defined(STM32L4) || defined(STM32H7) + #if defined(STM32L0) || defined(STM32L4) || defined(STM32H7) hrtc->Instance->OR &= (uint32_t)~RTC_OR_ALARMOUTTYPE; hrtc->Instance->OR |= (uint32_t)(hrtc->Init.OutPutType); #elif defined(STM32F7) @@ -539,7 +550,7 @@ mp_obj_t pyb_rtc_datetime(size_t n_args, const mp_obj_t *args) { } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_rtc_datetime_obj, 1, 2, pyb_rtc_datetime); -#if defined(STM32F0) +#if defined(STM32F0) || defined(STM32L0) #define RTC_WKUP_IRQn RTC_IRQn #endif diff --git a/ports/stm32/timer.c b/ports/stm32/timer.c index 7f4c0d85a9..cd01a41741 100644 --- a/ports/stm32/timer.c +++ b/ports/stm32/timer.c @@ -427,6 +427,8 @@ STATIC mp_obj_t compute_percent_from_pwm_value(uint32_t period, uint32_t cmp) { #endif } +#if !defined(STM32L0) + // Computes the 8-bit value for the DTG field in the BDTR register. // // 1 tick = 1 count of the timer's clock (source_freq) divided by div. @@ -486,6 +488,8 @@ STATIC void config_deadtime(pyb_timer_obj_t *self, mp_int_t ticks, mp_int_t brk) HAL_TIMEx_ConfigBreakDeadTime(&self->tim, &deadTimeConfig); } +#endif + TIM_HandleTypeDef *pyb_timer_get_handle(mp_obj_t timer) { if (mp_obj_get_type(timer) != &pyb_timer_type) { mp_raise_ValueError("need a Timer object"); @@ -514,6 +518,7 @@ STATIC void pyb_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ self->tim.Init.ClockDivision == TIM_CLOCKDIVISION_DIV4 ? 4 : self->tim.Init.ClockDivision == TIM_CLOCKDIVISION_DIV2 ? 2 : 1); + #if !defined(STM32L0) #if defined(IS_TIM_ADVANCED_INSTANCE) if (IS_TIM_ADVANCED_INSTANCE(self->tim.Instance)) #elif defined(IS_TIM_BREAK_INSTANCE) @@ -531,6 +536,7 @@ STATIC void pyb_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ mp_printf(print, ", brk=BRK_OFF"); } } + #endif mp_print_str(print, ")"); } } @@ -630,11 +636,15 @@ STATIC mp_obj_t pyb_timer_init_helper(pyb_timer_obj_t *self, size_t n_args, cons args[ARG_div].u_int == 4 ? TIM_CLOCKDIVISION_DIV4 : TIM_CLOCKDIVISION_DIV1; + #if !defined(STM32L0) init->RepetitionCounter = 0; + #endif // enable TIM clock switch (self->tim_id) { + #if defined(TIM1) case 1: __HAL_RCC_TIM1_CLK_ENABLE(); break; + #endif case 2: __HAL_RCC_TIM2_CLK_ENABLE(); break; #if defined(TIM3) case 3: __HAL_RCC_TIM3_CLK_ENABLE(); break; @@ -681,22 +691,40 @@ STATIC mp_obj_t pyb_timer_init_helper(pyb_timer_obj_t *self, size_t n_args, cons #if defined(TIM17) case 17: __HAL_RCC_TIM17_CLK_ENABLE(); break; #endif + #if defined(TIM18) + case 18: __HAL_RCC_TIM18_CLK_ENABLE(); break; + #endif + #if defined(TIM19) + case 19: __HAL_RCC_TIM19_CLK_ENABLE(); break; + #endif + #if defined(TIM20) + case 20: __HAL_RCC_TIM20_CLK_ENABLE(); break; + #endif + #if defined(TIM21) + case 21: __HAL_RCC_TIM21_CLK_ENABLE(); break; + #endif + #if defined(TIM22) + case 22: __HAL_RCC_TIM22_CLK_ENABLE(); break; + #endif } // set IRQ priority (if not a special timer) if (self->tim_id != 5) { NVIC_SetPriority(IRQn_NONNEG(self->irqn), IRQ_PRI_TIMX); if (self->tim_id == 1) { + #if defined(TIM1) NVIC_SetPriority(TIM1_CC_IRQn, IRQ_PRI_TIMX); - #if defined(TIM8) + #endif } else if (self->tim_id == 8) { + #if defined(TIM8) NVIC_SetPriority(TIM8_CC_IRQn, IRQ_PRI_TIMX); - #endif + #endif } } // init TIM HAL_TIM_Base_Init(&self->tim); + #if !defined(STM32L0) #if defined(IS_TIM_ADVANCED_INSTANCE) if (IS_TIM_ADVANCED_INSTANCE(self->tim.Instance)) { #elif defined(IS_TIM_BREAK_INSTANCE) @@ -707,6 +735,7 @@ STATIC mp_obj_t pyb_timer_init_helper(pyb_timer_obj_t *self, size_t n_args, cons config_deadtime(self, args[ARG_deadtime].u_int, args[ARG_brk].u_int); } + #endif // Enable ARPE so that the auto-reload register is buffered. // This allows to smoothly change the frequency of the timer. @@ -726,6 +755,7 @@ STATIC mp_obj_t pyb_timer_init_helper(pyb_timer_obj_t *self, size_t n_args, cons // It assumes that timer instance pointer has the lower 8 bits cleared. #define TIM_ENTRY(id, irq) [id - 1] = (uint32_t)TIM##id | irq STATIC const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = { + #if defined(TIM1) #if defined(STM32F0) TIM_ENTRY(1, TIM1_BRK_UP_TRG_COM_IRQn), #elif defined(STM32F4) || defined(STM32F7) @@ -733,6 +763,7 @@ STATIC const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = { #elif defined(STM32L4) TIM_ENTRY(1, TIM1_UP_TIM16_IRQn), #endif + #endif TIM_ENTRY(2, TIM2_IRQn), #if defined(TIM3) TIM_ENTRY(3, TIM3_IRQn), @@ -1054,10 +1085,12 @@ STATIC mp_obj_t pyb_timer_channel(size_t n_args, const mp_obj_t *pos_args, mp_ma oc_config.Pulse = args[3].u_int; } oc_config.OCPolarity = TIM_OCPOLARITY_HIGH; - oc_config.OCNPolarity = TIM_OCNPOLARITY_HIGH; oc_config.OCFastMode = TIM_OCFAST_DISABLE; + #if !defined(STM32L0) + oc_config.OCNPolarity = TIM_OCNPOLARITY_HIGH; oc_config.OCIdleState = TIM_OCIDLESTATE_SET; oc_config.OCNIdleState = TIM_OCNIDLESTATE_SET; + #endif HAL_TIM_PWM_ConfigChannel(&self->tim, &oc_config, TIMER_CHANNEL(chan)); if (chan->callback == mp_const_none) { @@ -1065,10 +1098,12 @@ STATIC mp_obj_t pyb_timer_channel(size_t n_args, const mp_obj_t *pos_args, mp_ma } else { pyb_timer_channel_callback(MP_OBJ_FROM_PTR(chan), chan->callback); } + #if !defined(STM32L0) // Start the complimentary channel too (if its supported) if (IS_TIM_CCXN_INSTANCE(self->tim.Instance, TIMER_CHANNEL(chan))) { HAL_TIMEx_PWMN_Start(&self->tim, TIMER_CHANNEL(chan)); } + #endif break; } @@ -1085,14 +1120,16 @@ STATIC mp_obj_t pyb_timer_channel(size_t n_args, const mp_obj_t *pos_args, mp_ma if (oc_config.OCPolarity == 0xffffffff) { oc_config.OCPolarity = TIM_OCPOLARITY_HIGH; } + oc_config.OCFastMode = TIM_OCFAST_DISABLE; + #if !defined(STM32L0) if (oc_config.OCPolarity == TIM_OCPOLARITY_HIGH) { oc_config.OCNPolarity = TIM_OCNPOLARITY_HIGH; } else { oc_config.OCNPolarity = TIM_OCNPOLARITY_LOW; } - oc_config.OCFastMode = TIM_OCFAST_DISABLE; oc_config.OCIdleState = TIM_OCIDLESTATE_SET; oc_config.OCNIdleState = TIM_OCNIDLESTATE_SET; + #endif if (!IS_TIM_OC_POLARITY(oc_config.OCPolarity)) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "invalid polarity (%d)", oc_config.OCPolarity)); @@ -1103,10 +1140,12 @@ STATIC mp_obj_t pyb_timer_channel(size_t n_args, const mp_obj_t *pos_args, mp_ma } else { pyb_timer_channel_callback(MP_OBJ_FROM_PTR(chan), chan->callback); } + #if !defined(STM32L0) // Start the complimentary channel too (if its supported) if (IS_TIM_CCXN_INSTANCE(self->tim.Instance, TIMER_CHANNEL(chan))) { HAL_TIMEx_OCN_Start(&self->tim, TIMER_CHANNEL(chan)); } + #endif break; } @@ -1155,8 +1194,12 @@ STATIC mp_obj_t pyb_timer_channel(size_t n_args, const mp_obj_t *pos_args, mp_ma nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "invalid polarity (%d)", enc_config.IC1Polarity)); } // Only Timers 1, 2, 3, 4, 5, and 8 support encoder mode - if (self->tim.Instance != TIM1 - && self->tim.Instance != TIM2 + if ( + #if defined(TIM1) + self->tim.Instance != TIM1 + && + #endif + self->tim.Instance != TIM2 #if defined(TIM3) && self->tim.Instance != TIM3 #endif @@ -1426,15 +1469,18 @@ STATIC mp_obj_t pyb_timer_channel_callback(mp_obj_t self_in, mp_obj_t callback) self->callback = mp_const_none; } else if (mp_obj_is_callable(callback)) { self->callback = callback; - uint8_t tim_id = self->timer->tim_id; __HAL_TIM_CLEAR_IT(&self->timer->tim, TIMER_IRQ_MASK(self->channel)); - if (tim_id == 1) { + #if defined(TIM1) + if (self->timer->tim_id == 1) { HAL_NVIC_EnableIRQ(TIM1_CC_IRQn); - #if defined(TIM8) // STM32F401 doesn't have a TIM8 - } else if (tim_id == 8) { - HAL_NVIC_EnableIRQ(TIM8_CC_IRQn); + } else #endif - } else { + #if defined(TIM8) // STM32F401 doesn't have a TIM8 + if (self->timer->tim_id == 8) { + HAL_NVIC_EnableIRQ(TIM8_CC_IRQn); + } else + #endif + { HAL_NVIC_EnableIRQ(self->timer->irqn); } // start timer, so that it interrupts on overflow diff --git a/ports/stm32/uart.c b/ports/stm32/uart.c index 3a21a0b31e..0d46ea9473 100644 --- a/ports/stm32/uart.c +++ b/ports/stm32/uart.c @@ -76,6 +76,11 @@ #define USART_CR2_IE_ALL (USART_CR2_IE_BASE) #define USART_CR3_IE_ALL (USART_CR3_IE_BASE | USART_CR3_RXFTIE | USART_CR3_TCBGTIE | USART_CR3_TXFTIE | USART_CR3_WUFIE) +#elif defined(STM32L0) +#define USART_CR1_IE_ALL (USART_CR1_IE_BASE | USART_CR1_EOBIE | USART_CR1_RTOIE | USART_CR1_CMIE) +#define USART_CR2_IE_ALL (USART_CR2_IE_BASE) +#define USART_CR3_IE_ALL (USART_CR3_IE_BASE | USART_CR3_WUFIE) + #elif defined(STM32L4) #define USART_CR1_IE_ALL (USART_CR1_IE_BASE | USART_CR1_EOBIE | USART_CR1_RTOIE | USART_CR1_CMIE) #define USART_CR2_IE_ALL (USART_CR2_IE_BASE) @@ -648,7 +653,7 @@ int uart_rx_char(pyb_uart_obj_t *self) { return data; } else { // no buffering - #if defined(STM32F0) || defined(STM32F7) || defined(STM32L4) || defined(STM32H7) + #if defined(STM32F0) || defined(STM32F7) || defined(STM32L0) || defined(STM32L4) || defined(STM32H7) int data = self->uartx->RDR & self->char_mask; self->uartx->ICR = USART_ICR_ORECF; // clear ORE if it was set return data; @@ -774,7 +779,7 @@ void uart_irq_handler(mp_uint_t uart_id) { uint16_t next_head = (self->read_buf_head + 1) % self->read_buf_len; if (next_head != self->read_buf_tail) { // only read data if room in buf - #if defined(STM32F0) || defined(STM32F7) || defined(STM32L4) || defined(STM32H7) + #if defined(STM32F0) || defined(STM32F7) || defined(STM32L0) || defined(STM32L4) || defined(STM32H7) int data = self->uartx->RDR; // clears UART_FLAG_RXNE self->uartx->ICR = USART_ICR_ORECF; // clear ORE if it was set #else