/* * Copyright (c) 2006-2025, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2021-12-20 BruceOu first implementation */ #include "drv_spi.h" #ifdef RT_USING_SPI #if defined(BSP_USING_SPI0) || defined(BSP_USING_SPI1) || defined(BSP_USING_SPI2) || defined(BSP_USING_SPI3) || defined(BSP_USING_SPI4) || defined(BSP_USING_SPI5) #define LOG_TAG "drv.spi" #include #ifdef BSP_USING_SPI0 static struct rt_spi_bus spi_bus0; #endif #ifdef BSP_USING_SPI1 static struct rt_spi_bus spi_bus1; #endif #ifdef BSP_USING_SPI2 static struct rt_spi_bus spi_bus2; #endif #ifdef BSP_USING_SPI3 static struct rt_spi_bus spi_bus3; #endif #ifdef BSP_USING_SPI4 static struct rt_spi_bus spi_bus4; #endif #ifdef BSP_USING_SPI5 static struct rt_spi_bus spi_bus5; #endif static const struct gd32_spi spi_bus_obj[] = { #ifdef BSP_USING_SPI0 { SPI0, "spi0", RCU_SPI0, RCU_GPIOA, RCU_GPIOA, RCU_GPIOA, &spi_bus0, GPIOA, GPIOA, GPIOA, #if defined (SOC_SERIES_GD32F4xx) || defined (SOC_SERIES_GD32H7xx) || (defined SOC_SERIES_GD32F5xx) GPIO_AF_5, #endif #if defined (SOC_SERIES_GD32E23x) GPIO_AF_0, #endif GPIO_PIN_5, GPIO_PIN_6, GPIO_PIN_7, }, #endif /* BSP_USING_SPI0 */ #ifdef BSP_USING_SPI1 { SPI1, "spi1", RCU_SPI1, RCU_GPIOB, RCU_GPIOB, RCU_GPIOB, &spi_bus1, GPIOB, GPIOB, GPIOB, #if defined (SOC_SERIES_GD32F4xx) || defined (SOC_SERIES_GD32H7xx) || (defined SOC_SERIES_GD32F5xx) GPIO_AF_5, #endif #if defined SOC_SERIES_GD32E23x GPIO_AF_0, #endif GPIO_PIN_13, GPIO_PIN_14, GPIO_PIN_15, }, #endif /* BSP_USING_SPI1 */ #ifdef BSP_USING_SPI2 { SPI2, "spi2", RCU_SPI2, RCU_GPIOB, RCU_GPIOB, RCU_GPIOB, &spi_bus2, GPIOB, GPIOB, GPIOB, #if defined (SOC_SERIES_GD32F4xx) || defined (SOC_SERIES_GD32H7xx) || (defined SOC_SERIES_GD32F5xx) GPIO_AF_6, #endif GPIO_PIN_3, GPIO_PIN_4, GPIO_PIN_5, }, #endif /* BSP_USING_SPI2 */ #ifdef BSP_USING_SPI3 { SPI3, "spi3", RCU_SPI3, RCU_GPIOE, RCU_GPIOE, RCU_GPIOE, &spi_bus3, GPIOE, GPIOE, GPIOE, #if defined (SOC_SERIES_GD32F4xx) || defined (SOC_SERIES_GD32H7xx) || (defined SOC_SERIES_GD32F5xx) GPIO_AF_5, #endif GPIO_PIN_2, GPIO_PIN_5, GPIO_PIN_6, }, #endif /* BSP_USING_SPI3 */ #ifdef BSP_USING_SPI4 { SPI4, "spi4", RCU_SPI4, RCU_GPIOF, RCU_GPIOF, RCU_GPIOF, &spi_bus4, GPIOF, GPIOF, GPIOF, #if defined (SOC_SERIES_GD32F4xx) || defined (SOC_SERIES_GD32H7xx) || (defined SOC_SERIES_GD32F5xx) GPIO_AF_5, #endif GPIO_PIN_7, GPIO_PIN_8, GPIO_PIN_9, }, #endif /* BSP_USING_SPI4 */ #ifdef BSP_USING_SPI5 { SPI5, "spi5", RCU_SPI5, RCU_GPIOG, RCU_GPIOG, RCU_GPIOG, &spi_bus5, GPIOG, GPIOG, GPIOG, #if defined (SOC_SERIES_GD32F4xx) || defined (SOC_SERIES_GD32H7xx) || (defined SOC_SERIES_GD32F5xx) GPIO_AF_5, #endif GPIO_PIN_13, GPIO_PIN_12, GPIO_PIN_14, } #endif /* BSP_USING_SPI5 */ }; /* private rt-thread spi ops function */ static rt_err_t spi_configure(struct rt_spi_device* device, struct rt_spi_configuration* configuration); static rt_ssize_t spixfer(struct rt_spi_device* device, struct rt_spi_message* message); static struct rt_spi_ops gd32_spi_ops = { .configure = spi_configure, .xfer = spixfer, }; /** * @brief SPI Initialization * @param gd32_spi: SPI BUS * @retval None */ static void gd32_spi_init(struct gd32_spi *gd32_spi) { /* enable SPI clock */ rcu_periph_clock_enable(gd32_spi->spi_clk); rcu_periph_clock_enable(gd32_spi->sck_gpio_clk); rcu_periph_clock_enable(gd32_spi->miso_gpio_clk); rcu_periph_clock_enable(gd32_spi->mosi_gpio_clk); #if defined (SOC_SERIES_GD32F4xx) || defined (SOC_SERIES_GD32H7xx) || (defined SOC_SERIES_GD32F5xx) || (defined SOC_SERIES_GD32E23x) /*GPIO pin configuration*/ gpio_af_set(gd32_spi->sck_spi_port, gd32_spi->alt_func_num, gd32_spi->sck_pin); gpio_af_set(gd32_spi->miso_spi_port, gd32_spi->alt_func_num, gd32_spi->miso_pin); gpio_af_set(gd32_spi->mosi_spi_port, gd32_spi->alt_func_num, gd32_spi->mosi_pin); gpio_mode_set(gd32_spi->sck_spi_port, GPIO_MODE_AF, GPIO_PUPD_NONE, gd32_spi->sck_pin); gpio_mode_set(gd32_spi->miso_spi_port, GPIO_MODE_AF, GPIO_PUPD_NONE, gd32_spi->miso_pin); gpio_mode_set(gd32_spi->mosi_spi_port, GPIO_MODE_AF, GPIO_PUPD_NONE, gd32_spi->mosi_pin); #if defined (SOC_SERIES_GD32H7xx) gpio_output_options_set(gd32_spi->sck_spi_port, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, gd32_spi->sck_pin); gpio_output_options_set(gd32_spi->miso_spi_port, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, gd32_spi->miso_pin); gpio_output_options_set(gd32_spi->mosi_spi_port, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, gd32_spi->mosi_pin); #elif defined (SOC_SERIES_GD32E23x) gpio_output_options_set(gd32_spi->sck_spi_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, gd32_spi->sck_pin); gpio_output_options_set(gd32_spi->miso_spi_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, gd32_spi->miso_pin); gpio_output_options_set(gd32_spi->mosi_spi_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, gd32_spi->mosi_pin); #else gpio_output_options_set(gd32_spi->sck_spi_port, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, gd32_spi->sck_pin); gpio_output_options_set(gd32_spi->miso_spi_port, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, gd32_spi->miso_pin); gpio_output_options_set(gd32_spi->mosi_spi_port, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, gd32_spi->mosi_pin); #endif #else /* Init SPI SCK MOSI */ gpio_init(gd32_spi->sck_spi_port, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, gd32_spi->sck_pin); gpio_init(gd32_spi->mosi_spi_port, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, gd32_spi->mosi_pin); /* Init SPI MISO */ gpio_init(gd32_spi->miso_spi_port, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, gd32_spi->miso_pin); #endif } static rt_err_t spi_configure(struct rt_spi_device* device, struct rt_spi_configuration* configuration) { struct rt_spi_bus * spi_bus = (struct rt_spi_bus *)device->bus; struct gd32_spi *spi_device = (struct gd32_spi *)spi_bus->parent.user_data; spi_parameter_struct spi_init_struct; uint32_t spi_periph = spi_device->spi_periph; RT_ASSERT(device != RT_NULL); RT_ASSERT(configuration != RT_NULL); /* Init SPI */ gd32_spi_init(spi_device); #if defined SOC_SERIES_GD32H7xx /* data_width */ if(configuration->data_width >=4 && configuration->data_width <= 32) { spi_init_struct.data_size = CFG0_DZ(configuration->data_width - 1); } else { return -RT_EIO; } #else /* data_width */ if(configuration->data_width <= 8) { spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT; } else if(configuration->data_width <= 16) { spi_init_struct.frame_size = SPI_FRAMESIZE_16BIT; } else { return -RT_EIO; } #endif /* baudrate */ { rcu_clock_freq_enum spi_src; uint32_t spi_apb_clock; uint32_t max_hz; max_hz = configuration->max_hz; LOG_D("sys freq: %d\n", rcu_clock_freq_get(CK_SYS)); LOG_D("CK_APB2 freq: %d\n", rcu_clock_freq_get(CK_APB2)); LOG_D("max freq: %d\n", max_hz); #if defined SOC_SERIES_GD32E23x spi_src = CK_APB2; #else if (spi_periph == SPI1 || spi_periph == SPI2) { spi_src = CK_APB1; } else { spi_src = CK_APB2; } #endif spi_apb_clock = rcu_clock_freq_get(spi_src); if(max_hz >= spi_apb_clock/2) { spi_init_struct.prescale = SPI_PSC_2; } else if (max_hz >= spi_apb_clock/4) { spi_init_struct.prescale = SPI_PSC_4; } else if (max_hz >= spi_apb_clock/8) { spi_init_struct.prescale = SPI_PSC_8; } else if (max_hz >= spi_apb_clock/16) { spi_init_struct.prescale = SPI_PSC_16; } else if (max_hz >= spi_apb_clock/32) { spi_init_struct.prescale = SPI_PSC_32; } else if (max_hz >= spi_apb_clock/64) { spi_init_struct.prescale = SPI_PSC_64; } else if (max_hz >= spi_apb_clock/128) { spi_init_struct.prescale = SPI_PSC_128; } else { /* min prescaler 256 */ spi_init_struct.prescale = SPI_PSC_256; } } /* baudrate */ switch(configuration->mode & RT_SPI_MODE_3) { case RT_SPI_MODE_0: spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE; break; case RT_SPI_MODE_1: spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_2EDGE; break; case RT_SPI_MODE_2: spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_1EDGE; break; case RT_SPI_MODE_3: spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE; break; } /* MSB or LSB */ if(configuration->mode & RT_SPI_MSB) { spi_init_struct.endian = SPI_ENDIAN_MSB; } else { spi_init_struct.endian = SPI_ENDIAN_LSB; } spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; spi_init_struct.device_mode = SPI_MASTER; spi_init_struct.nss = SPI_NSS_SOFT; spi_crc_off(spi_periph); /* init SPI */ spi_init(spi_periph, &spi_init_struct); /* Enable SPI_MASTER */ spi_enable(spi_periph); return RT_EOK; }; static rt_ssize_t spixfer(struct rt_spi_device* device, struct rt_spi_message* message) { struct rt_spi_bus * gd32_spi_bus = (struct rt_spi_bus *)device->bus; struct gd32_spi *spi_device = (struct gd32_spi *)gd32_spi_bus->parent.user_data; struct rt_spi_configuration * config = &device->config; uint32_t spi_periph = spi_device->spi_periph; RT_ASSERT(device != NULL); RT_ASSERT(message != NULL); /* take CS */ if (message->cs_take && !(device->config.mode & RT_SPI_NO_CS) && (device->cs_pin != PIN_NONE)) { if (device->config.mode & RT_SPI_CS_HIGH) { rt_pin_write(device->cs_pin, PIN_HIGH); } else { rt_pin_write(device->cs_pin, PIN_LOW); } } LOG_D("%s transfer prepare and start", spi_device->bus_name); LOG_D("%s sendbuf: %X, recvbuf: %X, length: %d", spi_device->bus_name, (uint32_t)message->send_buf, (uint32_t)message->recv_buf, message->length); { if(config->data_width <= 8) { const rt_uint8_t * send_ptr = message->send_buf; rt_uint8_t * recv_ptr = message->recv_buf; rt_uint32_t size = message->length; LOG_D("spi poll transfer start: %d\n", size); while(size--) { rt_uint8_t data = 0xFF; if(send_ptr != RT_NULL) { data = *send_ptr++; } /* Todo: replace register read/write by gd32f4 lib */ /* Wait until the transmit buffer is empty */ #if defined (SOC_SERIES_GD32H7xx) while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_TP)); #else while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_TBE)); #endif /* Send the byte */ spi_i2s_data_transmit(spi_periph, data); /* Wait until a data is received */ #if defined (SOC_SERIES_GD32H7xx) while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_RP)); #else while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_RBNE)); #endif /* Get the received data */ data = spi_i2s_data_receive(spi_periph); if(recv_ptr != RT_NULL) { *recv_ptr++ = data; } } LOG_D("spi poll transfer finsh\n"); } else if(config->data_width <= 16) { const rt_uint16_t * send_ptr = message->send_buf; rt_uint16_t * recv_ptr = message->recv_buf; rt_uint32_t size = message->length; while(size--) { rt_uint16_t data = 0xFF; if(send_ptr != RT_NULL) { data = *send_ptr++; } /* Wait until the transmit buffer is empty */ #if defined (SOC_SERIES_GD32H7xx) while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_TP)); #else while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_TBE)); #endif /* Send the byte */ spi_i2s_data_transmit(spi_periph, data); /* Wait until a data is received */ #if defined (SOC_SERIES_GD32H7xx) while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_RP)); #else while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_RBNE)); #endif /* Get the received data */ data = spi_i2s_data_receive(spi_periph); if(recv_ptr != RT_NULL) { *recv_ptr++ = data; } } } #if defined SOC_SERIES_GD32H7xx else if(config->data_width <= 32) { const rt_uint32_t * send_ptr = message->send_buf; rt_uint32_t * recv_ptr = message->recv_buf; rt_uint32_t size = message->length; while(size--) { rt_uint32_t data = 0xFF; if(send_ptr != RT_NULL) { data = *send_ptr++; } /* Wait until the transmit buffer is empty */ while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_TP)); /* Send the byte */ spi_i2s_data_transmit(spi_periph, data); /* Wait until a data is received */ while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_RP)); /* Get the received data */ data = spi_i2s_data_receive(spi_periph); if(recv_ptr != RT_NULL) { *recv_ptr++ = data; } } } #endif } /* release CS */ if (message->cs_release && !(device->config.mode & RT_SPI_NO_CS) && (device->cs_pin != PIN_NONE)) { if (device->config.mode & RT_SPI_CS_HIGH) rt_pin_write(device->cs_pin, PIN_LOW); else rt_pin_write(device->cs_pin, PIN_HIGH); } return message->length; }; /** * Attach the spi device to SPI bus, this function must be used after initialization. */ rt_err_t rt_hw_spi_device_attach(const char *bus_name, const char *device_name, rt_base_t cs_pin) { RT_ASSERT(bus_name != RT_NULL); RT_ASSERT(device_name != RT_NULL); rt_err_t result; struct rt_spi_device *spi_device; /* attach the device to spi bus*/ spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device)); RT_ASSERT(spi_device != RT_NULL); if(cs_pin != PIN_NONE) { /* initialize the cs pin && select the slave*/ rt_pin_mode(cs_pin, PIN_MODE_OUTPUT); rt_pin_write(cs_pin, PIN_HIGH); } result = rt_spi_bus_attach_device_cspin(spi_device, device_name, bus_name, cs_pin, RT_NULL); if (result != RT_EOK) { LOG_E("%s attach to %s faild, %d\n", device_name, bus_name, result); } RT_ASSERT(result == RT_EOK); LOG_D("%s attach to %s done", device_name, bus_name); return result; } int rt_hw_spi_init(void) { int result = 0; int i; for (i = 0; i < sizeof(spi_bus_obj) / sizeof(spi_bus_obj[0]); i++) { spi_bus_obj[i].spi_bus->parent.user_data = (void *)&spi_bus_obj[i]; result = rt_spi_bus_register(spi_bus_obj[i].spi_bus, spi_bus_obj[i].bus_name, &gd32_spi_ops); RT_ASSERT(result == RT_EOK); LOG_D("%s bus init done", spi_bus_obj[i].bus_name); } return result; } INIT_BOARD_EXPORT(rt_hw_spi_init); #endif /* BSP_USING_SPI0 || BSP_USING_SPI1 || BSP_USING_SPI2 || BSP_USING_SPI3 || BSP_USING_SPI4 || BSP_USING_SPI5 */ #endif /* RT_USING_SPI */