21、基于Arduino的SD卡数据记录全攻略

基于Arduino的SD卡数据记录全攻略

1. 准备工作

在开始使用SD卡与Arduino进行交互之前,需要确保SD卡已正确格式化。格式化完成后,就可以通过SD卡扩展板开始与SD卡进行交互了。

2. Arduino与SD卡的连接

SD卡是3.3V设备,因此需要通过能正确处理逻辑电平转换和电压供应的扩展板来连接SD卡。Arduino与SD卡通过SPI(Serial Peripheral Interface)总线进行通信,Arduino语言提供了SD库,它抽象了底层的SPI通信,让我们可以轻松读写SD卡上存储的文件。

3. SD卡扩展板介绍

有多种扩展板可用于为Arduino添加数据记录功能,下面是一些常见扩展板的介绍:
| 扩展板名称 | 特点 | CS引脚 | 其他特点 |
| — | — | — | — |
| Cooking Hacks Micro SD shield | 体积小,可连接到Uno的8 - 13引脚或ICSP 6引脚头,自带2GB SD卡 | 连接8 - 13引脚时为10;连接ISP头时可自定义 | 适合与其他需要使用引脚10的扩展板配合使用 |
| Official Arduino Wireless SD shield | 支持XBee无线电和SD卡,便于结合无线通信和数据记录功能 | 4 | 需将引脚10设为输出 |
| Official Arduino Ethernet SD shield | 可让Arduino连接有线网络,同时具备SD卡接口 | 4(SD卡),10(以太网控制器) | 主要用于通过网络访问存储的文件 |
| Official Arduino Wi - Fi SD shield | 利用Wi - Fi无线电实现网络连接,具备SD卡接口 | 4(SD卡),10(Wi - Fi控制器) | 同一时间只能激活一个CS线 |
| adafruit data logging shield | 包含实时时钟(RTC)芯片和SD卡接口 | 默认CS引脚 | 适合后续实验,RTC芯片连接到I2C总线 |
| SparkFun MicroSD shield | 简约设计,只有SD卡插槽,有原型制作区域 | 8 | 可焊接额外组件 |

所有扩展板的共同点如下:
- 通过6引脚编程头或复用数字引脚连接到SPI引脚。Uno的SPI引脚是11、12和13,Mega板是50、51和52,Leonardo的SPI引脚仅位于在线串行编程(ICSP)头。
- 指定一个芯片选择(CS)引脚,可能是默认的CS引脚(非Mega板为10,Mega板为53)。
- 为SD卡提供3.3V电压并进行逻辑电平转换。

4. SD卡SPI接口

Arduino通过SPI接口与SD卡通信,需要使用MOSI(主输出,从输入)、MISO(主输入,从输出)、SCLK(串行时钟)和CS(芯片选择)引脚。使用Arduino的SD卡库时,假设使用Arduino的硬件SPI引脚和默认或自定义的CS引脚。即使使用不同的CS引脚,SD卡库也必须将默认CS引脚设置为输出才能正常工作。对于Uno,默认CS引脚是10;对于Mega,是53。

5. 向SD卡写入数据

首先,使用SD卡库向SD卡写入一些示例数据。数据将存储在名为log.csv的文件中,后续可以在计算机上打开。如果SD卡格式化为FAT16,文件名必须遵循8.3格式,即扩展名必须为三个字符,文件名必须为八个字符或更少。

以下是写入数据的具体步骤和代码示例:
1. 确保SD扩展板正确安装在Arduino上,并插入SD卡。
2. 初始化SD卡通信:

if (!SD.begin(CS_pin))
{
  Serial.println("Card Failure");
  return;
}
Serial.println("Card Ready");
  1. 写入新数据行:
File dataFile = SD.open("log.csv", FILE_WRITE);
if (dataFile)
{
  dataFile.println("hello");
  dataFile.close(); // 关闭连接后数据才会写入!
}
else
{
  Serial.println("Couldn't open log file");
}
  1. 完整的写入测试代码如下:
//Write to SD card
#include <SD.h>

//Set by default for the SD card library
//MOSI = pin 11
//MISO = pin 12
//SCLK = pin 13
//We always need to set the CS Pin
const int CS_PIN = 10;
//We set this high to provide power
const int POW_PIN = 8;

void setup()
{
  Serial.begin(9600);
  Serial.println("Initializing Card");
  //CS pin is an output
  pinMode(CS_PIN, OUTPUT);

  //Card will draw power from pin 8, so set it high
  pinMode(POW_PIN, OUTPUT); 
  digitalWrite(POW_PIN, HIGH);

  if (!SD.begin(CS_PIN))
  {
    Serial.println("Card Failure");
    return;
  }
  Serial.println("Card Ready"); 
}

void loop()
{
  long timeStamp = millis();
  String dataString = "Hello There!";

  //Open a file and write to it. 
  File dataFile = SD.open("log.csv", FILE_WRITE);
  if (dataFile)
  {
    dataFile.print(timeStamp);
    dataFile.print(",");
    dataFile.println(dataString);
    dataFile.close(); //Data isn't actually written until we 
                     //close the connection!

    //Print same thing to the screen for debugging
    Serial.print(timeStamp);
    Serial.print(",");
    Serial.println(dataString);
  }
  else
  {
    Serial.println("Couldn't open log file");
  }
  delay(5000);
}

