CleanData 2018-01-11
转自:http://blog.csdn.net/hustyangju/article/details/21165721
原创博文,知识共享!转载请注明出处:http://blog.csdn.net/hustyangju/article/details/21165721
S3C6410 SPI全双工读写流程分析
一、SPI控制器datasheet
1详细请参考:http://blog.csdn.net/hustyangju/article/details/20474659
2 SPI的所有寄存器都是映射到内核空间的,采用基地址+偏移地址的方式访问
static volatile void __iomem *spiregs; //global variable for mapping spiregister
spiregs = (volatile)ioremap(0x7F00B000,0x30); //just request for the spi0
3 下文可能用到的偏移地址
[cpp]view plaincopy
二、重点参数及初始化步骤
1 双通道SPI管脚配置
2 传输模型配置
/* Set transfer type (CPOL & CPHA set) */
spi_chcfg= SPI_CH_RISING | SPI_CH_FORMAT_A;
spi_chcfg|= SPI_CH_MASTER;
writel(spi_chcfg , spiregs + S3C_CH_CFG);
详细请参考:http://blog.csdn.net/hustyangju/article/details/20474659
3 时钟配置
使用PCLK,外部时钟66M,100分屏:
/* Set clock configuration register
* SPIclockout = clock source / (2 * (prescaler +1))
* PCLK=66Mhz, SPI clockout = clock source /(2 * (prescaler +1)) */
spi_clkcfg= SPI_ENCLK_ENABLE;
spi_clkcfg|= SPI_CLKSEL_PCLK;
writel(spi_clkcfg , spiregs + S3C_CLK_CFG);
spi_clkcfg= readl( spiregs + S3C_CLK_CFG);
spi_clkcfg|= 49; // the least spi speed =660Khz
writel(spi_clkcfg , spiregs + S3C_CLK_CFG);
4 SPI 模块设置
/* Set SPI MODE configuration register */
spi_modecfg= SPI_MODE_CH_TSZ_BYTE| SPI_MODE_BUS_TSZ_BYTE;
spi_modecfg|= SPI_MODE_TXDMA_OFF| SPI_MODE_SINGLE| SPI_MODE_RXDMA_OFF;
spi_modecfg&= ~( 0x3f << 5);
spi_modecfg|= ( 0x1 << 5); // Tx FIFOtrigger level in INT mode
spi_modecfg&= ~( 0x3f << 11);
spi_modecfg|= ( 0x1 << 11); // Rx FIFOtrigger level in INT mode
spi_modecfg&= ~( 0x3ff << 19);
spi_modecfg|= ( 0x1 << 19); // Counting ofTailing Bytes
writel(spi_modecfg,spiregs + S3C_MODE_CFG);
设置在fifo和bus中的数据宽度为byte,关闭DMA访问fifo,并设置fifo中保存最大字节数为1,设置接收和发送fifo的触发值为1byte。
5 中断配置
/* SetSPI INT_EN register */
writel(spi_inten,spiregs + S3C_SPI_INT_EN); //u32 spi_inten =0x00
writel(0x1f,spiregs + S3C_PENDING_CLR);
6 设置最大接收数据包数量
SPI can control the number of packets to bereceived in master mode. If there is any number of packets to bereceived, justset the SFR (Packet_Count_reg). SPI stops generating SPICLK when the number ofpackets is thesame as what you set. It is mandatory to follow software orhardware reset before this function is reloaded.(Software reset can clear allregisters except special function registers, but hardware reset clears allregisters.)
这里使能并设置为最大值。
/* Set Packet Count configuration register */
spi_packet= SPI_PACKET_CNT_EN;
spi_packet|= 0xffff;
writel(spi_packet,spiregs + S3C_PACKET_CNT);
7 打开spi接收和发送通道
/* SetTx or Rx Channel on */
spi_chcfg= readl(spiregs + S3C_CH_CFG);
spi_chcfg|= SPI_CH_TXCH_OFF | SPI_CH_RXCH_OFF;
spi_chcfg|= SPI_CH_TXCH_ON;
spi_chcfg|= SPI_CH_RXCH_ON;
writel(spi_chcfg,spiregs + S3C_CH_CFG);
8 启动发送/接收和关闭送法/接收
通过置0和置1NSSOUT位来开启和关闭。
三 SPI全双工收发协议分析
1 全双工,就是TX和RX同时进行。
2 往哪里收?往哪里发?
datasheet上:CPU (or DMA) mustwrite data on the register SPI_TX_DATA, to write data in FIFO. Data on theregister areautomatically moved to Tx FIFOs. To read data from Rx FIFOs, CPU (orDMA) must access the registerSPI_RX_DATA and then data are automatically sentto the register SPI_RX_DATA.
所以,对于处在内核空间的驱动来说,发送数据是往SPI_TX_DATA寄存器里写数据,接收是往SPI_RX_DATA寄存器里读数据。当然没这么简单!!
3 SPI发数据流程分析
提醒一点:SPI支持全双工,注意外设是半双工还是全双工?
按照上文设置,我们每次发送和接收一个byte。
(1)置0 NSSOUT
(2)往SPI_TX_DATA寄存器写一个byte,byte数据会自动移入TX FIFO,因为(1)中已经选中了外设,所以SPI master会自动读取TX FIFO中的byte数据移入TX移位寄存器,并开始发往bus。
(3)也就是说,驱动只需要从内核空间往SPI_TX_DATA寄存器上写数据,就完成了SPI发数据的操作了。
(4)全双工,意味着,发数据的同时也要从SPI_RX_DATA寄存器中读一个byte数据。但是,有可能第一次读并不会真正读到数据,因为没有数据被接收到RX移位寄存器。所以忽略这次的读取数据。
(5)置1 NSSOUT
4 SPI接收数据流程分析
如果外设是单双工,考虑在读数据的时候对spi复位!因为如果读数据发生在写数据后面,数据已经在发数据时读取到RX FIFO中了,只需从SPI_RX_DATA中取出数据就行了。
(1)置0 NSSOUT
(2)因为全双工,在发送数据的同时,spi master会读取byte字节并移入RX FIFO。所以从SPI_RX_DATA寄存器读取一个byte,byte数据会自动R从X FIFO移入到SPI_RX_DATA寄存器。(3)也就是说,驱动只需要从内核空间往SPI_RX_DATA寄存器读取数据,就完成了SPI接收数据的操作了。
(4)全双工,意味着,接收数据的同时也要从SPI_TX_DATA寄存器中发送一个byte数据。但是,读数据的时候发送的数据可能不是真正想要发送的数据,因为有些外设不是全双工工作。
(5)置1 NSSOUT
5 SPI收发条件判断
这是SPI收发协议里最难的部分了。
SPI状态寄存器:
1 是否准备好发数据?
[cpp]view plaincopy
2 发数据是否完成?
[cpp]view plaincopy
3 是否准备好接收数据?
[cpp]view plaincopy
6 最终全双工SPI从半双工外设发送和接收数据函数
[cpp]view plaincopy