Jump to content

    
Sign in to follow this  
Nosaer

Проблема с I2C в STM32F4

Recommended Posts

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

К МК подключено 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.

 

Share this post


Link to post
Share on other sites

Тайм-ауты на чтение, и сброс 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. Разные устройства могут сбрасываться разными способами.

Share this post


Link to post
Share on other sites
3 часа назад, Nosaer сказал:

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

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

Цитата

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

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

Цитата

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

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

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

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

Share this post


Link to post
Share on other sites

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

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

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

Share this post


Link to post
Share on other sites
22 минуты назад, x893 сказал:

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

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

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

Share this post


Link to post
Share on other sites
28 minutes ago, jcxz said:

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

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

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

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

Share this post


Link to post
Share on other sites
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();
}

 

Share this post


Link to post
Share on other sites

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

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

Share this post


Link to post
Share on other sites

Я бы ещё увеличил число допустимых итераций (значение, при достижении которого счётчиком 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;
}

 

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites
2 часа назад, Aner сказал:

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

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

Share this post


Link to post
Share on other sites

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

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

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

Share this post


Link to post
Share on other sites

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

 

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

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

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

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

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

Share this post


Link to post
Share on other sites
On 12/29/2019 at 6:15 AM, Nosaer said:

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

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

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

 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this