diff --git a/boards/common/gd32v/include/cfg_spi_default.h b/boards/common/gd32v/include/cfg_spi_default.h new file mode 100644 index 000000000000..c7f28f96cc70 --- /dev/null +++ b/boards/common/gd32v/include/cfg_spi_default.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2020 Koen Zandberg + * 2023 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup boards_common_gd32v + * @{ + * + * @file + * @brief Common peripheral configuration for GD32VF103 boards + * + * @author Koen Zandberg + * @author Gunar Schorcht + */ + +#ifndef CFG_SPI_DEFAULT_H +#define CFG_SPI_DEFAULT_H + +#include "periph_cpu.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name SPI configuration + * @{ + */ +static const spi_conf_t spi_config[] = { + { + .dev = SPI1, + .mosi_pin = GPIO_PIN(PORT_B, 15), + .miso_pin = GPIO_PIN(PORT_B, 14), + .sclk_pin = GPIO_PIN(PORT_B, 13), + .cs_pin = GPIO_PIN(PORT_B, 12), + .rcumask = RCU_APB1EN_SPI1EN_Msk, + .apbbus = APB1, + }, +#ifndef MODULE_PERIPH_ADC + { + .dev = SPI0, + .mosi_pin = GPIO_PIN(PORT_A, 7), + .miso_pin = GPIO_PIN(PORT_A, 6), + .sclk_pin = GPIO_PIN(PORT_A, 5), + .cs_pin = GPIO_PIN(PORT_A, 4), + .rcumask = RCU_APB2EN_SPI0EN_Msk, + .apbbus = APB2, + }, +#endif +}; + +#define SPI_NUMOF ARRAY_SIZE(spi_config) +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* CFG_SPI_DEFAULT_H */ +/** @} */ diff --git a/boards/seeedstudio-gd32/Kconfig b/boards/seeedstudio-gd32/Kconfig index b0dcf5773c7d..46c9ea823073 100644 --- a/boards/seeedstudio-gd32/Kconfig +++ b/boards/seeedstudio-gd32/Kconfig @@ -16,6 +16,7 @@ config BOARD_SEEEDSTUDIO_GD32 select BOARD_HAS_LXTAL select HAS_PERIPH_I2C select HAS_PERIPH_PWM + select HAS_PERIPH_SPI select HAS_PERIPH_TIMER select HAS_PERIPH_UART select HAVE_SAUL_GPIO diff --git a/boards/seeedstudio-gd32/Makefile.features b/boards/seeedstudio-gd32/Makefile.features index 7f8956607c5f..f1a4d92d3583 100644 --- a/boards/seeedstudio-gd32/Makefile.features +++ b/boards/seeedstudio-gd32/Makefile.features @@ -3,6 +3,7 @@ CPU_MODEL = gd32vf103vbt6 # Put defined MCU peripherals here (in alphabetical order) FEATURES_PROVIDED += periph_i2c FEATURES_PROVIDED += periph_pwm +FEATURES_PROVIDED += periph_spi FEATURES_PROVIDED += periph_timer FEATURES_PROVIDED += periph_uart diff --git a/boards/seeedstudio-gd32/doc.txt b/boards/seeedstudio-gd32/doc.txt index 1ddffa1445f7..2419086fa05a 100644 --- a/boards/seeedstudio-gd32/doc.txt +++ b/boards/seeedstudio-gd32/doc.txt @@ -41,7 +41,7 @@ on-board components: | DAC | 2 x 12-bit channel | no | | UART | 2 | yes | | USART | 3 | yes | -| SPI | 3 | no | +| SPI | 3 | yes | | I2C | 2 x Fast Mode 400 kHz | yes | | I2S | 2 | no | | CAN | 2 x CAN 2.0B with up to 1 Mbps | no | @@ -64,9 +64,13 @@ MCU pins and their configuration in RIOT. | MCU Pin | MCU Peripheral | RIOT Peripheral | Board Function | Remark | |:--------|:---------------|:-----------------|:---------------|:-----------------------------| -| PA0 | | BTN0 | KEY1 | | +| PA0 | BOOT0 | BTN0 | KEY1 | | | PA9 | USART0 TX | UART_DEV(0) TX | UART TX | | | PA10 | USART0 RX | UART_DEV(0) RX | UART RX | | +| PA4 | SPI1 CS | SPI_DEV(1) CS | | | +| PA5 | SPI1 SCLK | SPI_DEV(1) SCLK | | | +| PA6 | SPI1 MISO | SPI_DEV(1) MISO | | | +| PA7 | SPI1 MOSI | SPI_DEV(1) MOSI | | | | PB0 | | PWM_DEV(0) CH0 | LED1 green | | | PB1 | | PWM_DEV(0) CH1 | LED2 blue | | | PB5 | | | LED0 red | | @@ -76,9 +80,13 @@ MCU pins and their configuration in RIOT. | PB9 | | PWM_DEV(1) CH1 | | N/A if CAN is used | | PB10 | I2C1 SCL | I2C_DEV(1) SCL | | | | PB11 | I2C1 SDA | I2C_DEV(1) SDA | | | +| PB12 | SPI0 CS | SPI_DEV(0) CS | | | +| PB13 | SPI0 SCLK | SPI_DEV(0) SCLK | | | +| PB14 | SPI0 MISO | SPI_DEV(0) MISO | | | +| PB15 | SPI0 MOSI | SPI_DEV(0) MOSI | | | | PC13 | | BTN1 | KEY2 | | -## Flash the board +## Flashing the Device The board is flashed via a JTAG interface with OpenOCD (at least [release version 0.12.0] (https://github.com/openocd-org/openocd/tree/9ea7f3d647c8ecf6b0f1424002dfc3f4504a162c)). diff --git a/boards/seeedstudio-gd32/include/periph_conf.h b/boards/seeedstudio-gd32/include/periph_conf.h index 64b667b112bb..9a807a24343a 100644 --- a/boards/seeedstudio-gd32/include/periph_conf.h +++ b/boards/seeedstudio-gd32/include/periph_conf.h @@ -37,6 +37,7 @@ #include "periph_common_conf.h" #include "cfg_i2c_default.h" +#include "cfg_spi_default.h" #include "cfg_timer_default.h" #include "cfg_uart_default.h" diff --git a/boards/sipeed-longan-nano/Kconfig b/boards/sipeed-longan-nano/Kconfig index ecd1fa339fc5..7597d38880b5 100644 --- a/boards/sipeed-longan-nano/Kconfig +++ b/boards/sipeed-longan-nano/Kconfig @@ -16,6 +16,7 @@ config BOARD_SIPEED_LONGAN_NANO select BOARD_HAS_LXTAL select HAS_PERIPH_I2C select HAS_PERIPH_PWM + select HAS_PERIPH_SPI select HAS_PERIPH_TIMER select HAS_PERIPH_UART select HAVE_SAUL_GPIO diff --git a/boards/sipeed-longan-nano/Makefile.features b/boards/sipeed-longan-nano/Makefile.features index 1fc54d3a1887..392f64364855 100644 --- a/boards/sipeed-longan-nano/Makefile.features +++ b/boards/sipeed-longan-nano/Makefile.features @@ -3,6 +3,7 @@ CPU_MODEL = gd32vf103cbt6 # Put defined MCU peripherals here (in alphabetical order) FEATURES_PROVIDED += periph_i2c FEATURES_PROVIDED += periph_pwm +FEATURES_PROVIDED += periph_spi FEATURES_PROVIDED += periph_timer FEATURES_PROVIDED += periph_uart diff --git a/boards/sipeed-longan-nano/doc.txt b/boards/sipeed-longan-nano/doc.txt index 43fa392ba4f7..04ec0c4187bc 100644 --- a/boards/sipeed-longan-nano/doc.txt +++ b/boards/sipeed-longan-nano/doc.txt @@ -35,7 +35,7 @@ on-board components: | DAC | 2 x 12-bit channel | no | | UART | - | yes | | USART | 3 | yes | -| SPI | 3 | no | +| SPI | 3 | yes | | I2C | 2 x Fast Mode 400 kHz | yes | | I2S | 2 | no | | CAN | 2 x CAN 2.0B with up to 1 Mbps | no | @@ -58,9 +58,13 @@ MCU pins and their configuration in RIOT. | MCU Pin | MCU Peripheral | RIOT Peripheral | Board Function | Remark | |:--------|:---------------|:-----------------|:---------------|:-----------------------------| -| PA0 | BOOT0 | | BTN0 | | +| PA0 | BOOT0 | BTN0 | BOOT | | | PA1 | | PWM_DEV(0) CH0 | LED1 green | | | PA2 | | PWM_DEV(0) CH1 | LED2 blue | | +| PA4 | SPI1 CS | SPI_DEV(1) CS | | | +| PA5 | SPI1 SCLK | SPI_DEV(1) SCLK | | | +| PA6 | SPI1 MISO | SPI_DEV(1) MISO | | | +| PA7 | SPI1 MOSI | SPI_DEV(1) MOSI | | | | PA9 | USART0 TX | UART_DEV(0) TX | UART TX | | | PA10 | USART0 RX | UART_DEV(0) RX | UART RX | | | PB6 | I2C0 SCL | I2C_DEV(0) SCL | | | @@ -69,6 +73,10 @@ MCU pins and their configuration in RIOT. | PB9 | | PWM_DEV(1) CH1 | | N/A if CAN is used | | PB10 | I2C1 SCL | I2C_DEV(1) SCL | | | | PB11 | I2C1 SDA | I2C_DEV(1) SDA | | | +| PB12 | SPI0 CS | SPI_DEV(0) CS | | | +| PB13 | SPI0 SCLK | SPI_DEV(0) SCLK | | | +| PB14 | SPI0 MISO | SPI_DEV(0) MISO | | | +| PB15 | SPI0 MOSI | SPI_DEV(0) MOSI | | | | PC13 | | | LED0 red | | ## Flashing the Device diff --git a/boards/sipeed-longan-nano/include/board.h b/boards/sipeed-longan-nano/include/board.h index f72ca46cdb93..f57ffc94fe4f 100644 --- a/boards/sipeed-longan-nano/include/board.h +++ b/boards/sipeed-longan-nano/include/board.h @@ -53,6 +53,27 @@ extern "C" { #define LED_BLUE_PIN LED2_PIN /**< LED2 is blue */ /** @} */ +#if defined(MODULE_SDCARD_SPI) +#define SDCARD_SPI_PARAM_SPI SPI_DEV(0) +#define SDCARD_SPI_PARAM_CS GPIO_PIN(PORT_B, 12) +#define SDCARD_SPI_PARAM_CLK GPIO_PIN(PORT_B, 13) +#define SDCARD_SPI_PARAM_MISO GPIO_PIN(PORT_B, 14) +#define SDCARD_SPI_PARAM_MOSI GPIO_PIN(PORT_B, 15) +#endif + +#if defined(MODULE_ST7735) && defined(CONFIG_SIPEED_LONGAN_NANO_WITH_TFT) +#define ST7735_PARAM_SPI SPI_DEV(1) /**< SPI device */ +#define ST7735_PARAM_SPI_CLK SPI_CLK_5MHZ /**< SPI clock frequency */ +#define ST7735_PARAM_SPI_MODE SPI_MODE_0 /**< SPI mode */ +#define ST7735_PARAM_CS GPIO_PIN(PORT_B, 2) /**< Chip Select pin */ +#define ST7735_PARAM_DCX GPIO_PIN(PORT_B, 0) /**< DCX pin */ +#define ST7735_PARAM_RST GPIO_PIN(PORT_B, 1) /**< Reset pin */ +#define ST7735_PARAM_RGB 1 /**< RGB mode enable */ +#define ST7735_PARAM_INVERTED 0 /**< Inverted mode enable */ +#define ST7735_PARAM_NUM_LINES 160U /**< Number of lines */ +#define ST7735_PARAM_RGB_CHANNELS 80U /**< Number of columns */ +#endif + #ifdef __cplusplus } #endif diff --git a/boards/sipeed-longan-nano/include/periph_conf.h b/boards/sipeed-longan-nano/include/periph_conf.h index 7b3ea51c0c2e..3f9027f7a666 100644 --- a/boards/sipeed-longan-nano/include/periph_conf.h +++ b/boards/sipeed-longan-nano/include/periph_conf.h @@ -37,6 +37,7 @@ #include "periph_common_conf.h" #include "cfg_i2c_default.h" +#include "cfg_spi_default.h" #include "cfg_timer_default.h" #include "cfg_uart_default.h" diff --git a/cpu/gd32v/include/periph_cpu.h b/cpu/gd32v/include/periph_cpu.h index 0f953eb04ba9..e0b3438e5e9c 100644 --- a/cpu/gd32v/include/periph_cpu.h +++ b/cpu/gd32v/include/periph_cpu.h @@ -205,22 +205,84 @@ typedef struct { #define UART_ISR_PRIO (2) /** - * @name This CPU makes use of the following shared SPI functions + * @brief Define a magic number that tells us to use hardware chip select + * + * We use a random value here, that does clearly differentiate from any possible + * GPIO_PIN(x) value. + */ +#define SPI_HWCS_MASK (0xffffff00) + +/** + * @brief Override the default SPI hardware chip select access macro + * + * Since the CPU does only support one single hardware chip select line, we can + * detect the usage of non-valid lines by comparing to SPI_HWCS_VALID. + */ +#define SPI_HWCS(x) (SPI_HWCS_MASK | x) + +/** + * @brief Define value for unused CS line + */ +#define SPI_CS_UNDEF (GPIO_UNDEF) + +#ifndef DOXYGEN +/** + * @brief Overwrite the default spi_cs_t type definition * @{ */ -#define PERIPH_SPI_NEEDS_TRANSFER_BYTE 1 -#define PERIPH_SPI_NEEDS_TRANSFER_REG 1 -#define PERIPH_SPI_NEEDS_TRANSFER_REGS 1 +#define HAVE_SPI_CS_T +typedef uint32_t spi_cs_t; +/** @} */ +#endif + +/** + * @brief Use the shared SPI functions + * @{ + */ +/** Use transfer byte function from periph common */ +#define PERIPH_SPI_NEEDS_TRANSFER_BYTE +/** Use transfer reg function from periph common */ +#define PERIPH_SPI_NEEDS_TRANSFER_REG +/** Use transfer regs function from periph common */ +#define PERIPH_SPI_NEEDS_TRANSFER_REGS +/** @} */ + +/** + * @brief Override SPI clock speed values + * @{ + */ +#define HAVE_SPI_CLK_T +enum { + SPI_CLK_100KHZ = KHZ(100), /**< drive the SPI bus with 100KHz */ + SPI_CLK_400KHZ = KHZ(400), /**< drive the SPI bus with 400KHz */ + SPI_CLK_1MHZ = MHZ(1), /**< drive the SPI bus with 1MHz */ + SPI_CLK_5MHZ = MHZ(5), /**< drive the SPI bus with 5MHz */ + SPI_CLK_10MHZ = MHZ(10), /**< drive the SPI bus with 10MHz */ +}; + +/** + * @brief SPI clock type + */ +typedef uint32_t spi_clk_t; /** @} */ /** * @brief Structure for SPI configuration data */ typedef struct { - uint32_t addr; /**< SPI control register address */ - gpio_t mosi; /**< MOSI pin */ - gpio_t miso; /**< MISO pin */ - gpio_t sclk; /**< SCLK pin */ + SPI_Type *dev; /**< SPI device base register address */ + gpio_t mosi_pin; /**< MOSI pin */ + gpio_t miso_pin; /**< MISO pin */ + gpio_t sclk_pin; /**< SCLK pin */ + spi_cs_t cs_pin; /**< HWCS pin, set to SPI_CS_UNDEF if not mapped */ + uint32_t rcumask; /**< bit in the RCC peripheral enable register */ + uint8_t apbbus; /**< APBx bus the device is connected to */ +#ifdef MODULE_PERIPH_DMA + dma_t tx_dma; /**< Logical DMA stream used for TX */ + uint8_t tx_dma_chan; /**< DMA channel used for TX */ + dma_t rx_dma; /**< Logical DMA stream used for RX */ + uint8_t rx_dma_chan; /**< DMA channel used for RX */ +#endif } spi_conf_t; /** diff --git a/cpu/gd32v/include/vendor/gd32vf103_periph.h b/cpu/gd32v/include/vendor/gd32vf103_periph.h index 70cb840f5689..52d0d437eabd 100644 --- a/cpu/gd32v/include/vendor/gd32vf103_periph.h +++ b/cpu/gd32v/include/vendor/gd32vf103_periph.h @@ -11555,9 +11555,9 @@ typedef struct { /*!< (@ 0x40002C00) WWDGT Struct //#define PMU_BASE 0x40007000UL //#define RCU_BASE 0x40021000UL //#define RTC_BASE 0x40002800UL -//#define SPI0_BASE 0x40013000UL -//#define SPI1_BASE 0x40003800UL -//#define SPI2_BASE 0x40003C00UL +#define SPI0_BASE 0x40013000UL +#define SPI1_BASE 0x40003800UL +#define SPI2_BASE 0x40003C00UL #define TIMER0_BASE 0x40012C00UL #define TIMER1_BASE 0x40000000UL #define TIMER2_BASE 0x40000400UL diff --git a/cpu/gd32v/periph/spi.c b/cpu/gd32v/periph/spi.c new file mode 100644 index 000000000000..8cedd7664abb --- /dev/null +++ b/cpu/gd32v/periph/spi.c @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2014 Hamburg University of Applied Sciences + * 2014-2017 Freie UniversitÀt Berlin + * 2016-2017 OTA keys S.A. + * 2023 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_gd32v + * @ingroup drivers_periph_spi + * @{ + * + * @file + * @brief Low-level SPI driver implementation + * + * + * @author Peter Kietzmann + * @author Fabian Nack + * @author Hauke Petersen + * @author Vincent Dupont + * @author Joakim NohlgÄrd + * @author Thomas Eichinger + * @author Gunar Schorcht + * + * @} + */ + +#include + +#include "bitarithm.h" +#include "cpu.h" +#include "mutex.h" +#include "periph/gpio.h" +#include "periph/spi.h" +#include "pm_layered.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +/** + * @brief Number of bits to shift the BR value in the CR1 register + */ +#define BR_SHIFT (3U) +#define BR_MAX (7U) + +#define SPI_CTL1_SETTINGS 0 + +/** + * @brief Allocate one lock per SPI device + */ +static mutex_t locks[SPI_NUMOF]; + +/** + * @brief Clock configuration cache + */ +static uint32_t clocks[SPI_NUMOF]; + +/** + * @brief Clock divider cache + */ +static uint8_t dividers[SPI_NUMOF]; + +static inline SPI_Type *dev(spi_t bus) +{ + return spi_config[bus].dev; +} + +/** + * @brief Multiplier for clock divider calculations + * + * Makes the divider calculation fixed point + */ +#define SPI_APB_CLOCK_SHIFT (4U) +#define SPI_APB_CLOCK_MULT (1U << SPI_APB_CLOCK_SHIFT) + +static uint8_t _get_clkdiv(const spi_conf_t *conf, uint32_t clock) +{ + uint32_t bus_clock = periph_apb_clk(conf->apbbus); + /* Shift bus_clock with SPI_APB_CLOCK_SHIFT to create a fixed point int */ + uint32_t div = (bus_clock << SPI_APB_CLOCK_SHIFT) / (2 * clock); + DEBUG("[spi] clock: divider: %"PRIu32"\n", div); + /* Test if the divider is 2 or smaller, keeping the fixed point in mind */ + if (div <= SPI_APB_CLOCK_MULT) { + return 0; + } + /* determine MSB and compensate back for the fixed point int shift */ + uint8_t rounded_div = bitarithm_msb(div) - SPI_APB_CLOCK_SHIFT; + /* Determine if rounded_div is not a power of 2 */ + if ((div & (div - 1)) != 0) { + /* increment by 1 to ensure that the clock speed at most the + * requested clock speed */ + rounded_div++; + } + return rounded_div > BR_MAX ? BR_MAX : rounded_div; +} + +void spi_init(spi_t bus) +{ + assert(bus < SPI_NUMOF); + + /* initialize device lock */ + mutex_init(&locks[bus]); + /* trigger pin initialization */ + spi_init_pins(bus); + + periph_clk_en(spi_config[bus].apbbus, spi_config[bus].rcumask); + + /* reset configuration */ + dev(bus)->CTL0 = 0; +#ifdef SPI0_I2SCTL_I2SSEL_Msk + dev(bus)->I2SCTL = 0; +#endif + dev(bus)->CTL1 = SPI_CTL1_SETTINGS; + periph_clk_dis(spi_config[bus].apbbus, spi_config[bus].rcumask); +} + +void spi_init_pins(spi_t bus) +{ + if (gpio_is_valid(spi_config[bus].sclk_pin)) { + gpio_init_af(spi_config[bus].sclk_pin, GPIO_AF_OUT_PP); + } + + if (gpio_is_valid(spi_config[bus].mosi_pin)) { + gpio_init_af(spi_config[bus].mosi_pin, GPIO_AF_OUT_PP); + } + + if (gpio_is_valid(spi_config[bus].miso_pin)) { + gpio_init(spi_config[bus].miso_pin, GPIO_IN); + } +} + +int spi_init_cs(spi_t bus, spi_cs_t cs) +{ + if (bus >= SPI_NUMOF) { + return SPI_NODEV; + } + + if (!gpio_is_valid(cs) || + (((cs & SPI_HWCS_MASK) == SPI_HWCS_MASK) && (cs & ~(SPI_HWCS_MASK)))) { + return SPI_NOCS; + } + + if (cs == SPI_HWCS_MASK) { + if (!gpio_is_valid(spi_config[bus].cs_pin)) { + return SPI_NOCS; + } + gpio_init_af(spi_config[bus].cs_pin, GPIO_AF_OUT_PP); + } + else { + gpio_init((gpio_t)cs, GPIO_OUT); + gpio_set((gpio_t)cs); + } + + return SPI_OK; +} + +#ifdef MODULE_PERIPH_SPI_GPIO_MODE +int spi_init_with_gpio_mode(spi_t bus, const spi_gpio_mode_t* mode) +{ + assert(bus < SPI_NUMOF); + + int ret = 0; + + /* This has no effect on GD32VF103 */ + return ret; +} +#endif + +void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) +{ + assert((unsigned)bus < SPI_NUMOF); + + /* lock bus */ + mutex_lock(&locks[bus]); + + /* block DEEP_SLEEP mode */ + pm_block(GD32V_PM_DEEPSLEEP); + + /* enable SPI device clock */ + periph_clk_en(spi_config[bus].apbbus, spi_config[bus].rcumask); + /* enable device */ + if (clk != clocks[bus]) { + dividers[bus] = _get_clkdiv(&spi_config[bus], clk); + clocks[bus] = clk; + } + uint8_t br = dividers[bus]; + + DEBUG("[spi] acquire: requested clock: %"PRIu32", resulting clock: %"PRIu32 + " BR divider: %u\n", + clk, + periph_apb_clk(spi_config[bus].apbbus)/(1 << (br + 1)), + br); + + uint16_t ctl0_settings = ((br << BR_SHIFT) | mode | SPI0_CTL0_MSTMOD_Msk); + /* Settings to add to CTL1 in addition to SPI_CTL1_SETTINGS */ + uint16_t ctl1_extra_settings = 0; + if (cs != SPI_HWCS_MASK) { + ctl0_settings |= (SPI0_CTL0_SWNSSEN_Msk | SPI0_CTL0_SWNSS_Msk); + } + else { + ctl1_extra_settings = (SPI0_CTL1_NSSDRV_Msk); + } + + dev(bus)->CTL0 = ctl0_settings; + /* Only modify CR2 if needed */ + if (ctl1_extra_settings) { + dev(bus)->CTL1 = (SPI_CTL1_SETTINGS | ctl1_extra_settings); + } +} + +void spi_release(spi_t bus) +{ + /* disable device and release lock */ + dev(bus)->CTL0 = 0; + dev(bus)->CTL1 = SPI_CTL1_SETTINGS; /* Clear the DMA and SSOE flags */ + periph_clk_dis(spi_config[bus].apbbus, spi_config[bus].rcumask); + + /* unblock DEEP_SLEEP mode */ + pm_unblock(GD32V_PM_DEEPSLEEP); + + mutex_unlock(&locks[bus]); +} + +static inline void _wait_for_end(spi_t bus) +{ + /* make sure the transfer is completed before continuing, see reference + * manual(s) -> section 'Disabling the SPI' */ + while (!(dev(bus)->STAT & SPI0_STAT_TBE_Msk)) {} + while (dev(bus)->STAT & SPI0_STAT_TRANS_Msk) {} +} + +static void _transfer_no_dma(spi_t bus, const void *out, void *in, size_t len) +{ + const uint8_t *outbuf = out; + uint8_t *inbuf = in; + + /* we need to recast the data register to uint_8 to force 8-bit access */ + volatile uint8_t *DR = (volatile uint8_t*)&(dev(bus)->DATA); + + /* transfer data, use shortpath if only sending data */ + if (!inbuf) { + for (size_t i = 0; i < len; i++) { + while (!(dev(bus)->STAT & SPI0_STAT_TBE_Msk)) {} + *DR = outbuf[i]; + } + /* wait until everything is finished and empty the receive buffer */ + while (!(dev(bus)->STAT & SPI0_STAT_TBE_Msk)) {} + while (dev(bus)->STAT & SPI0_STAT_TRANS_Msk) {} + while (dev(bus)->STAT & SPI0_STAT_RBNE_Msk) { + dev(bus)->DATA; /* we might just read 2 bytes at once here */ + } + } + else if (!outbuf) { + for (size_t i = 0; i < len; i++) { + while (!(dev(bus)->STAT & SPI0_STAT_TBE_Msk)) {} + *DR = 0; + while (!(dev(bus)->STAT & SPI0_STAT_RBNE_Msk)) {} + inbuf[i] = *DR; + } + } + else { + for (size_t i = 0; i < len; i++) { + while (!(dev(bus)->STAT & SPI0_STAT_TBE_Msk)) {} + *DR = outbuf[i]; + while (!(dev(bus)->STAT & SPI0_STAT_RBNE_Msk)) {} + inbuf[i] = *DR; + } + } + + _wait_for_end(bus); +} + +void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont, + const void *out, void *in, size_t len) +{ + /* make sure at least one input or one output buffer is given */ + assert(out || in); + + /* active the given chip select line */ + dev(bus)->CTL0 |= (SPI0_CTL0_SPIEN_Msk); /* this pulls the HW CS line low */ + if ((cs != SPI_HWCS_MASK) && gpio_is_valid(cs)) { + gpio_clear((gpio_t)cs); + } + + _transfer_no_dma(bus, out, in, len); + + /* release the chip select if not specified differently */ + if ((!cont) && gpio_is_valid(cs)) { + dev(bus)->CTL0 &= ~(SPI0_CTL0_SPIEN_Msk); /* pull HW CS line high */ + if (cs != SPI_HWCS_MASK) { + gpio_set((gpio_t)cs); + } + } +} diff --git a/dist/tools/doccheck/generic_exclude_patterns b/dist/tools/doccheck/generic_exclude_patterns index ddb307d8de7e..523ae35144a9 100644 --- a/dist/tools/doccheck/generic_exclude_patterns +++ b/dist/tools/doccheck/generic_exclude_patterns @@ -44,9 +44,13 @@ warning: Member pwm_config\[\] \(variable\) of warning: Member PWM_NUMOF \(macro definition\) of warning: Member RTT_FREQUENCY \(macro definition\) of warning: Member RTT_MAX_FREQUENCY \(macro definition\) of +warning: Member SDCARD_SPI_PARAM_[A-Z0-9_]* \(macro definition\) of warning: Member SHT1X_PARAMS \(macro definition\) of warning: Member SHT1X_PARAM_[A-Z0-9_]* \(macro definition\) of warning: Member SHT1X_SAULINFO \(macro definition\) of +warning: Member spi_config\[\] \(variable\) of +warning: Member SPI_NUMOF \(macro definition\) of +warning: Member ST7735_PARAM_[A-Z0-9_]* \(macro definition\) of warning: Member TIMER_[0-9]_IRQN \(macro definition\) of warning: Member TIMER_NUMOF \(macro definition\) of warning: Member timer_config\[\] \(variable\) of