【GD32】从零开始学GD32单片机高级篇——SDIO外设详解(GD32F470ZGT6)

目录

  • 简介
  • 总线拓扑
  • 总线操作
    • “无响应” 和 “无数据” 操作
    • 多块读写操作
    • 数据流读写操作
  • 总线协议
    • 命令
    • 响应
      • R1/R1b (普通命令响应)
      • R2 (CID, CSD 寄存器)
      • R3 (OCR 寄存器)
      • R4 (Fast IO)
      • R4b(Fast IO)
      • R5 (中断请求)
      • R5b(中断请求)
      • R6 (发布的RCA响应)
      • R7 (卡接口条件)
  • SD卡操作模式
    • 卡识别
    • 数据操作模式
  • 例程

简介

SDIO(Secure Digital Input and Output)全称安全的数字输入输出接口;是从SD内存卡接口的基础上演化出来的一种外设接口。SDIO接口兼容以前的SD内存卡,并且可以连接支持SDIO接口的设备。它可以连接SD 卡、SD I/O卡、多媒体卡(MMC)和 CE-ATA卡主机接口;除了连接常见的存储介质外,它还可以用于连接WiFi、GPS、摄像头等设备。

本文基于GD32F470ZGT6芯片,其SDIO外设对存储卡的兼容性如下:

  1. MMC:与多媒体卡系统规格书 V4.2 及之前的版本全兼容。有三种不同的数据总线模式:
    1 位(默认)、4 位和 8 位;
  2. SD 卡:与 SD 存储卡规格版本 2.0 全兼容;
  3. SD I/O:与 SD I/O 卡规格版本 2.0 全兼容,有两种不同的数据总线模式:1 位(默认)和 4
    位;
  4. CE-ATA:与 CE-ATA 数字协议版本 1.1 全兼容;

总线拓扑

在这里插入图片描述
SDIO外设通过时钟线(CLK)、命令线(CMD)和数据线(DATA)进行通讯;其中,根据SDIO模式的不同数据线的数量也会有不同,分别有1bit模式、4bit模式和8bit模式

在SDIO总线上的每一个消息都是由以下3个部分组成的:

  1. 命令:命令是启动一个操作的令牌,从主机发送到卡;由CMD线进行传输。
  2. 响应:响应是从卡发送到主机,作为命令的回应;由CMD线进行传输。
  3. 数据:数据可以从卡传输到主机或者从主机传输到卡;通过DATA线传送。

其中SDIO的命令也分为2种——流命令和面向块的命令

  1. 流命令:这些命令发起连续的数据流,只有当 CMD 信号线上出现停止命令时,数据传输
    终止。该模式将命令的开销减少到最低(仅支持 MMC)。
  2. 面向块的命令:这些命令成功发送一个数据块后紧跟一个 CRC 校验。读和写操作允许单
    个或多个块传输。与连续读相同,当 CMD 信号线上出现停止命令时,多块传输终止。

总线操作

“无响应” 和 “无数据” 操作

总线上的基本操作是命令/响应操作,如下图;有“无响应”操作和“无数据”操作两种。
在这里插入图片描述
无响应操作即主机只发送命令,但设备不响应;无数据操作即主机发送命令,设备响应,但设备和主机都不发送数据。

多块读写操作

多块读操作即主机发送命令,设备响应后,数据会在DAT线上以块的形式,一块块发送到主机,每个块的后面都紧跟CRC的校检;需要停止读数据时,主机发送停止命令,设备响应后即可停止。
SD存储卡、SD I/O卡(包括仅IO卡和组合卡)和CE-ATA设备直接的数据传输是以数据块
的方式完成的。
在这里插入图片描述
在这里插入图片描述

数据流读写操作

MMC卡以数据块或数据流方式进行数据传输。数据流读操作也是主机发送命令,设备响应后,设备通过DAT线发送数据,在该模式下数据是连续不断的,只到主机发送停止命令,设备响应后停止。
在这里插入图片描述
在这里插入图片描述

