Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: переключение контента+TimeOut I2C
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
pokk
Добрый день, есть функция чтение температуры из термодатчика Lm75ad во время ожидания ожидания флага передаю управления другому процессу portYIELD(); Так вот если замкнуть линии I2C то процесс чтения температуры зависает, а остальное все работает. Так вот как по феншую сделать отработку зависания ? Пока только одна идея перед каждый ожидание засекать время, и в диспетчере смотреть больше оно таймаута или нет, но мне это не нравиться тем что это надо прятать в дефайны, что не айс, или код "загромождать". Если другие способы определения ?


uint8_t OS_Lm75ad_Read(short unsigned int* dataout,const unsigned char addr){
static short unsigned int tmp[2];
static short unsigned int* dataout_temp;
//------------------------------------------------------------------------------------
LED_DEBUG_ON
dataout_temp=dataout;
while(I2C_GetFlagStatus(I2C_LM75AD, I2C_FLAG_BUSY)){portYIELD();};
LED_DEBUG_OFF
//------------------------------------------------------------------------------------
I2C_GenerateSTART(I2C_LM75AD, ENABLE);
/* check start bit flag */
while(!I2C_CheckEvent(I2C_LM75AD, I2C_EVENT_MASTER_MODE_SELECT)){portYIELD();};
LED_DEBUG_ON
//----------------------SEND_ADDR_Write-------------------------------------------------------
I2C_Send7bitAddress(I2C_LM75AD,(addr), I2C_Direction_Transmitter);
while(!I2C_CheckEvent(I2C_LM75AD, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){portYIELD();};
//----------------------POINT------------------------------------------------------------------
I2C_SendData(I2C_LM75AD,LM75AD_ADDR_REG_Temp); //Регист Temp(температура)
while(!I2C_CheckEvent(I2C_LM75AD, I2C_EVENT_MASTER_BYTE_TRANSMITTED)){portYIELD();};
LED_DEBUG_ON
//----------------------RE-START-------------------------------------------------------------------
I2C_GenerateSTART(I2C_LM75AD, ENABLE);
/* check start bit flag */
while(!I2C_CheckEvent(I2C_LM75AD, I2C_EVENT_MASTER_MODE_SELECT)){portYIELD();};
LED_DEBUG_OFF
//-------------------Send_addr_read------------------------------------------------------
I2C_Send7bitAddress(I2C_LM75AD, (addr), I2C_Direction_Receiver);
while(!I2C_CheckEvent(I2C_LM75AD, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)){portYIELD();};
LED_DEBUG_ON
//------------------------------------------------------------------------------------------
while(!I2C_CheckEvent(I2C_LM75AD, I2C_EVENT_MASTER_BYTE_RECEIVED)){portYIELD();};
LED_DEBUG_OFF
tmp[0]=I2C_ReceiveData(I2C_LM75AD);
//------------------------------------------------------------------------------------------
while(!I2C_CheckEvent(I2C_LM75AD, I2C_EVENT_MASTER_BYTE_RECEIVED)){portYIELD();};
LED_DEBUG_ON
tmp[1]=I2C_ReceiveData(I2C_LM75AD);
//------------------------------------------------------------------------------------------
I2C_GenerateSTOP(I2C_LM75AD, ENABLE);
/*stop bit flag*/
I2C_AcknowledgeConfig(I2C_LM75AD, ENABLE);
while(I2C_GetFlagStatus(I2C_LM75AD, I2C_FLAG_BUSY)){portYIELD();};
LED_DEBUG_OFF
//------------------------------------------------------------------------------------------
*dataout_temp=(tmp[0]<<8)|tmp[1];
NOP;NOP;NOP;NOP;NOP;
LED_DEBUG_ON
//------------------------------------------------------------------------------------------
}
firew0rker
Cчeтчик с условием таймаута можно запрятать в функцию portYIELD, возвращать 0, если надо продолжать попытки, или не 0 если таймаут. Вызывать, например, так:

while ( !(!I2C_GetFlagStatus(I2C_LM75AD, I2C_FLAG_BUSY) || portYIELD()) );
//Если I2C_GetFlagStatus вернет 1, будет вызываться portYIELD. Цикл будет повторяться, пока portYIELD возвращает 0.

while( !(I2C_CheckEvent(I2C_LM75AD, I2C_EVENT_MASTER_MODE_SELECT) || portYIELD()) );
//Если I2C_CheckEvent вернет 0, будет вызываться portYIELD. Цикл будет повторяться, пока portYIELD возвращает 0.
pokk
А где перезапустить счетчик ? Что бы для последующих ожиданий флагов был новый отсчет таймаута.

firew0rker
Цитата(pokk @ May 30 2017, 10:37) *
А где перезапустить счетчик ? Что бы для последующих ожиданий флагов был новый отсчет таймаута.


B начале функции чтения температуры, чтобы после появления неисправности остальные циклы не выполнялись. А в конце функции проверять был ли достигнут таймаут, т.е. наличие неисправности.
pokk
firew0rker, спасибо за помощь примерно то что я и хотел.

Только теперь возникла другая проблема, как можно повторно узнать о наличии аварии точнее о переходе из аварии в норму ?

Дело вот в чем, помимо термодатчика на I2C у меня еще подключен LCD через расширитель порта(только линия DATA), управляющие выводы идут напрямую от процессора, так вот в момент замыкания происходит следующее:
1) в момент замыкания (при маленьком таймауте 2-5ms) на расширитель идет полная фигня, по этому он выдает на выходе нули т.е на LCD приходит Data=0,а так как управляющие выводы работают от процессора то LCD их и принимает, и выводит "корябозябру".

