【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二十四:SD卡模块

article/2025/3/10 23:26:26

 

驱动SD卡是件容易让人抓狂的事情,驱动SD卡好比SDRAM执行页读写,SD卡虽然不及SDRAM的麻烦要求(时序参数),但是驱动过程却有猥琐操作。除此此外,描述语言只要稍微比较一下C语言,描述语言一定会泪流满面,因为嵌套循环,嵌套判断,或者嵌套函数等都是它的痛。.

史莱姆模块是多模块建模的通病,意指结构能力非常脆弱的模块,暴力的嵌套行为往往会击垮模块的美丽身躯,好让脆弱结构更加脆弱还有惨不忍睹,最终搞垮模块的表达能力。描述语言预想驾驭SD卡,关键的地方就是如何提升模块的结构能力。简单而言,描述语言如何不失自身的美丽,又用自身的方法,去实现嵌套循环或者嵌套函数等近似的内容呢?

低级建模I之际,论结构能力它确实有点勉强,所以SD卡的实验才姗姗来迟。如今病猫已经进化为老虎,而且进化之初的新生儿都会肌饿如心焚,理智也不健全。因为如此,低级建模II才会不停舔着嘴唇,然后渴望新生的第一祭品。遇见SD卡,它仿佛遇见美味的猎物,口水都下流到一塌糊涂。

诸位少年少女们,让我们一起欢呼活祭仪式的开始吧!

二十一世纪的今天,SD卡演化的速度简直迅雷不及掩耳,如今SD卡已经逐渐突破64GB大关。对此,SD卡也存在N多版本,如版本SDV1.×,版本SDV2,或者SDHCV2等,当然未来还会继续演化下去。所谓版本是指建造工艺还有协议,粗略而言,版本SDV1.×是指容量为2GB以下的SD卡,版本SDV2则指容量为2GB~4GB之间的SD卡,版本SDHCV2则是容量为4GB以上的SD卡。

话虽如此,不过实际情况还要根据各个厂商的心情而定,有些厂商的SD卡虽为4GB,但是版本却是SDV1.×,还有厂商的SD卡的虽为 2GB,不过版本却是SDV2,情况尽是让人哭笑不得。此外,版本不会印刷在硬件的表面上,而且不同版本也有不同驱动方法。俗语有云,擒贼先擒卒——凡事从娃娃抓起,所以笔者遵循伟大的智慧,从版本SDV1.×开始动手。

clip_image002

图24.1 SPI模式。

SD卡有SDIO还有SPI两种模式,后者简单又省事,所以SPI模式都是众多懒惰鬼的喜爱。SPI模式一般只用4只引脚,而且主机(FPGA)与从机(SD卡)之间的链接如图24.1所示,至于引脚的聂荣如表24.1所示:

表24.1 SD卡SPI模式的引脚说明。

引脚

说明

SD_CLK

串行时钟,闲置为高

SD_NCS

片选,闲置为高,拉低有效

SD_DI

数据输入,也是主机输出

SD_DOUT

数据输出,也是主机输入

虽然DS1302也有SPI,但是数据线是双向IO,反之SD卡则是一对出入的数据线。话虽如此,它们两者都有乖乖遵守SPI的传输协议,即下降沿设置数据,上升沿锁存数据。

clip_image004

图24.2 写一个字节(主机视角)。

图24.2是主机视角写一个字节的理想时序。主机会利用时钟的下降沿,由高至低发送一个字节的数据。

clip_image006

图24.3 读一个字节(主机视角)。

图24.2是主机视角读一个字节的理想时序。从机会利用时钟的下降沿,由高至低发送一个字节的数据,主机则会利用时钟信号的上升沿,由高至低读取一个字节的数据。

clip_image008

图24.4 同时读写一个字节(主机视角)。

我们知道SD卡有一对读写的数据线,为了节省时间,数据读写是同时发生的。如图24.4所示,那是主机在同时读写的理想时序,读者可以看成是图24.2 还有图24.3的结合体。

对此,Verilog可以这样描述,结果如代码24.1所示:

