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

    

PIC32MZ - Проблемы. I2C

 

Работает не стабильно. Как правило зависает на передаче байта данных I2C1TRN = pcfByte, не выходит из  следующего за командой цикла while(I2C1STATbits.TRSTAT) . Шина данных SDA уходит в "ноль" там и остаётся до выключения питания.  Частота шины 100кГц.  Никто не сталкивался с проблемой? 

 

    I2C1TRN = ((0x21 << 1) | I2C_WRITE);
    
    while(I2C1STATbits.TRSTAT) { ; } // wait for the transmission to finish
    
    if(!I2C1STATbits.ACKSTAT) { // if this is high, slave has not acknowledged
        
        I2C1TRN = pcfByte;
        
        while(I2C1STATbits.TRSTAT) { ; } // wait for the transmission to finish
        
        if(!I2C1STATbits.ACKSTAT) { // if this is high, slave has not acknowledged
            
            I2C1CONbits.PEN = 1; // comm is complete and master relinquishes bus
            
            while(I2C1CONbits.PEN) { ; }
            
            continue;
        }
    }
 

Изменено пользователем amischuk@bk.ru

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


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

Исходя из фразы "Работает не стабильно" я бы сказал что проблема в коммуникации: частота не верная, длинные связи или подтягивающие резаки "большие".

Больше деталей! Сколько слейвов,один? Кто это? Как это подключено? Частоту  проверяли? Как запитано?

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


Ссылка на сообщение
Поделиться на другие сайты
1 hour ago, Integro said:

Исходя из фразы "Работает не стабильно" я бы сказал что проблема в коммуникации: частота не верная, длинные связи или подтягивающие резаки "большие".

Больше деталей! Сколько слейвов,один? Кто это? Как это подключено? Частоту  проверяли? Как запитано?

 

Частота на шине SCL 100кГц, но пробовал менять вплоть до 40кГц, меняя значение I2C1BRG.  Питание 3.3V, пробовал подтягивающие резисторы от 1,8 ком., сейчас 4.7.  Запись идет через преобразователь уровня на PCF8574. Данные по приведённому коду записываются в регистр корректно.  Цикл отрабатывает 15-30 секунд, потом возникает ситуация, что шина данных SDA не отпущена. Моя версия - видимо PCF8574 что-то не отрабатывает и контроллер жизнерадостно подвисает на цикле ожидания окончания передачи. Или контроллер не отправляет должный сигнал.

 

 

 

 

20181019_131714_800.jpg

 

 

sc.gif.4d6fce990a445452c1dcb93297f9474a.gif

Изменено пользователем amischuk@bk.ru

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


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

