SPI详解

AD7888与S3C2410的SPI接口及Linux下嵌入式驱动的实现
 http://tech.ddvip.com   2008年07月07日    社区交流
   关键字: 视频监控系统 EJTAG调试 热水器 超声探伤仪 NGN业务安全 继电保护软件
内容摘要:带有SPI接口的串行A/D转换器和ARM微处理器的结合可广泛应用于实现数据采集功能的掌上设备及其他嵌入式的系统,如:医疗仪器、通信设备、抄表设备等领域。
         串行外围设备接口SPI(serial peripheral interface)总线技术是Motorola公司推出的一种同步串行接口,它允许CPU与TTL移位寄存器、A/D或D/A转换器、实时时钟(RTO)、存储器以及LCD和LED显示驱动器等外围接口器件以串行方式进行通讯。
  SPI总线只需3~4根数据线和控制线即可扩展具有SPI接口的各种I/O器件,其硬件功能很强,实现软件相当简单。串行A/D转换器具有电路简单、工作可靠的特点,而ARM芯片被设计用于手持设备以及普通的嵌人式应用的集成系统,将上述两种实用的芯片和SPI总线技术相结合以实现数据采集十分有效。
  1 AD7888的功能与使用
  AD7888是美国模拟器件公司推出的一款高速低功耗12位A/D转换器,采用2.7~5.25 V单电源供电,最大通过率可达到125 kSPS。AD7888的输入采样/保持电路在500 ns内获取一个信号,采用单端采样模式,包含8个单端模拟输入,模拟输入电压从0到VREF。AD7888有2.5 V的片内基准电压,也可以使用外部基准电压,范围从1.2 V到VDD。CMOS制造工艺确保了低功耗,正常工作时为2 mW,掉电状态下为3uW。可以选择多种电源管理模式(包括数据转换后自动处于掉电模式),与多种串行接口兼容(SPI/QSPI/MICOWIRE/DSP)。AD7888可广泛应用于电池供电系统(个人数字助理、医疗仪器、移动通信)、仪表控制系统和高速调制/解调器等领域。该器件采用16脚SOIC和TSSOP外形封装,外形及引脚定义见图1和表1。
 
  图1 AD7888的引脚图
 


AD7888的控制寄存器是8位只写寄存器。数据在SCLK的上升沿从DIN引脚载人,同时获取外部模拟量转换的结果。每次数据的传输需要准备16个连续时钟信号。只有在片选信号下降之后的前8个时钟脉冲的上升沿提供的信息装入控制寄存器。
 
  图2显示了详细的串行接口时序图,串行时钟提供了转换时序,且控制AD7888转换信息的输入输出。CS初始化数据传送和转换处理。在其下降沿之后的1.5个时钟周期开始采样输入信号,这段时间表示为tACQ(获取时间)。整个转换过程还需要14.5个时钟周期来完成,这段时间表示为tCONVERT(转换时间)。
  从AD7888获取数据的整个转换过程需要16个时钟周期。CS上升沿之后,总线返回高阻状态。如果CS继续保持低电平,则准备新一轮的转换。进行采样的输入通道的选择是提前写入控制寄存器的,因此在转换时,用户必须提前写入以备通道的转换。也就是说,在进行当前转换时,用户就必须提前写入通道的地址以备下次转换使用。
 
  图2串行接口时序图
  2 S3C2410的主要功能
  S3C2410是三星公司推出的采用RISC结构的16/32位微处理器。它基于ARM920T内核,采用五级流水线和哈佛结构,最高频率可达203 MHz,是高性能和低功耗的硬宏单元。ARM920T具有增强ARM体系的MMU(支持WinCE,EPOC 32和Linux)、16kB的指令和数据高速缓存以及高速AMBA总线接口。
S3C2410被设计用于手持设备以及普通的嵌入式应用的集成系统,为了降低整个系统的成本,S3C2410还包括下面的部分:LCD控制器(STN&TFT)、NAND Flash引导装入程序、系统管理(片选逻辑和SDRAM控制器)、3通道UART,4通道DMA、4通道PWM时钟、I/O口、RTC、8通道10位ADC及触摸屏接口、IIC总线接口、IIS总线接口、USB主口和USB设备口、SD主口和多媒体卡接口、2通道SPI和2通道PLL。
  S3C2410有2个SPI口,可以实现串行数据的传输。每个SPI接口各有2个移位寄存器分别负责接收和发送数据。在传送数据期间,发送数据和接收数据是同步进行的,传送的频率可由相应的控制寄存器设定。如果只想发送数据,则接收数据为哑元;如果只想接收数据。则需发送哑元“0xff”。SPI接口共有4个引脚信号:串行时钟SCK(SPICLK0,1)、主入从出MISO(SPICLK0,1)和主出从入MOSI(SPIMOSI0,1)数据线、低电平有效引脚/SS(nSSO,1)。
  S3C2410的SPI接口具有如下特点
  (1)兼容SPI协议(ver.2.11);
  (2)有分别用于发送和接收的8位移位寄存器;
  (3)有设定传送频率的8位寄存器;
  (4)有轮询、中断和DMA三种传送模式。
  3 接口与驱动
  根据S3C2410的SPI特点及AD7888的工作原理确定其接口如图3所示。
 
  图3 AD7888与S3C2410的连接图
  为了实现S3C2410和AD7888在嵌入式Linux下的高速A/D转换,还编写了两者接口的驱动程序,该驱动程序功能的实现主要由以下几个函数完成。
  (1)Init_SPI()完成SPI的初始化
   void Init_SPI(void)
{
  int i;
  rSPPRE0=0x32;
  rSPCON0=0x1e;
  for(i=0;i<10;i++)
  rSPTDAT0=0xff;
  rGPECON |=0x0a800000;
  rGPECON&=(~0x05400000);
  rGPEUP |=0x3800;
  //GPH5----->CS
  rGPHCON |=0x0400;
  rGPHCON&=(~0x0800);
  rGPHUP&=(~0x20);
  rGPHDAT |=0x20;
}


 (2)ad_wr()写入要求A/D转换的通道
  static ssize_t ad_wr(struCt file *file,const char *bur,size_t count,loft_t *offset)
{
  int ret="0";
  int i="0";
  dbuf="kmalloc"(count *sizeof(unsigned char),GFP_KERNEL);
  copy_from_user(dbuf,bur,count);
  for(i=0;i
ADTXdata[i]=dbuf[i];
  kfree(dbuf);
  return ret;
}
  (3)ad_rd()得到A/D转换的结果
   statie ssize_t ad_rd(struet file *file,char *bur,size_t count,loft t *offset)
{
  int ret="0";
  int i="0";
  ad_convert();
  ad_convert();
  dbuf="kmalloc"(count *sizeof(unsigned char),GFP KERNEL);
  for(i=0;i
dbuf[i]=ADRXdata[i];
  copy_to_user(bur,dbuf,count);
  kfree(dbuf);
  return ret;
}
  (4)ad_convert()实际完成A/D转换
  void ad_convert(void)
{
  rGPHDAT&=(~0x20);
  udelay(100000);
  spi_tx_data(ADTXdata[0]);
  ADRXdata[0]=rSPRDATO;
  spi_tx_data(0xff);
  ADRXdata [1 ]=rSPRDATO;
  rGPHDAT |=0x20;
}
  (5)spi_tx_data()完成发送数据
  void spi_tx_data(unsigned char data)
{
  spi_poll_done();
  rSPTDAT0=data;
  spi_poll_done();
}
  (6)spi_poll_done()轮询SPI状态
  static void spi_poll_done(void)
{
  while(!(rSPSTA0&0x01));
}
  说明:1)ADTXdata和ADRXdata是unsigned char的全局数组变量,分别负责存放AD7888的控制寄存器数据和A/D转换的结果。2)ad_rd()中ad_convert()调用了2次,第1次调用用于通知要采某通道的数据,第2次调用用于得到该通道A/D转换的结果。这样虽然牺牲了一些转换的速度,但可使应用程序编程更加直观。
  4 结论
  应用带SPI接口的串行A/D转换器占用较少的微处理器I/O资源,硬件联接简单、软件易于实现,程序运行效率高。带有SPI接口的串行A/D转换器和ARM微处理器的结合可广泛应用于实现数据采集功能的掌上设备及其他嵌入式的系统,如:医疗仪器、通信设备、抄表设备等领域。

 

 

