参考资料:
- 6410手册/2416手册
- Kernel部分驱动代码
联系邮箱 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的现成驱动,可直接配合使用。