Да, согласен все ОК!
Тогда предлагаю добавить проверку TBF, как-то так:

    ...
    I2C1TRN = pcfByte;       

    while(I2C1STATbits.TBF);    //wait for data transmission
    while(I2C1STATbits.TRSTAT) { ; } // wait for the transmission to finish
      
    if(!I2C1STATbits.ACKSTAT) { // if this is high, slave has not acknowledged
      
    ...

 

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


Ссылка на сообщение
Поделиться на другие сайты
On 10/19/2018 at 6:51 PM, Integro said:

Да, согласен все ОК!
Тогда предлагаю добавить проверку TBF, как-то так:


    ...
    I2C1TRN = pcfByte;       

    while(I2C1STATbits.TBF);    //wait for data transmission
    while(I2C1STATbits.TRSTAT) { ; } // wait for the transmission to finish
      
    if(!I2C1STATbits.ACKSTAT) { // if this is high, slave has not acknowledged
      
    ...

Добрый вечер!

Спасибо за помощь. В примерах исходников я видел и пробовал использовать для проверки статуса - I2C1STATbits.TBF. Не помогает. На периферию думать не могу, к данной схеме подключается микроконтроллер с Линукс. В бесконечном цикле запускаю штатную i2cset, работает сутками. В шоке. Думаю переписать аль не переписать аппаратный модуль pic32mz. 

 

 

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


Ссылка на сообщение
Поделиться на другие сайты
On 10/19/2018 at 6:51 PM, Integro said:

 


    ...
    I2C1TRN = pcfByte;       

    while(I2C1STATbits.TBF);    //wait for data transmission
    while(I2C1STATbits.TRSTAT) { ; } // wait for the transmission to finish
      
    if(!I2C1STATbits.ACKSTAT) { // if this is high, slave has not acknowledged
      
    ...

 

Проблема  c I2C обрела некоторую ясность. Вначале передаётся адрес периферийного устройства, следом данные. 

rc = I2C1Send(((0x21 << 1) | I2C_WRITE));

rc = I2C1Send(pcfByte);

 

Две команды идут одна за другой. После передачи адреса устройства, периферия выдаёт ASK, прижимает шину данных к земле. Модуль обработки I2C процессора это понял и отработал корректно,  сбросив и установив необходимые флаги, программа продолжила выполнение. С этого момента возникает проблема. Процессор быстрый и он проваливается в цикл передачи следующего байта не дождавшись пока периферийное устройство отпустит шину данных. У аппаратуры модуля I2C контроллера полностью сносит голову  когда он видит  на шине данных ноль, при этом шина SCL поднята.  В том числе отмечается и отказы в работе таймера. Пока решение проблемы выглядит так (двое суток без проблем):

 

Delay10uS(1);
            
rc = I2C1Send(((0x21 << 1) | I2C_WRITE));
            
Delay10uS(10);
            
rc = I2C1Send(pcfByte);

 

Но по хорошему надо дождаться пока периферийное устройство отпустит шину данных. Или самому анализировать линию данных, но в режиме I2C доступ к шине SDA вероятно будет заблокирован.  Delay10uS(10) - десять раз по десять uS

 

void Delay10uS(int num)
{
    T2CON = 0x8000; //enable TMR2 with 1:1   prescaler
   
    while (num > 0) //wait for specified number of multiples of 10 microseconds
    {
        TMR2 = 0;
        
        while( TMR2 < (10000000/10000));  //wait 10 microseconds
        
        num = num-1;
    }
}

 

 

Изменено пользователем amischuk@bk.ru

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


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

Не нужно переписывать, нужно просто убрать вечные while, то-есть добавить таймауты на ожидание статуса, и в случае проблемы просто генерировать стоп (или старт-стоп) и возвращать ошибку либо повторить транзакцию с начала.

Вообще, такие проблемы можно наблюдать при плохом подключении (со звоном), но здесь на осциллограмме ничего криминального, возможно, что-то проскакивает в момент работы.
Тот факт, что это работает с Linux еще не значит что с железом все - ОК, это лишь может говорить о том, что драйвер Linux и i2cset корректно обрабатывают ошибки.

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


Ссылка на сообщение
Поделиться на другие сайты
4 hours ago, Integro said:

Не нужно переписывать, нужно просто убрать вечные while, то-есть добавить таймауты на ожидание статуса, и в случае проблемы просто генерировать стоп (или старт-стоп) и возвращать ошибку либо повторить транзакцию с начала.

Вообще, такие проблемы можно наблюдать при плохом подключении (со звоном), но здесь на осциллограмме ничего криминального, возможно, что-то проскакивает в момент работы.
Тот факт, что это работает с Linux еще не значит что с железом все - ОК, это лишь может говорить о том, что драйвер Linux и i2cset корректно обрабатывают ошибки.

 

Вход в повторный цикл передачи данных пока  прижата к земле  линией SDA - ACK периферии,   даёт сбой в работе таймера,  PIC32MZ не выходит из цикла - while( TMR2 < (10000000/10000));  Поэтому пока оставил проверки статуса в бесконечном цикле. 

 

Корректная инициализация модуля I2C возможна если сбросить SDA/SCL  линии в нуль.  При отладке, в момент "ошибки", достаточно SСL посадить на землю, затем её отпустить, работа будет продолжена. Но пока включен I2C1 модуль I2C1CONbits.I2CEN = 1, доступа к значениям регистра LATD  RD9/RD10  у меня нет.

 

 

Полностью проблему можно решить, анализируя линии SDA/SCL по завершению приёма или передачи данных.

 

 

void I2C1Init(void)
{
    I2C1CONbits.I2CEN = 0; //Disable until everything set up is complete
    
    TRISD &= 0xFFFFF9FF;
    LATD  &= 0xFFFFF9FF;


    I2C1BRG = 0x220;                // I2CBRG = [1/(2*Fsck) - PGD]*Pblck - 2
    
    I2C1CONbits.SIDL   = ENABLED;
    I2C1CONbits.SCLREL = DISABLED;
    I2C1CONbits.STRICT = DISABLED;
    I2C1CONbits.A10M   = DISABLED; //7-bit address mode
    I2C1CONbits.DISSLW = DISABLED;
    I2C1CONbits.SMEN   = DISABLED;
    I2C1CONbits.GCEN   = DISABLED;
    I2C1CONbits.ACKDT  = DISABLED; //send NACK when requested

    //Clearing the recieve and transmit buffers
    I2C1RCV = 0x0000;
    I2C1TRN = 0x0000;

    I2C1CONbits.I2CEN = 1;  //Enable I2C1 module, pins will be taken care of by module; no need for peripheral pin select
    
    //IEC1bits.MI2C1IE = 1; //Enable I2C master interrupt if you want to use interrupts
}

Изменено пользователем amischuk@bk.ru

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


Ссылка на сообщение
Поделиться на другие сайты
1 hour ago, amischuk@bk.ru said:

Но пока включен I2C1 модуль I2C1CONbits.I2CEN = 1, доступа к значениям регистра LATD  RD9/RD10  у меня нет

да, все верно нужно просто "клокнуть" :) или как я писал сгенерить стоп или старт\стоп но затем начать транзакцию c начала

    I2C1CONbits.SEN = 1; 
    /*send stop*/         
    I2C1CONbits.PEN = 1;         
    while(I2C1CONbits.PEN);     

 

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


Ссылка на сообщение
Поделиться на другие сайты
3 minutes ago, Integro said:

да, все верно нужно просто "клокнуть" :) или как я писал сгенерить стоп или старт\стоп но затем начать транзакцию c начала


    I2C1CONbits.SEN = 1; 
    /*send stop*/         
    I2C1CONbits.PEN = 1;         
    while(I2C1CONbits.PEN);     

 

 

3 minutes ago, Integro said:

да, все верно нужно просто "клокнуть" :) или как я писал сгенерить стоп или старт\стоп но затем начать транзакцию c начала


    I2C1CONbits.SEN = 1; 
    /*send stop*/         
    I2C1CONbits.PEN = 1;         
    while(I2C1CONbits.PEN);     

 