SPI总线协议及SPI时序图详解【转】
电子工程 2009-12-04 22:29:56 阅读1219 评论0 字号:大中小
SPI,是英语Serial Peripheral Interface的缩写,顾名思义就是串行外围设备接口。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议。
      SPI是一个环形总线结构,由ss(cs)、sck、sdi、sdo构成,其时序其实很简单,主要是在sck的控制下,两个双向移位寄存器进行数据交换。
    上升沿发送、下降沿接收、高位先发送。
    上升沿到来的时候,sdo上的电平将被发送到从设备的寄存器中。
    下降沿到来的时候,sdi上的电平将被接收到主设备的寄存器中。
    假设主机和从机初始化就绪:并且主机的sbuff=0xaa (10101010),从机的sbuff=0x55 (01010101),下面将分步对spi的8个时钟周期的数据情况演示一遍(假设上升沿发送数据)。
---------------------------------------------------
脉冲           主机sbuff    从机sbuff     sdi    sdo
---------------------------------------------------
0    00-0     10101010     01010101      0      0
---------------------------------------------------
1    0--1     0101010x     10101011      0      1
1    1--0     01010100     10101011      0      1
---------------------------------------------------
2    0--1     1010100x     01010110      1      0
2    1--0     10101001     01010110      1      0
---------------------------------------------------
3    0--1     0101001x     10101101      0      1
3    1--0     01010010     10101101      0      1
---------------------------------------------------
4    0--1     1010010x     01011010      1      0
4    1--0     10100101     01011010      1      0
---------------------------------------------------
5    0--1     0100101x     10110101      0      1
5    1--0     01001010     10110101      0      1
---------------------------------------------------
6    0--1     1001010x     01101010      1      0
6    1--0     10010101     01101010      1      0
---------------------------------------------------
7    0--1     0010101x     11010101      0      1
7    1--0     00101010     11010101      0      1
---------------------------------------------------
8    0--1     0101010x     10101010      1      0
8    1--0     01010101     10101010      1      0
---------------------------------------------------
    这样就完成了两个寄存器8位的交换,上面的0--1表示上升沿、1--0表示下降沿,sdi、 sdo相对于主机而言的。根据以上分析,一个完整的传送周期是16位,即两个字节,因为,首先主机要发送命令过去,然后从机根据主机的名准备数据,主机在下一个8位时钟周期才把数据读回来。
    SPI总线是Motorola公司推出的三线同步接口,同步串行3线方式进行通信:一条时钟线SCK,一条数据输入线MOSI,一条数据输出线MISO;用于 CPU与各种外围器件进行全双工、同步串行通讯。SPI主要特点有:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束中断标志;写冲突保护;总线竞争保护等。
    SPI总线有四种工作方式(SP0, SP1, SP2, SP3),其中使用的最为广泛的是SPI0和SPI3方式。
    SPI模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。如果CPOL=0,串行同步时钟的空闲状态为低电平;如果CPOL=1,串行同步时钟的空闲状态为高电平。时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。如果 CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。 SPI主模块和与之通信的外设音时钟相位和极性应该一致。
SPI时序图详解-SPI接口在模式0下输出第一位数据的时刻
    SPI接口在模式0下输出第一位数据的时刻
SPI接口有四种不同的数据传输时序,取决于CPOL和CPHL这两位的组合。图1中表现了这四种时序,
时序与CPOL、CPHL的关系也可以从图中看出。
 
图1
CPOL是用来决定SCK时钟信号空闲时的电平,CPOL=0,空闲电平为低电平,CPOL=1时,
空闲电平为高电平。CPHA是用来决定采样时刻的,CPHA=0,在每个周期的第一个时钟沿采样,
CPHA=1,在每个周期的第二个时钟沿采样。
由于我使用的器件工作在模式0这种时序(CPOL=0,CPHA=0),所以将图1简化为图2,
只关注模式0的时序。
 
图2
我们来关注SCK的第一个时钟周期,在时钟的前沿采样数据(上升沿,第一个时钟沿),
在时钟的后沿输出数据(下降沿,第二个时钟沿)。首先来看主器件,主器件的输出口(MOSI)输出的数据bit1,
在时钟的前沿被从器件采样,那主器件是在何时刻输出bit1的呢?bit1的输出时刻实际上在SCK信号有效以前,
比 SCK的上升沿还要早半个时钟周期。bit1的输出时刻与SSEL信号没有关系。再来看从器件,
主器件的输入口MISO同样是在时钟的前沿采样从器件输出的bit1的,那从器件又是在何时刻输出bit1的呢。
从器件是在SSEL信号有效后,立即输出bit1,尽管此时SCK信号还没有起效。关于上面的主器件
和从器件输出bit1位的时刻,可以从图3、4中得到验证。
 
图3
注意图3中,CS信号有效后(低电平有效,注意CS下降沿后发生的情况),故意用延时程序
延时了一段时间,之后再向数据寄存器写入了要发送的数据,来观察主器件输出bit1的情况(MOSI)。
可以看出,bit1(值为1)是在SCK信号有效之前的半个时钟周期的时刻开始输出的(与CS信号无关),
到了SCK的第一个时钟周期的上升沿正好被从器件采样。
 
图4
图4中,注意看CS和MISO信号。我们可以看出,CS信号有效后,从器件立刻输出了bit1(值为1)。
通常我们进行的spi操作都是16位的。图5记录了第一个字节和第二个字节间的相互衔接的过程。
第一个字节的最后一位在SCK的上升沿被采样,随后的SCK下降沿,从器件就输出了第二个字节的第一位。
 
SPI总线协议介绍(接口定义,传输时序)
一、技术性能
SPI接口是Motorola 首先提出的全双工三线同步串行外围接口,采用主从模式(Master Slave)架构;支持多slave模式应用,一般仅支持单Master。
时钟由Master控制,在时钟移位脉冲下,数据按位传输,高位在前,低位在后(MSB first);SPI接口有2根单向数据线,为全双工通信,目前应用中的数据速率可达几Mbps的水平。
-------------------------------------------------------
二、接口定义
SPI接口共有4根信号线,分别是:设备选择线、时钟线、串行输出数据线、串行输入数据线。
 
(1)MOSI:主器件数据输出,从器件数据输入
(2)MISO:主器件数据输入,从器件数据输出
(3)SCLK :时钟信号,由主器件产生
(4)/SS:从器件使能信号,由主器件控制
-------------------------------------------------------
三、内部结构
 
-------------------------------------------------------
四、传输时序
SPI接口在内部硬件实际上是两个简单的移位寄存器,传输的数据为8位,在主器件产生的从器件使能信号和移位脉冲下,按位传输,高位在前,低位在后。如下图所示,在SCLK的下降沿上数据改变,上升沿一位数据被存入移位寄存器。
 
