Проблемы с I2C и Wire.Available()

Я работаю над проектом, который требует связи между Arduino Due (Master) и FDC2214Q1 C to D IC (Slave), но есть некоторые различия: от того, работает ли код или нет, в зависимости от, казалось бы, случайных величин.

Я сузил проблему до функции, которую использую для чтения 2 байтов из подчиненного устройства. Код довольно стандартен и выглядит следующим образом:

// Читаем 2 байта из FDC по адресу
uint16_t FDC2214::read16FDC(uint16_t address) {

    uint16_t data;

    Wire.beginTransmission(_i2caddr);
    Wire.write(address);
    Wire.endTransmission(false); //перезапуск
    Wire.requestFrom(_i2caddr, (uint8_t) 2);

    while (!Wire.available());
    data = Wire.read();
    data <<= 8;

    while (!Wire.available());
    data |= Wire.read();
    Wire.endTransmission(true); //конец

    return data;
}

Кажется, код иногда зависает на while (!Wire.available()), и если я просто удалю этот шаг, я никогда не получу правильный идентификатор устройства.

Однако, если я просто отключу DUE, закрою Serial Monitor, а затем снова подключу и загрузю, все будет работать. Я просто загружаю и отключаю несколько раз, открываю и закрываю Serial Monitor, и иногда он работает отлично, а иногда зависает?

Есть идеи, что может быть причиной такой проблемы?

Код на основе этой библиотеки: https://github.com/zharijs/FDC2214/blob /master/src/FDC2214.cpp


Окончательный работающий код устраняет необходимость в цикле While:

uint16_t FDC2214::read16FDC(uint16_t address) {

    uint16_t data;
        Wire.beginTransmission(_i2caddr); //постановка в очередь подчиненного адреса
        Wire.write(address); //постановка адреса регистра в очередь/указание регистратора
        byte busStatus = Wire.endTransmission(); // передаем все данные в очереди и вызываем состояние STOP на шине I2C
        if(busStatus != 0x00)
        {
           Serial.print("Transmission Error....!");//ошибка передачи ждет здесь вечно
           while(1);
        }

        Wire.requestFrom(_i2caddr, (uint8_t) 2);     //это циклический код; когда прибудет 2 байта, цикл завершается
        data = Wire.read();   //читаем первый байт из буфера FIFO
        data = (data <<8)| Wire.read();   //формируются 16-битные данные
        Wire.endTransmission(true); //конец
        return data;
}

, 👍0

Обсуждение

какая длина проводов? какие значения подтягиваний вы используете?, @dandavis

В настоящее время у меня нет подтягиваний, так как есть только один ведущий/ведомый, но я понимаю, что, возможно, мне следует это сделать в качестве меры предосторожности. Сейчас добавлю немного и сообщу, если это что-то исправит. Спасибо @dandavis, @tyler a

Привет, @dandavis, я только что добавил подтягивания (1 км), и, похоже, они сработали. Но, конечно же, после нескольких сеансов открытия и закрытия последовательного монитора и отключения/подключения Arduino я смог воспроизвести проблему еще раз., @tyler a

ваш код отформатирован неправильно..... две командные строки после команд while не должны иметь отступов... отступ подразумевает, что две команды находятся внутри цикла while.... они не являются частью цикла while, @jsotola

@jsotola, этот комментарий мне никоим образом не помогает, я понимаю, как форматировать код, просто подумал, что, возможно, было бы проще подчеркнуть циклы while, о которых я говорил, выделив базовый код табуляцией., @tyler a

это именно то, что я говорю... код с вкладками НЕ является частью цикла while.... если вы хотите выделить раздел, поместите пустую строку, @jsotola


1 ответ


Лучший ответ:

1

Adafruit советует избегать использования Arduino Due. Одна из причин — шина I2C. Можете ли вы использовать другую плату Arduino? Например одна из плат MKR с процессором SAMD21.

На плате Due имеются подтягивающие резисторы для SDA и SCL, номиналы которых слишком малы. Иногда шина I2C с SDA и SCL работает, а иногда не работает вообще. Возможно, вам удастся удалить подтягивающие резисторы на SDA и SCL с платы.

Если возможно, используйте Wire1 с SDA1 и SCL1. У них вообще нет подтягивающих резисторов, поэтому вам нужно добавить подтягивающие резисторы 4 к7 к 3,3 В. При использовании библиотеки вам, возможно, придется изменить ее, чтобы она использовала «Wire1» вместо «Wire».

Вам нужно удалить их, они ничего не делают:

while (!Wire.available());    <- not doing something

Удалите Wire.endTransmission в конце.

Wire.endTransmission(true); <- удалить это
возврат данных;

Эта библиотека не использует библиотеку Wire должным образом.


Подтягивающие резисторы номиналом 1 к5 или иногда 1 кОм расположены в правом верхнем углу, рядом с контактами 20 и 21. Значение 1 к5 должно подойти, если других подтягивающих резисторов нет. Если на вашей плате есть резисторы номиналом 1 кОм, я предлагаю их удалить.

фото подтягивающих резисторов подтягивающие резисторы на схеме

Удалить их можно с помощью паяльника и большого количества припоя. После этого проверьте, чистая ли плата, например, с помощью лупы. Избегайте перегрева дорогой платы Arduino Due.

Для работы шины I2C необходимы подтягивающие резисторы. Нормальное значение — 4k7. Иногда на сенсорном модуле уже есть подтягивающие резисторы номиналом 10 кОм.

Если с I2C возникли проблемы, начните с использования коротких отдельных проводов для SDA и SCL. Если для шины I2C используется кабель, то проблема, вероятно, связана с кабелем, а не с подтягивающими резисторами.

,

Спасибо за заметку, к сожалению, у меня нет плат MKR, но есть несколько nano, которые я могу опробовать. Я также перейду на подтягивания 4k7, удалю упомянутые вами части и попробую. Я читал, что while(!Wire.available) не нужен, но решил, что это не повредит (видимо, может). Результат обновлю сегодня или завтра! Спасибо @Jot, @tyler a

Не добавляйте дополнительные 4k7 к SDA и SCL. На Due уже есть подтягивающие резисторы, но они слишком малы по номиналу. Добавление 4k7 ухудшает ситуацию. Только при использовании SDA1 и SCL1 требуется добавить (обычные) подтягивающие резисторы., @Jot

Попался, для Дуэ я их удалю, а для тестирования на Нано их тоже надо удалить? @Jot (мне тоже нужно бежать, но завтра утром обновлю это как можно скорее.), @tyler a

Датчик рассчитан на 3,3 В, а нано работает на 5 В. Мне не нравится эта комбинация, если только вы не используете переключатель уровня для sda и scl., @Jot

Насколько я понимаю, i2c на Nano имел напряжение 3,3 В, но если это не так, как вы говорите, можно ли было бы отключить внутренние подтяжки, а затем просто использовать 2k7 или 4k7 для 3,3 В?, @tyler a

@JonathanA **Nano:** Можно отключить внутренние подтягивающие резисторы после Wire.begin. FDC2214 повреждается при напряжении выше 5 В, а не при 3,6 В, как это делают многие датчики. Итак, с внешними подтягивающими резисторами от 4 к7 до 3,3 В это может сработать для теста, но мне это все равно не нравится. **Срок:** Чтобы удалить неправильные подтягивающие резисторы, понадобится паяльник. Это компонент smd рядом с контактами 20 и 21., @Jot

Я потрачу на это завтрашнее утро. Буду информировать вас о том, что я найду. Однако на Дуэ я постараюсь убрать слабые подтягивания и добавить свои внешние. Дам вам знать! Спасибо за ваши постоянные советы., @tyler a

@JonathanA Я добавил абзац, чтобы избежать телеграммы. Встроенные подтягивающие резисторы представляют собой специфическую проблему, но существует множество общих проблем для шины i2c., @Jot

Спасибо! Я опубликовал код, который на данный момент, кажется, решил мою проблему. Тем не менее я приму ваш ответ как правильный, поскольку он мне очень помог! Спасибо @Jot., @tyler a

@JonathanA, вам все равно нужно удалить строку над возвратом (последняя Wire.endTransmission). Прочтите об этом: https://www.arduino.cc/en/Reference/WireEndTransmission, его не следует использовать после вызова Wire.requestFrom., @Jot

Давайте [продолжим обсуждение в чате](https://chat.stackexchange.com/rooms/89747/discussion-between-jonathan-a-and-jot)., @tyler a