OneNET麒麟座应用开发之九:与SD卡通讯并保存数据

由于需要记录的数据量比较大,而且有些时候,有的用户不方便实时上传数据,所以要求使用SD卡存储数据然后人工收取上传。为此我们选择了一种通用的SD卡读写器。

1、读卡器简介

该读卡器整合 SD 卡规范和 FAT 文件格式规范,只要通过本模块规定的通讯协议就可以把数据存储在 SD 卡中的文件中。该读卡器连接方便采用串口通讯方式,如下图:

该读卡器摸块通讯协议比较简单,本模块的通讯协议分为命令发送和命令的应答两部分,其中命令格式由4个部分组成:命令识别码(0x55 0xAA),命令号,字节数(参数的个数,占2个字节,先发送低位字节,再发送高位字节),参数(根据命令的不同而不同),校验和(除命令识别码和校验和本身,所有发送数据之和的低 8 位数据)。命令格式如图:

应答分为两部分:命令的执行情况(编码将附录 1),数据。数据根据命令的不同而不同。

2、硬件连接

因为采用的是串口通讯,所以硬件的连接比较简单。麒麟座上的USART1(PA9:USART1_TX,PA10:USART1_RX)端口已经引到了J2端子排的J2_6和J2_5,所以我们就是用这一接口,至于5V电源和接地以及控制及状态信号悬着相应的引脚即可。

3、软件设计

接下来我们根据协议编写读写SD卡的软件,主要实现状态检测、创建文件、打开文件、写文件、关闭文件、保存文件以及获取文件信息等。

1)获取系统的状态命令

获取系统的状态命令是用来获取模块当前的状态。命令编码是:0x01,命令格式如下:

//检测系统SD卡的状态

uint8_t GetSDCardStatus(void)

{

  uint8_t CommandText[6]={0x55,0xAA,0x01,0x00,0x00,0x01};

  uint8_t StatusByte=0xaa;

  StatusByte = SendCommand(CommandText,6);

  Delayms(50);

  return StatusByte;

}

2)创建文件命令

创建文件命令提供给主机创建文件的功能。参数为 N 字节 8.3 文件格式的文件名(字符串格式,即文件名以 0 结尾),即 8 字节的基本文件名(模块不支持汉字编码,字母不区分大小写),3 字节扩展名。命令编码是:0x02,命令格式如下:

//创建文件,返回操作状态

uint8_t CreateFile(uint8_t fileName[8])

{

  uint8_t CommandText[19]={0x55,0xAA,0x02,0x0D,0x00,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x2E,0x74,0x78,0x74,0x00,0x41};

  uint8_t StatusByte;

  StatusByte=0xaa;

  uint16_t i;

  for(i=0;i<8;i++)

  {

    CommandText[i+5]=fileName[i];

  }

  uint8_t checksum=0x00;

  for(i=2;i<18;i++)

  {

    checksum+=CommandText[i];

  }

  CommandText[18]=checksum;

  StatusByte = SendCommand(CommandText,19);

  return StatusByte;

}

(3)打开文件命令

该命令为主机提供打开文件的功能。参数为 N 字节 8.3 文件格式的文件名(字符串格式,即文件名以0 结尾),即 8 字节的基本文件名(模块不支持汉字编码,字母不区分大小写),3 字节扩展名。命令编码是:0x06命令格式如下,其中个数占 2 字节,低字节先发送:

//打开文件

uint8_t OpenFile(uint8_t fileName[8])

{

  uint8_t CommandText[19]={0x55,0xAA,0x06,0x0D,0x00,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x2E,0x74,0x78,0x74,0x00,0x45};//打开文件命令 0x06

  uint8_t StatusByte=0xaa;

  uint16_t i;

  for(i=0;i<8;i++)

  {

    CommandText[i+5]=fileName[i];

  }

  uint8_t checksum=0x00;

  for(i=2;i<18;i++)

  {

    checksum+=CommandText[i];

  }

  CommandText[18]=checksum;

  StatusByte = SendCommand(CommandText,19);

  return StatusByte;

}

4)获取文件信息命令

本命令为主机提供了读取当前打开文件的文件指针值和文件大小的功能。命令编码是:0x09,其命令格式如下:

//获取文件信息命令

void GetFileStatus(uint8_t rxData[])

{

  uint8_t CommandText[6]={0x55,0xAA,0x09,0x00,0x00,0x09};//获取文件信息命令0x09

  uint16_t i;

  for(i=0;i<6;i++)

  {

    //等待传送结束

    while(USART_GetFlagStatus(UART4, USART_FLAG_TXE) == RESET)

    {

    }

    // 写一个字节到对应的串口传送数据寄存器

    USART_SendData(UART4, CommandText[i]);

  }

  Delayms(20);

  for(i=0;i<9;i++)

  {

    // 等待字节被对应的串口完全接收

    //while(USART_GetFlagStatus(UART4, USART_FLAG_RXNE) == RESET)

    //{

    //}

    // 获取接收到的字节

    rxData[i] = USART_ReceiveData(UART4);

  }

}

