gutn abend ich habe an meinem atmega ein nokia 6100 display mit der glcd bibliothek angeschlossen, funktioniert auch alles toll, jetzt wollte ich noch über die spi eine mmc karte mit der bibliotjekl von ulrich radig einbinden, die beiden verstehen sich aber nicht, beim initialisieren hängt der atmel irgendwie, er reagiert nicht mehr auf tasteneingaben und so aus der bibliothek von ulrich habe ich die spi initialisierung entfernt, da sie ja sonst dioppelt initialisiert wird und da wurde das display dann immer gleich schwarz... angeschlossen ist die mmc richtig, habe ich ueberprueft, cs von der mmc hängt an portb 4, cs des display an portb 0 hat jemand von euch eine idee
Als erstes solltest du davon ausgehen das immer nur die MMC oder GLCD
das SPI benutzt. Eine der beiden Libraries sollte vor und nach dem
Zugriff das SPI auf die nötigen Werte initialisieren, sprich
Geschwindigkeit, Polarität usw. Zb. könntest du alle MMC Routinen so
umschreiben das du vor deren Nutzung das SPI einstellst, und nach deren
Nutzung wieder zurückstellst wie es die GLCD benötigt.
Das ChipSelect Signal in der GLCD wird autom. durch den
Interrupthandler zurückgestzt, Für die MMC Library kannst du das selbe
machen. Öffne Datei glcd00.S und hier:
// glcd library
.section .text
// SPI Interrupt to reset CS of LCD and deactivate SPI
#if defined (SIG_SPI)
.global SIG_SPI
SIG_SPI:
#else
#error "This chip has no SPI interrupts defined"
#endif
sbi LCD_PORT, LCD_CS
cbi LCD_SPCR, SPE
-->> hier MMC-CS Signal zurücksetzen
reti
Die Art und weise wie das SPI in der GLCD benutzt wird ist sehr
speziell. Um mehr Rechenzeit zu bekommen wartet die GLCD beim Senden
über das SPI nicht auf dessen fertige Übertragung. Stattdessen benutzt
sie den SPI Interrupt um das CS Signal bei Fertigstellung
zurückzusetzen. Vor dem Senden von Daten über das SPI wartet die
Routine nun darauf das das CS Signal inaktiv wird. Somit wird
sichergestellt das das nächste Byte erst bei fertiger Übertragung
gesendet wird. Nun, auf einem 16 MHz AVR mit 8Mhz SPI werden somit 16
CPU Takte an Zeit gespart, da nun der AVR dieses 16 takte NICHT warten
muß nach dem Senden eines Bytes. In dieser Zeit kann er schon die
nächsten Datenbytes aus dem Speicher holen oder zB. Berechnungen
durchführen.
Ich selber bastle zur Zeit an einer eigenen MMC Library. Diese wird
dann exakt wie die GLCD vorgehen.
gruß Hagen
wann wird den etwa deine mmc lib fertiggestellt,dann könnte ich eventuel bis dahin warten
Die MMC Basisroutinen sind schon fertig, und ich erreiche mit einem 16 MHz ATMega162 ca. 400Kb/sec an Lesegeschwindigkeit. Allerdings habe ich diese Routinen noch nicht so angepasst das sie mitder GLCD zusammen funktioniert. Für mein aktuelles Projekt benötige ich dies auch nicht. Wann ich aber die MMC Ansteuerung komplett fertig bekomme weiß ich jetzt noch nicht. Es gibt da aber ein generelles Problem. Während beim LCD das CS Signal durchaus jederzeit deaktiviert werden darf, darf dies bei einer MMC/SD Karte aber nicht erfolgen. Das CS Signal bei MMC/SD Karten sollte solange wie man Daten senden/lesen will im kompletten aktviert bleiben. Die GLCD deaktiviert es aber nach jedem gesendeten Byte. Gruß hagen
Ich habe das jetzt nochmal genauer angeschaut.
Die GLCD steuert so an:
SPCR = (1 << MSTR) | (1 << CPOL) | (1 << CPHA) | (1 << SPIE)
SPSR = (1 << SPI2X)
wobei aber nach der Übertragung eines Bytes durch den Interrupthandler
automatisch
SPCR &= ~(1 << SPE)
gesetzt wird, also das SPI deaktiviert wird. Die Senderoutinen der GLCD
warten aber solange bis eben SPE in SPCR gelöscht wurde. D.h. sie senden
nur wenn das SPI deaktiviert ist.
Die MMC wird im allgemeinen aber so initialisiert:
SPCR = (1 << MSTR) | (1 << SPE)
SPSR = (1 << SPI2X)
D.h. also andere Polarität, sofortige und dauerhafte Aktivierung des
SPI und keinen Interrupthandler !!
Du könntest nun versuchen die GLCD leicht abzuändern:
#if defined (SIG_SPI)
.global SIG_SPI
SIG_SPI:
#else
#error "This chip has no SPI interrupts defined"
#endif
sbi LCD_PORT, LCD_CS
// cbi LCD_SPCR, SPE <- auskommentieren
reti
.global glcdDispSend
glcdDispSend: // used T1 as SPI transmission
data, T flag indicate 9'th MSB
// sbic LCD_SPCR, SPE // don't touch
D0,D1,T0,__tmp_reg__,__zero_reg__
// rjmp glcdDispSend auskommentieren, statt dessen
// auf inaktives CS warten
sbis LCD_PORT, LCD_CS
rjmp glcdDispSend
cbi LCD_SPCR, SPE
cbi LCD_PORT, LCD_CS
cbi LCD_PORT, LCD_SCL
cbi LCD_PORT, LCD_SDA
brts glcdDispSend1
sbi LCD_PORT, LCD_SDA
glcdDispSend1:
sbi LCD_PORT, LCD_SCL
sbi LCD_SPCR, SPE
out LCD_SPDR, T1
ret
.global glcdDisplayRead // used T0 stack 0 return
P0H:P0L:P1H:P1L
glcdDisplayRead:
mov T0, P0
.global glcdDispRead
glcdDispRead:
clr P1L
clr P1H
clr P0L
clr P0H
glcdDispRead1:
// sbic LCD_SPCR, SPE // wait for SPI finished
// rjmp glcdDispRead1
sbis LCD_PORT, LCD_CS
rjmp glcdDispRead1
cbi LCD_SPCR, SPE
cbi LCD_DDR, LCD_SDA
cbi LCD_PORT, LCD_CS
glcdDispRead2:
cbi LCD_PORT, LCD_SCL
add P1L, P1L
adc P1H, P1H
adc P0L, P0L
adc P0H, P0H
sbi LCD_PORT, LCD_SCL
sbic LCD_PIN, LCD_SDA
inc P1L
dec T0
brne glcdDispRead2
sbi LCD_PORT, LCD_CS
sbi LCD_DDR, LCD_SDA
ret
D.h. statt nun nach jeder Übertragung eines Bytes das SPI in der ISR zu
deaktivieren wird dort nur das CS des LCD's deaktiviert. In den beiden
obigen Sende/Lese Routinen wird nicht gewartet bis SPE = 0 ist sondern
bis CS = 1 ist. Das Problem kommt daher das das LCD eine 9 Bit serielle
Datenübertragung benötigt, d.h. das erste Bitwird manuell in Software
übertragen. Damit dies funktioniert MUSS das SPI deaktiviert sein.
In den MMC Routinen muß man also EXPLIZIT vor jedem Zugriff das SPI
auf
SPCR = (1 << MSTR) | (1 << SPE)
setzen, und bevor man auf's LCD zugreift auf
SPCR = (1 << MSTR) | (1 << CPOL) | (1 << CPHA) | (1 << SPIE)
setzen. Den letzen Schritt würde ich bei der Deaktivierung der MMC
Übertragung durchführen.
Gruß Hagen
mit den ändrungn gehts nicht habe den mmc kram rausgelassen und nur die änderungen an der glcd lib gemacht, das display bleibt jetzt von anfang an komplett schwarz
Hm, das ist der Unterschied zwischen Theorie und Praxis ;) Mit der originalen GLCD ist es nur wichtig das die MMC Routinen vor dem Zugriff SPCR = (1 << MSTR) | (1 << SPE) setzen, und nach dem Zugriff SPCR = (1 << MSTR) | (1 << CPOL) | (1 << CPHA) | (1 << SPIE) setzen. Dann dürfte es funktionieren. Leider ist es ohne große Änderungen sehr schwierig die GLCD zu ändern. Dies liegt eben an dem 9Bit SPI das per Software 1Bit und Hardware 8Bit überträgt. Alternativ wäre es möglich die GLCD so umzubasteln das sie ohne SPI-Interrupt auskommt. Dazu muß glcdDisplayInit(), glcdDisplaySend() und glcdDisplayRead() modifiziert werden. Allerdings würde die Performance darunter leiden, da nun bei jedem Datenbyte (9Bit) auf die fertige Übertragung des SPI's gewartet werden muß. Sofort danach würde man das SPI auf die Einstellungen der MMC Karte zurücksetzen. Ich persönlich würde aber die MMC Routinen anpassen, da diese in diesem Falle nicht so Geschwindigkeitsoptinmiert sein müssen. Soll heisen, pro Datenbyte in der MMC Übertragung ist der nötige Overhead vor und nach der Ansteuerung der MMC mit dem Einstellen des SPI's relativ unkritisch. Im Falle der GCLD mit MMC würde ich zwei Makros/Funktionen bauen: #define MMC_SPI_ON() while (SPCR & (1 << SPE)) do; SPCR = (1 << MSTR) | (1 << SPE); #define MMC_SPI_OFF() SPCR = (1 << MSTR) | (1 << CPOL) | (1 << CPHA) | (1 << SPIE); D.h. im normalen Modus ist das SPI für die GLCD konfiguriert, und nur bei den Zugriffen auf die MMC wird das SPI umgestellt. Somit kann man nur eines der beiden Geräte zu einem Zeitpunkt ansteuern. Gruß hagen
okay, danke für deine ausführliche hilfe, werde ich gleich mal testen
das funktioniert irgendwie nicht, das Display zeigt etwas an, danach versuche ich die mmc zu initialisieren und dann wird das Display schwarz, bevor ich das Display wieder anspreche habe ich SPCR = (1 << MSTR) | (1 << CPOL) | (1 << CPHA) | (1 << SPIE) gesetzt
Wie schnell nacheinander greifst du aufs Display und danach auf die MMC
zu ?
Die GLCD geht in etwa so vor:
- warte das SPI fertig ist
- sende 9Bit
Die meisten MMC Routinen gehen so vor:
- sende 8 Bit
- warte bis fertig
Diese geänderte Vorgehensweise hat mehrere Gründe und auch Vorteile.
Bei 8Mhz SPI -> 16Mhz MCU Takt, werden nach out SPDR, Data, sprich dem
senden eines Bytes über SPI, per hardware in den nächsten 16 MCU
Taktzyklen das Byte übertragen. Würde man jetzt auf die Fertigstellung
der Übertragung warten so verschenkt man 16 Taktzyklen an Rechenpower.
Innerhalb dieser 16 Taktzyklen kann aber zB. in der GLCD Funktion
glcdLine() schon komplett der nächste zu setzende Pixel berechnet
werden. Ergo die Gesamtperformance steigt. Die meisten Funktionen in
der GLCD haben so "kurze" wiederholte Berechnungsfunktionen, zB.
Bitmaps, Fontroutinen, Ellipsen etc. pp. und das Display wird bei 8Mhz
SPI auch fast mit vollen 8Mhz angesteuert. Eben aus dem Grunde weil die
Software 16 MCU Takte Zeit hat bis zumnächsten Datenbyte.
Fazit: nachdem die GLCD komplett fertig übertragen hat muß
1.) LCD_CS auf 1 sein
2.) SPE in SPCR auf 0 sein.
Du solltest also in MMC_SPI_ON() warten, etwa so:
void MMC_SPI_ON(void) {
while ((SPCR & (1 << SPE)) != 0) do ;
SPCR = (1 << MSTR) || (1 << SPE);
}
void MMC_SPI_OFF(void) {
SPCR = (1 << MSTR) || (1 << CPOL) || (1 << CPHA) || (1 << SPIE);
}
Gruß Hagen
oder sogar so
void MMC_SPI_ON(void) {
if ((SPCR & (1 << SPIE)) != 0) {
while ((SPCR & (1 << SPE)) != 0) do ;
}
SPCR = (1 << MSTR) || (1 << SPE);
}
Gruß hagen
ich greife eigentlcih staendig im Wechsel auf beide zu. der MCU Takt ist 8 Mhz
ich greife eigentlcih staendig im Wechsel auf beide zu. der MCU Takt ist 8 Mhz. bei dem letzen meckert der compiler...
ich habe nochmal getestet, wenn ich die befehle so ausfuehre geht es, allerdings habe ich nicht versucht auf die mmc zuzugreifen, das werde ich gleich nochmal testen, dabei ist er immer abgestuerzt
also die initialisierung funktioniert schonmal, scheint also vorran zugehen. vielen dank, dadran hätte ich alleine bestimmt mehrere wochen gesucht :-)
Hallo, die MMC Lib. unterstützt doch eh auch Software SPI ... das LCD also ans Hardware SPI und die MMC an "normale" I/O Pins mit Software SPI ! mfG aleX
Naja,man könnte auch die GLCD per Soft-SPI ansteuern, oder sogar beides. Nur, wenn man schon Hardware-SPI zur Verfügung hat,und damit leben kann das immer nur ein Gerät zu einem Zeitpunkt angesteuert werden kann, dann sollte man auch beides mit Hardware ansteuern. Ich vermute mal das Michael mit obigen Vorschlägen schon weiterkommen wird. Gruß Hagen
Software SPI hört sich finde ich langsam an, zusätzlich kommt noch ein mp3 decoder mit dran und das alles per software spi, der arme atmega
die obigen vorschläge funktionieren bis jetzt soweit, das die mmc karte initialisiert wird, lesen und schreiben geht noch nicht, aber das schaffe ich mit den tipps von oben sicherlich bald
@ Michael: Mal ne ganz andre Frage: Welche MMC/SD Karte verwendest du? Ich hab die Lib. von ulrich radig mal ausprobiert mit ner SanDisk 128MB SD ... und ich konnte die Karte einfach nicht initialisieren. mfg aleX
ich verwende eine 256MB Karte von Reichelt. Die billigste die es dort gab.
Es gibt da einige Punkte mit den MMC/SD Karten die beachtet werden
müssen. Hier mal ein Ausszug aus meiner Ansteuerung, da kann man sehen
das ich einiges anders mache. Eine SanDisk 256MB Karte funktioniert
damit, AUCH direkt nach einem Powerup. SanDisk Karten können bis zu
500ms Timeouts erzeugen, deshalb sollte man die Commandos bei Mißerfolg
immer mehrmals senden.
Gruß Hagen
uint8_t mmcInit(void) {
MMC_PORT |= (1 << MMC_CS);
MMC_DDR |= (1 << MMC_CS) | (1 << MMC_SCLK) | (1 << MMC_MOSI);
MMC_DDR &= ~(1 << MMC_MISO);
SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0);
SPSR = 0;
uint8_t result = mmcCommandExpected(0x40, 0x0101);
if (result <= 0x01) {
for (uint8_t i = 20; i > 0; i--) {
result = mmcCommand(0x41);
if (result != 0x01) break;
}
if (result == 0x00) {
SPCR = (1 << SPE) | (1 << MSTR);
SPSR = (1 << SPI2X);
#ifdef MMC_CALC_CRC7
result = mmcCommandParam(0x7B, 1, 0x0000); // activate CRC
checking
#endif
if (result == 0) {
result = mmcReadCSD(MMC_Buffer);
}
}
}
mmcDisable();
return(result);
}
/*
Follow the equivalent C code of assembler procedures in file
mmc_spi_asm.S
The assembler is more efficient as the compiled C source on WinAVR
GCC.
#define mmcEnable() MMC_PORT &= ~(1 << MMC_CS)
#define mmcDisable() MMC_PORT |= (1 << MMC_CS)
#define mmcRead() SPDR
#define mmcSend(data) SPDR = data
#define mmcWait() while (!(SPSR & (1 << SPIF)))
#ifdef MMC_CALC_CRC7
uint8_t mmcCRC7(uint8_t crc, uint8_t data) {
crc ^= data;
unsigned char poly = 0x89;
for (unsigned char i = 8; i > 0; i--) {
if ((crc & 0x80) != 0) crc ^= poly;
crc <<= 1;
}
return(crc);
}
#endif
uint8_t mmcCommand(uint8_t command) {
return(mmcCommandParam(command, 0, 0));
}
uint8_t mmcCommandExpected(uint8_t command, uint16_t expected) {
return(mmcCommandParam(command, 0, expected));
}
uint8_t mmcCommandParam(uint8_t command, uint32_t address, uint16_t
expected) {
mmcDisable();
mmcSend(data);
uint8_t crc = 0;
#ifdef MMC_CALC_CRC7
crc = mmcCRC7(crc, command);
crc = mmcCRC7(crc, (uint8_t)(address >> 24));
crc = mmcCRC7(crc, (uint8_t)(address >> 16));
crc = mmcCRC7(crc, (uint8_t)(address >> 8));
crc = mmcCRC7(crc, (uint8_t)(address));
crc = crc | 1;
#else
crc = 0x95;
#endif
uint8_t result;
uint8_t data = 0xFF;
mmcWait();
mmcEnable();
for (uint8_t i = MMC_COMMAND_TRIALS; i > 0; i--) {
mmcSend(command);
mmcWait();
mmcSend((uint8_t)(address >> 24));
mmcWait();
mmcSend((uint8_t)(address >> 16));
mmcWait();
mmcSend((uint8_t)(address >> 8));
mmcWait();
mmcSend((uint8_t)(address));
mmcWait();
mmcSend(crc);
mmcWait();
for (uint8_t j = MMC_RESPONSE_TRIALS; j > 0; j--) {
mmcSend(data);
mmcWait();
result = mmcRead();
if ((result & 0x80) == 0) break;
}
if ((result >= (uint8_t)(expected >> 8)) && (result <=
(uint8_t)(expected))) break;
mmcDisable();
mmcSend(data);
for (uint8_t j = MMC_COMMAND_DELAY -1; j > 0; j--) {
mmcWait();
mmcSend(data);
}
mmcWait();
mmcEnable();
}
return(result);
}
uint8_t mmcReadData(uint16_t size, uint8_t *buffer) {
// the parameter order of size and buffer is choosen this way for
better assembler optimization
uint8_t result;
uint8_t data = 0xFF;
mmcSend(data);
for (uint16_t i = MMC_READ_TRIALS -1; i > 0; i--) {
mmcWait();
result = mmcRead();
mmcSend(data);
if (result == 0xFE) goto DoRead;
}
return(0xFF);
DoRead:;
for (; size > 0; size--) {
mmcWait();
result = mmcRead();
mmcSend(data);
*buffer++ = result;
}
mmcWait(); // skip 16Bit CRC
mmcRead();
mmcSend(data);
mmcWait();
mmcRead();
return(0);
}
uint8_t mmcReadSector(uint32_t sector, uint8_t *buffer) {
uint8_t result = mmcCommandParam(0x51, sector * 512, 0x0000);
if (result == 0x00) result = mmcReadData(buffer, 512);
mmcDisable();
return(result);
}
uint8_t mmcReadBuffer16(uint8_t command, uint8_t *buffer) {
uint8_t result = mmcCommand(command);
if (result == 0x00) result = mmcReadData(16, buffer);
mmcDisable();
return(result);
}
@Hagen: Ich weiß, langsam kommen wir echt vom eigentlichen Thema ab. Aber könntest du mir eventuell deinen kompletten MMC/SD Source schicken? Kann ihn zwar derzeit leider nicht testen (beim Transport ein Teil der Printplatte komplett zerstört ;( ) aber sieht echt gut aus ... vorallem weil es auch mit CRC Check funktioniert. Wäre nett, wenn dus mir (mit nem kleinem Beispiel? g) an meine eMail Adresse schicken könntest. mfG aleX
Ok, ich habe das mal angehangen. ATMega162 16MHz mit externem 32Kb SRAM, 3.3V, direkt MMC/SD Slot angeschlossen, 3 LEDs und UART zum Debuggen. Ich veröffentliche das nur als Studie und möchte eigentlich KEINEN Support oder so was dazu geben. Es funktioniert alles nur wäre es schön wenn ihr euch erst mal selber durchbeissen würdet. Ihr kennt das Problem mit der ZEIT ja selber. Gruß Hagen PS: Kommentare sind vorhanden, aber auch nur das nötigste.
Vielen Dank ... ... ich verwend den Code sowieso nur als "Vorlage" ... is für mein Abschlussprojekt - da muss ich eh alles selber machen ;p Danke nochmal, aleX
Gibt es inzwischn irgendwo eine Implementierung wo eine MMC und ein Nokiadisplay am selben Controller hängt?
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.