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

Доброго времени суток.

К МК подключено 2 микросхемы, с которыми он общается по средствам I2C.  Запрос к обеим идет раз в секунду

В процессе работы прилетает два типа косяков, которые могут себя проявить как через 10 часов работы, так и через 30. Поймать их отладчиком или анализатором довольно проблемно.

При первом я просто получаю единоразово не корректные данные. При втором косяке полное зависание шины I2C.

При первом косяке регистры: CR1 = 0x201 , SR1 = 0,  SR2 = 0x03

При втором косяке регистры: CR1 = 0x301 , SR1 = 0x200,  SR2 = 0x02

Случай с полным зависанием,  удалось поймать анализатором пару раз.  Видно,  что не выставляется NAK по окончанию передачи и линия SDA остается прижатой к земле.

В ерата на этот контроллер есть пункт 2.4.2, где описывается схожая проблема.  Рекомендуют проводить Reset  I2C. Но здесь или я чего не так делаю, или проблема не совсем та. В общем проблема никуда не уходит

Возможно кто то сталкивался с таким или у кого есть мысли по решению проблемы?! Так же интересует каков правильный алгоритм для сброса I2C.

 

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


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

Тайм-ауты на чтение, и сброс i2c в случае зависания. Помогает не всегда. Желательно иметь возможность отключить питание устройств i2c.

Вот один из вариантов сброса:

void reset2()
{
	// turn off i2c peripheral
    RCC->APB1ENR &= ~RCC_APB1ENR_I2C1EN;
    __DSB();

	// reset i2c peripheral
    RCC->APB1RSTR |= RCC_APB1RSTR_I2C1RST;
    RCC->APB1RSTR &= ~RCC_APB1RSTR_I2C1RST;
    __DSB();

    // configure pins as outputs (disconnect it from i2c peripheral)
    SCL::Mode(OUTPUT_OD_2MHZ);
    SDA::Mode(OUTPUT_OD_2MHZ);
    SCL::On();
    SDA::Off();

    OS::sleep(60); // hold SDA low for more than 50ms to reset I2C bus
}

Был ещё другой вариант, там надо было выдавать клоки до тех пор, пока слейв не отпустит SDA. Разные устройства могут сбрасываться разными способами.

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


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

3 часа назад, Nosaer сказал:

Случай с полным зависанием,  удалось поймать анализатором пару раз.  Видно,  что не выставляется NAK по окончанию передачи и линия SDA остается прижатой к земле.

Не выставляется кем?

Цитата

В ерата на этот контроллер есть пункт 2.4.2, где описывается схожая проблема.

Почему проблему ищете в errata, а не в своём коде? Или в схемотехнике линий подключения слэйвов?

Цитата

Возможно кто то сталкивался с таким или у кого есть мысли по решению проблемы?! Так же интересует каков правильный алгоритм для сброса I2C.

Не сталкиваюсь. Есть устройство на STM32F4 где на I2C висит 5 слэйвов и работает оно сутками без зависаний. Так что искать нужно явно не в errata.

Также можно уменьшить подтяжки, проверить провода и можно сделать SCL пуш-пульным (если уверены что нет clock stretching-а).

Если всё-же зависает интерфейс I2C-слэйва, то сбросить его можно переведя ноги SCL/SDA в режим GPIO и сформировав на на них ногодрыгом 15 раз условие "СТОП".

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


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

У NXP лет 15 назад был AN как нужно инициализировать i2c.

Найдите в гугле и сделайте как там написано и будет Вам СЧАСТЬЕ.

Примерно как AHTOXA пишет в конце. Но чуть сложнее.

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


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

22 минуты назад, x893 сказал:

Примерно как AHTOXA пишет в конце. Но чуть сложнее.

Если у ТСа его слэйв это что-то типа RAM-памяти, которая при достижении конца адресного пространства перескакивает на его начало; и ТС пишет в неё; и глюк происходит из-за какого-то глитча на SCL из-за которого произошла рассинхронизация числа клоков между мастером и слэйвом, то можно выдавать клоки хоть до посинения - не будет никакого толку - слэйв всегда будет выдавать ACK после очередных 8 клоков.

