I2C-RF-modtager
I2C-RF-modtager modulet er baseret på det ene af to billige moduler der kan købes sammen i Kina f.x. ved denne nethandel[1]
Modulet kan anvendes i forskellige sammenhænge hvor men skal modtage informationer trådløst, og det kan så kobles op i en sammenhæng med andre I2C-moduler som skitseret her:
Struktur for I2C kommunikation hvor der indgår et I2C-RF-modtager modul
Modulet skal arbejde sammen med en enhed der sender efter den specificerede protokol, hvor man kan overføre en række forskellige variabler af ønskede længder.
Som standard er modulet udviklet til at kunne håndtere 16 variabler af maksimalt 4 bytes hver.
Teori for RF-link modulerne
Der er ikke så meget teori for RF-link modulet, da det princip der udnyttes blot er at transmittermodulet kan styres til at lave en frekvens på 433 MHz som den sender, eller der kan slukkes for den, så den ikke sender noget. Modtagermodulet giver så et højt signal ud når den modtager 433 MHz og et lavt når den ikke modtager noget.
Den indledende teori er beskrevet i RF-link.
Tidsmæssig definitioner i pakken
For at modulet skal fungere bedst muligt, så skal man gerne skifte mellem sending/passivt eller høj/lav hele tiden for at modtageren bedst kan indstille sig på niveauet for høj/lav.
For at modtageren kan vide hvornår den skal starte med at læse en pakke, så sendes der i starten en marker som sammensættes af en høj i 500us og en lav i 300 us. Dette vil ikke kunne forekomme inde i en pakke, så hvis man ser denne kombination efter hinanden, så er det starten af en pakke. Starten af en pakke ser ud som følger:
Starten af en pakke med 500us høj og 300us lav, efterfulgt af en række bit
For opnå flest muligt skift, så kodes et sendt 0 som høj i 100us og lav i 100us, og for at der skal være en rimelig forskel kodes 1 som høj i 100 us og lav i 200 us. På denne måde kan modtageren skelne mellem 0 og 1 i modtagelsen. Dette kan ses på følgende måling taget midt i en pakke:
Modtagelse af bit med betydning 1 og 0 - hver bit består både høj og lav
Da pakkerne kan have forskellige længder, så skal der også være noget der indikerer afslutningen af en pakke. Alle de høje niveauer gennem pakken har været på 100us, så afslutningen kan markeres ved at sende i 300us. Dette ser ud som følger:
Afslutningen af en pakke med et par bit og til slut en 300us høj puls
En total pakke består som forklaret af en starte og en stop, og så er der en hel serie af data inde i pakken. Dette ser ud som følger:
En hel pakke med en to-bytes variabel
Når der ikke modtages noget
Når der ikke sendes noget, så vil modtageren skrue op for følsomheden, og vil på et tidspunkt modtage støj, som er skiftende høje og lave signaler, her vist hvor der er pulser fra 8us op over 1ms.
Måling af modtaget støj med højst skiftende tider i pulser og pauser
I denne støj ville den kunne modtage noget der kunne ses som starten af en pakke. Derfor kontrolleres gennem hele pakken om det modtagne ligger inden for rimelige grænser af hvad der kan accepteres som 0 og 1. Modtages der noget tidsmæssigt afvigende, så forkastens pakken. Derfor kan man ikke stole på at alle pakker kommer igennem til modtageren.
Specielt i starten af pakken, så kan modtageren tro at den modtager signal, hvor der ikke sendes noget. Når der så sendes 500 us, så kan den modtagne puls være noget længere, men ikke særligt meget kortere, derfor ledes efter en puls der er over 400us. Når modtageren så har indstillet sig på niveauet, så vil den pause der skal ledes efter være mellem 250us og 350us. Hvis begge dele kommer i træk så startes der med at modtages en pakke.
Indhold i pakken
Pakkerne er designet til at kunne indeholde en variabel, som kan være af typen byte, int eller long, altså hhv. 1, 2 eller 4 byte lang.
Ud over dette lægges der en adresse på modtageren (en byte) og der angives hvilken variabel det er (en byte 0-15), og for at man ikke kommer til at overskrive en variabel med noget forkert, så lægges der en del kontrol ind i pakken, som skal stemme overens med hinanden for at man kan acceptere pakkens indhold.
En pakke der indeholder en int (2 databyte) kan illustreres på følgende måde:
| start | adr | /adr | nr | /nr | low | /low | high | /high | CS | slut |
Start er de 500us høj og 300us lav.
Alle de efterfølgende bytes sendes to gange, hvor den anden gang er det bitmæssigt inverterede indhold (indikeret med / foran). Dette kan kontrolleres ved modtagelsen, og hvis der ikke er modtaget det samme inverteret, så kasseres pakken.
adr er den adresse modulet har, hvis den ikke er korrekt, så ignoreres pakken.
nr er det nummer variablen har, så protokollen kan håndtere forskellige variabler.
low og high er selve data-indholdet af pakken.
CS er en sammentælling af adr, nr og data-indholdet i en byte (overflower). Hvis sammentællingen ikke passer med den modtagne CS, så kasseres pakken.
Slut er den 300us høj puls der modtages.
En total pakke ser ud som følger:
En hel pakke med en to-bytes variabel
Princip for RF-modtager
Modulet er opbygget ved at bruge et simpelt 433 MHz modtager modul, der giver høj ud når det modtager 433 MHz og lav når det ikke modtager det.
Det digitale signal behandles af en microcontroller af typen ATTiny45, der forholder sig til de tider det modtager med og tolker om det modtagne overholder det pakkeformat der er beskrevet under de tidsmæssige definitioner af pakken. De pakker der modtages og godkendes lagres i microcontrolleren.
Microcontrolleren er programmeret til at fungere som en I2C enhed der kan svare på en bestemt I2C adresse, hvor man kan indstille forskellige ting i modtageren, og man kan hente indholdet af de pakker der er modtaget.
I det efterfølgende er både hardware og software i I2C modulet beskrevet.
Strukturen i I2C RF modtageren
Blokdiagram over I2C RF Modtager
Blokken med 433 MHz modtageren er et færdigt købt modul der placeres i et stik på modtageren, og giver et digitalt signal ud.
Microcontrolleren er en 8 bens ATTiny, der kan håndtere I2C på 2 ben, på de samme ben kan den programmeres ved også at tilgå reset-benet og endnu et ben - det er sådan at I2C kommunikationen og ICSP-programmeringen kan laves i samme 6 polede stik, efter samme standard som Arduinoens ISCP-stik.
Det sidste der er på RF-modtageren er en indikator-lysdiode, der anvendes til at give en indikation for om der er modtaget en pakke.
I2C RF Modtager hardware
I2C tastaturet er lagt ud ved hjælpe af Eagle og layoutet med en PDF til at lave print efter kan hentes i denne ZIP-fil.
Det samlede diagram ser ud som følger:
Total-diagram over I2C tastatur
Printet kan monteres efter følgende layout:
Komponent-layout over I2C tastatur og et monteret print
Komponentlisten til I2C RF Modtager er som følger:
Komponent | Type | Værdi |
---|---|---|
U1 | MikroController | ATTiny 45 (vendes rigtigt - i sokkel) |
U1 | IC Sokkel | 8 bens IC-sokkel (vendes rigtigt) |
D1 | Småsignal diode | 1N4148 (vendes rigtigt) |
LED1 | Lysdiode | 5mm LED Grøn (vendes rigtigt) |
LED2 | Lysdiode | 5mm LED Gul (vendes rigtigt) |
SV1 | Jumper stik | 2 polet pin-række med Jumper |
SV2 | Pin-header Hun | 4 polet hun stik i stribe, der klippes af 20 poles stribe |
PR1 | Molex stik | 2x3 polet pin-række til Moles fladkabel-stik |
R1 | Modstand | 10k ohm |
R2 | Modstand | 680 ohm |
R3 | Modstand | 680 ohm |
R4 | Modstand | 220k ohm |
R5 | Modstand | 2k2 ohm |
R24 | Modstand | 220k ohm |
C1 | Polyester kondensator | 100nF |
C2 | Elektrolyt kondensator | 10uF (vendes rigtigt) |
H1 - H4 | Monteringshul | 10mm M3 gevindstag med M3 skrue |
Kommunikationsporten til I2C
Som vist på diagrammet indeholder kommunikationsporten PR1 til I2C flere ting, da portbenene anvendes til flere forskellige funktioner.
Stikket er 6-polet selvom det kun er de 4 ben der anvendes til I2C-kommunikationen.
Til I2C har porten følgende funktion:
Ben nr | Signal-navn | Specielle forhold |
---|---|---|
1 | MISO | Anvendes ikke til I2C - skal svæve ved reset |
2 | Vcc (+ 5V) | Forsyning til I2C modulet |
3 | SCK | Serial Clock til I2C |
4 | SDA | Serial Data til I2C |
5 | Reset | Mikrocontrollerens Reset - skal svæve når modulet er i funktion |
6 | GND | Stel-forbindelse til I2C modulet |
PR1 porten kan også anvendes til at programmere Mikrocontrolleren igennem, hvor porten får følgende funktion:
Ben nr | Signal-navn | Specielle forhold |
---|---|---|
1 | MISO | En del af ICSP programmeringen |
2 | Vcc (+ 5V) | Forsyning til I2C modulet |
3 | SCK | En del af ICSP programmeringen |
4 | MOSI | En del af ICSP programmeringen |
5 | Reset | Mikrocontrollerens Reset - Anvendes til at initiere ICSP |
6 | GND | Stel-forbindelse til I2C modulet |
Adresse på I2C tastaturet med Jumper
Ud over dette anvendes ben 1 i PR1 til at angive om det er en lige eller en ulige adresse I2C-RF-modtageren skal reagere på. Dette angives ved hjælp af jumperen SV1, hvor den er trukket høj af R4 når jumperen ikke er monteret og bliver trukket lav gennem R5 når man sætter jumperen på. Niveauet læses ved reset af mikrocontrolleren.
Som det kan ses på billederne til højre, så er det blot en jumper der kan sættes på for at skifte adressen. Når jumperen ikke er på, så bliver adressen (0x28) en højere (0x29), end hvis den er på.
Ideen med dette er at man kan have to ens RF-modtagere på samme I2C, og endda have samme program liggende i dem, men at man kan henvende sig til hvert af dem, uden det giver konflikt.
Yderligere hardware på I2C RF Modtager
Der er yderligere komponeter på I2C RF-modtageren som har en mindre rolle at spille.
C1 og C2 er blot monteret for at fastholde forsyningen, hvis I2C modulet bliver forsynet gennem et længere kabel. Det er for at gøre modulet mindre støjfølsomt.
R1 og D1 er en del af reset-kredsløbet, som ellers håndteres internt i Mikrocontrolleren.
R2 og LED1 er blot en power-indikation der kan være praktisk at anvende i opstillinger hvor man kobler tingene sammen med kabler og løse ledninger. Hvis man ønsker at spare strøm i sin opstilling, så kan man spare 5 mA ved at undlade at montere disse to komponenter.
R3 og LED2 er indikation til hvornår modulet modtager pakker. Det kan være en praktisk indikation, hvis man ikke har en ide om hvad der sker i modulet. Hvis man ønsker at spare strøm i sin opstilling, så kan man spare 5 mA ved at undlade at montere disse to komponenter.
Elektronikken på det færdige RF Modtager-modul
Modtagermodulet består af en diskret opbygget modtager omkring to transistorer og en svingningskreds hvor der indgår en trimmekondensator som er afstemt til den ønskede frekvens. I forstærkningen udnyttes en AGC-teknik der tilpasser forstærkning til styrken af det modtagne signal bedst muligt. Dette betyder at modtagren vil forsøge at genkende et signal i det modtagne, så man uanset styrken næsten altid vil modtage et signal - også selvom der ikke sendes til modtageren. Herefter bliver signalet ensrettet og sammenlignet med et niveau, så modulet giver høj ud når det modtager noget på den ønskede frekvens, og det giver lavt niveau ud når der ikke modtages noget. AGC-kredsløbet betyder at der også vil modtages skift selvom der ikke sendes et signal - dette skal man tage høje for i modtagelsen.
Sammenligningen foretages med en LM358 comperator inde i modulet.
Specifikationer for RF-link Modtager modul
Specifikationerne er sakset fra de hjemmesider det har været muligt at finde, som dokumenterer funktionen.
Parameter | Data |
---|---|
Forsynings spænding | 3V - 8V DC (nom. 5V) |
Krævet forsynings-strøm | < 3mA @ 5V |
Modtage frekvens | 315 / 433 MHz (433 MHz i vores version) |
Frekvens område | 260 - 440 MHz justerbart (undlad dette) |
Modulationsform | ASK / OOK |
RF følsomhed | -105 dBm (50 Ω) |
Data rate | < 5 kbps (315 MHz, -95 dBm) |
Temperatur område | -20 til 70 grader C |
Data output | TTL niveau (0/5V) |
Antenne længde | 24 cm (315 MHz), 18 cm (433,92 MHz) |
Det samlede RF-Link
Til RF-linket anvendes to små hardware-moduler: et sendermodul med en tilhørende software og et modtagermodul med en tilhørende software. De to moduler skal være indstillet til samme frekvens, hvor de viste moduler arbejder på 433 MHz.
Ud over dette skal der etableres en protokol, så de to moduler kan "snakke sammen", og der kan overføres en information fra en enhed til en anden.
Sender-modul
Opbygningen af sendermodulet er en færdig opbygget oscillator, der arbejder på 433 MHz, hvor man styrer ind på en transistor i modulet, og på den måde styrer om oscillatoren skal være tændt eller slukket. Styringen kan gøres f.x. fra en Arduino og skal ske ved hjælp af et databene, hvor det skal have 5V for at sende og 0V for at være slukket.
Ben | Navn | Betydning | Arduino-ben |
---|---|---|---|
1 | GND | Stel | GND |
2 | Vcc | +5V forsyning | +5V eller en højere spænding op til 12 V |
3 | Data | Sende signal - når den er høj, sendes 433 MHz | Valgfrit digitalt signal 0-13 |
Software til RF-link sender modul
Denne side handler om modtageren, og der angives kun det mest nødvendige om senderen for at belyse hvordan modtagelsen foregår.
Den nærmere opbygning af sender-software er lavet i RF-sender.
Afsending ved hjælp af RF-modulet
For at kunne foretage en transmission hvor man sender nogle forskellige værdier hvor RF-send-demo1.ino illustrerer denne anvendelse sammen med RF-I2C-modt-demo1.ino
Modulerne udnytter modulet RF-I2C-link for at etablere
void loop() {
RF_send(0,tal_b);
tal_b++;
delay(5);
RF_send(1,tal_i);
tal_i++;
delay(5);
RF_send(2,tal_l);
tal_l++;
delay(50);
}
Koden er lavet så den sender de 3 variabler som er lavet i forskellig størrelse, hhv. en byte, en int (2 byte) og en long (4 byte), så de forskellige muligheder testes af.
Denne simple sending anvendes til at afteste de forskellige principper for modtagelse.
Første test modtagelse
For at få styr på om der kan modtages noget fornuftigt, så sættes RF-modtageren på en Arduino, hvor den bare kobles på pin 2-5, så pin 2 giver stel og pin 5 giver forsyning. Signalet kan i princippet læses både på pin 3 og 4, men læses bare på pin 3. Opstillingen kan ses her til højre.
Modtagelsen skal ske ved hjælp af tid, og for at kunne få det til at fungere nede ved så korte tider anvendes den indbyggede funktion micros(), der kan aflæse tiden i mikrosekunder siden start af Arduinoen.
I først omgang baseres koden bare på at der loopes hele tiden og at der ikke kaves andet, så man ikke misser noget af en bit.
Da modtageren er sat på en række I/O så skal den forsynes og signalet skal læses på en række pins. Det bliver defineret som følger. Der sættes desuden op til at kommunikere serielt, så vi kan se hvilke resultater vi får ud af det.:
const int ground = 2;
const int rfInput = 3;
const int supply = 5;
void setup() {
Serial.begin(9600);
Serial.write("Init RF receiver");
pinMode(ground, OUTPUT);
pinMode(supply, OUTPUT);
pinMode(rfInput, INPUT);
digitalWrite(ground, LOW);
digitalWrite(supply, HIGH);
}
Til modtagelsen anvendes en række variable som vist her:
byte buffer[16];
byte mode = 0;
byte inputVal;
byte bitPtr = 0;
byte bytePtr = 0;
long last;
long tid;
Modtagelsen bases på mode variablen, der fungerer som en simpel state-machine.
Forklaring af den state-machine der anvendes i første test
State-machinen kan illustreres med følgende state-diagram, der indikerer hvordan de forskellige skift sker hen gennem modtagelsen af pakken.
Statediagram der illustrerer modtagelsen i første version
I mode 0 ventes på den høje marker (starten af pakken).
I mode 1 og 2 kontrolleres at den høje og hhv. den lave marker har den korrekte tid. Dette er etableret med følgende kode:
void loop(){
switch (mode) {
case 0:
if (digitalRead(rfInput)) {
last = micros();
mode = 1;
}
break;
case 1:
if (!digitalRead(rfInput)) {
tid = micros() - last;
last = micros();
if (tid > 450) {
mode = 2;
} else {
mode = 0;
}
}
break;
case 2:
if (digitalRead(rfInput)) {
tid = micros() - last;
last = micros();
if ((tid > 250) && (tid < 350)) {
bitPtr = 0;
bytePtr = 0;
mode = 3;
} else {
mode = 1;
}
}
break;
Mode 3 håndterer den høje del af en bit-modtagelse, hvor den tester om det er ca. 100 us, er der det skiftes til mode 4.
Det mest komplicerede er hvis tiden ligger på ca. 300 us. Det indikerer at pakken er slut. Her testes først om der lige er afsluttet en byte, og hvis der er det, så analyseres bufferen igennem, så alle parametre for om modtagelsen er gået godt passer. Undervejs i analysen sendes serielt ud de vigtige data i modtagelsen, eller hvis noget går galt, så sendes en fejlkode.
Hvis der modtages andre tider, så sættes mode tilbage til 0, så søgningen på en ny pakke kan begynde.
case 3:
if (!digitalRead(rfInput)) {
tid = micros() - last;
last = micros();
if ((tid > 250) && (tid < 350)) {
if (bitPtr == 0) {
byte n = 0;
byte CS = 0;
byte fejl = 0;
do {
CS += buffer[n];
n++;
byte inv = ~buffer[n];
if (buffer[n-1] != inv) {
fejl = n;
} else {
Serial.print(buffer[n-1]);
Serial.print(" ");
}
n++;
} while (n < bytePtr - 1);
if (CS != buffer[n]) {
fejl = 99;
}
Serial.println(fejl);
} else {
Serial.print(bitPtr);
}
mode = 0;
} else if ((tid > 50) && (tid < 150)) {
mode = 4;
} else {
Serial.println(50);
mode = 0;
}
}
break;
I mode 4 modtages den lave del af en bit, og afhængigt af tiden registreres om det er en 0 eller 1 bit der modtages. Dette samles op i inputVal. Når der er samlet 8 bit op i en byte, så gemmes den i bufferen og der gøres klar til at modtage en ny byte. Hvis den modtagne tid ikke passer med enten 100 us eller 200 us, så skiftes også her tilbage til mode 0, så der søges efter en ny start på pakken.
case 4:
if (digitalRead(rfInput)) {
tid = micros() - last;
last = micros();
bitPtr++;
if ((tid > 150) && (tid < 250)) {
inputVal <<= 1;
inputVal++;
mode = 3;
} else if ((tid > 50) && (tid < 150)) {
inputVal <<= 1;
mode = 3;
} else {
mode = 1;
}
if (bitPtr == 8) {
bitPtr = 0;
buffer[bytePtr] = inputVal;
bytePtr++;
}
}
break;
}
}
Test af den første version af modtagelse
På den serielle monitor kan man se hvordan modtagelsen af pakker forløber. En visning kan se ud som følger:
40 0 162 0 40 1 138 164 0 40 2 226 226 15 0 0 40 0 163 0 40 1 139 164 0 40 0 164 0 40 2 228 226 15 0 0 50 50 40 2 229 226 15 0 0 40 0 166 0 40 1 142 164 0 40 2 230 226 15 0 0 40 0 167 0 40 1 143 164 0 40 2 231 226 15 0 0
Umiddelbart var det svært at få modtagelsen til at fejle, men ved at forstyrre med anden transmission ind over (en bilnøgle), så kunne man forstyrre så der kom en fejl - her indikeret med fejl 50 to gange i træk.
De fejltyper der kan detekteres kommer ud som følgende tal:
- 0 Ingen fejl
- 1-11 En fejl hvor den modtagne byte ikke modtages som den inverterede
- 21-27 Afslutning af en pakke, hvor det ikke passer med et helt antal bytes (sidste byte er ikke 8 bit)
- 40 Forkert tid i lav (ikke 100 us eller 200 us)
- 50 Forkert tid i høj (ikke 100 us)
- 99 Checksum passer ikke med summering af bytes
Der udskrives ikke fejl, hvis det er tidsfejl i starten af pakken. Disse fejl vil blot blive ignoreret.
Man vil kunne observere at nogle pakker fejler, hvilket er en del af konceptet, da der blot sendes uden nogen form for feedback, og derfor heller ikke nogen mulighed for fejlkontrol eller korrektion af fejl, så kommunikationen kan ikke anvendes i en kommunikation hvor der skal sikres data-overførsel for alle data der sendes.
Det bør altså være opbygget således at man sender sine variabler gentagene gange, og så bruger man dem kun når de er korrekt modtaget.
Refleksioner over første version af modtageren
Den første version af modtageren illustrerer at det sendte signal kan modtages med ret stor sikkerhed (over korte afstande), men er ikke testet yderliger.
Softwaren er lavet, så den måler tiden i hoved-loopet, så det vil være meget tidkritisk at bygge den sammen med andre aktiviteter der skal afvikles i softwaren. Tidsmæssige variationer ned omkring bit-tiden på 100 us vil kunne forstyrre modtagelsen lavet ud fra dette princip.
Anden test-modtagelse
Til den anden test anvendes stadig den samme hardware som i den første test.
Forskellen er at der anvendes kanttrigget interrupt til at fange inputtet med, og hele tolkningen og opsamlingen af pakken ligger i interrupt, mens udskriften ligger i loopet uden for interruptet.
Lige som sidst laves nogenlunde den samme initialisering i setup():
const int ground = 2;
const int rxPin = 3; // the number of the RF RX-pin
const int supply = 5;
const int highTime = 100;
const int lowZeroTime = 100;
const int lowOneTime = 200;
void setup() {
Serial.begin(9600);
Serial.write("Init RF receiver");
pinMode(ground, OUTPUT);
pinMode(supply, OUTPUT);
pinMode(rxPin, INPUT);
digitalWrite(ground, LOW);
digitalWrite(supply, HIGH);
attachInterrupt(1, kant, CHANGE);
}
Den store forskel er at der sættes en interrupt-rutine der hedder kant op med attachInterrupt til pin 3 (interrupt 1) og det skal reagere på skift i niveauet (CHANGE).
Desuden angives tiderne som konstanter.
Fordi det skal fungere i interrupt anvendes en del flere variabler, og der anvendes et lidt andet princip.
byte CS;
byte tal = 0;
byte inByte = 0;
byte inCount = 0;
byte byteCount = 1;
byte inMirror = 0;
byte add;
byte address;
byte inData[4];
byte inNr;
boolean inPackage;
boolean packageOK = false;
long lastMicros;
long thisMicros;
int tid;
Forklaring af interruptrutinen
Det centrale i modtagelsen af pakken her er interrupt-rutinen, og da den altid kaldes efter et skift af signalet, så ved vi at vi kan registrere hvor lang tid der er gået siden sidste kant - det gøres først i rutinen:
void kant() {
thisMicros = micros();
tid = thisMicros - lastMicros;
lastMicros = thisMicros;
Herefter forholder vi os til om vi er begyndt at modtage pakken, og hvis vi ikke er det, så kigger vi efter de to specielle tider - en høj på mere end 450 us og umiddelbart derefter en lav på ca. 300 us.
Dette kan betragtes på følgende måde, hvor vi bliver uden for modtagelsen af pakken, indtil vi har modtaget de korrekte tider.
Tilstandene, når vi ikke er i gang med at modtage en pakke
Det gøres ved at byteCount står på 1 når vi ikke er inde i pakken, og hvis vi modtager en høj puls der er lang nok, så sættes byteCount til 0, så vi ved den lige har været der, modtages der så en lav på ca. 300 us, så startes modtagelsen af selve pakkens data, ellers sættes byteCount tilbage til 1.
if (! inPackage) {
if (digitalRead(rxPin)) {
if ((tid > 250) && (tid < 350) && (byteCount == 0)) {
inPackage = true;
packageOK = false;
inCount = 0;
CS = 0;
} else {
byteCount = 1;
}
} else {
if (tid > 450) {
byteCount = 0;
}
}
Når vi er i gang med at modtage en pakke, så kan selve modtagelsen betragtes på følgende måde - der ligger selvfølgelig mere i det, men den grundlæggende tankegang er som illustreret her.
Illustration af skiftene når vi modtager en pakke
Hvis vi er i gang med at modtage pakken, så behandles først de lave signaler (der angiver om det er 0 eller 1 der modtages).
Der indikeres først at det er en forkert tid, og hvis ikke tiden enten er ca. 100 us (svarer til en 0-bit) eller 200 us (svarer til en 1-bit), så vil der registreres en fejl, og vi starter forfra med at vi ikke har modtaget noget, så vi kan lede efter starten af en pakke igen.
} else { // Vi er i gang med at modtage en pakke
if (digitalRead(rxPin)) {
add = 2;
if (tid > (lowZeroTime - 40) && tid < (lowZeroTime + 40)) {
add = 0;
} else if (tid > (lowOneTime - 40) && tid < (lowOneTime + 40)) {
add = 1;
}
if (add == 2) {
inPackage = false;
byteCount = 1;
Hvis modtagelsen af en bit gik godt, så skelnes imellem om der er talt under 8 eller 16 bit, hvor den modtagen bit lægges i hhv. inByte eller inMirror, hvor inMirror gerne skulle være den inverterede byte af inByte.
} else {
if (inCount < 8) {
inByte <<= 1;
inCount++;
inByte += add;
} else {
inMirror <<= 1;
inCount++;
inMirror += add;
Når begge bytes er modtaget, så inverteres (bit-mæssigt) den modtagne byte der er sendt inverteret, så de to inverteringer skulle gerne give den oprindelige byte, som er modtaget som inByte, det testes der for, og hvis en af dem er modtaget forkert, så vil de være forskellige, og der indikeres at det modtagne er forkert, ved at der ledes efter en ny pakke.
Hvis byten er OK, så vil den først modtagne byte være adressen, den næste nummeret og resten vil være data, hvor det lagres i de respektive variabler.
if (inCount == 16) {
inMirror = ~inMirror;
if (inByte == inMirror) {
if (byteCount == 0) {
address = inByte;
} else if (byteCount == 1) {
inNr = inByte;
} else if (byteCount >= 2) {
inData[byteCount - 2] = inByte;
}
CS += inByte;
byteCount += 1;
} else {
inPackage = false;
byteCount = 1;
}
inCount = 0;
}
}
}
Hvis det ikke er den lave periode af bitten der modtages, så må det være den høje, som ved alle databit skal være ca. 100 us. Hvis den i stedet er ca. 300 us, så betegner det afslutningen af en pakke, dog forudsat at der er modtaget netop 8 bit, som er lagret i inByte. Denne modtagne byte skal svare til den simple sammentælling af modtagne bytes der er foretaget i CS, og hvis det er tilfældet, så godkendes hele pakken ved at packageOK sættes true. Uanset om pakken er godkendt eller ej, så vil en lang høj puls stoppe modtagelsen af pakken.
} else {
if (tid > (highTime - 50) && tid < (highTime + 50)) {
// pause
} else if ((tid > 250) && (tid < 350)) {
if (inCount == 8) {
if (inByte == CS) {
packageOK = true;
}
} else {
}
inPackage = false;
byteCount = 1;
Hvis tiden er andet en det forventede, så stoppes modtagelsen også, og hermed er kant-interruptet behandlet.
} else {
inPackage = false;
byteCount = 1;
}
}
}
}
Hvis alle tjek på den modtagne pakke kommer gennem nåleøjet skulle variablen packageOK gerne være true.
Main-loopet i softwaren, der viser resultatet
I loopet laver vi ikke andet end at kigge efter denne variabel, og når den bliver true, så printes indholdet af pakken som vist her:
void loop(){
if (packageOK) {
Serial.print(address);
Serial.print(" ");
Serial.print(inNr);
if (inNr == 0) inByte = 1;
if (inNr == 1) inByte = 2;
if (inNr == 2) inByte = 4;
Serial.print(" ");
for (tal = 0; tal < inByte; tal++) {
Serial.print(inData[tal]);
Serial.print(" ");
}
Serial.println(CS);
packageOK = false;
}
delay(5); // Større delay giver fejl
}
Hvis denne software skal anvendes, så er det vigtigt at der tjekke mindst lige så hurtigt som der sendes pakker, ellers kan man risikere at miste nogen - det kan man faktisk alligevel, hvis der er en fejl i modtagelsen.
Test af anden version af modtagelsen
Ved testen af modtagelsen viste det sig at langt de fleste pakker kom fint igennem som vist her:
40 0 88 128 40 1 64 121 226 40 2 152 183 15 0 136 40 0 89 129 40 1 65 121 227 40 2 153 183 15 0 137 40 0 90 130 40 1 66 121 228 40 2 154 183 15 0 138 40 0 91 131 40 1 67 121 229 40 2 155 183 15 0 139 40 0 92 132 40 1 68 121 230 40 2 156 183 15 0 140
Refleksioner over anden version af modtageren
Den anden version af modtageren illustrerer at det sendte signal kan modtages med ret stor sikkerhed (over korte afstande), men er ikke testet yderligere.
Softwaren er lavet, så den måler tiden i en interrupt-rutine, så det er mindre tidkritisk end den første version. Det vil dog stadig stille krav til softwaren at bygge den sammen med andre aktiviteter der skal afvikles. Tidsmæssige variationer vil kunne forstyrre modtagelsen lavet ud fra dette princip, men det bliver afhængigt af hvor tit senderen fyrer pakker afsted. Man skal her sikre sig at der bliver kigget på packageOK i hvert fald hurtigere end længden af pausen mellem de sendte pakker, ellers vil man både kunne miste pakker, men det vil også kunne virke forstyrrende på modtagelsen.
Hvis man kunne leve med disse krav, så ville dette princip kunne anvendes, og modtageren kunne lægges i et software modul, så det på enkelt vis kunne anvendes sammen med anden software. Dette er ikke realiseret, da det der er formålet her er at lave et selvstændigt hardware-modul med endnu større fleksibilitet.
I2C-modulets software
Der kommunikeres med I2C modtagermodulet på adressen 0x28, og modulet er som standard sat op til at kunne modtage 16 variabler med hver 2 byte indhold, så hvis man ikke ændrer på disse størrelser så kan modulet hente ud fra følgende kommandostruktur:
Kommando | antal pakker | modtag 1 | modtag 2 |
---|---|---|---|
0 | antal pakker med variabel 0 siden sidst | mindst betydende byte i variabel 0 | mest betydende byte i variabel 0 |
1 | antal pakker med variabel 1 siden sidst | mindst betydende byte i variabel 1 | mest betydende byte i variabel 1 |
2 | antal pakker med variabel 2 siden sidst | mindst betydende byte i variabel 2 | mest betydende byte i variabel 2 |
. | . | . | . |
. | . | . | . |
15 | antal pakker med variabel 15 siden sidst | mindst betydende byte i variabel 15 | mest betydende byte i variabel 15 |
Alle variabler i modulet kan indstilles til at modtage forskelligt antal bytes via I2C kommunikationen. Som standard er alle variabler sat til 2 bytes, men kan valgfrit sættes til 1, 2 eller 4. Variabelnummeret i I2C kommunikationen tillægges 128 og der sendes antallet, så man har følgende kommandoer til at indstille antal med:
Kommando | antal |
---|---|
128 | antal byte i variabel 0 |
129 | antal byte i variabel 1 |
130 | antal byte i variabel 2 |
. | . |
. | . |
143 | antal byte i variabel 15 |
Dette antal gemmes i EEprom, og læses når I2C-RF modtageren startes. Dette gør at masteren ikke hele tiden skal sikre sig at antallet er korrekt, hvor I2C-RF modtageren har genstartet.
Referencer