Wie funktionieren Funktionen außerhalb der Void-Schleife?

9

Ich bin an Arduino-Skizzen mit einem void setup()Teil gewöhnt , der einmal ausgeführt wird, und einem void loop()Teil, der sich immer wieder wiederholt . Was passiert, wenn Sie ungültige Funktionen außerhalb der Hauptfunktion haben void loop()? Werden diese alle parallel weiterlaufen oder laufen sie nacheinander ab? Oder werden bestimmte Void-Funktionen erst ausgeführt, wenn bestimmte Kriterien erfüllt wurden (z. B. eine while-Schleife)?

Wann werden beispielsweise im folgenden Code void receiveData(int byteCount)die void sendData()Funktionen und ausgeführt?

//I2C_test

//This code demonstrates communication via an I2C bus between a raspberry pi and an arduino.
//When the Raspberry pi (master) sends data to the Arduino (slave), the Arduino uses this
//data to control a motor. After the Arduino has recieved data from the master, it then collects
//data from the external environment via a sensor and sends this data back to the Raspberry pi.

#include <Wire.h>
int number = 0; //Declare variables
int val = 0;

void setup() {
  //Anything between the curly brackets runs once when the arduino is turned on or reset
  pinMode(0, INPUT);
  //Set pin 0 as input and 3 as output
  pinMode(3, OUTPUT);
  Serial.begin(9600);
  //Set the data rate for serial transmission at 9600bps
  Wire.begin(0x04);
  //Initiate the Wire library, join the Arduino as a slave, and specify its 7 bit slave address
  Wire.onReceive(receiveData);
  //Define callbacks for i2c communication
  Wire.onRequest(sendData);
}

void loop() {
  //The code between the curly brackets keeps repeating
  delay(100);
}

void receiveData(int byteCount) {
  while(Wire.available()) {
    number = Wire.read();
    //Set the variable "number" to the data sent by the master
    analogWrite(3, number);
    //Write this number to pin 3 (PWM). This controls the motor speed
  }
  val = analogRead(0);
  //Read the voltage on pin 0 (connected to the sensor). Map input voltages between 0 and 5 volts into integer values between 0 and 1023
}

void sendData() {
  Wire.write(val);
  //Send the data read from the sensor to the master.
}
Blue7
quelle
Das sieht interessant aus. Ich frage mich, ob Sie Links zur Quelle des Codes (und Details zu Verbindungen zwischen Arduino und Pi) posten könnten.
Milliways
1
@ Milliways Ich habe dieses * Tutorial verwendet, um den Code auf dem Arduino Uno und Raspberry Pi (B + Modell) zu schreiben, aber ich habe ein paar kleine Änderungen vorgenommen. Verbinden Sie die SDA- und SCL-Pins der beiden Boards sowie die Erdungsstifte, falls vorhanden sind an verschiedene Netzteile angeschlossen. Ich hatte dann Pin 3 mit einem Sensor verbunden, der in einer Potentialteilerkonfiguration konfiguriert war und zwischen den + 5V- und Gnd-Pins angeschlossen war. Pin 0 und Gnd sind mit einer Motorantriebsplatine verbunden.
Blue7

Antworten:

11

Die setup()und loop()Funktionen sind ungewöhnlich, da sie vom Arduino-Code automatisch für Sie aufgerufen werden. Keine anderen Funktionen verhalten sich so.

Im Allgemeinen wird eine Funktion nur ausgeführt, wenn Sie sie explizit selbst aufrufen (z. B. von innen setup()oder loop()) oder einen anderen Teil des Programms anweisen, sie aufzurufen. (Es gibt andere Möglichkeiten, Funktionen auszuführen, aber dies beinhaltet normalerweise einige sehr fortgeschrittene Bastelarbeiten, die am besten vermieden werden.)

Zum Beispiel pinMode()ist eine Funktion wie jede andere. Es wird nur ausgeführt, wenn Sie tatsächlich so etwas wie pinMode(3, INPUT)in Ihren Code eingefügt haben. Zu diesem Zeitpunkt wird es einmal ausgeführt, beendet und dann wird die aufrufende Funktion dort fortgesetzt, wo sie aufgehört hat (sie werden nie parallel ausgeführt).

Der von Ihnen gepostete Beispielcode ist sehr interessant. Schauen Sie sich diese Zeilen an in setup():

Wire.onReceive(receiveData);
Wire.onRequest(sendData);

Diese Zeilen erzählen die WireObjekt aufrufen receiveData()und sendData()als Reaktion auf I2C Ereignisse. Dazu werden Funktionszeiger übergeben, die von gespeichert und verwendet werden Wire.

Ich würde empfehlen, online nach Informationen zu C / C ++ - Funktionszeigern zu suchen, wenn Sie mehr darüber erfahren möchten. Sie könnten auch interessiert sein, die attachInterrupt()Funktion von Arduino zu erkunden .