总线协议

命令

SDIO有4种控制卡的命令:

  1. 广播命令(bc),发送到所有卡,没有响应;
  2. 带响应的广播命令(bcr),发送到所有卡,同时从所有卡收到响应;
  3. 寻址(点对点)命令(ac),发送到寻址的卡上,DAT 信号线没有数据传输;
  4. 寻址(点对点)的数据传输的命令(adtc),发送到寻址的卡上,DAT 信号线进行数据传输。

所有命令都是48位的固定码长,定义如下图所示。
在这里插入图片描述
一个命令总是从一个起始位(始终为0)开始,随后的位表示传输的方向(主机=1)。接下来的6位表示命令的索引,该值被解释为一个二进制编码的数字(0到63之间)。一些命令需要一
个参数(例如,一个地址),由32位编码。上表中的表示为“x”的值表示这个变量依赖于该命
令。所有的命令有一个CRC 7位校验,由结束位(总是 1)终止。

响应

响应的类型有7种:

  1. R1/R1b: 普通命令响应
  2. R2: CID, CSD 寄存器
  3. R3: OCR 寄存器
  4. R4: Fast I/O
  5. R5: 中断请求
  6. R6: 发布的 RCA 响应
  7. R7: 卡接口条件

除了R2的响应长度是136位,其他响应长度均为48位

R1/R1b (普通命令响应)

在这里插入图片描述
长度为48位,位[45:40]指示要响应的命令索引。

R2 (CID, CSD 寄存器)

在这里插入图片描述
长度为136位,CID寄存器的内容作为对命令CMD2和CMD10的响应被发送,CSD寄
存器的内容将作为以CMD9响应被发送。

R3 (OCR 寄存器)

在这里插入图片描述
长度为48位,OCR寄存器的内容作为SD存储卡或MMC的响应被发送。

R4 (Fast IO)

R4响应仅适用于MMC。长度为48位。参数域包括选定卡的RCA,被读取或写入寄存器的地址及其内容。
在这里插入图片描述

R4b(Fast IO)

R4b响应仅适用于SD I/O卡。长度为48位。SD I/O卡接收到CMD5命令后会返回一个唯一的 SD I/O卡响应R4。
在这里插入图片描述
在这里插入图片描述

R5 (中断请求)

在这里插入图片描述
R5响应仅适用于MMC。长度为48位。若这个响应由主机产生,参数中RCA域为0x0。

R5b(中断请求)

在这里插入图片描述
R5b响应仅适用于SD I/O卡。SD I/O卡对于CMD52和CMD53命令的响应是R5。

R6 (发布的RCA响应)

在这里插入图片描述
代码长度为48位。位[45:40]表示对CMD3响应的命令索引。参数字段的16个最高位比特用于已发布的RCA号。

R7 (卡接口条件)

在这里插入图片描述
在这里插入图片描述
仅适用于SD存储卡。代码长度为48位。位[19:16]表明该卡支持的电压范围。

SD卡操作模式

卡识别

卡识别过程如下:
在这里插入图片描述
上电后,主机进入卡识别模式,寻找总线上的新卡;而卡则处于空闲状态。在与卡进行通信前主机必须先知道卡的电压范围,对于SD卡,需调用ACMD41命令;对于MMC卡需调用CMD1命令;对于SD I/O卡,需调用CMD5命令。另外,对于支持SDIO V2.0的SD卡,在调用ACMD41命令前还需调用CMD8命令,让卡知道主机支持物理层2.00协议及高版本功能。
接着卡会进入准备状态,然后发送CMD2命令使卡进入识别状态;最后再发送CMD3,使卡进入待机状态。

数据操作模式

