SD Card 驱动流程分析

2024-04-05 00:08
文章标签 分析 流程 驱动 sd card

本文主要是介绍SD Card 驱动流程分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、硬件接口电路

         首先来看一下SD card的一个硬件应用电路,如下图所示:

        SD卡有两种工作模式,一个是spi接口模式,一个是SD模式,后者传输速率快些,这里讨论SD模式;SD总线包含4个数据总线(SDIO0-SDIO3),1个命令线(SDCMD)和一个同步时钟SDCLK;图上第10脚是卡检测信号。这里需注意的是传输总线必须加上拉。数据总线不一定全部要用到,根据需求可以选择数据总线个数。

 

二、SD卡初始化过程

             SD卡的初始化过程,简单一点说包括:1.主机reset所有挂在SD总线上的CARD. 2.确认卡的工作电压范围。3.获取卡的RCA(相对地址)。卡划分为十个工作状态,分别是如下所示:

 

        初始化阶段需要涉及前面四个状态,即inactive state、idle 、ready、identification  state。 初始化状态图如下:

 

        SD卡是通过命令与host互动的,主机发命令,sd卡接收到命令后返回相应的响应。所有命令和响应的传输都是在命令线上进行的。刚上电时,host发送CMD0来reset所有的sd卡(不管当前处于什么状态),使之进入idle状态。

        通过发送CMD8命令,告诉SD卡host提供的工作电压范围,若SD卡支持host提供的工作范围,将在命令线上返回该值,可以说明当前是ver2.0SD存储卡。若SD卡不支持host提供的工作电压范围,它将没有响应而继续处在idle state。如果该卡没有响应则是ver1.x。对于高容量SD存储卡CMD8命令是必须的。接下来需要发送ACMD41来决定是否拒绝或者说抛弃当前的SD卡(让它们进入inactive state),很明显那些与host设计工作电压范围不匹配的卡将会遭此待遇。另外ACMD41命令的参数里面还包含了HCS位,即host capicity support,置1表示host支持高容量sd卡。对于高容量SD存储卡,该位必须设为1,否则高容量卡响应中的busy 位不会置1,该busy位在OCR寄存器的第31位,是用来指示SD卡是否初始化完毕,即进入ready state(busy bit is set to 1)。对于标准SD容量卡,HCS位设为0。要使的sd卡初始化完成并进入ready状态,不仅HCS位要设置正确,host还必须重复发送ACMD41命令,直到busy 位置1。ACMD41的响应中除了busy位,还有一个CCS位(card capcity status),当CCS = 1时,表示当前sd卡是高容量sd卡,CCS=0表示当前sd卡是标准容量sd卡。

      接着host发送cmd2来获取sd卡的一些出厂信息(CID),比如出厂序列号之类的,这些信息也是通过命令响应的方式传送给host,sd卡返回CID信息后就进入identification state。

      发送cmd3使card传送rca给host,rca是用来给sd卡编址的,用于后面的数据传送。一旦获取了rca,sd卡就进入stand-by state。这个时候如果host想得到新的rca,再次发送cmd3即可。

      具体的初始化和卡识别流程如下图:

      初始化注意事项:1、在初始化动作之前,至少发送74个null clock ,否则可能会遇到初始化失败。

2、ACMD命令发送之前需要发送cmd55命令,告诉sd卡host接下来要发送ACMD类型的命令,A表示application,初始化的时候cmd55命令参数中的rca使用默认的值0x0000,初始化之后必须是相应rca。

3、整个初始化过程使用小于400KHZ时钟,因为有的卡在初始化和识别阶段对clock有限制,在数据传送之前把clock调整即可。

 

三、数据传送      

         

       从上图可以很清晰地看到它的一个流程,进入数据传送模式开始sd卡是处在Stand-by状态的,此时可以发送命令获取SD卡的一些信息,比如发送cmd9来计算sd卡的容量。要想进入transfer状态,我们必须通过CMD7先选中某一张卡,该命令的参数设为我们想要选中sd卡的RCA,即初始化阶段得到的RCA。在同一个时刻,只能有一张卡处在transfer状态,其他与cmd7命令携带参数(RCA)不匹配的卡会回到stand-by状态。所以说要选中某张卡,只要设置相应的RCA,而释放一张卡,我们就设置与之不匹配的RCA(一般设为默认的值0x0000)。选中卡之后就进入transfer state,这时我们可以发送acmd6设置数据传输的总线位宽,默认是1,注意此操作有两个前提,一是处在transfer state ,二是当前卡没有被lock。发送cmd17读单快数据,cmd18读多块数据,cmd24写单块,cmd25写多块,这些命令都带需要读取数据的首地址。在对多块进行读写的时,直到host发送cmd12才停止。

       对block写数据操作完成后,sd卡会进入编程阶段,sd卡是提供缓冲区的,也就是说上一个block在编程时可以写下一个block数据。当缓冲区满了,并且这是卡还在programming,这时DAT0数据线会被拉低,告诉host此时忙碌。

四、程序

        程序是在ZSP200内核的DSP芯片上实现的,16位数据总线,60MH的奔跑速度。下面是调试代码,还没来及优化,实现了block的读写,在板子上验证ok

[cpp] view plain copy print ?
  1. <SPAN style="FONT-SIZE: 12px; FONT-FAMILY: Microsoft YaHei"></SPAN>   
 
