MSP430F5438A单片机基于SPI的FatFs移植笔记(一)

发布者:shiwanyongbing最新更新时间:2020-05-13 来源: eefocus关键字:MSP430F5438A  单片机  SPI  FatFs移植 手机看文章 扫描二维码
随时随地手机看文章

怎么说呢……太费劲了,前面的博客还烂尾了,主要是觉得自己在调试的过程当中思维太混乱。虽然说自己挖的坑,含着泪也要填上,这几个就先不填了吧我重新开个坑把调通的说清楚。


不管移植什么程序,最重要的就是,


不要自以为是

一定要先查资料,花一周查资料,查到查不到为止,否则你编了一半的程序再参考别人的,直接后果是你下不了决心推翻重来

1. FatFs移植要点:

相信能看到这个博客的都知道FatFs是什么了,目前应该是0.11版本,我就不多废话了,一个开源的文件系统,不全面的说,作用就是让你编程序操作写SD卡的内容能够被PC机读出来(有不对的话懂的大神请指正)

它的好处就是只要写底层的几个硬件驱动函数就OK了,上层的函数都已经写好了,清楚格式直接调用就可以了。

所谓“硬件驱动”函数,就是告诉单片机,完成一个动作(比如初始化)具体需要哪个IO口怎样变化,哪个IO口该高,哪个IO口该低,通信端口选哪个,发什么东西,这些基本动作组合在一起,就能够完成初始化。


FatFs所需要的驱动函数共有五个,diskio.h 文件里面看就是这样的:


DSTATUS disk_initialize (BYTE pdrv);

DSTATUS disk_status (BYTE pdrv);

DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);

DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);

DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);


函数返回值:


DSTATUS


是一个枚举类型变量,包含5个元素,依次是成功、读写错误、写保护、未准备好、参数错误:


/* Results of Disk Functions */

typedef enum {

RES_OK = 0, /* 0: Successful */

RES_ERROR, /* 1: R/W Error */

RES_WRPRT, /* 2: Write Protected */

RES_NOTRDY, /* 3: Not Ready */

RES_PARERR /* 4: Invalid Parameter */

} DRESULT;


实际在写程序的时候返回值分得细有助于快速定位问题的所在,但是我移植的时候图省事只用了成功、读写错误、参数错误三个,原因是写保护我没有编写专门的函数去判断,而未准备好出现的概率很低。


各个函数的输入参数具体到每个函数再进行一一说明


那么下面首先以初始化程序

DSTATUS disk_initialize (BYTE pdrv);

具体说一下:通过TI的单片机MSP430F5438A进行函数的实现步骤


这里需要参考的良心文档以及网站有:


1.FatFs官方网站:http://www.elm-chan.org/fsw/ff/00index_e.html

说明很浅显,优点是易懂缺点是靠他说的那点儿说明实现简直不可能


2.一个叫Tilen Majerle的老外的网站,基于STM32系列单片机开发的FatFs:http://stm32f4-discovery.com/2014/07/library-21-read-sd-card-fatfs-stm32f4xx-devices/

注意这个哥们把全套的实现文件分成了好几个下载链接,分类的效果挺好,一开始看的人就晕了,这个文件下载下来,代码里面的函数在另外一个下载链接里……不过是真的很全,虽然可能跟你需要实现的平台不大一样,但是对于了解实现过程和时序很有帮助


3.一个密歇根大学的人写的技术报告,MSP430F149写的程序,但是只实现了单次读写,并且使用了DMA(内存直读)这种高端的功能(我会上载到资料,待补全)


4.SD卡的官方协议说明书,这个基本上太重要了,名字是part1_410,看来只是整个的一部分,不过很详细,说得很明白,只要你耐心细心,很多东西都可以直接找到,如果你关心的是SPI模式的话第7章是重点!

2. disk_initialize函数实现

这个函数在diskio.c当中的函数体是这样的:

/*-----------------------------------------------------------------------*/

/* Inidialize a Drive                                                    */

/*-----------------------------------------------------------------------*/

 

DSTATUS disk_initialize (

BYTE pdrv /* Physical drive nmuber to identify the drive */

)

{

DSTATUS stat;

int result;

 

switch (pdrv) {

case ATA :

result = ATA_disk_initialize();

 

// translate the reslut code here

 

return stat;

 

case MMC :

result = MMC_disk_initialize();

 

// translate the reslut code here

 

return stat;

 

case USB :

result = USB_disk_initialize();

 

// translate the reslut code here

 

return stat;

}

return STA_NOINIT;

}


可以看到FatFs很“贴心”的提供了ATA、MMC、USB三款接口存储器的初始化程序,可是你要是真的认为它贴心你就错了……

首先来说输入参数pdrv,这个具体可以理解为你要操作的存储器编号,我们操作单片机的通常也就一个存储器(我项目里面是SD卡),所以在ffconf.h的定义中:


#define _VOLUMES 1


这样一来,所有的输入参数pdrv就都是0了,而ATA MMC USB的定义是这样的:


/* Definitions of physical drive number for each drive */

#define ATA 0 /* Example: Map ATA harddisk to physical drive 0 */

#define MMC 1 /* Example: Map MMC/SD card to physical drive 1 */

#define USB 2 /* Example: Map USB MSD to physical drive 2 */


你妹啊亏我一开始还天真的认为我的是SD卡,不过脑子就只实现了

MMC_disk_initialize();

哪知道单个存储器条件下人家根本执行不到这儿啊!


所以,如果你们只有一个盘,一定要实现的是ATA那个,管他名字是什么,这里我最后实现的时候折衷了一下,把

MMC_disk_initialize();

拷贝到ATA下面了,也算是偷了个懒。那么

MMC_disk_initialize();

这个函数怎么实现呢?

下面的思路是一步一步深入到最底层,所以可能你一眼看不完这个函数的实现过程,不要着急

我们主要通过这个函数的实现过程一步一步搞清楚所有的运作机制


就用到了我上面提到的几个良心文件了

流程其实很多人都提到了也很清楚,这里先上一张经典流程图,来自part1_410.pdf(171/202)(以下我就简称这个文档是410文档了)

可见,第一步是发送命令CMD0,关于各个命令的说明410文档中同样有叙述(179/202),注意SPI用户要看第七章的表格

一个命令的组成分以下部分:

一个命令一共是6个byte


第一个byte是命令头

最高位是起始位,0,第二位是传输位,1,后面6位就是command index了,上面那个流程图里面是CMD几,这里换算就可以了,例如我们首先发送CMD0,那么这头一个字节就是

0x40 ( 0100 0000b )

需要说明,SD卡所支持的SPI通信的发送方式是3pin 8bit MSB First,也就是高位先发送,一次8位,所以CMD0最先发的就是0x40


第二个到第五个共4 bytes 是 命令参数 argument

这个对应的命令都能够在410当中找到argument的含义,CMD0的argument标注的是stuff bits填充位,一律写0就OK


第六个byte是7位CRC校验位和最后一个始终是1的end bit

这个CRC校验位的计算过程我没有理会,因为SPI模式下,除了CMD0和后面提到的CMD8,其它的校验位SD卡不会去管对错的


这里对于CMD0,加入你的argument全0,那么这个最后一个byte固定为

0x95,固定即可不用管它


说完了命令组成,那么具体来说一个命令是如何发送的呢?下面用MSP430F5438A举例子来说

这个单片机的SPI接口有好几个,我用的是P3.1,P3.2,P3.3组成的3pin的SPI,具体配置方法应该很容易查到,TI的user's guide当中也会有的

主要来说说SPI的通信机制,其实和我们常见的RS232有类似也有不同


SPI的3个pin分别是 输入,输出,时钟,此外,SD卡还有一个片选端口CS需要连接一个GPIO,只有这个片选端口为低电平的时候,SD卡才有反应(当然各个阶段也有需要在CS为高电平的时候需要干活的情况)


通信中,Master(单片机)提供时钟信号,Slave(SD卡)接收时钟信号,双方都是根据时钟的沿来分别采样输入或者输出线上根据所发送数据而变化的电压,从而实现数据的传输

与串口类似的是,如果你的单片机需要向SD卡发送数据,在合适的时间(后面会说什么时间合适)向发送缓冲区写需要发送的数据就OK

与串口不同的是,如果你的单片机需要接收SD卡发送的数据,接收1个byte的数据,单片机需要写入一个0xFF的数据将需要接收的数据“推过来”,知道了这个,剩下的就好办了


对于一个SPI通信端口,5438A配有:

一个发送中断标志位:UCTXIFG

