⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mmc.c

📁 用ATmega8 做的MP3播放器
💻 C
字号:
/*
 * Copyright (c) 2003-2004 K. John '2B|!2B' Crispin
 * Copyright (c) 2005      Stephan Dobretsberger
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
 *
 * Feedback, Bugs, ... mail stephan.dobretsberger@gmx.at
 *
 */


// this code includes a few functions that were originally made for a ti msp340.
// i found it on www.mikrocontroller.net once upon a time
// the ripped functions are
// MMC_get_R1
// MMC_get_R2
// the cs_deselect; spi_io(0xff); CS_select; in the while loop in MMC_init doesn't work without
// unfortunatley i lost the link.
// if you know where this code can be found, let me know so i can put a link here.

#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>

#include "types.h"
#include "delay.h"
#include "spi.h"
#include "mmc.h"

//test
#include "config.h"
#ifndef PLAY_LED_ON
  #define PLAY_LED_ON   cbi(LED_PORT, LED_PLAY_PIN)
  #define PLAY_LED_OFF  sbi(LED_PORT, LED_PLAY_PIN)
#endif


static u16 current_blocklen = 0;

// sends a block of data from the mem over the spi bus
void spi_io_mem(u08* data, u16 length)
{ // transmit 'length' bytes over spi
  while(length)
  { spi_io(*data);
    data++;
    length--;
  };
};

// send cmd + arguments + default crc for init command. once in spi mode
// we dont need crc so we can keep this constant
void MMC_send_cmd(u08 cmd, u16 datah, u16 datal)
{ // default command sequence
  u08 buffer[6] ;
  // fill sequence with our specific data
  buffer[0]=0x40 + cmd;
  buffer[1]=(datah&0xff00)>>8;
  buffer[2]=datah&0xff;
  buffer[3]=(datal&0xff00)>>8;
  buffer[4]=datal&0xff;
  buffer[5]=0x95;
  // dispach data
  spi_io_mem(buffer,6);
};

// gets a 1 byte long R1
u08 MMC_get_R1(void)
{ u08 retval;
  u08 max_errors = 128;
  // wait for first valid response byte
  do
  { retval = spi_io(0xff);
    max_errors--;
  }while((retval & 0x80) && (max_errors>0));

  return retval;
};

// gets a 1 byte long R1B and then waits for the card to be available again
u08 MMC_get_R1B(u08 max_busy)
{ u08 retval;
  u08 max_errors = 64;
  // wait for first valid response byte
  do
  { retval = spi_io(0xff);
    max_errors--;
  } while((retval == 0xff) && max_errors);

  // loop while card sends the busy byte
  do
  { retval = spi_io(0xff);
    max_busy--;
  } while(  (retval == MMC_R1B_BUSY_BYTE) && max_busy);

  return retval;
};

// gets a 2 byte long R2
u16 MMC_get_R2(void)
{ u16 retval;
  u08 max_errors = 64;
  // wait for first valid response byte
  do
  { retval = spi_io(0xff);
    max_errors--;
  } while((retval & 0x80) && (max_errors>0));

  // move data to upper byte
  retval = (retval << 8);
  // get second byte;
  max_errors = spi_io(0xff);
  retval += max_errors;
  return retval;
};

// sends the start data blobk token to the MMC
void MMC_send_start_data_token(void)
{ spi_io(MMC_START_TOKEN_SINGLE);
};

// waits for the card to send the start data block token
void MMC_wait_for_start_token(u08 max_errors)
{ u08 retval;
  do
  { // get a byte from the spi bus
    retval = spi_io(0xff);
    // keep track of the trys
    max_errors--;
  } while((retval != MMC_START_TOKEN_SINGLE));
};

// allows sending data to the MMC
void MMC_enable(void)
{ cbi(MMC_EN_PORT,PIN_MMC_EN);
  cbi(MMC_EN_DDR, PIN_MMC_EN);
}

