网站页面设计布局,wordpress留白,如何在手机上学编程,wordpress 下载站点本篇将使用CubeMXKeil, 创建一个SD卡读写的工程。
目录
一、SD卡要点速读
二、SDIO要点速读
三、SD卡座接线原理图
四、CubeMX新建工程
五、CubeMX 生成 SD卡的SDIO通信部分
六、Keil 编辑工程代码
七、实验效果 实现效果#xff0c;如下图#xff1a; 一、SD卡 速读…
本篇将使用CubeMXKeil, 创建一个SD卡读写的工程。
目录
一、SD卡要点速读
二、SDIO要点速读
三、SD卡座接线原理图
四、CubeMX新建工程
五、CubeMX 生成 SD卡的SDIO通信部分
六、Keil 编辑工程代码
七、实验效果 实现效果如下图 一、SD卡 速读
SD卡全称Secure Digital Memory Card安全数码卡是嵌入式设备上常用的一种存储介质。 1、尺寸大小 分类
按卡的大小分类可以为3种
标准SD卡 体积较大卡侧带写保护开关常见于相机和摄像机中用于存储高分辨率照片和视频mini SD卡 现在较少看到已逐渐被microSD卡取代Micro SD卡旧称 TF卡2004年更名为 Micro SD Card, 常用于扩展手机和平板电脑的存储空间。
每种卡形状大小不一但功能一样遵循相同的 SD卡协议、相同的命令集、相同的块大小(512)。只需确保SDIO引脚配置正确并且遵循SD卡协议发送正确的命令程序即可通用。
都是SD卡但习惯上标准SD叫SD卡Micro SD叫TF卡。
目前STM32开发板、Linux开发板 等预留的卡座一般是TF卡座因为它占用空间最少。 2、卡的容量及标准 分类
在SD卡的表面丝印上会有HC、XC等字样表示它所使用的存储标准。
SD: 早期的版本基本停用最高 2GB, 分区格式为 FAT12(FAT)、FAT16。SDHC容量范围 2GB ~ 32GB 分区格式为 FAT32。SDXC容量范围 32GB ~ 2TB 分区格式为 exFAT。SDUC容量范围 2TB ~ 128TB 分区格式为 exFAT。 3、SD卡的传输速度
SD卡的可变时钟频率0~25MHz。当运行在25M数据带宽4位时最大理论传输速度是12.5MB/s。
而操作中会明显低于理论速度其受限于不同品牌的芯片优化、制造工芯、采用标准等。
SD卡是Flash存储读写速度特点是读快、写慢。
SD卡的最低写入速度用Class等级来标识。
在表面丝印上一般会有Class字样它后面的数表示最低写入速度单位是MB/s。
或者会用一个外面带半圆的数字表示。
Class 22MB/sClass 44MB/sClass 66MB/sClass 1010MB/s
附常用的SD卡读写速率参考非严谨值。
SD卡容量文件系统写入速度读取速度32GSDHC)FAT322MB/s8MB/s32GSDHC)exFAT3.5MB/s8.5MB/s64GSDXC)exFAT4MB/s8.5MB/s 4、SD卡的使用寿命
一般是指擦除的最大次数。
写入数据时需要先擦除扇区内容。读数据是不影响使用寿命的写数据才会影响使用寿命。
因此应避免频繁地对同一地址扇区进行写数据。如使用程序每隔一秒保存一次数据到同一地址这是不妥当的。
TLC1000~3000次MLC3000 ~1万次SLC可达10万次
擦写次数对使用寿命影响较小而更容易直接“致死”的是带电插拔很容易坏卡主要是静电原因 二、SDIO要点速读 原理比较复杂有兴趣的请自行csdn搜更详细的技术文档或STM32的官方文档。
SD卡的读写通信操作可以用 SPI、SDIO本示例使用SDIO。SDIO接口是在SD内存卡接口的基础上发展起来的SDIO接口除了能读写SD内存卡还能连接其它SDIO接口的设备常用的STM32F103C8没有SDIO接口F103系列R型号起才带SDIOSTM32F4系列芯片带更完善的SDIO主机接口能与MMC卡、SD卡、SDI/0卡、EC-ATA设备进行通信三种总线模式1-bit、4-bit、8-bit(不常用; 三、SD卡座接线原理图 STM32的SDIO外设与SD卡通信通用接线如下图。
注当使用弹簧式SD卡座会有第9个脚(CD), 可不接。它用于判断SD卡是否插入当插入SD卡时此脚输出低电平。 四、CubeMX新建工程 建议复制一个已带UART1、printf的工程这样更省时。
如果没有可参考以下步骤。 1、新建一个普通的工程
新手可参考如下图解老司机请直接跳过。
【STM32CubeMX】 新建工程_STM32F407 2、为工程添加UART1通信、printf输出
用于把SD卡的测试信息通过USB转TTl输出到串口助手观察。
如果你已知晓如何通过printf输出信息自行添加跳过即可。
USART1 DMA发送、DMA空闲中断 接收不定长数据
UART1 快速实现移植、通信 ( bsp_UART.c 、bsp_UART.h) 五、CubeMX 配置 SD卡的SDIO 初始化 通过 CubeMX配置SDIO, 极度简单。
本节为方便测试只使用普通的读写方式后续篇章再添加DMA、FATFS等方式。
1、使能SDIO
Mode选择SD的四线模式即 SD 4 bits Wide bus.参数部分F4系列不用修改配置默认即可。F103系列需把时钟分频系数修改为 6即SDIOCLK Clock divide factor这一项由默认0改为6, 不然会通信失败。 2、时钟设置
进入时钟树配置页面。
这时可能会弹出一个询问窗是否自动配置所需时钟
选择NO 手动修改即可。
如果Yes它将针对已使能的SDIO进行必须值的配置而系统时钟值会被修改为其它值。不推荐。 F4系列如果板上是25M的晶振用如下参数值要是8M的晶振修改晶振、分频两处为8即可。
重点箭头所指的Q值它用于控制USB 、SDIO和随机数生成器的时钟。
这个时钟必须是 48M ! 好了已完成配置。
重新生成工程即可 六、Keil 编辑工程代码 1、打开keil 工程先重新编译一次。
正常情况编译是0 Error的。如果有Error, 应该是新建工程时路径、名称有中文了重新开建工程用英文即可。 2、重要修改SD卡的初始化使用 1-bit 模式
CubeMX生成的SDIO初始化代码有一个bug需要手动修改操作如下
编译后右击 main.c 文件中函数 MX_SDIO_SD_Init(), 在弹出菜单中Go To Definition Of ...; 将跳转到SD卡初始化函数; 跳转到 sdio.c文件的 MX_SDIO_SD_Init()函数内部后
把下图位置中的 4B改为 1B
它下面还有一个4B不用修改只修改刚才那个即可。不要改错位置了
重点每次重新生成后都要手动修改一次。如果不修改初始化过程会导致程序卡死。 3、编写 读写测试 代码
SD卡的基础读写函数比较简单常用的函数共4个。
1、获取SD卡信息
HAL_SD_CardInfoTypeDef pCardInfo {0}; // SD卡信息结构体
HAL_SD_GetCardInfo(hsd, pCardInfo); // 获取 SD 卡的信息2、读数据
HAL_SD_ReadBlocks(hsd, aOldData, 7, 2, 3000); // SD卡的句柄、数据、块地址、块数量、超时ms3、写数据
HAL_SD_WriteBlocks(hsd, aTestData, 7, 2, 3000) // SD卡的句柄、数据、块地址、块数量、超时ms4、擦除数据
HAL_SD_Erase(hsd, 7, 8) // SD卡的句柄、块起始地址、块结束地址在 main函数内的 /* USER CODE BEGIN 2 */ 注释下方编写以下代码可复制
示例代码里已附详细注释比较容易理解流程是
获取SD卡信息读取测试块的原数据 写入测试擦除测试写回原数据 /***************** SD卡读写通信测试 *****************//* 1、获取卡信息打印到串口助手 *//* 2、读测试读出测试位置原数据保存在 aOldData[] *//* 3、写测试在测试的块上写入指定数据 *//* 读出刚才写入的块数据打印到串口助手观察 *//* 4、擦除测试擦除指定块上的数据 *//* 读出刚才擦除块的数据打印到串口助手观察 *//* 5、写回原数据到指定位置 *//* 读出刚才写入的块数据打印到串口助手观察 */ #define SD_TEST_SIZE 1024 // 测试数据的字节数刚好是2个块大小2x512static uint8_t aOldData[SD_TEST_SIZE] {0}; // 用于存放旧数据先读出来测试完了再把旧数据写回去static uint8_t aTestData[SD_TEST_SIZE] {0}; // 临时缓存用来存放测试数据HAL_SD_CardInfoTypeDef pCardInfo {0}; // SD卡信息结构体uint8_t status HAL_SD_GetCardState(hsd); // SD卡状态标志值if (status HAL_SD_CARD_TRANSFER){/* 1、获取卡信息打印到串口助手 */HAL_SD_GetCardInfo(hsd, pCardInfo); // 获取 SD 卡的信息printf(\r1、获取SD卡信息 ... \r\n);printf(卡类型%d \r\n, pCardInfo.CardType); // 类型返回0-SDSC、1-SDHC/SDXC、3-SECUREDprintf(卡版本%d \r\n, pCardInfo.CardVersion); // 版本返回0-CARD_V1、1-CARD_V2printf(块数量%d \r\n, pCardInfo.BlockNbr); // 可用的块数量printf(块大小%d \r\n, pCardInfo.BlockSize); // 每个块的大小; 单位字节printf(卡容量%lluG \r\n, ((unsigned long long)pCardInfo.BlockSize * pCardInfo.BlockNbr) / 1024 / 1024 / 1024); // 计算卡的容量HAL_Delay(1000); // 重要稍作延时再开始读写测试; 避免有些仿真器烧录期间的多次复位短暂运行了程序导致下列读写数据不完整。 /* 2、读测试读出测试位置原数据保存在 aOldData[] */printf(\r2、读取测试块的原数据 ... \r\n);memset(aOldData, 0, SD_TEST_SIZE); // 清0数组的数据if (HAL_SD_ReadBlocks(hsd, aOldData, 7, 2, 3000) HAL_OK) // 读SD卡数据块; 参数SD结构体、数据地址、块起始地址、读的块数量、超时时间;{while (HAL_SD_GetCardState(hsd) ! HAL_SD_CARD_TRANSFER); // 等待卡的读写操作结束for (uint32_t i 0; i SD_TEST_SIZE; i) // 打印 原数据printf(%X , aOldData[i]);printf(\r\n);}else{printf(SD卡 读测试 失败!\n); }/* 3-1、写测试在测试的块上写入数据 */printf(\r3、SD卡 写入测试 ...\r\n);memset(aTestData, 0x8, SD_TEST_SIZE); // 为数组准备要写入的测试数据整个数组填充指定值if (HAL_SD_WriteBlocks(hsd, aTestData, 7, 2, 3000) HAL_OK) // 向SD卡写入数据块; 参数SD结构体、数据地址、块起始地址、写入的块数量、超时时间;{while (HAL_SD_GetCardState(hsd) ! HAL_SD_CARD_TRANSFER); // 等待卡的读写操作结束printf(对指定块写入结束! \r写入的数据是\n);for (uint32_t i 0; i SD_TEST_SIZE; i) // 打印 写入的数据printf(%X , aTestData[i]);printf(\r\n);}else{printf(SD卡 写测试 失败!\n);}/* 3-2、读出刚才写测试的块内数据 */printf(\r现在块内的数据是\r\n);memset(aTestData, 0, SD_TEST_SIZE); // 清0数组的数据if (HAL_SD_ReadBlocks(hsd, aTestData, 7, 2, 3000) HAL_OK) // 读SD卡数据块; 参数SD结构体、数据地址、块起始地址、读的块数量、超时时间;{while (HAL_SD_GetCardState(hsd) ! HAL_SD_CARD_TRANSFER); // 等待卡的读写操作结束for (uint32_t i 0; i SD_TEST_SIZE; i) // 打印 写入后块内现在数据printf(%X , aTestData[i]);printf(\r\n);}else{printf(SD卡 读测试 失败!\n);}/* 4-1、擦除测试擦除指定块上的数据 */printf(\r4、擦除块测试 ...\r\n);if (HAL_SD_Erase(hsd, 7, 8) HAL_OK) // 擦除SD卡上的数据; 参数SD结构体、块的起始地址、块的结束地址{while (HAL_SD_GetCardState(hsd) ! HAL_SD_CARD_TRANSFER); // 等待卡的读写操作结束printf(擦除 成功 \r\n);}else{printf(擦除 失败 \r\n);}/* 4-2、读取擦除后指定块上的数据 */printf(擦除后现在块内的数据是\r\n);memset(aTestData, 0, SD_TEST_SIZE); // 清0数组的数据if (HAL_SD_ReadBlocks(hsd, aTestData, 7, 2, 3000) HAL_OK) // 读SD卡数据块; 参数SD结构体、数据地址、块起始地址、读的块数量、超时时间;{while (HAL_SD_GetCardState(hsd) ! HAL_SD_CARD_TRANSFER); // 等待卡的读写操作结束for (uint32_t i 0; i SD_TEST_SIZE; i) // 打印 块内现在的数据printf(%X , aTestData[i]);printf(\r\n);}else{printf(SD卡 读测试 失败!\n);}/* 5-1、写回测试块上的原数据 */printf(\r5、写回原数据 ...\r\n);//memset(aOldData, 1, SD_TEST_SIZE);if (HAL_SD_WriteBlocks(hsd, aOldData, 7, 2, 3000) HAL_OK) // 向SD卡写入数据块; 参数SD结构体、数据地址、块起始地址、写入的块数量、超时时间;{while (HAL_SD_GetCardState(hsd) ! HAL_SD_CARD_TRANSFER); // 等待卡的读写操作结束printf(写入结束! \n);}else{printf(SD卡 写回原数据 失败!\n);} /* 5-2、读取写入后的数据 */printf(现在块内的数据是: \r\n);memset(aTestData, 0, SD_TEST_SIZE); // 清0数组的数据if (HAL_SD_ReadBlocks(hsd, aTestData, 7, 2, 3000) HAL_OK) // 读SD卡数据块; 参数SD结构体、数据地址、块起始地址、读的块数量、超时时间;{while (HAL_SD_GetCardState(hsd) ! HAL_SD_CARD_TRANSFER); // 等待卡的读写操作结束for (uint32_t i 0; i SD_TEST_SIZE; i) // 打印 块内现在的数据printf(%X , aTestData[i]);printf(\r\n\r\n);}else{printf(SD卡 读测试 失败! \r\n);}printf(SD卡 读写测试结束\r\n);}
完成后位置如下图 七、实验效果 程序运行后串口助手输出如下 如有错漏 望指正~~~