一个接收中断标志位:UCRXIFG

一个发送缓冲器:UCxTXBUF

一个接收缓冲器:UCxRXBUF


中断标志位虽然可以手动清零,但是我个人不推荐。因为TI有自动的机制,我们查询就可以了,

发送数据:

不断查询确认发送中断标志位UCTXIFG,直到发送中断标志位为1,表示发送缓冲器可以写入数据,这时将需要发送的1byte数据写入发送缓冲器即可,写入后发送中断标志位自动归0,发送完成后重新变为1

接收数据:

不断查询接收中断标志位UCRXIFG,为1表明接收缓冲器中的数据有效,1byte可进行读取,读取后,接收中断标志位自动归零,直到再次接收到完整数据


有了这个流程,那么单个byte的发送和接收也不是问题了,我们可以顺利的发送命令CMD0了? NO,还有一个时序的问题


这里参考了另一个良心文献,振南的SD说明书

久违的中文啊哈哈,时序是这样的


首先在CS为高电平的情况下,输入8个唤醒时钟(可能落下了周期两个字,应该是8个时钟周期)(我理解是保险起见吧?)输入唤醒时钟周期的方法是,8个唤醒时钟周期就向写入缓冲器写入一个0xFF就可以了,因为SI是高的所以写FF,写入的时候时钟就运行了


然后拉低CS电平


然后读入nx8个时钟,同样是读入字节,方法是写入0xFF然后查输入寄存器,然后读数,注意如果SD卡就绪那么读入的数应该是0xFF


确定就绪后,写入6个byte的命令,之后保持CS为低电平,直到读入的返回值非0xFF表明命令处理完毕,SD卡开始发送回复数据,根据命令的恢复类型不同,读取不同byte数目的回复,读取完成之后将CS信号设为高电平,一次命令发送过程结束!


回复类型分为 R1,R1b,R2,R3,R7等等等等,同样可以在410当中找到


CMD的命令为回复为R1,长度1个byte,包含SD卡的状态,初始化时的正常回复为Idle:0x01,表明SD卡空闲!


发送CMD的完整代码如下:


// 发送CMD命令

char SD_Send_Command(unsigned char cmd, 

unsigned char response_type, 

unsigned char *response, 

unsigned char *argument)

