/* * c't-Bot * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your * option) any later version. * This program is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307, USA. * */ /*! @file mmc.c * @brief Routinen zum Auslesen/Schreiben einer MMC-Karte * @author Benjamin Benz (bbe@heise.de) * @author Ulrich Radig (mail@ulrichradig.de) www.ulrichradig.de * @date 07.11.06 */ /* Die (zeitkritischen) Low-Level-Funktionen read_ und write_byte bzw. _sector liegen jetzt * in mmc-low.S. Der Assembler-Code ist a) wesentlich schneller und macht b) das Timing * unabhaengig vom verwendeten Compiler. * Die Portkonfiguration findet sich in mmc-low.h. */ //TODO: * kleine Doku machen #include "ct-Bot.h" #ifdef MCU #ifdef MMC_AVAILABLE #include "mmc.h" #include #include "ena.h" #include "timer.h" #include "display.h" #include #ifndef NEW_AVR_LIB #include #endif #include "mmc-low.h" #include "mmc-vm.h" #include #define MMC_Disable() ENA_off(ENA_MMC); #define MMC_Enable() ENA_on(ENA_MMC); #define MMC_prepare() { MMC_DDR &=~(1< MMC_TIMEOUT) break; // Abbruch da die MMC/SD-Karte nicht antwortet } return result; } /*! * Initialisiere die SD/MMC-Karte * @return 0 wenn allles ok, sonst Nummer des Kommandos bei dem abgebrochen wurde */ uint8 mmc_init(void){ uint16 timeout = 0; uint8 i; mmc_init_state = 0; // Konfiguration des Ports an der die MMC/SD-Karte angeschlossen wurde MMC_CLK_DDR |= _BV(SPI_CLK); // Setzen von Pin MMC_Clock auf Output MMC_prepare(); MMC_Enable(); MMC_Disable(); // Initialisiere MMC/SD-Karte in den SPI-Mode for (i=0; i<0x0f; i++) // Sendet min 74+ Clocks an die MMC/SD-Karte mmc_write_byte(0xff); // Sendet Kommando CMD0 an MMC/SD-Karte uint8 cmd[] = {0x40,0x00,0x00,0x00,0x00,0x95}; while (mmc_write_command(cmd) != 1){ if (timeout++ > MMC_TIMEOUT){ MMC_Disable(); mmc_init_state = 1; return 1; // Abbruch bei Kommando 1 (Return Code 1) } } // Sendet Kommando CMD1 an MMC/SD-Karte timeout = 0; cmd[0] = 0x41; // Kommando 1 cmd[5] = 0xFF; while (mmc_write_command (cmd) !=0){ if (timeout++ > 3*MMC_TIMEOUT){ MMC_Disable(); mmc_init_state = 1; return 2; // Abbruch bei Kommando 2 (Return Code 2) } } // set MMC_Chip_Select to high (MMC/SD-Karte Inaktiv) MMC_Disable(); return 0; } /*! * Liest einen Block von der Karte * @param cmd Zeiger auf das Kommando, das erstmal an die Karte geht * @param Buffer Ein Puffer mit mindestens count Bytes * @param count Anzahl der zu lesenden Bytes */ uint8 mmc_read_block(uint8 *cmd, uint8 *buffer, uint16 count){ /* Initialisierung checken */ if (mmc_init_state != 0) if (mmc_init() != 0) return 1; // Sendet Kommando cmd an MMC/SD-Karte if (mmc_write_command(cmd) != 0) { mmc_init_state = 1; return 1; } // Wartet auf Start Byte von der MMC/SD-Karte (FEh/Start Byte) uint8 timeout=1; while (mmc_read_byte() != 0xfe){ if (timeout++ == 0) break; }; uint16 i; // Lesen des Blocks (max 512 Bytes) von MMC/SD-Karte for (i=0; i>6) + (csd[7] << 2) + ((csd[6] & 0x03) << 10); // c_size size +=1; // Fest in der Formel drin uint8 shift = 2; // eine 2 ist fest in der Formel drin shift += (csd[10]>>7) + ((csd[9] & 0x03) <<1); // c_size_mult beruecksichtigen shift += csd[5] & 0x0f; // Blockgroesse beruecksichtigen size = size << shift; return size; } #endif //MMC_INFO_AVAILABLE #ifdef MMC_WRITE_TEST_AVAILABLE /*! Testet die MMC-Karte. Schreibt nacheinander 2 Sektoren a 512 Byte mit Testdaten voll und liest sie wieder aus * !!! Achtung loescht die Karte * @return 0, wenn alles ok */ uint8 mmc_test(void){ static uint32 sector = 0xf000; /* Initialisierung checken */ if (mmc_init_state != 0) if (mmc_init() != 0){ sector = 0; return 1; } #ifdef MMC_VM_AVAILABLE // Version mit virtuellen Aressen uint16 i; static uint16 pagefaults = 0; static uint16 old_pf; /* virtuelle Adressen holen */ static uint32 v_addr1 = 0; static uint32 v_addr2 = 0; static uint32 v_addr3 = 0; static uint32 v_addr4 = 0; if (v_addr1 == 0) v_addr1 = mmcalloc(512, 1); // Testdaten 1 if (v_addr2 == 0) v_addr2 = mmcalloc(512, 1); // Testdaten 2 if (v_addr3 == 0) v_addr3 = mmcalloc(512, 1); // Dummy 1 if (v_addr4 == 0) v_addr4 = mmcalloc(512, 1); // Dummy 2 /* Zeitmessung starten */ uint16 start_ticks=TIMER_GET_TICKCOUNT_16; uint8 start_reg=TCNT2; /* Pointer auf Puffer holen */ uint8* p_addr = mmc_get_data(v_addr1); // Cache-Hit, CB 0 if (p_addr == NULL) return 2; /* Testdaten schreiben */ for (i=0; i<512; i++) p_addr[i] = (i & 0xff); /* Pointer auf zweiten Speicherbereich holen */ p_addr = mmc_get_data(v_addr2); // Cache-Hit, CB 1 if (p_addr == NULL) return 3; /* Testdaten Teil 2 schreiben */ for (i=0; i<512; i++) p_addr[i] = 255 - (i & 0xff); /* kleiner LRU-Test */ // p_addr = mmc_get_data(v_addr1); // Cache-Hit, CB 0 // p_addr = mmc_get_data(v_addr4); // Cache-Miss, => CB 1 // p_addr = mmc_get_data(v_addr1); // Cache-Hit, CB 0 // p_addr = mmc_get_data(v_addr3); // Cache-Miss, => CB 1 // p_addr = mmc_get_data(v_addr1); // Cache-Hit, CB 0 // p_addr = mmc_get_data(v_addr4); // Cache-Miss, => CB 1 /* Pointer auf Testdaten Teil 1 holen */ p_addr = mmc_get_data(v_addr1); // Cache-Hit, CB 0 if (p_addr == NULL) return 4; /* Testdaten 1 vergleichen */ for (i=0; i<512; i++) if (p_addr[i] != (i & 0xff)) return 5; /* Pointer auf Testdaten Teil 2 holen */ p_addr = mmc_get_data(v_addr2); // Cache-Miss, => CB 1 if (p_addr == NULL) return 6; /* Testdaten 2 vergleichen */ for (i=0; i<512; i++) if (p_addr[i] != (255 - (i & 0xff))) return 7; p_addr = mmc_get_data(v_addr4); /* Zeitmessung beenden */ int8 timer_reg=TCNT2; uint16 end_ticks=TIMER_GET_TICKCOUNT_16; timer_reg -= start_reg; #ifdef VM_STATS_AVAILABLE /* Pagefaults merken */ old_pf = pagefaults; pagefaults = mmc_get_pagefaults(); #endif /* kleine Statistik ausgeben */ display_cursor(3,1); display_printf("Pagefaults: %5u ", pagefaults); display_cursor(4,1); display_printf("Bei %3u PF: %5u us", pagefaults - old_pf, (end_ticks-start_ticks)*176 + timer_reg*4); #else // alte Version uint8 buffer[512]; uint16 i; uint8 result=0; /* Zeitmessung starten */ uint16 start_ticks=TIMER_GET_TICKCOUNT_16; uint8 start_reg=TCNT2; #if MMC_ASYNC_WRITE == 1 /* async-Test (wurde im letzten Durchlauf korrekt geschrieben?) */ if (sector > 0xf){ result= mmc_read_sector(sector-1, buffer); if (result != 0){ return result*10 + 9; } for (i=0; i<512; i++) if (buffer[i] != (i & 0xFF)){ return 10; } } #endif // MMC_ASYNC_WRITE // Puffer vorbereiten for (i=0; i< 512; i++) buffer[i]= (i & 0xFF); // und schreiben result= mmc_write_sector(sector, buffer, 0); if (result != 0){ return result*10 + 2; } // Puffer vorbereiten for (i=0; i< 512; i++) buffer[i]= 255 - (i & 0xFF); // und schreiben result= mmc_write_sector(sector+1, buffer, 0); if (result != 0){ return result*10 + 3; } // Puffer lesen result= mmc_read_sector(sector++, buffer); if (result != 0){ sector--; return result*10 + 4; } // und vergleichen for (i=0; i<512; i++) if (buffer[i] != (i & 0xFF)){ return 5; } // sector++; // Puffer lesen result= mmc_read_sector(sector++, buffer); if (result != 0){ sector--; return result*10 + 6; } // und vergleichen for (i=0; i<512; i++) if (buffer[i] != (255- (i & 0xFF))){ return 7; } #if MMC_ASYNC_WRITE == 1 for (i=0; i< 512; i++) buffer[i]= (i & 0xFF); result= mmc_write_sector(sector-1, buffer, MMC_ASYNC_WRITE); if (result != 0){ return result*10 + 8; } #endif // MMC_ASYNC_WRITE /* Zeitmessung beenden */ int8 timer_reg=TCNT2; uint16 end_ticks=TIMER_GET_TICKCOUNT_16; timer_reg -= start_reg; /* kleine Statistik ausgeben */ display_cursor(3,1); display_printf("Dauer: %5u us ", (end_ticks-start_ticks)*176 + timer_reg*4); display_cursor(4,1); display_printf("Sektor:%6u/", sector-2); display_printf("%6u", sector-1); #endif // MMC_VM_AVAILABLE // hierher kommen wir nur, wenn alles ok ist return 0; } #endif //MMC_WRITE_TEST_AVAILABLE #endif #endif