SPI接口没有指定的流控制,没有应答机制确认是否接收到数据。

 

 
SPI驱动流程(S3C2440)    
SPI驱动流程(S3C2440)
2007-12-27 22:50:25
简单的说就是写几个寄存器,其实非常简单的,呵呵
一、配置IO脚为SPI接口
        我用的是(s3c2440)SPI1通道,所有的IO都在GPG脚上,故配置的是GPGCON,
将对应的位置为SPI
             然后是GPGUP寄存器
二、开始SPI寄存器的配置
       1、SPPRE,设置传送频率,即SPICLK的频率
          SPI_CLK=PCLK/2/(SPPRE+1)
       根据S3C2440的资料,PCLK=48M,我要配置SPI_CLK为  1M,
       1=48/2/(SPPRE+1)=>  SPPRE=23=> SPPRE=0x18
    (个人理解,有错请指出,呵呵)
       2、SPCON 寄存器
          该寄存器有 7 位,对应的控制包括:SPIMOD(6:5),SCK enable (4),master/slave (3),CPOL(2),CPHA(1),TAGD(0).具体怎么设置根据自己的需要设置就OK。
       3、SPPIN 寄存器
        就三个寄存器,都设置好后就可以写和接收数据 啦
          写之前记得要检查SPSTA的 REDY位是否为 1 .
NOTE: 我的配置好IO之后,发现写SPCON寄存器无效,郁闷了两天终于找到原因,就CLKCON寄存器的SPI位没有
激活,所以写寄存器之前记得要检查CLKCON的状态

一点点经验,留个记号,呵呵
代码写的有点乱,测试的时候弄的,不好意思^_^
code :
/************************************************/
#ifndef MODULE
#define MODULE
#endif

#include
#include
#include
#include
#include

#include
/*----------  spi    ---------------*/
/*----------------- addr ----------  note  --------*/
#define GPG_CON   0x56000060     /* port G control */
#define GPG_DAT   0x56000064     /* port G data */
#define GPG_UP    0x56000068     /* pull up port G control */

#define SPCON_1   0x59000020    /*SPI1 control */
#define SPSTA_1   0x59000024    /* SPI1 status */
#define SPPIN_1      0x59000028    /* SPI1 pin control */
#define SPPRE_1   0X5900002C    /* spi1 baud rate prescaler */
#define SPTDAT_1  0x59000030    /* spi1 Tx data */
#define SPRDAT_1  0x59000034    /* spi1 Rx data */

                        /* GPGCON */
#define nSS1            (1<<6  | 1<<7)        /* GPG3 */
#define SPICLK1         (1<<14 | 1<<15)        /* GPG7 */
#define SPIMOSI1        (1<<12 | 1<<13)        /* GPG6 */
#define SPIMISO1        (1<<10 | 1<<11)        /* GPG5 */
#define SPI_USE         (nSS1 | SPICLK1 | SPIMOSI1 |SPIMISO1)


//static int  spi_major;
#define spi_name "S3C2440_SPI"
#define spi_minor       1

#ifdef CONFIG_DEVFS_FS
static devfs_handle_t devfs_spi_dir,devfs_spi_raw;
#endif

 

MODULE_LICENSE("GPL");

#if 1
#define     SPI_SPPRE1    (*(volatile unsigned long *)SPPRE1)    /* spi1 baud rate prescaler */   
#define     SPI_GPGCON      (*(volatile unsigned long *)GPGCON)     /* port-G control */
#define     SPI_SPCON1     (*(volatile unsigned long *)SPCON1)    /* spi1 control */
#define     SPI_SPSTA1     (*(volatile unsigned long *)SPSTA1)    /* spi1 status */
#define     SPI_SPPIN1     (*(volatile unsigned long *)SPPIN1)    /* spi1 pin control */
#define     SPI_SPTDAT1      (*(volatile unsigned long *)SPTDAT1)    /* spi1 Tx data */
#define     SPI_SPRDAT1      (*(volatile unsigned long *)SPRDAT1)    /* spi1 Rx data */
#define     SPTDAT1_READ    (*(volatile unsigned long *)SPTDAT1)     /* spi1 Tx data */
#define     SPI_GPGDAT    (*(volatile unsigned long *)GPGDAT)    /* port-G data */

#define     CLKCON        (*(volatile unsigned long *)ADDR_CLKCON)

#endif


unsigned long     GPGCON;
unsigned long    GPGDAT;
unsigned long     GPGUP;

unsigned long      SPCON1;
unsigned long      SPSTA1;
unsigned long     SPPIN1;
unsigned long     SPPRE1;
unsigned long    SPTDAT1;
unsigned long     SPRDAT1;
unsigned long   ADDR_CLKCON;
static void get_sys_addr(void)
{
    GPGCON =(unsigned long)ioremap(GPG_CON,4);
    GPGDAT =(unsigned long)ioremap(GPG_DAT,4);
    GPGUP  =(unsigned long)ioremap(GPG_UP,4);
    SPCON1 =(unsigned long)ioremap(SPCON_1,4);
    SPSTA1 =(unsigned long)ioremap(SPSTA_1,4);
    SPPIN1 =(unsigned long)ioremap(SPPIN_1,4);
    SPPRE1 =(unsigned long)ioremap(SPPRE_1,4);
    SPTDAT1=(unsigned long)ioremap(SPTDAT_1,4);
    SPRDAT1=(unsigned long)ioremap(SPRDAT_1,4);
    ADDR_CLKCON=(unsigned long )ioremap(0x4c00000c,4);
}
#if 1

static int spi_open(struct inode *inode,struct file *filp)
{
    unsigned int port_state;
     int i=0;
#if 1
/*===============  start init spi regiser ============*/
    /*  时钟使能  */
    printk("CLKCON: %x /n",(unsigned int )CLKCON);
    if (!(CLKCON&(1<<18)))
        CLKCON|=(1<<18);
    printk("CLKCON: %x /n",(unsigned int )CLKCON);
    /* 配置IO 脚为 SPI  */

    port_state = readl(GPGCON);  
    printk("<1>only read :port_state= %x /n",port_state);
    port_state &= ~SPI_USE;
    port_state |= SPI_USE;
/*    printk("<1>port_state= %x /n",port_state);   */
   
    SPI_GPGCON = port_state;  
//    writel(SPI_USE,GPGCON);
/*    printk("<1> after write :readl(GPGCON)= %x /n",SPI_GPGCON);  */
      /* IO 脚使能  */
    port_state=0x0;
    port_state = readl(GPGUP);   
/*    printk("<1> GPGUP before write: %x /n",port_state);    */
    port_state &=~(1<<3 | 1<<5 | 1<<6 | 1<<7);

    writel(port_state,GPGUP);   
/*    printk("<1>after write :GPGUP= %x /n",readl(GPGUP));    */
#endif

#if 1

    port_state = readl(SPPRE1);

//    writel(value,SPPRE1);    /* if PCLK=50MHz   PCLK/2/(value+1) =SPICLK  */
   
    SPI_SPPRE1=0x18;    /* set Baud rate 1M .get it in net.note: PCLE=48MHz ,SPICLK=48/2/(0x18+1)=1M  */
   
    /*  set spi module  */
/*    printk("<1>before write :readl(SPCON1)=%x /n",readl(SPCON1));     */

    port_state = readl(SPCON1);
    port_state &= ~0x03f;
    port_state |= (0<<0 | 0<<1 | 1<<2 | 1<<3 | 1<<4 | 0<<5 );
    /*  0<<0 normal mode , 0<<1 format A,1<<2 active low ,1<<3 master
     *  1<<4 SCK enable (master only), 0<<5   polling mode
      */       
//    writel(port_state,SPCON1);
    SPI_SPCON1=(0<<0 | 0<<1 | 1<<2 | 1<<3 | 1<<4 | 0<<5 );


    port_state = readl(SPPIN1);
    port_state &= ~(1<<0 | 1<<1 | 1<<2);
    port_state |= (0<<2 | 1<<1 | 0<<0);
    /* 0<<0:release  1<<1:SBO  0<<2:dis-ENMUL*/
//    writel(port_state,SPPIN1);
  
    SPI_SPPIN1=(0<<2 | 1<<1 | 0<<0);
    printk("<1>SPI_SPPIN1 = %x /n",(unsigned int )SPI_SPPIN1);
//    SPI_GPGDAT &= ~1<<3;
    printk("SPSTA1=%x /n",readl(SPSTA1));
/*==================  end init spiregister =========================*/
#if 0   // use for test spi wave ...

    while (1)
    {
//    printk("<1> readl(SPSTA1)&0X01: %x /n",readl(SPSTA1)&0x01);
    if (readl(SPSTA1)&0x01)
    {
    SPTDAT1_READ=0x1234;
//     writel(a,SPTDAT1);
//    SPI_SPTDAT1='a';
    }
    printk("<1>writel /n");
//    printk("<1>readl(SPRDAT1)=%x /n",readl(SPRDAT1));
    printk("<1> readl(SPTDAT1)=%s /n",readl(SPTDAT1));

    i++;
//    printk("<1>readl(SPRDAT1)=%c /n",readl(SPRDAT1));
//    printk("<1> readl(SPTDAT1)=%c /n",readl(SPTDAT1));
    }

#endif // test spi wave
#endif
//    writel(~(1<<4),SPCON1);// dis-SCK
//    printk("<1>is the OPEN   readl(GPGUP)=%x /n",readl(GPGUP));
    return 0;

}

