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

Зависает I2C

Добрый день, не как не могу понять почему виснет I2C в stm32f103, запускается работает работает потом бац зависло.

 

//==============================================================================
PT_THREAD(Send_data_fsm(struct pt *pt,unsigned char data1,unsigned char data2)){
static unsigned char data[2];
//---------------------------------------------------------------------------
   if(pt->lc!=0){ //Не первый запуск
         if(GetTimers(I2C_TimeOut)>TIMEOUT_I2C){
             Send_MSG(MSG_I2C_REBOOT);
             ResetTimers(I2C_TimeOut);
         }
   }
//---------------------------------------------------------------------------
PT_BEGIN(pt);
   ResetTimers(I2C_TimeOut);
data[0]=data1;
data[1]=data2;
   /* initiate start sequence */
while(TakeMutex(I2C_MUTEX)){PT_YIELD(pt);}
   I2C_GenerateSTART(I2C2, ENABLE);
   /* check start bit flag */
   while(!I2C_GetFlagStatus(I2C2, I2C_FLAG_SB)){PT_YIELD(pt);};
   /*send write command to chip*/
   I2C_Send7bitAddress(I2C2, I2C_ADDR_LCD_KEY_LED, I2C_Direction_Transmitter);
   /*check master is now in Tx mode*/
//--------------------------------------------------------------------------
   while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){};  //Spead=400KHz delay=1000 clock   Spead=1KHz delay=65000 clock
   /*mode register address*/
   I2C_SendData(I2C2, data[0]);
//--------------------------------------------------------------------------
   /*wait for byte send to complete*/
   while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)){PT_YIELD(pt);};  //3600 Speed=100Khz
   /*clear bits*/
   I2C_SendData(I2C2, data[1]);
//--------------------------------------------------------------------------
   /*wait for byte send to complete*/
   while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)){PT_YIELD(pt);};
//--------------------------------------------------------------------------
   /*generate stop*/
   I2C_GenerateSTOP(I2C2, ENABLE);
while(I2C_GetFlagStatus(I2C2, I2C_FLAG_STOPF)){PT_YIELD(pt);};
GiveMutex(I2C_MUTEX);
PT_END(pt);
}
//==============================================================================

 

 

Виснет на проверке:

while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){};  //Spead=400KHz delay=1000 clock

I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED содержит флаги( BUSY, MSL, ADDR, TXE, TRA)

Более глубокое копание показало что всё дело в флаге ADDR.

Изменение куска кода на

    I2C2->CR1 |= I2C_CR1_START;
         while (!(I2C2->SR1 & I2C_SR1_SB)){};
         (void) I2C2->SR1;
    I2C2->CR1|=I2C_CR1_ACK;
         I2C2->DR = I2C_ADDR_LCD_KEY_LED;
         while (!(I2C2->SR1 & I2C_SR1_ADDR)){};
         (void) I2C2->SR1;
         (void) I2C2->SR2;

Нечего не дало всё так же может пару тысяч раз отправить байт потом повиснуть на проверке I2C_SR1_ADDR.

         while (!(I2C2->SR1 & I2C_SR1_ADDR)){};

При пошаговой отладке в IAR заметил что до while (!(I2C2->SR1 & I2C_SR1_ADDR)){}; флаг ADDR взводится, но как только начинаешь проверку он сбрасывается и соответственно там всё виснет.

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


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

Сил уже нету с i2c разбираться, ни в какую он не хочет стабильно работать.

Уже весь проект перелопатил все сторонние воздействия от кода исключил, осталась только одна функция отправки данных по i2c которая крутится в цикле. Проблема в следующем программа всё так же зависает на строчке

 while (!(I2C2->SR1 & I2C_SR1_ADDR)){};