数据操作模式过程如下:
在这里插入图片描述
数据操作模式比较复杂,这里只介绍SD卡的流程。
对于写操作,主机先发CMD7,选中对应的卡,使其进入传输状态。接着发送CMD23(单块写命令)或CMD24(多块写命令)使卡进入数据接收状态,然后主机就可以开始传输数据。主机传输完指定的数据后必须发送CMD12命令终止数据传输,此时卡会进入编程状态。当卡完成数据的存储后,会自动返回传输状态。上面的过程中我们可以通过CMD13命令轮询检查卡的状态,判断编程状态是否已退出。
对于读操作,同样,主机先发CMD7命令,选中对应的卡,卡进入传输状态。接着发送CMD17(单块读操作)或CMD18(多块读操作)使卡进入数据发送状态,然后主机开始接收数据,数据传输完成卡会自动返回传输状态。

例程

例程中实现了SD卡的初始化、信息读取、单块读写操作、锁卡操作和多块读写操作。

int main(void)
{nvic_priority_group_set(NVIC_PRIGROUP_PRE1_SUB3);nvic_irq_enable(SDIO_IRQn, 0, 0);debug_init();printf("sdcard demo\r\n");// 初始化SD卡int i = 5;while ((SD_OK != sdcard_init()) && (i--));if (i == 0){printf("sdcard init failed\r\n");while (1);}// 打印卡信息sdcard_info_print();// 测试sdcard_read_write_test();sdcard_lock_test();sdcard_multi_read_write_test();while(1){}
}

兆易创新官方固件库很贴心地自带一个SDIO外设的驱动库,在sdio的例程里面;SD卡初始化的代码如下。

static sd_error_enum sdcard_init(void)
{sd_error_enum status = SD_OK;uint32_t cardstate = 0;// 初始化SD卡if ((status = sd_init()) != SD_OK){printf("sdcard init failed\r\n");return status;}// 获取SD卡信息if(SD_OK != (status = sd_card_information_get(&sd_cardinfo))){printf("get sdcard info failed\r\n");return status;}// 片选SD卡if(SD_OK != (status = sd_card_select_deselect(sd_cardinfo.card_rca))){printf("select card failed\r\n");return status;}// 获取SD卡状态if (SD_OK != (status = sd_cardstatus_get(&cardstate))){printf("get card status failed\r\n");return status;}else if(cardstate & 0x02000000){printf("the card is locked!\r\n");while(1);}// 设置4bit总线模式if(SD_OK != (status = sd_bus_mode_config(SDIO_BUSMODE_4BIT))){printf("set bus mode failed\r\n");return status;}// 设置DMA传输模式if(SD_OK != (status = sd_transfer_mode_config(SD_DMA_MODE))){printf("set dma mode failed\r\n");return status;}return status;
}

先调用sd_init函数初始化SDIO外设,驱动库内的代码如下。

sd_error_enum sd_init(void)
{sd_error_enum status = SD_OK;/* configure the RCU and GPIO, deinitialize the SDIO */rcu_config();gpio_config();sdio_deinit();/* configure the clock and work voltage */status = sd_power_on();if(SD_OK != status) {return status;}/* initialize the card and get CID and CSD of the card */status = sd_card_init();if(SD_OK != status) {return status;}/* configure the SDIO peripheral */sdio_clock_config(SDIO_SDIOCLKEDGE_RISING, SDIO_CLOCKBYPASS_DISABLE, SDIO_CLOCKPWRSAVE_DISABLE, SD_CLK_DIV_TRANS);sdio_bus_mode_set(SDIO_BUSMODE_1BIT);sdio_hardware_clock_disable();return status;
}

先使能外设时钟并配置管脚,所有使用到的管脚配置成复用推挽模式即可。
接着调用sd_power_on函数进行SD卡的配置;函数内部主要是先发送CMD0命令复位卡,然后发送CMD8命令判断卡版本,最后发送ACMD41命令获取卡电压值和容量支持信息。
然后调用sd_card_init函数初始化SD卡;函数内部主要是先发送CMD2获取卡的CID值,接着发送CMD3命令使卡发布新的RCA地址,最后发送CMD9命令获取卡的CSD值。

