📄 mmc.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 + -