Причём это происходит совсем неявно т.е может спокойно передаться 1000 посылок, потом зависнуть, а в следующий раз (после рестарта) штук 50 передаться. Так же выявил, что стабильность передачи зависит от задержки между пакетами, и вот тут начинается самое весёлое. без задержки всё работает(отправляется пакетов больше 10000) потом при какой-то магической задержки перестаёт работать(количество отправленных пакетов <100 в основном 15-60), а при увеличения её продолжает работать. Так эти задержки относятся и коду т.е если за комментировал какое-то условие(для строба синхронизации) то работает обратно вернул не работает :shock:

int main(void){
GPIO_InitTypeDef  GPIO_InitStructure;
I2C_InitTypeDef  I2C_InitStructure;
//==============================================================================
//I2C_Setup_fsm(&ModeDisplayUpdate);


RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB| RCC_APB2Periph_AFIO , ENABLE);//

/* I2C2 SDA and SCL configuration */
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10|GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
//GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
//GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/*SCL is pin06 and SDA is pin 07 for I2C2*/


   GPIO_StructInit(&GPIO_InitStructure);
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
   GPIO_Init(GPIOB, &GPIO_InitStructure);

//I2C_DeInit(I2C2);
//	I2C_Cmd(I2C2,DISABLE);
//	GPIO_DeInit(GPIOB);
//	GPIO_AFIODeInit();

/* I2C2 configuration */
I2C_StructInit(&I2C_InitStructure);
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 = 0x00;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = I2C_SPEED ;
I2C_Init(I2C2, &I2C_InitStructure);

// I2C2->CR1&=~I2C_CR1_SMBUS; // I2C MODE enable
// I2C2->CR2|=I2C_CR2_FREQ_2; // Peripheral clock frequency 36MHz
// I2C2->CR2|=I2C_CR2_FREQ_4; // Peripheral clock frequency 36MHz
// I2C2->CCR&=~I2C_CCR_CCR; // Clear CCR bit field
// I2C2->CCR|=0x64; // CCR=I2C_freq_clock/(2*I2C_freq_speed) = 36000000/(2*100000) - just for standart MODE (not fast) {see Ref.M. p.752}
// I2C2->CCR&=~I2C_CCR_FS; //Standart Mode I2C
// I2C2->TRISE|=20; // rise_time = 1000ns, T_pclk1=28ns (1/36000000) => TRISE=rise_time/T_pclk1 {see Ref.M. p.753}
// I2C2->CR1|=I2C_CR1_PE;// peripheral enable
// I2C_AcknowledgeConfig(I2C2, ENABLE);

//------------------------------------------------------------------------------
/*enable I2C*/
//I2C_Cmd(I2C2,ENABLE);
//==============================================================================
//==============================Главный цикл====================================
//==============================================================================
   while(1){
//---------------------------------------------------------------------------
//I2C2->CR1|=I2C_CR1_PE;// peripheral enable
//--------------Cтроб для синхронизации с осциллографом(поиска места обрыва)--
	if(TEST_delay123>=CounterPascet-1){
		TEST_DELAY_OFF
	}
//---------------------------------------------------------------------------
	I2C2->CR1 |= I2C_CR1_START;
//---------------------------------------------------------------------------
	while (!(I2C2->SR1 & I2C_SR1_SB)){};
	//EV5: SB=1, cleared by reading SR1 register followed by writing DR register with Address.
	(void) I2C2->SR1;
	I2C2->DR = I2C_ADDR_LCD_KEY_LED;
//----------------------------------------------------------------------------------------------
	while (!(I2C2->SR1 & I2C_SR1_ADDR)){};
	//EV6:ADDR=1, cleared by reading SR1 register followed by reading SR2.
	(void)I2C2->SR1;
	(void)I2C2->SR2;
	while (!(I2C2->SR1 & I2C_SR1_TXE)){};
	//EV8_1:TxE=1, shift register empty, data register empty, write Data1 in DR.
	I2C2->DR = 0xAA;
//----------------------------------------------------------------------------------------------
	while (!(I2C2->SR1 & I2C_SR1_TXE)){};
	//EV8:TxE=1, shift register not empty, dataregister empty, cleared by writing DR register
	I2C2->DR = 0xff;
//----------------------------------------------------------------------------------------------
	while (!(I2C2->SR1 & I2C_SR1_TXE)){};
	while (!(I2C2->SR1 & I2C_SR1_BTF)){}
	//EV8_2:TxE=1, BTF = 1, Program Stop request. TxE and BTF are cleared by hardware by the Stop condition
	I2C2->CR1 |= I2C_CR1_STOP;
//----------------------------------------------------------------------------------------------
	while(I2C_GetFlagStatus(I2C2, I2C_FLAG_STOPF));
//--------------Cтроб для синхронизации с осциллографом(поиска места обрыва)--------------------
       if(TEST_delay123>=CounterPascet-1){
           TEST_DELAY_ON
       }
	//I2C2->CR1&=~I2C_CR1_PE;// peripheral disable
	//----------МАГИЧЕСКИЕ ЗАДЕРЖКИ-------------------------------------------------------------
	//----------Сделал увеличение что бы было сразу было понятно работает или нет---------------
        delay123=TEST_delay123;
        //delay123=27;
        //delay1=1;
        //delay123=60;
        //delay123=11;
	 while(delay123--);
	 TEST_delay123++;
        if(TEST_delay123>=0xFFFF){TEST_delay123=0xFFFF;}
////------------------------------------------------------------------------------------------------
}
}

 

