diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index dfd268851e7..70f265f8d41 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -429,7 +429,7 @@ endif ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f0 f4 f7)) HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_, hal_can.c) -else ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),g0 g4 h7)) +else ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),g0 g4 h7 n6)) HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_, hal_fdcan.c) else ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),l4)) HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/Legacy/stm32$(MCU_SERIES)xx_, hal_can.c) diff --git a/ports/stm32/boards/OPENMV_N6/mpconfigboard.h b/ports/stm32/boards/OPENMV_N6/mpconfigboard.h index 751da650bfe..cc8a79e8944 100644 --- a/ports/stm32/boards/OPENMV_N6/mpconfigboard.h +++ b/ports/stm32/boards/OPENMV_N6/mpconfigboard.h @@ -78,6 +78,15 @@ #define MICROPY_HW_SPI4_MISO (pyb_pin_SPI4_MISO) #define MICROPY_HW_SPI4_MOSI (pyb_pin_SPI4_MOSI) +// FDCAN bus +#define MICROPY_HW_CAN1_NAME "FDCAN1" +#define MICROPY_HW_CAN1_TX (pyb_pin_CAN1_TX) +#define MICROPY_HW_CAN1_RX (pyb_pin_CAN1_RX) +// Support is not yet added for FDCAN CAN3 (see fdcan.c for details) +// #define MICROPY_HW_CAN3_NAME "FDCAN3" +// #define MICROPY_HW_CAN3_TX (pyb_pin_CAN3_TX) +// #define MICROPY_HW_CAN3_RX (pyb_pin_CAN3_RX) + // USER is pulled high, and pressing the button makes the input go low. #define MICROPY_HW_USRSW_PIN (pyb_pin_BUTTON) #define MICROPY_HW_USRSW_PULL (GPIO_NOPULL) diff --git a/ports/stm32/boards/OPENMV_N6/pins.csv b/ports/stm32/boards/OPENMV_N6/pins.csv index 2ca075a154a..7c36181c016 100644 --- a/ports/stm32/boards/OPENMV_N6/pins.csv +++ b/ports/stm32/boards/OPENMV_N6/pins.csv @@ -13,6 +13,8 @@ SPI2_CS,PA11 SPI2_SCK,PA12 UART4_RX,PA11 UART4_TX,PA12 +CAN1_RX,PA11 +CAN1_TX,PA12 P3,PA11 P2,PA12 ,PA13 @@ -92,6 +94,8 @@ SPI4_CS,PE11 SPI4_SCK,PE12 P15,PE11 P16,PE12 +CAN3_TX,PE11 +CAN3_RX,PE12 I2C4_SCL,PE13 I2C4_SDA,PE14 ,PE15 diff --git a/ports/stm32/boards/stm32n657_af.csv b/ports/stm32/boards/stm32n657_af.csv index b269d0497c6..4a7ad20e6d6 100644 --- a/ports/stm32/boards/stm32n657_af.csv +++ b/ports/stm32/boards/stm32n657_af.csv @@ -8,8 +8,8 @@ PortA,PA5 , , , , PortA,PA8 , , , , , , , , , , , , , , , , ,ADC12_INP5 PortA,PA9 , , , , , , , , , , , , , , , , ,ADC12_INP10 PortA,PA10, , , , , , , , , , , , , , , , ,ADC12_INP11/ADC12_INN10 -PortA,PA11, , , , , ,SPI2_NSS ,CAN1_RXFD , ,UART4_RX , , , , , , , ,ADC12_INP12/ADC12_INN11 -PortA,PA12, , , , , ,SPI2_SCK ,CAN1_TXFD , ,UART4_TX , , , , , , , ,ADC12_INP13/ADC12_INN12 +PortA,PA11, , , , , ,SPI2_NSS ,CAN1_RX , ,UART4_RX , , , , , , , ,ADC12_INP12/ADC12_INN11 +PortA,PA12, , , , , ,SPI2_SCK ,CAN1_TX , ,UART4_TX , , , , , , , ,ADC12_INP13/ADC12_INN12 PortB,PB4 , , , , , , , , , , , ,SDMMC2_D3 , , , , , PortB,PB6 , , , , , ,SPI4_MISO , ,TIM15_CH1 , , , , , , , , , PortB,PB7 , , , , , ,SPI4_MOSI , ,TIM15_CH2 , , , , , , , , , @@ -36,8 +36,8 @@ PortE,PE5 , , , , PortE,PE6 , , , , , , , ,USART1_RX , , , , , , , , , PortE,PE7 , , , , , , , , ,UART7_RX , , , , , , , , PortE,PE8 , , , , , , , , ,UART7_TX , , , , , , , , -PortE,PE11, , , , , ,SPI4_NSS ,CAN3_TXFD , , , , , , , , , , -PortE,PE12, , , , , ,SPI4_SCK ,CAN3_RXFD , , , , , , , , , , +PortE,PE11, , , , , ,SPI4_NSS ,CAN3_TX , , , , , , , , , , +PortE,PE12, , , , , ,SPI4_SCK ,CAN3_RX , , , , , , , , , , PortE,PE13, , , , ,I2C4_SCL , , , , , , , , , , , , PortE,PE14, , , , ,I2C4_SDA , , , , , , , , , , , , PortE,PE15, , , , , ,SPI5_SCK , , , , , , , , , , , diff --git a/ports/stm32/can.h b/ports/stm32/can.h index ff73e1a74d2..588a225ef36 100644 --- a/ports/stm32/can.h +++ b/ports/stm32/can.h @@ -62,7 +62,7 @@ #if defined(STM32G4) #define CAN_HW_MAX_STD_FILTER 28 #define CAN_HW_MAX_EXT_FILTER 8 -#elif defined(STM32H7) +#elif defined(STM32H7) || defined(STM32N6) // The RAM filtering section is configured for 64 x 1 word elements for 11-bit standard // identifiers, and 31 x 2 words elements for 29-bit extended identifiers. // The total number of words reserved for the filtering per FDCAN instance is 126 words. @@ -123,7 +123,7 @@ typedef struct { unsigned rx_fifo1_pending; } can_counters_t; -#if defined(STM32H7) +#if defined(STM32H7) || defined(STM32N6) #define CAN_TX_QUEUE_LEN 16 #else // FDCAN STM32G4, bxCAN diff --git a/ports/stm32/fdcan.c b/ports/stm32/fdcan.c index a7fcf7330b1..afec60e3c87 100644 --- a/ports/stm32/fdcan.c +++ b/ports/stm32/fdcan.c @@ -53,17 +53,22 @@ #define FDCAN_IT_RX_FULL_MASK (FDCAN_IT_RX_FIFO0_FULL | FDCAN_IT_RX_FIFO1_FULL) #define FDCAN_IT_RX_MESSAGE_LOST_MASK (FDCAN_IT_RX_FIFO0_MESSAGE_LOST | FDCAN_IT_RX_FIFO1_MESSAGE_LOST) -#if defined(STM32H7) -// adaptations for H7 to G4 naming convention in HAL +#if defined(STM32H7) || defined(STM32N6) +// adaptations for H7/N6 to G4 naming convention in HAL #define FDCAN_IT_GROUP_RX_FIFO0 (FDCAN_ILS_RF0NL | FDCAN_ILS_RF0FL | FDCAN_ILS_RF0LL) +#if defined(STM32H7) #define FDCAN_IT_GROUP_BIT_LINE_ERROR (FDCAN_ILS_EPE | FDCAN_ILS_ELOE) #define FDCAN_IT_GROUP_PROTOCOL_ERROR (FDCAN_ILS_ARAE | FDCAN_ILS_PEDE | FDCAN_ILS_PEAE | FDCAN_ILS_WDIE | FDCAN_ILS_BOE | FDCAN_ILS_EWE) +#else +#define FDCAN_IT_GROUP_BIT_LINE_ERROR (FDCAN_ILS_EPL | FDCAN_ILS_ELOL) +#define FDCAN_IT_GROUP_PROTOCOL_ERROR (FDCAN_ILS_ARAL | FDCAN_ILS_PEDL | FDCAN_ILS_PEAL | FDCAN_ILS_WDIL | FDCAN_ILS_BOL | FDCAN_ILS_EWL) +#endif #define FDCAN_IT_GROUP_RX_FIFO1 (FDCAN_ILS_RF1NL | FDCAN_ILS_RF1FL | FDCAN_ILS_RF1LL) // The dedicated Message RAM should be 2560 words, but the way it's defined in stm32h7xx_hal_fdcan.c // as (SRAMCAN_BASE + FDCAN_MESSAGE_RAM_SIZE - 0x4U) limits the usable number of words to 2559 words. #define FDCAN_MESSAGE_RAM_SIZE (2560 - 1) -#endif // STM32H7 +#endif // STM32H7 || STM32N6 #if defined(STM32G4) // These HAL APIs are not implemented for STM32G4, so we implement them here... @@ -132,6 +137,11 @@ bool can_init(CAN_HandleTypeDef *can, int can_id, can_tx_mode_t tx_mode, uint32_ init->NominalTimeSeg1 = bs1; // NominalTimeSeg1 = Propagation_segment + Phase_segment_1 init->NominalTimeSeg2 = bs2; + init->DataPrescaler = 1; + init->DataSyncJumpWidth = 1; + init->DataTimeSeg1 = 1; + init->DataTimeSeg2 = 1; + init->AutoRetransmission = ENABLE; init->TransmitPause = DISABLE; init->ProtocolException = ENABLE; @@ -141,12 +151,8 @@ bool can_init(CAN_HandleTypeDef *can, int can_id, can_tx_mode_t tx_mode, uint32_ #if defined(STM32G4) init->ClockDivider = FDCAN_CLOCK_DIV1; - init->DataPrescaler = 1; - init->DataSyncJumpWidth = 1; - init->DataTimeSeg1 = 1; - init->DataTimeSeg2 = 1; init->TxFifoQueueMode = fifo_queue_mode; - #elif defined(STM32H7) + #elif defined(STM32H7) || defined(STM32N6) // The dedicated FDCAN RAM is 2560 32-bit words and shared between the FDCAN instances. // To support 2 FDCAN instances simultaneously, the Message RAM is divided in half by // setting the second FDCAN memory offset to half the RAM size. With this configuration, @@ -185,11 +191,12 @@ bool can_init(CAN_HandleTypeDef *can, int can_id, can_tx_mode_t tx_mode, uint32_ // 2 words header + 16 words data field (to support up to 64 bytes of data). // The total number of words reserved for the Rx FIFOs per FDCAN instance is 864 words. init->RxBuffersNbr = 0; + init->RxBufferSize = FDCAN_DATA_BYTES_64; init->RxFifo0ElmtsNbr = 24; init->RxFifo0ElmtSize = FDCAN_DATA_BYTES_64; init->RxFifo1ElmtsNbr = 24; init->RxFifo1ElmtSize = FDCAN_DATA_BYTES_64; - #endif // STM32H7 + #endif // STM32H7 || STM32N6 const machine_pin_obj_t *pins[2]; @@ -316,7 +323,9 @@ void can_clearfilter(FDCAN_HandleTypeDef *can, uint32_t f, bool is_extid) { uint32_t can_get_source_freq(void) { // Find CAN kernel clock - #if defined(STM32H7) + #if defined(STM32N6) + return HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_FDCAN); + #elif defined(STM32H7) switch (__HAL_RCC_GET_FDCAN_SOURCE()) { case RCC_FDCANCLKSOURCE_HSE: return HSE_VALUE; @@ -362,7 +371,11 @@ static void encode_datalength(CanTxMsgTypeDef *txmsg) { size_t len_bytes = txmsg->DataLength; for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(DLCtoBytes); i++) { if (len_bytes <= DLCtoBytes[i]) { + #if defined(STM32N6) + txmsg->DataLength = i; + #else txmsg->DataLength = (i << 16); + #endif return; } } diff --git a/ports/stm32/main.c b/ports/stm32/main.c index 17111c6df98..c6b67696e54 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -420,6 +420,7 @@ void stm32_main(uint32_t reset_mode) { // Enable some APB peripherals during sleep. LL_APB1_GRP1_EnableClockLowPower(LL_APB1_GRP1_PERIPH_ALL); // I2C, I3C, LPTIM, SPI, TIM, UART, WWDG + LL_APB1_GRP2_EnableClockLowPower(LL_APB1_GRP2_PERIPH_FDCAN); // FDCAN LL_APB2_GRP1_EnableClockLowPower(LL_APB2_GRP1_PERIPH_ALL); // SAI, SPI, TIM, UART LL_APB4_GRP1_EnableClockLowPower(LL_APB4_GRP1_PERIPH_ALL); // I2C, LPTIM, LPUART, RTC, SPI #endif diff --git a/ports/stm32/mpconfigboard_common.h b/ports/stm32/mpconfigboard_common.h index e21f474d7af..36f16525056 100644 --- a/ports/stm32/mpconfigboard_common.h +++ b/ports/stm32/mpconfigboard_common.h @@ -742,7 +742,7 @@ // Enable CAN if there are any peripherals defined #if defined(MICROPY_HW_CAN1_TX) || defined(MICROPY_HW_CAN2_TX) || defined(MICROPY_HW_CAN3_TX) #define MICROPY_HW_ENABLE_CAN (1) -#if defined(STM32G0) || defined(STM32G4) || defined(STM32H7) +#if defined(STM32G0) || defined(STM32G4) || defined(STM32H7) || defined(STM32N6) #define MICROPY_HW_ENABLE_FDCAN (1) // define for MCUs with FDCAN #endif #else diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 19d4ef8ae0f..c2200e09370 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -123,7 +123,7 @@ #define MICROPY_PY_MACHINE_BITSTREAM (1) #endif #ifndef MICROPY_PY_MACHINE_CAN -#if defined(MICROPY_HW_CAN1_TX) || defined(MICROPY_HW_CAN2_TX) +#if defined(MICROPY_HW_CAN1_TX) || defined(MICROPY_HW_CAN2_TX) || defined(MICROPY_HW_CAN3_TX) #define MICROPY_PY_MACHINE_CAN (1) #else #define MICROPY_PY_MACHINE_CAN (0) diff --git a/ports/stm32/pin_defs_stm32.h b/ports/stm32/pin_defs_stm32.h index 645ec5b2df5..1020381abb8 100644 --- a/ports/stm32/pin_defs_stm32.h +++ b/ports/stm32/pin_defs_stm32.h @@ -133,6 +133,16 @@ enum { #define GPIO_AF9_CAN2 GPIO_AF9_FDCAN2 #endif +#if defined(STM32N6) +// Make N6 FDCAN more like CAN +#define CAN1 FDCAN1 +#define CAN2 FDCAN2 +#define CAN3 FDCAN3 +#define GPIO_AF6_CAN1 GPIO_AF6_FDCAN1 +#define GPIO_AF6_CAN2 GPIO_AF6_FDCAN2 +#define GPIO_AF6_CAN3 GPIO_AF6_FDCAN3 +#endif + enum { PIN_ADC1 = (1 << 0), PIN_ADC2 = (1 << 1), diff --git a/tests/ports/stm32/pyb_can.py b/tests/ports/stm32/pyb_can.py index 8178d91fe74..fda04a38b5e 100644 --- a/tests/ports/stm32/pyb_can.py +++ b/tests/ports/stm32/pyb_can.py @@ -8,14 +8,16 @@ import micropython import pyb import sys +import time # Classic CAN (aka bxCAN) hardware has a different filter API # and some different behaviours to newer FDCAN hardware IS_CLASSIC = hasattr(CAN, "MASK16") -# STM32H7 series has a gold-plated FDCAN peripheral with much deeper TX Queue +# STM32H7/STM32N6 series have a gold-plated FDCAN peripheral with much deeper TX Queue # than all other parts -IS_H7 = (not IS_CLASSIC) and "STM32H7" in str(sys.implementation) +sys_impl = str(sys.implementation) +HAS_DEEP_TXQ = (not IS_CLASSIC) and ("STM32H7" in sys_impl or "STM32N6" in sys_impl) # test we can correctly create by id (2 handled in can2.py test) for bus in (-1, 0, 1, 3): @@ -52,7 +54,7 @@ can.setfilter(0, CAN.MASK, 0, (0, 0), extframe=False) can.send("abcd", 123, timeout=5000) -pyb.delay(10) # For FDCAN, needs some time to send +time.sleep_ms(10) # For FDCAN, needs some time to send print("any+info", can.any(0), can.info()) print(can.recv(0)) @@ -168,7 +170,7 @@ except ValueError: print("failed") else: - pyb.delay(10) + time.sleep_ms(10) r = can.recv(0) if r[0] == 0x7FF + 1 and r[4] == b"abcde": print("extframe passed") @@ -189,13 +191,13 @@ can.setfilter(0, CAN.MASK, 0, (filter_id, filter_mask), extframe=True) can.send("ok", id_ok, timeout=5, extframe=True) - pyb.delay(10) + time.sleep_ms(10) if can.any(0): msg = can.recv(0) print((hex(filter_id), hex(filter_mask), hex(msg[0]), msg[1], msg[4])) can.send("fail", id_fail, timeout=5, extframe=True) - pyb.delay(10) + time.sleep_ms(10) if can.any(0): msg = can.recv(0) print((hex(filter_id), hex(filter_mask), hex(msg[0]), msg[1], msg[4])) @@ -226,10 +228,10 @@ can.send("abcde", 2, timeout=0) can.send("abcde", 3, timeout=0) can.send("abcde", 4, timeout=0) - if not IS_H7: + if not HAS_DEEP_TXQ: can.send("abcde", 5, timeout=0) else: - # Hack around the STM32H7's deeper transmit queue by pretending this call failed + # Hack around a deep transmit queue by pretending this call failed # (STM32G4 will fail here, using otherwise the same code, so there is still some test coverage.) print("send fail ok") except OSError as e: @@ -239,7 +241,7 @@ else: print("send fail not ok", e) -pyb.delay(500) +time.sleep_ms(500) while can.any(0): print(can.recv(0))