SPI驱动之南瑞NRSEC3000加密芯片驱动开发总结

来源:互联网 发布:网络短信收费版 编辑:程序博客网 时间:2024/05/20 21:58

绪:

    这是笔者第一次做SPI接口的驱动,由于对SPI接口的不熟悉,再加上遇到主从设备的SPI接口有一定的差异,导致被坑了很久。当然,这次驱动开发过程中不仅在技术上有所收获,更重要的收获是,懂得了在驱动开发中遇到问题后怎样有效的处理。下面将从开发流程配置SPI控制器相关须知用户空间SPI驱动Linux设备模型与platform总线(虚拟“平台”总线)Linux SPI驱动框架5方面做总结。如果不使用用户空间驱动的方式,只需了解前两方面即可。由于时间仓促加之本人水平有限,文中可能存在不妥之处,还请广大读者朋友与之交流指正。

时间地点:2017.7.7于成都

邮箱:m18349331694@163.com

 

一、开发流程

1.确认从设备完好。如果有测试程序能测试从设备相应功能,是最好的确认方法,不仅能测试设备是否完好,还能得到设备工作数据,比如获取加密芯片的版本号,得到的数据对以后的开发有很重要的对比意义。

2.确认主设备提供给从设备的供电是否正常。首先测量供电电压是否是从设备所接受范围(过低工作可能不正常,过高可能会烧坏从设备)

3.确认主设备SPI接口引脚是否正常。要先把复用功能的IO引脚功能选择成SPI(可以自己配置寄存器选择复用功能,但推荐通过板级代码配置)。将主从设备进行连接,如果有现成的SPI驱动,可以直接进行测试主设备SPI引脚是否有波形输出,确认输出是否正常。如果没有现成驱动,可以选择用户空间驱动写一个测试程序(其实一般内核都提供有各种驱动的测试程序,在内核目录的Documentation目录下)。可以用测试程序随意发些数据,用示波器看看各引脚有没有相应的波形。

4.选择用户空间驱动或者内核空间驱动(自己配置寄存器)进行开发。

二、配置SPI控制器相关须知

    1.SPI(SerialPeripheral Interface)接口特点:SPI首先由Motorola公司提出并使用,现在标准的SPI,四线式全双工同步串行通信总线。标准的SPI包含SSn(片选线)、SCLK(时钟线)、MOSI(主出从入线)、MISO(主入从出线)四根线。其中名称在不同的地方可以不同,如SSn可以叫CS、CSB等,MOSI可以叫SDO ,MISO可以叫SDI。在有些实际应用中可能将标准的SPI进行简化或者扩展,例如如果不需要使用片选可以去掉,不需要在同一个时钟周期内进行发送和接收的情况可以使用半双工,需要将位宽从标准的1位扩展到多位可以采用并行(就需要更多的数据线)。

    2.SPI接口协议须知:①SPI总线属于点对点传输,主设备通过片选后无需知道从设备地址。②通信前需要使用SSn(片选)选择从设备,SSn线可以有多根,低电平有效并表示选择该从设备。③配置从设备所支持的 SPI总线最高时钟频率(决定了SCK的频率)以及外设的CPHA(极性)、CPOL(相位) 模式,这决定了数据与时钟之间的偏移、采样的时刻以及触发的边沿是上升沿还是下降沿。如果 CPOL= 0,串行同步时钟的空闲状态为低电平;如果 CPOL= 1,串行同步时钟的空闲状态为高电平。如果 CPHA= 0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果 CPHA= 1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。

    3.时序分析:SPI时序如图2-1所示。阅读从设备需要的时序,可以了解从设备工作所需要的电信号条件,特别是设备工作不正常而调试时,时序输出是否符合从设备需求,是评判主设备SPI寄存器配置是否正确的唯一标准。

图2-1 SPI总线时序

4.本实例SPI时序分析与拓展:首先提一个问题:如果将MOSI与MISO短接能否接收到数据呢?如果接收和发送的采样时间节点一致,且SPI为全双工工作模式,主设备应该能收到自己发送的数据。但如果像IMX28的SPI为半双工,这时我们来分析一下时序,当在发送数据时钟时,将FIFO的数据推向MOSI->MISO->FIFO,当接收数据时钟时,MISO线上并没有数据到来,一直被拉低,传入FIFO的数据就为0x0。IMX28的SPI为半双工,而NRSEC3000加密芯片为全双工,如果不做分析直接安加密芯片的交互过程会发生什么呢?如图2-2是获取版本号的交互过程,其中接收数据的交互中,要求接收数据时发生aa,如果是全双工的SPI当然没问题,但最坑人的地方就是这里啦!按照IMX28的半双工时序,发送和接收是不同的两个时钟,如果你发一个aa(这时会产生一个字节的时钟,实际上从设备用这个时钟在回主设备数据)主设备发送aa的同时,从设备->MISO->FIFO,然后接收一个字节数据(这时又会产生一个字节的时钟),从设备->MISO->FIFO->DATA,这样读到的数据寄存器中的数据为本次从设备发送那个字节,虽然上一次发送的那个字节可能还在FIFO中但并未读到,循环这样,就会发现读到的数据是跳动的,比如本来读到的数据应该时12345678…,但却读到1357…(也可能是2468…或者357…等等)。IMX28的SPI为半双工,而NRSEC3000加密芯片为全双工,这种情况该怎么办?其实SPI总线本质上都是全双工,IMX28考虑到许多实际应用中都是用的半双工逻辑,也就把SPI控制器做成了半双工逻辑全双工SPI控制器中用了TxDATA(发送数据寄存器)和RxDATA(接收数据寄存器)两个数据寄存器,且包含TxFIFO和RxFIFO两个缓冲队列,而半双工的SPI控制器至少就节约了一个数据寄存器和一个FIFO队列,而只包含DATA(数据寄存器,发送和接收共用,发送时钟周期就为逻辑上的发送数据寄存器,否则为逻辑上的接收数据寄存器)和FIFO(发送和接收共用)做成了半双工SPI虽然节约了寄存器和FIFO,如果从设备也是半双工逻辑还清晰了时序逻辑,但在本例这种情况下,就得注意了,否则要被坑啦!主设备做成了半双工SPI,首先要确定从设备是否属于半双工的逻辑(虽然IMX28芯片手册说大多应用都是半双工逻辑,但也得确认一下),怎么确认逻辑上是否属于半双工通信呢?图2-3是加密芯片一个功能要求数据交互的例子,图里在大多数交互里发送时不需要接收,接收时不需要发送(这就说明逻辑上很可能就是半双工),虽然在接受ba和SW值时要求发送aa,但实际上aa就是一个接收标志,从设备接收并处理到接收标志就会开始发送数据,这时主设备就只管接收数据即可。所以实际上从设备为接收到aa这个接收标志时,并不会发送数据,这说明逻辑上确实是半双工的。图2-2中接收LEN和数据时实际上并不需要发送aa,也说明逻辑上是半双工的。按分析出来半双工逻辑进行交互即可成功地驱动从设备。

图2-2 加密芯片获取版本号交互过程

图2-3 加密芯片导入SM2私匙交互过程

 

三、用户空间SPI驱动

    入门用户空间驱动最快且有效的方法,是查看内核的测试程序。大多情况下用户不需要了解整个驱动框架,只需要用测试程序中用到的接口即可。以SPI用户空间驱动开发为例:

1.Open()函数打开驱动设备。

2.看看有哪些IOCTL命令,用ioctl()函数执行命令,SPI的命令如下:

SPI_IOC_RD_MODE:读取spi_device的mode。
SPI_IOC_RD_LSB_FIRST:如果是SPI_LSB_FIRST的方式则返回1。
SPI_IOC_RD_BITS_PER_WORD:读取spi_device的bits_per_word.
SPI_IOC_RD_MAX_SPEED_HZ:读取spi_device的max_speed_hz.

SPI_IOC_WR_MODE:设置spi_device的mode,并调用spi_setup立即使设置生效。
SPI_IOC_WR_LSB_FIRST:设置spi使用SPI_LSB_FIRST的传输模式。立即生效。
SPI_IOC_WR_BITS_PER_WORD:读取字长。
SPI_IOC_WR_MAX_SPEED_HZ:设置时钟速率。
SPI_IOC_MESSAGE:这个命令用来进行复杂的通信。参数涉及到一个结构体。各个成员的意义与spi_transfer一致。