卡初始化完后,调用sd_card_select_deselect函数,传入卡的RCA地址来选中卡;因为卡在复位后默认是工作在1bit模式下,因此根据需要调用sd_bus_mode_config函数使卡工作在4bit模式;最后根据需要调用sd_transfer_mode_config函数,使用DMA传输卡数据。

例程第一项测试单块数据的读写,代码如下。

static void sdcard_read_write_test(void)
{sd_error_enum sd_error;for (uint32_t i = 0; i < 512; i++)buf_write[i] = i;memset(buf_read, 0, sizeof(buf_read));/* single block operation test */sd_error = sd_block_write(buf_write, 100 * 512, 512);if(SD_OK != sd_error){printf("\r\n Block write fail!");return;}else{printf("\r\n Block write success!");}sd_error = sd_block_read(buf_read, 100 * 512, 512);if(SD_OK != sd_error){printf("\r\n Block read fail!");return;}else{printf("\r\n Block read success!");}
}

单块写调用sd_block_write函数;函数内部先发送CMD16设置块大小,然后发送CMD24命令写入一个块的数据。
单块读调用sd_block_read函数;函数内部操作与单块写同理,唯一的不同是最后发送CMD17命令读取一个块的数据。

例程第二项测试卡的上锁和解锁,代码如下。

static void sdcard_lock_test(void)
{sd_error_enum sd_error;if(SD_CCC_LOCK_CARD & sd_cardinfo.card_csd.ccc){/* lock the card */sd_error = sd_lock_unlock(SD_LOCK);if(SD_OK != sd_error){printf("\r\n Lock failed!");return;}else{printf("\r\n The card is locked!");}sd_error = sd_erase(100 * 512, 101 * 512);if(SD_OK != sd_error)printf("\r\n Erase failed!");elseprintf("\r\n Erase success!");/* unlock the card */sd_error = sd_lock_unlock(SD_UNLOCK);if(SD_OK != sd_error){printf("\r\n Unlock failed!");return;}else{printf("\r\n The card is unlocked!");}sd_error = sd_erase(100 * 512, 101 * 512);if(SD_OK != sd_error)printf("\r\n Erase failed!");elseprintf("\r\n Erase success!");}
}

使用sd_lock_unlock函数可实现上锁和解锁;上锁和解锁都是通过发送CMD41命令实现,若我们想知道卡是否上锁可以随时发送CMD13命令来判断。
例程中在上锁和解锁后都调用了sd_erase函数擦除指定块,验证是否上锁或解锁成功,函数内部先发送CMD32CMD33命令分别设置擦除的块起始地址和结束地址,最后发送CMD38命令执行擦除。

例程最后一项测试多块读写,代码如下。

static void sdcard_multi_read_write_test(void)
{sd_error_enum sd_error;for (uint32_t i = 0; i < 512; i++)buf_write[i] = i;memset(buf_read, 0, sizeof(buf_read));sd_error = sd_multiblocks_write(buf_write, 200 * 512, 512, 3);if(SD_OK != sd_error){printf("\r\n Multiple block write fail!");return;}else{printf("\r\n Multiple block write success!");}sd_error = sd_multiblocks_read(buf_read, 200 * 512, 512, 3);if(SD_OK != sd_error){printf("\r\n Multiple block read fail!");return;}else{printf("\r\n Multiple block read success!");}
}

多块写操作调用sd_multiblocks_write函数,内部主要是先发送CMD16命令设置块大小,然后发送ACMD23命令预擦除要写入的区域,最后发送CMD25命令写入所有的数据。
多块读操作调用sd_multiblocks_read函数,流程和多块写类似,也是先发送CMD16命令设置块大小,然后发送CMD18命令读取指定数量的块数据。

例程的完整打印如下。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/20485.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

skywalking介绍及搭建

