#include "spi_driver.h" #include "gd32f4xx.h" #include struct spi_hw_config_t { uint32_t spi_periph; rcu_periph_enum rcu_clock; uint32_t gpio_port; uint32_t sck_pin; uint32_t miso_pin; uint32_t mosi_pin; uint32_t af; rcu_periph_enum gpio_rcu; }; static constexpr spi_hw_config_t spi_hw_map[] = { {SPI0, RCU_SPI0, GPIOA, GPIO_PIN_5, GPIO_PIN_6, GPIO_PIN_7, GPIO_AF_5, RCU_GPIOA}, {SPI1, RCU_SPI1, GPIOB, GPIO_PIN_13, GPIO_PIN_14, GPIO_PIN_15, GPIO_AF_5, RCU_GPIOB}, {SPI2, RCU_SPI2, GPIOB, GPIO_PIN_13, GPIO_PIN_14, GPIO_PIN_15, GPIO_AF_6, RCU_GPIOB}, {SPI3, RCU_SPI3, GPIOB, GPIO_PIN_3, GPIO_PIN_4, GPIO_PIN_5, GPIO_AF_6, RCU_GPIOB}, {SPI4, RCU_SPI4, GPIOF, GPIO_PIN_7, GPIO_PIN_8, GPIO_PIN_9, GPIO_AF_5, RCU_GPIOF}, {SPI5, RCU_SPI5, GPIOF, GPIO_PIN_11, GPIO_PIN_12, GPIO_PIN_10, GPIO_AF_5, RCU_GPIOF}, }; static constexpr uint32_t spi_mode_map[4] = { SPI_CK_PL_LOW_PH_1EDGE, SPI_CK_PL_LOW_PH_2EDGE, SPI_CK_PL_HIGH_PH_1EDGE, SPI_CK_PL_HIGH_PH_2EDGE, }; struct prescale_entry_t { uint32_t div; uint32_t gd32_prescale; }; static constexpr prescale_entry_t prescale_table[] = { {2, SPI_PSC_2}, {4, SPI_PSC_4}, {8, SPI_PSC_8}, {16, SPI_PSC_16}, {32, SPI_PSC_32}, {64, SPI_PSC_64}, {128, SPI_PSC_128}, {256, SPI_PSC_256}, }; static uint32_t calc_prescaler(uint32_t apb_clock, uint32_t desired_speed) { if (desired_speed == 0) { return SPI_PSC_64; } for (uint32_t i = 0; i < sizeof(prescale_table) / sizeof(prescale_table[0]); i++) { uint32_t actual = apb_clock / prescale_table[i].div; if (actual <= desired_speed) { return prescale_table[i].gd32_prescale; } } return SPI_PSC_256; } struct gpio_rcu_map_t { uint32_t gpio_port; rcu_periph_enum rcu; }; static constexpr gpio_rcu_map_t gpio_rcu_table[] = { {GPIOA, RCU_GPIOA}, {GPIOB, RCU_GPIOB}, {GPIOC, RCU_GPIOC}, {GPIOD, RCU_GPIOD}, {GPIOE, RCU_GPIOE}, {GPIOF, RCU_GPIOF}, {GPIOG, RCU_GPIOG}, }; static rcu_periph_enum port_to_rcu(uint32_t gpio_port) { for (uint32_t i = 0; i < sizeof(gpio_rcu_table) / sizeof(gpio_rcu_table[0]); i++) { if (gpio_rcu_table[i].gpio_port == gpio_port) { return gpio_rcu_table[i].rcu; } } return static_cast(0); } extern "C" { static SpiBus spi_0(SpiPort::_0); static SpiBus spi_1(SpiPort::_1); static SpiBus spi_2(SpiPort::_2); static SpiBus spi_3(SpiPort::_3); static SpiBus spi_4(SpiPort::_4); static SpiBus spi_5(SpiPort::_5); static SpiBus *spi_instances[] = { &spi_0, &spi_1, &spi_2, &spi_3, &spi_4, &spi_5, }; static uint8_t spi_inited[6] = {0}; int spi_bus_init(uint8_t port, uint32_t speed_hz, uint8_t mode) { if (port >= 6) return -1; SpiConfig cfg; cfg.speed_hz = speed_hz; cfg.mode = mode; RetCode ret = spi_instances[port]->init(cfg); spi_inited[port] = (ret == RET_OK) ? 1 : 0; return spi_inited[port] ? 0 : -1; } int spi_transfer(uint8_t port, uint32_t cs_port, uint32_t cs_pin, const uint8_t *tx, uint8_t *rx, uint32_t len) { if (port >= 6 || !spi_inited[port]) return -1; SpiDevice dev(cs_port, cs_pin); dev.init(); BusTransfer xfer; xfer.tx_data = tx; xfer.rx_data = rx; xfer.length = len; xfer.timeout_ms = 1000; return spi_instances[port]->transfer(&dev, &xfer) == RET_OK ? 0 : -1; } } /* extern "C" */ SpiDevice::SpiDevice(uint32_t cs_port, uint32_t cs_pin) : cs_port_(cs_port), cs_pin_(cs_pin) { } RetCode SpiDevice::init() { rcu_periph_clock_enable(port_to_rcu(cs_port_)); gpio_mode_set(cs_port_, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, cs_pin_); gpio_output_options_set(cs_port_, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, cs_pin_); gpio_bit_set(cs_port_, cs_pin_); initialized_ = true; return RET_OK; } SpiBus::SpiBus(SpiPort port) : port_(port) { } SpiBus::~SpiBus() { if (initialized_) { deinit(); } } RetCode SpiBus::init(const SpiConfig &config) { uint8_t port_idx = static_cast(port_); if (port_idx >= sizeof(spi_hw_map) / sizeof(spi_hw_map[0])) { return RET_INVALID_PARAM; } const spi_hw_config_t &hw = spi_hw_map[port_idx]; if (hw.spi_periph == 0) { return RET_NOT_SUPPORTED; } config_ = config; rcu_periph_clock_enable(hw.rcu_clock); rcu_periph_clock_enable(hw.gpio_rcu); gpio_deinit(hw.gpio_port); uint32_t out_pins = hw.sck_pin | hw.mosi_pin; gpio_mode_set(hw.gpio_port, GPIO_MODE_AF, GPIO_PUPD_NONE, out_pins); gpio_output_options_set(hw.gpio_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, out_pins); gpio_af_set(hw.gpio_port, hw.af, out_pins); gpio_mode_set(hw.gpio_port, GPIO_MODE_AF, GPIO_PUPD_NONE, hw.miso_pin); gpio_af_set(hw.gpio_port, hw.af, hw.miso_pin); spi_parameter_struct spi_para; spi_struct_para_init(&spi_para); spi_para.trans_mode = SPI_TRANSMODE_FULLDUPLEX; spi_para.device_mode = SPI_MASTER; spi_para.frame_size = (config.data_width == 16) ? SPI_FRAMESIZE_16BIT : SPI_FRAMESIZE_8BIT; spi_para.nss = SPI_NSS_SOFT; spi_para.endian = SPI_ENDIAN_MSB; if (config.mode <= 3) { spi_para.clock_polarity_phase = spi_mode_map[config.mode]; } else { spi_para.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE; } uint32_t apb2_clock = rcu_clock_freq_get(CK_APB2); spi_para.prescale = calc_prescaler(apb2_clock, config.speed_hz); spi_init(hw.spi_periph, &spi_para); spi_enable(hw.spi_periph); initialized_ = true; return RET_OK; } RetCode SpiBus::deinit() { uint8_t port_idx = static_cast(port_); if (port_idx >= sizeof(spi_hw_map) / sizeof(spi_hw_map[0])) { return RET_INVALID_PARAM; } const spi_hw_config_t &hw = spi_hw_map[port_idx]; spi_i2s_deinit(hw.spi_periph); rcu_periph_clock_disable(hw.rcu_clock); initialized_ = false; return RET_OK; } RetCode SpiBus::transfer(SpiDevice *device, BusTransfer *transfer) { if (transfer == nullptr) { return RET_INVALID_PARAM; } uint8_t port_idx = static_cast(port_); if (port_idx >= sizeof(spi_hw_map) / sizeof(spi_hw_map[0])) { return RET_INVALID_PARAM; } if (!initialized_) return RET_NOT_INITIALIZED; const spi_hw_config_t &hw = spi_hw_map[port_idx]; uint32_t spi = hw.spi_periph; if (device != nullptr) { gpio_bit_reset(device->cs_port_, device->cs_pin_); for (volatile uint32_t i = 0; i < 100; i++); } for (uint32_t i = 0; i < transfer->length; i++) { uint8_t tx_byte = (transfer->tx_data != nullptr) ? transfer->tx_data[i] : 0xFF; while (RESET == spi_i2s_flag_get(spi, SPI_FLAG_TBE)); spi_i2s_data_transmit(spi, tx_byte); while (RESET == spi_i2s_flag_get(spi, SPI_FLAG_RBNE)); uint8_t received = static_cast(spi_i2s_data_receive(spi)); if (transfer->rx_data != nullptr) { transfer->rx_data[i] = received; } } if (device != nullptr) { gpio_bit_set(device->cs_port_, device->cs_pin_); for (volatile uint32_t i = 0; i < 100; i++); } return RET_OK; }