需要注意的是:
- CS_PIN应设置为SD卡CS引脚连接的实际引脚。如果不是10,还需要在 setup() 函数中添加 pinMode(10, OUTPUT) ,否则SD库将无法工作。
- 该扩展板从引脚8获取电源,因此需要在 setup 函数中将POW_PIN设置为输出并置为高电平。
- 每次循环时, timestamp 变量会更新为当前经过的毫秒数,由于其值可能超过16位,因此必须为 long 类型。

6. 从SD卡读取数据

虽然读取SD卡数据在数据记录中使用频率不如写入高,但在设置程序参数时很有用。例如,可以指定数据记录的频率。
以下是读取数据的步骤和代码示例:
1. 将SD卡插入计算机,创建一个名为 speed.txt 的新TXT文件,在文件中输入所需的刷新时间(毫秒)。
2. 将文件保存到SD卡,然后将SD卡放回Arduino扩展板。
3. 修改程序以读取该文件并设置数据记录的刷新速度:

File commandFile = SD.open("speed.txt");
if (commandFile)
{
  Serial.println("Reading Command File");
  while(commandFile.available())
  {
    refresh_rate = commandFile.parseInt();
  }
  Serial.print("Refresh Rate = ");
  Serial.print(refresh_rate);
  Serial.println("ms");
}
else
{
  Serial.println("Could not read command file.");
  return;
} 
  1. 整合读写功能的完整代码如下:
//SD read and write
#include <SD.h>
//Set by default for the SD card library
//MOSI = pin 11
//MISO = pin 12
//SCLK = pin 13
//We always need to set the CS pin
const int CS_PIN  =10;
const int POW_PIN =8;
//Default rate of 5 seconds
int refresh_rate = 5000;

void setup()
{
  Serial.begin(9600);
  Serial.println("Initializing Card");
  //CS pin is an output
  pinMode(CS_PIN, OUTPUT);

  //Card will draw power from pin 8, so set it high
  pinMode(POW_PIN, OUTPUT); 
  digitalWrite(POW_PIN, HIGH);

  if (!SD.begin(CS_PIN))
  {
    Serial.println("Card Failure");
    return;
  }
  Serial.println("Card Ready");

  //Read the configuration information (speed.txt)
  File commandFile = SD.open("speed.txt");
  if (commandFile)
  {
     Serial.println("Reading Command File");

     while(commandFile.available())
     {
       refresh_rate = commandFile.parseInt();
     }
     Serial.print("Refresh Rate = ");
     Serial.print(refresh_rate);
     Serial.println("ms");
     commandFile.close(); //Close the file when finished
  }  
  else
  {
    Serial.println("Could not read command file.");
    return;
  } 
}

void loop()
{
  long timeStamp = millis();
  String dataString = "Hello There!";

  //Open a file and write to it.
  File dataFile = SD.open("log.csv", FILE_WRITE);
  if (dataFile)
  {
    dataFile.print(timeStamp);
    dataFile.print(",");
    dataFile.println(dataString);
    dataFile.close(); //Data isn't actually written until we 
                      //close the connection!

    //Print same thing to the screen for debugging
    Serial.print(timeStamp);
    Serial.print(",");
    Serial.println(dataString);
  }
  else
  {
    Serial.println("Couldn't open log file");
  }
  delay(refresh_rate);
} 
7. 使用实时时钟

几乎所有数据记录应用都能从使用实时时钟中受益。使用实时时钟可以为测量数据添加时间戳,方便跟踪事件发生的时间。

7.1 实时时钟原理

实时时钟能精确计时,设置一次时间后,它能准确计时,甚至能处理闰年等情况。这里使用流行的DS1307实时时钟集成电路。

7.2 使用DS1307实时时钟

实时时钟通过I2C连接与Arduino通信,并连接到硬币电池,可保持数年计时。连接到实时时钟的晶体振荡器可实现精确计时。建议使用adafruit DS1307 breakout板,它将IC、振荡器、硬币电池、去耦电容和I2C上拉电阻集成在一起,方便安装在Arduino上。

如果选择自己组装,需要的组件有:32.768kHz的晶体、2.2千欧的I2C上拉电阻和标准3.0V硬币电池,可将这些组件放在面包板上并直接连接到Arduino。

以下是整个操作流程的mermaid流程图:

graph TD;
    A[准备SD卡] --> B[选择扩展板并连接到Arduino];
    B --> C[写入数据到SD卡];
    C --> D[从SD卡读取数据];
    D --> E[使用实时时钟添加时间戳];

通过以上步骤,我们可以利用Arduino和SD卡实现数据的记录、读取以及精确的时间记录,为各种数据采集和处理应用提供了强大的支持。

基于Arduino的SD卡数据记录全攻略

8. 常见问题及解决方法

