Forum: Mikrocontroller und Digitale Elektronik AVR - IDE Festplatte liefert immer Fehler beim lesen


von Oliver L. (ollil)


Lesenswert?

Hallo,

ich habe einen ATMega 1284P und eine ATA/IDE Schaltung inkl. Software 
welche mir das lesen und schreiben auf einer Festplatte erlaubt.

erstmal meine Schaltung:
http://www.pofo.de/P8000/notes/plaene/eigene/P8000_WDC_Emulator/P8000_WDC_Emulator_v1.2_Plan.pdf

und der relevante Softwareteil:
https://github.com/OlliL/P8000_WDC_Emulator/blob/master/P8000_WDC_Emulator/wdc_drv_pata.c

Ich habe nun ein paar Festplatten getestet und alle laufen problemlos 
bis auf eine.

WDC AC31600H (1.6GB)
WDC AC23200L (3.2GB)
WDC AC26400R (6.4GB)
Maxtor 2F040L0 (40GB)
MAXTOR 6L080J4 (80GB)

Probleme macht mir die WDC AC31600H. Diese liefert beim lesen eines 
Sektors immer 1 im ErrorBit des Statusregisters und ich weiss einfach 
nicht wiso. Ich habe die Festplatte mit MS-DOS 6.22 und FreeBSD 
problemlos lesen und beschreiben können. Die Platte an sich scheint also 
keine Probleme zu haben.

Die Ausgabe meiner IDENTIFY Routine:
1
INFO: Number of logical cylinders: 3148
2
INFO: Number of logical heads: 16
3
INFO: Number of logical sectors per logical track: 63
4
INFO: Serial number: WD-WT2891920914
5
INFO: Firmware revision: 23.16U73
6
INFO: Model number: WDC AC31600H
7
INFO: Capabilities: DMA, LBA, IORDY may be disabled, IORDY, Standard standby timer values,
8
INFO: User addressable sectors for 28-bit commands: 3173184
9
Single Word DMA modes: 0
10
Multiword Word DMA modes: 1031
11
PIO modes: 3
12
INFO: Minimum Multiword DMA cycle time per word: 120ns
13
INFO: Recommended Multiword DMA cycle time: 120ns
14
INFO: Minimum PIO transfer cycle time without flow control: 160ns
15
INFO: Minimum PIO cycle time with IORDY flow control: 120ns

Wie lese ich nun von der Platte. Ich versuche mal die entsprechende 
Routine aus wdc_drv_pata.c zusammenzufassen:

- AVR-Port auf Output stellen
- Setzen PATA_RW_SECTOR_COUNT_REGISTER
Datentransfer: Anzahl zu lesende Blöcke
- Setzen PATA_RW_SECTOR_NUMBER_REGISTER
Datentransfer: niedrigen 8 bit der niedrigen 16 bit des zu lesenden 
Blocks
- Setzen PATA_RW_CYLINDER_LOW_REGISTER
Datentransfer: hohen 8 bit der niedrigen 16 bit des zu lesenden Blocks
- Setzen PATA_RW_CYLINDER_HIGH_REGISTER
Datentransfer: niedrigen 8 bit der hohen 16 bit des zu lesenden Blocks
- Setzen PATA_RW_DEVICE_HEAD_REGISTER
Datentransfer: hohen 8 bit der hohen 16 bit des zu lesenden Blocks 
und-verknüpft mit 0x0F und ergänzt um 0xE0 für drive 0 (master)
- Setzen PATA_W_COMMAND_REGISTER
Datentransfer: 0x20

Wie läuft der eigentliche Datentransfer ab:
- Daten an den Port anlegen,
- /DIOW aktiv setzen
- 2 * nop()
- /DIOW deaktivieren

Danach werden dann die eigentlichen Nutzdaten gelesen:
- AVR-Port auf Input stellen
- Schleifeneinstieg:
- warten bis BSY low wird und DRQ high wird
- Setzen PATA_RW_DATA_REGISTER
- /DIOR aktiv setzen
- 3 * nop()
- die niederen 8 Bit rufe ich nun direkt vom Port ab, die oberen 8 Bit 
liegen automagisch in einem Latch zwischengeparkt
- /DIOR deaktivieren
- die oberen 8 Bit vom Latch abrufen
- alle Register (cs0,da0,....) deaktivieren
- und weiter bei "Schleifeneinstieg" bis alle Daten abgeholt wurden.

Danach rufe ich das STATUS-Register ab, und hier ist nun das Error-Bit 
gesetzt.

Sieht einer Fehler?

von Oliver L. (ollil)


Lesenswert?

Ich hasse es immer wenn ich meine Fragen selber beantworten muss....
1
        /* Wait for BSY goes low and DRQ goes high */
2
        while ( pata_bsy() & !pata_drq() ) {}

Was ist falsch? Genau... bitweises AND anstatt logisches AND... die 
"moderneren" Platten waren zufällig alle schnell genug... oh man... 
Problem erledigt....

von Oliver L. (ollil)


Lesenswert?

Irgendwie passt das immer noch nicht. Wenn ich mir andere 
Implementierungen anschaue, warten Diese bis bsy nicht mehr gesetzt ist, 
dann wird das Kommando geschickt (0x20) und dann wird gewartet bis DRQ 
high ist.

Wenn ich das bei mir so implementiere, dann kommt bei meinen 
funktionierenden Platten schon beim IDENTIFY Müll raus.

Wenn ich vor dem Kommando nur auf bsy warte, bekomme ich bei meinen 
funktionierenden Platten beim Lesen eines Blockes 2x d0 zu lesen und 
dann direkt 58. Also BEVOR ich das Kommando 0x20 sende ist bereits DRQ 
high. Wie soll man daraus schlau werden?
Bei meinem nicht funktionierenden LW geht er von 1x d0 auf 59 (error bit 
gesetzt) noch BEVOR ich Kommando 0x20 sende.

Überhaupt... lesen der IDENTIFY Daten klappt ja bei allen Laufwerken.

Kann es evtl. sein, dass das Drive nicht richtig in den LBA-Mode gesetzt 
wurde?

Läuft bei mir so ab:
1
uint8_t pata_init ()
2
{
3
    uart_putstring ( PSTR ( "INFO: PATA init start" ), true );
4
    while ( ( !pata_rdy() ) & pata_bsy() ) {}
5
6
    /*set drive 0 to LBA mode*/
7
    write_io_register ( PATA_RW_DEVICE_HEAD_REGISTER, ATA_LBA_DRIVE_0 );
8
    while ( ( !pata_rdy() ) & pata_bsy() ) {}
9
10
    /*recalibrate*/
11
    write_io_register ( PATA_W_COMMAND_REGISTER, CMD_RECALIBRATE );
12
    while ( pata_bsy() ) {}
13
14
15
    return ata_identify();
16
17
}

: Bearbeitet durch User
von Axelr. (Gast)


Lesenswert?

Zum Trost:
ich habe es gelesen, aber leider keinen Plan ...

von Oliver L. (ollil)


Lesenswert?

Habe auch einfach mal versucht "hardcoded" CHS zu machen:

Sector count 1
Sector number 0
Cylinder low 0
Cylinder high 0
Device head 0xA0 (Drive 0, CHS, Head 0 sollte das sein).

Beim lesen der Daten wandert er auch dort von D0 auf 59.....

von Michael U. (amiga)


Lesenswert?

Hallo,

aua... sowas habe ich vor 15 Jahren mal mit einem 8515 gemacht, in ASM 
als MP3-Player.

Den Source hätte ich wohl dmals etwas mehr kommentieren sollen.
Platte war irgendeine 2,5" 2GB.

Das HD war Memory-mapped am 8515, Schalplan müßte ich selber suchen.
Zugegriffen habe ich wohl im CHS-Mode, da muß ich aber erstmal genauer 
schauen.
Zumindest schicke ich hier Drive/Head,Zylinder High/Low, Sektornr, 
Sektoranzahl und dann die 0x20 für Read Sektor raus.

Dann warte ich, bis Busy nicht mehr aktiv ist und anschließend bis DRDY 
aktiv ist.

