Jump to content

    

STM32F1 и i2c зависает шина

Добрый день. 

Есть утройство у которого на шине i2c висит 3 слейва, все в приделах одной платы. Часы, ээпром и дисплей. Так вот, основная часть логики завязана с роботой еепром. Программа довольно много обращается к еепром и иногда бывают сбои. Пишу код под Coide и готового решения AN2824 для него я не нашел.

Собственно проблема в том, что если читаю 1 или 2 байта читается какой-то мусор, а если больше 3 то и вовсе виснет еепром и шина всегда занята, даже если перезагружаю i2c на контроллере. С записью проблем нет. DMA не использую так как приходится много читать для поиска пустого блока, проверку делаю "на лету" без выгрузки всего муссора с eeprom в озу. Второй день уже не могу понять в чем проблема, может чего уже не замечаю, вот код.

unsigned char I2C_Read_git(I2C_TypeDef* I2Cx, unsigned char SlaveAddress, unsigned char *buf, unsigned int nbyte){
	//I2C2->CR2 |= I2C_IT_ERR;  //interrupts for errors
	// Wait for idle I2C interface
	if(wait_for(FLAG, I2C_FLAG_BUSY) == 0) return I2CERR; //go out with timeout error
	// Enable Acknowledgement, clear POS flag
	I2C_AcknowledgeConfig(I2Cx, ENABLE);
	I2C_NACKPositionConfig(I2Cx, I2C_NACKPosition_Current);

	// Intiate Start Sequence (wait for EV5
	I2C_GenerateSTART(I2Cx, ENABLE);
	if(wait_for(EVENT, I2C_EVENT_MASTER_MODE_SELECT) == 0) return I2CERR; //go out with timeout error

	// Send Address
	I2C_Send7bitAddress(I2Cx, SlaveAddress, I2C_Direction_Receiver);

	// EV6
	if(wait_for(FLAG, I2C_FLAG_ADDR) == 0) return I2CERR; //go out with timeout error
	if(nbyte == 1){
		// Clear Ack bit
		I2C_AcknowledgeConfig(I2Cx, DISABLE);

		// EV6_1 -- must be atomic -- Clear ADDR, generate STOP
		__disable_irq();
		(void) I2Cx->SR2;
		I2C_GenerateSTOP(I2Cx,ENABLE);
		__enable_irq();

		// Receive data   EV7
		if(wait_for(FLAG, I2C_FLAG_RXNE) == 0) return I2CERR; //go out with timeout error
		*buf++ = I2C_ReceiveData(I2Cx);

    }else if(nbyte == 2){
    	// Set POS flag
    	I2C_NACKPositionConfig(I2Cx, I2C_NACKPosition_Next);

    	// EV6_1 -- must be atomic and in this order
    	__disable_irq();
    	(void) I2Cx->SR2;                           // Clear ADDR flag
    	I2C_AcknowledgeConfig(I2Cx, DISABLE);       // Clear Ack bit
    	__enable_irq();

    	// EV7_3  -- Wait for BTF, program stop, read data twice
    	if(wait_for(FLAG, I2C_FLAG_BTF) == 0) return I2CERR; //go out with timeout error

    	__disable_irq();
    	I2C_GenerateSTOP(I2Cx,ENABLE);
    	*buf++ = I2C_ReceiveData(I2Cx);
    	__enable_irq();
    	*buf++ = I2C_ReceiveData(I2Cx);

    	I2C_NACKPositionConfig(I2Cx, I2C_NACKPosition_Current);
    }else{
    	(void) I2Cx->SR2;                           // Clear ADDR flag
    	while (nbyte-- != 3){
    		// EV7 -- cannot guarantee 1 transfer completion time, wait for BTF
    		//        instead of RXNE
        	if(wait_for(FLAG, I2C_FLAG_BTF) == 0) return I2CERR; //go out with timeout error
    		*buf++ = I2C_ReceiveData(I2Cx);
    	}

    	if(wait_for(FLAG, I2C_FLAG_BTF) == 0) return I2CERR; //go out with timeout error

    	// EV7_2 -- Figure 1 has an error, doesn't read N-2 !
    	I2C_AcknowledgeConfig(I2Cx, DISABLE);           // clear ack bit

    	__disable_irq();
    	I2C_GenerateSTOP(I2Cx,ENABLE);              // program stop
		*buf++ = I2C_ReceiveData(I2Cx);             // receive byte N-2
    	__enable_irq();

    	// wait for byte N
    	if(wait_for(EVENT, I2C_EVENT_MASTER_BYTE_RECEIVED) == 0) return I2CERR; //go out with timeout error
    	*buf++ = I2C_ReceiveData(I2Cx);             // receive byte N-1
    	nbyte = 0;
    }

	// Wait for stop
	if(wait_for(FLAG, I2C_FLAG_STOPF) == 0) return I2CERR; //go out with timeout error
	I2C_AcknowledgeConfig(I2Cx, ENABLE);

	return I2COK;
}
unsigned char EEPROM_Read(unsigned int addr, unsigned char *data, unsigned char len){
	unsigned char y=0;
	// While the bus is busy
	 if(wait_for(FLAG, I2C_FLAG_BUSY) == 0) return I2CERR; //go out with timeout error
	 I2C_AcknowledgeConfig(I2C2, ENABLE);
	 // Generate the Start Condition
	 I2C_GenerateSTART(I2C2, ENABLE);
	 if(wait_for(EVENT, I2C_EVENT_MASTER_MODE_SELECT) == 0) return I2CERR; //go out with timeout error

	 I2C_Send7bitAddress(I2C2, 0xA0, I2C_Direction_Transmitter);     //send adress chip
	 if(wait_for(EVENT, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == 0) return I2CERR; //go out with timeout error

	 I2C_SendData(I2C2, addr>>8); 	//send upper bit
	 if(wait_for(EVENT, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == 0) return I2CERR; //go out with timeout error

	 I2C_SendData(I2C2, addr & 0xFF);		//send lower bit
	 if(wait_for(EVENT, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == 0) return I2CERR; //go out with timeout error

	 I2C_GenerateSTOP(I2C2, ENABLE);
	 if(wait_for(FLAG, I2C_FLAG_BUSY) == 0) return I2CERR; //go out with timeout error

	 y=I2C_Read_git(I2C2, 0xA0, data, len);
	 return y;
}

А вот код который работает, но здесь нет всех замечаний которые были написаны в AN2824 о глючном i2c в stm32f1:

unsigned char EEPROM_Read(unsigned int addr, unsigned char *data, unsigned char len){
	 unsigned int i;
	 // While the bus is busy
	 if(wait_for(FLAG, I2C_FLAG_BUSY) == 0) return I2CERR; //go out with timeout error
	 I2C_AcknowledgeConfig(I2C2, ENABLE);
	 // Generate the Start Condition
	 I2C_GenerateSTART(I2C2, ENABLE);
	 if(wait_for(EVENT, I2C_EVENT_MASTER_MODE_SELECT) == 0) return I2CERR; //go out with timeout error

	 I2C_Send7bitAddress(I2C2, 0xA0, I2C_Direction_Transmitter);     //send adress chip
	 if(wait_for(EVENT, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == 0) return I2CERR; //go out with timeout error

	 I2C_SendData(I2C2, addr>>8); 	//send upper bit
	 if(wait_for(EVENT, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == 0) return I2CERR; //go out with timeout error

	 I2C_SendData(I2C2, addr & 0xFF);		//send lower bit
	 if(wait_for(EVENT, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == 0) return I2CERR; //go out with timeout error

	 I2C_GenerateSTOP(I2C2, ENABLE);
	 if(wait_for(FLAG, I2C_FLAG_BUSY) == 0) return I2CERR; //go out with timeout error

	 I2C_GenerateSTART(I2C2, ENABLE);   // Generate the Start Condition
	 if(wait_for(EVENT, I2C_EVENT_MASTER_MODE_SELECT) == 0) return I2CERR; //go out with timeout error

	 I2C_Send7bitAddress(I2C2, 0xA0, I2C_Direction_Receiver);		//send adress chip
	 if(wait_for(EVENT, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) == 0) return I2CERR; //go out with timeout error

	 for(i = 0; i<len; i++){
		 if(i<len-1){
		 	if(wait_for(EVENT, I2C_EVENT_MASTER_BYTE_RECEIVED) == 0) return I2CERR; //go out with timeout error
		 }else{
		 	I2C_AcknowledgeConfig(I2C2, DISABLE);			// Prepare an NACK for the next data received
		 	if(wait_for(EVENT, I2C_EVENT_MASTER_BYTE_RECEIVED) == 0) return I2CERR; //go out with timeout error
		 	I2C_GenerateSTOP(I2C2, ENABLE);
		 }
		 *data = I2C_ReceiveData(I2C2);
		 data++;
	 }
	 if(wait_for(FLAG,I2C_FLAG_STOPF)==0) return I2CERR;
	 I2C_AcknowledgeConfig(I2C2, ENABLE);
	 //Delay_ms(5);
	 return I2COK;
}

За ранние спасибо.

Edited by Krik99

Share this post


Link to post
Share on other sites

Ну то, что можно порекомендовать без привязки к конкретному микроконтроллеру, так это подключить осциллограф, и посмотреть форму сигналов на шине. А затем, если с ней всё в порядке, можно поглядеть, что с данными.

Share this post


Link to post
Share on other sites
5 hours ago, haker_fox said:

Ну то, что можно порекомендовать без привязки к конкретному микроконтроллеру, так это подключить осциллограф, и посмотреть форму сигналов на шине. А затем, если с ней всё в порядке, можно поглядеть, что с данными.

Конкретный контроллер Stm32f103rbt6. Но на сколько я понял с AN2824 i2c у них у всех глючный. Ок, как будет доступ к осциллографу сделаю скрины

Edited by Krik99

Share this post


Link to post
Share on other sites
11 minutes ago, Krik99 said:

i2c у них у всех глючный

что это значит? Возможно в errata есть какая-то полезная информация. Возможно придётся всё отлаживать по-шагово, проверяя выполнение всех условий на шине (START, STOP, ACK, NACK) и т.п. Ну и снова повторюсь, лучше попробовать написать свой драйвер. Пусть медленно, зато знакомый до каждого угла)

Share this post


Link to post
Share on other sites
20 hours ago, Krik99 said:

а если больше 3 то и вовсе виснет еепром и шина всегда занята, даже если перезагружаю i2c на контроллере

В данном случае висит один из слейвов, вероятно eeprom, нужно в ручную(gpio) клочить и ждать пока этот слейв отпустить линию SDA, либо дернуть питание слейвам, если это конечно возможно и позволяет ситуация.
 

В  I2C_Read_git точно должно быть 3?

while (nbyte-- != 3){

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

Вообще, стремно выглядит этот I2C_Read_git...
 

Share this post


Link to post
Share on other sites
8 minutes ago, Integro said:

вероятно eeprom, нужно в ручную(gpio) клочить

В своё время я где-тона просторах сети встречало документ, в котором рекомедовалось при инициализации шины подать клок не менее 30 тактов на SCL. Что должно быть при этом с SDA я не помню. И только после этого можно было начинать работу. Вообще такое мне ни разу не понадобилось. Ассортимент микросхем с которыми я работал на этой шине не очень широк, но всё же: pca9534, ds1682, рахные часы, разные цифрорезисторы. И все они нормально работали сразу. Но вот с tsc2007 (контроллер резистивного тач-скрина) в нескольких приборах была проблема: он просто иногда (редко) при включении прибора не работал. Не отвечал. Логический анализатор показывал, что к микросхеме стучаться правильно. Не выручили и хололстые клоки на SCL. А вот передёргивание питания - выручило. Допускаю, что мы могли накосячить со схемотехникой, хотя где там можно ошибиться, я не знаю. Мы для этого приспособили полевичок на питании микросхемы, и просто при включении прибора включаем микросхему после инициализации шины. Работает надёжно. С первопричиной же пробовали разобраться, так и не вышло. В документации ничего на этот счёт не сказано. На форумах тоже тишина. В другом приборе на линуксе такой проблемы, похоже, нет. Но там драйвер этой микросхемы подробно никто не смотрел.

Share this post


Link to post
Share on other sites
17 hours ago, haker_fox said:

Ассортимент микросхем с которыми я работал на этой шине не очень широк

От сюда следствие...

17 hours ago, haker_fox said:

Вообще такое мне ни разу не понадобилось.

Не понимаю для каких целей Вы нам это написали...
 

17 hours ago, haker_fox said:

он просто иногда (редко) при включении прибора не работал.

А вот это не нормально, я бы грешил на ошибку в схемотехнике(проблемы с качесвом питания) или не соблюдение рекомендуемых Power-On\Reset timings

Share this post


Link to post
Share on other sites
10 minutes ago, Integro said:

Не понимаю для каких целей Вы нам это написали...

А что, чтобы делиться своим опытом, я должен разрешение спрашивать?

 

Share this post


Link to post
Share on other sites
5 minutes ago, haker_fox said:

чтобы делиться своим опытом

17 hours ago, haker_fox said:

Ассортимент микросхем с которыми я работал на этой шине не очень широк,

Так Вы ведь написали что опыта у Вас как такового нет. Простите если задел.

Share this post


Link to post
Share on other sites
1 minute ago, Integro said:

Так Вы ведь написали что опыта у Вас как такового нет.

Я написал, что ассортимент микросхем не очень широк. С опытом это прямо не связано.  Ну да, ещё, конечно же, забыл упомянуть разномастную память, начиная от 24c02, заканчивая какими-то современными от микрочип EEPROM (названия не помню). Но разве это меняет суть? Подход-то одинаковый. И про документ я написал. Я вообще сейчас заметил, что здесь обращался не к автору темы. Перепутал.

4 minutes ago, Integro said:

Простите если задел.

Всё нормально, я искренне удивился. Тема-то для начинающих. Я и подумал. что автору будет полезен мой скромный опыт.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now