1.SPI简介
一般说的SPI(Serial Peripheral Interface)是由Motorola公司定义的接口协议标准,还有一种是SSI(Synchronous Serial Interface)由TI公司定义的接口协议标准。两者的不同主要在NSS信号。
GD的硬件spi默认使用的是摩托罗拉定义的SPI协议。使用SSI时需要打开TI模式。
/* SPI TI mode enable */
void spi_ti_mode_enable(uint32_t spi_periph);
/* SPI TI mode disable */
void spi_ti_mode_disable(uint32_t spi_periph);
2.工程自带drv_spi
工程里带的spi驱动是真的坑,极不友好。
static struct rt_spi_bus spi_bus[];
static const struct gd32f4_spi spis[] = {
#ifdef RT_USING_SPI0
{SPI0, RCU_SPI0, &spi_bus[0]},
#endif
#ifdef RT_USING_SPI1
{SPI1, RCU_SPI1, &spi_bus[1]},
#endif
#ifdef RT_USING_SPI2
{SPI2, RCU_SPI2, &spi_bus[2]},
#endif
#ifdef RT_USING_SPI3
{SPI3, RCU_SPI3, &spi_bus[3]},
#endif
#ifdef RT_USING_SPI4
{SPI4, RCU_SPI4, &spi_bus[4]},
#endif
#ifdef RT_USING_SPI5
{SPI5, RCU_SPI5, &spi_bus[5]},
#endif
};
static struct rt_spi_bus spi_bus[ARR_LEN(spis)];
最后一行 static struct rt_spi_bus spi_bus[ARR_LEN(spis)] ; 数组spi_bus 的长度被定义为 数组spis 的长度,当只使用 RT_USING_SPI5 时 数组spi_bus的长度是1,代码中是 引用了spi_bus[5]。 指针越界,当代码运行起来就会发生一些奇妙的事情,让你百思不得其解。
比较简单粗暴的解决办法是把spi_bus长度定义为 6;
static struct rt_spi_bus spi_bus[6];
static const struct gd32f4_spi spis[] = {
/****** 省略 *****/
};
3.移植RT_Thread BSP中GD的spi驱动
3.1 复制粘贴
复制gd32_drivers 中 drv_spi.c 和drv_spi.h内容粘贴至工程中。
SPI外设定义的名字和原工程中的有一些不同,需要把文件内所有的BSP_USING_SPI替换成RT_USING_SPI。
#ifdef RT_USING_SPI
#if defined(BSP_USING_SPI0) || defined(BSP_USING_SPI1) || defined(BSP_USING_SPI2)
bsp中的工程文件把SPI硬件的初始化设置为定义了相应的SPI后便会自动初始化,个人习惯在外部初始化这里简单做了修改。
3.2 drv_spi.h
/* gd32 spi dirver class */
struct gd32_spi
{
uint32_t spi_periph;
char *bus_name;
rcu_periph_enum spi_clk;
struct rt_spi_bus *spi_bus;
// rcu_periph_enum gpio_clk;
// uint32_t spi_port;
// uint16_t sck_pin;
// uint16_t miso_pin;
// uint16_t mosi_pin;
};
3.3drv_spi.c
3.3.1 更改 spi_bus_obj
#ifdef RT_USING_SPI0
{
.spi_periph = SPI0,
.bus_name = "spi0",
.spi_clk = RCU_SPI0,
.spi_bus = &spi_bus0,
}
#endif /* RT_USING_SPI0 */
#ifdef RT_USING_SPI1
{
.spi_periph = SPI1,
.bus_name = "spi1",
.spi_clk = RCU_SPI1,
.spi_bus = &spi_bus1,
}
#endif /* RT_USING_SPI1 */
#ifdef RT_USING_SPI2
{
.spi_periph = SPI2,
.bus_name = "spi2",
.spi_clk = RCU_SPI2,
.spi_bus = &spi_bus2,
}
#endif /* RT_USING_SPI2 */
3.3.2 删除 gd32_spi_init 以及引用
static rt_err_t spi_configure(struct rt_spi_device* device,
struct rt_spi_configuration* configuration)
{
/**** 省略 ****/
//Init SPI
//gd32_spi_init(spi_device);
/**** 省略 ****/
}
//static void gd32_spi_init(struct gd32_spi *gd32_spi)
//{
// /**** 省略 ****/
//}
3.3.3 参考SPI0 1 2增加SPI3 SPI4 SPI5功能。
#if defined(RT_USING_SPI0) || defined(RT_USING_SPI1) || defined(RT_USING_SPI2) ||\
defined(RT_USING_SPI3) || defined(RT_USING_SPI4) || defined(RT_USING_SPI5)
#ifdef RT_USING_SPI3
static struct rt_spi_bus spi_bus3;
#endif
#ifdef RT_USING_SPI4
static struct rt_spi_bus spi_bus4;
#endif
#ifdef RT_USING_SPI5
static struct rt_spi_bus spi_bus5;
#ifdef RT_USING_SPI3
{
.spi_periph = SPI3,
.bus_name = "spi3",
.spi_clk = RCU_SPI3,
.spi_bus = &spi_bus3,
}
#endif /* RT_USING_SPI3 */
#ifdef RT_USING_SPI4
{
.spi_periph = SPI4,
.bus_name = "spi4",
.spi_clk = RCU_SPI4,
.spi_bus = &spi_bus4,
}
#endif /* RT_USING_SPI4 */
#ifdef RT_USING_SPI5
{
.spi_periph = SPI5,
.bus_name = "spi5",
.spi_clk = RCU_SPI5,
.spi_bus = &spi_bus5,
}
#endif /* RT_USING_SPI5 */
3.3.4 修改 gd32_spi_bus_register 函数
/* name spi0 spi1 spi2 spi3 spi4 spi5 */
static struct rt_spi_bus * rt_spi_bus_find(const char *name)
{
RT_ASSERT(name);
int cmp = 0;
for (int i = 0; i < sizeof(spi_bus_obj) / sizeof(spi_bus_obj[0]); i++)
{
cmp = rt_strcmp(spi_bus_obj[i].bus_name , name);
if( !cmp )
return spi_bus_obj[i].spi_bus;
}
return RT_NULL;
}
rt_err_t gd32_spi_bus_register( const char * spi_bus_name )
{
RT_ASSERT(spi_bus_name);
struct rt_spi_bus *spi_bus = RT_NULL;
spi_bus = rt_spi_bus_find(spi_bus_name);
rt_spi_bus_register(spi_bus, spi_bus_name, &gd32_spi_ops);
}
搞完收工,spi dma传输后面会继续加上去。