static int spi_release(struct inode *inode,struct file *filp)
{
//    MOD_DEC_USE_COUNT;
    printk("<1> release /n");
    return 0;
}
#endif
#if 1
static ssize_t spi_write(struct file *filp,const char *buf,size_t count,loff_t *f_ops)
{
    int i=0;
//    int config;
    char string;
    char str[20];
    char *txStr,*rxStr;
    volatile char *spiTxStr,*spiRxStr;
    volatile int endSpiTx;
    unsigned int port_state=0;

    endSpiTx=0;
    spiTxStr="spi123456test";
    spiRxStr=str;
    txStr=(char *)spiTxStr;
    rxStr=(char *)spiRxStr;
    /*  Determine SPI clock rate.
 *  Baud rate = PCLK / 2 / (Prescaler value + 1)
 *  Baud rate < 25 M HZ
 *       */
#if 0 // init spi in open ()
    port_state = readl(SPPRE1);
    writel(0x18,SPPRE1);  /*  SPPRE1= 0x18 ,Baud rate = 1MHz */
   
    printk("<1> spi_write/n");
   
    /*  set spi module  */

    port_state = readl(SPCON1);
    port_state &= ~0x07f;
    port_state |= (0<<0 | 0<<1 | 1<<2 | 1<<3 | 1<<4 | 0<<5);
    /*  0<<0 normal mode , 0<<1 format A,1<<2 active low ,1<<3 master
      *  1<<4 SCK enable (master only), 0<<5 | 0<<6  polling mode
      */       
    writel(port_state,SPCON1);
    printk("<1>set SPCON1/n");
    /* SPI : master,nSS pin :multi-master error */
    port_state = readl(SPPIN1);
    port_state &= ~(1<<0 | 1<<1 | 1<<2);
    port_state |= (1<<2 | 0<<1 | 1<<0);
    /* 1<<0: Drive the previous level; 1<<2:Multi master error detect enable*/    writel(port_state,SPPIN1);
    printk("<1>set SPCON1 /n");
#endif // init spi in open()
    while (endSpiTx == 0)
    {
        printk("SPSTA1=%x /n",readl(SPSTA1));
        if((readl(SPSTA1) & 0X01) == 0)
        {
            if (*spiTxStr != '/0')
            {
                writel(*spiTxStr,SPTDAT1);
                string = readl(SPTDAT1);
                spiTxStr++;
            }
            else
            {
                endSpiTx=1;
            }   
            str[i]=readl(SPRDAT1);
            printk("receive char = %c /n",str[i]);
            i++;
        }
    }
    port_state = readl(SPCON1);
    port_state &= ~0x07f;
    port_state |= (0<<1 | 0<<1 | 1<<2 | 0<<4 | 0<<5 );
    writel(port_state,SPCON1);
    *(spiRxStr-1)='/0';
    printk("<1> Tx string : %s /n",txStr);
    printk("<1> Rx string : %s /n",rxStr);
    return 0;
}
#endif

static struct file_operations spi_fops ={
    .owner   = THIS_MODULE,
    .open     = spi_open,
    .release = spi_release,
    .write     = spi_write,
};

static struct miscdevice spi_dev=
{
    123,
    "spi",
    &spi_fops,
};

static int __init spi_init(void)
{
//    unsigned int value;
//    int ret;
    get_sys_addr();

    misc_register(&spi_dev);
#if 0
    ret = register_chrdev(0,spi_name,&spi_fops);
    if(ret<0)
    {
        printk("<1> cannot get major number /n");
        return ret;
    }
    spi_major=ret;
#ifdef CONFIG_DEVFS_FS
    devfs_spi_dir = devfs_mk_dir(NULL,"spi",NULL);
    devfs_spi_raw = devfs_register(devfs_spi_dir,"0",DEVFS_FL_DEFAULT,spi_major,spi_minor,S_IFCHR | S_IRUSR | S_IWUSR,&spi_fops,NULL);
#endif
#endif     
   
    printk(KERN_ALERT "hello,world /n");
    printk("<1> readl(SPTDAT1)=%x ,  SPTDAT1_READ = %x /n",readl(SPTDAT1),(unsigned int)SPTDAT1_READ);

    return 0;
}

static void __exit spi_exit(void)
{
#if 0
#ifdef CONFIG_DEVFS_FS
    devfs_unregister(devfs_spi_raw);
    devfs_unregister(devfs_spi_dir);
#endif
    unregister_chrdev(spi_major,spi_name);
#endif
    misc_deregister(&spi_dev);
    printk(KERN_ALERT "goodbye ,cruel world /n");
}

module_init(spi_init);
module_exit(spi_exit);

 

 

 


 
2410_SPI接口与linux驱动   
   
   
2.6.18内核下已经添加了完整的spi子系统了,参考mtd的分析,将从下到上层,再从上到下层的对其进行分析。

以下先从下到上的进行分析:

driver/spi下有两个底层相关的spi驱动程序:
spi_s3c24xx.c和spi_s3c24xx_gpio.c

其中spi_s3c24xx.c是基于s3c24xx下相应的spi接口的驱动程序,spi_s3c24xx_gpio.c允许用户指定3个gpio口,分别充当spi_clk、spi_mosi和spi_miso接口,模拟标准的spi总线。

s3c2410自带了两个spi接口(spi0和spi1),在此我只研究基于s3c2410下spi接口的驱动程序spi_s3c24xx.c。

首先从spi驱动的检测函数进行分析:

