当前位置:首页 > 嵌入式培训 > 嵌入式学习 > 讲师博文 > 基于S5PC100裸机程序之SPI(下)

基于S5PC100裸机程序之SPI(下) 时间:2014-08-02      来源:未知

3 SPI接口应用示例

这里将介绍一种通过SPI通信的Flash,该芯片是M24PXX,如图5所示为该芯片的原理图,每根接线的意义已经清楚地标识出来了。


图5 M25PXX原理图

这一款芯片内部集成了12条指令,包括了通用的读、写、配置等命令,还有一个内置的状态寄存器,可以通过该寄存器获取芯片当前状态。

如表7所示为M5DPXX芯片指令集。

表7 M25PXX芯片指令集

 Instrucion  Description  One-byle Instruction Code  Address Bytes  Dummy Bytes  Date Bytes  
 WREN  写使能  0000 0110  06h  0  0  0
 WRDI  写禁止  0000 0100  04h  0  0  0
 RDID  读取ID  1001 1111  9Fh  0  0  1 to 3
 RDSR  读状态寄存器  0000 0101  05h  0  0  1 to ∞
 WRSR  写状态寄存器  0000 0001  01h  0  0  1
 READ  字节数据读取  0000 0011  03h  3  0  1 to ∞
 FAST_READ  高速的字节读取  0000 1011  0Bh  3  1  1 to ∞
 PP  页编程  0000 0010  02h  3  0  1 to 256
 SE  扇区擦除  1101 1000  D8h  3  0  0
 BE  块擦除  1100 0111  C7h  0  0  0
 DP  深度擦除  1011 1001  B9h  0  0  0
 RES  从睡眠模式唤醒以及读出电特性  1010 1011  ABh  0  3  1 to ∞
 唤醒  0  0  0      

有了上文的知识做铺垫,现在我们先来看一下SPI控制器的基本编程模型:

(1)设置时钟源并配置分频值等参数。

(2)软复位后,并设置SPI配置寄存器(SPI CONFIGURATION REGISTER)。

(3)设置模式寄存器。

(4)设置从机选择寄存器。

(5)收发数据。

根据以上信息,这里分成若干模块来逐一实现。

1.相关寄存器结构体定义及宏定义。

/*SPI总线控制器寄存器定义*/
        typedef struct {
               unsigned int CHCFG;
               unsigned int CLKCFG;
               unsigned int MODECFG;
               unsigned int SLAVESEL;
               unsigned int INTEN;
               unsigned int STATUS;
               unsigned int TXDATA;
               unsigned int RXDATA; 
               unsigned int PACKETCNT;
               unsigned int PENDINGCLR;
               unsigned int SWAPCFG;
               unsigned int FBCLK;
        }spi;
        #define SPI0 ( * (volatile spi *)0XEC300000 )
        /* Flash opcodes. */
        #define    OPCODE_WREN        0x06 /*写使能*/
        #define    OPCODE_WRDA        0x04 /*写禁止*/
        #define    OPCODE_RDSR        0x05 /*读状态寄存器*/
        #define    OPCODE_WRSR        0x01 /*写状态寄存器*/
        #define    OPCODE_NORM_READ   0x03 /*低频率的数据读取*/
        #define    OPCODE_FAST_READ   0x0b /*高频率的数据读取*/
        #define    OPCODE_PP          0x02 /*页编程*/
        #define    OPCODE_BE_4K       0x20 /* Erase 4KiB block */
        #define    OPCODE_BE_32K      0x52 /* Erase 32KiB block */
        #define    OPCODE_CHIP_ERASE  0xc7 /*擦除整块芯片*/
        #define    OPCODE_SE          0xd8 /*扇区擦除*/
        #define    OPCODE_RDID        0x9f /*读ID */
        /*状态寄存器*/
        #define    SR_WIP             1        /*写状态中*/
        #define    SR_WEL               2            /*写保护锁*/

2.延时函数,使能芯片及禁用芯片的实现如下。

void delay(int times)
        {
                volatile int i,j;
                for (j = 0; j < times; j++){
                      for (i = 0; i < 100000; i++);
                      i = i + 1;
                }
        }
        void disable_chip(void)
        { 
                /* disable chip*/
                SPI0.SLAVESEL |= 0x1;
                delay(1);
        }
        void enable_chip(void)
        { 
                /* enable chip*/
                SPI0.SLAVESEL &= ~0x1;
                delay(1);
        }

3.软件复位的代码如下

void soft_reset(void)
        {
                SPI0.CHCFG |= 0x1 << 5;    
                delay(1);
                SPI0.CHCFG &= ~(0x1 << 5);
        }

