diff options
Diffstat (limited to 'ports/stm')
| -rwxr-xr-x | ports/stm/Makefile | 7 | ||||
| -rw-r--r-- | ports/stm/boards/feather_stm32f405_express/pins.c | 3 | ||||
| -rw-r--r-- | ports/stm/boards/meowbit_v121/board.c | 3 | ||||
| -rw-r--r-- | ports/stm/boards/meowbit_v121/mpconfigboard.mk | 2 | ||||
| -rw-r--r-- | ports/stm/boards/thunderpack/mpconfigboard.mk | 3 | ||||
| -rw-r--r-- | ports/stm/common-hal/busio/SPI.c | 22 | ||||
| -rw-r--r-- | ports/stm/common-hal/canio/CAN.c | 297 | ||||
| -rw-r--r-- | ports/stm/common-hal/canio/CAN.h | 58 | ||||
| -rw-r--r-- | ports/stm/common-hal/canio/Listener.c | 315 | ||||
| -rw-r--r-- | ports/stm/common-hal/canio/Listener.h | 39 | ||||
| -rw-r--r-- | ports/stm/common-hal/canio/__init__.c | 25 | ||||
| -rw-r--r-- | ports/stm/common-hal/canio/__init__.h | 27 | ||||
| -rw-r--r-- | ports/stm/common-hal/rgbmatrix/RGBMatrix.c | 1 | ||||
| -rw-r--r-- | ports/stm/hal_conf/stm32_hal_conf.h | 2 | ||||
| -rw-r--r-- | ports/stm/mpconfigport.h | 1 | ||||
| -rw-r--r-- | ports/stm/mpconfigport.mk | 1 | ||||
| -rw-r--r-- | ports/stm/peripherals/stm32f4/stm32f405xx/periph.c | 23 | ||||
| -rw-r--r-- | ports/stm/peripherals/stm32f4/stm32f405xx/periph.h | 6 | ||||
| -rw-r--r-- | ports/stm/peripherals/timers.c | 4 | ||||
| -rw-r--r-- | ports/stm/supervisor/port.c | 24 |
20 files changed, 838 insertions, 25 deletions
diff --git a/ports/stm/Makefile b/ports/stm/Makefile index dccc749ed..b9426e07e 100755 --- a/ports/stm/Makefile +++ b/ports/stm/Makefile @@ -102,7 +102,8 @@ CFLAGS += $(INC) -Werror -Wall -std=gnu11 -fshort-enums $(BASE_CFLAGS) $(C_DEFS) # Undo some warnings. # STM32 HAL uses undefined preprocessor variables, shadowed variables, casts that change alignment reqs -CFLAGS += -Wno-undef -Wno-shadow -Wno-cast-align +# You can add your own temporary suppression by setting ADD_CFLAGS in the make command +CFLAGS += -Wno-undef -Wno-shadow -Wno-cast-align $(ADD_CFLAGS) CFLAGS += -mthumb -mabi=aapcs-linux @@ -197,6 +198,10 @@ SRC_STM32 = $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES_LOWER)xx_,\ ll_utils.c \ ) +ifeq ($(CIRCUITPY_CANIO),1) +SRC_STM32 += $(HAL_DIR)/Src/stm32$(MCU_SERIES_LOWER)xx_hal_can.c +endif + # Need this to avoid UART linker problems. TODO: rewrite to use registered callbacks. # Does not exist for F4 and lower ifeq ($(MCU_VARIANT),$(filter $(MCU_VARIANT),STM32F765xx STM32F767xx STM32F769xx STM32H743xx)) diff --git a/ports/stm/boards/feather_stm32f405_express/pins.c b/ports/stm/boards/feather_stm32f405_express/pins.c index 571076c33..e20ad5028 100644 --- a/ports/stm/boards/feather_stm32f405_express/pins.c +++ b/ports/stm/boards/feather_stm32f405_express/pins.c @@ -47,5 +47,8 @@ STATIC const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_SDIO_CLOCK), MP_ROM_PTR(&pin_PC12) }, { MP_ROM_QSTR(MP_QSTR_SDIO_COMMAND), MP_ROM_PTR(&pin_PD02) }, { MP_ROM_QSTR(MP_QSTR_SDIO_DATA), MP_ROM_PTR(&sdio_data_tuple) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_RX), MP_ROM_PTR(&pin_PB09) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_TX), MP_ROM_PTR(&pin_PB08) }, }; MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/stm/boards/meowbit_v121/board.c b/ports/stm/boards/meowbit_v121/board.c index b74f13516..f67b4a49d 100644 --- a/ports/stm/boards/meowbit_v121/board.c +++ b/ports/stm/boards/meowbit_v121/board.c @@ -111,7 +111,8 @@ void board_init(void) { false, // data_as_commands true, // auto_refresh 60, // native_frames_per_second - true); // backlight_on_high + true, // backlight_on_high + false); // SH1107_addressing } bool board_requests_safe_mode(void) { diff --git a/ports/stm/boards/meowbit_v121/mpconfigboard.mk b/ports/stm/boards/meowbit_v121/mpconfigboard.mk index 16ffb2022..c416700e3 100644 --- a/ports/stm/boards/meowbit_v121/mpconfigboard.mk +++ b/ports/stm/boards/meowbit_v121/mpconfigboard.mk @@ -1,5 +1,5 @@ USB_VID = 0x239A -USB_PID = 0x805A +USB_PID = 0x80CF USB_PRODUCT = "Meowbit" USB_MANUFACTURER = "Kittenbot" diff --git a/ports/stm/boards/thunderpack/mpconfigboard.mk b/ports/stm/boards/thunderpack/mpconfigboard.mk index afd0ffd75..d303582e0 100644 --- a/ports/stm/boards/thunderpack/mpconfigboard.mk +++ b/ports/stm/boards/thunderpack/mpconfigboard.mk @@ -1,5 +1,5 @@ USB_VID = 0x239A -USB_PID = 0x806A +USB_PID = 0x8069 USB_PRODUCT = "Thunderpack STM32F411" USB_MANUFACTURER = "Jeremy Gillick" @@ -7,6 +7,7 @@ INTERNAL_FLASH_FILESYSTEM = 1 LONGINT_IMPL = NONE CIRCUITPY_NVM = 1 +CIRCUITPY_BLEIO_HCI = 0 MCU_SERIES = F4 MCU_VARIANT = STM32F411xE diff --git a/ports/stm/common-hal/busio/SPI.c b/ports/stm/common-hal/busio/SPI.c index 0354d64ad..29fbdee56 100644 --- a/ports/stm/common-hal/busio/SPI.c +++ b/ports/stm/common-hal/busio/SPI.c @@ -25,6 +25,7 @@ * THE SOFTWARE. */ #include <stdbool.h> +#include <string.h> #include "shared-bindings/busio/SPI.h" #include "py/mperrno.h" @@ -126,15 +127,6 @@ STATIC int check_pins(busio_spi_obj_t *self, uint8_t mosi_len = MP_ARRAY_SIZE(mcu_spi_mosi_list); uint8_t miso_len = MP_ARRAY_SIZE(mcu_spi_miso_list); - //SCK is not optional. MOSI and MISO are - if (!sck) { - mp_raise_ValueError(translate("Must provide SCK pin")); - } - - if (!miso && !mosi) { - mp_raise_ValueError(translate("Must provide MISO or MOSI pin")); - } - // Loop over each possibility for SCK. Check whether MISO and/or MOSI can be used on the same peripheral for (uint i = 0; i < sck_len; i++) { const mcu_periph_obj_t *mcu_spi_sck = &mcu_spi_sck_list[i]; @@ -340,7 +332,7 @@ bool common_hal_busio_spi_write(busio_spi_obj_t *self, if (self->mosi == NULL) { mp_raise_ValueError(translate("No MOSI Pin")); } - HAL_StatusTypeDef result = HAL_SPI_Transmit (&self->handle, (uint8_t *)data, (uint16_t)len, HAL_MAX_DELAY); + HAL_StatusTypeDef result = HAL_SPI_Transmit(&self->handle, (uint8_t *)data, (uint16_t)len, HAL_MAX_DELAY); return result == HAL_OK; } @@ -349,7 +341,13 @@ bool common_hal_busio_spi_read(busio_spi_obj_t *self, if (self->miso == NULL) { mp_raise_ValueError(translate("No MISO Pin")); } - HAL_StatusTypeDef result = HAL_SPI_Receive (&self->handle, data, (uint16_t)len, HAL_MAX_DELAY); + HAL_StatusTypeDef result = HAL_OK; + if (self->mosi == NULL) { + result = HAL_SPI_Receive(&self->handle, data, (uint16_t)len, HAL_MAX_DELAY); + } else { + memset(data, write_value, len); + result = HAL_SPI_TransmitReceive(&self->handle, data, data, (uint16_t)len, HAL_MAX_DELAY); + } return result == HAL_OK; } @@ -358,7 +356,7 @@ bool common_hal_busio_spi_transfer(busio_spi_obj_t *self, if (self->miso == NULL || self->mosi == NULL) { mp_raise_ValueError(translate("Missing MISO or MOSI Pin")); } - HAL_StatusTypeDef result = HAL_SPI_TransmitReceive (&self->handle, + HAL_StatusTypeDef result = HAL_SPI_TransmitReceive(&self->handle, (uint8_t *) data_out, data_in, (uint16_t)len,HAL_MAX_DELAY); return result == HAL_OK; } diff --git a/ports/stm/common-hal/canio/CAN.c b/ports/stm/common-hal/canio/CAN.c new file mode 100644 index 000000000..52d5cad1f --- /dev/null +++ b/ports/stm/common-hal/canio/CAN.c @@ -0,0 +1,297 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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 <string.h> + +#include "py/runtime.h" +#include "py/mperrno.h" + +#include "common-hal/canio/CAN.h" +#include "peripherals/periph.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" +#include "supervisor/port.h" + +STATIC bool reserved_can[MP_ARRAY_SIZE(mcu_can_banks)]; + +STATIC const mcu_periph_obj_t *find_pin_function(const mcu_periph_obj_t *table, size_t sz, const mcu_pin_obj_t *pin, int periph_index) { + for(size_t i = 0; i<sz; i++, table++) { + if (periph_index != -1 && periph_index != table->periph_index) { + continue; + } + if (pin == table->pin) { + return table; + } + } + return NULL; +} + + +void common_hal_canio_can_construct(canio_can_obj_t *self, mcu_pin_obj_t *tx, mcu_pin_obj_t *rx, int baudrate, bool loopback, bool silent) +{ +#define DIV_ROUND(a, b) (((a) + (b)/2) / (b)) +#define DIV_ROUND_UP(a, b) (((a) + (b) - 1) / (b)) + + const uint8_t can_tx_len = MP_ARRAY_SIZE(mcu_can_tx_list); + const uint8_t can_rx_len = MP_ARRAY_SIZE(mcu_can_rx_list); + + const mcu_periph_obj_t *mcu_tx = find_pin_function(mcu_can_tx_list, can_tx_len, tx, -1); + if (!mcu_tx) { + mp_raise_ValueError_varg(translate("Invalid %q pin selection"), MP_QSTR_tx); + } + int periph_index = mcu_tx->periph_index; + + const mcu_periph_obj_t *mcu_rx = find_pin_function(mcu_can_rx_list, can_rx_len, rx, periph_index); + if (!mcu_rx) { + mp_raise_ValueError_varg(translate("Invalid %q pin selection"), MP_QSTR_rx); + } + + if (reserved_can[periph_index]) { + mp_raise_ValueError(translate("Hardware busy, try alternative pins")); + } + + const uint32_t can_frequency = 42000000; + uint32_t clocks_per_bit = DIV_ROUND(can_frequency, baudrate); + uint32_t clocks_to_sample = DIV_ROUND(clocks_per_bit * 7, 8); + uint32_t clocks_after_sample = clocks_per_bit - clocks_to_sample; + uint32_t divisor = MAX(DIV_ROUND_UP(clocks_to_sample, 16), DIV_ROUND_UP(clocks_after_sample, 8)); + const uint32_t sjw = 3; + + uint32_t tq_per_bit = DIV_ROUND(clocks_per_bit, divisor); + uint32_t tq_to_sample = DIV_ROUND(clocks_to_sample, divisor); + uint32_t tq_after_sample = tq_per_bit - tq_to_sample; + + if (divisor > 1023) { + mp_raise_OSError(MP_EINVAL); // baudrate cannot be attained (16kHz or something is lower bound, should never happen) + } + + { + GPIO_InitTypeDef GPIO_InitStruct = { + .Pin = pin_mask(tx->number), + .Speed = GPIO_SPEED_FREQ_VERY_HIGH, + .Mode = GPIO_MODE_AF_PP, + .Pull = GPIO_PULLUP, + .Alternate = mcu_tx->altfn_index, + }; + HAL_GPIO_Init(pin_port(tx->port), &GPIO_InitStruct); + + GPIO_InitStruct.Pin = pin_mask(rx->number); + GPIO_InitStruct.Alternate = mcu_rx->altfn_index; + HAL_GPIO_Init(pin_port(rx->port), &GPIO_InitStruct); + } + + CAN_TypeDef *hw = mcu_can_banks[periph_index - 1]; + + // CAN2 shares resources with CAN1. So we always enable CAN1, then split + // the filter banks equally between them. + + __HAL_RCC_CAN1_CLK_ENABLE(); + + if(hw == CAN2) { + __HAL_RCC_CAN2_CLK_ENABLE(); + self->start_filter_bank = 14; + self->end_filter_bank = 28; + self->filter_hw = CAN1; + } else { + self->start_filter_bank = 0; + self->end_filter_bank = 14; + self->filter_hw = hw; + } + + CAN_InitTypeDef init = { + .AutoRetransmission = ENABLE, + .AutoBusOff = ENABLE, + .Prescaler = divisor, + .Mode = (loopback ? CAN_MODE_LOOPBACK : 0) | (silent ? CAN_MODE_SILENT_LOOPBACK : 0), + .SyncJumpWidth = (sjw-1) << CAN_BTR_SJW_Pos, + .TimeSeg1 = (tq_to_sample-2) << CAN_BTR_TS1_Pos, + .TimeSeg2 = (tq_after_sample-1) << CAN_BTR_TS2_Pos, + }; + + self->periph_index = periph_index; + self->silent = silent; + self->loopback = loopback; + self->baudrate = baudrate; + + self->handle.Instance = hw; + self->handle.Init = init; + self->handle.State = HAL_CAN_STATE_RESET; + + HAL_CAN_Init(&self->handle); + + // Set the filter split as 14:14 + // COULDDO(@jepler): Dynamically allocate filter banks between CAN1/2 + self->filter_hw->FMR |= CAN_FMR_FINIT; + self->filter_hw->FMR = CAN_FMR_FINIT | (14 << CAN_FMR_CAN2SB_Pos); + + // Clear every filter enable bit for this can HW + uint32_t fa1r = self->filter_hw->FA1R; + for (int i = self->start_filter_bank; i<self->end_filter_bank; i++) { + fa1r &= ~(1 << i); + } + self->filter_hw->FA1R = fa1r; + CLEAR_BIT(self->filter_hw->FMR, CAN_FMR_FINIT); + + HAL_CAN_Start(&self->handle); + + reserved_can[periph_index] = true; +} + +bool common_hal_canio_can_loopback_get(canio_can_obj_t *self) +{ + return self->loopback; +} + +int common_hal_canio_can_baudrate_get(canio_can_obj_t *self) +{ + return self->baudrate; +} + +int common_hal_canio_can_transmit_error_count_get(canio_can_obj_t *self) +{ + return (self->handle.Instance->ESR & CAN_ESR_TEC) >> CAN_ESR_TEC_Pos; +} + +int common_hal_canio_can_receive_error_count_get(canio_can_obj_t *self) +{ + return (self->handle.Instance->ESR & CAN_ESR_REC) >> CAN_ESR_REC_Pos; +} + +canio_bus_state_t common_hal_canio_can_state_get(canio_can_obj_t *self) { + uint32_t esr = self->handle.Instance->ESR; + if (READ_BIT(esr, CAN_ESR_BOFF)) { + return BUS_STATE_OFF; + } + if (READ_BIT(esr, CAN_ESR_EPVF)) { + return BUS_STATE_ERROR_PASSIVE; + } + if (READ_BIT(esr, CAN_ESR_EWGF)) { + return BUS_STATE_ERROR_WARNING; + } + return BUS_STATE_ERROR_ACTIVE; +} + +void common_hal_canio_can_restart(canio_can_obj_t *self) { + if (!common_hal_canio_can_auto_restart_get(self)) { + // "If ABOM is cleared, the software must initiate the recovering + // sequence by requesting bxCAN to enter and to leave initialization + // mode." + self->handle.Instance->MCR |= CAN_MCR_INRQ; + while ((self->handle.Instance->MSR & CAN_MSR_INAK) == 0) { + } + self->handle.Instance->MCR &= ~CAN_MCR_INRQ; + while ((self->handle.Instance->MSR & CAN_MSR_INAK)) { + } + } +} + +bool common_hal_canio_can_auto_restart_get(canio_can_obj_t *self) { + return READ_BIT(self->handle.Instance->MCR, CAN_MCR_ABOM); +} + +void common_hal_canio_can_auto_restart_set(canio_can_obj_t *self, bool value) { + if(value) { + SET_BIT(self->handle.Instance->MCR, CAN_MCR_ABOM); + } else { + CLEAR_BIT(self->handle.Instance->MCR, CAN_MCR_ABOM); + } +} + +void common_hal_canio_can_send(canio_can_obj_t *self, mp_obj_t message_in) +{ + canio_message_obj_t *message = message_in; + uint32_t mailbox; + bool rtr = message->base.type == &canio_remote_transmission_request_type; + CAN_TxHeaderTypeDef header = { + .StdId = message->id, + .ExtId = message->id, + .IDE = message->extended ? CAN_ID_EXT : CAN_ID_STD, + .RTR = rtr ? CAN_RTR_REMOTE : CAN_RTR_DATA, + .DLC = message->size, + }; + uint32_t free_level = HAL_CAN_GetTxMailboxesFreeLevel(&self->handle); + if (free_level == 0) { + // There's no free Tx mailbox. We need to cancel some message without + // transmitting it, because once the bus returns to active state it's + // preferable to transmit the newest messages instead of older messages. + // + // We don't strictly guarantee that we abort the oldest Tx request, + // rather we just abort a different index each time. This permits us + // to just track a single cancel index + HAL_CAN_AbortTxRequest(&self->handle, 1 << (self->cancel_mailbox)); + self->cancel_mailbox = (self->cancel_mailbox + 1) % 3; + // The abort request may not have completed immediately, so wait for + // the Tx mailbox to become free + do { + free_level = HAL_CAN_GetTxMailboxesFreeLevel(&self->handle); + } while (!free_level); + } + HAL_StatusTypeDef status = HAL_CAN_AddTxMessage(&self->handle, &header, message->data, &mailbox); + if (status != HAL_OK) { + // this is a "shouldn't happen" condition. we don't throw because the + // contract of send() is that it queues the packet to be sent if + // possible and does not signal success or failure to actually send. + return; + } + + // wait 8ms (hard coded for now) for TX to occur + uint64_t deadline = port_get_raw_ticks(NULL) + 8; + while (port_get_raw_ticks(NULL) < deadline && HAL_CAN_IsTxMessagePending(&self->handle, 1 << mailbox)) { + RUN_BACKGROUND_TASKS; + } +} + +bool common_hal_canio_can_silent_get(canio_can_obj_t *self) { + return self->silent; +} + +bool common_hal_canio_can_deinited(canio_can_obj_t *self) { + return !self->handle.Instance; +} + +void common_hal_canio_can_check_for_deinit(canio_can_obj_t *self) { + if (common_hal_canio_can_deinited(self)) { + raise_deinited_error(); + } +} + +void common_hal_canio_can_deinit(canio_can_obj_t *self) +{ + if (self->handle.Instance) { + SET_BIT(self->handle.Instance->MCR, CAN_MCR_RESET); + while (READ_BIT(self->handle.Instance->MCR, CAN_MCR_RESET)) { + } + reserved_can[self->periph_index] = 0; + } + self->handle.Instance = NULL; +} + +void common_hal_canio_reset(void) { + for (size_t i=0; i<MP_ARRAY_SIZE(mcu_can_banks); i++) { + SET_BIT(mcu_can_banks[i]->MCR, CAN_MCR_RESET); + reserved_can[i] = 0; + } +} diff --git a/ports/stm/common-hal/canio/CAN.h b/ports/stm/common-hal/canio/CAN.h new file mode 100644 index 000000000..3157d0a03 --- /dev/null +++ b/ports/stm/common-hal/canio/CAN.h @@ -0,0 +1,58 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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. + */ + +#pragma once + +#include "py/obj.h" +#include "shared-bindings/canio/__init__.h" +#include "shared-bindings/canio/CAN.h" +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/canio/__init__.h" +#include "shared-module/canio/Message.h" + +#include "stm32f4xx_hal.h" +#include "stm32f4xx_hal_can.h" + +#define FILTER_BANK_COUNT (28) + +typedef struct canio_can_obj { + mp_obj_base_t base; + CAN_HandleTypeDef handle; + CAN_TypeDef *filter_hw; + int baudrate; + const mcu_pin_obj_t *rx_pin; + const mcu_pin_obj_t *tx_pin; + bool loopback:1; + bool silent:1; + bool auto_restart:1; + bool fifo0_in_use:1; + bool fifo1_in_use:1; + uint8_t periph_index:2; + uint8_t cancel_mailbox; + uint8_t start_filter_bank; + uint8_t end_filter_bank; + long filter_in_use; // bitmask for the 28 filter banks +} canio_can_obj_t; diff --git a/ports/stm/common-hal/canio/Listener.c b/ports/stm/common-hal/canio/Listener.c new file mode 100644 index 000000000..09456d39d --- /dev/null +++ b/ports/stm/common-hal/canio/Listener.c @@ -0,0 +1,315 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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 <math.h> +#include <string.h> + +#include "py/obj.h" +#include "py/runtime.h" + +#include "common-hal/canio/__init__.h" +#include "common-hal/canio/Listener.h" +#include "shared-bindings/canio/Listener.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/tick.h" +#include "supervisor/shared/safe_mode.h" + +STATIC void allow_filter_change(canio_can_obj_t *can) { + can->filter_hw->FMR |= CAN_FMR_FINIT; +} + +STATIC void prevent_filter_change(canio_can_obj_t *can) { + can->filter_hw->FMR &= ~CAN_FMR_FINIT; +} + +STATIC bool filter_in_use(canio_can_obj_t *can, int idx) { + return can->filter_hw->FA1R & (1<<idx); +} + +// One filter bank can hold: +// * one extended mask +// * two extended ids +// * two standard masks +// * four extended ids +// However, stm needs two filters to permit RTR and non-RTR messages +// so we ONLY use mask-type filter banks +STATIC size_t num_filters_needed(size_t nmatch, canio_match_obj_t **matches) { + if (nmatch == 0) { + return 1; + } + size_t num_extended_mask = 0; + size_t num_standard_mask = 1; + for(size_t i=0; i<nmatch; i++) { + if (matches[i]->extended) { + num_extended_mask += 1; + } else { + num_standard_mask += 1; + } + } + return num_extended_mask + num_standard_mask/2; +} + +STATIC size_t num_filters_available(canio_can_obj_t *can) { + size_t available = 0; + for(size_t i = can->start_filter_bank; i < can->end_filter_bank; i++) { + if (!filter_in_use(can, i)) { + available++; + } + } + return available; +} + +STATIC void clear_filters(canio_listener_obj_t *self) { + canio_can_obj_t *can = self->can; + + allow_filter_change(can); + uint32_t fa1r = can->filter_hw->FA1R; + for(size_t i = can->start_filter_bank; i < can->end_filter_bank; i++) { + if (((can->filter_hw->FFA1R >> i) & 1) == self->fifo_idx) { + fa1r &= ~(1<<i); + } + } + can->filter_hw->FA1R = fa1r; + prevent_filter_change(can); +} + +STATIC int next_filter(canio_can_obj_t *can) { + uint32_t fa1r = can->filter_hw->FA1R; + for(size_t i = can->start_filter_bank; i < can->end_filter_bank; i++) { + if (!(fa1r & (1<<i))) { + return i; + } + } + reset_into_safe_mode(MICROPY_FATAL_ERROR); + return -1; +} + +// IDE = "extended ID" flag of packet header. We always add this bit to the +// mask because a match is always for just one kind of address length +#define FILTER16_IDE (1<<3) +#define FILTER32_IDE (1<<2) + +STATIC void install_standard_filter(canio_listener_obj_t *self, canio_match_obj_t *match1, canio_match_obj_t *match2) { + int bank = next_filter(self->can); + + // filter is already deactivated, so we skip deactivating it here + // CLEAR_BIT(self->can->filter_hw->FA1R, bank); + + self->can->filter_hw->sFilterRegister[bank].FR1 = + (((match1->id & 0x7ff) << 5)) | + (((match1->mask & 0x7ff) << 5 | FILTER16_IDE)) << 16; + self->can->filter_hw->sFilterRegister[bank].FR2 = + (((match2->id & 0x7ff) << 5)) | + (((match2->mask & 0x7ff) << 5 | FILTER16_IDE)) << 16; + + // filter mode: 0 = mask + // (this bit should be clear already, we never set it; but just in case) + CLEAR_BIT(self->can->filter_hw->FM1R, 1 << bank); + // filter scale: 0 = 16 bits + CLEAR_BIT(self->can->filter_hw->FS1R, 1 << bank); + // fifo assignment: 1 = FIFO 1 + if (self->fifo_idx) { + SET_BIT(self->can->filter_hw->FFA1R, 1 << bank); + } else { + CLEAR_BIT(self->can->filter_hw->FFA1R, 1 << bank); + } + + // filter activation: 1 = enabled + SET_BIT(self->can->filter_hw->FA1R, 1 << bank); +} + +STATIC void install_extended_filter(canio_listener_obj_t *self, canio_match_obj_t *match) { + int bank = next_filter(self->can); + + // filter is already deactivated, so we skip deactivating it here + // CLEAR_BIT(self->can->filter_hw->FA1R, bank); + + self->can->filter_hw->sFilterRegister[bank].FR1 = + ((match->id << 3) | FILTER32_IDE); + self->can->filter_hw->sFilterRegister[bank].FR2 = + ((match->mask << 3) | FILTER32_IDE); + + // filter mode: 0 = mask + // (this bit should be clear already, we never set it; but just in case) + CLEAR_BIT(self->can->filter_hw->FM1R, 1 << bank); + // filter scale: 1 = 32 bits + SET_BIT(self->can->filter_hw->FS1R, 1 << bank); + // fifo assignment: 1 = FIFO 1 + if (self->fifo_idx) { + SET_BIT(self->can->filter_hw->FFA1R, 1 << bank); + } else { + CLEAR_BIT(self->can->filter_hw->FFA1R, 1 << bank); + } + + // filter activation: 1 = enabled + SET_BIT(self->can->filter_hw->FA1R, 1 << bank); +} + +STATIC void install_all_match_filter(canio_listener_obj_t *self) { + int bank = next_filter(self->can); + + // filter is already deactivated, so we skip deactivating it here + // CLEAR_BIT(self->can->filter_hw->FA1R, bank); + + self->can->filter_hw->sFilterRegister[bank].FR1 = 0; + self->can->filter_hw->sFilterRegister[bank].FR2 = 0; + + // filter mode: 0 = mask + // (this bit should be clear already, we never set it; but just in case) + CLEAR_BIT(self->can->filter_hw->FM1R, bank); + // filter scale: 1 = 32 bits + SET_BIT(self->can->filter_hw->FS1R, bank); + // fifo assignment: 1 = FIFO 1 + if (self->fifo_idx) { + SET_BIT(self->can->filter_hw->FFA1R, bank); + } else { + CLEAR_BIT(self->can->filter_hw->FFA1R, bank); + } + + // filter activation: 1 = enabled + SET_BIT(self->can->filter_hw->FA1R, (1 << bank)); +} + + +#define NO_ADDRESS (-1) +void set_filters(canio_listener_obj_t *self, size_t nmatch, canio_match_obj_t **matches) { + allow_filter_change(self->can); + if (!nmatch) { + install_all_match_filter(self); + } else { + canio_match_obj_t *first_match = NULL; + for(size_t i = 0; i<nmatch; i++) { + if(matches[i]->extended) { + install_extended_filter(self, matches[i]); + } else { + if (first_match) { + install_standard_filter(self, first_match, matches[i]); + first_match = NULL; + } else { + first_match = matches[i]; + } + } + } + if (first_match) { + install_standard_filter(self, first_match, first_match); + } + } + prevent_filter_change(self->can); +} + + +void common_hal_canio_listener_construct(canio_listener_obj_t *self, canio_can_obj_t *can, size_t nmatch, canio_match_obj_t **matches, float timeout) { + if (!can->fifo0_in_use) { + self->fifo_idx = 0; + self->rfr = &can->handle.Instance->RF0R; + can->fifo0_in_use = true; + } else if (!can->fifo1_in_use) { + self->fifo_idx = 1; + self->rfr = &can->handle.Instance->RF1R; + can->fifo1_in_use = true; + } else { + mp_raise_ValueError(translate("All RX FIFOs in use")); + } + + if (num_filters_needed(nmatch, matches) > num_filters_available(can)) { + mp_raise_ValueError(translate("Filters too complex")); + } + + // Nothing can fail now so it's safe to assign self->can + self->can = can; + + self->mailbox = &can->handle.Instance->sFIFOMailBox[self->fifo_idx]; + set_filters(self, nmatch, matches); + common_hal_canio_listener_set_timeout(self, timeout); +} + +void common_hal_canio_listener_set_timeout(canio_listener_obj_t *self, float timeout) { + self->timeout_ms = (int)MICROPY_FLOAT_C_FUN(ceil)(timeout * 1000); +} + +float common_hal_canio_listener_get_timeout(canio_listener_obj_t *self) { + return self->timeout_ms / 1000.0f; +} + +void common_hal_canio_listener_check_for_deinit(canio_listener_obj_t *self) { + if (!self->can) { + raise_deinited_error(); + } + common_hal_canio_can_check_for_deinit(self->can); +} + +int common_hal_canio_listener_in_waiting(canio_listener_obj_t *self) { + return *(self->rfr) & CAN_RF0R_FMP0; +} + +mp_obj_t common_hal_canio_listener_receive(canio_listener_obj_t *self) { + if (!common_hal_canio_listener_in_waiting(self)) { + uint64_t deadline = supervisor_ticks_ms64() + self->timeout_ms; + do { + if (supervisor_ticks_ms64() > deadline) { + return NULL; + } + } while (!common_hal_canio_listener_in_waiting(self)); + } + + uint32_t rir = self->mailbox->RIR; + uint32_t rdtr = self->mailbox->RDTR; + + bool rtr = rir & CAN_RI0R_RTR; + canio_message_obj_t *message = m_new_obj(canio_message_obj_t); + message->base.type = rtr ? &canio_remote_transmission_request_type : &canio_message_type; + message->extended = rir & CAN_RI0R_IDE; + if (message->extended) { + message->id = rir >> 3; + } else { + message->id = rir >> 21; + } + message->size = rdtr & CAN_RDT0R_DLC; + if (!rtr) { + uint32_t payload[] = { self->mailbox->RDLR, self->mailbox->RDHR }; + MP_STATIC_ASSERT(sizeof(payload) == sizeof(message->data)); + memcpy(message->data, payload, sizeof(payload)); + } + // Release the mailbox + SET_BIT(*self->rfr, CAN_RF0R_RFOM0); + return message; +} + +void common_hal_canio_listener_deinit(canio_listener_obj_t *self) { + if (self->can) { + clear_filters(self); + if (self->fifo_idx == 0) { + self->can->fifo0_in_use = false; + } + if (self->fifo_idx == 1) { + self->can->fifo1_in_use = false; + } + } + self->fifo_idx = -1; + self->can = NULL; + self->mailbox = NULL; + self->rfr = NULL; +} diff --git a/ports/stm/common-hal/canio/Listener.h b/ports/stm/common-hal/canio/Listener.h new file mode 100644 index 000000000..2d0e302e9 --- /dev/null +++ b/ports/stm/common-hal/canio/Listener.h @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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. + */ + +#pragma once + +#include "common-hal/canio/CAN.h" +#include "shared-module/canio/Match.h" + +typedef struct canio_listener_obj { + mp_obj_base_t base; + canio_can_obj_t *can; + CAN_FIFOMailBox_TypeDef *mailbox; + __IO uint32_t *rfr; + uint32_t timeout_ms; + uint8_t fifo_idx; +} canio_listener_obj_t; diff --git a/ports/stm/common-hal/canio/__init__.c b/ports/stm/common-hal/canio/__init__.c new file mode 100644 index 000000000..7932bfc2d --- /dev/null +++ b/ports/stm/common-hal/canio/__init__.c @@ -0,0 +1,25 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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. + */ diff --git a/ports/stm/common-hal/canio/__init__.h b/ports/stm/common-hal/canio/__init__.h new file mode 100644 index 000000000..20b6638cd --- /dev/null +++ b/ports/stm/common-hal/canio/__init__.h @@ -0,0 +1,27 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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. + */ + +#pragma once diff --git a/ports/stm/common-hal/rgbmatrix/RGBMatrix.c b/ports/stm/common-hal/rgbmatrix/RGBMatrix.c index 3b42631ad..2826cf53e 100644 --- a/ports/stm/common-hal/rgbmatrix/RGBMatrix.c +++ b/ports/stm/common-hal/rgbmatrix/RGBMatrix.c @@ -36,6 +36,7 @@ extern void _PM_IRQ_HANDLER(void); void *common_hal_rgbmatrix_timer_allocate() { TIM_TypeDef * timer = stm_peripherals_find_timer(); stm_peripherals_timer_reserve(timer); + stm_peripherals_timer_never_reset(timer); return timer; } diff --git a/ports/stm/hal_conf/stm32_hal_conf.h b/ports/stm/hal_conf/stm32_hal_conf.h index c91be86fd..8000e7e83 100644 --- a/ports/stm/hal_conf/stm32_hal_conf.h +++ b/ports/stm/hal_conf/stm32_hal_conf.h @@ -11,7 +11,7 @@ */ #define HAL_MODULE_ENABLED #define HAL_ADC_MODULE_ENABLED -// #define HAL_CAN_MODULE_ENABLED +#define HAL_CAN_MODULE_ENABLED // #define HAL_CEC_MODULE_ENABLED // #define HAL_COMP_MODULE_ENABLED #define HAL_CORTEX_MODULE_ENABLED diff --git a/ports/stm/mpconfigport.h b/ports/stm/mpconfigport.h index e054ffbb5..9489b4765 100644 --- a/ports/stm/mpconfigport.h +++ b/ports/stm/mpconfigport.h @@ -30,7 +30,6 @@ #include <stdint.h> -#define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1) #define MICROPY_PY_FUNCTION_ATTRS (1) #define MICROPY_PY_IO (1) #define MICROPY_PY_REVERSE_SPECIAL_METHODS (1) diff --git a/ports/stm/mpconfigport.mk b/ports/stm/mpconfigport.mk index b827aa48b..bcecaa517 100644 --- a/ports/stm/mpconfigport.mk +++ b/ports/stm/mpconfigport.mk @@ -4,6 +4,7 @@ INTERNAL_LIBM ?= 1 USB_SERIAL_NUMBER_LENGTH ?= 24 ifeq ($(MCU_VARIANT),STM32F405xx) + CIRCUITPY_CANIO = 1 CIRCUITPY_FRAMEBUFFERIO ?= 1 CIRCUITPY_RGBMATRIX ?= 1 CIRCUITPY_SDIOIO ?= 1 diff --git a/ports/stm/peripherals/stm32f4/stm32f405xx/periph.c b/ports/stm/peripherals/stm32f4/stm32f405xx/periph.c index c58234671..2f9accbf1 100644 --- a/ports/stm/peripherals/stm32f4/stm32f405xx/periph.c +++ b/ports/stm/peripherals/stm32f4/stm32f405xx/periph.c @@ -216,3 +216,26 @@ const mcu_periph_obj_t mcu_sdio_data2_list[1] = { const mcu_periph_obj_t mcu_sdio_data3_list[1] = { PERIPH(1, 12, &pin_PC11), }; + +//CAN +CAN_TypeDef * mcu_can_banks[2] = {CAN1, CAN2}; + +const mcu_periph_obj_t mcu_can_tx_list[6] = { + PERIPH(1, 9, &pin_PA11), + PERIPH(1, 9, &pin_PB08), + PERIPH(1, 9, &pin_PD00), + PERIPH(1, 9, &pin_PI09), + + PERIPH(2, 9, &pin_PB12), + PERIPH(2, 9, &pin_PB05), +}; + +const mcu_periph_obj_t mcu_can_rx_list[6] = { + PERIPH(1, 9, &pin_PA12), + PERIPH(1, 9, &pin_PB09), + PERIPH(1, 9, &pin_PD01), + PERIPH(1, 9, &pin_PH13), + + PERIPH(2, 9, &pin_PB13), + PERIPH(2, 9, &pin_PB06), +}; diff --git a/ports/stm/peripherals/stm32f4/stm32f405xx/periph.h b/ports/stm/peripherals/stm32f4/stm32f405xx/periph.h index 5b64fe10c..03198af55 100644 --- a/ports/stm/peripherals/stm32f4/stm32f405xx/periph.h +++ b/ports/stm/peripherals/stm32f4/stm32f405xx/periph.h @@ -71,5 +71,11 @@ extern const mcu_periph_obj_t mcu_sdio_data1_list[1]; extern const mcu_periph_obj_t mcu_sdio_data2_list[1]; extern const mcu_periph_obj_t mcu_sdio_data3_list[1]; +// CAN +extern CAN_TypeDef * mcu_can_banks[2]; + +extern const mcu_periph_obj_t mcu_can_tx_list[6]; +extern const mcu_periph_obj_t mcu_can_rx_list[6]; + #endif // MICROPY_INCLUDED_STM32_PERIPHERALS_STM32F405XX_PERIPH_H diff --git a/ports/stm/peripherals/timers.c b/ports/stm/peripherals/timers.c index baa81d2b5..7bc5ea495 100644 --- a/ports/stm/peripherals/timers.c +++ b/ports/stm/peripherals/timers.c @@ -195,9 +195,7 @@ TIM_TypeDef * stm_peripherals_find_timer(void) { // If no results are found, no unclaimed pins with this timer are in this package, // and it is safe to pick if (timer_in_package == false && mcu_tim_banks[i] != NULL) { - // DEBUG: print the timer return mcu_tim_banks[i]; - mp_printf(&mp_plat_print, "Timer: %d\n",i); } } //TODO: secondary search for timers outside the pins in the board profile @@ -205,8 +203,6 @@ TIM_TypeDef * stm_peripherals_find_timer(void) { // Work backwards - higher index timers have fewer pin allocations for (size_t i = (MP_ARRAY_SIZE(mcu_tim_banks) - 1); i >= 0; i--) { if ((!stm_timer_reserved[i]) && (mcu_tim_banks[i] != NULL)) { - // DEBUG: print the timer - mp_printf(&mp_plat_print, "Timer: %d\n",i); return mcu_tim_banks[i]; } } diff --git a/ports/stm/supervisor/port.c b/ports/stm/supervisor/port.c index e21105001..a8aab00ff 100644 --- a/ports/stm/supervisor/port.c +++ b/ports/stm/supervisor/port.c @@ -56,6 +56,8 @@ #include STM32_HAL_H +void NVIC_SystemReset(void) NORETURN; + #if (CPY_STM32H7) || (CPY_STM32F7) // Device memories must be accessed in order. @@ -188,6 +190,7 @@ safe_mode_t port_init(void) { _hrtc.Init.OutPut = RTC_OUTPUT_DISABLE; HAL_RTC_Init(&_hrtc); + HAL_RTCEx_EnableBypassShadow(&_hrtc); HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn); // Turn off SysTick @@ -247,7 +250,7 @@ void reset_port(void) { } void reset_to_bootloader(void) { - + NVIC_SystemReset(); } void reset_cpu(void) { @@ -326,9 +329,23 @@ volatile uint32_t cached_date = 0; volatile uint32_t seconds_to_minute = 0; volatile uint32_t cached_hours_minutes = 0; uint64_t port_get_raw_ticks(uint8_t* subticks) { - uint32_t subseconds = rtc_clock_frequency - (uint32_t)(RTC->SSR); + // Disable IRQs to ensure we read all of the RTC registers as close in time as possible. Read + // SSR twice to make sure we didn't read across a tick. + __disable_irq(); + uint32_t first_ssr = (uint32_t)(RTC->SSR); uint32_t time = (uint32_t)(RTC->TR & RTC_TR_RESERVED_MASK); uint32_t date = (uint32_t)(RTC->DR & RTC_DR_RESERVED_MASK); + uint32_t ssr = (uint32_t)(RTC->SSR); + while (ssr != first_ssr) { + first_ssr = ssr; + time = (uint32_t)(RTC->TR & RTC_TR_RESERVED_MASK); + date = (uint32_t)(RTC->DR & RTC_DR_RESERVED_MASK); + ssr = (uint32_t)(RTC->SSR); + } + __enable_irq(); + + uint32_t subseconds = rtc_clock_frequency - 1 - ssr; + if (date != cached_date) { uint32_t year = (uint8_t)((date & (RTC_DR_YT | RTC_DR_YU)) >> 16U); uint8_t month = (uint8_t)((date & (RTC_DR_MT | RTC_DR_MU)) >> 8U); @@ -347,6 +364,7 @@ uint64_t port_get_raw_ticks(uint8_t* subticks) { hours = (uint8_t)RTC_Bcd2ToByte(hours); minutes = (uint8_t)RTC_Bcd2ToByte(minutes); seconds_to_minute = 60 * (60 * hours + minutes); + cached_hours_minutes = hours_minutes; } uint8_t seconds = (uint8_t)(time & (RTC_TR_ST | RTC_TR_SU)); seconds = (uint8_t)RTC_Bcd2ToByte(seconds); @@ -405,7 +423,7 @@ void port_interrupt_after_ticks(uint32_t ticks) { alarm.AlarmMask = RTC_ALARMMASK_ALL; } - alarm.AlarmTime.SubSeconds = rtc_clock_frequency - + alarm.AlarmTime.SubSeconds = rtc_clock_frequency - 1 - ((raw_ticks % 1024) * 32); alarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; alarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_SET; |