I2C1CONbits.PEN = 1;       

while(I2C1CONbits.PEN);  

Этими командами всегда завершаю передача данных,  но увы,  команда завершается корректно,  но не поднимает прижатую к земле шину данных.

 

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


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

Года два назад была такая я же проблема, этот фикс лечил ситуацию, только вот точно не скажу толи там был просто стоп, толи повторный старт затем стоп. Эти операции по сути просто дергают SCL. Хотя давно это было может тоже без задержки не обошелся).
 

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


Ссылка на сообщение
Поделиться на другие сайты
29 minutes ago, Integro said:

Года два назад была такая я же проблема, этот фикс лечил ситуацию, только вот точно не скажу толи там был просто стоп, толи повторный старт затем стоп. Эти операции по сути просто дергают SCL. Хотя давно это было может тоже без задержки не обошелся).
 

 

Да. Всё верно. Достаточно дернуть шину SCL. Спасибо! Попробую!!

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


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

 

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

 

1. Добавлено адаптивное увеличение времени задержки между следующими друг за другом командами записи по шине i2c.

2. При возникновении ошибок на шине I2C1STATbits.BCL выполняется повторная инициализация модуля i2c,  которая включает в себя сброс SDA/SCL в ноль.

 

Исходные тексты записи I2C в пот PCF8574:

http://tesla.zabotavdome.ru/pic32/src/pic32mz_i2c.X.zip

 

Принципиальная схема прототипа:

http://tesla.zabotavdome.ru/pic32.html

 

 

Всем успехов!

 

 

pic32mz_i2cf800x600.jpg

 

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


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

Для публикации сообщений создайте учётную запись или авторизуйтесь

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

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти
Авторизация