![]() |
![]() |
![]() |
SPI驱动
这一节我们详细讲解TI CC13x0/CC26x0 SDK开发平台 基于TI-RTOS的SPI驱动实现,主要了解SPI驱动的分层实现、驱动接口,以及结合开发板板载SPIFlash调试通过驱动。
概述
SPI(Serial Perripheral Interface),串行外设接口,是Motorala公司推出的一种同步串行接口技术,它能够使MCU以全双工(数据能够同时进行双向传输)的同步串行方式与各种外围设备进行高速数据通信。它主要用在EEPROM、Flash、实时时钟(RTC)、数模转换器(ADC)、数字信号处理器(DSP)以及数字信号解码器之间,他只占用芯片的4根管脚来实现控制和数据传输,现在很多芯片上都集成了SPI技术。SPI有时也称为SSI(同步串行接口)。SPI的控制方式采用主-从模式(Master-Slave)。
SPI驱动程序能够驱动芯片与外围设备在SPI总线上进行数据的传输和接收。SPI驱动程序接口是典型的RTOS应用程序调用接口,它们被重定向到SPI_FxnTalble
中的指针指定的特定驱动程序实现,增强了程序的移植性。
SPI驱动的分层实现
虽然我们在应用层直接调用几个驱动接口就可以在SPI总线上发送数据,但是在驱动程序内部从接口函数到底层硬件操作是通过了多层封装的。如图1所示是SPI驱动程序的分层实现图:
图1 SPI驱动程序的分层实现
由图1我们可以看到,应用程序开发者只需要直接调用中间件层的驱动接口(例如:SPI_init,SPI_open等等)就可以实现SPI的驱动功能,这里的中间件层就是我们程序中的SPI.c和SPI.h所在层。这一层规范统一了应用程序的调用接口,也就是说对于TI不同类型的芯片平台它们在这一层给出的接口都是一样的。应用层都是调用相同的接口来实现SPI功能,这样做的好处在于增强了程序的可移植性,不管你的平台怎么换,我的应用程序都是不变的,因为驱动的接口相同。
中间件层往下就是业务逻辑层,从业务逻辑层开始往下根据不同的芯片平台其接口封装实现就不尽相同了。这里我们以CC26XX芯片平台为例,业务逻辑层就位于SPICC26XXDMA.c和SPICC26XXDMA.h所在的层。这里采用DMA的数据传输方式,所以这一层主要在操控DMA以进行数据传输以及调用驱动库中的一些函数实现相应功能。需要注意的是这一层封装的驱动接口函数被全部放在一个函数指针结构体中,如List1所示,中间件层不直接调用这些驱动接口,而是通过一个配置文件(CC2640R2_LAUNCHXL.c)将装有驱动接口指针的结构体指针注册到SPI_config中,如List2所示,这样中间件层通过调用SPI_config中的结构体指针就可以指定使用业务逻辑层的驱动接口了。
List1:业务逻辑层驱动接口指针结构体
const SPI_FxnTable SPICC26XXDMA_fxnTable = {
SPICC26XXDMA_close,
SPICC26XXDMA_control,
SPICC26XXDMA_init,
SPICC26XXDMA_open,
SPICC26XXDMA_transfer,
SPICC26XXDMA_transferCancel
};
List2:SPI_config中的驱动接口结构体指针注册
const SPI_Config SPI_config[CC2640R2_LAUNCHXL_SPICOUNT] = {
{
.fxnTablePtr = &SPICC26XXDMA_fxnTable,
.object = &spiCC26XXDMAObjects[CC2640R2_LAUNCHXL_SPI0],
.hwAttrs = &spiCC26XXDMAHWAttrs[CC2640R2_LAUNCHXL_SPI0]
},
{
.fxnTablePtr = &SPICC26XXDMA_fxnTable,
.object = &spiCC26XXDMAObjects[CC2640R2_LAUNCHXL_SPI1],
.hwAttrs = &spiCC26XXDMAHWAttrs[CC2640R2_LAUNCHXL_SPI1]
},
};
业务逻辑层再往下就是驱动库层(driver library),业务逻辑层会直接调用这一层的接口函数来来进行相应逻辑操作的。驱动库层位于ssi.c和ssi.h所在的层,这一层就开始与硬件接触,进行相应寄存器操作来实现SPI驱动了。
SPI的驱动配置
在上文中我们已经提到SPI的配置数组SPI_config[],它位于相应芯片平台的配置文件中,这里我们以CC26XX芯片平台为例,其配置文件为CC2640R2_LAUNCHXL.c。如List3所示,是CC2640R2_LAUNCHXL.c中关于SPI的配置代码段。
List3:UART的配置代码段
/*
* =============================== SPI DMA ===============================
*/
#include <ti/drivers/SPI.h>
#include <ti/drivers/spi/SPICC26XXDMA.h>
SPICC26XXDMA_Object spiCC26XXDMAObjects[CC2640R2_LAUNCHXL_SPICOUNT];
const SPICC26XXDMA_HWAttrsV1 spiCC26XXDMAHWAttrs[CC2640R2_LAUNCHXL_SPICOUNT] = {
{
.baseAddr = SSI0_BASE,
.intNum = INT_SSI0_COMB,
.intPriority = ~0,
.swiPriority = 0,
.powerMngrId = PowerCC26XX_PERIPH_SSI0,
.defaultTxBufValue = 0,
.rxChannelBitMask = 1<<UDMA_CHAN_SSI0_RX,
.txChannelBitMask = 1<<UDMA_CHAN_SSI0_TX,
.mosiPin = CC2640R2_LAUNCHXL_SPI0_MOSI,
.misoPin = CC2640R2_LAUNCHXL_SPI0_MISO,
.clkPin = CC2640R2_LAUNCHXL_SPI0_CLK,
.csnPin = CC2640R2_LAUNCHXL_SPI0_CSN
},
};
const SPI_Config SPI_config[CC2640R2_LAUNCHXL_SPICOUNT] = {
{
.fxnTablePtr = &SPICC26XXDMA_fxnTable,
.object = &spiCC26XXDMAObjects[CC2640R2_LAUNCHXL_SPI0],
.hwAttrs = &spiCC26XXDMAHWAttrs[CC2640R2_LAUNCHXL_SPI0]
},
};
const uint_least8_t SPI_count = CC2640R2_LAUNCHXL_SPICOUNT;
我们可以看到SPI_config[]数组中的元素有三个参数,分别是.fxnTablePtr
,.object
,.hwAttrs
,下面我们分别来看一下这三个参数的意义。
- .fxnTablePtr
.fxnTablePtr
里面放的就是我们驱动具体的实现函数,这些驱动函数就是来自业务逻辑层。我们看到这里它被赋值成SPICC26XXDMA_fxnTable
的指针,这个结构体就是List1中所示的业务逻辑层的驱动函数列表,在这里进行赋值配置之后,中间层的接口函数就可以链接使用它们了。 - .object
.object
是用来存放SPI的各种参数数据的,例如控制参数,传输参数等。 - .hwAttrs
.hwAttrs
是用来存放SPI硬件配置参数的数组,如List3所示,这些硬件参数是需要我们在使用SPI之前需要配置好的。
SPI驱动接口
在SPI的中间件文件SPI.c和SPI.h中,我们看到一共给出了7个接口函数,下面我们分别看一下每个接口函数的功能,形参,返回值以及注意事项。
void SPI_init(void)