Ich klebe das Fragment hier einfach mal rein, vielleicht hilft es 
irgendwie.
1
;*********************************************************************
2
;  IDE Read Sektor in ZYL_L, ZYL_H, SEKTOR, HEAD die Sektornummer
3
;           in HD_LOAD_L, HD_LOAD_H der Zeiger auf den Load-Buffer
4
;
5
;  Scratch-Reg: TEMP_A, TEMP_B, COPY, ZH, ZL, YH, YL, MCU_TMP
6
;*********************************************************************
7
8
ide_rd_sek:  
9
        ldi     ZL,RG_HEAD          ; Register Drive/Head
10
        mov     YL,LBA_0            ; Head
11
        rcall   ide_wr_byte
12
13
        ldi     ZL,RG_H_ZYL         ; Register Zylinder-Nummer High
14
        mov     YL,LBA_1            ; Zylinder High holen
15
        rcall   ide_wr_byte
16
17
        ldi     ZL,RG_L_ZYL         ; Register Zylinder-Nummer Low
18
        mov     YL,LBA_2            ; Zylinder Low holen
19
        rcall   ide_wr_byte
20
21
        ldi     ZL,RG_S_NUM         ; Register Sektor-Nummer
22
        mov     YL,LBA_3            ; Sektor holen
23
        rcall   ide_wr_byte
24
25
        ldi     ZL,RG_S_COUNT       ; Register Sektor-Anzahl
26
        ldi     YL,1                ; 1 Sektor
27
        rcall   ide_wr_byte
28
29
        ldi     ZL,RG_CMD           ; Register Command
30
        ldi     YL,$20              ; Command Read Sektor
31
        rcall   ide_wr_byte
32
33
sek_loop:
34
        ldi     ZL,RG_STAT          ; IDE-Adresse
35
        rcall   ide_rd_byte
36
        sbrc    YL,SR_BSY           ; Nicht mehr Busy
37
        rjmp    sek_loop            ; sonst weiter warten
38
39
        sbrs    YL,SR_DRDY          ; Ready ?
40
        rjmp    sek_loop
41
42
        sbrs    YL,SR_ERR           ; Fehler
43
        rjmp    read_sec
44
45
        push    YL                  ; Status-Byte merken
46
        ldi     ZL,RG_ERR           ; IDE-Adresse
47
        rcall   ide_rd_byte         ; Error-Byte nach YL
48
        pop     YH                  ; Status-Byte nach YH holen !!!
49
50
        ret                         ; Error, YH enthält Statusbyte, YL Error-Byte
51
52
read_sec:
53
        sbrs    YL,SR_DRQ           ; DRQ noch nicht da
54
        rjmp    sek_loop
55
56
        ldi     TEMP_B,2            ; 2* 256 Byte -> 1 Sektor
57
58
read_l:
59
60
; einlesen in MINI_BUF
61
62
        cli                         ; IRQ sperren
63
64
        ldi     ZL,RG_DATA          ; Datenregister
65
        ldi     ZH,EXT_RAM_DUMMY    ; $FF
66
        ld      TEMP_A,Z            ; Adresse im Latch
67
68
        ext_ram_off
69
70
        load_p  Z,MINI_BUF          ; weil ext. Ram aus !
71
        ldi     TEMP_A,128          ; für 256 Byte MINI_BUF und Word-Zugriff !
72
73
rd_loop:
74
        cbi     IDE_PORT,IDE_DIOR
75
        nop
76
        in      YL,IDE_L_IN
77
        in      YH,IDE_H_IN
78
        sbi     IDE_PORT,IDE_DIOR
79
80
        st      Z+,YL
81
        st      Z+,YH
82
83
        dec     TEMP_A
84
        brne    rd_loop
85
86
        ext_ram_on
87
        sei                         ; IRQ frei
88
89
; kopieren MINI_BUF -> (HD_LOAD)
90
91
        load_p  Z,MINI_BUF          ; weil ext. Ram aus !
92
        mov     YH,HD_LOAD_H
93
        mov     YL,HD_LOAD_L        ; Pufferadresse holen