3. 需要命令中有不理解的结构体参数,可以上网查阅,也可以看内核的测试程序,或者直接看内核驱动源码。

四、Linux设备模型与platform总线(虚拟“平台”总线)

    Linux内核将设备所具有的共同属性进行了抽象,并在内核中实现这些共同的属性,而向上层提供一般性的接口,这不仅简化了编程,还实现了统一化。

在Linux设备模型中,所有的设备都是挂在总线下的:如挂在对应的PCI、USB、IIC、SPI总线下;但有的设备直接挂在了SoC内存空间,如RTC、看门狗等,这就有了platform总线(虚拟“平台”总线),PCI、USB、IIC、SPI也可以当作platform总线从而进行了统一化。

    一般的Linux设备模型主要涉及bus_type(总线)、device(设备)、device_driver(设备驱动),而platform机制将一般的设备模型又进行了封装(优化),演变出了platform_bus_type(内核用bus_type类型实例化的)、platform_device(包含一个device类型字段)、platform_driver(包含一个device_driver类型字段)。platform_device相对于device主要多了resource字段(该字段可以提供设备的寄存器起始地址),这使得主板相关的内容和驱动进行了分离,具有更好的可扩展性和跨平台性。 

五、Linux SPI驱动框架

    一般我们都不需要知道Linux SPI驱动框架的细节,但了解它也有很多好处,比如当内核的SPI驱动设备没有生成时,可以做相应的修改。要生成SPI驱动设备和下面一些代码有关系:

1.  首先要在板级目录(IMX28的板级代码目录为:arch\arm\mach-mx28)的mx28evk.c文件中注册spi_board_info,其中的名称需要和外设驱动(spidev.c)中的设备名称相同。

2.  在板级目录的device.c文件中要有主机设备的信息,这是一个platform_device的设备信息,其中,名称和id字段需要分别和主机驱动(drivers\spi\spi_mxs.c)的名称和bus进行匹配,名称一致且bus=id+1匹配成功,resource字段指定SPI寄存器起始地址。

Linux的SPI子系统采用主机驱动和外设驱动分离的思想,首先主机SPI控制器是一种平台设备,因此它以platform的方式注册进内核,外设的信息是以boardinfo形式静态定义的,在创建spi_master时,会根据外设的bus_num和主机的bus_num是否相等,来选择是否将该外设挂接在该SPI主控制器下,大体的系统框架可参考图5-1(图中的①主机驱动是指板级SPI控制器驱动层,②外设驱动是指通用SPI驱动代码层)。

Linux SPI驱动框架代码主要分为两层:

3.  1.板级SPI控制器相关驱动代码层(如IMX28的文件是drivers\spi\spi_mxs.c)。该层用platform_driver实例化一个mxs_spi_driver,通过扫描arch\arm\mach-mx28\device.c文件用platform_device实例化的*pdev进行匹配,匹配成功后内核会实例化和注册一个spi_master(表示一个SPI控制器设备)。

4.  2.通用SPI驱动代码层(对应文件drivers\spi\spidev.c)。这一层将SPI驱动进行抽象,提供与平台无关的SPI驱动接口。该层用spi_driver实例化一个spidev_spi_driver,通过扫描arch\arm\mach-mx28\mx28evk.c文件中注册的spi_board_info进行匹配,如果匹配成功就会在文件系统/dev目录下生成相应的设备文件(设备名就为spidev_spi_driver中提供的名称加总线和片选信息,如spidev1.0,spi1总线,片选0)。

Linux SPI驱动通过分成板级驱动层和通用抽象层,将SPI总线业务和平台SPI控制器进行了彻底的分离,使得SPI用户空间驱动只需要了解通用抽象层提供的接口(spidev.c提供的IOCTL命令)即可驱动SPI外设,而无需知道寄存器是如何操作的。

图5-1 Linux SPI驱动框架

注:讲解Linux SPI驱动框架一篇不错的博客地址:http://blog.csdn.net/vanbreaker/article/details/7733476
原创粉丝点击