Если увеличивать таймаут больше периода обновления LCD то такого естественно не происходит. Но я хочу что бы момент замыкания не было видно и он максимально быстро отрабатывался.

PS: Флаг BERR и BUSY не помогают. BUSY ставиться только при замыкании на ноль, а BERR вообше не разу не выставился сколько бы не замыкал линию.

BERR – ошибка шины. Устанавливается при обнаружении сигнала start или stop в ”неуместном” месте. Для сброса BERR необходимо записать в него 0.
firew0rker
Подключите линию Е через расширитель. При КЗ на линии Е будет 0 и ЖКИ не среагирует.

узнать о наличии аварии точнее о переходе из аварии в норму можно повторным опросом датчика. Проверять не только отсутствие таймаута и битов ошибки, но и корректность считанных данных.
pokk
"Подключите линию Е через расширитель. При КЗ на линии Е будет 0 и ЖКИ не среагирует."
Все линии там уже заняты. =(

Повторный опрос это только с датчиком =(
firew0rker
Если ваш ЖК-модуль на основе контроллера HD44780, то в DDRAM символы с кодами 00...07 - это переопределяемые символы. При инициализации занулите их, т. е. сделайте пробелами, чтобы при получении кода 0 выводился пробел а не крякозабра.

Что надо обнаруживать: наличие и исправность датчика, или КЗ на I2C?
pokk
Цитата
Если ваш ЖК-модуль на основе контроллера HD44780, то в DDRAM символы с кодами 00...07 - это переопределяемые символы. При инициализации занулите их, т. е. сделайте пробелами, чтобы при получении кода 0 выводился пробел а не крякозабра.
так и есть в 0 ячейка стоит символ градуса, по нему и сужу что там 0 на дате приходит =))

Цитата
Что надо обнаруживать: наличие и исправность датчика, или КЗ на I2C?

Что бы вся периферия на I2C работала гарантированно и после КЗ что бы нечего не зависало, и в случае какой либо помехи,её не было заметно.
Но вообще хочу сделать по полной и наличие датчиков на линии тоже, и в случае выхода одного из строя что бы все остальное работало.
(над последним пока ещё думаю как это сделать,пока останавливаюсь на том что, если много раз зависал в одной функции то перейти на более медленный опрос этой функции раз в 10 секунд, или вообще выключить её опрос)
k155la3
Цитата(pokk @ May 31 2017, 07:32) *
(1) . . .. , и в случае выхода одного из строя что бы все остальное работало.
(2) . . . .если много раз зависал в одной функции то перейти на более медленный опрос этой функции раз в 10 секунд, или вообще выключить её опрос)

(1) если датчик закоротит одну из линий I2C - то естественно, "ляжет" вся шина. И с этим ничего не сделаешь.
Шина I2C - внутрисхемный интерфейс, и если датчик висит на проводе более 10 см - готовьтесь к проблемам.
Чтоб ОНО таки работало, проверьте подтягивающие резисторы I2C. Они должны быть (для надежной рабты периферии, в соотв-ии с миним. требованиями стандарта)
R = 4.7 ... 10 кОм. ОНО будет, конечно, работать и на 100к, но помехоустойчивать будет приближаться к 0.
Снижать вниз R нельзя, можно попалить выходные узлы как мастера, так и слейвов.
Экранируйте провод линии - если они внешние для прибора.
Для работы с длинной внешней шиной I2C есть спец. микросхемы-драйверы.
(2)
не используйте
Код
while( USCI_I2C_RDY_ ) { }  - прямой путь к завесам в нем.

