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

发布者:皮球最新更新时间:2018-09-19 来源: 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 SD卡SPI读写操作(4) —— FatFs文件系统实现
下一篇:单片机轮询模式多任务并行处理

推荐阅读最新更新时间:2024-03-16 16:14

AVR单片机(学习ing)—(十)、ATMEGA16的同步串行接口SPI—02
1)那就是在之前的介绍中说过,在说一遍~~ 主机和从机的两个移位寄存器可以被认为是一个公开的16位环形移位寄存器,当数据从主机移向从机时,同时从机饿数据也向相反的放向移向主机。这就意味着在一个以为周期内,主机和从机的数据进行了交换。(不过这个例子里没有用到这个,下个会用到~~呵呵~~),早知道对谁都好~~ 2)配置为SPI主机时,SPI接口不自动控制SS引脚,必须由用户软件来处理。还有配置为从机时,只要SS引脚为高,SPI接口将一直保持睡眠状态,并保持MISO为三态。(这个章节的第一篇文章有详细的介绍~~自己可以看看~~) 3)SPI系统的发送方向只有一个缓冲器,而接收方向有两个缓冲器。也就是说,在发送时一定要等到移位过程全部结束
[单片机]
AVR<font color='red'>单片机</font>(学习ing)—(十)、ATMEGA16的同步串行接口<font color='red'>SPI</font>—02
PIC单片机开发的若干问题
由美国Microchip公司生产的PIC系列单片机,由于其超小型、低功耗、低成本、多品种等特点,已广泛应用于工业控制、仪器、仪表、通信、家电、玩具等领域,本文总结了作者在PIC单片机开发过程中的一些经验、技巧,供同行参考。 1 怎样进一步降低功耗 功耗,在电池供电的仪器仪表中是一个重要的考虑因素。PIC16C××系列单片机本身的功耗较低(在5V,4MHz振荡频率时工作电流小于2mA)。为进一步降低功耗,在保证满足工作要求的前提下,可采用降低工作频率的方法,工作频率的下降可大大降低功耗(如PIC16C××在3V,32kHz下工作,其电流可减小到15μA),但较低的工作频率可能导致部分子程序(如数学计算)需占用较
[单片机]
PIC<font color='red'>单片机</font>开发的若干问题
基于双MCU架构的ABS/ASR/VDC故障诊断系统
集成了防抱死制动系统ABS(Anti-lock Braking System)、驱动防滑控制系统ASR(Acceleration Slip Regulation System)与车辆动力学控制系统VDC(Vehicle Dynamic Control System)的ABS/ASR/VDC集成系统是汽车主动安全性控制系统的核心装置之一。该系统可显著提高车辆的制动性、驱动性、转向可操纵性和横向稳定性,减少轮胎磨损和事故风险,增加行驶安全性和驾驶轻便性 。 为提高系统的可靠性,世界各大汽车整车厂或零部件厂商在推出的ABS/ASR/VDC产品中都配有故障诊断系统。该系统通过有关电气元件状态参数的在线测试,监控ABS/ASR/VDC系
[嵌入式]
贰:第一个51单片机的汇编实验
指令是指示单片机执行某种操作的指令。 1、机器码指令 用二进制(或十六进制)表示,这种形式的指令能够直接被计算机硬件识别和执行。 例如:二进制码“0000 0100B”,(十六进制“04H”)。 2、汇编语言指令 为了方便记忆,便于程序的编写和阅读,用助记符来表示每一条指令的功能。用助记符表示的指令不能被微处理器直接识别和执行,必须转换成机器码指令才能被机器执行。 例如,把5FH传送到累加器A中,实现这种操作的汇编语言指令形式为: MOV A,#5FH 其中“#”号为立即数5FH的标示符。这条指令的机器码为“74H 5FH” 汇编语言的语句格式 :操作码助记符 LOOP:MOV P0,#0FFH ;P0 端口
[单片机]
贰:第一个51<font color='red'>单片机</font>的汇编实验
实验0 聊聊单片机与机器人
对于机器人这个名词,大家应该都有所了解,我在这里就不讲太多。我主要来说说机器人是怎样工作的,单片机又是什么,有什么作用,如何来学习单片机。 单片机是可以用来编程的芯片,它对于机器人来说就相当于人的大脑。一个机器人,它除了有单片机,还应有传感器用来接收外界的图像、声音、温度等等信息,然后单片机去读取传感器接收的信息,就相当于人的感官。机器人可以采集外界信息,也可以对外界做出动作,例如控制电机等执行元件,通过机械结构做出行走或行驶动作,就相当于肌肉带动骨骼动作;又例如机器人也可以通过蜂鸣器或者喇叭发出声音、音乐,像人一样说话,也可以通过液晶屏显示文字或者图像信息。机器人与机器人或者电脑之间还可以进行通信来传递信息。以上所有功能都是要依
[单片机]
实验0 聊聊<font color='red'>单片机</font>与机器人
从LCD电极读数的单片机接口技术
   摘要 以测量仪表中常见的时分割驱动法驱动的段式LCD显示器为例,分析LCD显示器的电极连接结构和驱动信号波形;介绍单片机读取仪表LCD读数的接口电路。此接口电路应用于笔者开发的自动血压监控仪的研制及临床应用项目中,由8031单片机读取血压计的收缩压、舒张压、心率以及充气和放气时瞬时压强。实验证明,此接口电路工作稳定、可靠。   通过测量仪表拾取被测信号是单片机前向通道设计中常用的数据采集方式。通常,接口电路从仪表电路中取得相关的模拟信号,经过A/D转换或V/F 转换送入单片机;或者取得一个频率信号,经整形后送入单片机 。然而,有些测量仪表电路中可能找不到这样的信号。以电容式压力传感器血压计为例,尽管从其振荡电路中可以取得一
