Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Проблемы с TWI
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
UniBomb
Добрый день. Работаю с восьмой мегой и температурным датчиком ADT75. Написал собсвенные функции работы с TWI:

Код
unsigned char twi_get_last_status(void)
{
return (TWSR & (1 << TWS7|1 << TWS6|1 << TWS5|1 << TWS4|1 << TWS3));
}
//---------------------------------------------------------------------------
unsigned char twi_Wait_TWINT(void)
{
   register unsigned char i=0;
  //ожидаем TWIN==1
   do
    {
     if(TWCR & (1<<TWINT))
      return 0x01;  //true
    }
   while(--i);
   return 0x00;  //false
}
//---------------------------------------------------------------------------
void twi_init_asmaster(void)
{
  TWBR = 0x20;
  TWSR = (0 << TWPS1|0 << TWPS0);
  TWCR |= (1 << TWEN|1 << TWEA);
  
  twi_start();
  twi_write_address(0xff, 'r');
  twi_start();
  twi_stop();
}
//---------------------------------------------------------------------------
unsigned char twi_start(void) //он же повстарт
{
  TWCR = (1 << TWINT|1 << TWSTA|1 << TWEN);
  unsigned char twi_last_status = twi_get_last_status();
  if(twi_Wait_TWINT() == 0x01)
   {
    if((twi_last_status == 0x08) || (twi_last_status == 0x10))
     return 0x01;  //true
   }
  else
   return 0x00; //false
  return 0;
}
//---------------------------------------------------------------------------
unsigned char twi_stop(void)
{
  TWCR = (1 << TWINT|1 << TWSTO|1 << TWEN);
  register unsigned char i=0;
  do
   {
    if(PINC & 0x10) //смотрим, установилась ли в "1" линия SDA
     return 0x01;  //true
   }
  while(--i);
  TWCR = (1 << TWINT|1 << TWSTO);  
  return 0x00;  //false
}
//---------------------------------------------------------------------------
unsigned char twi_write_byte(unsigned char data_byte)
{
  TWDR = data_byte;
  TWCR = (1 << TWINT|1 << TWEN);
  if(twi_Wait_TWINT() == 0x01)
   {
    unsigned char twi_last_status = twi_get_last_status();
    if((twi_last_status == 0x28) || (twi_last_status == 0x30))
     return 0x01;  //true
    else if(twi_get_last_status() == 0x38)
     return 0x00; //потеряли приоритет
   }
  else
   return 0x00; //false
  return 0;
}
//---------------------------------------------------------------------------
unsigned char twi_write_address(unsigned char address_byte, char read_write)
{
  if(read_write == 'r')
   address_byte |= 0x01;
  else if(read_write == 'w')
   address_byte &= 0xFE;
  else
   return 0x00;
  TWDR = address_byte;
  TWCR = (1 << TWINT|1 << TWEN);
  if(twi_Wait_TWINT() == 0x01)
   {
    unsigned char twi_last_status = twi_get_last_status();
    if(read_write == 'w')
     {
      if((twi_last_status == 0x18) || (twi_last_status == 0x20))
       return 0x01;  //true
      else if(twi_get_last_status() == 0x38)
       return 0x00; //потеряли приоритет
     }
    else if(read_write == 'r')
     {
      if((twi_last_status == 0x40) || (twi_last_status == 0x48))
       {
       TWCR = (1 << TWINT|1 << TWEA|1 << TWEN);
        return 0x01;  //true
       }
      else if(twi_get_last_status() == 0x38)
       return 0x00; //потеряли приоритет
     }
   }
  else
   return 0x00; //false
  return 0;
}
//---------------------------------------------------------------------------
unsigned char twi_read_byte(unsigned char *data, unsigned char ack_nack) //ack_nack == 1 - подтверждение
{                                                                       //ack_nack == 0 - неподтверждение
  if(twi_Wait_TWINT() == 0x01)
   {
    unsigned char twi_last_status = twi_get_last_status();
    if((twi_last_status == 0x50) || (twi_last_status == 0x58))
     {
      *data = TWDR;
      if(ack_nack == 1) TWCR = (1 << TWINT|1 << TWEA|1 << TWEN);
      if(ack_nack == 0) TWCR = (1 << TWINT|1 << TWEN);
       return 0x01;  //true
     }
    }
   else
    return 0x00;
  return 0;
}


Соответсвенно для работы с температурной микросхемой использую вот такие функции:

