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.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 plain copy

  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. #define SPI_CH_SW_RST                   (1<<5)
  14. #define SPI_CH_MASTER                  (0<<4)
  15. #define SPI_CH_SLAVE              (1<<4)
  16. #define SPI_CH_RISING            (0<<3)
  17. #define SPI_CH_FALLING                   (1<<3)
  18. #define SPI_CH_FORMAT_A             (0<<2)
  19. #define SPI_CH_FORMAT_B             (1<<2)
  20. #define SPI_CH_RXCH_OFF              (0<<1)
  21. #define SPI_CH_RXCH_ON               (1<<1)
  22. #define SPI_CH_TXCH_OFF               (0<<0)
  23. #define SPI_CH_TXCH_ON                (1<<0)
  24. #define SPI_CLKSEL_PCLK                 (0<<9)
  25. #define SPI_CLKSEL_USBCLK   (1<<9)
  26. #define SPI_CLKSEL_ECLK                 (2<<9)
  27. #define SPI_ENCLK_DISABLE  (0<<8)
  28. #define SPI_ENCLK_ENABLE   (1<<8)
  29. #define SPI_MODE_CH_TSZ_BYTE (0<<29)
  30. #define SPI_MODE_CH_TSZ_HALFWORD       (1<<29)
  31. #define SPI_MODE_CH_TSZ_WORD       (2<<29)
  32. #define SPI_MODE_BUS_TSZ_BYTE        (0<<17)
  33. #define SPI_MODE_BUS_TSZ_HALFWORD     (1<<17)
  34. #define SPI_MODE_BUS_TSZ_WORD     (2<<17)
  35. #define SPI_MODE_RXDMA_OFF    (0<<2)
  36. #define SPI_MODE_RXDMA_ON     (1<<2)
  37. #define SPI_MODE_TXDMA_OFF    (0<<1)
  38. #define SPI_MODE_TXDMA_ON     (1<<1)
  39. #define SPI_MODE_SINGLE              (0<<0)
  40. #define SPI_MODE_4BURST             (1<<0)
  41. #define SPI_SLAVE_MAN                   (0<<1)
  42. #define SPI_SLAVE_AUTO                  (1<<1)
  43. #define SPI_SLAVE_SIG_ACT   (0<<0)
  44. #define SPI_SLAVE_SIG_INACT        (1<<0)
  45. #define SPI_INT_TRAILING_DIS      (0<<6)
  46. #define SPI_INT_TRAILING_EN       (1<<6)
  47. #define SPI_INT_RX_OVERRUN_DIS       (0<<5)
  48. #define SPI_INT_RX_OVERRUN_EN        (1<<5)
  49. #define SPI_INT_RX_UNDERRUN_DIS    (0<<4)
  50. #define SPI_INT_RX_UNDERRUN_EN     (1<<4)
  51. #define SPI_INT_TX_OVERRUN_DIS        (0<<3)
  52. #define SPI_INT_TX_OVERRUN_EN         (1<<3)
  53. #define SPI_INT_TX_UNDERRUN_DIS    (0<<2)
  54. #define SPI_INT_TX_UNDERRUN_EN     (1<<2)
  55. #define SPI_INT_RX_FIFORDY_DIS (0<<1)
  56. #define SPI_INT_RX_FIFORDY_EN  (1<<1)
  57. #define SPI_INT_TX_FIFORDY_DIS (0<<0)
  58. #define SPI_INT_TX_FIFORDY_EN  (1<<0)
  59. #define SPI_STUS_TX_DONE   (1<<21)
  60. #define SPI_STUS_TRAILCNT_ZERO        (1<<20)
  61. #define SPI_STUS_RX_OVERRUN_ERR   (1<<5)
  62. #define SPI_STUS_RX_UNDERRUN_ERR         (1<<4)
  63. #define SPI_STUS_TX_OVERRUN_ERR    (1<<3)
  64. #define SPI_STUS_TX_UNDERRUN_ERR          (1<<2)
  65. #define SPI_STUS_RX_FIFORDY       (1<<1)
  66. #define SPI_STUS_TX_FIFORDY       (1<<0)
  67. #define SPI_PACKET_CNT_DIS         (0<<16)
  68. #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 是否准备好发数据?

[cpp] view plain copy

  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. if(loops == 0)
  10. returnFALSE;
  11. else
  12. returnTRUE;
  13. }

2 发数据是否完成?

[cpp] view plain copy

  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. if(loops == 0)
  10. returnFALSE;
  11. else
  12. returnTRUE;
  13. }

3 是否准备好接收数据?

[cpp] view plain copy

  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. if(loops == 0)
  10. returnFALSE;
  11. else
  12. returnTRUE;
  13. }

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

[cpp] view plain copy

    1. BOOL spi_sendbyte( BYTE data)
    2. {
    3. BYTEchr;
    4. u32spi_chcfg = spiregs + S3C_CH_CFG;
    5. if(!spi_wait_TX_ready())
    6. {
    7. printk("%s:failed to get tx channel.\n");
    8. returnFALSE;
    9. }
    10. writel(data, spiregs + S3C_SPI_TX_DATA);
    11. while(!spi_wait_RX_ready());
    12. readl(spiregs + S3C_SPI_RX_DATA);
    13. returnTRUE;
    14. }
    15. /*     spi_flush_fifo()- Clear the TxFIFO , RxFIFO and TX/RX shift register
    16. *     @spiregs: the SPI register address*/
    17. VOID spi_flush_fifo(void *spiregs)
    18. {
    19. /*     soft rest the spi controller, flush theFIFO       */
    20. if(spi_wait_TX_done())
    21. {
    22. writel(readl(spiregs+ S3C_CH_CFG) | SPI_CH_SW_RST, spiregs + S3C_CH_CFG);
    23. writel(readl(spiregs+ S3C_CH_CFG) & ~SPI_CH_SW_RST, spiregs + S3C_CH_CFG);
    24. }
    25. }
    26. /*     spi_readbyte()- Read a byte received on SPI0         */
    27. BYTE spi_readbyte( void)
    28. {
    29. u32tmp;
    30. u32spi_chcfg = spiregs + S3C_CH_CFG;
    31. BYTEret;
    32. if(!spi_wait_TX_ready())
    33. returnFALSE;
    34. spi_flush_fifo(spiregs);
    35. writel(0xFF, spiregs + S3C_SPI_TX_DATA);
    36. if(spi_wait_RX_ready())
    37. {
    38. tmp= readl(spiregs + S3C_SPI_RX_DATA);
    39. ret= tmp & 0xff;
    40. }
    41. returnret;
    42. }

