64MB MMC und PIC-Mikrocontroller

MMC/SD-Speicherkarte mit PIC16F877 Schreiben /Lesen über SPI-Interface ( MMC 64MB und PIC-Microcontroller)
Ein SOFTWAREMÄßIGE SPI-Interface ermöglicht den Anschluss einer Multimedia Card (MMC) an jeden PIC-Mikrocontroller.
Schaltplan ,Theorie über SPI-Schnittstelle, Theorie über MMC, Signaldiagramme, Programm in C (CC5X).

Home
Programmierkurs für PIC-Microkontroller in C
( CC5X Compiler)

 
Erklärung des FAT-Systems
MMC mit FAT16 Formatierung auslesen,
Beispielprogramm

 
+

MMC (Multimedia Card) hier 64MB.
-Riesiger FLASH - Speicher.
-Sehr schnell.
-300000 Schreibzugriffe.
-Lesen ohne Einschränkung.
-Verbindung zum PIC mit 4 Leitungen.

Der PIC16F877 verfügt über :
- SPI-Schnittstelle
- RS232 Schnittstelle
- 8 Eingänge für den Analog/Digital-Wandler (10Bit)

PIN
Kürzel
Funktion
PIN
Kürzel
Funktion
1
/CS
Chip Aktivieren
18
RC2
Digital OUT
2
Data IN
Dateneingang
24
SDO
SPI Datenausgang
3
GND
-
-
-
-
4
Vdd
Spannungsversorgung
-
-
-
5
CLK
Taktleitung
18
SCL
SPI Clock
6
GND
-
-
-
-
7
Data OUT
Datenausgang
23
SDI
SPI Dateneingang
 

 

Schaltung und Schaltplan
Die MMC braucht als Spannungsversorgung 2,7V- 3,6V. Wir müssen auf jeden Fall einen Spannungsregler für 3,3V benutzen, zum Beispiel CS5203. PIC -Prozessoren können mit Spannungen zwischen 2V und 5,5V betrieben werden. Somit könnte man sich bei der Versorgung beider Einheiten auf 3,3V einigen. Dann ist es auch möglich, die MMC direkt mit dem Mikrocontroller zu verbinden.

Aufgrund von Überlegungen, dass die AD -Wandler und die serielle Kommunikation mit 5V stabiler funktioniert, wird in dieser Schaltung der PIC16F877 trotzdem mit 5V betrieben. Dafür bekommen die Mikrocontroller-Ausgänge SDO, SCL, /CS jeweils einen Spannungsteiler, so dass die MMC-Eingänge nur 3,2V erhalten. Die Eingänge des Mikroprozessors brauchen keine Verstärkung von 3,3V auf 5,0V, weil der PIC sowieso 3,3V als logische "1" versteht.

Schaltplan zum Ansehen und Drucken im TIF-Format
Der MAX232 sorgt für serielle Kommunikation mit PC ( 115200 Baud ).
Zur Kontaktierung von MMC passt ein alter Floppystecker ganz wunderbar oder es gibt bei http://www.segor.de einen sogenannten "MMC-Connector" für 4,90 EUR.


Wie funktioniert eigentlich SPI ?
SPI ist eine serielle synchrone Schnittstelle. Synchron heißt, dass jedes Bit während eines Taktimpulses empfangen / gesendet wird

Auf dem Bild sehen wir ein Register, wo ein Byte gesendet und empfangen wird. Es gab 2 Taktimpulse Bit 7 und Bit 6 sind über OUT gesendet. Und 2 Bits (auch 7, 6) von neuem Byte empfangen. Nach 8 Taktimpulsen ist das zu sendende Byte ganz "raus" und das empfangene Byte vollständig in dem Register. Wenn der PIC als Master arbeitet, dann erzeugt er selbst die Taktimpulse, die ein Slave -Gerät braucht, um rechtzeitig sein Register zu schieben und somit richtige Bits senden / empfangen zu können. Bei PIC-Microcontrollern genügt es in das SSPBUF-Register ein Byte zu übertragen, damit er anfängt dieses Byte zu senden und ein neues zu empfangen. Man darf aber nicht zu früh (während es Daten schiebt) SSPBUF auslesen. Deswegen prüfen wir das BF-Flag, es ist rückgesetzt während des Sendevorgangs.

