Full Version : Captain's Multi-Media Card (MMC) Interface (AVR)
avr >>TECHNICAL & HARDWARE >>Captain's Multi-Media Card (MMC) Interface (AVR)


AVR_Admin- 04-29-2006
Atmel ATmega (ATmega16) - MMC (Multi Media Card) Flash Memory Extension

It is easy to interface a MMC (MultiMediaCard) with an Atmel ATmega16 (ATmega series) via the SPI (Serial Port Interface). This is a very handy data logging circuit with lots of memory for data storage. I2C RAM's or EEPROM's are hardly available at sizes bigger than 256kb, but this solution with a 64MB Flash MMC is not to beat in both cost effectiveness and storage volume!
Also see: MicroChip PIC - MMC Flash Memory Extension

This version utilizes an ATmega16, but it's easy to port the schematic and software to other ATmega's.

The MMC is connected to the SPI pins of the ATmega16 via simple resistor voltage dividers to transform the +5V high levels to about 3.3V used by the MMC. The supply voltage for the MMC (2.7V - 3.6V) comes from a LD1086V33 voltage regulator (3.3V) or equivalent. The data-out pin from the MMC goes directly to the ATmega16, because 3.3V is high for the ATmega16 anyway.
The MMC Clock can be in the range of 0-20MHz.

The firmware:

Use avr-gcc or WinAVR to compile mmc.c (source code below) and the Atmel ATmega16 Parallel Port Programmer to program the uC. "make" compiles the firmware, "make load" programs the uC with "uisp".

Linux software (serterm2) for receiving data over the serial port can be found at the PIC - MMC (Multi Media Card) Flash Memory Extension page. Also on that page, the program "ser". It sends three characters ("ABC") via the PC serial port and tries to receive them. These three characters are returned by the MCU via the interrupt service routine.


Link to Site: http://www.captain.at/electronic-atmega-mmc.php

Testing:

If your hardware is ready, disconnect power to it and start "serterm2". Now connect power to your uC-board and you should see the following output on your computer via "serterm2":

# ./serterm
baud=9600
baud=9600
listening...
MCU online

MMC online

Captain was here! Captain was here! Captain was here! Captain was here! Captain was here!
Captain was here! Captain was here! Captain was here! Captain was here! Captain was here!
Captain was here! Captain was here! Captain was here! Captain was here! Captain was here!
Captain was here! Captain was here! Captain was here! Captain was here! Captain was here!
Captain was here! Captain was here! Captain was here! Captain was here! Captain was here!
Captain was here! Captain was here! Captain was here! Captain

512 bytes sent

blinking LED now

The firmware writes 512 bytes of data to the internal RAM and writes that sector to the MMC. Then those 512 bytes are read back from the MMC and sent via the serial port. After that the MCU goes into a loop and blinks the LED on pin 19 (similar to avrledtest.c at the Atmel ATmega16 Parallel Port Programmer page). Furthermore the interrupt service routine (ISR) returns any character sent over the serial port.

UPDATE:
The while loop in the MMC-read function can hang at high transfer speeds
while(SPI(0xFF) != (char)0xFE);

so you're probably better of with a solution like this:
int i;
uint8_t c;
for (i = 0; i < 100000; i++) {
c = SPI(0xFF);
if (c == (char)0xFE) break;
}
if (c != (char)0xFE) {
// ERROR HANDLING HERE
}
// proceed with MMC-read

(this code snippet has NOT been tested - it's just an idea)

Same goes for the while loop
while(SPI(0xFF) != (char)0xFF);

in the write function.


mmc.c:
CODE

/*********************************************
* Chip type           : ATmega16
* Clock frequency     : 2457600Hz
*********************************************/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <inttypes.h>
#include <avr/iom16.h>

#define F_OSC 2457600             /* oscillator-frequency in Hz */
#define UART_BAUD_RATE 9600
#define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)

#define SPIDI 6 // Port B bit 6 (pin7): data in (data from MMC)
#define SPIDO 5 // Port B bit 5 (pin6): data out (data to MMC)
#define SPICLK 7 // Port B bit 7 (pin8): clock
#define SPICS 4 // Port B bit 4 (pin5: chip select for MMC

char sector[512];

void delay_ms(unsigned short ms) {
unsigned short outer1, outer2;
outer1 = 200;
while (outer1) {
 outer2 = 1000;
 while (outer2) {
  while ( ms ) ms--;
  outer2--;
 }
 outer1--;
}
}

void usart_putc(unsigned char c) {
  // wait until UDR ready
while(!(UCSRA & (1 << UDRE)));
UDR = c;    // send character
}

void uart_puts (char *s) {
//  loop until *s != NULL
while (*s) {
 usart_putc(*s);
 s++;
}
}

void init(void) {
// set baud rate
UBRRH = (uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)>>8);
UBRRL = (uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);
// Enable receiver and transmitter; enable RX interrupt
UCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE);
//asynchronous 8N1
UCSRC = (1<<URSEL)|(3<<UCSZ0);
}

// INTERRUPT can be interrupted
// SIGNAL can't be interrupted
SIGNAL (SIG_UART_RECV) { // USART RX interrupt
unsigned char c;
c = UDR;
usart_putc(c);
}

void serialterminate(void) { // terminate sent string!!!
while(!(UCSRA & (1 << UDRE)));
UDR = 0x0d;
while(!(UCSRA & (1 << UDRE)));
UDR = 0x0a;
}