链路追踪框架比对&#xff1a; skywalking安装部署&#xff1a; 下载地址&#xff1a;Downloads | Apache SkyWalking 配置微服务与skywalking整合&#xff1a; copy agent/optional-plugins/apm-spring-cloud-getway-xx.jar到plugins&#xff0c;然后重启skywalking 监控界面…

会议管理系统(含源码+sql+视频导入教程)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 会议管理系统拥有两种角色 管理员&#xff1a;部门管理、员工管理、会议管理、会议室管理、预订会议、添加员工、注册员工审批等 用户&#xff1a;个人通知中心、预订会议、查看所有会议…

网络研究观-20240601

新战争时代的商业风险 美国人已经将战争视为遥远战场上发生的事件。然而&#xff0c;网络空间打破了这种看法&#xff0c;让全球战争的真正影响来到了美国家门口。 攻击不再局限于遥远的战场&#xff0c;而是在最意想不到的时间和地点发动袭击。 谁将主宰第五次工业革命&…

FreeSWITCH 1.10.10 简单图形化界面21-录音相关

FreeSWITCH 1.10.10 简单图形化界面21-录音相关 FreeSWITCH GUI界面预览00、安装FreeSWITCH GUI先看使用手册1、录音相关的应用11、record用法&#xff1a;举例&#xff1a;注意&#xff1a; 12、record_session用法&#xff1a;举例&#xff1a; 2、录音相关的变量3、单腿录音…

使用matplotlib绘制折线条形复合图

使用matplotlib绘制折线条形复合图 介绍效果代码 介绍 在数据可视化中&#xff0c;复合图形是一种非常有用的工具&#xff0c;可以同时显示多种数据类型的关系。在本篇博客中&#xff0c;我们将探讨如何使用 matplotlib 库来绘制包含折线图和条形图的复合图。 效果 代码 imp…

联合和枚举(自定义类型)

1.枚举&#xff08;关键字&#xff1a;enum) 1.1枚举类型的声明 把可能的值一一列举 赋的值是可能取值 1.2枚举类型的优点 1&#xff09;增加代码的可读性和可维护性 2&#xff09;和#define定义的标识符比较枚举有类型检查&#xff0c;更加严谨 3&#xff09;便于调试&a…

vue:实现丝滑上传进度条

一、效果展示 缓若江海凝清光 . 二、代码 const uploadProgress ref(); //上传进度//进度丝滑更新 //进度&#xff0c;时常 const ProgressChange (targetPercent: number, duration: number) > {//performance.now() 是浏览器提供的一个高性能时间 API&#xff0c;它返…

Linux系统编程(七)网络编程TCP、UDP

本文目录 一、基础知识点1. IP地址2. 端口3. 域名4. 网络协议类型5. IP协议类型6. 字节序7. socket套接字 二、常用API1. socket套接字描述符2. bind套接字绑定3. listen设置客户端连接个数4. accept接收客户端请求5. connect连接服务端 三、编程流程1.TCP编程 在学习本章之前&…

《mysql轻松学习·二》

1、创建数据表 contacts&#xff1a;数据表名 auto_increament&#xff1a;自动增长 primary key&#xff1a;主键 engineInnoDB default charsetutf8; 默认字符集utf8&#xff0c;不写就默认utf8 对数据表的操作&#xff1a; alter table 数据表名 add sex varchar(1); //添…

使用YOLOv10训练自己的数据集

1. yolov10源码下载 THU-MIG/yolov10: YOLOv10: Real-Time End-to-End Object Detection (github.com)https://github.com/THU-MIG/yolov10?tabreadme-ov-file 2. 环境配置 预先安装好ANACONDA、PyCharm或者VSCode等基本软件。参考以下博客&#xff1a; 史上最全最详细的An…

unity2D跑酷游戏

项目成果 项目网盘 导入资源包 放入Assets文件Assets资源文件 游戏流程分析 摄像机size调小&#xff0c;让图片占满屏幕 人跑本质&#xff0c;相对运动&#xff0c;图片无限向右滚动 图片720&#xff0c;缩小100倍第二个图片x为7.2每unit px100两张图片刚好挨着连贯 空对象Bg…

