PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : eeprom_dump(4 pages) und Zeiten für eeprom_write/read_byte()



HermannSW
12.04.2007, 09:12
Hi,

ich habe gestern die neue Asurolib v270rc3 installiert.

An dem Programm zum Dumpen (ausgeben) des EEPROM-Inhaltes des Asuro in ein Terminalprogramm stellte ich dann fest, daß ein einfaches NeuÜbersetzen die Anzahl der Seiten (pages), welche auf den Asuro geflashed werden, von 27 auf 17 reduzierte. Nun erwachte mein sportlicher Ehrgeiz und ich wollte es so klein wie möglich kriegen, damit EEPROM_dump.hex nicht nur ein nützliches, sondern auch ein schnelles Tool ist. Das waren die Schritte für die Größe von EEPROM_dump.hex: mit Asurolib v270rc2: 27 pages
mit Asurolib v270rc3: 17 pages
nur UartPutc (ohne SerWrite): 14 pages
Weglassen der asuro.c im Makefile (!): 5 pages
weiteres tuning: 4 pagesWeiter ging es nicht mehr, aber 4 pages sind auch schnell geflashed.

So sieht ein Output von EEPROM_dump.hex im Terminalprogramm aus, einfach 32 Zeilen â 16 bytes hexadezimal (das EEPROM hat eine Größe von 512 bytes):
000102030405060708090A0B0C0D0E0F
101112131415161718191A1B1C1D1E1F
202122232425262728292A2B2C2D2E2F
303132333435363738393A3B3C3D3E3F
404142434445464748494A4B4C4D4E4F
505152535455565758595A5B5C5D5E5F
606162636465666768696A6B6C6D6E6F
707172737475767778797A7B7C7D7E7F
808182838485868788898A8B8C8D8E8F
909192939495969798999A9B9C9D9E9F
A0A1A2A3A4A5A6A7A8A9AAABACADAEAF
B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF
C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF
D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF
E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF
F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF
000102030405060708090A0B0C0D0E0F
101112131415161718191A1B1C1D1E1F
202122232425262728292A2B2C2D2E2F
303132333435363738393A3B3C3D3E3F
404142434445464748494A4B4C4D4E4F
505152535455565758595A5B5C5D5E5F
606162636465666768696A6B6C6D6E6F
707172737475767778797A7B7C7D7E7F
808182838485868788898A8B8C8D8E8F
909192939495969798999A9B9C9D9E9F
A0A1A2A3A4A5A6A7A8A9AAABACADAEAF
B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF
C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF
D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF
E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF
F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF

Als Anhang gibt es EEPROM_dump.hex.txt zum direkten Flashen (.hex ist leider keine erlaubte Dateiendung für Attachments, einfach in .hex umbenennen), hier ist der Quelltext (nicht vergessen, asuro.c im makefile auszukommentieren #SRC += asuro.c):
#include <avr/eeprom.h>

void UartPutc (unsigned char zeichen)
{
UCSRB = 0x08; // enable transmitter
UCSRA |= 0x40; // clear transmitter flag
while (!(UCSRA & 0x20)) // wait for empty transmit buffer
;
UDR = zeichen;
while (!(UCSRA & 0x40)) // Wait for transmit complete flag (TXC)
;
}

unsigned char map(unsigned char x) { return ( (x<10) ? '0'+x : 'A'+(x-10) ); }

int main(void)
{
unsigned int u;

for(u=0; u<0x0200; ++u)
{
unsigned char ch = eeprom_read_byte((uint8_t*) u);

UartPutc(map(ch >> 4));
UartPutc(map(ch & 0x0F));

if ((u & 0x0F) == 0x0F)
{
UartPutc('\r');
UartPutc('\n');
}
}

while (1);

return 0;
}Da die Asurolib nicht benutzt wird, mußte natürlich UartPutc(), die einzige aus der Asurolib benötigte Routine, in den Quelltext hineinkopiert werden. Hier nun die Ausgabe von avr-objdump aus dem Make-Prozeß (C-Quelltext und Assembler gemischt):
test.elf: file format elf32-avr

Sections:
Idx Name Size VMA LMA File off Algn
0 .text 000000ce 00000000 00000000 00000094 2**0
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .data 00000000 00800060 000000ce 00000162 2**0
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 00800060 000000ce 00000162 2**0
ALLOC
3 .noinit 00000000 00800060 00800060 00000162 2**0
CONTENTS
4 .eeprom 00000000 00810000 00810000 00000162 2**0
CONTENTS
5 .stab 000003e4 00000000 00000000 00000164 2**2
CONTENTS, READONLY, DEBUGGING
6 .stabstr 000005d7 00000000 00000000 00000548 2**0
CONTENTS, READONLY, DEBUGGING
Disassembly of section .text:

00000000 <__vectors>:
0: 12 c0 rjmp .+36 ; 0x26
2: 2b c0 rjmp .+86 ; 0x5a
4: 2a c0 rjmp .+84 ; 0x5a
6: 29 c0 rjmp .+82 ; 0x5a
8: 28 c0 rjmp .+80 ; 0x5a
a: 27 c0 rjmp .+78 ; 0x5a
c: 26 c0 rjmp .+76 ; 0x5a
e: 25 c0 rjmp .+74 ; 0x5a
10: 24 c0 rjmp .+72 ; 0x5a
12: 23 c0 rjmp .+70 ; 0x5a
14: 22 c0 rjmp .+68 ; 0x5a
16: 21 c0 rjmp .+66 ; 0x5a
18: 20 c0 rjmp .+64 ; 0x5a
1a: 1f c0 rjmp .+62 ; 0x5a
1c: 1e c0 rjmp .+60 ; 0x5a
1e: 1d c0 rjmp .+58 ; 0x5a
20: 1c c0 rjmp .+56 ; 0x5a
22: 1b c0 rjmp .+54 ; 0x5a
24: 1a c0 rjmp .+52 ; 0x5a

00000026 <__ctors_end>:
26: 11 24 eor r1, r1
28: 1f be out 0x3f, r1 ; 63
2a: cf e5 ldi r28, 0x5F ; 95
2c: d4 e0 ldi r29, 0x04 ; 4
2e: de bf out 0x3e, r29 ; 62
30: cd bf out 0x3d, r28 ; 61

00000032 <__do_copy_data>:
32: 10 e0 ldi r17, 0x00 ; 0
34: a0 e6 ldi r26, 0x60 ; 96
36: b0 e0 ldi r27, 0x00 ; 0
38: ee ec ldi r30, 0xCE ; 206
3a: f0 e0 ldi r31, 0x00 ; 0
3c: 02 c0 rjmp .+4 ; 0x42

0000003e <.do_copy_data_loop>:
3e: 05 90 lpm r0, Z+
40: 0d 92 st X+, r0

00000042 <.do_copy_data_start>:
42: a0 36 cpi r26, 0x60 ; 96
44: b1 07 cpc r27, r17
46: d9 f7 brne .-10 ; 0x3e

00000048 <__do_clear_bss>:
48: 10 e0 ldi r17, 0x00 ; 0
4a: a0 e6 ldi r26, 0x60 ; 96
4c: b0 e0 ldi r27, 0x00 ; 0
4e: 01 c0 rjmp .+2 ; 0x52

00000050 <.do_clear_bss_loop>:
50: 1d 92 st X+, r1

00000052 <.do_clear_bss_start>:
52: a0 36 cpi r26, 0x60 ; 96
54: b1 07 cpc r27, r17
56: e1 f7 brne .-8 ; 0x50
58: 12 c0 rjmp .+36 ; 0x7e

0000005a <__bad_interrupt>:
5a: d2 cf rjmp .-92 ; 0x0

0000005c <UartPutc>:
#include <avr/eeprom.h>

void UartPutc (unsigned char zeichen)
{
5c: 98 2f mov r25, r24
UCSRB = 0x08; // enable transmitter
5e: 88 e0 ldi r24, 0x08 ; 8
60: 8a b9 out 0x0a, r24 ; 10
UCSRA |= 0x40; // clear transmitter flag
62: 5e 9a sbi 0x0b, 6 ; 11
while (!(UCSRA & 0x20)) // wait for empty transmit buffer
64: 5d 9b sbis 0x0b, 5 ; 11
66: fe cf rjmp .-4 ; 0x64
;
UDR = zeichen;
68: 9c b9 out 0x0c, r25 ; 12
while (!(UCSRA & 0x40)) // Wait for transmit complete flag (TXC)
6a: 5e 9b sbis 0x0b, 6 ; 11
6c: fe cf rjmp .-4 ; 0x6a
;
}
6e: 08 95 ret

00000070 <map>:

unsigned char map(unsigned char x) { return ( (x<10) ? '0'+x : 'A'+(x-10) ); }
70: 8a 30 cpi r24, 0x0A ; 10
72: 10 f4 brcc .+4 ; 0x78
74: 80 5d subi r24, 0xD0 ; 208
76: 01 c0 rjmp .+2 ; 0x7a
78: 89 5c subi r24, 0xC9 ; 201
7a: 99 27 eor r25, r25
7c: 08 95 ret

0000007e <main>:

int main(void)
{
7e: cf e5 ldi r28, 0x5F ; 95
80: d4 e0 ldi r29, 0x04 ; 4
82: de bf out 0x3e, r29 ; 62
84: cd bf out 0x3d, r28 ; 61
unsigned int u;

for(u=0; u<0x0200; ++u)
86: c0 e0 ldi r28, 0x00 ; 0
88: d0 e0 ldi r29, 0x00 ; 0
{
unsigned char ch = eeprom_read_byte((uint8_t*) u);
8a: ce 01 movw r24, r28
8c: 18 d0 rcall .+48 ; 0xbe
8e: 18 2f mov r17, r24

UartPutc(map(ch >> 4));
90: 82 95 swap r24
92: 8f 70 andi r24, 0x0F ; 15
94: ed df rcall .-38 ; 0x70
96: e2 df rcall .-60 ; 0x5c
UartPutc(map(ch & 0x0F));
98: 1f 70 andi r17, 0x0F ; 15
9a: 81 2f mov r24, r17
9c: e9 df rcall .-46 ; 0x70
9e: de df rcall .-68 ; 0x5c

if ((u & 0x0F) == 0x0F)
a0: ce 01 movw r24, r28
a2: 8f 70 andi r24, 0x0F ; 15
a4: 90 70 andi r25, 0x00 ; 0
a6: 0f 97 sbiw r24, 0x0f ; 15
a8: 21 f4 brne .+8 ; 0xb2
{
UartPutc('\r');
aa: 8d e0 ldi r24, 0x0D ; 13
ac: d7 df rcall .-82 ; 0x5c
UartPutc('\n');
ae: 8a e0 ldi r24, 0x0A ; 10
b0: d5 df rcall .-86 ; 0x5c
b2: 21 96 adiw r28, 0x01 ; 1
b4: 82 e0 ldi r24, 0x02 ; 2
b6: c0 30 cpi r28, 0x00 ; 0
b8: d8 07 cpc r29, r24
ba: 38 f3 brcs .-50 ; 0x8a
}
}

while (1);
bc: ff cf rjmp .-2 ; 0xbc

000000be <eeprom_read_byte>:

return 0;
}
be: e1 99 sbic 0x1c, 1 ; 28
c0: fe cf rjmp .-4 ; 0xbe
c2: 9f bb out 0x1f, r25 ; 31
c4: 8e bb out 0x1e, r24 ; 30
c6: e0 9a sbi 0x1c, 0 ; 28
c8: 99 27 eor r25, r25
ca: 8d b3 in r24, 0x1d ; 29
cc: 08 95 retWie man schön sehen kann, ist Alles recht kompakt, bis 0x5c gibt es C-Compiler-Kram, danach UartPutc(), und der Rest des Programms bis 0xbe, und angehängt eeprom_read_byte() aus der AVR-Library.


Hier noch eine Frage an die Entwickler der Library:
Warum klappt UartPutc() ohne Aufruf von Init() aus der Asurolib?
Wird der Asuro etwa schon vom BootLoader auf 2400bps eingestellt?
Der Code aus Init() wird ja gerade nicht ausgeführt:
...
void Init (
void)
{
/*
Timer2, zum Betrieb mit der seriellen Schnittstelle, fuer die
IR-Kommunikation auf 36 kHz eingestellt.
*/
TCCR2 = (1 << WGM20) | (1 << WGM21) | (1 << COM20) | (1 << COM21) | (1 << CS20);
OCR2 = 0x91; // duty cycle fuer 36kHz
TIMSK |= (1 << TOIE2); // 36kHz counter

/*
Die serielle Schnittstelle wurde waerend der Boot-Phase schon
programmiert und gestartet. Hier werden die Parameter auf 2400 1N8 gesetzt.
*/
UCSRA = 0x00;
UCSRB = 0x00;
UCSRC = 0x86; // 1 Stop Bit | No Parity | 8 Data Bit
UBRRL = 0xCF; // 2400bps @ 8.00MHz
...


Last, but not least, hier noch ein Programm, welches den EEPROM-Inhalt auf ein definiertes Muster setzt:
#include <asuro.h>

#include <avr/eeprom.h>

int main (void)
{
unsigned int u;
long t0,t1,t2,tmax;
unsigned char ch;

Init();

tmax = 0;
t0 = t1 = Gettime();

for(u=0x0000; u<0x0200; ++u)
{
eeprom_write_byte((uint8_t*)u,(unsigned char)(u&0xff));

t2 = Gettime();

if (t2-t1>tmax)
tmax=t2-t1;

t1=t2;
}

t2 = Gettime();

SerPrint("\r\naverage time for writing 1 byte to eeprom: ");
PrintLong((1000*(t1-t0))/512);
SerPrint("us\r\n");

SerPrint("maximal time for writing 1 byte to eeprom: ");
PrintLong(tmax);
SerPrint("ms\r\n");

ch = 0x00;

t0 = Gettime();

for(u=0x0000; u<0x0200; ++u)
ch ^= eeprom_read_byte((uint8_t*)u);

t1 = Gettime();

SerPrint("average time for reading 1 byte from eeprom: ");
PrintLong((1000*(t1-t0))/512);
SerPrint("us\r\n");

SerPrint("control value (should be 0): ");
PrintInt((int)ch);
SerPrint("\r\n");

while (1);

return 0;
}

Das Programm bestimmt noch nebenbei die durchschnittlichen Zeiten für Lesen/Schreiben von/auf EEPROM, hier der Output von 4 Läufen:
average time for writing 1 byte to eeprom: 8355us
maximal time for writing 1 byte to eeprom: 9ms
average time for reading 1 byte from eeprom: 3us
control value (should be 0): 0

average time for writing 1 byte to eeprom: 8306us
maximal time for writing 1 byte to eeprom: 9ms
average time for reading 1 byte from eeprom: 3us
control value (should be 0): 0

average time for writing 1 byte to eeprom: 8308us
maximal time for writing 1 byte to eeprom: 9ms
average time for reading 1 byte from eeprom: 5us
control value (should be 0): 0

average time for writing 1 byte to eeprom: 8302us
maximal time for writing 1 byte to eeprom: 9ms
average time for reading 1 byte from eeprom: 3us
control value (should be 0): 0


Also Lesen eines Bytes 3-5µs, Schreiben eines Bytes 8.3ms, also mehr als 3 Größenordnungen langsamer ...


Noch ein Tipp für die Nutzer von Hyperterminal:
Zeilen, die nach oben aus dem Screen rausrutschen, werden zumindest in meinem Hyperterminal verunstaltet, wenn man nach oben scrolled.
Deshalb vor Start von EEPROM_dump.hex auf dem Asuro nicht Vergessen, den Terminal-Output mittles "Transfer-->Capture Text" (habe gerade kein dt. Hyperterminal griffbereit) in eine Datei zu sichern. Hinterher "Transfer-->Capture Text-->Stop" nicht vergessen.

m.a.r.v.i.n
12.04.2007, 10:24
Hallo Hermann,

sehr interessant, deine Untersuchung. Mit dem EEPROM habe ich mich bisher noch gar nicht beschäftigt.

Zu deiner Frage (obwohle ich den Quellcode des Bootloaders nicht kenne). Ja, die UART wird bereits im Bootloader initialisiert. Muß ja so sein, sonnte würde das Flashen nicht funktionieren.

Zur Codegröße. Das Optimum wäre es, jede Funktion einzeln in eine Datei ablegen, und daraus die LIB übersetzen. Noch kleiner geht es nur noch in Assembler. Beim weglassen der asuro.c darf man aber auch kein Sleep oder Msleep aufrufen, weil die Timer ISR auch nicht eingebunden wird.