Files
lishanpi/drv/spi/spi_driver.cpp
T
hm fc10ef2e10 Initial commit: LSPi (LiangShanPi) project
GD32F470ZGT6 based project with full peripheral support:
- SDRAM (W9825G6KH) via EXMC
- LCD NT35510 (480x800) via EXMC NOR/PSRAM
- Flash (W25Q64) via SPI
- UART (USART0) debug console
- LED indicators
- CMSIS-DAP debug interface
- CMake + ARM GCC toolchain build system
2026-04-26 16:24:42 +08:00

262 lines
7.1 KiB
C++

#include "spi_driver.h"
#include "gd32f4xx.h"
#include <cstddef>
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<rcu_periph_enum>(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<uint8_t>(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<uint8_t>(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<uint8_t>(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<uint8_t>(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;
}