AdSense

Samstag, 27. Juli 2013

Atmega/Arduino: Funkmodul nRF24L01+ mit Interrupts

(Englich version) In meinem letzten Beitrag habe ich die Grundlagen zu den nRF24L01+ bzw. nRF24L01P-Modulen erläutert. Wie bereits am Ende des Beitrags erwähnt war die Umsetzung des Codes (vorallem beim Epfänger) nicht optimal. Der Mikrocontroller lief dauerhaft in einer Endlosschleife die nichts anderes tat, als zu prüfen, ob den nun endlich wieder neue Daten empfangen wurden. In der Praxis ist das natürlich eher unpraktisch, der µC soll schließlich auch noch andere Aufgaben übernehmen.

Genau dafür wurden die Interrupts erfunden ;) Im letzten Beitrag habe ich bereits die Interrupt-Funktionen der Funkmodule angesprochen:
"IRQ ist der Interrupt-Pin des Moduls und kann bei 3 verschiedenen Events active low werden: Daten wurden empfangen, Daten wurden gesendet, Senden fehlgeschlagen. Diese Interrupts lassen sich jedoch frei ein- und ausschalten."
Die Interrupts werden über das Register CONFIG gesteuert. Über das Bit 4 steuert man den Interrupt bei fehlgeschlagenem Senden, Bit 5 schaltet den Interrupt bei erfolgreichem Senden und Bit 6 den beim Empfang von Daten. Wird das Bit auf 1 gesetzt ist der jeweilige Interrupt deaktiviert, steht das Bit  auf 0 wird der IRQ-Pin beim Auftreten des entsprechenden Ereignisses auf LOW gesetzt. Die passende Verkabelung habe ich schon beim Aufbau im letzten Beitrag vorgenommen. Der IRQ-Pin muss mit einem der Interrupt-Pins des Mikrocontrollers verbunden werden.

Dies habe ich nun in Code umgesetzt. Die Software auf der Sender-Seite (also auf dem Arduino) wurde nicht geändert. Nur auf der Emfpänger-(Atmega8-)Seite gab es eine kleine Änderung:

#include <SPI.h>
#include <Mirf.h>
#include <nRF24L01.h>
#include <MirfHardwareSpiDriver.h>

void setup(){
  
  //Interrupt initialisieren
  attachInterrupt(0, receive, LOW);
   
  //Funkmodul initialisieren
  Mirf.spi = &MirfHardwareSpi;
  Mirf.init();
  Mirf.setRADDR((byte *)"serv1");
  Mirf.payload = sizeof(unsigned long);
  
  //auf 250kbit/s umstellen (0x26) (2Mbit => 0x0f)
  Mirf.configRegister(RF_SETUP, 0x26);
  
  //Interrupt nur wenn Daten empfangen
  Mirf.configRegister(CONFIG, 0x48);
  
  Mirf.config();

}

void loop(){ 
  //Hier passieren epische Dinge
}

void receive() {
   
  noInterrupts();
  byte data[Mirf.payload];
    
  if(!Mirf.isSending() && Mirf.dataReady()){
    Mirf.getData(data);
    Mirf.setTADDR((byte *)"clie1");
    Mirf.send(data);
  }
  
  interrupts();
  
}

Die setup()-Funktion ist weitestgehend identisch mit der des ursprünglichen Programms Es gibt nur zwei kleine Änderungen: die Funktion attachInterrupt(0, receive, LOW) aktiviert den Interrupt. Wird der Interrupt 0 (Pin 4 beim Atmega8) auf LOW gezogen, dann wird die Funktion receive() aufgerufen.
Die zweite Änderung ist der Befehlt Mirf.configRegister(CONFIG, 0x48). Damit wird das CONFIG-Register so eingestellt, dass nur beim Empfang von Daten ein Interrupt ausgelöst wird.

Das Hauptprogramm (void loop()) ist komplett leer, hier kann jetzt beliebiger Code stehen.

Die Funktion receive() wird bei einem Interrupt aufgerufen und beinhaltet den Code, der zuvor in der loop()-Funktion enthalten war. Auch hier gibt es nur zwei kleine Erweiterungen: der Befehl noInterrupts() deaktiviert zu Beginn der Funktion die Interrupts, um "Überlagerungen" der Interrupts zu vermeiden. Am Ende der Funktion werden sie mit interrupts() wieder aktiviert.

Keine Kommentare:

Kommentar veröffentlichen