Làm thế nào để các chức năng bên ngoài void loop hoạt động?


9

Tôi đã quen với các bản phác thảo Arduino với một void setup()phần chạy một lần và một void loop()phần cứ lặp đi lặp lại. Điều gì xảy ra khi bạn có các chức năng void bên ngoài chính void loop()? Tất cả những thứ này sẽ tiếp tục lặp song song hay chúng chạy lần lượt? Hoặc các hàm void nhất định chỉ chạy khi các tiêu chí nhất định đã được đáp ứng (như vòng lặp while)?

Ví dụ trong đoạn mã dưới đây, khi nào void receiveData(int byteCount)và các void sendData()hàm sẽ chạ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.
}

Điều này có vẻ thú vị. Tôi tự hỏi nếu bạn có thể gửi liên kết đến nguồn của mã (và chi tiết về các kết nối giữa Arduino và Pi).
Milliways

1
@Milliways Tôi đã sử dụng hướng dẫn * này để viết mã trên arduino uno và Raspberry pi (mô hình B +), tuy nhiên tôi đã thực hiện một vài thay đổi nhỏ. Kết nối các chân SDA và SCL của hai bảng, cũng như các chân nối đất nếu chúng được kết nối với các nguồn cung cấp năng lượng khác nhau. Sau đó, tôi có chân 3 được kết nối với một cảm biến được cấu hình trong cấu hình bộ chia tiềm năng, được kết nối giữa các chân + 5V và Gnd. Chân 0 và Gnd được kết nối với bảng điều khiển động cơ.
Blue7

Câu trả lời:


11

Các chức năng setup()loop()không bình thường bởi vì chúng được gọi tự động cho bạn bởi mã Arduino. Không có chức năng khác hành xử theo cách này.

Nói chung, một chức năng sẽ không bao giờ chạy trừ khi bạn tự gọi nó một cách rõ ràng (ví dụ từ bên trong setup()hoặc loop()), hoặc hướng dẫn một phần khác của chương trình gọi nó. (Có nhiều cách khác để thực thi các chức năng, nhưng điều đó thường liên quan đến một số sửa đổi rất tiên tiến mà tốt nhất nên tránh.)

Ví dụ, pinMode()là một chức năng giống như bất kỳ khác. Nó chỉ chạy khi bạn thực sự đặt một cái gì đó giống như pinMode(3, INPUT)trong mã của bạn. Tại thời điểm đó, nó chạy một lần, kết thúc và sau đó chức năng gọi tiếp tục từ nơi nó rời đi (chúng không bao giờ chạy song song).

Mã ví dụ bạn đã đăng khá thú vị. Nhìn vào những dòng này trong setup():

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

Những dòng này đang nói cho Wiređối tượng gọi receiveData()sendData()phản ứng với các sự kiện I2C. Nó thực hiện điều này bằng cách chuyển các con trỏ hàm được lưu trữ và sử dụng bởi Wire.

Tôi khuyên bạn nên tìm kiếm thông tin về con trỏ hàm C / C ++ trực tuyến nếu bạn muốn tìm hiểu thêm về điều này. Bạn cũng có thể quan tâm để khám phá attachInterrupt()chức năng của Arduino .


Cảm ơn câu trả lời của bạn. Điều này đang bắt đầu có ý nghĩa hơn bây giờ. Tuy nhiên, nếu hàm receiveData()sendData()hàm không chạy trừ khi chúng được gọi, thì tại sao chúng được gọi trong void setup()hàm chứ không phải void loop()hàm chính ? Chắc chắn các hàm này sẽ không bao giờ được gọi trừ khi có khả năng hiếm có sự kiện i2c trong khi con trỏ lệnh vẫn nằm trong void setuphàm? Sẽ không tốt hơn nếu gọi các hàm này từ bên trong void loophàm vì vậy bất cứ khi nào có sự kiện i2c, hàm sẽ được gọi?
Blue7

4
@ Blue7 Những chức năng này không được gọi vào void setup(), họ được thông qua như là tham số của onReceiveonRequest, họ đang callbacks như các quốc gia nhận xét. Tóm lại: điều này nói với (mã từ thư viện Dây) gọi các hàm này khi xảy ra sự cố cụ thể ( arduino.cc/en/Reference/WireOnReceive , arduino.cc/en/Reference/WireOnRequest ...)
FredP

@FredP À được rồi. Cảm ơn các liên kết, tôi sẽ kiểm tra chúng khi tôi không sử dụng điện thoại. Tôi có một câu hỏi nhanh trong lúc này, nếu bạn không phiền. Những cuộc gọi lại này luôn sẵn sàng và chờ đợi một sự kiện i2c? tức là, bất kể con trỏ lệnh ở đâu, các cuộc gọi lại này sẽ gọi hàm ngay lập tức ngay khi sự kiện i2c xảy ra?
Blue7

1
@ Blue7 Có lẽ sẽ sử dụng các ngắt để theo dõi hoạt động I2C. Khi một ngắt thực thi, nó tạm thời mất kiểm soát khỏi chương trình chính.
Peter Bloomfield

3
@ Blue7 Các cuộc gọi lại không chờ đợi (Arduino không phải là đa luồng), như @PeterRBloomfield nói, thư viện Dây cho phép I2C ngắt twi_init()khi bạn gọi Wire.begin. Khi có hoạt động I2C, thìCraftC ngừng thực hiện nhiệm vụ hiện tại của mình (trừ khi ... không có gì trong lúc này :-) và đi vào mã của thư viện Dây, sau đó gọi hàm (thích hợp, tùy thuộc vào những gì đang xảy ra) mà bạn đã đăng ký gọi lại ( receiveDataví dụ). Một callback là tên chung cho các chức năng như receiveDatahay sendData, họ đang gọi bằng một trình xử lý ngắt bên Wire.
FredP

2

Có phải đó không phải là trường hợp setup()được gọi một lần và loop()được gọi nhiều lần? tức là có một thứ không nhìn thấy main() như thế này:

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

Xin lỗi vì tôi chỉ nhìn vào Arduino và gần như không có kinh nghiệm về C / C ++; Tôi đang cố gắng tự xử lý loop()tình huống này .


Về cơ bản, có. Ngoài ra còn có một cuộc gọi đến init()mà được tính giờ sẽ cho millis, delayvv Vì vậy, init()là để khởi tạo nói chung, setup()là dành cho bạn khởi tạo, và looplà cho, tốt, vòng lặp. Bạn có thể tự viết mainnếu bạn muốn kiểm soát hoàn toàn.
Nick Gammon

Bài đăng hay. ;Không yêu cầu BTW sau áp chót }:-)
Greenonline

Ngoài ra còn có cuộc gọi của serial_event () phải không?
Divisadero

2

Tôi không thể nhận xét về phản ứng của Dee. Mã thực tế được thực thi trong vòng lặp chính ở đây :

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

    setup();

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

Và vâng, setup()được gọi một lần và loop()được gọi liên tục (cùng với một số nội dung nối tiếp).


0

Nó hoạt động như chức năng bình thường, nó phải được gọi để có ý nghĩa. loop () / setup () được gọi từ hàm main () được biên dịch từ thư mục Arduino và được liên kết trong. receiveData / sendData được gọi từ chương trình của bạn, hàm nằm trong hàm loop / setup.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.