RFM12 Protokoll Stack
A V R |
Application | ||||||||
---|---|---|---|---|---|---|---|---|---|
Mgmt | µIP | ... | Serial | ||||||
RFM12_LLC | |||||||||
RFM12_MAC | |||||||||
RFM12_PHY | |||||||||
GPIO | SPI | UART |
Dieser Artikel beschreibt einen ISO/OSI Protokoll Stack für den RFM12 Funkchip.
Einige Hardwarebeispiele finden sich in AVR RFM12.
Fehler und Anregungen können gerne auf der Diskussionsseite besprochen werden.
Eine Referenz-Implementierung für den AVR wird in folgendem SVN-Repository bereitgestellt:
svn://mikrocontroller.net/rfm12/trunk/RFM12_Stack |
- Implementierungskriterien
- Wo möglich werden Callback-Funktionen anstatt FIFOs verwendet um RAM zu sparen.
- Interruptgesteuerter Ablauf des Stacks
- Funktionen sollten nicht blockieren
Layer 1: Physical
Der Physical-Layer kümmert sich um die direkte Ansteuerung des RFM12. Alle Funktionen des Chips werden als Software-API bereitgestellt.
Folgende Eigenschaften sind bereits vom RFM12 vorgegeben:
- 70cm Band (433MHz)
- FSK-Modulation
- Sync-Erkennung auf 0x2D 0xD4
Definiert werden muss jedoch noch:
- Baudrate (Vorschlag: feste Kanal-Baudraten-Zuordnung)
- Zuordnung von Frequenzen zu Kanälen
- Bandbreite der Kanäle
- Management der Sendeleistung/Empfangsempfindlichkeit
API
void RFM12_PHY_modeTX(void);
void RFM12_PHY_modeRX(void);
bool RFM12_PHY_busy(void);
void RFM12_setBaudrate(uint32_t baud);
void RFM12_setCenterFrequency(uint16_t freq);
void RFM12_setDataFilter(bool autoLock, bool fastMode, bool analog, uint8_t dqdThres);
void RFM12_setReceiverBandwidth(RFM12_VDI_t vdi, RFM12_RxBW_t bandwidth, RFM12_GAIN_t gain, RFM12_RSSI_t drssi);
void RFM12_setPowerManagement(bool enRX, bool enBB, bool startTX, bool enSynth, bool enOSC, bool enBat, bool enWkT, bool clkOff);
void RFM12_setTransmitPower(RFM12_Power_t power, RFM12_TxDev_t deviation);
Layer 2: Data Link
Media Access Control (MAC)
Hier noch ein paar Anregungen zur Verbesserung des MAC: [1]
Der MAC-Layer ist sehr einfach gehalten und regelt den Zugriff auf das Medium. Hier soll ein CSMA/CA Verfahren eingesetzt werden.
- Im Grundzustand befindet sich der RFM12 im Empfangsmodus und wartet auf eine SYNC-Sequenz.
- Kommt eine Sendeanforderung, wird so lange gewartet bis das Medium frei ist, dann darf nach Ablauf einer (pseudo) zufälligen Zeitspanne gesendet werden.
- Wurde gerade ein Paket empfangen, darf sofort ein ACK gesendet werden.
- Die maximale Paketlänge beträgt 50ms. Die maximale Anzahl Bytes hängt daher von der Baudrate ab.
Der Frameaufbau ist teilweise vom RFM12 vorgegeben (SYNC-Erkennung). Der Start-Of-Frame (SOF) dient der Synchronisation. Als End-Of-Frame (EOF) wird ebenfalls die Sequenz 0xAA verwendet. Diese darf im Datenteil nicht vorkommen!
SOF | Sync | n Byte Daten | EOF | |||
---|---|---|---|---|---|---|
Bits | 16 | 16 | 0 - 8*n | 8 | ||
Wert | 0xAA | 0xAA | 0x2D | 0xD4 | Daten | 0xAA |
API
void RFM12_MAC_setChannel(uint8_t channel);
bool RFM12_MAC_isReceiving(void);
bool RFM12_MAC_mediaBusy(void);
void RFM12_MAC_startCtrlTransmission(void);
void RFM12_MAC_endCtrlTransmission(void);
Logical Link Control (LLC)
Der LLC-Layer kümmert sich um die Sicherung der zu übertragenden Daten. Außerdem können Geräte direkt mit ihrer Adresse (7-Bit) angesprochen werden.
- FEC
- Alle Bytes werden auf eine 16-Bit Hamming-Sequenz erweitert, wodurch die Datenrate halbiert wird.
- Dies erlaubt es einzelne Bitfehler zu korrigieren.
- Bei zu vielen Fehlern können diese jedoch nicht erkannt werden.
- Im Hamming-Code kommt die Sequenz 0xAA nicht vor (siehe MAC-Layer).
- Die FEC sorgt außerdem dafür, dass genügend 0-1 Übergänge vorkommen. So kann der Empfänger den Takt auch bei langen Paketen besser halten.
- CRC
- Der gesamte Frame wird nochmals durch eine Checksumme gesichert. (CRC-8: x8 + x5 + x4 + 1)
- Version
- Das Versionsfeld gibt die verwendete Version an (aktuell ist Version 1 = 0b00).
- Wenn die Version nicht unterstützt ist, wird der Empfang abgebrochen.
- Adressierung
- Broadcast (0xff) an alle Geräte (immer ohne ACK)
- 127 Multicast-Gruppen (0x80 - 0xfe)
- 128 Geräteadressen (0x00 - 0x7f) (mit oder ohne ACK)
Version | need ACK | is ACK | TypeID | Empfänger | Sender | 0-m Byte Daten | CRC-8 | |
---|---|---|---|---|---|---|---|---|
Bits (vor FEC) | 2 | 1 | 1 | 4 | 8 | 8 | 0 - 8*m | 8 |
Bits (nach FEC) | 4 | 2 | 2 | 8 | 16 | 16 | 0 - 16*m | 16 |
API
void RFM12_LLC_registerType(RFM12_ProtocolID_t typeID, RFM12_L3_Protocol_t proto);
uint8_t RFM12_LLC_sendFrame(RFM12_ProtocolID_t proto, uint8_t receiver, bool requireACK);
Layer 3: Network
Für den Network-Layer werden nur einige Beispiele angeführt. Ab hier können beliebige eigene Protokolle eingesetzt werden. Das verwendete Protokoll wird durch die TypeID im LLC-Layer angegeben. Die Layer-3-Protokolle sollten nach Möglichkeit nur über die LLC-API mit dem Rest des Stacks kommunizieren.
Die TypeIDs sind wie folgt definiert:
Typ 0: Management
Management Protokoll:
- Muss immer implementiert sein
- Sollte ähnliche Funktionen bieten wie das Service Discovery Protocol (SDP) bei Bluetooth
- Unterscheidung Request / Response
ID | Request | Response |
---|---|---|
0x00 | Device Discovery | Device Information |
0x01 | Available Layer 3 Protos | Protocol Information |
Typ 1: Bytestream
Die Einteilung in Pakete wird aufgehoben. Daten kommen 1:1 in der Reihenfolge an, wie sie gesendet wurden.
API
void RFM12_Serial_setPeer(uint8_t peer);
bool RFM12_Serial_txAvailable(void);
void RFM12_Serial_txData(uint8_t data);
bool RFM12_Serial_rxAvailable(void);
uint8_t RFM12_Serial_rxData(void);
Typ 3: IPv4
z. B. uIP TCP/IP Stack von Adam Dunkels <adam@dunkels.com>
Anhänge
Sequenzdiagramme
Empfang:
Senden:
Frames
Beachte: Durch die Hamming-Codierung sind alle Bytes innerhalb des MAC-Layer doppelt so lang wie angegeben!
MAC | LLC | L3 |
---|
SOF | Sync | Empfänger | Sender | need ACK | is ACK | | | TypeID | 0-28 Byte Daten | CRC-8 | EOF | |||
---|---|---|---|---|---|---|---|---|---|---|
Bits | 16 | 16 | 8 | 8 | 1 | 1 |1|1| 4 | 0-224 | 8 | 8 | ||
Wert | 0xAA | 0xAA | 0x2D | 0xD4 | 0xAA |
Kanalzuordnung
Kanal | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Mittenfrequenz | 431.0 | 431.5 | 432.0 | 432.5 | 433.0 | 433.5 | 434.0 | 434.5 | 435.0 | 435.5 | 436.0 | 436.5 | 437.0 | 437.5 | 438.0 | 438.5 |
Baudrate | 9.6k | 19.2k | 115.2k | 9.6k | 19.2k | 115.2k | 9.6k | 19.2k | 115.2k | 9.6k | 19.2k | 115.2k | 9.6k | 19.2k | 115.2k | 9.6k |
In der EU sind nur die Kanäle 5-7 möglich. Alle anderen Frequenzen stören andere Funkdienste und können zu nicht unerheblichen Bußgeldern und Kosten für Messeinsätze führen. Achtung: die Module dürfen auf den anderen Kanälen nur von Personen mit Amateurfunkgenehmigung betrieben werden!
Die o.g. Tabelle verwendet anscheinend gerundete Werte. Für die (je nach Baudrate und Frequenzhub) drei freien Kanäle ergeben sich folgende Eigenschaften:
Kanal | Mittenfrequenz | Bandbreite | untere Kanalgrenze | obere Kanalgrenze |
---|---|---|---|---|
5 | 433,34 MHz | ± 290 kHz | 433,05 MHz | 433,63 MHz |
6 | 433,92 MHz | ± 290 kHz | 433,63 MHz | 434,21 MHz |
6 breit | 433,92 MHz | ± 870 kHz | 433,05 MHz | 434,79 MHz |
7 | 434,50 MHz | ± 290 kHz | 434,21 MHz | 434,79 MHz |
Wie man hierbei sieht, fallen die Kanalgrenzen genau auf den zugelassenen freien Bereich von 433,05 MHz bis 434,79 MHz. Mit weniger Bandbreite lassen sich auch mehr Kanäle unterbringen.
Hamming-Tabelle
Encodieren
Algorithmus | Beispiel | |||||||
---|---|---|---|---|---|---|---|---|
1: | 8-Bit Wert | 10010110 | ||||||
2: | 4-7 | 0-3 | 1001 | 0110 | ||||
3: | HammingE[(4-7)] | HammingE[(0-3)] | 0xC7 | 0x38 | ||||
4: | high | low | 11000111 | 00111000 | ||||
5: | 16-Bit Codewort | 11000111 00111000 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x15 | 0x02 | 0x49 | 0x5E | 0x64 | 0x73 | 0x38 | 0x2F | 0xD0 | 0xC7 | 0x8C | 0x9B | 0xA1 | 0xB6 | 0xFD | 0xEA |
Die Einträge in der Tabelle sind so gewählt, dass der Code den maximalen Hamming-Abstand von 4 Bit hat.
Dekodieren
In der Tabelle HammingD stehen nur 4-Bit breite Werte. Um einen 8-Bit Wert zu erhalten müssen also 2 Einträge aus der Tabelle ausgewählt werden.
Wird nun ein 16-Bit Wert empfangen, bestimmt das High-Byte den ersten Eintrag, das Low-Byte den zweiten.
Es können bis zu zwei gekippte Bits im High- und im Low-Byte korrigiert werden.
Update: Dies stimmt so leider nicht, da der Code 2 Bit Fehler nur erkennen, aber nicht eindeutig reparieren kann. Beispiel: 0x29=0b00101001 kann durch zwei Bitfehler sowohl vom Code 0x2f=0b001011111 (codewort für 7) als auch vom Code 0x38=0b00111001 (codewort für 6) oder von Code 0x49=0b01001001 (Codewort für 2) entstanden sein. Eine eindeutige Rekonstruktion ist für zwei Bitfehler also nicht möglich. Für einen solchen Code wäre ein Hammingabstand von 5 erforderlich (lt. Wikipedia). Leider gibt es keinen (8,4,5) Code (8 Symbolbits, davon 4 Datenbits, alle Symbole haben Hammingabstand von 5).
(TODO die Tabelle überarbeiten und die nichteindeutigen Symbole markieren...)
Algorithmus | Beispiel (mit Bitfehlern) | |||||||
---|---|---|---|---|---|---|---|---|
1: | 16-Bit Codewort | 11010111 00101001 | ||||||
2: | high | low | 11010111 | 00101001 | ||||
3: | HammingD[high] | HammingD[low] | 0x09 | 0x06 (oder 0x07 oder 0x02...) | ||||
4: | 4-7 | 0-3 | 1001 | 0110 | ||||
5: | 8-Bit Wert | 10010110 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0x01 | 0x00 | 0x01 | 0x01 | 0x00 | 0x00 | 0x01 | 0x01 | 0x02 | 0x02 | 0x01 | 0x03 | 0x0A | 0x02 | 0x03 | 0x07 |
1 | 0x00 | 0x00 | 0x01 | 0x01 | 0x00 | 0x00 | 0x01 | 0x00 | 0x06 | 0x02 | 0x03 | 0x0B | 0x02 | 0x00 | 0x03 | 0x03 |
2 | 0x04 | 0x0C | 0x01 | 0x05 | 0x04 | 0x04 | 0x05 | 0x07 | 0x06 | 0x06 | 0x07 | 0x07 | 0x06 | 0x07 | 0x07 | 0x07 |
3 | 0x06 | 0x04 | 0x05 | 0x05 | 0x04 | 0x00 | 0x0D | 0x05 | 0x06 | 0x06 | 0x06 | 0x07 | 0x06 | 0x06 | 0x07 | 0x07 |
4 | 0x00 | 0x02 | 0x01 | 0x01 | 0x04 | 0x00 | 0x01 | 0x09 | 0x02 | 0x02 | 0x03 | 0x02 | 0x02 | 0x02 | 0x03 | 0x03 |
5 | 0x08 | 0x00 | 0x01 | 0x05 | 0x00 | 0x00 | 0x03 | 0x01 | 0x02 | 0x02 | 0x03 | 0x03 | 0x03 | 0x02 | 0x03 | 0x03 |
6 | 0x04 | 0x04 | 0x05 | 0x05 | 0x04 | 0x04 | 0x04 | 0x05 | 0x06 | 0x02 | 0x0F | 0x07 | 0x04 | 0x06 | 0x07 | 0x07 |
7 | 0x04 | 0x05 | 0x05 | 0x05 | 0x04 | 0x04 | 0x05 | 0x05 | 0x06 | 0x06 | 0x07 | 0x05 | 0x06 | 0x0E | 0x03 | 0x07 |
8 | 0x08 | 0x0C | 0x01 | 0x09 | 0x0A | 0x08 | 0x09 | 0x09 | 0x0A | 0x0A | 0x0B | 0x0B | 0x0A | 0x0A | 0x0A | 0x0B |
9 | 0x08 | 0x08 | 0x09 | 0x0B | 0x08 | 0x00 | 0x0D | 0x09 | 0x0A | 0x0B | 0x0B | 0x0B | 0x0A | 0x0A | 0x0B | 0x0B |
A | 0x0C | 0x0C | 0x0D | 0x0C | 0x0C | 0x0C | 0x0D | 0x0D | 0x0E | 0x0C | 0x0F | 0x0F | 0x0A | 0x0E | 0x0F | 0x07 |
B | 0x0C | 0x0C | 0x0D | 0x0D | 0x0D | 0x0C | 0x0D | 0x0D | 0x06 | 0x0E | 0x0F | 0x0B | 0x0E | 0x0E | 0x0D | 0x0F |
C | 0x08 | 0x08 | 0x09 | 0x09 | 0x08 | 0x09 | 0x09 | 0x09 | 0x0A | 0x02 | 0x0F | 0x0B | 0x0A | 0x0A | 0x0B | 0x09 |
D | 0x08 | 0x08 | 0x08 | 0x09 | 0x08 | 0x08 | 0x09 | 0x09 | 0x08 | 0x0A | 0x0B | 0x0B | 0x0A | 0x0E | 0x03 | 0x0B |
E | 0x0C | 0x0C | 0x0F | 0x0D | 0x04 | 0x0C | 0x0D | 0x09 | 0x0F | 0x0E | 0x0F | 0x0F | 0x0E | 0x0E | 0x0F | 0x0F |
F | 0x08 | 0x0C | 0x0D | 0x05 | 0x0C | 0x0E | 0x0D | 0x0D | 0x0E | 0x0E | 0x0F | 0x0F | 0x0E | 0x0E | 0x0F | 0x0E |