这是一个MSP430与MMC/SD卡接口设计,提供纯源代码。真正DIY设计!
由Rolf Freitag先生提供。
原文:A self documenting project (C source code) for reading/writing on MMC/SD cards in spi mode and with a hello world LED as output. The code works with many MMC/SD cards but not all; it is beta. The reasons are that some waiting times are not limited in the standard and that the cards are a little outside the standard specifications.
For a release version, which does not cost much and which has a mapping layer for reading/writing 1..512 Bytes starting at any address, you can contact me.
In 2003 TI planned to release an application note to this code but it seems that plan died.
I published the code under a beerware license:
main.c mmc.c mmc.h led.c led.h
main.c
/*********************************************************
Program for Communikation of an MSP430F149 and an MMC via SPI in unprotected Mode.
Sytem quartz: 8 MHz, layout: see mmc.h.
Version 0.02 from 11. May 2003
Status: Everything works, but approx. 2 % of all MMC/SDCs do need some longer waiting cycles,
because some times are not limitated in the standards and every card is not strictly standard
conforming; they all do need more waiting cycles as specified in the standards.
*********************************************************/
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 43):
* Rolf Freitag (webmaster at true-random.com) wrote this file.
* As long as you retain this notice you can do whatever
* the LGPL (Lesser GNU public License) allows with this stuff.
* If you think this stuff is worth it, you can send me money via
* paypal or if we met some day you can buy me a beer in return.
* ----------------------------------------------------------------------------
*/
#include <msp430x14x.h>
#include <string.h>
#include "mmc.h"
#include "led.h"
extern char card_state;
extern char mmc_buffer[512];
char card_state = 0; // card state: 0: not found, 1 found (init successfull)
unsigned long loop;
void main (void)
{
volatile unsigned char dco_cnt = 255;
BCSCTL1 &= ~XT2OFF; // XT2on
do // wait for MCLK from quartz
{
IFG1 &= ~OFIFG; // Clear OSCFault flag
for (dco_cnt = 0xff; dco_cnt > 0; dco_cnt--); // Time for flag to set
}
while ((IFG1 & OFIFG) != 0); // OSCFault flag still set?
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
BCSCTL1 = 0x07; // LFXT1: XT2on: LF quartz for MCLK
BCSCTL2 = SELM1 + SELS; // LFXT2: use HF quartz (XT2)
DCOCTL = 0xe0;
P3SEL = 0x00;
P3DIR = 0xcf;
P3OUT = 0x0f;
// Port 4 Function Dir On/Off
// 4.0-Led red Out 0 - off 1 - On
// 4.1-Led green Out 0 - off 1 - On
// 4.5-CardDetected ? 0 - ? 1 - ?
// 4.6-WriteProtected ? 0 - ? 1 - ?
// D 7 6 5 4 3 2 1 0
P4SEL = 0x00; // 0 0 0 0 0 0 0 0
P4DIR = 0x03; // 0 0 0 0 0 0 1 1
P4OUT = 0x00;
// Port 5 Function Dir On/Off
// 5.1-Dout Out 0 - off 1 - On
// 5.2-Din Inp 0 - off 1 - On
// 5.3-Clk Out -
// 5.4-mmcCS Out 0 - Active 1 - none Active
P5SEL = 0x00;
P5DIR = 0xff;
P5OUT = 0xfe;
P6SEL = 0x00;
P6OUT = 0x00;
P6DIR = 0xff;
ADC12IE = 0;
ADC12IFG = 0;
initLED();
for (;;)
{
// switch on red led to indicate -> insert card
// #ifdef DeBuG0
RED_LED_ON();
GREEN_LED_OFF();
// Card insert?
while(P4IN&0x20);
//switch off both led's
GREEN_LED_OFF();
RED_LED_OFF();
//init mmc card
if (initMMC() == MMC_SUCCESS) // card found
{
card_state |= 1;
GREEN_LED_ON();
// Read Out Card Type and print it or trace memory
memset(&mmc_buffer,0,512);
mmcReadRegister (10, 16);
mmc_buffer[7]=0;
// PLease mofify based on your Compiler sim io function
// debug_printf("Multi Media Card Name: %s",&mmc_buffer[3]);
// Fill first Block (0) with 'A'
memset(&mmc_buffer,'A',512); //set breakpoint and trace mmc_buffer contents
mmcWriteBlock(0x00);
// Fill second Block (1)-AbsAddr 512 with 'B'
memset(&mmc_buffer,'B',512);
mmcWriteBlock(512);
// Read first Block back to buffer
memset(&mmc_buffer,0x00,512);
mmcReadBlock(0x00,512);
memset(&mmc_buffer,0x00,512); //set breakpoint and trace mmc_buffer contents
mmcReadBlock(512,512);
memset(&mmc_buffer,0x00,512); //set breakpoint and trace mmc_buffer contents
}
else
{
//Error card not detected or rejected during writing
// switch red led on
card_state = 0; // no card
RED_LED_ON();
}
// #endif
}
// return;
}
mmc.h
/*
mmc.h: Dekcarations for Communikation with the MMC (see mmc.c) in unprotected spi mode.
Pin configuration at MSP430F149:
--------------------------------
MC MC Pin MMC MMC Pin
P5.4 48 ChipSelect 1
P5.1 / SlaveInMasterOut 45 DataIn 2
. GND 3 (0 V)
. VDD 4 (3.3 V)
P5.3 UCLK1 / SlaveCLocK 47 Clock 5
. GND 6 (0 V)
P5.2 / SlaveOutMasterIn 46 DataOut 7
P5.4 CardDetect with pullup
---------------------------------------------------------------------
Revisions
Date Author Revision
11. May 2003 Rolf Freitag 0.02
(2004: corrected MC pin numbers (switched only 45, 46))
*/
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 44):
* Rolf Freitag (webmaster at true-random.com) wrote this file.
* As long as you retain this notice you can do whatever
* the LGPL (Lesser GNU public License) allows with this stuff.
* If you think this stuff is worth it, you can send me money via
* paypal or if we met some day you can buy me a beer in return.
* ----------------------------------------------------------------------------
*/
#ifndef _MMCLIB_H
#define _MMCLIB_H
#ifndef TXEPT // transmitter-empty flag
#define TEXPT 0x01
#endif
// macro defines
#define HIGH(a) ((a>>8)&0xFF) // high byte from word
#define LOW(a) (a&0xFF) // low byte from word
#define CS_LOW() P5OUT &= ~0x10 // Card Select
#define CS_HIGH() P5OUT |= 0x10 // Card Deselect
#define SPI_RXC (IFG2 & URXIFG1)
#define SPI_TXC (IFG2 & UTXIFG1)
#define SPI_RX_COMPLETE (IFG2 & URXIFG1)
#define SPI_TX_READY (IFG2 & UTXIFG1)
#define DUMMY 0xff
// Tokens (nessisary because at nop/idle (and CS active) only 0xff is on the data/command line)
#define MMC_START_DATA_BLOCK_TOKEN 0xfe // Data token start byte, Start Single Block Read
#define MMC_START_DATA_MULTIPLE_BLOCK_READ 0xfe // Data token start byte, Start Multiple Block Read
#define MMC_START_DATA_BLOCK_WRITE 0xfe // Data token start byte, Start Single Block Write
#define MMC_START_DATA_MULTIPLE_BLOCK_WRITE 0xfc // Data token start byte, Start Multiple Block Write
#define MMC_STOP_DATA_MULTIPLE_BLOCK_WRITE 0xfd // Data toke stop byte, Stop Multiple Block Write
// an affirmative R1 response (no errors)
#define MMC_R1_RESPONSE 0x00
// this variable will be used to track the current block length
// this allows the block length to be set only when needed
// unsigned long _BlockLength = 0;
// error/success codes
#define MMC_SUCCESS 0x00
#define MMC_BLOCK_SET_ERROR 0x01
#define MMC_RESPONSE_ERROR 0x02
#define MMC_DATA_TOKEN_ERROR 0x03
#define MMC_INIT_ERROR 0x04
#define MMC_CRC_ERROR 0x10
#define MMC_WRITE_ERROR 0x11
#define MMC_OTHER_ERROR 0x12
#define MMC_TIMEOUT_ERROR 0xFF
// commands: first bit 0 (start bit), second 1 (transmission bit); CMD-number + 0ffsett 0x40
#define MMC_GO_IDLE_STATE 0x40 //CMD0
#define MMC_SEND_OP_COND 0x41 //CMD1
#define MMC_READ_CSD 0x49 //CMD9
#define MMC_SEND_CID 0x4a //CMD10
#define MMC_STOP_TRANSMISSION 0x4c //CMD12
#define MMC_SEND_STATUS 0x4d //CMD13
#define MMC_SET_BLOCKLEN 0x50 //CMD16 Set block length for next read/write
#define MMC_READ_SINGLE_BLOCK 0x51 //CMD17 Read block from memory
#define MMC_READ_MULTIPLE_BLOCK 0x52 //CMD18
#define MMC_CMD_WRITEBLOCK 0x54 //CMD20 Write block to memory
#define MMC_WRITE_BLOCK 0x58 //CMD25
#define MMC_WRITE_MULTIPLE_BLOCK 0x59 //CMD??
#define MMC_WRITE_CSD 0x5b //CMD27 PROGRAM_CSD
#define MMC_SET_WRITE_PROT 0x5c //CMD28
#define MMC_CLR_WRITE_PROT 0x5d //CMD29
#define MMC_SEND_WRITE_PROT 0x5e //CMD30
#define MMC_TAG_SECTOR_START 0x60 //CMD32
#define MMC_TAG_SECTOR_END 0x61 //CMD33
#define MMC_UNTAG_SECTOR 0x62 //CMD34
#define MMC_TAG_EREASE_GROUP_START 0x63 //CMD35
#define MMC_TAG_EREASE_GROUP_END 0x64 //CMD36
#define MMC_UNTAG_EREASE_GROUP 0x65 //CMD37
#define MMC_EREASE 0x66 //CMD38
#define MMC_READ_OCR 0x67 //CMD39
#define MMC_CRC_ON_OFF 0x68 //CMD40
//TI added sub function for top two spi_xxx
// mmc init
char initMMC (void);
// send command to MMC
void mmcSendCmd (const char cmd, unsigned long data, const char crc);
// set MMC block length of count=2^n Byte
char mmcSetBlockLength (const unsigned long);
// read a size Byte big block beginning at the address.
char mmcReadBlock(const unsigned long address, const unsigned long count);
// write a 512 Byte big block beginning at the (aligned) adress
char mmcWriteBlock (const unsigned long address);
// Register arg1 der Laenge arg2 auslesen (into the buffer)
char mmcReadRegister(const char, const unsigned char);
#endif /* _MMCLIB_H */
mmc.c
// mmc.c : MultiMediaCard functions: init, read, write ...
// Works also with SC cars. Modes: SPI mode.
//
// Rolf Freitag 5/2003
//
/* See also:
http://www.sandisk.com/pdf/oem/ProdManualSDCardv1.9.pdf Sandisk SD card product manual
http://www.sandisk.com/pdf/oem/ProdManualminiSDv1.1.pdf MiniSD card
http://www.sandisk.com/download/Product%20Manuals/Product%20ManualSDCardv1.7.pdf Older SD card
http://www.compile-it.com/support/crndatasheets/MMC%20ADAPTER.pdf
*/
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 44):
* Rolf Freitag (webmaster at true-random.com) wrote this file.
* As long as you retain this notice you can do whatever
* the LGPL (Lesser GNU public License) allows with this stuff.
* If you think this stuff is worth it, you can send me money via
* paypal or if we met some day you can buy me a beer in return.
* ----------------------------------------------------------------------------
*/
// MMC Lib
#ifndef _MMCLIB_C
#define _MMCLIB_C
//---------------------------------------------------------------------
#include "mmc.h"
#include "led.h"
#include "MSP430x14x.H"
#include "math.h"
#include "string.h"
char mmcGetResponse(void);
char mmcGetXXResponse(const char resp);
char mmcCheckBusy(void);
void initSPI (void);
unsigned char spiSendByte(const unsigned char data);
char mmc_buffer[512] = // Buffer for mmc i/o for data and registers
{
0
};
extern char card_state; // 0 for no card found, 1 for card found (init successfull)
//---------------------------------------------------------------------
// setup usart1 in spi mode
void initSPI (void)
{
ME2 |= USPIE1; // Enable USART1 SPI mode
UTCTL1 = CKPH | SSEL1 | SSEL0 | STC; // SMCLK, 3-pin mode, clock idle low, data valid on rising edge, UCLK delayed
UBR01 = 0x02; // 0x02: UCLK/2 (4 MHz), works also with 3 and 4
UBR11 = 0x00; // -"-
UMCTL1 = 0x00; // no modulation
UCTL1 = CHAR | SYNC | MM; // 8-bit SPI Master **SWRST**
P5SEL |= 0x0E; // P5.1-3 SPI option select
P5DIR |= 0x01; // P5.0 output direction
P5OUT = 0xff;
while (!(IFG2 & UTXIFG1)); // USART1 TX buffer ready (empty)?
// debug_printf("init......SPI");
}
// Initialisieren
char initMMC (void)
{
//raise SS and MOSI for 80 clock cycles
//SendByte(0xff) 10 times with SS high
//RAISE SS
int i;
char response=0x01;
// debug_printf("Start iniMMC......");
initSPI();
//initialization sequence on PowerUp
CS_HIGH();
for(i=0;i<=9;i++)
spiSendByte(0xff);
CS_LOW();
//Send Command 0 to put MMC in SPI mode
mmcSendCmd(0x00,0,0x95);
//Now wait for READY RESPONSE
if(mmcGetResponse()!=0x01);
// debug_printf("no responce");
while(response==0x01)
{
// debug_printf("Sending Command 1");
CS_HIGH();
spiSendByte(0xff);
CS_LOW();
mmcSendCmd(0x01,0x00,0xff);
response=mmcGetResponse();
}
CS_HIGH();
spiSendByte(0xff);
// debug_printf("MMC INITIALIZED AND SET TO SPI MODE PROPERLY.");
return MMC_SUCCESS;
}
// Ti added mmc Get Responce
char mmcGetResponse(void)
{
//Response comes 1-8bytes after command
//the first bit will be a 0
//followed by an error code
//data will be 0xff until response
int i=0;
char response;
while(i<=64)
{
response=spiSendByte(0xff);
if(response==0x00)break;
if(response==0x01)break;
i++;
}
return response;
}
char mmcGetXXResponse(const char resp)
{
//Response comes 1-8bytes after command
//the first bit will be a 0
//followed by an error code
//data will be 0xff until response
int i=0;
char response;
while(i<=500)
{
response=spiSendByte(0xff);
if(response==resp)break;
i++;
}
return response;
}
char mmcCheckBusy(void)
{
//Response comes 1-8bytes after command
//the first bit will be a 0
//followed by an error code
//data will be 0xff until response
int i=0;
char response;
char rvalue;
while(i<=64)
{
response=spiSendByte(0xff);
response &= 0x1f;
switch(response)
{
case 0x05: rvalue=MMC_SUCCESS;break;
case 0x0b: return(MMC_CRC_ERROR);
case 0x0d: return(MMC_WRITE_ERROR);
default:
rvalue = MMC_OTHER_ERROR;
break;
}
if(rvalue==MMC_SUCCESS)break;
i++;
}
i=0;
do
{
response=spiSendByte(0xff);
i++;
}while(response==0);
return response;
}
// The card will respond with a standard response token followed by a data
// block suffixed with a 16 bit CRC.
// Ti Modification: long int -> long ; int -> long
char mmcReadBlock(const unsigned long address, const unsigned long count)
{
unsigned long i = 0;
char rvalue = MMC_RESPONSE_ERROR;
// Set the block length to read
if (mmcSetBlockLength (count) == MMC_SUCCESS) // block length could be set
{
// SS = LOW (on)
CS_LOW ();
// send read command MMC_READ_SINGLE_BLOCK=CMD17
mmcSendCmd (17,address, 0xFF);
// Send 8 Clock pulses of delay, check if the MMC acknowledged the read block command
// it will do this by sending an affirmative response
// in the R1 format (0x00 is no errors)
if (mmcGetResponse() == 0x00)
{
// now look for the data token to signify the start of
// the data
if (mmcGetXXResponse(MMC_START_DATA_BLOCK_TOKEN) == MMC_START_DATA_BLOCK_TOKEN)
{
// clock the actual data transfer and receive the bytes; spi_read automatically finds the Data Block
for (i = 0; i < 512; i++)
mmc_buffer = spiSendByte(0xff); // is executed with card inserted
// get CRC bytes (not really needed by us, but required by MMC)
spiSendByte(0xff);
spiSendByte(0xff);
rvalue = MMC_SUCCESS;
}
else
{
// the data token was never received
rvalue = MMC_DATA_TOKEN_ERROR; // 3
}
}
else
{
// the MMC never acknowledge the read command
rvalue = MMC_RESPONSE_ERROR; // 2
}
}
else
{
rvalue = MMC_BLOCK_SET_ERROR; // 1
}
CS_HIGH ();
spiSendByte(0xff);
return rvalue;
} // mmc_read_block
//---------------------------------------------------------------------
// Ti Modification: long int -> long
char mmcWriteBlock (const unsigned long address)
{
unsigned long i = 0;
char rvalue = MMC_RESPONSE_ERROR; // MMC_SUCCESS;
char c = 0x00;
// Set the block length to read
if (mmcSetBlockLength (512) == MMC_SUCCESS) // block length could be set
{
// SS = LOW (on)
CS_LOW ();
// send write command
mmcSendCmd (24,address, 0xFF);
// check if the MMC acknowledged the write block command
// it will do this by sending an affirmative response
// in the R1 format (0x00 is no errors)
if (mmcGetXXResponse(MMC_R1_RESPONSE) == MMC_R1_RESPONSE)
{
spiSendByte(0xff);
// send the data token to signify the start of the data
spiSendByte(0xfe);
// clock the actual data transfer and transmitt the bytes
for (i = 0; i < 512; i++)
spiSendByte(mmc_buffer); // mmc_buffer; Test: i & 0xff
// put CRC bytes (not really needed by us, but required by MMC)
spiSendByte(0xff);
spiSendByte(0xff);
// read the data response xxx0<status>1 : status 010: Data accected, status 101: Data
// rejected due to a crc error, status 110: Data rejected due to a Write error.
mmcCheckBusy();
}
else
{
// the MMC never acknowledge the write command
rvalue = MMC_RESPONSE_ERROR; // 2
}
}
else
{
rvalue = MMC_BLOCK_SET_ERROR; // 1
}
// give the MMC the required clocks to finish up what ever it needs to do
// for (i = 0; i < 9; ++i)
// spiSendByte(0xff);
CS_HIGH ();
// Send 8 Clock pulses of delay.
spiSendByte(0xff);
return rvalue;
} // mmc_write_block
//---------------------------------------------------------------------
void mmcSendCmd (const char cmd, unsigned long data, const char crc)
{
char frame[6];
char temp;
int i;
frame[0]=(cmd|0x40);
for(i=3;i>=0;i--)
{
temp=(char)(data>>(8*i));
frame[4-i]=(temp);
}
frame[5]=(crc);
for(i=0;i<6;i++)
spiSendByte(frame);
}
//--------------- set blocklength 2^n ------------------------------------------------------
// Ti Modification: long int-> long
char mmcSetBlockLength (const unsigned long blocklength)
{
char rValue = MMC_TIMEOUT_ERROR;
char i = 0;
// SS = LOW (on)
CS_LOW ();
// Set the block length to read
//MMC_SET_BLOCKLEN =CMD16
mmcSendCmd(16, blocklength, 0xFF);
// get response from MMC - make sure that its 0x00 (R1 ok response format)
if(mmcGetResponse()!=0x00);
CS_HIGH ();
// Send 8 Clock pulses of delay.
spiSendByte(0xff);
return MMC_SUCCESS;
} // block_length
//TI added substitution routine for spi_read and spi_write
unsigned char spiSendByte(const unsigned char data)
{
while ((IFG2&UTXIFG1) ==0); // wait while not ready / for RX
TXBUF1 = data; // write
while ((IFG2 & URXIFG1)==0); // wait for RX buffer (full)
return (RXBUF1);
}
// Reading the contents of the CSD and CID registers in SPI mode is a simple
// read-block transaction.
char mmcReadRegister (const char cmd_register, const unsigned char length)
{
char uc = 0;
char rvalue = MMC_TIMEOUT_ERROR;
// char i = 0;
if (mmcSetBlockLength (length) == MMC_SUCCESS)
{
CS_LOW ();
// CRC not used: 0xff as last byte
mmcSendCmd(cmd_register, 0x000000, 0xff);
// wait for response
// in the R1 format (0x00 is no errors)
if (mmcGetResponse() == 0x00)
{
if (mmcGetXXResponse(0xfe)== 0xfe)
for (uc = 0; uc < length; uc++)
mmc_buffer[uc] = spiSendByte(0xff);
// get CRC bytes (not really needed by us, but required by MMC)
spiSendByte(0xff);
spiSendByte(0xff);
}
else
rvalue = MMC_RESPONSE_ERROR;
// CS = HIGH (off)
CS_HIGH ();
// Send 8 Clock pulses of delay.
spiSendByte(0xff);
}
CS_HIGH ();
return rvalue;
} // mmc_read_register
//---------------------------------------------------------------------
#endif /* _MMCLIB_C */
led.h
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 43):
* Rolf Freitag (webmaster at true-random.com) wrote this file.
* As long as you retain this notice you can do whatever
* the LGPL (Lesser GNU public License) allows with this stuff.
* If you think this stuff is worth it, you can send me money via
* paypal or if we met some day you can buy me a beer in return.
* ----------------------------------------------------------------------------
*/
#ifndef _LED_H
#define _LED_H
#include <msp430x14x.h>
#define GREEN_LED_ON() P4OUT |= 0x02
#define GREEN_LED_OFF() P4OUT &= ~0x02
#define RED_LED_ON() P4OUT |= 0x01
#define RED_LED_OFF() P4OUT &= ~0x01
void initLED(void);
#endif
led.c
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 44):
* Rolf Freitag (webmaster at true-random.com) wrote this file.
* As long as you retain this notice you can do whatever
* the LGPL (Lesser GNU public License) allows with this stuff.
* If you think this stuff is worth it, you can send me money via
* paypal or if we met some day you can buy me a beer in return.
* ----------------------------------------------------------------------------
*/
#include <msp430x14x.h>
void initLED(void)
{
P4DIR |= 0x03;
P4SEL &= ~0x03;
P4OUT = 0x03;
}
|