浅谈SPI驱动API的使用方法

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,或来邮件索取或咨询。

联想旗下摩托罗拉razr 5G折叠屏手机在中国正式发布
华为发布新专利,能用眼球操控屏幕的开关
语音芯片型号有哪些?为什么NVD系列更受“欢迎”?
中国在全球AI学术研究领域有多大的影响力?
索尼靓咔KW1相机评测 一种品位和格调的体现
浅谈SPI驱动API的使用方法
水处理工艺知识
影响无线监控系统视频稳定的因素分析
IIC-China 2010参展商展前专访:苏州雷度电子
土壤水分传感器电源要求不受土壤质地影响?
7月18日,备受期待的华为nova 3就要发布了
预发机料位测量中为何要选择可靠性高的料位开关
李斌:对于创业,你要做到就是尽量确保自己做对的事比做错的事要多
如何合理的利用物联网打造智能家居系统
三星在国内发布国行版本新旗舰Note 9,底部为Type-C接口,支持快充技术
荣耀6Plus拆解 做工及用料如何
谷歌在蒙特利尔新成立人工智能实验室
TE Connectivity 推出高压端子和连接器的全新易购选件
消息称荣耀V40不会配置麒麟9000系列处理器
RFIC设计中的MOS变容管