前言
SD卡的话是可以通过spi的方式来进行读取写入,还有获得关于这张卡的一些基本信息,例如容量。那么这一次的话我一直在想是否有一种方法可以检查,我没有插到相机,没有插入到读卡器再接到电脑时候的SD卡,它当前的容量、文件存储情况怎么样呢?所以就催生了这么一个项目
在这个项目中,我主要使用的有以下东西:
1.已经刷了Maple Mini的Bootloader的STM32F103C8T6最小系统芯片
2.Arduino IDE
3.一根友好的MicroUSB线
4.SD卡读取器
如果你是第1次看我写的内容,那可能不知道第1项是什么,第1项的话,简单来说他就是刷了一个特殊的Bootloader的STM32芯片,这可以使得我们不用再通过SWD来去烧写STM32,并且速度不亚于。 STM官方一直在推行以一种简单的方式给STM32编程,那最简单的方式是什么,自然是Arduino软硬件全家福了。
相关的资料可以看这一篇
《使用Arduino IDE来编写上传STM32以及STM8代码,STM32Duino教程》
https://mp.csdn.net/editor/html/101601991
在开发中需要用的引脚定义参考图示如下,主要看边沿的黑色数字
配置好STM32Duino的预先测试
在正式的开始之前,我会先测试一下当前的这个芯片它是否正常运作,引脚的对应是否正常,最主要是能不能正常上传
所以就使用官方程序例子中Blink,然后稍微修改一下。添加一个读取A0端口并且打印出来的功能。
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(200); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(300); // wait for a second
Serial.print("Hello,A0:");
float VOL =(analogRead(0)*1.0)/4096.0 * 3.3;
Serial.println(VOL);
}
如果一切顺利就可以完成上传,如果你的上传不是很顺利的话,可以翻阅一下原先文章,换条线、接口等等。当然多数时候还是驱动问题,另外要确定STM32CubeProgrammer正常的安装在电脑上。
正常情况下可以读取到A0口的引脚下电压,未配置悬空时候是1/2的,STM32的10Bit精度超过ATMEGA任意一款(毕竟是32Bit),但是推力,也就是端口的电流输出能力差点意思,3.3V很多时候也不太方便。
SD模块和STM32的连接
我选用的SD模块,主要特性是这样的
上图可以不看,主要在意这几个点就可以:
1.可以转换信号电压,比如你是5V电压的芯片例如Atmega的,德州仪器的或者C51的,这个可以自动切换信号电压,如果说是STM32那就保持3.3V
2.SPI接口,需要连接的有五条线,CS线看起来可以悬空(根据Arduino例子注释所述)
本来用于Arduino,比如说Arduino UNO这一个基于Atmega328P的,你可以简单的连接,但是如果是STM32,就要有一点小小的不一样了,比如接口的连接需要重新探索一下。
依照例子代码,本来是连接这些引脚的
** MOSI - pin 11 on Arduino Uno/Duemilanove/Diecimila
** MISO - pin 12 on Arduino Uno/Duemilanove/Diecimila
** CLK - pin 13 on Arduino Uno/Duemilanove/Diecimila
** CS - depends on your SD card shield or module.
我提前测试了一下,只要连接这几个位置就可以
SD模块上的引脚标识 | MOSI | MISO | SCK | CS |
STM32F103C8T6引脚 | A7 | A6 | A5 | A4 |
之后,可以去打开Arduino IDE自带的这个例子,然后上传这一个代码到STM32中
如果连线OK,那么我们就可以直接从串口读取到我们想要的信息了,这个库还是十分强大的。
如何使用SD库
我们今天要做的目标就是读取SD卡里面的指定文件,所以先了解这个库的主要用法,我解析自带例子以方便大家了解
必须包含的头文件
#include <SPI.h>
#include <SD.h>
然后是实例化三个结构体,Sd2Card结构体,SdVolume结构体和SdFile结构体
Sd2Card card; //这个结构体用于初始化和SD卡之间的通讯
SdVolume volume; //获得这张SD卡的容量信息,主要是分区相关参数
SdFile root; //获得这张SD卡的文件目录
之后先对SD卡进行初始化,这里如果说初始化失败了,那么就会返回FALSE,可以在这里做错误处理,比如返回FALSE就打印提示并且锁死等等等
card.init(SPI_HALF_SPEED, chipSelect)
然后,如果初始化成功,可以打印卡的基本信息,那么最基本的就是卡类型,可以识别三种分别是SD1和SD2和SDHC
switch (card.type()) {
case SD_CARD_TYPE_SD1:
Serial.println("SD1");
break;
case SD_CARD_TYPE_SD2:
Serial.println("SD2");
break;
case SD_CARD_TYPE_SDHC:
Serial.println("SDHC");
break;
default:
Serial.println("Unknown");
}
SD卡目前有这几个标准,MicroSD卡与SD卡基本一致
SD: 最大支持2GB。
SDHC: SD High Capacity, SD2.0发布的规范,容量2GB ~ 32GB,采用FAT32(FAT16最大只能到2GB)。
SDXC: SD eXtended Capactiy. SD3.0发布的规范,容量2GB ~ 2TB,采用exFAT(vista的新文件系统),支持UHS-1。
然后,我们可以输出SD卡的分区格式,一般来说有两种一个是FAT16一个是FAT32。
Serial.println(volume.fatType(), DEC);
之后是得出SD卡容量了,例子中使用的算法是[ SD卡每个簇有多少个块*SD卡有多少个簇/2 ] 这样会得到SD卡的容量(KB单位)
volumesize = volume.blocksPerCluster();
volumesize *= volume.clusterCount();
volumesize /= 2;
Serial.print("Volume size (Kb): ");
Serial.println(volumesize);
什么是簇大小,我们有时候格式化时选择的这个“分配单元大小”就是了。
在成功读取SD卡类型和SD卡的容量之后,可以读取目录树,这里同样的以一种简单的方法来获得,通过选择 LS_DATE | LS_SIZE 还可以输出文件日期和文件的大小。
root.openRoot(volume);
root.ls(LS_R | LS_DATE | LS_SIZE);
如何读写SD卡上面的文件
接着来试着读取一下我这张卡里面的TIME.TXT,这里面有一行时间码文本
首先初始化结构体,这里用到的是File这一个
File myFile;
然后使能SD卡,我们刚刚把SD卡的CS链接在PA4了,所以
SD.begin(4)
之后就是读取TIME.TXT文件,先用读方式打开这一个文件
myFile = SD.open("TIME.TXT", FILE_READ);
然后打印出这一个文件
while (myFile.available()) {
Serial.write(myFile.read());
}
需要注意的是要用write,如果用print那么打印出的是对应的ANSI码
完成后,使用close函数来结束读取
myFile.close();
如果要写文件,操作基本一样,就是打开,写,关闭。如果没有文件会自行创立一个文件
myFile = SD.open("test.txt", FILE_WRITE);
myFile.println("read file");
myFile.close();
如何查询和删改SD卡上面的文件
我们在操作电脑时候经常用的几个文件命令,复制,粘贴,删除,在这边也一样可以完成。
查询文件是否存在
SD.exists("example.txt"); //返回true或false
删除文件
SD.remove("example.txt");