MMC darf man bis 20MHz takten (CLK). Das SPI Interface der PIC kann maximal auf 5MHz eingestellt werden. Wenn man aber lange Leitungen zwischen PIC und MMC hat, dann muss man die Taktfrequenz niedriger einstellen, sonst geht die Signalform kaputt und es passieren die Fehler. (Bei mir sind es 15cm Leitungen).

Folgendes Diagramm zeigt Einstellungsmöglichkeiten für SPI -Interface bei PIC's



MMC Initialisieren in SPI-Modus
0 Spannung Ein
1 Senden 80 Taktimpulse
2 /CS Aktivieren , /CS=0, (1 passiv)
3 Senden CMD0
4 Bestätigungsbyte = 0x01 ? wenn Nein FEHLER
Wenn Ja
5 Sende 8 Taktimpulse
6 Senden CMD1
7 Bestätigungsbyte = 0x00 ?wenn Nein geh zum 6
Wenn Ja
8 Sende 8 Taktimpulse
MMC Initialisierung in SPI-Modus abgeschlossen.

Lauffähiges Programm, Funktionsbeschreibung

SerString("Hallo Welt") Diese Funktion dient zum Ausgeben von Meldungen.
while(!TXIF); //_ Prüfen ob Register TXREG leer ist
TXREG ='W'; // Buchstabe "W" seriell senden
Um ein Zeichen seriell zu senden, werden diese Befehle verwendet.
a=SPI(b); Funktion sendet über SPI Zeichen b und liefert empfangenes Zeichen in Variable a .
Command(0x49,0,512,0xFF ); Sendet zum MMC einen Befehl und eine Adresse.
MMC_Init() Hier wird SPI eingestellt, MMC Reset, MMC in SPI -Modus initialisiert.



#include <C:\cc5\16F877.H>
// Speicherschutz 12-13-4-5=aus, Debug 11=aus,ProgrammFlash 9=an, EEpromRead 8=an, NiederVoltProgr 7=aus
// NiederVoltReset 6=an, EinschaltTimer 3=an, WachDogTimer 2=aus, Oszilator 01=XC
#pragma config |= 0b.11.111101.11.00.10

#pragma bit CS @ PORTC.2	//Ausgang für Chip Select

#pragma origin 100		// Ab Adresse 100 im Programmspeicher
//*********************************************************************

void InitUSART()
{
BRGH=1;			// Hohe Geschwindigkeit für Datenübertragung
//SPBRG=129;		// (9600   baud @ 20MHz input clock)
//SPBRG=64;		// (19200  baud @ 20MHz input clock)
//SPBRG=32;		// (38400  baud @ 20MHz input clock)
SPBRG=10;		// (115200 baud @ 20MHz input clock)
SPEN = 1;		// Set_Serial_Pins;
SYNC = 0;		// Set_Async_Mode;
TX9 = 0;		// Set_8bit_Tx;
RX9 = 0;		// Set_8bit_Rx;
CREN = 1;		// Enable_Rx;
TXEN = 1;		// Enable_Tx;
RCIE=0;			// Rx Interrupt aus
}
//**********************************************************************

void SerString(const char *str)	// String seriell senden
{
char ps;
ps = *str;		// Pointer auf Start des Strings ins ps
while(ps>0)		// Prüfen ob String zu Ende ist
	{
	str++;			// Pointer auf nächstes Zeichen erhöhen
	if (ps== 0) break;	// Prüfen ob String zu Ende ist
	while(!TXIF);   	// Prüfen ob Register TXREG leer ist
	TXREG =ps ;		// Datenbyte senden
    	ps = *str;		// Inhalt des Pointers ins ps
	}
}
//********************************************************************

char SPI(char d)		// Über SPI-Schnittstelle senden
{				// 
SSPBUF=d;
while (!BF);			// 	Warten bis gesendet ist
return SSPBUF;			// gleichzeitig empfangen
}
//**********************************************************************