5)写文件命令

该命令为主机提供向已打开文件中写入数据的功能。每写一个数据文件指针自动加1,当数据写完,文件指针指向最后一个数据地址加1的位置。命令编码是:0x05,命令格式如下,其中个数占2字节,低字节先发送,起始地址占4字节,低字节先发送:

//写文件,返回写操作的状态

uint8_t WriteToFile(uint8_t * address,uint8_t data[],uint16_t datalength)

{

  uint16_t count=datalength+10+19;

  uint8_t CommandText[70];

  uint8_t StatusByte=0xaa;

  uint16_t i;

  CommandText[0]=0x55;

  CommandText[1]=0xAA;

  CommandText[2]=0x05;

  CommandText[3]=datalength+4+19;

  CommandText[4]=0x00;

  CommandText[5]=0xFF;

  CommandText[6]=0xFF;

  CommandText[7]=0xFF;

  CommandText[8]=0xFF;

  for(i=0;i<datalength;i++)

  {

    CommandText[i+9]=data[i];

  }

  CommandText[datalength+9]=(saveDate[0]/10)+0x30;

  CommandText[datalength+10]=(saveDate[0]%10)+0x30;

  CommandText[datalength+11]=0x2D;

  CommandText[datalength+12]=(saveDate[1]/10)+0x30;

  CommandText[datalength+13]=(saveDate[1]%10)+0x30;

  CommandText[datalength+14]=0x2D;

  CommandText[datalength+15]=(saveDate[2]/10)+0x30;

  CommandText[datalength+16]=(saveDate[2]%10)+0x30;

  CommandText[datalength+17]=0x20;

  CommandText[datalength+18]=(saveDate[3]/10)+0x30;

  CommandText[datalength+19]=(saveDate[3]%10)+0x30;

  CommandText[datalength+20]=0x3A;

  CommandText[datalength+21]=(saveDate[4]/10)+0x30;

  CommandText[datalength+22]=(saveDate[4]%10)+0x30;

  CommandText[datalength+23]=0x3A;

  CommandText[datalength+24]=(saveDate[5]/10)+0x30;

  CommandText[datalength+25]=(saveDate[5]%10)+0x30;

  CommandText[datalength+26]=0x0D;

  CommandText[datalength+27]=0x0A;

  uint8_t checksum=0x00;

  for(i=2;i<count-1;i++)

  {

    checksum+=CommandText[i];

  }

  CommandText[count-1]=checksum;

  StatusByte = SendCommand(CommandText,count);

  return StatusByte;

}

6)保存文件命令

该命令为主机提供保存当前打开文件的功能,为了防止频繁写 SD 卡,每次送入模块的数据先是保存在模块的 512 字节的扇区缓冲中,所以为了防止数据丢失,完成所有数据的传输后,要发送保存文件命令来保存文件。命令编码是:0x04,命令格式如下:

//保存文件,返回操作执行状态

uint8_t SaveFile(void)

{

  uint8_t CommandText[6]={0x55,0xAA,0x04,0x00,0x00,0x04};//保存文件命令 0x04

  uint8_t StatusByte=0xaa;

  StatusByte = SendCommand(CommandText,6);

  return StatusByte;

}

7)关闭文件命令

该命令为主机提供关闭当前打开的文件的功能。在创建文件、创建文件夹、打开文件之前要求关闭当前打开的文件,才可以执行这些命令,否则返回失败。命令编码是:0x08,命令格式如下:

//关闭文件,返回操作执行状态

uint8_t CloseFile(void)

{

  uint8_t CommandText[19]={0x55,0xAA,0x08,0x00,0x00,0x08};//关闭文件命令 0x08

  uint8_t StatusByte=0xaa;

  StatusByte = SendCommand(CommandText,6);

  return StatusByte;

}

编写完程序,我们测试以我们想要的格式写一些数据下去文件被保存为文本文件,以时间为文件名,数据格式与预期一致。至此SD卡读写完成。

时间: 2024-10-14 09:42:51

OneNET麒麟座应用开发之九:与SD卡通讯并保存数据的相关文章

OneNET麒麟座应用开发之十:空气质量数据监测站项目总结

大气质量数据监测站用于测试空气质量监测及数据采集,实现野外或者室内空气质量的检测. 1.项目概述 本项目是一个定制项目,要求采集大气的压力.温度.湿度.PM25.位置等数据并上传到指定的后台服务器.但有时候因为没有条件或因为各种原因不能联网,则采用本地保存的方式,本地保存我们决定使用SD卡来实现.除此外,为了实现显式需求,还需要配套一些东西来实现饮食的需求,如实时时钟记录,上传联网方式的选择等. 2.硬件设计 本项目涉及到的硬件并不复杂,我们对其中几个实用比较多的电路做一个说明.首先说一下串口通

OneNET麒麟座应用开发之二:串口读取PM25传感器数据

