Chinaunix首页 | 论坛 | 博客
  • 博客访问: 70735
  • 博文数量: 6
  • 博客积分: 175
  • 博客等级: 入伍新兵
  • 技术积分: 99
  • 用 户 组: 普通用户
  • 注册时间: 2011-05-30 23:28
文章分类
文章存档

2012年(2)

2011年(4)

我的朋友

分类: LINUX

2011-06-14 22:33:56

参考资料:

  • 6410手册/2416手册
  • Kernel部分驱动代码
作 者:agan
联系邮箱 beswipe@yahoo.com.cn
racer.blog.chinaunix.net

转载须注明出处!
 


  SPI接口简述

  SPI是 Serial Peripheral Interface(串型外部接口)的缩写。SPI接口有4根PIN脚,分别是:
          * SPICLK     : 用于传输数据的同步时钟
          * SPIMISO    : 用于主模式下的输入,或从模式下的输出信号线
          * SPIMOSI    : 用于主模式下的输出,或从模式下的输入信号线
          * PSS        : 用于从设备的片选信号,低有效

   SPI 通信两端各自扮演主设备(Master)和从设备(Slave)的角色。主设备在移位寄存器进行传输时(SPIMISO/SPIMOSI上面有信号时),提供SPICLK时钟作为对方同步和采样的依据,同时在传输的过程中,PSS要一直被Master拉底。传输完成,PSS拉高,SPICLK信号消失。具体传输时序,请浏览后续的“传输时序”章节。

   SPI接口有点类似于UART接口,他们通常都用于满足一些低速传输的需求,比如通信量小的控制数据传输/或者接入一些低速的输入设备(鼠标/键盘/触摸屏)等等。他们之间的主要区别如下:
        * SPI靠SPICLK作为传输同步信号,因此它走的是串性同步传输信号。
        * UART是通用异步串性传输信号的缩写,他的PIN定义中没有时钟信号,通信两端靠约定好的波特率,以及数据格式中的开始位/停止位/校验位来构造和解析数据。
        因为UART是异步传输,所以他还有RTX/CTS之类的PIN脚用于数据码流控制,现在大部分的UART控制器都支持硬件流控,这样就简化了这种老式数据传输方式的软件设计复杂度。

    SPI传输时序

    在SPI控制器的寄存器布局中,通常都有2个配置: CPOL (Polarity:极性) 和 CPHA (PHASE: 相位).
    CPOL 控制传输过程中SPICLK是高有效还是低有效, CPHA控制的是在对应的极性中,哪一个SPICLK EDGE进行数据同步,哪一个EDGE进行数据采集。
    这里的说的比较抽象,读者可以参考SPI传输时序图 。


    寄存器布局
    对比6410和2416的寄存器手册,发现他们的寄存器布局是一样的,由此判断他们芯片内部用的可能是同一种SPI控制器
    只不过6410有两个SPI总线,2416只有一个。

    寄存器CH_CFG
        SW_RST    : SW reset   
        软件复位,这非常重要。 6410的SPI驱动通常会频繁的reset SPI控制器,reset通常发生在一次批量数据的传输之后(批量是指小于FIFO大小64字节的传输)下一次传输之前。
        每次reset过后,大部分寄存器的值都需要重新配置。这一点区别于我们通常对其他芯片的理解,其他芯片通常都只需要在init/probe之类的函数内reset和配置寄存器一次即可。
        CPOL/CPHA
        选择传输时序,请查看前面的"SPI传输时序"章节。传输时许取决于SPI对端从设备的输入时序,查阅对端从设备的手册,它需要什么输入时序这里就选择输出怎样的时序。
        RxChOn/TxChOn
        读写通道的开关。6410 kernel的驱动经常会开关RxChOn/TxChOn,目的可能是只在需要传输时打开,不需要就关闭,避免信号干扰产生垃圾数据。或者是错误处理:在出错时先关掉传输通道,再清空2个64字节的FIFO.

    寄存器Clk_CFG
        用于配置SPI的输入时钟和输出时钟:
        ClkSel   
         这个跟具体的硬件有关,具体到2416, PCLK/EPLL都是通路。

        Prescaler Value
        分频参数,是通过始终输入,产生始终输出SPICLK的一个计算分母,计算公式:HS_SPI clock-out = Clock source / ( 2 x (Prescaler value +1))。
             
        具体怎么配置?先查看你的SPI挂接设备他需要多大的输入时钟频率,再看看你的SPI控制器有哪些时钟输入,然后根据公式一算便知。

    寄存器MODE_CFG
        用于配置传输方式,以及每个方式所对应的一些配置参数:
        先说一下2416 SPI的传输方式。 SPI有传输和接收移位寄存器HS_SPI_TX_DATA/HS_SPI_RX_DATA。不管你是使用的哪种传输方式,所有的传输出口都是HS_SPI_TX_DATA,所有的接收入口都是HS_SPI_RX_DATA. 这两个寄存器对应两个FIFO,每个FIFO64字节,两个寄存器就相当于两个FIFO的栈顶指针。

        * 轮训: 靠HS_SPI_STATUS寄存器的RxFifoLvl判断RxFIFO内是否有数据,有多少字节的数据。靠同一个寄存器的TxFifoLvl位来判断TxFifo内是否还有数据,用以判断刚才的TX动作是否成功完成。轮训同一个寄存器的其他位可获知传输过程中是否有错误发生。

        * 中断方式: 设置当前寄存器(MODE_CFG)的RxTrigger位,用以表明在RxFifo中的数据堆积了多少字节后,产生一个中断。TxTrigger同理。Trailing Count为用以配置在多少个时钟周期后(从最后一次写入RxFifo开始计时),产生一个中断。Trailing Count用以让用户获取RxFifo中小于Trigger字节的数据。       
       
        * DMA模式:DMA的介入点,只是从系统内存中拷贝批量数据到移位传输的出口地址,或是从移位接收的入口地址批量拷贝数据到系统内存中。注意,要配置DMA模块,在涉及SPI移位寄存器这一边的地址,在DMA过程中不能递增。

        当然,以上方式也不是互相排斥的。6410的driver就是用了DMA做为用户内存与移位寄存器之间的传输方式,使用中断方式作为传输等待/传输错误的捕获方式,使用轮训来判断错误处理函数中FIFO是否已经清空。

    寄存器Slave_slection_reg
        用于配置怎样产生PSS信号,就是对Slave端的片选信号。
        6410的驱动中,一直使用手动方式控制片选信号。   

        这里再说清楚信号之间的一个疑惑。PSS/CLK同时存在(CLK有信号并且PSS拉低),不管数据线上有没有信号,SPI主从设备都会采样,也就是你的TxFifo/RxFifo就可能产生数据。
        如果PSS/CLK信号都用手动控制,那肯定会产生垃圾数据,因为在你从你产生PSS/CLK信号到你往SPIMOSI/SPIMISO上读写数据这段时间内,肯定会有间隔时间(即使只有几个时钟周期),在这段间隔时间内,FIFO可能早就被垃圾数据填满了。
        幸好硬件设计可能想到了这点,这里的PSS信号手动控制,的确是让用户手动拉高拉低PSS信号。但是SPI_CLK信号,却是硬件自动调制的。
        前面CLK_CFG:ENCLK信号,并不是控制CLK信号的输出通断,它只是控制CLK输入输出/以及分频参数是否有效。
        真正的SPICLK输出信号,是靠移位寄存器自动控制的:发送移位寄存器开始传输时,就按照你所配置的CPOL+CPHA时序打开SPICLK信号。
        那么SPICLK在何时关闭?Master向Slave发送命令后,通常还要等待Slave返回的数据,返回数据长度不同,SPICLK的长度就不同,那么SPICLK该在什么时候关闭?
        这时寄存器Packet_Count_reg闪亮登场!这里一个Packet的长度,应该是你前面配置的8/16/32宽度。这个寄存器告诉接收移位寄存器,这次传输在收到X个Packet后,关闭SPICLK.
        如果Packet Count过大,则SPICLK可能长时间有效,如果PacketCount过小,则能截断对方发送的数据。

        总而言之,SPI的4根线,只有PSS是软件程序控制,其他SPICLK/SPIMOSI/SPIMISO是根据你的配置,硬件自动调制的。

    寄存器HS_SPI_INT_EN
        中断使能寄存器,用于控制让什么情况产生中断。


    寄存器HS_SPI_STATUS
        很重要的状态寄存器。

    接收移位寄存器HS_SPI_RX_DATA       
    发送移位寄存器HS_SPI_TX_DATA

        数据传输接收的唯一接口,请看上面的“传输方式”介绍。

    接受包计数寄存器Packet_Count_reg
        用于控制SPINCLK何时消失,请看上面的“传输方式”介绍。

    清除状态寄存器Pending_clr_reg
        用于清除状态寄存器,通常这样使用:
        writel(readl(sspi->regs + SAMSPI_PENDING_CLR), sspi->regs + SAMSPI_PENDING_CLR);
        即,哪个没清清哪个!

    SWAP_CFG/FB_Clk_sel
        这两个的意思不明,只能根据字面意思猜测。
        6410的driver给的都是默认值。



    调试笔记

        * CPOL的实际情况,可能与手册中写的相反。即CPOL=0,实际输出了低有效CLK;CPOL=1,实际上输出了高有效CLK。
        * HS_SPI_STATUS:TX_done,这一位实际情况与手册描述不符。实际情况中,完全让人搞不懂,6410的driver中也没有使用这一位。
        * 2416 kernel自带的SPI driver,挂在系统spi_dev结构下,就是一个字符设备,用于访问一些通用的外设,没有错误处理,只能读或者写,不能处理外设的先写后读协议,只适合用于调试目的,不适合驱动外设。
        * 6410 kernel自带的SPI driver,挂在系统的构架下,实现了一个SPI的MASTER。笔者研究的ads7846 ADC的现成驱动,可直接配合使用。

以上文章是作者原创,如有错误请指正!

阅读(5493) | 评论(1) | 转发(1) |
给主人留下些什么吧!~~

bingzheaaa2012-02-29 11:06:52

你好,我做一个6410的spi驱动移植,内核版本2.6.28.8,即使读了spi-summary还有一些文章仍然没搞明白mach-*文件如何修改,我能得到您的相关的详细些的总结笔记吗?