Yolov10笔记

一、前言 清华大学团队设计的Yolov10. 在这项工作中&#xff0c;我们主要从后处理和模型结构两方面进一步优化YOLO系列模型的性能和延迟平衡。我们首先为YOLO引入了端到端训练的一致双重分配&#xff0c;这在大大降低推理延迟的情况下保证了性能。此外&#xff0c;我们针对YOLO…

【计算机毕设】设计与实现基于SpringBoot的在线文档管理系统 - 源码免费(私信领取)

免费领取源码 &#xff5c; 项目完整可运行 &#xff5c; v&#xff1a;chengn7890 诚招源码校园代理&#xff01; 1. 研究目的 在当今信息爆炸的时代&#xff0c;文档管理对于任何组织都至关重要。基于SpringBoot的在线文档管理系统的设计旨在为用户提供一个便捷、高效、安全的…

轻松拿捏C语言——【内存函数】

&#x1f970;欢迎关注 轻松拿捏C语言系列&#xff0c;来和 小哇 一起进步&#xff01;✊ &#x1f389;创作不易&#xff0c;请多多支持&#x1f389; &#x1f308;感谢大家的阅读、点赞、收藏和关注&#x1f495; &#x1f339;如有问题&#xff0c;欢迎指正~~ 目录&#x1…

JVM学习-类加载过程(一)

概述 在Java中数据类型分为基本数据类型和引用数据类型&#xff0c;基本数据类型由虚拟机预先定义&#xff0c;引用数据类型则需要进行类的加载按Java虚拟机规范&#xff0c;从class文件加载到内存中的类&#xff0c;到类卸载出内存为止&#xff0c;它的整个生命周期包含以下7…

测试工具fio

一、安装部署 fio是一款优秀的磁盘IO测试工具&#xff0c;在Linux中比较常用于测试磁盘IO 其下载地址&#xff1a;https://brick.kernel.dk/snaps/fio-2.1.10.tar.gz 或者登录其官网&#xff1a;http://freshmeat.sourceforge.net/projects/fio/ 进行下载。 tar -zxvf fio-…

【redis】宝塔,线上环境报Redis error: ERR unknown command del 错误

两种方式&#xff1a; 1.打开宝塔上的redis&#xff0c;通过配置文件修改权限&#xff0c;注释&#xff1a;#rename-command DEL “” 2.打开服务器&#xff0c;宝塔中默认redis安装位置是&#xff1a;cd /www/server/redis 找到redis.conf,拉到最后&#xff0c;注释#rename-co…

Flutter 验证码输入框

前言&#xff1a; 验证码输入框很常见&#xff1a;处理不好 bug也会比较多 想实现方法很多&#xff0c;这里列举一种完美方式&#xff0c;完美兼容 软键盘粘贴方式 效果如下&#xff1a; 之前使用 uniapp 的方式实现过一次 两种方式&#xff08;原理相同&#xff09;&#xff1…

二叉树链式结构的前序、中序、后序、层序遍历

文章目录 一、二叉树创建二、前序遍历概念以及解释代码 三、中序遍历概念及解释代码 四、后序遍历概念及解释代码 五、层序遍历概念及解释代码 一、二叉树创建 &mesp; 实现二叉树的遍历&#xff0c;我们要先手搓出一个二叉树&#xff0c;在次基础上实现二叉树的前序、中序…

【RLHF个人笔记】RLHF:Reinforcement Learning from Human Feedback具体过程

【RLHF个人笔记】RLHF:Reinforcement Learning from Human Feedback具体过程 RLHF训练的三个步骤步骤1&#xff1a;收集数据与有监督训练策略步骤2&#xff1a;收集数据训练奖励模型步骤3&#xff1a;结合奖励模型利用强化学习算法如PPO算法来优化策略 参考内容 RLHF训练的三个…