原文地址:https://www.cnblogs.com/sky-heaven/p/8269712.html

时间: 2024-11-08 19:09:38

S3C6410 SPI全双工读写流程分析(原创)【转】的相关文章

spark block读写流程分析

之前分析了spark任务提交以及计算的流程,本文将分析在计算过程中数据的读写过程.我们知道:spark抽象出了RDD,在物理上RDD通常由多个Partition组成,一个partition对应一个block.在driver和每个executor端,都有一个Blockmanager.Blockmanager是spark在计算过程中对block进行读写的入口,它屏蔽了在读取数据时涉及到的内存分配,从其他executor端远程获取等具体细节.接下来,本文将以读写block为主线,分析spark在计算过

HDFS(一)架构及文件读写流程

Hadoop 中有三大组件:HDFS.MapReduce.YARN,HDFS 负责大文件存储的问题,MapReduce 负责大数据计算,而 YARN 负责资源的调度,接下来的文章我会一一介绍这几个组件.今天我们先来聊聊 HDFS 的架构及文件的读写流程. 总体架构 HDFS 设计的目的是为了存储大数据集的文件,因此一台服务器是应付不了的,我们需要一个集群来实现这个目标.当用户需要存储一个文件时,HDFS 会将这个文件切分为一个个小的数据块(在 2.x 的版本中,每个数据块默认大小为 128M),

SpringBoot启动流程分析(四):IoC容器的初始化过程

SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一):SpringApplication类初始化过程 SpringBoot启动流程分析(二):SpringApplication的run方法 SpringBoot启动流程分析(三):SpringApplication的run方法之prepareContext()方法 SpringBoot启动流程分析(四

Linux系统启动流程分析与关机流程

Linux 系统启动流程分析 Linux系统的启动过程并不是大家想象中的那么复杂,其过程可以分为5个阶段: 内核的引导. 运行 init. 系统初始化. 建立终端. 用户登录系统. init程序的类型: SysV: init, CentOS 5之前, 配置文件: /etc/inittab. Upstart: init,CentOS 6, 配置文件: /etc/inittab, /etc/init/*.conf. Systemd: systemd, CentOS 7,配置文件: /usr/lib/

u-boot启动流程分析(2)_板级(board)部分

转自:http://www.wowotech.net/u-boot/boot_flow_2.html 目录: 1. 前言 2. Generic Board 3. _main 4. global data介绍以及背后的思考 5. 前置的板级初始化操作 6. u-boot的relocation 7. 后置的板级初始化操作 1. 前言 书接上文(u-boot启动流程分析(1)_平台相关部分),本文介绍u-boot启动流程中和具体版型(board)有关的部分,也即board_init_f/board_i

17.U-boot的工作流程分析-6410

17.U-boot的工作流程分析-6410 6410开发板: 1.uboot的入口: 要看uboot工程的入口,首先打开顶层目录的Makefile: Uboot所支持的开发板,在顶层的Makefile中都会有一个配置选项.比如6410,在Makefile中的配置选项是make forlinx_nand_ram256_config:在vim的命令模式按下/,然后输入make forlinx_nand_ram256_config回车会定位到这里: 这是Makefile里的一个目标.这是来配置6410

JAVAWEB开发之Struts2详解(一)——Struts2框架介绍与快速入门、流程分析与工具配置以及Struts2的配置以及Action和Result的详细使用

Struts2框架介绍 三大框架:是企业主流JavaEE开发的一套架构.Struts2 + Spring + Hibernate 什么是框架?为什么要学习框架? 框架是实现部分功能的代码(半成品),使用框架简化企业级软件开发. Struts2与MVC? Struts是一款优秀的MVC框架 MVC:是一种思想,是一种模式,将软件分为Model模型.View视图.Controller控制器 JAVAEE软件三层架构:web层(表现层).业务逻辑层.数据持久层(Sun提供javaEE开发规范) Jav

crtmpserver 基本流程分析

近期在研究crtmpserver,这里记录下学习过程,首先我们先分析下基本流程. 1.初始化流程 InitNetworking---初始化网络 Initialize Logger::Init()---初始化日志 lowerCase(extension) == "lua"---载入.lua后缀配置文件 LoadLuaFile Normalize NormalizeLogAppenders 初始化日志配置 NormalizeApplications 初始化监听配置 gRs.pConfigF

U-boot引导流程分析一

U-Boot,全称 Universal Boot Loader,即通用引导程序,是遵循GPL条款的开放源码项目.它的源码目录.编译形式与Linux内核很相似,事实上,不少U-Boot源码就是相应的Linux内核源程序的简化,尤其是一些设备的驱动程序,这从U-Boot源码的注释中能体现这一点.U-Boot不仅仅支持嵌入式Linux系统的引导,它还支持NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS嵌入式操作系统. U-Boot的工作模式有启动加载模式和下载模式.