S3C6410 SPI全双工读写流程分析(原创)【转】

简介: 转自:http://blog.csdn.net/hustyangju/article/details/21165721 原创博文,知识共享!转载请注明出处:http://blog.csdn.net/hustyangju/article/details/21165721 S3C6410 SPI全双工读写流程分析 一、SPI控制器datasheet 1详细请参考:http://blog.

转自: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 下文可能用到的偏移地址

 

  1. #define S3C_CH_CFG                (0x00)     //SPI configuration  
  2. #define S3C_CLK_CFG               (0x04)      //Clock configuration  
  3. #define S3C_MODE_CFG                   (0x08)     //SPI FIFO control  
  4. #define S3C_SLAVE_SEL            (0x0C)      //Slave selection  
  5. #define S3C_SPI_INT_EN                   (0x10)      //SPI interrupt enable  
  6. #define S3C_SPI_STATUS          (0x14)      //SPI status  
  7. #define S3C_SPI_TX_DATA                (0x18)      //SPI TX data  
  8. #define S3C_SPI_RX_DATA                (0x1C)      //SPI RX data  
  9. #define S3C_PACKET_CNT                (0x20)      //count how many data master gets  
  10. #define S3C_PENDING_CLR             (0x24)      //Pending clear  
  11. #define S3C_SWAP_CFG           (0x28)      //SWAPconfig register  
  12. #define S3C_FB_CLK                  (0x28)     //SWAP FB config register  
  13.    
  14.    
  15. #define SPI_CH_SW_RST                   (1<<5)  
  16. #define SPI_CH_MASTER                  (0<<4)  
  17. #define SPI_CH_SLAVE              (1<<4)  
  18. #define SPI_CH_RISING            (0<<3)  
  19. #define SPI_CH_FALLING                   (1<<3)  
  20. #define SPI_CH_FORMAT_A             (0<<2)  
  21. #define SPI_CH_FORMAT_B             (1<<2)  
  22. #define SPI_CH_RXCH_OFF              (0<<1)  
  23. #define SPI_CH_RXCH_ON               (1<<1)  
  24. #define SPI_CH_TXCH_OFF               (0<<0)  
  25. #define SPI_CH_TXCH_ON                (1<<0)  
  26.    
  27. #define SPI_CLKSEL_PCLK                 (0<<9)  
  28. #define SPI_CLKSEL_USBCLK   (1<<9)  
  29. #define SPI_CLKSEL_ECLK                 (2<<9)  
  30. #define SPI_ENCLK_DISABLE  (0<<8)  
  31. #define SPI_ENCLK_ENABLE   (1<<8)  
  32.    
  33. #define SPI_MODE_CH_TSZ_BYTE (0<<29)  
  34. #define SPI_MODE_CH_TSZ_HALFWORD       (1<<29)  
  35. #define SPI_MODE_CH_TSZ_WORD       (2<<29)  
  36. #define SPI_MODE_BUS_TSZ_BYTE        (0<<17)  
  37. #define SPI_MODE_BUS_TSZ_HALFWORD     (1<<17)  
  38. #define SPI_MODE_BUS_TSZ_WORD     (2<<17)  
  39. #define SPI_MODE_RXDMA_OFF    (0<<2)  
  40. #define SPI_MODE_RXDMA_ON     (1<<2)  
  41. #define SPI_MODE_TXDMA_OFF    (0<<1)  
  42. #define SPI_MODE_TXDMA_ON     (1<<1)  
  43. #define SPI_MODE_SINGLE              (0<<0)  
  44. #define SPI_MODE_4BURST             (1<<0)  
  45.    
  46. #define SPI_SLAVE_MAN                   (0<<1)  
  47. #define SPI_SLAVE_AUTO                  (1<<1)  
  48. #define SPI_SLAVE_SIG_ACT   (0<<0)  
  49. #define SPI_SLAVE_SIG_INACT        (1<<0)  
  50.    
  51. #define SPI_INT_TRAILING_DIS      (0<<6)  
  52. #define SPI_INT_TRAILING_EN       (1<<6)  
  53. #define SPI_INT_RX_OVERRUN_DIS       (0<<5)  
  54. #define SPI_INT_RX_OVERRUN_EN        (1<<5)  
  55. #define SPI_INT_RX_UNDERRUN_DIS    (0<<4)  
  56. #define SPI_INT_RX_UNDERRUN_EN     (1<<4)  
  57. #define SPI_INT_TX_OVERRUN_DIS        (0<<3)  
  58. #define SPI_INT_TX_OVERRUN_EN         (1<<3)  
  59. #define SPI_INT_TX_UNDERRUN_DIS    (0<<2)  
  60. #define SPI_INT_TX_UNDERRUN_EN     (1<<2)  
  61. #define SPI_INT_RX_FIFORDY_DIS (0<<1)  
  62. #define SPI_INT_RX_FIFORDY_EN  (1<<1)  
  63. #define SPI_INT_TX_FIFORDY_DIS (0<<0)  
  64. #define SPI_INT_TX_FIFORDY_EN  (1<<0)  
  65.    
  66. #define SPI_STUS_TX_DONE   (1<<21)  
  67. #define SPI_STUS_TRAILCNT_ZERO        (1<<20)  
  68. #define SPI_STUS_RX_OVERRUN_ERR   (1<<5)  
  69. #define SPI_STUS_RX_UNDERRUN_ERR         (1<<4)  
  70. #define SPI_STUS_TX_OVERRUN_ERR    (1<<3)  
  71. #define SPI_STUS_TX_UNDERRUN_ERR          (1<<2)  
  72. #define SPI_STUS_RX_FIFORDY       (1<<1)  
  73. #define SPI_STUS_TX_FIFORDY       (1<<0)  
  74.    
  75. #define SPI_PACKET_CNT_DIS         (0<<16)  
  76. #define SPI_PACKET_CNT_EN (1<<16)  