void SPIinit(void) {
DDRB &= ~(1 << SPIDI); // set port B SPI data input to input
DDRB |= (1 << SPICLK); // set port B SPI clock to output
DDRB |= (1 << SPIDO); // set port B SPI data out to output
DDRB |= (1 << SPICS); // set port B SPI chip select to output
SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0);
PORTB &= ~(1 << SPICS); // set chip select to low (MMC is selected)
}

char SPI(char d) {  // send character over SPI
char received = 0;
SPDR = d;
while(!(SPSR & (1<<SPIF)));
received = SPDR;
return (received);
}


char Command(char befF, uint16_t AdrH, uint16_t AdrL, char befH )
{ // sends a command to the MMC
SPI(0xFF);
SPI(befF);
SPI((uint8_t)(AdrH >> 8));
SPI((uint8_t)AdrH);
SPI((uint8_t)(AdrL >> 8));
SPI((uint8_t)AdrL);
SPI(befH);
SPI(0xFF);
return SPI(0xFF); // return the last received character
}

int MMC_Init(void) { // init SPI
char i;
PORTB |= (1 << SPICS); // disable MMC
// start MMC in SPI mode
for(i=0; i < 10; i++) SPI(0xFF); // send 10*8=80 clock pulses
PORTB &= ~(1 << SPICS); // enable MMC

if (Command(0x40,0,0,0x95) != 1) goto mmcerror; // reset MMC

st: // if there is no MMC, prg. loops here
if (Command(0x41,0,0,0xFF) !=0) goto st;
return 1;
mmcerror:
return 0;
}

void fillram(void)  { // fill RAM sector with ASCII characters
int i,c;
char mystring[18] = "Captain was here! ";
c = 0;
for (i=0;i<=512;i++) {
 sector[i] = mystring[c];
 c++;
 if (c > 17) { c = 0; }
}
}

int writeramtommc(void) { // write RAM sector to MMC
int i;
uint8_t c;
// 512 byte-write-mode
if (Command(0x58,0,512,0xFF) !=0) {
 uart_puts("MMC: write error 1 ");
 return 1;
}
SPI(0xFF);
SPI(0xFF);
SPI(0xFE);
// write ram sectors to MMC
for (i=0;i<512;i++) {
 SPI(sector[i]);
}
// at the end, send 2 dummy bytes
SPI(0xFF);
SPI(0xFF);

c = SPI(0xFF);
c &= 0x1F;  // 0x1F = 0b.0001.1111;
if (c != 0x05) { // 0x05 = 0b.0000.0101
 uart_puts("MMC: write error 2 ");
 return 1;
}
// wait until MMC is not busy anymore
while(SPI(0xFF) != (char)0xFF);
return 0;
}

int sendmmc(void) { // send 512 bytes from the MMC via the serial port
int i;
// 512 byte-read-mode
if (Command(0x51,0,512,0xFF) != 0) {
 uart_puts("MMC: read error 1 ");
 return 1;
}
// wait for 0xFE - start of any transmission
// ATT: typecast (char)0xFE is a must!
while(SPI(0xFF) != (char)0xFE);

for(i=0; i < 512; i++) {
 while(!(UCSRA & (1 << UDRE))); // wait for serial port
 UDR = SPI(0xFF);  // send character
}
serialterminate();
// at the end, send 2 dummy bytes
SPI(0xFF); // actually this returns the CRC/checksum byte
SPI(0xFF);
return 0;
}

int main(void) {
init();
SPIinit();

uart_puts("MCU online");
serialterminate();

MMC_Init();

uart_puts("MMC online");
serialterminate();

sei(); // enable interrupts

fillram();
writeramtommc();
sendmmc();

uart_puts("512 bytes sent");
serialterminate();
uart_puts("blinking LED now");
serialterminate();

// enable  PD5 as output
DDRD |= (1<<PD5);
while (1) {
 // PIN5 PORTD clear -> LED off
 PORTD &= ~(1<<PD5);
 delay_ms(500);
 // PIN5 PORTD set -> LED on
 PORTD |= (1<<PD5);
 delay_ms(500);
}
return 0;
}

Makefile:
CODE

MCU=atmega16
CC=avr-gcc
OBJCOPY=avr-objcopy
# optimize for size:
CFLAGS=-g -mmcu=$(MCU) -Wall -Wstrict-prototypes -Os -mcall-prologues
#-------------------
all: mmc.hex
#-------------------
mmc.hex : mmc.out
$(OBJCOPY) -R .eeprom -O ihex mmc.out mmc.hex
mmc.out : mmc.o
$(CC) $(CFLAGS) -o mmc.out -Wl,-Map,mmc.map mmc.o
mmc.o : mmc.c
$(CC) $(CFLAGS) -Os -c mmc.c
# you need to erase first before loading the program.
# load (program) the software into the eeprom:
load: mmc.hex
uisp -dlpt=/dev/parport0 --erase  -dprog=dapa
uisp -dlpt=/dev/parport0 --upload if=mmc.hex -dprog=dapa  -v=3 --hash=32
#-------------------
clean:
rm -f *.o *.map *.out
#-------------------




Forumer™ is Voted #1 Free Forum Hosting provider
Build your own community today with the largest message board hosting company.