EM9280系列产品包括EM9280、EM9287和EM9281,是英创公司新一代的低成本嵌入式主板产品。该主板的SPI接口,在内部DMA(Direct Memory Access直接内存存取)机制的驱动下,其最高数据传输速度可达20Mbps。另外SPI接口可支持4bit、8bit、16bit位长的数据通讯;也可对SPI时序的极性及相位进行设置。
针对SPI接口的应用特点,EM9280的SPI的驱动进行了专门的优化,不仅可支持常规的SPI读、写操作,还可支持外部中断触发的读写操作。中断触发的读写操作,主要应用于工业控制的高速数据采集。另一方面,针对AD芯片控制需求,SPI驱动还支持混合读写模式的数据传输操作。
本文以下部分重点介绍SPI驱动API的使用方法。
操作SPI设备的基本步骤
1、打开SPI设备文件,其设备文件名为“SPI1:”
2、根据应用需求,设置SPI数据帧的基本参数,包括数据长度、波特率、时钟极性等参数。
3、若需要用到外部中断触发SPI读取操作,则需要设置外部GPIO中断管脚,及中断后的读取数据的长度。
4、设置完成后,对常规操作,即可使用标准的ReadFile函数接收SPI数据、使用WriteFile发送SPI数据。
5、对需要读写混合操作的,则需要调用DeviceIoControl来实现。
6、当启动了外部中断,则通过调用DeviceIoControl来等待外部事件,然后再调用ReadFile函数来读取已缓冲在驱动程序内部的SPI数据。
7、调用CloseHandle将关闭SPI接口并清除相关设置。即使重新打开SPI设备文件,需重新设置SPI的参数,才能进行读写。
SPI数据帧参数设置
初始化SPI,需要用到下面这个数据结构:
typedef struct _SPIFrame
{
UCHAR ucBitLength; // SPI数据bit长度,= 4、8、16
DWORD dwBitRate; // SPI波特率,20000000对应20Mbps
BOOL bPhase; // 时钟相位
BOOL bPolarity; // 时钟极性
} SPIFrame , *PSPIFrame;
该数据结构在hw_spi.h头文件中进行的定义,数据结构中的变量说明:
ucBitLength:SPI通讯的数据位长,EM9280/EM9287支持4bit、8bit、16bit三种数据位长格式,在hw_spi.h中定义了这三种数据位长的常量。
dwBitRate:SPI时钟速率,为每秒传输的bit数,参数20000000表示20Mbps,
bPhase:SPI时序相位设置(如下图所示)
bPolarity:SPI时序极性设置(如下图所示)
bPhase=0 , bPolarity=0
bPhase=1 , bPolarity=0
bPhase=0 , bPolarity=1
bPhase=1 , bPolarity=1
SPI设备的初始化例子
HANDLE hSPI;
SPIFrame ConfigSPI;
// 打开设备驱动文件
hSPI = CreateFile(L”SPI1:”, // name of device
GENERIC_READ|GENERIC_WRITE, // desired access
FILE_SHARE_READ|FILE_SHARE_WRITE, // sharing mode
NULL, // security attributes (ignored)
OPEN_EXISTING, // creation disposition
FILE_FLAG_RANDOM_ACCESS, // flags/attributes
NULL); // template file (ignored)
if(hSPI == FALSE )
{
printf('SPI Open False!!!\r\n');
return 0;
}
// 配置SPI参数
ConfigSPI.ucBitLength=SSP_WORD_LENGTH_8BITS; // Len_8BITS
ConfigSPI.dwBitRate=10000000; // 10Mbps
ConfigSPI.bPhase=0;
ConfigSPI.bPolarity=0;
DeviceIoControl(hSPI, // file handle to the driver
SPI_IOCTL_SSPCONFIGURE, // I/O control code
&ConfigSPI, // in buffer
sizeof(ConfigSPI), // in buffer size
NULL,
0,
NULL,
NULL)
SPI接口的单向读写操作
用标准的ReadFile和WriteFile就可实现常规的SPI数据接收(读)或发送(写)。
SPI数据接收的函数调用:
ReadFile(hSPI, // 设备驱动文件句柄
pDatBuf, // 数据buffer指针,注意指针类型!
dwBufLength, // 数据buffer的字节长度
pdwBytesRead, // 实际读取的SPI数据字节数
NULL)
pDatBuf:数据BUFF指针。需要注意的是SPI数据帧长度若为4-bit或8-bit,则每个SPI数据占用一个字节,而对16-bit的SPI数据,则占用2个字节。一般来说,对4-bit或8-bit的SPI传输,其数据buffer应当是BYTE类型的;对16-bit的SPI传输,数据buffer则为WORD类型的。
dwBufLength:需要传输的数据字节长度。该参数是以字节为单位,其涵义也与SPI数据长度有关,对16-bit的SPI传输,dwBufLength应为2的倍数。
pdwBytesRead:SPI数据实际接收的字节数。一个正确的SPI数据接收调用后,指针pdwByteRead所包含的数据应等于dwBufLength,才能表示SPI数据接收执行完全正确。
SPI数据发送的函数调用:
WriteFile(hSPI, // 设备驱动文件句柄
pDatBuf, // 数据buffer指针,事先应把数据填入
dwBufLength, // 数据buffer的字节长度
pdwBytesWritten, // 实际发送的SPI数据字节数
NULL)
发送函数的参数定义与接收函数的参数定义是一致的。特别的,一个正确的SPI数据发送调用后,指针pdwByteWritten所包含的数据应等于dwBufLength。
读写混合型的SPI操作
在SPI的实际应用,有时需要在一个连续的片选过程中,既有读操作,也有写操作。这时间需要用到所谓的混合型SPI操作。
混合型SPI操作需要用到以下数据结构:
typedef struct _SPITransfer
{
LPVOID pTxBuff; // SPI发送buffer指针
LPVOID pRxBuff; // SPI接收buffer指针
DWORD dwBufLength; // 本次SPI传输的字节数
} SPITransfer;
pTxBuff:SPI输出数据BUFF指针
pRxBuff:SPI读入数据BUFF指针
dwBufLength:SPI数据传输长度,以字节为单位
注意,EM9280的SPI接口仅支持半双工操作,因此在上述结构中,只能有一个buffer指针为有效指针,另一个必须为NULL。dwBufLength的定义与单向读写的定义一致。具体的传输是通过DeviceIoControl来实现的,举例说明,本例首先进行发送1个字节(8-bit SPI),然后接收2个字节。
SPITransfer Trans[2];
BYTE Tx[16], Rx[16]; // buffer足够大
Tx[0] = 0xE5; // 发送的字节
Trans[0].pTxBuf = Tx;
Trans[0].pRxBuf = NULL;
Trans[0].dwBufLength = 1; // 要发送1字节
Trans[1].pTxBuf = NULL;
Trans[1].pRxBuf = Rx;
Trans[1].dwBufLength = 2; // 要接收2字节
DeviceIoControl(hSPI,
SPI_IOCTL_EXCHANGE,
Trans, // in buffer
sizeof(Trans) , // in buffer size
NULL,
0,
NULL,
NULL))
在上述调用中需要注意的是,DeviceIoControl()输入参数中的buffer长度必须是数据结构SPITransfer大小的整倍数,否则将被视作无效参数。
外部中断触发的SPI操作
外部中断触发的SPI操作,主要是利用SPI的高速特性,进行实时的大数据量读取。因为SPI的接线非常简单,作为一种高效低成本的接口模式在工业控制领域有广泛的应用。使用这种SPI操作方式,需要用到以下数据结构:
typedef struct _SPI_IrqTransfer
{
DWORD dwGpioPin; // 外部中断管脚,上升沿触发中断
DWORD dwBufLength; // 中断触发的SPI传输的字节数,小于64KB
DWORD dwRVSD; // 保留,必须设置为0
} SPI_IrqTransfer;
dwGpioPin:要用作外部中断源的GPIO引脚
dwBufLength:要读取的数据字节长度
dwRVSD:系统保留,必须设置为0
在上述结构中,dwBufLength的定义与单向读写的定义一致,如果dwGpioPin与dwBufLength同时设置为0,则将关闭已打开的GPIO中断资源并禁止该功能启动。dwGpioPin为EM9280主板的GPIO引脚编号,与GPIO操作时的引脚数据一致。注意:由于系统功能的占用,不是所有的GPIO引脚都可以用作外部中断触发源。
• EM9280可以使用的GPIO引脚有:GPIO0、GPIO1、GPIO6、GPIO7、GPIO10、GPIO11、GPIO20、GPIO21、GPIO22、GPIO23。
• EM9287和EM9281可以使用的GPIO引脚有:GPIO0 - GPIO23。
该操作的具体的设置操作仍然需要调用DeviceIoControl()来实现。
SPI_IrqTransfer irq_transfer;
irq_transfer. dwGpioPin=GPIO0; // 使用GPIO0作为SPI的外部中断源
irq_transfer. dwBufLength=1024; // 中断产生后需要读取1024字节的数据
irq_transfer. dwRVSD=0;
DeviceIoControl(hSPI,
SPI_IOCTL_SSP_IRQTransfer,
& irq_transfer, // 输入参数
sizeof(SPI_IrqTransfer), // 输入参数字节数
NULL,
0,
NULL,
NULL);
设置完成即启动外部中断自动触发SPI操作,一旦中断产生,驱动程序将自动接收dwBufLength长度的数据,存储在驱动程序的内部缓冲区中。数据接收完成后,将发送事件通知应用层。应用程序可通过DeviceIoControl()调用来等待该事件,得到事件后再调用ReadFile读取数据。通过调用DeviceIOControl()等待SPI事件,可以给定一个时间参数作为等待超时的条件,以ms为单位。成功等到SPI执行完成的消息时,DeviceIoControl会返回TRUE,否则返回FALSE。SPI事件等待的调用方法如下:
DeviceIoControl(hSPI,
SPI_IOCTL_SSP_WaitSPIEvent,
&DelayTime, // 等待超时,时间为ms
Sizeof(DWORD),
NULL,
0,
NULL,
NULL)
调用上述方法启动了外部中断触发SPI读取数据的功能后,该功能将一直存在,即每次在所设置的GPIO引脚上产生中断信号,都会执行一次SPI读取操作,直到应用程序关闭该中断,即设置dwGpioPin和dwBufLength等于0,再调用DeviceIoControl()进行设置操作。
SPI操作相关的范例代码请参考光盘中的EM9280_SPIDemo,或来邮件索取或咨询。
全部0条评论
快来发表一下你的评论吧 !