二、重点参数及初始化步骤


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 是否准备好发数据?

  1. /*     spi_wait_TX_ready()- wait for TX_READY and TX_DONE       */  
  2. static BOOL spi_wait_TX_ready( void)  
  3. {  
  4.          unsignedlong loops = msecs_to_loops(10);  
  5.          u32val = 0;  
  6.          do{  
  7.                    val= readl(spiregs + S3C_SPI_STATUS);  
  8.          }while(!((val & SPI_STUS_TX_DONE) && (val & SPI_STUS_TX_FIFORDY))&& loops--);  
  9.           
  10.          if(loops == 0)  
  11.                    returnFALSE;  
  12.          else  
  13.                    returnTRUE;  
  14. }  


2 发数据是否完成?

  1. /*     spi_wait_TX_done()- wait for TX_DONE         */  
  2. static BOOL spi_wait_TX_done( void)  
  3. {  
  4.          unsignedlong loops = msecs_to_loops(10);  
  5.          u32val = 0;  
  6.          do{  
  7.                    val= readl(spiregs + S3C_SPI_STATUS);  
  8.          }while(!(val & SPI_STUS_TX_DONE)  &&loops--);  
  9.           
  10.          if(loops == 0)  
  11.                    returnFALSE;  
  12.          else  
  13.                    returnTRUE;  
  14. }  


3 是否准备好接收数据?

  1. /*     spi_wait_RX_ready()- wait for RX_READY      */  
  2. static BOOL spi_wait_RX_ready( void)  
  3. {  
  4.          unsignedlong loops = msecs_to_loops(10);  
  5.          u32val = 0;  
  6.          do{  
  7.                    val= readl(spiregs + S3C_SPI_STATUS);  
  8.          }while(!(val & SPI_STUS_TRAILCNT_ZERO) && loops--);  
  9.           
  10.          if(loops == 0)  
  11.                    returnFALSE;  
  12.          else  
  13.                    returnTRUE;  
  14. }  


6 最终全双工SPI从半双工外设发送和接收数据函数

    1. BOOL spi_sendbyte( BYTE data)  
    2. {  
    3.          BYTEchr;  
    4.          u32spi_chcfg = spiregs + S3C_CH_CFG;  
    5.    
    6.          if(!spi_wait_TX_ready())  
    7.          {  
    8.                    printk("%s:failed to get tx channel.\n");  
    9.                    returnFALSE;  
    10.          }  
    11.          writel(data, spiregs + S3C_SPI_TX_DATA);  
    12.          while(!spi_wait_RX_ready());  
    13.          readl(spiregs + S3C_SPI_RX_DATA);  
    14.          returnTRUE;  
    15. }  
    16.    
    17. /*     spi_flush_fifo()- Clear the TxFIFO , RxFIFO and TX/RX shift register 
    18.  *     @spiregs: the SPI register address*/  
    19. VOID spi_flush_fifo(void *spiregs)  
    20. {  
    21.          /*     soft rest the spi controller, flush theFIFO       */  
    22.          if(spi_wait_TX_done())  
    23.          {  
    24.                    writel(readl(spiregs+ S3C_CH_CFG) | SPI_CH_SW_RST, spiregs + S3C_CH_CFG);  
    25.                    writel(readl(spiregs+ S3C_CH_CFG) & ~SPI_CH_SW_RST, spiregs + S3C_CH_CFG);  
    26.          }  
    27. }  
    28.    
    29. /*     spi_readbyte()- Read a byte received on SPI0         */  
    30. BYTE spi_readbyte( void)  
    31. {  
    32.          u32tmp;  
    33.          u32spi_chcfg = spiregs + S3C_CH_CFG;  
    34.          BYTEret;  
    35.    
    36.          if(!spi_wait_TX_ready())  
    37.                    returnFALSE;  
    38.          spi_flush_fifo(spiregs);  
    39.          writel(0xFF, spiregs + S3C_SPI_TX_DATA);  
    40.           
    41.          if(spi_wait_RX_ready())  
    42.          {  
    43.                    tmp= readl(spiregs + S3C_SPI_RX_DATA);  
    44.                    ret= tmp & 0xff;  
    45.          }  
    46.          returnret;  
    47. }  
    48.           