лучше так
Код
MyTimer.Start(1000);
while( USCI_I2C_RDY )  // бит периферийного узла I2C
{
   if( MyTimer.IsElaps() )
   {
        RetCode = eI2C_ERR_RDY;
        break;
   }
}

Завесы при работе с периферией I2C вполне возможны. slave может войти в "ступор" (ожидание) и ваш софт получит коллизию - навечно, до второго пришествия
Reset. Вывести шину из этого состояния софтово можно принудительной подачей 9 clk (узел I2C отключить. ногодрыг SCK)

Вообще, если что-то не работает или "глючит" - 99 проц. - вина аффтара софта и железа sm.gif
Не устраняйте последствия, найдите и устраните причину сбойной работы.
firew0rker
Согласна с написанным k155la3 и ещё добавлю: чтобы освободить шину от зависшего slave, может быть мало 9 импульсов CLK (a может быть и много). Лучше после каждого проверять уровень на линии (slave отпустил — хватит дёргать). И ещё несколько раз сформировать СТОП-условие на шине.
Можно предусмотреть сброс всех, или только глючных устройств шины по питанию. Если у них есть вход "reset", то дергать им, если нет - то подавать питание через ключ и выключать его при зависании.
jcxz
Цитата(k155la3 @ May 31 2017, 10:29) *
Вообще, если что-то не работает или "глючит" - 99 проц. - вина аффтара софта и железа sm.gif
Не устраняйте последствия, найдите и устраните причину сбойной работы.

Т.е. - заменить автора? biggrin.gif
pokk
Цитата
(1) если датчик закоротит одну из линий I2C - то естественно, "ляжет" вся шина. И с этим ничего не сделаешь.

С этим понятно я имел ввиду то что он по адресу не будет отвечать.
Цитата
Вообще, если что-то не работает или "глючит" - 99 проц. - вина аффтара софта и железа sm.gif
Не устраняйте последствия, найдите и устраните причину сбойной работы.

Да да автор виноват ибо нефиг пинцетом в шину тыкать, да на землю и питание её замыкать. biggrin.gif biggrin.gif biggrin.gif
Цитата
Reset. Вывести шину из этого состояния софтово можно принудительной подачей 9 clk (узел I2C отключить. ногодрыг SCK)

Клоки есть, но надо ещё сделать проверку уровня на линии после клока как посоветовал firew0rker.
k155la3
Цитата(jcxz @ May 31 2017, 12:30) *
Т.е. - заменить автора?

Нет. Пока мы тут "бухтим как косм. корабли бороздят Большой Театр" ОН должен "расти на собой".
Это не к ТС или к кому либо sm.gif

Цитата(pokk @ May 31 2017, 13:02) *
С этим понятно я имел ввиду то что он по адресу не будет отвечать. . . .

При холодном старте
1. Проверка на "залипший" слейв. Если залипло - попытка сброса залипа. Если неудачно - LCD_Display("I2C fault", __LINE__);
2. Проверки на наличие и исправность всх имеющихся на шине slave
Код
for(. . . )
{
    if( I2C_SlaveIsConnect( slave_list[i] )
           continue;
    else
    {
            LCD_Display("Slave error", i);
            while(1)  {}
    }
}

3. Надежность работы конечно зависит от типа-марки-произовдителя slave. Мне пока достаточно I2C_SlaveIsConnect( ADDR )
так как небыло необходимости.
4. Таймауты готовности slave (в смысле его внутреннего состояния). К примеру если это EEPROM в том или ином виде - то надо при последоватьельной записи
потока данных выдерживать таймауты, пока в EEPROM пройдут внутренние операции (собственно запись). В вашем случае - время оцифровки. Пред любой операцией
- читать регистры статуса напредмет готовности.

ps - "Так вот если замкнуть линии I2C то процесс чтения температуры зависает, ... "
Это понятно, на шине любое изменение (фронт, спад) отслеживается автоматами как master так и slave.
Можно завесить все - если при этом вошел в коллизию (фактически из-за помехи) автомат master-а, или "зависнет" какой либо слейв - ожидая неизвестно чего,
напр. после ложного "старта".
Проверяйте HW - если глючек в нем, то копаться в софте бессмыслено.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2024 Invision Power Services, Inc.