1.    case(i)
2.        
3.        0,1,2,3,4,5,6,7:
4.        begin
5.             rDI <= iData[ 7-i ];                    
6.            
7.             if( C1 == 0 ) rSCLK <= 1'b0;
8.            else if( C1 == isHalf ) rSCLK <= 1'b1;
9.                                
10.            if( C1 == isQuarter ) D1[ 7-i ] <= SD_DOUT;
11.                                
12.            if( C1 == isFull -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end
13.            else begin C1 <= C1 + 1'b1; end
14.        end

代码24.1

如代码24.1所示,第12~13行表示步骤逗留的时间,其中isFull表示一个时钟周期。第7~8行表示,C1为0拉低时钟,C1为半个周期则拉高时钟。第5行表示,任何时候都更新数据,也可以看成C1为0输出数据。第10行表示,C1为四分之一周期锁存数据。

第3行表示,步骤0~7造就一个字节的读写。还有第5~10行的 D1[7-i] 表示,读写数据由高至低。

好奇的朋友一定会疑惑道,为何第10行的锁存行为不是时钟的半周期(上升沿),而是四分之一呢?原因很单纯,因为数据在这个时候最为有效。

clip_image010

图24.5 写命令(主机视角)。

当然,SD卡不是给足两只骨头就会满足的哈士奇 ... 为此,除了单纯的读写数据意外,SD卡还有所谓的写命令,而写命令则是读写字节的复合体。如图24.5所示,那是主机写命令的理想时序,主机先由高至低发送6个字节的命令。SD卡接受完毕以后,便会反馈一个字节的数据。期间,片选信号必须处于拉低状态。对此,Verilog可以这样表示,结果如代码24.2所示:

1.     case( i )
2.            
3.         0:
4.         begin rCMD <= iAddr; i <= i + 1'b1; end                      
5.    
6.         1,2,3,4,5,6:
7.         begin T <= rCMD[47:40]; rCMD <= rCMD << 8; i <= FF_Write; Go <= i + 1'b1; end
8.                         
9.          7:
10.         begin i <= FF_Read; Go <= i + 1'b1; end
11.                         
12.         8:
13.         if( C2 == 100 ) begin C2 <= 10'd0; i <= i + 1'b1; end
14.         else if( D1 != 8'hff ) begin C2 <= 10'd0; i <= i + 1'b1; end
15.         else begin C2 <= C2 + 1'b1; i <= FF_Read; Go <= i; end
16.                         
17.         ...
18.                                              
19.         12,13,14,15,16,17,18,19: 
20.         begin
21.              rDI <= T[ 19-i ];        
22.              if( C1 == 0 ) rSCLK <= 1'b0;
23.             else if( C1 == isHalf ) rSCLK <= 1'b1;
24.                                
25.             if( C1 == isQuarter ) D1[ 19-i ] <= SD_DOUT;
26.                              
27.             if( C1 == isFull -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end
28.             else begin C1 <= C1 + 1'b1; end
29.         end
30.                         
31.        20: 
32.        begin i <= Go; end

代码24.2

步骤12~20是读写一个字节的伪函数,步骤0准备6个字节的命令,步骤1~6由高至低发送命令,并且进入伪函数。步骤7进入伪函数,并且读取一个字节的反馈数据(注意FF_Write与FF_Read都指向步骤12)。反馈数据一般都是 8’hff 以外的结果,如果不是则重复读取反馈数据100次,如果SD卡反应正常,都会在这100次以内反馈 8’hff以外的结果。

简单而言,如何驱动SD卡就是如何使用相关的命令。版本SDV1.×的SD卡只需4个命令而已,亦即:

(一)CMD0,复位命令;

(二)CMD1,初始化命令;

(三)CMD24,写命令;

(四)CMD17,读命令。

CMD0用来复位SD卡,好让SD卡处于(IDLE)待机状态。CMD1用来初始化SD卡,好让SD卡处于(Transfer)传输状态。CMD24将512字节数据写入指定的地址,CMD17则将512字节数据从指定的地址读出来。

clip_image012

图24.6 CMD0的理想时序图。

图24.6是CMD0的理想时序图,首先在T1延迟1ms给予SD卡热身时间,然后再在T2给予80个准备的时钟。T3之际拉低CS,T4之际则发送命令CMD0 { 8’h40, 32’d0, 8’h95},然后等待SD卡反馈数据R1。如果SD卡成功接收命令CMD0,内容则是8’h01。T5之际拉高CS,T6之际再8个结束时钟。对此,Verilog可以这样描述,结果如代码24.3所示:

1.     case( i )
2.                    
3.        0: // Disable cs, prepare Cmd0
4.        begin rCS <= 1'b1; D4 <=  {8'h40, 32'd0, 8'h95}; i <= i + 1'b1; end
5.                        
6.        1: // Wait 1MS for warm up;
7.        if( C1 == T1MS -1) begin C1 <= 16'd0; i <= i + 1'b1; end
8.        else begin C1 <= C1 + 1'b1; end
9.    
10.        2: // Send 80 free clock
11.        if( C1 == 10'd10 ) begin C1 <= 16'd0; i <= i + 1'b1; end
12.        else if( iDone ) begin isCall[0] <= 1'b0; C1 <= C1 + 1'b1; end
13.        else begin isCall[0] <= 1'b1; D1 <= 8'hff; end
14.                         
15.        3: // Enable cs
16.        begin rCS <= 1'b0; i <= i + 1'b1; end
17.                        
18.        4: // Try 200 time, ready error code.
19.        if( C1 == 10'd200 ) begin D2 <= CMD0ERR; C1 <= 16'd0; i <= 4'd8; end
20.        else if( iDone && iData != 8'h01) begin isCall[1] <= 1'b0; C1 <= C1 + 1'b1; end
21.        else if( iDone && iData == 8'h01 ) begin isCall[1] <= 1'b0; C1 <= 16'd0; i <= i + 1'b1; end 
22.        else isCall[1] <= 1'b1;  
23.                         
24.        5: // Disable cs
25.        begin rCS <= 1'b1 ; i <= i + 1'b1; end
26.                         
27.        6: // Send free clock
28.        if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
29.        else begin isCall[0] <= 1'b1; D1 <= 8'hff; end
30.    
31.        7: // Disable cs, ready OK code
32.        begin D2 <= CMD0OK; i <= i + 1'b1; end //; 
33.    
34.         8: // Disbale cs, generate done signal
35.        begin rCS <= 1'b1; isDone <= 1'b1; i <= i + 1'b1; end
36.                         
37.        9:
38.        begin isDone <= 1'b0; i <= 4'd0; end

代码24.3

我们先假设 isCall[1]执行写命令,isCall[0]则是执行读写字节。如代码24.3所示,步骤0用来准备CMD0命令。步骤1延迟1ms。步骤2执行10次无意义的读写,以示给予80个准备时钟。在此读者稍微注意一下第12行,每当完成一次读写C1便会递增一下,C1递增10次便表示读写执行10次。

步骤3拉低CS,并且步骤4发送命令。步骤4可能会吓坏一群小朋友,不过只要耐心解读,其它它并不可怕。首先执行第22行的写命令,如果反馈数据不为8’h01(第20行),消除isDo便递增C1,然后再返回第22行。如果反馈数据为 8’h01(第21行)

,消除isDo与C1然后继续步骤。如果重复执行100次都失败,D2赋值CMD0的失败信息,消除C1并且i直接步骤8。

步骤5拉低CS,步骤6则给予8个结束时钟。步骤7为D2赋值CMD0的成功信息,步骤8~9拉高CS并且产生完成信号。

clip_image014

图24.7 CMD1的理想时序图。

图24.7是CMD1的理想时序图,T0&T1之际拉低CS并且发送六个字节的命令CMD1 {8’h41,32’d0,8’hff}。SD卡接受命令以后便反馈数据R1——8’h00。T2&T3之际拉高CS并且给予8个结束时钟。Verilog的描述结果如代码24.4所示:

1.     case( i )
2.                    
3.         0: // Enable cs, prepare Cmd1
4.         begin rCS <= 1'b0; D4 <= { 8'h41,32'd0,8'hff }; i <= i + 1'b1; end
5.                         
6.         1: // Try 100 times, ready error code.
7.         if( C1 == 10'd100 ) begin D2 <= CMD1ERR; C1 <= 16'd0; i <= 4'd5; end
8.         else if( iDone && iData != 8'h00) begin isCall[1]<= 1'b0; C1 <= C1 + 1'b1; end
9.         else if( iDone && iData == 8'h00 ) begin isCall[1] <= 1'b0; C1 <= 16'd0; i <= i + 1'b1; end 
10.         else isCall[1] <= 1'b1;  
11.                         
12.         2: // Disable cs
13.         begin rCS <= 1'b1; i <= i + 1'b1; end
14.                         
15.        3: // Send free clock
16.        if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
17.        else begin isCall[0] <= 1'b1; D1 <= 8'hff; end
18.                         
19.        4: // Disable cs, ready OK code.
20.        begin D2 <= CMD1OK; i <= i + 1'b1; end
21.                         
22.        5: // Disable cs, generate done signal
23.        begin rCS <= 1'b1; isDone <= 1'b1; i <= i + 1'b1; end
24.                         
25.        6:
26.        begin isDone <= 1'b0; i <= 4'd0; end

代码24.4

如代码24.4所示,步骤0准备命令CMD1。步骤1重复发送CMD1命令100次,直至反馈数据R1为8’h00为止,否则反馈错误信息。步骤2拉高CS,步骤3则给予结束时钟。步骤4反馈成功信息,步骤5~6拉高CS之余也产生完成信号。

好奇的同学一定会觉得疑惑,命令CMD0与命令CMD1同样反馈数据R1,为何前者是8’h01,后者则是8’h00呢?事实上,R1的内容也反应SD卡的当前状态,SD卡有待机状态(IDLE)还有传输状态(Transfer)等两个常见状态。

clip_image016

图24.8 版本V1.×的初始化流程图。

如图24.8所示,那是版本V1.x的初始化流程图。主机先发送CMD0,SD卡接收以后如果反馈R1为8’h01便继续流程,否则重复发送CMD0。主机接着发送CMD1,如果SD卡接收并且反馈R1为8’h00,该结果表示SD卡以从待机状态进入传输状态,余下CMD24还有CMD17才有效。


http://chatgpt.dhexx.cn/article/jCJavnQ1.shtml

相关文章

黑金AX7Z100 FPGA开发板移植LWIP库(二)PL端

前言 上一篇博文中实验了PS端移植LwIP库的演示程序。本篇接下来基于Vivado17.4整理比较详细的PL端移植过程。 一、Vivado 工程建立 1、新建一个空工程&#xff0c;名称为net_lwip_pl。 2、FPGA芯片选择xc7z100ffg900-2。 二、配置PS&PL系统硬件 1、工程建好以后&…

【FPGA从0开始系列】黑金EP4CE10F17C8开发板按键实验(二)

项目目录 1.实验目的2.准备阶段3.实验原理4.编写Verilog HDL程序5.配置引脚6.查看和分析RTL7.下载程序8.总结 1.实验目的 查阅AX4010黑金系列用户手册&#xff0c;编写按键程序&#xff0c;实验简单的按键控制LED的功能&#xff0c;同时学习 Quartus RTL Viewer 的使用 2.准备…

汉诺塔(C语言实现)

目录 汉诺塔的游戏规则&#xff1a; 当A只有一个环的时候&#xff1a; 当A只有两个环的时候&#xff1a; 当A只有三个环的时候&#xff1a; 思路&#xff1a; 当n1时&#xff1a; 当n2时&#xff1a; 当n3时&#xff1a; 当n4时&#xff1a; 见代码 运行截图 汉诺塔的游戏…

【C语言】汉诺塔问题

汉诺塔是一个非常经典的问题&#xff0c;其背后是一个传说故事&#xff1a; 在世界中心贝拿勒斯&#xff08;在印度北部&#xff09;的圣庙里&#xff0c;一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候&#xff0c;在其中一根针上从下到上地穿好了由大到小的6…

汉诺塔C语言步骤解析

汉诺塔问题在C语言中一般采用递归法来写&#xff0c;假设有A、B、C三根棒&#xff0c;A棒放着若干个圆盘&#xff0c;将其移动到C棒上&#xff0c;中途可在B棒中暂时放置圆盘。 分析&#xff1a; (1) 如果只有一个圆盘&#xff0c;则把该圆盘从A棒移动到C棒 (2) 如果圆盘数量…

汉诺塔递归的c语言实现(递归)

对于递归来讲, 汉诺塔实际是经典到不能再经典的例子了, 每个数据结构的教材对会提到. 但是到最后只给出一段类似下面的一段代码: #include<stdio.h>void move(int n,char a,char b,char c) {if(n1)printf("\t%c->%c\n",a,c); //当n只有1个的时候直接从…

C语言编程实现汉诺塔问题

C语言编程实现汉诺塔问题 1.首先解释一下&#xff0c;汉诺塔问题&#xff1a;古代梵塔内有A、B、C3个座&#xff0c;开始时A座上面有64个盘子&#xff0c;盘子大小不等&#xff0c;大的在下&#xff0c;小的在上。一个老和尚想把64个盘子从A移到C&#xff0c;规定移动过程中3个…

汉诺塔C语言实现(纯代码)

a、b、c三座塔&#xff0c;将n个从小到大&#xff08;自上而下&#xff09;的圆盘从a移动到c&#xff0c;移动期间小圆盘必须在大圆盘上面&#xff0c;问移动步骤。 #include<stdio.h>int main() {void hanoi(int n,char one,char two,char three);int m;printf("…

【汉诺塔】C语言递归解法,深层次地带你理解汉诺塔公式

目录 汉诺塔公式 汉诺塔问题在数学层面的公式&#xff1a; C语言递归公式 两层汉诺塔 三层汉诺塔 递归问题可谓是学习C语言以来的第一个拦路虎&#xff0c;而汉诺塔问题更是递归中对新手很不友好的一道经典题&#xff0c;我们接下来从公式角度和更深层的图解角度来让你理解…

汉诺塔c语言代码实现

汉诺塔&#xff08;Tower of Hanoi&#xff09;&#xff0c;又称河内塔&#xff0c;是一个源于印度古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子&#xff0c;在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重…

【C语言】汉诺塔超详解(脑把脑解释)

文章目录 一、前言二、准备工作关于递归函数 三、汉诺塔步数计算详解&#xff1a;函数的设计符合递归函数的两个基本条件&#xff1a; 四、汉诺塔步骤打印&#xff08;绝对详细&#xff0c;仔细看就能看懂 &#xff09;铺垫换个思路&#xff1a;柱子竟然是可以移动的&#xff0…

汉诺塔C语言实现

汉诺塔背景 文章目录 汉诺塔背景前言一、什么是汉诺塔&#xff1f;二、汉诺塔问题的背景三、汉诺塔问题的代码实现1.代码原理2.代码实现 前言 汉诺塔问题&#xff0c;是心理学实验研究常用的任务之一。该问题的主要材料包括三根高度相同的柱子和一些大小及颜色不同的圆盘&…

C语言汉诺塔详解

问题概述&#xff1a;在A柱子上从下往上按照大小顺序摞着N片黄金圆盘。要求把圆盘从下面开始按大小顺序重新摆放在另一根(B or C)柱子上。并且规定&#xff0c;在小圆盘上不能放大圆盘&#xff0c;在三根柱子之间一次只能移动一个圆盘。 如图所示&#xff1a; (ps&#xff1a;…

C语言实现汉诺塔详细步骤(递归与非递归)及代码

前言 C语言汉诺塔问题是一个经典的问题&#xff0c;在学习编程的初学者中非常流行。它涉及到了递归的思想&#xff0c;能够帮助我们理解递归的基本原理。 首先&#xff0c;我们来了解一下汉诺塔的问题。汉诺塔问题是指&#xff1a;有三根柱子A,B,C&#xff0c;A柱子上有n个盘…

汉诺塔(C语言)

文章目录 一、什么是汉诺塔问题&#xff1f; 二、实现步骤 三、代码实现 四、代码分析 一、什么是汉诺塔问题&#xff1f; 汉诺塔&#xff08;Tower of Hanoi&#xff09;&#xff0c;又称河内塔&#xff0c;是一个源于印度古老传说的益智玩具。大梵天创造世界的时候做了三根金…

图文详解汉诺塔(附C语言实现代码)

关注、星标公众号&#xff0c;直达精彩内容 来源&#xff1a;http://a.nxw.so/1iDyQD 一、前言 汉诺塔&#xff08;Tower of Hanoi&#xff09;&#xff0c;又称河内塔&#xff0c;是一个源于印度古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子&#xff0c;在一根…

C语言 - 汉诺塔详解(超详细)

文章目录 一、前言二、玩游戏三、汉诺塔打印步数四、汉诺塔打印步骤 一、前言 一、汉诺塔&#xff08;Tower of Hanoi&#xff09;&#xff0c;又称河内塔&#xff0c;是一个源于印度古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子&#xff0c;在一根柱子上从下…

汉诺塔问题以及青蛙跳台阶问题(附C语言代码)

汉诺塔问题&#xff1a; 汉诺塔问题的源于印度一个古老传说的益智玩具。大焚天创造世界的时候做了三根金刚石柱子&#xff0c;在一根柱子上从下往上按照先大后小的顺序摞着64片圆盘。大焚天命令婆罗门把圆盘从下面按大小顺序重新摆放在另一根柱子上&#xff0c;并且规定在小盘…

Mac上显示实时网速小工具

1.iStat Menus 6 功能较丰富&#xff0c;比如你想看大冬天电脑温度等&#xff0c;毕竟Mac本出现过无法充电和无法开机的情况&#xff0c;明明是90多的点&#xff08;自己MBP2018出现过&#xff09; 下载地址&#xff1a;https://bjango.com/mac/istatmenus/ 激活&#xff1a…

在mac上显示网速的软件——iStat Menus 5:

在mac上显示网速的软件——iStat Menus 5: https://bjango.com/mac/istatmenus/ 注册码: Email: 982092332qq.com SN: GAWAE-FCWQ3-P8NYB-C7GF7-NEDRT-Q5DTB-MFZG6-6NEQC-CRMUD-8MZ2K-66SRB-SU8EW-EDLZ9-TGH3S-8SGA 「注册码源于:http://www.pc6.com/mac/111587.html」 转载于…