目录
前言
SPI作为基本的通信协议,广泛应用在嵌入式产品中,它是一种高速、全双工、同步通信总线,目前接触的较多的是SPI、QSPI,Dual SPI暂时没接触到,这里不对SPI总线基础知识做记录,本文仅仅是Rt_thread系统下的SPI设备学习笔记,学习RT_thread中对SPI设备的封装,从其设备驱动层、设备框架驱动层以及应用层入手学习,最终实现使用stm32的SPI3驱动LCD显示屏,用QSPI驱动Flash芯片。
1 SPI设备的相关结构体
这里有必要先了解下RT_thread中SPI设备的相关结构体定义,因为我们熟知RT_thread对IO设备采用的编程思路是面向对象,一切都是为了定义一个设备“类”,并实例化成一个设备对象,我们在应用层只需调用“类”成员函数/变量即可访问想要访问的设备。
1.1 SPI设备驱动层结构体
在RT_thread系统下的SPI驱动层定义了几个结构体,分别是:
(1)片选引脚
struct stm32_hw_spi_cs/*片选脚*/
{
GPIO_TypeDef* GPIOx;
uint16_t GPIO_Pin;
};
(2)片上SPI和自定义SPI总线名称
struct stm32_spi_config
{
SPI_TypeDef *Instance;
char *bus_name;
};
(3)SPI设备的引脚号、总线名称以及设备名称。一般规范的写法是,总线名称是spix,挂载在总线上的设备名称为spixy
struct stm32_spi_device
{
rt_uint32_t pin;
char *bus_name;//总线名称
char *device_name;//设备名称
};
(4)SPI驱动类,里面包含了常见的SPI_HandleTypeDef类型的变量,接着嵌套了三个结构体(类),后两者是属于设备框架驱动层的
/* stm32 spi dirver class */
struct stm32_spi
{
SPI_HandleTypeDef handle;
const struct stm32_spi_config *config;
/*下面两个结构体是设备框架驱动层的*/
struct rt_spi_configuration *cfg;
struct rt_spi_bus spi_bus;
};
1.2 SPI设备框架驱动层结构体
在RT_thread系统下的SPI框架驱动层定义了几个结构体,分别是:
(1)SPI的配置结构体,包含了模式,通信数据位数宽度,和最大通信频率
struct rt_spi_configuration
{
rt_uint8_t mode;
rt_uint8_t data_width;
rt_uint16_t reserved;
rt_uint32_t max_hz;
};
(2)SPI总线结构体,继承struct rt_device,同时又有模式变量,关键的还嵌套了spi的操作函数结构体,额外加上一个互斥锁机制,最后嵌套了表述SPI总线的拥有者属于哪个设备的struct rt_spi_device结构体。
struct rt_spi_bus
{
struct rt_device parent;
rt_uint8_t mode;
const struct rt_spi_ops *ops;
struct rt_mutex lock;
struct rt_spi_device *owner;
};
(3)SPI相关操作函数结构体,仅仅两个函数,一个配置,另一个数据传输。
struct rt_spi_ops
{
rt_err_t (*configure)(struct rt_spi_device *device, struct rt_spi_configuration *configuration);
rt_uint32_t (*xfer)(struct rt_spi_device *device, struct rt_spi_message *message);
};
(4)SPI的消息结构体,用户可以自定义每个待传输的消息结构体各参数的数值,从而可以
很方便的控制数据传输方式。注意其中的length要小于等于发送或接收的缓存大小,不然执行程序会导致缓冲区溢出。
struct rt_spi_message
{
const void *send_buf;/*发送缓冲区指针*/
void *recv_buf;/*接收缓冲区指针*/
rt_size_t length;/*发送/接收字节数*/
struct rt_spi_message *next;/*指向下一个消息,对于发送多条消息比较有用,单向链表*/
unsigned cs_take : 1;/*片选选中*/
unsigned cs_release : 1;/*片选信号释放*/
};
(5)最后一个,SPI设备结构体
struct rt_spi_device
{
struct rt_device parent;
struct rt_spi_bus *bus;
struct rt_spi_configuration config;
void *user_data;
};
/关于QSPI,另起笔记,这里暂不做记录/
2 SPI设备驱动层
万丈高楼平地起,要使用SPI设备之前,需要先将SPI总线底层驱动写好,有SPI的初始化、SPI的收发驱动。RT_thread有stm32的SPI驱动,可以直接拿过来用。下面解析下在STM32平台下RT_thread中的SPI驱动。
2.1 SPI的初始化
用函数stm32_spi_init更新struct stm32_spi结构体中的SPI_HandleTypeDef型变量handle,并且完成SPI的初始化。函数stm32_spi_init 的原型为:
static rt_err_t stm32_spi_init(struct stm32_spi *spi_drv, struct rt_spi_configuration *cfg)
{
/*判断传参是否有效*/
RT_ASSERT(spi_drv != RT_NULL);
RT_ASSERT(cfg != RT_NULL);
/*获取spix的handle句柄*/
SPI_HandleTypeDef *spi_handle = &spi_drv->handle;
/*更新主、从模式*/
if (cfg->mode & RT_SPI_SLAVE)
{
spi_handle->Init.Mode = SPI_MODE_SLAVE;
}
else
{
spi_handle->Init.Mode = SPI_MODE_MASTER;
}
/*配置SPI总线的数据线方向:单线、双线*/
if (cfg->mode & RT_SPI_3WIRE)
{
spi_handle->Init.Direction = SPI_DIRECTION_1LINE;
}
else
{
spi_handle->Init.Direction = SPI_DIRECTION_2LINES;
}
/*SPI总线的数据宽度*/
if (cfg->data_width == 8)
{
spi_handle->Init.DataSize = SPI_DATASIZE_8BIT;
spi_handle->TxXferSize = 8;
spi_handle->RxXferSize = 8;
}
else if (cfg->data_width == 16)
{
spi_handle->Init.DataSize = SPI_DATASIZE_16BIT;
}
else
{
return RT_EIO;
}
/*配置SPI总线的模式,有4种*/
if (cfg->mode & RT_SPI_CPHA)
{
spi_handle->Init.CLKPhase = SPI_PHASE_2EDGE;
}
else
{
spi_handle->Init.CLKPhase = SPI_PHASE_1EDGE;
}
if (cfg->mode & RT_SPI_CPOL)
{
spi_handle->Init.CLKPolarity = SPI_POLARITY_HIGH;
}
else
{
spi_handle->Init.CLKPolarity = SPI_POLARITY_LOW;
}
/*SPI的片选,这里都选软片选*/
if (cfg->mode & RT_SPI_NO_CS)
{
spi_handle->Init.NSS = SPI_NSS_SOFT;
}
else
{
spi_handle->Init.NSS = SPI_NSS_SOFT;
}
/*SPI的总线波特率设置*/
uint32_t SPI_APB_CLOCK;
SPI_APB_CLOCK = HAL_RCC_GetPCLK2Freq();
if (cfg->max_hz >= SPI_APB_CLOCK / 2)
{
spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
}
else if (cfg->max_hz >= SPI_APB_CLOCK / 4)
{
spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
}
else if (cfg->max_hz >= SPI_APB_CLOCK / 8)
{
spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
}
else if (cfg->max_hz >= SPI_APB_CLOCK / 16)
{
spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
}
else if (cfg->max_hz >= SPI_APB_CLOCK / 32)
{
spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;
}
else if (cfg->max_hz >= SPI_APB_CLOCK / 64)
{
spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64;
}
else if (cfg->max_hz >= SPI_APB_CLOCK / 128)
{
spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;
}
else
{
/* min prescaler 256 */
spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
}
LOG_D("sys freq: %d, pclk2 freq: %d, SPI limiting freq: %d, BaudRatePrescaler: %d",
HAL_RCC_GetSysClockFreq(),
SPI_APB_CLOCK,
cfg->max_hz,
spi_handle->Init.BaudRatePrescaler);
/*SPI传输数据是高位优先还是低位优先*/
if (cfg->mode & RT_SPI_MSB)
{
spi_handle->Init.FirstBit = SPI_FIRSTBIT_MSB;
}
else
{
spi_handle->Init.FirstBit = SPI_FIRSTBIT_LSB;
}
spi_handle->Init.TIMode = SPI_TIMODE_DISABLE;
spi_handle->Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
spi_handle->State = HAL_SPI_STATE_RESET;
/*调用hal库函数初始化SPI*/
if (HAL_SPI_Init(spi_handle) != HAL_OK)
{
return RT_EIO;
}
/*调用Hal库函数使能SPI*/
__HAL_SPI_ENABLE(spi_handle);
LOG_D("%s init done", spi_drv->config->bus_name);
return RT_EOK;
}
上面的函数被函数spi_configure调用,体现为:
static rt_err_t spi_configure(struct rt_spi_device *device,
struct rt_spi_configuration *configuration)
{
/*先判断传参有效性*/
RT_ASSERT(device != RT_NULL);
RT_ASSERT(configuration != RT_NULL);
/*获取device->bus隶属的struct stm32_spi类型的结构体句柄*/
/*然后更新结构体中的cfg*/
struct stm32_spi *spi_drv = rt_container_of(device->bus, struct stm32_spi, spi_bus);
spi_drv->cfg = configuration;
return stm32_spi_init(spi_drv, configuration);
}
2.2 SPI总线的收发
static rt_uint32_t spixfer(struct rt_spi_device *device, struct rt_spi_message *message)
{
/*检查传参有效性*/
RT_ASSERT(device != RT_NULL);
RT_ASSERT(device->bus != RT_NULL);
RT_ASSERT(device->bus->parent.user_data != RT_NULL);
RT_ASSERT(message != RT_NULL);
/*获取SPI设备对象的硬件SPI信息*/
struct stm32_spi *spi_drv = rt_container_of(device->bus, struct stm32_spi, spi_bus);
SPI_HandleTypeDef * spi_handle = &spi_drv->handle;
/*RT_thread中的SPI设备一般用成员user_data存放SPI的片选信息*/
/*获取片选引脚信息*/
struct stm32_hw_spi_cs *cs = device->parent.user_data;
rt_int32_t length = message->length;//消息长度
/*总线数据宽度*/
rt_int32_t data_width = spi_drv->cfg->data_width;
/*若选中片选*/
if (message->cs_take)
{
HAL_GPIO_WritePin(cs->GPIOx, cs->GPIO_Pin, GPIO_PIN_RESET);//片选选中
}
/*总线数据宽度是8位*/
if (data_width == 8)
{
const rt_uint8_t * send_ptr = message->send_buf;
rt_uint8_t * recv_ptr = message->recv_buf;
/*读取和写入SPI的DR寄存器,实现数据收发*/
while (length--)
{
rt_uint8_t data = ~0;
if(send_ptr != RT_NULL)
{
data = *send_ptr++;
}
/* send data once */
while (__HAL_SPI_GET_FLAG(spi_handle, SPI_FLAG_TXE) == RESET);
*(volatile rt_uint8_t *)(&spi_handle->Instance->DR) = data;
/* receive data once */
SET_BIT(spi_handle->Instance->CR2, SPI_RXFIFO_THRESHOLD_HF);
while (__HAL_SPI_GET_FLAG(spi_handle, SPI_FLAG_RXNE) == RESET);
data = *(volatile rt_uint8_t *)(&spi_handle->Instance->DR);
if(recv_ptr != RT_NULL)
{
*recv_ptr++ = data;
}
}
} else//1位的
{
/**/
}
/*这里需要先判断忙标志是否松开,然后再释放片选,保证数据正确完整*/
/* Wait until Busy flag is reset before disabling SPI */
while (__HAL_SPI_GET_FLAG(spi_handle, SPI_FLAG_BSY) == SET);
if (message->cs_release)
{
HAL_GPIO_WritePin(cs->GPIOx, cs->GPIO_Pin, GPIO_PIN_SET);
}
return message->length;
}
现在SPI的初始化和收发函数已经有了,把它们俩封装进struct rt_rt_spi_ops结构体里面:
static const struct rt_spi_ops stm_spi_ops =
{
.configure = spi_configure,
.xfer = spixfer,
};
应用层的程序可以间接调用stm_spi_ops结构体中的函数实现SPI的初始化和通信。为了实现这一功能,需要将stm_spi_ops中的成员赋值给rt_device设备对象的函数指针。下面的函数rt_hw_spi_bus_init() 的功能是给SPI总线对象赋值,并将它注册到系统中。
static int rt_hw_spi_bus_init(void)
{
rt_err_t result;
for (int i = 0; i < sizeof(spi_config) / sizeof(spi_config[0]); i++)
{
spi_bus_obj[i].config = &spi_config[i];
spi_bus_obj[i].spi_bus.parent.user_data = &spi_config[i];
spi_bus_obj[i].handle.Instance = spi_config[i].Instance;
result = rt_spi_bus_register(&spi_bus_obj[i].spi_bus, spi_config[i].bus_name, &stm_spi_ops);
RT_ASSERT(result == RT_EOK);
LOG_D("%s bus init done", spi_config[i].bus_name);
}
return result;
}
注册好SPI总线之后,就可以往总线上挂载设备了,使用函数rt_hw_spi_device_attach() 将SPI设备挂载到SPI总线上,并且将SPI设备注册到IO设备管理器中。
rt_err_t rt_hw_spi_device_attach(const char *bus_name, const char *device_name, GPIO_TypeDef* cs_gpiox, uint16_t cs_gpio_pin)
{
RT_ASSERT(bus_name != RT_NULL);
RT_ASSERT(device_name != RT_NULL);
rt_err_t result;
struct rt_spi_device *spi_device;
struct stm32_hw_spi_cs *cs_pin;
/* initialize the cs pin && select the slave*/
GPIO_InitTypeDef GPIO_Initure;
GPIO_Initure.Pin = cs_gpio_pin;
GPIO_Initure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_Initure.Pull = GPIO_PULLUP;
GPIO_Initure.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(cs_gpiox, &GPIO_Initure);
HAL_GPIO_WritePin(cs_gpiox, cs_gpio_pin, GPIO_PIN_SET);
/* attach the device to spi bus*/
spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device));
RT_ASSERT(spi_device != RT_NULL);
cs_pin = (struct stm32_hw_spi_cs *)rt_malloc(sizeof(struct stm32_hw_spi_cs));
RT_ASSERT(cs_pin != RT_NULL);
cs_pin->GPIOx = cs_gpiox;
cs_pin->GPIO_Pin = cs_gpio_pin;
result = rt_spi_bus_attach_device(spi_device, device_name, bus_name, (void *)cs_pin);
if (result != RT_EOK)
{
LOG_E("%s attach to %s faild, %d\n", device_name, bus_name, result);
}
RT_ASSERT(result == RT_EOK);
LOG_D("%s attach to %s done", device_name, bus_name);
return result;
}
2 SPI设备框架驱动层
2.1 SPI总线注册函数
上面提到的函数rt_spi_bus_register() 原型如下:
rt_err_t rt_spi_bus_register(struct rt_spi_bus *bus,
const char *name,
const struct rt_spi_ops *ops)
{
rt_err_t result;
/*spi总线注册到IO设备管理器中*/
result = rt_spi_bus_device_init(bus, name);
if (result != RT_EOK)
return result;
/* 注册成功之后初始化互斥锁 */
rt_mutex_init(&(bus->lock), name, RT_IPC_FLAG_FIFO);
/* set ops */
bus->ops = ops;
/* initialize owner */
bus->owner = RT_NULL;
/* set bus mode */
bus->mode = RT_SPI_BUS_MODE_SPI;
return RT_EOK;
}
里面调用函数rt_spi_bus_device_init的原型是:
/*初始化总线设备*/
rt_err_t rt_spi_bus_device_init(struct rt_spi_bus *bus, const char *name)
{
struct rt_device *device;
RT_ASSERT(bus != RT_NULL);
/*更新总线的父类*/
device = &bus->parent;
/* set device type */
device->type = RT_Device_Class_SPIBUS;
/* initialize device interface */
#ifdef RT_USING_DEVICE_OPS
device->ops = &spi_bus_ops;
#else
device->init = RT_NULL;
device->open = RT_NULL;
device->close = RT_NULL;
device->read = _spi_bus_device_read;
device->write = _spi_bus_device_write;
device->control = _spi_bus_device_control;
#endif
/* 将SPI总线注册到设备管理器中 */
return rt_device_register(device, name, RT_DEVICE_FLAG_RDWR);
}
2.2 总线挂载设备函数
在前面的函数rt_hw_spi_device_attach() 中调用了函数rt_spi_bus_attach_device() ,后者函数原型如下:
rt_err_t rt_spi_bus_attach_device(struct rt_spi_device *device, const char *name, const char *bus_name, void *user_data)
{
rt_err_t result;
rt_device_t bus;
/* 获取spi总线,返回SPI总线句柄 */
bus = rt_device_find(bus_name);
if (bus != RT_NULL && bus->type == RT_Device_Class_SPIBUS)
{
device->bus = (struct rt_spi_bus *)bus;
/* 初始化SPI设备 */
result = rt_spidev_device_init(device, name);
if (result != RT_EOK)
return result;
rt_memset(&device->config, 0, sizeof(device->config));
device->parent.user_data = user_data;
return RT_EOK;
}
/* not found the host bus */
return -RT_ERROR;
}
rt_err_t rt_spidev_device_init(struct rt_spi_device *dev, const char *name)
{
struct rt_device *device;
RT_ASSERT(dev != RT_NULL);
/*更新SPI设备的父类*/
device = &(dev->parent);
/* set device type */
device->type = RT_Device_Class_SPIDevice;
#ifdef RT_USING_DEVICE_OPS
device->ops = &spi_device_ops;
#else
device->init = RT_NULL;
device->open = RT_NULL;
device->close = RT_NULL;
device->read = _spidev_device_read;
device->write = _spidev_device_write;
device->control = _spidev_device_control;
#endif
/* 将SPI设备注册到IO设备管理器中 */
return rt_device_register(device, name, RT_DEVICE_FLAG_RDWR);
}
在SPI总线注册好了以后,我们就可以将设备(例如:SPI flash、SPI接口型的LCD、其他SPI接口芯片)虚拟成SPI设备spix0,spix1,spix2,…,将它们挂载到总线上,其中spix表示SPI总线的名称。
2.3 SPI总线和设备对象的成员函数
注意,RT_thread中不但将总线看成IO设备对象,还对总线上的设备看成IO设备对象,所以整个框架说到底最终是配置两个结构体,一个是struct rt_spi_bus,另一个是struct rt_spi_device。
2.3.1 SPI总线成员函数
在前面的函数rt_spi_bus_device_init中对SPI总线控制块关联上了3个操作函数:
(1)_spi_bus_device_read
(2)_spi_bus_device_write
(3)_spi_bus_device_control
这3个函数比较简单,原型如下:
static rt_size_t _spi_bus_device_read(rt_device_t dev,
rt_off_t pos,
void *buffer,
rt_size_t size)
{
struct rt_spi_bus *bus;
bus = (struct rt_spi_bus *)dev;
RT_ASSERT(bus != RT_NULL);
RT_ASSERT(bus->owner != RT_NULL);
return rt_spi_transfer(bus->owner, RT_NULL, buffer, size);
}
static rt_size_t _spi_bus_device_write(rt_device_t dev,
rt_off_t pos,
const void *buffer,
rt_size_t size)
{
struct rt_spi_bus *bus;
bus = (struct rt_spi_bus *)dev;
RT_ASSERT(bus != RT_NULL);
RT_ASSERT(bus->owner != RT_NULL);
return rt_spi_transfer(bus->owner, buffer, RT_NULL, size);
}
static rt_err_t _spi_bus_device_control(rt_device_t dev,
int cmd,
void *args)
{
/* TODO: 自己按需求添加命令扩展 */
switch (cmd)
{
case 0: /* set device */
break;
case 1:
break;
}
return RT_EOK;
}
前两个函数都是调用函数rt_spi_transfer进行总线读写,我们看看函数rt_spi_transfer(归属文件名“spi_core.c”)的原型是什么:
rt_size_t rt_spi_transfer(struct rt_spi_device *device,
const void *send_buf,
void *recv_buf,
rt_size_t length)
{
rt_err_t result;
struct rt_spi_message message;
/*首先判断SPI设备是否有效、总线是否有效*/
RT_ASSERT(device != RT_NULL);
RT_ASSERT(device->bus != RT_NULL);
/*占用互斥锁*/
result = rt_mutex_take(&(device->bus->lock), RT_WAITING_FOREVER);
if (result == RT_EOK)
{
/*如果总线上的SPI设备不是当前的SPI设备*/
if (device->bus->owner != device)
{
/* not the same owner as current, re-configure SPI bus */
result = device->bus->ops->configure(device, &device->config);
//实际上调用spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *configuration)
if (result == RT_EOK)
{
/* 将总线归属于当前设备 */
device->bus->owner = device;
}
else
{
/* configure SPI bus failed */
rt_set_errno(-RT_EIO);
result = 0;
goto __exit;
}
}
/* 初始化待传输的消息 */
message.send_buf = send_buf;
message.recv_buf = recv_buf;
message.length = length;
message.cs_take = 1;
message.cs_release = 1;
message.next = RT_NULL;//表明这里只发送一次消息
/* 开始传输消息 */
result = device->bus->ops->xfer(device, &message);
//实际上调用spixfer(struct rt_spi_device *device, struct rt_spi_message *message);
if (result == 0)
{
rt_set_errno(-RT_EIO);
goto __exit;
}
}
else
{
rt_set_errno(-RT_EIO);
return 0;
}
__exit:
/*释放互斥锁*/
rt_mutex_release(&(device->bus->lock));
return result;
}
注意,上面的rt_spi_transfer 用到了互斥信号量,所以不能在中断服务函数中调用函数rt_spi_transfer,会出现assertion错误。
2.3.2 SPI设备成员函数
SPI设备成员函数有3个,跟SPI总线的成员函数基本上一样,读写用的还是同样的函数rt_spi_transfer()
static rt_size_t _spidev_device_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
struct rt_spi_device *device;
device = (struct rt_spi_device *)dev;
RT_ASSERT(device != RT_NULL);
RT_ASSERT(device->bus != RT_NULL);
return rt_spi_transfer(device, RT_NULL, buffer, size);
}
static rt_size_t _spidev_device_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
struct rt_spi_device *device;
device = (struct rt_spi_device *)dev;
RT_ASSERT(device != RT_NULL);
RT_ASSERT(device->bus != RT_NULL);
return rt_spi_transfer(device, buffer, RT_NULL, size);
}
static rt_err_t _spidev_device_control(rt_device_t dev, int cmd, void *args)
{
switch (cmd)
{
case 0: /* set device */
break;
case 1:
break;
}
return RT_EOK;
}
3 SPI应用层
一般情况下MCU的SPI器件都是作为主机和从机通讯,在RT_thread中将SPI主机虚拟为SPI总线设备,应用程序使用SPI设备管理接口来访问SPI从机器件,主要接口有:
函数 | 描述 |
---|---|
rt_device_find() | 根据SPI设备名称查找设备获取设备句柄 |
rt_spi_transfer_message() | 自定义传输数据 |
rt_spi_transfer() | 传输一次数据 |
rt_spi_send() | 发送一次数据 |
rt_spi_recv() | 接收一次数据 |
rt_spi_send_then_send() | 连续两次发送 |
rt_spi_send_then_recv() | 先发送后接收 |
rt_spi_configure() | 配置SPI |
上面的这几个函数最终调用的函数前面已经介绍,这里不增加篇幅记录。
以上都是对学习RT_thread系统BSP下的SPI设备的记录笔记,如果有哪里说错或者哪里记录有问题的,还请大佬们不吝赐教,一起学习RT_thread!
总结
相比于裸机下或者其他RTOS系统,我们写SPI驱动实现对外设访问的代码不算多,总体没有Rtthread的复杂,RT_thread的复杂性主要表现在它对设备的程序包装。在使用RT_thread的SPI设备驱动外设时,主要注意几个点:
(1)调用函数rt_hw_spi_device_attach() 将SPI设备挂载到SPI总线上,并完成注册。
(2)使用函数rt_device_find() 查找SPI设备返回SPI设备的句柄。
(3)根据外设使用函数rt_spi_configure() 稍微修改SPI设备驱动层的配置,比如SPI的模式、速率可能需要修改。
(4)对SPI设备进行读写操作。