尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

51单片机SD卡读卡器Proteus仿真设计与SPI驱动实现

51单片机SD卡读卡器Proteus仿真设计与SPI驱动实现 简介基于51单片机的SD卡读卡器仿真设计资料面向单片机初学者、电子类课程设计与毕业设计人群用来解决SD卡SPI通信协议理解与仿真验证问题。压缩包共19个文件大小约203KB主要包含Proteus仿真原理图DSN/DBK、Keil工程源码C语言源文件、STARTUP启动代码、UV2工程文件、编译生成的hex/obj/lst中间文件以及MMC卡镜像文件可直接打开仿真环境查看运行效果并对应源码逐行学习。内容覆盖SD卡初始化、GO_IDLE_STATE与SEND_IF_COND识别配置、CMD16设置块长度、CMD24写入与CMD17读取数据块、应答码错误处理及CRC校验等完整流程能够帮助读者从硬件连接和软件驱动两个层面掌握51单片机与SD卡的交互机制并了解仿真调试和程序烧录的整体思路。目前已有215人浏览学习适合需要快速搭建读卡器仿真项目、开展课程设计或二次开发的人员参考使用。1. 51单片机SD卡读卡器为什么仿真先行做SD卡读卡器最大的坑不在C语言而在时序和电气连接。这个基于51单片机SD卡读卡器仿真设计把整条链路放进Proteus51单片机用SPI驱动SD卡模型读CID、按扇区读写LCD1602显示附带的源程序直接编译进仿真。仿真先行能把主控逻辑和硬件问题分开排查。51单片机课程设计里SD卡出错大多在初始化顺序和响应判断而不是C语言本身。Proteus里波形、电平、响应字节都能直接观测这是实物调试给不了的可视性。适合两类人做单片机课程设计的学生要能答辩、能现场演示的完整方案想吃透SPI外设驱动的开发者把SD卡当标准SPI从设备拆解后续换STM32或移植FatFs时只需要替换底层读写两个函数。2. Proteus仿真51单片机SD卡读卡器硬件连接与引脚分配2.1 SD卡SPI模式引脚定义与51单片机接线先说选型。常见做法是用AT89C51配合Proteus元件库里的SD/MMC卡模型。51单片机没有硬件SPI外设SD卡通信全部用软件模拟四个引脚可以任选IO口这里我习惯用P1口的低四位原因是P1口内部带上拉复位后默认高电平贴近SPI模式下SD卡空闲电平的要求。Proteus仿真51单片机搭建读卡器时连线按下面这张表SD卡引脚SPI功能51单片机引脚说明CS/DAT3片选P1.0低电平选中空闲拉高CLK/SCLK时钟P1.1空闲低电平上升沿采样DI/CMDMOSIP1.2主到从数据DO/DAT0MISOP1.3从到主数据VCC供电3.3V网络与单片机电源分开GND地GND与51共地P1.0片选线上接一颗10kΩ上拉电阻到3.3V防止单片机复位期间电平不确定误选中SD卡P1.3的MISO线上同样建议接上拉。仿真时SD卡模型对悬空输入比较敏感少一颗上拉最常见的表现是CMD0之后MISO一直返回0xFF后面排查初始化问题会反复遇到。引脚定义固定下来后源程序开头写成这样接线和代码一一对应#include reg52.h sbit SD_CS P1^0; // 片选低有效 sbit SD_SCK P1^1; // SPI 时钟 sbit SD_MOSI P1^2; // 主出从入 sbit SD_MISO P1^3; // 主入从出这四行用了sbit位寻址对应AT89C51的P1端口。换引脚时只需要改这一处后面所有函数都引用SD_CS、SD_SCK这些符号不用动逻辑。这个习惯在51单片机硬件设计里值得保持外设在代码里和原理图用同一套命名答辩时讲起来也顺。2.2 5V单片机与3.3V SD卡的电气匹配仿真阶段这个点容易被跳过但它恰恰是读卡器原理图里最需要提前说明的地方。SD卡供电是3.3V数据线电平也是3.3VAT89C51供电5VIO口输出高电平接近5V。把5V直接送到SD卡的DI、CLK、CSProteus的卡模型一般不会报错实物上长期这样用会损伤卡。我的处理办法是单片机VCC保持5VSD卡模型接独立的3.3V网络MOSI、SCK、CS三条输出线上各串一颗1kΩ限流电阻。仿真时模型只认逻辑电平串阻不影响通信但这么画出来的原理图可以直接迁移到实物——实物上把这组电阻替换成电阻分压或电平转换芯片即可。MISO方向不用处理SD卡输出的3.3V高于AT89C51高电平阈值2.0V能被正确识别。另外在SD卡VCC和GND之间放一颗100nF去耦电容贴紧卡电源脚。仿真时这颗电容不是必需品但原理图里带入它后面画PCB时不会漏这是我从SD卡原理图类资料里总结的通用习惯。2.3 Proteus元件清单与连接检查顺序搭建仿真图用到的都是Proteus自带库元件不需要额外下载模型清单如下元件型号/数值数量用途单片机AT89C511主控晶振11.0592MHz1主时钟电容33pF2晶振负载电容10µF、100nF各1复位、去耦电阻10kΩ4复位上拉、SD卡上拉电阻1kΩ3数据线串阻SD卡SD/MMC Card1从设备液晶LCD16021状态显示按键BUTTON2复位、触发读卡布线检查固定按三个顺序先看电源和地是否共地再看四根SPI线有没有和LED、按键复用引脚最后双击SD卡模型检查属性页里的容量设置。Proteus的卡模型容量如果偏小后面读到高扇区时数据全FF先回来改这里。LCD1602接8位数据接口和源程序保持一致。3. SD卡SPI模式协议与C51源程序初始化实现3.1 SD卡命令格式与SPI时序SPI模式下SD卡命令包固定6字节第一字节是0x40或运算命令号第二到第五字节是32位参数第六字节是CRC7校验值。SPI模式下除CMD0和CMD8外命令CRC不校验填0xFF即可。响应方面初始化阶段只用R1响应一个字节最高位为0表示命令被接受bit0为1表示当前处于Idle状态。表初始化与读写涉及的关键命令命令参数作用期望R1CMD00x00000000切入SPI模式0x01CMD80x000001AA探测V2.0卡0x01CMD100x00000000读CID寄存器0x00CMD550x00000000标志下条为应用命令0x01ACMD410x40000000初始化卡并等待就绪0x00CMD10x00000000V1.0老卡初始化0x00CMD17扇区号读单扇区0x00CMD24扇区号写单扇区0x00SPI时钟配置为模式0空闲低电平、上升沿发数据、下降沿采样。软件模拟时的动作顺序就是拉高SCK之前放好MOSI拉低SCK之前读走MISO下面的代码严格按这个顺序不要颠倒。3.2 最小可运行的SPI读写与初始化代码底层字节收发用一个函数同时完成读和写整个源程序都依赖它// SPI模式0字节收发传入要发的字节返回收到的字节 unsigned char SPI_Transfer(unsigned char dat) { unsigned char i, rx 0; for (i 0; i 8; i) { SD_MOSI (dat 0x80) ? 1 : 0; // 上升沿前放好输出位 dat 1; SD_SCK 1; // 上升沿SD卡采样MOSI rx 1; if (SD_MISO) rx | 0x01; // 高电平期间读MISO SD_SCK 0; // 下降沿SD卡切换下一位 } return rx; }SPI_Transfer的8次循环就是8个时钟周期MOSI先送位再拉高SCK保证SD卡在上升沿采到稳定电平MISO的读取放在SCK为高期间51单片机单周期IO操作足够快不需要额外延时。读数据时传0xFF即可因为这个函数同时充当SPI读函数。命令发送和初始化流程// 发送命令并等待R1响应返回R1字节 unsigned char SD_Cmd(unsigned char cmd, unsigned long arg, unsigned char crc) { unsigned char i, r1; SD_CS 0; // 低电平选中 SPI_Transfer(0xFF); // 命令前补8个时钟 SPI_Transfer(0x40 | cmd); // 命令号0~63 SPI_Transfer((arg 24) 0xFF); // 参数高字节 SPI_Transfer((arg 16) 0xFF); SPI_Transfer((arg 8) 0xFF); SPI_Transfer(arg 0xFF); // 参数低字节 SPI_Transfer(crc); // CRC无关命令填0xFF for (i 0; i 8; i) { // 最多等8个字节周期 r1 SPI_Transfer(0xFF); if ((r1 0x80) 0) break; // 高位为0才算有效R1 } SD_CS 1; SPI_Transfer(0xFF); // 收尾补时钟 return r1; } unsigned char SD_Init(void) { unsigned char i, r1; for (i 0; i 10; i) SPI_Transfer(0xFF); // 上电至少74个时钟 SD_CS 1; for (i 0; i 10; i) { // 反复CMD0直到Idle r1 SD_Cmd(0, 0x00000000, 0x95); if (r1 0x01) break; } if (r1 ! 0x01) return 1; // 错误1未进入SPI模式 r1 SD_Cmd(8, 0x000001AA, 0x87); // 探测V2.0支持 if (r1 0x01) { for (i 0; i 4; i) SPI_Transfer(0xFF); // 丢弃4字节返回参数 for (i 0; i 100; i) { SD_Cmd(55, 0x00000000, 0xFF); r1 SD_Cmd(41, 0x40000000, 0xFF); // ACMD41 if (r1 0x00) break; // 0x00表示就绪 } } else { for (i 0; i 100; i) { // 老卡走CMD1路径 r1 SD_Cmd(1, 0x00000000, 0xFF); if (r1 0x00) break; } } if (r1 ! 0x00) return 2; // 错误2初始化未完成 return 0; }几个参数必须说明。CMD0的CRC是固化的0x95CMD8的CRC是固化的0x87这两个值改一个就切不进SPI模式。ACMD41参数0x40000000把HCS位置1告诉卡主控支持高容量卡SD卡模型容量小于2GB时HCS填0更稳妥。CMD55的作用是把下一条命令标记为应用命令ACMD41只有在CMD55之后发送才有效。SD_Cmd在每条命令前后各自拉低再拉高CS这个间隙在SD卡规范允许范围内。提示12MHz晶振下软件SPI位周期约1µs接近SD卡初始化阶段400kHz的时钟上限。个别Proteus模型会初始化超时稳妥做法是在SD_Init开头的SPI_Transfer里临时插入两个_nop_()降速初始化完成后恢复正常。3.3 初始化失败时看哪几个字节仿真里最常见的三类失败可以对号入座。第一种SD_Cmd返回0xFF读到的MISO一直是高电平查P1.3接线、查上拉电阻是否误接在P1.0上、确认SD卡模型确实放进了原理图。第二种CMD0永远等不到0x01检查74个时钟是否在上电后第一时间发出单片机复位期间IO为高电平复位时间过长会让卡错失切入窗口。第三种CMD8返回0x05bit2置位表示命令非法说明这是V1.0老卡代码自动走CMD1分支属于正常路径。4. 读卡器核心功能扇区读写、CID读取与FAT16文件解析4.1 单扇区读写与CID读取代码初始化完成后第一件验证SPI链路的事是读CID。CID能读到说明SPI物理层和命令层都通了再做扇区级功能才有意义// 读CID寄存器结果存入16字节数组 unsigned char SD_Read_CID(unsigned char *cid) { unsigned char i, r1; SD_CS 0; SPI_Transfer(0xFF); SPI_Transfer(0x40 | 10); // CMD10读取CID SPI_Transfer(0x00); SPI_Transfer(0x00); SPI_Transfer(0x00); SPI_Transfer(0x00); SPI_Transfer(0xFF); for (i 0; i 8; i) { // 等R1响应 r1 SPI_Transfer(0xFF); if ((r1 0x80) 0) break; } if (r1 ! 0x00) { SD_CS 1; return 1; } for (i 0; i 16; i) cid[i] SPI_Transfer(0xFF); SPI_Transfer(0xFF); SPI_Transfer(0xFF); // 2字节CRC丢弃 SD_CS 1; return 0; }这里读CID命令保持CS拉低期间读完16个字节和SD_Cmd不同因为SD_Cmd返回R1后会把CS拉高后续数据阶段就断了。这也是很多源程序读CID失败的直接原因。扇区读写用CMD17和CMD24参数是扇区号左移9位因为SD卡按字节寻址一个扇区512字节unsigned char SD_Read_Sector(unsigned long sector, unsigned char *buf) { unsigned char i, r1; r1 SD_Cmd(17, sector 9, 0xFF); if (r1 ! 0x00) return 1; // 命令被拒地址超容量或卡忙 for (i 0; i 200; i) { // 等待0xFE数据起始令牌 if (SPI_Transfer(0xFF) 0xFE) break; } if (i 200) return 2; // 数据超时 for (i 0; i 512; i) buf[i] SPI_Transfer(0xFF); SPI_Transfer(0xFF); // 2字节CRCSPI模式不校验 SPI_Transfer(0xFF); return 0; } unsigned char SD_Write_Sector(unsigned long sector, unsigned char *buf) { unsigned char i, r1; r1 SD_Cmd(24, sector 9, 0xFF); if (r1 ! 0x00) return 1; SPI_Transfer(0xFE); // 数据起始令牌 for (i 0; i 512; i) SPI_Transfer(buf[i]); SPI_Transfer(0xFF); // CRC可任意填 SPI_Transfer(0xFF); r1 SPI_Transfer(0xFF); // 写应答 if ((r1 0x05) ! 0x05) return 2; // 应答格式无效 for (i 0; i 65535; i) { // 等忙信号结束 if (SPI_Transfer(0xFF) 0xFF) break; } if (i 65535) return 3; return 0; }写应答的低三位格式是xxx00x05表示数据被卡接受随后反复发送0xFF读到0x00表示卡还在写内部Flash读到0xFF表示写完成。仿真里常见的诡异现象是写后读回全FF优先查0xFE令牌是否在CS拉低期间发出、写之前是否执行过SD_Init。4.2 FAT16文件读取从BPB到簇链只做扇区读写还不算完整读卡器我一般会在源程序里加一段FAT16文件读取演示时直接把卡里的文本文件内容显示到LCD。FAT16布局是引导扇区、FAT表、根目录区、数据区四段先读第0扇区解析BPBBPB偏移长度含义用途0x0B2每扇区字节数固定5120x0D1每簇扇区数换算数据位置0x0E2保留扇区数定位FAT起始0x101FAT表数量通常为20x112根目录条目数计算根目录扇区数0x162FAT表大小定位根目录根目录起始扇区保留扇区数FAT表数量×FAT表大小数据区起始扇区根目录起始扇区根目录条目数×32/512。目录项32字节文件名在偏移0首簇号低16位在偏移26文件大小在偏移28。找到文件名后按FAT簇链往下读// 读取文件下一簇号fat_start是FAT表起始扇区 unsigned int FAT16_Next_Cluster(unsigned int cluster, unsigned long fat_start, unsigned char *fat_buf) { unsigned long fat_sector fat_start (cluster * 2) / 512; unsigned int offset (cluster * 2) % 512; SD_Read_Sector(fat_sector, fat_buf); return fat_buf[offset] | ((unsigned int)fat_buf[offset 1] 8); }FAT16的表项宽度是2字节簇号乘2得到表内偏移表项值就是下一簇号读到0xFFFF表示文件结束。每簇对应的数据扇区数据区起始扇区簇号-2×每簇扇区数用SD_Read_Sector搬数据循环即可。注意FAT16的簇号从2开始计0和1是保留值换算数据扇区时一定要做簇号-2的偏移漏掉这个会整体错一个簇读出的文件开头是乱的。4.3 LCD1602显示与读卡流程整合显示层用LCD1602的8位接口初始化写0x38、0x0C、0x06三条命令即可。我把读卡演示做成三步按键触发第一步显示SD Init结果第二步显示CID前8字节的十六进制第三步读指定扇区前16字节或读FAT16文件内容。每一步都有屏幕反馈答辩时能顺着状态讲清楚当前执行到哪一层。51单片机没有格式化输出的硬件支持不引入sprintf字节转十六进制用查表unsigned char hex2char(unsigned char x) { return (x 10) ? (0 x) : (A x - 10); }16字节一行、每字节两个字符正好排满LCD的第一行第二行放文件名或扇区号。整个源程序的顶层就是按键扫描加状态机初始化失败时把错误码显示出来对应第3章3.3节的判断表排查。5. 仿真调试必查点与实物移植的3个关键改动5.1 用虚拟示波器确认SPI时序Proteus的虚拟示波器直接拖到P1.1和P1.3两个引脚触发选下降沿。正常波形是SCK空闲低电平、每8个脉冲一组组间有明显间隔MISO在CMD0阶段能看到0x01ACMD41完成后能看到0x00。如果SCK波形不完整或MOSI与SCK边沿对不齐回第3章检查SPI_Transfer的置位顺序。5.2 仿真现象与处理对照现象判断处理初始化返回1未进入SPI模式查CMD0的CRC、上电74时钟初始化返回2初始化超时查CMD55、HCS位、时钟速度读扇区返回1命令被拒查扇区号是否超出模型容量写后读回全FF写入未生效查0xFE令牌和CS时序5.3 从仿真到实物的3个改动仿真跑通后转实物三处必改。第一电平转换把原理图里的1kΩ串阻换成真正的电平匹配方案MOSI、SCK、CS三条输出线用电阻分压或74LVC245MISO方向保持直连SD卡输出的3.3V高于51单片机高电平阈值2.0V能被直接识别。第二卡座供电实物卡座VCC脚必须就近放100nF和10µF两颗电容仿真里没有的电源噪声在实物上表现为初始化时好时坏。第三初始化降速实物SD卡对初始化阶段400kHz时钟上限更敏感在SD_Init开头的SPI_Transfer里加nop延时初始化完成后再恢复正常速度只影响几十个时钟。收尾验证用一个动作把SD卡格式化成FAT16放入固定内容的短文本文件源程序锁定目标文件名LCD能显示文件内容和长度说明SPI物理层、命令层、文件系统层整条链路全部打通。这时候再往FatFs迁移或改STM32版本都只需要替换底层读写两个函数。本文还有配套的精品资源点击获取
返回列表