EDN首页   博客首页

日志档案

发表于 2008-7-28 17:10:45

307

标签: STM32  SPI  TIMER  SD  FAT  

基于STM32的MP3播放器设计与实现(一)(提供MDK完整源码)

本文将介绍一个利用STM32处理器实现简易MP3 Player的设计实例,这个综合应用实例有助于读者了解STM32SPI接口、SD卡、TIMER、中断、FAT文件系统、USB等的应用。

这里提供了两种设计方案,第一种方案是简易声波播放器,仅使用STM103V100评估板,令计时器TIM4工作在PWM模式下,将wav格式的声波文件从SD卡中读出,由TIM4产生不同频率的方波通过低通滤波器和放大器送喇叭,如图1所示;第二种方案则是简易MP3播放器,还需要使用额外的解码芯片,将MP3格式的文件从SD卡读出,然后送解码芯片解码播放,如图2所示。本节将先介绍SD卡、FAT16文件格式、VS1003编解码器等关键部分,然后再分别给出两种设计方案的软件设计。

1 简易声波播放器方案

2 简易MP3 Player方案

1 SD卡的结构及读写方法

STM103V100评估板有SD连接器,其使用SPI总线与STM32处理器连接,如图3所示。

3 SD连接器与STM32处理器SPI连接图

SD卡(Secure Digital Memory Card)是一种为满足安全性、容量、性能和使用环境等各方面的需求而设计的一种新型存储器件,SD卡允许在两种模式下工作,即SD模式和SPI模式,本系统采用SPI模式。本小节仅简要介绍在SPI模式下,STM32处理器如何读写SD卡,如果读者如希望详细了解SD卡,可以参考相关资料。SD卡内部结构及引脚如图4所示。

4 SD卡内部结构及引脚

SD卡主要引脚和功能为:

n         CLK:时钟信号,每个时钟周期传输一个命令或数据位,频率可在025MHz之间变化,SD卡的总线管理器可以不受任何限制的自由产生025MHz的频率;

n         CMD:双向命令和回复线,命令是一次主机到从卡操作的开始,命令可以是从主机到单卡寻址,也可以是到所有卡;回复是对之前命令的回答,回复可以来自单卡或所有卡;

n         DAT03:数据线,数据可以从卡传向主机也可以从主机传向卡。

     SD卡以命令形式来控制SD卡的读写等操作。可根据命令对多块或单块进行读写操作。在SPI模式下其命令由6个字节构成,其中高位在前。SD卡命令的格式如表1所示,其中相关参数可以查阅SD卡规范。

1 SPI命令格式

Byte 1

Byte2-5

Byte 6

7

6

5        0

31         0

7

0

0

1

Command

Command Argument

CRC

1

下面分别给出读写SD卡的两个函数:

n         读取SD卡函数u8 MSD_ReadBlock(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead);

/*****************************************************************

* Function Name : MSD_ReadBlock

* Description    : Reads a block of data from the MSD.

* Input          : - pBuffer : pointer to the buffer that receives the data read

*                    from the MSD.

*                  - ReadAddr : MSD's internal address to read from.

*                  - NumByteToRead : number of bytes to read from the MSD.

* Output         : None

* Return         : The MSD Response:

- MSD_RESPONSE_FAILURE: Sequence failed

*                   - MSD_RESPONSE_NO_ERROR: Sequence succeed

*****************************************************************/

u8 MSD_ReadBlock(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)

{

  u32 i = 0;

  u8 rvalue = MSD_RESPONSE_FAILURE;

 

  /* MSD chip select low */

  MSD_CS_LOW();

  /* Send CMD17 (MSD_READ_SINGLE_BLOCK) to read one block */

  MSD_SendCmd(MSD_READ_SINGLE_BLOCK, ReadAddr, 0xFF);

 

  /* Check if the MSD acknowledged the read block command:

 R1 response (0x00: no errors) */

  if (!MSD_GetResponse(MSD_RESPONSE_NO_ERROR))

  {

    /* Now look for the data token to signify the start of the data */

    if (!MSD_GetResponse(MSD_START_DATA_SINGLE_BLOCK_READ))

    {

      /* Read the MSD block data : read NumByteToRead data */

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

      {

        /* Save the received data */

        *pBuffer = MSD_ReadByte();

        /* Point to the next location where the byte read will be saved */

        pBuffer++;

      }

      /* Get CRC bytes (not really needed by us, but required by MSD) */

      MSD_ReadByte();

      MSD_ReadByte();

      /* Set response value to success */

      rvalue = MSD_RESPONSE_NO_ERROR;

    }

  }

 

  /* MSD chip select high */

  MSD_CS_HIGH();

  /* Send dummy byte: 8 Clock pulses of delay */

  MSD_WriteByte(DUMMY);

  /* Returns the reponse */

  return rvalue;

}

n         写读取SD卡函数u8 MSD_WriteBlock(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)

/*****************************************************************

* Function Name : MSD_WriteBlock

* Description    : Writes a block on the MSD

* Input          : - pBuffer : pointer to the buffer containing the data to be

*                    written on the MSD.

*                  - WriteAddr : address to write on.

*                  - NumByteToWrite: number of data to write

* Output         : None

* Return         : The MSD Response:

- MSD_RESPONSE_FAILURE: Sequence failed

*                   - MSD_RESPONSE_NO_ERROR: Sequence succeed

*****************************************************************/

