IPAQ AVR Hacking
Inhaltsverzeichnis
5358S09TA eht gnireenigne esrever
{maketoc}
jippie, wie unter IpaqRfmFunkmod näher dargelegt, habe ich mich dazu entschieden, die Firmware des auf dem iPAQ verbauten AT90S8535 etwas näher unter die Lupe zu nehmen. Meine Fortschritte in Punkto Programmcode kannst Du unter http://git.brokenpipe.de/cgi-bin/gitweb.cgi?r=stesie;p=h3600_micro;a=summary begutachten.
Der Controller AT90S8535 ...
- läuft mit maximal 8 MHz, verbaut ist ein Quarz mit 3,68 MHz
- hat 8 kB Flashspeicher für Programmcode
- 512 Byte RAM
- 512 Byte EEPROM
Die Außenbeschaltung des Controllers
ist unter http://handhelds.org/Compaq/iPAQH3600/iPAQ_H3600.html#pgpio ausführlichst beschrieben.
Protokoll
Der AVR ist mittels UART an den Strong-ARM des iPAQ angebunden. Dabei scheint der iPAQ zu entscheiden, wann der AVR sprechen darf und wann nicht. Von sich aus sagt der AVR scheinbar nichts, er macht sich nur mittels Interrupt beim ARM bemerkbar und wird dann von diesem ausgefragt.
Der Befehlssatz
Version | 0 |
Keyboard | 2 |
Touchpanel | 3 |
EEPROM Read | 4 |
EEPROM Write | 5 |
Thermal Sensor | 6 |
Notify LED | 8 |
Battery | 9 |
SPI Read | 11 |
SPI Write | 12 |
Backlight | 13 |
Codec Control | 14 |
Paketaufbau AVR -> ARM
SOF | ID + Len | Data | Chksum |
immer 0x02 | Die Befehls-ID wird im oberen Nibble,
die Länge im unteren Nibble übermittelt |
zu übertragende Daten (max. 15 Byte) | Prüfsumme = Summe der ASCII-Werte der Bytes
ID/Len und der Datenbytes |
Das heißt es werden immer mindestens drei Bytes übertragen, maximal 18.
Als Default Ack wird die Reaktion des AVR bezeichnet, wenn keine Daten zurück gegeben werden, sondern nur der Befehl als solches bestätigt wird. Die Original-Firmware kennt vier Befehle, die immer ein Default-Ack zurück geben:
- EEPROM Write
- Notify LED
- SPI Write
- Codec Control
Im Falle von EEPROM Write (ID 5) würde also 0x02, 0x05, 0x05 zurück übertragen.
Version Ack
0x02 | 0x09 | 1.07 | x.xx | yy | Chksum |
SOF | ID = 0, Len = 0 | Host Version
bei mir 1.07, wird als ASCII-String übertragen |
Pack Version
ebenfalls ASCII String aus RAM 0x00BC ff. |
Boot Type
RAM 0x00BF |
- |
Der Inhalt von 0x00BF wird nach dem Lesen auf 0x00 zurück gesetzt.
Keyboard Ack
0x02 | 0x21 | RAM 0x006B | Chksum |
Im RAM werden in 0x0068 die Bits 3 und 6 gelöscht (AND 0xB7).
Paketaufbau ARM -> AVR
TODO
Registerbelegung
Register | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
r0 | SREG carry (zum Zwischenspeichern während Interrupts) | |||||||
r1,r2 | temp?? | |||||||
r3..r6 | unused | |||||||
r7 | temp?? | |||||||
r8 | Counter, counting from 3 down in TOV0 | |||||||
r9 | some state (what??) | |||||||
r10..15 | temporary? state? ?? (usage handling r9 register) | |||||||
r16..r21 | temporary registers | |||||||
r22 | Batt. Temp. related ??? | Thermal Sensor Ack | Send SPI Read Ack | |||||
r23 | Default Ack
zurückgeben |
Battery Ack | Backlight Ack
Light sensor status from 0x008F |
Version Ack
zurückgeben |
EEprom Read Ack
sendet Bytes ab 0x00D5 |
Keyboard Ack
zurückgeben sendet Byte 0x006B |
Touchpanel Read Ack
sendet Bytes 0x0060..63 |
CPU IRQ Reply
Code nicht ausführen |
r24 | Skip ADC Block | Sotre ADC
Batt. temp. sensor |
Store ADC
light sensor |
Store ADC
charger current |
Store ADC
main batt. 2/3 voltage |
store ADC
key signal input |
||
r25 | 7 | Trigger ADC for
Batt. temp. sensor |
Trigger ADC for
light sensor |
Trigger ADC for
charger current |
Trigger ADC for
main batt. 2/3 voltage |
Trigger ADC for
key signal input |
Trigger ADC for
TP X coord |
ADC start over
Trigger for TP Y coord. |
r26:r27 | X Pointer | |||||||
r28:r29 | Y Pointer | |||||||
r30:r31 | Z Pointer |
Speicherbelegung
Speicherzelle | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
0x0000..0x005F | MCU Register | |||||||
0x0060..0x0063 | Touchpanel Read Buffer | |||||||
0x0064 | Touchpanel related (whatever?) | |||||||
0x0065 | RX buf busy
(Packet zu verarbeiten) |
|||||||
0x0066 | Bytes left to receive / Packet length | |||||||
0x0067 | Default Ack: SPI Write
(vgl. r23, bit 7) |
Default Ack: EEPROM Write
(vgl. r23, bit 7) |
Default Ack: Notify LED
(vgl. r23, bit 7) |
Default Ack: Codec Control
(vgl. r23, bit 7) | ||||
0x006B | Keyboard "Read" Buffer (wird beim Keyboard Ack zurückgegeben) | |||||||
0x006D | Notify LED nicht ändern | Notify LED abschalten | ||||||
0x0075 | Battery Dead | Thermal related ?? | ||||||
0x007B | Battery Voltage MSB | |||||||
0x007C | Battery Voltage LSB | |||||||
0x007D | Battery Voltage MSB (raw value) | |||||||
0x007E | Battery Voltage LSB (raw value) | |||||||
0x007F | Battery Voltage Iteration Counter (0..) ?? | |||||||
0x0080 | Countdown triggering batt. voltage sense (from TOV2) | |||||||
0x0081 | Battery Voltage MSB (latest measurement) | |||||||
0x0082 | Battery Voltage LSB (latest measurement) | |||||||
0x0083 | Charge Current LSB | |||||||
0x0084 | Charge Current MSB | |||||||
0x0087 | Thermal Batt. Status LSB | |||||||
0x0088 | Thermal Batt. Status MSB | |||||||
0x008C | charging (1. Batt) | charging (1. Batt) | charging (1. Batt) | |||||
0x008D | Timer/Counter Charger | |||||||
0x008E | Light sensor ?? | |||||||
0x008F | Light sensor status (??); value returned on backlight ack | |||||||
0x0090..0x00A0 | SPI (send??) buffer | |||||||
0x00A1 | Zeiger auf nächstes zu sendendes Byte (SPI) | |||||||
0x00A2 | Zeiger auf das letzte zu sendende Byte (SPI) | |||||||
0x00A3 | Zeiger wo nächstes gelesenes Byte gespeichert werden soll (SPI) | |||||||
0x00A4 | dto. ?? | |||||||
0x00A5.. | SPI buffer ?? | |||||||
0x00B6 | Second Battery Chemistry | |||||||
0x00B7 | Akkustand 2. Batt. (Prozent) | |||||||
0x00B8 | 2. Batt. nicht vorh. | 2. Batt. voll | 2. Batt not inst. | 1. Batt charging | 2. Batt charging | 2. Batt critical | 2. Batt status low | 2. Batt Status high |
0x00B9 | Countdown 5..0, SPI related, Sleeve/2nd Batt. detection related ?? | |||||||
0x00BA | Counter Charge/Battery Temperature related ?? | |||||||
0x00BB | Battery Temp. related ?? | |||||||
0x00BC | Vorkommastelle der Pack-Version | |||||||
0x00BD | 1. Nachkommastelle Pack-Version | |||||||
0x00BE | 2. Nachkommastelle Pack-Version | |||||||
0x00BF | Boot Type (0x01 = Power-on Reset, 0x02 = External Reset, 0x03 = Watchdog Reset)
Der Wert wird nach einmaliger Abfrage der Version (Befehl 0x00) gelöscht | |||||||
0x00C0..0x00CF | Send buffer | |||||||
0x00D1 | Zeiger auf nächstes zu sendendes Datenbyte (UART) | |||||||
0x00D2 | Zeiger auf das letzte zu sendende Datenbyte (UART) | |||||||
0x00D3 | EEprom Read REQ Addr | |||||||
0x00D4 | EEprom Read REQ Len | |||||||
0x00D5 | Länge der EEprom-Read-Buffer | |||||||
0x00D6.. | EEprom-Read-Buffer (nur 8 Byte??) | |||||||
0x00DE..0x00EE | Incoming Message Buffer; an 00DE liegt der Befehl, ab 00DF kommen die Daten | |||||||
0x00EF | Pointer auf Incoming Message Buffer; wenn 0xDE -> dann Puffer leer; beim Empfang Zeiger auf Stelle, an die das nächste Byte zu speichern ist | |||||||
0x00F1 | Light sensor ?? | |||||||
0x00F3 | Skip SPI Block | SPI ?? | read ext. batt state (??) | SPI Read/Write Complete | read ext. batt state (??) | SPI Datenrichtung
(1 = Writing) | ||
0x00F4 | SPI write active | SPI read active | ||||||
0x00F5 | SPI ?? | SPI ?? | SPI busy ?? | |||||
0x00F6 | SPI related ?? | |||||||
0x00F7 | Anzahl der noch zu empfangenden Bytes (SPI) | |||||||
0x00F8 | SPI Read Buffer len | |||||||
0x00FF | ||||||||
0x0100..0x010F | SPI Read Buffer (Data out over UART) | |||||||
0x0113.. | SPI (Send?) Buffer |
Bytes 00B6, 00B7, 00B8 and 00FF cleared after every power down.
Modifikationskonzept für den RFM12
Alter SPI Code raus? ... nein! bloß nicht!
... Zunächst war angedacht den alten SPI-Code von Bord zu werfen. Aber wir können auf den originalen SPI-Code nicht verzichten, da damit EEPROMs in der Sleeve abgefragt werden. Auf diese Art und Weise wird unter anderem abgefragt, welcher Akku verbaut ist, Ladezustand, Gerätetyp, Seriennummer, etc.
Wohl oder übel haben wir dann beschlossen, dass unser RFM12 einen eigenen Chip Select zur Verfügung bekommt. Nachdem der AVR keine freien I/O-Ports hat, muss eine andere Funktion leiden. Das kleinste Übel ist da wohl die Notify LED -- bislang haben wir mit der ohnehin nichts angestellt. Ist auch die Frage, warum die LED "Notify" heißt, ein Dialogfenster am Bildschirm fällt doch tausendmal mehr auf ...
Interrupt
Auf Grund akutem Pinmangel muss sich der RFM12 den Interrupt mit dem Touchscreen teilen, ... der Pin ist active-low, kann also ohne größere Probleme vom RFM12 angesteuert werden. Auf der Leitung sind auch nicht all zu viele Interrupts aktiv, sodass ein Abfragen des RFM12, ob er denn der Schuldige ist, nicht erheblich ins Gewicht fällt (Interrupts treten immer dann auf, wenn der Anwender auf's Display klopft) ...
Der andere Interrupt-Pin ist übrigens nicht ohne weiteres verwendbar. Es hängt eine ganze Reihe von Logikgattern davor, die alle angesteuert werden möchten. Da ist der Pin fürs Touchscreen doch wesentlich leichter zu handlen ...
Neues Protokollbackend für RFM2
Natürlich brauchen wir einen neuen Befehl, Befehls-ID nehmen wir die 10. Mit der Fünf-/Dreiundzwanzig-Logik kommen wir diesmal nicht weiter. Die 5 ist unmittelbar belegt, die 8 zwar eigentlich unbesetzt, aber da hätten wir ein Problem wenn der ARM von dem RFM12 nichts weiß und wirklich die Notify-LED steuern möchte.
Gesendet werden an den AVR immer mindestens zwei Byte, egal welche Richtung, d.h. der Identifier ist fix 0xA2 bis 0xAF.
Die Initialisierung des RFM12 erfolgt zunächst vom ARM, d.h. der ARM sendet die typischen Initialisierungsbefehle an den AVR, der diese schlicht weiterleitet. Das hat den Vorteil, dass man die Baudrate, Modulation, etc. relativ leicht abändern kann.
Spezielle Befehle ARM->AVR:
0xF000 | Internen Status abfragen (liefert Inhalt von r5 und r6) |
0xF100 | Enable RX |
0XF20n | Trigger TX, letzter zu sendender Puffer ist n (wahlweise 3 bis F) |
0xF3 | TX einleiten, Puffer 3 (= erster Datenpuffer) schreiben, max. 14 Bytes können folgen |
0xFn | TX-Puffer n schreiben (4 bis F), max. 14 Bytes können folgen |
0x0000 | Status vom RFM12 abfragen |
Alle anderen Befehle werden direkt an den RFM12 gesendet. Zurück gegeben wird nicht die Antwort des RFM12, sondern der interne Status!!
Antwortpakete AVR->ARM
A0 | TX-mode, FIFO fill phase (triggered by 0xF3...) |
A1 xx | RX-mode, Datenbyte xx empfangen |
A2 xx yy | Statusbytes (abhängig vom vorher gesendeten Befehl) |
Aufbau des RFM12-Statusregisters
r3 | Wait-Byte 0 | Wait-Byte 1 | unused | unused | unused | bit10 | bit9 | bit8 |
r4 | (RX) bytes left to receive (bits 7..0) | |||||||
r5 | FIFO overrun | reset request | FIFO busy (RX) | FIFO notification request | Ack Internal Status | Ack Rfm Status | RX active | TX active |
r6 | (RX) fifo byte |
Speicherbelegung durch RFM12-Codeteil
0x0130..0x01ff | TX-Puffer | |
0x0200 | Frequency Config Byte | 0xa6nn |
0x0201 | Bandwidth Config Byte | 0x94nn |
0x0202 | Baudrate Config Byte | 0xc6nn |
0x0203 | Power Config Byte | 0x98nn |