4.接收(字节读)的实现。

在实现读字节功能的时候,第一次发送读指令后,紧接着芯片就会回送数据,这时芯片会自动累加地址,因此需要人为控制所读数据的范围,读时序如图6所示。


图6 读时序

void receive(unsigned char *buf, int len)
        {
                int i;
                SPI0.CHCFG &= ~0x1; // disable Tx  
                SPI0.CHCFG |= 0x1 << 1; // enable Rx    
                delay(1);
                for (i = 0; i < len; i++){
                      buf[i] = SPI0.RXDATA;
                       //S5PC100 SPI支持在FiFo为空时,读缓存产生SPI时钟,否则需要发送数据产生时钟 
                      delay(1);
                }
                SPI0.CHCFG &= ~(0x1 << 1);
        }

5.发送(写页面)的实现。

在发出写指令后,要紧跟着发出第一个数据要存放的地址,这个时候再顺序写入数据,和读的时候一样,芯片会自动累加地址,芯片的页面为256Bytes,写时序如图7所示。


图7 写时序

关键代码如下。

void transfer(unsigned char *data, int len)
        {
                int i;
                SPI0.CHCFG &= ~(0x1 << 1);
                SPI0.CHCFG = SPI0.CHCFG | 0x1; // enable Tx and disable Rx
                delay(1);
                for (i = 0; i < len; i++){
                        SPI0.TXDATA = data[i];
                        while( !(SPI0.STATUS & (0x1 << 21)) );
                        delay(1);
                }
        SPI0.CHCFG &= ~0x1; 
        }

6.擦除芯片相关操作如下。

这块芯片提供了两种擦除方式,第一种是分扇区来进行擦除,重点在于选好指定的地址,然后进行擦除,结合如图8所示的时序图。


图8 擦除扇区时序图

第二种是整片芯片擦除,如图9所示为整片芯片的擦除时的时序情况。


图9 整片芯片擦除时序图

关键代码如下。

/*擦除扇区*/
        void erase_sector(int addr)
        {
                unsigned char buf[4];
                buf[0] = OPCODE_SE;
                buf[1] = addr >> 16;
                buf[2] = addr >> 8;
                buf[3] = addr;
                enable_chip();
                transfer(buf, 4);
                disable_chip();
        }
        /*擦除芯片*/
        void erase_chip()
        {
                unsigned char buf[4];
                buf[0] = OPCODE_CHIP_ERASE;
                enable_chip();
                transfer(buf, 1);
                disable_chip();
        }

7.解析状态。

判断芯片工作是否结束,如图10所示为状态寄存器读时序。


图10 状态寄存器读时序

关键代码如下。

void wait_till_write_finished()
        {
                unsigned char buf[1]; 
                enable_chip();
                buf[0] = OPCODE_RDSR;
                transfer(buf, 1); 
                while(1) {
                      receive(buf, 1);
                      if(buf[0] & SR_WIP) {
                              //   printf( "Write is still in progress\n" );
                      }
                      else {
                           printf( "Write is finished.\n" );
                           break;
                      }
                }
                disable_chip();
        }

8.读芯片ID。

读ID时序如图11所示。


图11 读ID时序

关键代码如下。

void read_ID(void)
        { 
                unsigned char buf[3];
                int i;
                buf[0] = OPCODE_RDID;
                soft_reset();
                enable_chip();
                transfer(buf, 1);
                receive(buf, 3);
                disable_chip();
                printf("MI = %x\tMT = %x\tMC = %x\t\n", buf[0], buf[1], buf[2]);
        }

9.主程序

int main()
        {
                unsigned char buf[10] = "home\n";
                unsigned char data[10] = "morning\n";
                uart0_init();
                printf("aaaaa \n");
                cfg_gpio();       //配置SPI IO功能 
                set_clk();        //使能SPI控制器的时钟 
                cfg_spi0();       //配置SPI0控制器 
                while(1)
                {
                      read_ID();               //读出SPI Flash的ID号 
                      write_spi(buf, 4, 0);  //向目标芯片0地址写入4个字节数据 
                      read_spi(data, 4, 0);  //从目标芯片0地址读出4个字节数据 
                      printf("read from spi :%s", data);
                }
                return 0;
        }

上一篇:关于管道使用规则的总结

下一篇:sscanf函数的用法

热点文章推荐
华清学员就业榜单
高薪学员经验分享
热点新闻推荐
前台专线:010-82525158 企业培训洽谈专线:010-82525379 院校合作洽谈专线:010-82525379 Copyright © 2004-2022 北京华清远见科技集团有限公司 版权所有 ,京ICP备16055225号-5京公海网安备11010802025203号

回到顶部