summaryrefslogtreecommitdiffstats
path: root/source/ct-Bot/mcu/mmc.c
blob: 3dbf183994e003daec082273fcab00d7aeab792c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
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