[应用]
针对节能汽车驱动电机控制的32位微控制器
  富士通半导体(上海)有限公司日前宣布发布3款MB91580系列产品,主要针对节能汽车驱动电机控制。作为高性能32位闪存嵌入微控制器(MCU) 的FR家族成员,该系列产品可广泛应用于电动汽车(EV)和混合动力汽车(HV)的驱动电机控制功能。   随着节能环保成为汽车行业发展的重要方向,作为节能汽车的电动汽车(EV)和混合动力汽车(HV)需求在全球范围内快速增长。而普及节能汽车、促进驾驶生态化需在改善电机运行、降低能耗和降低系统成本方面进行革新。富士通半导体紧跟市场需求,此次首次交付的节能汽车驱动电机控制芯片MB91580系列拥有专用嵌入旋转变压器感应器接口,能够开拓性地控制EV和HV中使用的三相电机,支持汽车驱动电机控制MC
[电源管理]
针对节能汽车驱动电机控制的32位<font color='red'>微控制器</font>
适合单片机裸机的开源软件框架:Zorb
很多时候,做单片机项目,会因为性能和内存资源的限制,没办法运行一些“大型”的通用框架,这个时候,一些轻量级的软件框架有显得尤为重要了。 这里就给大家分享一款一款适合单片机裸机的开源软件框架:Zorb Zorb简介 Zorb Framework是一个基于面向对象的思想来搭建一个轻量级的嵌入式框架。 搭建Zorb Framework的目的是为在不能运行Linux的芯片上快速开发应用,不用反复造轮子。 Zorb Framework的初步设计功能有: 1、时间系统功能zf_time 2、环形缓冲区功能zf_buffer 3、列表功能zf_list 4、状态机功能zf_fsm 5、事件功能zf_event 6、定时器功能zf_time
[单片机]
适合<font color='red'>单片机</font>裸机的开源软件框架:Zorb
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
  • 学习ARM开发(16)
    ARM有很多东西要学习,那么中断,就肯定是需要学习的东西。自从CPU引入中断以来,才真正地进入多任务系统工作,并且大大提高了工作效率。采 ...
  • 学习ARM开发(17)
    因为嵌入式系统里全部要使用中断的,那么我的S3C44B0怎么样中断流程呢?那我就需要了解整个流程了。要深入了解,最好的方法,就是去写程序 ...
  • 学习ARM开发(18)
    上一次已经了解ARM的中断处理过程,并且可以设置中断函数,那么它这样就可以工作了吗?答案是否定的。因为S3C44B0还有好几个寄存器是控制中 ...
  • 嵌入式系统调试仿真工具
    嵌入式硬件系统设计出来后就要进行调试,不管是硬件调试还是软件调试或者程序固化,都需要用到调试仿真工具。 随着处理器新品种、新 ...
  • 最近困扰在心中的一个小疑问终于解惑了~~
    最近在驱动方面一直在概念上不能很好的理解 有时候结合别人写的一点usb的例子能有点感觉,但是因为arm体系里面没有像单片机那样直接讲解引脚 ...
  • 学习ARM开发(1)
  • 学习ARM开发(2)
  • 学习ARM开发(4)
  • 学习ARM开发(6)
何立民专栏 单片机及嵌入式宝典

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

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