Надо выдавать не просто клоки, а многократно сгенерить СТОП-условие. Как я писал выше.

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


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

28 minutes ago, jcxz said:

Если у ТСа его слэйв это что-то типа RAM-памяти, которая при достижении конца адресного пространства перескакивает на его начало; и ТС пишет в неё; и глюк происходит из-за какого-то глитча на SCL из-за которого произошла рассинхронизация числа клоков между мастером и слэйвом, то можно выдавать клоки хоть до посинения - не будет никакого толку - слэйв всегда будет выдавать ACK после очередных 8 клоков.

Надо выдавать не просто клоки, а многократно сгенерить СТОП-условие. Как я писал выше.

В документе 15 летней давности от NXP всё это прописано и пример есть для гуру программирования.

Лень искать и в гугле меня забанили (ирония).

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


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

1 час назад, x893 сказал:

У NXP лет 15 назад был AN как нужно инициализировать i2c.

Найдите в гугле и сделайте как там написано и будет Вам СЧАСТЬЕ.

Примерно как AHTOXA пишет в конце. Но чуть сложнее.

Да, именно про метод от NXP я и и написал:

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

Был ещё другой вариант, там надо было выдавать клоки до тех пор, пока слейв не отпустит SDA.

Но почему-то этот метод не срабатывал с моим устройством (какой-то термодатчик был).

На всякий случай вот как он у меня выглядел:

void reset()
{
	// turn off i2c peripheral
	RCC->APB1ENR &= ~RCC_APB1ENR_I2C1EN;
	__DSB();

	// reset i2c peripheral
	RCC->APB1RSTR |= RCC_APB1RSTR_I2C1RST;
	RCC->APB1RSTR &= ~RCC_APB1RSTR_I2C1RST;
	__DSB();

	// configure pins as outputs (disconnect it from i2c peripheral)
	SCL::Mode(OUTPUT_OD_2MHZ);
	SDA::Mode(OUTPUT_OD_2MHZ);
	SCL::On();
	SDA::On();

	// feed clock to slave until it releases SDA
	for (int i = 0; i < 20; ++i)  // 9 should be enough
	{
		Hdel();
		if (SDA::Signalled())
			break;
		SCL::Off();
		Hdel();
		SCL::On();
	}
		
	// generate "stop"
	SDA::Off();
	Hdel();
	SCL::On();
	qdel();
	SDA::On();
	Hdel();
}

 

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


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

AHTOXA

практически в одном шаге от рекомендаций NXP (авторов i2c)

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


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

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

On 12/29/2019 at 1:52 PM, jcxz said:

Не выставляется кем?

Не выставляется мастером. Хотя я больше склоняюсь, что ведомый удерживает линию.

On 12/29/2019 at 1:52 PM, jcxz said:

Почему проблему ищете в errata, а не в своём коде? Или в схемотехнике линий подключения слэйвов?

Сначала использовал HAL. После того, как столкнулся с проблемой переписал все на CMSIS. Проблема осталась та же. Поэтому исходник я в какой то степени исключил.

Плюс у меня есть проекты на МК F0 и F1 серии, и там я тоже использую I2C и подобных проблем у меня нет.

On 12/29/2019 at 11:01 AM, AHTOXA said:

Желательно иметь возможность отключить питание устройств i2c.

Питание слейвов отключаю. Делаю сброс как в вашем первом сообщении. В итоге на 2 или 3 измерении показаний опять зависает в том же месте.  Опять делаю сброс.  Опять хватает на 2-3 измерения и опять зависает. 

Итого после примерно 20 сбросов, начинает работать. Через сколько опять зависнет  остается загадкой.

Сейчас пробую второй предложенный вами способ. Тактировать линию, пока ведомый не отпустит SDA.

 