// disables sending data to the MMC
void MMC_disable(void)
{ sbi(MMC_EN_PORT,PIN_MMC_EN);
  sbi(MMC_EN_DDR, PIN_MMC_EN);
}

// selects the CS for spi xfer
void MMC_CS_select(void)
{ // pull down the MMC CS line
  cbi(MMC_CS_PORT, PIN_MMC_CS);
};

// deselects the CS for spi xfer
void MMC_CS_deselect(void)
{ // pull up the MMC CS card
  sbi(MMC_CS_PORT, PIN_MMC_CS);
};


// stops the MMC transmission and sends the 8 clock cycles needed by the mmc for cleanup
void MMC_cleanup(void)
{ // deselect the MMC card
  MMC_CS_deselect();
  // pulse the SCK 8 times
  spi_io(0xff);
};

// gets n bytes plus crc from spi bus
void MMC_get_data(u08* ptr_data, u16 length)
{ MMC_wait_for_start_token(128);
  while(length)
  { *ptr_data = spi_io(0xff);
    length--;
    ptr_data++;
  };
  // get the 2 CRC bytes
  spi_io(0xff);
  spi_io(0xff);
};

// gets the status register
u16 MMC_get_status_reg(void)
{ u16 retval;
  // select card
  MMC_CS_select();
  // tell the MMC card that we want to know its status
  MMC_send_cmd(MMC_CMD_13_SEND_STATUS,0x0,0x0);
  // get the R2 response
  retval = MMC_get_R2();
  // cleanup behind us
  MMC_cleanup();
  return retval;
};

// reads the CID reg from the card
void MMC_get_CID(u08 *ptr_data)
{ // select card
  MMC_CS_select();
  // tell the MMC card that we want to know its status
  MMC_send_cmd(MMC_CMD_10_SEND_CID,0x0,0x0);
  // get the response
  MMC_get_R1();
  // get the register data
  MMC_get_data(ptr_data, 16);
  // cleanup behind us
  MMC_cleanup();
};

// reads the CSD reg from the card
void MMC_get_CSD(u08 *ptr_data)
{ // select card
  MMC_CS_select();
  // tell the MMC card that we want to know its status
  MMC_send_cmd(MMC_CMD_9_SEND_CSD,0x0,0x0);
  // get the response
  MMC_get_R1();
  // get the register data
  MMC_get_data(ptr_data, 16);
  // cleanup behind us
  MMC_cleanup();
};

// set the BLOCKLEN for transmissions
void MMC_set_blocklen(u16 blocklen)
{ // make sure this block len is not already set
  if(current_blocklen != blocklen)
  { current_blocklen= blocklen;
    // select card
    MMC_CS_select();
    // tell the MMC card that we want to know its status
    MMC_send_cmd(MMC_CMD_16_BLOCKLEN,0x0,blocklen);
    // get the response
    MMC_get_R1();
    // cleanup behind us
    MMC_cleanup();
  };
};


// returns the :
//    size of the card in MB ( ret * 1024^2) == bytes
//    sector count and multiplier MB are in u08 == C_SIZE / (2^(9-C_SIZE_MULT))
//    name of the media
void MMC_get_volume_info(VOLUME_INFO* vinf)
{ u08 data[16];
  // read the CSD register
  MMC_get_CSD(data);
  // get the C_SIZE value. bits [73:62] of data
  // [73:72] == data[6] && 0x03
  // [71:64] == data[7]
  // [63:62] == data[8] && 0xc0
  vinf->sector_count = data[6] & 0x03;
  vinf->sector_count <<= 8;
  vinf->sector_count += data[7];
  vinf->sector_count <<= 2;
  vinf->sector_count += (data[8] & 0xc0) >> 6;

  // get the val for C_SIZE_MULT. bits [49:47] of data
  // [49:48] == data[5] && 0x03
  // [47]    == data[4] && 0x80
  vinf->sector_multiply = data[9] & 0x03;
  vinf->sector_multiply <<= 1;
  vinf->sector_multiply += (data[10] & 0x80) >> 7;

  // work out the MBs
  // mega bytes in u08 == C_SIZE / (2^(9-C_SIZE_MULT))
  vinf->size_MB = vinf->sector_count >> (9-vinf->sector_multiply);
  // get the name of the card
  MMC_get_CID(data);
  vinf->name[0] = data[3];
  vinf->name[1] = data[4];
  vinf->name[2] = data[5];
  vinf->name[3] = data[6];
  vinf->name[4] = data[7];
  vinf->name[5] = '\0';
};