char Command(char befF,uns16 AdrH,uns16 AdrL,char befH )
{				// Einen Befehl zum MMC senden
char a;
SPI(0xFF);
SPI(befF);
SPI(AdrH.high8);
SPI(AdrH.low8);
SPI(AdrL.high8);
SPI(AdrL.low8);
SPI(befH);
SPI(0xFF);
return SPI(0xFF);		// Response als Rückgabewert
}
//**************************************************************************

bit MMC_Init()
{
// Init SPI
SMP=0;		// Input ist gültig in der Mitte des Clock's
CKE=0;		// Bei steigender Flanke Datenübernahme
CKP=1;		// High ist passiver Zustand
SSPM1=1;	// Geschwindigkeit f/64(312kHz), Master
//SSPM0=1;	// Geschwindigkeit f/16(1,25MHz), Master
SSPEN=1;	// SPI Ein

CS=1;		// MMC-Disabled

char i;		// Variablen

//MMC in SPI-Modus starten, Reset
for(i=0; i < 10; i++)SPI(0xFF);		// 10*8=80 mal takten
CS=0;					// MMC-Enabled

// CMD0
if (Command(0x40,0,0,0x95) !=1)goto Fehler ; 	// Reset

st:						// Wenn MMC nicht da ist, bleibt
// CMD1						// Programm hier stehen
if (Command(0x41,0,0,0xFF) !=0) goto st ;	// CMD1 solange wiederholen
/*
// CID auslesen
if (Command(0x4A,0,0,0xFF) !=0) goto Fehler;	// Karten ID-Nummer
for(i=0; i < 20; i++)
{
while(!TXIF);
TXREG =SPI(0xFF);
}

// CSD auslesen					// Kartenspezifische Daten
if (Command(0x49,0,0,0xFF) !=0) goto Fehler;
for(i=0; i < 20; i++)
{
while(!TXIF);
TXREG =SPI(0xFF);
}
*/
// Set Lenge (Default ist 512 Byte)
//if (Command(0x50,0,16,0xFF) !=0) goto Fehler;	// 16 Byte-Mode für Lesen

return 1;
Fehler:
return 0;
}
//**************************************************************************

void main(void)
{
INTCON=0;				// Interrupts aus
ADCON1=0x6;				// PortA Digital
TRISC=0b.1101.0011;			// sck rc3-0, sdo rc5-0, CS rc2-0.
TRISB=0b.0000.0010;   			// RB2>TX, RB1>RX
uns16 i;				// Variable 0...65535
InitUSART();				// Serielle Datenübertragung initialisieren
SerString("Ich bin ON ");		// Startmeldung
if (MMC_Init()) SerString("MMC ON ");	// MMC Initialisieren und Meldung falls OK!
//******************************************************************************

// Lesen  512 Byte-Mode 
if (Command(0x51,0,512,0xFF) !=0) SerString("Lese_resp_Fehler ");
while(SPI(0xFF) != 0xFE);			// Warten auf 0xFE, Anfang jeder Datenübertr.
for(i=0; i < 512; i++)
	{
	while(!TXIF);   			// Prüfen ob Register TXREG leer ist
	TXREG =SPI(0xFF);			// Datenbyte senden
	}
SPI(0xFF);				// Am Ende 2 Byte's ohne Bedeutung
SPI(0xFF);
//************************************************************************


// Schreiben  512 Byte-Mode
if (Command(0x58,0,512,0xFF) !=0) SerString("Schreib_resp_Fehler ");
SPI(0xFF);
SPI(0xFF);
SPI(0xFE);
for(i=0; i < 512; i++)
	{
	SPI('M');
	}
SPI(255);				// Am Ende 2 Byte's ohne Bedeutung senden
SPI(255);
i=SPI(0xFF);
i &=0b.0001.1111;
if (i != 0b.0000.0101) SerString("Schreib_Fehler ");
while(SPI(0xFF) !=0xFF);		// warten auf Ende des Busy -Zustandes.

//**********************************************************************
while(1);				//hier bleibt Programm stehen
}

Programmablauf

