Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Зависает I2C
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > ARM, 32bit
pokk
Добрый день, не как не могу понять почему виснет I2C в stm32f103, запускается работает работает потом бац зависло.

CODE
//==============================================================================
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 взводится, но как только начинаешь проверку он сбрасывается и соответственно там всё виснет.
pokk
Сил уже нету с i2c разбираться, ни в какую он не хочет стабильно работать.
Уже весь проект перелопатил все сторонние воздействия от кода исключил, осталась только одна функция отправки данных по i2c которая крутится в цикле. Проблема в следующем программа всё так же зависает на строчке
Код
while (!(I2C2->SR1 & I2C_SR1_ADDR)){};

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

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: В момент зависания на осциллограмме видно, что стартовый бит выставился, а вот адрес даже не начал передаваться.
esaulenka
У меня тоже нестабильно работало.
Подумал-подумал, и решил, что бит сброса I2C в периферии не просто так.

Все ожидания - не while (), а циклы с таймаутом. При превышении таймаута - сброс периферии и повторная попытка (до трёх штук).
Работает, на помехи в линии и прочие тыканья пинцетом по контактам реагирует адекватно.
pokk
Цитата(esaulenka @ Nov 13 2015, 11:07) *
У меня тоже нестабильно работало.
Подумал-подумал, и решил, что бит сброса I2C в периферии не просто так.
Я уже тоже об этом думаю, но если бы он глючил, то это бы не зависело от кода особенно от наличия нопов.

У меня изначально было всё с таймаутами и тд. потом в процессе отладки всё по удалял.
gerber
Вполне возможно, что в какой-то момент на шине происходит ошибка (с точки зрения контроллера), например, фиксируется липовое "старт" условие посреди адреса (misplaced start condition), из-за близких фронтов и емкостных свойств линий. Поэтому ждать нужно не только ADDR бита, но и возможных битов ошибок, в особенности BERR. Проскакивание таких редких ошибок наблюдал лично на STM32F103.
То же самое касается слейва, с которым вы работаете, он тоже неидеален, и в какой-то момент по своим внутренним причинам можеть не подтвердить вам адрес (NACK), и вы до посинения будете его ожтдать в бесконечном цикле, так работать нельзя, конечно.
И ещё момент - работать поллингом с контроллером I2C крайне плохо, статусные регистры "не любят", когда их задалбывают чтениями и могут сбросить нужный бит ADDR "в глубине души". С этим я тоже сталкивался, правда, на других контроллерах, но "раз в год и палка стреляет".
Всегда нужно работать с прерываниями, как по основным, так и по ошибочным событиям, в обработчике прерывания один раз прочитать статусный регистр и последовательно разобрать в нём установленные биты. Всё.
x893
можно всегда посмотреть как в HAL или CPAL это делается. Без таймоута в поллинге конечно опасно это делать.
AHTOXA
Цитата(esaulenka @ Nov 13 2015, 12:07) *
Все ожидания - не while (), а циклы с таймаутом. При превышении таймаута - сброс периферии и повторная попытка (до трёх штук).

+1. Ещё полезно при затыке подёргать клоками, держа линию данных в единице, чтоб сбросились машины состояний устройств на шине.
А что там раскорячивается - фиг его знает. Слишком сложная периферия.
adnega
Errata sheet для I2C у F103 довольно толстый раздел.
Не пробовали там поискать причины?
x893
У NXP есть мануал как инициализировать I2C после ошибки/сброса
toweroff
Цитата(x893 @ Nov 13 2015, 13:54) *
У NXP есть мануал как инициализировать I2C после ошибки/сброса

а зачем оно? у ТС - STM32
AHTOXA
Цитата(toweroff @ Nov 13 2015, 18:30) *
а зачем оно? у ТС - STM32

А NXP (philips) придумали I2C sm.gif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.