diff options
Diffstat (limited to 'source/ct-Bot/mcu/mmc.c')
-rw-r--r-- | source/ct-Bot/mcu/mmc.c | 420 |
1 files changed, 420 insertions, 0 deletions
diff --git a/source/ct-Bot/mcu/mmc.c b/source/ct-Bot/mcu/mmc.c new file mode 100644 index 0000000..3dbf183 --- /dev/null +++ b/source/ct-Bot/mcu/mmc.c @@ -0,0 +1,420 @@ +/* + * 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 <avr/io.h> +#include "ena.h" +#include "timer.h" +#include "display.h" + +#include <avr/interrupt.h> +#ifndef NEW_AVR_LIB + #include <avr/signal.h> +#endif + +#include "mmc-low.h" +#include "mmc-vm.h" +#include <stdlib.h> + +#define MMC_Disable() ENA_off(ENA_MMC); +#define MMC_Enable() ENA_on(ENA_MMC); + +#define MMC_prepare() { MMC_DDR &=~(1<<SPI_DI); MMC_DDR |= (1<<SPI_DO); } + +volatile uint8 mmc_init_state=1; /*!< Initialierungsstatus der Karte, 0: ok, 1: Fehler */ + +/*! + * Checkt die Initialisierung der Karte + * @return 0, wenn initialisiert + */ +inline uint8 mmc_get_init_state(void){ + return mmc_init_state; +} + +/*! + * Schreibt ein Byte an die Karte + * @param data Das zu sendende Byte + * @author Timo Sandmann (mail@timosandmann.de) + * @date 14.11.2006 + * @see mmc-low.s + */ +void mmc_write_byte(uint8 data); + + +/*! + * Liest ein Byte von der Karte + * @return Das gelesene Byte + * @author Timo Sandmann (mail@timosandmann.de) + * @date 14.11.2006 + * @see mmc-low.s + */ + uint8 mmc_read_byte(void); + + +/*! + * Schickt ein Kommando an die Karte + * @param cmd Ein Zeiger auf das Kommando + * @return Die Antwort der Karte oder 0xFF im Fehlerfall + */ +uint8 mmc_write_command(uint8 *cmd){ + uint8 result = 0xff; + uint16 timeout = 0; + + mmc_enable(); // MMC / SD-Card aktiv schalten + + // sendet 6 Byte Kommando + uint8 i; + for (i=0; i<6; i++) // sendet 6 Byte Kommando zur MMC/SD-Karte + mmc_write_byte(*cmd++); + + // Wartet auf eine gueltige Antwort von der MMC/SD-Karte + while (result == 0xff){ + result = mmc_read_byte(); + if (timeout++ > 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<count; i++) + *buffer++ = mmc_read_byte(); + + // CRC-Byte auslesen + mmc_read_byte(); //CRC - Byte wird nicht ausgewertet + mmc_read_byte(); //CRC - Byte wird nicht ausgewertet + + // set MMC_Chip_Select to high (MMC/SD-Karte inaktiv) + MMC_Disable(); + if (timeout == 0) { + mmc_init_state = 1; + return 1; // Abbruch durch Timeout + } + return 0; // alles ok +} + +#ifdef MMC_INFO_AVAILABLE +/*! + * Liest das CID-Register (16 Byte) von der Karte + * @param Buffer Puffer von mindestens 16 Byte + */ +void mmc_read_cid (uint8 *buffer){ + // Kommando zum Lesen des CID Registers + uint8 cmd[] = {0x4A,0x00,0x00,0x00,0x00,0xFF}; + mmc_read_block(cmd, buffer, 16); +} + +/*! + * Liest das CSD-Register (16 Byte) von der Karte + * @param Buffer Puffer von mindestens 16 Byte + */ +void mmc_read_csd (uint8 *buffer){ + // Kommando zum lesen des CSD Registers + uint8 cmd[] = {0x49,0x00,0x00,0x00,0x00,0xFF}; + mmc_read_block(cmd, buffer, 16); +} + +/*! + * Liefert die Groesse der Karte zurueck + * @return Groesse der Karte in Byte. Bei einer 4 GByte-Karte kommt 0xFFFFFFFF zurueck + */ +uint32 mmc_get_size(void){ + uint8 csd[16]; + + mmc_read_csd(csd); + + uint32 size = (csd[8]>>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 |