Zuerst werden die Ports eingestellt.
Dann wird die serielle Schnittstelle initialisiert mit 115200 Baud, kein Paritätsbit, 1 Stoppbit und die Meldung: "Ich bin ON " ausgegeben.
Danach wird die MMC initialisiert; wenn das erfolgreich ist, dann wird die Meldung: "MMC ON " ausgegeben.
Als nächstes werden 512 Byte eingelesen und seriell zum PC gesendet.
Danach werden 512 Byte mit dem Zeichen 'M' beschrieben.
Geschrieben und gelesen wird ab Adresse 512. Ab Adresse 512 fängt der reservierte Bereich bei Formatierung mit FAT16 an, so dass sogar die Daten und Dateisystem auf Ihrer MMC-Karte erhalten bleiben.

Bei Fehlern können folgende Meldungen auftreten:

- "Lese_resp_Fehler" - Keine Antwort auf Befehl lesen ,

- "Schreib_resp_Fehler" - Keine Antwort auf Befehl schreiben ,

- "Schreib_Fehler " - nach dem Schreibvorgang keine Bestätigung.

Mit diesem Programm lässt sich jede Adresse beschreiben und lesen. Man kann die Lese-Blockgröße beliebig einstellen.

Hier sind Signaldiagramme des Schreibvorgangs und Lesevorgangs.

Zum Download: Schaltplan +Quellkode +Hex-Datei


Softwaremäßiges SPI-Interface

Das SPI-Interface haben nicht alle PIC -Typen, deswegen habe ich ein Unterprogramm geschrieben, welches die SPI-Kommunikation mit jedem PIC-Mikrocontroller ermöglicht. Bei dem Programm kann man die Pins für den MMC-Anschluss frei auswählen.
Diese Auswahl geschieht am Anfang des Programms:

#pragma bit CS @ PORTC.2 // Ausgang für Chip Select
#pragma bit SCK @ PORTC.3 // Ausgang Clock
#pragma bit SDO @ PORTC.5 // Ausgang Daten Output
#pragma bit SDI @ PORTC.4 // Eingang Daten Input

Nicht vergessen, die Ein- / Ausgänge entsprechend einzustellen !

TRISC=0b.1101.0011;		// SCK rc3-0, SDO rc5-0, CS rc2-0        

Sie können natürlich jeden anderen Port und Pins nehmen !

Funktion char SPI(char d) ersetzen gegen:

char SPI(char out)		// SPI-Softwaremäßig Frequenz etwa 620 kHz
{
char in,i;
for (i=0;i<8;i++)
	{
	nop2();
	SCK=0;				// Abfallende Flanke des Clocks
	nop();
	SDO=out.7;			// Daten vorbereiten
	nop();
	out=out<<1;
	nop2();				// Zeit lassen für Datenvorbereitung bei Slave
	in=in<<1;
	SCK=1;				// Steigende Flanke des Clocks
	nop();
	in.0=SDI;			// Daten Einlesen
	}
return in;
}


Bei niedrigeren Taktfrequenzen arbeitet das Programm auch, nur langsamer.
MMC lässt sich anschließen an: PIC16F870, PIC16F871, PIC16F872, PIC16F873, PIC16F874, PIC16F876, PIC16F877, PIC16F84, PIC12F675, PIC12F629.

Hier ist ein Programm zum testen der softwaremäßigen SPI -Schnittstelle an MMC. Es ist für den PIC16F877 angepasst, weil ich die Hardware nicht ändern wollte.

Offene Fragen:

- Die MMC nimmt zum Schreiben nur Blöcke von 512 Byte, wo soll ich die zwischenspeichern?
- Wie kann ich FAT - Tabelle benutzen, um von einem MMC-Kartenreader gespeicherte Datei auszulesen?

Auf diese Fragen suche ich gerade Antwort. Wenn mir etwas einfällt stelle ich das im Rahmen meiner Webseite hier herein.

Zwischenspeichern?
Braucht man nicht, denn man kann jedes Byte mit beliebiger Geschwindigkeit zum MMC übertragen, sogar 1 Byte pro Stunde. Nur wird es erst nach 512 Byte gespeichert.

FAT
Erklärung des FAT-Systems, MMC mit FAT16 Formatierung auslesen, Beispielprogramm

 

  Letzte Änderung 05.01.04 Michael Dworkin