Перейти к содержанию
    

PIC18 сбои при работе I2C

Добрый день!

Возникла проблема при работе связки микроконтроллера PIC18 и микросхемы памяти/часов FM31256-g по I2C. Для пробы решил сделать обмен с помощью поллинга. Скажем, когда обращаемся к часам посылаем команду D0h. Иногда обмен проходит нормально,

2077783030_.png.ec0d44932936b8fa273815f8f3b7cefa.png

а иногда почему-то линия SCL опускается и больше не поднимается.

1204915456_.png.9c3364a2910749b13f812d14e5985c67.png

В опросе я соответственно ожидаю, пока ACKSTAT1==1 и это подтверждение так и не наступает. В чем может быть дело?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Что означает "обмен с помощью поллинга" применительно к I2C ?

До "опускается и не поднимается" Ваш slave не дал ACK. Или ему не дали.

Схема подключения по I2C "стандартная" ? Или "соптимизированная" ? Какие резисторы подтяжки к Vcc.

Настройка периферийного узла I2C сделана верно ?

ps (для экономии электронов и наносекунд).

В разрыв линии SCL поставьте резистор 30-50 Ом, для проверки, с какой стороны, master-а или slave -а, выдается "уровень" нуля (сравнить падение напряжения на выводах резистора).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Несколько оффтоп, но по осциллографу мало что поймёшь.

Лучше использовать логический анализатор с дешифровщиком необходимого протокола.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

4 часа назад, Smen сказал:

Несколько оффтоп, но по осциллографу мало что поймёшь.

Что там понимать, простейшая шина I2C, логический анализатор тут не нужен, а вот схема и программа нужны таки. Очень похоже на  некорректную конфигурацию I2C. Тактовая PIC18 так же интересна.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вообщем, разобрался были некоторые ошибки в обмене. Нашел похожий пример в интернете и сделал следующее для начала операции чтения из часов.

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-ого такта не дает подтверждения АСК. См. осц.

2065930455_..thumb.png.da42c250d6b03d0aee93c3b81ccd3069.png1306515849_..thumb.png.7a6196359159ade542d02552f2e59b95.png

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Изучить работу 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 ее не отпустит. 

 

Изменено пользователем k155la3
дополнение ps

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 7/20/2022 at 5:23 AM, sidy said:

почему-то иногда slave после 8-ого такта не дает подтверждения АСК. См. осц.

 

Например, если в предыдущей операции не было STOP-a. У вас в коде нигде не проверяется, был выдан STOP или нет.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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)

1030059020_.png.e2f3c78e56568fae1b789b28e42d17a1.png

Нормальный обмен

1623088275_.png.005ca491058e257c1cd173f4122a7744.png

Сбой в конце первого байта

1726601024_.png.fda2d1b101e27865eef91f47587a111d.png

Период сбоя

 

Еще не понятна строка кода, я ее закомментировал. Зачем сдвигать адрес?

signed char writeAddress = (address << 1) & ~I2C_RW_BIT;
    

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 21.07.2022 в 15:19, sidy сказал:

Еще не понятна строка кода, я ее закомментировал. Зачем сдвигать адрес?

signed char writeAddress = (address << 1) & ~I2C_RW_BIT;
    

Очевидно что для того, чтобы вставить бит чтения/записи, который передаётся в первом слове I2C-транзакции.

Странно - как Вы писали какую-то работу с I2C не зная такой фундаментальной вещи о нём?  :biggrin:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 7/21/2022 at 4:03 PM, jcxz said:

Очевидно что для того, чтобы вставить бит чтения/записи, который передаётся в первом слове I2C-транзакции.

Странно - как Вы писали какую-то работу с I2C не зная такой фундаментальной вещи о нём?  :biggrin:

Чтобы обратиться к часам я должен передать адрес 0xD0. Если я сдвину этот адрес на 1 влево, то получу 0хА0. И микросхема не воспримет этот адрес как верный.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 21.07.2022 в 16:22, sidy сказал:

Чтобы обратиться к часам я должен передать адрес 0xD0. Если я сдвину этот адрес на 1 влево, то получу 0хА0. И микросхема не воспримет этот адрес как верный.

Вы невнимательно читали мануал. Прочитайте внимательнее:

image.thumb.png.eba75c6fb80a7eba36e2236e3f476555.png

0xDx - это уже сдвинуто.

А в том примере, который Вы где-то нарыли, очевидно - используются несдвинутые адреса.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 7/21/2022 at 10:52 PM, sidy said:

Чтобы обратиться к часам я должен передать адрес 0xD0. Если я сдвину этот адрес на 1 влево, то получу 0хА0. И микросхема не воспримет этот адрес как верный.

Вы ошибаетесь, 0xD0  это не I2C адрес. Это значение, которое вы передаете при записи в RTCC, при чтении вам надо передавать значение 0xD1. А I2C адрес RTCС в вашем устройстве 0x68, он одинаковый и при чтении, и при записи, как и положено быть адресу. Вот этот адрес и надо сдвинуть влево на один разряд, а младший бит оставить нулем при записи или установить в единицу при чтении. Что и сделано в той строке кода, которая вам непонятна.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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, он одинаковый и при чтении, и при записи, как и положено быть адресу. Вот этот адрес и надо сдвинуть влево на один разряд, а младший бит оставить нулем при записи или установить в единицу при чтении. Что и сделано в той строке кода, которая вам непонятна.

Понятно. Спасибо.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Стало понятно почему так происходит. Есть функция проверки подтверждение 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 условие?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...