Код
void ADT75_init(void)
{
twi_start();
twi_write_address(0x90, 'w');
twi_write_byte(0x01);
twi_write_byte(0x20);
twi_stop();
}
//---------------------------------------------------------------------------
void ADT75_start(void)
{
twi_start();
twi_write_address(0x90, 'w');
twi_write_byte(0x04);
twi_stop();
}
//---------------------------------------------------------------------------
char ADT75_read(void)
{
unsigned char read_temp_h = 0;
unsigned char read_temp_l = 0;
twi_start();
twi_write_address(0x90,'w');
twi_write_byte(0x00);
twi_stop();
twi_start();
twi_write_address(0x90, 'r');
twi_read_byte(&read_temp_h, 1);
twi_read_byte(&read_temp_l, 0);
twi_stop();
return (char)read_temp_h;
}


Но почему то всё время функция ADT75_read(); очень часто возвращает 0. Т.е. симптомы такие - работает всё около часа, потом всё время возвращается 0. Если выключить и снова включить прибор, то может часа три поработать, потом опять нули.

Посмотрите пожалуйста свежим взглядом, а то я уже замылился...

зы: если надо - соответсвующие порты настроены как высокоомные входы, линия подтягиваеться резюками 6,8к (пробовал разные)... Так же на шине присутсвсует микросхема памяти 24CO4N (вроде как), так она работает правильно всё время...
UniBomb
ммм...... неужели нет мыслей?

Путём отладки выяснил, что модуль TWI вроде как вообе не работает. После любой операции не устнавливается флаг TWINT, а код статуса всё время 0xF8. Так что даже в состояние СТАРТ неполучается вывести...
UniBomb
Перепробовал все примеры использования, какие только мог найти в интернетах. С использованием прерываний и без оных. Мой вариант как я понял мало чем отличается от других, посему мысль о своей неполноценности я отбросил (не без колебаний, кстате)... Получается вот что:

при первых двух-трёх вызовов функций СТАРТ (это когда происхоит запись TWCR = (1 << TWINT|1 << TWSTA|1 << TWEN);) и последущих вызовов функций СТОП (это когда происхоит запись TWCR = (1 << TWINT|1 << TWSTO|1 << TWEN);). Всё происходит нормально. Затем происходит мистика - на линии SDA постоянно висит "0" если была вызвана функция инициализации модуля TWI и "1" если соответсвенно не была вызвана та функция. Осцилограф показывает постоянный сигнал на обеих линиях (SDA и SCL). Кода статуса постоянно 0xF8, а флаг TWINT постоянно сброшен. Думалка уже отказывается работать и причина этого мне не ясна... Пойду ваять программный TWI чтоли...

и всё таки - что я делаю не так?
sensor_ua
Посмотри туда - http://homepage.hispeed.ch/peterfleury/avr-software.html - найди 10 отличий
ReAl
Цитата(UniBomb @ Mar 23 2009, 16:46) *
Добрый день. Работаю с восьмой мегой и температурным датчиком ADT75. Написал собсвенные функции работы с TWI:

Код
//---------------------------------------------------------------------------
unsigned char twi_read_byte(unsigned char *data, unsigned char ack_nack)
{
...
      if(ack_nack == 1) TWCR = (1 << TWINT|1 << TWEA|1 << TWEN);
      if(ack_nack == 0) TWCR = (1 << TWINT|1 << TWEN);
...

}
Не стоит так делать. Две записи в управляющий регистр. Кто его знает, как оно там реально внутри отрабатывает, вдруг иногда с учётом тактирования TWI попадает на то, что после первой записи начинает отрабатывать чтение с ACK-ом, когда надо бы с NAK-ом и как при этом реагирует именно термодатчик. Он может выдать на SDA уровень 0 как бы от следующего байта и не даст сделать стоп.
Лучше так
Код
      if(ack_nack == 1) TWCR = (1 << TWINT|1 << TWEA|1 << TWEN);
      else TWCR = (1 << TWINT|1 << TWEN);

или, что то же самое, так
Код
      TWCR = (ack_nack == 1) ? (1 << TWINT|1 << TWEA|1 << TWEN) : (1 << TWINT|1 << TWEN);

или, что в итоге то же самое, но несколько уменьшает шансы оптимизатора сделать пессимизацию
Код
    uint8_t temp = (1 << TWINT|1 << TWEN);
    if(ack_nack == 1) temp |= (1 << TWEA);
    TWCR = temp;