作为环境数据监测站首先要获取大气中可吸入颗粒物的数据.为了检测PM25数据,我们采用北京海联信为的HLPM025K3型号传感器,该传感器使用激光法测量PM25和PM10的数据. 该型传感器的检测对象如下: M2.5:测量空气中0.3-2.5微米颗粒物: PM10:测量空气中 0.3-10微米颗粒物. 该传感器采用的通讯协议如下: (1)波特率: 9600bit/S;数据位:8位;停止位:1位;校验位:无; (2)数据发送间隔时间为0.8-1.2S(秒) , (3)数据格式:7个字节,其中校验位=

Android开发之InstanceState详解(转)---利用其保存Activity状态

Android开发之InstanceState详解 本文介绍Android中关于Activity的两个神秘方法:onSaveInstanceState() 和 onRestoreInstanceState(),并且在介绍这两个方法之后,再分别来实现使用InstanceState保存和恢复数据功能.Android实现屏幕旋转异步下载效果这样两个示例. 首先来介绍onSaveInstanceState() 和 onRestoreInstanceState() .关于这两个方法,一些朋友可能在Andr

JAVA实战教程_JAVA案例开发之JAVA开发微信二维码大数据开发03

大家好,这次是第三个课时的视频,欢迎大家继续学习. 视频简介:本视频是关于JAVA实战教程,JAVA开发微信二维码大数据系统.这个JAVA开发案例可以协助一些从零基础开始学习JAVA,正处于理论走完实践的路程上的初学者能接触到实际开发项目过程中,在实践当中巩固自己的JAVA方面的知识外,更能在项目案例当中学到解决在JAVA学习或者实践当中遇上问题的一些解决方式.仅供参考!自设交流群:457036818,欢迎一起加入交流. PS:该案例共十个课时,本小节为第二课时 课程原地址:http://www

Unity3D游戏开发之从&quot;复活&quot;和&quot;暂停/恢复&quot;谈游戏数据配置管理

随着游戏制作技术的不断发展,在经历了从2D到3D.从单机到网游.从PC游戏到移动游戏的种种演变后,玩家对于游戏质量的要求越来越高,游戏制作的难度相应地增加,整个游戏研发的体系开始变得庞大而复杂,由此就产生了游戏数据配置和管理的相关问题.本文将从游戏中的"复活"和"暂停/恢复"这两个应用场景的角度来谈谈在游戏开发中如何对游戏中的数据进行管理和配置. 为什么要谈游戏数据的配置和管理 不知道大家是不是会和博主有一样的想法,就是当你回头来思考游戏开发的时候,你常常会发现,如

Android开发之ListView利用OnScrollListener实现分页加载数据

上篇博文和大家分享了下拉刷新,这是一个用户体验非常好的操作方式.新浪微薄就是使用这种方式的典型. 还有个问题,当用户从网络上读取微薄的时候,如果一下子全部加载用户未读的微薄这将耗费比较长的时间,造成不好的用户体验,同时一屏的内容也不足以显示如此多的内容.这时候,我们就需要用到另一个功能,那就是listview的分页了.通过分页分次加载数据,用户看多少就去加载多少. 通常这也分为两种方式,一种是设置一个按钮,用户点击即加载.另一种是当用户滑动到底部时自动加载.今天我就和大家分享一下这个功能的实现.

Android 安全开发之 ZIP 文件目录遍历

1.ZIP文件目录遍历简介 因为ZIP压缩包文件中允许存在"../"的字符串,攻击者可以利用多个"../"在解压时改变ZIP包中某个文件的存放位置,覆盖掉应用原有的文件.如果被覆盖掉的文件是动态链接so.dex或者odex文件,轻则产生本地拒绝服务漏洞,影响应用的可用性,重则可能造成任意代码执行漏洞,危害用户的设备安全和信息安全.比如近段时间发现的"寄生兽"漏洞.海豚浏览器远程命令执行漏洞.三星默认输入法远程代码执行漏洞等都与ZIP文件目录遍历有

Android开发之Html类详解

在进行Android开发中经常回忽略Html类.这个类其实很简单,就是将HTML标签文本解析成普通的样式文本.下面就让我么看一下这个类的具体介绍. 类结构: java.lang.Object    ? android.text.Html 类概述: 这个类用于处理的HTML字符串并将其转换成可显示的样式文本.但并不是所有的HTML标记的支持. 公有方法: 说其简单是应为它就有四个方法: Public Methods static String escapeHtml(CharSequence tex

Android开发之assets目录下资源使用总结

预前知识: Android资源文件分类: Android资源文件大致可以分为两种: 第一种是res目录下存放的可编译的资源文件: 这种资源文件系统会在R.java里面自动生成该资源文件的ID,所以访问这种资源文件比较简单,通过R.XXX.ID即可: 第二种是assets目录下存放的原生资源文件: 因为系统在编译的时候不会编译assets下的资源文件,所以我们不能通过R.XXX.ID的方式访问它们.那我么能不能通过该资源的绝对路径去访问它们呢?因为apk安装之后会放在/data/app/**.ap