1 /******************************************************************************************************************************************
2 * 文件名称: SWM341_sdio.c
3 * 功能说明: SWM341单片机的SDIO接口驱动库
4 * 技术支持: http://www.synwit.com.cn/e/tool/gbook/?bid=1
5 * 注意事项: 为了通用性、兼容性、易用性,只支持以512字节为单位的读写
6 * 版本日期: V1.1.0      2017年10月25日
7 * 升级记录:
8 *
9 *
10 *******************************************************************************************************************************************
11 * @attention
12 *
13 * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS WITH CODING INFORMATION
14 * REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME. AS A RESULT, SYNWIT SHALL NOT BE HELD LIABLE
15 * FOR ANY DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE CONTENT
16 * OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING INFORMATION CONTAINED HEREIN IN CONN-
17 * -ECTION WITH THEIR PRODUCTS.
18 *
19 * COPYRIGHT 2012 Synwit Technology
20 *******************************************************************************************************************************************/
21 #include "SWM341.h"
22 #include "SWM341_sdio.h"
23 
24 
25 SD_CardInfo SD_cardInfo;
26 
27 /******************************************************************************************************************************************
28 * 函数名称: SDIO_Init()
29 * 功能说明: SDIO读写SD卡初始化,初始化成高速4线模式、读写以512字节大小进行
30 * 输    入: uint32_t freq         SDIO_CLK时钟频率
31 * 输    出: uint32_t              SD_RES_OK 操作成功    SD_RES_ERR 操作失败    SD_RES_TIMEOUT 操作超时
32 * 注意事项: 无
33 ******************************************************************************************************************************************/
SDIO_Init(uint32_t freq)34 uint32_t SDIO_Init(uint32_t freq)
35 {
36     uint32_t res;
37     uint32_t resp, resps[4];
38 
39     SYS->CLKSEL &= ~SYS_CLKSEL_SDIO_Msk;
40     if(SystemCoreClock > 80000000)      //SDIO时钟需要小于52MHz
41         SYS->CLKSEL |= (2 << SYS_CLKSEL_SDIO_Pos);  //SDCLK = SYSCLK / 4
42     else
43         SYS->CLKSEL |= (0 << SYS_CLKSEL_SDIO_Pos);  //SDCLK = SYSCLK / 2
44 
45     SYS->CLKEN0 |= (0x01 << SYS_CLKEN0_SDIO_Pos);
46 
47     SDIO->CR2 = (1 << SDIO_CR2_RSTALL_Pos);
48 
49     SDIO->CR1 = (1 << SDIO_CR1_CDSRC_Pos) |
50                 (0 << SDIO_CR1_8BIT_Pos)  |
51                 (0 << SDIO_CR1_4BIT_Pos)  |
52                 (1 << SDIO_CR1_PWRON_Pos) |
53                 (7 << SDIO_CR1_VOLT_Pos);
54 
55     SDIO->CR2 = (1 << SDIO_CR2_CLKEN_Pos) |
56                 (1 << SDIO_CR2_SDCLKEN_Pos) |
57                 (calcSDCLKDiv(100000) << SDIO_CR2_SDCLKDIV_Pos) |
58                 (0xC << SDIO_CR2_TIMEOUT_Pos);      // 2**25 SDIO_CLK
59 
60     while((SDIO->CR2 & SDIO_CR2_CLKRDY_Msk) == 0);
61 
62     for(int i = 0; i < CyclesPerUs * 10; i++) __NOP();
63 
64     SDIO->IM = 0xFFFFFFFF;
65 
66 
67     SDIO_SendCmd(SD_CMD_GO_IDLE_STATE, 0x00, SD_RESP_NO, 0);                //CMD0: GO_IDLE_STATE
68 
69     res = SDIO_SendCmd(SD_CMD_SEND_IF_COND, 0x1AA, SD_RESP_32b, &resp);     //CMD8: SEND_IF_COND, 检测工作电压、检测是否支持SD 2.0
70     if(res != SD_RES_OK)
71         return res;
72 
73     if(resp == 0x1AA) SD_cardInfo.CardType = SDIO_STD_CAPACITY_SD_CARD_V2_0;
74     else              SD_cardInfo.CardType = SDIO_STD_CAPACITY_SD_CARD_V1_1;
75 
76     do                                                                      //ACMD41: SD_CMD_SD_APP_OP_COND
77     {
78         res = SDIO_SendCmd(SD_CMD_APP_CMD, 0x00, SD_RESP_32b, &resp);
79         if(res != SD_RES_OK)
80             return res;
81 
82         if((resp & SD_CS_APP_CMD) == 0) return SD_RES_ERR;
83 
84         if(SD_cardInfo.CardType == SDIO_STD_CAPACITY_SD_CARD_V2_0)
85             SDIO_SendCmd(SD_CMD_SD_APP_OP_COND, 0x80100000|0x40000000, SD_RESP_32b, &resp);
86         else
87             SDIO_SendCmd(SD_CMD_SD_APP_OP_COND, 0x80100000|0x00000000, SD_RESP_32b, &resp);
88     } while(((resp >> 31) & 0x01) == 0);        //上电没完成时resp[31] == 0
89 
90     if(((resp >> 30) & 0x01) == 1) SD_cardInfo.CardType = SDIO_HIGH_CAPACITY_SD_CARD;
91 
92 
93     SDIO_SendCmd(SD_CMD_ALL_SEND_CID, 0x00, SD_RESP_128b, resps);           //CMD2: SD_CMD_ALL_SEND_CID,获取CID
94 
95     parseCID(resps);
96 
97 
98     SDIO_SendCmd(SD_CMD_SET_REL_ADDR, 0x00, SD_RESP_32b, &resp);            //CMD3: SD_CMD_SET_REL_ADDR,设置RCA
99 
100     SD_cardInfo.RCA = resp >> 16;
101 
102 
103     SDIO_SendCmd(SD_CMD_SEND_CSD, SD_cardInfo.RCA << 16, SD_RESP_128b, resps);  //CMD9: SD_CMD_SEND_CSD,获取CSD
104 
105     parseCSD(resps);
106 
107     if(SD_cardInfo.CardBlockSize < 0x200) return SD_RES_ERR;    //本驱动只支持以512字节为单位的读写,所以最大读写单位必须不小于512
108 
109 
110     SDIO->CR2 &= ~(SDIO_CR2_SDCLKEN_Msk | SDIO_CR2_SDCLKDIV_Msk);
111     SDIO->CR2 |= (1 << SDIO_CR2_SDCLKEN_Pos) |
112                  (calcSDCLKDiv(freq) << SDIO_CR2_SDCLKDIV_Pos); //初始化完成,SDCLK切换到高速
113 
114 
115     SDIO_SendCmd(SD_CMD_SEL_DESEL_CARD, SD_cardInfo.RCA << 16, SD_RESP_32b_busy, &resp);    //CMD7: 选中卡,从Standy模式进入Transfer模式
116     SDIO->IF = SDIO_IF_TRXDONE_Msk;
117 
118     SDIO_SendCmd(SD_CMD_APP_CMD, SD_cardInfo.RCA << 16, SD_RESP_32b, &resp);
119 
120     SDIO_SendCmd(SD_CMD_APP_SD_SET_BUSWIDTH, SD_BUSWIDTH_4b, SD_RESP_32b, &resp);   //切换成4位总线模式
121 
122     SDIO->CR1 |= (1 << SDIO_CR1_4BIT_Pos);
123 
124 
125     SDIO_SendCmd(SD_CMD_SET_BLOCKLEN, 512, SD_RESP_32b, &resp);     //固定块大小位512字节
126 
127     SD_cardInfo.CardBlockSize = 512;
128 
129     SDIO->BLK = 512;
130 
131     return SD_RES_OK;
132 }
133 
134 /******************************************************************************************************************************************
135 * 函数名称: SDIO_BlockWrite()
136 * 功能说明: 向SD卡写入数据
137 * 输    入: uint32_t block_addr       SD卡块地址,每块512字节
138 *           uint32_t buff[]         要写入的数据
139 * 输    出: uint32_t              SD_RES_OK 操作成功    SD_RES_ERR 操作失败    SD_RES_TIMEOUT 操作超时
140 * 注意事项: 无
141 ******************************************************************************************************************************************/
SDIO_BlockWrite(uint32_t block_addr,uint32_t buff[])142 uint32_t SDIO_BlockWrite(uint32_t block_addr, uint32_t buff[])
143 {
144     uint32_t res, i;
145     uint32_t addr, resp;
146 
147     if(SD_cardInfo.CardType == SDIO_HIGH_CAPACITY_SD_CARD)  addr = block_addr;
148     else                                                    addr = block_addr * 512;
149 
150     res = SDIO_SendCmdWithData(SD_CMD_WRITE_SINGLE_BLOCK, addr, SD_RESP_32b, &resp, 0, 1);
151     if(res != SD_RES_OK)
152         return res;
153 
154     while((SDIO->IF & SDIO_IF_BUFWRRDY_Msk) == 0) __NOP();
155     SDIO->IF = SDIO_IF_BUFWRRDY_Msk;
156 
157     for(i = 0; i < 512/4; i++) SDIO->DATA = buff[i];
158 
159     while((SDIO->IF & SDIO_IF_TRXDONE_Msk) == 0) __NOP();
160     SDIO->IF = SDIO_IF_TRXDONE_Msk;
161 
162     return SD_RES_OK;
163 }
164 
165 /******************************************************************************************************************************************
166 * 函数名称: SDIO_MultiBlockWrite()
167 * 功能说明: 向SD卡写入多块数据
168 * 输    入: uint32_t block_addr       SD卡块地址,每块512字节
169 *           uint16_t block_cnt      要写入的块数
170 *           uint32_t buff[]         要写入的数据
171 * 输    出: uint32_t              SD_RES_OK 操作成功    SD_RES_ERR 操作失败    SD_RES_TIMEOUT 操作超时
172 * 注意事项: 无
173 ******************************************************************************************************************************************/
SDIO_MultiBlockWrite(uint32_t block_addr,uint16_t block_cnt,uint32_t buff[])174 uint32_t SDIO_MultiBlockWrite(uint32_t block_addr, uint16_t block_cnt, uint32_t buff[])
175 {
176     uint32_t res, i, j;
177     uint32_t addr, resp;
178 
179     if(SD_cardInfo.CardType == SDIO_HIGH_CAPACITY_SD_CARD)  addr = block_addr;
180     else                                                    addr = block_addr * 512;
181 
182     res = SDIO_SendCmdWithData(SD_CMD_WRITE_MULT_BLOCK, addr, SD_RESP_32b, &resp, 0, block_cnt);
183     if(res != SD_RES_OK)
184         return res;
185 
186     for(i = 0; i < block_cnt; i++)
187     {
188         while((SDIO->IF & SDIO_IF_BUFWRRDY_Msk) == 0) __NOP();
189         SDIO->IF = SDIO_IF_BUFWRRDY_Msk;
190 
191         for(j = 0; j < 512/4; j++) SDIO->DATA = buff[i*(512/4) + j];
192     }
193 
194     while((SDIO->IF & SDIO_IF_TRXDONE_Msk) == 0) __NOP();
195     SDIO->IF = SDIO_IF_TRXDONE_Msk;
196 
197     return SD_RES_OK;
198 }
199 
200 /******************************************************************************************************************************************
201 * 函数名称: SDIO_DMABlockWrite()
202 * 功能说明: 通过DMA向SD卡写入多块数据
203 * 输    入: uint32_t block_addr       SD卡块地址,每块512字节
204 *           uint16_t block_cnt      要写入的块数
205 *           uint32_t buff[]         要写入的数据
206 * 输    出: uint32_t              SD_RES_OK 操作成功    SD_RES_ERR 操作失败    SD_RES_TIMEOUT 操作超时
207 * 注意事项: 无
208 ******************************************************************************************************************************************/
SDIO_DMABlockWrite(uint32_t block_addr,uint16_t block_cnt,uint32_t buff[])209 uint32_t SDIO_DMABlockWrite(uint32_t block_addr, uint16_t block_cnt, uint32_t buff[])
210 {
211     uint32_t res;
212     uint32_t addr, resp;
213 
214     if(SD_cardInfo.CardType == SDIO_HIGH_CAPACITY_SD_CARD)  addr = block_addr;
215     else                                                    addr = block_addr * 512;
216 
217     SDIO->DMA_MEM_ADDR = (uint32_t) buff;
218 
219     res = SDIO_SendCmdWithDataByDMA(SD_CMD_WRITE_MULT_BLOCK, addr, SD_RESP_32b, &resp, 0, block_cnt);
220     if(res != SD_RES_OK)
221         return res;
222 
223     while((SDIO->IF & SDIO_IF_TRXDONE_Msk) == 0) __NOP();
224     SDIO->IF = SDIO_IF_TRXDONE_Msk;
225 
226     return SD_RES_OK;
227 }
228 
229 /******************************************************************************************************************************************
230 * 函数名称: SDIO_BlockRead()
231 * 功能说明: 从SD卡读出数据
232 * 输    入: uint32_t block_addr       SD卡块地址,每块512字节
233 *           uint32_t buff[]         读出的数据
234 * 输    出: uint32_t              SD_RES_OK 操作成功    SD_RES_ERR 操作失败    SD_RES_TIMEOUT 操作超时
235 * 注意事项: 无
236 ******************************************************************************************************************************************/
SDIO_BlockRead(uint32_t block_addr,uint32_t buff[])237 uint32_t SDIO_BlockRead(uint32_t block_addr, uint32_t buff[])
238 {
239     uint32_t res, i;
240     uint32_t addr, resp;
241 
242     if(SD_cardInfo.CardType == SDIO_HIGH_CAPACITY_SD_CARD)  addr = block_addr;
243     else                                                    addr = block_addr * 512;
244 
245     res = SDIO_SendCmdWithData(SD_CMD_READ_SINGLE_BLOCK, addr, SD_RESP_32b, &resp, 1, 1);
246     if(res != SD_RES_OK)
247         return res;
248 
249     while((SDIO->IF & SDIO_IF_BUFRDRDY_Msk) == 0) __NOP();
250     SDIO->IF = SDIO_IF_BUFRDRDY_Msk;
251 
252     for(i = 0; i < 512/4; i++) buff[i] = SDIO->DATA;
253 
254     while((SDIO->IF & SDIO_IF_TRXDONE_Msk) == 0) __NOP();
255     SDIO->IF = SDIO_IF_TRXDONE_Msk;
256 
257     return SD_RES_OK;
258 }
259 
260 /******************************************************************************************************************************************
261 * 函数名称: SDIO_MultiBlockRead()
262 * 功能说明: 从SD卡读出多块数据
263 * 输    入: uint32_t block_addr       SD卡块地址,每块512字节
264 *           uint16_t block_cnt      要读出的块数
265 *           uint32_t buff[]         读出的数据
266 * 输    出: uint32_t              SD_RES_OK 操作成功    SD_RES_ERR 操作失败    SD_RES_TIMEOUT 操作超时
267 * 注意事项: 无
268 ******************************************************************************************************************************************/
SDIO_MultiBlockRead(uint32_t block_addr,uint16_t block_cnt,uint32_t buff[])269 uint32_t SDIO_MultiBlockRead(uint32_t block_addr, uint16_t block_cnt, uint32_t buff[])
270 {
271     uint32_t res, i, j;
272     uint32_t addr, resp;
273 
274     if(SD_cardInfo.CardType == SDIO_HIGH_CAPACITY_SD_CARD)  addr = block_addr;
275     else                                                    addr = block_addr * 512;
276 
277     res = SDIO_SendCmdWithData(SD_CMD_READ_MULT_BLOCK, addr, SD_RESP_32b, &resp, 1, block_cnt);
278     if(res != SD_RES_OK)
279         return res;
280 
281     for(i = 0; i < block_cnt; i++)
282     {
283         while((SDIO->IF & SDIO_IF_BUFRDRDY_Msk) == 0) __NOP();
284         SDIO->IF = SDIO_IF_BUFRDRDY_Msk;
285 
286         for(j = 0; j < 512/4; j++) buff[i*(512/4) + j] = SDIO->DATA;
287     }
288 
289     while((SDIO->IF & SDIO_IF_TRXDONE_Msk) == 0) __NOP();
290     SDIO->IF = SDIO_IF_TRXDONE_Msk;
291 
292     return SD_RES_OK;
293 }
294 
295 /******************************************************************************************************************************************
296 * 函数名称: SDIO_DMABlockRead()
297 * 功能说明: 通过DMA从SD卡读出多块数据
298 * 输    入: uint32_t block_addr       SD卡块地址,每块512字节
299 *           uint16_t block_cnt      要读出的块数
300 *           uint32_t buff[]         读出的数据
301 * 输    出: uint32_t              SD_RES_OK 操作成功    SD_RES_ERR 操作失败    SD_RES_TIMEOUT 操作超时
302 * 注意事项: 无
303 ******************************************************************************************************************************************/
SDIO_DMABlockRead(uint32_t block_addr,uint16_t block_cnt,uint32_t buff[])304 uint32_t SDIO_DMABlockRead(uint32_t block_addr, uint16_t block_cnt, uint32_t buff[])
305 {
306     uint32_t res;
307     uint32_t addr, resp;
308 
309     if(SD_cardInfo.CardType == SDIO_HIGH_CAPACITY_SD_CARD)  addr = block_addr;
310     else                                                    addr = block_addr * 512;
311 
312     SDIO->DMA_MEM_ADDR = (uint32_t)buff;
313 
314     res = SDIO_SendCmdWithDataByDMA(SD_CMD_READ_MULT_BLOCK, addr, SD_RESP_32b, &resp, 1, block_cnt);
315     if(res != SD_RES_OK)
316         return res;
317 
318     while((SDIO->IF & SDIO_IF_TRXDONE_Msk) == 0) __NOP();
319     SDIO->IF = SDIO_IF_TRXDONE_Msk;
320 
321     return SD_RES_OK;
322 }
323 
324 /******************************************************************************************************************************************
325 * 函数名称: _SDIO_SendCmd()
326 * 功能说明: SDIO向SD卡发送命令
327 * 输    入: uint32_t cmd          命令索引
328 *           uint32_t arg            命令参数
329 *           uint32_t resp_type      响应类型,取值SD_RESP_NO、SD_RESP_32b、SD_RESP_128b、SD_RESP_32b_busy
330 *           uint32_t *resp_data     响应内容
331 *           uint32_t have_data      是否有数据传输
332 *           uint32_t data_read      1 读SD卡    0 写SD卡
333 *           uint16_t block_cnt      读写块个数
334 *           uint32_t use_dma        1 使用DMA搬运数据
335 * 输    出: uint32_t              SD_RES_OK 操作成功    SD_RES_ERR 操作失败    SD_RES_TIMEOUT 操作超时
336 * 注意事项: 无
337 ******************************************************************************************************************************************/
_SDIO_SendCmd(uint32_t cmd,uint32_t arg,uint32_t resp_type,uint32_t * resp_data,uint32_t have_data,uint32_t data_read,uint16_t block_cnt,uint32_t use_dma)338 uint32_t _SDIO_SendCmd(uint32_t cmd, uint32_t arg, uint32_t resp_type, uint32_t *resp_data, uint32_t have_data, uint32_t data_read, uint16_t block_cnt, uint32_t use_dma)
339 {
340     SDIO->BLK &= ~SDIO_BLK_COUNT_Msk;
341     SDIO->BLK |= (block_cnt << SDIO_BLK_COUNT_Pos);
342 
343     SDIO->ARG = arg;
344     SDIO->CMD = (cmd             << SDIO_CMD_CMDINDX_Pos)   |
345                 (0               << SDIO_CMD_CMDTYPE_Pos)   |
346                 (0               << SDIO_CMD_IDXCHECK_Pos)  |
347                 (0               << SDIO_CMD_CRCCHECK_Pos)  |
348                 (resp_type       << SDIO_CMD_RESPTYPE_Pos)  |
349                 (have_data       << SDIO_CMD_HAVEDATA_Pos)  |
350                 (data_read       << SDIO_CMD_DIRREAD_Pos)   |
351                 ((block_cnt > 1) << SDIO_CMD_MULTBLK_Pos)   |
352                 ((block_cnt > 1) << SDIO_CMD_BLKCNTEN_Pos)  |
353                 (((cmd == 53) ? 0 : (block_cnt > 1)) << SDIO_CMD_AUTOCMD12_Pos) |
354                 (use_dma         << SDIO_CMD_DMAEN_Pos);
355 
356     while((SDIO->IF & SDIO_IF_CMDDONE_Msk) == 0)
357     {
358         if(SDIO->IF & SDIO_IF_CMDTIMEOUT_Msk)
359         {
360             SDIO->IF = SDIO_IF_CMDTIMEOUT_Msk;
361 
362             return SD_RES_TIMEOUT;
363         }
364         else if(SDIO->IF & SDIO_IF_ERROR_Msk)
365         {
366             SDIO->IF = 0xFFFFFFFF;
367 
368             return SD_RES_ERR;
369         }
370     }
371     SDIO->IF = SDIO_IF_CMDDONE_Msk;
372 
373     if(resp_type == SD_RESP_32b)
374     {
375         resp_data[0] = SDIO->RESP[0];
376     }
377     else if(resp_type == SD_RESP_128b)
378     {
379         //寄存器中将CID/CSD[127-8]依次存放在了RESP3-0[119-0],最低位的CRC被丢掉
380         //读出数据时调整了顺序,将CID/CSD[127-8]存放在resp_data0-3[127-8],最低8位填充0x00
381         resp_data[0] = (SDIO->RESP[3] << 8) + ((SDIO->RESP[2] >> 24) & 0xFF);
382         resp_data[1] = (SDIO->RESP[2] << 8) + ((SDIO->RESP[1] >> 24) & 0xFF);
383         resp_data[2] = (SDIO->RESP[1] << 8) + ((SDIO->RESP[0] >> 24) & 0xFF);
384         resp_data[3] = (SDIO->RESP[0] << 8) + 0x00;
385     }
386 
387     return SD_RES_OK;
388 }
389 
390 
parseCID(uint32_t CID_Tab[4])391 void parseCID(uint32_t CID_Tab[4])
392 {
393     uint8_t tmp = 0;
394 
395     /*!< Byte 0 */
396     tmp = (uint8_t)((CID_Tab[0] & 0xFF000000) >> 24);
397     SD_cardInfo.SD_cid.ManufacturerID = tmp;
398 
399     /*!< Byte 1 */
400     tmp = (uint8_t)((CID_Tab[0] & 0x00FF0000) >> 16);
401     SD_cardInfo.SD_cid.OEM_AppliID = tmp << 8;
402 
403     /*!< Byte 2 */
404     tmp = (uint8_t)((CID_Tab[0] & 0x000000FF00) >> 8);
405     SD_cardInfo.SD_cid.OEM_AppliID |= tmp;
406 
407     /*!< Byte 3 */
408     tmp = (uint8_t)(CID_Tab[0] & 0x000000FF);
409     SD_cardInfo.SD_cid.ProdName1 = tmp << 24;
410 
411     /*!< Byte 4 */
412     tmp = (uint8_t)((CID_Tab[1] & 0xFF000000) >> 24);
413     SD_cardInfo.SD_cid.ProdName1 |= tmp << 16;
414 
415     /*!< Byte 5 */
416     tmp = (uint8_t)((CID_Tab[1] & 0x00FF0000) >> 16);
417     SD_cardInfo.SD_cid.ProdName1 |= tmp << 8;
418 
419     /*!< Byte 6 */
420     tmp = (uint8_t)((CID_Tab[1] & 0x0000FF00) >> 8);
421     SD_cardInfo.SD_cid.ProdName1 |= tmp;
422 
423     /*!< Byte 7 */
424     tmp = (uint8_t)(CID_Tab[1] & 0x000000FF);
425     SD_cardInfo.SD_cid.ProdName2 = tmp;
426 
427     /*!< Byte 8 */
428     tmp = (uint8_t)((CID_Tab[2] & 0xFF000000) >> 24);
429     SD_cardInfo.SD_cid.ProdRev = tmp;
430 
431     /*!< Byte 9 */
432     tmp = (uint8_t)((CID_Tab[2] & 0x00FF0000) >> 16);
433     SD_cardInfo.SD_cid.ProdSN = tmp << 24;
434 
435     /*!< Byte 10 */
436     tmp = (uint8_t)((CID_Tab[2] & 0x0000FF00) >> 8);
437     SD_cardInfo.SD_cid.ProdSN |= tmp << 16;
438 
439     /*!< Byte 11 */
440     tmp = (uint8_t)(CID_Tab[2] & 0x000000FF);
441     SD_cardInfo.SD_cid.ProdSN |= tmp << 8;
442 
443     /*!< Byte 12 */
444     tmp = (uint8_t)((CID_Tab[3] & 0xFF000000) >> 24);
445     SD_cardInfo.SD_cid.ProdSN |= tmp;
446 
447     /*!< Byte 13 */
448     tmp = (uint8_t)((CID_Tab[3] & 0x00FF0000) >> 16);
449     SD_cardInfo.SD_cid.Reserved1 |= (tmp & 0xF0) >> 4;
450     SD_cardInfo.SD_cid.ManufactDate = (tmp & 0x0F) << 8;
451 
452     /*!< Byte 14 */
453     tmp = (uint8_t)((CID_Tab[3] & 0x0000FF00) >> 8);
454     SD_cardInfo.SD_cid.ManufactDate |= tmp;
455 }
456 
parseCSD(uint32_t CSD_Tab[4])457 void parseCSD(uint32_t CSD_Tab[4])
458 {
459     uint8_t tmp = 0;
460 
461     /*!< Byte 0 */
462     tmp = (uint8_t)((CSD_Tab[0] & 0xFF000000) >> 24);
463     SD_cardInfo.SD_csd.CSDStruct = (tmp & 0xC0) >> 6;
464     SD_cardInfo.SD_csd.SysSpecVersion = (tmp & 0x3C) >> 2;
465     SD_cardInfo.SD_csd.Reserved1 = tmp & 0x03;
466 
467     /*!< Byte 1 */
468     tmp = (uint8_t)((CSD_Tab[0] & 0x00FF0000) >> 16);
469     SD_cardInfo.SD_csd.TAAC = tmp;
470 
471     /*!< Byte 2 */
472     tmp = (uint8_t)((CSD_Tab[0] & 0x0000FF00) >> 8);
473     SD_cardInfo.SD_csd.NSAC = tmp;
474 
475     /*!< Byte 3 */
476     tmp = (uint8_t)(CSD_Tab[0] & 0x000000FF);
477     SD_cardInfo.SD_csd.MaxBusClkFrec = tmp;
478 
479     /*!< Byte 4 */
480     tmp = (uint8_t)((CSD_Tab[1] & 0xFF000000) >> 24);
481     SD_cardInfo.SD_csd.CardComdClasses = tmp << 4;
482 
483     /*!< Byte 5 */
484     tmp = (uint8_t)((CSD_Tab[1] & 0x00FF0000) >> 16);
485     SD_cardInfo.SD_csd.CardComdClasses |= (tmp & 0xF0) >> 4;
486     SD_cardInfo.SD_csd.RdBlockLen = tmp & 0x0F;
487 
488     /*!< Byte 6 */
489     tmp = (uint8_t)((CSD_Tab[1] & 0x0000FF00) >> 8);
490     SD_cardInfo.SD_csd.PartBlockRead = (tmp & 0x80) >> 7;
491     SD_cardInfo.SD_csd.WrBlockMisalign = (tmp & 0x40) >> 6;
492     SD_cardInfo.SD_csd.RdBlockMisalign = (tmp & 0x20) >> 5;
493     SD_cardInfo.SD_csd.DSRImpl = (tmp & 0x10) >> 4;
494     SD_cardInfo.SD_csd.Reserved2 = 0; /*!< Reserved */
495 
496     if ((SD_cardInfo.CardType == SDIO_STD_CAPACITY_SD_CARD_V1_1) ||
497         (SD_cardInfo.CardType == SDIO_STD_CAPACITY_SD_CARD_V2_0))
498     {
499         SD_cardInfo.SD_csd.DeviceSize = (tmp & 0x03) << 10;
500 
501         /*!< Byte 7 */
502         tmp = (uint8_t)(CSD_Tab[1] & 0x000000FF);
503         SD_cardInfo.SD_csd.DeviceSize |= (tmp) << 2;
504 
505         /*!< Byte 8 */
506         tmp = (uint8_t)((CSD_Tab[2] & 0xFF000000) >> 24);
507         SD_cardInfo.SD_csd.DeviceSize |= (tmp & 0xC0) >> 6;
508 
509         SD_cardInfo.SD_csd.MaxRdCurrentVDDMin = (tmp & 0x38) >> 3;
510         SD_cardInfo.SD_csd.MaxRdCurrentVDDMax = (tmp & 0x07);
511 
512         /*!< Byte 9 */
513         tmp = (uint8_t)((CSD_Tab[2] & 0x00FF0000) >> 16);
514         SD_cardInfo.SD_csd.MaxWrCurrentVDDMin = (tmp & 0xE0) >> 5;
515         SD_cardInfo.SD_csd.MaxWrCurrentVDDMax = (tmp & 0x1C) >> 2;
516         SD_cardInfo.SD_csd.DeviceSizeMul = (tmp & 0x03) << 1;
517         /*!< Byte 10 */
518         tmp = (uint8_t)((CSD_Tab[2] & 0x0000FF00) >> 8);
519         SD_cardInfo.SD_csd.DeviceSizeMul |= (tmp & 0x80) >> 7;
520 
521         SD_cardInfo.CardCapacity = (SD_cardInfo.SD_csd.DeviceSize + 1) ;
522         SD_cardInfo.CardCapacity *= (1 << (SD_cardInfo.SD_csd.DeviceSizeMul + 2));
523         SD_cardInfo.CardBlockSize = 1 << (SD_cardInfo.SD_csd.RdBlockLen);
524         SD_cardInfo.CardCapacity *= SD_cardInfo.CardBlockSize;
525     }
526     else if (SD_cardInfo.CardType == SDIO_HIGH_CAPACITY_SD_CARD)
527     {
528         /*!< Byte 7 */
529         tmp = (uint8_t)(CSD_Tab[1] & 0x000000FF);
530         SD_cardInfo.SD_csd.DeviceSize = (tmp & 0x3F) << 16;
531 
532         /*!< Byte 8 */
533         tmp = (uint8_t)((CSD_Tab[2] & 0xFF000000) >> 24);
534 
535         SD_cardInfo.SD_csd.DeviceSize |= (tmp << 8);
536 
537         /*!< Byte 9 */
538         tmp = (uint8_t)((CSD_Tab[2] & 0x00FF0000) >> 16);
539 
540         SD_cardInfo.SD_csd.DeviceSize |= (tmp);
541 
542         /*!< Byte 10 */
543         tmp = (uint8_t)((CSD_Tab[2] & 0x0000FF00) >> 8);
544 
545         SD_cardInfo.CardCapacity = (uint64_t)(SD_cardInfo.SD_csd.DeviceSize + 1) * 512 * 1024;
546         SD_cardInfo.CardBlockSize = 512;
547     }
548 
549     SD_cardInfo.SD_csd.EraseGrSize = (tmp & 0x40) >> 6;
550     SD_cardInfo.SD_csd.EraseGrMul = (tmp & 0x3F) << 1;
551 
552     /*!< Byte 11 */
553     tmp = (uint8_t)(CSD_Tab[2] & 0x000000FF);
554     SD_cardInfo.SD_csd.EraseGrMul |= (tmp & 0x80) >> 7;
555     SD_cardInfo.SD_csd.WrProtectGrSize = (tmp & 0x7F);
556 
557     /*!< Byte 12 */
558     tmp = (uint8_t)((CSD_Tab[3] & 0xFF000000) >> 24);
559     SD_cardInfo.SD_csd.WrProtectGrEnable = (tmp & 0x80) >> 7;
560     SD_cardInfo.SD_csd.ManDeflECC = (tmp & 0x60) >> 5;
561     SD_cardInfo.SD_csd.WrSpeedFact = (tmp & 0x1C) >> 2;
562     SD_cardInfo.SD_csd.MaxWrBlockLen = (tmp & 0x03) << 2;
563 
564     /*!< Byte 13 */
565     tmp = (uint8_t)((CSD_Tab[3] & 0x00FF0000) >> 16);
566     SD_cardInfo.SD_csd.MaxWrBlockLen |= (tmp & 0xC0) >> 6;
567     SD_cardInfo.SD_csd.WriteBlockPaPartial = (tmp & 0x20) >> 5;
568     SD_cardInfo.SD_csd.Reserved3 = 0;
569     SD_cardInfo.SD_csd.ContentProtectAppli = (tmp & 0x01);
570 
571     /*!< Byte 14 */
572     tmp = (uint8_t)((CSD_Tab[3] & 0x0000FF00) >> 8);
573     SD_cardInfo.SD_csd.FileFormatGrouop = (tmp & 0x80) >> 7;
574     SD_cardInfo.SD_csd.CopyFlag = (tmp & 0x40) >> 6;
575     SD_cardInfo.SD_csd.PermWrProtect = (tmp & 0x20) >> 5;
576     SD_cardInfo.SD_csd.TempWrProtect = (tmp & 0x10) >> 4;
577     SD_cardInfo.SD_csd.FileFormat = (tmp & 0x0C) >> 2;
578     SD_cardInfo.SD_csd.ECC = (tmp & 0x03);
579 }
580 
calcSDCLKDiv(uint32_t freq)581 uint32_t calcSDCLKDiv(uint32_t freq)
582 {
583     uint32_t prediv;
584     switch((SYS->CLKSEL & SYS_CLKSEL_SDIO_Msk) >> SYS_CLKSEL_SDIO_Pos)
585     {
586     case 0: prediv = 1; break;
587     case 1: prediv = 3; break;
588     case 2: prediv = 2; break;
589     case 3: prediv = 0; break;
590     }
591 
592     uint32_t clkdiv = (SystemCoreClock / (1 << prediv)) / freq;
593     uint32_t regdiv = 0;
594 
595     if(clkdiv > 128)     regdiv = 0x80;
596     else if(clkdiv > 64) regdiv = 0x40;
597     else if(clkdiv > 32) regdiv = 0x20;
598     else if(clkdiv > 16) regdiv = 0x10;
599     else if(clkdiv >  8) regdiv = 0x08;
600     else if(clkdiv >  4) regdiv = 0x04;
601     else if(clkdiv >  2) regdiv = 0x02;
602     else if(clkdiv >  1) regdiv = 0x01;
603     else                 regdiv = 0x00;
604 
605     return regdiv;
606 }
607 
608 
609 /******************************************************************************************************************************************
610 * 函数名称: SDIO_IO_Init()
611 * 功能说明: SDIO读写IO卡初始化
612 * 输    入: uint32_t freq         SDIO_CLK时钟频率
613 *           enum SDIO_bus_width w   SDIO_1bit 1-bit bus   SDIO_4bit 4-bit bus
614 * 输    出: uint32_t              SD_RES_OK 操作成功    SD_RES_ERR 操作失败    SD_RES_TIMEOUT 操作超时
615 * 注意事项: 无
616 ******************************************************************************************************************************************/
SDIO_IO_Init(uint32_t freq,enum SDIO_bus_width w)617 uint32_t SDIO_IO_Init(uint32_t freq, enum SDIO_bus_width w)
618 {
619     uint32_t res;
620     uint32_t resp, resps[4];
621 
622     SYS->CLKSEL &= ~SYS_CLKSEL_SDIO_Msk;
623     if(SystemCoreClock > 80000000)      //SDIO时钟需要小于52MHz
624         SYS->CLKSEL |= (2 << SYS_CLKSEL_SDIO_Pos);  //SDCLK = SYSCLK / 4
625     else
626         SYS->CLKSEL |= (0 << SYS_CLKSEL_SDIO_Pos);  //SDCLK = SYSCLK / 2
627 
628     SYS->CLKEN0 |= (0x01 << SYS_CLKEN0_SDIO_Pos);
629 
630 //  SDIO->CR2 = (1 << SDIO_CR2_RSTALL_Pos);
631     for(int i = 0; i < CyclesPerUs; i++) __NOP();
632 
633     SDIO->CR1 = (1 << SDIO_CR1_CDSRC_Pos) |
634                 (0 << SDIO_CR1_8BIT_Pos)  |
635                 (w << SDIO_CR1_4BIT_Pos)  |
636                 (1 << SDIO_CR1_PWRON_Pos) |
637                 (7 << SDIO_CR1_VOLT_Pos);
638 
639     SDIO->CR2 = (1 << SDIO_CR2_CLKEN_Pos) |
640                 (1 << SDIO_CR2_SDCLKEN_Pos) |
641                 (calcSDCLKDiv(freq) << SDIO_CR2_SDCLKDIV_Pos) |
642                 (0xC << SDIO_CR2_TIMEOUT_Pos);      // 2**25 SDIO_CLK
643 
644     while((SDIO->CR2 & SDIO_CR2_CLKRDY_Msk) == 0);
645 
646     for(int i = 0; i < CyclesPerUs * 10; i++) __NOP();
647 
648     SDIO->IM = 0xFFFFFFFF;
649 
650     return SD_RES_OK;
651 }
652 
653 
654 /******************************************************************************************************************************************
655 * 函数名称: SDIO_IO_ByteWrite()
656 * 功能说明: 向IO卡写入单个字节
657 * 输    入: uint8_t func          The number of the function within the I/O card you wish to read or write
658 *           uint32_t addr           Start Address of I/O register to read or write. Range is 0--0x1FFFF
659 *           uint32_t buff[]         要写出的数据
660 *           uint16_t block_size     要写出的字节个数,取值 1--512
661 * 输    出: uint32_t              SD_RES_OK 操作成功    SD_RES_ERR 操作失败    SD_RES_TIMEOUT 操作超时
662 * 注意事项: 无
663 ******************************************************************************************************************************************/
SDIO_IO_ByteWrite(uint8_t func,uint32_t addr,uint8_t data)664 uint32_t SDIO_IO_ByteWrite(uint8_t func, uint32_t addr, uint8_t data)
665 {
666     uint32_t res;
667     uint32_t arg, resp;
668 
669     arg = (1u   << SD_CMD53_ARG_nRW) |
670           (func << SD_CMD53_ARG_Function) |
671           (addr << SD_CMD53_ARG_Addr) | data;
672 
673     res = SDIO_SendCmd(52, arg, SD_RESP_32b, &resp);
674     if(res != SD_RES_OK)
675         return res;
676 
677     return SD_RES_OK;
678 }
679 
680 
681 /******************************************************************************************************************************************
682 * 函数名称: SDIO_IO_ByteRead()
683 * 功能说明: 从IO卡读出单个字节
684 * 输    入: uint8_t func          The number of the function within the I/O card you wish to read or write
685 *           uint32_t addr           Start Address of I/O register to read or write. Range is 0--0x1FFFF
686 *           uint32_t buff[]         读取到的数据存入此数组
687 *           uint16_t block_size     要读取的字节个数,取值 1--512
688 * 输    出: uint32_t              SD_RES_OK 操作成功    SD_RES_ERR 操作失败    SD_RES_TIMEOUT 操作超时
689 * 注意事项: 无
690 ******************************************************************************************************************************************/
SDIO_IO_ByteRead(uint8_t func,uint32_t addr,uint8_t * data)691 uint32_t SDIO_IO_ByteRead(uint8_t func, uint32_t addr, uint8_t * data)
692 {
693     uint32_t res;
694     uint32_t arg, resp;
695 
696     arg = (0u   << SD_CMD53_ARG_nRW) |
697           (func << SD_CMD53_ARG_Function) |
698           (addr << SD_CMD53_ARG_Addr) | 0x00;
699 
700     res = SDIO_SendCmd(52, arg, SD_RESP_32b, &resp);
701     if(res != SD_RES_OK)
702         return res;
703 
704     *data = resp & 0xFF;
705 
706     return SD_RES_OK;
707 }
708 
709 
710 /******************************************************************************************************************************************
711 * 函数名称: SDIO_IO_BlockWrite()
712 * 功能说明: 向IO卡写入单个块数据
713 * 输    入: uint8_t func          The number of the function within the I/O card you wish to read or write
714 *           uint32_t addr           Start Address of I/O register to read or write. Range is 0--0x1FFFF
715 *           uint8_t addrInc         0 Multi byte R/W to fixed address   1 Multi byte R/W to incrementing address
716 *           uint32_t buff[]         要写出的数据
717 *           uint16_t block_size     要写出的字节个数,取值 1--512
718 * 输    出: uint32_t              SD_RES_OK 操作成功    SD_RES_ERR 操作失败    SD_RES_TIMEOUT 操作超时
719 * 注意事项: 无
720 ******************************************************************************************************************************************/
SDIO_IO_BlockWrite(uint8_t func,uint32_t addr,uint8_t addrInc,uint32_t buff[],uint16_t block_size)721 uint32_t SDIO_IO_BlockWrite(uint8_t func, uint32_t addr, uint8_t addrInc, uint32_t buff[], uint16_t block_size)
722 {
723     uint32_t res, i;
724     uint32_t arg, resp;
725 
726     SDIO->BLK = block_size;
727 
728     arg = (1u                   << SD_CMD53_ARG_nRW) |
729           (func                 << SD_CMD53_ARG_Function) |
730           (addr                 << SD_CMD53_ARG_Addr) |
731           (addrInc              << SD_CMD53_ARG_AddrInc) |
732           ((block_size % 512)   << SD_CMD53_ARG_Count) |
733           (0                    << SD_CMD53_ARG_CountUnit);
734 
735     res = SDIO_SendCmdWithData(53, arg, SD_RESP_32b, &resp, 0, 1);
736     if(res != SD_RES_OK)
737         return res;
738 
739     while((SDIO->IF & SDIO_IF_BUFWRRDY_Msk) == 0) __NOP();
740     SDIO->IF = SDIO_IF_BUFWRRDY_Msk;
741 
742     for(i = 0; i < block_size/4; i++) SDIO->DATA = buff[i];
743 
744     while((SDIO->IF & SDIO_IF_TRXDONE_Msk) == 0) __NOP();
745     SDIO->IF = SDIO_IF_TRXDONE_Msk;
746 
747     return SD_RES_OK;
748 }
749 
750 
751 /******************************************************************************************************************************************
752 * 函数名称: SDIO_IO_BlockRead()
753 * 功能说明: 从IO卡读出单个块数据
754 * 输    入: uint8_t func          The number of the function within the I/O card you wish to read or write
755 *           uint32_t addr           Start Address of I/O register to read or write. Range is 0--0x1FFFF
756 *           uint8_t addrInc         0 Multi byte R/W to fixed address   1 Multi byte R/W to incrementing address
757 *           uint32_t buff[]         读取到的数据存入此数组
758 *           uint16_t block_size     要读取的字节个数,取值 1--512
759 * 输    出: uint32_t              SD_RES_OK 操作成功    SD_RES_ERR 操作失败    SD_RES_TIMEOUT 操作超时
760 * 注意事项: 无
761 ******************************************************************************************************************************************/
SDIO_IO_BlockRead(uint8_t func,uint32_t addr,uint8_t addrInc,uint32_t buff[],uint16_t block_size)762 uint32_t SDIO_IO_BlockRead(uint8_t func, uint32_t addr, uint8_t addrInc, uint32_t buff[], uint16_t block_size)
763 {
764     uint32_t res, i;
765     uint32_t arg, resp;
766 
767     SDIO->BLK = block_size;
768 
769     arg = (0u                   << SD_CMD53_ARG_nRW) |
770           (func                 << SD_CMD53_ARG_Function) |
771           (addr                 << SD_CMD53_ARG_Addr) |
772           (addrInc              << SD_CMD53_ARG_AddrInc) |
773           ((block_size % 512)   << SD_CMD53_ARG_Count) |
774           (0                    << SD_CMD53_ARG_CountUnit);
775 
776     res = SDIO_SendCmdWithData(53, arg, SD_RESP_32b, &resp, 1, 1);
777     if(res != SD_RES_OK)
778         return res;
779 
780     while((SDIO->IF & SDIO_IF_BUFRDRDY_Msk) == 0) __NOP();
781     SDIO->IF = SDIO_IF_BUFRDRDY_Msk;
782 
783     for(i = 0; i < block_size/4; i++) buff[i] = SDIO->DATA;
784 
785     while((SDIO->IF & SDIO_IF_TRXDONE_Msk) == 0) __NOP();
786     SDIO->IF = SDIO_IF_TRXDONE_Msk;
787 
788     return SD_RES_OK;
789 }
790 
791 
792 /******************************************************************************************************************************************
793 * 函数名称: SDIO_IO_MultiBlockWrite()
794 * 功能说明: 向IO卡写入多个块数据
795 * 输    入: uint8_t func          The number of the function within the I/O card you wish to read or write
796 *           uint32_t addr           Start Address of I/O register to read or write. Range is 0--0x1FFFF
797 *           uint8_t addrInc         0 Multi byte R/W to fixed address   1 Multi byte R/W to incrementing address
798 *           uint32_t buff[]         要写出的数据
799 *           uint16_t block_count    要写出的块个数,块大小为 512 字节
800 * 输    出: uint32_t              SD_RES_OK 操作成功    SD_RES_ERR 操作失败    SD_RES_TIMEOUT 操作超时
801 * 注意事项: 无
802 ******************************************************************************************************************************************/
SDIO_IO_MultiBlockWrite(uint8_t func,uint32_t addr,uint8_t addrInc,uint32_t buff[],uint16_t block_count)803 uint32_t SDIO_IO_MultiBlockWrite(uint8_t func, uint32_t addr, uint8_t addrInc, uint32_t buff[], uint16_t block_count)
804 {
805     uint32_t res, i, j;
806     uint32_t arg, resp;
807 
808     SDIO->BLK = 512;
809 
810     arg = (1u           << SD_CMD53_ARG_nRW) |
811           (func         << SD_CMD53_ARG_Function) |
812           (addr         << SD_CMD53_ARG_Addr) |
813           (addrInc      << SD_CMD53_ARG_AddrInc) |
814           (block_count  << SD_CMD53_ARG_Count) |
815           (1            << SD_CMD53_ARG_CountUnit);
816 
817     res = SDIO_SendCmdWithData(53, arg, SD_RESP_32b, &resp, 0, block_count);
818     if(res != SD_RES_OK)
819         return res;
820 
821     for(i = 0; i < block_count; i++)
822     {
823         while((SDIO->IF & SDIO_IF_BUFWRRDY_Msk) == 0) __NOP();
824         SDIO->IF = SDIO_IF_BUFWRRDY_Msk;
825 
826         for(j = 0; j < 512/4; j++) SDIO->DATA = buff[i*(512/4) + j];
827     }
828 
829     while((SDIO->IF & SDIO_IF_TRXDONE_Msk) == 0) __NOP();
830     SDIO->IF = SDIO_IF_TRXDONE_Msk;
831 
832     return SD_RES_OK;
833 }
834 
835 
836 /******************************************************************************************************************************************
837 * 函数名称: SDIO_IO_MultiBlockRead()
838 * 功能说明: 从IO卡读出多个块数据
839 * 输    入: uint8_t func          The number of the function within the I/O card you wish to read or write
840 *           uint32_t addr           Start Address of I/O register to read or write. Range is 0--0x1FFFF
841 *           uint8_t addrInc         0 Multi byte R/W to fixed address   1 Multi byte R/W to incrementing address
842 *           uint32_t buff[]         读取到的数据存入此数组
843 *           uint16_t block_count    要读取的块个数,块大小为 512 字节
844 * 输    出: uint32_t              SD_RES_OK 操作成功    SD_RES_ERR 操作失败    SD_RES_TIMEOUT 操作超时
845 * 注意事项: 无
846 ******************************************************************************************************************************************/
SDIO_IO_MultiBlockRead(uint8_t func,uint32_t addr,uint8_t addrInc,uint32_t buff[],uint16_t block_count)847 uint32_t SDIO_IO_MultiBlockRead(uint8_t func, uint32_t addr, uint8_t addrInc, uint32_t buff[], uint16_t block_count)
848 {
849     uint32_t res, i, j;
850     uint32_t arg, resp;
851 
852     SDIO->BLK = 512;
853 
854     arg = (0u           << SD_CMD53_ARG_nRW) |
855           (func         << SD_CMD53_ARG_Function) |
856           (addr         << SD_CMD53_ARG_Addr) |
857           (addrInc      << SD_CMD53_ARG_AddrInc) |
858           (block_count  << SD_CMD53_ARG_Count) |
859           (1            << SD_CMD53_ARG_CountUnit);
860 
861     res = SDIO_SendCmdWithData(53, arg, SD_RESP_32b, &resp, 1, block_count);
862     if(res != SD_RES_OK)
863         return res;
864 
865     for(i = 0; i < block_count; i++)
866     {
867         while((SDIO->IF & SDIO_IF_BUFRDRDY_Msk) == 0) __NOP();
868         SDIO->IF = SDIO_IF_BUFRDRDY_Msk;
869 
870         for(j = 0; j < 512/4; j++) buff[i*(512/4) + j] = SDIO->DATA;
871     }
872 
873     while((SDIO->IF & SDIO_IF_TRXDONE_Msk) == 0) __NOP();
874     SDIO->IF = SDIO_IF_TRXDONE_Msk;
875 
876     return SD_RES_OK;
877 }
878