p.s. не знаю, кто такой ADT75, с провалявшимися 10 лет в столе LM75 нормально работает atmega168, не хуже, чем когда-то с ними же atmega8 работала.
UniBomb
sensor_ua, лучше бы ты меня идиотом назвал... Проверил, коды функций мало чем отличаются, все различия несущественны и заключаются в различном офрмлении.

ReAl, насчёт того, чего делать не стоит - да, там опечатка, но ничего вопиющего в принципе нет. Принял к сведению. А насчёт работы - это с моими функциями работает? с теми, что в первом посте?

Сделал программный TWI используя функции, взятые тута - тоже не работает. Думаю пока что именно.
ReAl
Цитата(UniBomb @ Mar 30 2009, 15:09) *
А насчёт работы - это с моими функциями работает? с теми, что в первом посте?
Ну оно дет не помню сколко назад работало - три термодатчика на atmega8.
У меня I2C и master и slave (сейчас на atmega168 кроме 24c256, ds1307 и LM75 ещё горсть atmega48 висит) сделаны в прерываниях на базе, как это ни смешно, апнот AVR311 и AVR315, если правильно помню.
С мелкими непринципиальными коррекциями (что-то упростил, так как мне мультимастер не нужен, что-то просто по форме изменил).

Цитата(UniBomb @ Mar 30 2009, 15:09) *
Сделал программный TWI используя функции, взятые тута - тоже не работает. Думаю пока что именно.
Ну не знаю, я те исходники тоже не смотрел.
Может и помеха какая сбивает тот ADT75, поставь проверку по таймауту и сброс шины импульсами на SCK до поднятия SDA с последующим формированием STOP (вот этого я до сих пор в этой своей "настольной" системе не сделал smile.gif, не зависала шина ещё, пусть пока так потренируется).
sensor_ua
Цитата
все различия несущественны

функция i2c_start_wait (от Fleury) - там
Код
    
while ( 1 )
    {
        // send START condition
        TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);

        // wait until transmission completed
        while(!(TWCR & (1<<TWINT)));

        // check value of TWI Status Register. Mask prescaler bits.
        twst = TW_STATUS & 0xF8;

        if ( (twst != TW_START) && (twst != TW_REP_START)) continue;
....
}

обратите внимание на continue
и это чисто формально, не разбираясь в работе TWI.
UniBomb
sensor_ua, continue там стоит логично (по идее), но совершенно непонятно как это делает мой код нерабочим. Ведь если код статуса будет всё время 0xF8 то из цикла while ( 1 ) программа никогда не выйдет вне зависимости от того стоит там continue или нет...
M_Andrey
Цитата(UniBomb @ Mar 23 2009, 17:46) *
... линия подтягиваеться резюками 6,8к (пробовал разные)...

я еще кондеры вешаю 30-100 пикф к земле
sensor_ua
Цитата
совершенно непонятно как это делает мой код нерабочим
Вы меня не поняли. Я не берусь судить рабочий Ваш код или нет - я обращаю внимание на то, что отличия есть.
Ещё у Fleury в записи проверяется вроде бы (лень искать эту маску) нужные флаги вместе (главное, что "в один присест")
Код
    if( twst != TW_MT_DATA_ACK) return 1;
    return 0;

а у Вас в twi_write_address
Код
   unsigned char twi_last_status = twi_get_last_status();
    if(read_write == 'w')
     {
      if((twi_last_status == 0x18) || (twi_last_status == 0x20))
       return 0x01;  //true
      else if(twi_get_last_status() == 0x38)
       return 0x00; //потеряли приоритет
     }
....

Проверка похожа, но, думаю, что не такая, ну и второе чтение регистра статуса - вроде бы у Fleury его нет. Аналогично с проверкой и вторым чтением регистра у Вас в twi_write_byte.
Не знаю, что правильно, но думаю, что такие отличия существенны
UniBomb
M_Andrey, я надеюсь не на шину, а к питанию? wink.gif

ensor_ua, ясно))) Вы немного неправильно делаете - над именно судить код, а не искать различия реализации алгоритма.

Цитата(sensor_ua @ Apr 3 2009, 08:52) *
Ещё у Fleury в записи проверяется вроде бы (лень искать эту маску) нужные флаги вместе (главное, что "в один присест")
...
Проверка похожа, но, думаю, что не такая

