diff --git a/.github/workflows/manual_trigger_scons_except_STM32_all.yml b/.github/workflows/manual_trigger_scons_except_STM32_all.yml index 5e173eb76e24..6b1debcda324 100644 --- a/.github/workflows/manual_trigger_scons_except_STM32_all.yml +++ b/.github/workflows/manual_trigger_scons_except_STM32_all.yml @@ -190,18 +190,21 @@ jobs: #- {RTT_BSP_NAME: "mm32f327x", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "mm32f327x"} #编译问题 - {RTT_BSP_NAME: "mm32l07x", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "mm32l07x"} - {RTT_BSP_NAME: "mm32l3xx", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "mm32l3xx"} - - {RTT_BSP_NAME: "n32_n32g43xcl-stb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32g43xcl-stb"} - - {RTT_BSP_NAME: "n32_n32g457qel-stb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32g457qel-stb"} - - {RTT_BSP_NAME: "n32_n32g45xcl-stb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32g45xcl-stb"} - - {RTT_BSP_NAME: "n32_n32g45xml-stb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32g45xml-stb"} - - {RTT_BSP_NAME: "n32_n32g45xrl-stb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32g45xrl-stb"} - - {RTT_BSP_NAME: "n32_n32g45xvl-stb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32g45xvl-stb"} - - {RTT_BSP_NAME: "n32_n32g4frml-stb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32g4frml-stb"} - - {RTT_BSP_NAME: "n32_n32l40xcl-stb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32l40xcl-stb"} - - {RTT_BSP_NAME: "n32_n32l436-evb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32l436-evb"} - - {RTT_BSP_NAME: "n32_n32l43xml-stb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32l43xml-stb"} - - {RTT_BSP_NAME: "n32_n32l43xrl-stb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32l43xrl-stb"} - - {RTT_BSP_NAME: "n32_n32wb45xl-evb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32wb45xl-evb"} + - {RTT_BSP_NAME: "n32_n32gxx_lxx_n32g43xcl-stb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32gxx_lxx/n32g43xcl-stb"} + - {RTT_BSP_NAME: "n32_n32gxx_lxx_n32g457qel-stb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32gxx_lxx/n32g457qel-stb"} + - {RTT_BSP_NAME: "n32_n32gxx_lxx_n32g45xcl-stb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32gxx_lxx/n32g45xcl-stb"} + - {RTT_BSP_NAME: "n32_n32gxx_lxx_n32g45xml-stb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32gxx_lxx/n32g45xml-stb"} + - {RTT_BSP_NAME: "n32_n32gxx_lxx_n32g45xrl-stb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32gxx_lxx/n32g45xrl-stb"} + - {RTT_BSP_NAME: "n32_n32gxx_lxx_n32g45xvl-stb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32gxx_lxx/n32g45xvl-stb"} + - {RTT_BSP_NAME: "n32_n32gxx_lxx_n32g4frml-stb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32gxx_lxx/n32g4frml-stb"} + - {RTT_BSP_NAME: "n32_n32gxx_lxx_n32l40xcl-stb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32gxx_lxx/n32l40xcl-stb"} + - {RTT_BSP_NAME: "n32_n32gxx_lxx_n32l436-evb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32gxx_lxx/n32l436-evb"} + - {RTT_BSP_NAME: "n32_n32gxx_lxx_n32l43xml-stb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32gxx_lxx/n32l43xml-stb"} + - {RTT_BSP_NAME: "n32_n32gxx_lxx_n32l43xrl-stb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32gxx_lxx/n32l43xrl-stb"} + - {RTT_BSP_NAME: "n32_n32gxx_lxx_n32wb45xl-evb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32gxx_lxx/n32wb45xl-evb"} + - {RTT_BSP_NAME: "n32_n32hxxx_n32h487zgl7-evb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32hxxx/n32h487zgl7-evb"} + - {RTT_BSP_NAME: "n32_n32hxxx_n32h497zgl7-evb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32hxxx/n32h497zgl7-evb"} + - {RTT_BSP_NAME: "n32_n32hxxx_n32h760zil7-stb", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32/n32hxxx/n32h760zil7-stb"} - {RTT_BSP_NAME: "n32g452xx_n32g452xx-mini-system", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "n32g452xx/n32g452xx-mini-system"} #- {RTT_BSP_NAME: "nios_ii", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "nios_ii"} #编译问题 - {RTT_BSP_NAME: "nrf5x_nrf51822", RTT_TOOL_CHAIN: "sourcery-arm", RTT_BSP: "nrf5x/nrf51822"} diff --git a/bsp/README.md b/bsp/README.md index da23db8e1585..38c63ab6cf8a 100644 --- a/bsp/README.md +++ b/bsp/README.md @@ -333,8 +333,8 @@ This document is based on the RT-Thread mainline repository and categorizes the | [n32l43xrl-stb](n32/n32gxx_lxx/n32l43xrl-stb) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | - | - | - | - | - | - | - | - | - | | [n32wb45xl-evb](n32/n32gxx_lxx/n32wb45xl-evb) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | - | - | - | - | - | - | - | - | - | | [n32h760zil7-stb](n32/n32hxxx/n32h760zil7-stb) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [n32h497zgl7-evb](n32/n32hxxx/n32h497zgl7-evb) | ✅ | ✅ | - | ✅ | - | - | - | ✅ | - | - | - | - | - | - | - | ✅ | - | - | - | -| [n32h487zgl7-evb](n32/n32hxxx/n32h487zgl7-evb) | ✅ | ✅ | - | ✅ | - | - | - | ✅ | - | - | - | - | - | - | - | ✅ | - | - | - | +| [n32h497zgl7-evb](n32/n32hxxx/n32h497zgl7-evb) | ✅ | ✅ | ✅ | ✅ | ✅ | - | ✅ | ✅ | ✅ | ✅ | - | - | - | ✅ | ✅ | ✅ | ✅ | - | ✅ | +| [n32h487zgl7-evb](n32/n32hxxx/n32h487zgl7-evb) | ✅ | ✅ | ✅ | ✅ | ✅ | - | ✅ | ✅ | ✅ | ✅ | - | - | - | ✅ | ✅ | ✅ | - | - | ✅ | #### 🟡 NRF5x diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h47x_48x/adc_config.h b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h47x_48x/adc_config.h new file mode 100644 index 000000000000..d8a29195aea9 --- /dev/null +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h47x_48x/adc_config.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-08-28 FUNMSAN first version + */ + +#ifndef __ADC_CONFIG_H__ +#define __ADC_CONFIG_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define N32H47X_48X_ADC_CONFIG(ADC_INSTANCE, ADC_PERIPH) \ + { \ + .ADCx = ADC_INSTANCE, \ + .AHB_periph = ADC_PERIPH, \ + .EnablePeriphClk = RCC_EnableAHB1PeriphClk, \ + .ADC_ClkMode = ADC_CTRL3_CKMOD_AHB, \ + .RCC_ADCPrescaler = RCC_ADCHCLK_DIV16, \ + .ADC1M_ClkSource = RCC_ADC1MCLK_SRC_HSI, \ + .ADC1M_ClkPrescaler = RCC_ADC1MCLK_DIV8, \ + .Rank = 1U, \ + .ADC_SampleTime = ADC_SAMP_TIME_CYCLES_239_5, \ + .Init.WorkMode = ADC_WORKMODE_INDEPENDENT, \ + .Init.MultiChEn = DISABLE, \ + .Init.ContinueConvEn = DISABLE, \ + .Init.ExtTrigSelect = ADC_EXT_TRIG_REG_CONV_SOFTWARE, \ + .Init.DatAlign = ADC_DAT_ALIGN_R, \ + .Init.ChsNumber = 1U, \ + .Init.Resolution = ADC_DATA_RES_12BIT, \ + } + +#ifdef BSP_USING_ADC1 +#ifndef ADC1_CONFIG +#define ADC1_CONFIG N32H47X_48X_ADC_CONFIG(ADC1, RCC_AHB_PERIPHEN_ADC1) +#endif /* ADC1_CONFIG */ +#endif /* BSP_USING_ADC1 */ + +#ifdef BSP_USING_ADC2 +#ifndef ADC2_CONFIG +#define ADC2_CONFIG N32H47X_48X_ADC_CONFIG(ADC2, RCC_AHB_PERIPHEN_ADC2) +#endif /* ADC2_CONFIG */ +#endif /* BSP_USING_ADC2 */ + +#ifdef BSP_USING_ADC3 +#ifndef ADC3_CONFIG +#define ADC3_CONFIG N32H47X_48X_ADC_CONFIG(ADC3, RCC_AHB_PERIPHEN_ADC3) +#endif /* ADC3_CONFIG */ +#endif /* BSP_USING_ADC3 */ + +#ifdef BSP_USING_ADC4 +#ifndef ADC4_CONFIG +#define ADC4_CONFIG N32H47X_48X_ADC_CONFIG(ADC4, RCC_AHB_PERIPHEN_ADC4) +#endif /* ADC4_CONFIG */ +#endif /* BSP_USING_ADC4 */ + +#ifdef __cplusplus +} +#endif + +#endif /* __ADC_CONFIG_H__ */ diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h47x_48x/dac_config.h b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h47x_48x/dac_config.h new file mode 100644 index 000000000000..eec6279a7035 --- /dev/null +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h47x_48x/dac_config.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-08-28 FUNMSAN first version + */ + +#ifndef __DAC_CONFIG_H__ +#define __DAC_CONFIG_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(N32H473) || defined(N32H474) +#define N32H47X_48X_DAC1234_CLK_PRESCALER 100U +#else +#define N32H47X_48X_DAC1234_CLK_PRESCALER 120U +#endif + +#define N32H47X_48X_DAC5678_CLK_PRESCALER 16U + +#define N32H47X_48X_DAC_CONFIG(DAC_INSTANCE, DAC_MODULE, DAC_PERIPH, CLK_ENABLE, CLK_PRESCALER) \ + { \ + .DACx = DAC_INSTANCE, \ + .DAC_Module = DAC_MODULE, \ + .periph_clk = DAC_PERIPH, \ + .EnablePeriphClk = CLK_ENABLE, \ + .DAC_clk_pres = CLK_PRESCALER, \ + .Init.DAC_Trigger = DAC_Trigger_Software, \ + .Init.DAC_Trigger2 = DAC_Trigger2_Software, \ + .Init.DAC_WaveGeneration = DAC_WaveGeneration_None, \ + .Init.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0, \ + .Init.DAC_OutputBuffer = DISABLE, \ + .Init.DAC_TriggerEnable = ENABLE, \ + .Init.DAC_ConnectOnChipPeripheral = DISABLE, \ + .Init.DAC_ConnectExternalPin = ENABLE, \ + .Init.DAC_DMADoubleDataMode = DISABLE, \ + .Init.DAC_SignedFormat = DISABLE, \ + } + +#ifdef BSP_USING_DAC1 +#ifndef DAC1_CONFIG +#define DAC1_CONFIG N32H47X_48X_DAC_CONFIG(DAC1, DAC12, RCC_APB1_PERIPH_DAC12, \ + RCC_EnableAPB1PeriphClk, N32H47X_48X_DAC1234_CLK_PRESCALER) +#endif /* DAC1_CONFIG */ +#endif /* BSP_USING_DAC1 */ + +#ifdef BSP_USING_DAC2 +#ifndef DAC2_CONFIG +#define DAC2_CONFIG N32H47X_48X_DAC_CONFIG(DAC2, DAC12, RCC_APB1_PERIPH_DAC12, \ + RCC_EnableAPB1PeriphClk, N32H47X_48X_DAC1234_CLK_PRESCALER) +#endif /* DAC2_CONFIG */ +#endif /* BSP_USING_DAC2 */ + +#ifdef BSP_USING_DAC3 +#ifndef DAC3_CONFIG +#define DAC3_CONFIG N32H47X_48X_DAC_CONFIG(DAC3, DAC34, RCC_APB1_PERIPH_DAC34, \ + RCC_EnableAPB1PeriphClk, N32H47X_48X_DAC1234_CLK_PRESCALER) +#endif /* DAC3_CONFIG */ +#endif /* BSP_USING_DAC3 */ + +#ifdef BSP_USING_DAC4 +#ifndef DAC4_CONFIG +#define DAC4_CONFIG N32H47X_48X_DAC_CONFIG(DAC4, DAC34, RCC_APB1_PERIPH_DAC34, \ + RCC_EnableAPB1PeriphClk, N32H47X_48X_DAC1234_CLK_PRESCALER) +#endif /* DAC4_CONFIG */ +#endif /* BSP_USING_DAC4 */ + +#ifdef BSP_USING_DAC5 +#ifndef DAC5_CONFIG +#define DAC5_CONFIG N32H47X_48X_DAC_CONFIG(DAC5, DAC56, RCC_AHB_PERIPHEN_DAC56, \ + RCC_EnableAHBPeriphClk, N32H47X_48X_DAC5678_CLK_PRESCALER) +#endif /* DAC5_CONFIG */ +#endif /* BSP_USING_DAC5 */ + +#ifdef BSP_USING_DAC6 +#ifndef DAC6_CONFIG +#define DAC6_CONFIG N32H47X_48X_DAC_CONFIG(DAC6, DAC56, RCC_AHB_PERIPHEN_DAC56, \ + RCC_EnableAHBPeriphClk, N32H47X_48X_DAC5678_CLK_PRESCALER) +#endif /* DAC6_CONFIG */ +#endif /* BSP_USING_DAC6 */ + +#ifdef BSP_USING_DAC7 +#ifndef DAC7_CONFIG +#define DAC7_CONFIG N32H47X_48X_DAC_CONFIG(DAC7, DAC78, RCC_AHB_PERIPHEN_DAC78, \ + RCC_EnableAHBPeriphClk, N32H47X_48X_DAC5678_CLK_PRESCALER) +#endif /* DAC7_CONFIG */ +#endif /* BSP_USING_DAC7 */ + +#ifdef BSP_USING_DAC8 +#ifndef DAC8_CONFIG +#define DAC8_CONFIG N32H47X_48X_DAC_CONFIG(DAC8, DAC78, RCC_AHB_PERIPHEN_DAC78, \ + RCC_EnableAHBPeriphClk, N32H47X_48X_DAC5678_CLK_PRESCALER) +#endif /* DAC8_CONFIG */ +#endif /* BSP_USING_DAC8 */ + +#ifdef __cplusplus +} +#endif + +#endif /* __DAC_CONFIG_H__ */ diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h47x_48x/dma_config.h b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h47x_48x/dma_config.h index f14ac094851c..f42203ba24ec 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h47x_48x/dma_config.h +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h47x_48x/dma_config.h @@ -37,6 +37,14 @@ extern "C" { #define UART2_RX_DMA_RCC RCC_AHB_PERIPHEN_DMA1 #define UART2_RX_DMA_REQUEST DMA_REMAP_USART2_RX #define UART2_RX_DMA_CHANNEL 2U +#elif defined(BSP_I2C2_RX_USING_DMA) && !defined(I2C2_RX_DMA) +#define I2C2_RX_DMA DMA1 +#define I2C2_RX_DMA_IRQHandler DMA1_Channel2_IRQHandler +#define I2C2_RX_DMA_CHType DMA1_CH2 +#define I2C2_RX_DMA_IRQ DMA1_Channel2_IRQn +#define I2C2_RX_DMA_RCC RCC_AHB_PERIPHEN_DMA1 +#define I2C2_RX_DMA_REQUEST DMA_REMAP_I2C2_RX +#define I2C2_RX_DMA_CHANNEL 2U #endif /* DMA1 channel3 */ @@ -48,6 +56,14 @@ extern "C" { #define UART3_RX_DMA_RCC RCC_AHB_PERIPHEN_DMA1 #define UART3_RX_DMA_REQUEST DMA_REMAP_USART3_RX #define UART3_RX_DMA_CHANNEL 3U +#elif defined(BSP_I2C3_RX_USING_DMA) && !defined(I2C3_RX_DMA) +#define I2C3_RX_DMA DMA1 +#define I2C3_RX_DMA_IRQHandler DMA1_Channel3_IRQHandler +#define I2C3_RX_DMA_CHType DMA1_CH3 +#define I2C3_RX_DMA_IRQ DMA1_Channel3_IRQn +#define I2C3_RX_DMA_RCC RCC_AHB_PERIPHEN_DMA1 +#define I2C3_RX_DMA_REQUEST DMA_REMAP_I2C3_RX +#define I2C3_RX_DMA_CHANNEL 3U #endif /* DMA1 channel4 */ @@ -59,6 +75,22 @@ extern "C" { #define UART4_RX_DMA_RCC RCC_AHB_PERIPHEN_DMA1 #define UART4_RX_DMA_REQUEST DMA_REMAP_USART4_RX #define UART4_RX_DMA_CHANNEL 4U +#elif defined(BSP_SPI6_RX_USING_DMA) && !defined(SPI6_RX_DMA) +#define SPI6_RX_DMA DMA1 +#define SPI6_RX_DMA_IRQHandler DMA1_Channel4_IRQHandler +#define SPI6_RX_DMA_CHType DMA1_CH4 +#define SPI6_RX_DMA_IRQ DMA1_Channel4_IRQn +#define SPI6_RX_DMA_RCC RCC_AHB_PERIPHEN_DMA1 +#define SPI6_RX_DMA_REQUEST DMA_REMAP_SPI6_RX +#define SPI6_RX_DMA_CHANNEL 4U +#elif defined(BSP_I2C4_RX_USING_DMA) && !defined(I2C4_RX_DMA) +#define I2C4_RX_DMA DMA1 +#define I2C4_RX_DMA_IRQHandler DMA1_Channel4_IRQHandler +#define I2C4_RX_DMA_CHType DMA1_CH4 +#define I2C4_RX_DMA_IRQ DMA1_Channel4_IRQn +#define I2C4_RX_DMA_RCC RCC_AHB_PERIPHEN_DMA1 +#define I2C4_RX_DMA_REQUEST DMA_REMAP_I2C4_RX +#define I2C4_RX_DMA_CHANNEL 4U #endif /* DMA1 channel5 */ @@ -70,6 +102,14 @@ extern "C" { #define UART5_RX_DMA_RCC RCC_AHB_PERIPHEN_DMA1 #define UART5_RX_DMA_REQUEST DMA_REMAP_UART5_RX #define UART5_RX_DMA_CHANNEL 5U +#elif defined(BSP_SPI5_RX_USING_DMA) && !defined(SPI5_RX_DMA) +#define SPI5_RX_DMA DMA1 +#define SPI5_RX_DMA_IRQHandler DMA1_Channel5_IRQHandler +#define SPI5_RX_DMA_CHType DMA1_CH5 +#define SPI5_RX_DMA_IRQ DMA1_Channel5_IRQn +#define SPI5_RX_DMA_RCC RCC_AHB_PERIPHEN_DMA1 +#define SPI5_RX_DMA_REQUEST DMA_REMAP_SPI5_RX +#define SPI5_RX_DMA_CHANNEL 5U #endif /* DMA1 channel6 */ @@ -81,6 +121,29 @@ extern "C" { #define UART6_RX_DMA_RCC RCC_AHB_PERIPHEN_DMA1 #define UART6_RX_DMA_REQUEST DMA_REMAP_UART6_RX #define UART6_RX_DMA_CHANNEL 6U +#elif defined(BSP_SPI4_RX_USING_DMA) && !defined(SPI4_RX_DMA) +#define SPI4_RX_DMA DMA1 +#define SPI4_RX_DMA_IRQHandler DMA1_Channel6_IRQHandler +#define SPI4_RX_DMA_CHType DMA1_CH6 +#define SPI4_RX_DMA_IRQ DMA1_Channel6_IRQn +#define SPI4_RX_DMA_RCC RCC_AHB_PERIPHEN_DMA1 +#define SPI4_RX_DMA_REQUEST DMA_REMAP_SPI4_RX +#define SPI4_RX_DMA_CHANNEL 6U +#elif defined(BSP_USING_SDIO) +#define SDIO_RX_DMA DMA1 +#define SDIO_TX_DMA DMA1 +#define SDIO_RX_TX_DMA_IRQHandler DMA1_Channel6_IRQHandler +#define SDIO_TX_DMA_CHType DMA1_CH6 +#define SDIO_RX_DMA_CHType DMA1_CH6 +#define SDIO_TX_DMA_IRQ DMA1_Channel6_IRQn +#define SDIO_RX_DMA_IRQ DMA1_Channel6_IRQn +#define SDIO_TX_DMA_RCC RCC_AHB_PERIPHEN_DMA1 +#define SDIO_RX_DMA_RCC RCC_AHB_PERIPHEN_DMA1 +#define SDIO_TX_DMA_REQUEST DMA_REMAP_SDIO_DMA +#define SDIO_RX_DMA_REQUEST DMA_REMAP_SDIO_DMA + +#define SDIO_TX_DMA_CHANNEL 6U +#define SDIO_RX_DMA_CHANNEL 6U #endif /* DMA1 channel7 */ @@ -92,6 +155,14 @@ extern "C" { #define UART7_RX_DMA_RCC RCC_AHB_PERIPHEN_DMA1 #define UART7_RX_DMA_REQUEST DMA_REMAP_UART7_RX #define UART7_RX_DMA_CHANNEL 7U +#elif defined(BSP_SPI3_RX_USING_DMA) && !defined(SPI3_RX_DMA) +#define SPI3_RX_DMA DMA1 +#define SPI3_RX_DMA_IRQHandler DMA1_Channel7_IRQHandler +#define SPI3_RX_DMA_CHType DMA1_CH7 +#define SPI3_RX_DMA_IRQ DMA1_Channel7_IRQn +#define SPI3_RX_DMA_RCC RCC_AHB_PERIPHEN_DMA1 +#define SPI3_RX_DMA_REQUEST DMA_REMAP_SPI3_I2S3_RX +#define SPI3_RX_DMA_CHANNEL 7U #endif /* DMA1 channel8 */ @@ -103,6 +174,15 @@ extern "C" { #define UART8_RX_DMA_RCC RCC_AHB_PERIPHEN_DMA1 #define UART8_RX_DMA_REQUEST DMA_REMAP_UART8_RX #define UART8_RX_DMA_CHANNEL 8U +#elif defined(BSP_SPI2_RX_USING_DMA) && !defined(SPI2_RX_DMA) +#define SPI2_RX_DMA DMA1 +#define SPI2_RX_DMA_IRQHandler DMA1_Channel8_IRQHandler +#define SPI2_RX_DMA_CHType DMA1_CH8 +#define SPI2_RX_DMA_IRQ DMA1_Channel8_IRQn +#define SPI2_RX_DMA_RCC RCC_AHB_PERIPHEN_DMA1 +#define SPI2_RX_DMA_REQUEST DMA_REMAP_SPI2_I2S2_RX +#define SPI2_RX_DMA_CHANNEL 8U + #endif /* DMA2 channel1 */ @@ -114,6 +194,22 @@ extern "C" { #define UART1_TX_DMA_RCC RCC_AHB_PERIPHEN_DMA2 #define UART1_TX_DMA_REQUEST DMA_REMAP_USART1_TX #define UART1_TX_DMA_CHANNEL 1U +#elif defined(BSP_SPI1_RX_USING_DMA) && !defined(SPI1_RX_DMA) +#define SPI1_RX_DMA DMA2 +#define SPI1_RX_DMA_IRQHandler DMA2_Channel1_IRQHandler +#define SPI1_RX_DMA_CHType DMA2_CH1 +#define SPI1_RX_DMA_IRQ DMA2_Channel1_IRQn +#define SPI1_RX_DMA_RCC RCC_AHB_PERIPHEN_DMA2 +#define SPI1_RX_DMA_REQUEST DMA_REMAP_SPI1_RX +#define SPI1_RX_DMA_CHANNEL 1U +#elif defined(BSP_I2C1_RX_USING_DMA) && !defined(I2C1_RX_DMA) +#define I2C1_RX_DMA DMA2 +#define I2C1_RX_DMA_IRQHandler DMA2_Channel1_IRQHandler +#define I2C1_RX_DMA_CHType DMA2_CH1 +#define I2C1_RX_DMA_IRQ DMA2_Channel1_IRQn +#define I2C1_RX_DMA_RCC RCC_AHB_PERIPHEN_DMA2 +#define I2C1_RX_DMA_REQUEST DMA_REMAP_I2C1_RX +#define I2C1_RX_DMA_CHANNEL 1U #endif /* DMA2 channel2 */ @@ -125,17 +221,34 @@ extern "C" { #define UART2_TX_DMA_RCC RCC_AHB_PERIPHEN_DMA2 #define UART2_TX_DMA_REQUEST DMA_REMAP_USART2_TX #define UART2_TX_DMA_CHANNEL 2U + #endif /* DMA2 channel3 */ #if defined(BSP_UART3_TX_USING_DMA) && !defined(UART3_TX_DMA) #define UART3_TX_DMA DMA2 -#define I2C10_RX_DMA_IRQHandler DMA2_Channel3_IRQHandler +#define UART3_TX_DMA_IRQHandler DMA2_Channel3_IRQHandler #define UART3_TX_DMA_CHType DMA2_CH3 #define UART3_TX_DMA_IRQ DMA2_Channel3_IRQn #define UART3_TX_DMA_RCC RCC_AHB_PERIPHEN_DMA2 #define UART3_TX_DMA_REQUEST DMA_REMAP_USART3_TX #define UART3_TX_DMA_CHANNEL 3U +#elif defined(BSP_SPI6_TX_USING_DMA) && !defined(SPI6_TX_DMA) +#define SPI6_TX_DMA DMA2 +#define SPI6_TX_DMA_IRQHandler DMA2_Channel3_IRQHandler +#define SPI6_TX_DMA_CHType DMA2_CH3 +#define SPI6_TX_DMA_IRQ DMA2_Channel3_IRQn +#define SPI6_TX_DMA_RCC RCC_AHB_PERIPHEN_DMA2 +#define SPI6_TX_DMA_REQUEST DMA_REMAP_SPI6_TX +#define SPI6_TX_DMA_CHANNEL 3U +#elif defined(BSP_I2C1_TX_USING_DMA) && !defined(I2C1_TX_DMA) +#define I2C1_TX_DMA DMA2 +#define I2C1_TX_DMA_IRQHandler DMA2_Channel3_IRQHandler +#define I2C1_TX_DMA_CHType DMA2_CH3 +#define I2C1_TX_DMA_IRQ DMA2_Channel3_IRQn +#define I2C1_TX_DMA_RCC RCC_AHB_PERIPHEN_DMA2 +#define I2C1_TX_DMA_REQUEST DMA_REMAP_I2C1_TX +#define I2C1_TX_DMA_CHANNEL 3U #endif /* DMA2 channel4 */ @@ -147,6 +260,14 @@ extern "C" { #define UART4_TX_DMA_RCC RCC_AHB_PERIPHEN_DMA2 #define UART4_TX_DMA_REQUEST DMA_REMAP_USART4_TX #define UART4_TX_DMA_CHANNEL 4U +#elif defined(BSP_SPI5_TX_USING_DMA) && !defined(SPI5_TX_DMA) +#define SPI5_TX_DMA DMA2 +#define SPI5_TX_DMA_IRQHandler DMA2_Channel4_IRQHandler +#define SPI5_TX_DMA_CHType DMA2_CH4 +#define SPI5_TX_DMA_IRQ DMA2_Channel4_IRQn +#define SPI5_TX_DMA_RCC RCC_AHB_PERIPHEN_DMA2 +#define SPI5_TX_DMA_REQUEST DMA_REMAP_SPI5_TX +#define SPI5_TX_DMA_CHANNEL 4U #endif /* DMA2 channel5 */ @@ -158,6 +279,22 @@ extern "C" { #define UART5_TX_DMA_RCC RCC_AHB_PERIPHEN_DMA2 #define UART5_TX_DMA_REQUEST DMA_REMAP_UART5_TX #define UART5_TX_DMA_CHANNEL 5U +#elif defined(BSP_SPI4_TX_USING_DMA) && !defined(SPI4_TX_DMA) +#define SPI4_TX_DMA DMA2 +#define SPI4_TX_DMA_IRQHandler DMA2_Channel5_IRQHandler +#define SPI4_TX_DMA_CHType DMA2_CH5 +#define SPI4_TX_DMA_IRQ DMA2_Channel5_IRQn +#define SPI4_TX_DMA_RCC RCC_AHB_PERIPHEN_DMA2 +#define SPI4_TX_DMA_REQUEST DMA_REMAP_SPI4_TX +#define SPI4_TX_DMA_CHANNEL 5U +#elif defined(BSP_I2C2_TX_USING_DMA) && !defined(I2C2_TX_DMA) +#define I2C2_TX_DMA DMA2 +#define I2C2_TX_DMA_IRQHandler DMA2_Channel5_IRQHandler +#define I2C2_TX_DMA_CHType DMA2_CH5 +#define I2C2_TX_DMA_IRQ DMA2_Channel5_IRQn +#define I2C2_TX_DMA_RCC RCC_AHB_PERIPHEN_DMA2 +#define I2C2_TX_DMA_REQUEST DMA_REMAP_I2C2_TX +#define I2C2_TX_DMA_CHANNEL 5U #endif /* DMA2 channel6 */ @@ -169,6 +306,23 @@ extern "C" { #define UART6_TX_DMA_RCC RCC_AHB_PERIPHEN_DMA2 #define UART6_TX_DMA_REQUEST DMA_REMAP_UART6_TX #define UART6_TX_DMA_CHANNEL 6U +#elif defined(BSP_SPI3_TX_USING_DMA) && !defined(SPI3_TX_DMA) +#define SPI3_TX_DMA DMA2 +#define SPI3_TX_DMA_IRQHandler DMA2_Channel6_IRQHandler +#define SPI3_TX_DMA_CHType DMA2_CH6 +#define SPI3_TX_DMA_IRQ DMA2_Channel6_IRQn +#define SPI3_TX_DMA_RCC RCC_AHB_PERIPHEN_DMA2 +#define SPI3_TX_DMA_REQUEST DMA_REMAP_SPI3_I2S3_TX +#define SPI3_TX_DMA_CHANNEL 6U +#elif defined(BSP_I2C3_TX_USING_DMA) && !defined(I2C3_TX_DMA) +#define I2C3_TX_DMA DMA2 +#define I2C3_TX_DMA_IRQHandler DMA2_Channel6_IRQHandler +#define I2C3_TX_DMA_CHType DMA2_CH6 +#define I2C3_TX_DMA_IRQ DMA2_Channel6_IRQn +#define I2C3_TX_DMA_RCC RCC_AHB_PERIPHEN_DMA2 +#define I2C3_TX_DMA_REQUEST DMA_REMAP_I2C3_TX +#define I2C3_TX_DMA_CHANNEL 6U + #endif /* DMA2 channel7 */ @@ -180,6 +334,22 @@ extern "C" { #define UART7_TX_DMA_RCC RCC_AHB_PERIPHEN_DMA2 #define UART7_TX_DMA_REQUEST DMA_REMAP_UART7_TX #define UART7_TX_DMA_CHANNEL 7U +#elif defined(BSP_SPI2_TX_USING_DMA) && !defined(SPI2_TX_DMA) +#define SPI2_TX_DMA DMA2 +#define SPI2_TX_DMA_IRQHandler DMA2_Channel7_IRQHandler +#define SPI2_TX_DMA_CHType DMA2_CH7 +#define SPI2_TX_DMA_IRQ DMA2_Channel7_IRQn +#define SPI2_TX_DMA_RCC RCC_AHB_PERIPHEN_DMA2 +#define SPI2_TX_DMA_REQUEST DMA_REMAP_SPI2_I2S2_TX +#define SPI2_TX_DMA_CHANNEL 7U +#elif defined(BSP_I2C4_TX_USING_DMA) && !defined(I2C4_TX_DMA) +#define I2C4_TX_DMA DMA2 +#define I2C4_TX_DMA_IRQHandler DMA2_Channel7_IRQHandler +#define I2C4_TX_DMA_CHType DMA2_CH7 +#define I2C4_TX_DMA_IRQ DMA2_Channel7_IRQn +#define I2C4_TX_DMA_RCC RCC_AHB_PERIPHEN_DMA2 +#define I2C4_TX_DMA_REQUEST DMA_REMAP_I2C4_TX +#define I2C4_TX_DMA_CHANNEL 7U #endif /* DMA2 channel8 */ @@ -191,6 +361,14 @@ extern "C" { #define UART8_TX_DMA_RCC RCC_AHB_PERIPHEN_DMA2 #define UART8_TX_DMA_REQUEST DMA_REMAP_UART8_TX #define UART8_TX_DMA_CHANNEL 8U +#elif defined(BSP_SPI1_TX_USING_DMA) && !defined(SPI1_TX_DMA) +#define SPI1_TX_DMA DMA2 +#define SPI1_TX_DMA_IRQHandler DMA2_Channel8_IRQHandler +#define SPI1_TX_DMA_CHType DMA2_CH8 +#define SPI1_TX_DMA_IRQ DMA2_Channel8_IRQn +#define SPI1_TX_DMA_RCC RCC_AHB_PERIPHEN_DMA2 +#define SPI1_TX_DMA_REQUEST DMA_REMAP_SPI1_TX +#define SPI1_TX_DMA_CHANNEL 8U #endif #ifdef __cplusplus diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h47x_48x/i2c_hard_config.h b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h47x_48x/i2c_hard_config.h new file mode 100644 index 000000000000..f82c7cb16027 --- /dev/null +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h47x_48x/i2c_hard_config.h @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-01-24 ox-horse first version + */ + +#ifndef __I2C_HARD_CONFIG_H__ +#define __I2C_HARD_CONFIG_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef BSP_USING_HARD_I2C1 +#ifndef I2C1_BUS_CONFIG +#define I2C1_BUS_CONFIG \ + { \ + .name = "hwi2c1", \ + .Instance = I2C1, \ + .timeout = 0x1000, \ + .evirq_type = I2C1_EV_IRQn, \ + .erirq_type = I2C1_ER_IRQn, \ + .scl_af_width = 0, \ + .sda_af_width = 0, \ + .df_width = 0, \ + .periph = RCC_APB1_PERIPH_I2C1, \ + .EnablePeriphClk = RCC_EnableAPB1PeriphClk, \ + } +#endif /* I2C1_BUS_CONFIG */ +#endif /* BSP_USING_HARD_I2C1 */ +#ifdef BSP_I2C1_RX_USING_DMA +#ifndef I2C1_RX_DMA_CONFIG +#define I2C1_RX_DMA_CONFIG \ + { \ + .DMAy = I2C1_RX_DMA, \ + .DMAChx = I2C1_RX_DMA_CHType, \ + .dma_irq = I2C1_RX_DMA_IRQ, \ + .dma_rcc = I2C1_RX_DMA_RCC, \ + .request = I2C1_RX_DMA_REQUEST, \ + .channel = I2C1_RX_DMA_CHANNEL, \ + } +#endif /* I2C1_RX_DMA_CONFIG */ +#endif /* BSP_I2C1_RX_USING_DMA */ +#ifdef BSP_I2C1_TX_USING_DMA +#ifndef I2C1_TX_DMA_CONFIG +#define I2C1_TX_DMA_CONFIG \ + { \ + .DMAy = I2C1_TX_DMA, \ + .DMAChx = I2C1_TX_DMA_CHType, \ + .dma_irq = I2C1_TX_DMA_IRQ, \ + .dma_rcc = I2C1_TX_DMA_RCC, \ + .request = I2C1_TX_DMA_REQUEST, \ + .channel = I2C1_TX_DMA_CHANNEL, \ + } +#endif /* I2C1_TX_DMA_CONFIG */ +#endif /* BSP_I2C1_TX_USING_DMA */ + + +#ifdef BSP_USING_HARD_I2C2 +#ifndef I2C2_BUS_CONFIG +#define I2C2_BUS_CONFIG \ + { \ + .name = "hwi2c2", \ + .Instance = I2C2, \ + .timeout = 0x1000, \ + .evirq_type = I2C2_EV_IRQn, \ + .erirq_type = I2C2_ER_IRQn, \ + .scl_af_width = 0, \ + .sda_af_width = 0, \ + .df_width = 0, \ + .periph = RCC_APB1_PERIPH_I2C2, \ + .EnablePeriphClk = RCC_EnableAPB1PeriphClk, \ + } +#endif /* I2C2_BUS_CONFIG */ +#endif /* BSP_USING_HARD_I2C2 */ +#ifdef BSP_I2C2_RX_USING_DMA +#ifndef I2C2_RX_DMA_CONFIG +#define I2C2_RX_DMA_CONFIG \ + { \ + .DMAy = I2C2_RX_DMA, \ + .DMAChx = I2C2_RX_DMA_CHType, \ + .dma_irq = I2C2_RX_DMA_IRQ, \ + .dma_rcc = I2C2_RX_DMA_RCC, \ + .request = I2C2_RX_DMA_REQUEST, \ + .channel = I2C2_RX_DMA_CHANNEL, \ + } +#endif /* I2C2_RX_DMA_CONFIG */ +#endif /* BSP_I2C2_RX_USING_DMA */ +#ifdef BSP_I2C2_TX_USING_DMA +#ifndef I2C2_TX_DMA_CONFIG +#define I2C2_TX_DMA_CONFIG \ + { \ + .DMAy = I2C2_TX_DMA, \ + .DMAChx = I2C2_TX_DMA_CHType, \ + .dma_irq = I2C2_TX_DMA_IRQ, \ + .dma_rcc = I2C2_TX_DMA_RCC, \ + .request = I2C2_TX_DMA_REQUEST, \ + .channel = I2C2_TX_DMA_CHANNEL, \ + } +#endif /* I2C2_TX_DMA_CONFIG */ +#endif /* BSP_I2C2_TX_USING_DMA */ + + +#ifdef BSP_USING_HARD_I2C3 +#ifndef I2C3_BUS_CONFIG +#define I2C3_BUS_CONFIG \ + { \ + .name = "hwi2c3", \ + .Instance = I2C3, \ + .timeout = 0x1000, \ + .evirq_type = I2C3_EV_IRQn, \ + .erirq_type = I2C3_ER_IRQn, \ + .scl_af_width = 0, \ + .sda_af_width = 0, \ + .df_width = 0, \ + .periph = RCC_APB1_PERIPH_I2C3, \ + .EnablePeriphClk = RCC_EnableAPB1PeriphClk, \ + } +#endif /* I2C3_BUS_CONFIG */ +#endif /* BSP_USING_HARD_I2C3 */ +#ifdef BSP_I2C3_RX_USING_DMA +#ifndef I2C3_RX_DMA_CONFIG +#define I2C3_RX_DMA_CONFIG \ + { \ + .DMAy = I2C3_RX_DMA, \ + .DMAChx = I2C3_RX_DMA_CHType, \ + .dma_irq = I2C3_RX_DMA_IRQ, \ + .dma_rcc = I2C3_RX_DMA_RCC, \ + .request = I2C3_RX_DMA_REQUEST, \ + .channel = I2C3_RX_DMA_CHANNEL, \ + } +#endif /* I2C3_RX_DMA_CONFIG */ +#endif /* BSP_I2C3_RX_USING_DMA */ +#ifdef BSP_I2C3_TX_USING_DMA +#ifndef I2C3_TX_DMA_CONFIG +#define I2C3_TX_DMA_CONFIG \ + { \ + .DMAy = I2C3_TX_DMA, \ + .DMAChx = I2C3_TX_DMA_CHType, \ + .dma_irq = I2C3_TX_DMA_IRQ, \ + .dma_rcc = I2C3_TX_DMA_RCC, \ + .request = I2C3_TX_DMA_REQUEST, \ + .channel = I2C3_TX_DMA_CHANNEL, \ + } +#endif /* I2C3_TX_DMA_CONFIG */ +#endif /* BSP_I2C3_TX_USING_DMA */ + + +#ifdef BSP_USING_HARD_I2C4 +#ifndef I2C4_BUS_CONFIG +#define I2C4_BUS_CONFIG \ + { \ + .name = "hwi2c4", \ + .Instance = I2C4, \ + .timeout = 0x1000, \ + .evirq_type = I2C4_EV_IRQn, \ + .erirq_type = I2C4_ER_IRQn, \ + .scl_af_width = 0, \ + .sda_af_width = 0, \ + .df_width = 0, \ + .periph = RCC_APB1_PERIPH_I2C4, \ + .EnablePeriphClk = RCC_EnableAPB1PeriphClk, \ + } +#endif /* I2C4_BUS_CONFIG */ +#endif /* BSP_USING_HARD_I2C4 */ +#ifdef BSP_I2C4_RX_USING_DMA +#ifndef I2C4_RX_DMA_CONFIG +#define I2C4_RX_DMA_CONFIG \ + { \ + .DMAy = I2C4_RX_DMA, \ + .DMAChx = I2C4_RX_DMA_CHType, \ + .dma_irq = I2C4_RX_DMA_IRQ, \ + .dma_rcc = I2C4_RX_DMA_RCC, \ + .request = I2C4_RX_DMA_REQUEST, \ + .channel = I2C4_RX_DMA_CHANNEL, \ + } +#endif /* I2C4_RX_DMA_CONFIG */ +#endif /* BSP_I2C4_RX_USING_DMA */ +#ifdef BSP_I2C4_TX_USING_DMA +#ifndef I2C4_TX_DMA_CONFIG +#define I2C4_TX_DMA_CONFIG \ + { \ + .DMAy = I2C4_TX_DMA, \ + .DMAChx = I2C4_TX_DMA_CHType, \ + .dma_irq = I2C4_TX_DMA_IRQ, \ + .dma_rcc = I2C4_TX_DMA_RCC, \ + .request = I2C4_TX_DMA_REQUEST, \ + .channel = I2C4_TX_DMA_CHANNEL, \ + } +#endif /* I2C4_TX_DMA_CONFIG */ +#endif /* BSP_I2C4_TX_USING_DMA */ + +#ifdef __cplusplus +} + +#endif + +#endif /*__I2C_HARD_CONFIG_H__ */ diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h47x_48x/spi_config.h b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h47x_48x/spi_config.h new file mode 100644 index 000000000000..6b0b97cdef10 --- /dev/null +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h47x_48x/spi_config.h @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-06-03 hu-wenhao first version + */ + +#ifndef __SPI_CONFIG_H__ +#define __SPI_CONFIG_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef BSP_USING_SPI1 +#ifndef SPI1_BUS_CONFIG +#define SPI1_BUS_CONFIG \ + { \ + .SPIx = SPI1, \ + .bus_name = "spi1", \ + .irq_type = SPI1_IRQn, \ + } +#endif /* SPI1_BUS_CONFIG */ +#endif /* BSP_USING_SPI1 */ +#ifdef BSP_SPI1_TX_USING_DMA +#ifndef SPI1_TX_DMA_CONFIG +#define SPI1_TX_DMA_CONFIG \ + { \ + .DMAy = SPI1_TX_DMA, \ + .DMAChx = SPI1_TX_DMA_CHType, \ + .dma_irq = SPI1_TX_DMA_IRQ, \ + .dma_rcc = SPI1_TX_DMA_RCC, \ + .request = SPI1_TX_DMA_REQUEST, \ + .channel = SPI1_TX_DMA_CHANNEL, \ + } +#endif /* SPI1_TX_DMA_CONFIG */ +#endif /* BSP_SPI1_TX_USING_DMA */ +#ifdef BSP_SPI1_RX_USING_DMA +#ifndef SPI1_RX_DMA_CONFIG +#define SPI1_RX_DMA_CONFIG \ + { \ + .DMAy = SPI1_RX_DMA, \ + .DMAChx = SPI1_RX_DMA_CHType, \ + .dma_irq = SPI1_RX_DMA_IRQ, \ + .dma_rcc = SPI1_RX_DMA_RCC, \ + .request = SPI1_RX_DMA_REQUEST, \ + .channel = SPI1_RX_DMA_CHANNEL, \ + } +#endif /* SPI1_RX_DMA_CONFIG */ +#endif /* BSP_SPI1_RX_USING_DMA */ + + +#ifdef BSP_USING_SPI2 +#ifndef SPI2_BUS_CONFIG +#define SPI2_BUS_CONFIG \ + { \ + .SPIx = SPI2, \ + .bus_name = "spi2", \ + .irq_type = SPI2_I2S2_IRQn, \ + } +#endif /* SPI2_BUS_CONFIG */ +#endif /* BSP_USING_SPI2 */ +#ifdef BSP_SPI2_TX_USING_DMA +#ifndef SPI2_TX_DMA_CONFIG +#define SPI2_TX_DMA_CONFIG \ + { \ + .DMAy = SPI2_TX_DMA, \ + .DMAChx = SPI2_TX_DMA_CHType, \ + .dma_irq = SPI2_TX_DMA_IRQ, \ + .dma_rcc = SPI2_TX_DMA_RCC, \ + .request = SPI2_TX_DMA_REQUEST, \ + .channel = SPI2_TX_DMA_CHANNEL, \ + } +#endif /* SPI2_TX_DMA_CONFIG */ +#endif /* BSP_SPI2_TX_USING_DMA */ +#ifdef BSP_SPI2_RX_USING_DMA +#ifndef SPI2_RX_DMA_CONFIG +#define SPI2_RX_DMA_CONFIG \ + { \ + .DMAy = SPI2_RX_DMA, \ + .DMAChx = SPI2_RX_DMA_CHType, \ + .dma_irq = SPI2_RX_DMA_IRQ, \ + .dma_rcc = SPI2_RX_DMA_RCC, \ + .request = SPI2_RX_DMA_REQUEST, \ + .channel = SPI2_RX_DMA_CHANNEL, \ + } +#endif /* SPI2_RX_DMA_CONFIG */ +#endif /* BSP_SPI2_RX_USING_DMA */ + + +#ifdef BSP_USING_SPI3 +#ifndef SPI3_BUS_CONFIG +#define SPI3_BUS_CONFIG \ + { \ + .SPIx = SPI3, \ + .bus_name = "spi3", \ + .irq_type = SPI3_I2S3_IRQn, \ + } + +#endif /* SPI3_BUS_CONFIG */ +#endif /* BSP_USING_SPI3 */ +#ifdef BSP_SPI3_TX_USING_DMA +#ifndef SPI3_TX_DMA_CONFIG +#define SPI3_TX_DMA_CONFIG \ + { \ + .DMAy = SPI3_TX_DMA, \ + .DMAChx = SPI3_TX_DMA_CHType, \ + .dma_irq = SPI3_TX_DMA_IRQ, \ + .dma_rcc = SPI3_TX_DMA_RCC, \ + .request = SPI3_TX_DMA_REQUEST, \ + .channel = SPI3_TX_DMA_CHANNEL, \ + } +#endif /* SPI3_TX_DMA_CONFIG */ +#endif /* BSP_SPI3_TX_USING_DMA */ +#ifdef BSP_SPI3_RX_USING_DMA +#ifndef SPI3_RX_DMA_CONFIG +#define SPI3_RX_DMA_CONFIG \ + { \ + .DMAy = SPI3_RX_DMA, \ + .DMAChx = SPI3_RX_DMA_CHType, \ + .dma_irq = SPI3_RX_DMA_IRQ, \ + .dma_rcc = SPI3_RX_DMA_RCC, \ + .request = SPI3_RX_DMA_REQUEST, \ + .channel = SPI3_RX_DMA_CHANNEL, \ + } +#endif /* SPI3_RX_DMA_CONFIG */ +#endif /* BSP_SPI3_RX_USING_DMA */ + + +#ifdef BSP_USING_SPI4 +#ifndef SPI4_BUS_CONFIG +#define SPI4_BUS_CONFIG \ + { \ + .SPIx = SPI4, \ + .bus_name = "spi4", \ + .irq_type = SPI4_IRQn, \ + } +#endif /* SPI4_BUS_CONFIG */ +#endif /* BSP_USING_SPI4 */ +#ifdef BSP_SPI4_TX_USING_DMA +#ifndef SPI4_TX_DMA_CONFIG +#define SPI4_TX_DMA_CONFIG \ + { \ + .DMAy = SPI4_TX_DMA, \ + .DMAChx = SPI4_TX_DMA_CHType, \ + .dma_irq = SPI4_TX_DMA_IRQ, \ + .dma_rcc = SPI4_TX_DMA_RCC, \ + .request = SPI4_TX_DMA_REQUEST, \ + .channel = SPI4_TX_DMA_CHANNEL, \ + } +#endif /* SPI4_TX_DMA_CONFIG */ +#endif /* BSP_SPI4_TX_USING_DMA */ +#ifdef BSP_SPI4_RX_USING_DMA +#ifndef SPI4_RX_DMA_CONFIG +#define SPI4_RX_DMA_CONFIG \ + { \ + .DMAy = SPI4_RX_DMA, \ + .DMAChx = SPI4_RX_DMA_CHType, \ + .dma_irq = SPI4_RX_DMA_IRQ, \ + .dma_rcc = SPI4_RX_DMA_RCC, \ + .request = SPI4_RX_DMA_REQUEST, \ + .channel = SPI4_RX_DMA_CHANNEL, \ + } +#endif /* SPI4_RX_DMA_CONFIG */ +#endif /* BSP_SPI4_RX_USING_DMA */ + + +#ifdef BSP_USING_SPI5 +#ifndef SPI5_BUS_CONFIG +#define SPI5_BUS_CONFIG \ + { \ + .SPIx = SPI5, \ + .bus_name = "spi5", \ + .irq_type = SPI5_IRQn, \ + } +#endif /* SPI5_BUS_CONFIG */ +#endif /* BSP_USING_SPI5 */ +#ifdef BSP_SPI5_TX_USING_DMA +#ifndef SPI5_TX_DMA_CONFIG +#define SPI5_TX_DMA_CONFIG \ + { \ + .DMAy = SPI5_TX_DMA, \ + .DMAChx = SPI5_TX_DMA_CHType, \ + .dma_irq = SPI5_TX_DMA_IRQ, \ + .dma_rcc = SPI5_TX_DMA_RCC, \ + .request = SPI5_TX_DMA_REQUEST, \ + .channel = SPI5_TX_DMA_CHANNEL, \ + } +#endif /* SPI5_TX_DMA_CONFIG */ +#endif /* BSP_SPI5_TX_USING_DMA */ +#ifdef BSP_SPI5_RX_USING_DMA +#ifndef SPI5_RX_DMA_CONFIG +#define SPI5_RX_DMA_CONFIG \ + { \ + .DMAy = SPI5_RX_DMA, \ + .DMAChx = SPI5_RX_DMA_CHType, \ + .dma_irq = SPI5_RX_DMA_IRQ, \ + .dma_rcc = SPI5_RX_DMA_RCC, \ + .request = SPI5_RX_DMA_REQUEST, \ + .channel = SPI5_RX_DMA_CHANNEL, \ + } +#endif /* SPI5_RX_DMA_CONFIG */ +#endif /* BSP_SPI5_RX_USING_DMA */ + + +#ifdef BSP_USING_SPI6 +#ifndef SPI6_BUS_CONFIG +#define SPI6_BUS_CONFIG \ + { \ + .SPIx = SPI6, \ + .bus_name = "spi6", \ + .irq_type = SPI6_IRQn, \ + } +#endif /* SPI6_BUS_CONFIG */ +#endif /* BSP_USING_SPI6 */ +#ifdef BSP_SPI6_TX_USING_DMA +#ifndef SPI6_TX_DMA_CONFIG +#define SPI6_TX_DMA_CONFIG \ + { \ + .DMAy = SPI6_TX_DMA, \ + .DMAChx = SPI6_TX_DMA_CHType, \ + .dma_irq = SPI6_TX_DMA_IRQ, \ + .dma_rcc = SPI6_TX_DMA_RCC, \ + .request = SPI6_TX_DMA_REQUEST, \ + .channel = SPI6_TX_DMA_CHANNEL, \ + } +#endif /* SPI6_TX_DMA_CONFIG */ +#endif /* BSP_SPI6_TX_USING_DMA */ +#ifdef BSP_SPI6_RX_USING_DMA +#ifndef SPI6_RX_DMA_CONFIG +#define SPI6_RX_DMA_CONFIG \ + { \ + .DMAy = SPI6_RX_DMA, \ + .DMAChx = SPI6_RX_DMA_CHType, \ + .dma_irq = SPI6_RX_DMA_IRQ, \ + .dma_rcc = SPI6_RX_DMA_RCC, \ + .request = SPI6_RX_DMA_REQUEST, \ + .channel = SPI6_RX_DMA_CHANNEL, \ + } +#endif /* SPI6_RX_DMA_CONFIG */ +#endif /* BSP_SPI6_RX_USING_DMA */ + + +#ifdef __cplusplus +} +#endif + +#endif /*__SPI_CONFIG_H__ */ diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h49x/adc_config.h b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h49x/adc_config.h new file mode 100644 index 000000000000..44d41e0af4c7 --- /dev/null +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h49x/adc_config.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-08-28 FUNMSAN first version + */ + +#ifndef __ADC_CONFIG_H__ +#define __ADC_CONFIG_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define N32H49X_ADC_CONFIG(ADC_INSTANCE, ADC_PERIPH) \ + { \ + .ADCx = ADC_INSTANCE, \ + .AHB_periph = ADC_PERIPH, \ + .EnablePeriphClk = RCC_EnableAHB1PeriphClk, \ + .ADC_ClkMode = ADC_CTRL3_CKMOD_AHB, \ + .RCC_ADCPrescaler = RCC_ADCHCLK_DIV16, \ + .ADC1M_ClkSource = RCC_ADC1MCLK_SRC_HSI, \ + .ADC1M_ClkPrescaler = RCC_ADC1MCLK_DIV8, \ + .Rank = 1U, \ + .ADC_SampleTime = ADC_SAMP_TIME_CYCLES_239_5, \ + .Init.WorkMode = ADC_WORKMODE_INDEPENDENT, \ + .Init.MultiChEn = DISABLE, \ + .Init.ContinueConvEn = DISABLE, \ + .Init.ExtTrigSelect = ADC_EXT_TRIG_REG_CONV_SOFTWARE, \ + .Init.DatAlign = ADC_DAT_ALIGN_R, \ + .Init.ChsNumber = 1U, \ + .Init.Resolution = ADC_DATA_RES_12BIT, \ + } + +#ifdef BSP_USING_ADC1 +#ifndef ADC1_CONFIG +#define ADC1_CONFIG N32H49X_ADC_CONFIG(ADC1, RCC_AHB_PERIPHEN_ADC1) +#endif /* ADC1_CONFIG */ +#endif /* BSP_USING_ADC1 */ + +#ifdef BSP_USING_ADC2 +#ifndef ADC2_CONFIG +#define ADC2_CONFIG N32H49X_ADC_CONFIG(ADC2, RCC_AHB_PERIPHEN_ADC2) +#endif /* ADC2_CONFIG */ +#endif /* BSP_USING_ADC2 */ + +#ifdef BSP_USING_ADC3 +#ifndef ADC3_CONFIG +#define ADC3_CONFIG N32H49X_ADC_CONFIG(ADC3, RCC_AHB_PERIPHEN_ADC3) +#endif /* ADC3_CONFIG */ +#endif /* BSP_USING_ADC3 */ + +#ifdef __cplusplus +} +#endif + +#endif /* __ADC_CONFIG_H__ */ diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h49x/dac_config.h b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h49x/dac_config.h new file mode 100644 index 000000000000..490257436ce4 --- /dev/null +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h49x/dac_config.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-08-28 FUNMSAN first version + */ + +#ifndef __DAC_CONFIG_H__ +#define __DAC_CONFIG_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define N32H49X_DAC_CONFIG(DAC_INSTANCE) \ + { \ + .DACx = DAC_INSTANCE, \ + .DAC_Module = DAC12, \ + .periph_clk = RCC_APB1_PERIPHEN_DAC12, \ + .EnablePeriphClk = RCC_EnableAPB1PeriphClk, \ + .DAC_clk_pres = 120U, \ + .Init.DAC_Trigger = DAC_Trigger_Software, \ + .Init.DAC_Trigger2 = DAC_Trigger2_Software, \ + .Init.DAC_WaveGeneration = DAC_WaveGeneration_None, \ + .Init.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0, \ + .Init.DAC_OutputBuffer = DISABLE, \ + .Init.DAC_TriggerEnable = ENABLE, \ + .Init.DAC_ConnectOnChipPeripheral = DISABLE, \ + .Init.DAC_ConnectExternalPin = ENABLE, \ + .Init.DAC_DMADoubleDataMode = DISABLE, \ + .Init.DAC_SignedFormat = DISABLE, \ + } + +#ifdef BSP_USING_DAC1 +#ifndef DAC1_CONFIG +#define DAC1_CONFIG N32H49X_DAC_CONFIG(DAC1) +#endif /* DAC1_CONFIG */ +#endif /* BSP_USING_DAC1 */ + +#ifdef BSP_USING_DAC2 +#ifndef DAC2_CONFIG +#define DAC2_CONFIG N32H49X_DAC_CONFIG(DAC2) +#endif /* DAC2_CONFIG */ +#endif /* BSP_USING_DAC2 */ + +#ifdef __cplusplus +} +#endif + +#endif /* __DAC_CONFIG_H__ */ diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h49x/dma_config.h b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h49x/dma_config.h index 2959dabb03c6..cdc6b803d512 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h49x/dma_config.h +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h49x/dma_config.h @@ -239,7 +239,7 @@ extern "C" { #define UART10_RX_DMA_RCC RCC_AHB_PERIPHEN_DMA2 #define UART10_RX_DMA_REQUEST DMA_REMAP_UART10_RX #define UART10_RX_DMA_CHANNEL 2U -#elif defined(BSP_USING_SDIO) && !defined(UART10_RX_DMA) +#elif defined(BSP_USING_SDIO) #define SDIO_RX_DMA DMA2 #define SDIO_TX_DMA DMA2 #define SDIO_RX_TX_DMA_IRQHandler DMA2_Channel2_IRQHandler diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h49x/i2c_hard_config.h b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h49x/i2c_hard_config.h new file mode 100644 index 000000000000..513bba9e0246 --- /dev/null +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h49x/i2c_hard_config.h @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-01-24 ox-horse first version + */ + +#ifndef __I2C_HARD_CONFIG_H__ +#define __I2C_HARD_CONFIG_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef BSP_USING_HARD_I2C1 +#ifndef I2C1_BUS_CONFIG +#define I2C1_BUS_CONFIG \ + { \ + .name = "hwi2c1", \ + .Instance = I2C1, \ + .timeout = 0x1000, \ + .evirq_type = I2C1_EV_IRQn, \ + .erirq_type = I2C1_ER_IRQn, \ + .scl_af_width = 0, \ + .sda_af_width = 0, \ + .df_width = 0, \ + .periph = RCC_APB1_PERIPHEN_I2C1, \ + .EnablePeriphClk = RCC_EnableAPB1PeriphClk, \ + } +#endif /* I2C1_BUS_CONFIG */ +#endif /* BSP_USING_HARD_I2C1 */ +#ifdef BSP_I2C1_RX_USING_DMA +#ifndef I2C1_RX_DMA_CONFIG +#define I2C1_RX_DMA_CONFIG \ + { \ + .DMAy = I2C1_RX_DMA, \ + .DMAChx = I2C1_RX_DMA_CHType, \ + .dma_irq = I2C1_RX_DMA_IRQ, \ + .dma_rcc = I2C1_RX_DMA_RCC, \ + .request = I2C1_RX_DMA_REQUEST, \ + .channel = I2C1_RX_DMA_CHANNEL, \ + } +#endif /* I2C1_RX_DMA_CONFIG */ +#endif /* BSP_I2C1_RX_USING_DMA */ +#ifdef BSP_I2C1_TX_USING_DMA +#ifndef I2C1_TX_DMA_CONFIG +#define I2C1_TX_DMA_CONFIG \ + { \ + .DMAy = I2C1_TX_DMA, \ + .DMAChx = I2C1_TX_DMA_CHType, \ + .dma_irq = I2C1_TX_DMA_IRQ, \ + .dma_rcc = I2C1_TX_DMA_RCC, \ + .request = I2C1_TX_DMA_REQUEST, \ + .channel = I2C1_TX_DMA_CHANNEL, \ + } +#endif /* I2C1_TX_DMA_CONFIG */ +#endif /* BSP_I2C1_TX_USING_DMA */ + + +#ifdef BSP_USING_HARD_I2C2 +#ifndef I2C2_BUS_CONFIG +#define I2C2_BUS_CONFIG \ + { \ + .name = "hwi2c2", \ + .Instance = I2C2, \ + .timeout = 0x1000, \ + .evirq_type = I2C2_EV_IRQn, \ + .erirq_type = I2C2_ER_IRQn, \ + .scl_af_width = 0, \ + .sda_af_width = 0, \ + .df_width = 0, \ + .periph = RCC_APB1_PERIPHEN_I2C2, \ + .EnablePeriphClk = RCC_EnableAPB1PeriphClk, \ + } +#endif /* I2C2_BUS_CONFIG */ +#endif /* BSP_USING_HARD_I2C2 */ +#ifdef BSP_I2C2_RX_USING_DMA +#ifndef I2C2_RX_DMA_CONFIG +#define I2C2_RX_DMA_CONFIG \ + { \ + .DMAy = I2C2_RX_DMA, \ + .DMAChx = I2C2_RX_DMA_CHType, \ + .dma_irq = I2C2_RX_DMA_IRQ, \ + .dma_rcc = I2C2_RX_DMA_RCC, \ + .request = I2C2_RX_DMA_REQUEST, \ + .channel = I2C2_RX_DMA_CHANNEL, \ + } +#endif /* I2C2_RX_DMA_CONFIG */ +#endif /* BSP_I2C2_RX_USING_DMA */ +#ifdef BSP_I2C2_TX_USING_DMA +#ifndef I2C2_TX_DMA_CONFIG +#define I2C2_TX_DMA_CONFIG \ + { \ + .DMAy = I2C2_TX_DMA, \ + .DMAChx = I2C2_TX_DMA_CHType, \ + .dma_irq = I2C2_TX_DMA_IRQ, \ + .dma_rcc = I2C2_TX_DMA_RCC, \ + .request = I2C2_TX_DMA_REQUEST, \ + .channel = I2C2_TX_DMA_CHANNEL, \ + } +#endif /* I2C2_TX_DMA_CONFIG */ +#endif /* BSP_I2C2_TX_USING_DMA */ + + +#ifdef BSP_USING_HARD_I2C3 +#ifndef I2C3_BUS_CONFIG +#define I2C3_BUS_CONFIG \ + { \ + .name = "hwi2c3", \ + .Instance = I2C3, \ + .timeout = 0x1000, \ + .evirq_type = I2C3_EV_IRQn, \ + .erirq_type = I2C3_ER_IRQn, \ + .scl_af_width = 0, \ + .sda_af_width = 0, \ + .df_width = 0, \ + .periph = RCC_APB1_PERIPHEN_I2C3, \ + .EnablePeriphClk = RCC_EnableAPB1PeriphClk, \ + } +#endif /* I2C3_BUS_CONFIG */ +#endif /* BSP_USING_HARD_I2C3 */ +#ifdef BSP_I2C3_RX_USING_DMA +#ifndef I2C3_RX_DMA_CONFIG +#define I2C3_RX_DMA_CONFIG \ + { \ + .DMAy = I2C3_RX_DMA, \ + .DMAChx = I2C3_RX_DMA_CHType, \ + .dma_irq = I2C3_RX_DMA_IRQ, \ + .dma_rcc = I2C3_RX_DMA_RCC, \ + .request = I2C3_RX_DMA_REQUEST, \ + .channel = I2C3_RX_DMA_CHANNEL, \ + } +#endif /* I2C3_RX_DMA_CONFIG */ +#endif /* BSP_I2C3_RX_USING_DMA */ +#ifdef BSP_I2C3_TX_USING_DMA +#ifndef I2C3_TX_DMA_CONFIG +#define I2C3_TX_DMA_CONFIG \ + { \ + .DMAy = I2C3_TX_DMA, \ + .DMAChx = I2C3_TX_DMA_CHType, \ + .dma_irq = I2C3_TX_DMA_IRQ, \ + .dma_rcc = I2C3_TX_DMA_RCC, \ + .request = I2C3_TX_DMA_REQUEST, \ + .channel = I2C3_TX_DMA_CHANNEL, \ + } +#endif /* I2C3_TX_DMA_CONFIG */ +#endif /* BSP_I2C3_TX_USING_DMA */ + + +#ifdef BSP_USING_HARD_I2C4 +#ifndef I2C4_BUS_CONFIG +#define I2C4_BUS_CONFIG \ + { \ + .name = "hwi2c4", \ + .Instance = I2C4, \ + .timeout = 0x1000, \ + .evirq_type = I2C4_EV_IRQn, \ + .erirq_type = I2C4_ER_IRQn, \ + .scl_af_width = 0, \ + .sda_af_width = 0, \ + .df_width = 0, \ + .periph = RCC_APB1_PERIPHEN_I2C4, \ + .EnablePeriphClk = RCC_EnableAPB1PeriphClk, \ + } +#endif /* I2C4_BUS_CONFIG */ +#endif /* BSP_USING_HARD_I2C4 */ +#ifdef BSP_I2C4_RX_USING_DMA +#ifndef I2C4_RX_DMA_CONFIG +#define I2C4_RX_DMA_CONFIG \ + { \ + .DMAy = I2C4_RX_DMA, \ + .DMAChx = I2C4_RX_DMA_CHType, \ + .dma_irq = I2C4_RX_DMA_IRQ, \ + .dma_rcc = I2C4_RX_DMA_RCC, \ + .request = I2C4_RX_DMA_REQUEST, \ + .channel = I2C4_RX_DMA_CHANNEL, \ + } +#endif /* I2C4_RX_DMA_CONFIG */ +#endif /* BSP_I2C4_RX_USING_DMA */ +#ifdef BSP_I2C4_TX_USING_DMA +#ifndef I2C4_TX_DMA_CONFIG +#define I2C4_TX_DMA_CONFIG \ + { \ + .DMAy = I2C4_TX_DMA, \ + .DMAChx = I2C4_TX_DMA_CHType, \ + .dma_irq = I2C4_TX_DMA_IRQ, \ + .dma_rcc = I2C4_TX_DMA_RCC, \ + .request = I2C4_TX_DMA_REQUEST, \ + .channel = I2C4_TX_DMA_CHANNEL, \ + } +#endif /* I2C4_TX_DMA_CONFIG */ +#endif /* BSP_I2C4_TX_USING_DMA */ + +#ifdef __cplusplus +} + +#endif + +#endif /*__I2C_HARD_CONFIG_H__ */ diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h49x/spi_config.h b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h49x/spi_config.h new file mode 100644 index 000000000000..6b0b97cdef10 --- /dev/null +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h49x/spi_config.h @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-06-03 hu-wenhao first version + */ + +#ifndef __SPI_CONFIG_H__ +#define __SPI_CONFIG_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef BSP_USING_SPI1 +#ifndef SPI1_BUS_CONFIG +#define SPI1_BUS_CONFIG \ + { \ + .SPIx = SPI1, \ + .bus_name = "spi1", \ + .irq_type = SPI1_IRQn, \ + } +#endif /* SPI1_BUS_CONFIG */ +#endif /* BSP_USING_SPI1 */ +#ifdef BSP_SPI1_TX_USING_DMA +#ifndef SPI1_TX_DMA_CONFIG +#define SPI1_TX_DMA_CONFIG \ + { \ + .DMAy = SPI1_TX_DMA, \ + .DMAChx = SPI1_TX_DMA_CHType, \ + .dma_irq = SPI1_TX_DMA_IRQ, \ + .dma_rcc = SPI1_TX_DMA_RCC, \ + .request = SPI1_TX_DMA_REQUEST, \ + .channel = SPI1_TX_DMA_CHANNEL, \ + } +#endif /* SPI1_TX_DMA_CONFIG */ +#endif /* BSP_SPI1_TX_USING_DMA */ +#ifdef BSP_SPI1_RX_USING_DMA +#ifndef SPI1_RX_DMA_CONFIG +#define SPI1_RX_DMA_CONFIG \ + { \ + .DMAy = SPI1_RX_DMA, \ + .DMAChx = SPI1_RX_DMA_CHType, \ + .dma_irq = SPI1_RX_DMA_IRQ, \ + .dma_rcc = SPI1_RX_DMA_RCC, \ + .request = SPI1_RX_DMA_REQUEST, \ + .channel = SPI1_RX_DMA_CHANNEL, \ + } +#endif /* SPI1_RX_DMA_CONFIG */ +#endif /* BSP_SPI1_RX_USING_DMA */ + + +#ifdef BSP_USING_SPI2 +#ifndef SPI2_BUS_CONFIG +#define SPI2_BUS_CONFIG \ + { \ + .SPIx = SPI2, \ + .bus_name = "spi2", \ + .irq_type = SPI2_I2S2_IRQn, \ + } +#endif /* SPI2_BUS_CONFIG */ +#endif /* BSP_USING_SPI2 */ +#ifdef BSP_SPI2_TX_USING_DMA +#ifndef SPI2_TX_DMA_CONFIG +#define SPI2_TX_DMA_CONFIG \ + { \ + .DMAy = SPI2_TX_DMA, \ + .DMAChx = SPI2_TX_DMA_CHType, \ + .dma_irq = SPI2_TX_DMA_IRQ, \ + .dma_rcc = SPI2_TX_DMA_RCC, \ + .request = SPI2_TX_DMA_REQUEST, \ + .channel = SPI2_TX_DMA_CHANNEL, \ + } +#endif /* SPI2_TX_DMA_CONFIG */ +#endif /* BSP_SPI2_TX_USING_DMA */ +#ifdef BSP_SPI2_RX_USING_DMA +#ifndef SPI2_RX_DMA_CONFIG +#define SPI2_RX_DMA_CONFIG \ + { \ + .DMAy = SPI2_RX_DMA, \ + .DMAChx = SPI2_RX_DMA_CHType, \ + .dma_irq = SPI2_RX_DMA_IRQ, \ + .dma_rcc = SPI2_RX_DMA_RCC, \ + .request = SPI2_RX_DMA_REQUEST, \ + .channel = SPI2_RX_DMA_CHANNEL, \ + } +#endif /* SPI2_RX_DMA_CONFIG */ +#endif /* BSP_SPI2_RX_USING_DMA */ + + +#ifdef BSP_USING_SPI3 +#ifndef SPI3_BUS_CONFIG +#define SPI3_BUS_CONFIG \ + { \ + .SPIx = SPI3, \ + .bus_name = "spi3", \ + .irq_type = SPI3_I2S3_IRQn, \ + } + +#endif /* SPI3_BUS_CONFIG */ +#endif /* BSP_USING_SPI3 */ +#ifdef BSP_SPI3_TX_USING_DMA +#ifndef SPI3_TX_DMA_CONFIG +#define SPI3_TX_DMA_CONFIG \ + { \ + .DMAy = SPI3_TX_DMA, \ + .DMAChx = SPI3_TX_DMA_CHType, \ + .dma_irq = SPI3_TX_DMA_IRQ, \ + .dma_rcc = SPI3_TX_DMA_RCC, \ + .request = SPI3_TX_DMA_REQUEST, \ + .channel = SPI3_TX_DMA_CHANNEL, \ + } +#endif /* SPI3_TX_DMA_CONFIG */ +#endif /* BSP_SPI3_TX_USING_DMA */ +#ifdef BSP_SPI3_RX_USING_DMA +#ifndef SPI3_RX_DMA_CONFIG +#define SPI3_RX_DMA_CONFIG \ + { \ + .DMAy = SPI3_RX_DMA, \ + .DMAChx = SPI3_RX_DMA_CHType, \ + .dma_irq = SPI3_RX_DMA_IRQ, \ + .dma_rcc = SPI3_RX_DMA_RCC, \ + .request = SPI3_RX_DMA_REQUEST, \ + .channel = SPI3_RX_DMA_CHANNEL, \ + } +#endif /* SPI3_RX_DMA_CONFIG */ +#endif /* BSP_SPI3_RX_USING_DMA */ + + +#ifdef BSP_USING_SPI4 +#ifndef SPI4_BUS_CONFIG +#define SPI4_BUS_CONFIG \ + { \ + .SPIx = SPI4, \ + .bus_name = "spi4", \ + .irq_type = SPI4_IRQn, \ + } +#endif /* SPI4_BUS_CONFIG */ +#endif /* BSP_USING_SPI4 */ +#ifdef BSP_SPI4_TX_USING_DMA +#ifndef SPI4_TX_DMA_CONFIG +#define SPI4_TX_DMA_CONFIG \ + { \ + .DMAy = SPI4_TX_DMA, \ + .DMAChx = SPI4_TX_DMA_CHType, \ + .dma_irq = SPI4_TX_DMA_IRQ, \ + .dma_rcc = SPI4_TX_DMA_RCC, \ + .request = SPI4_TX_DMA_REQUEST, \ + .channel = SPI4_TX_DMA_CHANNEL, \ + } +#endif /* SPI4_TX_DMA_CONFIG */ +#endif /* BSP_SPI4_TX_USING_DMA */ +#ifdef BSP_SPI4_RX_USING_DMA +#ifndef SPI4_RX_DMA_CONFIG +#define SPI4_RX_DMA_CONFIG \ + { \ + .DMAy = SPI4_RX_DMA, \ + .DMAChx = SPI4_RX_DMA_CHType, \ + .dma_irq = SPI4_RX_DMA_IRQ, \ + .dma_rcc = SPI4_RX_DMA_RCC, \ + .request = SPI4_RX_DMA_REQUEST, \ + .channel = SPI4_RX_DMA_CHANNEL, \ + } +#endif /* SPI4_RX_DMA_CONFIG */ +#endif /* BSP_SPI4_RX_USING_DMA */ + + +#ifdef BSP_USING_SPI5 +#ifndef SPI5_BUS_CONFIG +#define SPI5_BUS_CONFIG \ + { \ + .SPIx = SPI5, \ + .bus_name = "spi5", \ + .irq_type = SPI5_IRQn, \ + } +#endif /* SPI5_BUS_CONFIG */ +#endif /* BSP_USING_SPI5 */ +#ifdef BSP_SPI5_TX_USING_DMA +#ifndef SPI5_TX_DMA_CONFIG +#define SPI5_TX_DMA_CONFIG \ + { \ + .DMAy = SPI5_TX_DMA, \ + .DMAChx = SPI5_TX_DMA_CHType, \ + .dma_irq = SPI5_TX_DMA_IRQ, \ + .dma_rcc = SPI5_TX_DMA_RCC, \ + .request = SPI5_TX_DMA_REQUEST, \ + .channel = SPI5_TX_DMA_CHANNEL, \ + } +#endif /* SPI5_TX_DMA_CONFIG */ +#endif /* BSP_SPI5_TX_USING_DMA */ +#ifdef BSP_SPI5_RX_USING_DMA +#ifndef SPI5_RX_DMA_CONFIG +#define SPI5_RX_DMA_CONFIG \ + { \ + .DMAy = SPI5_RX_DMA, \ + .DMAChx = SPI5_RX_DMA_CHType, \ + .dma_irq = SPI5_RX_DMA_IRQ, \ + .dma_rcc = SPI5_RX_DMA_RCC, \ + .request = SPI5_RX_DMA_REQUEST, \ + .channel = SPI5_RX_DMA_CHANNEL, \ + } +#endif /* SPI5_RX_DMA_CONFIG */ +#endif /* BSP_SPI5_RX_USING_DMA */ + + +#ifdef BSP_USING_SPI6 +#ifndef SPI6_BUS_CONFIG +#define SPI6_BUS_CONFIG \ + { \ + .SPIx = SPI6, \ + .bus_name = "spi6", \ + .irq_type = SPI6_IRQn, \ + } +#endif /* SPI6_BUS_CONFIG */ +#endif /* BSP_USING_SPI6 */ +#ifdef BSP_SPI6_TX_USING_DMA +#ifndef SPI6_TX_DMA_CONFIG +#define SPI6_TX_DMA_CONFIG \ + { \ + .DMAy = SPI6_TX_DMA, \ + .DMAChx = SPI6_TX_DMA_CHType, \ + .dma_irq = SPI6_TX_DMA_IRQ, \ + .dma_rcc = SPI6_TX_DMA_RCC, \ + .request = SPI6_TX_DMA_REQUEST, \ + .channel = SPI6_TX_DMA_CHANNEL, \ + } +#endif /* SPI6_TX_DMA_CONFIG */ +#endif /* BSP_SPI6_TX_USING_DMA */ +#ifdef BSP_SPI6_RX_USING_DMA +#ifndef SPI6_RX_DMA_CONFIG +#define SPI6_RX_DMA_CONFIG \ + { \ + .DMAy = SPI6_RX_DMA, \ + .DMAChx = SPI6_RX_DMA_CHType, \ + .dma_irq = SPI6_RX_DMA_IRQ, \ + .dma_rcc = SPI6_RX_DMA_RCC, \ + .request = SPI6_RX_DMA_REQUEST, \ + .channel = SPI6_RX_DMA_CHANNEL, \ + } +#endif /* SPI6_RX_DMA_CONFIG */ +#endif /* BSP_SPI6_RX_USING_DMA */ + + +#ifdef __cplusplus +} +#endif + +#endif /*__SPI_CONFIG_H__ */ diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h7xx/dma_config.h b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h7xx/dma_config.h index e83eec43104b..e666d29fc632 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h7xx/dma_config.h +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/config/h7xx/dma_config.h @@ -492,7 +492,10 @@ extern "C" { #define SPI3_RX_DMA_IRQ DMA2_Channel7_IRQn #define SPI3_RX_DMA_HANDSHAK DMA_CH_HARDWARE_HANDSHAKING_IF_7 #define SPI3_RX_DMA_DMA_RCC RCC_AHB1_PERIPHEN_M7_DMA2 -#define SPI3_RX_DMA_DMA_CHANNEL DMA_CHANNEL_5 +/* DMAMUX1 output 15 feeds DMA2 channel 7 (output N -> DMA(N/8+1) ch(N%8)), + * so the channel must match the IRQ/handshake below; DMA_CHANNEL_5 was a + * copy-paste defect that armed ch5 while the request lands on ch7. */ +#define SPI3_RX_DMA_DMA_CHANNEL DMA_CHANNEL_7 #define SPI3_RX_DMA_DMAMUX_CHANNEL DMAMUX_CHANNEL_15 #define SPI3_RX_DMA_DMAMUX_REQUEST DMAMUX1_REQUEST_SPI3_RX #endif diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_adc.c b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_adc.c index 6e31a8dc82b0..746e23f087fa 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_adc.c +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_adc.c @@ -6,18 +6,34 @@ * Change Logs: * Date Author Notes * 2026-01-24 ox-horse first version + * 2026-08-28 FUNMSAN add N32H47_48x and N32H49x support */ #include #include -#if defined(BSP_USING_ADC1) || defined(BSP_USING_ADC2) || defined(BSP_USING_ADC3) +#if defined(BSP_USING_ADC1) || defined(BSP_USING_ADC2) || defined(BSP_USING_ADC3) || \ + (defined(SOC_SERIES_N32H47x_48x) && defined(BSP_USING_ADC4)) + +#if defined(SOC_SERIES_N32H7xx) +#include "n32h7xx_adc.h" +#include "n32h7xx_rcc.h" +#elif defined(SOC_SERIES_N32H47x_48x) +#include "n32h47x_48x_adc.h" +#include "n32h47x_48x_rcc.h" +#elif defined(SOC_SERIES_N32H49x) +#include "n32h49x_adc.h" +#include "n32h49x_rcc.h" +#endif + #include "drv_config.h" //#define DRV_DEBUG #define LOG_TAG "drv.adc" #include +#define N32_ADC_POLL_TIMEOUT 1000000U + typedef struct { @@ -28,6 +44,10 @@ typedef struct ADC_CTRL3_CKMOD ADC_ClkMode; uint32_t Pll_CLK_source; uint32_t RCC_ADCPrescaler; +#if defined(SOC_SERIES_N32H47x_48x) || defined(SOC_SERIES_N32H49x) + uint32_t ADC1M_ClkSource; + uint32_t ADC1M_ClkPrescaler; +#endif uint32_t Rank; uint32_t ADC_SampleTime; @@ -47,6 +67,10 @@ static ADC_InitInfo_t adc_config[] = { #ifdef BSP_USING_ADC3 ADC3_CONFIG, #endif + +#if defined(SOC_SERIES_N32H47x_48x) && defined(BSP_USING_ADC4) + ADC4_CONFIG, +#endif }; @@ -54,6 +78,10 @@ struct n32_adc { ADC_InitInfo_t ADC_Info; struct rt_adc_device n32_adc_device; +#if defined(SOC_SERIES_N32H47x_48x) || defined(SOC_SERIES_N32H49x) + rt_bool_t calibrated; + rt_bool_t enabled; +#endif }; @@ -121,9 +149,11 @@ static rt_err_t n32_adc_get_channel(rt_int8_t rt_channel, uint32_t *n32_channel) case 18: *n32_channel = ADC_CH_18; break; +#ifdef ADC_CH_19 case 19: *n32_channel = ADC_CH_19; break; +#endif #if defined(SOC_SERIES_N32H7xx) #ifdef ADC3_Channel_19_VREFINT @@ -143,6 +173,18 @@ static rt_err_t n32_adc_get_channel(rt_int8_t rt_channel, uint32_t *n32_channel) *n32_channel = ADC3_Channel_18_Temperture_Sensor; break; #endif /* ADC3_Channel_18_Temperture_Sensor */ +#elif defined(SOC_SERIES_N32H47x_48x) || defined(SOC_SERIES_N32H49x) + case RT_ADC_INTERN_CH_VREF: + *n32_channel = ADC1_Channel_18_VREFINT; + break; + + case RT_ADC_INTERN_CH_VBAT: + *n32_channel = ADC1_Channel_17_Battery_DIV2; + break; + + case RT_ADC_INTERN_CH_TEMPER: + *n32_channel = ADC1_Channel_16_Temperture_Sensor; + break; #endif default: @@ -155,12 +197,20 @@ static rt_err_t n32_adc_get_channel(rt_int8_t rt_channel, uint32_t *n32_channel) static rt_err_t n32_adc_enabled(struct rt_adc_device *device, rt_int8_t channel, rt_bool_t enabled) { +#if defined(SOC_SERIES_N32H7xx) volatile uint32_t index = 0; +#elif defined(SOC_SERIES_N32H47x_48x) || defined(SOC_SERIES_N32H49x) + rt_uint32_t timeout; + struct n32_adc *adc_obj; +#endif uint32_t ADC_Channel; ADC_InitInfo_t *n32_adc_info; RT_ASSERT(device != RT_NULL); n32_adc_info = device->parent.user_data; +#if defined(SOC_SERIES_N32H47x_48x) || defined(SOC_SERIES_N32H49x) + adc_obj = rt_container_of(device, struct n32_adc, n32_adc_device); +#endif if (enabled) { @@ -229,6 +279,82 @@ static rt_err_t n32_adc_enabled(struct rt_adc_device *device, rt_int8_t channel, /* Start ADC Software Conversion */ ADC_EnableSoftwareStartConv(n32_adc_info->ADCx, ENABLE); +#elif defined(SOC_SERIES_N32H47x_48x) || defined(SOC_SERIES_N32H49x) + if (channel == RT_ADC_INTERN_CH_TEMPER) + { + if (n32_adc_info->ADCx != ADC1) + { + LOG_E("%s not supported", device->parent.parent.name); + return -RT_EINVAL; + } + ADC_EnableTempSensorVrefint(ENABLE); + } + else if (channel == RT_ADC_INTERN_CH_VREF) + { +#if defined(SOC_SERIES_N32H47x_48x) + if ((n32_adc_info->ADCx != ADC1) && (n32_adc_info->ADCx != ADC2)) +#elif defined(SOC_SERIES_N32H49x) + if ((n32_adc_info->ADCx != ADC1) && (n32_adc_info->ADCx != ADC2) && + (n32_adc_info->ADCx != ADC3)) +#endif + { + LOG_E("%s not supported", device->parent.parent.name); + return -RT_EINVAL; + } + ADC_EnableTempSensorVrefint(ENABLE); + } + else if (channel == RT_ADC_INTERN_CH_VBAT) + { + if (n32_adc_info->ADCx != ADC1) + { + LOG_E("%s not supported", device->parent.parent.name); + return -RT_EINVAL; + } + ADC_EnableBatteryVoltageMonitor(n32_adc_info->ADCx, ENABLE); + } + + if (adc_obj->enabled == RT_FALSE) + { + /* RDY/PDRDY are sticky write-one-to-clear flags. Clear the status + * from the previous power cycle before waiting for a new RDY. */ + ADC_ClearFlag(n32_adc_info->ADCx, ADC_FLAG_RDY); + ADC_ClearFlag(n32_adc_info->ADCx, ADC_FLAG_PDRDY); + + ADC_Enable(n32_adc_info->ADCx, ENABLE); + timeout = N32_ADC_POLL_TIMEOUT; + while (ADC_GetFlagStatus(n32_adc_info->ADCx, ADC_FLAG_RDY) == RESET) + { + if (timeout-- == 0U) + { + ADC_Enable(n32_adc_info->ADCx, DISABLE); + LOG_E("%s ready timeout", device->parent.parent.name); + return -RT_ETIMEOUT; + } + } + + /* The calibration factor is retained while the ADC is disabled. Re-running + * calibration on every RT_ADC_CMD_ENABLE can leave a new calibration in + * progress because the vendor status API also checks the old factor. */ + if (adc_obj->calibrated == RT_FALSE) + { + ADC_CalibrationOperation(n32_adc_info->ADCx, ADC_CALIBRATION_SINGLE_MODE); + timeout = N32_ADC_POLL_TIMEOUT; + while (ADC_GetCalibrationStatus(n32_adc_info->ADCx, ADC_CALIBRATION_SINGLE_MODE) != RESET) + { + if (timeout-- == 0U) + { + ADC_Enable(n32_adc_info->ADCx, DISABLE); + LOG_E("%s calibration timeout", device->parent.parent.name); + return -RT_ETIMEOUT; + } + } + adc_obj->calibrated = RT_TRUE; + } + + adc_obj->enabled = RT_TRUE; + } + + ADC_ConfigRegularChannel(n32_adc_info->ADCx, ADC_Channel, n32_adc_info->Rank, n32_adc_info->ADC_SampleTime); #endif } else @@ -252,6 +378,26 @@ static rt_err_t n32_adc_enabled(struct rt_adc_device *device, rt_int8_t channel, /* Disable ADC */ ADC_Enable(n32_adc_info->ADCx, DISABLE); +#elif defined(SOC_SERIES_N32H47x_48x) || defined(SOC_SERIES_N32H49x) + if ((channel == RT_ADC_INTERN_CH_VREF) || (channel == RT_ADC_INTERN_CH_TEMPER)) + { + ADC_EnableTempSensorVrefint(DISABLE); + } + else if (channel == RT_ADC_INTERN_CH_VBAT) + { + ADC_EnableBatteryVoltageMonitor(n32_adc_info->ADCx, DISABLE); + } + + if (adc_obj->enabled == RT_TRUE) + { + ADC_Enable(n32_adc_info->ADCx, DISABLE); + adc_obj->enabled = RT_FALSE; + + /* ADC_Enable(DISABLE) clears ON directly. PDRDY is not asserted in + * every power mode, so it must not be a mandatory completion event. */ + ADC_ClearFlag(n32_adc_info->ADCx, ADC_FLAG_RDY); + ADC_ClearFlag(n32_adc_info->ADCx, ADC_FLAG_PDRDY); + } #endif } @@ -263,6 +409,9 @@ static rt_err_t n32_adc_get_value(struct rt_adc_device *device, rt_int8_t channe { uint16_t Convert_Value = 0; ADC_InitInfo_t *n32_adc_info; +#if defined(SOC_SERIES_N32H47x_48x) || defined(SOC_SERIES_N32H49x) + rt_uint32_t timeout; +#endif RT_ASSERT(device != RT_NULL); RT_ASSERT(value != RT_NULL); @@ -286,6 +435,28 @@ static rt_err_t n32_adc_get_value(struct rt_adc_device *device, rt_int8_t channe { Convert_Value *= 4U; } +#elif defined(SOC_SERIES_N32H47x_48x) || defined(SOC_SERIES_N32H49x) + /* Start one software conversion for the selected regular channel */ + ADC_EnableSoftwareStartConv(n32_adc_info->ADCx, ENABLE); + + timeout = N32_ADC_POLL_TIMEOUT; + while (ADC_GetFlagStatus(n32_adc_info->ADCx, ADC_FLAG_ENDC) != SET) + { + if (timeout-- == 0U) + { + LOG_E("%s conversion timeout", device->parent.parent.name); + return -RT_ETIMEOUT; + } + } + + ADC_ClearFlag(n32_adc_info->ADCx, ADC_FLAG_ENDC); + ADC_ClearFlag(n32_adc_info->ADCx, ADC_FLAG_STR); + Convert_Value = ADC_GetDat(n32_adc_info->ADCx); + + if (channel == RT_ADC_INTERN_CH_VBAT) + { + Convert_Value *= 2U; + } #endif *value = Convert_Value; @@ -315,6 +486,30 @@ static rt_uint8_t n32_adc_get_resolution(struct rt_adc_device *device) Resolution = 10; break; } +#elif defined(SOC_SERIES_N32H47x_48x) || defined(SOC_SERIES_N32H49x) + case ADC_DATA_RES_12BIT: + { + Resolution = 12; + break; + } + + case ADC_DATA_RES_10BIT: + { + Resolution = 10; + break; + } + + case ADC_DATA_RES_8BIT: + { + Resolution = 8; + break; + } + + case ADC_DATA_RES_6BIT: + { + Resolution = 6; + break; + } #endif default: break; @@ -368,6 +563,19 @@ static int n32_adc_init(void) name_buf[3] = '3'; } #endif +#if defined(ADC4) + if (n32_adc_obj[i].ADC_Info.ADCx == ADC4) + { + name_buf[3] = '4'; + } +#endif + +#if defined(SOC_SERIES_N32H47x_48x) || defined(SOC_SERIES_N32H49x) + n32_adc_obj[i].ADC_Info.EnablePeriphClk(n32_adc_obj[i].ADC_Info.AHB_periph, ENABLE); + ADC_ConfigClk(n32_adc_obj[i].ADC_Info.ADC_ClkMode, n32_adc_obj[i].ADC_Info.RCC_ADCPrescaler); + RCC_ConfigAdc1mClk(n32_adc_obj[i].ADC_Info.ADC1M_ClkSource, + n32_adc_obj[i].ADC_Info.ADC1M_ClkPrescaler); +#endif ADC_Init(n32_adc_obj[i].ADC_Info.ADCx, &n32_adc_obj[i].ADC_Info.Init); diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_config.h b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_config.h index 42e6334d2c0c..7240f9660257 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_config.h +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_config.h @@ -6,6 +6,7 @@ * Change Logs: * Date Author Notes * 2026-01-24 ox-horse first version + * 2026-08-28 FUNMSAN add N32H47_48x and N32H49x support */ #ifndef __DRV_CONFIG_H_ @@ -34,11 +35,19 @@ extern "C" { #elif defined(SOC_SERIES_N32H49x) #include "h49x/dma_config.h" #include "h49x/uart_config.h" +#include "h49x/adc_config.h" +#include "h49x/dac_config.h" #include "h49x/can_config.h" +#include "h49x/spi_config.h" +#include "h49x/i2c_hard_config.h" #elif defined(SOC_SERIES_N32H47x_48x) #include "h47x_48x/dma_config.h" #include "h47x_48x/uart_config.h" +#include "h47x_48x/adc_config.h" +#include "h47x_48x/dac_config.h" #include "h47x_48x/can_config.h" +#include "h47x_48x/spi_config.h" +#include "h47x_48x/i2c_hard_config.h" #endif /* defined(SOC_SERIES_N32H7xx) */ #ifdef __cplusplus diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_dac.c b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_dac.c index 16f4b9bb5524..1a4256f3affa 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_dac.c +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_dac.c @@ -7,15 +7,28 @@ * Date Author Notes * 2026-01-24 ox-horse first version * 2026-06-09 li.mengmeng port for N32H7xx + * 2026-08-28 FUNMSAN add N32H47_48x and N32H49x support */ #include #include -#if defined(BSP_USING_DAC1) || defined(BSP_USING_DAC2) +#if defined(BSP_USING_DAC1) || defined(BSP_USING_DAC2) || \ + (defined(SOC_SERIES_N32H47x_48x) && (defined(BSP_USING_DAC3) || defined(BSP_USING_DAC4) || \ + defined(BSP_USING_DAC5) || defined(BSP_USING_DAC6) || defined(BSP_USING_DAC7) || \ + defined(BSP_USING_DAC8))) +#if defined(SOC_SERIES_N32H7xx) #include "n32h7xx_rcc.h" #include "n32h7xx_dac.h" +#elif defined(SOC_SERIES_N32H47x_48x) +#include "n32h47x_48x_rcc.h" +#include "n32h47x_48x_dac.h" +#elif defined(SOC_SERIES_N32H49x) +#include "n32h49x_rcc.h" +#include "n32h49x_dac.h" +#endif + #include "drv_config.h" //#define DRV_DEBUG @@ -46,9 +59,32 @@ static DAC_InitInfo_t dac_config[] = { DAC2_CONFIG, #endif -}; +#if defined(SOC_SERIES_N32H47x_48x) +#ifdef BSP_USING_DAC3 + DAC3_CONFIG, +#endif -static struct n32_dac n32_dac_obj[sizeof(dac_config) / sizeof(dac_config[0])]; +#ifdef BSP_USING_DAC4 + DAC4_CONFIG, +#endif + +#ifdef BSP_USING_DAC5 + DAC5_CONFIG, +#endif + +#ifdef BSP_USING_DAC6 + DAC6_CONFIG, +#endif + +#ifdef BSP_USING_DAC7 + DAC7_CONFIG, +#endif + +#ifdef BSP_USING_DAC8 + DAC8_CONFIG, +#endif +#endif /* SOC_SERIES_N32H47x_48x */ +}; struct n32_dac { @@ -56,6 +92,8 @@ struct n32_dac struct rt_dac_device n32_dac_device; }; +static struct n32_dac n32_dac_obj[sizeof(dac_config) / sizeof(dac_config[0])]; + static rt_err_t n32_dac_enabled(struct rt_dac_device *device, rt_uint32_t channel) { DAC_InitInfo_t *n32_dac_info; @@ -86,7 +124,7 @@ static rt_uint8_t n32_dac_get_resolution(struct rt_dac_device *device) { RT_ASSERT(device != RT_NULL); - /* N32H7xx DAC supports 12-bit resolution */ + /* N32H7xx and N32H47x/48x DACs support 12-bit resolution */ return 12; } @@ -142,13 +180,48 @@ static int n32_dac_init(void) { name_buf[3] = '2'; } +#if defined(SOC_SERIES_N32H47x_48x) + else if (n32_dac_obj[i].DAC_Info.DACx == DAC3) + { + name_buf[3] = '3'; + } + else if (n32_dac_obj[i].DAC_Info.DACx == DAC4) + { + name_buf[3] = '4'; + } + else if (n32_dac_obj[i].DAC_Info.DACx == DAC5) + { + name_buf[3] = '5'; + } + else if (n32_dac_obj[i].DAC_Info.DACx == DAC6) + { + name_buf[3] = '6'; + } + else if (n32_dac_obj[i].DAC_Info.DACx == DAC7) + { + name_buf[3] = '7'; + } + else if (n32_dac_obj[i].DAC_Info.DACx == DAC8) + { + name_buf[3] = '8'; + } +#endif /* SOC_SERIES_N32H47x_48x */ /* Enable DAC peripheral clock */ n32_dac_obj[i].DAC_Info.EnablePeriphClk(n32_dac_obj[i].DAC_Info.periph_clk, ENABLE); /* Config DAC12 prescaler factor,DAC_CLK = 1M*/ DAC_ConfigClkPrescaler(n32_dac_obj[i].DAC_Info.DAC_Module, n32_dac_obj[i].DAC_Info.DAC_clk_pres); +#if defined(SOC_SERIES_N32H7xx) || defined(SOC_SERIES_N32H49x) DAC_SetHighFrequencyMode(n32_dac_obj[i].DAC_Info.DAC_Module, DAC_HIGH_FREQ_MODE_BELOW_160M); +#elif defined(SOC_SERIES_N32H47x_48x) + /* The high-frequency mode field is only available for DAC1~DAC4. */ + if ((n32_dac_obj[i].DAC_Info.DAC_Module == DAC12) || + (n32_dac_obj[i].DAC_Info.DAC_Module == DAC34)) + { + DAC_SetHighFrequencyMode(n32_dac_obj[i].DAC_Info.DAC_Module, DAC_HIGH_FREQ_MODE_BELOW_160M); + } +#endif /* Initialize DAC */ DAC_Init(n32_dac_obj[i].DAC_Info.DACx, &n32_dac_obj[i].DAC_Info.Init); diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_eth.c b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_eth.c index 071538a173cf..115f54a44aa7 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_eth.c +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_eth.c @@ -9,6 +9,7 @@ */ #include "drv_config.h" +#include #include "drv_eth.h" #include #include @@ -99,10 +100,12 @@ static rt_err_t rt_n32_eth_init(rt_device_t dev) struct rt_n32_eth *eth = (struct rt_n32_eth *)dev; EthFuncStatusType status; +#if defined(SOC_SERIES_N32H7xx) /* Enable or disable PWR Clock */ RCC_EnableAHB5PeriphClk2(RCC_AHB5_PERIPHEN_PWR, ENABLE); /* Enable or disable the power domain of the ETH */ PWR_MoudlePowerEnable(ETH_PWR_CTRL, ENABLE); +#endif /* Enable or disable ETH Clock */ ETH_RCC_ENABLE_AHB_PERIPHEN_CLK(ETH_RCC_AHB_PERIPHEN, ENABLE); @@ -132,7 +135,7 @@ static rt_err_t rt_n32_eth_init(rt_device_t dev) ETH_DeInit(ETH); /* Set ETH initialization parameters by default */ - ETH_StructInit(ETH, &sETH_InitParam); + ETH_STRUCT_INIT(&sETH_InitParam); /* Modify ETH initialization parameters (only override StructInit defaults) */ /* MAC Configuration */ @@ -687,6 +690,44 @@ static void phy_linkchange(void) phy_speed_new |= PHY_FULL_DUPLEX; } +#elif defined(PHY_USING_DM9162EP) + /* DM9162EP: read PHY specific status register (0x11) for speed/duplex */ + ETH_ReadPHYRegister(ETH, phy_addr, PHY_SDSR, &SR); + LOG_D("DM9162EP status reg is 0x%X", (unsigned int)SR); + + if (PHY_Status_SPEED_100M(SR)) + { + phy_speed_new |= PHY_100M; + } + else + { + phy_speed_new |= PHY_10M; + } + + if (PHY_Status_FULL_DUPLEX(SR)) + { + phy_speed_new |= PHY_FULL_DUPLEX; + } + +#elif defined(PHY_USING_LAN8720A) + /* LAN8720A: read PHY specific status register (0x1F) for speed/duplex */ + ETH_ReadPHYRegister(ETH, phy_addr, PHY_SDSR, &SR); + LOG_D("LAN8720A status reg is 0x%X", (unsigned int)SR); + + if (PHY_Status_SPEED_100M(SR)) + { + phy_speed_new |= PHY_100M; + } + else + { + phy_speed_new |= PHY_10M; + } + + if (PHY_Status_FULL_DUPLEX(SR)) + { + phy_speed_new |= PHY_FULL_DUPLEX; + } + #else /* Generic PHY: read from status register only, no extended info */ phy_speed_new |= PHY_100M; @@ -703,12 +744,15 @@ static void phy_linkchange(void) { LOG_I("link up"); +#if defined(SOC_SERIES_N32H7xx) if (phy_speed & PHY_1000M) { LOG_D("1000Mbps"); n32_eth_device.ETH_Speed = ETH_SPEED_1000M; } - else if (phy_speed & PHY_100M) + else +#endif + if (phy_speed & PHY_100M) { LOG_D("100Mbps"); n32_eth_device.ETH_Speed = ETH_SPEED_100M; @@ -734,7 +778,7 @@ static void phy_linkchange(void) * in MACCFG (PS[15], FES[14], DM[13]), then restart everything. */ ETH_StopIT(ETH, &sEthInfo); MODIFY_REG(ETH->MACCFG, - (ETH_MACCFG_PS | ETH_MACCFG_FES | ETH_MACCFG_DM), + ETH_MACCFG_SPEED_DUPLEX_MASK, (n32_eth_device.ETH_Speed | n32_eth_device.ETH_Mode)); ETH_StartIT(ETH, &sEthInfo); @@ -894,7 +938,7 @@ static int rt_hw_n32_eth_init(void) /* Set default speed based on media interface: * GMII → 1000M, MII/RMII → 100M. Actual speed is updated after PHY auto-negotiation. */ -#if (ETH_MEDIA_INTERFACE == ETH_GMII_MODE) +#if defined(SOC_SERIES_N32H7xx) && (ETH_MEDIA_INTERFACE == ETH_GMII_MODE) n32_eth_device.ETH_Speed = ETH_SPEED_1000M; #else n32_eth_device.ETH_Speed = ETH_SPEED_100M; @@ -1026,6 +1070,10 @@ const char *n32_eth_get_phy_name(void) return "RTL8211EG"; #elif defined(PHY_USING_YT8522H) return "YT8522H"; +#elif defined(PHY_USING_DM9162EP) + return "DM9162EP"; +#elif defined(PHY_USING_LAN8720A) + return "LAN8720A"; #else return "Unknown"; #endif diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_eth.h b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_eth.h index 6a636d4dd01a..f0812418a9b0 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_eth.h +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_eth.h @@ -15,9 +15,15 @@ #include #include #include +#if defined(SOC_SERIES_N32H47x_48x) +#include "n32h47x_48x_eth.h" +#elif defined(SOC_SERIES_N32H49x) +#include "n32h49x_eth.h" +#elif defined(SOC_SERIES_N32H7xx) #include "n32h7xx_eth.h" #include "n32h7xx_rcc.h" #include "n32h7xx_pwr.h" +#endif #ifdef __cplusplus extern "C" { @@ -25,6 +31,14 @@ extern "C" { /* ======================= ETH Instance Selection ======================= */ +#if defined(SOC_SERIES_N32H47x_48x) || defined(SOC_SERIES_N32H49x) +/* n32h47x_48x / n32h49x have a single ETH instance; ETH pointer & ETH_IRQn come from the device header. */ +#define ETH_RCC_ENABLE_AHB_PERIPHEN_CLK RCC_EnableAHBPeriphClk +#define ETH_RCC_AHB_PERIPHEN RCC_AHB_PERIPHEN_ETH +#define ETH_GLOBAL_IRQ ETH_IRQn +#define ETH_GLOBAL_IRQHANDLER ETH_IRQHandler + +#elif defined(SOC_SERIES_N32H7xx) #if defined(BSP_USING_ETH1) #define ETH ETH1 #define ETH_RCC_ENABLE_AHB_PERIPHEN_CLK RCC_EnableAHB2PeriphClk2 @@ -49,6 +63,8 @@ extern "C" { #endif /* BSP_USING_ETH1 / BSP_USING_ETH2 */ +#endif /* SOC_SERIES_N32H47x_48x / SOC_SERIES_N32H7xx */ + /* ======================= Media Interface Selection ======================= */ #if defined(ETH_INTERFACE_USING_GMII) @@ -58,7 +74,21 @@ extern "C" { #elif defined(ETH_INTERFACE_USING_RMII) #define ETH_MEDIA_INTERFACE ETH_RMII_MODE #else -#define ETH_MEDIA_INTERFACE ETH_GMII_MODE +#error "No ETH media interface selected (define ETH_INTERFACE_USING_GMII/MII/RMII)" +#endif + +/* ======================= MACCFG Speed/Duplex Bit Mask ======================= */ +#if defined(SOC_SERIES_N32H47x_48x) || defined(SOC_SERIES_N32H49x) +#define ETH_MACCFG_SPEED_DUPLEX_MASK (ETH_SPEED_100M | ETH_FULL_DUPLEX_MODE) +#else +#define ETH_MACCFG_SPEED_DUPLEX_MASK (ETH_MACCFG_PS | ETH_MACCFG_FES | ETH_MACCFG_DM) +#endif + +/* ======================= ETH_Init Param Helper ======================= */ +#if defined(SOC_SERIES_N32H47x_48x) || defined(SOC_SERIES_N32H49x) +#define ETH_STRUCT_INIT(p) ETH_StructInit(p) +#else +#define ETH_STRUCT_INIT(p) ETH_StructInit(ETH, p) #endif /* ======================= Standard IEEE 802.3 PHY Registers ======================= */ @@ -141,6 +171,29 @@ extern "C" { #define PHY_INT_MASK ((1 << 15) | (1 << 14) | (1 << 13) | (1 << 11) | (1 << 10)) #endif /* PHY_USING_YT8522H */ +/* ======================= DM9162EP PHY Specific Registers ======================= */ + +#ifdef PHY_USING_DM9162EP +/* DM9162EP is a Fast Ethernet PHY supporting MII/RMII, up to 100M */ +#define PHY_DM9162EP_ID1 0x0181U +#define PHY_DM9162EP_ID2 0xB8A0U + +/* DM9162EP PHY Specific Status Register (0x11) + * bit15 = 100M Full-Duplex, bit14 = 100M Half-Duplex, + * bit13 = 10M Full-Duplex, bit12 = 10M Half-Duplex */ +#define PHY_SDSR 0x11U +#define PHY_Status_SPEED_100M(sr) (((sr) & ((1 << 15) | (1 << 14))) != 0) +#define PHY_Status_SPEED_10M(sr) (!PHY_Status_SPEED_100M(sr)) +#define PHY_Status_FULL_DUPLEX(sr) (((sr) & ((1 << 15) | (1 << 13))) != 0) + +/* DM9162EP PHY Specific Interrupt Registers + * 0x15 Interrupt Source Flag / Interrupt Mask */ +#define PHY_INTERRUPT_FLAG_REG 0x15U +#define PHY_INTERRUPT_MASK_REG 0x15U +#define PHY_INT_MASK 0U + +#endif /* PHY_USING_DM9162EP */ + /* ======================= RTL8211EG PHY Specific Registers ======================= */ #ifdef PHY_USING_RTL8211EG @@ -166,6 +219,28 @@ extern "C" { #define PHY_INT_MASK ((1 << 15) | (1 << 11) | (1 << 10)) #endif /* PHY_USING_RTL8211EG */ +/* ======================= LAN8720A PHY Specific Registers ======================= */ + +#ifdef PHY_USING_LAN8720A +/* LAN8720A is a Fast Ethernet PHY supporting MII/RMII, up to 100M */ +#define PHY_LAN8720A_ID1 0x0007U +#define PHY_LAN8720A_ID2 0xC0F1U + +/* LAN8720A PHY Specific Status Register (0x1F) + * bit4 = Full-Duplex, bit3 = 100M, bit2 = 10M */ +#define PHY_SDSR 0x1FU +#define PHY_Status_SPEED_100M(sr) (((sr) & (1 << 3)) != 0) +#define PHY_Status_SPEED_10M(sr) (((sr) & (1 << 2)) != 0) +#define PHY_Status_FULL_DUPLEX(sr) (((sr) & (1 << 4)) != 0) + +/* LAN8720A PHY Specific Interrupt Registers + * 0x1D Interrupt Source Flag, 0x1E Interrupt Mask */ +#define PHY_INTERRUPT_FLAG_REG 0x1DU +#define PHY_INTERRUPT_MASK_REG 0x1EU +#define PHY_INT_MASK (1 << 4) + +#endif /* PHY_USING_LAN8720A */ + /* ======================= Buffer & Descriptor Configuration ======================= */ #ifndef ETH_TX_DESC_NUMBER @@ -175,7 +250,12 @@ extern "C" { #define ETH_RX_DESC_NUMBER ((uint32_t)4U) #endif -/* ETH_MAX_PACKET_SIZE is defined in n32h7xx_eth.h (1528U) */ +/* ETH_MAX_PACKET_SIZE is defined in n32h7xx_eth.h (1528U); n32h47x_48x has no such macro. */ +#if defined(SOC_SERIES_N32H47x_48x) || defined(SOC_SERIES_N32H49x) +#ifndef ETH_MAX_PACKET_SIZE +#define ETH_MAX_PACKET_SIZE (1528U) +#endif +#endif #define ETH_TX_BUF_SIZE (ETH_TX_DESC_NUMBER * ETH_MAX_PACKET_SIZE) #define ETH_RX_BUF_SIZE (ETH_RX_DESC_NUMBER * ETH_MAX_PACKET_SIZE) diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_gpio.c b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_gpio.c index f63d680db48e..a3ab1844c3fd 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_gpio.c +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_gpio.c @@ -24,40 +24,17 @@ static uint32_t pin_irq_enable_mask = 0; -#if defined(GPIOK) + #if defined(SOC_SERIES_N32H7xx) + +#define __N32_PORT_MAX 11u + +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + #define __N32_PORT_MAX 8u + #endif -#elif defined(GPIOJ) -#define __N32_PORT_MAX 16u -#elif defined(GPIOI) -#define __N32_PORT_MAX 16u -#elif defined(GPIOH) -#if defined(SOC_SERIES_N32H7xx) -#define __N32_PORT_MAX 16u -#elif defined(SOC_SERIES_N32H49x) -#define __N32_PORT_MAX 6u -#elif defined(SOC_SERIES_N32H47x_48x) -#define __N32_PORT_MAX 7u -#endif -#elif defined(GPIOG) -#define __N32_PORT_MAX 16u -#elif defined(GPIOF) -#define __N32_PORT_MAX 16u -#elif defined(GPIOE) -#define __N32_PORT_MAX 16u -#elif defined(GPIOD) -#define __N32_PORT_MAX 16u -#elif defined(GPIOC) -#define __N32_PORT_MAX 16u -#elif defined(GPIOB) -#define __N32_PORT_MAX 16u -#elif defined(GPIOA) -#define __N32_PORT_MAX 16u -#else -#define __N32_PORT_MAX 0u -#error Unsupported N32 GPIO peripheral. -#endif + #define PIN_STPORT_MAX __N32_PORT_MAX @@ -390,12 +367,12 @@ static rt_err_t n32_pin_irq_enable(struct rt_device *device, rt_base_t pin, break; case PIN_IRQ_MODE_FALLING: GPIO_InitStructure.GPIO_Mode = GPIO_MODE_INPUT; - GPIO_InitStructure.GPIO_Pull = GPIO_PULL_DOWN; + GPIO_InitStructure.GPIO_Pull = GPIO_PULL_UP; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; break; case PIN_IRQ_MODE_RISING_FALLING: GPIO_InitStructure.GPIO_Mode = GPIO_MODE_INPUT; - GPIO_InitStructure.GPIO_Pull = GPIO_NO_PULL; + GPIO_InitStructure.GPIO_Pull = GPIO_PULL_UP; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; break; } @@ -476,25 +453,49 @@ rt_inline void pin_irq_hdr(int irqno) void N32_GPIO_EXTI_Callback(uint16_t line_num) { -#if defined(SOC_SERIES_N32H7xx) - if (pin_irq_hdr_tab[line_num].pin != -1 && EXTI_GetITStatus(line_num) != RESET) +#if defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + + if (EXTI_GetITStatus(line_num) != RESET) { /* Clear EXTI line pending bit */ EXTI_ClrITPendBit(line_num); - pin_irq_hdr(line_num); - } -#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) - { - rt_int32_t idx = bit2bitno(line_num); - if (idx >= 0 && idx < (rt_int32_t)ITEM_NUM(pin_irq_hdr_tab) && pin_irq_hdr_tab[idx].pin != -1 && EXTI_GetITStatus(line_num) != RESET) + uint8_t index = 0; + uint32_t mask = line_num; + + if (mask & 0xFF00) + { + index += 8; + mask >>= 8; + } + if (mask & 0xF0) + { + index += 4; + mask >>= 4; + } + if (mask & 0xC) + { + index += 2; + mask >>= 2; + } + if (mask & 0x2) { - /* Clear EXTI line pending bit */ - EXTI_ClrITPendBit(line_num); + index += 1; + } - pin_irq_hdr(idx); + if ((index < 16) && (pin_irq_hdr_tab[index].pin != -1)) + { + pin_irq_hdr(index); } } +#elif defined(SOC_SERIES_N32H7xx) + if (pin_irq_hdr_tab[line_num].pin != -1 && EXTI_GetITStatus(line_num) != RESET) + { + /* Clear EXTI line pending bit */ + EXTI_ClrITPendBit(line_num); + + pin_irq_hdr(line_num); + } #endif } diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_gpio.h b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_gpio.h index db0ff4852b9f..65db99408821 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_gpio.h +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_gpio.h @@ -14,6 +14,7 @@ #include #include + #ifdef __cplusplus extern "C" { #endif diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_hard_i2c.c b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_hard_i2c.c index 3fa639d58d51..38acff1ba780 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_hard_i2c.c +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_hard_i2c.c @@ -11,6 +11,19 @@ #include #include #include + +/* The driver owns the peripheral registers, so it pulls in the peripheral + * header itself instead of relying on board.h to have declared it. Only the + * H49x board happens to include it today. + */ +#if defined(SOC_SERIES_N32H7xx) +#include +#elif defined(SOC_SERIES_N32H49x) +#include +#elif defined(SOC_SERIES_N32H47x_48x) +#include +#endif + #include "drv_hard_i2c.h" #include "drv_config.h" #include @@ -18,9 +31,11 @@ #if defined(BSP_USING_HARD_I2C1) || defined(BSP_USING_HARD_I2C2) || defined(BSP_USING_HARD_I2C3) || defined(BSP_USING_HARD_I2C4) || defined(BSP_USING_HARD_I2C5) || \ defined(BSP_USING_HARD_I2C6) || defined(BSP_USING_HARD_I2C7) || defined(BSP_USING_HARD_I2C8) || defined(BSP_USING_HARD_I2C9) || defined(BSP_USING_HARD_I2C10) +#define MAX_NBYTE_SIZE (255U) + +#if defined(SOC_SERIES_N32H7xx) #define I2C_CTRL2_NBYTES_POS (16U) -#define MAX_NBYTE_SIZE (255U) #define I2C_NO_STARTSTOP (0x00000000U) #define I2C_START_READ (I2C_CTRL2_START | I2C_CTRL2_RWN) @@ -40,14 +55,252 @@ /* Slave address | Transfer direction | START generation | STOP generation | Number of bytes | NBYTES reload mode | Automatic end mode */ #define I2C_CTRL2_CLEAR (I2C_CTRL2_SADR | I2C_CTRL2_RWN | I2C_CTRL2_START | I2C_CTRL2_STOP | I2C_CTRL2_BYTECNT | I2C_CTRL2_REFILL | I2C_CTRL2_AUTOSTOP) +static rt_bool_t first_valid_rx_isr = RT_FALSE; + +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + +#define I2C_RELOAD_MODE (0x00000000U) +#define I2C_FIRST_FRAME (0x00000000U) +#define I2C_FIRST_AND_NEXT_FRAME (0x00000000U) +#define I2C_LAST_FRAME (0x00000000U) +#define I2C_LAST_FRAME_NO_STOP (0x00000000U) + +#endif //#define DRV_DEBUG #define LOG_TAG "drv.i2c.hw" #include -static rt_bool_t first_valid_rx_isr = RT_FALSE; static void i2c_master_ev_isr_handler_it(struct n32_i2c *drv_i2c); static void i2c_master_ev_isr_handler_dma(struct n32_i2c *drv_i2c); +#if defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) +/* Wait for the bus to fall idle before asserting a new START. + * + * Asserting START while a previous STOP is still on the wire wedges the state + * machine: BUSY stays set, SB stays set, the address sits unsent in DAT and + * ADDRF never arrives. Callers must abort the transfer rather than start it. + */ +static rt_err_t n32_i2c_wait_bus_idle(I2C_Module *Instance) +{ + rt_uint32_t timeout = 1000000U; + + while (((Instance->STS2 & I2C_STS2_BUSY) == I2C_STS2_BUSY) && (--timeout > 0U)) + { + } + + return (timeout != 0U) ? RT_EOK : -RT_ETIMEOUT; +} + +/* Clear residue left by an aborted transfer before asserting a new START. + * + * A STOPGEN that was latched with no frame to terminate stays pending and would + * tear down the START we are about to generate. Stale status flags would + * likewise fire the moment the new transfer re-enables the error interrupt. + */ +static void n32_i2c_prepare_start(I2C_Module *Instance) +{ + Instance->CTRL1 &= ~(I2C_CTRL1_STOPGEN | I2C_CTRL1_STARTGEN); + + I2C_ClrIntPendingBit(Instance, + I2C_INT_ACKFAIL | I2C_INT_BUSERR | I2C_INT_ARLOST | + I2C_INT_OVERRUN | I2C_INT_PECERR); +} + +/* Tear down a transfer that the waiting thread has given up on. + * + * Must disarm the ISR before returning: master_xfer's callers pass stack + * buffers, so a late RXDATNE writing through transfer.pBuffPtr would corrupt + * a stack frame that no longer exists once the caller unwinds. + */ +static void n32_i2c_abort_transfer(struct n32_i2c *i2c_obj) +{ + I2C_Module *Instance = i2c_obj->config->Instance; + + /* Disable interrupts and DMA first - stop the ISR touching pBuffPtr */ + I2C_ConfigInt(Instance, I2C_INT_BUF | I2C_INT_EVENT | I2C_INT_ERR, DISABLE); + I2C_EnableDMA(Instance, DISABLE); + + i2c_obj->i2c_isr_callback = RT_NULL; + i2c_obj->transfer.pBuffPtr = RT_NULL; + i2c_obj->transfer.XferCount = 0; + i2c_obj->transfer.XferSize = 0; + i2c_obj->transfer.state = I2C_READY; + + /* Release the bus only if we still own it, then clear residue */ + if ((Instance->STS2 & I2C_STS2_BUSY) == I2C_STS2_BUSY) + { + Instance->CTRL1 |= I2C_CTRL1_STOPGEN; + (void)n32_i2c_wait_bus_idle(Instance); + } + + n32_i2c_prepare_start(Instance); +} + +/* Generate START, send the slave address, and clear ADDRF so the data phase + * (and therefore the DMA request) can begin. The DMA paths cannot rely on the + * event ISR to clear ADDRF (they enable EVTINTEN only after the DMA transfer + * completes, to catch STOPF), so the address phase is handled synchronously. + */ +static rt_err_t n32_i2c_master_start_addr(struct n32_i2c *i2c, uint8_t DevAddress, uint8_t Direction) +{ + I2C_Module *Instance = i2c->config->Instance; + + /* Wait for the previous STOP to complete */ + if (n32_i2c_wait_bus_idle(Instance) != RT_EOK) + { + return -RT_ETIMEOUT; + } + + /* Drop any STOPGEN/flags left latched by an aborted transfer */ + n32_i2c_prepare_start(Instance); + + /* Generate START condition */ + I2C_GenerateStart(Instance, ENABLE); + + /* Wait for START generation to complete before sending address */ + { + rt_uint32_t timeout = 1000000U; + while (!(Instance->STS1 & I2C_STS1_STARTBF) && (--timeout > 0U)) + { + } + if (timeout == 0U) + { + return -RT_ETIMEOUT; + } + } + + /* Send the 7-bit slave address (this write clears STARTBF) */ + I2C_SendAddr7bit(Instance, DevAddress, Direction); + + /* Wait for the address phase to finish, then clear ADDRF so the data phase + * (and therefore the DMA request) can begin. */ + { + rt_uint32_t timeout = 1000000U; + while (!(Instance->STS1 & (I2C_STS1_ADDRF | I2C_STS1_ACKFAIL)) && (--timeout > 0U)) + { + } + if (timeout == 0U) + { + return -RT_ETIMEOUT; + } + if (Instance->STS1 & I2C_STS1_ACKFAIL) + { + return -RT_ERROR; + } + /* Clear ADDRF by reading STS1 then STS2 */ + { + volatile uint32_t sr1 = Instance->STS1; + volatile uint32_t sr2 = Instance->STS2; + (void)sr1; + (void)sr2; + } + } + + return RT_EOK; +} +#endif /* defined(SOC_SERIES_N32H49x) */ + +#if defined(SOC_SERIES_N32H7xx) +/* Wait for the bus to fall idle before asserting a new STOP or START. + * + * Asserting either while a previous STOP is still on the wire wedges the state + * machine: BUSY stays set, the address never goes out and the transfer stalls. + */ +static rt_err_t n32_i2c_wait_bus_idle(I2C_Module *Instance) +{ + rt_uint32_t timeout = 1000000U; + + while ((I2C_GetFlag(Instance, I2C_FLAG_BUSY) != RESET) && (--timeout > 0U)) + { + } + + return (timeout != 0U) ? RT_EOK : -RT_ETIMEOUT; +} + +/* Clear residue left by an aborted transfer before the next one starts. + * + * A STOPGEN that was latched with no frame to terminate stays pending and would + * tear down the transfer we are about to set up. Stale status flags would + * likewise fire the moment the new transfer re-enables the error interrupt. + * + * I2C_ClrFlag() is a plain "INTCLR |= flag" write, so only flags that actually + * have a matching INTCLR bit may be passed. TFC (0x40) and BUSY (0x8000) have + * none - passing them lands in reserved bits and clears nothing. + */ +static void n32_i2c_prepare_start(I2C_Module *Instance) +{ + I2C_GenerateStop(Instance, DISABLE); + + I2C_ClrFlag(Instance, I2C_FLAG_NAKF | I2C_FLAG_BSER | I2C_FLAG_ABLO | + I2C_FLAG_OVF | I2C_FLAG_TMOUT | I2C_FLAG_ALRT | + I2C_FLAG_STOPF | I2C_FLAG_ADR | I2C_FLAG_CRCERR); +} + +/* Tear down a transfer that the waiting thread has given up on. + * + * Must disarm the ISR before returning: master_xfer's callers pass stack + * buffers, so a late read writing through transfer.pBuffPtr would corrupt a + * stack frame that no longer exists once the caller unwinds. The DMA channel + * targets that same buffer, so it is disabled here as well - dropping the + * request enable alone leaves the channel armed. + */ +static void n32_i2c_abort_transfer(struct n32_i2c *i2c_obj) +{ + I2C_Module *Instance = i2c_obj->config->Instance; + I2C_StateTypeDef was_state = i2c_obj->transfer.state; + + /* Disable interrupts and DMA first - stop the ISR touching pBuffPtr */ + Instance->CTRL1 &= ~(I2C_CTRL1_TFCIE | I2C_CTRL1_WDRIE | I2C_CTRL1_RDRIE | + I2C_CTRL1_STOPIE | I2C_CTRL1_NAKIE | I2C_CTRL1_ERRIE); + + if ((was_state == I2C_BUSY_TX) && + ((i2c_obj->i2c_dma_flag & I2C_USING_TX_DMA_FLAG) == I2C_USING_TX_DMA_FLAG)) + { + Instance->CTRL1 &= ~I2C_CTRL1_DMAWREN; + + DMA_ChannelCmd(i2c_obj->config->dma_tx->Instance, + i2c_obj->config->dma_tx->dma_channel, DISABLE); + DMA_ChannelEventCmd(i2c_obj->config->dma_tx->Instance, + i2c_obj->config->dma_tx->dma_channel, + DMA_CH_EVENT_BLOCK_TRANSFER_COMPLETE, DISABLE); + DMA_ClearChannelEventStatus(i2c_obj->config->dma_tx->Instance, + i2c_obj->config->dma_tx->dma_channel, + DMA_CH_EVENT_BLOCK_TRANSFER_COMPLETE); + } + else if ((was_state == I2C_BUSY_RX) && + ((i2c_obj->i2c_dma_flag & I2C_USING_RX_DMA_FLAG) == I2C_USING_RX_DMA_FLAG)) + { + Instance->CTRL1 &= ~I2C_CTRL1_DMARDEN; + + DMA_ChannelCmd(i2c_obj->config->dma_rx->Instance, + i2c_obj->config->dma_rx->dma_channel, DISABLE); + DMA_ChannelEventCmd(i2c_obj->config->dma_rx->Instance, + i2c_obj->config->dma_rx->dma_channel, + DMA_CH_EVENT_BLOCK_TRANSFER_COMPLETE, DISABLE); + DMA_ClearChannelEventStatus(i2c_obj->config->dma_rx->Instance, + i2c_obj->config->dma_rx->dma_channel, + DMA_CH_EVENT_BLOCK_TRANSFER_COMPLETE); + } + + i2c_obj->i2c_isr_callback = RT_NULL; + i2c_obj->transfer.pBuffPtr = RT_NULL; + i2c_obj->transfer.XferCount = 0; + i2c_obj->transfer.XferSize = 0; + i2c_obj->transfer.state = I2C_READY; + + /* Release the bus only if we still own it, then clear residue */ + if (I2C_GetFlag(Instance, I2C_FLAG_BUSY) != RESET) + { + I2C_GenerateStop(Instance, ENABLE); + (void)n32_i2c_wait_bus_idle(Instance); + } + + n32_i2c_prepare_start(Instance); +} +#endif /* defined(SOC_SERIES_N32H7xx) */ + +#define I2C_ABORT_ON_TIMEOUT(obj) n32_i2c_abort_transfer(obj) + enum { #ifdef BSP_USING_HARD_I2C1 @@ -166,6 +419,41 @@ static rt_ssize_t n32_iic_transfer_by_dma(struct n32_i2c_config *config, uint8_t /* Enable the specified DMA channel */ DMA_ChannelCmd(dma_config->Instance, dma_config->dma_channel, ENABLE); + +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + /* Disable DMA channel before reconfiguration */ + DMA_EnableChannel(dma_config->DMAChx, DISABLE); + + /* Configure DMA request remapping */ + DMA_RequestRemap(dma_config->request, dma_config->DMAChx, ENABLE); + + { + DMA_InitType *dma_init_str; + + if (is_rx) + { + dma_init_str = &config->dma_rx_init_str; + dma_init_str->PeriphAddr = (uint32_t)&config->Instance->DAT; + dma_init_str->MemAddr = (uint32_t)pData; + dma_init_str->Direction = DMA_DIR_PERIPH_SRC; + } + else + { + dma_init_str = &config->dma_tx_init_str; + dma_init_str->PeriphAddr = (uint32_t)&config->Instance->DAT; + dma_init_str->MemAddr = (uint32_t)pData; + dma_init_str->Direction = DMA_DIR_PERIPH_DST; + } + dma_init_str->BufSize = Size; + + DMA_Init(dma_config->DMAChx, dma_init_str); + } + + /* Enable transfer complete interrupt */ + DMA_ConfigInt(dma_config->DMAChx, DMA_INT_TXC, ENABLE); + + /* Enable the specified DMA channel */ + DMA_EnableChannel(dma_config->DMAChx, ENABLE); #endif return Size; @@ -175,11 +463,13 @@ static rt_ssize_t n32_iic_transfer_by_dma(struct n32_i2c_config *config, uint8_t static rt_err_t n32_i2c_dma_init(struct n32_i2c_config *config, rt_bool_t is_rx) { struct dma_config *dma_config; - DMA_ChInitType DMA_ChInitStr; + RT_ASSERT(config != RT_NULL); #if defined(SOC_SERIES_N32H7xx) + + DMA_ChInitType DMA_ChInitStr; /* Enable DMAMUX clock */ RCC_EnableAHB1PeriphClk1(RCC_AHB1_PERIPHEN_M7_DMAMUX1, ENABLE); @@ -235,6 +525,53 @@ static rt_err_t n32_i2c_dma_init(struct n32_i2c_config *config, rt_bool_t is_rx) { return -RT_ERROR; } +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + { + DMA_InitType *dma_init_str; + + if (is_rx) + { + dma_config = config->dma_rx; + dma_init_str = &config->dma_rx_init_str; + + /* DMA clock enable */ + RCC_EnableAHBPeriphClk(dma_config->dma_rcc, ENABLE); + + /* Initialize the persistent DMA init struct */ + DMA_StructInit(dma_init_str); + dma_init_str->PeriphDataSize = DMA_PERIPH_DATA_WIDTH_BYTE; + dma_init_str->MemDataSize = DMA_MEM_DATA_WIDTH_BYTE; + dma_init_str->MemoryInc = DMA_MEM_INC_ENABLE; + dma_init_str->Direction = DMA_DIR_PERIPH_SRC; + dma_init_str->Priority = DMA_PRIORITY_HIGH; + dma_init_str->PeriphAddr = (uint32_t)&config->Instance->DAT; + dma_init_str->MemAddr = (uint32_t)RT_NULL; + } + else + { + dma_config = config->dma_tx; + dma_init_str = &config->dma_tx_init_str; + + /* DMA clock enable */ + RCC_EnableAHBPeriphClk(dma_config->dma_rcc, ENABLE); + + /* Initialize the persistent DMA init struct */ + DMA_StructInit(dma_init_str); + dma_init_str->PeriphDataSize = DMA_PERIPH_DATA_WIDTH_BYTE; + dma_init_str->MemDataSize = DMA_MEM_DATA_WIDTH_BYTE; + dma_init_str->MemoryInc = DMA_MEM_INC_ENABLE; + dma_init_str->Direction = DMA_DIR_PERIPH_DST; + dma_init_str->Priority = DMA_PRIORITY_HIGH; + dma_init_str->PeriphAddr = (uint32_t)&config->Instance->DAT; + dma_init_str->MemAddr = (uint32_t)RT_NULL; + } + + /* Disable channel before configuration */ + DMA_EnableChannel(dma_config->DMAChx, DISABLE); + } +#endif + +#if defined(SOC_SERIES_N32H7xx) || defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) /* enable dma irq */ NVIC_SetPriority(dma_config->dma_irq, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0)); NVIC_EnableIRQ(dma_config->dma_irq); @@ -246,8 +583,8 @@ static rt_err_t n32_i2c_dma_init(struct n32_i2c_config *config, rt_bool_t is_rx) static rt_err_t n32_i2c_init(struct n32_i2c *i2c_drv) { rt_err_t ret; - uint32_t BusTim_Reg = 0; - I2C_InitType I2C_InitStructure; + + RT_ASSERT(i2c_drv != RT_NULL); struct n32_i2c_config *cfg = i2c_drv->config; @@ -256,8 +593,8 @@ static rt_err_t n32_i2c_init(struct n32_i2c *i2c_drv) cfg->EnablePeriphClk(cfg->periph, ENABLE); #if defined(SOC_SERIES_N32H7xx) - /* Call I2C_Configuration() from an external file */ - I2C_Configuration(); + I2C_InitType I2C_InitStructure; + uint32_t BusTim_Reg; /* Disable IIC */ I2C_Enable(cfg->Instance, DISABLE); /* Get IIC BUSTM Register value */ @@ -278,10 +615,13 @@ static rt_err_t n32_i2c_init(struct n32_i2c *i2c_drv) } #endif +#if defined(SOC_SERIES_N32H7xx) + /* Deinitializes the I2Cx peripheral registers to their default reset values */ I2C_DeInit(cfg->Instance); I2C_InitStruct(&I2C_InitStructure); + I2C_InitStructure.Timing = ((BusTim_Reg != 0) ? BusTim_Reg : cfg->timing); I2C_InitStructure.OwnAddress1 = 0x0; I2C_InitStructure.AddressingMode = I2C_ADDRESSINGMODE_7BIT; @@ -290,10 +630,14 @@ static rt_err_t n32_i2c_init(struct n32_i2c *i2c_drv) I2C_InitStructure.OwnAddress2Masks = I2C_ADDRESS2MASK_NONE; I2C_InitStructure.GeneralCallMode = I2C_GENERALCALL_DISABLE; I2C_InitStructure.NoStretchMode = I2C_NOSTRCH_DISABLE; + I2C_Init(cfg->Instance, &I2C_InitStructure); I2C_Enable(cfg->Instance, ENABLE); +#endif + + #if defined(SOC_SERIES_N32H7xx) /* Configure Analogue filter */ I2C_EnableAnalogFilter(cfg->Instance, ENABLE); @@ -304,6 +648,16 @@ static rt_err_t n32_i2c_init(struct n32_i2c *i2c_drv) /* Configure Digital filter */ I2C_SetDigitalFilterWidth(cfg->Instance, cfg->df_width); +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + /* Configure Analogue filter */ + I2C_EnableSCLAnalogFilter(cfg->Instance, ENABLE); + I2C_EnableSDAAnalogFilter(cfg->Instance, ENABLE); + I2C_SetSCLAnalogFilterWidth(cfg->Instance, cfg->scl_af_width); + I2C_SetSDAAnalogFilterWidth(cfg->Instance, cfg->sda_af_width); + + /* Configure Digital filter */ + I2C_SetSCLDigitalFilterWidth(cfg->Instance, cfg->df_width); + I2C_SetSDADigitalFilterWidth(cfg->Instance, cfg->df_width); #endif /* defined(SOC_SERIES_N32H7xx) */ /* I2C2 DMA Init */ @@ -334,6 +688,7 @@ static rt_err_t n32_i2c_init(struct n32_i2c *i2c_drv) static rt_err_t n32_i2c_master_seq_receive_it(struct n32_i2c *i2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions) { +#if defined(SOC_SERIES_N32H7xx) uint32_t setmask; uint32_t xfermode; @@ -373,10 +728,101 @@ static rt_err_t n32_i2c_master_seq_receive_it(struct n32_i2c *i2c, uint16_t DevA { return -RT_EBUSY; } +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + + if (i2c->transfer.state == I2C_READY) + { + /* A master receive fits in a single BYTENUM window: the counter is + * loaded once below and this path never reloads it, so the BSF + * completion ends the transfer after that many bytes. A longer message + * would be silently truncated yet still reported as success; refuse it + * instead. BYTENUM is 8 bits (the SDK's own setter takes a uint8_t), so + * the count cannot be widened either. Only the DMA path reloads the + * window as it drains. + * + * n32_i2c_master_xfer screens the same condition before dispatching, so + * the caller gets an errno; this check keeps the invariant stated where + * the window is actually programmed. + */ + if (Size > MAX_NBYTE_SIZE) + { + LOG_E("I2C IT receive of %u bytes exceeds the %u-byte BYTENUM window, enable RX DMA for longer reads", (unsigned int)Size, (unsigned int)MAX_NBYTE_SIZE); + return -RT_EINVAL; + } + + /* Set transfer parameters */ + i2c->transfer.state = I2C_BUSY_RX; + i2c->transfer.pBuffPtr = pData; + i2c->transfer.XferCount = Size; + i2c->transfer.XferOptions = XferOptions; + i2c->i2c_isr_callback = i2c_master_ev_isr_handler_it; + + /* One window covers the whole transfer - see the size check above */ + i2c->transfer.XferSize = i2c->transfer.XferCount; + +#if defined(SOC_SERIES_N32H49x) + I2C_EnableByteNum(i2c->config->Instance, ENABLE); + /* Set the number of bytes to receive via BYTENUM register */ + I2C_SetMasterReceivedDataBytesNum(i2c->config->Instance, i2c->transfer.XferSize); +#elif defined(SOC_SERIES_N32H47x_48x) + I2C_EnableBYTENUM(i2c->config->Instance, ENABLE); + I2C_SetReceivedDataBytesNum(i2c->config->Instance, i2c->transfer.XferSize); +#endif + /* Enable ACK for multi-byte receive (NACK for last byte handled in ISR) */ + if (i2c->transfer.XferCount > 1U) + { + I2C_ConfigAck(i2c->config->Instance, ENABLE); + } + + /* Wait for the previous STOP to complete. Toggling PE here would not + * reset the state machine while BUSY is set, and could hold the lines. + */ + if (n32_i2c_wait_bus_idle(i2c->config->Instance) != RT_EOK) + { + i2c->transfer.state = I2C_READY; + return -RT_ETIMEOUT; + } + + /* Drop any STOPGEN/flags left latched by an aborted transfer */ + n32_i2c_prepare_start(i2c->config->Instance); + + /* Generate START condition */ + I2C_GenerateStart(i2c->config->Instance, ENABLE); + + /* Wait for START generation to complete before sending address */ + { + rt_uint32_t timeout = 1000000U; + while (!(i2c->config->Instance->STS1 & I2C_STS1_STARTBF) && (--timeout > 0U)) + { + } + if (timeout == 0U) + { + i2c->transfer.state = I2C_READY; + return -RT_ETIMEOUT; + } + } + + /* Send START and 7-bit slave address with read direction. + * This write also clears STARTBF, which must happen before the event + * interrupt is enabled - an unhandled pending SB would storm the ISR. + */ + I2C_SendAddr7bit(i2c->config->Instance, (uint8_t)(DevAddress), I2C_DIRECTION_RECV); + + /* Enable Buffer, Event and Error interrupts (after address to avoid STARTBF) */ + I2C_ConfigInt(i2c->config->Instance, I2C_INT_BUF | I2C_INT_EVENT | I2C_INT_ERR, ENABLE); + + return RT_EOK; + } + else + { + return -RT_EBUSY; + } +#endif } static rt_err_t n32_i2c_master_seq_send_it(struct n32_i2c *i2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions) { +#if defined(SOC_SERIES_N32H7xx) uint32_t setmask; uint32_t xfermode; @@ -406,9 +852,17 @@ static rt_err_t n32_i2c_master_seq_send_it(struct n32_i2c *i2c, uint16_t DevAddr /* Slave address | Transfer direction | START generation | STOP generation | Number of bytes | NBYTES reload mode | Automatic end mode */ setmask = ((uint32_t)DevAddress & I2C_CTRL2_SADR) | (uint32_t)I2C_START_WRITE | (((uint32_t)i2c->transfer.XferSize << I2C_CTRL2_NBYTES_POS) & I2C_CTRL2_BYTECNT) | (uint32_t)xfermode; - /* Wait for bus idele */ - while ((i2c->config->Instance->STSINT & I2C_STSINT_BUSY) == I2C_STSINT_BUSY) + /* Wait for bus idle with timeout to prevent deadlock */ { + rt_uint32_t timeout_cnt = 1000000U; + while ((i2c->config->Instance->STSINT & I2C_STSINT_BUSY) == I2C_STSINT_BUSY && (--timeout_cnt > 0U)) + { + } + if (timeout_cnt == 0U) + { + i2c->transfer.state = I2C_READY; + return -RT_ETIMEOUT; + } } /* Set IIC CTRL2 Register */ @@ -420,16 +874,81 @@ static rt_err_t n32_i2c_master_seq_send_it(struct n32_i2c *i2c, uint16_t DevAddr { return -RT_EBUSY; } +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + + if (i2c->transfer.state == I2C_READY) + { + /* Set transfer parameters */ + i2c->transfer.state = I2C_BUSY_TX; + i2c->transfer.pBuffPtr = pData; + i2c->transfer.XferCount = Size; + i2c->transfer.XferOptions = XferOptions; + i2c->i2c_isr_callback = i2c_master_ev_isr_handler_it; + + /* No 255-byte window here: BYTENUM counts received bytes only, and this + * ISR feeds DAT one byte per TXDATE interrupt, so XferSize is nothing + * more than the remaining byte count. Capping it at MAX_NBYTE_SIZE made + * the TXDATE branch stop feeding after the 255th byte of a longer + * message - the tail was never sent while TXDATE stayed asserted and + * stormed this handler until the caller timed out. + */ + i2c->transfer.XferSize = i2c->transfer.XferCount; + + /* Wait for the previous STOP to complete. Toggling PE here would not + * reset the state machine while BUSY is set, and could hold the lines. + */ + if (n32_i2c_wait_bus_idle(i2c->config->Instance) != RT_EOK) + { + i2c->transfer.state = I2C_READY; + return -RT_ETIMEOUT; + } + + /* Drop any STOPGEN/flags left latched by an aborted transfer */ + n32_i2c_prepare_start(i2c->config->Instance); + + /* Generate START condition */ + I2C_GenerateStart(i2c->config->Instance, ENABLE); + + /* Wait for START generation to complete before sending address */ + { + rt_uint32_t timeout = 1000000U; + while (!(i2c->config->Instance->STS1 & I2C_STS1_STARTBF) && (--timeout > 0U)) + { + } + if (timeout == 0U) + { + i2c->transfer.state = I2C_READY; + return -RT_ETIMEOUT; + } + } + + /* Send START and 7-bit slave address with write direction. + * This write also clears STARTBF, which must happen before the event + * interrupt is enabled - an unhandled pending SB would storm the ISR. + */ + I2C_SendAddr7bit(i2c->config->Instance, (uint8_t)(DevAddress), I2C_DIRECTION_SEND); + + /* Enable Buffer, Event and Error interrupts (after address to avoid STARTBF) */ + I2C_ConfigInt(i2c->config->Instance, I2C_INT_BUF | I2C_INT_EVENT | I2C_INT_ERR, ENABLE); + + return RT_EOK; + } + else + { + return -RT_EBUSY; + } +#endif } static rt_err_t n32_i2c_master_seq_receive_dma(struct n32_i2c *i2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions) { +#if defined(SOC_SERIES_N32H7xx) uint32_t setmask; uint32_t xfermode; +#endif if (i2c->transfer.state == I2C_READY) { - first_valid_rx_isr = RT_FALSE; /* Set transfer parameters */ i2c->transfer.state = I2C_BUSY_RX; i2c->transfer.pBuffPtr = pData; @@ -441,18 +960,23 @@ static rt_err_t n32_i2c_master_seq_receive_dma(struct n32_i2c *i2c, uint16_t Dev if (Size > MAX_NBYTE_SIZE) { i2c->transfer.XferSize = MAX_NBYTE_SIZE; +#if defined(SOC_SERIES_N32H7xx) xfermode = I2C_RELOAD_MODE; // Use reload mode +#endif } else { i2c->transfer.XferSize = i2c->transfer.XferCount; +#if defined(SOC_SERIES_N32H7xx) xfermode = i2c->transfer.XferOptions; +#endif } if (i2c->transfer.XferSize > 0U) { n32_iic_transfer_by_dma(i2c->config, i2c->transfer.pBuffPtr, i2c->transfer.XferSize, RT_TRUE); +#if defined(SOC_SERIES_N32H7xx) /* Slave address | Transfer direction | START generation | STOP generation | Number of bytes | NBYTES reload mode | Automatic end mode */ setmask = ((uint32_t)DevAddress & I2C_CTRL2_SADR) | (uint32_t)I2C_START_READ | (((uint32_t)i2c->transfer.XferSize << I2C_CTRL2_NBYTES_POS) & I2C_CTRL2_BYTECNT) | (uint32_t)xfermode; @@ -467,11 +991,52 @@ static rt_err_t n32_i2c_master_seq_receive_dma(struct n32_i2c *i2c, uint16_t Dev /* Enable DMA Request */ i2c->config->Instance->CTRL1 |= I2C_CTRL1_DMARDEN; + +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + /* Update XferCount value */ + i2c->transfer.XferCount -= i2c->transfer.XferSize; +#if defined(SOC_SERIES_N32H49x) + I2C_EnableByteNum(i2c->config->Instance, ENABLE); + /* Set the number of bytes to receive via BYTENUM register */ + I2C_SetMasterReceivedDataBytesNum(i2c->config->Instance, i2c->transfer.XferSize); +#elif defined(SOC_SERIES_N32H47x_48x) + I2C_EnableBYTENUM(i2c->config->Instance, ENABLE); + I2C_SetReceivedDataBytesNum(i2c->config->Instance, i2c->transfer.XferSize); +#endif + /* Enable ACK so the slave keeps sending; BYTENUM auto-NACKs the + * last byte once the configured count is reached. */ + I2C_ConfigAck(i2c->config->Instance, ENABLE); + + /* Enable DMA Request */ + I2C_EnableDMA(i2c->config->Instance, ENABLE); + + /* Enable Buffer and Error interrupts */ + I2C_ConfigInt(i2c->config->Instance, I2C_INT_BUF | I2C_INT_ERR, ENABLE); + + /* Generate START, send the slave address, and clear ADDRF so the + * data phase (and thus the DMA request) can begin. */ + { + rt_err_t start_ret = n32_i2c_master_start_addr(i2c, (uint8_t)DevAddress, I2C_DIRECTION_RECV); + if (start_ret != RT_EOK) + { + /* The DMA request and the BUF/ERR interrupts are armed by + * now and the DMA channel still targets the caller's + * buffer, so resetting only the state would let a late + * byte write through a stack frame that is about to + * unwind. The timeout paths of n32_i2c_master_start_addr + * raise no error interrupt that could tear this down, so + * do it here. */ + I2C_ABORT_ON_TIMEOUT(i2c); + return start_ret; + } + } +#endif } else { i2c->i2c_isr_callback = i2c_master_ev_isr_handler_it; +#if defined(SOC_SERIES_N32H7xx) /* Slave address | Transfer direction | START generation | STOP generation | Number of bytes | NBYTES reload mode | Automatic end mode */ setmask = ((uint32_t)DevAddress & I2C_CTRL2_SADR) | (uint32_t)I2C_START_READ | (((uint32_t)i2c->transfer.XferSize << I2C_CTRL2_NBYTES_POS) & I2C_CTRL2_BYTECNT) | (uint32_t)I2C_AUTOEND_MODE; @@ -479,6 +1044,11 @@ static rt_err_t n32_i2c_master_seq_receive_dma(struct n32_i2c *i2c, uint16_t Dev MODIFY_REG(i2c->config->Instance->CTRL2, I2C_CTRL2_CLEAR, (uint32_t)setmask); i2c->config->Instance->CTRL1 |= (I2C_CTRL1_ERRIE | I2C_CTRL1_TFCIE | I2C_CTRL1_STOPIE | I2C_CTRL1_NAKIE | I2C_CTRL1_WDRIE); +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + /* Empty transfer - send START+ADDR then generate STOP */ + I2C_ConfigInt(i2c->config->Instance, I2C_INT_EVENT | I2C_INT_ERR, ENABLE); + I2C_SendAddr7bit(i2c->config->Instance, (uint8_t)(DevAddress), I2C_DIRECTION_RECV); +#endif } return RT_EOK; @@ -491,8 +1061,10 @@ static rt_err_t n32_i2c_master_seq_receive_dma(struct n32_i2c *i2c, uint16_t Dev static rt_err_t n32_i2c_master_seq_send_dma(struct n32_i2c *i2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions) { +#if defined(SOC_SERIES_N32H7xx) uint32_t setmask; uint32_t xfermode; +#endif if (i2c->transfer.state == I2C_READY) { @@ -507,18 +1079,23 @@ static rt_err_t n32_i2c_master_seq_send_dma(struct n32_i2c *i2c, uint16_t DevAdd if (Size > MAX_NBYTE_SIZE) { i2c->transfer.XferSize = MAX_NBYTE_SIZE; +#if defined(SOC_SERIES_N32H7xx) xfermode = I2C_RELOAD_MODE; // Use reload mode +#endif } else { i2c->transfer.XferSize = i2c->transfer.XferCount; +#if defined(SOC_SERIES_N32H7xx) xfermode = i2c->transfer.XferOptions; +#endif } if (i2c->transfer.XferSize > 0U) { n32_iic_transfer_by_dma(i2c->config, i2c->transfer.pBuffPtr, i2c->transfer.XferSize, RT_FALSE); +#if defined(SOC_SERIES_N32H7xx) /* Slave address | Transfer direction | START generation | STOP generation | Number of bytes | NBYTES reload mode | Automatic end mode */ setmask = ((uint32_t)DevAddress & I2C_CTRL2_SADR) | (uint32_t)I2C_START_WRITE | (((uint32_t)i2c->transfer.XferSize << I2C_CTRL2_NBYTES_POS) & I2C_CTRL2_BYTECNT) | (uint32_t)xfermode; @@ -533,11 +1110,46 @@ static rt_err_t n32_i2c_master_seq_send_dma(struct n32_i2c *i2c, uint16_t DevAdd /* Enable DMA Request */ i2c->config->Instance->CTRL1 |= I2C_CTRL1_DMAWREN; +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + /* Update XferCount value */ + i2c->transfer.XferCount -= i2c->transfer.XferSize; + + /* Enable DMA Request */ + I2C_EnableDMA(i2c->config->Instance, ENABLE); + + /* If this is the last frame, enable DMA Last Transfer to auto-generate STOP */ + if (i2c->transfer.XferCount == 0U) + { + I2C_EnableDmaLastSend(i2c->config->Instance, ENABLE); + } + + /* Enable Buffer and Error interrupts */ + I2C_ConfigInt(i2c->config->Instance, I2C_INT_BUF | I2C_INT_ERR, ENABLE); + + /* Generate START, send the slave address, and clear ADDRF so the + * data phase (and thus the DMA request) can begin. */ + { + rt_err_t start_ret = n32_i2c_master_start_addr(i2c, (uint8_t)DevAddress, I2C_DIRECTION_SEND); + if (start_ret != RT_EOK) + { + /* The DMA request and the BUF/ERR interrupts are armed by + * now and the DMA channel still targets the caller's + * buffer, so resetting only the state would let a late + * byte write through a stack frame that is about to + * unwind. The timeout paths of n32_i2c_master_start_addr + * raise no error interrupt that could tear this down, so + * do it here. */ + I2C_ABORT_ON_TIMEOUT(i2c); + return start_ret; + } + } +#endif } else { i2c->i2c_isr_callback = i2c_master_ev_isr_handler_it; +#if defined(SOC_SERIES_N32H7xx) /* Slave address | Transfer direction | START generation | STOP generation | Number of bytes | NBYTES reload mode | Automatic end mode */ setmask = ((uint32_t)DevAddress & I2C_CTRL2_SADR) | (uint32_t)I2C_START_WRITE | (((uint32_t)i2c->transfer.XferSize << I2C_CTRL2_NBYTES_POS) & I2C_CTRL2_BYTECNT) | (uint32_t)I2C_AUTOEND_MODE; @@ -545,6 +1157,11 @@ static rt_err_t n32_i2c_master_seq_send_dma(struct n32_i2c *i2c, uint16_t DevAdd MODIFY_REG(i2c->config->Instance->CTRL2, I2C_CTRL2_CLEAR, (uint32_t)setmask); i2c->config->Instance->CTRL1 |= (I2C_CTRL1_ERRIE | I2C_CTRL1_TFCIE | I2C_CTRL1_STOPIE | I2C_CTRL1_NAKIE | I2C_CTRL1_WDRIE); +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + /* Empty transfer - send START+ADDR then generate STOP */ + I2C_ConfigInt(i2c->config->Instance, I2C_INT_EVENT | I2C_INT_ERR, ENABLE); + I2C_SendAddr7bit(i2c->config->Instance, (uint8_t)(DevAddress), I2C_DIRECTION_SEND); +#endif } return RT_EOK; @@ -587,6 +1204,51 @@ static rt_ssize_t n32_i2c_master_xfer(struct rt_i2c_bus_device *bus, i2c_obj = rt_container_of(bus, struct n32_i2c, i2c_bus); completion = &i2c_obj->completion; +#if defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + /* This series cannot chain frames, so neither flag can be honoured. The + * mode constants the translate step below derives from them are all + * 0x00000000U on this series (see the definitions at the top of this file), + * so XferOptions is always zero here and changes nothing in the CTRL2 + * write. Every message instead re-runs the START + 7-bit address sequence + * in its own per-message setup, and completion always ends in a STOP + * (STOPGEN in i2c_it_completion_done, or the BYTENUM auto-stop on the DMA + * receive). A caller asking for RT_I2C_NO_START or RT_I2C_NO_STOP would + * therefore silently get a fresh START and a STOP instead of a repeated + * start -- a different bus transaction than the one requested, which some + * slaves reject. Refuse it rather than change it behind the caller's back; + * a repeated-start sequence needs the frame chaining this controller + * lacks. + */ + for (i = 0; i < num; i++) + { + if (msgs[i].flags & (RT_I2C_NO_START | RT_I2C_NO_STOP)) + { + LOG_E("I2C: RT_I2C_NO_START/RT_I2C_NO_STOP are not supported on this series (no frame chaining), msg[%d] flags=0x%x", i, msgs[i].flags); + return -RT_ENOSYS; + } + + /* A receive that the interrupt path has to serve fits in one BYTENUM + * window, and that path never reloads the counter. Screen it here as + * well as in the receive setup below, because a rejection raised from + * inside the transfer is reported as a message count rather than an + * errno (see "out:"), so the caller would only see a silent short + * read. The test mirrors this function's own DMA dispatch: the + * interrupt path is taken when RX DMA is off or the message is too + * short for it. + */ + if ((msgs[i].flags & RT_I2C_RD) && (msgs[i].len > MAX_NBYTE_SIZE)) + { + rt_bool_t rx_dma_ready = (i2c_obj->i2c_dma_flag & I2C_USING_RX_DMA_FLAG) ? RT_TRUE : RT_FALSE; + + if ((rx_dma_ready != RT_TRUE) || (msgs[i].len < DMA_TRANS_MIN_LEN)) + { + LOG_E("I2C IT receive of %u bytes exceeds the %u-byte BYTENUM window, enable RX DMA for longer reads", (unsigned int)msgs[i].len, (unsigned int)MAX_NBYTE_SIZE); + return -RT_EINVAL; + } + } + } +#endif + LOG_D("xfer start %d mags", num); for (i = 0; i < (num - 1); i++) { @@ -596,7 +1258,7 @@ static rt_ssize_t n32_i2c_master_xfer(struct rt_i2c_bus_device *bus, next_msg = &msgs[i + 1]; next_flag = next_msg->flags; - timeout = msg->len / TRANS_TIMEOUT_PERSEC + 2; + timeout = msg->len * TRANS_TIMEOUT_PERSEC + 10; if (next_flag & RT_I2C_NO_START) { if ((next_flag & RT_I2C_RD) == (msg->flags & RT_I2C_RD)) @@ -637,6 +1299,8 @@ static rt_ssize_t n32_i2c_master_xfer(struct rt_i2c_bus_device *bus, if (rt_completion_wait(completion, timeout) != RT_EOK) { LOG_D("receive time out"); + /* Disarm the ISR before the caller's buffer goes out of scope */ + I2C_ABORT_ON_TIMEOUT(i2c_obj); goto out; } } @@ -658,9 +1322,12 @@ static rt_ssize_t n32_i2c_master_xfer(struct rt_i2c_bus_device *bus, LOG_D("[%s:%d]I2C Write error(%d)!\n", __func__, __LINE__, ret); goto out; } + if (rt_completion_wait(completion, timeout) != RT_EOK) { LOG_D("transmit time out"); + /* Disarm the ISR before the caller's buffer goes out of scope */ + I2C_ABORT_ON_TIMEOUT(i2c_obj); goto out; } } @@ -669,7 +1336,8 @@ static rt_ssize_t n32_i2c_master_xfer(struct rt_i2c_bus_device *bus, /* last msg */ msg = &msgs[i]; - timeout = msg->len / TRANS_TIMEOUT_PERSEC + 2; + + timeout = msg->len * TRANS_TIMEOUT_PERSEC + 10; if (msg->flags & RT_I2C_NO_STOP) { @@ -692,7 +1360,6 @@ static rt_ssize_t n32_i2c_master_xfer(struct rt_i2c_bus_device *bus, } else { - uint32_t itflags = READ_REG(i2c_obj->config->Instance->STSINT); ret = n32_i2c_master_seq_receive_it(i2c_obj, (msg->addr << 1), msg->buf, msg->len, mode); } if (ret != RT_EOK) @@ -703,6 +1370,8 @@ static rt_ssize_t n32_i2c_master_xfer(struct rt_i2c_bus_device *bus, if (rt_completion_wait(completion, timeout) != RT_EOK) { LOG_D("receive time out"); + /* Disarm the ISR before the caller's buffer goes out of scope */ + I2C_ABORT_ON_TIMEOUT(i2c_obj); goto out; } } @@ -724,17 +1393,25 @@ static rt_ssize_t n32_i2c_master_xfer(struct rt_i2c_bus_device *bus, LOG_D("[%s:%d]I2C Write error(%d)!\n", __func__, __LINE__, ret); goto out; } + if (rt_completion_wait(completion, timeout) != RT_EOK) { LOG_D("transmit time out"); + /* Disarm the ISR before the caller's buffer goes out of scope */ + I2C_ABORT_ON_TIMEOUT(i2c_obj); goto out; } } + LOG_D("xfer end %d mags\r\n", num); + return num; out: - return (i - 1); + /* Return the number of messages actually transferred: msgs[i] is the one + * that failed, so the preceding i messages succeeded. + */ + return i; } @@ -853,6 +1530,7 @@ static void n32_get_dma_info(void) static void i2c_flush_wdr(I2C_Module *Instance) { +#if defined(SOC_SERIES_N32H7xx) /* If a pending WRAVL flag is set, Write a dummy data in WDR to clear it */ if ((((Instance->STSINT) & I2C_STSINT_WRAVL) == I2C_STSINT_WRAVL) ? SET : RESET) { @@ -864,27 +1542,46 @@ static void i2c_flush_wdr(I2C_Module *Instance) { Instance->STSINT |= I2C_STSINT_WRE; } +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + /* If TX data register is empty, clear the flag without sending a spurious byte */ + if (Instance->STS1 & I2C_STS1_TXDATE) + { + Instance->STS1 = I2C_STS1_TXDATE; + } +#endif } static void i2c_it_error_handler(struct n32_i2c *drv_i2c, uint32_t itflags) { - uint32_t setmask; I2C_StateTypeDef tmpstate = drv_i2c->transfer.state; drv_i2c->transfer.pBuffPtr = RT_NULL; drv_i2c->transfer.XferCount = 0; +#if defined(SOC_SERIES_N32H7xx) + uint32_t setmask; setmask = I2C_CTRL1_TFCIE | I2C_CTRL1_WDRIE | I2C_CTRL1_RDRIE | I2C_CTRL1_STOPIE | I2C_CTRL1_NAKIE | I2C_CTRL1_ERRIE; /* Disable all interrupts */ drv_i2c->config->Instance->CTRL1 &= (~(setmask)); +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + /* Disable all interrupts */ + I2C_ConfigInt(drv_i2c->config->Instance, I2C_INT_BUF | I2C_INT_EVENT | I2C_INT_ERR, DISABLE); +#endif drv_i2c->transfer.state = I2C_READY; drv_i2c->i2c_isr_callback = RT_NULL; + /* Wake the waiting thread. Do not rt_completion_init() here: that clears + * the suspended-thread pointer, so a thread already blocked in + * rt_completion_wait() would never be woken and could only exit by timeout. + */ + rt_completion_done(&drv_i2c->completion); + /* Abort DMA TX transfer */ if ((tmpstate == I2C_BUSY_TX) && (drv_i2c->i2c_dma_flag & I2C_USING_TX_DMA_FLAG) == I2C_USING_TX_DMA_FLAG) { +#if defined(SOC_SERIES_N32H7xx) if ((drv_i2c->config->Instance->CTRL1 & I2C_CTRL1_DMAWREN) == I2C_CTRL1_DMAWREN) { drv_i2c->config->Instance->CTRL1 &= ~I2C_CTRL1_DMAWREN; @@ -895,11 +1592,18 @@ static void i2c_it_error_handler(struct n32_i2c *drv_i2c, uint32_t itflags) DMA_ChannelEventCmd(drv_i2c->config->dma_tx->Instance, drv_i2c->config->dma_tx->dma_channel, DMA_CH_EVENT_BLOCK_TRANSFER_COMPLETE, DISABLE); DMA_ClearChannelEventStatus(drv_i2c->config->dma_tx->Instance, drv_i2c->config->dma_tx->dma_channel, DMA_CH_EVENT_BLOCK_TRANSFER_COMPLETE); +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + + I2C_EnableDMA(drv_i2c->config->Instance, DISABLE); + DMA_ConfigInt(drv_i2c->config->dma_tx->DMAChx, DMA_INT_TXC, DISABLE); + DMA_EnableChannel(drv_i2c->config->dma_tx->DMAChx, DISABLE); +#endif } /* Abort DMA RX transfer */ if ((tmpstate == I2C_BUSY_RX) && (drv_i2c->i2c_dma_flag & I2C_USING_RX_DMA_FLAG) == I2C_USING_RX_DMA_FLAG) { +#if defined(SOC_SERIES_N32H7xx) if ((drv_i2c->config->Instance->CTRL1 & I2C_CTRL1_DMARDEN) == I2C_CTRL1_DMARDEN) { drv_i2c->config->Instance->CTRL1 &= ~I2C_CTRL1_DMARDEN; @@ -910,8 +1614,15 @@ static void i2c_it_error_handler(struct n32_i2c *drv_i2c, uint32_t itflags) DMA_ChannelEventCmd(drv_i2c->config->dma_rx->Instance, drv_i2c->config->dma_rx->dma_channel, DMA_CH_EVENT_BLOCK_TRANSFER_COMPLETE, DISABLE); DMA_ClearChannelEventStatus(drv_i2c->config->dma_rx->Instance, drv_i2c->config->dma_rx->dma_channel, DMA_CH_EVENT_BLOCK_TRANSFER_COMPLETE); +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + I2C_EnableDMA(drv_i2c->config->Instance, DISABLE); + + DMA_ConfigInt(drv_i2c->config->dma_rx->DMAChx, DMA_INT_TXC, DISABLE); + DMA_EnableChannel(drv_i2c->config->dma_rx->DMAChx, DISABLE); +#endif } +#if defined(SOC_SERIES_N32H7xx) if (((((itflags & I2C_STSINT_NAKF) == I2C_STSINT_NAKF) ? SET : RESET) != RESET) || ((((itflags & I2C_STSINT_BSER) == I2C_STSINT_BSER) ? SET : RESET) != RESET)) { @@ -919,10 +1630,59 @@ static void i2c_it_error_handler(struct n32_i2c *drv_i2c, uint32_t itflags) LOG_D("I2C NACK Error or BUS Error now stoped"); drv_i2c->config->Instance->CTRL1 |= I2C_CTRL1_STOPIE; } +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + { + uint32_t sts1 = READ_REG(drv_i2c->config->Instance->STS1); + if ((sts1 & I2C_STS1_ACKFAIL) || (sts1 & I2C_STS1_BUSERR)) + { + /* Send stop signal to prevent bus lock-up */ + LOG_D("I2C NACK Error or BUS Error now stoped"); + } +#ifdef N32X_DRV_I2C_DEBUG + else + { + /* Unexpected error flags: also send STOP to release the bus. + * Without STOP, the I2C peripheral state machine remains stuck + * (e.g. START sent but no STOP), causing all subsequent transfers + * to fail with bus busy or timeout. + */ + LOG_W("I2C unexpected error flags 0x%08lX, sending STOP", sts1); + } +#endif + /* Clear the latched error flags before leaving. ERRINTEN is disabled + * above, so a flag left set here stays quiet only until the next + * transfer re-enables it - at which point it fires immediately and + * aborts a transaction that had not yet done anything wrong. + */ + I2C_ClrIntPendingBit(drv_i2c->config->Instance, + I2C_INT_ACKFAIL | I2C_INT_BUSERR | I2C_INT_ARLOST | + I2C_INT_OVERRUN | I2C_INT_PECERR); + + /* Release the bus only if we still own it. + * + * Do NOT re-enable the event interrupt to wait for STOPF: on this IP + * STOPF is only raised in slave mode, so the interrupt would never + * arrive, leaving EVTINTEN enabled with no callback installed. Poll the + * STOP out instead - bounded, ~10us at 100kHz. + * + * Asserting STOPGEN when the bus is already idle (NACK released SDA, or + * BYTENUM auto-STOP already ended the frame) latches the bit with no + * transfer to terminate. It then collides with the next START: the + * START is generated, SB sets, and the stray STOP immediately tears it + * down and drops us out of master mode. + */ + if ((drv_i2c->config->Instance->STS2 & I2C_STS2_BUSY) == I2C_STS2_BUSY) + { + drv_i2c->config->Instance->CTRL1 |= I2C_CTRL1_STOPGEN; + (void)n32_i2c_wait_bus_idle(drv_i2c->config->Instance); + } + } +#endif } static void i2c_it_master_complete(struct n32_i2c *drv_i2c, uint32_t itflags) { +#if defined(SOC_SERIES_N32H7xx) uint32_t setmask; __IO uint32_t tmpreg; @@ -966,6 +1726,42 @@ static void i2c_it_master_complete(struct n32_i2c *drv_i2c, uint32_t itflags) } i2c_flush_wdr(drv_i2c->config->Instance); +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + /* Clear STOP Flag */ + I2C_ClrIntPendingBit(drv_i2c->config->Instance, I2C_INT_STOPF); + + /* Disable DMA */ + I2C_EnableDMA(drv_i2c->config->Instance, DISABLE); + + /* Disable interrupts */ + I2C_ConfigInt(drv_i2c->config->Instance, I2C_INT_BUF | I2C_INT_EVENT | I2C_INT_ERR, DISABLE); + + drv_i2c->i2c_isr_callback = RT_NULL; + + { + uint32_t sts1 = READ_REG(drv_i2c->config->Instance->STS1); + + if (sts1 & I2C_STS1_ACKFAIL) + { + /* Clear NACK Flag */ + I2C_ClrIntPendingBit(drv_i2c->config->Instance, I2C_INT_ACKFAIL); + } + + /* Fetch Last receive data */ + if (sts1 & I2C_STS1_RXDATNE) + { + /* Read data from DAT */ + __IO uint32_t tmpreg = (uint8_t)drv_i2c->config->Instance->DAT; + RT_UNUSED(tmpreg); + } + + /* Flush TX if needed */ + if (sts1 & I2C_STS1_TXDATE) + { + drv_i2c->config->Instance->STS1 = I2C_STS1_TXDATE; + } + } +#endif drv_i2c->transfer.state = I2C_READY; rt_completion_done(&drv_i2c->completion); @@ -973,18 +1769,28 @@ static void i2c_it_master_complete(struct n32_i2c *drv_i2c, uint32_t itflags) static void i2c_it_completion_done(struct n32_i2c *drv_i2c) { - uint32_t setmask; - /* I2C Write */ if (drv_i2c->transfer.state == I2C_BUSY_TX) { drv_i2c->transfer.state = I2C_READY; drv_i2c->i2c_isr_callback = RT_NULL; +#if defined(SOC_SERIES_N32H7xx) + uint32_t setmask; setmask = I2C_CTRL1_TFCIE | I2C_CTRL1_WDRIE | I2C_CTRL1_STOPIE | I2C_CTRL1_NAKIE | I2C_CTRL1_ERRIE; /* Disable interrupt */ drv_i2c->config->Instance->CTRL1 &= (~(setmask)); +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + /* Generate STOP condition to end the transaction */ + drv_i2c->config->Instance->CTRL1 |= I2C_CTRL1_STOPGEN; + + /* Disable interrupts */ + I2C_ConfigInt(drv_i2c->config->Instance, I2C_INT_BUF | I2C_INT_EVENT | I2C_INT_ERR, DISABLE); + + /* Disable DMA */ + I2C_EnableDMA(drv_i2c->config->Instance, DISABLE); +#endif rt_completion_done(&drv_i2c->completion); } @@ -994,17 +1800,62 @@ static void i2c_it_completion_done(struct n32_i2c *drv_i2c) drv_i2c->transfer.state = I2C_READY; drv_i2c->i2c_isr_callback = RT_NULL; +#if defined(SOC_SERIES_N32H7xx) + uint32_t setmask; setmask = I2C_CTRL1_TFCIE | I2C_CTRL1_RDRIE | I2C_CTRL1_STOPIE | I2C_CTRL1_NAKIE | I2C_CTRL1_ERRIE; /* Disable interrupt */ drv_i2c->config->Instance->CTRL1 &= (~(setmask)); +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + /* Generate STOP condition to end the transaction */ + drv_i2c->config->Instance->CTRL1 |= I2C_CTRL1_STOPGEN; + + /* Disable interrupts */ + I2C_ConfigInt(drv_i2c->config->Instance, I2C_INT_BUF | I2C_INT_EVENT | I2C_INT_ERR, DISABLE); + + /* Disable DMA */ + I2C_EnableDMA(drv_i2c->config->Instance, DISABLE); +#endif rt_completion_done(&drv_i2c->completion); } } +static void i2c_dma_completion_done(struct n32_i2c *drv_i2c) +{ +#if defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + if (drv_i2c->transfer.state == I2C_BUSY_TX) + { + /* TX: the final byte is still in the shift register when the DMA + * completes and DMALAST does not auto-generate STOP. Wait for BSF (last + * byte fully sent and acknowledged), then generate STOP and complete. */ + rt_uint32_t timeout = 1000000U; + while (!(drv_i2c->config->Instance->STS1 & I2C_STS1_BSF) && (--timeout > 0U)) + { + } + /* Clear BSF so it cannot fire a spurious event interrupt on the next + * transfer, where BUFINTEN is re-enabled before the address phase. */ + I2C_ClrIntPendingBit(drv_i2c->config->Instance, I2C_INT_BSF); + + i2c_it_completion_done(drv_i2c); + } + else /* I2C_BUSY_RX */ + { + /* RX: BYTENUM already auto-generates STOP after the last byte, so tear + * down and complete without asserting STOPGEN (a stray STOP would + * collide with the next START). */ + drv_i2c->transfer.state = I2C_READY; + drv_i2c->i2c_isr_callback = RT_NULL; + I2C_ConfigInt(drv_i2c->config->Instance, I2C_INT_BUF | I2C_INT_EVENT | I2C_INT_ERR, DISABLE); + I2C_EnableDMA(drv_i2c->config->Instance, DISABLE); + rt_completion_done(&drv_i2c->completion); + } +#endif +} + static void i2c_master_ev_isr_handler_it(struct n32_i2c *drv_i2c) { +#if defined(SOC_SERIES_N32H7xx) uint16_t devaddress; uint32_t setmask; /* Get current IT Flags and IT sources value */ @@ -1125,10 +1976,150 @@ static void i2c_master_ev_isr_handler_it(struct n32_i2c *drv_i2c) { i2c_it_master_complete(drv_i2c, itflags); } +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + + volatile uint32_t itflags = READ_REG(drv_i2c->config->Instance->STS1); + volatile uint32_t itsources = READ_REG(drv_i2c->config->Instance->CTRL2); + + /* START condition generated (SB) - must never be left pending. + * + * SB is an event-interrupt source cleared only by reading STS1 followed by + * writing DAT. The normal setup path writes the address itself before + * enabling interrupts, so seeing SB here means the transfer is out of sync + * (e.g. a START asserted while a previous STOP was still on the wire). + * Returning without clearing it would re-assert the IRQ immediately and + * storm this handler at preempt priority 2, starving SysTick and hanging + * the system. Abort the transfer instead. + */ + if ((itflags & I2C_STS1_STARTBF) && (itsources & I2C_CTRL2_EVTINTEN)) + { + LOG_W("I2C unexpected STARTBF, aborting transfer"); + i2c_it_error_handler(drv_i2c, itflags); + return; + } + + /* Address sent (master mode) - read STS2 to clear ADDRF */ + if ((itflags & I2C_STS1_ADDRF) && (itsources & I2C_CTRL2_EVTINTEN)) + { + /* Clear ADDRF by reading STS2. TXDATE/RXDATNE will be set next. */ + volatile uint32_t tmp = drv_i2c->config->Instance->STS2; + (void)tmp; + + /* Zero-length transfer (e.g. I2C scan) - generate STOP immediately */ + if (drv_i2c->transfer.XferCount == 0U) + { + i2c_it_completion_done(drv_i2c); + } + } + + /* ACKFAIL (NACK) interrupt */ + if ((itflags & I2C_STS1_ACKFAIL) && (itsources & I2C_CTRL2_ERRINTEN)) + { + i2c_it_error_handler(drv_i2c, itflags); + /* The transfer is already torn down (pBuffPtr NULL, state READY) and + * the latched error flags were cleared inside the handler. Fall + * through only to the flag checks below would touch the dead + * transfer: the independent TXDATE block writes through pBuffPtr, + * which the handler just set to NULL. Return like the STARTBF path + * above does. */ + return; + } + /* RX data register not empty */ + else if ((itflags & I2C_STS1_RXDATNE) && (itsources & I2C_CTRL2_BUFINTEN)) + { + /* Disable ACK before reading the last byte (to send NACK and end slave TX) */ + if (drv_i2c->transfer.XferCount == 1U) + { + I2C_ConfigAck(drv_i2c->config->Instance, DISABLE); + } + + /* Read data from DAT */ + *drv_i2c->transfer.pBuffPtr = (uint8_t)drv_i2c->config->Instance->DAT; + + /* Increment Buffer pointer */ + drv_i2c->transfer.pBuffPtr++; + + drv_i2c->transfer.XferSize--; + drv_i2c->transfer.XferCount--; + + /* DAT read also clears BSF, so handle completion here when done */ + if (drv_i2c->transfer.XferCount == 0U) + { + i2c_it_completion_done(drv_i2c); + } + } + /* TX data register empty - independent check, must run alongside BSF */ + if ((itflags & I2C_STS1_TXDATE) && (itsources & I2C_CTRL2_BUFINTEN)) + { + if (drv_i2c->transfer.XferSize > 0) + { + /* Write data to DAT */ + drv_i2c->config->Instance->DAT = *drv_i2c->transfer.pBuffPtr; + + /* Increment Buffer pointer */ + drv_i2c->transfer.pBuffPtr++; + + drv_i2c->transfer.XferSize--; + drv_i2c->transfer.XferCount--; + } + } + + /* Byte Sequence Finished - only reachable with XferCount == 0, so completing + * unconditionally is correct: the RX setup refuses sizes beyond the single + * BYTENUM window it programs, and a master transmit is not windowed at all - + * its last byte is fed to DAT exactly when XferCount reaches zero. A nonzero + * XferCount here means the two setups above drifted out of sync, which is + * worth a trace rather than silently truncating the message. + */ + if ((itflags & I2C_STS1_BSF) && (itsources & I2C_CTRL2_BUFINTEN)) + { + /* Clear BSF flag */ + I2C_ClrIntPendingBit(drv_i2c->config->Instance, I2C_INT_BSF); + + if (drv_i2c->transfer.XferCount != 0U) + { + LOG_W("I2C BSF with %u bytes still queued", (unsigned int)drv_i2c->transfer.XferCount); + } + + /* Transfer complete - BYTENUM expired (RX) or all bytes sent (TX) */ + i2c_it_completion_done(drv_i2c); + } + + /* STOPF interrupt */ + if ((itflags & I2C_STS1_STOPF) && (itsources & I2C_CTRL2_EVTINTEN)) + { + i2c_it_master_complete(drv_i2c, itflags); + } + + /* Catch-all for error flags that reached this handler. + * + * itflags is STS1 and itsources is CTRL2 - they have unrelated bit layouts, + * so they must never be AND-ed together. (Masking them against each other + * made this check dead code: CLKFREQ in CTRL2[7:0] carries the peripheral + * clock in MHz, so which STS1 bits appeared "enabled" depended purely on + * the clock value.) Gate on the single error-enable bit instead, and test + * only flags that genuinely indicate a fault - a mid-transfer NACK from a + * marginal bus lands here. + */ + { + const uint32_t error_flags = I2C_STS1_BUSERR | I2C_STS1_ARLOST | I2C_STS1_OVERRUN | I2C_STS1_PECERR; + uint32_t unexpected = itflags & error_flags; + + if (unexpected && (itsources & I2C_CTRL2_ERRINTEN) && (drv_i2c->transfer.state == I2C_BUSY_TX || drv_i2c->transfer.state == I2C_BUSY_RX)) + { + LOG_W("I2C error flags 0x%08X, sts1=0x%08X", + (unsigned int)unexpected, (unsigned int)itflags); + i2c_it_error_handler(drv_i2c, itflags); + } + } + + +#endif } static void i2c_master_ev_isr_handler_dma(struct n32_i2c *drv_i2c) { +#if defined(SOC_SERIES_N32H7xx) uint16_t devaddress; uint32_t setmask; uint32_t xfermode; @@ -1227,11 +2218,41 @@ static void i2c_master_ev_isr_handler_dma(struct n32_i2c *drv_i2c) { /* Nothing to do */ } +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + volatile uint32_t itflags = READ_REG(drv_i2c->config->Instance->STS1); + volatile uint32_t itsources = READ_REG(drv_i2c->config->Instance->CTRL2); + + /* ACKFAIL (NACK) interrupt */ + if ((itflags & I2C_STS1_ACKFAIL) && (itsources & I2C_CTRL2_ERRINTEN)) + { + /* Clear ACKFAIL flag */ + I2C_ClrIntPendingBit(drv_i2c->config->Instance, I2C_INT_ACKFAIL); + + /* Enable STOP detection via Event interrupt to complete the transfer */ + I2C_ConfigInt(drv_i2c->config->Instance, I2C_INT_EVENT, ENABLE); + + /* Generate STOP */ + drv_i2c->config->Instance->CTRL1 |= I2C_CTRL1_STOPGEN; + + /* Flush TX data register */ + i2c_flush_wdr(drv_i2c->config->Instance); + } + /* STOPF interrupt */ + else if ((itflags & I2C_STS1_STOPF) && (itsources & I2C_CTRL2_EVTINTEN)) + { + i2c_it_master_complete(drv_i2c, itflags); + } + else + { + /* Nothing to do */ + } +#endif } static void i2c_master_er_isr_handler(struct n32_i2c *drv_i2c) { uint32_t tmperror = 0; +#if defined(SOC_SERIES_N32H7xx) volatile uint32_t itflags = READ_REG(drv_i2c->config->Instance->STSINT); volatile uint32_t itsources = READ_REG(drv_i2c->config->Instance->CTRL1); @@ -1271,7 +2292,7 @@ static void i2c_master_er_isr_handler(struct n32_i2c *drv_i2c) drv_i2c->config->Instance->INTCLR = I2C_INTCLR_CRCCLR; } - /* I2C PEC error interrupt occurred */ + /* I2C timeout error interrupt occurred */ if (((((itflags & I2C_STSINT_TMOUT) == I2C_STSINT_TMOUT) ? SET : RESET) != RESET) && ((((itsources & I2C_CTRL1_ERRIE) == I2C_CTRL1_ERRIE) ? SET : RESET) != RESET)) { @@ -1284,6 +2305,51 @@ static void i2c_master_er_isr_handler(struct n32_i2c *drv_i2c) { i2c_it_error_handler(drv_i2c, itflags); } +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + + volatile uint32_t itflags = READ_REG(drv_i2c->config->Instance->STS1); + volatile uint32_t itsources = READ_REG(drv_i2c->config->Instance->CTRL2); + + /* I2C Bus error interrupt occurred */ + if ((itflags & I2C_STS1_BUSERR) && (itsources & I2C_CTRL2_ERRINTEN)) + { + tmperror |= I2C_STS1_BUSERR; + I2C_ClrIntPendingBit(drv_i2c->config->Instance, I2C_INT_BUSERR); + } + + /* I2C Over-Run/Under-Run interrupt occurred */ + if ((itflags & I2C_STS1_OVERRUN) && (itsources & I2C_CTRL2_ERRINTEN)) + { + tmperror |= I2C_STS1_OVERRUN; + I2C_ClrIntPendingBit(drv_i2c->config->Instance, I2C_INT_OVERRUN); + } + + /* I2C Arbitration Loss error interrupt occurred */ + if ((itflags & I2C_STS1_ARLOST) && (itsources & I2C_CTRL2_ERRINTEN)) + { + tmperror |= I2C_STS1_ARLOST; + I2C_ClrIntPendingBit(drv_i2c->config->Instance, I2C_INT_ARLOST); + } + + /* I2C ACKFAIL error interrupt occurred */ + if ((itflags & I2C_STS1_ACKFAIL) && (itsources & I2C_CTRL2_ERRINTEN)) + { + tmperror |= I2C_STS1_ACKFAIL; + I2C_ClrIntPendingBit(drv_i2c->config->Instance, I2C_INT_ACKFAIL); + } + + /* I2C PEC error interrupt occurred */ + if ((itflags & I2C_STS1_PECERR) && (itsources & I2C_CTRL2_ERRINTEN)) + { + tmperror |= I2C_STS1_PECERR; + I2C_ClrIntPendingBit(drv_i2c->config->Instance, I2C_INT_PECERR); + } + + if (tmperror != 0) + { + i2c_it_error_handler(drv_i2c, itflags); + } +#endif } static void i2c_master_ev_isr_handler(struct n32_i2c *drv_i2c) @@ -1331,6 +2397,9 @@ static void i2c_master_dma_receive_isr_handler(struct n32_i2c *drv_i2c) n32_iic_transfer_by_dma(drv_i2c->config, drv_i2c->transfer.pBuffPtr, drv_i2c->transfer.XferSize, RT_TRUE); + /* Update XferCount for the upcoming frame */ + drv_i2c->transfer.XferCount -= drv_i2c->transfer.XferSize; + /* Enable TC interrupts */ drv_i2c->config->Instance->CTRL1 |= I2C_CTRL1_TFCIE; } @@ -1339,6 +2408,125 @@ static void i2c_master_dma_receive_isr_handler(struct n32_i2c *drv_i2c) DMA_ClearChannelEventStatus(drv_i2c->config->dma_rx->Instance, drv_i2c->config->dma_rx->dma_channel, DMA_CH_EVENT_BLOCK_TRANSFER_COMPLETE); } } +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + { + DMA_Module *dma_module = (DMA_Module *)((uint32_t)drv_i2c->config->dma_rx->DMAChx < DMA2_BASE ? DMA1 : DMA2); + uint32_t dma_int_tc = 0; + if (drv_i2c->config->dma_rx->DMAChx == DMA1_CH1) + { + dma_int_tc = DMA_INT_TXC1; + } + else if (drv_i2c->config->dma_rx->DMAChx == DMA1_CH2) + { + dma_int_tc = DMA_INT_TXC2; + } + else if (drv_i2c->config->dma_rx->DMAChx == DMA1_CH3) + { + dma_int_tc = DMA_INT_TXC3; + } + else if (drv_i2c->config->dma_rx->DMAChx == DMA1_CH4) + { + dma_int_tc = DMA_INT_TXC4; + } + else if (drv_i2c->config->dma_rx->DMAChx == DMA1_CH5) + { + dma_int_tc = DMA_INT_TXC5; + } + else if (drv_i2c->config->dma_rx->DMAChx == DMA1_CH6) + { + dma_int_tc = DMA_INT_TXC6; + } + else if (drv_i2c->config->dma_rx->DMAChx == DMA1_CH7) + { + dma_int_tc = DMA_INT_TXC7; + } + else if (drv_i2c->config->dma_rx->DMAChx == DMA1_CH8) + { + dma_int_tc = DMA_INT_TXC8; + } + else if (drv_i2c->config->dma_rx->DMAChx == DMA2_CH1) + { + dma_int_tc = DMA_INT_TXC1; + } + else if (drv_i2c->config->dma_rx->DMAChx == DMA2_CH2) + { + dma_int_tc = DMA_INT_TXC2; + } + else if (drv_i2c->config->dma_rx->DMAChx == DMA2_CH3) + { + dma_int_tc = DMA_INT_TXC3; + } + else if (drv_i2c->config->dma_rx->DMAChx == DMA2_CH4) + { + dma_int_tc = DMA_INT_TXC4; + } + else if (drv_i2c->config->dma_rx->DMAChx == DMA2_CH5) + { + dma_int_tc = DMA_INT_TXC5; + } + else if (drv_i2c->config->dma_rx->DMAChx == DMA2_CH6) + { + dma_int_tc = DMA_INT_TXC6; + } + else if (drv_i2c->config->dma_rx->DMAChx == DMA2_CH7) + { + dma_int_tc = DMA_INT_TXC7; + } + else if (drv_i2c->config->dma_rx->DMAChx == DMA2_CH8) + { + dma_int_tc = DMA_INT_TXC8; + } + + RT_ASSERT(dma_int_tc != 0); + + if (dma_int_tc != 0 && DMA_GetIntStatus(dma_int_tc, dma_module) == SET) + { + /* Disable DMA Request */ + I2C_EnableDMA(drv_i2c->config->Instance, DISABLE); + + /* Last transfer: DMALAST (TX) / BYTENUM (RX) auto-generate STOP, so + * complete directly instead of waiting for STOPF (slave-mode only). */ + if (drv_i2c->transfer.XferCount == 0U) + { + i2c_dma_completion_done(drv_i2c); + } + /* else prepare a new DMA transfer */ + else + { + /* Update Buffer pointer */ + drv_i2c->transfer.pBuffPtr += drv_i2c->transfer.XferSize; + + /* Set the XferSize to transfer */ + if (drv_i2c->transfer.XferCount > MAX_NBYTE_SIZE) + { + drv_i2c->transfer.XferSize = MAX_NBYTE_SIZE; + } + else + { + drv_i2c->transfer.XferSize = drv_i2c->transfer.XferCount; + } + + n32_iic_transfer_by_dma(drv_i2c->config, drv_i2c->transfer.pBuffPtr, drv_i2c->transfer.XferSize, RT_TRUE); + + /* Update XferCount for the upcoming frame */ + drv_i2c->transfer.XferCount -= drv_i2c->transfer.XferSize; + +#if defined(SOC_SERIES_N32H49x) + I2C_EnableByteNum(drv_i2c->config->Instance, ENABLE); + /* Set the number of bytes to receive via BYTENUM register */ + I2C_SetMasterReceivedDataBytesNum(drv_i2c->config->Instance, drv_i2c->transfer.XferSize); +#elif defined(SOC_SERIES_N32H47x_48x) + I2C_EnableBYTENUM(drv_i2c->config->Instance, ENABLE); + I2C_SetReceivedDataBytesNum(drv_i2c->config->Instance, drv_i2c->transfer.XferSize); +#endif + /* Re-enable DMA */ + I2C_EnableDMA(drv_i2c->config->Instance, ENABLE); + } + + /* Clear interrupt pending bit */ + DMA_ClrIntPendingBit(dma_int_tc, dma_module); + } + } #endif } #endif @@ -1377,7 +2565,7 @@ static void i2c_master_dma_send_isr_handler(struct n32_i2c *drv_i2c) drv_i2c->transfer.XferSize = drv_i2c->transfer.XferCount; } - n32_iic_transfer_by_dma(drv_i2c->config, drv_i2c->transfer.pBuffPtr, drv_i2c->transfer.XferSize, RT_TRUE); + n32_iic_transfer_by_dma(drv_i2c->config, drv_i2c->transfer.pBuffPtr, drv_i2c->transfer.XferSize, RT_FALSE); /* Enable TC interrupts */ drv_i2c->config->Instance->CTRL1 |= I2C_CTRL1_TFCIE; @@ -1387,6 +2575,123 @@ static void i2c_master_dma_send_isr_handler(struct n32_i2c *drv_i2c) DMA_ClearChannelEventStatus(drv_i2c->config->dma_tx->Instance, drv_i2c->config->dma_tx->dma_channel, DMA_CH_EVENT_BLOCK_TRANSFER_COMPLETE); } } +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + { + DMA_Module *dma_module = (DMA_Module *)((uint32_t)drv_i2c->config->dma_tx->DMAChx < DMA2_BASE ? DMA1 : DMA2); + uint32_t dma_int_tc = 0; + if (drv_i2c->config->dma_tx->DMAChx == DMA1_CH1) + { + dma_int_tc = DMA_INT_TXC1; + } + else if (drv_i2c->config->dma_tx->DMAChx == DMA1_CH2) + { + dma_int_tc = DMA_INT_TXC2; + } + else if (drv_i2c->config->dma_tx->DMAChx == DMA1_CH3) + { + dma_int_tc = DMA_INT_TXC3; + } + else if (drv_i2c->config->dma_tx->DMAChx == DMA1_CH4) + { + dma_int_tc = DMA_INT_TXC4; + } + else if (drv_i2c->config->dma_tx->DMAChx == DMA1_CH5) + { + dma_int_tc = DMA_INT_TXC5; + } + else if (drv_i2c->config->dma_tx->DMAChx == DMA1_CH6) + { + dma_int_tc = DMA_INT_TXC6; + } + else if (drv_i2c->config->dma_tx->DMAChx == DMA1_CH7) + { + dma_int_tc = DMA_INT_TXC7; + } + else if (drv_i2c->config->dma_tx->DMAChx == DMA1_CH8) + { + dma_int_tc = DMA_INT_TXC8; + } + else if (drv_i2c->config->dma_tx->DMAChx == DMA2_CH1) + { + dma_int_tc = DMA_INT_TXC1; + } + else if (drv_i2c->config->dma_tx->DMAChx == DMA2_CH2) + { + dma_int_tc = DMA_INT_TXC2; + } + else if (drv_i2c->config->dma_tx->DMAChx == DMA2_CH3) + { + dma_int_tc = DMA_INT_TXC3; + } + else if (drv_i2c->config->dma_tx->DMAChx == DMA2_CH4) + { + dma_int_tc = DMA_INT_TXC4; + } + else if (drv_i2c->config->dma_tx->DMAChx == DMA2_CH5) + { + dma_int_tc = DMA_INT_TXC5; + } + else if (drv_i2c->config->dma_tx->DMAChx == DMA2_CH6) + { + dma_int_tc = DMA_INT_TXC6; + } + else if (drv_i2c->config->dma_tx->DMAChx == DMA2_CH7) + { + dma_int_tc = DMA_INT_TXC7; + } + else if (drv_i2c->config->dma_tx->DMAChx == DMA2_CH8) + { + dma_int_tc = DMA_INT_TXC8; + } + + RT_ASSERT(dma_int_tc != 0); + + if (dma_int_tc != 0 && DMA_GetIntStatus(dma_int_tc, dma_module) == SET) + { + /* Disable DMA Request */ + I2C_EnableDMA(drv_i2c->config->Instance, DISABLE); + + /* Last transfer: DMALAST (TX) / BYTENUM (RX) auto-generate STOP, so + * complete directly instead of waiting for STOPF (slave-mode only). */ + if (drv_i2c->transfer.XferCount == 0U) + { + i2c_dma_completion_done(drv_i2c); + } + /* else prepare a new DMA transfer */ + else + { + /* Update Buffer pointer */ + drv_i2c->transfer.pBuffPtr += drv_i2c->transfer.XferSize; + + /* Set the XferSize to transfer */ + if (drv_i2c->transfer.XferCount > MAX_NBYTE_SIZE) + { + drv_i2c->transfer.XferSize = MAX_NBYTE_SIZE; + } + else + { + drv_i2c->transfer.XferSize = drv_i2c->transfer.XferCount; + } + + /* Update XferCount for the upcoming frame */ + drv_i2c->transfer.XferCount -= drv_i2c->transfer.XferSize; + + n32_iic_transfer_by_dma(drv_i2c->config, drv_i2c->transfer.pBuffPtr, drv_i2c->transfer.XferSize, RT_FALSE); + + /* Set DMALAST if this is the last frame */ + if (drv_i2c->transfer.XferCount == 0U) + { + I2C_EnableDmaLastSend(drv_i2c->config->Instance, ENABLE); + } + + /* Re-enable DMA */ + I2C_EnableDMA(drv_i2c->config->Instance, ENABLE); + } + + /* Clear interrupt pending bit */ + DMA_ClrIntPendingBit(dma_int_tc, dma_module); + } + } #endif } #endif @@ -1406,7 +2711,7 @@ void I2C1_EV_IRQHandler(void) rt_interrupt_leave(); } /** - * @brief This function handles I2C2 error interrupt. + * @brief This function handles I2C1 error interrupt. */ void I2C1_ER_IRQHandler(void) { @@ -1445,6 +2750,167 @@ void I2C1_TX_DMA_IRQHandler(void) #endif /* defined(BSP_I2C1_TX_USING_DMA) && defined(I2C1_TX_DMA_IRQHandler) */ #endif /* BSP_USING_HARD_I2C1 */ +#ifdef BSP_USING_HARD_I2C2 +/** + * @brief This function handles I2C2 event interrupt. + */ +void I2C2_EV_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + i2c_master_ev_isr_handler(&i2c_objs[I2C2_INDEX]); + + /* leave interrupt */ + rt_interrupt_leave(); +} +/** + * @brief This function handles I2C2 error interrupt. + */ +void I2C2_ER_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + i2c_master_er_isr_handler(&i2c_objs[I2C2_INDEX]); + + /* leave interrupt */ + rt_interrupt_leave(); +} +#if defined(BSP_I2C2_RX_USING_DMA) && defined(I2C2_RX_DMA_IRQHandler) +void I2C2_RX_DMA_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + i2c_master_dma_receive_isr_handler(&i2c_objs[I2C2_INDEX]); + + /* leave interrupt */ + rt_interrupt_leave(); +} +#endif /* defined(BSP_I2C2_RX_USING_DMA) && defined(I2C2_RX_DMA_IRQHandler) */ + +#if defined(BSP_I2C2_TX_USING_DMA) && defined(I2C2_TX_DMA_IRQHandler) +void I2C2_TX_DMA_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + i2c_master_dma_send_isr_handler(&i2c_objs[I2C2_INDEX]); + + /* leave interrupt */ + rt_interrupt_leave(); +} +#endif /* defined(BSP_I2C2_TX_USING_DMA) && defined(I2C2_TX_DMA_IRQHandler) */ +#endif /* BSP_USING_HARD_I2C2 */ + +#ifdef BSP_USING_HARD_I2C3 +/** + * @brief This function handles I2C3 event interrupt. + */ +void I2C3_EV_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + i2c_master_ev_isr_handler(&i2c_objs[I2C3_INDEX]); + + /* leave interrupt */ + rt_interrupt_leave(); +} +/** + * @brief This function handles I2C2 error interrupt. + */ +void I2C3_ER_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + i2c_master_er_isr_handler(&i2c_objs[I2C3_INDEX]); + + /* leave interrupt */ + rt_interrupt_leave(); +} +#if defined(BSP_I2C3_RX_USING_DMA) && defined(I2C3_RX_DMA_IRQHandler) +void I2C3_RX_DMA_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + i2c_master_dma_receive_isr_handler(&i2c_objs[I2C3_INDEX]); + + /* leave interrupt */ + rt_interrupt_leave(); +} +#endif /* defined(BSP_I2C3_RX_USING_DMA) && defined(I2C3_RX_DMA_IRQHandler) */ + +#if defined(BSP_I2C3_TX_USING_DMA) && defined(I2C3_TX_DMA_IRQHandler) +void I2C3_TX_DMA_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + i2c_master_dma_send_isr_handler(&i2c_objs[I2C3_INDEX]); + + /* leave interrupt */ + rt_interrupt_leave(); +} +#endif /* defined(BSP_I2C3_TX_USING_DMA) && defined(I2C3_TX_DMA_IRQHandler) */ +#endif /* BSP_USING_HARD_I2C3 */ + +#ifdef BSP_USING_HARD_I2C4 +/** + * @brief This function handles I2C2 event interrupt. + */ +void I2C4_EV_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + i2c_master_ev_isr_handler(&i2c_objs[I2C4_INDEX]); + + /* leave interrupt */ + rt_interrupt_leave(); +} +/** + * @brief This function handles I2C2 error interrupt. + */ +void I2C4_ER_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + i2c_master_er_isr_handler(&i2c_objs[I2C4_INDEX]); + + /* leave interrupt */ + rt_interrupt_leave(); +} +#if defined(BSP_I2C4_RX_USING_DMA) && defined(I2C4_RX_DMA_IRQHandler) +void I2C4_RX_DMA_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + i2c_master_dma_receive_isr_handler(&i2c_objs[I2C4_INDEX]); + + /* leave interrupt */ + rt_interrupt_leave(); +} +#endif /* defined(BSP_I2C4_RX_USING_DMA) && defined(I2C4_RX_DMA_IRQHandler) */ + +#if defined(BSP_I2C4_TX_USING_DMA) && defined(I2C4_TX_DMA_IRQHandler) +void I2C4_TX_DMA_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + i2c_master_dma_send_isr_handler(&i2c_objs[I2C4_INDEX]); + + /* leave interrupt */ + rt_interrupt_leave(); +} +#endif /* defined(BSP_I2C4_TX_USING_DMA) && defined(I2C4_TX_DMA_IRQHandler) */ +#endif /* BSP_USING_HARD_I2C4 */ int rt_hw_hw_i2c_init(void) { diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_hard_i2c.h b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_hard_i2c.h index 28c83ec6de39..6f137ac664af 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_hard_i2c.h +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_hard_i2c.h @@ -40,7 +40,9 @@ struct n32_i2c_config { const char *name; I2C_Module *Instance; +#if defined(SOC_SERIES_N32H7xx) rt_uint32_t timing; +#endif rt_uint32_t timeout; IRQn_Type evirq_type; IRQn_Type erirq_type; @@ -51,9 +53,16 @@ struct n32_i2c_config uint32_t periph; void (*EnablePeriphClk)(uint32_t periph, FunctionalState cmd); +#if defined(SOC_SERIES_N32H7xx) void (*I2CKerClkSource)(uint32_t CLK_source); +#endif struct dma_config *dma_rx, *dma_tx; + +#if defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + DMA_InitType dma_rx_init_str; + DMA_InitType dma_tx_init_str; +#endif }; struct hard_i2c_transfer diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_nand.c b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_nand.c index 4f611b727c1c..2f7d0cfbb582 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_nand.c +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_nand.c @@ -34,8 +34,103 @@ static FEMC_InitType NAND_InitStructure = { 0 }; #define NAND_BANK_ADDR ((uint32_t)0xB0000000) #endif +#endif /* SOC_SERIES_N32H7xx */ + +/* Standard NAND flash commands (platform independent) */ +#define NAND_CMD_READ_1ST ((uint8_t)0x00) +#define NAND_CMD_READ_2ND ((uint8_t)0x30) +#define NAND_CMD_WRITE_1ST ((uint8_t)0x80) +#define NAND_CMD_WRITE_2ND ((uint8_t)0x10) +#define NAND_CMD_ERASE_1ST ((uint8_t)0x60) +#define NAND_CMD_ERASE_2ND ((uint8_t)0xD0) +#define NAND_CMD_READ_ID ((uint8_t)0x90) +#define NAND_CMD_STATUS ((uint8_t)0x70) +#define NAND_CMD_RESET ((uint8_t)0xFF) + +/* NAND status bits */ +#define NAND_STATUS_READY ((uint32_t)0x40) +#define NAND_STATUS_ERROR ((uint32_t)0x01) + +#if defined(SOC_SERIES_N32H47x_48x) || defined(SOC_SERIES_N32H49x) +/* + * N32H47x/48x and N32H49x FEMC NAND support. + * + * The FEMC NAND controller of these two series uses the classic + * HADDR[17:16] area-select model. + * Bank2 base = 0x70000000 + * Bank3 base = 0x80000000 + * + * HADDR[17:16]: + * 00 -> data area + * 01 -> command area + * 1x -> address area. + * A16 drives CLE, A17 drives ALE. + */ + +/* NAND bank base address derived from Kconfig */ +#ifdef BSP_USING_NAND_BANK2 +#define NAND_BANK_ADDR ((uint32_t)0x70000000U) +#define NAND_BANK FEMC_NAND_BANK2 #endif +#ifdef BSP_USING_NAND_BANK3 +#define NAND_BANK_ADDR ((uint32_t)0x80000000U) +#define NAND_BANK FEMC_NAND_BANK3 +#endif + +#ifndef NAND_BANK_ADDR +#error "No FEMC NAND bank selected. Enable BSP_USING_NAND_BANK2 or BSP_USING_NAND_BANK3 in Kconfig." +#endif + +/* FEMC NAND address areas */ +#define NAND_CMD_AREA ((uint32_t)0x00010000U) /* bit16 = CLE */ +#define NAND_ADDR_AREA ((uint32_t)0x00020000U) /* bit17 = ALE */ +#define NAND_DATA_AREA ((uint32_t)0x00000000U) + +/* + * Wait for NAND operation completion + * + * NWAIT hardware wait (FEMC_NAND_NWAIT_ENABLE) blocks the CPU inside the + * data/status access itself, so the 0x70 status read below returns the final + * status. A software poll with timeout is kept as a safety net in case the + * NWAIT line is not connected or a device stalls. + * + * Returns -RT_EIO if the error bit is set, -RT_ETIMEOUT on timeout. + */ +static rt_err_t nand_wait_status(void) +{ + uint32_t timeout = 0x100000; + uint32_t bank = NAND_BANK_ADDR; + uint8_t status; + + do + { + *(__IO uint8_t *)(bank | NAND_CMD_AREA) = NAND_CMD_STATUS; /* 0x70 */ + status = *(__IO uint8_t *)(bank | NAND_DATA_AREA); + + if (status & NAND_STATUS_ERROR) + { + return -RT_EIO; + } + if (status & NAND_STATUS_READY) + { + return RT_EOK; + } + timeout--; + rt_thread_delay(rt_tick_from_millisecond(1)); + } while (timeout); + + return -RT_ETIMEOUT; +} + +/* Send NAND RESET (0xFF) and wait until the device is ready. */ +static rt_err_t nand_reset(void) +{ + *(__IO uint8_t *)(NAND_BANK_ADDR | NAND_CMD_AREA) = NAND_CMD_RESET; + return nand_wait_status(); +} +#endif /* SOC_SERIES_N32H47x_48x || SOC_SERIES_N32H49x */ + static struct rt_mtd_nand_device _nand_dev; /* @@ -49,8 +144,10 @@ static struct rt_mtd_nand_device _nand_dev; * [10:3] Start command * [2:0] Byte lane strobes (AXI standard) */ +#if defined(SOC_SERIES_N32H7xx) #define NAND_CMD_AREA ((uint32_t)0x00000000) /* bit19 = 0 */ #define NAND_DATA_AREA ((uint32_t)0x00080000) /* bit19 = 1 */ +#endif /* SOC_SERIES_N32H7xx */ /* * Address cycle count auto-derived from Kconfig NAND chip parameters. @@ -79,6 +176,7 @@ static struct rt_mtd_nand_device _nand_dev; #define NAND_RW_ADDR_CYCLES (2 + NAND_ROW_ADDR_CYCLES) #define NAND_ERASE_ADDR_CYCLES NAND_ROW_ADDR_CYCLES +#if defined(SOC_SERIES_N32H7xx) /* Encode cycle count into AXI address bits [23:21] */ #define NAND_ADDR_CYCLES(n) ((uint32_t)(n) << 21) @@ -89,21 +187,7 @@ static struct rt_mtd_nand_device _nand_dev; #define NAND_ECC_LAST_ENABLE ((uint32_t)0x00000400) #define NAND_ECC_LAST_DISABLE ((uint32_t)0x00000000) - -/* Standard NAND flash commands */ -#define NAND_CMD_READ_1ST ((uint8_t)0x00) -#define NAND_CMD_READ_2ND ((uint8_t)0x30) -#define NAND_CMD_WRITE_1ST ((uint8_t)0x80) -#define NAND_CMD_WRITE_2ND ((uint8_t)0x10) -#define NAND_CMD_ERASE_1ST ((uint8_t)0x60) -#define NAND_CMD_ERASE_2ND ((uint8_t)0xD0) -#define NAND_CMD_READ_ID ((uint8_t)0x90) -#define NAND_CMD_STATUS ((uint8_t)0x70) -#define NAND_CMD_RESET ((uint8_t)0xFF) - -/* NAND status bits */ -#define NAND_STATUS_READY ((uint32_t)0x40) -#define NAND_STATUS_ERROR ((uint32_t)0x01) +#endif /* SOC_SERIES_N32H7xx */ /* * FEMC uses Hamming code with 1-bit correction / 2-bit detection. @@ -163,11 +247,50 @@ static rt_err_t rt_nand_init(void) reset_cmd = NAND_BANK_ADDR | NAND_ADDR_CYCLES(0) | NAND_CMD_END_DISABLE | NAND_CMD_AREA | (NAND_CMD_RESET << 3); *((__IO uint16_t *)reset_cmd) = 0x0000; } -#endif - return RT_EOK; +#elif defined(SOC_SERIES_N32H47x_48x) || defined(SOC_SERIES_N32H49x) + /* FEMC NAND init */ + FEMC_NandInitType nandInit = { 0 }; + FEMC_NandTimingInitType timing = { 0 }; + + /* Make sure the FEMC clock is enabled. GPIO is configured by the board + * layer (not here), but the FEMC peripheral clock must be on. */ + RCC_EnableAHBPeriphClk(RCC_AHB_PERIPHEN_FEMC, ENABLE); + + FEMC_InitNandStruct(&nandInit); + + /* Bank select from Kconfig */ + nandInit.Bank = NAND_BANK; + +#ifdef BSP_USING_NAND_BUS_WIDTH_8B + nandInit.MemDataWidth = FEMC_NAND_BUS_WIDTH_8B; +#endif /* BSP_USING_NAND_BUS_WIDTH_8B */ + +#ifdef BSP_USING_NAND_BUS_WIDTH_16B + nandInit.MemDataWidth = FEMC_NAND_BUS_WIDTH_16B; +#endif /* BSP_USING_NAND_BUS_WIDTH_16B */ + + /* Wait feature (NWAIT) enabled; ECC disabled for now (matches demo) */ + nandInit.WaitFeatureEnable = FEMC_NAND_NWAIT_ENABLE; + nandInit.EccEnable = FEMC_NAND_ECC_DISABLE; + nandInit.EccPageSize = FEMC_NAND_ECC_PAGE_2048BYTES; + + /* Timing (incl. TCLR/TAR) from board/ports/femc_timing.c */ + FEMC_Nand_Timing_Config(&nandInit, &timing); + + FEMC_InitNand(&nandInit); + FEMC_EnableNand(nandInit.Bank, ENABLE); + + /* Reset NAND chip */ + return nand_reset(); +#else + /* Unsupported platform: the SOC series macro is always selected by the + * BSP Kconfig, so this branch should never be compiled. */ + return -RT_ERROR; +#endif } +#if defined(SOC_SERIES_N32H7xx) /* * Wait for NAND operation to complete via FEMC busy flag. * @@ -231,6 +354,7 @@ static rt_err_t nand_check_status(uint32_t bank_addr) return -RT_ETIMEOUT; } +#endif /* SOC_SERIES_N32H7xx */ /* * =========================================================================== @@ -244,6 +368,7 @@ static rt_err_t nand_check_status(uint32_t bank_addr) * Pack bytes 1-4 (Device + 3rd + 4th + 5th), discard byte 0 (Maker ID). * This matches the stm32mp1 convention in the RT-Thread codebase. */ +#if defined(SOC_SERIES_N32H7xx) static rt_err_t _read_id(struct rt_mtd_nand_device *device) { uint32_t cmd_addr, data_addr; @@ -272,8 +397,37 @@ static rt_err_t _read_id(struct rt_mtd_nand_device *device) return (rt_err_t)id; } +#elif defined(SOC_SERIES_N32H47x_48x) || defined(SOC_SERIES_N32H49x) +static rt_err_t _read_id(struct rt_mtd_nand_device *device) +{ + uint32_t bank = NAND_BANK_ADDR; + uint8_t id_bytes[5]; + rt_uint32_t id; + + /* READ_ID (0x90) + 1 address byte, then read 5 ID bytes */ + *(__IO uint8_t *)(bank | NAND_CMD_AREA) = NAND_CMD_READ_ID; + *(__IO uint8_t *)(bank | NAND_ADDR_AREA) = 0x00; + + for (uint8_t i = 0; i < 5; i++) + { + id_bytes[i] = *(__IO uint8_t *)(bank | NAND_DATA_AREA); + } + + LOG_D("NAND ID: %02X %02X %02X %02X %02X", + id_bytes[0], id_bytes[1], id_bytes[2], id_bytes[3], id_bytes[4]); + + /* Pack bytes 1-4 into 32-bit return value (byte 0 = Maker ID, discarded) */ + id = (rt_uint32_t)id_bytes[1] << 24; + id |= (rt_uint32_t)id_bytes[2] << 16; + id |= (rt_uint32_t)id_bytes[3] << 8; + id |= (rt_uint32_t)id_bytes[4]; + + return (rt_err_t)id; +} +#endif /* read one page (data + spare) */ +#if defined(SOC_SERIES_N32H7xx) static rt_err_t _read_page(struct rt_mtd_nand_device *device, rt_off_t page, rt_uint8_t *data, rt_uint32_t data_len, @@ -341,8 +495,69 @@ static rt_err_t _read_page(struct rt_mtd_nand_device *device, } return ret; } +#elif defined(SOC_SERIES_N32H47x_48x) || defined(SOC_SERIES_N32H49x) +static rt_err_t _read_page(struct rt_mtd_nand_device *device, + rt_off_t page, + rt_uint8_t *data, rt_uint32_t data_len, + rt_uint8_t *spare, rt_uint32_t spare_len) +{ + uint32_t bank = NAND_BANK_ADDR; + uint32_t row_addr = (uint32_t)page; + uint32_t i; + rt_err_t ret; + + /* Command phase: READ_1ST + column(2x 0) + row(NAND_ROW_ADDR_CYCLES) + READ_2ND */ + *(__IO uint8_t *)(bank | NAND_CMD_AREA) = NAND_CMD_READ_1ST; + *(__IO uint8_t *)(bank | NAND_ADDR_AREA) = 0x00; + *(__IO uint8_t *)(bank | NAND_ADDR_AREA) = 0x00; + *(__IO uint8_t *)(bank | NAND_ADDR_AREA) = (uint8_t)(row_addr & 0xFF); + *(__IO uint8_t *)(bank | NAND_ADDR_AREA) = (uint8_t)((row_addr >> 8) & 0xFF); +#if NAND_ROW_ADDR_CYCLES == 3 + *(__IO uint8_t *)(bank | NAND_ADDR_AREA) = (uint8_t)((row_addr >> 16) & 0xFF); +#endif + *(__IO uint8_t *)(bank | NAND_CMD_AREA) = NAND_CMD_READ_2ND; + + /* + * Read main data area one byte at a time. + * + * NOTE: do NOT send a READ STATUS (0x70) between the 0x30 command and + * the data reads below. 0x70 switches the NAND into status-output mode, + * so the data area would return the status register (e.g. 0xE0) instead + * of the page contents. This mirrors demo's FEMC_Nand_ReadPage_Byte(): + * data is read directly after 0x30 and relies on the NWAIT feature + * (FEMC_NAND_NWAIT_ENABLE) to stall the CPU until the page is ready. + * + * The data area can be accessed with 8/16/32-bit AHB accesses regardless + * of the NAND bus width (FEMC splits wide accesses automatically, see + * FEMC manual "supported memories and operations"). The byte loop is + * used here for simplicity and maximum compatibility. + */ + if (data && data_len) + { + for (i = 0; i < data_len; i++) + { + data[i] = *(__IO uint8_t *)(bank | NAND_DATA_AREA); + } + } + + /* spare area read not implemented yet */ + + /* + * Check status after the data has been read, mirroring demo's + * FEMC_Nand_ReadPage_Byte -> FEMC_Nand_GetStatus(). A READ STATUS (0x70) + * after the data phase is safe and checks for any pending read error. + */ + ret = nand_wait_status(); + if (ret != RT_EOK) + { + LOG_E("read page %d status error %d", (int)page, ret); + } + return ret; +} +#endif /* write one page (data + spare) */ +#if defined(SOC_SERIES_N32H7xx) static rt_err_t _write_page(struct rt_mtd_nand_device *device, rt_off_t page, const rt_uint8_t *data, rt_uint32_t data_len, @@ -436,6 +651,118 @@ static rt_err_t _write_page(struct rt_mtd_nand_device *device, } return ret; } +#elif defined(SOC_SERIES_N32H47x_48x) || defined(SOC_SERIES_N32H49x) +static rt_err_t _write_page(struct rt_mtd_nand_device *device, + rt_off_t page, + const rt_uint8_t *data, rt_uint32_t data_len, + const rt_uint8_t *spare, rt_uint32_t spare_len) +{ + uint32_t bank = NAND_BANK_ADDR; + uint32_t row_addr = (uint32_t)page; + uint32_t i; + rt_err_t ret; + +#ifdef BSP_USING_NAND_BUS_WIDTH_16B + /* 16-bit NAND has no 8-bit store (see the data phase below), so an odd + * data_len has no halfword-only encoding: its trailing byte could only + * leave as an 8-bit write, and padding would program a byte the caller + * never asked for. Refuse it before the command phase, so the chip is + * never left in the middle of a program sequence. + */ + if (data_len & 1U) + { + LOG_E("page %d: 16-bit NAND needs an even data length, got %u", (int)page, (unsigned int)data_len); + return -RT_EINVAL; + } +#endif + + /* Command phase: WRITE_1ST + column(2x 0) + row(NAND_ROW_ADDR_CYCLES) */ + *(__IO uint8_t *)(bank | NAND_CMD_AREA) = NAND_CMD_WRITE_1ST; + *(__IO uint8_t *)(bank | NAND_ADDR_AREA) = 0x00; + *(__IO uint8_t *)(bank | NAND_ADDR_AREA) = 0x00; + *(__IO uint8_t *)(bank | NAND_ADDR_AREA) = (uint8_t)(row_addr & 0xFF); + *(__IO uint8_t *)(bank | NAND_ADDR_AREA) = (uint8_t)((row_addr >> 8) & 0xFF); +#if NAND_ROW_ADDR_CYCLES == 3 + *(__IO uint8_t *)(bank | NAND_ADDR_AREA) = (uint8_t)((row_addr >> 16) & 0xFF); +#endif + + /* + * Data phase: write main data to the data area. + * + * @note FEMC manual "supported memories and operations": 16-bit NAND + * does NOT support 8-bit AHB writes (only 16/32-bit). So for 16-bit bus + * use 16-bit accesses, otherwise the byte loop is safe. + */ + if (data && data_len) + { +#ifdef BSP_USING_NAND_BUS_WIDTH_16B + /* + * 16-bit NAND: 8-bit AHB writes are NOT supported (see FEMC manual + * "supported memories and operations"), so data must be written as + * 16-bit accesses. If the source buffer is not 16-bit aligned, copy + * it into an aligned temporary buffer first. An odd data_len was + * refused at the top of this function. + */ + if (((uint32_t)data & 0x1U) == 0U) + { + uint16_t *p16 = (uint16_t *)data; + uint32_t word_cnt = data_len / 2; + + for (i = 0; i < word_cnt; i++) + { + *(__IO uint16_t *)(bank | NAND_DATA_AREA) = p16[i]; + } + } + else + { + uint16_t *tmp; + uint32_t word_cnt = data_len / 2; + + tmp = rt_malloc(data_len); + if (!tmp) + { + return -RT_ENOMEM; + } + rt_memcpy(tmp, data, data_len); + + for (i = 0; i < word_cnt; i++) + { + *(__IO uint16_t *)(bank | NAND_DATA_AREA) = tmp[i]; + } + rt_free(tmp); + } +#else + for (i = 0; i < data_len; i++) + { + *(__IO uint8_t *)(bank | NAND_DATA_AREA) = data[i]; + } +#endif + } + else + { + /* No payload: one store still has to be issued so the data phase clocks + * out. 16-bit NAND rejects 8-bit AHB writes (see the note above), so the + * dummy element must be halfword-sized on that bus. */ +#ifdef BSP_USING_NAND_BUS_WIDTH_16B + *(__IO uint16_t *)(bank | NAND_DATA_AREA) = 0x0000; +#else + *(__IO uint8_t *)(bank | NAND_DATA_AREA) = 0x00; +#endif + } + + /* spare area write not implemented yet */ + + *(__IO uint8_t *)(bank | NAND_CMD_AREA) = NAND_CMD_WRITE_2ND; + + /* Wait for program completion, then check status */ + ret = nand_wait_status(); + if (ret != RT_EOK) + { + LOG_E("write page %d status error %d", (int)page, ret); + } + return ret; +} +#endif /* move page (for garbage collection) */ static rt_err_t _move_page(struct rt_mtd_nand_device *device, @@ -465,6 +792,7 @@ static rt_err_t _move_page(struct rt_mtd_nand_device *device, } /* erase one block */ +#if defined(SOC_SERIES_N32H7xx) static rt_err_t _erase_block(struct rt_mtd_nand_device *device, rt_uint32_t block) { uint32_t cmd_addr; @@ -508,6 +836,31 @@ static rt_err_t _erase_block(struct rt_mtd_nand_device *device, rt_uint32_t bloc } return ret; } +#elif defined(SOC_SERIES_N32H47x_48x) || defined(SOC_SERIES_N32H49x) +static rt_err_t _erase_block(struct rt_mtd_nand_device *device, rt_uint32_t block) +{ + uint32_t bank = NAND_BANK_ADDR; + uint32_t row_addr = block * device->pages_per_block; + rt_err_t ret; + + /* Convert block number to row address (no column address for erase) */ + *(__IO uint8_t *)(bank | NAND_CMD_AREA) = NAND_CMD_ERASE_1ST; + *(__IO uint8_t *)(bank | NAND_ADDR_AREA) = (uint8_t)(row_addr & 0xFF); + *(__IO uint8_t *)(bank | NAND_ADDR_AREA) = (uint8_t)((row_addr >> 8) & 0xFF); +#if NAND_ROW_ADDR_CYCLES == 3 + *(__IO uint8_t *)(bank | NAND_ADDR_AREA) = (uint8_t)((row_addr >> 16) & 0xFF); +#endif + *(__IO uint8_t *)(bank | NAND_CMD_AREA) = NAND_CMD_ERASE_2ND; + + /* Wait for erase completion, then check status */ + ret = nand_wait_status(); + if (ret != RT_EOK) + { + LOG_E("erase block %d status error %d", (int)block, ret); + } + return ret; +} +#endif /* * Check if a block is bad. diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_qspi.c b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_qspi.c index 84d17a85b18e..7b2b6e03db9b 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_qspi.c +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_qspi.c @@ -9,10 +9,9 @@ #include "board.h" -#if defined(BSP_USING_QSPI2) +#if defined(BSP_USING_QSPI2) || defined(BSP_USING_QSPI) #include "drv_qspi.h" -#include "n32h7xx_xspi_v2.h" #include "drv_config.h" #include @@ -22,11 +21,14 @@ #define XSPI_TIMEOUT_TICKS (1000U) #define DMA_TRANS_MIN_LEN (10U) +#if defined(SOC_SERIES_N32H7xx) #define XSPI_XIP_BASE_ADDR 0x90000000 +#endif static struct rt_spi_bus _xspi_bus; static struct n32_xspi _n32_xspi; +#if !defined(SOC_SERIES_N32H47x_48x) static rt_err_t xspi_wait_flag(XSPI_Module *xspi, uint32_t flag, uint32_t timeout) { @@ -43,23 +45,12 @@ static rt_err_t xspi_wait_flag(XSPI_Module *xspi, uint32_t flag, uint32_t timeou return RT_EOK; } -static void XSPI_Wait_Tx_Compelete(void) -{ - while (XSPI_GetFlagStatus(xSPI2, XSPI_TXFE_FLAG) != SET) - { - } - - while (XSPI_GetFlagStatus(xSPI2, XSPI_BUSY_FLAG) != RESET) - { - } -} - - -static rt_err_t xspi_wait_flag_clear(XSPI_Module *xspi, uint32_t flag, uint32_t timeout) +static rt_err_t xspi_wait_busy(XSPI_Module *xspi, uint32_t timeout) { uint32_t tickstart = rt_tick_get(); - while (XSPI_GetFlagStatus(xspi, flag) != RESET) + /* wait until the transfer completes (BUSY flag cleared) */ + while (XSPI_GetFlagStatus(xspi, XSPI_BUSY_FLAG) == SET) { if (((rt_tick_get() - tickstart) >= timeout) && (timeout != 0xFFFFFFFFU)) { @@ -70,9 +61,22 @@ static rt_err_t xspi_wait_flag_clear(XSPI_Module *xspi, uint32_t flag, uint32_t return RT_EOK; } -static rt_err_t xspi_wait_busy(XSPI_Module *xspi, uint32_t timeout) +/* TX FIFO drained + transfer finished. Both waits carry the caller's timeout: + * an unresponsive bus (TXFE that never sets after a failed arm, or a slave + * that stretches the clock and never releases BUSY) must not spin here + * forever, because that would silently defeat the timeout its caller was + * given. */ +static rt_err_t xspi_wait_tx_complete(XSPI_Module *xspi, uint32_t timeout) { - return xspi_wait_flag_clear(xspi, XSPI_BUSY_FLAG, timeout); + rt_err_t result; + + result = xspi_wait_flag(xspi, XSPI_TXFE_FLAG, timeout); + if (result != RT_EOK) + { + return result; + } + + return xspi_wait_busy(xspi, timeout); } /* ---- multi-line helpers ---- */ @@ -115,6 +119,7 @@ static uint32_t xspi_get_frame_format(uint8_t lines) return XSPI_STANDARD_MODE; } } + static void xspi_config_enhanced_for_msg(struct n32_xspi *xspi_config, struct rt_qspi_message *qspi_msg, uint32_t transfer_mode, rt_uint8_t ddr_enabled) @@ -216,12 +221,15 @@ static rt_err_t xspi_qspi_transmit(struct n32_xspi *xspi_drv, struct rt_qspi_mes XSPI_SendData(xspi, qspi_msg->instruction.content); XSPI_SendData(xspi, qspi_msg->address.content); - while (i < len) + for (i = 0; i < len; i++) { - if (XSPI_GetFlagStatus(xspi, XSPI_TXFNF_FLAG) == SET) + if (xspi_wait_flag(xspi, XSPI_TXFNF_FLAG, timeout) != RT_EOK) { - XSPI_SendData(xspi, buf[i++]); + XSPI_Enable(xspi, DISABLE); + return -RT_ETIMEOUT; } + + XSPI_SendData(xspi, buf[i]); } ret = xspi_wait_busy(xspi, timeout); @@ -271,8 +279,13 @@ static rt_err_t xspi_qspi_receive(struct n32_xspi *xspi_drv, struct rt_qspi_mess } - while (XSPI_GetFlagStatus(xspi, XSPI_BUSY_FLAG) != RESET) + /* Bound the post-transfer wait like the receive loop above: a slave that + * stretches the clock and never releases BUSY must not spin here forever, + * defeating the timeout the caller was given. */ + if (xspi_wait_busy(xspi, timeout) != RT_EOK) { + ret = -RT_ETIMEOUT; + goto exit; } XSPI_ClearRxFIFO(xspi); @@ -306,7 +319,10 @@ static rt_err_t xspi_spi_transmit(struct n32_xspi *xspi_drv, struct rt_qspi_mess XSPI_SetNumberOfDataFrame(xspi, qspi_msg->parent.length); - XSPI_Wait_Tx_Compelete(); + if (xspi_wait_tx_complete(xspi, timeout) != RT_EOK) + { + return -RT_ETIMEOUT; + } XSPI_Enable(xspi, ENABLE); @@ -325,17 +341,24 @@ static rt_err_t xspi_spi_transmit(struct n32_xspi *xspi_drv, struct rt_qspi_mess if (qspi_msg->parent.length <= 16) { - while (i < qspi_msg->parent.length) + for (i = 0; i < qspi_msg->parent.length; i++) { - if (XSPI_GetFlagStatus(xspi, XSPI_TXFNF_FLAG) != RESET) - { - uint8_t tx = send_buf ? send_buf[i++] : 0xFF; + uint8_t tx = send_buf ? send_buf[i] : 0xFF; - XSPI_SendData(xspi, tx); + if (xspi_wait_flag(xspi, XSPI_TXFNF_FLAG, timeout) != RT_EOK) + { + XSPI_Enable(xspi, DISABLE); + return -RT_ETIMEOUT; } + + XSPI_SendData(xspi, tx); } - XSPI_Wait_Tx_Compelete(); + if (xspi_wait_tx_complete(xspi, timeout) != RT_EOK) + { + XSPI_Enable(xspi, DISABLE); + return -RT_ETIMEOUT; + } if (recv_buf) { @@ -360,6 +383,7 @@ static rt_err_t xspi_spi_transmit(struct n32_xspi *xspi_drv, struct rt_qspi_mess if (xspi_wait_flag(xspi, XSPI_TXFNF_FLAG, timeout) != RT_EOK) { + XSPI_Enable(xspi, DISABLE); return -RT_ETIMEOUT; } @@ -369,6 +393,7 @@ static rt_err_t xspi_spi_transmit(struct n32_xspi *xspi_drv, struct rt_qspi_mess { if (xspi_wait_flag(xspi, XSPI_RXFNE_FLAG, timeout) != RT_EOK) { + XSPI_Enable(xspi, DISABLE); return -RT_ETIMEOUT; } @@ -409,7 +434,11 @@ static rt_err_t xspi_spi_send(struct n32_xspi *xspi_drv, struct rt_qspi_message XSPI_Enable(xspi, ENABLE); - XSPI_Wait_Tx_Compelete(); + if (xspi_wait_tx_complete(xspi, timeout) != RT_EOK) + { + XSPI_Enable(xspi, DISABLE); + return -RT_ETIMEOUT; + } XSPI_ClearRxFIFO(xspi); @@ -433,15 +462,22 @@ static rt_err_t xspi_spi_send(struct n32_xspi *xspi_drv, struct rt_qspi_message XSPI_SendData(xspi, qspi_msg->address.content & 0xff); - while (number < qspi_msg->parent.length) + for (number = 0; number < qspi_msg->parent.length; number++) { - if (XSPI_GetFlagStatus(xspi, XSPI_TXFNF_FLAG) != RESET) + if (xspi_wait_flag(xspi, XSPI_TXFNF_FLAG, timeout) != RT_EOK) { - XSPI_SendData(xspi, buf[number++]); + XSPI_Enable(xspi, DISABLE); + return -RT_ETIMEOUT; } + + XSPI_SendData(xspi, buf[number]); } - XSPI_Wait_Tx_Compelete(); + if (xspi_wait_tx_complete(xspi, timeout) != RT_EOK) + { + XSPI_Enable(xspi, DISABLE); + return -RT_ETIMEOUT; + } XSPI_Enable(xspi, DISABLE); @@ -493,9 +529,14 @@ static rt_err_t xspi_spi_receive(struct n32_xspi *xspi_drv, struct rt_qspi_messa XSPI_SetTXStartFIFOThreshold(xspi, (qspi_msg->parent.length - 1)); } - while (i < qspi_msg->parent.length + 4) /*Tx Fifo not full*/ + while (i < qspi_msg->parent.length + 4) /*Tx Fifo not full*/ { - while ((xspi->STS & XSPI_TXFNF_FLAG) != XSPI_TXFNF_FLAG); /*wait tx FIFO not full flag set*/ + /* Wait for TX FIFO not full, bounded: the write below assumes room. */ + if (xspi_wait_flag(xspi, XSPI_TXFNF_FLAG, timeout) != RT_EOK) + { + XSPI_Enable(xspi, DISABLE); + return -RT_ETIMEOUT; + } if (i < 4) { @@ -506,7 +547,7 @@ static rt_err_t xspi_spi_receive(struct n32_xspi *xspi_drv, struct rt_qspi_messa xspi->DAT0 = 0xFF; } - if ((xspi->STS & XSPI_RXFNE_FLAG)) /*Rx Fifo not empty set*/ + if ((xspi->STS & XSPI_RXFNE_FLAG)) /*Rx Fifo not empty set*/ { if (j < 4) { @@ -523,13 +564,24 @@ static rt_err_t xspi_spi_receive(struct n32_xspi *xspi_drv, struct rt_qspi_messa i++; } + /* Drain the RX FIFO until the transfer finishes. The reads must stay on + * every iteration -- the waits in this file stop as soon as a flag + * clears, which here would strand the last bytes in the FIFO -- so only + * the exit condition is bounded. */ + uint32_t tickstart = rt_tick_get(); + do { - if ((xspi->STS & XSPI_RXFNE_FLAG)) /*Rx Fifo not empty set*/ + if ((xspi->STS & XSPI_RXFNE_FLAG)) /*Rx Fifo not empty set*/ { *(buf++) = xspi->DAT0; /*read data register*/ } + if (((rt_tick_get() - tickstart) >= timeout) && (timeout != 0xFFFFFFFFU)) + { + XSPI_Enable(xspi, DISABLE); + return -RT_ETIMEOUT; + } } while ((xspi->STS & XSPI_BUSY_FLAG) == SET); XSPI_Enable(xspi, DISABLE); @@ -547,7 +599,6 @@ static rt_err_t xspi_configure(struct rt_spi_device *device, struct rt_spi_confi xspi_drv->qspi_cfg = &qspi_dev->config; xspi_drv->cfg = configuration; -#if defined(SOC_SERIES_N32H7xx) /* set scpol/scph value */ switch (xspi_drv->qspi_cfg->parent.mode & 0x03) { @@ -618,12 +669,478 @@ static rt_err_t xspi_configure(struct rt_spi_device *device, struct rt_spi_confi xspi_drv->InitStructure.DataFrameSize = XSPI_FRAME_SIZE_32_BIT; } + return RT_EOK; +} -#endif +#else /* SOC_SERIES_N32H47x_48x: DesignWare-style XSPI, single global instance */ + +static rt_err_t xspi_wait_flag(uint32_t flag, uint32_t timeout) +{ + uint32_t tickstart = rt_tick_get(); + + while (XSPI_GetFlagStatus(flag) == RESET) + { + if (((rt_tick_get() - tickstart) >= timeout) && (timeout != 0xFFFFFFFFU)) + { + return -RT_ETIMEOUT; + } + } return RT_EOK; } +static rt_err_t xspi_wait_busy(uint32_t timeout) +{ + uint32_t tickstart = rt_tick_get(); + + while (XSPI_GetFlagStatus(XSPI_STS_BUSY) == SET) + { + if (((rt_tick_get() - tickstart) >= timeout) && (timeout != 0xFFFFFFFFU)) + { + return -RT_ETIMEOUT; + } + } + + return RT_EOK; +} + +/* TX FIFO drained + transfer finished. Both waits carry the caller's timeout: + * an unresponsive bus (TXFE that never sets after a failed arm, or a slave + * that stretches the clock and never releases BUSY) must not spin here + * forever, because that would silently defeat the timeout its caller was + * given. */ +static rt_err_t xspi_wait_tx_complete(uint32_t timeout) +{ + rt_err_t result; + + result = xspi_wait_flag(XSPI_STS_TXFE, timeout); + if (result != RT_EOK) + { + return result; + } + + return xspi_wait_busy(timeout); +} + +/* data line width -> SPIFRF frame format */ +static uint32_t xspi_get_spifrf(uint8_t lines) +{ + switch (lines) + { + case 2: + return XSPI_CTRL0_SPIFRF_DUAL_FORMAT; + case 4: + return XSPI_CTRL0_SPIFRF_QUAD_FORMAT; + case 8: + return XSPI_CTRL0_SPIFRF_OCTAL_FORMAT; + default: + return XSPI_CTRL0_SPIFRF_STANDARD_FORMAT; + } +} + +/* instruction/address line width -> enhanced TRANSTYPE */ +static uint32_t xspi_get_transtype(uint8_t instr_lines, uint8_t addr_lines) +{ + if (instr_lines == 1 && addr_lines == 1) + { + return XSPI_ENH_CTRL0_TRANSTYPE_STANDARD; + } + if (instr_lines == 1 && addr_lines > 1) + { + return XSPI_ENH_CTRL0_TRANSTYPE_ADDRESS_BY_FRF; + } + return XSPI_ENH_CTRL0_TRANSTYPE_ALL_BY_FRF; +} + +/* address bit length -> enhanced ADDRLEN */ +static uint32_t xspi_get_addrlen(uint8_t size) +{ + switch (size) + { + case 8: + return XSPI_ENH_CTRL0_ADDRLEN_8_BIT; + case 16: + return XSPI_ENH_CTRL0_ADDRLEN_16_BIT; + case 24: + return XSPI_ENH_CTRL0_ADDRLEN_24_BIT; + case 32: + return XSPI_ENH_CTRL0_ADDRLEN_32_BIT; + default: + return 0; + } +} + +/* ---- standard SPI (base mode) ---- */ + +static rt_err_t xspi_spi_transmit(struct n32_xspi *xspi_drv, struct rt_qspi_message *qspi_msg, uint32_t timeout) +{ + uint16_t i = 0, rindex = 0; + uint8_t *send_buf = (uint8_t *)qspi_msg->parent.send_buf; + uint8_t *recv_buf = (uint8_t *)qspi_msg->parent.recv_buf; + uint32_t len = qspi_msg->parent.length; + + xspi_drv->InitStructure.FRF = XSPI_CTRL0_FRF_MOTOROLA; + xspi_drv->InitStructure.SPIFRF = XSPI_CTRL0_SPIFRF_STANDARD_FORMAT; + xspi_drv->InitStructure.TMOD = (send_buf && !recv_buf) ? XSPI_CTRL0_TMOD_TX_ONLY + : XSPI_CTRL0_TMOD_TX_AND_RX; + xspi_drv->InitStructure.NDF = len - 1; + + XSPI_Cmd(DISABLE); + XSPI_Init(&xspi_drv->InitStructure); + if (recv_buf) + { + XSPI_ClrFifo(); + } + XSPI_Cmd(ENABLE); + + /* The sends below stay non-blocking on purpose: a blocking wait would stop + * draining the RX FIFO, which the comment inside warns would overflow on + * large full-duplex transfers. Only the loop's exit is bounded. */ + uint32_t tickstart = rt_tick_get(); + + while (i < len) + { + if (XSPI_GetFlagStatus(XSPI_STS_TXFNF) == SET) + { + XSPI_SendData(send_buf ? send_buf[i] : 0xFF); + i++; + } + + /* drain RX FIFO in lockstep to avoid overflow on large full-duplex transfers */ + if (recv_buf && (XSPI_GetFlagStatus(XSPI_STS_RXFNE) == SET)) + { + if (rindex < len) + { + recv_buf[rindex++] = (uint8_t)XSPI_ReceiveData(); + } + else + { + (void)XSPI_ReceiveData(); + } + } + + if (((rt_tick_get() - tickstart) >= timeout) && (timeout != 0xFFFFFFFFU)) + { + XSPI_Cmd(DISABLE); + return -RT_ETIMEOUT; + } + } + + if (xspi_wait_tx_complete(timeout) != RT_EOK) + { + XSPI_Cmd(DISABLE); + return -RT_ETIMEOUT; + } + + if (recv_buf) + { + while ((rindex < len) && (XSPI_GetFlagStatus(XSPI_STS_RXFNE) == SET)) + { + recv_buf[rindex++] = (uint8_t)XSPI_ReceiveData(); + } + } + + XSPI_Cmd(DISABLE); + return RT_EOK; +} + +static rt_err_t xspi_spi_send(struct n32_xspi *xspi_drv, struct rt_qspi_message *qspi_msg, uint32_t timeout) +{ + uint16_t i = 0; + uint8_t *buf = (uint8_t *)qspi_msg->parent.send_buf; + uint32_t len = qspi_msg->parent.length; + uint8_t s_data[4] = { 0 }; + + if (qspi_msg->instruction.content == 0) + { + return RT_ERROR; + } + + /* base mode full-duplex: clock out instruction(1) + address(3) + data(len), + * discard whatever is received. */ + xspi_drv->InitStructure.FRF = XSPI_CTRL0_FRF_MOTOROLA; + xspi_drv->InitStructure.SPIFRF = XSPI_CTRL0_SPIFRF_STANDARD_FORMAT; + xspi_drv->InitStructure.TMOD = XSPI_CTRL0_TMOD_TX_AND_RX; + xspi_drv->InitStructure.NDF = (len + 4) - 1; + + XSPI_Cmd(DISABLE); + XSPI_Init(&xspi_drv->InitStructure); + XSPI_ClrFifo(); + XSPI_Cmd(ENABLE); + + s_data[0] = qspi_msg->instruction.content; + s_data[1] = (qspi_msg->address.content & 0xff0000) >> 16; + s_data[2] = (qspi_msg->address.content & 0xff00) >> 8; + s_data[3] = qspi_msg->address.content & 0xff; + + /* Non-blocking sends + lockstep RX discard; only the exit is bounded (a + * blocking wait would stall the discard and let the RX FIFO overflow). */ + uint32_t tickstart = rt_tick_get(); + + while (i < len + 4) + { + if (XSPI_GetFlagStatus(XSPI_STS_TXFNF) == SET) + { + XSPI_SendData((i < 4) ? s_data[i] : buf[i - 4]); + i++; + } + + if (XSPI_GetFlagStatus(XSPI_STS_RXFNE) == SET) + { + (void)XSPI_ReceiveData(); /* discard received bytes */ + } + + if (((rt_tick_get() - tickstart) >= timeout) && (timeout != 0xFFFFFFFFU)) + { + XSPI_Cmd(DISABLE); + return -RT_ETIMEOUT; + } + } + + /* drain the remaining received bytes */ + while (XSPI_GetFlagStatus(XSPI_STS_RXFNE) == SET) + { + (void)XSPI_ReceiveData(); + } + + if (xspi_wait_tx_complete(timeout) != RT_EOK) + { + XSPI_Cmd(DISABLE); + return -RT_ETIMEOUT; + } + XSPI_Cmd(DISABLE); + return RT_EOK; +} + +static rt_err_t xspi_spi_receive(struct n32_xspi *xspi_drv, struct rt_qspi_message *qspi_msg, uint32_t timeout) +{ + uint16_t i = 0, j = 0; + uint8_t *buf = (uint8_t *)qspi_msg->parent.recv_buf; + uint32_t len = qspi_msg->parent.length; + uint8_t s_data[4] = { 0 }; + + if (qspi_msg->instruction.content == 0) + { + return RT_ERROR; + } + + /* base mode full-duplex: clock out instruction(1) + address(3) + dummy(len), + * discard the 4 bytes received during the command/address phase. */ + xspi_drv->InitStructure.FRF = XSPI_CTRL0_FRF_MOTOROLA; + xspi_drv->InitStructure.SPIFRF = XSPI_CTRL0_SPIFRF_STANDARD_FORMAT; + xspi_drv->InitStructure.TMOD = XSPI_CTRL0_TMOD_TX_AND_RX; + xspi_drv->InitStructure.NDF = (len + 4) - 1; + + XSPI_Cmd(DISABLE); + XSPI_Init(&xspi_drv->InitStructure); + XSPI_ClrFifo(); + XSPI_Cmd(ENABLE); + + s_data[0] = qspi_msg->instruction.content; + s_data[1] = (qspi_msg->address.content & 0xff0000) >> 16; + s_data[2] = (qspi_msg->address.content & 0xff00) >> 8; + s_data[3] = qspi_msg->address.content & 0xff; + + /* Non-blocking sends + lockstep RX collect; only the exits are bounded. */ + uint32_t tickstart = rt_tick_get(); + + while (i < len + 4) + { + if (XSPI_GetFlagStatus(XSPI_STS_TXFNF) == SET) + { + XSPI_SendData((i < 4) ? s_data[i] : 0xFF); + i++; + } + + if (XSPI_GetFlagStatus(XSPI_STS_RXFNE) == SET) + { + uint8_t r = (uint8_t)XSPI_ReceiveData(); + if (j >= 4) + { + buf[j - 4] = r; /* drop the 4 command/address echo bytes */ + } + j++; + } + + if (((rt_tick_get() - tickstart) >= timeout) && (timeout != 0xFFFFFFFFU)) + { + XSPI_Cmd(DISABLE); + return -RT_ETIMEOUT; + } + } + + /* drain the remaining received bytes (len + 4 in total) */ + while (j < len + 4) + { + if (XSPI_GetFlagStatus(XSPI_STS_RXFNE) == SET) + { + uint8_t r = (uint8_t)XSPI_ReceiveData(); + if (j >= 4) + { + buf[j - 4] = r; + } + j++; + } + + if (((rt_tick_get() - tickstart) >= timeout) && (timeout != 0xFFFFFFFFU)) + { + XSPI_Cmd(DISABLE); + return -RT_ETIMEOUT; + } + } + + XSPI_Cmd(DISABLE); + return RT_EOK; +} + +/* ---- enhanced (dual/quad/octal) mode ---- */ + +static void xspi_config_enhanced(struct n32_xspi *xspi_drv, struct rt_qspi_message *qspi_msg, uint32_t tmod) +{ + XSPI_InitType *init = &xspi_drv->InitStructure; + + init->FRF = XSPI_CTRL0_FRF_MOTOROLA; + init->SPIFRF = xspi_get_spifrf(qspi_msg->qspi_data_lines); + init->TMOD = tmod; + init->NDF = qspi_msg->parent.length - 1; + + init->ENHANCED_TRANSTYPE = xspi_get_transtype(qspi_msg->instruction.qspi_lines, qspi_msg->address.qspi_lines); + init->ENHANCED_INST_L = (qspi_msg->instruction.qspi_lines == 1) + ? XSPI_ENH_CTRL0_INST_L_8_LINE + : XSPI_ENH_CTRL0_INST_L_0_LINE; + init->ENHANCED_ADDR_L = xspi_get_addrlen(qspi_msg->address.size); + init->ENHANCED_WAITCYCLES = ((uint32_t)qspi_msg->dummy_cycles & 0x1FU) << 11U; + init->ENHANCED_CLKSTREN = 0; + + if (tmod != XSPI_CTRL0_TMOD_TX_AND_RX) + { + /* RX/TX-only enhanced mode: enable clock stretch and set RX sample delay = BAUD/2 + * (mirrors official XSPI_QUAD demo) */ + init->ENHANCED_CLKSTREN = XSPI_ENH_CTRL0_CLKSTREN; + init->SDCN = init->CLKDIV >> 1; /* RX_DELAY = BAUD/2, BAUD = CLKDIV << 1 */ + } +} + +static rt_err_t xspi_qspi_transmit(struct n32_xspi *xspi_drv, struct rt_qspi_message *qspi_msg, uint32_t timeout) +{ + uint8_t *buf = (uint8_t *)qspi_msg->parent.send_buf; + uint32_t len = qspi_msg->parent.length, i = 0; + + xspi_config_enhanced(xspi_drv, qspi_msg, XSPI_CTRL0_TMOD_TX_ONLY); + + XSPI_Cmd(DISABLE); + XSPI_Init(&xspi_drv->InitStructure); + XSPI_Cmd(ENABLE); + + XSPI_SendData(qspi_msg->instruction.content); + XSPI_SendData(qspi_msg->address.content); + + for (i = 0; i < len; i++) + { + if (xspi_wait_flag(XSPI_STS_TXFNF, timeout) != RT_EOK) + { + XSPI_Cmd(DISABLE); + return -RT_ETIMEOUT; + } + + XSPI_SendData(buf[i]); + } + + if (xspi_wait_tx_complete(timeout) != RT_EOK) + { + XSPI_Cmd(DISABLE); + return -RT_ETIMEOUT; + } + XSPI_Cmd(DISABLE); + return RT_EOK; +} + +static rt_err_t xspi_qspi_receive(struct n32_xspi *xspi_drv, struct rt_qspi_message *qspi_msg, uint32_t timeout) +{ + uint8_t *buf = (uint8_t *)qspi_msg->parent.recv_buf; + uint32_t len = qspi_msg->parent.length, i = 0; + + xspi_config_enhanced(xspi_drv, qspi_msg, XSPI_CTRL0_TMOD_RX_ONLY); + + XSPI_Cmd(DISABLE); + XSPI_Init(&xspi_drv->InitStructure); + XSPI_ClrFifo(); + XSPI_Cmd(ENABLE); + + XSPI_SendData(qspi_msg->instruction.content); + XSPI_SendData(qspi_msg->address.content); + + for (i = 0; i < len; i++) + { + if (xspi_wait_flag(XSPI_STS_RXFNE, timeout) != RT_EOK) + { + XSPI_Cmd(DISABLE); + return -RT_ETIMEOUT; + } + buf[i] = (uint8_t)XSPI_ReceiveData(); + } + + if (xspi_wait_tx_complete(timeout) != RT_EOK) + { + XSPI_Cmd(DISABLE); + return -RT_ETIMEOUT; + } + XSPI_Cmd(DISABLE); + return RT_EOK; +} + +static rt_err_t xspi_configure(struct rt_spi_device *device, struct rt_spi_configuration *configuration) +{ + struct n32_xspi *xspi_drv = device->bus->parent.user_data; + struct rt_qspi_device *qspi_dev = (struct rt_qspi_device *)device; + + xspi_drv->qspi_cfg = &qspi_dev->config; + xspi_drv->cfg = configuration; + + /* set scpol/scph value */ + switch (xspi_drv->qspi_cfg->parent.mode & 0x03) + { + case RT_SPI_MODE_0: + xspi_drv->InitStructure.SCPH = XSPI_CTRL0_SCPH_FIRST_EDGE; + xspi_drv->InitStructure.SCPOL = XSPI_CTRL0_SCPOL_LOW; + break; + case RT_SPI_MODE_1: + xspi_drv->InitStructure.SCPH = XSPI_CTRL0_SCPH_SECOND_EDGE; + xspi_drv->InitStructure.SCPOL = XSPI_CTRL0_SCPOL_LOW; + break; + case RT_SPI_MODE_2: + xspi_drv->InitStructure.SCPH = XSPI_CTRL0_SCPH_FIRST_EDGE; + xspi_drv->InitStructure.SCPOL = XSPI_CTRL0_SCPOL_HIGH; + break; + case RT_SPI_MODE_3: + default: + xspi_drv->InitStructure.SCPH = XSPI_CTRL0_SCPH_SECOND_EDGE; + xspi_drv->InitStructure.SCPOL = XSPI_CTRL0_SCPOL_HIGH; + break; + } + + /* data line width -> SPIFRF */ + xspi_drv->InitStructure.SPIFRF = xspi_get_spifrf(xspi_drv->qspi_cfg->qspi_dl_width); + + /* data frame size -> DFS */ + if (xspi_drv->qspi_cfg->parent.data_width == 16) + { + xspi_drv->InitStructure.DFS = XSPI_CTRL0_DFS_16_BIT; + } + else if (xspi_drv->qspi_cfg->parent.data_width == 32) + { + xspi_drv->InitStructure.DFS = XSPI_CTRL0_DFS_32_BIT; + } + else + { + xspi_drv->InitStructure.DFS = XSPI_CTRL0_DFS_8_BIT; + } + + return RT_EOK; +} + +#endif /* !SOC_SERIES_N32H47x_48x */ + static rt_ssize_t xspixfer(struct rt_spi_device *device, struct rt_spi_message *message) { RT_ASSERT(device != RT_NULL && device->bus != RT_NULL && message != RT_NULL); @@ -680,6 +1197,7 @@ static const struct rt_spi_ops n32_xspi_ops = { }; +#if defined(SOC_SERIES_N32H7xx) /* XSPI XIP */ static rt_err_t xspi_control(rt_device_t dev, int cmd, void *args) { @@ -786,6 +1304,8 @@ static rt_err_t xspi_control(rt_device_t dev, int cmd, void *args) return -RT_EINVAL; } } +#endif /* SOC_SERIES_N32H7xx */ + /* ---- bus register + device attach ---- */ static int n32_xspi_register_bus(struct n32_xspi *xspi_drv, const char *name) @@ -811,6 +1331,19 @@ rt_err_t rt_hw_xspi_device_attach(const char *bus_name, const char *device_name, if (cfg != RT_NULL) { +#if defined(SOC_SERIES_N32H47x_48x) + _n32_xspi.InitStructure.MST = cfg->role; + _n32_xspi.InitStructure.SSTE = cfg->nss_toggle; + _n32_xspi.InitStructure.DFS = cfg->data_frame_size; + _n32_xspi.InitStructure.SCPH = cfg->scph; + _n32_xspi.InitStructure.SCPOL = cfg->scpol; + _n32_xspi.InitStructure.TMOD = cfg->transfer_mode; + _n32_xspi.InitStructure.CLKDIV = cfg->baudr << 1; /* BAUD = baudr << 1, see official XSPI_QUAD demo */ + _n32_xspi.InitStructure.SPIFRF = cfg->frame_format; + _n32_xspi.InitStructure.SEN = cfg->slave_sel; + _n32_xspi.InitStructure.SES = cfg->rxd_sampling_edge; + _n32_xspi.InitStructure.SDCN = cfg->rxd_sample_delay; +#else _n32_xspi.InitStructure.Role = cfg->role; _n32_xspi.InitStructure.NssToggle = cfg->nss_toggle; @@ -834,6 +1367,7 @@ rt_err_t rt_hw_xspi_device_attach(const char *bus_name, const char *device_name, _n32_xspi.EnhInitStructure.WaitCycles = cfg->Enhance_WaitCycles; _n32_xspi.EnhInitStructure.DDREable = cfg->Enhance_DDR; } +#endif } qspi_device = (struct rt_qspi_device *)rt_malloc(sizeof(struct rt_qspi_device)); @@ -856,7 +1390,9 @@ rt_err_t rt_hw_xspi_device_attach(const char *bus_name, const char *device_name, return result; } +#if defined(SOC_SERIES_N32H7xx) qspi_device->parent.parent.control = xspi_control; +#endif return result; } @@ -866,20 +1402,31 @@ rt_err_t rt_hw_xspi_device_attach(const char *bus_name, const char *device_name, static int rt_hw_xspi_bus_init(void) { -#if defined(SOC_SERIES_N32H7xx) +#if defined(SOC_SERIES_N32H47x_48x) + /* enable XSPI clock and release reset (single global XSPI instance) */ + RCC_EnableAHBPeriphClk(RCC_AHB_PERIPHEN_XSPI, ENABLE); + RCC_EnableAHBPeriphReset(RCC_AHB_PERIPHRST_XSPI); + + _n32_xspi.bus_name = "qspi"; +#elif defined(SOC_SERIES_N32H49x) + /* enable XSPI clock and release reset */ + RCC_EnableAHBPeriphClk(RCC_AHB_PERIPHEN_XSPI, ENABLE); + RCC_EnableAHBPeriphReset(RCC_AHB_PERIPHRST_XSPI); + + _n32_xspi.xSPIx = XSPI; + _n32_xspi.bus_name = "qspi"; +#else RCC_EnableAHB5PeriphClk2(RCC_AHB5_PERIPHEN_PWR, ENABLE); RCC_EnableAXIPeriphReset4(RCC_AXI_PERIPHRST_XSPI2); RCC_EnableAXIPeriphClk4(RCC_AXI_PERIPHEN_M7_XSPI2, ENABLE); -#endif - _n32_xspi.xSPIx = xSPI2; _n32_xspi.bus_name = "qspi2"; - +#endif rt_completion_init(&_n32_xspi.cpt); - return n32_xspi_register_bus(&_n32_xspi, "qspi2"); + return n32_xspi_register_bus(&_n32_xspi, _n32_xspi.bus_name); } INIT_BOARD_EXPORT(rt_hw_xspi_bus_init); diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_qspi.h b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_qspi.h index 113e6141374a..d44f3940be97 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_qspi.h +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_qspi.h @@ -12,12 +12,19 @@ #include #include "drv_common.h" +#if defined(SOC_SERIES_N32H47x_48x) +#include "n32h47x_48x_xspi.h" +#elif defined(SOC_SERIES_N32H49x) +#include "n32h49x_xspi_v2.h" +#else #include "n32h7xx_xspi_v2.h" +#endif #ifdef __cplusplus extern "C" { #endif +#if defined(SOC_SERIES_N32H7xx) struct mdma_config { MDMA_ChNumType mdma_channel; @@ -26,10 +33,12 @@ struct mdma_config uint32_t dmamux_request; uint32_t hs_interface; }; +#endif #define XSPI_USING_RX_DMA_FLAG (1 << 0) #define XSPI_USING_TX_DMA_FLAG (1 << 1) +#if defined(SOC_SERIES_N32H7xx) typedef enum { XSPI_Tx_Rx = 0U, @@ -38,17 +47,24 @@ typedef enum XSPI_Idle, } XSPI_Work_Direct_t; +#endif struct n32_xspi { +#if defined(SOC_SERIES_N32H47x_48x) + XSPI_InitType InitStructure; /* v1: single combined init struct, global XSPI instance */ +#else XSPI_Module *xSPIx; - char *bus_name; XSPI_InitType InitStructure; XSPI_EnhancedInitType EnhInitStructure; - XSPI_XIPInitType XIPInitStructure; +#endif + char *bus_name; struct rt_qspi_configuration *qspi_cfg; struct rt_spi_configuration *cfg; + +#if defined(SOC_SERIES_N32H7xx) + XSPI_XIPInitType XIPInitStructure; XSPI_Work_Direct_t Direct; rt_uint8_t xspi_dma_flag; @@ -60,8 +76,10 @@ struct n32_xspi MDMA_ChInitType RX_DMA_ChInitStr; } dma; - rt_uint8_t slave_sel; rt_uint8_t xip_enabled; +#endif + + rt_uint8_t slave_sel; struct rt_completion cpt; }; @@ -89,6 +107,7 @@ struct n32_xspi_config uint32_t Enhance_DDR; }; +#if defined(SOC_SERIES_N32H7xx) struct xspi_xip_config { uint32_t XipInstructLen; @@ -111,6 +130,7 @@ struct xspi_xip_config #define XSPI_CTRL_ENTER_XIP 0x01 #define XSPI_CTRL_EXIT_XIP 0x02 #define XSPI_CTRL_GET_XIP_ADDR 0x03 +#endif rt_err_t rt_hw_xspi_device_attach(const char *bus_name, const char *device_name, rt_uint8_t data_line_width, diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_sdram.c b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_sdram.c index 34e76c09bd6c..eeb30afc19ea 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_sdram.c +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_sdram.c @@ -13,8 +13,14 @@ #ifdef BSP_USING_SDRAM #include + +#if defined(SOC_SERIES_N32H7xx) #include #include +#elif defined(SOC_SERIES_N32H49x) +#include +#include +#endif #define DRV_DEBUG #define LOG_TAG "drv.sdram" @@ -115,6 +121,7 @@ static SDRAM_DeviceType sdram_device; * SDRAM Load Mode Register type (from N32 SDK bsp_sdram.h) * Represents the value sent to the SDRAM chip via LOADMODE command. *---------------------------------------------------------------------------*/ +#if defined(SOC_SERIES_N32H7xx) typedef union { uint32_t cmd; @@ -128,6 +135,7 @@ typedef union uint32_t Reserved : 22; } Bits; } SDRAM_LoadModeRegisterType; +#endif /* Mode register field values (from N32 SDK bsp_sdram.h) */ #define LOADMODE_BURSTLEN_1 0 @@ -147,9 +155,16 @@ typedef union *---------------------------------------------------------------------------*/ rt_weak void rt_hw_sdram_clock_init(void) { +#if defined(SOC_SERIES_N32H7xx) /* Configure SDRAM delay chain (board-specific, default 0.2ns step) */ RCC_ConfigSDRAMDelay(RCC_SDRAM_DELAY_0_2NS); RCC_EnableSDRAMDelayChain(ENABLE); +#elif defined(SOC_SERIES_N32H49x) + /* Configure SDRAM clock */ + RCC_EnableAHBPeriphClk(RCC_AHB_PERIPHEN_SDRAM, ENABLE); + /* Configure SDRAM delay chain (board-specific, default 0.5) */ + SDRAM_ConfigSampleDelay(SDRAM_DELAY_0_5_PERIOD); +#endif } /*---------------------------------------------------------------------------- diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_spi.c b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_spi.c index 5188b2639a85..893436a3283d 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_spi.c +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_spi.c @@ -84,6 +84,306 @@ static struct n32_spi_config spi_config[] = { static struct n32_spi spi_bus_obj[sizeof(spi_config) / sizeof(spi_config[0])] = { 0 }; +#if defined(SOC_SERIES_N32H7xx) +/* Static 0xFF fill source for 4-wire full-duplex MASTER receive-only DMA. + * A recv-only master still has to clock the bus, so the TX DMA pushes this + * fill pattern onto MOSI while the RX DMA captures into the caller's buffer. + * Sized for the widest single read of the buffer: one DMA block holds at + * most 4095 items, and at 16-bit width that is 4095 * 2 = 8190B (8-bit: + * 4095B). LLI nodes never read further because n32_spi_dma_arm re-points + * every node's SrcAddr at the base, so each node re-reads at most one block + * from the start. Filled once in rt_hw_spi_bus_init and read-only afterwards + * -> one shared instance for every SPI bus is safe. */ +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 6010050) +#define N32_SPI_DUMMY_ALIGN32 __align(32) +#else +#define N32_SPI_DUMMY_ALIGN32 __attribute__((aligned(32))) +#endif +N32_SPI_DUMMY_ALIGN32 static rt_uint8_t spi_fd_rxonly_dummy[8192]; +#endif /* SOC_SERIES_N32H7xx */ + + +#if defined(SOC_SERIES_N32H7xx) +/* Build an LLI chain for a 'size'-element transfer: the memory side advances + * SPI_DMA_BLOCK_MAX elements per node while the peripheral (DAT) address + * stays fixed. Only the last node has IntEn=1, so the whole chain raises a + * single TC interrupt. */ +static rt_uint16_t n32_spi_lli_build(DMA_LinkListItemType *lli, const DMA_ChInitType *ch, + rt_uint32_t periph_addr, rt_uint32_t mem_addr, + rt_bool_t mem_is_src, rt_uint16_t size) +{ + rt_uint16_t n = (rt_uint16_t)((size + SPI_DMA_BLOCK_MAX - 1U) / SPI_DMA_BLOCK_MAX); + rt_uint16_t i; + rt_uint16_t rest = size; + + for (i = 0; i < n; i++) + { + DMA_LinkListItemType *node = &lli[i]; + rt_uint16_t blk = (rest > SPI_DMA_BLOCK_MAX) ? SPI_DMA_BLOCK_MAX : rest; + rt_bool_t has_next = (i + 1U < n) ? RT_TRUE : RT_FALSE; + + /* BlkTfrSize counts data items, so the memory side advances one block + * worth of ELEMENTS per node: the byte stride is SPI_DMA_BLOCK_MAX + * items times the memory-side transfer width. That width field is the + * DMA transfer width code (0 = 8-bit, 1 = 16-bit, 2 = 32-bit), i.e. + * 1 << width bytes -- the very value programmed into the node below, + * so stride and width cannot disagree. Advancing by SPI_DMA_BLOCK_MAX + * BYTES instead overlaps the nodes by half a word at 16-bit width. */ + rt_uint32_t mem_stride = (rt_uint32_t)SPI_DMA_BLOCK_MAX * + (1UL << (mem_is_src ? ch->SrcTfrWidth : ch->DstTfrWidth)); + node->SrcAddr = mem_is_src ? (mem_addr + (rt_uint32_t)i * mem_stride) : periph_addr; + node->DstAddr = mem_is_src ? periph_addr : (mem_addr + (rt_uint32_t)i * mem_stride); + node->pNext = has_next ? &lli[i + 1U] : RT_NULL; + node->IntEn = has_next ? 0U : 1U; + node->DstTfrWidth = ch->DstTfrWidth; + node->SrcTfrWidth = ch->SrcTfrWidth; + node->DstAddrCountMode = ch->DstAddrCountMode; + node->SrcAddrCountMode = ch->SrcAddrCountMode; + node->DstBurstLen = ch->DstBurstLen; + node->SrcBurstLen = ch->SrcBurstLen; + node->SrcGatherEn = ch->SrcGatherEn; + node->DstScatterEn = ch->DstScatterEn; + node->TfrTypeFlowCtrl = ch->TfrTypeFlowCtrl; + node->DstLinkedListEn = has_next ? 1U : 0U; + node->SrcLinkedListEn = has_next ? 1U : 0U; + node->BlkTfrSize = blk; + rest -= blk; + } + return n; +} + +/* Arm one RX/TX DMA channel: + * - size <= 4095 : single-block transfer (original behaviour) + * - size > 4095 : seamless LLI-chain transfer (no software gap + * between blocks, so a clock-driven slave never + * drops bytes) + * - active == RT_FALSE : arm the channel as an empty block (this direction + * does not take part in this transfer) */ +static rt_err_t n32_spi_dma_arm(struct n32_spi *spi_drv, rt_bool_t is_rx, + rt_bool_t active, uint8_t *buf, rt_uint16_t size) +{ + struct dma_config *cfg = is_rx ? spi_drv->config->dma_rx : spi_drv->config->dma_tx; + DMA_ChInitType *ch = is_rx ? &spi_drv->dma.RX_DMA_ChInitStr : &spi_drv->dma.TX_DMA_ChInitStr; + DMA_LinkListItemType *lli = is_rx ? spi_drv->dma.lli_rx : spi_drv->dma.lli_tx; + + DMA_ChannelCmd(cfg->Instance, cfg->dma_channel, DISABLE); + + if (!DMA_ControllerIsEnabled(cfg->Instance)) + { + DMA_ControllerCmd(cfg->Instance, ENABLE); + } + DMA_ControllerCmd(cfg->Instance, ENABLE); + + if (!active) + { + /* Empty arm: this direction does not take part in this transfer */ + ch->IntEn = 0U; + if (is_rx) + { + ch->DstAddr = (uint32_t)RT_NULL; + } + else + { + ch->SrcAddr = (uint32_t)RT_NULL; + } + ch->BlkTfrSize = 0U; + ch->pLinkListItem = RT_NULL; + ch->TfrType = DMA_CH_TRANSFER_TYPE_SINGLE_BLOCK; + if (DMA_ChannelInit(cfg->Instance, ch, cfg->dma_channel) != 0U) + { + LOG_E(is_rx ? "Master DMA Rx channel initialization failed." + : "Master DMA Tx channel initialization failed."); + return -RT_ERROR; + } + return RT_EOK; + } + + /* Active direction: load the buffer address and block size */ + if (is_rx) + { + /* Unconditionally drain any stale DAT/RNE byte before arming: once + * RDMAEN is set, the first DMA transfer would swallow a leftover byte + * as rx[0] (slave first-byte right-shift plus trailing RNE=1). + * Cross-case residue is exactly this source. */ + SPI_I2S_ReceiveData(spi_drv->config->SPIx); + SPI_I2S_GetStatus(spi_drv->config->SPIx, SPI_I2S_OVER_FLAG); + + ch->DstAddr = (uint32_t)buf; + } + else + { + ch->SrcAddr = (uint32_t)buf; + } + + if (size > SPI_DMA_BLOCK_MAX) + { + /* Seamless chain transfer: the whole message is one DMA task, so + * there is no reload gap between blocks */ + rt_uint16_t nodes; + + nodes = n32_spi_lli_build(lli, ch, (uint32_t)&spi_drv->config->SPIx->DAT, + (uint32_t)buf, !is_rx, size); + if ((!is_rx) && spi_drv->fd_rxonly_dummy_tx) + { + /* FD master recv-only: the TX source is the 0xFF fill buffer. + * Re-point every LLI node at its base - without this the chain + * steps 4095B per node and would walk past the fill buffer. + * Each node then re-reads its own block (<= 4095 items, <= the + * 8192B fill) from the base, which is exactly the constant 0xFF + * stream this path must clock. */ + rt_uint16_t k; + + for (k = 0U; k < nodes; k++) + { + lli[k].SrcAddr = (uint32_t)buf; + } + } + ch->pLinkListItem = &lli[0]; + ch->TfrType = DMA_CH_TRANSFER_TYPE_MULTI_BLOCK_SRCADR_LINKED_DSTADR_LINKED; + ch->BlkTfrSize = SPI_DMA_BLOCK_MAX; /* channel's first block matches lli[0] */ + } + else + { + ch->pLinkListItem = RT_NULL; + ch->TfrType = DMA_CH_TRANSFER_TYPE_SINGLE_BLOCK; + ch->BlkTfrSize = size; + } + ch->IntEn = 1U; + + if (DMA_ChannelInit(cfg->Instance, ch, cfg->dma_channel) == 0U) + { + /* Clear stale TC/error events first: a TC flag left by the previous + * case would fire a bogus completion interrupt as soon as the channel + * is enabled, completing the slave DMA before the real data has + * arrived (BUSY stuck; mode2 symptom). */ + DMA_ClearChannelEventStatus(cfg->Instance, cfg->dma_channel, DMA_CH_EVENT_TRANSFER_COMPLETE); + DMA_ClearChannelEventStatus(cfg->Instance, cfg->dma_channel, DMA_CH_EVENT_ERROR); + + /* Enable transaction complete interrupt event */ + DMA_ChannelEventCmd(cfg->Instance, cfg->dma_channel, DMA_CH_EVENT_TRANSFER_COMPLETE, ENABLE); + + /* Order matters: set the SPI-side RDMAEN/TDMAEN first, then enable the + * DMA channel. The reverse order leaves a window in which a first byte + * received by a clock-driven slave is not moved out by any DMA request + * and stays in DAT, shifting all following real data (slave rx + * right-shift, first byte lost). A slave is armed before the master + * clock starts, so this order is safe. */ + SPI_I2S_EnableDma(spi_drv->config->SPIx, + is_rx ? SPI_I2S_DMA_RX : SPI_I2S_DMA_TX, ENABLE); + + /* Enable the specified DMA channel */ + DMA_ChannelCmd(cfg->Instance, cfg->dma_channel, ENABLE); + } + else + { + LOG_E("DMA channel init fail: %s ch%u size=%u (leaving RDMAEN off)", + is_rx ? "RX" : "TX", cfg->dma_channel, size); + return -RT_ERROR; + } + return RT_EOK; +} +#endif /* SOC_SERIES_N32H7xx */ + +/* True when this device is a mode2 (CPOL=1/CPHA=0) full-duplex master. Such + * transfers use the "engine always-on + warm-engine level-triggered DMA" + * scheme (see the SPI_DMA_TransmitReceive notes, the spixfer message tail and + * the n32_spi_init configure pre-enable). On the always-selected 4-wire + * cross-wiring used by the test board (SPI1 master <-> SPI2 slave), a mode2 + * slave counts the SCK LOW->HIGH edge of the master SPIEN 0->1 into its bit + * phase and the whole frame grid shifts (the slave SPIEN 0->1 by itself also + * pulls SCK low; no GPIO pull can fight an ms-scale gap - the vendor demo + * only passes because its two enables are ns apart, a timing coincidence + * disproved by a log-delay experiment). Keeping the master engine on makes + * the slave arm against an SCK line already clamped idle-high by the master's + * push-pull driver, and message start no longer creates a 0->1 edge. */ +static rt_bool_t n32_spi_mode2_fd_master(struct n32_spi *spi_drv) +{ + return (spi_drv->SPI_InitStructure.SpiMode == SPI_MODE_MASTER) && + (spi_drv->SPI_InitStructure.DataDirection == SPI_DIR_DOUBLELINE_FULLDUPLEX) && + (spi_drv->SPI_InitStructure.CLKPOL == SPI_CLKPOL_HIGH) && + (spi_drv->SPI_InitStructure.CLKPHA == SPI_CLKPHA_FIRST_EDGE); +} + +/* Same always-on treatment for a 3-wire (single-line) mode2 master. Its + * per-message cold start (SPIEN 0->1) drops the SCK pad low first and then + * rises into the already-enabled mode2 slave, so the slave bit counter + * gains one spurious edge and the whole frame grid shifts by one bit + * (rx[j] = (b[j-1] & 1) << 7 | b[j] >> 1; modes 0/1/3 sample after the + * data lands and are immune). Keeping this engine on clamps SCK idle-high + * exactly like the FD variant. Mutually exclusive with it: FD requires + * DOUBLELINE_FULLDUPLEX, this requires SINGLELINE, so a bus never matches + * both. */ +static rt_bool_t n32_spi_mode2_3w_master(struct n32_spi *spi_drv) +{ + return (spi_drv->SPI_InitStructure.SpiMode == SPI_MODE_MASTER) && + (spi_drv->SPI_InitStructure.DataDirection == SPI_DIR_SINGLELINE_TX || + spi_drv->SPI_InitStructure.DataDirection == SPI_DIR_SINGLELINE_RX) && + (spi_drv->SPI_InitStructure.CLKPOL == SPI_CLKPOL_HIGH) && + (spi_drv->SPI_InitStructure.CLKPHA == SPI_CLKPHA_FIRST_EDGE); +} + +/* Warm (SPIEN=1) BIDI direction flip for the always-on mode2 3-wire master. + * SPI_Init cannot do a TX->RX flip on a live engine: the vendor CLR_MASK + * (0xF9F7, n32h7xx_spi.c) preserves bit14 (BIDIOE) under its &=, so Init + * with SINGLELINE_RX silently leaves the engine in TX and a BA leg then + * waits for an RX-DMA interrupt that can never fire. Toggle only the + * BIDI output-enable bit instead, keeping SPIEN=1 so SCK never drops: + * bit14 set = TX (master drives, line clamped idle-high, nothing clocks + * until DAT is written); bit14 clear = RX (the mode2 receiver free-runs + * from that moment until the direction leaves RX, so the RX DMA must be + * armed before the clear). */ +static void n32_spi_mode2_3w_set_dir(struct n32_spi *spi_drv, uint16_t dir) +{ + if (dir == SPI_DIR_SINGLELINE_TX) + { + spi_drv->config->SPIx->CTRL1 |= SPI_BIDIRECTION_TX; + } + else + { + spi_drv->config->SPIx->CTRL1 &= SPI_BIDIRECTION_RX; + } + spi_drv->SPI_InitStructure.DataDirection = dir; +} + +/* Error/abort path for the always-on mode2 3-wire master: a failed or + * aborted receive must not leave the RX engine free-running (it clocks + * SCK continuously until the direction leaves RX), so flip back to the + * TX-idle rest posture and clear the stale byte. No-op for every other + * role/direction. */ +static void n32_spi_mode2_3w_rx_abort(struct n32_spi *spi_drv) +{ + if (n32_spi_mode2_3w_master(spi_drv)) + { + n32_spi_mode2_3w_set_dir(spi_drv, SPI_DIR_SINGLELINE_TX); + SPI_I2S_ReceiveData(spi_drv->config->SPIx); + SPI_I2S_GetStatus(spi_drv->config->SPIx, SPI_I2S_OVER_FLAG); + } +} + +/* Switch the 3-wire single-line data direction. A mode2 3-wire master keeps + * its engine on, so its flip must not drop SPIEN (that would pull SCK low + * and re-introduce the spurious-edge shift on the enabled slave): its warm + * flip is a direct BIDIOE toggle (n32_spi_mode2_3w_set_dir - SPI_Init + * cannot clear BIDIOE warm, see above). Everyone else keeps the legacy cold + * cycle (disable + peripheral DeInit + Init), whose reset also re-zeros the + * slave bit counter. */ +static void n32_spi_singleline_dir(struct n32_spi *spi_drv, uint16_t dir) +{ + if (n32_spi_mode2_3w_master(spi_drv)) + { + n32_spi_mode2_3w_set_dir(spi_drv, dir); + } + else + { + /* Disable the sFLASH_SPI */ + SPI_Enable(spi_drv->config->SPIx, DISABLE); + /* DeInitializes the sFLASH_SPI */ + SPI_I2S_DeInit(spi_drv->config->SPIx); + spi_drv->SPI_InitStructure.DataDirection = dir; + SPI_Init(spi_drv->config->SPIx, &spi_drv->SPI_InitStructure); + } +} + static rt_err_t SPI_DMA_TransmitReceive(struct n32_spi *spi_drv, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size) { RT_ASSERT(spi_drv != RT_NULL); @@ -95,32 +395,119 @@ static rt_err_t SPI_DMA_TransmitReceive(struct n32_spi *spi_drv, uint8_t *pTxDat return -RT_ERROR; } + /* Clear any stale RX residue BEFORE arming RX/TX DMA: once RDMAEN is set + * the first DMA transfer would swallow a leftover byte in DAT as rx[0] + * (slave-side 1-byte right-shift + trailing RNE=1 symptom). For a slave + * the master has not started clocking yet, so no real first byte can be + * lost here. */ + SPI_I2S_ReceiveData(spi_drv->config->SPIx); + SPI_I2S_GetStatus(spi_drv->config->SPIx, SPI_I2S_OVER_FLAG); + + /* FD engine cold-start scheme (non-mode2 path): each transfer disables + * first, then arms, and enables at the end. While SPIEN=0 the TE=0 keeps + * the TX DMA request away; TE becomes 1 only after the SPIEN 0->1, so the + * request looks like a "TE 0->1 edge trigger" (warm-engine probes proved + * the request is actually level-triggered; see the mode2 notes below). + * The 3-wire path uses the same scheme in SPI_DMA_Transmit (verified). + * + * But SPIEN 1->0 pulls the SCK pad low on CPOL=1 (idle-high) configs: the + * slave bit counter counts falling edges, so a stray edge shifts the + * whole round's frame grid by k bits; and SPIEN 0->1 does not clear the + * slave bit counter (measured: mode3 rounds>=1 shift the whole frame by k + * bits - slave receives (prev.byte bit0<<7)|(this.byte>>1), master + * receives (this.byte<<1)|(next.byte>>7); byte-level deterministic. + * Modes 0/1 with CPOL=0 idle-low have no transition and are immune). + * Mitigation (see the marked places below): + * a) Slave: run a peripheral DeInit+Init here (the same one configure + * uses) - only a peripheral reset clears the bit counter, so the + * slave phase is zeroed before frame 0 of every round; + * b) Master: never drop the line at the top of an arm - the disable + * always happens at the message tail (the spixfer bus-release point), + * making the disable here a no-op for master and slave; the tail drop + * falls into the slave's own finish period of this round and is + * cleared by the slave's next arm reset. + * + * mode2 (CPOL=1, CPHA=0) extra problem: the scheme above cold-starts the + * master SPIEN 0->1 after the slave is enabled, and that 0->1 SCK + * LOW->HIGH rising edge is counted by a mode2 slave -> whole-frame k-bit + * offset from the first frame (pad-bit fingerprints b2 67 9d 52..., + * matched byte-for-byte on a full 4096B frame). A mode2 FD master + * therefore never cold-starts: the engine is pre-enabled at configure and + * kept across messages (not disabled at the spixfer message tail), and no + * disable/enable cycle happens here. On a warm engine the TX DMA request + * is level-triggered: with TE=1 the channel enable alone fires it (after + * RX/TX arm with no CPU write at all, TE goes 1->0, data flows by itself + * and the ISR completes normally; during a cold start SPIEN=0 keeps TE=0, + * so the request only appears after SPIEN 0->1 and looks edge-triggered). + * The original CPU-seed write raced the DMA self-start (the seed written + * into a full DAT was swallowed and the first byte shifted) and has been + * removed. When the engine is (unexpectedly) off, fall back to the + * traditional cold-start cycle as before. */ + /* Warm re-arm: arming a MASTER whose SPIEN is still set is legitimate + * only for (a) the always-on mode2 FD master (see the notes above) and + * (b) a non-first chunk of a 4-wire full-duplex message -- spixfer marks + * (b) with fd_chunk_cont because it keeps the engine on across chunk + * boundaries (only the message tail disables). In both cases SPIEN must + * NOT be dropped here: the 1->0 pulls the SCK pad low on CPOL=1 + * (idle-high) and the 0->1 re-enable raises it again -- both edges land + * on the enabled mid-arm slave, whose bit counter counts them and shifts + * its TX reply (a chunked 8192-element mode3 message corrupts from byte + * 4096 onward). On the warm engine the TX + * DMA request is level-triggered (note above): the channel enable alone + * fires it, so no SPI enable is needed either. Every other arm -- slave + * (phase reset below) or a master whose engine is off -- keeps the + * traditional cold-start cycle. */ + rt_bool_t engine_warm = RT_FALSE; + if (spi_drv->fd_chunk_cont && + (spi_drv->SPI_InitStructure.SpiMode != SPI_MODE_SLAVE) && + ((spi_drv->config->SPIx->CTRL2 & SPI_CTRL2_SPIEN) != 0U)) + { + engine_warm = RT_TRUE; + } + else if (n32_spi_mode2_fd_master(spi_drv) && + (Size > 1U) && + ((spi_drv->config->SPIx->CTRL2 & SPI_CTRL2_SPIEN) != 0U)) + { + engine_warm = RT_TRUE; + } + else + { + SPI_Enable(spi_drv->config->SPIx, DISABLE); + } + if (spi_drv->SPI_InitStructure.SpiMode == SPI_MODE_SLAVE) + { + SPI_I2S_DeInit(spi_drv->config->SPIx); + SPI_Init(spi_drv->config->SPIx, &spi_drv->SPI_InitStructure); + } + if (spi_drv->dma.DMA_Rx_Init == RT_TRUE) { #if defined(SOC_SERIES_N32H7xx) - /* SPI RX DMA Receive Data */ - DMA_ChannelCmd(spi_drv->config->dma_rx->Instance, spi_drv->config->dma_rx->dma_channel, DISABLE); - - if (!DMA_ControllerIsEnabled(spi_drv->config->dma_rx->Instance)) + /* SPI RX DMA receive (single block or seamless LLI chain) */ + if (n32_spi_dma_arm(spi_drv, RT_TRUE, RT_TRUE, pRxData, Size) != RT_EOK) { - DMA_ControllerCmd(spi_drv->config->dma_rx->Instance, ENABLE); + SPI_Enable(spi_drv->config->SPIx, ENABLE); + return -RT_ERROR; } +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + /* SPI RX DMA Receive Data for H49X */ + DMA_EnableChannel(spi_drv->config->dma_rx->DMAChx, DISABLE); - spi_drv->dma.RX_DMA_ChInitStr.IntEn = 1U; - spi_drv->dma.RX_DMA_ChInitStr.DstAddr = (uint32_t)pRxData; - spi_drv->dma.RX_DMA_ChInitStr.BlkTfrSize = Size; + /* Configure DMA request remapping */ + DMA_RequestRemap(spi_drv->config->dma_rx->request, spi_drv->config->dma_rx->DMAChx, ENABLE); - DMA_ControllerCmd(spi_drv->config->dma_rx->Instance, ENABLE); - if (DMA_ChannelInit(spi_drv->config->dma_rx->Instance, &spi_drv->dma.RX_DMA_ChInitStr, spi_drv->config->dma_rx->dma_channel) == 0U) - { - /* Enable transaction complete interrupt event */ - DMA_ChannelEventCmd(spi_drv->config->dma_rx->Instance, spi_drv->config->dma_rx->dma_channel, DMA_CH_EVENT_TRANSFER_COMPLETE, ENABLE); + spi_drv->dma.RX_DMA_ChInitStr.MemAddr = (uint32_t)pRxData; + spi_drv->dma.RX_DMA_ChInitStr.BufSize = Size; - /* Enable the specified DMA channel */ - DMA_ChannelCmd(spi_drv->config->dma_rx->Instance, spi_drv->config->dma_rx->dma_channel, ENABLE); + DMA_Init(spi_drv->config->dma_rx->DMAChx, &spi_drv->dma.RX_DMA_ChInitStr); - SPI_I2S_EnableDma(spi_drv->config->SPIx, SPI_I2S_DMA_RX, ENABLE); - } + /* Enable transfer complete interrupt */ + DMA_ConfigInt(spi_drv->config->dma_rx->DMAChx, DMA_INT_TXC, ENABLE); + + /* Enable the specified DMA channel */ + DMA_EnableChannel(spi_drv->config->dma_rx->DMAChx, ENABLE); + + SPI_I2S_EnableDma(spi_drv->config->SPIx, SPI_I2S_DMA_RX, ENABLE); #endif } else @@ -132,50 +519,54 @@ static rt_err_t SPI_DMA_TransmitReceive(struct n32_spi *spi_drv, uint8_t *pTxDat if (spi_drv->dma.DMA_Tx_Init == RT_TRUE) { #if defined(SOC_SERIES_N32H7xx) - /* SPI TX DMA Send Data */ - DMA_ChannelCmd(spi_drv->config->dma_tx->Instance, spi_drv->config->dma_tx->dma_channel, DISABLE); - - if (!DMA_ControllerIsEnabled(spi_drv->config->dma_tx->Instance)) + /* SPI TX DMA send (single block or seamless LLI chain). On a warm + * engine (engine_warm) the channel enable level-triggers the transfer + * (see the big note above); the first byte goes out from the source + * start address, no seed needed. */ + if (n32_spi_dma_arm(spi_drv, RT_FALSE, RT_TRUE, pTxData, Size) != RT_EOK) { - DMA_ControllerCmd(spi_drv->config->dma_tx->Instance, ENABLE); + SPI_Enable(spi_drv->config->SPIx, ENABLE); + return -RT_ERROR; } +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + /* SPI TX DMA Send Data for H49X */ + DMA_EnableChannel(spi_drv->config->dma_tx->DMAChx, DISABLE); - spi_drv->dma.TX_DMA_ChInitStr.IntEn = 1U; - spi_drv->dma.TX_DMA_ChInitStr.SrcAddr = (uint32_t)pTxData; - spi_drv->dma.TX_DMA_ChInitStr.BlkTfrSize = Size; + /* Configure DMA request remapping */ + DMA_RequestRemap(spi_drv->config->dma_tx->request, spi_drv->config->dma_tx->DMAChx, ENABLE); - DMA_ControllerCmd(spi_drv->config->dma_tx->Instance, ENABLE); - if (DMA_ChannelInit(spi_drv->config->dma_tx->Instance, &spi_drv->dma.TX_DMA_ChInitStr, spi_drv->config->dma_tx->dma_channel) == 0U) - { - /* Enable transaction complete interrupt event */ - DMA_ChannelEventCmd(spi_drv->config->dma_tx->Instance, spi_drv->config->dma_tx->dma_channel, DMA_CH_EVENT_TRANSFER_COMPLETE, ENABLE); + spi_drv->dma.TX_DMA_ChInitStr.MemAddr = (uint32_t)pTxData; + spi_drv->dma.TX_DMA_ChInitStr.BufSize = Size; - /* Enable the specified DMA channel */ - DMA_ChannelCmd(spi_drv->config->dma_tx->Instance, spi_drv->config->dma_tx->dma_channel, ENABLE); + DMA_Init(spi_drv->config->dma_tx->DMAChx, &spi_drv->dma.TX_DMA_ChInitStr); - SPI_I2S_EnableDma(spi_drv->config->SPIx, SPI_I2S_DMA_TX, ENABLE); - } + /* Enable transfer complete interrupt */ + DMA_ConfigInt(spi_drv->config->dma_tx->DMAChx, DMA_INT_TXC, ENABLE); + + /* Enable the specified DMA channel */ + DMA_EnableChannel(spi_drv->config->dma_tx->DMAChx, ENABLE); + + SPI_I2S_EnableDma(spi_drv->config->SPIx, SPI_I2S_DMA_TX, ENABLE); #endif } else { LOG_E("In full-duplex mode, TX DMA did not complete initialization."); + SPI_Enable(spi_drv->config->SPIx, ENABLE); return -RT_ERROR; } - /* Clear Over Flag */ - SPI_I2S_ReceiveData(spi_drv->config->SPIx); - SPI_I2S_GetStatus(spi_drv->config->SPIx, SPI_I2S_OVER_FLAG); - /* Enable Error Interrupt */ SPI_I2S_EnableInt(spi_drv->config->SPIx, SPI_I2S_INT_ERR, ENABLE); /* Set SPI Direction */ spi_drv->Direct = SPI_Tx_Rx; - /* Check if the SPI is already enabled */ - if ((spi_drv->config->SPIx->CTRL2 & SPI_CTRL2_SPIEN) != SPI_CTRL2_SPIEN) + if (!engine_warm) { + /* Enable 0->1 after arming: the TE rising edge happens only once + * RDMAEN/TDMAEN and both DMA channels are ready, so the first-byte TX + * request can actually go out (see the note above). */ SPI_Enable(spi_drv->config->SPIx, ENABLE); } @@ -189,63 +580,55 @@ static rt_err_t SPI_DMA_Transmit(struct n32_spi *spi_drv, uint8_t *pData, uint16 if (spi_drv->SPI_InitStructure.DataDirection == SPI_DIR_SINGLELINE_RX || spi_drv->SPI_InitStructure.DataDirection == SPI_DIR_SINGLELINE_TX) { - /* Disable the sFLASH_SPI */ - SPI_Enable(spi_drv->config->SPIx, DISABLE); - /* DeInitializes the sFLASH_SPI */ - SPI_I2S_DeInit(spi_drv->config->SPIx); - /* Set the sFLASH_SPI SendOnly*/ - spi_drv->SPI_InitStructure.DataDirection = SPI_DIR_SINGLELINE_TX; - SPI_Init(spi_drv->config->SPIx, &spi_drv->SPI_InitStructure); + n32_spi_singleline_dir(spi_drv, SPI_DIR_SINGLELINE_TX); } if (spi_drv->dma.DMA_Rx_Init == RT_TRUE) { #if defined(SOC_SERIES_N32H7xx) - DMA_ChannelCmd(spi_drv->config->dma_rx->Instance, spi_drv->config->dma_rx->dma_channel, DISABLE); - - if (!DMA_ControllerIsEnabled(spi_drv->config->dma_rx->Instance)) + /* Send mode: the RX channel is armed as an empty block (this direction does not take part) */ + if (n32_spi_dma_arm(spi_drv, RT_TRUE, RT_FALSE, RT_NULL, 0U) != RT_EOK) { - DMA_ControllerCmd(spi_drv->config->dma_rx->Instance, ENABLE); + return -RT_ERROR; } +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) - spi_drv->dma.RX_DMA_ChInitStr.IntEn = 0U; - spi_drv->dma.RX_DMA_ChInitStr.DstAddr = NULL; - spi_drv->dma.RX_DMA_ChInitStr.BlkTfrSize = 0U; + DMA_EnableChannel(spi_drv->config->dma_rx->DMAChx, DISABLE); + spi_drv->dma.RX_DMA_ChInitStr.MemAddr = 0; + spi_drv->dma.RX_DMA_ChInitStr.BufSize = 0; - DMA_ControllerCmd(spi_drv->config->dma_rx->Instance, ENABLE); - if (DMA_ChannelInit(spi_drv->config->dma_rx->Instance, &spi_drv->dma.RX_DMA_ChInitStr, spi_drv->config->dma_rx->dma_channel) != 0U) - { - LOG_E("Master DMA Rx channel initialization failed."); - return -RT_ERROR; - } + DMA_EnableChannel(spi_drv->config->dma_rx->DMAChx, ENABLE); + DMA_Init(spi_drv->config->dma_rx->DMAChx, &spi_drv->dma.RX_DMA_ChInitStr); #endif } if (spi_drv->dma.DMA_Tx_Init == RT_TRUE) { #if defined(SOC_SERIES_N32H7xx) - DMA_ChannelCmd(spi_drv->config->dma_tx->Instance, spi_drv->config->dma_tx->dma_channel, DISABLE); - - if (!DMA_ControllerIsEnabled(spi_drv->config->dma_tx->Instance)) + /* SPI TX DMA send (single block or seamless LLI chain) */ + if (n32_spi_dma_arm(spi_drv, RT_FALSE, RT_TRUE, pData, Size) != RT_EOK) { - DMA_ControllerCmd(spi_drv->config->dma_tx->Instance, ENABLE); + return -RT_ERROR; } +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + /* SPI TX DMA Send Data for H49X */ + DMA_EnableChannel(spi_drv->config->dma_tx->DMAChx, DISABLE); - spi_drv->dma.TX_DMA_ChInitStr.IntEn = 1U; - spi_drv->dma.TX_DMA_ChInitStr.SrcAddr = (uint32_t)pData; - spi_drv->dma.TX_DMA_ChInitStr.BlkTfrSize = Size; + /* Configure DMA request remapping */ + DMA_RequestRemap(spi_drv->config->dma_tx->request, spi_drv->config->dma_tx->DMAChx, ENABLE); - DMA_ControllerCmd(spi_drv->config->dma_tx->Instance, ENABLE); - if (DMA_ChannelInit(spi_drv->config->dma_tx->Instance, &spi_drv->dma.TX_DMA_ChInitStr, spi_drv->config->dma_tx->dma_channel) == 0U) - { - /* Enable transaction complete interrupt event */ - DMA_ChannelEventCmd(spi_drv->config->dma_tx->Instance, spi_drv->config->dma_tx->dma_channel, DMA_CH_EVENT_TRANSFER_COMPLETE, ENABLE); + spi_drv->dma.TX_DMA_ChInitStr.MemAddr = (uint32_t)pData; + spi_drv->dma.TX_DMA_ChInitStr.BufSize = Size; - /* Enable the specified DMA channel */ - DMA_ChannelCmd(spi_drv->config->dma_tx->Instance, spi_drv->config->dma_tx->dma_channel, ENABLE); + DMA_Init(spi_drv->config->dma_tx->DMAChx, &spi_drv->dma.TX_DMA_ChInitStr); - SPI_I2S_EnableDma(spi_drv->config->SPIx, SPI_I2S_DMA_TX, ENABLE); - } + /* Enable transfer complete interrupt */ + DMA_ConfigInt(spi_drv->config->dma_tx->DMAChx, DMA_INT_TXC, ENABLE); + + /* Enable the specified DMA channel */ + DMA_EnableChannel(spi_drv->config->dma_tx->DMAChx, ENABLE); + + SPI_I2S_EnableDma(spi_drv->config->SPIx, SPI_I2S_DMA_TX, ENABLE); #endif } else @@ -272,46 +655,84 @@ static rt_err_t SPI_DMA_Receive(struct n32_spi *spi_drv, uint8_t *pData, uint16_ if (spi_drv->SPI_InitStructure.DataDirection == SPI_DIR_DOUBLELINE_FULLDUPLEX && spi_drv->SPI_InitStructure.SpiMode == SPI_MODE_MASTER) { +#if defined(SOC_SERIES_N32H7xx) + /* Master recv-only in 4-wire full-duplex still has to clock the bus, + * but must not echo the caller's RX buffer onto MOSI (the old bug: + * TX DMA sourced pData itself and pushed the pre-message RX contents + * out on MOSI). The TX leg now reads the static 0xFF fill buffer; the + * fd_rxonly_dummy_tx flag makes n32_spi_dma_arm re-point every LLI TX + * node at the fill base. RX keeps capturing into pData and the arm + * order / Direct / ISR / warm-arm (engine_warm) semantics of + * SPI_DMA_TransmitReceive are unchanged (it stays textually + * untouched; completion still arrives via spi_rx_dma_isr on + * Direct == SPI_Tx_Rx). */ + rt_err_t result; + + spi_drv->fd_rxonly_dummy_tx = RT_TRUE; + result = SPI_DMA_TransmitReceive(spi_drv, spi_fd_rxonly_dummy, pData, Size); + spi_drv->fd_rxonly_dummy_tx = RT_FALSE; + return result; +#else return SPI_DMA_TransmitReceive(spi_drv, pData, pData, Size); +#endif } + rt_bool_t m2warm_rx_defer = RT_FALSE; + if (spi_drv->SPI_InitStructure.DataDirection == SPI_DIR_SINGLELINE_RX || spi_drv->SPI_InitStructure.DataDirection == SPI_DIR_SINGLELINE_TX) { - /* Disable the sFLASH_SPI */ - SPI_Enable(spi_drv->config->SPIx, DISABLE); - /* DeInitializes the sFLASH_SPI */ - SPI_I2S_DeInit(spi_drv->config->SPIx); - /* Set the sFLASH_SPI SendOnly*/ - spi_drv->SPI_InitStructure.DataDirection = SPI_DIR_SINGLELINE_RX; - SPI_Init(spi_drv->config->SPIx, &spi_drv->SPI_InitStructure); + if (n32_spi_mode2_3w_master(spi_drv)) + { + /* Always-on mode2 master: defer the RX flip until both DMA + * channels are armed below. The flip itself starts the + * free-running receiver, so the residue drain and the arms must + * come first or the opening bytes are missed; a cold engine + * additionally needs the SPIEN 0->1 enable after the arms, + * which the deferred flip then also covers. */ + m2warm_rx_defer = RT_TRUE; + } + else + { + n32_spi_singleline_dir(spi_drv, SPI_DIR_SINGLELINE_RX); + } } + /* Clear any stale RX residue BEFORE arming the RX DMA channel: once + * RDMAEN is set the first DMA transfer would swallow a leftover byte in + * DAT as rx[0] (slave-side 1-byte right-shift + trailing RNE=1 symptom). + * For a slave the master has not started clocking yet, so no real first + * byte can be lost here. */ + SPI_I2S_ReceiveData(spi_drv->config->SPIx); + SPI_I2S_GetStatus(spi_drv->config->SPIx, SPI_I2S_OVER_FLAG); + if (spi_drv->dma.DMA_Rx_Init == RT_TRUE) { #if defined(SOC_SERIES_N32H7xx) - DMA_ChannelCmd(spi_drv->config->dma_rx->Instance, spi_drv->config->dma_rx->dma_channel, DISABLE); - - if (!DMA_ControllerIsEnabled(spi_drv->config->dma_rx->Instance)) + /* SPI RX DMA receive (single block or seamless LLI chain) */ + if (n32_spi_dma_arm(spi_drv, RT_TRUE, RT_TRUE, pData, Size) != RT_EOK) { - DMA_ControllerCmd(spi_drv->config->dma_rx->Instance, ENABLE); + return -RT_ERROR; } +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + /* SPI RX DMA Receive Data for H49X */ + DMA_EnableChannel(spi_drv->config->dma_rx->DMAChx, DISABLE); - spi_drv->dma.RX_DMA_ChInitStr.IntEn = 1U; - spi_drv->dma.RX_DMA_ChInitStr.DstAddr = (uint32_t)pData; - spi_drv->dma.RX_DMA_ChInitStr.BlkTfrSize = Size; + /* Configure DMA request remapping */ + DMA_RequestRemap(spi_drv->config->dma_rx->request, spi_drv->config->dma_rx->DMAChx, ENABLE); - DMA_ControllerCmd(spi_drv->config->dma_rx->Instance, ENABLE); - if (DMA_ChannelInit(spi_drv->config->dma_rx->Instance, &spi_drv->dma.RX_DMA_ChInitStr, spi_drv->config->dma_rx->dma_channel) == 0U) - { - /* Enable transaction complete interrupt event */ - DMA_ChannelEventCmd(spi_drv->config->dma_rx->Instance, spi_drv->config->dma_rx->dma_channel, DMA_CH_EVENT_TRANSFER_COMPLETE, ENABLE); + spi_drv->dma.RX_DMA_ChInitStr.MemAddr = (uint32_t)pData; + spi_drv->dma.RX_DMA_ChInitStr.BufSize = Size; - /* Enable the specified DMA channel */ - DMA_ChannelCmd(spi_drv->config->dma_rx->Instance, spi_drv->config->dma_rx->dma_channel, ENABLE); + DMA_Init(spi_drv->config->dma_rx->DMAChx, &spi_drv->dma.RX_DMA_ChInitStr); - SPI_I2S_EnableDma(spi_drv->config->SPIx, SPI_I2S_DMA_RX, ENABLE); - } + /* Enable transfer complete interrupt */ + DMA_ConfigInt(spi_drv->config->dma_rx->DMAChx, DMA_INT_TXC, ENABLE); + + /* Enable the specified DMA channel */ + DMA_EnableChannel(spi_drv->config->dma_rx->DMAChx, ENABLE); + + SPI_I2S_EnableDma(spi_drv->config->SPIx, SPI_I2S_DMA_RX, ENABLE); #endif } else @@ -323,30 +744,24 @@ static rt_err_t SPI_DMA_Receive(struct n32_spi *spi_drv, uint8_t *pData, uint16_ if (spi_drv->dma.DMA_Tx_Init == RT_TRUE) { #if defined(SOC_SERIES_N32H7xx) - DMA_ChannelCmd(spi_drv->config->dma_tx->Instance, spi_drv->config->dma_tx->dma_channel, DISABLE); - - if (!DMA_ControllerIsEnabled(spi_drv->config->dma_tx->Instance)) + /* Receive mode: the TX channel is armed as an empty block (this direction does not take part) */ + if (n32_spi_dma_arm(spi_drv, RT_FALSE, RT_FALSE, RT_NULL, 0U) != RT_EOK) { - DMA_ControllerCmd(spi_drv->config->dma_tx->Instance, ENABLE); + return -RT_ERROR; } +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) - spi_drv->dma.TX_DMA_ChInitStr.IntEn = 0U; - spi_drv->dma.TX_DMA_ChInitStr.SrcAddr = NULL; - spi_drv->dma.TX_DMA_ChInitStr.BlkTfrSize = 0U; + DMA_EnableChannel(spi_drv->config->dma_tx->DMAChx, DISABLE); + + spi_drv->dma.TX_DMA_ChInitStr.MemAddr = 0; + spi_drv->dma.TX_DMA_ChInitStr.BufSize = 0; + + DMA_EnableChannel(spi_drv->config->dma_tx->DMAChx, ENABLE); + DMA_Init(spi_drv->config->dma_tx->DMAChx, &spi_drv->dma.TX_DMA_ChInitStr); - DMA_ControllerCmd(spi_drv->config->dma_tx->Instance, ENABLE); - if (DMA_ChannelInit(spi_drv->config->dma_tx->Instance, &spi_drv->dma.TX_DMA_ChInitStr, spi_drv->config->dma_tx->dma_channel) != 0U) - { - LOG_E("Master DMA Tx channel initialization failed."); - return -RT_ERROR; - } #endif } - /* Clear Over Flag */ - SPI_I2S_ReceiveData(spi_drv->config->SPIx); - SPI_I2S_GetStatus(spi_drv->config->SPIx, SPI_I2S_OVER_FLAG); - /* Enable Error Interrupt */ SPI_I2S_EnableInt(spi_drv->config->SPIx, SPI_I2S_INT_ERR, ENABLE); @@ -359,6 +774,15 @@ static rt_err_t SPI_DMA_Receive(struct n32_spi *spi_drv, uint8_t *pData, uint16_ SPI_Enable(spi_drv->config->SPIx, ENABLE); } + if (m2warm_rx_defer) + { + /* Start the always-on RX engine (see the note at the flip site): + * the live BIDIOE clear makes the mode2 receiver free-run, so the + * RX DMA must already be armed (it is - above) to catch every byte + * from the very first clock. */ + n32_spi_mode2_3w_set_dir(spi_drv, SPI_DIR_SINGLELINE_RX); + } + return RT_EOK; } @@ -371,6 +795,14 @@ static rt_err_t SPI_Transmit_Receive(struct n32_spi *spi_drv, uint8_t *pTxData, RT_ASSERT(spi_drv != RT_NULL); + /* Bidirectional PIO is master-only: a slave cannot meet the per-byte + * service deadline by CPU polling. The master owns SCLK (slave has no + * flow control) and the master/slave threads on one core interleave at + * the ms scheduler quantum, far coarser than the byte time - see the + * DMA_TRANS_MIN_LEN note in spixfer for the full reasoning. In DMA-on + * builds a slave message never reaches this gate (slave is forced + * through DMA); in a no-DMA build it does and fails fast with -EIO + * instead of silently dropping bytes. */ if (!((spi_drv->SPI_InitStructure.SpiMode == SPI_MODE_MASTER) && (spi_drv->SPI_InitStructure.DataDirection == SPI_DIR_DOUBLELINE_FULLDUPLEX))) { return -RT_EIO; @@ -550,13 +982,7 @@ static rt_err_t SPI_Transmit(struct n32_spi *spi_drv, uint8_t *pData, uint16_t S if (spi_drv->SPI_InitStructure.DataDirection == SPI_DIR_SINGLELINE_RX || spi_drv->SPI_InitStructure.DataDirection == SPI_DIR_SINGLELINE_TX) { - /* Disable the sFLASH_SPI */ - SPI_Enable(spi_drv->config->SPIx, DISABLE); - /* DeInitializes the sFLASH_SPI */ - SPI_I2S_DeInit(spi_drv->config->SPIx); - /* Set the sFLASH_SPI SendOnly*/ - spi_drv->SPI_InitStructure.DataDirection = SPI_DIR_SINGLELINE_TX; - SPI_Init(spi_drv->config->SPIx, &spi_drv->SPI_InitStructure); + n32_spi_singleline_dir(spi_drv, SPI_DIR_SINGLELINE_TX); } if ((spi_drv->config->SPIx->CTRL2 & SPI_CTRL2_SPIEN) != SPI_CTRL2_SPIEN) @@ -603,7 +1029,6 @@ static rt_err_t SPI_Transmit(struct n32_spi *spi_drv, uint8_t *pData, uint16_t S pData += sizeof(uint8_t); Transfer_Size--; } - while ((Transfer_Size > 0U)) { tickstart = rt_tick_get(); @@ -662,6 +1087,191 @@ static rt_err_t SPI_Transmit(struct n32_spi *spi_drv, uint8_t *pData, uint16_t S return RT_EOK; } +/* 4-wire full-duplex MASTER receive-only (PIO): a recv-only master still has + * to clock the bus and must put a deterministic 0xFF fill on MOSI (the old + * path echoed the caller's RX buffer into the TX slot). This mirrors + * SPI_Transmit_Receive's per-frame discipline - per frame: wait TE, write the + * fill, wait TE, wait BUSY clear, wait RNE, read the response - with the TX + * source replaced by a constant write, so no TX buffer is touched at all + * (no RX-buffer echo, no dummy-buffer length bound; any PIO length is safe). + * The master-only FD gate mirrors SPI_Transmit_Receive (bidirectional PIO is + * master-only; why: see the DMA_TRANS_MIN_LEN note in spixfer). Callers: + * SP_Receive's full-duplex master recv-only shortcut. + * The pre-drain of RNE/OVER belongs to the caller (SP_Receive does it). */ +static rt_err_t SPI_Transmit_Receive_Fill(struct n32_spi *spi_drv, uint8_t *pData, + uint16_t Size, uint32_t Timeout) +{ + uint16_t Tx_Size_Const = Size; + uint16_t Tx_Size = Size; + uint16_t Rx_Size = Size; + uint32_t tickstart; + + RT_ASSERT(spi_drv != RT_NULL); + + if (!((spi_drv->SPI_InitStructure.SpiMode == SPI_MODE_MASTER) && (spi_drv->SPI_InitStructure.DataDirection == SPI_DIR_DOUBLELINE_FULLDUPLEX))) + { + return -RT_EIO; + } + + if ((spi_drv->config->SPIx->CTRL2 & SPI_CTRL2_SPIEN) != SPI_CTRL2_SPIEN) + { + SPI_Enable(spi_drv->config->SPIx, ENABLE); + } + + if (spi_drv->SPI_InitStructure.DataLen == SPI_DATA_SIZE_16BITS) + { + if (Tx_Size_Const == 1U) + { + /* Fill word: a recv-only master clocks a deterministic 0xFFFF */ + spi_drv->config->SPIx->DAT = 0xFFFFU; + Tx_Size--; + } + + while ((Tx_Size > 0U) || (Rx_Size > 0U)) + { + if (Tx_Size > 0U) + { + tickstart = rt_tick_get(); + /* Loop while DAT register in not emplty */ + while (SPI_I2S_GetStatus(spi_drv->config->SPIx, SPI_I2S_TE_FLAG) == RESET) + { + if ((((rt_tick_get() - tickstart) >= Timeout) && (Timeout != 0xFFFFFFFFU)) || (Timeout == 0U)) + { + LOG_E("Checking TE flag timeout befor send data in full-duplex mode."); + return -RT_ETIMEOUT; + } + } + + /* Fill word: a recv-only master clocks a deterministic 0xFFFF */ + SPI_I2S_TransmitData(spi_drv->config->SPIx, 0xFFFFU); + Tx_Size--; + } + + tickstart = rt_tick_get(); + /* Wait for DATA send has complete */ + while (SPI_I2S_GetStatus(spi_drv->config->SPIx, SPI_I2S_TE_FLAG) == RESET) + { + if ((((rt_tick_get() - tickstart) >= Timeout) && (Timeout != 0xFFFFFFFFU)) || (Timeout == 0U)) + { + LOG_E("Checking TE flag timeout after send data in full-duplex mode."); + return -RT_ETIMEOUT; + } + } + + tickstart = rt_tick_get(); + /* Wait for SPI bus idle */ + while (SPI_I2S_GetStatus(spi_drv->config->SPIx, SPI_I2S_BUSY_FLAG) != RESET) + { + if ((((rt_tick_get() - tickstart) >= Timeout) && (Timeout != 0xFFFFFFFFU)) || (Timeout == 0U)) + { + LOG_E("Checking BUSY flag timeout in full-duplex mode."); + return -RT_ETIMEOUT; + } + } + + tickstart = rt_tick_get(); + /* Wait to receive a byte */ + while (SPI_I2S_GetStatus(spi_drv->config->SPIx, SPI_I2S_RNE_FLAG) == RESET) + { + if ((((rt_tick_get() - tickstart) >= Timeout) && (Timeout != 0xFFFFFFFFU)) || (Timeout == 0U)) + { + LOG_E("Checking RNE flag timeout after send data in full-duplex mode."); + return -RT_ETIMEOUT; + } + } + + /* Return the byte read from the SPI bus */ + *((uint16_t *)pData) = (uint16_t)SPI_I2S_ReceiveData(spi_drv->config->SPIx); + pData += sizeof(uint16_t); + + Rx_Size--; + } + } + else if (spi_drv->SPI_InitStructure.DataLen == SPI_DATA_SIZE_8BITS) + { + if (Tx_Size_Const == 1U) + { + /* Fill byte: a recv-only master clocks a deterministic 0xFF */ + spi_drv->config->SPIx->DAT = 0xFFU; + Tx_Size--; + } + + while ((Tx_Size > 0U) || (Rx_Size > 0U)) + { + if (Tx_Size > 0U) + { + tickstart = rt_tick_get(); + /* Loop while DAT register in not emplty */ + while (SPI_I2S_GetStatus(spi_drv->config->SPIx, SPI_I2S_TE_FLAG) == RESET) + { + if ((((rt_tick_get() - tickstart) >= Timeout) && (Timeout != 0xFFFFFFFFU)) || (Timeout == 0U)) + { + LOG_E("Checking TE flag timeout befor send data in full-duplex mode."); + return -RT_ETIMEOUT; + } + } + + /* Fill byte: a recv-only master clocks a deterministic 0xFF */ + SPI_I2S_TransmitData(spi_drv->config->SPIx, 0xFFU); + Tx_Size--; + } + + tickstart = rt_tick_get(); + /* Wait for DATA send has complete */ + while (SPI_I2S_GetStatus(spi_drv->config->SPIx, SPI_I2S_TE_FLAG) == RESET) + { + if ((((rt_tick_get() - tickstart) >= Timeout) && (Timeout != 0xFFFFFFFFU)) || (Timeout == 0U)) + { + LOG_E("Checking TE flag timeout after send data in full-duplex mode."); + return -RT_ETIMEOUT; + } + } + + tickstart = rt_tick_get(); + /* Wait for SPI bus idle */ + while (SPI_I2S_GetStatus(spi_drv->config->SPIx, SPI_I2S_BUSY_FLAG) != RESET) + { + if ((((rt_tick_get() - tickstart) >= Timeout) && (Timeout != 0xFFFFFFFFU)) || (Timeout == 0U)) + { + LOG_E("Checking BUSY flag timeout in full-duplex mode."); + return -RT_ETIMEOUT; + } + } + + tickstart = rt_tick_get(); + /* Wait to receive a byte */ + while (SPI_I2S_GetStatus(spi_drv->config->SPIx, SPI_I2S_RNE_FLAG) == RESET) + { + if ((((rt_tick_get() - tickstart) >= Timeout) && (Timeout != 0xFFFFFFFFU)) || (Timeout == 0U)) + { + LOG_E("Checking RNE flag timeout after send data in full-duplex mode."); + return -RT_ETIMEOUT; + } + } + + /*!< Return the byte read from the SPI bus */ + *((uint8_t *)pData) = (uint8_t)SPI_I2S_ReceiveData(spi_drv->config->SPIx); + pData += sizeof(uint8_t); + + Rx_Size--; + } + } + else + { + return -RT_EINVAL; + } + + if (SPI_I2S_GetStatus(spi_drv->config->SPIx, SPI_I2S_OVER_FLAG) != RESET) + { + volatile uint32_t reg_tmp; + reg_tmp = spi_drv->config->SPIx->DAT; + reg_tmp = spi_drv->config->SPIx->STS; + (void)reg_tmp; + } + + return RT_EOK; +} + static rt_err_t SP_Receive(struct n32_spi *spi_drv, uint8_t *pData, uint16_t Size, uint32_t Timeout) { @@ -687,19 +1297,13 @@ static rt_err_t SP_Receive(struct n32_spi *spi_drv, uint8_t *pData, uint16_t Siz if (spi_drv->SPI_InitStructure.DataDirection == SPI_DIR_DOUBLELINE_FULLDUPLEX && spi_drv->SPI_InitStructure.SpiMode == SPI_MODE_MASTER) { - return SPI_Transmit_Receive(spi_drv, pData, pData, Size, Timeout); + return SPI_Transmit_Receive_Fill(spi_drv, pData, Size, Timeout); } if (spi_drv->SPI_InitStructure.DataDirection == SPI_DIR_SINGLELINE_RX || spi_drv->SPI_InitStructure.DataDirection == SPI_DIR_SINGLELINE_TX) { - /* Disable the sFLASH_SPI */ - SPI_Enable(spi_drv->config->SPIx, DISABLE); - /* DeInitializes the sFLASH_SPI */ - SPI_I2S_DeInit(spi_drv->config->SPIx); - /* Set the sFLASH_SPI SendOnly*/ - spi_drv->SPI_InitStructure.DataDirection = SPI_DIR_SINGLELINE_RX; - SPI_Init(spi_drv->config->SPIx, &spi_drv->SPI_InitStructure); + n32_spi_singleline_dir(spi_drv, SPI_DIR_SINGLELINE_RX); } if ((spi_drv->config->SPIx->CTRL2 & SPI_CTRL2_SPIEN) != SPI_CTRL2_SPIEN) @@ -718,6 +1322,7 @@ static rt_err_t SP_Receive(struct n32_spi *spi_drv, uint8_t *pData, uint16_t Siz if ((((rt_tick_get() - tickstart) >= Timeout) && (Timeout != 0xFFFFFFFFU)) || (Timeout == 0U)) { LOG_E("During receiving data, check the RNE flags for timeout."); + n32_spi_mode2_3w_rx_abort(spi_drv); return -RT_ETIMEOUT; } } @@ -740,6 +1345,7 @@ static rt_err_t SP_Receive(struct n32_spi *spi_drv, uint8_t *pData, uint16_t Siz if ((((rt_tick_get() - tickstart) >= Timeout) && (Timeout != 0xFFFFFFFFU)) || (Timeout == 0U)) { LOG_E("During receiving data, check the RNE flags for timeout."); + n32_spi_mode2_3w_rx_abort(spi_drv); return -RT_ETIMEOUT; } } @@ -759,21 +1365,18 @@ static rt_err_t SP_Receive(struct n32_spi *spi_drv, uint8_t *pData, uint16_t Siz spi_drv->SPI_InitStructure.DataDirection == SPI_DIR_SINGLELINE_RX) && spi_drv->SPI_InitStructure.SpiMode == SPI_MODE_MASTER) { - /* Disable the sFLASH_SPI */ - SPI_Enable(spi_drv->config->SPIx, DISABLE); - } - - tickstart = rt_tick_get(); - /* Wait to receive a byte */ - while (SPI_I2S_GetStatus(spi_drv->config->SPIx, SPI_I2S_RNE_FLAG) == RESET) - { - volatile rt_uint32_t dummy = spi_drv->config->SPIx->DAT; - (void)dummy; - - if ((((rt_tick_get() - tickstart) >= Timeout) && (Timeout != 0xFFFFFFFFU)) || (Timeout == 0U)) + if (n32_spi_mode2_3w_master(spi_drv)) { - LOG_E("After receiving data, check the RNE flags for timeout."); - return -RT_ETIMEOUT; + /* Always-on mode2 3-wire master: dropping the engine would pull + * SCK low into the enabled slave (the mode2 edge problem), so + * instead leave RX and rest idle in TX - the bit14 set stops the + * free-running receiver, which otherwise clocks continuously. */ + n32_spi_mode2_3w_set_dir(spi_drv, SPI_DIR_SINGLELINE_TX); + } + else + { + /* Disable the sFLASH_SPI */ + SPI_Enable(spi_drv->config->SPIx, DISABLE); } } @@ -805,6 +1408,12 @@ static rt_err_t n32_spi_init(struct n32_spi *spi_drv, struct rt_spi_configuratio RT_ASSERT(spi_drv != RT_NULL); RT_ASSERT(cfg != RT_NULL); + /* Force a clean start: DeInit resets the peripheral (clearing stale + * 3WIRE/BIDIR/DMA-enable/OVER state); otherwise leftovers of a previous + * configuration (e.g. a half-duplex 3WIRE run), such as BIDIR/TDMAEN, + * would pollute this one. */ + SPI_I2S_DeInit(spi_drv->config->SPIx); + SPI_InitStruct(&spi_drv->SPI_InitStructure); if (cfg->mode & RT_SPI_SLAVE) @@ -820,6 +1429,25 @@ static rt_err_t n32_spi_init(struct n32_spi *spi_drv, struct rt_spi_configuratio { /* Switch TX/RX direction dynamically according to the application */ spi_drv->SPI_InitStructure.DataDirection = SPI_DIR_SINGLELINE_RX; + /* NOTE: mode2 (CPOL=1, CPHA=0) corrupts data as long as the engine + * cold-starts per message: the SPIEN 0->1 pulls SCK low then rises + * into the already-enabled mode2 slave, its bit counter gains one + * spurious edge and the whole frame grid shifts by one bit + * (rx[j] = (b[j-1] & 1) << 7 | b[j] >> 1; a whole 4096B frame + * matches this deterministically; m0/1/3 sample after the data + * lands, so they are immune). Not a silicon defect: the vendor + * full-duplex soft-NSS demo on the same chip passes mode2 with an + * always-on engine. Fixed for the 3-wire master exactly like the + * 4-wire FD master: the engine is pre-enabled at configure (in the + * TX posture) and kept across message tails; the per-message + * direction flip toggles CTRL1's BIDIOE bit directly without + * dropping SPIEN (re-running SPI_Init cannot switch TX->RX warm: its + * CLR_MASK 0xF9F7 preserves bit14, making the flip a silent no-op; + * see n32_spi_mode2_3w_set_dir / n32_spi_singleline_dir). The 3-wire + * slave keeps its legacy cold cycle per message (its enable now + * happens on an SCK line clamped idle-high by the master, so no + * spurious edge is possible). FD mode2 uses the same always-on + * scheme via n32_spi_mode2_fd_master. */ } else { @@ -883,6 +1511,20 @@ static rt_err_t n32_spi_init(struct n32_spi *spi_drv, struct rt_spi_configuratio { return -RT_EIO; } +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + + RCC_ClocksType RCC_ClockFreq = { 0 }; + + RCC_GetClocksFreqValue(&RCC_ClockFreq); + + if ((spi_drv->config->SPIx == SPI1) || (spi_drv->config->SPIx == SPI4) || (spi_drv->config->SPIx == SPI5) || (spi_drv->config->SPIx == SPI6)) + { + SPI_CLOCK = RCC_ClockFreq.Pclk2Freq; + } + else if ((spi_drv->config->SPIx == SPI2) || (spi_drv->config->SPIx == SPI3)) + { + SPI_CLOCK = RCC_ClockFreq.Pclk1Freq; + } #endif if (cfg->max_hz >= SPI_CLOCK / 2) @@ -931,6 +1573,20 @@ static rt_err_t n32_spi_init(struct n32_spi *spi_drv, struct rt_spi_configuratio /* Initializes the SPIx peripheral */ SPI_Init(spi_drv->config->SPIx, &spi_drv->SPI_InitStructure); + /* Disable leftover DMA channels/events: SPI_Init does not clear DMA + * enables or TC events, so residue across configurations keeps the + * previous channels alive (notably on 3WIRE->FD switches). */ +#if defined(SOC_SERIES_N32H7xx) + if (spi_drv->config->dma_rx) + { + DMA_ChannelCmd(spi_drv->config->dma_rx->Instance, spi_drv->config->dma_rx->dma_channel, DISABLE); + } + if (spi_drv->config->dma_tx) + { + DMA_ChannelCmd(spi_drv->config->dma_tx->Instance, spi_drv->config->dma_tx->dma_channel, DISABLE); + } +#endif + /* DMA configuration */ if (spi_drv->spi_dma_flag & SPI_USING_RX_DMA_FLAG) { @@ -939,6 +1595,9 @@ static rt_err_t n32_spi_init(struct n32_spi *spi_drv, struct rt_spi_configuratio #if defined(SOC_SERIES_N32H7xx) spi_drv->dma.RX_DMA_ChInitStr.SrcTfrWidth = DMA_CH_TRANSFER_WIDTH_8; spi_drv->dma.RX_DMA_ChInitStr.DstTfrWidth = DMA_CH_TRANSFER_WIDTH_8; +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + spi_drv->dma.RX_DMA_ChInitStr.PeriphDataSize = DMA_PERIPH_DATA_WIDTH_BYTE; + spi_drv->dma.RX_DMA_ChInitStr.MemDataSize = DMA_MEM_DATA_WIDTH_BYTE; #endif } else if (cfg->data_width == 16) @@ -946,6 +1605,9 @@ static rt_err_t n32_spi_init(struct n32_spi *spi_drv, struct rt_spi_configuratio #if defined(SOC_SERIES_N32H7xx) spi_drv->dma.RX_DMA_ChInitStr.SrcTfrWidth = DMA_CH_TRANSFER_WIDTH_16; spi_drv->dma.RX_DMA_ChInitStr.DstTfrWidth = DMA_CH_TRANSFER_WIDTH_16; +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + spi_drv->dma.RX_DMA_ChInitStr.PeriphDataSize = DMA_PERIPH_DATA_WIDTH_HALFWORD; + spi_drv->dma.RX_DMA_ChInitStr.MemDataSize = DMA_MEM_DATA_WIDTH_HALFWORD; #endif } @@ -961,6 +1623,9 @@ static rt_err_t n32_spi_init(struct n32_spi *spi_drv, struct rt_spi_configuratio #if defined(SOC_SERIES_N32H7xx) spi_drv->dma.TX_DMA_ChInitStr.SrcTfrWidth = DMA_CH_TRANSFER_WIDTH_8; spi_drv->dma.TX_DMA_ChInitStr.DstTfrWidth = DMA_CH_TRANSFER_WIDTH_8; +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + spi_drv->dma.TX_DMA_ChInitStr.PeriphDataSize = DMA_PERIPH_DATA_WIDTH_BYTE; + spi_drv->dma.TX_DMA_ChInitStr.MemDataSize = DMA_MEM_DATA_WIDTH_BYTE; #endif } else if (cfg->data_width == 16) @@ -968,6 +1633,9 @@ static rt_err_t n32_spi_init(struct n32_spi *spi_drv, struct rt_spi_configuratio #if defined(SOC_SERIES_N32H7xx) spi_drv->dma.TX_DMA_ChInitStr.SrcTfrWidth = DMA_CH_TRANSFER_WIDTH_16; spi_drv->dma.TX_DMA_ChInitStr.DstTfrWidth = DMA_CH_TRANSFER_WIDTH_16; +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + spi_drv->dma.TX_DMA_ChInitStr.PeriphDataSize = DMA_PERIPH_DATA_WIDTH_HALFWORD; + spi_drv->dma.TX_DMA_ChInitStr.MemDataSize = DMA_MEM_DATA_WIDTH_HALFWORD; #endif } @@ -983,6 +1651,28 @@ static rt_err_t n32_spi_init(struct n32_spi *spi_drv, struct rt_spi_configuratio NVIC_EnableIRQ(spi_drv->config->irq_type); } + /* Pre-enable the engine of a mode2 master (FD or 3-wire single-line, + * see the big note in SPI_DMA_TransmitReceive): configuration itself + * does the 0->1 start and clamps SCK idle-high -- the peer slave engine + * is off at that moment (its message tail disabled it), so the rising + * edge on a dead slave is harmless. Every later slave arm then happens + * on an idle-high SCK and the message self-starts via the warm-engine + * DMA level trigger (no more SPIEN 0->1). Non-mode2 configurations keep + * the engine off (SPI_I2S_DeInit+Init already reset it) and keep the + * legacy cold-start semantics per message. */ + if (n32_spi_mode2_fd_master(spi_drv) || n32_spi_mode2_3w_master(spi_drv)) + { + if (n32_spi_mode2_3w_master(spi_drv)) + { + /* Pre-enable in the TX posture (bit14 set): a single-line RX + * engine free-runs SCK as soon as SPIEN=1 (measured: OVER=1 + * garbage polluting the first message after the mode boundary), + * while TX idles quietly until DAT is written. */ + n32_spi_mode2_3w_set_dir(spi_drv, SPI_DIR_SINGLELINE_TX); + } + SPI_Enable(spi_drv->config->SPIx, ENABLE); + } + LOG_D("%s init done", spi_drv->config->bus_name); return RT_EOK; } @@ -1003,7 +1693,40 @@ static rt_err_t spi_configure(struct rt_spi_device *device, static rt_ssize_t spixfer(struct rt_spi_device *device, struct rt_spi_message *message) { -#define DMA_TRANS_MIN_LEN 10 /* only buffer length >= DMA_TRANS_MIN_LEN will use DMA mode */ +#define DMA_TRANS_MIN_LEN 10 /* Slave messages never use CPU-polled (PIO) + transfer, and the PIO helpers are + master-only. Why: + + An SPI slave has no flow control: the + master owns SCLK, so the slave must service + every byte within one byte time (at 1 MHz + that is 8 us - drain RNE / refill DAT before + the next shift) or the frame is lost. + Under RT-Thread the master and slave SPIs + are driven by two threads on one core, so + they can never be serviced at the same + time; the interleave granularity is the + scheduler quantum (ms), about three orders + of magnitude coarser than the per-byte + deadline -> thread polling drops bytes the + moment a message outlives one quantum. Only + DMA (hardware moves each byte the instant + the SPI requests it) or a per-byte + RNE/TE interrupt could meet the deadline; + this driver implements DMA and polling but + no per-byte IRQ path, so DMA is the only + viable slave path. Hence every slave + message (>= 1B) is forced through DMA + (SpiMode == SPI_MODE_SLAVE overrides this + length limit at every dispatch), and the + PIO helpers reject SLAVE with -RT_EIO. A + no-DMA build therefore has no working slave + path at all - expected by design (no partial + frames are produced either). Master + messages below this length (and any master + message in a no-DMA build) are CPU-polled, + which a master can afford because it owns + the clock and paces itself. */ rt_err_t state = RT_EOK; uint32_t tickstart; @@ -1039,10 +1762,77 @@ static rt_ssize_t spixfer(struct rt_spi_device *device, struct rt_spi_message *m message_length = message->length; recv_buf = message->recv_buf; send_buf = message->send_buf; + /* Whether this message is 4-wire full-duplex (send and recv both set). + * An FD message must disable SPI at its tail (see the message-tail + * note) -- both DMA and PIO ones (PIO = non-mode2 master len<10): if a + * PIO FD message leaves the engine ON, the next message's cold-start + * disable->enable produces spurious SCK edges on the already-enabled + * slave (a short PIO mode3 message corrupts from its first byte). The + * mode2 FD master with its always-on engine is the only exception (see + * the message-tail note). */ + rt_bool_t fd_msg = (message->send_buf && message->recv_buf) ? RT_TRUE : RT_FALSE; while (message_length) { #if defined(SOC_SERIES_N32H7xx) + /* One DMA arm covers up to ARM_MAX elements on the clock-driving + * side only; a longer message is split into consecutive arms. The + * engine stays on across chunk boundaries (only the message tail + * disables -- see the loop bottom) and chunk-2+ master arms re-arm + * the live engine warm (fd_chunk_cont, see SPI_DMA_TransmitReceive). + * The slave must NOT chunk: its DMA legs are request-driven (they + * only advance under the master's clock), so a single + * request-driven arm of up to CHAIN_MAX absorbs the master's + * inter-arm clock gaps without any re-arm -- a slave chunk boundary + * would instead race the master's continuous clock (its thread must + * re-arm the RX channel while the master is already clocking the + * next chunk; every 8192-element case loses that race at the + * boundary). The cap keeps each master arm under the N32H7xx + * per-arm transfer ceiling (~4128 items at the 20 MHz top rate, see + * SPI_DMA_ARM_MAX) -- a ceiling that applies to a clock-driving arm, + * i.e. one that keeps the engine on for its whole length while a + * second DMA channel competes for the bus. + * + * A single-line (3-wire) master message arms exactly one channel + * with data (SPI_DMA_Transmit / SPI_DMA_Receive arm the other one as + * an empty block), so it never meets that dual-channel arm and it + * gets the chain cap like the slave. Chunking a 3-wire master leg is + * not merely unnecessary, it CORRUPTS: every chunk-2+ arm takes the + * cold path (n32_spi_singleline_dir -> DISABLE + DeInit + Init for + * every mode but mode2), so the mid-message peripheral restart puts + * a spurious SCK edge onto the peer whose frame grid is already + * mid-message, and the 3-wire tail DISABLE block below used to fire + * at every chunk tail as well. Every len=8192 case with CPHA=1 then + * fails with the first wrong byte at index 4096, in both 3-wire + * directions and byte for byte identically at 1 MHz and at 100 kHz + * -- which rules out a re-arm timing race and pins it on the + * edge/phase corruption of the restart. A 4-wire message keeps the + * bounded arm (both channels live: FD, or the recv-only master that + * clocks through the 0xFF dummy TX). */ + rt_uint32_t arm_max; + if (spi_drv->SPI_InitStructure.SpiMode == SPI_MODE_SLAVE) + { + arm_max = SPI_DMA_CHAIN_MAX; + } + else if ((device->config.mode & RT_SPI_3WIRE) && !fd_msg) + { + arm_max = SPI_DMA_CHAIN_MAX; + } + else + { + arm_max = SPI_DMA_ARM_MAX; + } + if (message_length > arm_max) + { + send_length = arm_max; + message_length = message_length - arm_max; + } + else + { + send_length = message_length; + message_length = 0; + } +#else /* DMA uses a single block, with a maximum of 4095 per block */ if (message_length > 4095) { @@ -1058,52 +1848,151 @@ static rt_ssize_t spixfer(struct rt_spi_device *device, struct rt_spi_message *m /* calculate the start address */ already_send_length = message->length - send_length - message_length; - /* avoid null pointer problems */ + /* Mark a non-first chunk of a 4-wire full-duplex message for the + * arm (fd_chunk_cont): chunks keep the engine on, so this chunk's + * SPI_DMA_TransmitReceive arm must re-arm the live engine warm + * rather than drop SPIEN and re-enable -- that mid-message cycle + * puts CPOL=1 SCK edges onto the enabled slave and shifts its frame + * grid (see the warm re-arm note). Slaves never chunk below + * SPI_DMA_CHAIN_MAX and ignore the flag anyway. */ + spi_drv->fd_chunk_cont = (fd_msg && (already_send_length != 0U)) ? RT_TRUE : RT_FALSE; + /* avoid null pointer problems. + * already_send_length counts elements while the buffers are byte + * addressed, so this chunk's offset must be scaled by the data width + * (16-bit data = 2 bytes per element). Without it every chunk after + * the first starts one byte early per element -- half the real offset + * -- and the DMA walks the wrong half of the buffer. */ if (message->send_buf) { - send_buf = (rt_uint8_t *)message->send_buf + already_send_length; + send_buf = (rt_uint8_t *)message->send_buf + (already_send_length * (spi_drv->cfg->data_width / 8u)); } if (message->recv_buf) { - recv_buf = (rt_uint8_t *)message->recv_buf + already_send_length; + recv_buf = (rt_uint8_t *)message->recv_buf + (already_send_length * (spi_drv->cfg->data_width / 8u)); } - rt_uint32_t *dma_aligned_buffer = RT_NULL; - rt_uint32_t *p_txrx_buffer = RT_NULL; - - if ((spi_drv->spi_dma_flag & SPI_USING_TX_DMA_FLAG) && (send_length >= DMA_TRANS_MIN_LEN)) + rt_uint32_t *dma_aligned_buffer = RT_NULL; /* TX staging buffer (copy path only) */ + rt_uint32_t *dma_rx_staging = RT_NULL; /* RX staging buffer (copy path only) */ + rt_uint32_t *p_tx_buffer = RT_NULL; /* real source that TX DMA reads */ + rt_uint32_t *p_rx_buffer = RT_NULL; /* real destination RX DMA writes to */ + /* Which legs of this chunk the DMA serves: the PIO length gate plus + * two exceptions - a slave never PIO-polls, and neither does a + * 3-wire master receive (see the dispatch note for the measured + * reason). The 3-wire exception is receive-only by construction + * (send_buf == RT_NULL): a bidirectional 3-wire message below the + * gate is served by the PIO helper, which writes the caller's + * buffers directly, so staging such a message would only copy an + * uninitialized buffer back over the real data. + * Every site below that stages, dispatches, synchronizes or tails a + * leg reads these - they must all agree. If they disagree - a + * 3-wire receive dispatch on the DMA gate whose completion wait and + * tail still use the PIO gate - the receive returns before its DMA + * has run: zero bytes received, instant return, at every rate. */ + rt_bool_t tx_leg_dma = ((spi_drv->spi_dma_flag & SPI_USING_TX_DMA_FLAG) != 0U) && + (send_buf != RT_NULL) && + ((send_length >= DMA_TRANS_MIN_LEN) || + (spi_drv->SPI_InitStructure.SpiMode == SPI_MODE_SLAVE)); + rt_bool_t rx_leg_dma = ((spi_drv->spi_dma_flag & SPI_USING_RX_DMA_FLAG) != 0U) && + (recv_buf != RT_NULL) && + ((send_length >= DMA_TRANS_MIN_LEN) || + (spi_drv->SPI_InitStructure.SpiMode == SPI_MODE_SLAVE) || + ((send_buf == RT_NULL) && ((device->config.mode & RT_SPI_3WIRE) != 0))); + rt_bool_t msg_dma = tx_leg_dma || rx_leg_dma; + /* Loop work vars: declared above the error gotos so a chunk-exit + * jump never bypasses an initialization (armcc #546-D) */ + rt_tick_t busy_to; + rt_bool_t busy_to_ok; + rt_bool_t dma_leg; + + /* TX source preparation: allocate/use directly only when data is + * actually being sent. The direct path (p_tx_buffer == send_buf) is + * kept apart from the copy path so full-duplex DMA never shares one + * buffer between TX and RX (an RX echo would overwrite the TX source + * and recv_buf would never see data - fixed). */ + if (tx_leg_dma) { #if defined(SOC_SERIES_N32H7xx) - if (RT_IS_ALIGN((rt_uint32_t)send_buf, 32) && send_buf != RT_NULL) /* aligned with 32 bytes? */ + /* send_length counts elements: buffer/copy/cache sizes must be + * converted to bytes by the data width */ + rt_uint32_t send_bytes = send_length * (spi_drv->cfg->data_width / 8u); + if (RT_IS_ALIGN((rt_uint32_t)send_buf, 32)) /* aligned with 32 bytes? */ { - p_txrx_buffer = (rt_uint32_t *)send_buf; /* send_buf aligns with 32 bytes, no more operations */ + p_tx_buffer = (rt_uint32_t *)send_buf; /* send_buf aligns with 32 bytes, DMA reads it directly */ } else { /* send_buf doesn't align with 32 bytes, so creat a cache buffer with 32 bytes aligned */ - dma_aligned_buffer = (rt_uint32_t *)rt_malloc_align(send_length, 32); - rt_memcpy(dma_aligned_buffer, send_buf, send_length); - p_txrx_buffer = dma_aligned_buffer; + dma_aligned_buffer = (rt_uint32_t *)rt_malloc_align(send_bytes, 32); + if (dma_aligned_buffer == RT_NULL) + { + LOG_E("malloc TX align buf failed, len=%u", send_length); + state = -RT_ENOMEM; + goto spi_staging_free; + } + rt_memcpy(dma_aligned_buffer, send_buf, send_bytes); + p_tx_buffer = dma_aligned_buffer; } if ((SCB->CCR & (uint32_t)SCB_CCR_DC_Msk) != 0U) { - rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, dma_aligned_buffer, send_length); + /* flush the REAL DMA source: direct path must flush send_buf too */ + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, p_tx_buffer, send_bytes); } #else + /* send_length counts elements: the staging buffer must be sized and + * filled in bytes (x2 for 16-bit data). Sized in elements the + * halfword DMA reads twice as far as the allocation and the copy + * only carries half of the payload. */ + rt_uint32_t send_bytes = send_length * (spi_drv->cfg->data_width / 8u); if (RT_IS_ALIGN((rt_uint32_t)send_buf, 4) && send_buf != RT_NULL) /* aligned with 4 bytes? */ { - p_txrx_buffer = (rt_uint32_t *)send_buf; /* send_buf aligns with 4 bytes, no more operations */ + p_tx_buffer = (rt_uint32_t *)send_buf; /* send_buf aligns with 4 bytes, no more operations */ } else { /* send_buf doesn't align with 4 bytes, so creat a cache buffer with 4 bytes aligned */ - dma_aligned_buffer = (rt_uint32_t *)rt_malloc(send_length); /* aligned with RT_ALIGN_SIZE (8 bytes by default) */ - rt_memcpy(dma_aligned_buffer, send_buf, send_length); - p_txrx_buffer = dma_aligned_buffer; + dma_aligned_buffer = (rt_uint32_t *)rt_malloc(send_bytes); /* aligned with RT_ALIGN_SIZE (8 bytes by default) */ + if (dma_aligned_buffer == RT_NULL) + { + LOG_E("SPI DMA TX buffer malloc failed!"); + state = -RT_ENOMEM; + goto spi_staging_free; + } + rt_memcpy(dma_aligned_buffer, send_buf, send_bytes); + p_tx_buffer = dma_aligned_buffer; } #endif } + /* RX destination preparation: allocate/use directly only when data + * is actually being received. The direct path (p_rx_buffer == + * recv_buf) is kept apart from the staging path; staging is always + * copied back on completion. */ + if (rx_leg_dma) + { +#if defined(SOC_SERIES_N32H7xx) + /* send_length counts elements: the RX staging buffer is likewise + * allocated in bytes (x2 for 16-bit data) */ + rt_uint32_t recv_bytes = send_length * (spi_drv->cfg->data_width / 8u); + if (RT_IS_ALIGN((rt_uint32_t)recv_buf, 32)) + { + p_rx_buffer = (rt_uint32_t *)recv_buf; /* DMA writes straight into caller's buffer */ + } + else + { + dma_rx_staging = (rt_uint32_t *)rt_malloc_align(recv_bytes, 32); + if (dma_rx_staging == RT_NULL) + { + LOG_E("malloc RX staging buf failed, len=%u", send_length); + state = -RT_ENOMEM; + goto spi_staging_free; + } + p_rx_buffer = dma_rx_staging; + } +#else + p_rx_buffer = (rt_uint32_t *)recv_buf; +#endif + } + tickstart = rt_tick_get(); /* Check the SPI RNE Flag */ while (SPI_I2S_GetStatus(spi_drv->config->SPIx, SPI_I2S_RNE_FLAG) != RESET) @@ -1140,19 +2029,28 @@ static rt_ssize_t spixfer(struct rt_spi_device *device, struct rt_spi_message *m /* start once data exchange in DMA mode */ if (message->send_buf && message->recv_buf) { - if ((spi_drv->spi_dma_flag & SPI_USING_TX_DMA_FLAG) && (spi_drv->spi_dma_flag & SPI_USING_RX_DMA_FLAG) && (send_length >= DMA_TRANS_MIN_LEN)) + if ((spi_drv->spi_dma_flag & SPI_USING_TX_DMA_FLAG) && (spi_drv->spi_dma_flag & SPI_USING_RX_DMA_FLAG) && ((send_length >= DMA_TRANS_MIN_LEN || spi_drv->SPI_InitStructure.SpiMode == SPI_MODE_SLAVE))) { - state = SPI_DMA_TransmitReceive(spi_drv, (uint8_t *)p_txrx_buffer, (uint8_t *)p_txrx_buffer, send_length); + /* TX/RX buffer separation: even when send_buf and recv_buf + * differ, DMA TX/RX must never share one source */ + if ((p_tx_buffer == RT_NULL) || (p_rx_buffer == RT_NULL)) + { + LOG_E("full-duplex DMA: TX %p RX %p buf missing (from %p,%p len=%u)", + p_tx_buffer, p_rx_buffer, send_buf, recv_buf, send_length); + state = -RT_ERROR; + goto spi_staging_free; + } + state = SPI_DMA_TransmitReceive(spi_drv, (uint8_t *)p_tx_buffer, (uint8_t *)p_rx_buffer, send_length); } - else if ((spi_drv->spi_dma_flag & SPI_USING_TX_DMA_FLAG) && (send_length >= DMA_TRANS_MIN_LEN)) + else if ((spi_drv->spi_dma_flag & SPI_USING_TX_DMA_FLAG) && ((send_length >= DMA_TRANS_MIN_LEN || spi_drv->SPI_InitStructure.SpiMode == SPI_MODE_SLAVE))) { - state = SPI_DMA_Transmit(spi_drv, (uint8_t *)p_txrx_buffer, send_length); + state = SPI_DMA_Transmit(spi_drv, (uint8_t *)p_tx_buffer, send_length); } - else if ((spi_drv->spi_dma_flag & SPI_USING_RX_DMA_FLAG) && (send_length >= DMA_TRANS_MIN_LEN)) + else if ((spi_drv->spi_dma_flag & SPI_USING_RX_DMA_FLAG) && ((send_length >= DMA_TRANS_MIN_LEN || spi_drv->SPI_InitStructure.SpiMode == SPI_MODE_SLAVE))) { state = -RT_ERROR; LOG_E("It shoule be enabled both BSP_SPIx_TX_USING_DMA and BSP_SPIx_TX_USING_DMA flag, if wants to use SPI DMA Rx singly."); - break; + goto spi_staging_free; } else { @@ -1161,27 +2059,45 @@ static rt_ssize_t spixfer(struct rt_spi_device *device, struct rt_spi_message *m } else if (message->send_buf) { - if ((spi_drv->spi_dma_flag & SPI_USING_TX_DMA_FLAG) && (send_length >= DMA_TRANS_MIN_LEN)) + if ((spi_drv->spi_dma_flag & SPI_USING_TX_DMA_FLAG) && ((send_length >= DMA_TRANS_MIN_LEN || spi_drv->SPI_InitStructure.SpiMode == SPI_MODE_SLAVE))) { - state = SPI_DMA_Transmit(spi_drv, (uint8_t *)p_txrx_buffer, send_length); + if (p_tx_buffer == RT_NULL) + { + LOG_E("SPI DMA TX: source buf missing"); + state = -RT_ERROR; + goto spi_staging_free; + } + state = SPI_DMA_Transmit(spi_drv, (uint8_t *)p_tx_buffer, send_length); } else { state = SPI_Transmit(spi_drv, (uint8_t *)send_buf, send_length, 1000); } - - if (message->cs_release && (device->config.mode & RT_SPI_3WIRE)) - { - /* release the CS by disable SPI when using 3 wires SPI */ - SPI_Enable(spi_drv->config->SPIx, DISABLE); - } } else if (message->recv_buf) { - rt_memset((uint8_t *)recv_buf, 0xff, send_length); - if ((spi_drv->spi_dma_flag & SPI_USING_RX_DMA_FLAG) && (send_length >= DMA_TRANS_MIN_LEN)) + /* rx_leg_dma: a 3-wire master receive must never be PIO-polled. + * The 3-wire master receiver free-runs SCLK the moment the + * direction flips to RX, so SP_Receive has no deadline it can + * meet by construction - it occasionally reads DAT while a byte + * is still shifting in and stores the partial shift register. + * The stored value is always the expected byte shifted right by + * k bits with the idle-high tail filled (0xc6 read back as + * 0x01/0x03/0x07/0x0d/0x19, 0x31 as 0x01) - a deterministic per + * (rate, mode, len) beat rather than a rare glitch, so retrying + * does not help. The DMA path never showed it at any rate up to + * 16384 elements. 4-wire messages (incl. the + * recv-only 4-wire master that clocks via SPI_Transmit_Receive_ + * Fill) keep the PIO gate unchanged. */ + if (rx_leg_dma) { - state = SPI_DMA_Receive(spi_drv, (uint8_t *)p_txrx_buffer, send_length); + if (p_rx_buffer == RT_NULL) + { + LOG_E("SPI DMA RX: dest buf missing"); + state = -RT_ERROR; + goto spi_staging_free; + } + state = SPI_DMA_Receive(spi_drv, (uint8_t *)p_rx_buffer, send_length); } else { @@ -1204,7 +2120,7 @@ static rt_ssize_t spixfer(struct rt_spi_device *device, struct rt_spi_message *m { LOG_E("SPI transfer error: %d", state); message->length = 0; - break; + goto spi_staging_free; } else { @@ -1214,45 +2130,204 @@ static rt_ssize_t spixfer(struct rt_spi_device *device, struct rt_spi_message *m /* For simplicity reasons, this example is just waiting till the end of the transfer, but application may perform other tasks while transfer operation is ongoing. */ - if ((spi_drv->spi_dma_flag & (SPI_USING_TX_DMA_FLAG | SPI_USING_RX_DMA_FLAG)) && (send_length >= DMA_TRANS_MIN_LEN)) + if (msg_dma) { /* blocking the thread,and the other tasks can run */ if (rt_completion_wait(&spi_drv->cpt, 1000) != RT_EOK) { state = -RT_ERROR; LOG_E("wait for DMA interrupt overtime!"); - break; + /* Always-on mode2 3-wire master: a timed-out RX leg would + * otherwise keep its free-running engine clocking into the + * next message - stop it (no-op for every other role). */ + n32_spi_mode2_3w_rx_abort(spi_drv); + goto spi_staging_free; + } + } + + /* The DMA leg (the if below) waits for BUSY with "benign timeout" + * semantics; the non-DMA (PIO) leg keeps its original 1000-tick + * ceiling and still errors on expiry: + * a) The DMA-send leg first waits for TE=1: DMA TC fires on the + * last DAT write, right after the second-to-last load, when the + * last byte is still in DAT waiting for the next frame-boundary + * load. If BUSY=0 were waited on directly, polling on a slow + * clock (<= 1MHz) can catch the momentary BUSY drop before the + * last byte is loaded (BUSY idles between two adjacent bytes) + * and move ahead to the 3-wire DISABLE -> the last byte never + * shifts out -> the peer receives an empty tail byte (measured: + * rx[len-1] always wrong). Follow the manual: wait for TE=1 + * first (the last byte has left DAT into the shift register). + * b) Then wait for BUSY=0, but cap it with a short timeout derived + * from the configured bit clock (at 128 bit-times there is >= 8x + * headroom over the real clock: prescaler rounding makes the + * actual baud rate > max_hz/2). On a single-wire slave transfer + * BUSY may never fall after all data has shifted out/in (DMA TC + * proves the last byte arrived: CPHA=1 (mode1/3) slave sending, + * CPOL=1+CPHA=0 (mode2) slave receiving -- the engine waits for a + * frame-boundary clock that never comes; measured: all wire data + * correct, peer compare passed). Timeout expiry is treated as + * end-of-transfer and the tail finishes as a success -- treating + * it as an error would skip the 3-wire DISABLE and leave SPIEN=1 + * stuck. + * In mode0 BUSY falls normally and the flag exits immediately; + * this timeout never applies. */ + busy_to = 1000U; + busy_to_ok = RT_FALSE; /* DMA-leg timeout expiry = benign end */ + dma_leg = msg_dma; + if (dma_leg) + { + busy_to_ok = RT_TRUE; + if (message->send_buf && (spi_drv->spi_dma_flag & SPI_USING_TX_DMA_FLAG)) + { + tickstart = rt_tick_get(); + while (SPI_I2S_GetStatus(spi_drv->config->SPIx, SPI_I2S_TE_FLAG) == RESET) + { + if ((rt_tick_get() - tickstart) > 1000U) + { + state = -RT_ERROR; + LOG_E("Wait SPI TE Flag Timeout!"); + break; + } + } + } + if (state == RT_EOK) + { + rt_uint32_t hz = spi_drv->cfg->max_hz; + if (hz != 0U) + { + rt_uint32_t ms = (128000U + hz - 1U) / hz; /* 128 bit-times -> ms */ + busy_to = (rt_tick_t)ms * RT_TICK_PER_SECOND / 1000U; + if (busy_to < 1U) + { + busy_to = 1U; + } + } } } tickstart = rt_tick_get(); while (SPI_I2S_GetStatus(spi_drv->config->SPIx, SPI_I2S_BUSY_FLAG) != RESET) { - if ((rt_tick_get() - tickstart) > 1000U) + if ((rt_tick_get() - tickstart) > busy_to) { - state = -RT_ERROR; - LOG_E("Wait SPI BUSY Flag Timeout!"); + if (!busy_to_ok) + { + state = -RT_ERROR; + LOG_E("Wait SPI BUSY Flag Timeout!"); + } break; } } - if (dma_aligned_buffer != RT_NULL) /* re-aligned, so need to copy the data to recv_buf */ + /* 3-wire has no CS line: disable SPI once the message (chunk) really + * ends to release the bus. DISABLE must come after the DMA completes + * and BUSY clears: disabling right after the DMA is armed (as the + * old send-only branch did) cuts the clock that drives the slave TX + * DMA -> the DMA never completes -> 3-wire leg BA (slave send-only) + * wait DMA interrupt overtime. The mode2 3-wire master engine stays + * on (pre-enabled at configure, never disabled between messages): + * dropping it would pull the idle-high SCK low and the next slave + * arm (which precedes the master clock) would land on a low line -> + * the mode2 spurious-edge problem returns. + * + * Message tail only (message_length == 0), exactly like the FD block + * below: a 3-wire master message is armed as one single-line leg + * (CHAIN_MAX, see the arm_max note) so an intermediate chunk exists + * only past 65520 elements, but should it ever happen, disabling + * mid-message would put a spurious SCK edge on the peer same as the + * FD case. The last chunk still disables -- the 3-wire mode3 skew + * fix (single-chunk messages included) depends on that. */ + if ((device->config.mode & RT_SPI_3WIRE) && message->cs_release && (state == RT_EOK) && + (message_length == 0U)) { - if (recv_buf != RT_NULL) + if (!n32_spi_mode2_3w_master(spi_drv)) { + SPI_Enable(spi_drv->config->SPIx, DISABLE); + } + } + + /* RX data out: the staging path copies back unconditionally; the + * direct path (dma_rx_staging == NULL) only needs a cache flush */ + if ((recv_buf != RT_NULL) && (p_rx_buffer != RT_NULL)) + { #if defined(SOC_SERIES_N32H7xx) - if ((SCB->CCR & (uint32_t)SCB_CCR_DC_Msk) != 0U) - { - rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, p_txrx_buffer, send_length); - } + /* RX buffer/cache sizes are in bytes (16-bit elements x2) */ + rt_uint32_t recv_bytes = send_length * (spi_drv->cfg->data_width / 8u); + if ((SCB->CCR & (uint32_t)SCB_CCR_DC_Msk) != 0U) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, p_rx_buffer, recv_bytes); + } #endif /* SOC_SERIES_N32H7xx */ - rt_memcpy(recv_buf, p_txrx_buffer, send_length); + if (dma_rx_staging != RT_NULL) + { + rt_memcpy(recv_buf, p_rx_buffer, send_length * (spi_drv->cfg->data_width / 8u)); } + } + + /* Release the staging buffers (on success or failure). Chunk-exit + * error paths jump here via goto: a failed transfer must never + * leak the staging buffers it already allocated. */ + spi_staging_free: #if defined(SOC_SERIES_N32H7xx) + if (dma_aligned_buffer != RT_NULL) + { rt_free_align(dma_aligned_buffer); + } + if (dma_rx_staging != RT_NULL) + { + rt_free_align(dma_rx_staging); + } #else + if (dma_aligned_buffer != RT_NULL) + { rt_free(dma_aligned_buffer); + } #endif /* SOC_SERIES_N32H7xx */ + + if (state != RT_EOK) + { + /* Error: leave the chunk loop. Restore the between-message + * engine posture (off for every role except the always-on + * mode2 FD/3W masters) so the next message cold-starts exactly + * as after a clean tail -- a mid-message error can leave the + * engine enabled, and the next arm's cold cycle relies on + * finding it off. */ + if (!n32_spi_mode2_fd_master(spi_drv) && !n32_spi_mode2_3w_master(spi_drv)) + { + SPI_Enable(spi_drv->config->SPIx, DISABLE); + } + break; /* error: leave the chunk loop (tail returns -RT_ERROR) */ + } + + /* Disable SPI once an FD (4-wire) message really ends (DMA and PIO + * alike) -- same semantics as the 3-wire case above (wait for DMA + * completion + BUSY clear). A message chunked at SPI_DMA_ARM_MAX + * keeps the engine ON across chunk boundaries: only the final chunk + * (message_length == 0) disables here, and chunk-2+ arms re-arm the + * live engine through the warm path (see SPI_DMA_TransmitReceive / + * fd_chunk_cont). Dropping SPIEN at an intermediate chunk tail + * would put the CPOL=1 idle-high SCK falling edge onto the slave, + * which is mid-arm (single request-driven arm per message): the + * slave counts the edge and its TX reply shifts -- measured as + * chunk-2 corruption from byte 4096 of an 8192-element mode3 + * message at 20 MHz (mode0/1 are idle-low and have no edge; the + * mode2 master never disables). For a single-chunk message this + * block runs where it always did, at the message tail -- the mode3 + * skew fix depends on disabling SPI at the message tail. */ + if (fd_msg && message->cs_release && (message_length == 0U)) + { + /* The mode2 FD master engine stays on (pre-enabled at + * configure, never disabled between messages): disabling at the + * tail would lose the idle-high SCK, and the next message's + * slave arm (which precedes the master clock) would land on a + * low line -> the mode2 spurious-edge problem returns (see the + * SPI_DMA_TransmitReceive note). All other modes keep the + * tail-disable semantics. */ + if (!n32_spi_mode2_fd_master(spi_drv)) + { + SPI_Enable(spi_drv->config->SPIx, DISABLE); + } } } @@ -1285,6 +2360,17 @@ static int rt_hw_spi_bus_init(void) { rt_err_t result; +#if defined(SOC_SERIES_N32H7xx) + /* Fill the FD master recv-only TX dummy once: it is read-only afterwards + * (every LLI node re-reads from its base), so a single init-time fill + * and cache flush covers all buses and all subsequent messages. */ + rt_memset(spi_fd_rxonly_dummy, 0xFF, sizeof(spi_fd_rxonly_dummy)); + if ((SCB->CCR & SCB_CCR_DC_Msk) != 0U) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, spi_fd_rxonly_dummy, sizeof(spi_fd_rxonly_dummy)); + } +#endif + for (rt_size_t i = 0; i < sizeof(spi_config) / sizeof(spi_config[0]); i++) { spi_bus_obj[i].config = &spi_config[i]; @@ -1342,6 +2428,36 @@ static int rt_hw_spi_bus_init(void) LOG_E("SPI RX DMA channel initialization failed!"); return -RT_ERROR; } +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + /* Configure the SPI RX DMA for Transmission process */ + /* Enable DMA clock */ + RCC_EnableAHBPeriphClk(spi_bus_obj[i].config->dma_rx->dma_rcc, ENABLE); + + /* Receive DMA Config */ + DMA_StructInit(&spi_bus_obj[i].dma.RX_DMA_ChInitStr); + spi_bus_obj[i].dma.RX_DMA_ChInitStr.PeriphAddr = (uint32_t)&spi_bus_obj[i].config->SPIx->DAT; + spi_bus_obj[i].dma.RX_DMA_ChInitStr.MemAddr = 0; + spi_bus_obj[i].dma.RX_DMA_ChInitStr.Direction = DMA_DIR_PERIPH_SRC; + spi_bus_obj[i].dma.RX_DMA_ChInitStr.BufSize = 0; + spi_bus_obj[i].dma.RX_DMA_ChInitStr.PeriphInc = DMA_PERIPH_INC_DISABLE; + spi_bus_obj[i].dma.RX_DMA_ChInitStr.MemoryInc = DMA_MEM_INC_ENABLE; + spi_bus_obj[i].dma.RX_DMA_ChInitStr.PeriphDataSize = DMA_PERIPH_DATA_WIDTH_BYTE; + spi_bus_obj[i].dma.RX_DMA_ChInitStr.MemDataSize = DMA_MEM_DATA_WIDTH_BYTE; + spi_bus_obj[i].dma.RX_DMA_ChInitStr.CircularMode = DMA_MODE_NORMAL; + spi_bus_obj[i].dma.RX_DMA_ChInitStr.Priority = DMA_PRIORITY_HIGH; + spi_bus_obj[i].dma.RX_DMA_ChInitStr.Mem2Mem = DMA_M2M_DISABLE; +#if defined(SOC_SERIES_N32H49x) + spi_bus_obj[i].dma.RX_DMA_ChInitStr.BurstCmd = DMA_BURST_DISABLE; +#endif + /* Initialize the specified DMA channel and Whether the specified channel was successfully initialized */ + DMA_Init(spi_bus_obj[i].config->dma_rx->DMAChx, &spi_bus_obj[i].dma.RX_DMA_ChInitStr); + + /* Enable transfer complete interrupt */ + DMA_ConfigInt(spi_bus_obj[i].config->dma_rx->DMAChx, DMA_INT_TXC, ENABLE); + + spi_bus_obj[i].dma.DMA_Rx_Init = RT_TRUE; + + #endif } @@ -1396,6 +2512,36 @@ static int rt_hw_spi_bus_init(void) LOG_E("SPI TX DMA channel initialization failed!"); return -RT_ERROR; } +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + /* Configure the SPI TX DMA for Transmission process */ + /* Enable DMA clock */ + RCC_EnableAHBPeriphClk(spi_bus_obj[i].config->dma_tx->dma_rcc, ENABLE); + + /* SPI_MASTER_Tx_DMA_Channel DMA1 Channel1 configuration ---------------------------------------------*/ + DMA_StructInit(&spi_bus_obj[i].dma.TX_DMA_ChInitStr); + spi_bus_obj[i].dma.TX_DMA_ChInitStr.PeriphAddr = (uint32_t)&spi_bus_obj[i].config->SPIx->DAT; + spi_bus_obj[i].dma.TX_DMA_ChInitStr.MemAddr = 0; + spi_bus_obj[i].dma.TX_DMA_ChInitStr.Direction = DMA_DIR_PERIPH_DST; + spi_bus_obj[i].dma.TX_DMA_ChInitStr.BufSize = 0; + spi_bus_obj[i].dma.TX_DMA_ChInitStr.PeriphInc = DMA_PERIPH_INC_DISABLE; + spi_bus_obj[i].dma.TX_DMA_ChInitStr.MemoryInc = DMA_MEM_INC_ENABLE; + spi_bus_obj[i].dma.TX_DMA_ChInitStr.PeriphDataSize = DMA_PERIPH_DATA_WIDTH_BYTE; + spi_bus_obj[i].dma.TX_DMA_ChInitStr.MemDataSize = DMA_MEM_DATA_WIDTH_BYTE; + spi_bus_obj[i].dma.TX_DMA_ChInitStr.CircularMode = DMA_MODE_NORMAL; + spi_bus_obj[i].dma.TX_DMA_ChInitStr.Priority = DMA_PRIORITY_HIGH; + spi_bus_obj[i].dma.TX_DMA_ChInitStr.Mem2Mem = DMA_M2M_DISABLE; +#if defined(SOC_SERIES_N32H49x) + spi_bus_obj[i].dma.TX_DMA_ChInitStr.BurstCmd = DMA_BURST_DISABLE; +#endif + /* Initialize the specified DMA channel and Whether the specified channel was successfully initialized */ + DMA_Init(spi_bus_obj[i].config->dma_tx->DMAChx, &spi_bus_obj[i].dma.TX_DMA_ChInitStr); + + /* Enable transfer complete interrupt */ + DMA_ConfigInt(spi_bus_obj[i].config->dma_tx->DMAChx, DMA_INT_TXC, ENABLE); + + spi_bus_obj[i].dma.DMA_Tx_Init = RT_TRUE; + + #endif } @@ -1459,6 +2605,8 @@ static void spi_isr(struct n32_spi *spi_drv) { #if defined(SOC_SERIES_N32H7xx) DMA_ChannelEventCmd(spi_drv->config->dma_tx->Instance, spi_drv->config->dma_tx->dma_channel, DMA_CH_EVENT_TRANSFER_COMPLETE, DISABLE); +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + DMA_ConfigInt(spi_drv->config->dma_tx->DMAChx, DMA_INT_TXC, DISABLE); #endif } @@ -1466,6 +2614,8 @@ static void spi_isr(struct n32_spi *spi_drv) { #if defined(SOC_SERIES_N32H7xx) DMA_ChannelEventCmd(spi_drv->config->dma_rx->Instance, spi_drv->config->dma_rx->dma_channel, DMA_CH_EVENT_TRANSFER_COMPLETE, DISABLE); +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + DMA_ConfigInt(spi_drv->config->dma_rx->DMAChx, DMA_INT_TXC, DISABLE); #endif } @@ -1490,6 +2640,78 @@ static void spi_rx_dma_isr(struct n32_spi *spi_drv) { if (DMA_GetChannelIntTfrStatus(spi_drv->config->dma_rx->Instance, spi_drv->config->dma_rx->dma_channel) == SET) { +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + DMA_Module *dma_module = (DMA_Module *)((uint32_t)spi_drv->config->dma_rx->DMAChx < DMA2_BASE ? DMA1 : DMA2); + uint32_t dma_int_tc = 0; + + /* Obtain the corresponding interrupt flag macro based on the channel */ + if (spi_drv->config->dma_rx->DMAChx == DMA1_CH1) + { + dma_int_tc = DMA_INT_TXC1; + } + else if (spi_drv->config->dma_rx->DMAChx == DMA1_CH2) + { + dma_int_tc = DMA_INT_TXC2; + } + else if (spi_drv->config->dma_rx->DMAChx == DMA1_CH3) + { + dma_int_tc = DMA_INT_TXC3; + } + else if (spi_drv->config->dma_rx->DMAChx == DMA1_CH4) + { + dma_int_tc = DMA_INT_TXC4; + } + else if (spi_drv->config->dma_rx->DMAChx == DMA1_CH5) + { + dma_int_tc = DMA_INT_TXC5; + } + else if (spi_drv->config->dma_rx->DMAChx == DMA1_CH6) + { + dma_int_tc = DMA_INT_TXC6; + } + else if (spi_drv->config->dma_rx->DMAChx == DMA1_CH7) + { + dma_int_tc = DMA_INT_TXC7; + } + else if (spi_drv->config->dma_rx->DMAChx == DMA1_CH8) + { + dma_int_tc = DMA_INT_TXC8; + } + else if (spi_drv->config->dma_rx->DMAChx == DMA2_CH1) + { + dma_int_tc = DMA_INT_TXC1; + } + else if (spi_drv->config->dma_rx->DMAChx == DMA2_CH2) + { + dma_int_tc = DMA_INT_TXC2; + } + else if (spi_drv->config->dma_rx->DMAChx == DMA2_CH3) + { + dma_int_tc = DMA_INT_TXC3; + } + else if (spi_drv->config->dma_rx->DMAChx == DMA2_CH4) + { + dma_int_tc = DMA_INT_TXC4; + } + else if (spi_drv->config->dma_rx->DMAChx == DMA2_CH5) + { + dma_int_tc = DMA_INT_TXC5; + } + else if (spi_drv->config->dma_rx->DMAChx == DMA2_CH6) + { + dma_int_tc = DMA_INT_TXC6; + } + else if (spi_drv->config->dma_rx->DMAChx == DMA2_CH7) + { + dma_int_tc = DMA_INT_TXC7; + } + else if (spi_drv->config->dma_rx->DMAChx == DMA2_CH8) + { + dma_int_tc = DMA_INT_TXC8; + } + + if (dma_int_tc != 0 && DMA_GetIntStatus(dma_int_tc, dma_module) == SET) + { #endif if (spi_drv->Direct == SPI_Tx_Rx) { @@ -1498,10 +2720,22 @@ static void spi_rx_dma_isr(struct n32_spi *spi_drv) #if defined(SOC_SERIES_N32H7xx) DMA_ChannelEventCmd(spi_drv->config->dma_rx->Instance, spi_drv->config->dma_rx->dma_channel, DMA_CH_EVENT_TRANSFER_COMPLETE, DISABLE); DMA_ChannelEventCmd(spi_drv->config->dma_tx->Instance, spi_drv->config->dma_tx->dma_channel, DMA_CH_EVENT_TRANSFER_COMPLETE, DISABLE); + /* Disable the RX/TX DMA channels: avoid leftover moves that + * RDMAEN would re-trigger after the slave completes */ + DMA_ChannelCmd(spi_drv->config->dma_rx->Instance, spi_drv->config->dma_rx->dma_channel, DISABLE); + DMA_ChannelCmd(spi_drv->config->dma_tx->Instance, spi_drv->config->dma_tx->dma_channel, DISABLE); +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + DMA_ConfigInt(spi_drv->config->dma_rx->DMAChx, DMA_INT_TXC, DISABLE); + DMA_ConfigInt(spi_drv->config->dma_tx->DMAChx, DMA_INT_TXC, DISABLE); #endif - if (spi_drv->SPI_InitStructure.SpiMode == SPI_MODE_MASTER && spi_drv->SPI_InitStructure.DataDirection == SPI_DIR_DOUBLELINE_FULLDUPLEX) + if (spi_drv->SPI_InitStructure.DataDirection == SPI_DIR_DOUBLELINE_FULLDUPLEX) { + /* After a full-duplex DMA completes, both master and + * slave must clear the SPI DMA enables: if RDMAEN/TDMAEN + * linger on the slave, a live DMA request would move the + * stale DAT byte of the next case -> the slave's first + * byte misaligns (rx[0] old value + right shift). */ SPI_I2S_EnableDma(spi_drv->config->SPIx, SPI_I2S_DMA_TX | SPI_I2S_DMA_RX, DISABLE); } @@ -1516,7 +2750,20 @@ static void spi_rx_dma_isr(struct n32_spi *spi_drv) if (spi_drv->SPI_InitStructure.SpiMode == SPI_MODE_MASTER && (spi_drv->SPI_InitStructure.DataDirection == SPI_DIR_SINGLELINE_RX || spi_drv->SPI_InitStructure.DataDirection == SPI_DIR_DOUBLELINE_RONLY)) { - SPI_Enable(spi_drv->config->SPIx, DISABLE); + if (n32_spi_mode2_3w_master(spi_drv)) + { + /* Always-on mode2 3-wire master: the RX DMA is done, + * but the receiver free-runs until the direction + * leaves RX - flip back to TX (bit14 set, engine + * stays on; dropping SPIEN would pull SCK low into + * the enabled slave) so the next message starts from + * a quiet idle-high line. */ + n32_spi_mode2_3w_set_dir(spi_drv, SPI_DIR_SINGLELINE_TX); + } + else + { + SPI_Enable(spi_drv->config->SPIx, DISABLE); + } } SPI_I2S_EnableDma(spi_drv->config->SPIx, SPI_I2S_DMA_RX, DISABLE); @@ -1525,6 +2772,8 @@ static void spi_rx_dma_isr(struct n32_spi *spi_drv) #if defined(SOC_SERIES_N32H7xx) DMA_ChannelEventCmd(spi_drv->config->dma_rx->Instance, spi_drv->config->dma_rx->dma_channel, DMA_CH_EVENT_TRANSFER_COMPLETE, DISABLE); +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + DMA_ConfigInt(spi_drv->config->dma_rx->DMAChx, DMA_INT_TXC, DISABLE); #endif SPI_I2S_ReceiveData(spi_drv->config->SPIx); @@ -1537,8 +2786,11 @@ static void spi_rx_dma_isr(struct n32_spi *spi_drv) #if defined(SOC_SERIES_N32H7xx) /* Clear interrupt event status */ DMA_ClearChannelEventStatus(spi_drv->config->dma_rx->Instance, spi_drv->config->dma_rx->dma_channel, DMA_CH_EVENT_TRANSFER_COMPLETE); -#endif } +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + /* Clear interrupt event status */ + DMA_ClrIntPendingBit(dma_int_tc, dma_module); +#endif } } #endif @@ -1550,12 +2802,89 @@ static void spi_tx_dma_isr(struct n32_spi *spi_drv) { if (DMA_GetChannelIntTfrStatus(spi_drv->config->dma_tx->Instance, spi_drv->config->dma_tx->dma_channel) == SET) { +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + DMA_Module *dma_module = (DMA_Module *)((uint32_t)spi_drv->config->dma_tx->DMAChx < DMA2_BASE ? DMA1 : DMA2); + uint32_t dma_int_tc = 0; + + if (spi_drv->config->dma_tx->DMAChx == DMA1_CH1) + { + dma_int_tc = DMA_INT_TXC1; + } + else if (spi_drv->config->dma_tx->DMAChx == DMA1_CH2) + { + dma_int_tc = DMA_INT_TXC2; + } + else if (spi_drv->config->dma_tx->DMAChx == DMA1_CH3) + { + dma_int_tc = DMA_INT_TXC3; + } + else if (spi_drv->config->dma_tx->DMAChx == DMA1_CH4) + { + dma_int_tc = DMA_INT_TXC4; + } + else if (spi_drv->config->dma_tx->DMAChx == DMA1_CH5) + { + dma_int_tc = DMA_INT_TXC5; + } + else if (spi_drv->config->dma_tx->DMAChx == DMA1_CH6) + { + dma_int_tc = DMA_INT_TXC6; + } + else if (spi_drv->config->dma_tx->DMAChx == DMA1_CH7) + { + dma_int_tc = DMA_INT_TXC7; + } + else if (spi_drv->config->dma_tx->DMAChx == DMA1_CH8) + { + dma_int_tc = DMA_INT_TXC8; + } + else if (spi_drv->config->dma_tx->DMAChx == DMA2_CH1) + { + dma_int_tc = DMA_INT_TXC1; + } + else if (spi_drv->config->dma_tx->DMAChx == DMA2_CH2) + { + dma_int_tc = DMA_INT_TXC2; + } + else if (spi_drv->config->dma_tx->DMAChx == DMA2_CH3) + { + dma_int_tc = DMA_INT_TXC3; + } + else if (spi_drv->config->dma_tx->DMAChx == DMA2_CH4) + { + dma_int_tc = DMA_INT_TXC4; + } + else if (spi_drv->config->dma_tx->DMAChx == DMA2_CH5) + { + dma_int_tc = DMA_INT_TXC5; + } + else if (spi_drv->config->dma_tx->DMAChx == DMA2_CH6) + { + dma_int_tc = DMA_INT_TXC6; + } + else if (spi_drv->config->dma_tx->DMAChx == DMA2_CH7) + { + dma_int_tc = DMA_INT_TXC7; + } + else if (spi_drv->config->dma_tx->DMAChx == DMA2_CH8) + { + dma_int_tc = DMA_INT_TXC8; + } + + if (dma_int_tc != 0 && DMA_GetIntStatus(dma_int_tc, dma_module) == SET) + { #endif if (spi_drv->Direct == SPI_Tx) { +#if defined(SOC_SERIES_N32H7xx) DMA_ChannelEventCmd(spi_drv->config->dma_tx->Instance, spi_drv->config->dma_tx->dma_channel, DMA_CH_EVENT_TRANSFER_COMPLETE, DISABLE); SPI_I2S_EnableDma(spi_drv->config->SPIx, SPI_I2S_DMA_TX, DISABLE); +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + SPI_I2S_EnableInt(spi_drv->config->SPIx, SPI_I2S_INT_ERR, DISABLE); + + SPI_I2S_EnableDma(spi_drv->config->SPIx, SPI_I2S_DMA_TX, DISABLE); +#endif SPI_I2S_ReceiveData(spi_drv->config->SPIx); SPI_I2S_GetStatus(spi_drv->config->SPIx, SPI_I2S_OVER_FLAG); @@ -1564,10 +2893,16 @@ static void spi_tx_dma_isr(struct n32_spi *spi_drv) rt_completion_done(&spi_drv->cpt); } +#if defined(SOC_SERIES_N32H7xx) /* Clear interrupt event status */ DMA_ClearChannelEventStatus(spi_drv->config->dma_tx->Instance, spi_drv->config->dma_tx->dma_channel, DMA_CH_EVENT_TRANSFER_COMPLETE); } } +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + /* Clear interrupt event status */ + DMA_ClrIntPendingBit(dma_int_tc, dma_module); + } +#endif } #endif @@ -1610,7 +2945,11 @@ void SPI1_TX_DMA_IRQHandler(void) #if defined(BSP_SPI2_TX_USING_DMA) || defined(BSP_SPI2_RX_USING_DMA) +#if defined(SOC_SERIES_N32H7xx) void SPI2_IRQHandler(void) +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) +void SPI2_I2S2_IRQHandler(void) +#endif { /* enter interrupt */ rt_interrupt_enter(); @@ -1648,7 +2987,11 @@ void SPI2_TX_DMA_IRQHandler(void) #if defined(BSP_SPI3_TX_USING_DMA) || defined(BSP_SPI3_RX_USING_DMA) +#if defined(SOC_SERIES_N32H7xx) void SPI3_IRQHandler(void) +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) +void SPI3_I2S3_IRQHandler(void) +#endif { /* enter interrupt */ rt_interrupt_enter(); diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_spi.h b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_spi.h index 073831568b97..5e726fe4e4fe 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_spi.h +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_spi.h @@ -17,6 +17,7 @@ #include #include "drv_dma.h" #include +#include "drv_config.h" #ifdef __cplusplus extern "C" { @@ -47,6 +48,20 @@ struct n32_spi_device #define SPI_USING_RX_DMA_FLAG (1 << 0) #define SPI_USING_TX_DMA_FLAG (1 << 1) +/* Max elements (data items) of a single DMA block (12-bit BlkTfrSize) */ +#define SPI_DMA_BLOCK_MAX (4095U) +/* Number of LLI chain nodes per DMA leg (sizes lli_tx/lli_rx) */ +#define SPI_DMA_CHAIN_NODES (16U) +#define SPI_DMA_CHAIN_MAX (SPI_DMA_BLOCK_MAX * SPI_DMA_CHAIN_NODES) +/* Max elements of a single DMA arm: spixfer chunks every message at this + * size. A N32H7xx DMA arm stalls past ~4128 items at the 20MHz top rate + * (silicon-level: the LLI chain is verified intact and frozen mid-node; + * sharp 4128 PASS / 4136 FAIL boundary, and the ceiling scales with the + * bit clock so a 4096-item arm passes at every rate). 4096 items = 2 LLI + * nodes (4095+1) -- the same per-arm shape as a standalone 4096-element + * message. */ +#define SPI_DMA_ARM_MAX (4096U) + typedef enum { SPI_Tx_Rx = 0U, @@ -64,6 +79,7 @@ struct n32_spi SPI_InitType SPI_InitStructure; SPI_Work_Direct_t Direct; +#if defined(SOC_SERIES_N32H7xx) struct { rt_bool_t DMA_Tx_Init; @@ -71,9 +87,40 @@ struct n32_spi rt_bool_t DMA_Rx_Init; DMA_ChInitType RX_DMA_ChInitStr; + + /* LLI node arrays: seamless single-task DMA for > SPI_DMA_BLOCK_MAX */ + DMA_LinkListItemType lli_rx[SPI_DMA_CHAIN_NODES]; + DMA_LinkListItemType lli_tx[SPI_DMA_CHAIN_NODES]; } dma; +#elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + struct + { + rt_bool_t DMA_Tx_Init; + DMA_InitType TX_DMA_ChInitStr; + + rt_bool_t DMA_Rx_Init; + DMA_InitType RX_DMA_ChInitStr; + } dma; +#endif rt_uint8_t spi_dma_flag; + /* TRUE while a 4-wire full-duplex MASTER receive-only message is being + * armed: the TX DMA source is then the static 0xFF fill buffer, and + * n32_spi_dma_arm re-points every LLI TX node at its base. Read only + * inside n32_spi_dma_arm; set/cleared around the SPI_DMA_TransmitReceive + * call in SPI_DMA_Receive. Per-bus messages are serialized by the + * RT-Thread bus lock and no ISR reads the flag, so a plain rt_bool_t is + * race-free. Unused on non-H7 series. */ + rt_bool_t fd_rxonly_dummy_tx; + /* TRUE while a non-first chunk of a 4-wire full-duplex message is being + * armed: spixfer keeps the SPI engine on across chunk boundaries (only + * the message tail disables), so SPI_DMA_TransmitReceive re-arms the + * live engine warm (no SPIEN drop/re-enable) instead of cold-starting + * it. Read only inside SPI_DMA_TransmitReceive; set at the top of every + * chunk in spixfer. Per-bus messages are serialized by the RT-Thread + * bus lock and no ISR reads the flag, so a plain rt_bool_t is + * race-free. Unused on non-H7 series. */ + rt_bool_t fd_chunk_cont; struct rt_spi_bus spi_bus; struct rt_completion cpt; diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_usart.c b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_usart.c index 04b74d68f5cf..99406391d3db 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_usart.c +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_usart.c @@ -161,36 +161,66 @@ static void dma_recv_callback(struct rt_serial_device *serial, rt_uint8_t isr_fl #if defined(SOC_SERIES_N32H7xx) - rt_size_t read_len; - read_len = DMA_GetTransferredNumber(uart->config->dma_rx->Instance, uart->config->dma_rx->dma_channel); - + /* BTS (per-block byte count in CHNCTRL) is only valid within the current + * LLI block: the hardware loads the next block and wraps BTS the instant a + * block completes. Reading BTS inside the TC ISR may therefore see the + * already-wrapped value, and diffing it against the last value would + * produce a huge underflow count that permanently corrupts the FIFO put + * index. Correct scheme: + * - TC ISR only accumulates "actual bytes completed" (no BTS read, no RX + * report; the DMA IRQ has a higher priority than the UART IRQ, so the + * accumulated value is always up to date when the IDLE handler runs); + * - BTS is read only at IDLE (end of frame): + * cumulative = completed-block bytes + BTS + * bytes since last IDLE = cumulative difference. + * NOTE: the last LLI item of the ring may be shorter than 4095B + * (remain_len), so the TC ISR must add each block's real size by its index, + * otherwise the counter drifts per ring turn (16384B buffer -> 4091B). */ switch (isr_flag) { case UART_RX_DMA_IT_IDLE_FLAG: { - if (uart->dma.rx_dma.block_has_recv_cnt != 0) + rt_uint32_t read_len; + rt_uint32_t cum; + rt_uint32_t delta; + + read_len = DMA_GetTransferredNumber(uart->config->dma_rx->Instance, uart->config->dma_rx->dma_channel); + cum = uart->dma.rx_dma.lli_byte_base + read_len; + delta = cum - uart->dma.rx_dma.lli_last_cum; /* u32 wrap-safe subtraction */ + uart->dma.rx_dma.lli_last_cum = cum; + + /* A frame must fit into the FIFO/ring buffer, otherwise the DMA has + * already overwritten it: an illegal state, drop the report */ + if (delta > 0 && delta <= serial->config.bufsz) { - recv_len = read_len - uart->dma.rx_dma.block_has_recv_cnt; + recv_len = delta; } - else + else if (delta > 0) { - recv_len = read_len; + LOG_E("%s RX DMA delta %u > bufsz %u, drop", uart->config->name, delta, serial->config.bufsz); } - uart->dma.rx_dma.block_has_recv_cnt = read_len; - break; } case UART_RX_DMA_IT_BLOCK_TC_FLAG: { - if (uart->dma.rx_dma.block_has_recv_cnt != 0) - { - recv_len = read_len - uart->dma.rx_dma.block_has_recv_cnt; - uart->dma.rx_dma.block_has_recv_cnt = 0; - } - else + /* Add the real size of the block that just completed: the tail item + * is remain_len bytes, all other items are DMA_BLOCK_SIZE bytes */ + rt_uint16_t blk_num = uart->dma.rx_dma.block_num; + + if (blk_num > 0) { - recv_len = DMA_BLOCK_SIZE; + rt_uint16_t blk_idx = (rt_uint16_t)(uart->dma.rx_dma.lli_blk_cnt % blk_num); + + if ((blk_idx == (rt_uint16_t)(blk_num - 1)) && uart->dma.rx_dma.remain_len != 0) + { + uart->dma.rx_dma.lli_byte_base += uart->dma.rx_dma.remain_len; + } + else + { + uart->dma.rx_dma.lli_byte_base += DMA_BLOCK_SIZE; + } } + uart->dma.rx_dma.lli_blk_cnt++; break; } @@ -950,7 +980,9 @@ static rt_err_t n32_configure(struct rt_serial_device *serial, struct serial_con if (!(serial->parent.open_flag & RT_DEVICE_OFLAG_OPEN)) { #if defined(SOC_SERIES_N32H7xx) - uart->dma.rx_dma.block_has_recv_cnt = 0; + uart->dma.rx_dma.lli_blk_cnt = 0; + uart->dma.rx_dma.lli_byte_base = 0; + uart->dma.rx_dma.lli_last_cum = 0; #elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) uart->dma.rx_dma.remaining_cnt = cfg->bufsz; #endif /* SOC_SERIES_N32H7xx */ @@ -2194,7 +2226,7 @@ int rt_hw_usart_init(void) uart_obj[i].dma.rx_dma.Read_LinkList[j].pNext = &uart_obj[i].dma.rx_dma.Read_LinkList[j + 1U]; } - /* The last block and block size ≠ BLOCK_SIZE */ + /* The last block and block size != BLOCK_SIZE */ if ((j + 1U) == uart_obj[i].dma.rx_dma.block_num && uart_obj[i].dma.rx_dma.remain_len != 0U) { uart_obj[i].dma.rx_dma.Read_LinkList[j].BlkTfrSize = uart_obj[i].dma.rx_dma.remain_len; diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_usart.h b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_usart.h index 8abb2b16e436..45775cd5149b 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_usart.h +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_usart.h @@ -73,7 +73,25 @@ struct n32_uart { rt_uint16_t block_num; rt_uint16_t remain_len; - rt_size_t block_has_recv_cnt; + /* LLI ring accounting (maintained in the TC ISR; BTS is never read + * here: the hardware wraps BTS the instant a block completes, so + * reading BTS at TC and diffing it against the last value would + * underflow and permanently corrupt the FIFO put index): + * lli_blk_cnt number of completed LLI blocks (also used as + * the block index, modulo block_num) + * lli_byte_base actual bytes accumulated for completed blocks. + * NOTE: the last LLI item may be shorter than + * 4095B (remain_len; 4B when bufsz = 16384), so + * the TC ISR must add each block's real size. + * Otherwise the counter drifts +4095-remain_len + * bytes per ring turn (e.g. +4091B per turn for + * a 16384B buffer), eventually corrupting the + * FIFO read pointer. + * lli_last_cum cumulative bytes at the last IDLE report (the + * difference between two reports = frame len) */ + rt_uint32_t lli_blk_cnt; + rt_uint32_t lli_byte_base; + rt_uint32_t lli_last_cum; DMA_LinkListItemType *Read_LinkList; } rx_dma; diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_wdt.c b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_wdt.c index f779883524d0..b73f599a6b33 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_wdt.c +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_wdt.c @@ -14,6 +14,14 @@ #ifdef RT_USING_WDT +#if defined(SOC_SERIES_N32H47x_48x) +#include "n32h47x_48x_iwdg.h" +#elif defined(SOC_SERIES_N32H49x) +#include "n32h49x_iwdg.h" +#elif defined(SOC_SERIES_N32H7xx) +#include "n32h7xx_iwdg.h" +#endif + //#define DRV_DEBUG #define LOG_TAG "drv.wdt" #include @@ -58,7 +66,9 @@ static rt_uint32_t wdt_get_prescaler_factor(rt_uint16_t prescaler) static rt_err_t wdt_init(rt_watchdog_t *wdt) { +#if defined(SOC_SERIES_N32H7xx) n32_wdt.IWDGX_ClkCmd(n32_wdt.IWDG_Clk, ENABLE); +#endif return RT_EOK; } @@ -73,7 +83,11 @@ static rt_err_t wdt_control(rt_watchdog_t *wdt, int cmd, void *arg) { /* feed the watchdog */ case RT_DEVICE_CTRL_WDT_KEEPALIVE: +#if defined(SOC_SERIES_N32H7xx) IWDG_ReloadKey(n32_wdt.IWDGx); +#elif defined(SOC_SERIES_N32H47x_48x) || defined(SOC_SERIES_N32H49x) + IWDG_ReloadKey(); +#endif break; /* set watchdog timeout */ @@ -86,6 +100,11 @@ static rt_err_t wdt_control(rt_watchdog_t *wdt, int cmd, void *arg) } reload_value = (*(rt_uint32_t *)arg); + if (reload_value == 0U) + { + LOG_W("wdg timeout must be greater than 0 s\n"); + return -RT_EINVAL; + } prescaler_factor = wdt_get_prescaler_factor(n32_wdt.Prescaler); if (LSI_VALUE) @@ -104,6 +123,7 @@ static rt_err_t wdt_control(rt_watchdog_t *wdt, int cmd, void *arg) reload_value = (reload_value * (LSI_VALUE / prescaler_factor)) - 1U; /* Enable write access to IWDG_PR and IWDG_RLR registers */ +#if defined(SOC_SERIES_N32H7xx) IWDG_WriteConfig(n32_wdt.IWDGx, IWDG_WRITE_ENABLE); /* Set IWDG PREDIV */ IWDG_SetPrescalerDiv(n32_wdt.IWDGx, n32_wdt.Prescaler); @@ -114,6 +134,18 @@ static rt_err_t wdt_control(rt_watchdog_t *wdt, int cmd, void *arg) /* Disable write access to IWDG_PR and IWDG_RLR registers */ IWDG_WriteConfig(n32_wdt.IWDGx, IWDG_WRITE_DISABLE); +#elif defined(SOC_SERIES_N32H47x_48x) || defined(SOC_SERIES_N32H49x) + IWDG_WriteConfig(IWDG_WRITE_ENABLE); + /* Set IWDG PREDIV */ + IWDG_SetPrescalerDiv(n32_wdt.Prescaler); + /* Set Counter Reload Value */ + IWDG_CntReload((uint16_t)reload_value); + /* Reload IWDG counter */ + IWDG_ReloadKey(); + + /* Disable write access to IWDG_PR and IWDG_RLR registers */ + IWDG_WriteConfig(IWDG_WRITE_DISABLE); +#endif } } else @@ -135,7 +167,11 @@ static rt_err_t wdt_control(rt_watchdog_t *wdt, int cmd, void *arg) LOG_W("IWDG is already started\n"); return RT_EOK; } +#if defined(SOC_SERIES_N32H7xx) IWDG_Enable(n32_wdt.IWDGx); +#elif defined(SOC_SERIES_N32H47x_48x) || defined(SOC_SERIES_N32H49x) + IWDG_Enable(); +#endif n32_wdt.is_start = 1; break; diff --git a/bsp/n32/n32hxxx/n32h487zgl7-evb/.config b/bsp/n32/n32hxxx/n32h487zgl7-evb/.config index 4d4870d53578..6bb204b3bae3 100644 --- a/bsp/n32/n32hxxx/n32h487zgl7-evb/.config +++ b/bsp/n32/n32hxxx/n32h487zgl7-evb/.config @@ -158,15 +158,14 @@ CONFIG_RT_USING_MESSAGEQUEUE=y # # Memory Management # -CONFIG_RT_USING_MEMPOOL=y -# CONFIG_RT_USING_SMALL_MEM is not set +# CONFIG_RT_USING_MEMPOOL is not set +CONFIG_RT_USING_SMALL_MEM=y # CONFIG_RT_USING_SLAB is not set CONFIG_RT_USING_MEMHEAP=y CONFIG_RT_MEMHEAP_FAST_MODE=y # CONFIG_RT_MEMHEAP_BEST_MODE is not set -# CONFIG_RT_USING_SMALL_MEM_AS_HEAP is not set -CONFIG_RT_USING_MEMHEAP_AS_HEAP=y -CONFIG_RT_USING_MEMHEAP_AUTO_BINDING=y +CONFIG_RT_USING_SMALL_MEM_AS_HEAP=y +# CONFIG_RT_USING_MEMHEAP_AS_HEAP is not set # CONFIG_RT_USING_SLAB_AS_HEAP is not set # CONFIG_RT_USING_USERHEAP is not set # CONFIG_RT_USING_NOHEAP is not set @@ -183,7 +182,7 @@ CONFIG_RT_USING_CONSOLE=y CONFIG_RT_CONSOLEBUF_SIZE=512 CONFIG_RT_CONSOLE_DEVICE_NAME="uart1" CONFIG_RT_USING_CONSOLE_OUTPUT_CTL=y -CONFIG_RT_VER_NUM=0x50300 +CONFIG_RT_VER_NUM=0x50301 # CONFIG_RT_USING_STDC_ATOMIC is not set CONFIG_RT_BACKTRACE_LEVEL_MAX_NR=32 # end of RT-Thread Kernel @@ -584,6 +583,7 @@ CONFIG_RT_LIBC_TZ_DEFAULT_SEC=0 # CONFIG_PKG_USING_GUIENGINE is not set # CONFIG_PKG_USING_PERSIMMON is not set # CONFIG_PKG_USING_3GPP_AMRNB is not set +# CONFIG_PKG_USING_LITETYPE is not set # end of multimedia packages # @@ -1032,6 +1032,7 @@ CONFIG_PKG_N32H47X_48X_CMSIS_DRIVER_VER="latest" # CONFIG_PKG_USING_QMI8658 is not set # CONFIG_PKG_USING_ICM20948 is not set # CONFIG_PKG_USING_SCD4X is not set +# CONFIG_PKG_USING_BMI270 is not set # end of sensors drivers # @@ -1086,6 +1087,7 @@ CONFIG_PKG_N32H47X_48X_CMSIS_DRIVER_VER="latest" # CONFIG_PKG_USING_WK2124 is not set # CONFIG_PKG_USING_LY68L6400 is not set # CONFIG_PKG_USING_DM9051 is not set +# CONFIG_PKG_USING_CH390 is not set # CONFIG_PKG_USING_SSD1306 is not set # CONFIG_PKG_USING_QKEY is not set # CONFIG_PKG_USING_RS485 is not set @@ -1154,6 +1156,9 @@ CONFIG_PKG_N32H47X_48X_CMSIS_DRIVER_VER="latest" # CONFIG_PKG_USING_NAXOS is not set # CONFIG_PKG_USING_R_TINYMAIX is not set # CONFIG_PKG_USING_LLMCHAT is not set +# CONFIG_PKG_USING_LIME_TM is not set +# CONFIG_PKG_USING_AIFES is not set +# CONFIG_PKG_USING_AGENT is not set # end of AI packages # @@ -1488,6 +1493,8 @@ CONFIG_SOC_SERIES_N32H47x_48x=y # # Onboard Peripheral Drivers # +# CONFIG_BSP_USING_ETH is not set +# end of Onboard Peripheral Drivers # # On-chip Peripheral Drivers @@ -1499,9 +1506,20 @@ CONFIG_BSP_USING_UART1=y # CONFIG_BSP_UART1_TX_USING_DMA is not set # CONFIG_BSP_USING_UART2 is not set # CONFIG_BSP_USING_UART3 is not set +# CONFIG_BSP_USING_UART4 is not set +# CONFIG_BSP_USING_UART5 is not set +# CONFIG_BSP_USING_UART6 is not set +# CONFIG_BSP_USING_UART7 is not set +# CONFIG_BSP_USING_UART8 is not set +# CONFIG_BSP_USING_SPI is not set # CONFIG_BSP_USING_ON_CHIP_FLASH is not set +# CONFIG_BSP_USING_QSPI is not set +# CONFIG_BSP_USING_HARD_I2C is not set # CONFIG_BSP_USING_ONCHIP_RTC is not set +# CONFIG_BSP_USING_WDT is not set # CONFIG_BSP_USING_FDCAN is not set +# CONFIG_BSP_USING_NAND is not set +# CONFIG_BSP_USING_SDIO is not set # end of On-chip Peripheral Drivers # diff --git a/bsp/n32/n32hxxx/n32h487zgl7-evb/board/Cube_Config/Cube_Config.ntfx b/bsp/n32/n32hxxx/n32h487zgl7-evb/board/Cube_Config/Cube_Config.ntfx index 468240de0ee4..73388dca5007 100644 Binary files a/bsp/n32/n32hxxx/n32h487zgl7-evb/board/Cube_Config/Cube_Config.ntfx and b/bsp/n32/n32hxxx/n32h487zgl7-evb/board/Cube_Config/Cube_Config.ntfx differ diff --git a/bsp/n32/n32hxxx/n32h487zgl7-evb/board/Cube_Config/USER/src/n32h47x_48x_cfg.c b/bsp/n32/n32hxxx/n32h487zgl7-evb/board/Cube_Config/USER/src/n32h47x_48x_cfg.c index 607700155c75..fb6075f7a7bb 100644 --- a/bsp/n32/n32hxxx/n32h487zgl7-evb/board/Cube_Config/USER/src/n32h47x_48x_cfg.c +++ b/bsp/n32/n32hxxx/n32h487zgl7-evb/board/Cube_Config/USER/src/n32h47x_48x_cfg.c @@ -134,7 +134,12 @@ bool RCC_Configuration(void) ClockStatus = RCC_WaitHsiStable(); if (ClockStatus != SUCCESS) return false; - RCC_ConfigPll(RCC_PLL_SRC_HSI,RCC_PLL_PRE_2,RCC_PLL_MUL_60,RCC_PLLOUT_DIV_1); + RCC_ConfigHse(RCC_HSE_ENABLE); + /* Wait till HSE is ready */ + ClockStatus = RCC_WaitHseStable(); + if (ClockStatus != SUCCESS) return false; + + RCC_ConfigPll(RCC_PLL_SRC_HSE,RCC_PLL_PRE_2,RCC_PLL_MUL_60,RCC_PLLOUT_DIV_1); /* Enable PLL */ RCC_EnablePll(ENABLE); /* Wait till PLL is ready */ @@ -177,7 +182,7 @@ bool GPIO_Configuration(void) GPIO_InitStruct(&GPIO_InitStructure); /* Enable the GPIO clock*/ - RCC_EnableAHB1PeriphClk(RCC_AHB_PERIPHEN_GPIOA | RCC_AHB_PERIPHEN_GPIOH, ENABLE); + RCC_EnableAHB1PeriphClk(RCC_AHB_PERIPHEN_GPIOA | RCC_AHB_PERIPHEN_GPIOH | RCC_AHB_PERIPHEN_GPIOC, ENABLE); RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_AFIO,ENABLE); diff --git a/bsp/n32/n32hxxx/n32h487zgl7-evb/board/Kconfig b/bsp/n32/n32hxxx/n32h487zgl7-evb/board/Kconfig index c0b5821609b3..b291cb34f35d 100644 --- a/bsp/n32/n32hxxx/n32h487zgl7-evb/board/Kconfig +++ b/bsp/n32/n32hxxx/n32h487zgl7-evb/board/Kconfig @@ -1,7 +1,57 @@ menu "Hardware Drivers Config" + config BSP_PERIPH_PIN_CFG_READY + bool "Optional peripheral AF pins are configured on this board" + default n + help + The peripheral clock and IO must be configured in + board/Cube_Config/USER/src/n32h47x_48x_cfg.c. + menu "Onboard Peripheral Drivers" + menuconfig BSP_USING_ETH + bool "Enable Ethernet" + default n + depends on BSP_PERIPH_PIN_CFG_READY + select RT_USING_LWIP + if BSP_USING_ETH + choice BSP_ETH_INTERFACE_SEL + prompt "Select Media Interface" + default ETH_INTERFACE_USING_RMII + + config ETH_INTERFACE_USING_MII + bool "MII" + + config ETH_INTERFACE_USING_RMII + bool "RMII" + endchoice + + choice BSP_ETH_PHY_SEL + prompt "Select PHY Chip" + default PHY_USING_DM9162EP + + config PHY_USING_DM9162EP + bool "DM9162EP (10M/100M, MII/RMII)" + + config PHY_USING_LAN8720A + bool "LAN8720A (10M/100M, MII/RMII)" + endchoice + + config BSP_ETH_USING_HW_CHECKSUM + bool "Enable Hardware Checksum" + select RT_LWIP_USING_HW_CHECKSUM + default n + + config PHY_USING_INTERRUPT_MODE + bool "Enable PHY Link Change Interrupt Mode" + default n + if PHY_USING_INTERRUPT_MODE + config PHY_INT_PIN + int "PHY Interrupt Pin Number" + range 0 255 + default 0 + endif + endif endmenu menu "On-chip Peripheral Drivers" @@ -58,12 +108,203 @@ menu "On-chip Peripheral Drivers" depends on BSP_USING_UART3 && RT_SERIAL_USING_DMA default n + config BSP_USING_UART4 + bool "Enable UART4" + default n + + config BSP_UART4_RX_USING_DMA + bool "Enable UART4 RX DMA" + depends on BSP_USING_UART4 && RT_SERIAL_USING_DMA + default n + + config BSP_UART4_TX_USING_DMA + bool "Enable UART4 TX DMA" + depends on BSP_USING_UART4 && RT_SERIAL_USING_DMA + default n + + config BSP_USING_UART5 + bool "Enable UART5" + default n + + config BSP_UART5_RX_USING_DMA + bool "Enable UART5 RX DMA" + depends on BSP_USING_UART5 && RT_SERIAL_USING_DMA + default n + + config BSP_UART5_TX_USING_DMA + bool "Enable UART5 TX DMA" + depends on BSP_USING_UART5 && RT_SERIAL_USING_DMA + default n + + config BSP_USING_UART6 + bool "Enable UART6" + default n + + config BSP_UART6_RX_USING_DMA + bool "Enable UART6 RX DMA" + depends on BSP_USING_UART6 && RT_SERIAL_USING_DMA + default n + + config BSP_UART6_TX_USING_DMA + bool "Enable UART6 TX DMA" + depends on BSP_USING_UART6 && RT_SERIAL_USING_DMA + default n + + config BSP_USING_UART7 + bool "Enable UART7" + default n + + config BSP_UART7_RX_USING_DMA + bool "Enable UART7 RX DMA" + depends on BSP_USING_UART7 && RT_SERIAL_USING_DMA + default n + + config BSP_UART7_TX_USING_DMA + bool "Enable UART7 TX DMA" + depends on BSP_USING_UART7 && RT_SERIAL_USING_DMA + default n + + config BSP_USING_UART8 + bool "Enable UART8" + default n + + config BSP_UART8_RX_USING_DMA + bool "Enable UART8 RX DMA" + depends on BSP_USING_UART8 && RT_SERIAL_USING_DMA + default n + + config BSP_UART8_TX_USING_DMA + bool "Enable UART8 TX DMA" + depends on BSP_USING_UART8 && RT_SERIAL_USING_DMA + default n + + endif + + menuconfig BSP_USING_SPI + bool "Enable SPI BUS" + default n + select RT_USING_SPI + if BSP_USING_SPI + config BSP_USING_SPI1 + bool "Enable SPI1 BUS" + default n + + config BSP_SPI1_TX_USING_DMA + bool "Enable SPI1 TX DMA" + depends on BSP_USING_SPI1 + default n + + config BSP_SPI1_RX_USING_DMA + bool "Enable SPI1 RX DMA" + depends on BSP_USING_SPI1 + select BSP_SPI1_TX_USING_DMA + default n + + config BSP_USING_SPI2 + bool "Enable SPI2 BUS" + default n + + config BSP_SPI2_TX_USING_DMA + bool "Enable SPI2 TX DMA" + depends on BSP_USING_SPI2 + default n + + config BSP_SPI2_RX_USING_DMA + bool "Enable SPI2 RX DMA" + depends on BSP_USING_SPI2 + select BSP_SPI2_TX_USING_DMA + default n + + config BSP_USING_SPI3 + bool "Enable SPI3 BUS" + default n + + config BSP_SPI3_TX_USING_DMA + bool "Enable SPI3 TX DMA" + depends on BSP_USING_SPI3 + default n + + config BSP_SPI3_RX_USING_DMA + bool "Enable SPI3 RX DMA" + depends on BSP_USING_SPI3 + select BSP_SPI3_TX_USING_DMA + default n + endif config BSP_USING_ON_CHIP_FLASH bool "Enable on-chip FLASH" default n + menuconfig BSP_USING_QSPI + bool "Enable QSPI (XSPI) BUS" + default n + depends on BSP_PERIPH_PIN_CFG_READY + select RT_USING_QSPI + select RT_USING_SPI + + menuconfig BSP_USING_HARD_I2C + bool "Enable Hard I2C BUS" + default n + select RT_USING_I2C + if BSP_USING_HARD_I2C + config BSP_USING_HARD_I2C1 + bool "Enable I2C1 BUS" + default n + + config BSP_I2C1_RX_USING_DMA + bool "Enable I2C1 RX DMA" + depends on BSP_USING_HARD_I2C1 + default n + + config BSP_I2C1_TX_USING_DMA + bool "Enable i2c1 TX DMA" + depends on BSP_USING_HARD_I2C1 + default n + + config BSP_USING_HARD_I2C2 + bool "Enable I2C2 BUS" + default n + + config BSP_I2C2_RX_USING_DMA + bool "Enable I2C2 RX DMA" + depends on BSP_USING_HARD_I2C2 + default n + + config BSP_I2C2_TX_USING_DMA + bool "Enable i2c2 TX DMA" + depends on BSP_USING_HARD_I2C2 + default n + + config BSP_USING_HARD_I2C3 + bool "Enable I2C3 BUS" + default n + + config BSP_I2C3_RX_USING_DMA + bool "Enable I2C3 RX DMA" + depends on BSP_USING_HARD_I2C3 + default n + + config BSP_I2C3_TX_USING_DMA + bool "Enable i2c3 TX DMA" + depends on BSP_USING_HARD_I2C3 + default n + + config BSP_USING_HARD_I2C4 + bool "Enable I2C4 BUS" + default n + + config BSP_I2C4_RX_USING_DMA + bool "Enable I2C4 RX DMA" + depends on BSP_USING_HARD_I2C4 + default n + + config BSP_I2C4_TX_USING_DMA + bool "Enable i2c4 TX DMA" + depends on BSP_USING_HARD_I2C4 + default n + endif + menuconfig BSP_USING_ONCHIP_RTC bool "Enable RTC" select RT_USING_RTC @@ -81,6 +322,33 @@ menu "On-chip Peripheral Drivers" endchoice endif + menuconfig BSP_USING_ADC + bool "Enable ADC" + default n + select RT_USING_ADC + if BSP_USING_ADC + config BSP_USING_ADC1 + bool "Enable ADC1" + default n + + config BSP_USING_ADC2 + bool "Enable ADC2" + default n + + config BSP_USING_ADC3 + bool "Enable ADC3" + default n + + config BSP_USING_ADC4 + bool "Enable ADC4" + default n + endif + + config BSP_USING_WDT + bool "Enable Watchdog Timer" + select RT_USING_WDT + default n + menuconfig BSP_USING_FDCAN bool "Enable FDCAN (CAN FD)" default n @@ -383,6 +651,107 @@ menu "On-chip Peripheral Drivers" endif endif + menuconfig BSP_USING_DAC + bool "Enable DAC" + default n + select RT_USING_DAC + if BSP_USING_DAC + config BSP_USING_DAC1 + bool "Enable DAC1" + default n + + config BSP_USING_DAC2 + bool "Enable DAC2" + default n + + config BSP_USING_DAC3 + bool "Enable DAC3" + default n + + config BSP_USING_DAC4 + bool "Enable DAC4" + default n + + config BSP_USING_DAC5 + bool "Enable DAC5" + default n + + config BSP_USING_DAC6 + bool "Enable DAC6" + default n + + config BSP_USING_DAC7 + bool "Enable DAC7" + default n + + config BSP_USING_DAC8 + bool "Enable DAC8" + default n + endif + + menuconfig BSP_USING_NAND + bool "Enable Nand" + default n + depends on BSP_PERIPH_PIN_CFG_READY + select RT_USING_MTD_NAND + if BSP_USING_NAND + choice BSP_NAND_BANK_SEL + prompt "Select Bank" + default BSP_USING_NAND_BANK3 + + config BSP_USING_NAND_BANK2 + bool "USING FEMC NAND BANK2" + + config BSP_USING_NAND_BANK3 + bool "USING FEMC NAND BANK3" + endchoice + + choice BSP_NAND_BUS_WIDTH_SEL + prompt "Select Nand Bus Width" + default BSP_USING_NAND_BUS_WIDTH_8B + + config BSP_USING_NAND_BUS_WIDTH_8B + bool "USING FEMC NAND BUS WIDTH 8BIT" + + config BSP_USING_NAND_BUS_WIDTH_16B + bool "USING FEMC NAND BUS WIDTH 16BIT" + endchoice + + config BSP_NAND_PAGE_SIZE + int "Page Size (bytes)" + default 2048 + help + NAND flash page size in bytes, typically 2048 or 4096. + + config BSP_NAND_OOB_SIZE + int "OOB (Spare Area) Size (bytes)" + default 64 + help + NAND flash spare area size per page, typically 64 or 128. + + config BSP_NAND_PAGES_PER_BLOCK + int "Pages Per Block" + default 64 + help + Number of pages per erase block, typically 64 or 128. + + config BSP_NAND_BLOCK_COUNT + int "Total Block Count" + default 1024 + help + Total number of blocks in the NAND flash chip. + + config BSP_NAND_PLANE_NUM + int "Plane Number" + default 1 + help + Number of planes in the NAND flash chip, typically 1 or 2. + endif + + config BSP_USING_SDIO + bool "Enable SDIO" + default n + source "$BSP_DIR/../libraries/N32_Drivers/drivers/Kconfig" endmenu diff --git a/bsp/n32/n32hxxx/n32h487zgl7-evb/board/SConscript b/bsp/n32/n32hxxx/n32h487zgl7-evb/board/SConscript index fa12bc475122..56260ee6bf49 100644 --- a/bsp/n32/n32hxxx/n32h487zgl7-evb/board/SConscript +++ b/bsp/n32/n32hxxx/n32h487zgl7-evb/board/SConscript @@ -11,6 +11,7 @@ Cube_Config/USER/src/n32h47x_48x_cfg.c path = [cwd] path += [os.path.join(cwd, 'Cube_Config', 'USER', 'inc')] +path += [os.path.join(cwd, 'ports')] CPPDEFINES = ['N32H488', 'CORE_CM4'] diff --git a/bsp/n32/n32hxxx/n32h487zgl7-evb/board/ports/SConscript b/bsp/n32/n32hxxx/n32h487zgl7-evb/board/ports/SConscript index 1948a8d10a53..6b1d35097894 100644 --- a/bsp/n32/n32hxxx/n32h487zgl7-evb/board/ports/SConscript +++ b/bsp/n32/n32hxxx/n32h487zgl7-evb/board/ports/SConscript @@ -7,7 +7,11 @@ cwd = GetCurrentDir() # add general drivers src = [] path = [cwd] - +if GetDepend(['BSP_USING_NAND']): + src += Glob('femc_timing.c') + +if GetDepend(['BSP_USING_ETH']): + src += Glob('phy_reset.c') CPPDEFINES = [''] group = DefineGroup('Drivers', src, depend = [''], CPPPATH = path, CPPDEFINES = CPPDEFINES) diff --git a/bsp/n32/n32hxxx/n32h487zgl7-evb/board/ports/femc_timing.c b/bsp/n32/n32hxxx/n32h487zgl7-evb/board/ports/femc_timing.c new file mode 100644 index 000000000000..77526c0a0a03 --- /dev/null +++ b/bsp/n32/n32hxxx/n32h487zgl7-evb/board/ports/femc_timing.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-08-25 ox-horse first version + */ + +#include "femc_timing.h" + +#ifdef BSP_USING_NAND + +/* + * FEMC NAND timing configuration (N32H47x/48x). + * + * Timing values are in HCLK cycles, taken from the Nations FEMC Nand_Flash + * demo (FEMC_Nand_Init): SET=4, WAIT=6, HOLD=5, HIZ=5, + * TCLR/TAR = CLE/ALE delay of 1 HCLK. + * + * Actual time (ns) = value * HCLK_period (ns). When porting to a different + * system clock, recalculate HCLK period from RCC_Configuration() and adjust + * all values below to satisfy the NAND datasheet timing. + * + * Registers written by FEMC_InitNand(): + * FEMC_NCTRLx <- WaitFeatureEnable | MemDataWidth | EccEnable | + * EccPageSize | TCLRSetTime | TARSetTime + * FEMC_NCMEMTMx <- CommSpaceTimingStruct (timing) + * FEMC_NATTMEMTMx <- AttrSpaceTimingStruct (timing) + */ +void FEMC_Nand_Timing_Config(FEMC_NandInitType *nandInit, + FEMC_NandTimingInitType *timing) +{ + /* Address setup time: 4 HCLK */ + timing->SetTime = FEMC_NAND_SETUP_TIME(0x4); + /* Command assert width: 6 HCLK */ + timing->WaitSetTime = FEMC_NAND_WAIT_TIME(0x6); + /* Address/data hold time: 5 HCLK */ + timing->HoldSetTime = FEMC_NAND_HOLD_TIME(0x5); + /* Data bus HiZ time: 5 HCLK */ + timing->HiZSetTime = FEMC_NAND_HIZ_TIME(0x5); + + nandInit->CommSpaceTimingStruct = timing; + nandInit->AttrSpaceTimingStruct = timing; + + /* CLE to RE delay (TCLR): 1 HCLK */ + nandInit->TCLRSetTime = FEMC_NAND_CLE_DELAY_1HCLK; + /* ALE to RE delay (TAR): 1 HCLK */ + nandInit->TARSetTime = FEMC_NAND_ALE_DELAY_1HCLK; +} + +#endif /* BSP_USING_NAND */ diff --git a/bsp/n32/n32hxxx/n32h487zgl7-evb/board/ports/femc_timing.h b/bsp/n32/n32hxxx/n32h487zgl7-evb/board/ports/femc_timing.h new file mode 100644 index 000000000000..a408416c1a6f --- /dev/null +++ b/bsp/n32/n32hxxx/n32h487zgl7-evb/board/ports/femc_timing.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-08-25 ox-horse first version + */ + +#ifndef __FEMC_TIMING_H__ +#define __FEMC_TIMING_H__ + +#include "board.h" + +/* + * Configure FEMC NAND timing parameters (N32H47x/48x). + * + * This fills the timing-related fields of the NAND init structures: + * - timing: SET/WAIT/HOLD/HIZ (then linked to nandInit's + * CommSpaceTimingStruct / AttrSpaceTimingStruct) + * - nandInit: TCLRSetTime / TARSetTime (CLE/ALE to RE delay) + * + * @note structures are of type FEMC_NandInitType / FEMC_NandTimingInitType + * (N32H47x/48x SDK), which differ from N32H7xx FEMC_TimingInitType. + */ +void FEMC_Nand_Timing_Config(FEMC_NandInitType *nandInit, + FEMC_NandTimingInitType *timing); + +#ifdef __cplusplus +} +#endif + +#endif /* __FEMC_TIMING_H__ */ diff --git a/bsp/n32/n32hxxx/n32h487zgl7-evb/board/ports/phy_reset.c b/bsp/n32/n32hxxx/n32h487zgl7-evb/board/ports/phy_reset.c new file mode 100644 index 000000000000..3230c65234cd --- /dev/null +++ b/bsp/n32/n32hxxx/n32h487zgl7-evb/board/ports/phy_reset.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-06-15 ox-horse first version + */ + +#include +#include + +#define RESET_IO GET_PIN(D, 3) + +void phy_reset(void) +{ + rt_pin_write(RESET_IO, PIN_LOW); + rt_thread_mdelay(50); + rt_pin_write(RESET_IO, PIN_HIGH); +} + +int phy_init(void) +{ + rt_pin_mode(RESET_IO, PIN_MODE_OUTPUT); + rt_pin_write(RESET_IO, PIN_HIGH); + return RT_EOK; +} +INIT_BOARD_EXPORT(phy_init); diff --git a/bsp/n32/n32hxxx/n32h487zgl7-evb/board/ports/usb_config.h b/bsp/n32/n32hxxx/n32h487zgl7-evb/board/ports/usb_config.h new file mode 100644 index 000000000000..cf3dde8044f9 --- /dev/null +++ b/bsp/n32/n32hxxx/n32h487zgl7-evb/board/ports/usb_config.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-08-28 ox-horse first version + */ + +#ifndef CHERRYUSB_CONFIG_H +#define CHERRYUSB_CONFIG_H + +/* ================ USB common Configuration ================ */ + +#ifdef __RTTHREAD__ +#include + +#define CONFIG_USB_PRINTF(...) rt_kprintf(__VA_ARGS__) +#else +#define CONFIG_USB_PRINTF(...) printf(__VA_ARGS__) +#endif + +#ifndef CONFIG_USB_DBG_LEVEL +#define CONFIG_USB_DBG_LEVEL USB_DBG_INFO +#endif + +/* Enable print with color */ +#define CONFIG_USB_PRINTF_COLOR_ENABLE + +/* N32H47x_48x is a Cortex-M4 without cache, so no dcache or dma alignment + * is required, and no noncacheable section is needed. */ +#define CONFIG_USB_ALIGN_SIZE 4 + +#define USB_NOCACHE_RAM_SECTION + +/* use usb_memcpy default for high performance but cost more flash memory. + * And, arm libc has a bug that memcpy() may cause data misalignment when the size is not a multiple of 4. +*/ +// #define CONFIG_USB_MEMCPY_DISABLE + +/* ================= USB Device Stack Configuration ================ */ + +/* Ep0 in and out transfer buffer */ +#ifndef CONFIG_USBDEV_REQUEST_BUFFER_LEN +#define CONFIG_USBDEV_REQUEST_BUFFER_LEN 512 +#endif + +#ifndef CONFIG_USBDEV_MSC_MAX_BUFSIZE +#define CONFIG_USBDEV_MSC_MAX_BUFSIZE 512 +#endif + +#ifndef CONFIG_USBDEV_MAX_BUS +#define CONFIG_USBDEV_MAX_BUS 1 // for now, bus num must be 1 except hpm ip +#endif + +#ifndef CONFIG_USBDEV_EP_NUM +#define CONFIG_USBDEV_EP_NUM 8 +#endif + +/* ================ USB Device Port Configuration ================*/ + +/* FSDEV port (Synopsys USBFS) PMA access mode. The N32H4x USBFS + * (usb_fsdev_reg.h) re-arranges the register map but keeps the buffer + * descriptor table layout that matches the ST macro addressing with + * PMA_ACCESS=2, which is what usb_dc_fsdev.c requires. */ +#define CONFIG_USBDEV_FSDEV_PMA_ACCESS 2 + +#endif diff --git a/bsp/n32/n32hxxx/n32h487zgl7-evb/project.ewp b/bsp/n32/n32hxxx/n32h487zgl7-evb/project.ewp index 3d2a236c17c8..f2fbc5276a12 100644 --- a/bsp/n32/n32hxxx/n32h487zgl7-evb/project.ewp +++ b/bsp/n32/n32hxxx/n32h487zgl7-evb/project.ewp @@ -364,7 +364,6 @@ $PROJ_DIR$\..\..\..\..\libcpu\arm\common $PROJ_DIR$\..\..\..\..\libcpu\arm\cortex-m4 $PROJ_DIR$\..\..\..\..\components\drivers\include - $PROJ_DIR$\..\..\..\..\components\drivers\phy $PROJ_DIR$\..\..\..\..\components\drivers\smp_call $PROJ_DIR$\board $PROJ_DIR$\board\Cube_Config\USER\inc @@ -384,6 +383,7 @@ $PROJ_DIR$\..\..\..\..\components\libc\posix\ipc $PROJ_DIR$\packages\n32h47x_48x_std_driver-latest\n32h47x_48x_std_periph_driver\inc $PROJ_DIR$\packages\n32h47x_48x_cmsis_driver-latest\device + $PROJ_DIR$\applications\test_file $PROJ_DIR$\..\..\..\..\components\net\utest