¿Cómo funcionan las funciones fuera del bucle vacío?

9

Estoy acostumbrado a los bocetos de Arduino con una void setup()parte que se ejecuta una vez y una void loop()parte que sigue en bucle. ¿Qué sucede cuando tienes funciones anuladas fuera de la principal void loop()? ¿Seguirán todos en bucle en paralelo o se ejecutarán uno tras otro? ¿O ciertas funciones nulas solo se ejecutan una vez que se cumplen ciertos criterios (como un ciclo while)?

Por ejemplo, en el siguiente código, ¿cuándo se ejecutarán void receiveData(int byteCount)las void sendData()funciones y?

//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.
}
Azul7
fuente
Esto se ve interesante. Me pregunto si podría publicar enlaces a la fuente del código (y detalles de las conexiones entre Arduino y Pi).
Milliways
1
@Milliways Utilicé este * tutorial para escribir el código en el arduino uno y Raspberry pi (modelo B +), sin embargo, hice algunos pequeños cambios. Conecte los pines SDA y SCL de los dos tableros, así como los pines de tierra si están conectados a diferentes fuentes de alimentación. Luego tuve el pin 3 conectado a un sensor configurado en una configuración de divisor potencial, conectado entre los pines + 5V y Gnd. Pin 0 y Gnd está conectado a una placa de accionamiento del motor.
Azul7

Respuestas:

11

Las funciones setup()y loop()son inusuales porque el código Arduino las llama automáticamente. Ninguna otra función se comporta de esta manera.

En términos generales, una función nunca se ejecutará a menos que la llame explícitamente usted mismo (por ejemplo, desde adentro setup()o loop()), o le indique a otra parte del programa que la llame. (Hay otras formas de ejecutar funciones, pero eso generalmente implica algunos retoques muy avanzados que es mejor evitar).

Por ejemplo, pinMode()es una función como cualquier otra. Solo se ejecuta cuando realmente pones algo como pinMode(3, INPUT)en tu código. En ese punto, se ejecuta una vez, finaliza y luego la función de llamada continúa desde donde se quedó (nunca se ejecuta en paralelo).

El código de ejemplo que has publicado es bastante interesante. Mira estas líneas en setup():

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

Estas líneas le dicen al Wireobjeto que llame receiveData()y sendData()en respuesta a eventos I2C. Lo hace pasando punteros de función que son almacenados y utilizados por Wire.

Recomiendo buscar información sobre punteros de función C / C ++ en línea si desea obtener más información al respecto. También te puede interesar explorar la attachInterrupt()función de Arduino .

Peter Bloomfield
fuente
Gracias por tu respuesta. Esto está empezando a tener más sentido ahora. Sin embargo, si las funciones receiveData()y sendData()no se ejecutan a menos que se invoquen, ¿por qué se invocan dentro de la void setup()función y no en la void loop()función principal ? ¿Seguramente estas funciones nunca se llamarán a menos que exista la rara posibilidad de que haya un evento i2c mientras el puntero de instrucción todavía está dentro de la void setupfunción? ¿No sería mejor llamar a estas funciones desde dentro de la void loopfunción para que cada vez que haya un evento i2c, se llame a la función?
Azul7
44
@ Blue7 Estas funciones no están denominados en void setup(), se pasan como parámetro de onReceivey onRequest, son devoluciones de llamada como dice el comentario. En resumen muy breve: esto le dice al (código de) la biblioteca Wire que llame a estas funciones cuando suceden cosas específicas ( arduino.cc/en/Reference/WireOnReceive , arduino.cc/en/Reference/WireOnRequest ...)
FredP
@ FredP Ah, está bien. Gracias por los enlaces, los revisaré cuando no esté en mi teléfono. Mientras tanto, tengo una pregunta rápida, si no te importa. ¿Estas devoluciones de llamada siempre están listas y esperando un evento i2c? es decir, sin importar dónde se encuentre el puntero de instrucción, estas devoluciones de llamada llamarán instantáneamente a la función tan pronto como ocurra un evento i2c.
Azul7
1
@ Blue7 Presumiblemente usará interrupciones para monitorear la actividad de I2C. Cuando se ejecuta una interrupción, quita el control del programa principal temporalmente.
Peter Bloomfield
3
@ Blue7 Las devoluciones de llamada no están esperando (Arduino no es multiproceso), como dice @PeterRBloomfield, la biblioteca Wire permite la interrupción de I2C twi_init()cuando llama Wire.begin. Cuando hay actividad I2C, el µC deja de hacer su tarea actual (a menos que ... no importa por el momento :-) y va en el código de la biblioteca Wire, que luego llama a la función (apropiada, dependiendo de lo que esté sucediendo) que usted registró como devolución de llamada ( receiveDatapor ejemplo). Una devolución de llamada es el nombre genérico para funciones como receiveDatao sendData, son llamadas por un controlador de interrupciones dentro de Wire.
FredP
2

¿No es ese caso el que setup()se llama una vez y loop()se llama repetidamente? es decir, hay un invisible main() que podría verse así:

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

Disculpas ya que solo estoy investigando el Arduino y casi no tengo experiencia en C / C ++; Estoy tratando de manejar esta loop()situación yo mismo.

Dee
fuente
Básicamente sí. También hay una llamada a la init()que se activan los temporizadores millis, delayetc. Así que init()es para la inicialización general, setup()es para su inicialización y loopes, bueno, para bucle. Puedes escribir el tuyo mainsi quieres tomar el control total.
Nick Gammon
Buen post. Por cierto, ;no se requiere después del penúltimo }:-)
Greenonline
También hay una llamada de serial_event () ¿no es así?
Divisadero
2

No puedo comentar sobre la respuesta de Dee. El código real que se ejecuta en el bucle principal está aquí :

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

    setup();

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

Y sí, setup()se llama una vez y loop()se llama repetidamente (junto con algunas cosas en serie).

Petrus
fuente
0

Funciona como una función normal, debe llamarse para que tenga sentido. Se llama a loop () / setup () desde una función main () que se compila desde el directorio Arduino y se vincula.

TMa
fuente