Нужные флаги тут проверить ну никак нельзя, т.к. нужно смотреть их совокупность (совокупное значение). Этот товарищ делает точно также за двумя исключеними - я пользуюсь числовым обозначением - он предопределёнными константами; он отсекает то что не надо одним махом - я же смотрю только то, что нужно. Критического тут ничего нет.

Цитата(sensor_ua @ Apr 3 2009, 08:52) *
ну и второе чтение регистра статуса - вроде бы у Fleury его нет.

Проверяет он регистр статуса - без этого никак. С TWI можно работать двумя способами - либо через прерывания (в котором всё равно смотрится код статуса в регистре статуса), либо самому регистр статуса всё время мониторить. Вот как он это делает:


Код
twst = TW_STATUS & 0xF8;
if( twst != TW_MT_DATA_ACK) return 1;


где TW_STATUS - предопределённая константа, идущая в хедере вместе с компилятором :

Код
#define TW_STATUS_MASK        (_BV(TWS7)|_BV(TWS6)|_BV(TWS5)|_BV(TWS4)|\
                _BV(TWS3))
#define TW_STATUS        (TWSR & TW_STATUS_MASK)


Для наглядности (и просто по привычке) я этими макросами не пользуюсь и представляю всё в явном виде.

Возврщаясь к проблеме скажу, что код, на который вы указали работает и у меня запустился. Температура читается и вроде даже без глюков. Но понять почему - я пока так и не смог biggrin.gif Так что спасибо за ссылку и за диалог. Вопрос вроде как снимается)))
sensor_ua
Цитата
ясно))) Вы немного неправильно делаете
извините, но это моё дело. А в моих замечаниях Вы, похоже, не поняли ничего.
Цитата
Проверяет он регистр статуса

В том конкретном месте у Fleury в коде проверка статуса делается ОДИН раз и по ОДНОЙ маске, а у Вас проверка выполняется с ДРУГОЙ логикой - по наличию любого из конкретных СОСТОЯНИЙ - и, в добавок, с ДВОЙНЫМ чтением регистра статуса.
Цитата
TW_STATUS - предопределённая константа, идущая в хедере вместе с компилятором
Это замечание отправьте в детский сад, плз.
ЗЫ. Прошу не считать меня излишне резким
UniBomb
Так, вопрос всё ещё актуальный...

sensor_ua, прошу прощения за возможно-безосновательные претензии smile.gif

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

Код
for(;;)
  {
   set_conG;
   for(int i = 0; i < 20; i++)
    _delay_loop_2(0xFFFF);
   ds1621_start();
   clr_conG;
   for(int i = 0; i < 20; i++)
    _delay_loop_2(0xFFFF);
   temperature = ds1621_read();
  }


Причём как при использовании функций товарища Fleury. Температура считывается, передаётся по RS-485 и всё в общем пучком. Если те же самые функции запихнуть в основную программу, то происходит то, что я написал в третьем посте. Казалось бы виной тому остальные части большой программы, но я в ней уже всё вичистил. Функции для работы с TWI ни на кого не завязаны, порты нигде не меняюются и т.д. Аггрр....
UniBomb
вопрос - как связаны между собой модуль TWI и спящий режим?

Методом исключения я выяснил причину сбоев шины - её оказалась команда sleep. Незнаю почему, но если хоть раз мк был переведён в спящий режим, то TWI сбоит вышеописанным способом... Теперь ломаю голову почему так происходит....

зы: используемый мной режим спящего режима - ADC Noise Reduction.
sensor_ua
Выключается ли модуль TWI перед сном?
Как GPIO настроены? Не на выход ли и порты в нуле?
Если модуль не выключается, то что в статусе первое после сна?
(не знаю, можно ли вообще модуль TWI использовать в слипе)
UniBomb
Цитата(sensor_ua @ Apr 8 2009, 00:06) *
Выключается ли модуль TWI перед сном?

Нет.

Цитата(sensor_ua @ Apr 8 2009, 00:06) *
Как GPIO настроены? Не на выход ли и порты в нуле?

Как высокоимпендансные входы.

Цитата(sensor_ua @ Apr 8 2009, 00:06) *
Если модуль не выключается, то что в статусе первое после сна?

Незнаю. Сейчас посмотрю

Цитата(sensor_ua @ Apr 8 2009, 00:06) *
(не знаю, можно ли вообще модуль TWI использовать в слипе)

В слипе я модуль не использую. Две функции не вызываются в одно время и следовательно работают либо функции TWI, либо функции АЦП.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.