u8 MSD_WriteBlock(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)

{

  u32 i = 0;

  u8 rvalue = MSD_RESPONSE_FAILURE;

 

  /* MSD chip select low */

  MSD_CS_LOW();

  /* Send CMD24 (MSD_WRITE_BLOCK) to write multiple block */

  MSD_SendCmd(MSD_WRITE_BLOCK, WriteAddr, 0xFF);

 

  /* Check if the MSD acknowledged the write block command:

R1 response (0x00: no errors) */

  if (!MSD_GetResponse(MSD_RESPONSE_NO_ERROR))

  {

    /* Send a dummy byte */

    MSD_WriteByte(DUMMY);

    /* Send the data token to signify the start of the data */

    MSD_WriteByte(0xFE);

    /* Write the block data to MSD : write count data by block */

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

    {

      /* Send the pointed byte */

      MSD_WriteByte(*pBuffer);

      /* Point to the next location where the byte read will be saved */

      pBuffer++;

    }

    /* Put CRC bytes (not really needed by us, but required by MSD) */

    MSD_ReadByte();

    MSD_ReadByte();

    /* Read data response */

    if (MSD_GetDataResponse() == MSD_DATA_OK)

    {

      rvalue = MSD_RESPONSE_NO_ERROR;

    }

  }

 

  /* MSD chip select high */

  MSD_CS_HIGH();

  /* Send dummy byte: 8 Clock pulses of delay */

  MSD_WriteByte(DUMMY);

  /* Returns the reponse */

  return rvalue;

}

2 FAT16文件系统简介

     SD卡如果采用FAT16文件格式,按照其不同的特点和作用大致可分为5 部分:MBR区、DBR区、FAT区、FDT区和DATA区。由于SD卡一般不做引导盘,一般也不分区,因此通常无MBR区,直接从DBR区开始。下面对后面四个区分别作简介:

n         DBR

    内容为系统引导记录,它包括一个引导程序和一个被称为BPBBios Parameter Block)的本分区参数记录表。引导程序的主要任务是当MBR将系统控制权交给它时,判断本分区根目录是否有操作系统引导文件,如果有则将其读入内存,并把控制权交给该文件。BPB参数块记录着本分区的起始扇区、结束扇区、文件存储格式、根目录大小、FAT个数,分配单元大小等重要参数。本系统采用的DBR结构为:

typedef __packed struct

{/* 由于Cortex-M3内核默认以对齐方式访问,因此可能导致结构体元素之间有“空隙”,读出的结构体元素有误,因此需要加上关键字__packed,强制其以压缩方式存储结构体。这样该结构体在内存空间上是一片连续的空间,不存在“空隙”情况。其它地方同理 */

     u8         BS_jmpBoot[3];     //ofs:0.典型的如:0xEB,0x3E,0x90

     u8         BS_OEMName[8];  //ofs:3.典型的如:“MSWIN4.1

     u16        BPB_BytesPerSec;  //ofs:11.每扇区字节数

     u8         BPB_SecPerClus;          //ofs:13.每簇扇区数

     u16        BPB_RsvdSecCnt;         //ofs:14.保留扇区数,从DBR FAT 的扇区数

     u8         BPB_NumFATs;     //ofs:16.FAT 的个数,通常为2

     u16        BPB_RootEntCnt;         //ofs:17.根目录项数

     u16        BPB_TotSec16;            //ofs:19.分区总扇区数(<32M 时用)

     u8         BPB_Media;        //ofs:21.分区介质标识,SD卡一般用0xF8

     u16        BPB_FATSz16;             //ofs:22.每个FAT 占的扇区数

     u16        BPB_SecPerTrk;           //ofs:24.每道扇区数,对于SD卡无意义

     u16        BPB_NumHeads;    //ofs:26.磁头数,对于SD卡无意义

     u32        BPB_HiddSec;       //ofs:28.隐藏扇区数,从MBRDBR的扇区数

     u32        BPB_TotSec32;      //ofs:32.分区总扇区数(32M时用)

     u8         BS_DrvNum;         //ofs:36.软盘:0x00,硬盘:0x80SD卡无意义

     u8         BS_Reservedl;       //ofs:37.保留

     u8         BS_BootSig;         //ofs:38.扩展引导标记:0x29,通常对于SD卡无意义

     u32        BS_VolID;          //ofs:39.盘序列号

     u8         BS_VolLab[11];    //ofs:43.如“Msdos

     u8         BS_FilSysType[8];  //ofs:54.FAT16

     u8         ExecutableCode[448];         //ofs:62.引导代码

     u8         ExecutableMarker[2];    //ofs:510.结束标识:0xAA55

} FAT_BPB;

n         FAT

    该区内容为文件分配表,FAT16文件系统进行空间分配的最基本单位是簇。文件分配表反映了SD卡所有簇的使用情况,通过查文件分配表可以得知任一簇的使用情况。对于FAT16来说,FAT表每项占用两个字节。FAT表的第一项通常为FFF8H。对于其它项,若其值为0000H