Собственно мой код, где я выставляю Nak при чтении

	for(register uint16_t Counter = 0; Counter < Size; Counter++)
	{
		if(Counter < (Size-1))
		{
			EmergencyCycleCounter = 0;
			while(!READ_BIT(I2C1->SR1, I2C_SR1_RXNE))
			{
				EmergencyCycleCounter++;
				if(EmergencyCycleCounter > 1000)		break;
			}
			pData[Counter] = READ_BIT(I2C1->DR, I2C_DR_DR);
		}
		else
		{
			CLEAR_BIT(I2C1->CR1, I2C_CR1_ACK);
			SET_BIT(I2C1->CR1, I2C_CR1_STOP);
			EmergencyCycleCounter = 0;
			while(!READ_BIT(I2C1->SR1, I2C_SR1_RXNE))
			{
				EmergencyCycleCounter++;
				if(EmergencyCycleCounter > 1000)		break;
			}
			pData[Counter] = READ_BIT(I2C1->DR, I2C_DR_DR);
		}
	}

 

I2C.png

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


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

Я бы ещё увеличил число допустимых итераций (значение, при достижении которого счётчиком EmergencyCycleCounter вы выпрыгиваете из циклов ожидания RXNE). Потому что F4 обычно быстрее, чем F1 и F0. У меня лично там даже на F0 число 500000 :-)

bool waitSR1(uint16_t flag)
{
	for (uint32_t timeout = 500000; timeout; --timeout)
		if (I2C1->SR1 & flag)
			return true;
	return false;
}

 

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


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

Что-то похожее на вашу проблему было в моем проекте с 417 процем пару лет назад. Через сутки из сотни устройств у двух подвисли I2C шины, у одного кривые данные. Все в компактном носимом устойстве, питание от бат. Равлизованый метод от NXP помог, но частично. Проблема была и аппатратной также, после пере разводки платы, проблем не было.  

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


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

2 часа назад, Aner сказал:

у двух подвисли I2C шины, у одного кривые данные

Общение с I2C через куб? Достался мне от коллеги проект (правда, на L072) с общением с датчиками по I2C через кубовые функции. Все вроде бы работает нормально, но очень редко и непредсказуемо вдруг оба датчика одновременно как-будто сходят с ума и начинают гнать чушь до снятия питания. Совершенно случайно такой отказ произошел у меня на столе под отладчиком. Что выяснилось: куб по какой-то причине вышел из функции чтения не считав принятый байт (возможно, по таймауту). При следующем чтении он сначала вычитывал оставшийся байт, потом пять байтов из новой транзакции и шестой снова оставлял в регистре приема. Таким образом, все принятые данные сдвигались на один байт. Никакого сброса периферии I2C в случае ошибки там не предусмотрено. Не стал пытаться это исправлять, просто перевел все на свои самописные функции.

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


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

Попробуйте посмотреть сигнал осциллографом, а не анализатором.

На приведенной картинке мне не нравится момент переключения сигнала SDA. Он должен быть когда SCL равен нулю. А на вашей картинке похоже, что SCL переключается в ноль и SDA изменяется одновременно.

Попробуйте задержать изменение SDA на середину интервала нуля SCL.

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


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

С наступившим Новым годом!

 

Использовал аппаратный I2C в STM32 почти всех серий и калибров (кроме F7/H7, Gx), в различных режимах Master/Slave, с и без DMA.

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

Контролировать зависание надо как в Master-, так и Slave-режимах. В режиме Master, бывало, Slave затыкался. Приходилось делать по NXP-шному документу.

Только я не просто SCL болтал, а сначала болтал, а потом STOP-ов столько же выдавал, тут @jcxz правильную мысль озвучил.

Но самый эпик, если Slave, поддерживающий Clock Stretching, растягивал линию SCL и в таком положении зависал на бесконечно долгое время. Тут только Power Reset.

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


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

On 12/29/2019 at 6:15 AM, Nosaer said:

Доброго времени суток.

При первом я просто получаю единоразово не корректные данные. При втором косяке полное зависание шины I2C.

Тема I2C с STM32 стара как мир. Серии F10x и F4xx имеют схожие I2C (для F0xx их переделали). Здесь ссылка на мой пост (один из многих) на тему. Надеюсь, поможет.

 

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


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

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

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

Гость
Ответить в этой теме...

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

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

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

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

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

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