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.