Peter Bloomfield
quelle
Danke für deine Antwort. Das macht jetzt mehr Sinn. Wenn die Funktionen receiveData()und sendData()jedoch nur ausgeführt werden, wenn sie aufgerufen werden, warum werden sie dann innerhalb der void setup()Funktion und nicht innerhalb der Hauptfunktion aufgerufen void loop()? Sicherlich werden diese Funktionen niemals aufgerufen, es sei denn, es besteht die seltene Möglichkeit, dass ein i2c-Ereignis vorliegt, während sich der Befehlszeiger noch in der void setupFunktion befindet. Wäre es nicht besser, diese Funktionen innerhalb der void loopFunktion aufzurufen, damit die Funktion immer dann aufgerufen wird, wenn ein i2c-Ereignis vorliegt?
Blue7
4
Diese Funktionen @ Blue7 werden nicht genannt in void setup(), sie werden als Parameter übergeben von onReceiveund onRequest, sie sind Rückrufe als Kommentar Staaten. In einer sehr kurzen Zusammenfassung: Dies weist die (Code aus der) Wire-Bibliothek an, diese Funktionen aufzurufen , wenn bestimmte Dinge passieren ( arduino.cc/en/Reference/WireOnReceive , arduino.cc/en/Reference/WireOnRequest ...)
FredP
@FredP Ah okay. Vielen Dank für die Links, ich werde sie überprüfen, wenn ich nicht auf meinem Handy bin. In der Zwischenzeit habe ich jedoch eine kurze Frage, wenn Sie nichts dagegen haben. Sind diese Rückrufe immer bereit und warten auf ein i2c-Ereignis? Unabhängig davon, wo sich der Befehlszeiger befindet, rufen diese Rückrufe die Funktion sofort auf, sobald ein i2c-Ereignis eintritt.
Blue7
1
@ Blue7 Vermutlich werden Interrupts zur Überwachung der I2C-Aktivität verwendet. Wenn ein Interrupt ausgeführt wird, entzieht er dem Hauptprogramm vorübergehend die Kontrolle.
Peter Bloomfield
3
@ Blue7 Die Rückrufe warten nicht (Arduino ist nicht multithreaded), wie @PeterRBloomfield sagt, aktiviert die Wire-Bibliothek den I2C-Interrupt durch, twi_init()wenn Sie anrufen Wire.begin. Wenn es eine I2C-Aktivität gibt, beendet der µC seine aktuelle Aufgabe (es sei denn ... vergiss es im Moment :-) und geht in den Code der Wire-Bibliothek, der dann die (je nach dem, was gerade passiert) Funktion aufruft, als die Sie sich registriert haben Rückruf ( receiveDatazum Beispiel). Ein Rückruf ist der generische Name für Funktionen wie receiveDataoder sendData, die von einem Interrupt-Handler in Wire aufgerufen werden .
FredP
2

Wird dieser Fall nicht setup()einmal aufgerufen und loop()wiederholt aufgerufen? dh dass es ein Unsichtbares gibt, main() das so aussehen könnte:

void main(){
  setup();
  while(True){
    loop();
  }
}

Entschuldigung, ich schaue nur in das Arduino und habe fast keine C / C ++ - Erfahrung. Ich versuche, diese loop()Situation selbst in den Griff zu bekommen .

Dee
quelle
Grundsätzlich ja. Es gibt auch einen Anruf zu init()dem wird der Timer für den Gang millis, delayetc. Also init()für die allgemeine Initialisierung ist, setup()ist für Ihre Initialisierung und loopist für, na ja, Looping. Sie können Ihre eigenen schreiben, mainwenn Sie die volle Kontrolle übernehmen möchten.
Nick Gammon
Netter Post. Übrigens ;ist nach dem vorletzten nicht erforderlich }:-)
Greenonline
Es gibt auch einen Aufruf von serial_event (), nicht wahr?
Divisadero
2

Ich kann Dees Antwort nicht kommentieren. Der eigentliche Code, der in der Hauptschleife ausgeführt wird, ist hier :

    int main(void) {
    init();
    initVariant();

    setup();

    for (;;) {
        loop();
        if (serialEventRun) serialEventRun();
    }   
    return 0;
}

Und ja, setup()wird einmal angerufen und loop()wiederholt aufgerufen (zusammen mit einigen seriellen Sachen).

Petrus
quelle
0

Es funktioniert wie eine normale Funktion, es muss aufgerufen werden, um einen Sinn zu ergeben. loop () / setup () werden von einer main () -Funktion aufgerufen, die aus dem Arduino-Verzeichnis kompiliert und darin verknüpft ist. receiveData / sendData werden von Ihrem Programm aufgerufen, dessen Root sich in loop / setup-Funktionen befindet.

TMa
quelle