94
95
        clr     TEMP_A              ; für 256 Byte MINI_BUF und Byte-Zugriff !
96
97
copy_loop:
98
        ld      COPY,Z+
99
        st      Y+,COPY
100
101
        dec     TEMP_A
102
        brne    copy_loop
103
104
        mov     HD_LOAD_H,YH
105
        mov     HD_LOAD_L,YL         ; aktuelle Puffer-Adresse merken
106
107
        dec     TEMP_B
108
        brne    read_l
109
110
        ret

Gruß aus Berlin
Michael

: Bearbeitet durch User
von Oliver L. (ollil)


Lesenswert?

Ich fahre nun erstmal alle meine Platten mit CHS an und lese den 1. 
Sektor auf jeder Platte.

1
    while ( pata_bsy() == 1);
2
3
    write_io_register (PATA_RW_SECTOR_COUNT_REGISTER,  1);
4
    write_io_register (PATA_RW_SECTOR_NUMBER_REGISTER, 1);
5
    write_io_register (PATA_RW_CYLINDER_LOW_REGISTER,  0);
6
    write_io_register (PATA_RW_CYLINDER_HIGH_REGISTER, 0);
7
    write_io_register (PATA_RW_DEVICE_HEAD_REGISTER,   0xA0);
8
9
    write_io_register (PATA_W_COMMAND_REGISTER, 0x21 );
10
11
    while ( pata_bsy() == 1);
12
    while ( pata_drq() == 0 );
13
14
    daten lesen
15
16
    error-bit auswerten

Das erste pata_bsy() liefert 0x50 -> es geht los
und schon beim nächsten pata_bsy() kommt 0x59, pata_drq() liefert dann 
natürlich auch 0x59.

Warum frage ich mich nur.... seufz

Ist doch jetzt auch nicht groß anders als ataReadSectorsCHS() in
https://arduino.googlecode.com/svn-history/r1/trunk/build/shared/lib/avrlib/ata.c

PS:
Habe jetzt sogar mal eine ST3290A (261MB) ausgegraben die z.B. nur CHS 
kann... selbst die läuft problemlos.

: Bearbeitet durch User
von Oliver L. (ollil)


Lesenswert?

Habe jetzt mal das Error-Register ausgelesen. 0x04 kommt zurück - bei 
allen Lese- und Schreibversuchen.

ABRT
indicates the requested  command has been aborted  due to a
drive status error (such as not ready or write fault) or because
the command is invalid.

von Oliver L. (ollil)


Lesenswert?

Keine IDE-Cracks hier? ;)

ich habe es nun mal umgebaut und setze Drive/Head zuerst.
Ich habe dann mal 10ms delays nach dem setzen der Daten der jeweiligen 
register und nach dem aktivieren von /WR und weitere 10ms nach dem 
deaktivieren von /WR
Dann habe ich noch ein Status-Reg-Check nach jedem Register-set 
eingebaut und es  bleibt kontinuierlich bei 0x50 bis ich dann das 
Kommando setze. Danach geht es sofort auf 0x59.

:(

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Oliver L. schrieb:
> while ( ( !pata_rdy() ) & pata_bsy() ) {}

Das ist doch wieder ein bitweises AND - Du solltest Deinen Code 
gründlich auf derartige Fallstricke überprüfen.

von Oliver L. (ollil)


Lesenswert?

Jo... hatte ich in der Zwischenzeit schon getan. Ist nix mehr davon 
vorhanden in

https://github.com/OlliL/P8000_WDC_Emulator/blob/master/P8000_WDC_Emulator/wdc_drv_pata.c

: Bearbeitet durch User
von Oliver L. (ollil)


Lesenswert?

Habs rausgefunden.... Logik Analysator und MS-DOS sei dank der nach dem 
Power-Cycle der Platte im laufenden Betrieb und anschliessendem Zugriff 
auch den gleichen Fehler wie ich bekommt aber dann ein Drive-Initialize 
absetzt und danach nochmal probiert - erfolgreich.

Also mache ich beim Bootup meines AVR einfadch auch ein 
Drive-Inititalize-Command und nun läufts.....

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.