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 下文可能用到的偏移地址
- #define S3C_CH_CFG (0x00) //SPI configuration
- #define S3C_CLK_CFG (0x04) //Clock configuration
- #define S3C_MODE_CFG (0x08) //SPI FIFO control
- #define S3C_SLAVE_SEL (0x0C) //Slave selection
- #define S3C_SPI_INT_EN (0x10) //SPI interrupt enable
- #define S3C_SPI_STATUS (0x14) //SPI status
- #define S3C_SPI_TX_DATA (0x18) //SPI TX data
- #define S3C_SPI_RX_DATA (0x1C) //SPI RX data
- #define S3C_PACKET_CNT (0x20) //count how many data master gets
- #define S3C_PENDING_CLR (0x24) //Pending clear
- #define S3C_SWAP_CFG (0x28) //SWAPconfig register
- #define S3C_FB_CLK (0x28) //SWAP FB config register
- #define SPI_CH_SW_RST (1<<5)
- #define SPI_CH_MASTER (0<<4)
- #define SPI_CH_SLAVE (1<<4)
- #define SPI_CH_RISING (0<<3)
- #define SPI_CH_FALLING (1<<3)
- #define SPI_CH_FORMAT_A (0<<2)
- #define SPI_CH_FORMAT_B (1<<2)
- #define SPI_CH_RXCH_OFF (0<<1)
- #define SPI_CH_RXCH_ON (1<<1)
- #define SPI_CH_TXCH_OFF (0<<0)
- #define SPI_CH_TXCH_ON (1<<0)
- #define SPI_CLKSEL_PCLK (0<<9)
- #define SPI_CLKSEL_USBCLK (1<<9)
- #define SPI_CLKSEL_ECLK (2<<9)
- #define SPI_ENCLK_DISABLE (0<<8)
- #define SPI_ENCLK_ENABLE (1<<8)
- #define SPI_MODE_CH_TSZ_BYTE (0<<29)
- #define SPI_MODE_CH_TSZ_HALFWORD (1<<29)
- #define SPI_MODE_CH_TSZ_WORD (2<<29)
- #define SPI_MODE_BUS_TSZ_BYTE (0<<17)
- #define SPI_MODE_BUS_TSZ_HALFWORD (1<<17)
- #define SPI_MODE_BUS_TSZ_WORD (2<<17)
- #define SPI_MODE_RXDMA_OFF (0<<2)
- #define SPI_MODE_RXDMA_ON (1<<2)
- #define SPI_MODE_TXDMA_OFF (0<<1)
- #define SPI_MODE_TXDMA_ON (1<<1)
- #define SPI_MODE_SINGLE (0<<0)
- #define SPI_MODE_4BURST (1<<0)
- #define SPI_SLAVE_MAN (0<<1)
- #define SPI_SLAVE_AUTO (1<<1)
- #define SPI_SLAVE_SIG_ACT (0<<0)
- #define SPI_SLAVE_SIG_INACT (1<<0)
- #define SPI_INT_TRAILING_DIS (0<<6)
- #define SPI_INT_TRAILING_EN (1<<6)
- #define SPI_INT_RX_OVERRUN_DIS (0<<5)
- #define SPI_INT_RX_OVERRUN_EN (1<<5)
- #define SPI_INT_RX_UNDERRUN_DIS (0<<4)
- #define SPI_INT_RX_UNDERRUN_EN (1<<4)
- #define SPI_INT_TX_OVERRUN_DIS (0<<3)
- #define SPI_INT_TX_OVERRUN_EN (1<<3)
- #define SPI_INT_TX_UNDERRUN_DIS (0<<2)
- #define SPI_INT_TX_UNDERRUN_EN (1<<2)
- #define SPI_INT_RX_FIFORDY_DIS (0<<1)
- #define SPI_INT_RX_FIFORDY_EN (1<<1)
- #define SPI_INT_TX_FIFORDY_DIS (0<<0)
- #define SPI_INT_TX_FIFORDY_EN (1<<0)
- #define SPI_STUS_TX_DONE (1<<21)
- #define SPI_STUS_TRAILCNT_ZERO (1<<20)
- #define SPI_STUS_RX_OVERRUN_ERR (1<<5)
- #define SPI_STUS_RX_UNDERRUN_ERR (1<<4)
- #define SPI_STUS_TX_OVERRUN_ERR (1<<3)
- #define SPI_STUS_TX_UNDERRUN_ERR (1<<2)
- #define SPI_STUS_RX_FIFORDY (1<<1)
- #define SPI_STUS_TX_FIFORDY (1<<0)
- #define SPI_PACKET_CNT_DIS (0<<16)
- #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 是否准备好发数据?
- /* spi_wait_TX_ready()- wait for TX_READY and TX_DONE */
- static BOOL spi_wait_TX_ready( void)
- {
- unsignedlong loops = msecs_to_loops(10);
- u32val = 0;
- do{
- val= readl(spiregs + S3C_SPI_STATUS);
- }while(!((val & SPI_STUS_TX_DONE) && (val & SPI_STUS_TX_FIFORDY))&& loops--);
- if(loops == 0)
- returnFALSE;
- else
- returnTRUE;
- }
2 发数据是否完成?
- /* spi_wait_TX_done()- wait for TX_DONE */
- static BOOL spi_wait_TX_done( void)
- {
- unsignedlong loops = msecs_to_loops(10);
- u32val = 0;
- do{
- val= readl(spiregs + S3C_SPI_STATUS);
- }while(!(val & SPI_STUS_TX_DONE) &&loops--);
- if(loops == 0)
- returnFALSE;
- else
- returnTRUE;
- }
3 是否准备好接收数据?
- /* spi_wait_RX_ready()- wait for RX_READY */
- static BOOL spi_wait_RX_ready( void)
- {
- unsignedlong loops = msecs_to_loops(10);
- u32val = 0;
- do{
- val= readl(spiregs + S3C_SPI_STATUS);
- }while(!(val & SPI_STUS_TRAILCNT_ZERO) && loops--);
- if(loops == 0)
- returnFALSE;
- else
- returnTRUE;
- }
6 最终全双工SPI从半双工外设发送和接收数据函数
- BOOL spi_sendbyte( BYTE data)
- {
- BYTEchr;
- u32spi_chcfg = spiregs + S3C_CH_CFG;
- if(!spi_wait_TX_ready())
- {
- printk("%s:failed to get tx channel.\n");
- returnFALSE;
- }
- writel(data, spiregs + S3C_SPI_TX_DATA);
- while(!spi_wait_RX_ready());
- readl(spiregs + S3C_SPI_RX_DATA);
- returnTRUE;
- }
- /* spi_flush_fifo()- Clear the TxFIFO , RxFIFO and TX/RX shift register
- * @spiregs: the SPI register address*/
- VOID spi_flush_fifo(void *spiregs)
- {
- /* soft rest the spi controller, flush theFIFO */
- if(spi_wait_TX_done())
- {
- writel(readl(spiregs+ S3C_CH_CFG) | SPI_CH_SW_RST, spiregs + S3C_CH_CFG);
- writel(readl(spiregs+ S3C_CH_CFG) & ~SPI_CH_SW_RST, spiregs + S3C_CH_CFG);
- }
- }
- /* spi_readbyte()- Read a byte received on SPI0 */
- BYTE spi_readbyte( void)
- {
- u32tmp;
- u32spi_chcfg = spiregs + S3C_CH_CFG;
- BYTEret;
- if(!spi_wait_TX_ready())
- returnFALSE;
- spi_flush_fifo(spiregs);
- writel(0xFF, spiregs + S3C_SPI_TX_DATA);
- if(spi_wait_RX_ready())
- {
- tmp= readl(spiregs + S3C_SPI_RX_DATA);
- ret= tmp & 0xff;
- }
- returnret;
- }
S3C6410 SPI全双工读写流程分析(原创)【转】的更多相关文章
- spark block读写流程分析
之前分析了spark任务提交以及计算的流程,本文将分析在计算过程中数据的读写过程.我们知道:spark抽象出了RDD,在物理上RDD通常由多个Partition组成,一个partition对应一个bl ...
- hbase读写流程分析
前言 最近被大佬问到一个问题,hbase查询数据在最坏的场景下需要进行几次rpc,当时就懵了..下面主要对client端代码进行分析.阅读文章和看源码更配~ 读数据 流程总览 1. 从zookeepe ...
- SD卡spi读写流程
SD卡spi读写流程 1.SD卡的命令格式: SD卡的指令由6字节(Byte)组成,如下: Byte1:0 1 x x x x x x(命令号,由指令标志定义,如CMD39为100111即16进制0x ...
- Uboot启动流程分析(转载)
最近一段时间一直在做uboot移植相关的工作,需要将uboot-2016-7移植到单位设计的ARMv7的处理器上.正好元旦放假三天闲来无事,有段完整的时间来整理下最近的工作成果.之前在学习uboot时 ...
- SpringBoot启动流程分析(四):IoC容器的初始化过程
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- 【转】HDFS读写流程
概述开始之前先看看其基本属性,HDFS(Hadoop Distributed File System)是GFS的开源实现. 特点如下: 能够运行在廉价机器上,硬件出错常态,需要具备高容错性流式数据访问 ...
- u-boot启动流程分析(2)_板级(board)部分
转自:http://www.wowotech.net/u-boot/boot_flow_2.html 目录: 1. 前言 2. Generic Board 3. _main 4. global dat ...
- HDFS2.x之RPC流程分析
HDFS2.x之RPC流程分析 1 概述 Hadoop提供了一个统一的RPC机制来处理client-namenode, namenode-dataname,client-dataname之间的通信.R ...
- SQL Server中CURD语句的锁流程分析
我只在数据库选项已开启“行版本控制的已提交读”(READ_COMMITTED_SNAPSHOT为ON)中进行了观察. 因此只适用于这种环境的数据库. 该类数据库支持四种不同事务隔离级别,下面分别观察数 ...
随机推荐
- C++解析(24):抽象类和接口、多重继承
0.目录 1.抽象类和接口 1.1 抽象类 1.2 纯虚函数 1.3 接口 2.被遗弃的多重继承 2.1 C++中的多重继承 2.2 多重继承的问题一 2.3 多重继承的问题二 2.4 多重继承的问题 ...
- P2764 最小路径覆盖问题(网络流24题之一)
题目描述 «问题描述: 给定有向图G=(V,E).设P 是G 的一个简单路(顶点不相交)的集合.如果V 中每个顶点恰好在P 的一条路上,则称P是G 的一个路径覆盖.P 中路径可以从V 的任何一个顶点开 ...
- 【刷题】BZOJ 4573 [Zjoi2016]大森林
Description 小Y家里有一个大森林,里面有n棵树,编号从1到n.一开始这些树都只是树苗,只有一个节点,标号为1.这些树都有一个特殊的节点,我们称之为生长节点,这些节点有生长出子节点的能力.小 ...
- CentOS 7下安装pptp服务端手记 ok
主要配置步骤 1. 安装前检查系统支持 2. 安装必要包 3. 修改相关配置文件 4. 设置开机自动启动 pptpd, iptables 5. iptables配置网络 6. 阿里云ECS可能还需要几 ...
- java 调用 keytool 生成keystore 和 cer 证书
keytool是一个Java数据证书的管理工具, keytool将密钥(key)和证书(certificates)存在一个称为keystore的文件中在keystore里, 包含两种数据:密钥实体(K ...
- POJ1275 Cashier Employment 【二分 + 差分约束】
题目链接 POJ1275 题解 显然可以差分约束 我们记\(W[i]\)为\(i\)时刻可以开始工作的人数 令\(s[i]\)为前\(i\)个时刻开始工作的人数的前缀和 每个时刻的要求\(r[i]\) ...
- 解题:AHOI 2005 航线规划
题面 这种不断删边的首先肯定想到时光倒流啊=.= 在最后剩下的连通图上跑出一棵搜索树,先将边权都赋为$1$,那么两点间的关键航线就是链上边权和,而每加入一条非树边$u,v$都会使得$u,v$链上的边的 ...
- 【bzoj4811】由乃的OJ
Portal --> bzoj4811 Solution 这题可以用树剖+线段树做也可以用LCT做,不过大体思路是一样的 (接下来先讲的是树剖+线段树的做法,再提LCT的做法) 首先位 ...
- c++ 智能指针(转)
智能指针的使用 智能指针是在 <memory> 标头文件中的 std 命名空间中定义的. 它们对 RAII 或“获取资源即初始化”编程惯用法至关重要. 此习惯用法的主要目的是确保资源获取与 ...
- The 14th Zhejiang Provincial Collegiate Programming Contest Sponsored by TuSimple - C 暴力 STL
What Kind of Friends Are You? Time Limit: 1 Second Memory Limit: 65536 KB Japari Park is a larg ...