Jak działają funkcje poza pustą pętlą?

9

Jestem przyzwyczajony do szkiców Arduino z void setup()częścią, która działa raz i void loop()częścią, która ciągle się zapętla. Co się stanie, gdy będziesz mieć nieważne funkcje poza głównym void loop()? Czy to wszystko będzie się zapętlać równolegle, czy będą działać jeden po drugim? Czy też niektóre funkcje void działają tylko po spełnieniu określonych kryteriów (np. Pętla while)?

Na przykład w poniższym kodzie, kiedy będzie void receiveData(int byteCount)i void sendData()funkcje uruchomić?

//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
źródło
To wygląda interesująco. Zastanawiam się, czy mógłbyś zamieścić linki do źródła kodu (i szczegóły połączeń między Arduino i Pi).
Milliways
1
@Milliways Użyłem tego * samouczka, aby napisać kod na arduino uno i Raspberry pi (model B +), jednak dokonałem kilku drobnych zmian. Podłącz piny SDA i SCL dwóch płyt, a także piny uziemienia, jeśli są podłączone do różnych zasilaczy. Następnie miałem pin 3 podłączony do czujnika skonfigurowanego w konfiguracji dzielnika potencjału, podłączonego między pinami + 5V i Gnd. Pin 0, a Gnd jest podłączony do płyty napędu silnikowego.
Blue7

Odpowiedzi:

11

Funkcje setup()i loop()są niezwykłe, ponieważ są wywoływane automatycznie przez kod Arduino. Żadne inne funkcje nie zachowują się w ten sposób.

Ogólnie rzecz biorąc, funkcja nigdy się nie uruchomi, chyba że użytkownik wywoła ją jawnie (np. Z wewnątrz setup()lub loop()) lub nie poinstruuje innej części programu, aby ją wywołała. (Istnieją inne sposoby wykonywania funkcji, ale zwykle wymaga to bardzo zaawansowanego majsterkowania, którego najlepiej unikać).

Na przykład pinMode()jest funkcją taką jak każda inna. Działa tylko wtedy, gdy faktycznie umieścisz coś takiego jak pinMode(3, INPUT)w kodzie. W tym momencie uruchamia się raz, kończy, a następnie funkcja wywoływania jest kontynuowana od miejsca, w którym została przerwana (nigdy nie działają równolegle).

Przykładowy kod, który opublikowałeś, jest dość interesujący. Spójrz na te linie w setup():

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

Linie te informują Wireobiekt o wywołaniu receiveData()i sendData()w odpowiedzi na zdarzenia I2C. Robi to poprzez przekazywanie wskaźników funkcji, które są przechowywane i używane przez Wire.

Polecam wyszukiwanie informacji o wskaźnikach funkcji C / C ++ online, jeśli chcesz dowiedzieć się więcej na ten temat. Możesz być także zainteresowany poznaniem attachInterrupt()funkcji Arduino .

Peter Bloomfield
źródło
Dzięki za odpowiedź. To zaczyna mieć teraz większy sens. Jeśli jednak funkcje receiveData()i sendData()nie są uruchamiane, chyba że zostaną wywołane, to dlaczego są wywoływane w ramach void setup()funkcji, a nie void loop()funkcji głównej ? Z pewnością te funkcje nigdy nie zostaną wywołane, chyba że istnieje rzadka szansa, że ​​wystąpi zdarzenie i2c, gdy wskaźnik instrukcji nadal znajduje się w void setupfunkcji? Czy nie byłoby lepiej wywoływać te funkcje z poziomu void loopfunkcji, więc gdy występuje zdarzenie i2c, funkcja jest wywoływana?
Blue7
4
@ Blue7 Te funkcje nie są nazywane w void setup(), są one przekazywane jako parametr onReceivea onRequest, są wywołania zwrotne jak stwierdza komentarz. W bardzo krótkim podsumowaniu: mówi (kod z) biblioteki Wire, aby wywoływała te funkcje, gdy zdarzają się określone rzeczy ( arduino.cc/en/Reference/WireOnReceive , arduino.cc/en/Reference/WireOnRequest ...)
FredP
@FredP Ah w porządku. Dzięki za linki, sprawdzę je, kiedy nie będę przy telefonie. Tymczasem mam krótkie pytanie, jeśli nie masz nic przeciwko. Czy te połączenia zwrotne są zawsze gotowe i czekają na zdarzenie i2c? tzn. bez względu na to, gdzie znajduje się wskaźnik instrukcji, te wywołania zwrotne natychmiast wywołają funkcję, jak tylko zdarzy się zdarzenie i2c?
Blue7
1
@ Blue7 Prawdopodobnie będzie używać przerwań do monitorowania aktywności I2C. Kiedy wykonuje się przerwanie, tymczasowo przejmuje kontrolę od głównego programu.
Peter Bloomfield
3
@ Blue7 Połączenia zwrotne nie czekają (Arduino nie jest wielowątkowe), jak mówi @PeterRBloomfield, biblioteka Wire umożliwia przerwanie I2C twi_init()podczas rozmowy Wire.begin. Gdy występuje aktywność I2C, µC przestaje wykonywać swoje bieżące zadanie (chyba że ... nie ma na razie znaczenia :-) i przechodzi do kodu biblioteki Wire, który następnie wywołuje funkcję (odpowiednią, w zależności od tego, co się dzieje) zarejestrowaną jako oddzwanianie ( receiveDatana przykład). Zwrotna to ogólna nazwa dla funkcji, takich jak receiveDatalub sendData, są one nazywane przez obsługi przerwań wewnątrz Wire.
FredP
2

Czy to nie przypadek, który setup()jest wywoływany raz i loop()jest wywoływany wielokrotnie? tzn. że jest coś niewidocznego, main() które może wyglądać tak:

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

Przepraszam, że patrzę tylko na Arduino i prawie nie mam doświadczenia w C / C ++; Sam próbuję poradzić sobie z tą loop()sytuacją.

Dee
źródło
Zasadniczo tak. Istnieje również wezwanie do init()którego dostaje liczniki dzieje na millis, delayitd. Tak init()jest do ogólnego inicjalizacji setup()jest dla swojej inicjacji, a loopto za dobrze, zapętlenie. Możesz napisać własny, mainjeśli chcesz przejąć pełną kontrolę.
Nick Gammon
Niezły post. BTW ;nie jest wymagane po przedostatnim }:-)
Greenonline
Istnieje również wywołanie serial_event (), czyż nie?
Divisadero,
2

Nie mogę skomentować odpowiedzi Dee. Rzeczywisty kod wykonywany w głównej pętli znajduje się tutaj :

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

    setup();

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

I tak, setup()zostaje wywołany raz i loop()jest wywoływany wielokrotnie (wraz z kilkoma seriami).

Petrus
źródło
0

Działa jak normalna funkcja, należy ją wywołać, aby miała sens. loop () / setup () są wywoływane z funkcji main (), która jest skompilowana z katalogu Arduino i połączona z.

TMa
źródło