From 08bba34f4032183820840b474b895f10f9c8a4b6 Mon Sep 17 00:00:00 2001 From: rcitach Date: Tue, 30 Jun 2026 08:37:10 +0000 Subject: [PATCH 1/4] Add NS800RT7 IIC Functionality --- .../libraries/HAL_Drivers/drivers/SConscript | 3 + .../drivers/config/rt7/i2c_hard_config.h | 323 +++------ .../HAL_Drivers/drivers/drv_config.h | 2 +- .../libraries/HAL_Drivers/drivers/drv_i2c.c | 627 ++++++++++++++++++ .../libraries/HAL_Drivers/drivers/drv_i2c.h | 53 ++ .../.ci/attachconfig/ci.attachconfig.yml | 6 + .../ns800/ns800rt7p65-nssinepad/board/Kconfig | 24 + 7 files changed, 811 insertions(+), 227 deletions(-) create mode 100644 bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_i2c.c create mode 100644 bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_i2c.h diff --git a/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/SConscript b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/SConscript index 8cbb9ff8467..50008655f3c 100644 --- a/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/SConscript +++ b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/SConscript @@ -38,6 +38,9 @@ if GetDepend('BSP_USING_WDT'): if GetDepend('BSP_USING_EPWM'): src += ['drv_epwm.c'] +if GetDepend('BSP_USING_I2C'): + src += ['drv_i2c.c'] + group = DefineGroup('HAL_Drivers', src, depend = [''], CPPPATH = path) Return('group') diff --git a/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/config/rt7/i2c_hard_config.h b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/config/rt7/i2c_hard_config.h index 7168665eba2..d48f6221f16 100644 --- a/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/config/rt7/i2c_hard_config.h +++ b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/config/rt7/i2c_hard_config.h @@ -4,10 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 * * Change Logs: - * Date Author Notes - * 2024-02-06 Dyyt587 first version - * 2024-04-23 Zeidan Add I2Cx_xx_DMA_CONFIG - * 2024-06-23 wdfk-prog Add H7 hard I2C config + * Date Author Notes + * 2026-06-07 dirtwillfly first version for NS800 */ #ifndef __I2C_HARD_CONFIG_H__ #define __I2C_HARD_CONFIG_H__ @@ -18,242 +16,115 @@ extern "C" { #endif +/* I2C1 default GPIO configuration */ +/* GPIO_0 (SDA) -> GPIOA, GPIO_PIN_0 */ +/* GPIO_1 (SCL) -> GPIOA, GPIO_PIN_1 */ +#ifndef BSP_I2C1_SCL_PIN_NUM +#define BSP_I2C1_SCL_PIN_NUM 1U +#endif +#ifndef BSP_I2C1_SDA_PIN_NUM +#define BSP_I2C1_SDA_PIN_NUM 0U +#endif +#ifndef BSP_I2C1_SCL_PORT +#define BSP_I2C1_SCL_PORT GPIOA +#endif +#ifndef BSP_I2C1_SDA_PORT +#define BSP_I2C1_SDA_PORT GPIOA +#endif +#ifndef BSP_I2C1_SCL_PIN +#define BSP_I2C1_SCL_PIN GPIO_PIN_1 +#endif +#ifndef BSP_I2C1_SDA_PIN +#define BSP_I2C1_SDA_PIN GPIO_PIN_0 +#endif +#ifndef BSP_I2C1_SCL_ALT_FUNC +#define BSP_I2C1_SCL_ALT_FUNC ALT6_FUNCTION +#endif +#ifndef BSP_I2C1_SDA_ALT_FUNC +#define BSP_I2C1_SDA_ALT_FUNC ALT6_FUNCTION +#endif + +/* I2C2 default GPIO configuration */ +/* GPIO_34 (SDA) -> GPIOB, GPIO_PIN_2 */ +/* GPIO_35 (SCL) -> GPIOB, GPIO_PIN_3 */ +#ifndef BSP_I2C2_SCL_PIN_NUM +#define BSP_I2C2_SCL_PIN_NUM 35U +#endif +#ifndef BSP_I2C2_SDA_PIN_NUM +#define BSP_I2C2_SDA_PIN_NUM 34U +#endif +#ifndef BSP_I2C2_SCL_PORT +#define BSP_I2C2_SCL_PORT GPIOB +#endif +#ifndef BSP_I2C2_SDA_PORT +#define BSP_I2C2_SDA_PORT GPIOB +#endif +#ifndef BSP_I2C2_SCL_PIN +#define BSP_I2C2_SCL_PIN GPIO_PIN_3 +#endif +#ifndef BSP_I2C2_SDA_PIN +#define BSP_I2C2_SDA_PIN GPIO_PIN_2 +#endif +#ifndef BSP_I2C2_SCL_ALT_FUNC +#define BSP_I2C2_SCL_ALT_FUNC ALT6_FUNCTION +#endif +#ifndef BSP_I2C2_SDA_ALT_FUNC +#define BSP_I2C2_SDA_ALT_FUNC ALT6_FUNCTION +#endif + #ifdef BSP_USING_HARD_I2C1 +#ifndef BSP_I2C1_BAUDRATE +#define BSP_I2C1_BAUDRATE 100000 +#endif + #ifndef I2C1_BUS_CONFIG -#define I2C1_BUS_CONFIG \ - { \ - .Instance = I2C1, \ - .timing = 0x307075B1, \ - .timeout = 1000, \ - .name = "hwi2c1", \ - .evirq_type = I2C1_EV_IRQn, \ - .erirq_type = I2C1_ER_IRQn, \ +#define I2C1_BUS_CONFIG \ + { \ + .name = "hwi2c1", \ + .Instance = I2C1, \ + .baudrate = BSP_I2C1_BAUDRATE, \ + .timeout = 1000, \ + .irq_type = I2C1_MST_IRQn, \ + .scl_pin_num = BSP_I2C1_SCL_PIN_NUM, \ + .sda_pin_num = BSP_I2C1_SDA_PIN_NUM, \ + .scl_port = BSP_I2C1_SCL_PORT, \ + .sda_port = BSP_I2C1_SDA_PORT, \ + .scl_pin = BSP_I2C1_SCL_PIN, \ + .sda_pin = BSP_I2C1_SDA_PIN, \ + .scl_alt_func = BSP_I2C1_SCL_ALT_FUNC, \ + .sda_alt_func = BSP_I2C1_SDA_ALT_FUNC, \ } #endif /* I2C1_BUS_CONFIG */ #endif /* BSP_USING_HARD_I2C1 */ -#ifdef BSP_I2C1_TX_USING_DMA -#ifndef I2C1_TX_DMA_CONFIG -#if defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) -#define I2C1_TX_DMA_CONFIG \ - { \ - .dma_rcc = I2C1_TX_DMA_RCC, \ - .Instance = I2C1_TX_DMA_INSTANCE, \ - .dma_irq = I2C1_TX_DMA_IRQ, \ - .channel = I2C1_TX_DMA_CHANNEL \ - } -#elif defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32G0) || defined(SOC_SERIES_STM32MP1) || defined(SOC_SERIES_STM32WB) || defined(SOC_SERIES_STM32H7) -#define I2C1_TX_DMA_CONFIG \ - { \ - .dma_rcc = I2C1_TX_DMA_RCC, \ - .Instance = I2C1_TX_DMA_INSTANCE, \ - .dma_irq = I2C1_TX_DMA_IRQ, \ - .request = DMA_REQUEST_I2C1_TX \ - } -#endif /* defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) */ -#endif /* I2C1_TX_DMA_CONFIG */ -#endif /* BSP_I2C1_TX_USING_DMA */ - -#ifdef BSP_I2C1_RX_USING_DMA -#ifndef I2C1_RX_DMA_CONFIG -#if defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) -#define I2C1_RX_DMA_CONFIG \ - { \ - .dma_rcc = I2C1_RX_DMA_RCC, \ - .Instance = I2C1_RX_DMA_INSTANCE, \ - .dma_irq = I2C1_RX_DMA_IRQ, \ - .channel = I2C1_RX_DMA_CHANNEL, \ - } -#elif defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32G0) || defined(SOC_SERIES_STM32MP1) || defined(SOC_SERIES_STM32WB) || defined(SOC_SERIES_STM32H7) -#define I2C1_RX_DMA_CONFIG \ - { \ - .dma_rcc = I2C1_RX_DMA_RCC, \ - .Instance = I2C1_RX_DMA_INSTANCE, \ - .dma_irq = I2C1_RX_DMA_IRQ, \ - .request = DMA_REQUEST_I2C1_RX \ - } -#endif /* defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) */ -#endif /* I2C1_RX_DMA_CONFIG */ -#endif /* BSP_I2C1_RX_USING_DMA */ - #ifdef BSP_USING_HARD_I2C2 +#ifndef BSP_I2C2_BAUDRATE +#define BSP_I2C2_BAUDRATE 100000 +#endif + #ifndef I2C2_BUS_CONFIG -#define I2C2_BUS_CONFIG \ - { \ - .Instance = I2C2, \ - .timing = 0x307075B1, \ - .timeout = 1000, \ - .name = "hwi2c2", \ - .evirq_type = I2C2_EV_IRQn, \ - .erirq_type = I2C2_ER_IRQn, \ +#define I2C2_BUS_CONFIG \ + { \ + .name = "hwi2c2", \ + .Instance = I2C2, \ + .baudrate = BSP_I2C2_BAUDRATE, \ + .timeout = 1000, \ + .irq_type = I2C2_MST_IRQn, \ + .scl_pin_num = BSP_I2C2_SCL_PIN_NUM, \ + .sda_pin_num = BSP_I2C2_SDA_PIN_NUM, \ + .scl_port = BSP_I2C2_SCL_PORT, \ + .sda_port = BSP_I2C2_SDA_PORT, \ + .scl_pin = BSP_I2C2_SCL_PIN, \ + .sda_pin = BSP_I2C2_SDA_PIN, \ + .scl_alt_func = BSP_I2C2_SCL_ALT_FUNC, \ + .sda_alt_func = BSP_I2C2_SDA_ALT_FUNC, \ } #endif /* I2C2_BUS_CONFIG */ #endif /* BSP_USING_HARD_I2C2 */ -#ifdef BSP_I2C2_TX_USING_DMA -#ifndef I2C2_TX_DMA_CONFIG -#if defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) -#define I2C2_TX_DMA_CONFIG \ - { \ - .dma_rcc = I2C2_TX_DMA_RCC, \ - .Instance = I2C2_TX_DMA_INSTANCE, \ - .dma_irq = I2C2_TX_DMA_IRQ, \ - .channel = I2C2_TX_DMA_CHANNEL, \ - } -#elif defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32G0) || defined(SOC_SERIES_STM32MP1) || defined(SOC_SERIES_STM32WB) || defined(SOC_SERIES_STM32H7) -#define I2C2_TX_DMA_CONFIG \ - { \ - .dma_rcc = I2C2_TX_DMA_RCC, \ - .Instance = I2C2_TX_DMA_INSTANCE, \ - .dma_irq = I2C2_TX_DMA_IRQ, \ - .request = DMA_REQUEST_I2C2_TX \ - } -#endif /* defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) */ -#endif /* I2C2_TX_DMA_CONFIG */ -#endif /* BSP_I2C2_TX_USING_DMA */ - -#ifdef BSP_I2C2_RX_USING_DMA -#ifndef I2C2_RX_DMA_CONFIG -#if defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) -#define I2C2_RX_DMA_CONFIG \ - { \ - .dma_rcc = I2C2_RX_DMA_RCC, \ - .Instance = I2C2_RX_DMA_INSTANCE, \ - .dma_irq = I2C2_RX_DMA_IRQ, \ - .channel = I2C2_RX_DMA_CHANNEL, \ - } -#elif defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32G0) || defined(SOC_SERIES_STM32MP1) || defined(SOC_SERIES_STM32WB) || defined(SOC_SERIES_STM32H7) -#define I2C2_RX_DMA_CONFIG \ - { \ - .dma_rcc = I2C2_RX_DMA_RCC, \ - .Instance = I2C2_RX_DMA_INSTANCE, \ - .dma_irq = I2C2_RX_DMA_IRQ, \ - .request = DMA_REQUEST_I2C2_RX \ - } -#endif /* defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) */ -#endif /* I2C2_RX_DMA_CONFIG */ -#endif /* BSP_I2C2_RX_USING_DMA */ - -#ifdef BSP_USING_HARD_I2C3 -#ifndef I2C3_BUS_CONFIG -#define I2C3_BUS_CONFIG \ - { \ - .Instance = I2C3, \ - .timing = 0x307075B1, \ - .timeout = 1000, \ - .name = "hwi2c3", \ - .evirq_type = I2C3_EV_IRQn, \ - .erirq_type = I2C3_ER_IRQn, \ - } -#endif /* I2C3_BUS_CONFIG */ -#endif /* BSP_USING_HARD_I2C3 */ - -#ifdef BSP_I2C3_TX_USING_DMA -#ifndef I2C3_TX_DMA_CONFIG -#if defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) -#define I2C3_TX_DMA_CONFIG \ - { \ - .dma_rcc = I2C3_TX_DMA_RCC, \ - .Instance = I2C3_TX_DMA_INSTANCE, \ - .dma_irq = I2C3_TX_DMA_IRQ, \ - .channel = I2C3_TX_DMA_CHANNEL, \ - } -#elif defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32G0) || defined(SOC_SERIES_STM32MP1) || defined(SOC_SERIES_STM32WB) || defined(SOC_SERIES_STM32H7) -#define I2C3_TX_DMA_CONFIG \ - { \ - .dma_rcc = I2C3_TX_DMA_RCC, \ - .Instance = I2C3_TX_DMA_INSTANCE, \ - .dma_irq = I2C3_TX_DMA_IRQ, \ - .request = DMA_REQUEST_I2C3_TX \ - } -#endif /* defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) */ -#endif /* I2C3_TX_DMA_CONFIG */ -#endif /* BSP_I2C3_TX_USING_DMA */ - -#ifdef BSP_I2C3_RX_USING_DMA -#ifndef I2C3_RX_DMA_CONFIG -#if defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) -#define I2C3_RX_DMA_CONFIG \ - { \ - .dma_rcc = I2C3_RX_DMA_RCC, \ - .Instance = I2C3_RX_DMA_INSTANCE, \ - .dma_irq = I2C3_RX_DMA_IRQ, \ - .channel = I2C3_RX_DMA_CHANNEL, \ - } -#elif defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32G0) || defined(SOC_SERIES_STM32MP1) || defined(SOC_SERIES_STM32WB) || defined(SOC_SERIES_STM32H7) -#define I2C3_RX_DMA_CONFIG \ - { \ - .dma_rcc = I2C3_RX_DMA_RCC, \ - .Instance = I2C3_RX_DMA_INSTANCE, \ - .dma_irq = I2C3_RX_DMA_IRQ, \ - .request = DMA_REQUEST_I2C3_RX \ - } -#endif /* defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) */ -#endif /* I2C3_RX_DMA_CONFIG */ -#endif /* BSP_I2C3_RX_USING_DMA */ - -#ifdef BSP_USING_HARD_I2C4 -#ifndef I2C4_BUS_CONFIG -#define I2C4_BUS_CONFIG \ - { \ - .Instance = I2C4, \ - .timing = 0x307075B1, \ - .timeout = 1000, \ - .name = "hwi2c4", \ - .evirq_type = I2C4_EV_IRQn, \ - .erirq_type = I2C4_ER_IRQn, \ - } -#endif /* I2C4_BUS_CONFIG */ -#endif /* BSP_USING_HARD_I2C4 */ - -#ifdef BSP_I2C4_TX_USING_DMA -#ifndef I2C4_TX_DMA_CONFIG -#if defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) -#define I2C4_TX_DMA_CONFIG \ - { \ - .dma_rcc = I2C4_TX_DMA_RCC, \ - .Instance = I2C4_TX_DMA_INSTANCE, \ - .dma_irq = I2C4_TX_DMA_IRQ, \ - .channel = I2C4_TX_DMA_CHANNEL, \ - } -#elif defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32G0) || defined(SOC_SERIES_STM32MP1) || defined(SOC_SERIES_STM32WB) || defined(SOC_SERIES_STM32H7) -#define I2C4_TX_DMA_CONFIG \ - { \ - .dma_rcc = I2C4_TX_DMA_RCC, \ - .Instance = I2C4_TX_DMA_INSTANCE, \ - .dma_irq = I2C4_TX_DMA_IRQ, \ - .request = DMA_REQUEST_I2C4_TX \ - } -#endif /* defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) */ -#endif /* I2C4_TX_DMA_CONFIG */ -#endif /* BSP_I2C4_TX_USING_DMA */ - -#ifdef BSP_I2C4_RX_USING_DMA -#ifndef I2C4_RX_DMA_CONFIG -#if defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) -#define I2C4_RX_DMA_CONFIG \ - { \ - .dma_rcc = I2C4_RX_DMA_RCC, \ - .Instance = I2C4_RX_DMA_INSTANCE, \ - .dma_irq = I2C4_RX_DMA_IRQ, \ - .channel = I2C4_RX_DMA_CHANNEL, \ - } -#elif defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32G0) || defined(SOC_SERIES_STM32MP1) || defined(SOC_SERIES_STM32WB) || defined(SOC_SERIES_STM32H7) -#define I2C4_RX_DMA_CONFIG \ - { \ - .dma_rcc = I2C4_RX_DMA_RCC, \ - .Instance = I2C4_RX_DMA_INSTANCE, \ - .dma_irq = I2C4_RX_DMA_IRQ, \ - .request = DMA_REQUEST_I2C4_RX \ - } -#endif /* defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) */ -#endif /* I2C4_RX_DMA_CONFIG */ -#endif /* BSP_I2C4_RX_USING_DMA */ - - #ifdef __cplusplus } #endif -#endif /*__I2C_HARD_CONFIG_H__ */ +#endif /* __I2C_HARD_CONFIG_H__ */ diff --git a/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_config.h b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_config.h index 9907a5c283e..7f486c47811 100644 --- a/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_config.h +++ b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_config.h @@ -20,7 +20,7 @@ extern "C" { #if defined(SOC_SERIES_NS800RT7) #include "rt7/uart_config.h" - +#include "rt7/i2c_hard_config.h" #endif #ifdef __cplusplus diff --git a/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_i2c.c b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_i2c.c new file mode 100644 index 00000000000..8d35c14bd22 --- /dev/null +++ b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_i2c.c @@ -0,0 +1,627 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-06-07 dirtwillfly first version for NS800 + */ + +#include "rtthread.h" +#include "drv_i2c.h" + +#if defined(BSP_USING_I2C) + +#define DRV_DEBUG +#define LOG_TAG "drv.i2c" +#include + +#define DEFAULT_I2C_BAUDRATE (100 * 1000U) +#define I2C_TIMEOUT_MS (1000U) + +#if !defined(BSP_USING_HARD_I2C1) && !defined(BSP_USING_HARD_I2C2) +#error "BSP_USING_I2C is enabled, but no hardware I2C instance is selected" +#endif + +enum +{ +#ifdef BSP_USING_HARD_I2C1 + I2C1_INDEX, +#endif +#ifdef BSP_USING_HARD_I2C2 + I2C2_INDEX, +#endif +}; + +static struct ns800_i2c_config i2c_config[] = +{ +#ifdef BSP_USING_HARD_I2C1 + I2C1_BUS_CONFIG, +#endif +#ifdef BSP_USING_HARD_I2C2 + I2C2_BUS_CONFIG, +#endif +}; + +static struct ns800_i2c i2c_objs[sizeof(i2c_config) / sizeof(i2c_config[0])] = {0}; + +static I2C_Status ns800_i2c_wait_master_tx_ready(I2C_TypeDef *i2c, uint32_t timeout_ms) +{ + uint32_t start_tick = rt_tick_get(); + uint32_t status; + I2C_Status result; + uint8_t txCount; + uint8_t txFifoSize = 16U; + + while ((rt_tick_get() - start_tick) < rt_tick_from_millisecond(timeout_ms)) + { + txCount = txFifoSize - I2C_getMasterTxFifoCounts(i2c); + + status = I2C_getMasterStatusFlags(i2c); + result = I2C_checkAndClearMasterError(i2c, status); + if (result != I2C_STATUS_SUCCESS) + { + return result; + } + + if (txCount > 0U) + { + return I2C_STATUS_SUCCESS; + } + + rt_thread_mdelay(1); + } + + LOG_E("I2C wait TX ready timeout"); + return I2C_STATUS_TIMEOUT; +} + +static I2C_Status ns800_i2c_wait_bus_available(I2C_TypeDef *i2c, uint32_t timeout_ms) +{ + uint32_t start_tick = rt_tick_get(); + + while ((rt_tick_get() - start_tick) < rt_tick_from_millisecond(timeout_ms)) + { + I2C_Status result = I2C_checkMasterBusyBus(i2c); + if (result == I2C_STATUS_SUCCESS) + { + return I2C_STATUS_SUCCESS; + } + + rt_thread_mdelay(1); + } + + return I2C_STATUS_BUSY; +} + +static I2C_Status ns800_i2c_send_stop(I2C_TypeDef *i2c, uint32_t timeout_ms) +{ + uint32_t start_tick = rt_tick_get(); + uint32_t status; + I2C_Status result; + + result = ns800_i2c_wait_master_tx_ready(i2c, timeout_ms); + if (result != I2C_STATUS_SUCCESS) + { + return result; + } + + WRITE_REG(i2c->MTDR.WORDVAL, (uint32_t)I2C_COMMAND_STOP); + + while ((rt_tick_get() - start_tick) < rt_tick_from_millisecond(timeout_ms)) + { + status = I2C_getMasterStatusFlags(i2c); + if ((status & (uint32_t)I2C_MASTER_FLAG_STOP) != 0U) + { + result = I2C_checkAndClearMasterError(i2c, status); + I2C_clearMasterStatusFlags(i2c, (uint32_t)I2C_MASTER_FLAG_CLEAR); + return result; + } + + if ((status & (uint32_t)I2C_MASTER_FLAG_NACK) != 0U) + { + result = I2C_checkAndClearMasterError(i2c, status); + I2C_clearMasterStatusFlags(i2c, (uint32_t)I2C_MASTER_FLAG_CLEAR); + return result; + } + + rt_thread_mdelay(1); + } + + status = I2C_getMasterStatusFlags(i2c); + LOG_E("I2C wait STOP timeout, status=0x%x, resetting I2C", status); + I2C_resetMaster(i2c); + I2C_enableMasterModule(i2c); + + return I2C_STATUS_TIMEOUT; +} + +static void ns800_i2c_reset_bus(I2C_TypeDef *i2c) +{ + I2C_resetMaster(i2c); + I2C_enableMasterModule(i2c); + I2C_clearMasterStatusFlags(i2c, (uint32_t)I2C_MASTER_FLAG_CLEAR); +} + +static void ns800_i2c_apply_default_config(struct ns800_i2c_config *cfg) +{ + RT_ASSERT(cfg != RT_NULL); + + if (cfg->baudrate == 0U) + { + cfg->baudrate = DEFAULT_I2C_BAUDRATE; + } +} + +/** + * @brief Initialize I2C GPIO pins + * @param cfg I2C configuration structure + */ +static void ns800_i2c_gpio_init(struct ns800_i2c_config *cfg) +{ + RT_ASSERT(cfg != RT_NULL); + + LOG_D("I2C[%s] GPIO init: SCL(port=%p, pin=%d, alt=%d), SDA(port=%p, pin=%d, alt=%d)", + cfg->name, (void *)cfg->scl_port, cfg->scl_pin, cfg->scl_alt_func, + (void *)cfg->sda_port, cfg->sda_pin, cfg->sda_alt_func); + + /* SCL pin configuration - follow SDK example order */ + if (cfg->scl_port != RT_NULL && cfg->scl_alt_func != 0U) + { + GPIO_setPinConfig(cfg->scl_port, cfg->scl_pin, cfg->scl_alt_func); + GPIO_setAnalogMode(cfg->scl_port, cfg->scl_pin, GPIO_ANALOG_DISABLED); + GPIO_setPadConfig(cfg->scl_port, cfg->scl_pin, GPIO_PIN_TYPE_PULLUP); + GPIO_setQualificationMode(cfg->scl_port, cfg->scl_pin, GPIO_QUAL_ASYNC); + GPIO_setDirectionMode(cfg->scl_port, cfg->scl_pin, GPIO_DIR_MODE_IN); + } + + /* SDA pin configuration - follow SDK example order */ + if (cfg->sda_port != RT_NULL && cfg->sda_alt_func != 0U) + { + GPIO_setPinConfig(cfg->sda_port, cfg->sda_pin, cfg->sda_alt_func); + GPIO_setAnalogMode(cfg->sda_port, cfg->sda_pin, GPIO_ANALOG_DISABLED); + GPIO_setPadConfig(cfg->sda_port, cfg->sda_pin, GPIO_PIN_TYPE_PULLUP); + GPIO_setQualificationMode(cfg->sda_port, cfg->sda_pin, GPIO_QUAL_ASYNC); + GPIO_setDirectionMode(cfg->sda_port, cfg->sda_pin, GPIO_DIR_MODE_IN); + } +} + +static rt_err_t ns800_i2c_init(struct ns800_i2c *i2c_drv) +{ + RT_ASSERT(i2c_drv != RT_NULL); + + I2C_TypeDef *i2c = i2c_drv->config->Instance; + struct ns800_i2c_config *cfg = i2c_drv->config; + + LOG_D("I2C[%s] init start, Instance=%p, baudrate=%d", + cfg->name, (void *)i2c, cfg->baudrate); + + ns800_i2c_apply_default_config(cfg); + + /* Initialize GPIO pins FIRST - follow SDK example order */ + ns800_i2c_gpio_init(cfg); + + /* Enable I2C module clock */ + if (i2c == I2C1) + { + LOG_D("I2C[%s] enabling I2C1 clock", cfg->name); + RCC_enableI2c1Clock(); + } + else if (i2c == I2C2) + { + LOG_D("I2C[%s] enabling I2C2 clock", cfg->name); + RCC_enableI2c2Clock(); + } + + LOG_D("I2C[%s] clock enabled, PCLKEN5=0x%x", cfg->name, RCC->PCLKEN5.WORDVAL); + + /* Reset module before configuring it */ + I2C_resetMaster(i2c); + LOG_D("I2C[%s] master reset done", cfg->name); + + /* Disable debug mode */ + I2C_disableMasterDebug(i2c); + + /* Set master water marks */ + I2C_setMasterWatermarks(i2c, I2C_MASTER_WATERMARK_0, I2C_MASTER_WATERMARK_0); + + /* Configure master glitch filters + And set FILTSDA to 0 disables the filter, so the min value is 1 */ + I2C_setMasterGlitchFilter(i2c, I2C_MASTER_FILTER_PCLK1, I2C_MASTER_FILTER_PCLK1); + + /* Configure baudrate after the SDA/SCL glitch filter setting */ + I2C_configMasterBaudRate(i2c, RCC_getPclk2Frequency(), cfg->baudrate); + + /* Configure bus idle timeouts after baudrate setting + The value is equal to BUSIDLE cycles of functional clock divided by prescaler + And set BUSIDLE to 0 disables the filter, so the min value is 1 */ + I2C_setMasterBusIdleTimeout(i2c, 1U); + + /* Configure pin low timeouts after baudrate setting + The value is equal to PINLOW cycles of functional clock divided by prescaler + Set PINLOW to 0 to disable the timeout filter */ + I2C_setMasterPinLowTimeout(i2c, I2C_MASTER_PINLOW_SCLSDA, 0U); + + /* Config master data match mode, match code and rxmatchonly */ + I2C_configMasterDataMatch(i2c, I2C_MASTER_MATCHMODE_DISABLED, 0U, 0U, I2C_MASTER_RXMATCHDATAONLY_DISABLE); + + /* Enable master module */ + I2C_enableMasterModule(i2c); + LOG_D("I2C[%s] master module enabled", cfg->name); + + /* Verify I2C is properly initialized */ + uint32_t mcr = READ_REG(i2c->MCR.WORDVAL); + uint32_t msr = READ_REG(i2c->MSR.WORDVAL); + LOG_D("I2C[%s] MCR=0x%x, MSR=0x%x", cfg->name, mcr, msr); + + return RT_EOK; +} + +static rt_err_t ns800_i2c_configure(struct rt_i2c_bus_device *bus) +{ + RT_ASSERT(RT_NULL != bus); + struct ns800_i2c *i2c_drv = rt_container_of(bus, struct ns800_i2c, i2c_bus); + + return ns800_i2c_init(i2c_drv); +} + +static rt_ssize_t ns800_i2c_master_xfer(struct rt_i2c_bus_device *bus, + struct rt_i2c_msg msgs[], + rt_uint32_t num) +{ + rt_uint32_t i = 0; + rt_int32_t ret = 0; + struct rt_i2c_msg *msg = RT_NULL; + struct ns800_i2c *i2c_obj; + I2C_Status status; + rt_uint32_t timeout; + rt_bool_t xfer_active = RT_FALSE; + + if (num == 0) + { + return 0; + } + + RT_ASSERT((msgs != RT_NULL) && (bus != RT_NULL)); + i2c_obj = rt_container_of(bus, struct ns800_i2c, i2c_bus); + RT_ASSERT(i2c_obj != RT_NULL); + I2C_TypeDef *i2c = i2c_obj->config->Instance; + + /* Get timeout from config, default 1000ms */ + timeout = i2c_obj->config->timeout > 0 ? i2c_obj->config->timeout : I2C_TIMEOUT_MS; + + LOG_D("xfer start %d msgs", num); + + status = ns800_i2c_wait_bus_available(i2c, timeout); + if (status != I2C_STATUS_SUCCESS) + { + LOG_E("I2C[%s] Bus is busy", bus->parent.parent.name); + return -RT_EBUSY; + } + + for (i = 0; i < num; i++) + { + msg = &msgs[i]; + LOG_D("xfer msgs[%d] addr=0x%02x buf=%p len=%u flags=0x%x", + i, msg->addr, (void *)msg->buf, (unsigned int)msg->len, msg->flags); + + if ((msg->flags & RT_I2C_ADDR_10BIT) || (msg->addr > 0x7FU)) + { + LOG_E("I2C[%s] only 7-bit addressing is supported, addr=0x%x", + bus->parent.parent.name, msg->addr); + ret = -RT_EINVAL; + goto out; + } + + if (!(msg->flags & RT_I2C_NO_START)) + { + /* RT-Thread uses 7-bit address, SDK expects 8-bit address */ + /* Convert 7-bit address to 8-bit address by shifting left */ + uint8_t addr_8bit = msg->addr << 1; + + if (msg->flags & RT_I2C_RD) + { + status = (i == 0U) ? I2C_sendStart(i2c, addr_8bit, I2C_DIRECTION_READ) + : I2C_sendReStart(i2c, addr_8bit, I2C_DIRECTION_READ); + } + else + { + status = (i == 0U) ? I2C_sendStart(i2c, addr_8bit, I2C_DIRECTION_WRITE) + : I2C_sendReStart(i2c, addr_8bit, I2C_DIRECTION_WRITE); + } + + LOG_D("I2C[%s] SendStart status=%d, MSR=0x%x", bus->parent.parent.name, status, I2C_getMasterStatusFlags(i2c)); + + if (status != I2C_STATUS_SUCCESS) + { + LOG_E("I2C[%s] SendStart error(%d)", bus->parent.parent.name, status); + ret = -RT_EIO; + goto out; + } + + xfer_active = RT_TRUE; + } + else + { + /* RT_I2C_NO_START: do not generate START/RESTART. + * Only sequential writes (continuing the previous address phase) + * are supported. + */ + if ((i == 0) || ((msg->flags & RT_I2C_RD))) + { + LOG_E("I2C[%s] RT_I2C_NO_START is not supported for the first message or read transfer", + bus->parent.parent.name); + ret = -RT_EINVAL; + goto out; + } + + status = I2C_STATUS_SUCCESS; + } + + if (msg->flags & RT_I2C_RD) + { + status = I2C_receiveDataMaster(i2c, msg->buf, msg->len); + if (status != I2C_STATUS_SUCCESS) + { + LOG_E("I2C[%s] Read error(%d)", bus->parent.parent.name, status); + ret = -RT_EIO; + goto out; + } + } + else + { + status = I2C_sendDataMaster(i2c, msg->buf, msg->len); + if (status != I2C_STATUS_SUCCESS) + { + LOG_E("I2C[%s] Write error(%d)", bus->parent.parent.name, status); + ret = -RT_EIO; + goto out; + } + } + + if (((i + 1U) == num) && !(msg->flags & RT_I2C_NO_STOP)) + { + /* Use timeout protected STOP */ + status = ns800_i2c_send_stop(i2c, timeout); + if (status != I2C_STATUS_SUCCESS) + { + LOG_E("I2C[%s] SendStop error(%d), resetting bus", bus->parent.parent.name, status); + ns800_i2c_reset_bus(i2c); + xfer_active = RT_FALSE; + ret = -RT_EIO; + goto out; + } + xfer_active = RT_FALSE; + } + } + + ret = num; + LOG_D("xfer end %d msgs", num); + return ret; + +out: + if (xfer_active) + { + /* Safe error recovery - use timeout protected STOP or reset. */ + ns800_i2c_send_stop(i2c, timeout / 2); /* Shorter timeout for recovery. */ + } + return ret; +} + +static rt_err_t ns800_i2c_bus_control(struct rt_i2c_bus_device *bus, + int cmd, + void *args) +{ + rt_err_t result = RT_EOK; + struct ns800_i2c *i2c_obj; + + RT_ASSERT(bus != RT_NULL); + i2c_obj = rt_container_of(bus, struct ns800_i2c, i2c_bus); + RT_ASSERT(i2c_obj != RT_NULL); + + I2C_TypeDef *i2c = i2c_obj->config->Instance; + + switch (cmd) + { + case RT_I2C_DEV_CTRL_ADDR: + /* Set slave address - not typically used for master */ + result = -RT_ENOSYS; + break; + + case RT_I2C_DEV_CTRL_TIMEOUT: + /* Set timeout in milliseconds */ + if (args != RT_NULL) + { + i2c_obj->config->timeout = *(rt_uint32_t *)args; + } + break; + + case RT_I2C_CTRL_SET_MAX_HZ: + /* Change baudrate */ + if (args != RT_NULL) + { + i2c_obj->config->baudrate = *(rt_uint32_t *)args; + I2C_resetMaster(i2c); + I2C_configMasterBaudRate(i2c, RCC_getPclk2Frequency(), i2c_obj->config->baudrate); + I2C_enableMasterModule(i2c); + } + break; + + case RT_I2C_DEV_CTRL_RW: + /* Combined read/write operation */ + result = -RT_ENOSYS; + break; + + case RT_I2C_DEV_CTRL_CLK: + /* Set clock - similar to baud rate */ + if (args != RT_NULL) + { + i2c_obj->config->baudrate = *(rt_uint32_t *)args; + I2C_resetMaster(i2c); + I2C_configMasterBaudRate(i2c, RCC_getPclk2Frequency(), i2c_obj->config->baudrate); + I2C_enableMasterModule(i2c); + } + break; + + case BSP_I2C_CTRL_SET_TIMING: + /* Set timing - map to baudrate setting */ + if (args != RT_NULL) + { + i2c_obj->config->baudrate = *(rt_uint32_t *)args; + I2C_resetMaster(i2c); + I2C_configMasterBaudRate(i2c, RCC_getPclk2Frequency(), i2c_obj->config->baudrate); + I2C_enableMasterModule(i2c); + } + break; + + default: + result = -RT_ENOSYS; + break; + } + + return result; +} + +static const struct rt_i2c_bus_device_ops ns800_i2c_ops = +{ + .master_xfer = ns800_i2c_master_xfer, + .slave_xfer = RT_NULL, + .i2c_bus_control = ns800_i2c_bus_control, +}; + +int ns800_hw_i2c_bus_init(void) +{ + int ret = -RT_ERROR; + rt_size_t obj_num = sizeof(i2c_objs) / sizeof(i2c_objs[0]); + + for (int i = 0; i < obj_num; i++) + { + i2c_objs[i].i2c_bus.ops = &ns800_i2c_ops; + i2c_objs[i].config = &i2c_config[i]; + ns800_i2c_apply_default_config(&i2c_config[i]); + + /* Initialize bus timeout from config */ + i2c_objs[i].i2c_bus.timeout = i2c_config[i].timeout; + i2c_objs[i].i2c_bus.retries = 0; + + ret = ns800_i2c_configure(&i2c_objs[i].i2c_bus); + if (ret != RT_EOK) + { + LOG_E("%s bus configure failed %d", i2c_config[i].name, ret); + return -RT_ERROR; + } + + ret = rt_i2c_bus_device_register(&i2c_objs[i].i2c_bus, i2c_objs[i].config->name); + if (ret != RT_EOK) + { + LOG_E("%s bus init failed %d", i2c_config[i].name, ret); + } + else + { + LOG_D("%s bus init done", i2c_config[i].name); + } + } + + return ret; +} + +int rt_hw_i2c_init(void) +{ + return ns800_hw_i2c_bus_init(); +} +INIT_BOARD_EXPORT(rt_hw_i2c_init); + +#ifdef RT_USING_FINSH + +/** + * @brief Scan I2C bus for devices + * @param i2c I2C instance + * @param bus_name Bus name for logging + */ +static void ns800_i2c_scan_bus(I2C_TypeDef *i2c, const char *bus_name) +{ + uint8_t found_count = 0; + uint32_t timeout = 100; + + LOG_I("Scanning I2C bus: %s...", bus_name); + + for (uint8_t addr = 1; addr < 128; addr++) + { + I2C_Status status; + + /* RT-Thread uses 7-bit address, SDK expects 8-bit address */ + uint8_t addr_8bit = addr << 1; + + /* Send START with write direction */ + status = I2C_sendStart(i2c, addr_8bit, I2C_DIRECTION_WRITE); + + if (status == I2C_STATUS_SUCCESS) + { + uint32_t msr = I2C_getMasterStatusFlags(i2c); + + /* Check if we got ACK (no NACK) */ + if (!(msr & I2C_MASTER_FLAG_NACK)) + { + LOG_I("I2C device found at address: 0x%02X", addr); + found_count++; + } + + /* Clear any pending flags */ + I2C_clearMasterStatusFlags(i2c, I2C_MASTER_FLAG_NACK); + + /* Send STOP to release bus */ + ns800_i2c_send_stop(i2c, timeout); + } + else + { + /* Failed to send start, reset and continue */ + I2C_resetMaster(i2c); + I2C_enableMasterModule(i2c); + } + + /* Small delay between devices */ + rt_thread_mdelay(1); + } + + if (found_count == 0) + { + LOG_W("No I2C devices found on bus: %s", bus_name); + } + else + { + LOG_I("Found %d I2C device(s) on bus: %s", found_count, bus_name); + } +} + +/** + * @brief Finsh command to scan I2C buses + * @param argc Argument count + * @param argv Argument array + */ +static void i2c_scan(int argc, char **argv) +{ + const char *bus_name = RT_NULL; + + if (argc == 2) + { + bus_name = argv[1]; + } + + for (int i = 0; i < sizeof(i2c_objs) / sizeof(i2c_objs[0]); i++) + { + if (i2c_objs[i].config->name != RT_NULL) + { + if (bus_name == RT_NULL || rt_strcmp(bus_name, i2c_objs[i].config->name) == 0) + { + ns800_i2c_scan_bus(i2c_objs[i].config->Instance, i2c_objs[i].config->name); + } + } + } +} +MSH_CMD_EXPORT(i2c_scan, Scan I2C bus for devices); + +#endif /* RT_USING_FINSH */ + +#endif /* defined(BSP_USING_I2C) */ + diff --git a/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_i2c.h b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_i2c.h new file mode 100644 index 00000000000..b173b586531 --- /dev/null +++ b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_i2c.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-06-07 dirtwillfly first version for NS800 + */ + +#ifndef __DRV_I2C_H__ +#define __DRV_I2C_H__ + +#include "drv_config.h" +#include "board.h" +#include "rtdevice.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define BSP_I2C_CTRL_SET_TIMING 0x40 + +struct ns800_i2c_config +{ + const char *name; + I2C_TypeDef *Instance; + uint32_t baudrate; + uint32_t timeout; + IRQn_Type irq_type; + uint32_t scl_pin_num; /* SCL pin number for get_pin_info */ + uint32_t sda_pin_num; /* SDA pin number for get_pin_info */ + GPIO_TypeDef *scl_port; /* SCL GPIO port */ + GPIO_TypeDef *sda_port; /* SDA GPIO port */ + GPIO_PinNum scl_pin; /* SCL pin in the port */ + GPIO_PinNum sda_pin; /* SDA pin in the port */ + GPIO_AltFunc scl_alt_func; /* SCL alternate function */ + GPIO_AltFunc sda_alt_func; /* SDA alternate function */ +}; + +struct ns800_i2c +{ + struct ns800_i2c_config *config; + struct rt_i2c_bus_device i2c_bus; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* __DRV_I2C_H__ */ + diff --git a/bsp/novosns/ns800/ns800rt7p65-nssinepad/.ci/attachconfig/ci.attachconfig.yml b/bsp/novosns/ns800/ns800rt7p65-nssinepad/.ci/attachconfig/ci.attachconfig.yml index 70332c07d91..2fb5187ffa1 100644 --- a/bsp/novosns/ns800/ns800rt7p65-nssinepad/.ci/attachconfig/ci.attachconfig.yml +++ b/bsp/novosns/ns800/ns800rt7p65-nssinepad/.ci/attachconfig/ci.attachconfig.yml @@ -16,6 +16,12 @@ devices.spi: - CONFIG_BSP_USING_SPI=y - CONFIG_BSP_USING_SPI1=y - CONFIG_BSP_USING_QSPI=y +devices.i2c: + <<: *scons + kconfig: + - CONFIG_BSP_USING_I2C=y + - CONFIG_BSP_USING_HARD_I2C1=y + - CONFIG_BSP_USING_HARD_I2C2=y devices.wdt: <<: *scons kconfig: diff --git a/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/Kconfig b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/Kconfig index 2ee7710bcd1..2e6bad550c7 100644 --- a/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/Kconfig +++ b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/Kconfig @@ -307,4 +307,28 @@ menu "On-chip Peripheral Drivers" default n endif + menuconfig BSP_USING_I2C + bool "Enable I2C" + select RT_USING_I2C + default n + if BSP_USING_I2C + menuconfig BSP_USING_HARD_I2C1 + bool "Enable Hardware I2C1" + default n + if BSP_USING_HARD_I2C1 + config BSP_I2C1_BAUDRATE + int "I2C1 baud rate (Hz)" + default 100000 + endif + + menuconfig BSP_USING_HARD_I2C2 + bool "Enable Hardware I2C2" + default n + if BSP_USING_HARD_I2C2 + config BSP_I2C2_BAUDRATE + int "I2C2 baud rate (Hz)" + default 100000 + endif + endif + endmenu From b2d10a3d2451e2d0a71dd9c25de1cee5c2f99c70 Mon Sep 17 00:00:00 2001 From: rcitach Date: Tue, 30 Jun 2026 09:25:37 +0000 Subject: [PATCH 2/4] Add RTC functionality --- .../libraries/HAL_Drivers/drivers/SConscript | 3 + .../libraries/HAL_Drivers/drivers/drv_rtc.c | 206 +++++++++++ .../libraries/HAL_Drivers/drivers/drv_rtc.h | 37 ++ .../ns800/ns800rt7p65-nssinepad/board/Kconfig | 64 ++++ .../ns800rt7p65-nssinepad/board/SConscript | 8 +- .../ns800rt7p65-nssinepad/board/port/ds1307.c | 345 ++++++++++++++++++ .../ns800rt7p65-nssinepad/board/port/ds1307.h | 22 ++ .../board/port/soft_rtc.c | 233 ++++++++++++ .../board/port/soft_rtc.h | 22 ++ 9 files changed, 939 insertions(+), 1 deletion(-) create mode 100644 bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_rtc.c create mode 100644 bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_rtc.h create mode 100644 bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/ds1307.c create mode 100644 bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/ds1307.h create mode 100644 bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/soft_rtc.c create mode 100644 bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/soft_rtc.h diff --git a/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/SConscript b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/SConscript index 50008655f3c..dc649c1ce2b 100644 --- a/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/SConscript +++ b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/SConscript @@ -41,6 +41,9 @@ if GetDepend('BSP_USING_EPWM'): if GetDepend('BSP_USING_I2C'): src += ['drv_i2c.c'] +if GetDepend('BSP_USING_RTC'): + src += ['drv_rtc.c'] + group = DefineGroup('HAL_Drivers', src, depend = [''], CPPPATH = path) Return('group') diff --git a/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_rtc.c b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_rtc.c new file mode 100644 index 00000000000..24f39ea38cb --- /dev/null +++ b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_rtc.c @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-06-18 wenzhi346 add NS800 rtc driver + */ +#include +#include +#include +#include "drv_rtc.h" + +struct ns800_rtc_backend +{ + const char *name; + const struct ns800_rtc_ops *ops; + void *user_data; + rt_bool_t initialized; +}; + +static rt_rtc_dev_t g_rtc_dev; +static struct ns800_rtc_backend g_rtc_backend; + +rt_err_t ns800_rtc_register(const char *name, + const struct ns800_rtc_ops *ops, + void *user_data) +{ + if ((ops == RT_NULL) || (ops->get_time == RT_NULL) || (ops->set_time == RT_NULL)) + { + return -RT_EINVAL; + } + + if (g_rtc_backend.ops != RT_NULL) + { + return -RT_EBUSY; + } + + g_rtc_backend.name = (name != RT_NULL) ? name : "external"; + g_rtc_backend.ops = ops; + g_rtc_backend.user_data = user_data; + g_rtc_backend.initialized = RT_FALSE; + + return RT_EOK; +} + +static rt_err_t ns800_rtc_init(void) +{ + rt_err_t ret; + + if (g_rtc_backend.ops == RT_NULL) + { + return -RT_ENOSYS; + } + + if (g_rtc_backend.initialized) + { + return RT_EOK; + } + + ret = RT_EOK; + if (g_rtc_backend.ops->init != RT_NULL) + { + ret = g_rtc_backend.ops->init(g_rtc_backend.user_data); + } + + if (ret == RT_EOK) + { + g_rtc_backend.initialized = RT_TRUE; + } + + return ret; +} + +static rt_err_t ns800_rtc_get_secs(time_t *sec) +{ + struct tm time_struct; + rt_err_t result; + + if (sec == RT_NULL) + { + return -RT_EINVAL; + } + + result = ns800_rtc_init(); + if (result != RT_EOK) + { + return result; + } + + result = g_rtc_backend.ops->get_time(g_rtc_backend.user_data, &time_struct); + if (result == RT_EOK) + { + *sec = mktime(&time_struct); + } + + return result; +} + +static rt_err_t ns800_rtc_set_secs(time_t *sec) +{ + struct tm time_struct; + rt_err_t result; + + if (sec == RT_NULL) + { + return -RT_EINVAL; + } + + result = ns800_rtc_init(); + if (result != RT_EOK) + { + return result; + } + + localtime_r(sec, &time_struct); + + return g_rtc_backend.ops->set_time(g_rtc_backend.user_data, &time_struct); +} + +static rt_err_t ns800_rtc_get_timeval(struct timeval *tv) +{ + time_t sec; + rt_err_t result; + + if (tv == RT_NULL) + { + return -RT_EINVAL; + } + + result = ns800_rtc_get_secs(&sec); + if (result != RT_EOK) + { + return result; + } + + tv->tv_sec = sec; + tv->tv_usec = 0; + + return RT_EOK; +} + +static rt_err_t ns800_rtc_set_timeval(struct timeval *tv) +{ + time_t sec; + + if (tv == RT_NULL) + { + return -RT_EINVAL; + } + + sec = tv->tv_sec; + return ns800_rtc_set_secs(&sec); +} + +static const struct rt_rtc_ops ns800_rtc_ops = +{ + ns800_rtc_init, + ns800_rtc_get_secs, + ns800_rtc_set_secs, + RT_NULL, + RT_NULL, + ns800_rtc_get_timeval, + ns800_rtc_set_timeval, +}; + +static rt_err_t ns800_rtc_try_init_device(void) +{ + rt_err_t result; + + result = rt_device_init(&g_rtc_dev.parent); + if (result != RT_EOK) + { + rt_kprintf("rtc: %s init deferred, result %d\n", g_rtc_backend.name, result); + return result; + } + + rt_kprintf("rtc: %s ready\n", g_rtc_backend.name); + return RT_EOK; +} + +int rt_hw_rtc_init(void) +{ + rt_err_t result; + + if (g_rtc_backend.ops == RT_NULL) + { + rt_kprintf("rtc: no external backend registered\n"); + return -1; + } + + g_rtc_dev.ops = &ns800_rtc_ops; + + result = rt_hw_rtc_register(&g_rtc_dev, "rtc", RT_DEVICE_FLAG_RDWR, RT_NULL); + if (result != RT_EOK) + { + rt_kprintf("rtc: register failed, result %d\n", result); + return -1; + } + + ns800_rtc_try_init_device(); + return 0; +} +INIT_DEVICE_EXPORT(rt_hw_rtc_init); + diff --git a/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_rtc.h b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_rtc.h new file mode 100644 index 00000000000..8dadacb4a28 --- /dev/null +++ b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_rtc.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#ifndef __DRV_RTC_H__ +#define __DRV_RTC_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct ns800_rtc_ops +{ + rt_err_t (*init)(void *user_data); + rt_err_t (*get_time)(void *user_data, struct tm *time); + rt_err_t (*set_time)(void *user_data, const struct tm *time); +}; + +rt_err_t ns800_rtc_register(const char *name, + const struct ns800_rtc_ops *ops, + void *user_data); +int rt_hw_rtc_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __DRV_RTC_H__ */ + diff --git a/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/Kconfig b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/Kconfig index 2e6bad550c7..94742d97cd8 100644 --- a/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/Kconfig +++ b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/Kconfig @@ -184,6 +184,70 @@ menu "On-chip Peripheral Drivers" endif + menuconfig BSP_USING_RTC + bool "Enable external RTC" + select RT_USING_RTC + default n + help + Enable the board-level RTC framework for an external RTC chip. + + if BSP_USING_RTC + choice + prompt "External RTC backend" + default BSP_RTC_USING_DS1307 + + config BSP_RTC_USING_DS1307 + bool "DS1307 over I2C" + select BSP_USING_I2C + select BSP_USING_HARD_I2C1 + select RT_USING_I2C + + config BSP_RTC_USING_SOFT + bool "Software RTC" + endchoice + + if BSP_RTC_USING_DS1307 + config BSP_RTC_DS1307_I2C_BUS_NAME + string "DS1307 I2C bus name" + default "hwi2c1" + + config BSP_RTC_DS1307_I2C_ADDR + hex "DS1307 I2C address" + default 0x68 + endif + + if BSP_RTC_USING_SOFT + config BSP_RTC_SOFT_DEFAULT_YEAR + int "Software RTC default year" + default 2026 + + config BSP_RTC_SOFT_DEFAULT_MONTH + int "Software RTC default month" + range 1 12 + default 1 + + config BSP_RTC_SOFT_DEFAULT_DAY + int "Software RTC default day" + range 1 31 + default 1 + + config BSP_RTC_SOFT_DEFAULT_HOUR + int "Software RTC default hour" + range 0 23 + default 0 + + config BSP_RTC_SOFT_DEFAULT_MIN + int "Software RTC default minute" + range 0 59 + default 0 + + config BSP_RTC_SOFT_DEFAULT_SEC + int "Software RTC default second" + range 0 59 + default 0 + endif + endif + menuconfig BSP_USING_SPI bool "Enable SPI" select RT_USING_SPI diff --git a/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/SConscript b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/SConscript index 5eeed838dc1..b26046ba060 100644 --- a/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/SConscript +++ b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/SConscript @@ -3,7 +3,13 @@ from building import * cwd = GetCurrentDir() src = ['board.c'] -path = [cwd] +path = [cwd, cwd + '/port'] + +if GetDepend('BSP_RTC_USING_DS1307'): + src += ['port/ds1307.c'] + +if GetDepend('BSP_RTC_USING_SOFT'): + src += ['port/soft_rtc.c'] cppdefines = ['NS800RT7P65X', 'USE_HAL_DRIVER', 'DUAL_CORE_ENABLE=0', 'SYSCLK_USE_PLL', 'SYSCLK_SOURCE_USE_HXTL', 'PLLCLK_SOURCE_USE_HXTL'] diff --git a/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/ds1307.c b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/ds1307.c new file mode 100644 index 00000000000..bbb272ef697 --- /dev/null +++ b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/ds1307.c @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-06-24 wenzhi346 first version + */ + +#include +#include +#include "drv_rtc.h" +#include "ds1307.h" + +#ifndef BSP_RTC_DS1307_I2C_BUS_NAME +#define BSP_RTC_DS1307_I2C_BUS_NAME "hwi2c1" +#endif + +#ifndef BSP_RTC_DS1307_I2C_ADDR +#define BSP_RTC_DS1307_I2C_ADDR 0x68 +#endif + +#define DS1307_REG_SEC 0x00 +#define DS1307_REG_MIN 0x01 +#define DS1307_REG_HOUR 0x02 +#define DS1307_REG_WDAY 0x03 +#define DS1307_REG_MDAY 0x04 +#define DS1307_REG_MON 0x05 +#define DS1307_REG_YEAR 0x06 + +#define DS1307_SEC_CH 0x80 +#define DS1307_HOUR_12H 0x40 +#define DS1307_HOUR_PM 0x20 + +struct ds1307_device +{ + const char *bus_name; + rt_uint16_t addr; + struct rt_i2c_bus_device *i2c; + rt_bool_t clock_halt_reported; +}; + +static struct ds1307_device g_ds1307 = +{ + BSP_RTC_DS1307_I2C_BUS_NAME, + BSP_RTC_DS1307_I2C_ADDR, + RT_NULL, + RT_FALSE, +}; + +static rt_uint8_t bcd2bin(rt_uint8_t bcd) +{ + return ((bcd >> 4) * 10) + (bcd & 0x0F); +} + +static rt_uint8_t bin2bcd(rt_uint8_t val) +{ + return ((val / 10) << 4) | (val % 10); +} + +static rt_bool_t bcd_in_range(rt_uint8_t bcd, rt_uint8_t min, rt_uint8_t max) +{ + rt_uint8_t val; + + if (((bcd & 0x0F) > 9) || (((bcd >> 4) & 0x0F) > 9)) + { + return RT_FALSE; + } + + val = bcd2bin(bcd); + return ((val >= min) && (val <= max)) ? RT_TRUE : RT_FALSE; +} + +static rt_bool_t is_leap_year(int year) +{ + return (((year % 4) == 0) && (((year % 100) != 0) || ((year % 400) == 0))) ? RT_TRUE : RT_FALSE; +} + +static rt_uint8_t month_days(int year, int mon) +{ + static const rt_uint8_t days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + if ((mon == 1) && is_leap_year(year)) + { + return 29; + } + + return days[mon]; +} + +static rt_bool_t tm_is_valid(const struct tm *tm) +{ + int year; + + if (tm == RT_NULL) + { + return RT_FALSE; + } + + year = tm->tm_year + 1900; + if ((year < 2000) || (year > 2099)) + { + return RT_FALSE; + } + + if ((tm->tm_mon < 0) || (tm->tm_mon > 11)) + { + return RT_FALSE; + } + + if ((tm->tm_mday < 1) || (tm->tm_mday > month_days(year, tm->tm_mon))) + { + return RT_FALSE; + } + + if ((tm->tm_wday < 0) || (tm->tm_wday > 6)) + { + return RT_FALSE; + } + + if ((tm->tm_hour < 0) || (tm->tm_hour > 23) || + (tm->tm_min < 0) || (tm->tm_min > 59) || + (tm->tm_sec < 0) || (tm->tm_sec > 59)) + { + return RT_FALSE; + } + + return RT_TRUE; +} + +static rt_err_t ds1307_read_regs(struct ds1307_device *dev, rt_uint8_t reg, rt_uint8_t *buf, rt_uint8_t len) +{ + struct rt_i2c_msg msgs[2]; + + if ((dev == RT_NULL) || (dev->i2c == RT_NULL) || (buf == RT_NULL) || (len == 0)) + { + return -RT_EINVAL; + } + + msgs[0].addr = dev->addr; + msgs[0].flags = RT_I2C_WR; + msgs[0].len = 1; + msgs[0].buf = ® + + msgs[1].addr = dev->addr; + msgs[1].flags = RT_I2C_RD; + msgs[1].len = len; + msgs[1].buf = buf; + + return (rt_i2c_transfer(dev->i2c, msgs, 2) == 2) ? RT_EOK : -RT_ERROR; +} + +static rt_err_t ds1307_write_regs(struct ds1307_device *dev, rt_uint8_t reg, const rt_uint8_t *buf, rt_uint8_t len) +{ + rt_uint8_t data[8]; + + if ((dev == RT_NULL) || (dev->i2c == RT_NULL) || (buf == RT_NULL) || + (len == 0) || (len >= sizeof(data))) + { + return -RT_EINVAL; + } + + data[0] = reg; + rt_memcpy(&data[1], buf, len); + + return (rt_i2c_master_send(dev->i2c, dev->addr, 0, data, len + 1) == (len + 1)) + ? RT_EOK : -RT_ERROR; +} + +static rt_err_t ds1307_probe(struct ds1307_device *dev) +{ + rt_uint8_t sec; + + return ds1307_read_regs(dev, DS1307_REG_SEC, &sec, 1); +} + +rt_err_t ds1307_init(void) +{ + if (g_ds1307.i2c == RT_NULL) + { + g_ds1307.i2c = rt_i2c_bus_device_find(g_ds1307.bus_name); + if (g_ds1307.i2c == RT_NULL) + { + rt_kprintf("ds1307: i2c bus %s not found\n", g_ds1307.bus_name); + return -RT_ENOSYS; + } + } + + return ds1307_probe(&g_ds1307); +} + +rt_err_t ds1307_get_time(struct tm *tm) +{ + rt_uint8_t buf[7]; + rt_uint8_t hour; + rt_uint8_t wday; + + if (tm == RT_NULL) + { + return -RT_EINVAL; + } + + if (ds1307_read_regs(&g_ds1307, DS1307_REG_SEC, buf, sizeof(buf)) != RT_EOK) + { + return -RT_ERROR; + } + + if ((buf[DS1307_REG_SEC] & DS1307_SEC_CH) != 0) + { + if (!g_ds1307.clock_halt_reported) + { + rt_kprintf("ds1307: clock halted, please set time\n"); + g_ds1307.clock_halt_reported = RT_TRUE; + } + return -RT_ERROR; + } + + if (!bcd_in_range(buf[DS1307_REG_SEC] & 0x7F, 0, 59) || + !bcd_in_range(buf[DS1307_REG_MIN] & 0x7F, 0, 59) || + !bcd_in_range(buf[DS1307_REG_MDAY] & 0x3F, 1, 31) || + !bcd_in_range(buf[DS1307_REG_MON] & 0x1F, 1, 12) || + !bcd_in_range(buf[DS1307_REG_YEAR], 0, 99)) + { + return -RT_ERROR; + } + + if ((buf[DS1307_REG_HOUR] & DS1307_HOUR_12H) != 0) + { + hour = bcd2bin(buf[DS1307_REG_HOUR] & 0x1F); + if ((hour < 1) || (hour > 12)) + { + return -RT_ERROR; + } + + if ((buf[DS1307_REG_HOUR] & DS1307_HOUR_PM) != 0) + { + if (hour != 12) + { + hour += 12; + } + } + else if (hour == 12) + { + hour = 0; + } + } + else + { + if (!bcd_in_range(buf[DS1307_REG_HOUR] & 0x3F, 0, 23)) + { + return -RT_ERROR; + } + hour = bcd2bin(buf[DS1307_REG_HOUR] & 0x3F); + } + + if (!bcd_in_range(buf[DS1307_REG_WDAY] & 0x07, 1, 7)) + { + return -RT_ERROR; + } + wday = bcd2bin(buf[DS1307_REG_WDAY] & 0x07); + + rt_memset(tm, 0, sizeof(*tm)); + tm->tm_sec = bcd2bin(buf[DS1307_REG_SEC] & 0x7F); + tm->tm_min = bcd2bin(buf[DS1307_REG_MIN] & 0x7F); + tm->tm_hour = hour; + tm->tm_wday = wday - 1; + tm->tm_mday = bcd2bin(buf[DS1307_REG_MDAY] & 0x3F); + tm->tm_mon = bcd2bin(buf[DS1307_REG_MON] & 0x1F) - 1; + tm->tm_year = bcd2bin(buf[DS1307_REG_YEAR]) + 100; + tm->tm_isdst = -1; + + if (!tm_is_valid(tm)) + { + return -RT_ERROR; + } + + return RT_EOK; +} + +rt_err_t ds1307_set_time(const struct tm *tm) +{ + rt_uint8_t buf[7]; + + if (!tm_is_valid(tm)) + { + return -RT_EINVAL; + } + + buf[DS1307_REG_SEC] = bin2bcd(tm->tm_sec); + buf[DS1307_REG_MIN] = bin2bcd(tm->tm_min); + buf[DS1307_REG_HOUR] = bin2bcd(tm->tm_hour); + buf[DS1307_REG_WDAY] = bin2bcd(tm->tm_wday + 1); + buf[DS1307_REG_MDAY] = bin2bcd(tm->tm_mday); + buf[DS1307_REG_MON] = bin2bcd(tm->tm_mon + 1); + buf[DS1307_REG_YEAR] = bin2bcd(tm->tm_year - 100); + + g_ds1307.clock_halt_reported = RT_FALSE; + + return ds1307_write_regs(&g_ds1307, DS1307_REG_SEC, buf, sizeof(buf)); +} + +static rt_err_t ds1307_backend_init(void *user_data) +{ + RT_UNUSED(user_data); + + return ds1307_init(); +} + +static rt_err_t ds1307_backend_get_time(void *user_data, struct tm *tm) +{ + RT_UNUSED(user_data); + + return ds1307_get_time(tm); +} + +static rt_err_t ds1307_backend_set_time(void *user_data, const struct tm *tm) +{ + RT_UNUSED(user_data); + + return ds1307_set_time(tm); +} + +static const struct ns800_rtc_ops ds1307_rtc_ops = +{ + ds1307_backend_init, + ds1307_backend_get_time, + ds1307_backend_set_time, +}; + +int rt_hw_ds1307_init(void) +{ + rt_err_t result; + + result = ns800_rtc_register("ds1307", &ds1307_rtc_ops, &g_ds1307); + if (result != RT_EOK) + { + rt_kprintf("ds1307: register rtc backend failed, result %d\n", result); + return -1; + } + + return 0; +} +INIT_BOARD_EXPORT(rt_hw_ds1307_init); + diff --git a/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/ds1307.h b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/ds1307.h new file mode 100644 index 00000000000..a06ce0078d1 --- /dev/null +++ b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/ds1307.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-06-24 wenzhi346 first version + */ +#ifndef __DS1307_H__ +#define __DS1307_H__ + +#include +#include + +rt_err_t ds1307_init(void); +rt_err_t ds1307_get_time(struct tm *time); +rt_err_t ds1307_set_time(const struct tm *time); +int rt_hw_ds1307_init(void); + +#endif + diff --git a/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/soft_rtc.c b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/soft_rtc.c new file mode 100644 index 00000000000..a4732e483ba --- /dev/null +++ b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/soft_rtc.c @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-06-30 rcitach first version + */ + +#include +#include +#include +#include "drv_rtc.h" +#include "soft_rtc.h" + +#ifndef BSP_RTC_SOFT_DEFAULT_YEAR +#define BSP_RTC_SOFT_DEFAULT_YEAR 2026 +#endif + +#ifndef BSP_RTC_SOFT_DEFAULT_MONTH +#define BSP_RTC_SOFT_DEFAULT_MONTH 1 +#endif + +#ifndef BSP_RTC_SOFT_DEFAULT_DAY +#define BSP_RTC_SOFT_DEFAULT_DAY 1 +#endif + +#ifndef BSP_RTC_SOFT_DEFAULT_HOUR +#define BSP_RTC_SOFT_DEFAULT_HOUR 0 +#endif + +#ifndef BSP_RTC_SOFT_DEFAULT_MIN +#define BSP_RTC_SOFT_DEFAULT_MIN 0 +#endif + +#ifndef BSP_RTC_SOFT_DEFAULT_SEC +#define BSP_RTC_SOFT_DEFAULT_SEC 0 +#endif + +struct soft_rtc_device +{ + time_t base_sec; + rt_tick_t base_tick; + rt_bool_t valid; +}; + +static struct soft_rtc_device g_soft_rtc; + +static rt_bool_t soft_rtc_is_leap_year(int year) +{ + return (((year % 4) == 0) && (((year % 100) != 0) || ((year % 400) == 0))) ? RT_TRUE : RT_FALSE; +} + +static rt_uint8_t soft_rtc_month_days(int year, int mon) +{ + static const rt_uint8_t days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + if ((mon == 1) && soft_rtc_is_leap_year(year)) + { + return 29; + } + + return days[mon]; +} + +static rt_bool_t soft_rtc_tm_is_valid(const struct tm *tm) +{ + int year; + + if (tm == RT_NULL) + { + return RT_FALSE; + } + + year = tm->tm_year + 1900; + if ((year < 1970) || + (tm->tm_mon < 0) || (tm->tm_mon > 11) || + (tm->tm_mday < 1) || (tm->tm_mday > soft_rtc_month_days(year, tm->tm_mon)) || + (tm->tm_hour < 0) || (tm->tm_hour > 23) || + (tm->tm_min < 0) || (tm->tm_min > 59) || + (tm->tm_sec < 0) || (tm->tm_sec > 59)) + { + return RT_FALSE; + } + + return RT_TRUE; +} + +static void soft_rtc_set_secs(time_t sec) +{ + rt_base_t level; + + level = rt_hw_interrupt_disable(); + g_soft_rtc.base_sec = sec; + g_soft_rtc.base_tick = rt_tick_get(); + g_soft_rtc.valid = RT_TRUE; + rt_hw_interrupt_enable(level); +} + +static time_t soft_rtc_get_secs(void) +{ + time_t base_sec; + rt_tick_t base_tick; + rt_bool_t valid; + rt_base_t level; + + level = rt_hw_interrupt_disable(); + base_sec = g_soft_rtc.base_sec; + base_tick = g_soft_rtc.base_tick; + valid = g_soft_rtc.valid; + rt_hw_interrupt_enable(level); + + if (!valid) + { + return 0; + } + + return base_sec + (time_t)(rt_tick_get_delta(base_tick) / RT_TICK_PER_SECOND); +} + +rt_err_t soft_rtc_init(void) +{ + struct tm default_time; + + if (g_soft_rtc.valid) + { + return RT_EOK; + } + + rt_memset(&default_time, 0, sizeof(default_time)); + default_time.tm_year = BSP_RTC_SOFT_DEFAULT_YEAR - 1900; + default_time.tm_mon = BSP_RTC_SOFT_DEFAULT_MONTH - 1; + default_time.tm_mday = BSP_RTC_SOFT_DEFAULT_DAY; + default_time.tm_hour = BSP_RTC_SOFT_DEFAULT_HOUR; + default_time.tm_min = BSP_RTC_SOFT_DEFAULT_MIN; + default_time.tm_sec = BSP_RTC_SOFT_DEFAULT_SEC; + default_time.tm_isdst = -1; + + if (!soft_rtc_tm_is_valid(&default_time)) + { + return -RT_EINVAL; + } + + soft_rtc_set_secs(mktime(&default_time)); + return RT_EOK; +} + +rt_err_t soft_rtc_get_time(struct tm *tm) +{ + time_t sec; + + if (tm == RT_NULL) + { + return -RT_EINVAL; + } + + if (!g_soft_rtc.valid) + { + rt_err_t result = soft_rtc_init(); + if (result != RT_EOK) + { + return result; + } + } + + sec = soft_rtc_get_secs(); + localtime_r(&sec, tm); + + return RT_EOK; +} + +rt_err_t soft_rtc_set_time(const struct tm *tm) +{ + struct tm time_copy; + time_t sec; + + if (!soft_rtc_tm_is_valid(tm)) + { + return -RT_EINVAL; + } + + time_copy = *tm; + time_copy.tm_isdst = -1; + sec = mktime(&time_copy); + soft_rtc_set_secs(sec); + + return RT_EOK; +} + +static rt_err_t soft_rtc_backend_init(void *user_data) +{ + RT_UNUSED(user_data); + + return soft_rtc_init(); +} + +static rt_err_t soft_rtc_backend_get_time(void *user_data, struct tm *tm) +{ + RT_UNUSED(user_data); + + return soft_rtc_get_time(tm); +} + +static rt_err_t soft_rtc_backend_set_time(void *user_data, const struct tm *tm) +{ + RT_UNUSED(user_data); + + return soft_rtc_set_time(tm); +} + +static const struct ns800_rtc_ops soft_rtc_ops = +{ + soft_rtc_backend_init, + soft_rtc_backend_get_time, + soft_rtc_backend_set_time, +}; + +int rt_hw_soft_rtc_init(void) +{ + rt_err_t result; + + result = ns800_rtc_register("soft_rtc", &soft_rtc_ops, &g_soft_rtc); + if (result != RT_EOK) + { + rt_kprintf("soft_rtc: register rtc backend failed, result %d\n", result); + return -1; + } + + return 0; +} +INIT_BOARD_EXPORT(rt_hw_soft_rtc_init); + diff --git a/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/soft_rtc.h b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/soft_rtc.h new file mode 100644 index 00000000000..92196db5ba1 --- /dev/null +++ b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/soft_rtc.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-06-30 rcitach first version + */ +#ifndef __SOFT_RTC_H__ +#define __SOFT_RTC_H__ + +#include +#include + +rt_err_t soft_rtc_init(void); +rt_err_t soft_rtc_get_time(struct tm *time); +rt_err_t soft_rtc_set_time(const struct tm *time); +int rt_hw_soft_rtc_init(void); + +#endif /* __SOFT_RTC_H__ */ + From be1ef1f9dc254b448b7d771184e987b9fabc8484 Mon Sep 17 00:00:00 2001 From: rcitach Date: Tue, 30 Jun 2026 10:21:27 +0000 Subject: [PATCH 3/4] Improve the CAN driver --- .../drivers/config/rt7/can_config.h | 60 + .../libraries/HAL_Drivers/drivers/drv_can.c | 1032 +++++++++++++---- .../libraries/HAL_Drivers/drivers/drv_can.h | 32 +- .../HAL_Drivers/drivers/drv_config.h | 1 + .../.ci/attachconfig/ci.attachconfig.yml | 7 + .../ns800/ns800rt7p65-nssinepad/board/Kconfig | 9 + 6 files changed, 902 insertions(+), 239 deletions(-) create mode 100644 bsp/novosns/ns800/libraries/HAL_Drivers/drivers/config/rt7/can_config.h diff --git a/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/config/rt7/can_config.h b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/config/rt7/can_config.h new file mode 100644 index 00000000000..acadf2e2ce0 --- /dev/null +++ b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/config/rt7/can_config.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __CAN_CONFIG_H__ +#define __CAN_CONFIG_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define CAN_USING_INTERNAL_CLOCK 0U +#define CAN_USING_EXTERNAL_OSC 1U + +#ifndef CAN_CLOCK_SELECTION +#define CAN_CLOCK_SELECTION CAN_USING_EXTERNAL_OSC +#endif + +#if CAN_CLOCK_SELECTION == CAN_USING_EXTERNAL_OSC +#define BSP_CAN_CLOCK_FREQ HXTL_FREQ_VALUE +#elif CAN_CLOCK_SELECTION == CAN_USING_INTERNAL_CLOCK +#define BSP_CAN_CLOCK_FREQ MIRC2_FREQ_VALUE +#else +#error "Unsupported CAN_CLOCK_SELECTION" +#endif + +#ifndef BSP_CAN1_TX_PIN +#define BSP_CAN1_TX_PIN GPIO_19_CAN1_TX +#endif + +#ifndef BSP_CAN1_RX_PIN +#define BSP_CAN1_RX_PIN GPIO_18_CAN1_RX +#endif + +#ifndef BSP_CANFD1_TX_PIN +#define BSP_CANFD1_TX_PIN GPIO_4_CANFD1_TX +#endif + +#ifndef BSP_CANFD1_RX_PIN +#define BSP_CANFD1_RX_PIN GPIO_10_CANFD1_RX +#endif + +#ifndef BSP_CANFD2_TX_PIN +#define BSP_CANFD2_TX_PIN GPIO_6_CANFD2_TX +#endif + +#ifndef BSP_CANFD2_RX_PIN +#define BSP_CANFD2_RX_PIN GPIO_7_CANFD2_RX +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __CAN_CONFIG_H__ */ + diff --git a/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_can.c b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_can.c index ddba6a3f0f7..db7699b7d53 100644 --- a/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_can.c +++ b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_can.c @@ -5,20 +5,12 @@ * * Change Logs: * Date Author Notes - * 2018-08-05 Xeon Xu the first version - * 2019-01-22 YLZ port from stm324xx-HAL to bsp stm3210x-HAL - * 2019-02-19 YLZ add support EXTID RTR Frame. modify send, recv functions. - * fix bug.port to BSP [stm32] - * 2019-03-27 YLZ support double can channels, support stm32F4xx (only Legacy mode). - * 2019-06-17 YLZ port to new STM32F1xx HAL V1.1.3. - * 2021-02-02 YuZhe XU fix bug in filter config - * 2021-8-25 SVCHAO The baud rate is configured according to the different APB1 frequencies. - f4-series only. - * 2025-09-20 wdfk_prog Implemented sendmsg_nonblocking op to support framework's async TX. - * 2026-02-02 wdfk_prog Drain multiple RX frames per ISR with a bounded limit. + * 2026-05-30 hu.tang Add CAN-FD full support: dynamic MB size, FD data path, + * FD control commands, sendmsg_nonblocking, fix hdr_index bug. */ #include "drv_can.h" +#include "drv_config.h" #ifdef BSP_USING_CAN @@ -26,11 +18,28 @@ #include -#define RX_MB_COUNT (1) -static FLEXCANDRV_MsgObjType frame[RX_MB_COUNT]; /* one frame buffer per RX MB */ +#define RX_MB_COUNT NS800RT7_CAN_RX_MB_COUNT +#define TX_MB_COUNT NS800RT7_CAN_TX_MB_COUNT /* MB0 is dedicated TX */ +#define TOTAL_MB_COUNT NS800RT7_CAN_TOTAL_MB_COUNT -void CAN_Handler(void); -void CAN_Err_Handler(void); +#if !defined(BSP_USING_CAN1) && !defined(BSP_USING_CANFD1) && !defined(BSP_USING_CANFD2) +#error "BSP_USING_CAN requires at least one CAN instance" +#endif + +#ifdef BSP_USING_CAN1 +void CAN1_1_IRQHandler(void); +void CAN1_2_IRQHandler(void); +#endif + +#ifdef BSP_USING_CANFD1 +void CANFD1_1_IRQHandler(void); +void CANFD1_2_IRQHandler(void); +#endif + +#ifdef BSP_USING_CANFD2 +void CANFD2_1_IRQHandler(void); +void CANFD2_2_IRQHandler(void); +#endif static const struct ns800rt7_baud_rate_tab can_baud_rate_tab[] = @@ -50,93 +59,153 @@ static const struct ns800rt7_baud_rate_tab can_baud_rate_tab[] = static ns800rt7_can drv_can1 = { .name = "can1", + .instance = NS800RT7_CAN_INSTANCE_CAN1, .CanHandle.flexCanReg = (FLEXCANREG_TypeDef *)CAN1, .irqn1 = CAN1_1_IRQn, .irqn2 = CAN1_2_IRQn, + .irq_handler = CAN1_1_IRQHandler, + .err_irq_handler = CAN1_2_IRQHandler, + .tx_pin = {BSP_CAN1_TX_PIN}, + .rx_pin = {BSP_CAN1_RX_PIN}, + .can_instance_idx = 0U, + .can_ram_num = 1U, + .fd_capable = RT_FALSE, }; #endif #ifdef BSP_USING_CANFD1 -static ns800rt7_can drv_can1 = +static ns800rt7_can drv_canfd1 = { .name = "canfd1", + .instance = NS800RT7_CAN_INSTANCE_CANFD1, .CanHandle.flexCanReg = (FLEXCANREG_TypeDef *)CANFD1, .irqn1 = CANFD1_1_IRQn, .irqn2 = CANFD1_2_IRQn, + .irq_handler = CANFD1_1_IRQHandler, + .err_irq_handler = CANFD1_2_IRQHandler, + .tx_pin = {BSP_CANFD1_TX_PIN}, + .rx_pin = {BSP_CANFD1_RX_PIN}, + .can_instance_idx = 1U, + .can_ram_num = 4U, + .fd_capable = RT_TRUE, }; #endif #ifdef BSP_USING_CANFD2 -static ns800rt7_can drv_can1 = +static ns800rt7_can drv_canfd2 = { .name = "canfd2", + .instance = NS800RT7_CAN_INSTANCE_CANFD2, .CanHandle.flexCanReg = (FLEXCANREG_TypeDef *)CANFD2, .irqn1 = CANFD2_1_IRQn, .irqn2 = CANFD2_2_IRQn, + .irq_handler = CANFD2_1_IRQHandler, + .err_irq_handler = CANFD2_2_IRQHandler, + .tx_pin = {BSP_CANFD2_TX_PIN}, + .rx_pin = {BSP_CANFD2_RX_PIN}, + .can_instance_idx = 2U, + .can_ram_num = 4U, + .fd_capable = RT_TRUE, }; #endif -static void _can_gpio_init(void) +static ns800rt7_can * const can_devices[] = { #ifdef BSP_USING_CAN1 - GPIO_setAnalogMode(GPIO_4, GPIO_ANALOG_DISABLED); - GPIO_setAnalogMode(GPIO_10, GPIO_ANALOG_DISABLED); - GPIO_setPinConfig(GPIO_4_CANFD1_TX); - GPIO_setPinConfig(GPIO_10_CANFD1_RX); + &drv_can1, #endif - #ifdef BSP_USING_CANFD1 - GPIO_setAnalogMode(GPIO_4, GPIO_ANALOG_DISABLED); - GPIO_setAnalogMode(GPIO_10, GPIO_ANALOG_DISABLED); - GPIO_setPinConfig(GPIO_4_CANFD1_TX); - GPIO_setPinConfig(GPIO_10_CANFD1_RX); + &drv_canfd1, #endif - #ifdef BSP_USING_CANFD2 - GPIO_setAnalogMode(GPIO_4, GPIO_ANALOG_DISABLED); - GPIO_setAnalogMode(GPIO_10, GPIO_ANALOG_DISABLED); - GPIO_setPinConfig(GPIO_4_CANFD1_TX); - GPIO_setPinConfig(GPIO_10_CANFD1_RX); + &drv_canfd2, #endif +}; + +static void _can_gpio_init(ns800rt7_can *drv_can) +{ + GPIO_setAnalogMode(drv_can->tx_pin.port, drv_can->tx_pin.pin, GPIO_ANALOG_DISABLED); + GPIO_setAnalogMode(drv_can->rx_pin.port, drv_can->rx_pin.pin, GPIO_ANALOG_DISABLED); + GPIO_setPinConfig(drv_can->tx_pin.port, drv_can->tx_pin.pin, drv_can->tx_pin.alt_func); + GPIO_setPinConfig(drv_can->rx_pin.port, drv_can->rx_pin.pin, drv_can->rx_pin.alt_func); + GPIO_setQualificationMode(drv_can->rx_pin.port, drv_can->rx_pin.pin, GPIO_QUAL_ASYNC); } -static void _can_clock_init(void) +static rt_err_t _can_clock_init(ns800rt7_can *drv_can) { - /* unlock RCC register access */ + rt_err_t ret = RT_EOK; + RCC_unlockRccRegister(); #if CAN_CLOCK_SELECTION == CAN_USING_EXTERNAL_OSC - - /* setup external OSC if selected */ - /* configure HXTL */ WRITE_REG(RCC->HXTLCR.WORDVAL, (HXTL_CONFIG | RCC_HXTLCR_KEY)); RCC_enableHxtl(); RCC_disableHxtlBypass(); - uint32_t wait_time = 5000000; - while ((RCC_readHxtlRdyFlag() != 1) && (--wait_time)); - while (wait_time == 0) { + uint32_t wait_time = 5000000; + + while ((RCC_readHxtlRdyFlag() != 1U) && (--wait_time)) + { + } + + if (wait_time == 0U) + { + ret = -RT_ETIMEOUT; + goto __exit; + } } - /* enable MIRC2 and HXTL kernal func */ RCC_enablePeriphKernalUseMirc2(); RCC_enablePeriphKernalUseHxtl(); - /* select the HXTL as the working clocks for CANFD1 */ - RCC_selectCanfdOscClkSource(RCC_CANFD1SEL, RCC_CANFD_OSCCLK_SEL_HXTLKER); + drv_can->clock_freq = HXTL_FREQ_VALUE; #elif CAN_CLOCK_SELECTION == CAN_USING_INTERNAL_CLOCK - - /* enable MIRC2 kernal func */ - RCC_enableKernalUseMirc2(); - /* select the HXTL as the working clocks for CANFD1 */ - RCC_selectCanfdOscClkSource(RCC_CANFD1SEL, RCC_CANFD_OSCCLK_SEL_MIRC2KER); + RCC_enablePeriphKernalUseMirc2(); + drv_can->clock_freq = MIRC2_FREQ_VALUE; +#else +#error "Unsupported CAN_CLOCK_SELECTION" #endif - /* enable CANFD clocks */ - RCC_enableCanfd1Clock(); + switch (drv_can->instance) + { + case NS800RT7_CAN_INSTANCE_CAN1: +#if CAN_CLOCK_SELECTION == CAN_USING_EXTERNAL_OSC + RCC_selectCan1OscClkSource(RCC_CAN1_OSCCLK_SEL_HXTLKER); +#else + RCC_selectCan1OscClkSource(RCC_CAN1_OSCCLK_SEL_MIRC2KER); +#endif + RCC_enableCan1Clock(); + RCC_resetCan1Module(); + RCC_releaseCan1Module(); + break; + case NS800RT7_CAN_INSTANCE_CANFD1: +#if CAN_CLOCK_SELECTION == CAN_USING_EXTERNAL_OSC + RCC_selectCanfdOscClkSource(RCC_CANFD1SEL, RCC_CANFD_OSCCLK_SEL_HXTLKER); +#else + RCC_selectCanfdOscClkSource(RCC_CANFD1SEL, RCC_CANFD_OSCCLK_SEL_MIRC2KER); +#endif + RCC_enableCanfd1Clock(); + RCC_resetCanfd1Module(); + RCC_releaseCanfd1Module(); + break; + case NS800RT7_CAN_INSTANCE_CANFD2: +#if CAN_CLOCK_SELECTION == CAN_USING_EXTERNAL_OSC + RCC_selectCanfdOscClkSource(RCC_CANFD2SEL, RCC_CANFD_OSCCLK_SEL_HXTLKER); +#else + RCC_selectCanfdOscClkSource(RCC_CANFD2SEL, RCC_CANFD_OSCCLK_SEL_MIRC2KER); +#endif + RCC_enableCanfd2Clock(); + RCC_resetCanfd2Module(); + RCC_releaseCanfd2Module(); + break; + default: + ret = -RT_ERROR; + break; + } - /* lock RCC register access */ +__exit: RCC_lockRccRegister(); + return ret; } static rt_uint32_t get_can_baud_index(rt_uint32_t baud) @@ -153,37 +222,94 @@ static rt_uint32_t get_can_baud_index(rt_uint32_t baud) return 0; /* default baud is CAN1MBaud */ } -static rt_err_t _can_sendBlocking(ns800rt7_can *base, FLEXCANDRV_MsgObjType *TxMsgObj) +static rt_err_t _can_sendBlocking(ns800rt7_can *base, FLEXCANDRV_MsgObjType *TxMsgObj, + rt_uint32_t msg_id, rt_uint8_t is_ext_id, + rt_uint8_t brs) { - /* Check if Message Buffer is idle. */ - if (StateIdle == base->mbState[TxMsgObj->msgBufId]) + /* Check if Message Buffer is idle. + * In loopback mode the controller may generate a spurious TX-done event + * right after FLEXCANDRV_Configure, leaving the software state in limbo. + * If the MB is not idle, wait briefly for the ISR to catch up. */ + if (StateIdle != base->mbState[TxMsgObj->msgBufId]) + { + rt_uint32_t retry; + for (retry = 0; retry < 50; retry++) + { + rt_thread_mdelay(1); + if (StateIdle == base->mbState[TxMsgObj->msgBufId]) + { + break; + } + } + + if (StateIdle != base->mbState[TxMsgObj->msgBufId]) + { + LOG_E("sendBlocking: MB%u not idle, state=%u", + (unsigned int)TxMsgObj->msgBufId, + (unsigned int)base->mbState[TxMsgObj->msgBufId]); + return -RT_EBUSY; + } + } + { base->mbState[TxMsgObj->msgBufId] = StateTxData; + /* Update MB CS and ID words before sending. + * FLEXCANDRV_SetTxMsg() only copies data bytes; it does NOT write + * the message ID or DLC. Those are set once during + * FLEXCANDRV_CfgMsgObj() and never touched again without this + * step every frame goes out with the same ID (0x78). */ + uint32_t *msgBufPtr = (uint32_t *)FLEXCANDRV_GetMsgBufStartAddr( + &base->CanHandle, TxMsgObj->msgBufId); + FLEXCANDRV_SetMsgId(msgBufPtr, msg_id, (bool)is_ext_id); + FLEXCANDRV_SetMsgDLC(msgBufPtr, (FLEXCANDRV_DataLenCodeType)TxMsgObj->dlc); + + /* Set FD/EDL and BRS bits independently in the CS word. + * FLEXCANDRV_SetFDEnable() forces BRS=1 when fdEnable=true, + * which prevents sending FD frames with BRS=0. + * Direct CS-word manipulation allows per-frame BRS control. */ + if (TxMsgObj->isFd) + { + msgBufPtr[MESSAGE_BUFFER_CS_WORD_NUM] |= MESSAGE_BUFFER_CS_EDL_MASK; + if (brs) + { + msgBufPtr[MESSAGE_BUFFER_CS_WORD_NUM] |= MESSAGE_BUFFER_CS_BRS_MASK; + } + else + { + msgBufPtr[MESSAGE_BUFFER_CS_WORD_NUM] &= ~MESSAGE_BUFFER_CS_BRS_MASK; + } + } + else + { + msgBufPtr[MESSAGE_BUFFER_CS_WORD_NUM] &= ~(MESSAGE_BUFFER_CS_EDL_MASK + | MESSAGE_BUFFER_CS_BRS_MASK); + } + FLEXCANDRV_SetTxMsg(&base->CanHandle, TxMsgObj); /* transmit canfd Tx message */ FLEXCANDRV_TransmitMsg(&base->CanHandle, TxMsgObj); return RT_EOK; } - else - { - return -RT_ERROR; - } } -static rt_err_t _can_receiveNonBlocking(ns800rt7_can *base, FLEXCANDRV_MsgObjType *RxMsgObj) +static void _can_apply_bit_timing(FLEXCANDRV_BitTimingType *dst, + const struct rt_can_bit_timing *src) { - /* Check if Message Buffer is idle. */ - if (StateIdle == base->mbState[RxMsgObj->msgBufId]) - { - base->mbState[RxMsgObj->msgBufId] = StateRxData; + dst->prescalerDiv = src->prescaler; + dst->resyncJumpWidth = src->num_sjw; + dst->phaseSeg2 = src->num_seg2; - return RT_EOK; + if (src->num_seg1 > src->num_seg2) + { + dst->propSeg = src->num_seg1 - src->num_seg2; + dst->phaseSeg1 = src->num_seg2; } else { - return -RT_ERROR; + dst->propSeg = 0U; + dst->phaseSeg1 = src->num_seg1; } } @@ -191,54 +317,115 @@ static rt_err_t _can_config(struct rt_can_device *can, struct can_configure *cfg { ns800rt7_can *drv_can; rt_uint32_t baud_index; - uint32_t FlexcanClockFreq = HXTL_FREQ_VALUE; + uint32_t flexcan_clock_freq; + rt_uint8_t use_fd = 0U; + rt_err_t ret; RT_ASSERT(can); RT_ASSERT(cfg); drv_can = (ns800rt7_can *)can->parent.user_data; RT_ASSERT(drv_can); - _can_gpio_init(); - _can_clock_init(); +#ifdef RT_CAN_USING_CANFD + use_fd = (cfg->enable_canfd && drv_can->fd_capable) ? 1U : 0U; + if (cfg->enable_canfd && !drv_can->fd_capable) + { + LOG_E("%s does not support CAN-FD", drv_can->name); + return -RT_ERROR; + } +#endif - FLEXCANDRV_InitHwParType initHwPar; - initHwPar.canRamNum = 4; - initHwPar.canInstanceIdx = 1; - FLEXCANDRV_Init(&(drv_can->CanHandle), drv_can->CanHandle.flexCanReg, &initHwPar); + _can_gpio_init(drv_can); + ret = _can_clock_init(drv_can); + if (ret != RT_EOK) + { + return ret; + } + flexcan_clock_freq = drv_can->clock_freq ? drv_can->clock_freq : BSP_CAN_CLOCK_FREQ; + + { + FLEXCANDRV_InitHwParType initHwPar; + + initHwPar.canRamNum = drv_can->can_ram_num; + initHwPar.canInstanceIdx = drv_can->can_instance_idx; + FLEXCANDRV_Init(&(drv_can->CanHandle), drv_can->CanHandle.flexCanReg, &initHwPar); + } - /* get FLEXCAN controller default configuration */ FLEXCANDRV_GetDefaultCfg(&(drv_can->CanCfg)); baud_index = get_can_baud_index(cfg->baud_rate); - FLEXCANDRV_BitTimingCalc(&(drv_can->CanCfg.bitTiming), - FlexcanClockFreq, /* module clock source*/ - BAUD_DATA(baud_index), /* baudrate: 500K */ - 7500, /* sample point: 75% */ - 2500, /* SJW: 25% */ - 0); /* classic CAN bit timing */ - - FLEXCANDRV_BitTimingCalc(&(drv_can->CanCfg.fdBitTiming), - FlexcanClockFreq, /* module clock source*/ - BAUD_DATA(baud_index), /* baudrate: 500K */ - 7500, /* sample point: 75% */ - 2500, /* SJW: 25% */ - 1); /* CANFD bit timing */ - - drv_can->CanCfg.clkSrc = FLEXCANDRV_CLKSRC_OSC; - drv_can->CanCfg.msgNum = drv_can->FilterNum; - drv_can->CanCfg.msgCfg = drv_can->FilterConfig; - drv_can->CanCfg.individualMaskEnable = true; - drv_can->CanCfg.rxMBGlobalMask = 0xFFFFFFFF; - drv_can->CanCfg.loopbackEnable = false; - drv_can->CanCfg.msgBufDataLenSel = FLEXCANDRV_MB_SIZE_BYTE_8; - drv_can->CanCfg.fdEnable = true; - drv_can->CanCfg.fdISOEnable = true; +#ifdef RT_CAN_USING_CANFD + if (cfg->use_bit_timing) + { + _can_apply_bit_timing(&(drv_can->CanCfg.bitTiming), &(cfg->can_timing)); + } + else +#endif + { + FLEXCANDRV_BitTimingCalc(&(drv_can->CanCfg.bitTiming), + flexcan_clock_freq, + BAUD_DATA(baud_index), + 7500, + 2500, + 0); + } + + if (use_fd) + { +#ifdef RT_CAN_USING_CANFD + if (cfg->use_bit_timing) + { + _can_apply_bit_timing(&(drv_can->CanCfg.fdBitTiming), &(cfg->canfd_timing)); + } + else + { + rt_uint32_t fd_baud = cfg->baud_rate_fd ? cfg->baud_rate_fd : BAUD_DATA(baud_index); + + FLEXCANDRV_BitTimingCalc(&(drv_can->CanCfg.fdBitTiming), + flexcan_clock_freq, + fd_baud, + 7500, + 2500, + 1); + } +#endif + drv_can->CanCfg.fdEnable = true; + drv_can->CanCfg.fdISOEnable = true; + drv_can->CanCfg.msgBufDataLenSel = FLEXCANDRV_MB_SIZE_BYTE_64; + } + else + { + if (drv_can->fd_capable) + { + FLEXCANDRV_BitTimingCalc(&(drv_can->CanCfg.fdBitTiming), + flexcan_clock_freq, + BAUD_DATA(baud_index), + 7500, + 2500, + 1); + drv_can->CanCfg.fdEnable = true; + drv_can->CanCfg.fdISOEnable = true; + } + else + { + drv_can->CanCfg.fdEnable = false; + drv_can->CanCfg.fdISOEnable = false; + } + drv_can->CanCfg.msgBufDataLenSel = FLEXCANDRV_MB_SIZE_BYTE_8; + } + + drv_can->CanCfg.clkSrc = FLEXCANDRV_CLKSRC_OSC; + drv_can->CanCfg.msgNum = drv_can->FilterNum; + drv_can->CanCfg.msgCfg = drv_can->FilterConfig; + drv_can->CanCfg.individualMaskEnable = true; + drv_can->CanCfg.rxMBGlobalMask = 0xFFFFFFFF; + drv_can->CanCfg.loopbackEnable = false; + drv_can->CanCfg.listenMode = false; switch (cfg->mode) { case RT_CAN_MODE_NORMAL: - /* default mode */ break; case RT_CAN_MODE_LISTEN: drv_can->CanCfg.listenMode = true; @@ -247,13 +434,42 @@ static rt_err_t _can_config(struct rt_can_device *can, struct can_configure *cfg drv_can->CanCfg.loopbackEnable = true; break; case RT_CAN_MODE_LOOPBACKANLISTEN: + drv_can->CanCfg.listenMode = true; + drv_can->CanCfg.loopbackEnable = true; break; + default: + return -RT_ERROR; + } + + { + rt_uint8_t i; + + for (i = 0; i < drv_can->FilterNum; i++) + { + drv_can->FilterConfig[i].isFd = use_fd; + drv_can->FilterConfig[i].dlc = use_fd ? DLC_BYTE_64 : DLC_BYTE_8; + } } - /* init can */ - /* initialize FLEXCAN module */ FLEXCANDRV_Configure(&(drv_can->CanHandle), &(drv_can->CanCfg)); +#ifdef RT_CAN_USING_CANFD + drv_can->device.config.enable_canfd = use_fd; + drv_can->device.config.baud_rate_fd = cfg->baud_rate_fd; + drv_can->device.config.use_bit_timing = cfg->use_bit_timing; + drv_can->device.config.can_timing = cfg->can_timing; + drv_can->device.config.canfd_timing = cfg->canfd_timing; +#endif + + { + rt_uint8_t k; + + for (k = 0; k < sizeof(drv_can->mbState) / sizeof(drv_can->mbState[0]); k++) + { + drv_can->mbState[k] = StateIdle; + } + } + return RT_EOK; } @@ -261,9 +477,6 @@ static rt_err_t _can_control(struct rt_can_device *can, int cmd, void *arg) { rt_uint32_t argval; ns800rt7_can *drv_can; - struct rt_can_filter_config *filter_cfg; - struct rt_can_filter_item *item; - rt_uint8_t i, count, index; RT_ASSERT(can != RT_NULL); drv_can = (ns800rt7_can *)can->parent.user_data; @@ -280,17 +493,94 @@ static rt_err_t _can_control(struct rt_can_device *can, int cmd, void *arg) argval = (rt_uint32_t) arg; if (argval == RT_DEVICE_FLAG_INT_RX || argval == RT_DEVICE_FLAG_INT_TX) { - Interrupt_register(drv_can->irqn1, &CAN_Handler); + Interrupt_register(drv_can->irqn1, drv_can->irq_handler); Interrupt_enable(drv_can->irqn1); } else if (argval == RT_DEVICE_CAN_INT_ERR) { - Interrupt_register(drv_can->irqn2, &CAN_Err_Handler); + Interrupt_register(drv_can->irqn2, drv_can->err_irq_handler); Interrupt_enable(drv_can->irqn2); } break; case RT_CAN_CMD_SET_FILTER: - break; + { + struct rt_can_filter_config *filter_cfg; + rt_uint8_t i; + + filter_cfg = (struct rt_can_filter_config *)arg; + if (filter_cfg == RT_NULL) + { + return -RT_ERROR; + } + + if (filter_cfg->actived != 1) + { + /* Deactivate filter: do nothing for now */ + return RT_EOK; + } + + if (filter_cfg->count > RX_MB_COUNT) + { + return -RT_ERROR; + } + + /* Write user filters starting from FilterConfig[TX_MB_COUNT], + * preserving FilterConfig[0] which is the TX mailbox (MB0). + * This prevents TX MB configuration from being overwritten + * by RX filter settings. */ + for (i = 0; i < filter_cfg->count; i++) + { + struct rt_can_filter_item *item = &filter_cfg->items[i]; + rt_uint8_t idx = (rt_uint8_t)(TX_MB_COUNT + i); + + if (item->hdr_bank < -1 || item->hdr_bank >= (rt_int32_t)RX_MB_COUNT) + { + return -RT_ERROR; + } + + /* Map user hdr_bank to hardware MB index, offsetting past TX MB0. + * hdr_bank=-1 → auto-assign sequentially (MB1, MB2, ...) + * hdr_bank=N → explicit hardware MB index (N still maps + * to FilterConfig idx, via offset) */ + drv_can->FilterConfig[idx].msgBufId = + (uint16_t)(item->hdr_bank >= 0 + ? (rt_uint32_t)(TX_MB_COUNT + (rt_uint16_t)item->hdr_bank) + : idx); + drv_can->FilterConfig[idx].msgBufLen = 1; + drv_can->FilterConfig[idx].msgId = item->id; + drv_can->FilterConfig[idx].isExtMsgId = (item->ide == RT_CAN_EXTID); + drv_can->FilterConfig[idx].msgType = FLEXCANDRV_MSGTYPE_RX; /* default RX */ + drv_can->FilterConfig[idx].dlc = DLC_BYTE_8; +#ifdef RT_CAN_USING_CANFD + drv_can->FilterConfig[idx].isFd = + drv_can->fd_capable ? drv_can->device.config.enable_canfd : 0U; +#else + drv_can->FilterConfig[idx].isFd = 0U; +#endif + drv_can->FilterConfig[idx].intEnable = true; + /* FLEXCAN stores standard IDs at ID-word bits 18-28, + * extended IDs at bits 0-28. The RXIMR mask register + * is bit-aligned with the ID word, so standard-frame + * masks must be shifted left by 18 bits. */ + if (item->ide == RT_CAN_EXTID) + { + drv_can->FilterConfig[idx].individualMask = + item->mask & 0x1FFFFFFFUL; + } + else + { + drv_can->FilterConfig[idx].individualMask = + (item->mask & 0x7FFUL) << MESSAGE_BUFFER_ID_STD_ID_SHIFT; + } + drv_can->FilterConfig[idx].rtrmask = false; + drv_can->FilterConfig[idx].rtrfilter = false; + } + + /* Update FilterNum to include TX MB + user filters */ + drv_can->FilterNum = (rt_uint8_t)(TX_MB_COUNT + filter_cfg->count); + + return _can_config(&drv_can->device, &drv_can->device.config); + } case RT_CAN_CMD_SET_MODE: argval = (rt_uint32_t) arg; if (argval != RT_CAN_MODE_NORMAL && @@ -328,6 +618,87 @@ static rt_err_t _can_control(struct rt_can_device *can, int cmd, void *arg) return _can_config(&drv_can->device, &drv_can->device.config); } break; +#ifdef RT_CAN_USING_CANFD + case RT_CAN_CMD_SET_CANFD: + argval = (rt_uint32_t) arg; + if (argval > 1) + { + return -RT_ERROR; + } + + if (argval && !drv_can->fd_capable) + { + return -RT_ERROR; + } + + if ((rt_uint32_t)drv_can->device.config.enable_canfd != argval) + { + drv_can->device.config.enable_canfd = (rt_uint8_t)argval; + return _can_config(&drv_can->device, &drv_can->device.config); + } + break; + case RT_CAN_CMD_SET_BAUD_FD: + argval = (rt_uint32_t) arg; + if (argval == 0) + { + return -RT_ERROR; + } + + if (!drv_can->fd_capable) + { + return -RT_ERROR; + } + + if (argval != drv_can->device.config.baud_rate_fd) + { + drv_can->device.config.baud_rate_fd = argval; + return _can_config(&drv_can->device, &drv_can->device.config); + } + break; + case RT_CAN_CMD_SET_BITTIMING: + { + struct rt_can_bit_timing_config *timing_cfg; + struct rt_can_bit_timing *rt_timing; + + timing_cfg = (struct rt_can_bit_timing_config *)arg; + if (timing_cfg == RT_NULL || timing_cfg->items == RT_NULL) + { + return -RT_ERROR; + } + + if (timing_cfg->count < 1 || (!drv_can->fd_capable && timing_cfg->count > 1)) + { + return -RT_ERROR; + } + + if (drv_can->device.config.enable_canfd && timing_cfg->count < 2) + { + return -RT_ERROR; + } + + drv_can->device.config.use_bit_timing = 1; + + if (timing_cfg->count >= 1) + { + rt_timing = &timing_cfg->items[0]; + drv_can->device.config.can_timing = *rt_timing; + } + + if (timing_cfg->count >= 2) + { + rt_timing = &timing_cfg->items[1]; + drv_can->device.config.canfd_timing = *rt_timing; + } + + return _can_config(&drv_can->device, &drv_can->device.config); + } + break; +#endif /* RT_CAN_USING_CANFD */ + case RT_CAN_CMD_START: + /* Controller starts automatically during _can_config(). + * For a true start/stop, re-init would be needed. + * This command is a no-op for this driver. */ + break; case RT_CAN_CMD_SET_PRIV: return -RT_ERROR; break; @@ -344,82 +715,246 @@ static rt_err_t _can_control(struct rt_can_device *can, int cmd, void *arg) return RT_EOK; } -#define IS_CAN_DLC(DLC) ((DLC) <= ((uint8_t)0x08)) +/* DLC / byte-length validation helpers classic CAN vs CAN-FD */ +#ifdef RT_CAN_USING_CANFD +#ifndef CANFD_MAX_DLC +#define CANFD_MAX_DLC 15U /* per ISO 11898-1 */ +#endif + +/** + * @brief Convert data byte length to CAN-FD DLC code (per ISO 11898-1). + * Based on the standard can_len2dlc() lookup in can_dm.c. + */ +static rt_uint8_t _can_len_to_dlc(rt_uint8_t len) +{ + static const rt_uint8_t len2dlc[65] = + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, /* 0-8 bytes DLC 0-8 */ + 9, 9, 9, 9, /* 9-12 bytes DLC 9 */ + 10, 10, 10, 10, /* 13-16 bytes DLC 10 */ + 11, 11, 11, 11, /* 17-20 bytes DLC 11 */ + 12, 12, 12, 12, /* 21-24 bytes DLC 12 */ + 13, 13, 13, 13, 13, 13, 13, 13, /* 25-32 bytes DLC 13 */ + 14, 14, 14, 14, 14, 14, 14, 14, /* 33-48 bytes DLC 14 */ + 14, 14, 14, 14, 14, 14, 14, 14, /* (continued) */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 49-64 bytes DLC 15 */ + 15, 15, 15, 15, 15, 15, 15, 15 /* (continued) */ + }; + return len < 65 ? len2dlc[len] : (rt_uint8_t)CANFD_MAX_DLC; +} + +#define IS_CAN_LEN_VALID(LEN) ((LEN) <= 64U) +#else +#define IS_CAN_LEN_VALID(LEN) ((LEN) <= 8U) +#endif /** * @internal * @brief Low-level function to send a CAN message to a specific hardware mailbox. * * This function is part of the **blocking** send mechanism. It is called by - * `_can_int_tx` after a hardware mailbox has already been acquired. Its role is - * to format the message according to the STM32 hardware requirements and place - * it into the specified mailbox for transmission. + * `_can_int_tx` after a hardware mailbox has already been acquired. * * @param[in] can A pointer to the CAN device structure. * @param[in] buf A pointer to the `rt_can_msg` to be sent. - * @param[in] box_num The specific hardware mailbox index (0, 1, or 2) to use for this tran + * @param[in] box_no The specific hardware mailbox index to use for this + * transmission. * * @return `RT_EOK` on success, or an error code on failure. */ -static rt_ssize_t _can_sendmsg(struct rt_can_device *can, const void *buf, rt_uint32_t box_num) +static rt_ssize_t _can_sendmsg(struct rt_can_device *can, const void *buf, rt_uint32_t box_no) { rt_ssize_t status; ns800rt7_can *hcan; - hcan = (ns800rt7_can *) can->parent.user_data; - struct rt_can_msg *pmsg = (struct rt_can_msg *) buf; + struct rt_can_msg *pmsg; FLEXCANDRV_MsgObjType fdTxMsgObj; + rt_uint8_t data_len; - /* Check the parameters */ - RT_ASSERT(IS_CAN_DLC(pmsg->len)); + hcan = (ns800rt7_can *) can->parent.user_data; + pmsg = (struct rt_can_msg *) buf; - fdTxMsgObj.msgBufId = box_num; + if (box_no >= TX_MB_COUNT || !IS_CAN_LEN_VALID(pmsg->len)) + { + return -RT_ERROR; + } - /* Set up the DLC */ - fdTxMsgObj.dlc = pmsg->len & 0x0FU; - /* Set up the data field */ - for(uint8_t i=0u; i<8u; i++) +#ifdef RT_CAN_USING_CANFD + if (pmsg->fd_frame && (!hcan->fd_capable || !hcan->device.config.enable_canfd)) { - fdTxMsgObj.data[i] = pmsg->data[i]; + return -RT_ERROR; } - status = _can_sendBlocking(hcan, &fdTxMsgObj); + if (!pmsg->fd_frame && pmsg->len > 8U) + { + return -RT_ERROR; + } +#endif + + rt_memset(&fdTxMsgObj, 0, sizeof(fdTxMsgObj)); + fdTxMsgObj.msgBufId = box_no; + + /* Convert byte length to hardware DLC code and determine payload size */ + data_len = pmsg->len; +#ifdef RT_CAN_USING_CANFD + fdTxMsgObj.dlc = _can_len_to_dlc(pmsg->len); + fdTxMsgObj.isFd = (pmsg->fd_frame != 0 && hcan->device.config.enable_canfd) ? true : false; +#else + fdTxMsgObj.dlc = pmsg->len; /* classic CAN: DLC == byte length (0-8) */ +#endif + + /* Copy payload */ + rt_memcpy(fdTxMsgObj.data, pmsg->data, data_len); + + status = _can_sendBlocking(hcan, &fdTxMsgObj, + pmsg->id, (pmsg->ide == RT_CAN_EXTID) ? 1 : 0, +#ifdef RT_CAN_USING_CANFD + (rt_uint8_t)(pmsg->brs != 0 ? 1 : 0)); +#else + 0U); +#endif return status; } -static rt_ssize_t _can_recvmsg(struct rt_can_device *can, void *buf, rt_uint32_t fifo) +/** + * @internal + * @brief Low-level non-blocking send. Places message into hardware mailbox + * directly without waiting if the mailbox is idle. + * + * @param[in] can A pointer to the CAN device structure. + * @param[in] buf A pointer to the `rt_can_msg` to be sent. + * + * @return `RT_EOK` on success, `-RT_EBUSY` if TX mailbox is busy, + * or other error code on failure. + */ +static rt_ssize_t _can_sendmsg_nonblocking(struct rt_can_device *can, const void *buf) { - FLEXCANDRV_Type *hcan; + ns800rt7_can *hcan; struct rt_can_msg *pmsg; + FLEXCANDRV_MsgObjType fdTxMsgObj; + rt_uint8_t data_len; RT_ASSERT(can); + RT_ASSERT(buf); + hcan = (ns800rt7_can *)can->parent.user_data; + pmsg = (struct rt_can_msg *)buf; + + /* Check if TX MB (MB0) is idle */ + if (StateIdle != hcan->mbState[0]) + { + return -RT_EBUSY; + } + + /* Validate payload byte length */ + if (!IS_CAN_LEN_VALID(pmsg->len)) + { + return -RT_ERROR; + } + +#ifdef RT_CAN_USING_CANFD + if (pmsg->fd_frame && (!hcan->fd_capable || !hcan->device.config.enable_canfd)) + { + return -RT_ERROR; + } - hcan = &(((ns800rt7_can *)can->parent.user_data)->CanHandle); + if (!pmsg->fd_frame && pmsg->len > 8U) + { + return -RT_ERROR; + } +#endif + + rt_memset(&fdTxMsgObj, 0, sizeof(fdTxMsgObj)); + fdTxMsgObj.msgBufId = 0; /* MB0 is the dedicated TX mailbox */ + + /* Convert byte length to hardware DLC code */ + data_len = pmsg->len; +#ifdef RT_CAN_USING_CANFD + fdTxMsgObj.dlc = _can_len_to_dlc(pmsg->len); + fdTxMsgObj.isFd = (pmsg->fd_frame != 0 && hcan->device.config.enable_canfd) ? true : false; +#else + fdTxMsgObj.dlc = pmsg->len; +#endif + + rt_memcpy(fdTxMsgObj.data, pmsg->data, data_len); + + return _can_sendBlocking(hcan, &fdTxMsgObj, + pmsg->id, (pmsg->ide == RT_CAN_EXTID) ? 1 : 0, +#ifdef RT_CAN_USING_CANFD + (rt_uint8_t)(pmsg->brs != 0 ? 1 : 0)); +#else + 0U); +#endif +} + +static rt_ssize_t _can_recvmsg(struct rt_can_device *can, void *buf, rt_uint32_t fifo) +{ + struct rt_can_msg *pmsg; + ns800rt7_can *hcan; + rt_uint8_t data_len; + rt_uint8_t frame_idx; + rt_uint8_t mb_idx; + + RT_ASSERT(can); + hcan = (ns800rt7_can *)can->parent.user_data; pmsg = (struct rt_can_msg *) buf; - rt_uint8_t index; + /* Zero out the message, then fill from hardware frame buffer */ + rt_memset(pmsg, 0, sizeof(struct rt_can_msg)); + pmsg->rxfifo = (rt_uint32_t)fifo; + + /* Select the correct frame buffer: MB1:rx_frame[0], MB2:rx_frame[1] */ + frame_idx = (fifo >= TX_MB_COUNT && fifo < TOTAL_MB_COUNT) ? + (rt_uint8_t)(fifo - TX_MB_COUNT) : 0U; + mb_idx = (rt_uint8_t)fifo; /* hardware MB index (1 or 2) */ + + /* Set hdr_index to the filter bank index (0 for MB1, 1 for MB2). + * When RT_CAN_USING_HDR is enabled, rt_hw_can_isr() asserts + * hdr_index >= 0 and hdr_index < maxhdr. -1 would trigger + * an assertion failure. */ + pmsg->hdr_index = (rt_int8_t)frame_idx; + + /* rx_frame[N].dlc is already converted from DLC code to byte length + * by FLEXCANDRV_DLC2DataLen() inside FLEXCANDRV_GetRxMsg(). + * Store as byte length in the RT-Thread message len field. */ + pmsg->id = hcan->rx_frame[frame_idx].msgId; + data_len = (rt_uint8_t)hcan->rx_frame[frame_idx].dlc; + if (data_len > sizeof(pmsg->data)) + { + data_len = sizeof(pmsg->data); + } + pmsg->len = data_len; + + /* Read IDE, RTR, and BRS from the hardware MB CS word. + * FLEXCANDRV_GetRxMsg() does not report these fields individually, + * but they are available in the CS word of the received frame. + * CS word bits: 31=EDL(FD), 30=BRS, 21=IDE, 20=RTR */ + { + uint32_t *msgBufPtr = (uint32_t *)FLEXCANDRV_GetMsgBufStartAddr( + &hcan->CanHandle, mb_idx); + uint32_t cs_word = msgBufPtr[MESSAGE_BUFFER_CS_WORD_NUM]; + pmsg->ide = (cs_word & MESSAGE_BUFFER_CS_IDE_MASK) + ? RT_CAN_EXTID : RT_CAN_STDID; + pmsg->rtr = (cs_word & MESSAGE_BUFFER_CS_RTR_MASK) + ? RT_CAN_RTR : RT_CAN_DTR; +#ifdef RT_CAN_USING_CANFD + pmsg->fd_frame = (cs_word & MESSAGE_BUFFER_CS_EDL_MASK) ? 1 : 0; + pmsg->brs = (cs_word & MESSAGE_BUFFER_CS_BRS_MASK) ? 1 : 0; +#endif + } - pmsg->hdr_index = index; /* one hdr filter per MB */ - pmsg->len = frame[0].dlc; - pmsg->id = frame[0].msgId; - pmsg->data[0] = frame[0].data[0]; - pmsg->data[1] = frame[0].data[1]; - pmsg->data[2] = frame[0].data[2]; - pmsg->data[3] = frame[0].data[3]; - pmsg->data[4] = frame[0].data[4]; - pmsg->data[5] = frame[0].data[5]; - pmsg->data[6] = frame[0].data[6]; - pmsg->data[7] = frame[0].data[7]; + /* Bulk copy payload from frame buffer */ + rt_memcpy(pmsg->data, hcan->rx_frame[frame_idx].data, data_len); return RT_EOK; } static const struct rt_can_ops _can_ops = { - .configure = _can_config, - .control = _can_control, - .sendmsg = _can_sendmsg, - .recvmsg = _can_recvmsg, + .configure = _can_config, + .control = _can_control, + .sendmsg = _can_sendmsg, + .recvmsg = _can_recvmsg, + .sendmsg_nonblocking = _can_sendmsg_nonblocking, }; static uint32_t FLEXCANREG_GetMsgBufInterruptFlagAll(FLEXCANREG_TypeDef *obj) @@ -464,16 +999,14 @@ static uint8_t FLEXCANREG_GetMsgBufInterruptMask (FLEXCANREG_TypeDef *obj, uint3 static void _can_tx_rx_isr(struct rt_can_device *can) { uint8_t flag, mask; - RT_ASSERT(can); ns800rt7_can *hcan; - hcan = (ns800rt7_can *) can->parent.user_data; FLEXCANDRV_MsgObjType msgObj; + uint32_t result; - /* Assertion. */ + RT_ASSERT(can); + hcan = (ns800rt7_can *) can->parent.user_data; RT_ASSERT(hcan); - uint32_t result; - do { /* For this implementation, we solve the Message with lowest MB index first. */ @@ -494,30 +1027,22 @@ static void _can_tx_rx_isr(struct rt_can_device *can) break; } - /* Get current State of Message Buffer. */ - switch (result) + if (result == 0U) { - /* Solve Rx Data Frame. */ - case 1: - msgObj.msgBufId = result; - FLEXCANDRV_GetRxMsg(&hcan->CanHandle, &msgObj); - FLEXCANDRV_ClearMsgObjFlag(&hcan->CanHandle, result); - memcpy(&frame[0], &msgObj, sizeof(msgObj)); - rt_hw_can_isr(can, RT_CAN_EVENT_RX_IND | result << 8); - hcan->mbState[result] = StateIdle; - break; - - /* Solve Tx Data Frame. */ - case 0: - rt_hw_can_isr(can, RT_CAN_EVENT_TX_DONE | result << 8); - hcan->mbState[result] = StateIdle; - break; + rt_hw_can_isr(can, RT_CAN_EVENT_TX_DONE | (result << 8)); + hcan->mbState[result] = StateIdle; + } + else if (result >= TX_MB_COUNT && result < TOTAL_MB_COUNT) + { + rt_uint8_t frame_idx = (rt_uint8_t)(result - TX_MB_COUNT); - default: - break; + msgObj.msgBufId = result; + FLEXCANDRV_GetRxMsg(&hcan->CanHandle, &msgObj); + rt_memcpy(&hcan->rx_frame[frame_idx], &msgObj, sizeof(msgObj)); + rt_hw_can_isr(can, RT_CAN_EVENT_RX_IND | (result << 8)); + hcan->mbState[result] = StateIdle; } - /* Clear resolved Message Buffer IRQ. */ FLEXCANDRV_ClearMsgObjFlag(&(hcan->CanHandle), result); } while((0 != FLEXCANREG_GetMsgBufInterruptFlagAll(hcan->CanHandle.flexCanReg))); @@ -525,62 +1050,63 @@ static void _can_tx_rx_isr(struct rt_can_device *can) static void _can_err_isr(struct rt_can_device *can) { - + RT_ASSERT(can); + ns800rt7_can *hcan = (ns800rt7_can *)can->parent.user_data; + + /* Read error status from hardware to clear interrupt flags */ + FLEXCANDRV_ErrStatusType errStatus; + rt_memset(&errStatus, 0, sizeof(errStatus)); + FLEXCANDRV_GetErrorStatusFlag(&hcan->CanHandle, &errStatus); + FLEXCANDRV_ClearErrStatusFlag(&hcan->CanHandle, FLEXCANDRV_ERR_STATUS_MASK_ALL); + + /* Update device error counters */ + FLEXCANREG_TypeDef *canReg = hcan->CanHandle.flexCanReg; + can->status.rcverrcnt = FLEXCANREG_GetEcrRxerrcnt(canReg); + can->status.snderrcnt = FLEXCANREG_GetEcrTxerrcnt(canReg); + +#ifdef RT_CAN_USING_CANFD + /* Accumulate FD-specific error counters */ + can->status.biterrcnt += errStatus.bit1FastErr; + can->status.biterrcnt += errStatus.bit0FastErr; + can->status.crcerrcnt += errStatus.crcFastErr; + can->status.formaterrcnt += errStatus.formFastErr; + can->status.bitpaderrcnt += errStatus.stuffFastErr; +#endif + can->status.ackerrcnt += errStatus.ackErr; + can->status.crcerrcnt += errStatus.crcErr; + can->status.formaterrcnt += errStatus.formErr; + can->status.bitpaderrcnt += errStatus.stuffErr; + can->status.biterrcnt += errStatus.bit1Err + errStatus.bit0Err; } #ifdef BSP_USING_CAN1 -/** - * @brief This function handles CAN1 RX0 interrupts. - */ void CAN1_1_IRQHandler(void) { rt_interrupt_enter(); -#if 0 - if() - { - _can_rx_isr(&drv_can1.device); - } - else - { - _can_tx_isr(&drv_can1.device); - } -#endif + _can_tx_rx_isr(&drv_can1.device); rt_interrupt_leave(); } -/** - * @brief This function handles CAN1 RX1 interrupts. - */ void CAN1_2_IRQHandler(void) { rt_interrupt_enter(); -#if 0 - if() - { - _can_rx_isr(&drv_can1.device); - } - else - { - _can_tx_isr(&drv_can1.device); - } -#endif + _can_err_isr(&drv_can1.device); rt_interrupt_leave(); } - #endif /* BSP_USING_CAN1 */ #ifdef BSP_USING_CANFD1 -void CAN_Handler(void) +void CANFD1_1_IRQHandler(void) { rt_interrupt_enter(); - _can_tx_rx_isr(&drv_can1.device); + _can_tx_rx_isr(&drv_canfd1.device); rt_interrupt_leave(); } -void CAN_Err_Handler(void) +void CANFD1_2_IRQHandler(void) { rt_interrupt_enter(); - _can_err_isr(&drv_can1.device); + _can_err_isr(&drv_canfd1.device); rt_interrupt_leave(); } #endif /* BSP_USING_CANFD1 */ @@ -589,67 +1115,99 @@ void CAN_Err_Handler(void) void CANFD2_1_IRQHandler(void) { rt_interrupt_enter(); - _can_sce_isr(&drv_can1.device); + _can_tx_rx_isr(&drv_canfd2.device); rt_interrupt_leave(); } void CANFD2_2_IRQHandler(void) { rt_interrupt_enter(); - _can_sce_isr(&drv_can1.device); + _can_err_isr(&drv_canfd2.device); rt_interrupt_leave(); } #endif /* BSP_USING_CANFD2 */ +static void _can_default_filter_init(ns800rt7_can *drv_can, rt_uint8_t use_fd) +{ + rt_memset(drv_can->FilterConfig, 0, sizeof(drv_can->FilterConfig)); + + drv_can->FilterConfig[0].msgBufId = 0; + drv_can->FilterConfig[0].msgBufLen = 1; + drv_can->FilterConfig[0].msgId = 0x78; + drv_can->FilterConfig[0].isExtMsgId = false; + drv_can->FilterConfig[0].msgType = FLEXCANDRV_MSGTYPE_TX; + drv_can->FilterConfig[0].dlc = use_fd ? DLC_BYTE_64 : DLC_BYTE_8; + drv_can->FilterConfig[0].isFd = use_fd; + drv_can->FilterConfig[0].intEnable = true; + drv_can->FilterConfig[0].individualMask = 0xFFFFFFFF; + drv_can->FilterConfig[0].rtrmask = false; + drv_can->FilterConfig[0].rtrfilter = false; + + drv_can->FilterConfig[1].msgBufId = 1; + drv_can->FilterConfig[1].msgBufLen = 1; + drv_can->FilterConfig[1].msgId = 0; + drv_can->FilterConfig[1].isExtMsgId = false; + drv_can->FilterConfig[1].msgType = FLEXCANDRV_MSGTYPE_RX; + drv_can->FilterConfig[1].dlc = use_fd ? DLC_BYTE_64 : DLC_BYTE_8; + drv_can->FilterConfig[1].isFd = use_fd; + drv_can->FilterConfig[1].intEnable = true; + drv_can->FilterConfig[1].individualMask = 0; + drv_can->FilterConfig[1].rtrmask = false; + drv_can->FilterConfig[1].rtrfilter = false; + + drv_can->FilterConfig[2].msgBufId = 2; + drv_can->FilterConfig[2].msgBufLen = 1; + drv_can->FilterConfig[2].msgId = 0; + drv_can->FilterConfig[2].isExtMsgId = true; + drv_can->FilterConfig[2].msgType = FLEXCANDRV_MSGTYPE_RX; + drv_can->FilterConfig[2].dlc = use_fd ? DLC_BYTE_64 : DLC_BYTE_8; + drv_can->FilterConfig[2].isFd = use_fd; + drv_can->FilterConfig[2].intEnable = true; + drv_can->FilterConfig[2].individualMask = 0; + drv_can->FilterConfig[2].rtrmask = false; + drv_can->FilterConfig[2].rtrfilter = false; + + drv_can->FilterNum = TOTAL_MB_COUNT; +} + int rt_hw_can_init(void) { int ret = RT_EOK; struct can_configure config = CANDEFAULTCONFIG; + rt_uint32_t i; + config.privmode = RT_CAN_MODE_NOPRIV; config.ticks = 50; - config.sndboxnumber = 1; + config.sndboxnumber = TX_MB_COUNT; config.msgboxsz = RX_MB_COUNT; #ifdef RT_CAN_USING_HDR config.maxhdr = RX_MB_COUNT; /* filter count,one filter per MB */ #endif - /* config default filter */ - FLEXCANDRV_MsgCfgType fdMsgCfgObj[2] = {0}; - - drv_can1.FilterConfig[0].msgBufId = 0; - drv_can1.FilterConfig[0].msgBufLen = 1; - drv_can1.FilterConfig[0].msgId = 0x78; - drv_can1.FilterConfig[0].isExtMsgId = false; - drv_can1.FilterConfig[0].msgType = FLEXCANDRV_MSGTYPE_TX; - drv_can1.FilterConfig[0].dlc = DLC_BYTE_8; - drv_can1.FilterConfig[0].isFd = false; - drv_can1.FilterConfig[0].intEnable = true; - drv_can1.FilterConfig[0].individualMask = 0xFFFFFFFF; - drv_can1.FilterConfig[0].rtrmask = false; - drv_can1.FilterConfig[0].rtrfilter = false; - - drv_can1.FilterConfig[1].msgBufId = 1; - drv_can1.FilterConfig[1].msgBufLen = 1; - drv_can1.FilterConfig[1].msgId = 0x400; - drv_can1.FilterConfig[1].isExtMsgId = false; - drv_can1.FilterConfig[1].msgType = FLEXCANDRV_MSGTYPE_RX; - drv_can1.FilterConfig[1].dlc = DLC_BYTE_8; - drv_can1.FilterConfig[1].isFd = false; - drv_can1.FilterConfig[1].intEnable = true; - drv_can1.FilterConfig[1].individualMask = 0; - drv_can1.FilterConfig[1].rtrmask = false; - drv_can1.FilterConfig[1].rtrfilter = false; - - drv_can1.FilterNum = 2; -#ifdef BSP_USING_CANFD1 - drv_can1.device.config = config; - /* register CAN1 device */ - ret = rt_hw_can_register(&drv_can1.device, - drv_can1.name, - &_can_ops, - &drv_can1); -#endif /* BSP_USING_CAN1 */ + for (i = 0; i < sizeof(can_devices) / sizeof(can_devices[0]); i++) + { + ns800rt7_can *drv_can = can_devices[i]; + struct can_configure dev_config = config; + +#ifdef RT_CAN_USING_CANFD + dev_config.enable_canfd = drv_can->fd_capable ? 1U : 0U; +#endif + _can_default_filter_init(drv_can, +#ifdef RT_CAN_USING_CANFD + dev_config.enable_canfd +#else + 0U +#endif + ); + + drv_can->device.config = dev_config; + ret = rt_hw_can_register(&drv_can->device, drv_can->name, &_can_ops, drv_can); + if (ret != RT_EOK) + { + break; + } + } return ret; } diff --git a/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_can.h b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_can.h index 9c9bac4c8e0..bb9ebe4a45e 100644 --- a/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_can.h +++ b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_can.h @@ -23,6 +23,9 @@ extern "C" { #include #define CAN_FILTER_NUM_MAX (16U) +#define NS800RT7_CAN_RX_MB_COUNT (2U) +#define NS800RT7_CAN_TX_MB_COUNT (1U) +#define NS800RT7_CAN_TOTAL_MB_COUNT (NS800RT7_CAN_TX_MB_COUNT + NS800RT7_CAN_RX_MB_COUNT) enum _can_state { @@ -40,16 +43,41 @@ struct ns800rt7_baud_rate_tab }; #define BAUD_DATA(NO) (can_baud_rate_tab[NO].config_data) +typedef enum +{ + NS800RT7_CAN_INSTANCE_CAN1 = 0U, + NS800RT7_CAN_INSTANCE_CANFD1, + NS800RT7_CAN_INSTANCE_CANFD2, +} ns800rt7_can_instance; + +typedef struct +{ + GPIO_TypeDef *port; + GPIO_PinNum pin; + GPIO_AltFunc alt_func; +} ns800rt7_can_pin; + +typedef void (*ns800rt7_can_irq_handler)(void); + -/* stm32 can device */ typedef struct { - char *name; + const char *name; + ns800rt7_can_instance instance; FLEXCANDRV_ControllerCfgType CanCfg; IRQn_Type irqn1; IRQn_Type irqn2; + ns800rt7_can_irq_handler irq_handler; + ns800rt7_can_irq_handler err_irq_handler; FLEXCANDRV_Type CanHandle; + ns800rt7_can_pin tx_pin; + ns800rt7_can_pin rx_pin; + rt_uint8_t can_instance_idx; + rt_uint8_t can_ram_num; + rt_bool_t fd_capable; + rt_uint32_t clock_freq; FLEXCANDRV_MsgCfgType FilterConfig[CAN_FILTER_NUM_MAX]; + FLEXCANDRV_MsgObjType rx_frame[NS800RT7_CAN_RX_MB_COUNT]; uint8_t FilterNum; volatile uint8_t mbState[128]; struct rt_can_device device; /* inherit from can device */ diff --git a/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_config.h b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_config.h index 7f486c47811..9bb32c68cfe 100644 --- a/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_config.h +++ b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_config.h @@ -21,6 +21,7 @@ extern "C" { #if defined(SOC_SERIES_NS800RT7) #include "rt7/uart_config.h" #include "rt7/i2c_hard_config.h" +#include "rt7/can_config.h" #endif #ifdef __cplusplus diff --git a/bsp/novosns/ns800/ns800rt7p65-nssinepad/.ci/attachconfig/ci.attachconfig.yml b/bsp/novosns/ns800/ns800rt7p65-nssinepad/.ci/attachconfig/ci.attachconfig.yml index 2fb5187ffa1..6aaaf059287 100644 --- a/bsp/novosns/ns800/ns800rt7p65-nssinepad/.ci/attachconfig/ci.attachconfig.yml +++ b/bsp/novosns/ns800/ns800rt7p65-nssinepad/.ci/attachconfig/ci.attachconfig.yml @@ -10,6 +10,13 @@ devices.adc: <<: *scons kconfig: - CONFIG_BSP_USING_ADC=y +devices.can: + <<: *scons + kconfig: + - CONFIG_BSP_USING_CAN=y + - CONFIG_BSP_USING_CAN1=y + - CONFIG_BSP_USING_CANFD1=y + - CONFIG_BSP_USING_CANFD2=y devices.spi: <<: *scons kconfig: diff --git a/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/Kconfig b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/Kconfig index 94742d97cd8..2b0491a6254 100644 --- a/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/Kconfig +++ b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/Kconfig @@ -158,11 +158,20 @@ menu "On-chip Peripheral Drivers" select RT_USING_CAN default n if BSP_USING_CAN + config BSP_USING_CAN1 + bool "Enable CAN1" + default n + config BSP_USING_CANFD1 bool "Enable CANFD1" select RT_CAN_USING_CANFD default n + config BSP_USING_CANFD2 + bool "Enable CANFD2" + select RT_CAN_USING_CANFD + default n + endif menuconfig BSP_USING_TIM From 5a86641d9b588380736a9ec547753e6f31f27d01 Mon Sep 17 00:00:00 2001 From: rcitach Date: Tue, 30 Jun 2026 10:22:45 +0000 Subject: [PATCH 4/4] Refresh menuconfig to keep it as minimal as possible --- .../ns800/ns800rt7p65-nssinepad/.config | 18 +++++++++++++++--- .../ns800/ns800rt7p65-nssinepad/rtconfig.h | 5 +++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/bsp/novosns/ns800/ns800rt7p65-nssinepad/.config b/bsp/novosns/ns800/ns800rt7p65-nssinepad/.config index adf30071e85..1b6cd779d9b 100644 --- a/bsp/novosns/ns800/ns800rt7p65-nssinepad/.config +++ b/bsp/novosns/ns800/ns800rt7p65-nssinepad/.config @@ -272,7 +272,6 @@ CONFIG_RT_CAN_NB_TX_FIFO_SIZE=256 # CONFIG_RT_USING_BLK is not set # CONFIG_RT_USING_REGULATOR is not set # CONFIG_RT_USING_POWER_SUPPLY is not set -# CONFIG_RT_USING_VIRTIO is not set CONFIG_RT_USING_PIN=y # CONFIG_RT_USING_CHERRYUSB is not set # end of Device Drivers @@ -1447,11 +1446,24 @@ CONFIG_BSP_USING_GPIO=y # CONFIG_BSP_GPIO_PIN_IRQ is not set CONFIG_BSP_USING_UART=y CONFIG_BSP_NS800_UART_TX_TIMEOUT=6000 +CONFIG_BSP_UART_DEFAULT_BAUDRATE=115200 +# CONFIG_BSP_UART_DATABITS_7 is not set +CONFIG_BSP_UART_DATABITS_8=y +# CONFIG_BSP_UART_DATABITS_9 is not set +CONFIG_BSP_UART_STOPBITS_1=y +# CONFIG_BSP_UART_STOPBITS_2 is not set CONFIG_BSP_USING_UART1=y # CONFIG_BSP_USING_UART2 is not set # CONFIG_BSP_USING_UART3 is not set # CONFIG_BSP_USING_UART4 is not set # CONFIG_BSP_USING_ECAP is not set -CONFIG_BSP_USING_CAN=y -CONFIG_BSP_USING_CANFD1=y +# CONFIG_BSP_USING_ADC is not set +# CONFIG_BSP_USING_CAN is not set +# CONFIG_BSP_USING_TIM is not set +# CONFIG_BSP_USING_RTC is not set +# CONFIG_BSP_USING_SPI is not set +# CONFIG_BSP_USING_QSPI is not set +# CONFIG_BSP_USING_WDT is not set +# CONFIG_BSP_USING_EPWM is not set +# CONFIG_BSP_USING_I2C is not set # end of On-chip Peripheral Drivers diff --git a/bsp/novosns/ns800/ns800rt7p65-nssinepad/rtconfig.h b/bsp/novosns/ns800/ns800rt7p65-nssinepad/rtconfig.h index 9300af36c69..b0853e3e423 100644 --- a/bsp/novosns/ns800/ns800rt7p65-nssinepad/rtconfig.h +++ b/bsp/novosns/ns800/ns800rt7p65-nssinepad/rtconfig.h @@ -420,9 +420,10 @@ #define BSP_USING_GPIO #define BSP_USING_UART #define BSP_NS800_UART_TX_TIMEOUT 6000 +#define BSP_UART_DEFAULT_BAUDRATE 115200 +#define BSP_UART_DATABITS_8 +#define BSP_UART_STOPBITS_1 #define BSP_USING_UART1 -#define BSP_USING_CAN -#define BSP_USING_CANFD1 /* end of On-chip Peripheral Drivers */ #endif