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 |
Erklärung
des FAT-Systems
MMC mit FAT16 Formatierung auslesen, Beispielprogramm |
![]() |
+
|
![]() |
|
MMC (Multimedia Card) hier 64MB. |
Der PIC16F877 verfügt über : |
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||
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 |
| 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. |
| 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 |