【作者】 张昺华
【新浪微博】 张昺华--sky
【twitter】 @sky2030_
【facebook】 张昺华 zhangbinghua
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
目录
相关文章
|
9月前
|
人工智能 关系型数据库 数据处理
快速构建,JeeLowCode让你的应用开发速度飞起来#快速开发
低代码平台通过直观的图形化界面和模块化设计,大幅缩短开发时间,降低成本,使企业无需大量技术人员即可快速构建应用。平台支持多种数据库、数据处理与管理,具备高效引擎和丰富的插件生态,帮助企业快速应对市场变化,推动数字化转型。演示地址:http://demo.jeelowcode.com/ 官网地址:http://www.jeelowcode.com
快速构建,JeeLowCode让你的应用开发速度飞起来#快速开发
|
Docker 容器 Linux
幻兽帕鲁存档迁移问题心得_告别存档丢失_进入就掉线
你是不是也遇到了存档文件迁移后,还是让你创建新角色,或者是迁移后没几秒就掉线,我也遇到了一样的问题,花了好半天终于解决了,这里记录分享一下。
8226 4
幻兽帕鲁存档迁移问题心得_告别存档丢失_进入就掉线
|
存储 开发者
CodeWave智能开发平台--03--目标:应用创建--07供应商数据表格01
CodeWave智能开发平台--03--目标:应用创建--07供应商数据表格01
|
存储 算法 C语言
STL标准模板库《实战案例汇总》
STL标准模板库《实战案例汇总》
175 1
|
开发框架 安全 .NET
C# .NET面试系列三:集合、异常、泛型、LINQ、委托、EF!
<h2>集合、异常、泛型、LINQ、委托、EF! #### 1. IList 接口与 List 的区别是什么? IList 接口和 List 类是C#中集合的两个相关但不同的概念。下面是它们的主要区别: <b>IList 接口</b> IList 接口是C#中定义的一个泛型接口,位于 System.Collections 命名空间。它派生自 ICollection 接口,定义了一个可以通过索引访问的有序集合。 ```c# IList 接口包含一系列索引化的属性和方法,允许按索引访问、插入、移除元素等。 由于是接口,它只定义了成员的契约,而不提供具体的实现。类似于 IEnumera
629 2
|
Python
Python virtualenv 虚拟环境(详细使用,包含打包 exe/app )
Python virtualenv 虚拟环境(详细使用,包含打包 exe/app )
726 0
|
存储 安全 Java
java 之Hashtable
当涉及到在 Java 中存储和管理键值对数据时,`Hashtable` 是一个重要且值得探讨的工具。作为 Java 集合框架中的一员,`Hashtable` 提供了一种有序、高效的数据存储方式,可以满足许多开发场景的需求。在本文中,我们将深入探讨 Java 中的 `Hashtable`,了解其特点、用法以及何时适用。
|
网络架构 架构师 网络协议
《IP组播(第1卷)》一导读
本书包含了基本IP组播原理和路由技术,尤其是Cisco路由器和交换机使用的组播技术,其中切合实际地讨论了 IP 组播网络的常见特性、部署模型和实战经验,之后讨论了 Cisco IP组播网络在实施和排错时使用的命令和方法。
2189 0
|
运维 资源调度 Java
定时任务报警通知解决方案详解
本文详细介绍定时任务通知的解决方案,以及市面上常见的开源定时任务通知方案对比。
1228 2
|
存储 分布式计算 大数据
SPARK Parquet嵌套类型的向量化支持以及列索引(column index)
SPARK Parquet嵌套类型的向量化支持以及列索引(column index)
501 0
SPARK Parquet嵌套类型的向量化支持以及列索引(column index)