[cpp] view plain copy print ?
  1. <SPAN style="FONT-SIZE: 12px; FONT-FAMILY: Microsoft YaHei">void sd_mmc_setup()  
  2. {  
  3.     uint16 reg16_val;  
  4.     uint32 reg32_val;  
  5.     U8 i;  
  6.       
  7.     SysReadAMBAReg((uint32)SYS_PWRPEN_REG,reg32_val);  
  8.     reg32_val |= (1L<<2);  
  9.     SysWriteAMBAReg((uint32)SYS_PWRPEN_REG,reg32_val);      //开启SD/MMC控制器   
  10.   
  11.     sys_delay_ms(300L);  
  12.      
  13.     mmc_write_reg(MMC_CAR_SEL, 0xdd);        // enable module, enable mmcclk    
  14.     mmc_write_reg(MMC_CRC_CTL, 0xd0);        // CRC circuit enable   
  15.     mmc_write_reg(MMC_CTL, 0x0b);            // 1bit,low speed(256KHZ),1/4 divider,auto transfer, mmc mode.   
  16.     //mmc_write_reg(MMC_INT_MASK, 0x7f);       // unmask all interrupt    
  17.     
  18.     for (i = 0; i<10; i++)                   // at least 74 clock for setup   
  19.     {  
  20.         mmc_write_reg(MMC_IO,0x24);          // only 8 null clock generation   
  21.   
  22.         mmc_read_reg(MMC_INT_CLR,reg16_val);  
  23.         while(reg16_val != 0x01)  
  24.         {  
  25.             mmc_read_reg(MMC_INT_CLR,reg16_val);  
  26.         }  
  27.         mmc_write_reg(MMC_INT_CLR,reg16_val);  
  28.     }  
  29.   
  30.     myptf("sd_mmc_setup finished!\r\n");  
  31.       
  32.     return;  
  33. }  
  34.   
  35. U8 sd_write_cmd(U8 *cmd, BOOL b_resp)  
  36. {  
  37.     U8 i;  
  38.     uint16 reg16_val;  
  39.     uint32 addr = MMC_CMD_BUF4;  
  40.       
  41.     for(i=0;i<5;i++)  
  42.     {  
  43.       mmc_write_reg(addr, cmd[i]);  
  44.       addr -= 2;  
  45.     }  
  46.     if(b_resp == FALSE)  
  47.     {  
  48.         mmc_write_reg(MMC_IO,0x04);     //auto only command enable   
  49.     }  
  50.     else if ((cmd == cmd2) || (cmd == cmd9))  
  51.     {  
  52.         mmc_write_reg(MMC_IO,0x54);     //auto command + response,enable get CID front command buffer[135:8]   
  53.         //mmc_write_reg(MMC_IO,0x0c);    //auto only response enable   
  54.     }  
  55.     /*else if (cmd == cmd24) 
  56.     { 
  57.         mmc_write_reg(MMC_IO, 0x01);              //write data, trig transfer 
  58.     }*/  
  59.     else   
  60.     {  
  61.         mmc_write_reg(MMC_IO,0x44);     //auto command + response   
  62.     }  
  63.   
  64.     /*wait and clear sdmmc CMD done interrupt*/  
  65.     mmc_read_reg(MMC_INT_CLR,reg16_val);  
  66.     while(!(reg16_val & (1<<0)))  
  67.     {  
  68.         mmc_read_reg(MMC_INT_CLR,reg16_val);  
  69.         if ((reg16_val & (1<<6)) != 0)           //command or response timer out   
  70.         {  
  71.             mmc_write_reg(MMC_INT_CLR,0x40);    //clear interrupt;   
  72.             myptf("Send Cmd%d  Timerout!\r\n", cmd[0]-0x40);  
  73.             return 0;  
  74.         }  
  75.     }  
  76.     myptf("Interrupt Flag : %d\r\n", reg16_val);  
  77.     mmc_write_reg(MMC_INT_CLR,0x1);        //clear CMD done interrupt;   
  78.       
  79.     return 1;  
  80. }  
  81. SD_RESPONSE_INFO get_response_info(void//Only for 48 bit response   
  82. {  
  83.     SD_RESPONSE_INFO res;  
  84.     uint16 reg16_val;  
  85.     uint32 reg32_val,addr;  
  86.     U8 i;  
  87.       
  88.     addr = MMC_CMD_BUF3;  
  89.     mmc_read_reg(MMC_CMD_BUF4, reg16_val);  
  90.     myptf("res.cmd_index =%d\r\n", reg16_val);  
  91.     res.cmd_index = reg16_val;  
  92.   
  93.     for (i=0; i<4; i++)  
  94.     {     
  95.         reg32_val <<= 8;  
  96.         mmc_read_reg(addr, reg16_val);  
  97.         reg32_val |= reg16_val;  
  98.         addr -= 2;  
  99.     }  
  100.     res.card_status = reg32_val;  
  101.       
  102.     myptf("res.card_status =%ld\r\n", reg32_val);  
  103.   
  104.     mmc_read_reg(MMC_CRC_VAL, reg16_val);  
  105.     res.crc = reg16_val;  
  106.   
  107.     return res;  
  108.   
  109. }  
  110.   
  111. BOOL sd_mmc_identify()  
  112. {  
  113.     SD_RESPONSE_INFO resp;  
  114.     U16 reg16_val;  
  115.     U8 i;  
  116.   
  117.     sd_write_cmd(cmd0,FALSE);       //set the card into idle state   
  118.   
  119.     sd_write_cmd(cmd8,TRUE);        //verify the card operation condition;   
  120.     resp = get_response_info();       
  121.   
  122.     CardType = 0; //ver2.0   
  123.     if ( (resp.cmd_index != 0x08)  
  124.       || (resp.card_status != 0x15a) )  
  125.     {  
  126.         myptf("Erro Response for cmd8\r\n");  
  127.         CardType = 1;   
  128.     }  
  129.   
  130.     myptf("Ready to Tansmit ACMD!\r\n");  
  131.     do  
  132.     {  
  133. CMD55:                                    
  134.         sd_write_cmd(cmd55, TRUE);      //For Transmit ACMD;   
  135.         resp = get_response_info();  
  136.         if ((resp.card_status & (1L<<5)) != 0x20)  
  137.         {  
  138.             goto CMD55;  
  139.         }  
  140.   
  141.         if(CardType == 0)  
  142.             sd_write_cmd(acmd41_1, TRUE);  
  143.         else  
  144.             sd_write_cmd(acmd41_0, TRUE);  
  145.         resp = get_response_info();  
  146.         if ( (resp.cmd_index != 0x3f)   
  147.           || ((resp.card_status & 0x00ffffff) != 0xff8000) )  
  148.         {  
  149.             myptf("Unusable Card!\r\n");  
  150.             //return FALSE;   
  151.         }  
  152.     }while( (resp.card_status & (1L<<31)) == 0 ); //card is busy Wait for power up!    
  153.     myptf("SD Card Power On!\r\n");  
  154.     if ( !(resp.card_status & (1L<<30)) )  
  155.     {  
  156.         myptf("This is Standard Capacity SD Memery Card!\r\n");  
  157.     }  
  158.     else  
  159.     {  
  160.         myptf("This is High Capacity SD Memery Card!\r\n");  
  161.     }  
  162. CMD2:  
  163.     sd_write_cmd(cmd2,TRUE);    //it makes card identification state   
  164.     mmc_read_reg(MMC_CMD_BUF15, reg16_val);  
  165.     if (reg16_val != 0x3f)  
  166.     {  
  167.         goto CMD2;  
  168.     }  
  169.     myptf("Read CID...\r\n");  
  170. CMD3:  
  171.     sd_write_cmd(cmd3,TRUE);    //get the card's RCA and change to standby state;   
  172.     resp = get_response_info();  
  173.     if (resp.cmd_index != 0x03)  
  174.     {  
  175.         //myptf("Erro Response for cmd3!\r\n");   
  176.         goto CMD3;  
  177.     }  
  178.     card_addr = (U16)((resp.card_status & 0xffff0000) >> 16);  
  179.     card_state = (U16)(resp.card_status & 0x0000ffff);  
  180.     myptf("current card addr is %ld\r\n",card_addr);  
  181.     myptf("current card state is %ld\r\n",card_state);  
  182.       
  183.     return TRUE;  
  184. }  
  185. BOOL read_cards_capacity(U16 rca)  
  186. {  
  187.     U16 reg16_val;  
  188.     U32 c_size;  
  189.   
  190.     cmd9[1] = rca/256;  
  191.     cmd9[2] = rca%256;  
  192.     sd_write_cmd(cmd9, TRUE);  
  193.     mmc_read_reg(MMC_CMD_BUF15, reg16_val);  
  194.     if (reg16_val != 0x3f)  
  195.     {  
  196.         myptf("Read Capacity Failed!\r\n");  
  197.         return FALSE;  
  198.     }  
  199.       
  200.     mmc_read_reg(MMC_CMD_BUF7, reg16_val); //read c_zize value   
  201.     c_size = reg16_val & 0x3f;  
  202.     c_size <<= 8;  
  203.     mmc_read_reg(MMC_CMD_BUF6, reg16_val);  
  204.     c_size |= reg16_val;  
  205.     c_size <<= 8;  
  206.     mmc_read_reg(MMC_CMD_BUF5, reg16_val);  
  207.     c_size |= reg16_val;  
  208.   
  209.     card_capacity = ((c_size + 1) * 512) / 1024;  
  210.   
  211.     return TRUE;    
  212. }  
  213. void card_select(U16 rca, BOOL sel)  //change between standby and transfer state;   
  214. {  
  215.     SD_RESPONSE_INFO resp;  
  216.       
  217.     cmd7[1] = 0x00;     //deselect card;   
  218.     cmd7[2] = 0x00;  
  219.     if (sel == TRUE)    //Select card   
  220.     {  
  221.         cmd7[1] = rca/256;  
  222.         cmd7[2] = rca%256;  
  223.     }  
  224. CMD7:  
  225.     if (!(sd_write_cmd(cmd7, TRUE)))  
  226.     {  
  227.         goto CMD7;  
  228.     }  
  229.     //get_response_info();   
  230. }  
  231.   
  232. void set_bus_width(U8 wide)               // width = 0:1bit; width = 2: 4bit ;   
  233. {  
  234.   
  235.     acmd6[4] = wide;  
  236.     cmd55[1] = card_addr/256;  
  237.     cmd55[2] = card_addr%256;                 //note: cmd55's argument include rca;   
  238. SETBUS:  
  239.     sd_write_cmd(cmd55, TRUE);                //For Transmit ACMD;   
  240.     //get_response_info();   
  241.     if (!(sd_write_cmd(acmd6, TRUE)))  
  242.     goto SETBUS;  
  243.     get_response_info();      
  244.     return;  
  245. }  
  246.   
  247. void set_block_size()  
  248. {  
  249.     sd_write_cmd(cmd16, TRUE);  // set block length 512 bytes;   
  250.     get_response_info();  
  251. }  
  252.   
  253. void read_block(U32 addr, U8 block_cnt)  
  254. {  
  255.     U16 reg16_val;  
  256.     U32 reg32_val;  
  257.     U8 i,j;  
  258.     //SD_RESPONSE_INFO resp;   
  259.   
  260.     //addr <<= 9;             // addr*521   
  261.     mmc_write_reg(MMC_buf_ctl, 0x9000);       //active fifo status,flush fifo,disable dma,read sd-card,wm-128   
  262.       
  263.     if (block_cnt < 2)      //read single block   
  264.     {  
  265.         cmd17[1] = (U8)(addr>>24);  
  266.         cmd17[2] = (U8)((addr>>16) & 0xff);  
  267.         cmd17[3] = (U8)((addr>>8) & 0xff);  
  268.         cmd17[4] = (U8)(addr & 0xff);                 
  269. CMD17:  
  270.         if(!(sd_write_cmd(cmd17, TRUE)))  
  271.         {  
  272.             goto CMD17;  
  273.         }  
  274.         get_response_info();  
  275.         mmc_write_reg(MMC_BYTE_CNTH, 0x02);               //transfer 512 bytes   
  276.         mmc_write_reg(MMC_IO, 0x03);                      //read data, auto transfer      
  277.     }  
  278.     else                                                  //read multi blockss   
  279.     {  
  280.         cmd18[1] = (U8)(addr>>24);  
  281.         cmd18[2] = (U8)((addr>>16) & 0xff);  
  282.         cmd18[3] = (U8)((addr>>8) & 0xff);  
  283.         cmd18[4] = (U8)(addr & 0xff);  
  284. CMD18:          
  285.         if (!(sd_write_cmd(cmd18, TRUE)))  
  286.         {  
  287.             goto CMD18;  
  288.         }  
  289.         //get_response_info();   
  290.         //sys_delay_ms(20L);                               //delay befor trig transfer orelse erro   
  291.         mmc_write_reg(MMC_BLOCK_CNT, block_cnt);         // set read block number;   
  292.         //mmc_write_reg(MMC_IO, 0xc0);   
  293.         mmc_write_reg(MMC_IO_MBCTL, 0x53);               // trig data transfer;       
  294.     }  
  295.       
  296.     mmc_read_reg(MMC_buf_ctl, reg16_val);  
  297.     while( !( reg16_val & ( 1<<0 ) ) )                  //wait for fifo full;   
  298.     {  
  299.         mmc_read_reg(MMC_buf_ctl, reg16_val);  
  300.     }  
  301.     //myptf("FIFO Full\r\n");   
  302.     for(j=0; j<128*block_cnt; j++)  
  303.     {  
  304.         /*mmc_read_reg(MMC_buf_ctl, reg16_val); 
  305.         while( ( reg16_val & ( 1<<1 ) ) == 0x02 )              // fifo empty? 
  306.         { 
  307.             mmc_read_reg(MMC_buf_ctl, reg16_val); 
  308.             myptf("fifo empty!\nPlease Wait...\r\n"); 
  309.         }*/  
  310.         SysReadAMBAReg(MMC_DATA_BUF0, sd_rd_buf[j]);  
  311.         //for(i=0; i<50; i++);                           //delay for reading data else data erro appeared;   
  312.     }    
  313.     mmc_read_reg(MMC_INT_CLR,reg16_val);               //wait for data transfer finished   
  314.     while( (reg16_val & (1<<1)) != 0x02 )         
  315.     {  
  316.         mmc_read_reg(MMC_INT_CLR,reg16_val);  
  317.     }  
  318.     myptf("Read Data Finished!\r\n");  
  319.     mmc_write_reg(MMC_INT_CLR,0x02);                  //clear interrupt;   
  320.       
  321.     //mmc_read_reg(MMC_buf_ctl, reg16_val);   
  322.     //myptf("fifo full?:%d\r\n", (reg16_val&0x01));   
  323.     //myptf("fifo Empty?:%d\r\n", (reg16_val&0x02));   
  324.   
  325.     if (block_cnt > 1)  
  326.     {  
  327.         while((reg16_val & (1<<4)) != 0x10)  
  328.         {  
  329.             mmc_read_reg(MMC_INT_CLR,reg16_val);  
  330.         }  
  331.         mmc_write_reg(MMC_INT_CLR,0x10);            //clear interrupt flag;    
  332.         myptf("Read Muliti Block Finished!\r\n");  
  333. CMD12:  
  334.         if (!(sd_write_cmd(cmd12, TRUE)))  
  335.         {  
  336.             goto CMD12;  
  337.         }  
  338.     }  
  339. }  
  340.   
  341. void write_block(U32 addr, U8 block_cnt)        //block_cnt <65536   
  342. {  
  343.     U16 reg16_val;  
  344.     U8 i,j;  
  345.     //SD_RESPONSE_INFO resp;   
  346.   
  347.     //addr <<= 9;   
  348.     if (block_cnt < 2)                            //write single block   
  349.     {  
  350.         cmd24[1] = (U8)(addr>>24);  
  351.         cmd24[2] = (U8)((addr>>16) & 0xff);  
  352.         cmd24[3] = (U8)((addr>>8) & 0xff);  
  353.         cmd24[4] = (U8)(addr & 0xff);  
  354. CMD24:  
  355.         if(!(sd_write_cmd(cmd24, TRUE)))  
  356.         {  
  357.             goto CMD24;  
  358.         }  
  359.         //get_response_info();   
  360.         //sys_delay_ms(20L);   
  361.         mmc_write_reg(MMC_BYTE_CNTH, 0x02);       //transfer 512 bytes    
  362.         mmc_write_reg(MMC_buf_ctl, 0x9800);       //active fifo status,disable dma, write sd-card,   
  363.         mmc_write_reg(MMC_IO, 0x01);              //write data, trig transfer   
  364.     }  
  365.     else                                          //write multi block   
  366.     {  
  367.         cmd25[1] = (U8)(addr>>24);  
  368.         cmd25[2] = (U8)((addr>>16) & 0xff);  
  369.         cmd25[3] = (U8)((addr>>8) & 0xff);  
  370.         cmd25[4] = (U8)(addr & 0xff);  
  371.         cmd55[1] = card_addr / 256;  
  372.         cmd55[2] = card_addr % 256;  
  373.         acmd23[3] = block_cnt / 256;  
  374.         acmd23[4] = block_cnt % 256;  
  375. ACMD23:  
  376.         sd_write_cmd(cmd55, TRUE);  
  377.         if(!(sd_write_cmd(acmd23, TRUE)))  
  378.         {  
  379.             goto ACMD23;  
  380.         }  
  381. CMD25:    
  382.         if(!(sd_write_cmd(cmd25, TRUE)))  
  383.         {  
  384.             goto CMD25;  
  385.         }  
  386.         //get_response_info();   
  387.         //sys_delay_ms(20L);   
  388.         mmc_write_reg(MMC_BLOCK_CNT, block_cnt);   // set write block number;    
  389.         mmc_write_reg(MMC_buf_ctl, 0x9800);       //active fifo status,disable dma, write sd-card,   
  390.         mmc_write_reg(MMC_IO_MBCTL, 0x51);        // trig data transfer;     
  391.     }  
  392.     mmc_read_reg(MMC_buf_ctl, reg16_val);  
  393.     while( !( reg16_val & ( 1<<1 ) ) )           //wait for fifo empty;   
  394.     {  
  395.         mmc_read_reg(MMC_buf_ctl, reg16_val);  
  396.     }  
  397.     //myptf("FIFO Empty\r\n");   
  398.     for(j=0; j<128*block_cnt; j++)  
  399.     {             
  400.         SysWriteAMBAReg(MMC_DATA_BUF0, sd_rd_buf[j]);  
  401.         //for(i=0; i<220; i++);   
  402.         /*mmc_read_reg(MMC_buf_ctl, reg16_val); 
  403.         while((reg16_val & 0x01) == 0x01)              //fifo full? 
  404.         { 
  405.             myptf("fifo full!\nPlease Wait...\r\n"); 
  406.             mmc_read_reg(MMC_buf_ctl, reg16_val); 
  407.         }*/  
  408.     }  
  409.     mmc_read_reg(MMC_INT_CLR,reg16_val);         //wait for data transfer finished    
  410.     while((reg16_val & (1<<1)) != 0x02 )          
  411.     {   
  412.         mmc_read_reg(MMC_INT_CLR,reg16_val);  
  413.     }  
  414.     myptf("Interrupt Flag : %d\r\n", reg16_val);  
  415.     mmc_write_reg(MMC_INT_CLR,0x02);             //clear data done interrupt;   
  416.     myptf("Write Data finished!\r\n");  
  417.     if ( block_cnt > 1 )  
  418.     {  
  419.         while((reg16_val & (1<<4)) != 0x10)     //wait multi block done   
  420.         {  
  421.             mmc_read_reg(MMC_INT_CLR,reg16_val);  
  422.         }  
  423.         mmc_write_reg(MMC_INT_CLR,0x10);        //clear multi block done interrupt;   
  424.         myptf("Write multi block finished!\r\n");  
  425. CMD12_1:  
  426.         if (!(sd_write_cmd(cmd12, TRUE)))       //End Data transfer;   
  427.         {  
  428.             goto CMD12_1;  
  429.         }  
  430.         get_response_info();  
  431.     }         
  432. }  
  433.   
  434. void sd_mmc_initial()  
  435. {  
  436.     sd_mmc_setup();  
  437.     if (sd_mmc_identify() == FALSE)  
  438.     {  
  439.         myptf("sd card initial failed!\r\n");  
  440.     }  
  441.     else  
  442.     {            
  443.         myptf("sd card initial succesful!\r\n");  
  444.           
  445.         if ( read_cards_capacity(card_addr) == TRUE )  
  446.         {  
  447.             myptf("current card capacity is: %ldM\r\n", card_capacity);  
  448.         }  
  449.         card_select(card_addr, TRUE);  
  450.         set_bus_width(BUS_4_BIT);             // set transfer data bus;   
  451.         set_block_size();  
  452.         mmc_write_reg(MMC_CTL,0xc3);          // 4bit data,high speed(30M),1/2 divider,auto transfer, mmc mode.   
  453.         mmc_write_reg(MMC_IO_MBCTL, 0x50);    // timer out scal    
  454.     }     
  455. }  
  456.   
  457. </SPAN>  
void sd_mmc_setup()
{
uint16 reg16_val;
uint32 reg32_val;
U8 i;
SysReadAMBAReg((uint32)SYS_PWRPEN_REG,reg32_val);
reg32_val |= (1L<<2);
SysWriteAMBAReg((uint32)SYS_PWRPEN_REG,reg32_val);      //开启SD/MMC控制器
sys_delay_ms(300L);
mmc_write_reg(MMC_CAR_SEL, 0xdd);        // enable module, enable mmcclk 
mmc_write_reg(MMC_CRC_CTL, 0xd0);        // CRC circuit enable
mmc_write_reg(MMC_CTL, 0x0b);            // 1bit,low speed(256KHZ),1/4 divider,auto transfer, mmc mode.
//mmc_write_reg(MMC_INT_MASK, 0x7f);       // unmask all interrupt 
for (i = 0; i<10; i++)                   // at least 74 clock for setup
{
mmc_write_reg(MMC_IO,0x24);          // only 8 null clock generation
mmc_read_reg(MMC_INT_CLR,reg16_val);
while(reg16_val != 0x01)
{
mmc_read_reg(MMC_INT_CLR,reg16_val);
}
mmc_write_reg(MMC_INT_CLR,reg16_val);
}
myptf("sd_mmc_setup finished!\r\n");
return;
}
U8 sd_write_cmd(U8 *cmd, BOOL b_resp)
{
U8 i;
uint16 reg16_val;
uint32 addr = MMC_CMD_BUF4;
for(i=0;i<5;i++)
{
mmc_write_reg(addr, cmd[i]);
addr -= 2;
}
if(b_resp == FALSE)
{
mmc_write_reg(MMC_IO,0x04);     //auto only command enable
}
else if ((cmd == cmd2) || (cmd == cmd9))
{
mmc_write_reg(MMC_IO,0x54);     //auto command + response,enable get CID front command buffer[135:8]
//mmc_write_reg(MMC_IO,0x0c);    //auto only response enable
}
/*else if (cmd == cmd24)
{
mmc_write_reg(MMC_IO, 0x01);              //write data, trig transfer
}*/
else 
{
mmc_write_reg(MMC_IO,0x44);     //auto command + response
}
/*wait and clear sdmmc CMD done interrupt*/
mmc_read_reg(MMC_INT_CLR,reg16_val);
while(!(reg16_val & (1<<0)))
{
mmc_read_reg(MMC_INT_CLR,reg16_val);
if ((reg16_val & (1<<6)) != 0)           //command or response timer out
{
mmc_write_reg(MMC_INT_CLR,0x40);    //clear interrupt;
myptf("Send Cmd%d  Timerout!\r\n", cmd[0]-0x40);
return 0;
}
}
myptf("Interrupt Flag : %d\r\n", reg16_val);
mmc_write_reg(MMC_INT_CLR,0x1);        //clear CMD done interrupt;
return 1;
}
SD_RESPONSE_INFO get_response_info(void) //Only for 48 bit response
{
SD_RESPONSE_INFO res;
uint16 reg16_val;
uint32 reg32_val,addr;
U8 i;
addr = MMC_CMD_BUF3;
mmc_read_reg(MMC_CMD_BUF4, reg16_val);
myptf("res.cmd_index =%d\r\n", reg16_val);
res.cmd_index = reg16_val;
for (i=0; i<4; i++)
{   
reg32_val <<= 8;
mmc_read_reg(addr, reg16_val);
reg32_val |= reg16_val;
addr -= 2;
}
res.card_status = reg32_val;
myptf("res.card_status =%ld\r\n", reg32_val);
mmc_read_reg(MMC_CRC_VAL, reg16_val);
res.crc = reg16_val;
return res;
}
BOOL sd_mmc_identify()
{
SD_RESPONSE_INFO resp;
U16 reg16_val;
U8 i;
sd_write_cmd(cmd0,FALSE);       //set the card into idle state
sd_write_cmd(cmd8,TRUE);        //verify the card operation condition;
resp = get_response_info();     
CardType = 0; //ver2.0
if ( (resp.cmd_index != 0x08)
|| (resp.card_status != 0x15a) )
{
myptf("Erro Response for cmd8\r\n");
CardType = 1; 
}
myptf("Ready to Tansmit ACMD!\r\n");
do
{
CMD55:                                  
sd_write_cmd(cmd55, TRUE);      //For Transmit ACMD;
resp = get_response_info();
if ((resp.card_status & (1L<<5)) != 0x20)
{
goto CMD55;
}
if(CardType == 0)
sd_write_cmd(acmd41_1, TRUE);
else
sd_write_cmd(acmd41_0, TRUE);
resp = get_response_info();
if ( (resp.cmd_index != 0x3f) 
|| ((resp.card_status & 0x00ffffff) != 0xff8000) )
{
myptf("Unusable Card!\r\n");
//return FALSE;
}
}while( (resp.card_status & (1L<<31)) == 0 ); //card is busy Wait for power up! 
myptf("SD Card Power On!\r\n");
if ( !(resp.card_status & (1L<<30)) )
{
myptf("This is Standard Capacity SD Memery Card!\r\n");
}
else
{
myptf("This is High Capacity SD Memery Card!\r\n");
}
CMD2:
sd_write_cmd(cmd2,TRUE);    //it makes card identification state
mmc_read_reg(MMC_CMD_BUF15, reg16_val);
if (reg16_val != 0x3f)
{
goto CMD2;
}
myptf("Read CID...\r\n");
CMD3:
sd_write_cmd(cmd3,TRUE);    //get the card's RCA and change to standby state;
resp = get_response_info();
if (resp.cmd_index != 0x03)
{
//myptf("Erro Response for cmd3!\r\n");
goto CMD3;
}
card_addr = (U16)((resp.card_status & 0xffff0000) >> 16);
card_state = (U16)(resp.card_status & 0x0000ffff);
myptf("current card addr is %ld\r\n",card_addr);
myptf("current card state is %ld\r\n",card_state);
return TRUE;
}
BOOL read_cards_capacity(U16 rca)
{
U16 reg16_val;
U32 c_size;
cmd9[1] = rca/256;
cmd9[2] = rca%256;
sd_write_cmd(cmd9, TRUE);
mmc_read_reg(MMC_CMD_BUF15, reg16_val);
if (reg16_val != 0x3f)
{
myptf("Read Capacity Failed!\r\n");
return FALSE;
}
mmc_read_reg(MMC_CMD_BUF7, reg16_val); //read c_zize value
c_size = reg16_val & 0x3f;
c_size <<= 8;
mmc_read_reg(MMC_CMD_BUF6, reg16_val);
c_size |= reg16_val;
c_size <<= 8;
mmc_read_reg(MMC_CMD_BUF5, reg16_val);
c_size |= reg16_val;
card_capacity = ((c_size + 1) * 512) / 1024;
return TRUE;  
}
void card_select(U16 rca, BOOL sel)  //change between standby and transfer state;
{
SD_RESPONSE_INFO resp;
cmd7[1] = 0x00;     //deselect card;
cmd7[2] = 0x00;
if (sel == TRUE)    //Select card
{
cmd7[1] = rca/256;
cmd7[2] = rca%256;
}
CMD7:
if (!(sd_write_cmd(cmd7, TRUE)))
{
goto CMD7;
}
//get_response_info();
}
void set_bus_width(U8 wide)               // width = 0:1bit; width = 2: 4bit ;
{
acmd6[4] = wide;
cmd55[1] = card_addr/256;
cmd55[2] = card_addr%256;                 //note: cmd55's argument include rca;
SETBUS:
sd_write_cmd(cmd55, TRUE);                //For Transmit ACMD;
//get_response_info();
if (!(sd_write_cmd(acmd6, TRUE)))
goto SETBUS;
get_response_info();    
return;
}
void set_block_size()
{
sd_write_cmd(cmd16, TRUE);  // set block length 512 bytes;
get_response_info();
}
void read_block(U32 addr, U8 block_cnt)
{
U16 reg16_val;
U32 reg32_val;
U8 i,j;
//SD_RESPONSE_INFO resp;
//addr <<= 9;             // addr*521
mmc_write_reg(MMC_buf_ctl, 0x9000);       //active fifo status,flush fifo,disable dma,read sd-card,wm-128
if (block_cnt < 2)      //read single block
{
cmd17[1] = (U8)(addr>>24);
cmd17[2] = (U8)((addr>>16) & 0xff);
cmd17[3] = (U8)((addr>>8) & 0xff);
cmd17[4] = (U8)(addr & 0xff);               
CMD17:
if(!(sd_write_cmd(cmd17, TRUE)))
{
goto CMD17;
}
get_response_info();
mmc_write_reg(MMC_BYTE_CNTH, 0x02);               //transfer 512 bytes
mmc_write_reg(MMC_IO, 0x03);                      //read data, auto transfer   
}
else                                                  //read multi blockss
{
cmd18[1] = (U8)(addr>>24);
cmd18[2] = (U8)((addr>>16) & 0xff);
cmd18[3] = (U8)((addr>>8) & 0xff);
cmd18[4] = (U8)(addr & 0xff);
CMD18:        
if (!(sd_write_cmd(cmd18, TRUE)))
{
goto CMD18;
}
//get_response_info();
//sys_delay_ms(20L);                               //delay befor trig transfer orelse erro
mmc_write_reg(MMC_BLOCK_CNT, block_cnt);         // set read block number;
//mmc_write_reg(MMC_IO, 0xc0);
mmc_write_reg(MMC_IO_MBCTL, 0x53);               // trig data transfer;    
}
mmc_read_reg(MMC_buf_ctl, reg16_val);
while( !( reg16_val & ( 1<<0 ) ) )                  //wait for fifo full;
{
mmc_read_reg(MMC_buf_ctl, reg16_val);
}
//myptf("FIFO Full\r\n");
for(j=0; j<128*block_cnt; j++)
{
/*mmc_read_reg(MMC_buf_ctl, reg16_val);
while( ( reg16_val & ( 1<<1 ) ) == 0x02 )              // fifo empty?
{
mmc_read_reg(MMC_buf_ctl, reg16_val);
myptf("fifo empty!\nPlease Wait...\r\n");
}*/
SysReadAMBAReg(MMC_DATA_BUF0, sd_rd_buf[j]);
//for(i=0; i<50; i++);                           //delay for reading data else data erro appeared;
}  
mmc_read_reg(MMC_INT_CLR,reg16_val);               //wait for data transfer finished
while( (reg16_val & (1<<1)) != 0x02 )       
{
mmc_read_reg(MMC_INT_CLR,reg16_val);
}
myptf("Read Data Finished!\r\n");
mmc_write_reg(MMC_INT_CLR,0x02);                  //clear interrupt;
//mmc_read_reg(MMC_buf_ctl, reg16_val);
//myptf("fifo full?:%d\r\n", (reg16_val&0x01));
//myptf("fifo Empty?:%d\r\n", (reg16_val&0x02));
if (block_cnt > 1)
{
while((reg16_val & (1<<4)) != 0x10)
{
mmc_read_reg(MMC_INT_CLR,reg16_val);
}
mmc_write_reg(MMC_INT_CLR,0x10);            //clear interrupt flag; 
myptf("Read Muliti Block Finished!\r\n");
CMD12:
if (!(sd_write_cmd(cmd12, TRUE)))
{
goto CMD12;
}
}
}
void write_block(U32 addr, U8 block_cnt)        //block_cnt <65536
{
U16 reg16_val;
U8 i,j;
//SD_RESPONSE_INFO resp;
//addr <<= 9;
if (block_cnt < 2)                            //write single block
{
cmd24[1] = (U8)(addr>>24);
cmd24[2] = (U8)((addr>>16) & 0xff);
cmd24[3] = (U8)((addr>>8) & 0xff);
cmd24[4] = (U8)(addr & 0xff);
CMD24:
if(!(sd_write_cmd(cmd24, TRUE)))
{
goto CMD24;
}
//get_response_info();
//sys_delay_ms(20L);
mmc_write_reg(MMC_BYTE_CNTH, 0x02);       //transfer 512 bytes 
mmc_write_reg(MMC_buf_ctl, 0x9800);       //active fifo status,disable dma, write sd-card,
mmc_write_reg(MMC_IO, 0x01);              //write data, trig transfer
}
else                                          //write multi block
{
cmd25[1] = (U8)(addr>>24);
cmd25[2] = (U8)((addr>>16) & 0xff);
cmd25[3] = (U8)((addr>>8) & 0xff);
cmd25[4] = (U8)(addr & 0xff);
cmd55[1] = card_addr / 256;
cmd55[2] = card_addr % 256;
acmd23[3] = block_cnt / 256;
acmd23[4] = block_cnt % 256;
ACMD23:
sd_write_cmd(cmd55, TRUE);
if(!(sd_write_cmd(acmd23, TRUE)))
{
goto ACMD23;
}
CMD25:  
if(!(sd_write_cmd(cmd25, TRUE)))
{
goto CMD25;
}
//get_response_info();
//sys_delay_ms(20L);
mmc_write_reg(MMC_BLOCK_CNT, block_cnt);   // set write block number; 
mmc_write_reg(MMC_buf_ctl, 0x9800);       //active fifo status,disable dma, write sd-card,
mmc_write_reg(MMC_IO_MBCTL, 0x51);        // trig data transfer;  
}
mmc_read_reg(MMC_buf_ctl, reg16_val);
while( !( reg16_val & ( 1<<1 ) ) )           //wait for fifo empty;
{
mmc_read_reg(MMC_buf_ctl, reg16_val);
}
//myptf("FIFO Empty\r\n");
for(j=0; j<128*block_cnt; j++)
{           
SysWriteAMBAReg(MMC_DATA_BUF0, sd_rd_buf[j]);
//for(i=0; i<220; i++);
/*mmc_read_reg(MMC_buf_ctl, reg16_val);
while((reg16_val & 0x01) == 0x01)              //fifo full?
{
myptf("fifo full!\nPlease Wait...\r\n");
mmc_read_reg(MMC_buf_ctl, reg16_val);
}*/
}
mmc_read_reg(MMC_INT_CLR,reg16_val);         //wait for data transfer finished 
while((reg16_val & (1<<1)) != 0x02 )        
{ 
mmc_read_reg(MMC_INT_CLR,reg16_val);
}
myptf("Interrupt Flag : %d\r\n", reg16_val);
mmc_write_reg(MMC_INT_CLR,0x02);             //clear data done interrupt;
myptf("Write Data finished!\r\n");
if ( block_cnt > 1 )
{
while((reg16_val & (1<<4)) != 0x10)     //wait multi block done
{
mmc_read_reg(MMC_INT_CLR,reg16_val);
}
mmc_write_reg(MMC_INT_CLR,0x10);        //clear multi block done interrupt;
myptf("Write multi block finished!\r\n");
CMD12_1:
if (!(sd_write_cmd(cmd12, TRUE)))       //End Data transfer;
{
goto CMD12_1;
}
get_response_info();
}       
}
void sd_mmc_initial()
{
sd_mmc_setup();
if (sd_mmc_identify() == FALSE)
{
myptf("sd card initial failed!\r\n");
}
else
{          
myptf("sd card initial succesful!\r\n");
if ( read_cards_capacity(card_addr) == TRUE )
{
myptf("current card capacity is: %ldM\r\n", card_capacity);
}
card_select(card_addr, TRUE);
set_bus_width(BUS_4_BIT);             // set transfer data bus;
set_block_size();
mmc_write_reg(MMC_CTL,0xc3);          // 4bit data,high speed(30M),1/2 divider,auto transfer, mmc mode.
mmc_write_reg(MMC_IO_MBCTL, 0x50);    // timer out scal 
}   
}

/*测试代码*/

[cpp] view plain copy print ?
  1. U8 cmd0[5]={0x40,0x00,0x00,0x00,0x00};  
  2. U8 cmd2[5]={0x42,0x00,0x00,0x00,0x00};  
  3. U8 cmd3[5]={0x43,0x00,0x00,0x00,0x00};  
  4. U8 cmd7[5]={0x47,0x00,0x00,0x00,0x00};  
  5. U8 cmd8[5]={0x48,0x00,0x00,0x01,0x5a};     // 2.7-3.6V; use‘10101010b’for the‘check pattern’   
  6. U8 cmd9[5]={0x49,0x00,0x00,0x00,0x00};  
  7. U8 cmd12[5]={0x4c,0x00,0x00,0x00,0x00};    // stop transfer command;   
  8. U8 cmd16[5]={0x50,0x00,0x00,0x02,0x00};    // set block length 512 bytes;   
  9. U8 cmd17[5]={0x51,0x00,0x00,0x00,0x00};    // read single block;   
  10. U8 cmd18[5]={0x52,0x00,0x00,0x00,0x00};    // read multi block;    
  11. U8 cmd24[5]={0x58,0x00,0x00,0x00,0x00};    // write single block;   
  12. U8 cmd25[5]={0x59,0x00,0x00,0x00,0x00};    // write single block;   
  13. U8 cmd55[5]={0x77,0x00,0x00,0x00,0x00};  
  14. U8 acmd6[5]={0x46,0x00,0x00,0x00,0x00};    // set transfer data bus   
  15. U8 acmd23[5]={0x57,0x00,0xff,0x80,0x00};  
  16. U8 acmd41_1[5]={0x69,0x40,0xff,0x80,0x00}; // HCS =1; voltage range 2.7-3.6V;   
  17. U8 acmd41_0[5]={0x69,0x00,0xff,0x80,0x00}; // HCS =0; voltage range 2.7-3.6V;   
  18.      
  19. typedef struct  
  20. {  
  21.     U8 cmd_index;  
  22.     uint32 card_status;  
  23.     U8 crc;  
  24. }SD_RESPONSE_INFO;  
  25.   
  26. uint16 card_addr;  
  27. uint16 card_state;  
  28. U8 CardType;  
  29. U32 card_capacity;  
  30. U32 sd_rd_buf[1024]={0};  
  31. U32 sd_write_buf[512]={0};  
U8 cmd0[5]={0x40,0x00,0x00,0x00,0x00};
U8 cmd2[5]={0x42,0x00,0x00,0x00,0x00};
U8 cmd3[5]={0x43,0x00,0x00,0x00,0x00};
U8 cmd7[5]={0x47,0x00,0x00,0x00,0x00};
U8 cmd8[5]={0x48,0x00,0x00,0x01,0x5a};     // 2.7-3.6V; use‘10101010b’for the‘check pattern’
U8 cmd9[5]={0x49,0x00,0x00,0x00,0x00};
U8 cmd12[5]={0x4c,0x00,0x00,0x00,0x00};    // stop transfer command;
U8 cmd16[5]={0x50,0x00,0x00,0x02,0x00};    // set block length 512 bytes;
U8 cmd17[5]={0x51,0x00,0x00,0x00,0x00};    // read single block;
U8 cmd18[5]={0x52,0x00,0x00,0x00,0x00};    // read multi block; 
U8 cmd24[5]={0x58,0x00,0x00,0x00,0x00};    // write single block;
U8 cmd25[5]={0x59,0x00,0x00,0x00,0x00};    // write single block;
U8 cmd55[5]={0x77,0x00,0x00,0x00,0x00};
U8 acmd6[5]={0x46,0x00,0x00,0x00,0x00};    // set transfer data bus
U8 acmd23[5]={0x57,0x00,0xff,0x80,0x00};
U8 acmd41_1[5]={0x69,0x40,0xff,0x80,0x00}; // HCS =1; voltage range 2.7-3.6V;
U8 acmd41_0[5]={0x69,0x00,0xff,0x80,0x00}; // HCS =0; voltage range 2.7-3.6V;
typedef struct
{
U8 cmd_index;
uint32 card_status;
U8 crc;
}SD_RESPONSE_INFO;
uint16 card_addr;
uint16 card_state;
U8 CardType;
U32 card_capacity;
U32 sd_rd_buf[1024]={0};
U32 sd_write_buf[512]={0};


[cpp] view plain copy print ?
  1.    
 
[cpp] view plain copy print ?
  1. void main()  
void main()
[cpp] view plain copy print ?
  1. {  
{
[cpp] view plain copy print ?
  1. sd_mmc_initial();  
  2. while(1)  
  3. {  
  4.     sys_wdtrestart();  
  5.     read_block(153600, 8);           
  6.     //read_block(153601, 3);           //31760   
  7.     //read_block(300, 3);             //30760*512   
  8.     //read_block(30, 1);           //30*512   
  9.     for(j=0; j<128; j++)  
  10.     {            
  11.         myptf("sd_rd_buf[%d] = %ld\r\n", j, sd_rd_buf[j]);  
  12.     }  
  13.     write_block(153605, 1);  
  14.     write_block(153606, 8);  
  15.     //write_block(153609, 3);   
  16.     //read_block(153604, 3);   
  17.     /*for(j=0; j<384; j++) 
  18.     {           
  19.         myptf("sd_rd_buf[%d] = %ld\r\n", j, sd_rd_buf[j]); 
  20.     }*/  
  21.     //while(1);   
  22. }  
    sd_mmc_initial();
while(1)
{
sys_wdtrestart();
read_block(153600, 8);         
//read_block(153601, 3);           //31760
//read_block(300, 3);             //30760*512
//read_block(30, 1);           //30*512
for(j=0; j<128; j++)
{          
myptf("sd_rd_buf[%d] = %ld\r\n", j, sd_rd_buf[j]);
}
write_block(153605, 1);
write_block(153606, 8);
//write_block(153609, 3);
//read_block(153604, 3);
/*for(j=0; j<384; j++)
{          
myptf("sd_rd_buf[%d] = %ld\r\n", j, sd_rd_buf[j]);
}*/
//while(1);
}
[cpp] view plain copy print ?
  1. }  
}

 就分析到这里,有分析不对的地方请博友们指正,愿意同大家交流。

http://blog.csdn.net/rxllh/article/details/8100698

这篇关于SD Card 驱动流程分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/877173

相关文章

[职场] 公务员的利弊分析 #知识分享#经验分享#其他

公务员的利弊分析     公务员作为一种稳定的职业选择,一直备受人们的关注。然而,就像任何其他职业一样,公务员职位也有其利与弊。本文将对公务员的利弊进行分析,帮助读者更好地了解这一职业的特点。 利: 1. 稳定的职业:公务员职位通常具有较高的稳定性,一旦进入公务员队伍,往往可以享受到稳定的工作环境和薪资待遇。这对于那些追求稳定的人来说,是一个很大的优势。 2. 薪资福利优厚:公务员的薪资和

工作流Activiti初体验—流程撤回【二】

已经玩工作流了,打算还是研究一下撤回的功能。但是流程图里面并不带撤回的组件,所以需要自己动态改造一下,还是延续上一个流程继续试验撤回功能。《工作流Activiti初体验【一】》 完整流程图 我们研究一下分发任务撤回到发起任务,其他环节的撤回类似 撤回的原理大概如下: 将分发任务后面的方向清空,把发起任务拼接到原来的判断网关,然后结束分发任务,这样流程就到发起任务了 此时的流程如上图,

ROS话题通信流程自定义数据格式

ROS话题通信流程自定义数据格式 需求流程实现步骤定义msg文件编辑配置文件编译 在 ROS 通信协议中,数据载体是一个较为重要组成部分,ROS 中通过 std_msgs 封装了一些原生的数据类型,比如:String、Int32、Int64、Char、Bool、Empty… 但是,这些数据一般只包含一个 data 字段,结构的单一意味着功能上的局限性,当传输一些复杂的数据,比如:

高度内卷下,企业如何通过VOC(客户之声)做好竞争分析?

VOC,即客户之声,是一种通过收集和分析客户反馈、需求和期望,来洞察市场趋势和竞争对手动态的方法。在高度内卷的市场环境下,VOC不仅能够帮助企业了解客户的真实需求,还能为企业提供宝贵的竞争情报,助力企业在竞争中占据有利地位。 那么,企业该如何通过VOC(客户之声)做好竞争分析呢?深圳天行健企业管理咨询公司解析如下: 首先,要建立完善的VOC收集机制。这包括通过线上渠道(如社交媒体、官网留言

WDF驱动开发-WDF总线枚举(一)

支持在总线驱动程序中进行 PnP 和电源管理 某些设备永久插入系统,而其他设备可以在系统运行时插入和拔出电源。 总线驱动 必须识别并报告连接到其总线的设备,并且他们必须发现并报告系统中设备的到达和离开情况。 总线驱动程序标识和报告的设备称为总线的 子设备。 标识和报告子设备的过程称为 总线枚举。 在总线枚举期间,总线驱动程序会为其子 设备创建设备对象 。  总线驱动程序本质上是同时处理总线枚

打包体积分析和优化

webpack分析工具:webpack-bundle-analyzer 1. 通过<script src="./vue.js"></script>方式引入vue、vuex、vue-router等包(CDN) // webpack.config.jsif(process.env.NODE_ENV==='production') {module.exports = {devtool: 'none

完整的申请邓白氏编码的流程(手把手教你申请邓白氏编码

完整的申请邓白氏编码的流程(手把手教你申请邓白氏编码)  标签: 编码邓白氏编码申请流程苹果开发者账号申请 2016-07-08 16:13  2274人阅读  评论(2)  收藏  举报   分类: 技术  苹果开发  邓白氏编码申请 版权声明:本文为博主原创文章,未经博主允许不得转载。     申请公司的苹果开发者账号和企业级的苹

办理河南建筑工程乙级设计资质的流程与要点

办理河南建筑工程乙级设计资质的流程与要点 办理河南建筑工程乙级设计资质的流程与要点主要包括以下几个方面: 流程: 工商注册与资质规划:确保企业具有独立法人资格,完成工商注册,并明确乙级设计资质的具体要求,包括注册资本、人员配置、技术条件等。 专业技术人员配置: 雇佣或签约符合资质要求的专业技术人员,包括但不限于:一级注册结构工程师2名、一级注册建筑师2名、注册暖通工程师1名、注册供配电工

Java中的大数据处理与分析架构

Java中的大数据处理与分析架构 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们来讨论Java中的大数据处理与分析架构。随着大数据时代的到来,海量数据的存储、处理和分析变得至关重要。Java作为一门广泛使用的编程语言,在大数据领域有着广泛的应用。本文将介绍Java在大数据处理和分析中的关键技术和架构设计。 大数据处理与

段,页,段页,三种内存(RAM)管理机制分析

段,页,段页         是为实现虚拟内存而产生的技术。直接使用物理内存弊端:地址空间不隔离,内存使用效率低。 段 段:就是按照二进制文件的格式,在内存给进程分段(包括堆栈、数据段、代码段)。通过段寄存器中的段表来进行虚拟地址和物理地址的转换。 段实现的虚拟地址 = 段号+offset 物理地址:被分为很多个有编号的段,每个进程的虚拟地址都有段号,这样可以实现虚实地址之间的转换。其实所谓的地