static int s3c24xx_spi_probe(struct platform_device *pdev)
{
        struct s3c24xx_spi *hw;
        struct spi_master *master;
        struct spi_board_info *bi;
        struct resource *res;
        int err = 0;
        int i;

    /* pi_alloc_master函数申请了struct spi_master+struct s3c24xx_spi大小的数据,
         * spi_master_get_devdata和pi_master_get分别取出struct s3c24xx_spi和struct spi_master结构指针
          */
        master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi));
        if (master == NULL) {
                dev_err(&pdev->dev, "No memory for spi_master/n");
                err = -ENOMEM;
                goto err_nomem;
         }

     /* 填充struct spi_master结构 */
        hw = spi_master_get_devdata(master);
        memset(hw, 0, sizeof(struct s3c24xx_spi));

        hw->master = spi_master_get(master);
        hw->pdata = pdev->dev.platform_data;
        hw->dev = &pdev->dev;

        if (hw->pdata == NULL) {
                dev_err(&pdev->dev, "No platform data supplied/n");
                err = -ENOENT;
                goto err_no_pdata;
        }

        platform_set_drvdata(pdev, hw);//dev_set_drvdata(&pdev->dev, hw)
        init_completion(&hw->done);

        /* setup the state for the bitbang driver */
   
     /* 填充hw->bitbang结构(hw->bitbang结构充当一个中间层,相当与input system的input_handle struct) */
        hw->bitbang.master         = hw->master;
        hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer;
        hw->bitbang.chipselect     = s3c24xx_spi_chipsel;
        hw->bitbang.txrx_bufs      = s3c24xx_spi_txrx;
        hw->bitbang.master->setup  = s3c24xx_spi_setup;

        dev_dbg(hw->dev, "bitbang at %p/n", &hw->bitbang);

        /* find and map our resources */
         /* 申请spi所用到的资源:io、irq、时钟等 */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (res == NULL) {
                dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM/n");
                err = -ENOENT;
                goto err_no_iores;
        }

        hw->ioarea = request_mem_region(res->start, (res->end - res->start)+1,
                                        pdev->name);

        if (hw->ioarea == NULL) {
                dev_err(&pdev->dev, "Cannot reserve region/n");
                err = -ENXIO;
                goto err_no_iores;
        }

        hw->regs = ioremap(res->start, (res->end - res->start)+1);
        if (hw->regs == NULL) {
                dev_err(&pdev->dev, "Cannot map IO/n");
                err = -ENXIO;
                goto err_no_iomap;
        }

        hw->irq = platform_get_irq(pdev, 0);
        if (hw->irq < 0) {
                dev_err(&pdev->dev, "No IRQ specified/n");
                err = -ENOENT;
                goto err_no_irq;
        }

        err = request_irq(hw->irq, s3c24xx_spi_irq, 0, pdev->name, hw);
        if (err) {
                dev_err(&pdev->dev, "Cannot claim IRQ/n");
                goto err_no_irq;
        }

        hw->clk = clk_get(&pdev->dev, "spi");
        if (IS_ERR(hw->clk)) {
                dev_err(&pdev->dev, "No clock for device/n");
                err = PTR_ERR(hw->clk);
                goto err_no_clk;
        }

        /* for the moment, permanently enable the clock */

        clk_enable(hw->clk);

        /* program defaults into the registers */
         /* 初始化spi相关的寄存器 */
        writeb(0xff, hw->regs + S3C2410_SPPRE);
        writeb(SPPIN_DEFAULT, hw->regs + S3C2410_SPPIN);
        writeb(SPCON_DEFAULT, hw->regs + S3C2410_SPCON);
        /* add by lfc */
        s3c2410_gpio_setpin(S3C2410_GPE13, 0);
        s3c2410_gpio_setpin(S3C2410_GPE12, 0);
        s3c2410_gpio_cfgpin(S3C2410_GPE13, S3C2410_GPE13_SPICLK0);
        s3c2410_gpio_cfgpin(S3C2410_GPE12, S3C2410_GPE12_SPIMOSI0);
        s3c2410_gpio_cfgpin(S3C2410_GPE11, S3C2410_GPE11_SPIMISO0);
        /* end add */

        /* setup any gpio we can */
     /* 片选 */
        if (!hw->pdata->set_cs) {
                s3c2410_gpio_setpin(hw->pdata->pin_cs, 1);
                s3c2410_gpio_cfgpin(hw->pdata->pin_cs, S3C2410_GPIO_OUTPUT);
        }

        /* register our spi controller */
         /* 最终通过调用spi_register_master来注册spi控制器(驱动) */
        err = spi_bitbang_start(&hw->bitbang);
        if (err) {
                dev_err(&pdev->dev, "Failed to register SPI master/n");
                goto err_register;
        }

        dev_dbg(hw->dev, "shutdown=%d/n", hw->bitbang.shutdown);

        /* register all the devices associated */
         /* 注册所用使用本spi驱动的设备 */

        bi = &hw->pdata->board_info[0];
        for (i = 0; i < hw->pdata->board_size; i++, bi++) {
                dev_info(hw->dev, "registering %s/n", bi->modalias);

                bi->controller_data = hw;
                spi_new_device(master, bi);
        }

        return 0;

 err_register:
        clk_disable(hw->clk);
        clk_put(hw->clk);

 err_no_clk:
        free_irq(hw->irq, hw);

 err_no_irq:
        iounmap(hw->regs);

 err_no_iomap:
        release_resource(hw->ioarea);
        kfree(hw->ioarea);

 err_no_iores:
 err_no_pdata:
        spi_master_put(hw->master);;

 err_nomem:
        return err;
}

/*
 * spi_alloc_master - allocate SPI master controller
 * @dev: the controller, possibly using the platform_bus
 * @size: how much driver-private data to preallocate; the pointer to this
 *      memory is in the class_data field of the returned class_device,
 *      accessible with spi_master_get_devdata().
 *
 * This call is used only by SPI master controller drivers, which are the
 * only ones directly touching chip registers.  It's how they allocate
 * an spi_master structure, prior to calling spi_register_master().
 *
 * This must be called from context that can sleep.  It returns the SPI
 * master structure on success, else NULL.
 *
 * The caller is responsible for assigning the bus number and initializing
 * the master's methods before calling spi_register_master(); and (after errors
 * adding the device) calling spi_master_put() to prevent a memory leak.
 */
/*注释已经写得很清楚了,本函数旨在分配spi_master struct
 *其中,device为主控制设备,size为需要预分配的设备私有数据大小
 *该函数被spi主控制器驱动所调用,用于在调用spi_register_master注册主控制器前
 *分配spi_master struct,分配bus number和初始化主控制器的操作方法
 *注意在分配spi_master struct的时候多分配了大小为size的设备私有数据
 *并通过spi_master_set_devdata函数把其放到class_data field里,以后可以通过spi_master_get_devdata来访问
 */
struct spi_master * __init_or_module
spi_alloc_master(struct device *dev, unsigned size)
{
        struct spi_master       *master;

        if (!dev)
                return NULL;

        master = kzalloc(size + sizeof *master, SLAB_KERNEL);
        if (!master)
                return NULL;

        class_device_initialize(&master->cdev);
        master->cdev.class = &spi_master_class;
        master->cdev.dev = get_device(dev);
        spi_master_set_devdata(master, &master[1]);

        return master;
}


