Связь I2C между arduino и raspberry pi прерывается по непонятным причинам. Почему?

У меня возникает поведение, которое я не могу объяснить при использовании arduino mega в качестве подчиненного устройства I2C для главного устройства Raspberry Pi.

Здесь я показываю идеализированную программу, которая показывает проблему. Я испытываю примерно такое же поведение в реальном приложении (прошивка клавиатуры). Я делаю следующее:

  • arduino выдает прерывание на контакте 23 в каждом цикле, чтобы сообщить raspi, что есть данные для чтения.
  • raspi отправляет запрос в качестве мастера на arduino.
  • arduino получает запрос в обработчике Wire и выдает ответ размером 32 байта. Я заполняю эти 32 байта счетчиком uint8 просто для проверки.
  • цикл начинается снова.

Это код для raspi

import time
import RPi.GPIO as GPIO
import smbus

GPIO.setmode(GPIO.BCM)
GPIO.setup(4, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

bus = smbus.SMBus(1)
address = 0x42

def callback(channel):
    data = bus.read_i2c_block_data(address, 0x00, 32)
    print(data)

GPIO.add_event_detect(4, GPIO.RISING, callback=callback)

while True:
    time.sleep(1)

GPIO.cleanup()

а это код Arduino

#include <Wire.h>

byte IRQ_PIN = 23;

volatile unsigned int counter;

void setup() {
    Serial.begin(115200);

    pinMode(IRQ_PIN, OUTPUT);
    digitalWrite(IRQ_PIN, LOW);
    Wire.begin(0x42);
    Wire.onRequest(requestEvent);
    Wire.onReceive(receiveData);

}

void signalMaster() {
  noInterrupts();
  irq();
  interrupts();
}

void irq() {
  digitalWrite(IRQ_PIN, HIGH);
  digitalWrite(IRQ_PIN, LOW);    
}

void requestEvent() {
  byte buf[32];
  counter++;
  memset(buf, counter, 32*sizeof(byte));
  Wire.write(buf, 32);
}

void receiveData(int byteCount){
  while(Wire.available()) {
    Wire.read();
  }
}

void loop() {
  signalMaster();
  delay(1);
}

Как вы видите, дело в том, что я очень-очень быстро нажимаю на I2C, а петля получается очень тугой и быстрой. В этом суть упражнения, и отсюда вытекает проблема, которую необходимо решить.

Теперь время от времени я получаю случайную ошибку ввода-вывода с кодом raspi python. Этот код иногда дает сбой один раз, а затем все возвращается в нормальное русло, но время от времени он полностью ломается и никогда не восстанавливается.

Я подключил к нему логический анализатор и вот что обнаружил. D3 — контакт прерывания. Остальные шины I2C. В обычной транзакции я вижу, что все работает правильно (не обращайте внимания на другое содержание ответа, это был более ранний запуск)

и увеличьте основной запрос и ответ:

Однако когда у меня постоянный сбой, картина совсем другая

Увеличить:

Для меня это не имеет особого смысла. Зачем ему выполнять запись данных, и кто записывает 0x85FF... в мой буфер?

Спасибо

, 👍2

Обсуждение

Формат идеально подходит для реальной многобайтовой транзакции записи. Как будто библиотека Python вызывает write_i2c_block_data вместо read_i2c_block_data. Это должно быть какая-то ошибка в конце Raspberry, возможно, в библиотеке py-smbus., @Majenko

сегодня вечером займусь отладкой., @Stefano Borini

@Majenko Хорошо, из краткой проверки текущего кода видно, что модуль smbus представляет собой прямой вызов i2c_smbus_access, который затем просто выполняет ioctl ... так что, если есть ошибка, это довольно глубоко. Любопытно, может ли быть фактором тот факт, что я использую 3,3 В вместо 5 В? Я читал противоречивые мнения о соединении arduino и raspi без переключателя уровня посередине., @Stefano Borini

Разница в напряжении не заставит Pi выполнять совершенно другую транзакцию., @Majenko

Это может быть полной чушью, но отключение прерываний на Arduino, а затем запуск raspi и включение прерываний переводит arduino в состояние, в котором он не может реагировать на (теперь это возможно) прерывания I2C/SMBus. Raspi действительно быстр по сравнению к arduino, и может быть состояние гонки. Я признаю; Я сам не верю в это решение, но если нет другого решения, стоит попробовать ;-). Как вы включаете/подтягиваете шину I2C. Резисторы могут быть слишком высокими. Ваши логические анализаторы не осци. Он показывает вам логические уровни, а не реальные напряжения. Т. тоже может быть проблемой., @Peter Paul Kiefer

@PeterPaulKiefer Я использую arduino mega 2560 с внутренними резисторами для шины i2c. У меня тоже есть osci, так что я могу дать ему чек. Я должен приостановить прерывания, так как я хочу быть уверен, что контакт прерывания не будет оставаться высоким в течение очень долгого времени, если арду выйдет из строя., @Stefano Borini

Если вы питаете шину I2C с помощью Arduino, вы используете 5 В (или это 3,3 В Arduino?). Это может разрушить ваш raspi, пожалуйста, будьте осторожны с этим. Это также проблема с PIN-кодом прерывания 5V, который слишком высок для raspi, он может терпеть это какое-то время, но это риск. Можно использовать делитель напряжения на два резистора., @Peter Paul Kiefer

Процедуры прерывания Arduino не должны выполняться в течение длительного времени. И, поскольку вы реагируете на нарастающий фронт вывода прерывания на raspi, это не проблема, если вывод прерывания имеет высокий уровень, если вы установили его в LOW перед следующим прерыванием. С другой стороны, прерывания необходимы для работы IC2. Вы можете потерять информацию, если прерывания не работают, когда raspi начал отправлять до включения прерываний., @Peter Paul Kiefer

@PeterPaulKiefer Отличные очки. Я собираюсь переделать несколько вещей завтра вечером и посмотреть, что произойдет. Что касается питания, то в настоящее время arduino питается от USB, а raspi — от блока питания. Так что да, технически они на разном напряжении. Однако идея состоит в том, что оба будут питаться от одного и того же источника питания 5 В. Конечно, логические уровни не будут меняться независимо от источника питания. Достаточно ли резистора делителя напряжения или мне следует использовать преобразователь логического уровня? в чем их физическая разница?, @Stefano Borini

5 В от источника питания регулируются до рабочего напряжения Raspi. Это не то же самое. Это 3,3В. См. ответ Бруно Броноски. https://raspberrypi.stackexchange.com/questions/3209/what-are-the-min-max-voltage-current-values-the-gpio-pins-can-handle., @Peter Paul Kiefer

Если ваш сигнал течет от более высокого напряжения к более низкому, резисторного делителя может быть достаточно. Если сигнал идет в обоих направлениях highV <--> lowV, вам нужен переключатель уровня. Если сигнал течет от более низкого к более высокому напряжению, а устройство с более высоким напряжением также интерпретирует высокий уровень устройства с более низким напряжением как высокий, вы можете соединить устройства напрямую друг с другом, если вы уверены, что более высокое напряжение устройство никогда не подавать питание на провод., @Peter Paul Kiefer

Какую малину вы используете? Это может быть проблема с растяжкой часов. У меня это тоже есть с проектом Arduino. Если ведомое устройство не может ответить, оно пытается растянуть тактовый сигнал. У Raspberry есть некоторые проблемы с этим, и как только часы синхронизации будут потеряны, оба устройства больше не будут работать ... Пока вы не сбросите линию i2c., @Adriano

@adriano я использую ноль пи, @Stefano Borini

Не уверен, но я думаю, что это также связано с ошибкой растяжения часов. Как только Arduino растянет часы, raspy потеряет синхронизацию с вашим Arduino., @Adriano


1 ответ


2

Используйте микросхему преобразования логического уровня.

источник изображения http://msx-elektronika.pl/en/logic-level-converter

,

Купил два, на выходных попробую, @Stefano Borini

@StefanoBorini, результаты?, @Juraj

Я добавил преобразователь логического уровня, но все равно получаю такие же ошибки. На данный момент я не уверен, в чем причина. Я решил, что на данный момент я просто использую традиционный микроконтроллер клавиатуры, но это определенно то, к чему я должен вернуться. Вечером добавлю фото своей установки, @Stefano Borini

@StefanoBorini, хорошо. Я собрал половину награды и два голоса, поэтому, если вы сейчас или в будущем захотите назначить награду за вопрос, напишите комментарий здесь, и я назначу награду в 50 баллов за этот вопрос (но только если я могу) т написать полезный ответ на него :-)), @Juraj