PS: В момент зависания на осциллограмме видно, что стартовый бит выставился, а вот адрес даже не начал передаваться.

Изменено пользователем pokk

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


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

У меня тоже нестабильно работало.

Подумал-подумал, и решил, что бит сброса I2C в периферии не просто так.

 

Все ожидания - не while (), а циклы с таймаутом. При превышении таймаута - сброс периферии и повторная попытка (до трёх штук).

Работает, на помехи в линии и прочие тыканья пинцетом по контактам реагирует адекватно.

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


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

У меня тоже нестабильно работало.

Подумал-подумал, и решил, что бит сброса I2C в периферии не просто так.

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

 

У меня изначально было всё с таймаутами и тд. потом в процессе отладки всё по удалял.

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


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

Вполне возможно, что в какой-то момент на шине происходит ошибка (с точки зрения контроллера), например, фиксируется липовое "старт" условие посреди адреса (misplaced start condition), из-за близких фронтов и емкостных свойств линий. Поэтому ждать нужно не только ADDR бита, но и возможных битов ошибок, в особенности BERR. Проскакивание таких редких ошибок наблюдал лично на STM32F103.

То же самое касается слейва, с которым вы работаете, он тоже неидеален, и в какой-то момент по своим внутренним причинам можеть не подтвердить вам адрес (NACK), и вы до посинения будете его ожтдать в бесконечном цикле, так работать нельзя, конечно.

И ещё момент - работать поллингом с контроллером I2C крайне плохо, статусные регистры "не любят", когда их задалбывают чтениями и могут сбросить нужный бит ADDR "в глубине души". С этим я тоже сталкивался, правда, на других контроллерах, но "раз в год и палка стреляет".

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

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


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

можно всегда посмотреть как в HAL или CPAL это делается. Без таймоута в поллинге конечно опасно это делать.

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


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

Все ожидания - не while (), а циклы с таймаутом. При превышении таймаута - сброс периферии и повторная попытка (до трёх штук).

+1. Ещё полезно при затыке подёргать клоками, держа линию данных в единице, чтоб сбросились машины состояний устройств на шине.

А что там раскорячивается - фиг его знает. Слишком сложная периферия.

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


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

Errata sheet для I2C у F103 довольно толстый раздел.

Не пробовали там поискать причины?

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


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

У NXP есть мануал как инициализировать I2C после ошибки/сброса

а зачем оно? у ТС - STM32

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


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

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

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

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

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

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

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

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

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

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