/*
 * spi_bitbang_start - start up a polled/bitbanging SPI master driver
 * @bitbang: driver handle
 *
 * Caller should have zero-initialized all parts of the structure, and then
 * provided callbacks for chip selection and I/O loops.  If the master has
 * a transfer method, its final step should call spi_bitbang_transfer; or,
 * that's the default if the transfer routine is not initialized.  It should
 * also set up the bus number and number of chipselects.
 *
 * For i/o loops, provide callbacks either per-word (for bitbanging, or for
 * hardware that basically exposes a shift register) or per-spi_transfer
 * (which takes better advantage of hardware like fifos or DMA engines).
 *
 * Drivers using per-word I/O loops should use (or call) spi_bitbang_setup and
 * spi_bitbang_cleanup to handle those spi master methods.  Those methods are
 * the defaults if the bitbang->txrx_bufs routine isn't initialized.
 *
 * This routine registers the spi_master, which will process requests in a
 * dedicated task, keeping IRQs unblocked most of the time.  To stop
 * processing those requests, call spi_bitbang_stop().
 */
int spi_bitbang_start(struct spi_bitbang *bitbang)
{
        int     status;

        if (!bitbang->master || !bitbang->chipselect)
                return -EINVAL;
     /*bitbang_work
       * 初始化a work,后面再create_singlethread_workqueue,
      * 等到有数据要传输的时候,在spi_bitbang_transfer函数中通过调用queue_work(bitbang->workqueue, &bitbang->work)
      * 把work扔进workqueue中调度运行
      * 这是内核的一贯做法,在mmc/sd驱动中也是这样处理的^_^
      */
        INIT_WORK(&bitbang->work, bitbang_work, bitbang);

     /* 初始化自旋锁和链表头,以后用到 */
        spin_lock_init(&bitbang->lock);
     spi_new_device   INIT_LIST_HEAD(&bitbang->queue);

        if (!bitbang->master->transfer)
                bitbang->master->transfer = spi_bitbang_transfer;//spi数据的传输就是通过调用这个方法来实现的
     /* spi_s3c24xx.c驱动中有相应的txrx_bufs处理方法,在bitbang_work中被调用 */
        if (!bitbang->txrx_bufs) {
                bitbang->use_dma = 0;
                bitbang->txrx_bufs = spi_bitbang_bufs;
                if (!bitbang->master->setup) {
                        if (!bitbang->setup_transfer)
                                bitbang->setup_transfer =
                                         spi_bitbang_setup_transfer;
                        bitbang->master->setup = spi_bitbang_setup;
                        bitbang->master->cleanup = spi_bitbang_cleanup;
                }
     /* spi_s3c24xx.c驱动中有相应的setup处理方法,在稍后的spi_new_device中被调用 */
        } else if (!bitbang->master->setup)
                return -EINVAL;

        /* this task is the only thing to touch the SPI bits */
        bitbang->busy = 0;
     /* 创建工作者进程 */
        bitbang->workqueue = create_singlethread_workqueue(
                        bitbang->master->cdev.dev->bus_id);
        if (bitbang->workqueue == NULL) {
                status = -EBUSY;
                goto err1;
        }

        /* driver may get busy before register() returns, especially
         * if someone registered boardinfo for devices
         */
        status = spi_register_master(bitbang->master);
        if (status < 0)
                goto err2;

        return status;

err2:
        destroy_workqueue(bitbang->workqueue);
err1:
        return status;
}


/**
 * spi_register_master - register SPI master controller
 * @master: initialized master, originally from spi_alloc_master()
 *
 * SPI master controllers connect to their drivers using some non-SPI bus,
 * such as the platform bus.  The final stage of probe() in that code
 * includes calling spi_register_master() to hook up to this SPI bus glue.
 *
 * SPI controllers use board specific (often SOC specific) bus numbers,
 * and board-specific addressing for SPI devices combines those numbers
 * with chip select numbers.  Since SPI does not directly support dynamic
 * device identification, boards need configuration tables telling which
 * chip is at which address.
 *
 * This must be called from context that can sleep.  It returns zero on
 * success, else a negative error code (dropping the master's refcount).
 * After a successful return, the caller is responsible for calling
 * spi_unregister_master().
 */
int __init_or_module
spi_register_master(struct spi_master *master)
{
        static atomic_t         dyn_bus_id = ATOMIC_INIT((1<<16) - 1);
        struct device           *dev = master->cdev.dev;
        int                     status = -ENODEV;
        int                     dynamic = 0;

        if (!dev)
                return -ENODEV;

        /* convention:  dynamically assigned bus IDs count down from the max */
        if (master->bus_num < 0) {
                master->bus_num = atomic_dec_return(&dyn_bus_id);
                dynamic = 1;
        }

        /* register the device, then userspace will see it.
         * registration fails if the bus ID is in use.
         */
        snprintf(master->cdev.class_id, sizeof master->cdev.class_id,
                "spi%u", master->bus_num);
        status = class_device_add(&master->cdev);//注册设备
        if (status < 0)
                goto done;
        dev_dbg(dev, "registered master %s%s/n", master->cdev.class_id,
                        dynamic ? " (dynamic)" : "");

        /* populate children from any spi device tables */
        scan_boardinfo(master);
        status = 0;
done:
        return status;
}


/* FIXME someone should add support for a __setup("spi", ...) that
 * creates board info from kernel command lines
 */

/*
 * scan board_list for spi_board_info which is registered by spi_register_board_info
 * 很可惜,s3c24xx的spi驱动中不支持spi_register_board_info这种标准方式注册方式,而是直接调用spi_new_device内部函数
 */

static void __init_or_module
scan_boardinfo(struct spi_master *master)
{
        struct boardinfo        *bi;
        struct device           *dev = master->cdev.dev;

        down(&board_lock);
        list_for_each_entry(bi, &board_list, list) {
                struct spi_board_info   *chip = bi->board_info;
                unsigned                n;

                for (n = bi->n_board_info; n > 0; n--, chip++) {
                        if (chip->bus_num != master->bus_num)
                                continue;
                        /* some controllers only have one chip, so they
                         * might not use chipselects.  otherwise, the
                         * chipselects are numbered 0..max.
                         */
                        if (chip->chip_select >= master->num_chipselect
                                        && master->num_chipselect) {
                                dev_dbg(dev, "cs%d > max %d/n",
                                        chip->chip_select,
                                        master->num_chipselect);
                                continue;
                        }
                        (void) spi_new_device(master, chip);
                }
        }
        up(&board_lock);
}


/*
 * Board-specific early init code calls this (probably during arch_initcall)
 * with segments of the SPI device table.  Any device nodes are created later,
 * after the relevant parent SPI controller (bus_num) is defined.  We keep
 * this table of devices forever, so that reloading a controller driver will
 * not make Linux forget about these hard-wired devices.
 *
 * Other code can also call this, e.g. a particular add-on board might provide
 * SPI devices through its expansion connector, so code initializing that board
 * would naturally declare its SPI devices.
 *
 * The board info passed can safely be __initdata ... but be careful of
 * any embedded pointers (platform_data, etc), they're copied as-is.
 */
int __init
spi_register_board_info(struct spi_board_info const *info, unsigned n)
{
        struct boardinfo        *bi;

        bi = kmalloc(sizeof(*bi) + n * sizeof *info, GFP_KERNEL);
        if (!bi)
                return -ENOMEM;
        bi->n_board_info = n;
        memcpy(bi->board_info, info, n * sizeof *info);

        down(&board_lock);
        list_add_tail(&bi->list, &board_list);
        up(&board_lock);
        return 0;
}


/* On typical mainboards, this is purely internal; and it's not needed
 * after board init creates the hard-wired devices.  Some development
 * platforms may not be able to use spi_register_board_info though, and
 * this is exported so that for example a USB or parport based adapter
 * driver could add devices (which it would learn about out-of-band).
 */
