最近遇到某客户需要调试一款SPI接口的NFC模块fm17550,它采用SPI接口,规格书上说明最高支持10Mbps。但是客户对寄存器的读写时间有要求:每个寄存器(8位)的读取时间不能超过60us。
采用Android 驱动原生的代码,增加打印:
printk(KERN_ERR “start read reg \n”);
tmp = Read_Reg((u8)tmp);
printk(KERN_ERR “end cmd=%d %02X\n”, cmd, (u8)tmp);
[ 900.232668] start read reg
[ 900.235575] cmd=8 00
[ 900.237496] start read reg
[ 900.240704] cmd=8 03
读寄存器的时间大约4ms,(当然增加printk打印会多耗费时间的)这个时间的差距和客户的要求不在一个数量级的。
linux驱动中spi的读写时间耗在哪儿了呢?继续增加打印:
Read_Reg–>
spi_write_then_read (kernel\drivers\spi\spi.c)–>
spi_sync -->
__spi_sync -->
wait_for_completion(&done);
这里wait_for_completion是需要等待进程完成的。
高通的SPI驱动涉及到管道pipe的设置、中断的设置,写寄存器之前要检查spi的状态、读寄存器要等待中断
writel_relaxed(word, dd->base + SPI_OUTPUT_FIFO);
data_in = readl_relaxed(dd->base + SPI_INPUT_FIFO);
如果用IOctrl的方式,从用户层调用到驱动,时间上也是不能满足要求的。
究其原因,linux并不是一个实时系统,spi驱动一般都不考虑实时性,都是将message挂到spi控制器的queue上,然后调用queue_work或者采用类似方法。
在驱动层修改的路走不通,参考网上的提示,在用户空间把GPIO的地址映射过来,直接操作寄存器试试。
参考代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#define Anticollision 0x02
#define TIMEOUT_Err 0x20
#define CommandReg 0x01
#define ComIEnReg 0x02
#define DivIEnReg 0x03
#define ComIrqReg 0x04
#define DivIrqReg 0x05
#define ErrorReg 0x06
#define Status1Reg 0x07
#define Status2Reg 0x08
#define FIFODataReg 0x09
#define FIFOLevelReg 0x0A
#define WaterLevelReg 0x0B
#define ControlReg 0x0C
#define BitFramingReg 0x0D
#define CollReg 0x0E
#define ModeReg 0x11
#define TxModeReg 0x12
#define RxModeReg 0x13
#define TxControlReg 0x14
#define TxAutoReg 0x15
#define TxSelReg 0x16
#define RxSelReg 0x17
#define RxThresholdReg 0x18
#define DemodReg 0x19
#defin

针对客户对SPI接口NFC模块读写时间的要求,原生Android驱动耗时过长。分析发现,Linux SPI驱动的延迟主要在于进程等待和中断处理。为达到60us的读取时间,尝试在用户空间直接映射GPIO访问寄存器,通过/dev/mem进行内存映射并注意地址对齐,成功将读取时间降至满足条件。但这种方法在批量传输和容错性方面存在局限性。
最低0.47元/天 解锁文章
6053





