开篇分割线,再研究驱动程序之前我们先看下RTT 关于SPI应用的代码层级结构:
应用层:只做应用层开发的小伙伴是有福的,你不需要了解整个驱动是怎样构架的,只需要了解驱动构架层提供的API接口函数直接调用,即可操作SPI设备。
设备驱动框架层:由RTT系统提供的一个重要的中间层,用于驱动层和应用层之间联系的建立,对应用层开放API调用接口,对驱动层开放注册函数和操作函数,将驱动层的操作注册到系统中,实现应用与驱动之间的软连接。
总线设备驱动层:该层是驱动开发工程师的主攻战场,后面展开详细讲。
硬件SPI控制器层:一般情况下就是MCU自带的控制外设,这里当然就是SPI控制器,玩过单片机的小伙伴最熟悉的部分。
外挂模块层:最后一层就是SPI类型总线通信设备的模块,常用的flash芯片、网络控制模块等都生活在这一层,实际电路中通过SPI通信线与MCU连接。
接下来就是看下基于STM32开发SPI的设备模型(模型即结构体定义)了,代码如下:
/* stm32 spi dirver class */
struct stm32_spi
{
SPI_HandleTypeDef handle;
struct stm32_spi_config *config;
struct rt_spi_configuration *cfg;
struct
{
DMA_HandleTypeDef handle_rx;
DMA_HandleTypeDef handle_tx;
} dma;
rt_uint8_t spi_dma_flag;
struct rt_spi_bus spi_bus;
};
这里需要注意的是,模型的最后一个成员spi_bus,它是由RTT系统提供,有了它这个模型就与系统之间产生了联系,spi_bus是系统的一个SPI类型硬件设备模型。 有了模型以后,我们就是针对这个模型进行设备的实例化,一般来讲MCU会有多个SPI控制器,所以针对这一情况,我们就定义了另一个模型专门用于实例化SPI对象模型:
struct stm32_spi_config
{
SPI_TypeDef *Instance; /*SPI外设*/
char *bus_name; /*SPI总线设备名称*/
struct dma_config *dma_rx, *dma_tx; /*DMA发送及接收配置参数*/
};
接下来需要采用预处理命令宏定义硬件外设所有的SPI设备配置项,至于是否开启我们可以通过rt_config.h文件是否定义该宏来决定:
#ifdef BSP_USING_SPI1
#ifndef SPI1_BUS_CONFIG
#define SPI1_BUS_CONFIG \
{ \
.Instance = SPI1, \
.bus_name = "spi1", \
}
#endif /* SPI1_BUS_CONFIG */
#endif /* BSP_USING_SPI1 */
#ifdef BSP_USING_SPI2
#ifndef SPI2_BUS_CONFIG
#define SPI2_BUS_CONFIG \
{ \
.Instance = SPI2, \
.bus_name = "spi2", \
}
#endif /* SPI2_BUS_CONFIG */
#endif /* BSP_USING_SPI2 */
有了设备配置模型,就需要定义一个真实的需要配置的设备模型数据,用于实例化多个SPI控制器,实际上就一个数组搞定:
static struct stm32_spi_config spi_config[] =
{
#ifdef BSP_USING_SPI1
SPI1_BUS_CONFIG,
#endif
#ifdef BSP_USING_SPI2
SPI2_BUS_CONFIG,
#endif
};
有了设备类型模型,也有了配置模型数组,就需要定义通过模型来定义真实的设备实例,定义完并没有进行初始化:
/*定义SPI总线对象*/
static struct stm32_spi spi_bus_obj[sizeof(spi_config) / sizeof(spi_config[0])] = {0};
到这里,关于SPI总线对象的创建就完了,但是关于SPI总线对象还是一个空空如也的数组,我想你也应该知道如何基于配置对象给总线对象进行初始化,当然这个我们下篇接着讲。