struct spi_device *__init_or_module
spi_new_device(struct spi_master *master, struct spi_board_info *chip)
{
        struct spi_device       *proxy;//这个结构很重要,以后就是通过这个结构来操作实际的spi设备的
        struct device           *dev = master->cdev.dev;
        int                     status;

        /* NOTE:  caller did any chip->bus_num checks necessary */

        if (!spi_master_get(master))
                return NULL;

        proxy = kzalloc(sizeof *proxy, GFP_KERNEL);
        if (!proxy) {
                dev_err(dev, "can't alloc dev for cs%d/n",
                        chip->chip_select);
                goto fail;
         }
     /* 初始化spi_device 结构各成员 */
        proxy->master = master;
        proxy->chip_select = chip->chip_select;
        proxy->max_speed_hz = chip->max_speed_hz;
        proxy->mode = chip->mode;
        proxy->irq = chip->irq;
        proxy->modalias = chip->modalias;

        snprintf(proxy->dev.bus_id, sizeof proxy->dev.bus_id,
                        "%s.%u", master->cdev.class_id,
                        chip->chip_select);
        proxy->dev.parent = dev;
        proxy->dev.bus = &spi_bus_type;
        proxy->dev.platform_data = (void *) chip->platform_data;
        proxy->controller_data = chip->controller_data;
        proxy->controller_state = NULL;
        proxy->dev.release = spidev_release;

        /* drivers may modify this default i/o setup */
     /* 调用master->setup(即s3c24xx_spi_setup)函数初始化spi设备 */
        status = master->setup(proxy);
        if (status < 0) {
                dev_dbg(dev, "can't %s %s, status %d/n",
                                "setup", proxy->dev.bus_id, status);
                goto fail;
        }

        /* driver core catches callers that misbehave by defining
         * devices that already exist.
         */
        status = device_register(&proxy->dev);//真正注册原始设备
        if (status < 0) {
                dev_dbg(dev, "can't %s %s, status %d/n",
                                "add", proxy->dev.bus_id, status);
                goto fail;
        }
        dev_dbg(dev, "registered child %s/n", proxy->dev.bus_id);
        return proxy;

fail:
        spi_master_put(master);
        kfree(proxy);
        return NULL;
}


static int s3c24xx_spi_setup(struct spi_device *spi)
{
        int ret;
     /* 进行一些检查性操作 */
        if (!spi->bits_per_word)
                spi->bits_per_word = 8;

        if ((spi->mode & SPI_LSB_FIRST) != 0)
                return -EINVAL;

        ret = s3c24xx_spi_setupxfer(spi, NULL);
        if (ret < 0) {
                dev_err(&spi->dev, "setupxfer returned %d/n", ret);
                return ret;
        }

        dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz/n",
                __FUNCTION__, spi->mode, spi->bits_per_word,
                spi->max_speed_hz);

        return 0;
}


static int s3c24xx_spi_setupxfer(struct spi_device *spi,
                                 struct spi_transfer *t)
{
        struct s3c24xx_spi *hw = to_hw(spi);
        unsigned int bpw;
        unsigned int hz;
        unsigned int div;

        bpw = t ? t->bits_per_word : spi->bits_per_word;
        hz  = t ? t->speed_hz : spi->max_speed_hz;

        if (bpw != 8) {
                dev_err(&spi->dev, "invalid bits-per-word (%d)/n", bpw);
                return -EINVAL;
        }

        div = clk_get_rate(hw->clk) / hz;

        /* is clk = pclk / (2 * (pre+1)), or is it
         *    clk = (pclk * 2) / ( pre + 1) */

        div = (div / 2) - 1;//求出预分频值

        if (div < 0)
                div = 1;

        if (div > 255)
                div = 255;

        dev_dbg(&spi->dev, "setting pre-scaler to %d (hz %d)/n", div, hz);
        writeb(div, hw->regs + S3C2410_SPPRE);//设置预分频值

        spin_lock(&hw->bitbang.lock);
        if (!hw->bitbang.busy) {
                hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE);//修改时钟,先禁用spi
                /* need to ndelay for 0.5 clocktick ? */
        }
        spin_unlock(&hw->bitbang.lock);

        return 0;
}


static void s3c24xx_spi_chipsel(struct spi_device *spi, int value)
{
        struct s3c24xx_spi *hw = to_hw(spi);
        unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0;
        unsigned int spcon;

        switch (value) {
        case BITBANG_CS_INACTIVE:
     /* 禁用spi(禁用片选) */
                if (hw->pdata->set_cs)
                        hw->pdata->set_cs(hw->pdata, value, cspol);
                else
                        s3c2410_gpio_setpin(hw->pdata->pin_cs, cspol ^ 1);
                break;

        case BITBANG_CS_ACTIVE:
    /*
     * 启用spi:根据需要设置寄存器并启用使能片选
     * (如果spi_board_info中没有设置相应的mode选项的话,那就只能使用默认值SPPIN_DEFAULT和SPCON_DEFAULT了)
     */
                spcon = readb(hw->regs + S3C2410_SPCON);

                if (spi->mode & SPI_CPHA)
                        spcon |= S3C2410_SPCON_CPHA_FMTB;
                else
                        spcon &= ~S3C2410_SPCON_CPHA_FMTB;

                if (spi->mode & SPI_CPOL)
                        spcon |= S3C2410_SPCON_CPOL_HIGH;
                else
                        spcon &= ~S3C2410_SPCON_CPOL_HIGH;

                spcon |= S3C2410_SPCON_ENSCK;

                /* write new configration */

                writeb(spcon, hw->regs + S3C2410_SPCON);

                if (hw->pdata->set_cs)
                        hw->pdata->set_cs(hw->pdata, value, cspol);
                else
                        s3c2410_gpio_setpin(hw->pdata->pin_cs, cspol);

                break;

        }
}

好了,至此spi主控制器(驱动)和板上spi设备注册完毕,以后要使用spi来传输数据的话,只要先获得spi设备结构,然后就可以利用它来和spi驱动打交道了(就好像你要操作一个文件,先要获取文件句柄一样,明白吧^_^)

   
 原文地址 http://www.cnitblog.com/luofuchong/archive/2007/09/24/33942.html  

 

~~~~~~~~~~~~~~~~~~``


任何SPI都有4种模式
2008-11-06 22:17
 

