Как работают функции вне void loop?

9

Я привык к наброскам Arduino с void setup()частью, которая выполняется один раз, и void loop()частью, которая продолжает цикл. Что происходит, когда у вас есть пустые функции вне основного void loop()? Все ли они будут продолжать работать параллельно, или они будут работать один за другим? Или определенные пустые функции запускаются только после того, как определенные критерии были выполнены (например, цикл while)?

Например , в коде ниже, когда будет void receiveData(int byteCount)и void sendData()функция запуска?

//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
источник
Это выглядит интересно. Интересно, не могли бы вы опубликовать ссылки на источник кода (и подробности связей между Arduino и Pi).
Milliways
1
@Milliways Я использовал этот * учебник, чтобы написать код для Arduino Uno и Raspberry Pi (модель B +), однако я сделал несколько небольших изменений. Соедините выводы SDA и SCL двух плат, а также заземляющие контакты, если они подключены к разным источникам питания. Затем я подключил контакт 3 к датчику, настроенному в конфигурации делителя потенциала, подключенной между контактами + 5 В и Gnd. Контакт 0 и Gnd подключен к плате привода двигателя.
Blue7

Ответы:

11

Функции setup()и loop()необычны, потому что они автоматически вызываются для вас кодом Arduino. Никакие другие функции не ведут себя таким образом.

Вообще говоря, функция никогда не запустится, если вы сами не вызовете ее (например, из setup()или loop()) или не проинструктируете другую часть программы вызывать ее. (Существуют и другие способы выполнения функций, но обычно они включают в себя некоторые очень сложные операции, которых лучше избегать.)

Например, pinMode()это функция, как и любая другая. Он запускается только тогда, когда вы действительно добавляете что-то подобное pinMode(3, INPUT)в свой код. В этот момент он запускается один раз, завершается, а затем вызывающая функция продолжается с того места, где она остановилась (они никогда не запускаются параллельно).

Пример кода, который вы опубликовали, довольно интересный. Посмотрите на эти строки в setup():

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

Эти строки сообщают Wireобъекту о вызове receiveData()и sendData()в ответ на события I2C. Это делается путем передачи указателей на функции, которые хранятся и используются Wire.

Я бы порекомендовал поискать информацию об указателях функций C / C ++ онлайн, если вы хотите узнать больше об этом. Вам также может быть интересно изучить attachInterrupt()функцию Arduino .

Питер Блумфилд
источник
Спасибо за Ваш ответ. Это начинает иметь больше смысла сейчас. Однако, если receiveData()и sendData()функция не работает , если они не называют, то почему они называются внутри void setup()функции и не является основной void loop()функцией? Конечно, эти функции никогда не будут вызваны, если только редкий шанс, что есть событие i2c, пока указатель инструкции все еще находится внутри void setupфункции? Не лучше ли вызывать эти функции изнутри void loopфункции, чтобы при возникновении события i2c функция вызывалась?
Blue7 22.10.14
4
@ Blue7 Эти функции не называются в void setup(), они передаются в качестве параметра onReceiveи onRequest, они обратные вызовы в качестве комментария состояний. Вкратце: это говорит (код из) библиотеки Wire вызывать эти функции, когда происходят определенные вещи ( arduino.cc/en/Reference/WireOnReceive , arduino.cc/en/Reference/WireOnRequest ...)
FredP
@FredP А, ладно. Спасибо за ссылки, я проверю их, когда я не на моем телефоне. У меня есть быстрый вопрос, если вы не возражаете. Эти обратные вызовы всегда готовы и ждут события i2c? т.е. независимо от того, где находится указатель инструкции, эти обратные вызовы будут мгновенно вызывать функцию, как только происходит событие i2c?
Blue7
1
@ Blue7 Предположительно, будут использоваться прерывания для мониторинга активности I2C. Когда прерывание выполняется, оно временно отнимает контроль над основной программой.
Питер Блумфилд,
3
@ Blue7 Обратные вызовы не ожидают (Arduino не является многопоточным), как говорит @PeterRBloomfield, библиотека Wire позволяет прерывать I2C twi_init()при вызове Wire.begin. Когда есть активность I2C, микроконтроллер перестает выполнять свою текущую задачу (если ... на данный момент не имеет значения :-) и входит в код библиотеки Wire, которая затем вызывает (соответствующую, в зависимости от происходящего) функцию, которую вы зарегистрировали как обратный вызов ( receiveDataнапример). Обратный вызов это общее название для функций , таких как receiveDataили sendData, как они называются с помощью обработчика прерываний внутри провода.
FredP
2

Разве это не тот случай, который setup()вызывается один раз и loop()вызывается неоднократно? то есть, что есть невидимое, main() которое может выглядеть так:

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

Извиняюсь за то, что я просто изучаю Arduino и почти не имею опыта работы с C / C ++; Я пытаюсь разобраться в этой loop()ситуации сам.

Dee
источник
В основном да. Существует также вызов, init()который запускает таймеры millisи delayт. Д. Так init()же для общей инициализации, setup()для вашей инициализации и loopдля, ну, в общем , зацикливания. Вы можете написать свой собственный, mainесли вы хотите получить полный контроль.
Ник Гэммон
Хороший пост. КСТАТИ ;не требуется после предпоследнего }:-)
Greenonline
Существует также вызов serial_event () не так ли?
Divisadero
2

Я не могу комментировать ответ Ди. Фактический код, который выполняется в основном цикле, находится здесь :

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

    setup();

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

И да, вызывается setup()один раз и loop()вызывается неоднократно (вместе с некоторыми последовательными вещами).

Petrus
источник
0

Работает как обычная функция, ее нужно вызывать, чтобы иметь смысл. loop () / setup () вызываются из функции main (), которая скомпилирована из каталога Arduino и связана с ней. receiveData / sendData должны вызываться из вашей программы, корень которой находится в функциях loop / setup.

TMA
источник