{

int i;

char response_length;

unsigned char tmp;

 

        // 根据振南的建议,添加一个唤醒的过程:

        SPI_CS_HIGH();

        SPI_SendByte(0xFF);

        //--------------------------------------------

        

        while(0xFF != SPI_RcveByte());

// 发送命令前将CS置低

        

SPI_CS_LOW();

 

// 发送CMD头

tmp = 0x40 + cmd;

SPI_SendByte(tmp);

 

// 发送Argument

for (i=3; i>=0; i--) //注意!这里是MSB First,但是不太明白这么写到底有Argument的是如何排列的,参见 Application Note P15

{

          SPI_SendByte(argument[i]);

}

 

// 发送CRC

SPI_SendByte(0x95); //说白了只有CMD0需要,其它之后的都不用了,所以这里索性把所有的CRC都写成CMD0的

 

// 确定回复种类

response_length = 0;

switch (response_type)

{

case R1:

case R1B:

response_length = 1;

break;

case R2:

response_length = 2;

break;

case R3:

response_length = 5;

break;

default:

break;

}

        

// 等待回复-有效回复的第一位是0,所以要设置一个有退出机制的循环来等待这个0开头的回复byte

i=0;

do

{

tmp = SPI_RcveByte();

i++;

}

while( ((tmp&0x80)!=0) && (i        

        

        

// 如果失败只能返回0退出

if ( i >= SD_MAX_CMD_RETRY )

{

          // DEBUG4

          /*

          if(cmd == 13)

          {

            sd_RS232TX_PROC("ACMD13 FAIL!");

            sd_RS232TX_PROC(sd_NewRow);

          }

          */

SPI_CS_HIGH();

return 0;

}

 

// 如果成功

        

        i = response_length - 1;

        response[i] = tmp;

        i--;

        while (i>=0)

        {

          tmp = SPI_RcveByte();

          response[i] = tmp;

          i--;

        }

 

// 下面的内容涉及到:如果返回值是 R1B 即 BusyType,那么有一些额外的任务要做:

// SD 卡会输出一连串的 “0”,所以,任何非零回复是 BUSY 状态结束的标志

i=0;

if (response_type == R1B)

{

do

{

i++;

tmp = SPI_RcveByte();

}

while (tmp != 0xFF);

 

SPI_SendByte(0xFF);// 最后这句不知道什么意思,先这样

}

 

// 能到这里说明已经顺利完成命令执行并得到相应结果,返回退出

SPI_CS_HIGH();

return 1;

}




以上程序参考了密歇根大学的老兄,几点说明:

argument 和 response分别是函数所在的C文件中定义的全局static char类型数组

元素个数分别为4 和 5,因为R3和R7类型会返回5个byte的response


发送CRC时,没有考虑CMD8的情况,所以针对CMD8需要单独编写一个函数,或者在这里添加一个判断,都是没有问题的(CMD8干什么的后面再说)


关于R1 R1b 等等的定义其实都是一些便于区别的整数,按顺序从0开始定义就是了


关于输入变量cmd,可以在h文件中定义 CMD0 为 0, 其它同理,例如 CMD24 为 24 等等,即便是ACMD也按照排号定义就可以了,只不过ACMD命令之前要首先发送前缀命令CMD55


此外,函数中有一些地方比较匪夷所思,我的函数和密歇根那个哥们的也有一点区别,我这个是实际调试通过的,不知道和我自己用的开发板有关系没有,有兴趣的朋友可以重新看一下他的原版程序


以上就是发送命令的完整函数,以后发送各个命令就直接说“发送CMDxxx”了,不再详述过程(CMD8 和 ACMD怎么发会另外在后面说细一些)


今天先到这里

关键字:MSP430F5438A  单片机  SPI  FatFs移植 引用地址:MSP430F5438A单片机基于SPI的FatFs移植笔记(一)

上一篇:MSP430 大型数组 上电不启动
下一篇:MSP430 SD卡SPI读写操作(3) —— SD卡读写实现(以MSP430F5438A为例)

推荐阅读最新更新时间:2024-11-17 04:31

一文读懂SPI串行外设接口
SPI总线系统是一种同步串行外设接口,它可以使MCU与各种外围设备以串行方式进行通信以交换信息。正是由于有了通信方式,我们才能够通过芯片控制各种各样的外围器件,实现很多“不可思议”的现代科技。这里将以SPI为题,从编程角度来介绍SPI总线。下面就随网络通信小编一起来了解一下相关内容吧。 1、SPI协议简介 图 1 SPI接口 SPI 是英语 Serial Peripheral interface 的缩写,顾名思义就是串行外围设备接口。是 Motorola首先在其 MC68HCXX 系列处理器上定义的。SPI是一种高速的,全双工,同步的通信总线,由于其简单易用的特性,现在很多的nor flash和nandflash芯片集成
[网络通信]
富士通半导体推出宽电压双Flash MCU MB95650系列
富士通半导体(上海)有限公司日前宣布推出采用新工艺的宽工作电压、12bit高精度ADC、且带主从I2C控制器的高性价比双FLASH通用8位微控制器MB95560系列。该系列产品包括采用SOP24、TSSOP24、QFN32三种封装形式的24款产品。2012年6月下旬开始提供样片,2012年9月开始批量供货。 MB95650系列以8位微控制器F2MC-New8FX家族为基础,采用宽工作电压设计,能在1.8V~5.5V的应用环境中工作。该系列产品集成了主从I2C控制器和12bit高精度AD转换器,并采用双通道FLASH技术,使得程序在FLASH上运行的同时可以对另一通道FLASH进行读写,从而保护客户所需的重要数据。该系列产品还集成
[单片机]
基于单片机的气压式高度计设计
  高度是载体到某一基准水平面的垂直距离,是导航的一个重要依据。气压 传感器 是气压式高度计的重要器件。传统的气压 传感器 信号调理电路校准和补偿电路复杂,稳定性差,不能直接用于计算机数字化处理,也不便于在便携式设备中集成。本气压式高度测量系统使用微型压阻式 传感器 ,通过对静压的测量,经过模/数变换后由单片机进行数字滤波,函数解算出载体当前的绝对高度,具有较高精度和抗干扰能力。   1 高度测量系统的设计   大气压力在数值上等于所在海拔高度往上直到大气上界整个空气柱的重量,因此理想情况下,大气压力与海拔高度具有一一对应的关系。在海平面附近,海拔高度每升高100 m,气压下降大约0.7 kPa。由于空气具有可压缩性,大气压力
[单片机]
基于<font color='red'>单片机</font>的气压式高度计设计
单片机驱动EC11编码器
EC11编码器 EC11编码器通常又被称为旋转编码器,一般主要是用于亮度,温度,频率,音量调节等参数控制。 三只脚中的C脚接地,AB脚接上拉电阻后,当左转或右转时,AB脚就有脉冲信号输出。S1和S2脚为按压开关,按下时导通。 旋转编码器的引脚图和原理图为: 顺时针和逆时针旋转AB引脚输出波形图 和单片机通信的程序为: EC11.h #ifndef EC11 #define EC11 EC11.c #include All.h uchar EC11_Value = 125; // 获取旋转的值 /********************************* * @函数名:EC11
[单片机]
<font color='red'>单片机</font>驱动EC11编码器
关于STC单片机IO口的状态
新入职一个公司,做智能家居的,只用宏晶51单片机。好长时间没搞了,今天就遇到一个问题之前没遇到过,就是配置IO口的工作类型。 数据手册里面是这样说的,在此多说一句,数据手册做的真烂。 这里举个例子。 如果给P1M1赋值0X03,给P1M0赋值0X05,那么P1口各个引脚对应的模式就是: { P1M1=0x03=00000011b P1M0=0x05=00000101b } P1M1 P1M0 IO口模式0 0 P1.7准双向口0 0 P1.6准双向口0 0 P1.5准双向口0 0 P1.4准双向口0 0 P1.3准双向口0 1 P1.2推挽输出1 0 P1.1高阻1 1 P1.0开漏
[单片机]
关于STC<font color='red'>单片机</font>IO口的状态
MCS51系列单片机软件抗干扰技术中的误区介绍
单片机中存在这样一种广泛流传的误解:在MCS-51系列单片机中,只要用指令使程序从起始地址开始执行,就可以复位单片机,摆脱干扰。通过一个简单的实验,揭示了软件复位的可靠方法。   有的单片机(如8098)有专门的复位指令,某些增强型MCS-51系统单片机虽然没有复位指令,但片内集成了WATCHDOG电路,故抗干扰也不成问题。而普及型MCS-51系列单片机(如8031和8032)既然无复位指令,又不带硬件WATCHDOS,如果没有外接硬件WATCHDOG电路,就必须采用软件抗干扰技术。常用的软件抗干扰技术有:软件陷阱、指令冗余、软件WATCHDOG等,它们的作用是在系统受干扰时能及时发现,再用软件的方法使系统复位。所谓软件复位就是
[单片机]
51单片机音乐盒 仿真+程序
电路介绍: 1.此项目采用51单片机实现音乐盒的功能,同时电路中采用模拟示波器,可以实时查看波形的变动。 2.通过一个可调电阻实现音频信号的音量大小 3.基于8欧姆/1w的喇叭作为音频输出部件 proteus仿真原理图: 源代码: #include reg52.h #include intrins.h #define uchar unsigned char #define uint unsigned int sbit SPK = P2^7; uchar tone_h; //高音 uchar tone_l; //低音 uint note = 1; //音符 uchar
[单片机]
51<font color='red'>单片机</font>音乐盒 仿真+程序
【GD32 MCU 入门教程】一、GD32 MCU 开发环境搭建(2)使用 IAR 开发 GD32
IAR版本众多,版本之间的兼容性并不好,如果初次使用建议安装7.3以上的版本,安装好IAR以后再根据该文档来添加GD的器件型号,进行相关的debug工作。 2.1 在IAR中添加GD32 MCU Device 2.1.1 从官网上下载相应的GD32系列插件。 下面以GD32F30x系列为例,下载插件IAR_GD32F30x_ADDON.1.0.0.exe: 2.1.2 运行IAR_GD32F30x_ADDON.1.0.0.exe,单击start开始安装插件。 2.1.3 安装成功后单击Finish,结束插件安装。 2.2 在IAR中编译调试GD32 在上一小节中我们已经添加了GD32F30x系列的插件,这一小节我们介绍
[单片机]
【GD32 <font color='red'>MCU</font> 入门教程】一、GD32 <font color='red'>MCU</font> 开发环境搭建(2)使用 IAR 开发 GD32
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved