sidy 1 18 июля, 2022 Опубликовано 18 июля, 2022 · Жалоба Добрый день! Возникла проблема при работе связки микроконтроллера PIC18 и микросхемы памяти/часов FM31256-g по I2C. Для пробы решил сделать обмен с помощью поллинга. Скажем, когда обращаемся к часам посылаем команду D0h. Иногда обмен проходит нормально, а иногда почему-то линия SCL опускается и больше не поднимается. В опросе я соответственно ожидаю, пока ACKSTAT1==1 и это подтверждение так и не наступает. В чем может быть дело? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
k155la3 27 18 июля, 2022 Опубликовано 18 июля, 2022 · Жалоба Что означает "обмен с помощью поллинга" применительно к I2C ? До "опускается и не поднимается" Ваш slave не дал ACK. Или ему не дали. Схема подключения по I2C "стандартная" ? Или "соптимизированная" ? Какие резисторы подтяжки к Vcc. Настройка периферийного узла I2C сделана верно ? ps (для экономии электронов и наносекунд). В разрыв линии SCL поставьте резистор 30-50 Ом, для проверки, с какой стороны, master-а или slave -а, выдается "уровень" нуля (сравнить падение напряжения на выводах резистора). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Smen 3 19 июля, 2022 Опубликовано 19 июля, 2022 · Жалоба Несколько оффтоп, но по осциллографу мало что поймёшь. Лучше использовать логический анализатор с дешифровщиком необходимого протокола. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
byRAM 24 19 июля, 2022 Опубликовано 19 июля, 2022 · Жалоба 4 часа назад, Smen сказал: Несколько оффтоп, но по осциллографу мало что поймёшь. Что там понимать, простейшая шина I2C, логический анализатор тут не нужен, а вот схема и программа нужны таки. Очень похоже на некорректную конфигурацию I2C. Тактовая PIC18 так же интересна. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Smen 3 19 июля, 2022 Опубликовано 19 июля, 2022 · Жалоба В 19.07.2022 в 10:33, byRAM сказал: логический анализатор тут не нужен Кому как. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
sidy 1 19 июля, 2022 Опубликовано 19 июля, 2022 · Жалоба Вообщем, разобрался были некоторые ошибки в обмене. Нашел похожий пример в интернете и сделал следующее для начала операции чтения из часов. void I2C_Hold(void) { while ((SSPCON2 & 0b00011111)||(SSPSTAT & 0b00000100)) ; //check the this on registers to make sure the I2C is not in progress } void I2C_Begin() { I2C_Hold(); //Hold the program is I2C is busy SEN = 1; // Start } void I2C_End() { I2C_Hold(); //Hold the program is I2C is busy PEN = 1; // Stop } void I2C_Write(char data) { I2C_Hold(); //Hold the program is I2C is busy SSPBUF = data; } void I2C_Write_One (char rtcadress, char rtcdata) { I2C_Begin(); I2C_Write(0xD0); I2C_Write(rtcadress); I2C_Write(rtcdata); I2C_End(); } I2C инициализируется следующим образом: SSP1CON1 = 0x28; // MSSP модуль Включен, Режим Master SSP1ADD = 9; // F=Fosc/(4*(SSPADD+1)) (Fosc = 10 MHz) Теперь обмен работает стабильно, такого поведения как в первом сообщении нет. НО! почему-то иногда slave после 8-ого такта не дает подтверждения АСК. См. осц. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
k155la3 27 19 июля, 2022 Опубликовано 19 июля, 2022 (изменено) · Жалоба Изучить работу I2C наугад выдергивая из интернета примеры - занятие малопродуктивное. Да, "что-то" и "как-то" будет работать. Возможно даже без заметных сбоев или даже без сбоев - как повезет. Именно I2C, в отличие от SPI. Те фрагменты кода, IMHO - неудачный вариант. В протоколе I2C может быть штатная ситуация, когда отсутствует ACK (not acknowledge, NACK). Легкое для понимания описание работу с NACK - не припомню. Подразумевает раскуривание стандарта протокола шины. Возьмите у Microchip примеры кода или готовый драйвер, первое что попалось Getting Started with I2C Using MSSP on PIC18 pdf Цитата НО! почему-то иногда . . . Так может случиться, когда slave ("автомат" управления шиной I2C) вводится предшествующими посылками (уж и не знаю как) в нештатный (или такой штатный) режим. Нештатный - это может быть жесткий завес, из которого выход по перевключению питания slave-а (зависит от конкретного slave, наблюдалось на AТ24Cxx непомню каком). Штатный - если, например, из slave считываются данные, и он (slave) не должен выставлять подтверждение, а наоборот, это должен сделать master - поскольку данные принимает он. Кроме того, это может произойти по аппаратной причине (вопросы выше, на которые не было ответа) ps неплохое описание основ у DI HALT Интерфейсная шина IIC (I2C) Easy Electronics Цитата Если Slave торомоз и не успевает (у EEPROM, например, низкая скорость записи), то он может насильно положить линию SCL в землю и не давать ведущему генерировать новые такты. Мастер должен это понять и дать слейву прожевать байт. Так что нельзя тупо генерить такты, при отпускании SCL надо следить за тем, что линия поднялась. Если не поднялась, то надо остановиться и ждать до тех пор, пока Slave ее не отпустит. Изменено 19 июля, 2022 пользователем k155la3 дополнение ps Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
=AK= 18 20 июля, 2022 Опубликовано 20 июля, 2022 · Жалоба On 7/20/2022 at 5:23 AM, sidy said: почему-то иногда slave после 8-ого такта не дает подтверждения АСК. См. осц. Например, если в предыдущей операции не было STOP-a. У вас в коде нигде не проверяется, был выдан STOP или нет. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
sidy 1 21 июля, 2022 Опубликовано 21 июля, 2022 · Жалоба On 7/20/2022 at 2:35 AM, k155la3 said: Возьмите у Microchip примеры кода или готовый драйвер, первое что попалось Getting Started with I2C Using MSSP on PIC18 pdf Попробовал использовать данный пример: // The INTERRUPT function waits for the SSP1IF flag to be triggered by the hardware and clears it: // Функция INTERRUPT ожидает, пока аппаратное обеспечение активирует флаг SSP1IF и очищает его static void I2C1_interruptFlagPolling(void) { while (!PIR1bits.SSP1IF){}; // Polling Interrupt Flag PIR1bits.SSP1IF = 0; // Clear the Interrupt Flag } // The OPEN function prepares an I2C operation: Resets the SSP1IF flag and enables the SSP1 module: // Функция OPEN подготавливает работу I2C: сбрасывает флаг SSP1IF и включает модуль SSP1: static void I2C1_open(void) { PIR1bits.SSP1IF = 0; // Clear IRQ SSP1CON1bits.SSPEN = 1; // I2C Master Open } // The CLOSE function disables the SSP1 module // Функция CLOSE отключает модуль SSP1 static void I2C1_close(void) { SSP1CON1bits.SSPEN = 0; // Disable I2C1 } // The START function sends the Start bit by setting the SEN bit and waits for the SSP1IF flag to be triggered // Функция START отправляет бит Start, устанавливая бит SEN, и ожидает срабатывания флага SSP1IF static void I2C1_start(void) { SSP1CON2bits.SEN = 1; // Start Condition I2C1_interruptFlagPolling(); } // The STOP function sends the Stop bit and waits for the SSP1IF flag to be triggered // Функция STOP отправляет стоп-бит и ожидает срабатывания флага SSP1IF static void I2C1_stop(void) { SSP1CON2bits.PEN = 1; // Stop Condition I2C1_interruptFlagPolling(); } // The SEND DATA function loads in SSP1BUF the argument value and waits for the SSP1IF flag to be triggered: // Функция SEND DATA загружает в SSP1BUF значение аргумента и ожидает срабатывания флага SSP1IF: static void I2C1_sendData(unsigned char byte) { SSP1BUF = byte; I2C1_interruptFlagPolling(); } // The GET_ACKSTAT_BIT function returns the ACKSTAT bit from the SSP1CON2 register: // Функция GET_ACKSTAT_BIT возвращает бит ACKSTAT из регистра SSP1CON2: static unsigned char I2C1_getAckstatBit(void) { return SSP1CON2bits.ACKSTAT; // Return ACKSTAT bit } static void I2C1_write1ByteRegister(unsigned char address, unsigned char reg, unsigned char data) { /* Shift the 7-bit address and add a 0 bit to indicate a write operation */ //signed char writeAddress = (address << 1) & ~I2C_RW_BIT; signed char writeAddress = address; I2C1_open(); I2C1_start(); I2C1_sendData(writeAddress); if (I2C1_getAckstatBit()) { return ; } I2C1_sendData(reg); if (I2C1_getAckstatBit()) { return ; } I2C1_sendData(data); if (I2C1_getAckstatBit()) { return ; } I2C1_stop(); I2C1_close(); } При использовании данных функций вернулся к примерно тому что было в первом сообщении - периодически линия SCL опускается, и поднимается не на следующий период обмена (вызов ф-ии I2C1_write1ByteRegister()), а через несколько периодов: (желтый - SCL, синий - SDA) Нормальный обмен Сбой в конце первого байта Период сбоя Еще не понятна строка кода, я ее закомментировал. Зачем сдвигать адрес? signed char writeAddress = (address << 1) & ~I2C_RW_BIT; Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 241 21 июля, 2022 Опубликовано 21 июля, 2022 · Жалоба В 21.07.2022 в 15:19, sidy сказал: Еще не понятна строка кода, я ее закомментировал. Зачем сдвигать адрес? signed char writeAddress = (address << 1) & ~I2C_RW_BIT; Очевидно что для того, чтобы вставить бит чтения/записи, который передаётся в первом слове I2C-транзакции. Странно - как Вы писали какую-то работу с I2C не зная такой фундаментальной вещи о нём? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
sidy 1 21 июля, 2022 Опубликовано 21 июля, 2022 · Жалоба On 7/21/2022 at 4:03 PM, jcxz said: Очевидно что для того, чтобы вставить бит чтения/записи, который передаётся в первом слове I2C-транзакции. Странно - как Вы писали какую-то работу с I2C не зная такой фундаментальной вещи о нём? Чтобы обратиться к часам я должен передать адрес 0xD0. Если я сдвину этот адрес на 1 влево, то получу 0хА0. И микросхема не воспримет этот адрес как верный. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 241 21 июля, 2022 Опубликовано 21 июля, 2022 · Жалоба В 21.07.2022 в 16:22, sidy сказал: Чтобы обратиться к часам я должен передать адрес 0xD0. Если я сдвину этот адрес на 1 влево, то получу 0хА0. И микросхема не воспримет этот адрес как верный. Вы невнимательно читали мануал. Прочитайте внимательнее: 0xDx - это уже сдвинуто. А в том примере, который Вы где-то нарыли, очевидно - используются несдвинутые адреса. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
=AK= 18 21 июля, 2022 Опубликовано 21 июля, 2022 · Жалоба On 7/21/2022 at 10:52 PM, sidy said: Чтобы обратиться к часам я должен передать адрес 0xD0. Если я сдвину этот адрес на 1 влево, то получу 0хА0. И микросхема не воспримет этот адрес как верный. Вы ошибаетесь, 0xD0 это не I2C адрес. Это значение, которое вы передаете при записи в RTCC, при чтении вам надо передавать значение 0xD1. А I2C адрес RTCС в вашем устройстве 0x68, он одинаковый и при чтении, и при записи, как и положено быть адресу. Вот этот адрес и надо сдвинуть влево на один разряд, а младший бит оставить нулем при записи или установить в единицу при чтении. Что и сделано в той строке кода, которая вам непонятна. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
sidy 1 21 июля, 2022 Опубликовано 21 июля, 2022 · Жалоба On 7/21/2022 at 4:39 PM, jcxz said: 0xDx - это уже сдвинуто. А в том примере, который Вы где-то нарыли, очевидно - используются несдвинутые адреса. On 7/21/2022 at 4:44 PM, =AK= said: Вы ошибаетесь, 0xD0 это не I2C адрес. Это значение, которое вы передаете при записи в RTCC, при чтении вам надо передавать значение 0xD1. А I2C адрес RTCС в вашем устройстве 0x68, он одинаковый и при чтении, и при записи, как и положено быть адресу. Вот этот адрес и надо сдвинуть влево на один разряд, а младший бит оставить нулем при записи или установить в единицу при чтении. Что и сделано в той строке кода, которая вам непонятна. Понятно. Спасибо. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
sidy 1 21 июля, 2022 Опубликовано 21 июля, 2022 · Жалоба Стало понятно почему так происходит. Есть функция проверки подтверждение slave: // Функция GET_ACKSTAT_BIT возвращает бит ACKSTAT из регистра SSP1CON2: static unsigned char I2C1_getAckstatBit(void) { return SSP1CON2bits.ACKSTAT; // Return ACKSTAT bit } Она вызывается if (I2C1_getAckstatBit()) { return ; } Т.е. периодически нет подтверждения от slave. Осталось только понять почему это происходит. Причем это происходит не хаотично, а всегда после отправки 0xD0. I2C1_sendData(writeAddress); Т.е. возможно как тут уже писал ув. =AK= некорректно проверяется STOP условие? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться