Получение байтов i2c на attiny85
На прошлой неделе я пытался написать небольшую библиотеку i2c на C. Мне удалось заставить работать запись на ведомые устройства (например, я могу писать на oled ssd1306), но чтение (например, дата/время с ds3231) никогда не работает, поскольку мои ведомые устройства, похоже, не устанавливают высокий уровень SDA, когда я запрашиваю байты.
uint8_t
i2c_read(uint8_t ack)
{
uint8_t i, val;
val = 0;
cbi(SCL);
DDRB &= ~(1 << SDA); /* Use SDA for input. */
cbi(SDA); /* We need to pull down SDA to receive input. */
/* Read 8bit from the slave. */
_delay_us(DELAY);
for (i = 0; i < 8; ++i) {
sbi(SCL); /* Request one more bit. */
_delay_us(DELAY); /* Wait a little so the slave can send his answer. */
if (PORTB & (1 << SDA)) { /* Here the slave should have set SDA to 1 but it never does. */
PORTB |= (1 << PB4);
val = (val << 1) | 1;
} else {
val = (val << 1);
}
cbi(SCL);
_delay_us(DELAY);
}
/* Send back an ACK or NAK. */
DDRB |= 1 << SDA; /* Use SDA for writing. */
if (ack)
cbi(SDA); /* Set SDA to 0 to send ACK. */
else
sbi(SDA); /* Set SDA to 1 to send NAK. */
sbi(SCL);
_delay_us(DELAY);
cbi(SCL);
cbi(SDA);
return val;
}
Я устанавливаю PB4 with в 1, когда ведомое устройство фактически отправляет 1. По-видимому, этого никогда не происходит. Я также посмотрел, как это делают различные другие библиотеки, но я не заметил ничего, что имело бы значение.
Есть какие-нибудь подсказки о том, что я мог упустить? Перед чтением моя программа отправляет в шину адрес ведомого. Также: пожалуйста, не говорите мне использовать другие библиотеки, так как это учебный проект.
@kessler bebe, 👍1
Обсуждение1 ответ
Не уверен, что это единственная проблема, но вы читаете не из того регистра.
С цифровым выходным контактом связаны 3 регистра: DDR, PORT и PIN. Чтобы прочитать вывод, вам нужно прочитать из регистра вывода. В настоящее время вы читаете регистр PORT, который всегда будет иметь только то значение, которое вы установили (в данном случае ноль, потому что вы заранее очистили бит с помощью cbi(SDA);
). Когда вы настраиваете контакт в качестве входа с помощью регистра DDR, регистр PORT определяет, активирован ли внутренний подтягивающий резистор или нет, поэтому он не может отражать фактическое состояние контакта. Для этого вам понадобится пин.
Помимо этого, вы можете захотеть изучить еще одну вещь, которая может быть или не быть проблемой в вашем случае: I2C — это шина с открытым стоком, что означает, что она управляется активно низким и пассивно высоким (через подтягивающие резисторы). . Я предполагаю, что у вас уже есть внешние подтягивающие резисторы на ваших линиях, так что вы действительно можете деактивировать внутреннюю подтяжку, не прерывая связь. Но представьте следующую ситуацию: подчиненный получает запрос, но не может так быстро его обработать (по какой-то причине). Он может переводить часы в НИЗКИЙ уровень, пока не будет готов вытолкнуть бит данных, замедляя тактовую частоту. (Это можно сделать для каждого бита, если только не в скоростном режиме, то только до начала нового байта). (Это поведение называется растяжением часов - спасибо Эдгару за информацию) Вы не контролируете линию, поэтому может случиться так, что вы активно переводите линию часов в высокий уровень, прежде чем ведомое устройство освободит ее. Это приводит к значительному току между ведущим и ведомым, так как ведущий активирует высокий уровень, а подчиненный - низкий уровень.
Существуют реализации связи I2C, которые просто делают это и нормально работают в большинстве ситуаций. Я просто хотел дать вам эту информацию. И если вы в какой-то момент захотите внедрить арбитраж шины (чтобы разрешить настройку с несколькими ведущими), вам определенно понадобится это поведение открытого стока.
Ре. ведомое устройство тянет SCL на низкий уровень: я бы добавил, что это называется «растягиванием часов», просто чтобы указать ключевое слово для поиска., @Edgar Bonet
Вы правы, я совершенно забыл упомянуть об этом, хотя я думал, что сделал это. Я добавлю эту информацию к ответу, @chrisl
Спасибо за информацию! Я не знал, что есть еще один регистр для чтения входных контактов. Но это имеет смысл, так как PORT используется для подтягивания/опускания в режиме ввода. У меня в цепи есть подтягивающие резисторы. На самом деле они интегрированы в ведомые микросхемы i2l. Я также жду, пока ведомое устройство перестанет сбрасывать SCL (растягивание часов). Теперь у меня та же проблема, но ведомый не сбрасывает SDA (я думаю), поэтому мой SDA всегда высокий (я читаю из PINB). Это довольно запутанно. Есть ли у вас какие-либо идеи о том, что я могу делать неправильно?, @kessler bebe
Изменить: у меня есть два ведомых устройства i2c, оба из которых имеют внутренний подтягивающий резистор. Возможно ли, что они как-то мешают? Я не очень разбираюсь в электронике., @kessler bebe
- Библиотеки I2C для ATTiny85?
- длина провода i2c
- I2C запрашивает более одного байта
- Всегда 255-ответ в I2C между ATTiny85 (8 МГц) и Arduino Uno
- Связь AtTiny44 и AtTiny 85
- Работа с основной библиотекой Wire
- Соединение ATTiny85 I2C с Arduino Nano
- Digispark Rev. 3 Kickstarter ATtiny85 использует все 6 контактов
Привет и добро пожаловать в сообщество Arduino.SE. Спасибо за ваш вопрос. Пожалуйста, добавьте в свой вопрос более подробную информацию о том, как это относится к Arduino. Ваше здоровье., @sa_leinad
Линия часов действительно становится высокой? Ведомый может перевести часы вниз, чтобы приостановить связь, пока она не будет готова. Можете ли вы показать нам вывод осциллографа или логического анализатора? Чтобы мы могли видеть, что именно происходит на линиях?, @chrisl