void McBSPObj::McBSP0Init(void)//McBSP从设备SPI硬件配置
{
/*-------------------------------------------------------------
   DSP5402之McBSP从设备SPI硬件四种模式配置实例(经过硬件测试)
--------------------------------------------------------------*/
//PCR设置过程(FSM=0,CLKM=0)
   McBSP0->SPSA = PCR;
   McBSP1->SPSD = (0 << PCR_XIOEN)    //发送非通用I/O模式位
                | (0 << PCR_RIOEN)    //接收非通用I/O模式位
                | (0 << PCR_FSXM)   //外部发送帧同步脉冲(外部片选)
                | (0 << PCR_FSRM)   //外部接收帧同步脉冲(外部片选)
                | (0 << PCR_CLKXM) //外部发送时钟(外部时钟源)
                | (0 << PCR_CLKRM) //外部接收时钟(外部时钟源)
#if SPIMODE == 0               
//SPI设置过程00(0--FS高电平有效,0--CLK上升沿收发数据)

                | (0 << PCR_FSXP)   //发送帧同步脉冲极性(高电平有效)
                | (0 << PCR_FSRP)   //接收帧同步脉冲极性(高电平有效)
                | (0 << PCR_CLKXP) //发送时钟极性(上升沿发送数据)
                | (0 << PCR_CLKRP);//接收时钟极性(上升沿接收数据)
#endif
#if SPIMODE == 1
//SPI设置过程01(0--FS高电平有效,1--CLK下降沿收发数据)
                | (0 << PCR_FSXP)   //发送帧同步脉冲极性(高电平有效)
                | (0 << PCR_FSRP)   //接收帧同步脉冲极性(高电平有效)
                | (1 << PCR_CLKXP) //发送时钟极性(下降沿发送数据)
                | (1 << PCR_CLKRP);//接收时钟极性(下降沿接收数据)
#endif
#if SPIMODE == 2
//SPI设置过程10(1--FS低电平有效,0--CLK上升沿收发数据)
                | (1 << PCR_FSXP)   //发送帧同步脉冲极性(低电平有效)
                | (1 << PCR_FSRP)   //接收帧同步脉冲极性(低电平有效)
                | (0 << PCR_CLKXP) //发送时钟极性(上升沿发送数据)
                | (0 << PCR_CLKRP);//接收时钟极性(上升沿接收数据)
#endif
#if SPIMODE == 3
//SPI设置过程11(1--FS低电平有效,1--CLK下降沿收发数据)
                | (1 << PCR_FSXP)   //发送帧同步脉冲极性(低电平有效)
                | (1 << PCR_FSRP)   //接收帧同步脉冲极性(低电平有效)
                | (1 << PCR_CLKXP) //发送时钟极性(下降沿发送数据)
                | (1 << PCR_CLKRP);//接收时钟极性(下降沿接收数据)
#endif
//MCR1设置过程(RMCM=0)
   McBSP0->SPSA = MCR1;//
   McBSP0->SPSD = (0 << MCR1_RMCM) //允许接收多通道选择(0)
                | (0x00 << MCR1_RPBBLK)
                | (0x00 << MCR1_RPABLK)
                | (0x00 << MCR1_RCBLK);
//MCR2设置过程(XMCM=0)
   McBSP0->SPSA = MCR2;//
   McBSP0->SPSD = (0x00 << MCR2_XMCM) //允许发送多通道选择(00b)
                | (0x00 << MCR2_XPBBLK)
                | (0x00 << MCR2_XPABLK)
                | (0x00 << MCR2_XCBLK);

//RCERA设置过程
   McBSP0->SPSA = RCERA;//
   McBSP0->SPSD = 0;

//RCERB设置过程
   McBSP0->SPSA = RCERB;//
   McBSP0->SPSD = 0;

//XCERA设置过程
   McBSP0->SPSA = XCERA;//
   McBSP0->SPSD = 0;

//XCERB设置过程
   McBSP0->SPSA = XCERB;//
   McBSP0->SPSD = 0;

//XCR1设置过程
   McBSP0->SPSA = XCR1;//
   McBSP0->SPSD = (0x00 << XCR1_XFRLEN1) //每帧1个字(每帧中断的次数1!!!)
//                | (0x02 << XCR1_XWDLEN1);//每字16位长(每次中断的字节数2!!!)
                | (0x00 << XCR1_XWDLEN1);//每字8位长(每次中断的字节数2!!!)
//XCR2设置过程
   McBSP0->SPSA = XCR2;
   McBSP0->SPSD = (0 << XCR2_XPHASE) //单相帧(其他设置都为0)
                | (0x00 << XCR2_XCOMPAND)//发送数据从最高位(MSB)开始
                | (0x00 << XCR2_XDATDLY);//同步后延迟0位数据
//RCR1设置过程
   McBSP0->SPSA = RCR1;               
   McBSP0->SPSD = (0x00 << RCR1_RFRLEN1) //每帧1个字(每帧中断的次数1!!!)
//                | (0x02 << RCR1_RWDLEN1);//每字16位长(每次中断的字节数2!!!)
                | (0x00 << RCR1_RWDLEN1);//每字8位长(每次中断的字节数2!!!)
//RCR2设置过程
   McBSP0->SPSA = RCR2;               
   McBSP0->SPSD = (0 << RCR2_RPHASE) //单相帧(其他设置都为0)
                | (0x00 << RCR2_RCOMPAND)//接收数据从最高位(MSB)开始
                | (0x00 << RCR2_RDATDLY);//同步后延迟0位数据
//SRGR1设置过程
   McBSP0->SPSA = SRGR1;
   McBSP0->SPSD = (0x00 << SRGR1_CLKGDV);//1
//SRGR2设置过程
   McBSP0->SPSA = SRGR2;
   McBSP0->SPSD = (0 << SRGR2_FSGM)
                | (1 << SRGR2_CLKSM)//由CPU时钟产生的采样率时钟1
                | (0 << SRGR2_CLKSP)//0
                | (1 << SRGR2_GSYNC)//
                | (0x0f << SRGR2_FPER);//0x0f
//SPCR1设置过程(CLKSTP=1Xb,RINTM=00b)
   McBSP0->SPSA = SPCR1;
   McBSP0->SPSD = (0x00 << SPCR1_RINTM)   //接收中断模式00(每帧接收1次中?
                              | (0 << SPCR1_DLB)        //禁止回送
                | (1 << SPCR1_DXENA)      //DX使能
                | (0x00 << SPCR1_RJUST)   //接收符号不扩展
                | (0x02 << SPCR1_CLKSTP);//SPI模式时钟开始于上升沿(无延迟)
//SPCR2设置过程(XINTM=02b)
   McBSP0->SPSA = SPCR2;
   McBSP0->SPSD = (0x02 << SPCR2_XINTM)//发送中断模式02
                | (1 << SPCR2_XEMPTY)   //发送移位寄存器空
                | (1 << SPCR2_XRDY);    //发送准备好
//SPCR1复位过程
   McBSP0->SPSA = SPCR1;
   McBSP0->SPSD|= (1 << SPCR1_RRST);//接收器复位
//SPCR2复位过程
   McBSP0->SPSA = SPCR2;
   McBSP0->SPSD|= (1 << SPCR2_XRST)//发送器复位
                | (1 << SPCR2_GRST)//采样率发生器复位
                | (1 << SPCR2_FRST);//帧同步发生器复位
//清除允许BXINT0中断过程
//   SREG->IFR = (1 << IFR_BXINT0);//清除BXINT0中断标志
//   SREG->IMR |= (1 << IMR_BXINT0);//允许BXINT0中断
//清除允许BRINT0中断过程
   SREG->IFR = (1 << IFR_BRINT0);//清除BRINT0中断标志
   SREG->IMR |= (1 << IMR_BRINT0);//允许BRINT0中断
}   

void McBSPObj::McBSP1Init(void)//GPIO配置
{
   McBSP1->SPSA = SPCR1;
   McBSP1->SPSD = 0;
   McBSP1->SPSA = SPCR2;
   McBSP1->SPSD = 0;
   McBSP1->SPSA = PCR;
//设置收发为IO接口,DX输出,DR,CLKS输入  
   McBSP1->SPSD = (1 << PCR_RIOEN)    //通用I/O模式位
//硬件RDDOG喂狗信号(BDX1)管脚输出模式默认I/O设置
                | (1 << PCR_XIOEN)    //通用I/O模式位
//硬件RD SIA信号(BFSR1)管脚输入模式设置               
                | (0 << PCR_FSRM)     //FSR为输入IO
//硬件FM CE信号(BFSX1)管脚输出模式设置               
                | (1 << PCR_FSXM)     //FSR为输出IO,FLASH的片选信号
//硬件FM CE信号(BFSX1)管脚高电平输出控制
                | (1 << PCR_FSXP)     //关闭FLASH的片选信号(FSX=1)
                | (0 << PCR_CLKRM)    //CLKR为输入IO
                | (0 << PCR_CLKXM);   //CLKX为输入IO,CLKX信号

你可能感兴趣的:(linux)