rt-thread-official/bsp/nuclei/libraries/gd32vf103/HAL_Drivers/drv_spi.c

288 lines
6.6 KiB
C

/*
* Copyright (c) 2006-2020, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2020-05-28 hqfang first implementation.
*/
#include "drv_spi.h"
#ifdef RT_USING_SPI
#if !defined(BSP_USING_SPI0) && !defined(BSP_USING_SPI1) && !defined(BSP_USING_SPI2)
#error "Please define at least one BSP_USING_SPIx"
/* this driver can be disabled at menuconfig -> Hardware Drivers Config -> On-chip Peripheral Drivers -> Enable SPI */
#endif
static struct gd32_spi_config spi_config[] =
{
#ifdef BSP_USING_SPI0
{
"spi0",
SPI0,
},
#endif
#ifdef BSP_USING_SPI1
{
"spi1",
SPI1,
},
#endif
#ifdef BSP_USING_SPI2
{
"spi2",
SPI2,
},
#endif
};
static struct gd32_spi spi_obj[sizeof(spi_config) / sizeof(spi_config[0])] = {0};
static rt_err_t gd32_spi_init(rt_uint32_t spi_periph, struct rt_spi_configuration *cfg)
{
spi_parameter_struct spicfg;
uint32_t apbfreq;
uint32_t scale;
RT_ASSERT(cfg != RT_NULL);
spi_struct_para_init(&spicfg);
if (cfg->data_width != 8 && cfg->data_width != 16)
{
return (-RT_EINVAL);
}
switch (spi_periph)
{
case SPI0:
apbfreq = rcu_clock_freq_get(CK_APB2);
break;
default:
apbfreq = rcu_clock_freq_get(CK_APB1);
break;
}
scale = apbfreq / cfg->max_hz;
if (scale <= 2)
{
spicfg.prescale = SPI_PSC_2;
}
else if (scale <= 4)
{
spicfg.prescale = SPI_PSC_4;
}
else if (scale <= 8)
{
spicfg.prescale = SPI_PSC_8;
}
else if (scale <= 16)
{
spicfg.prescale = SPI_PSC_16;
}
else if (scale <= 32)
{
spicfg.prescale = SPI_PSC_32;
}
else if (scale <= 64)
{
spicfg.prescale = SPI_PSC_64;
}
else if (scale <= 128)
{
spicfg.prescale = SPI_PSC_128;
}
else if (scale <= 256)
{
spicfg.prescale = SPI_PSC_256;
}
else
{
spicfg.prescale = SPI_PSC_256;
}
if (cfg->data_width == 8)
{
spicfg.frame_size = SPI_FRAMESIZE_8BIT;
}
else
{
spicfg.frame_size = SPI_FRAMESIZE_16BIT;
}
if (cfg->mode & RT_SPI_MSB)
{
spicfg.endian = SPI_ENDIAN_MSB;
}
else
{
spicfg.endian = SPI_ENDIAN_LSB;
}
spicfg.clock_polarity_phase = 0;
if (cfg->mode & RT_SPI_CPHA)
{
spicfg.clock_polarity_phase |= SPI_CTL0_CKPH;
}
if (cfg->mode & RT_SPI_CPOL)
{
spicfg.clock_polarity_phase |= SPI_CTL0_CKPL;
}
if (cfg->mode & RT_SPI_SLAVE)
{
spicfg.device_mode = SPI_SLAVE;
}
else
{
spicfg.device_mode = SPI_MASTER;
}
spicfg.nss = SPI_NSS_SOFT;
spicfg.trans_mode = SPI_TRANSMODE_FULLDUPLEX;
spi_init(spi_periph, &spicfg);
/* set crc polynomial */
spi_crc_polynomial_set(spi_periph, 7);
return RT_EOK;
}
static rt_err_t gd32_spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *cfg)
{
rt_err_t ret = RT_EOK;
RT_ASSERT(device != RT_NULL);
struct gd32_spi *spi_obj = (struct gd32_spi *)(device->bus->parent.user_data);
struct gd32_spi_config *spi_cfg = (struct gd32_spi_config *)(spi_obj->config);
ret = gd32_spi_init(spi_cfg->spi_periph, cfg);
/* enable SPI */
spi_enable(spi_cfg->spi_periph);
return ret;
}
/**
* 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_uint32_t pin)
{
rt_err_t ret = RT_EOK;
struct rt_spi_device *spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device));
RT_ASSERT(spi_device != RT_NULL);
struct gd32_spi_cs *cs_pin = (struct gd32_spi_cs *)rt_malloc(sizeof(struct gd32_spi_cs));
RT_ASSERT(cs_pin != RT_NULL);
cs_pin->pin = pin;
rt_pin_mode(pin, PIN_MODE_OUTPUT);
rt_pin_write(pin, PIN_HIGH);
ret = rt_spi_bus_attach_device(spi_device, device_name, bus_name, (void *)cs_pin);
return ret;
}
rt_size_t gd32_spi_transmit(rt_uint32_t spi_periph, const void *send_buf, void *recv_buf, rt_size_t length)
{
uint8_t *send_buf_8b = (uint8_t *)send_buf;
uint8_t *recv_buf_8b = (uint8_t *)recv_buf;
uint8_t sndbyte = 0xFF, rcvbyte;
rt_size_t idx = 0;
while (idx < length)
{
while (RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_TBE));
if (send_buf_8b)
{
sndbyte = send_buf_8b[idx];
}
spi_i2s_data_transmit(spi_periph, sndbyte);
while (RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_RBNE));
rcvbyte = spi_i2s_data_receive(spi_periph);
if (recv_buf_8b)
{
recv_buf_8b[idx] = rcvbyte;
}
idx ++;
}
return length;
}
static rt_uint32_t gd32_spi_xfer(struct rt_spi_device *device, struct rt_spi_message *message)
{
rt_uint32_t total_length = 0;
rt_err_t ret = RT_EOK;
RT_ASSERT(device != RT_NULL);
struct gd32_spi *spi_obj = (struct gd32_spi *)(device->bus->parent.user_data);
struct gd32_spi_config *spi_cfg = (struct gd32_spi_config *)(spi_obj->config);
RT_ASSERT(spi_cfg != RT_NULL);
struct gd32_spi_cs *cs = (struct gd32_spi_cs *)(device->parent.user_data);
if (message && message->cs_take)
{
rt_pin_write(cs->pin, PIN_LOW);
}
if (message && message->length)
{
total_length += gd32_spi_transmit(spi_cfg->spi_periph, message->send_buf, \
message->recv_buf, message->length);
}
if (message && message->cs_release)
{
rt_pin_write(cs->pin, PIN_HIGH);
}
return total_length;
}
static const struct rt_spi_ops spi_ops =
{
gd32_spi_configure,
gd32_spi_xfer
};
int rt_hw_spi_init(void)
{
rt_size_t obj_num;
int index;
rt_err_t result = 0;
#ifdef BSP_USING_SPI0
rcu_periph_clock_enable(RCU_SPI0);
#endif
#ifdef BSP_USING_SPI1
rcu_periph_clock_enable(RCU_SPI1);
#endif
#ifdef BSP_USING_SPI2
rcu_periph_clock_enable(RCU_SPI2);
#endif
obj_num = sizeof(spi_obj) / sizeof(struct gd32_spi);
for (index = 0; index < obj_num; index++)
{
/* init spi object */
spi_obj[index].config = &spi_config[index];
spi_obj[index].bus.parent.user_data = &spi_obj[index];
/* register spi device */
result = rt_spi_bus_register(&spi_obj[index].bus,
spi_obj[index].config->name,
&spi_ops);
RT_ASSERT(result == RT_EOK);
}
return 0;
}
INIT_DEVICE_EXPORT(rt_hw_spi_init);
#endif
/* end of spi driver */