// sets up the pins used by the MMC interface
void MMC_hw_init(void)
{ // set MMC CS pin high output
  sbi(MMC_CS_DDR, PIN_MMC_CS);
  sbi(MMC_CS_PORT,PIN_MMC_CS);
  MMC_enable();
};

// starts up the MMC card
void MMC_init(void)
{ u08 i;
  u08 res;
  // the data sheet says that the MMC needs 74 clock pulses to startup
  // 10*8== 80; 80>76
  for( i = 0; i < 10; i++)
  { spi_io(0xff);
  };

  // select card
  MMC_CS_select();
  // put MMC in idle
  MMC_send_cmd(MMC_CMD_0_GO_IDLE,0x0,0x0);
  // get the response
  res = MMC_get_R1();

  // tell the MMC to start its init process by sending the MMC_CMD_1_SEND_OP_COND comand
  // until the response has the idle bit set to 0
  while(res==0x01)
  { // deselect card
    MMC_CS_deselect();
    // send 8 clock pulses
    spi_io(0xff);
    // select card
    MMC_CS_select();
    // send wake up signal s.t. MMC leaves idle state and switches to operation mode
    MMC_send_cmd(MMC_CMD_1_SEND_OP_COND,0x0,0x0);
    // get response
    res = MMC_get_R1();
  };
  // cleanup behind us
  MMC_cleanup();

  // find out some info on card
  VOLUME_INFO vinf;
  MMC_get_volume_info(&vinf);

  if (vinf.name[0]==0 && vinf.name[0]==0 && vinf.name[0]==0 &&
      vinf.name[0]==0 && vinf.name[0]==0)
      while(1); // no card found -> stop

};

// starts the read process of a sector
void MMC_get_sec_start(u16 sectorh, u16 sectorl)
{ MMC_set_blocklen(512);
  // turn sectors into byte addr
  sectorh = (sectorh << 9) + (sectorl >> 7);
  sectorl = sectorl << 9;
  // select card
  MMC_CS_select();
  // tell the MMC card that we want to know its status
  MMC_send_cmd(MMC_CMD_17_READ_SINGLE, sectorh, sectorl);
  // get the response
  MMC_get_R1();
  // wait till the mmc starts sending data
  MMC_wait_for_start_token(255);
};

// starts the read process of a part of a sector
void MMC_get_part_sec_start(u16 sectorh, u16 sectorl, u16 offset, u16 length)
{ MMC_set_blocklen(length);
  // turn sectors into byte addr
  sectorh = (sectorh << 9) + (sectorl >> 7);
  sectorl = sectorl << 9;
  sectorl += offset;
  // select card
  MMC_CS_select();
  // tell the MMC card that we want to know its status
  MMC_send_cmd(MMC_CMD_17_READ_SINGLE, sectorh, sectorl);
  // get the response
  MMC_get_R1();
  // wait till the mmc starts sending data
  MMC_wait_for_start_token(255);
};

// gets the next byte from the MMC card
u08 MMC_get_sec_next(void)
{ u08 tmp = spi_io(0xff);
  return tmp;
};

// stop read process of a sector
void MMC_get_sec_stop(void)
{ // get the 2 CRC bytes
  spi_io(0xff);
  spi_io(0xff);
  // give enough time
  MMC_cleanup();
};

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -