[решено]Raspberry <=> Arduino через I2C

Тема в разделе "Raspberry Pi", создана пользователем Mitrandir, 25 июн 2018.

  1. Mitrandir

    Mitrandir Гуру

    Здравствуйте,
    Пытаюсь освоить коммуникации малины и ардуины.
    Чтоб не мутить с согласованием уровней, соединил через шину I2C, ибо на малине есть подтягивающие до 3 вольт резисторы.
    собрал.
    Малина мастер ардуино slave

    вот скетч для ардуины:
    Код (C++):


    #include <Wire.h>

    #define SLAVE_ADDRESS 0x40

    byte result;
    byte stp;
      byte fa;

    void setup() {
      // put your setup code here, to run once:
       pinMode(13, OUTPUT);
       Serial.begin(9600);         // start serial for output
         // initialize i2c as slave
        Wire.begin(SLAVE_ADDRESS);
        Wire.onRequest(sendData);
        Wire.onReceive(readData);
        Serial.println("Ready!");
        digitalWrite(13,LOW);

    }

    void loop() {
      // put your main code here, to run repeatedly:

    }

    void readData(int byteCount){

      Serial.print("i2c Incoming! bc =");
      Serial.print(byteCount);
      Serial.print(" avaiable");
      Serial.println(Wire.available());
      while(Wire.available()){
        byte rd = Wire.read();
        Serial.print("Step:");
        Serial.print(stp);
        Serial.print(" received:");
        Serial.println(rd);
        switch (stp) {
          case 0: stp++; result = rd; break;
          case 1: stp++; fa = rd; break;
          case 2: stp=0;
                  if(rd == 1){
                     result+= fa;
                  }else if(rd ==2){
                    result-=fa;
                  }
        }
      }
    }
    void sendData(){
        Wire.write(result);
        Serial.print("Sending data:");
        Serial.println(result);

    }
    если на малине прописать i2cdetect -y 0 то видим: Screenshot_1.jpg

    при этом ардуино видит опрос и в консоль пишет

    i2c Incoming! bc =0 avaiable0

    попробовал запустить посылку через wiringPi
    вот код:
    Код (C++):
    #include <iostream>
    #include <wiringPiI2C.h>

    using namespace std;

    int main()
    {

    int fd = wiringPiI2CSetup (0x40); // the device address goes here
    cout << "Init result: "<< fd << endl;
    wiringPiI2CWrite (fd, 1); // we send the command 1 to the ATtiny
    wiringPiI2CWrite (fd, 3);
    wiringPiI2CWrite (fd, 2);

    int result = wiringPiI2CRead(fd);
    cout<< result<<endl;

    }
    Ардуина выдала:

    то есть третий вызов wiringPiI2CWrite (fd, 2); не удался, чтение тоже провалилось

    решил сделать без библиотеки, напрямую.
    Вот код:

    Код (C++):
    #include <unistd.h>                //Needed for I2C port
    #include <fcntl.h>                //Needed for I2C port
    #include <sys/ioctl.h>            //Needed for I2C port
    #include <linux/i2c-dev.h>        //Needed for I2C port
    #include <iostream>

    using namespace std;

    int main(){

        int file_i2c;
        int length;
        unsigned char buffer[60] = {0};

     
        //----- OPEN THE I2C BUS -----
        char *filename = (char*)"/dev/i2c-0";
        if ((file_i2c = open(filename, O_RDWR)) < 0)
        {
            //ERROR HANDLING: you can check errno to see what went wrong
            cout << "error opening i2c bus" <<endl;
            return 1;
        }

        int addr = 0x40;          //<<<<<The I2C address of the slave
        if (ioctl(file_i2c, I2C_SLAVE, addr) < 0)
        {
            cout <<"Failed to acquire bus access and/or talk to slave."<<endl;
            //ERROR HANDLING; you can check errno to see what went wrong
            return 1;
        }

     
        //----- WRITE BYTES -----
        buffer[0] = 0x01;
        buffer[1] = 0x02;
        buffer[2] = 0x01;
        length = 3;            //<<< Number of bytes to write
        if (write(file_i2c, buffer, length) != length)        //write() returns the number of bytes actually written, if it doesn't match then an error occurred (e.g. no response from the device)
        {
            /* ERROR HANDLING: i2c transaction failed */
            cout<<"Failed to write to the i2c bus"<<endl;
        }

        length = 1;
        if (read(file_i2c, buffer, length) != length)        //read() returns the number of bytes actually read, if it doesn't match then an error occurred (e.g. no response from the device)
        {
            //ERROR HANDLING: i2c transaction failed
            cout<<"Failed to read from the i2c bus."<<endl;
        }
        else
        {
            cout<<"Data read: "<< buffer <<endl;;
        }
        return 0;
    }
     
    он работат чуть лучше:

    вот что пишет ардуино:

    но увы малина уже не читает:
    Failed to read from the i2c bus.

    как правильно написать взаимодействие?
     
    Последнее редактирование: 25 июн 2018
  2. Mitrandir

    Mitrandir Гуру

    Причем если программу на распбери перевести на питон:
    Код (Python):
    import smbus

    import time

    # for RPI version 1, use "bus = smbus.SMBus(0)"

    bus = smbus.SMBus(0)

    # This is the address we setup in the Arduino Program

    address = 0x40


    def writeNumber(value):

        bus.write_byte(address, value)

        # bus.write_byte_data(address, 0, value)

        return -1


    def readNumber():

        number = bus.read_byte(address)

        # number = bus.read_byte_data(address, 1)

        return number



    while True:

        var = input("Enter 1 - 9: ")

        if not var:

            continue

        writeNumber(var)

        print "RPI: Hi Arduino, I sent you ", var

        var = input("Enter second 1-9:")

        if not var:
            continue

        writeNumber(var)

        writeNumber(1)

        # sleep one second

        time.sleep(1)



        number = readNumber()

        print "Arduino: Hey RPI, I received a digit ", number

        print
    То все работает как часы
     
  3. DIYMan

    DIYMan Guest

    Я правильно понял, что у малины уровни 3.3В, а у ардуины - 5В? Если да - конвертер уровней ннада.
     
  4. Mitrandir

    Mitrandir Гуру

    зачем?

    малина поднимает уровни линий SDA и SCL до 3.3 вольт

    затем если надо передать ардуина или малина просто роняет эти линии в 0

    Ардуино не пускает логическую 1 на линию.
     
  5. DIYMan

    DIYMan Guest

    Моё дело предложить, что называется. А ответ на вопрос простой: затем, что так схемотехнически грамотно, без отдавания на откуп гаданию на кофейной гуще - просядет в какой-то момент питалово и, как следствие, уровень сигнала - или не просядет. Если считаете, что незачем - ок, незачем, пускай не работает и дальше ;)
     
  6. Mitrandir

    Mitrandir Гуру

    у меня проблема не в электрической части, а в программной.

    если пишу на питоне все работет
    если пишу на с++ не работает

    вывод виноват мой код на с++.
    Не работала бы электроника, то питон бы тоже не работал

    Стандарт I2C придумали инженеры Филлипса, а не я.
    Я всего лишь использую то что предложили умные дядьки
     
  7. Mitrandir

    Mitrandir Гуру

    Анализируя чем отличается программа на питоне и на с++ пришел к выводу:
    надо перед тем как начать читать , надо сделать задержку.

    если перед строкой
    if (read(file_i2c, buffer, length) != length)
    вставить
    bcm2835_delay(1000);

    то все работает.

    возникает вопрос, какую задержку ставить?
     
  8. Mitrandir

    Mitrandir Гуру

    опытным путем выяснил, что bcm2835_delay(100) достаточно.
    походу малина слишком шустрая и ардуино неуспевает ответить
     
  9. DIYMan

    DIYMan Guest

    Ок, отметаем рассогласование уровней, пущай их.
    Как вариант ;)
     
  10. parovoZZ

    parovoZZ Гуру

    В данном случае - нет. На 3.3 в МК уже "1". Я бы беспокоился за малину - у неё нет защит совсем.
     
  11. Mitrandir

    Mitrandir Гуру

    Это да, главное по ошибке не залить скетч в ардуино, который поднимет пины i2c в 1
     
  12. AlexU

    AlexU Гуру

    Сопротивление подтяжки какое?
    Успевает. Проблема скорее в неправильной подтяжке линий шины I2C.
     
  13. AlexU

    AlexU Гуру

    Отлично! Продолжайте дальше искать проблему в исходном коде...
     
  14. Mitrandir

    Mitrandir Гуру

    1.8 k по даташиту
    Screenshot_2018-06-26-10-18-25-324_com.google.android.apps.docs.png
     
  15. Mitrandir

    Mitrandir Гуру

    Все статьи которые находил, описывали подключение именно так, напрямую.

    Врут?
     
  16. AlexU

    AlexU Гуру

    Почему сразу -- "врут"? Вполне нормальное подключение. Меня просто удивил ответ, что не знаете какая подтяжка у линий шины I2C.
    1.8к вполне нормальная подтяжка.
     
    Igor68 и Mitrandir нравится это.
  17. Mitrandir

    Mitrandir Гуру

    Я просто не лазил в потроха малины, почитал про стандарт и2с , почитал что они там есть , и принял на веру что инженеры малины сделали правильно
     
  18. AlexU

    AlexU Гуру

    Следующее на что обратите внимание:
    функции 'void readData(int byteCount)' и 'void sendData()' -- callback'и в ардуиновском коде -- вызываются внутри обработчика прерывания. В тоже время в этих функциях активно используется вывод в Serial, который тоже основан на прерываниях. Если передающий буфер переполнится, то вызов функции 'Serial.print();' заблокирует выполнение программы до тех пор, пока буфер не очистится. А он не очистится до тех пор, пока не отработает обработчик прерывания отправки данных в Serial.
    Так что при такой организации кода велика вероятность появления такого явления как 'deadlock' -- к примеру, функция 'sendData()' не сможет завершить своё выполнение из-за переполненного буфера Serial, а буфер Serial не может быть очищен, т.к. прерывания запрещены (программа уже находится в обработчике прерывания TWI).
     
    Последнее редактирование: 26 июн 2018
    Igor68 и Mitrandir нравится это.
  19. Mitrandir

    Mitrandir Гуру

    Спасибо, вечером как вернусь попробую удалить вывод из прерываний, он там был чтоб отладить ардуино.
     
  20. AlexU

    AlexU Гуру

    Освежил в памяти функционал Serial и выяснил следующее:
    deadlock'а не будет, но в случае переполнения буфера Serial, пока передаются данные через Serial, данные по I2C передаваться/приниматься не будут.
     
    Igor68 и Mitrandir нравится это.