在使用Arduino与SD卡进行数据记录的过程中,可能会遇到一些常见问题,以下是这些问题及相应的解决方法:
| 问题描述 | 可能原因 | 解决方法 |
| — | — | — |
| SD卡初始化失败 | SD卡未正确插入、SD卡损坏、CS引脚设置错误 | 检查SD卡是否正确插入,尝试更换SD卡,确认CS引脚设置是否正确 |
| 无法打开日志文件 | 文件权限问题、SD卡文件系统损坏 | 检查SD卡文件系统,尝试格式化SD卡,确保文件权限正确 |
| 数据写入不完整 | 写入数据时SD卡异常、未正确关闭文件 | 检查SD卡状态,确保在写入数据后正确关闭文件 |
| 实时时钟时间不准确 | 时钟芯片未正确初始化、电池电量不足 | 重新初始化时钟芯片,更换硬币电池 |

9. 高级应用场景

除了基本的数据记录和读取功能,结合Arduino、SD卡和实时时钟,还可以实现一些高级应用场景。

9.1 传感器数据记录

可以连接各种传感器,如温度传感器、湿度传感器、光照传感器等,将传感器采集的数据实时记录到SD卡中。以下是一个简单的温度传感器数据记录示例代码:

#include <SD.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_DHT.h>

#define DHTPIN 2
#define DHTTYPE DHT11

DHT dht(DHTPIN, DHTTYPE);

const int CS_PIN = 10;
const int POW_PIN = 8;

void setup() {
  Serial.begin(9600);
  Serial.println("Initializing Card");
  pinMode(CS_PIN, OUTPUT);
  pinMode(POW_PIN, OUTPUT);
  digitalWrite(POW_PIN, HIGH);

  if (!SD.begin(CS_PIN)) {
    Serial.println("Card Failure");
    return;
  }
  Serial.println("Card Ready");

  dht.begin();
}

void loop() {
  float h = dht.readHumidity();
  float t = dht.readTemperature();

  if (isnan(h) || isnan(t)) {
    Serial.println("Failed to read from DHT sensor!");
    return;
  }

  File dataFile = SD.open("sensor_log.csv", FILE_WRITE);
  if (dataFile) {
    dataFile.print(millis());
    dataFile.print(",");
    dataFile.print(t);
    dataFile.print(",");
    dataFile.println(h);
    dataFile.close();

    Serial.print(millis());
    Serial.print(",");
    Serial.print(t);
    Serial.print(",");
    Serial.println(h);
  } else {
    Serial.println("Couldn't open sensor log file");
  }

  delay(5000);
}
9.2 远程数据传输

结合无线模块(如XBee、Wi - Fi模块),可以将SD卡中记录的数据远程传输到服务器或其他设备。以下是一个简单的Wi - Fi数据传输示例流程:
1. 连接Wi - Fi模块到Arduino。
2. 读取SD卡中的数据。
3. 通过Wi - Fi模块将数据发送到指定的服务器。

#include <SD.h>
#include <WiFi.h>

const char* ssid = "your_SSID";
const char* password = "your_PASSWORD";
const char* serverName = "your_server_address";

const int CS_PIN = 10;
const int POW_PIN = 8;

WiFiClient client;

void setup() {
  Serial.begin(9600);
  Serial.println("Initializing Card");
  pinMode(CS_PIN, OUTPUT);
  pinMode(POW_PIN, OUTPUT);
  digitalWrite(POW_PIN, HIGH);

  if (!SD.begin(CS_PIN)) {
    Serial.println("Card Failure");
    return;
  }
  Serial.println("Card Ready");

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.println("Connected to WiFi");
}

void loop() {
  File dataFile = SD.open("log.csv", FILE_READ);
  if (dataFile) {
    while (dataFile.available()) {
      String line = dataFile.readStringUntil('\n');
      if (client.connect(serverName, 80)) {
        client.println("POST /upload.php HTTP/1.1");
        client.println("Host: " + String(serverName));
        client.println("Content-Type: text/plain");
        client.print("Content-Length: ");
        client.println(line.length());
        client.println();
        client.print(line);
        client.stop();
      }
    }
    dataFile.close();
  }
  delay(60000);
}
10. 总结与展望

通过上述内容,我们详细介绍了如何使用Arduino与SD卡进行数据记录、读取,以及如何结合实时时钟为数据添加精确的时间戳。同时,还探讨了常见问题的解决方法和一些高级应用场景。

在未来,随着技术的不断发展,我们可以进一步扩展这些功能。例如,结合机器学习算法对记录的数据进行分析,实现智能决策;利用物联网技术将多个Arduino设备连接起来,构建大规模的数据采集网络。相信在不久的将来,基于Arduino和SD卡的数据记录系统将在更多领域发挥重要作用。

以下是高级应用场景的mermaid流程图:

graph TD;
    A[传感器数据记录] --> B[读取传感器数据];
    B --> C[将数据写入SD卡];
    D[远程数据传输] --> E[读取SD卡数据];
    E --> F[通过无线模块发送数据到服务器];

通过不断探索和实践,我们可以充分发挥Arduino和SD卡的潜力,为各种数据处理和应用需求提供有效的解决方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值