MSP430F5438A单片机基于SPI的FatFs移植笔记(二)
2015-04-16 10:06
405 查看
上回说到,CMD0命令的实现,通过它完整的实现了命令发送动作,好下一步让我们继续回到初始化的过程当中:
![](http://img.blog.csdn.net/20150416100700624?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl1amowNQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
初始化的过程在CMD0 之前,有一个Power ON 注意这不是简简单单的上个电这么容易的
你需要在Power ON 这一个过程当中:
1. 正确连接各个IO
2. 插入SD卡(不要笑,当你调试蒙了的时候真的可能就忘了)
3. 通电
4, 拉高CS引脚电平
5. 在保持输出为高的前提下,发送至少74个时钟周期(发送一个0xFF是8个时钟周期,我为了保险发了10次,那么就是80个,够了)
6. 拉低CS引脚电平
7. 等待一会儿(我索性就又发了16个时钟周期,也就是2个0xFF,实际证明是OK的)
这些都做完就可以开始发送CMD0了
发送CMD0过后,假如你收到的response是0x01,恭喜你完成了第一步
前面说过,CMD8的返回值是R7,而且CRC也不是0x95,我是在开发过程中,为了便于调试所以给CMD8单独编了个函数,其实它的过程是和其他CMD差不多的
只不过CMD8的argument的格式是这样的(在410文档的7.3.1.4有说明,顺便一提大家百度CMD8 SD卡出来的那个百度文库的说明对CMD8的回复格式的说明有些不严谨,以我的为准!哈哈哈):
首先是 0x48命令头
然后是32 bits 的 argument
31-12位保留,填入0即可
11-8 供电电压(这个供电电压是你向SD卡说我提供的电压是否符合你的要求,你可以设计一个AD进行一下检验,如果自信没问题可以直接写 0001b通过)
7-0 最不同的就是这8bit,是一个 check pattern,这几位你发的是什么,CMD8的response的特定几位就会返回什么
最后是CRC校验,假如你的check pattern写的是0xAA,那么这里的CRC写0x87就可以了,要是check pattern 写了别的就拿CRC计算器去算一下吧
然后,假如你的CMD8收到了以0x01开头的回复,表明卡片支持CMD8,否则可能会收到别的东西,表明不支持。
支持CMD8的是2.0的卡,反之为1.0
唯一要注意的是ACMD41发送的过程中,它的argument的bit 30 给个1,表明发送方是主控芯片(连蒙带猜应该是这个意思)
虽然这个命令可以省略,初始化也能通过,可是你确实需要直到你手里的是什么卡这个非常重要,影响到了后面的读写函数
如果你成功的执行了以上所有命令那么恭喜你初始化成功了
这是源代码
其中那个CMD8的发送命令是这样的:
这里还有点儿BUG和其他的需要说明,为了保险
我的response这个static unsigned char 的全局变量数组给了8个元素,CMD8命令结束之后我读了6个byte多读了一个,也没什么大问题,最后一个是0xFF罢了我就没有改
另外,在初始化的函数当中所有注释了DEBUG的语句其实是我开通了串口,通过串口把调试结果实时发送到PC机上,奉劝大家别嫌麻烦,串口很简单的,搞定了以后比仿真器有优势,可以一定程度上避免你单步调试中对时序的影响
初始化的过程在CMD0 之前,有一个Power ON 注意这不是简简单单的上个电这么容易的
你需要在Power ON 这一个过程当中:
1. 正确连接各个IO
2. 插入SD卡(不要笑,当你调试蒙了的时候真的可能就忘了)
3. 通电
4, 拉高CS引脚电平
5. 在保持输出为高的前提下,发送至少74个时钟周期(发送一个0xFF是8个时钟周期,我为了保险发了10次,那么就是80个,够了)
6. 拉低CS引脚电平
7. 等待一会儿(我索性就又发了16个时钟周期,也就是2个0xFF,实际证明是OK的)
这些都做完就可以开始发送CMD0了
发送CMD0过后,假如你收到的response是0x01,恭喜你完成了第一步
判断卡片版本的CMD8
接下来需要发送的是CMD8,它的作用是判断这个SD卡是1.0还是2.0的版本的,注意这个版本判断主要用于在后期通过CMD9和ACMD13获取卡片的总大小以及block size时判断返回response的内容,因为1.0版本和2.0版本的卡返回内容有所不同。要特别强调的是,这个1.0啊2.0似乎和WinHex读你的SD卡的时候显示的固件版本不是同一个东西,我手头这个8G的卡winhex显示的固件版本是1.0,通过CMD8查出来却是2.0的卡,还奇怪了好一阵子前面说过,CMD8的返回值是R7,而且CRC也不是0x95,我是在开发过程中,为了便于调试所以给CMD8单独编了个函数,其实它的过程是和其他CMD差不多的
只不过CMD8的argument的格式是这样的(在410文档的7.3.1.4有说明,顺便一提大家百度CMD8 SD卡出来的那个百度文库的说明对CMD8的回复格式的说明有些不严谨,以我的为准!哈哈哈):
首先是 0x48命令头
然后是32 bits 的 argument
31-12位保留,填入0即可
11-8 供电电压(这个供电电压是你向SD卡说我提供的电压是否符合你的要求,你可以设计一个AD进行一下检验,如果自信没问题可以直接写 0001b通过)
7-0 最不同的就是这8bit,是一个 check pattern,这几位你发的是什么,CMD8的response的特定几位就会返回什么
最后是CRC校验,假如你的check pattern写的是0xAA,那么这里的CRC写0x87就可以了,要是check pattern 写了别的就拿CRC计算器去算一下吧
然后,假如你的CMD8收到了以0x01开头的回复,表明卡片支持CMD8,否则可能会收到别的东西,表明不支持。
支持CMD8的是2.0的卡,反之为1.0
再次判断卡片工作电压的CMD58
这里的标准流程是运行CMD58并通过卡片回复再次确认是否工作在正常电压下,但是我这里第一因为是调试,第二因为工作环境理想,电压可以保证,所以就省略了这一步了,发现不会对初始化造成任何不良影响正式启动卡片初始化流程的ACMD41
发送ACMD与原来的CMD基本一样,不过你要首先发送一个CMD55,这是一个ACMD的先导命令,表明紧跟着发送的是一个ACMD命令,否则ACMD13和CMD13的命令号都是13,无法区分唯一要注意的是ACMD41发送的过程中,它的argument的bit 30 给个1,表明发送方是主控芯片(连蒙带猜应该是这个意思)
再次发送CMD58判断卡片是SDSC还是SDHC/XC
这个步骤:能省略!!!!但是我就是省略了结果2了,倒不是说我不知道自己手里的是SDHC卡,上面那么大字写着呢我开始觉得也没什么,默认HC就行了,现在谁还有2G往下的SD卡,但是问题其实不在这里,而是密歇根大学那个哥们也偷懒了,但是那个家伙的的报告是04年写的啊04年!劳资还没上大学呢啊!有木有!有些地方他默认卡是SDSC啊,我就悲剧了啊!虽然这个命令可以省略,初始化也能通过,可是你确实需要直到你手里的是什么卡这个非常重要,影响到了后面的读写函数
如果你成功的执行了以上所有命令那么恭喜你初始化成功了
这是源代码
char SD_once_disk_initialize(char* SD_type) { char i,j; // ===0=== // 设置通信频率为约400kHz // 默认SPI初始化的时候进行设置了,这里不再重复 //============================================ // DEBUG4 //-------------------------------------------- sd_RS232TX_PROC("Init-Building Argument..."); sd_RS232TX_PROC(sd_NewRow); //============================================ // ===1=== // 构造argument for (i = 0; i < 4; i++) { argument[i] = 0; } // ===2=== // 延迟至少74个时钟周期,期间: // (1) 保持CS持续高 // (2) 保持信号线持续高 //============================================ // DEBUG4 //-------------------------------------------- sd_RS232TX_PROC("Waiting 74 Cicles..."); sd_RS232TX_PROC(sd_NewRow); //============================================ SPI_CS_HIGH(); SPI_SD_Wait(100); //至少10个,这里保险起见给了100 // ===3=== // 将CS拉低 // 根据 Application Note Secure Digital Card Interface for the MPS430 // 这里拉低之后加入一个延时 //============================================ // DEBUG4 //-------------------------------------------- sd_RS232TX_PROC("CS become low..."); sd_RS232TX_PROC(sd_NewRow); //============================================ SPI_CS_LOW(); SPI_SD_Wait(2); // ===3=== // 发送 CMD0 命令 //============================================ // DEBUG4 //-------------------------------------------- sd_RS232TX_PROC("CMD0..."); sd_RS232TX_PROC(sd_NewRow); //============================================ if (SD_Send_Command(CMD0, CMD0_R, response, argument) == 0) return 0; // 发送命令 CMD8 这个命令的CRC不是0x95,如果checkpattern是0x0A那CRC是0x87 // 为了判断 SD 卡的版本,这里比Application Note多了这个过程 //============================================ // DEBUG4 //-------------------------------------------- sd_RS232TX_PROC("Checking Version..."); sd_RS232TX_PROC(sd_NewRow); //============================================ if (SD_Check_Card_Version() == 0) { //============================================ // DEBUG4 //-------------------------------------------- sd_RS232TX_PROC("Checking Version Finished"); sd_RS232TX_PROC(sd_NewRow); //============================================ return 0; } else { if (response[2] == 0x01)// 判断SD卡版本 { //============================================ // DEBUG4 //-------------------------------------------- sd_RS232TX_PROC("Version 2"); sd_RS232TX_PROC(sd_NewRow); //============================================ *SD_type = SD_TYPE2;// 新版SD } else { //============================================ // DEBUG4 //-------------------------------------------- sd_RS232TX_PROC("Version 1"); sd_RS232TX_PROC(sd_NewRow); //============================================ *SD_type = SD_TYPE1;// 旧版SD } } // ===5=== // 之前省略了CMD58看电压 // 发送CMD0成功之后,进一步发送ACMD41命令,读取SD卡的OCR寄存器 // 本项目操作的SD卡为SDHC,根据规定,若是SDHC或者SDHX,argument第30位需要置1 argument[3] = 0x40; argument[2] = 0x00; argument[1] = 0x00; argument[0] = 0x00; j=0; do { j++; // 首先发送CMD55 if (SD_Send_Command(CMD55, CMD55_R, response, argument) == 1) SD_Send_Command(ACMD41, ACMD41_R, response, argument); else j = SD_IDLE_WAIT_MAX; } while( ((response[0] & MSK_IDLE)==MSK_IDLE) && (j<SD_IDLE_WAIT_MAX) ); // 如果超过查询次数阈值而没有结果,那么直接返回0 if (j >= SD_IDLE_WAIT_MAX) { return 0; } // ===6=== // 之后本来是查询卡种,命令为CMD58这里不查了 return 1; }
其中那个CMD8的发送命令是这样的:
// 通过CMD8命令检测SD卡的版本 char SD_Check_Card_Version(void) { int i; char response_length; unsigned char tmp; unsigned char check_pattern; // CMD8也加入一个唤醒过程 // 根据振南的建议,添加一个唤醒的过程: SPI_CS_HIGH(); SPI_SendByte(0xFF); // 等待不忙 while(0xFF != SPI_RcveByte()); // 发送命令前将CS置低 SPI_CS_LOW(); //============================================ // DEBUG4 //-------------------------------------------- sd_RS232TX_PROC("CMD8-Head..."); sd_RS232TX_PROC(sd_NewRow); //============================================ // 发送CMD头 tmp = 0x40 + 8; //CMD8 SPI_SendByte(tmp); // 发送Argument // CMD8 的 Argument 是有意义的,从高到低如下: // 最低位 argument[0],check pattern,CMD8 发什么 收的 responce 就是什么 // 随便给,这里设个0xAA check_pattern = 0xAA; argument[0] = check_pattern; // 第二位 argument[1],低4位供电电压,高4位保留(写0) // 供电电压那四位,如果电压在2.7~3.6V之间,写 0001b 就可以了 argument[1] = 0x01; // 第三位 和 最高位 argument[2~3], 全部保留写0: argument[2] = 0x00; argument[3] = 0x00; // 发送 //============================================ // DEBUG4 //-------------------------------------------- sd_RS232TX_PROC("CMD8-Arguments..."); sd_RS232TX_PROC(sd_NewRow); //============================================ for (i = 3; i >= 0; i--) { SPI_SendByte(argument[i]); argument[i] = 0x00; } // 发送CRC //============================================ // DEBUG4 //-------------------------------------------- sd_RS232TX_PROC("CMD8-CRC..."); sd_RS232TX_PROC(sd_NewRow); //============================================ SPI_SendByte(0x87); //CMD8 的 CRC 校验是 0x87 (在 check pattern 是 0xAA 的情况下) // CMD8 的回复种类是R7,6个字节(包括CRC) response_length = 5; // 等待回复-有效回复的第一位是0,所以要设置一个有退出机制的循环来等待这个0开头的回复byte //============================================ // DEBUG4 //-------------------------------------------- sd_RS232TX_PROC("CMD8-Waiting Valid Response..."); sd_RS232TX_PROC(sd_NewRow); //============================================ i=0; do { tmp = SPI_RcveByte(); i++; } while( ((tmp&0x80)!=0) && (i<SD_MAX_CMD_RETRY) ); // 满足两个条件,继续等:第一,首位非零;第二,没有超出等待极限 // 如果失败只能返回0退出 if ( i >= SD_MAX_CMD_RETRY ) { //============================================ // DEBUG4 //-------------------------------------------- sd_RS232TX_PROC("CMD8-Waiting Fail..."); sd_RS232TX_PROC(sd_NewRow); //============================================ SPI_CS_HIGH(); return 0; } // 如果成功,接收剩下的 5 个字节 // 其中 response[0] 最低位是 CRC //============================================ // DEBUG4 //-------------------------------------------- sd_RS232TX_PROC("CMD8-Recv Rest Responses..."); sd_RS232TX_PROC(sd_NewRow); //============================================ response[5] = tmp; //============================================ // DEBUG4 //-------------------------------------------- sd_RS232TX_PROC("CMD8-Resp[5]: "); sd_RS232TX_PROC("0x"); sprintf(sd_DEBUG_STR, "%02X",tmp); sd_RS232TX_PROC(sd_DEBUG_STR); sd_RS232TX_PROC(sd_NewRow); //============================================ for (i=response_length-1; i>=0; i--) {// 这里还是有这个多收了的问题 tmp = SPI_RcveByte(); //============================================ // DEBUG4 //-------------------------------------------- sd_RS232TX_PROC("CMD8-Resp"); sprintf(sd_DEBUG_STR, "[%d]: ",i); sd_RS232TX_PROC(sd_DEBUG_STR); sd_RS232TX_PROC("0x"); sprintf(sd_DEBUG_STR, "%02X",tmp); sd_RS232TX_PROC(sd_DEBUG_STR); sd_RS232TX_PROC(sd_NewRow); //============================================ response[i] = tmp; } // 回复内容解析放在这个函数的外面比较合适 // 接收完成之后,直接返回接收成功 SPI_CS_HIGH(); return 1; }
这里还有点儿BUG和其他的需要说明,为了保险
我的response这个static unsigned char 的全局变量数组给了8个元素,CMD8命令结束之后我读了6个byte多读了一个,也没什么大问题,最后一个是0xFF罢了我就没有改
另外,在初始化的函数当中所有注释了DEBUG的语句其实是我开通了串口,通过串口把调试结果实时发送到PC机上,奉劝大家别嫌麻烦,串口很简单的,搞定了以后比仿真器有优势,可以一定程度上避免你单步调试中对时序的影响
相关文章推荐
- MSP430F5438A单片机基于SPI的FatFs移植笔记(一)
- 基于S3C2440的Linux SPI驱动移植笔记
- STM32例程之FATFS文件系统(SPI方式)移植笔记(源码下载)
- 基于ATMEL AT91RM9200的嵌入式Linux移植笔记(4)
- RT-Thread 学习笔记(十一)--- 开启基于RTGUI的LCD显示功能(1)<LCD驱动接口移植>
- FatFs移植笔记
- FatFs移植笔记(R0.09)
- tinyxml移植(基于fatfs文件系统)
- 基于CC2530的SPI通信调试笔记
- 基于SD卡的FatFs文件系统(FatFs移植到STM32)
- RT-Thread 学习笔记(十三)--- 开启基于RTGUI的LCD显示功能(3)<触屏屏驱动移植和测试>
- RT-Thread 学习笔记(七)---开启基于SPI Flash的elmfat文件系统(中)
- STM32 SPI总线移植fatfs产生硬件异常问题
- 利用MSP430F5438A单片机进行SD卡初始化——实战应用(四)与FatFs的整合1
- 蓝桥杯大赛单片机组学习笔记,基于stc15f2k61s2转接板
- 基于ARM11的嵌入式linux系统移植与实现--笔记四
- 基于ARM11的嵌入式linux系统移植与实现--(笔记一)
- 基于以7920为核心的点阵型LCD12864成熟代码工程u8g2的移植——stm32单片机
- LPC1768 SPI模式下SD卡FatFs文件系统移植
- 基于ATMEL AT91RM9200的嵌入式Linux移植笔记(1)