dragonnk 发表于 2014-3-1 21:28:08

裸机SD卡驱动程序

本帖最后由 dragonnk 于 2014-3-2 12:08 编辑

    折腾了一个多月终于把SD卡的驱动写好了,用1G卡测试过了,实现了基本的块读和块写,没有对错误进行处理,请读者自行添加,相信写SD卡驱动的朋友对SD卡及SD卡控制器有了一定的了解,废话少说了,直接上程序,注意的地方我在程序中注明,供大家参考。如有疑问可加我QQ:270428231。

unsigned int card_cap;   // 标记卡是标准卡还是大容量卡      
void sd_delay(void)      //延时程序
       {
      unsigned int i;
      for(i=0;i<1000;i++);
       }

//发送命令
void sd_send_com(unsigned int com_index,unsigned int arg,unsigned int com_type,unsigned int data_present)
//第一个参数为命令号,第二个参数为参数,第三个参数为手册里COMMAND REGISTER下面那                  
//个表中五行从上到下的编号,从0开始,第四个参数如命令使用DAT线则为1,否则为0
{
      unsigned short int rcom=0;
      while(CONTROL4_0&0x01); //-- 等待总线允许发送命令
         while(PRNSTS0&0x01);      //-- 等待CMD总线允许发送命令
      while(PRNSTS0&0x02);      //-- 等待DAT总线允许发送命令
      if(data_present)
                rcom|=0x20;
      switch(com_type)
      {
                case 1:
                        rcom|=0x9;
                        break;
                case 2:
                        rcom|=0x2;
                        break;
                case 3:
                        rcom|=0x1A;
                        break;
                case 4:
                        rcom|=0x1B;
                        break;
                default:
                        break;
      }
      rcom|=(unsigned short int)(com_index<<8);
      ARGUMENT0=arg;
      CMDREG0=rcom;
      sd_delay();          //必须延时,千百次试验的结果,呵呵
      if(com_index==8)//CMD8可能有应答,可能没有,所以产生命令超时也返回
      {
         while(!((NORINTSTS0&0x01)||(ERRINTSTS0&0x01)));
         NORINTSTS0=0x01;         //等待命令完成并清除
         ERRINTSTS0=0x01;
         return;
      }      
      if(com_type)      //其它命令
      {
         while(!(NORINTSTS0&0x01));
         NORINTSTS0=0x01;   //等待命令完成并清除
         return;
      }
}
//初始化
void sd0_init()
{
      unsigned int response=0;      
      
      GPGCON=0x2222222;
      GPGPUD=0x0;//数据端口设置
      
         SWRST0=0x01;   //软件复位
      while(SWRST0&0x01);
      CLK_SRC|=(3<<18);//SMMC选用外部27M
      SPCON|=(0x03<<26);
      CONTROL4_0=(0x03<<16);    //选择电流驱动能力位9mA :Clock      
         
      CONTROL2_0=(0x0<<15)|(0x0<<14)|(0x1<<8)|(2<<4)|3|(8<<24);

             //CMD,DAT不要设为由PWRCON控制,SDCD# 不要设为由DAT3控制,原理图中SDCD始终为高
      CONTROL3_0=(0<<31)|(0<<23)|(0<<15)|(0<<7);//时钟设置
      CLKCON0=0x4001;             //选择最低平率 并且开启中断时钟 约200K
      while(!(CLKCON0&0x02));   //等待内部时钟稳定
      CLKCON0=(CLKCON0|(1<<2));   //开启时钟
      TIMEOUTCON0=(TIMEOUTCON0&(0xf<<4))|0xe;   //超时时间设置为最大
       HOSTCTL0=HOSTCTL0&(~(1<<2));
       NORINTSTSEN0=0x7fff;         //启动所有中断
      
      sd_delay();
      sd_send_com(0,0,0,0);
      sd_delay();                  //必须延时
      sd_send_com(8,0x1aa,3,0);
               
      while(!response)
      {
      sd_send_com(55,0,3,0);      
      sd_send_com(41,0x40FF8000,2,0);
      response=(RSPREG0_0&(1<<31));
      }
      card_cap=(RSPREG0_0&(1<<30));
      response=0;
      sd_send_com(2,0,1,0);
      while(response==0)
      {
          sd_send_com(3,0,3,0);
         response=(RSPREG0_0&0xFFFF0000);
      }      


      //sd_send_com(9,response,1,0);
      //sd_send_com(4,0x8010000,0,0);//这两处命令根据需要写

      sd_send_com(7,response,4,0);
      
      CLKCON0=0x8000;             //停止时钟
      if(card_cap)
            CLKCON0=0x001;
      else
            CLKCON0=0x101;                  //开启内部时钟
      while(!(CLKCON0&0x02));             //等待内部时钟稳定
      CLKCON0=(CLKCON0|(1<<2));    //开启时钟13.5M
      sd_send_com(16,512,3,0);
}

//单块512字节读
void sd_read_single(unsigned int sect_arg,unsigned int *p)
{      //第一个参数为扇区号
      unsigned int i;
      
      NORINTSTSEN0=0x33;
      BLKSIZE0=0x200;
      TRNMOD0=0x10;
      if(card_cap)
         sd_send_com(17,sect_arg,3,1);
      else
            sd_send_com(17,sect_arg*512,3,1);         
      while(!(NORINTSTS0&0x020));   //等待读准备好中断
      NORINTSTS0=0x20;
      for(i=0;i<128;i++)
      {
                *p=BDATA0;
                p++;
      }
      while(!(NORINTSTS0&0x02));         //等待传输完成
      NORINTSTS0=0x02;
}

//单块512字节写
void sd_write_single(unsigned int sect_arg,unsigned int *p)
{      //第一个参数为扇区号
      unsigned int i;
      
      NORINTSTSEN0=0x33;
      BLKSIZE0=0x200;
      TRNMOD0=0x00;
      if(card_cap)
                sd_send_com(24,sect_arg,3,1);
      else
            sd_send_com(24,sect_arg*512,3,1);            //等待写准备好中断
      while(!(NORINTSTS0&0x010));
      NORINTSTS0=0x10;
      for(i=0;i<128;i++)
      {
                BDATA0=*p;
                p++;
      }
      while(!(NORINTSTS0&0x02));                  //等待传输完成
      NORINTSTS0=0x02;
}



青苹果 发表于 2014-3-2 15:14:48

谢谢分享。楼主辛苦了。

qm3234 发表于 2014-3-3 20:01:31

dragonnk 发表于 2014-3-3 21:15:51

不客气,不对之处还请指正!

飞凌-路飞 发表于 2014-3-4 08:37:49

感谢楼主的分享!!辛苦了!!!

白云峰 发表于 2014-7-3 23:09:50

楼主,您辛苦了,我想和您了解一下winCE下底层驱动程序的操作流程,和如何去验证驱动.:)
页: [1]
查看完整版本: 裸机SD卡驱动程序