420 lines
11 KiB
C
420 lines
11 KiB
C
/*
|
|
* 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
|