前言
本文介绍RT-Thread中SPI设备的使用方法,通过访问APOLLO板载FLASH芯片W25Q256演示SPI具体操作。
一、SPI简介
SPI(Serial Periperal Interface,串行外设接口)是一种高速全双工同步串行总线,采用主(Master)从(Slave)工作模式,通信双方最多采用四线连接,分别为MOSI(主出从入)、MISO(主如从出)、SCK(串行时钟)和CS(片选),总线上可以有一个主机和多个从机,SPI总线常应用于EEPROM、Flash、实时时钟(RTC)、数模转换器(ADC)。
主机控制CS为低电平选择要访问的从机,任何时刻,一个 SPI 主机上只有一个 CS 引脚处于有效状态,与该有效 CS 引脚连接的从机此时被选中,可以与主机通信。
主机同时为从机提供同步时钟SCK,用于确定通信双方的通信速率。
MOSI将主机数据发送给从机,而MISO是将从机数据发送给主机。
SPI总线的工作时序由CPOL(Clock Polarity,时钟极性)和 CPHA(Clock Phase,时钟相位)之间的相位关系决定。CPOL 表示时钟信号的初始电平的状态,CPOL 为 0 表示时钟信号初始状态为低电平,为 1 表示时钟信号的初始电平是高电平。CPHA 表示在哪个时钟沿采样数据,CPHA 为 0 表示在首个时钟变化沿采样数据,而 CPHA 为 1 则表示在第二个时钟变化沿采样数据。根据 CPOL 和 CPHA 的不同组合共有 4 种工作时序模式:①CPOL=0,CPHA=0、②CPOL=0,CPHA=1、③CPOL=1,CPHA=0、④CPOL=1,CPHA=1。
二、访问SPI设备
SPI设备的使用可以参考RT-Thread官方网站
SPI设备
1.配置SPI设备
在进行SPI通信前,需要先将与主机连接的SPI从机挂载到SPI总线上。
static int rt_hw_spi_flash_init(void)
{
rt_hw_spi_device_attach("spi5", W25Q_SPI_DEVICE_NAME, GPIOF, GPIO_PIN_6);
if (RT_NULL == rt_sfud_flash_probe("W25Q256", W25Q_SPI_DEVICE_NAME))
{
return -RT_ERROR;
}
return RT_EOK;
}
2.操作SPI设备
1.访问SPI设备
在使用 SPI 设备前需要根据 SPI 设备名称获取设备句柄,进而才可以操作 SPI 设备。
#define W25Q_SPI_DEVICE_NAME "spi50" /* SPI 设备名称 */
struct rt_spi_device *spi_dev_w25q; /* SPI 设备句柄 */
/* 查找 spi 设备获取设备句柄 */
spi_dev_w25q = (struct rt_spi_device *)rt_device_find(W25Q_SPI_DEVICE_NAME);
2.自定义SPI传输数据
获取到 SPI 设备句柄就可以使用 SPI 设备管理接口访问 SPI 设备器件,进行数据收发。
代码如下(示例):
struct rt_spi_message msg1, msg2;
rt_uint8_t w25x_read_id = 0x90; /* 命令 */
rt_uint8_t id[5] = {0};
msg1.send_buf = &w25x_read_id;
msg1.recv_buf = RT_NULL;
msg1.length = 1;
msg1.cs_take = 1;
msg1.cs_release = 0;
msg1.next = &msg2;
msg2.send_buf = RT_NULL;
msg2.recv_buf = id;
msg2.length = 5;
msg2.cs_take = 0;
msg2.cs_release = 1;
msg2.next = RT_NULL;
rt_spi_transfer_message(spi_dev_w25q, &msg1);
除此之外还有其他数据传输API函数,具体使用方法参考官方文档
3.获取总线
在多线程的情况下,同一个 SPI 总线可能会在不同的线程中使用,为了防止 SPI 总线正在传输的数据丢失,从设备在开始传输数据前需要先获取 SPI 总线的使用权,获取成功才能够使用总线传输数据,可使用如下函数获取 SPI 总线
rt_err_t rt_spi_take_bus(struct rt_spi_device *device);
4.选中片选
从设备获取总线的使用权后,需要设置自己对应的片选信号为有效,可使用如下函数选中片选
rt_err_t rt_spi_take(struct rt_spi_device *device);
5.释放片选
从设备数据传输完成后,需要释放片选,可使用如下函数释放片选
rt_err_t rt_spi_release(struct rt_spi_device *device);
6.释放总线
从设备不在使用 SPI 总线传输数据,必须尽快释放总线,这样其他从设备才能使用 SPI 总线传输数据,可使用如下函数释放总线
rt_err_t rt_spi_release_bus(struct rt_spi_device *device);
三、访问SPI设备举例
打开board.h文件,按提示配置SPI设备
1.环境设置中启动SPI功能
2.开启宏定义
#define BSP_USING_SPI5
3.在STMCUBEMX中配置spi功能,将生成代码
void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
复制到board.c文件后
4.代开文件stm32f7xx_hal_conf.h,开启宏定义
至此,SPI设备功能已经启动,接下来写代码实现具体功能即可,参考代码如下
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#define W25Q_SPI_DEVICE_NAME "spi50" /* SPI 设备名称 */
struct rt_spi_device *spi_w25q;
static int rt_hw_spi_flash_init(void)
{
rt_hw_spi_device_attach("spi5", W25Q_SPI_DEVICE_NAME, GPIOF, GPIO_PIN_6);
if (RT_NULL == rt_sfud_flash_probe("W25Q256", W25Q_SPI_DEVICE_NAME))
{
return -RT_ERROR;
}
return RT_EOK;
}
/* 导出到自动初始化 */
INIT_COMPONENT_EXPORT(rt_hw_spi_flash_init);
rt_uint8_t id[5]={0};
int spi5_w25q_init(void)
{
rt_uint8_t w25q_read_id = 0x90;
spi_w25q=(struct rt_spi_device *)rt_device_find(W25Q_SPI_DEVICE_NAME); /* 查找 spi 设备获取设备句柄 */
/* 配置SPI,如果与默认值相同,此处可不做配置*/
struct rt_spi_configuration cfg;
cfg.data_width = 8;
cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_3 | RT_SPI_MSB;
cfg.max_hz = 20 * 1000 *1000; /* 20M */
rt_spi_configure(spi_w25q, &cfg);
rt_thread_mdelay(1000);
struct rt_spi_message msg1, msg2;
msg1.send_buf = &w25q_read_id;
msg1.recv_buf = RT_NULL;
msg1.length = 1;
msg1.cs_take = 1;
msg1.cs_release = 0;
msg1.next = &msg2;
msg2.send_buf = RT_NULL;
msg2.recv_buf = id;
msg2.length = 5;
msg2.cs_take = 0;
msg2.cs_release = 1;
msg2.next = RT_NULL;
rt_spi_transfer_message(spi_w25q, &msg1);
return RT_EOK;
}
总结
此处介绍了SPI设备的配置方法及步骤,实现了简单的W25Q芯片ID的读取,关于W25Q具体的操作需要编写代码驱动,会在后续文章中介绍。