Comment fonctionnent les fonctions en dehors de la boucle vide?

9

Je suis habitué aux croquis Arduino avec une void setup()partie qui s'exécute une fois et une void loop()partie qui continue à boucler. Que se passe-t-il lorsque vous avez des fonctions d'annulation en dehors du principal void loop()? Est-ce que tous continueront à boucler en parallèle ou fonctionneront-ils l'un après l'autre? Ou certaines fonctions void ne s'exécutent-elles qu'une fois certains critères remplis (comme une boucle while)?

Par exemple, dans le code ci-dessous, quand void receiveData(int byteCount)les void sendData()fonctions et s'exécuteront-elles?

//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
la source
Cela semble intéressant. Je me demande si vous pourriez poster des liens vers la source du code (et les détails des connexions entre Arduino et Pi).
Milliways
1
@Milliways J'ai utilisé ce * tutoriel pour écrire le code sur l'arduino uno et le Raspberry pi (modèle B +), mais j'ai fait quelques petites modifications: connectez les broches SDA et SCL des deux cartes, ainsi que les broches de terre si elles sont connectés à différentes alimentations. J'ai ensuite branché la broche 3 à un capteur configuré dans une configuration de diviseur de potentiel, connecté entre les broches + 5V et Gnd. La broche 0 et Gnd sont connectés à une carte de commande de moteur.
Blue7

Réponses:

11

Les fonctions setup()et loop()sont inhabituelles car elles sont appelées automatiquement pour vous par le code Arduino. Aucune autre fonction ne se comporte de cette façon.

D'une manière générale, une fonction ne s'exécutera jamais à moins que vous ne l'appeliez explicitement vous-même (par exemple depuis setup()ou loop()) ou que vous demandiez à une autre partie du programme de l'appeler. (Il existe d'autres façons d'exécuter des fonctions, mais cela implique généralement un bricolage très avancé qu'il vaut mieux éviter.)

Par exemple, pinMode()est une fonction comme les autres. Il ne s'exécute que lorsque vous mettez quelque chose comme pinMode(3, INPUT)dans votre code. À ce stade, il s'exécute une fois, se termine, puis la fonction appelante continue là où elle s'était arrêtée (elles ne s'exécutent jamais en parallèle).

L'exemple de code que vous avez publié est assez intéressant. Regardez ces lignes dans setup():

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

Ces lignes indiquent à l' Wireobjet d'appeler receiveData()et sendData()en réponse aux événements I2C. Il le fait en passant des pointeurs de fonction qui sont stockés et utilisés par Wire.

Je vous recommande de rechercher des informations sur les pointeurs de fonction C / C ++ en ligne si vous souhaitez en savoir plus. Vous pouvez également être intéressé à explorer la attachInterrupt()fonction d'Arduino .

Peter Bloomfield
la source
Merci pour votre réponse. Cela commence à avoir plus de sens maintenant. Cependant, si les fonctions receiveData()et sendData()ne sont exécutées que si elles sont appelées, pourquoi sont-elles appelées dans la void setup()fonction et non dans la void loop()fonction principale ? Sûrement ces fonctions ne seront jamais appelées à moins que les rares chances qu'il y ait un événement i2c alors que le pointeur d'instruction est toujours dans la void setupfonction? Ne serait-il pas préférable d'appeler ces fonctions à partir de la void loopfonction, donc chaque fois qu'il y a un événement i2c, la fonction est appelée?
Blue7
4
@ Blue7 Ces fonctions ne sont pas appelées à void setup(), ils sont passés comme paramètre de onReceiveet onRequest, ils sont callbacks comme les états de commentaires. En résumé: ceci indique au (code de la) bibliothèque Wire d'appeler ces fonctions lorsque des choses spécifiques se produisent ( arduino.cc/en/Reference/WireOnReceive , arduino.cc/en/Reference/WireOnRequest ...)
FredP
@FredP Ah d'accord. Merci pour les liens, je les vérifierai lorsque je ne serai pas sur mon téléphone. J'ai une petite question en attendant, si cela ne vous dérange pas. Ces rappels sont-ils toujours prêts et en attente d'un événement i2c? c'est-à-dire, peu importe où se trouve le pointeur d'instruction, ces rappels appellent instantanément la fonction dès qu'un événement i2c se produit?
Blue7
1
@ Blue7 Il utilisera vraisemblablement des interruptions pour surveiller l'activité I2C. Lorsqu'une interruption s'exécute, elle prend temporairement le contrôle du programme principal.
Peter Bloomfield
3
@ Blue7 Les rappels n'attendent pas (Arduino n'est pas multithread), comme @PeterRBloomfield le dit, la bibliothèque Wire permet l'interruption I2C twi_init()lorsque vous appelez Wire.begin. Quand il y a une activité I2C, le µC cesse de faire sa tâche actuelle (à moins que ... pour le moment :-) et passe dans le code de la bibliothèque Wire, qui appelle ensuite la fonction (appropriée, selon ce qui se passe) que vous avez enregistrée en tant que rappel ( receiveDatapar exemple). Un rappel est le nom générique de fonctions comme receiveDataou sendData, elles sont appelées par un gestionnaire d'interruption à l' intérieur de Wire.
FredP
2

N'est-ce pas ce cas qui setup()est appelé une fois et qui loop()est appelé à plusieurs reprises? c'est-à-dire qu'il y a un invisible main() qui pourrait ressembler à ceci:

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

Toutes mes excuses car je ne fais que regarder l'Arduino et je n'ai pratiquement aucune expérience en C / C ++; J'essaie de maîtriser cette loop()situation moi-même.

Dee
la source
Fondamentalement, oui. Il y a aussi un appel init()qui déclenche les temporisations millis, delayetc. Il en init()va de même pour l'initialisation générale, setup()pour votre initialisation et looppour, eh bien, pour la boucle. Vous pouvez écrire le vôtre mainsi vous voulez prendre le contrôle total.
Nick Gammon
Belle publication. BTW ;n'est pas requis après l'avant-dernier }:-)
Greenonline
Il y a aussi appel de serial_event () n'est-ce pas?
Divisadero
2

Je ne peux pas commenter la réponse de Dee. Le code réel qui est exécuté dans la boucle principale est ici :

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

    setup();

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

Et oui, setup()est appelé une fois et loop()est appelé à plusieurs reprises (avec quelques trucs en série).

Petrus
la source
0

Cela fonctionne comme une fonction normale, il faut l'appeler pour avoir du sens. loop () / setup () sont appelés à partir d'une fonction main () qui est compilée à partir du répertoire Arduino et liée. receiveData / sendData sont appelés à partir de votre programme dont la racine est dans les fonctions loop / setup.

TMa
la source