Модераторы: PILOT, ManiaK, Mazzi
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> avr + twi (+ ADT75) 
:(
    Опции темы
UniBomb
Дата 26.3.2009, 10:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок
***
Награды: 1



Профиль
Группа: Завсегдатай
Сообщений: 1754
Регистрация: 24.10.2006
Где: Санкт-Петербург

Репутация: 9
Всего: 97



Добрый день. Есть устройство на восьмой меге, которая по шине TWI общается с температурной микросхемой (ADT75). Так вот иногда температура отваливалась (т.е. функции работы с ней возвращали 0), но т.к. устройство пока в разработке я на это внимания не обращал. Потом, когда программа была уже написана я стал её вычищать, оптимизировать и производить другие весёлые действия. Причём функции работы с TWI я не трогал вообще. И как это ни сранно TWI отвалился вообще  smile 

Симптомы следующие - после любого действия взвращается код 0xF8, что говорит о том, что предыдущее действие небыло выполнено и флаг TWINT небыл установлен в "1". И после чего установки этого флага можно ждать бесконечно долго. Вот незнаю прям даже что делать... Гляньте поалуйста свежим взглядом на мои функции, может я чего то напортачил:

Код

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 int 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 << TWINT|1 << TWEN|1 << TWEA);
 }
//---------------------------------------------------------------------------
unsigned char twi_start(void)
 {
  TWCR = (1 << TWINT|1 << TWSTA|1 << TWEN);
  if(twi_Wait_TWINT() == 0x01)
   {
    unsigned char twi_last_status = twi_get_last_status();
    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);
  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_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); //будет принят байт данных и передано ACK
        return 0x01;  //true
       }
      else if(twi_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;
}
//---------------------------------------------------------------------------


соответсвенно для работы с ADT75 я использую следующие функции:

Код

//*****Функции для работы с температурной мелкосхемой************************
void ds1621_init(void)
{
 twi_start();   //фунция возвращает "0", код статуса 0xF8
 twi_write_address(0x90, 'w');
 twi_write_byte(0x01);
 twi_write_byte(0x20);
 twi_stop();
}
//---------------------------------------------------------------------------
void ds1621_start(void)
{
 twi_start();
 twi_write_address(0x90, 'w');
 twi_write_byte(0x04);
 twi_stop();
}
//---------------------------------------------------------------------------
char ds1621_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_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;/
}


Из других полезных сведений - avr-gcc, 0s, подтягивающие резюки номиналом 6,8к(пробовал разные), на шине также ещё висит атмеловский EEPROM 24C04N.
PM MAIL ICQ Skype   Вверх
UniBomb
Дата 27.3.2009, 16:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок
***
Награды: 1



Профиль
Группа: Завсегдатай
Сообщений: 1754
Регистрация: 24.10.2006
Где: Санкт-Петербург

Репутация: 9
Всего: 97



я в отчаянии.... 
PM MAIL ICQ Skype   Вверх
Severyanin
Дата 6.4.2009, 07:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Исследователь
**


Профиль
Группа: Участник
Сообщений: 554
Регистрация: 31.7.2007
Где: Россия, Омск

Репутация: нет
Всего: 9



UniBomb, на первый взгляд кажется немного диким условие цикла в функции twi_wait_TWINT. Почему ты ждешь именно 0xFF циклов? думаю, логичнее было бы while(1) или for(;;) юзать smile

Добавлено через 22 секунды
попозже подробнее вникнуть попытаюсь)))


--------------------
"Звонким вереском скроются наши следы, и не вспомнят о них. Кто поверит нам, рыцарям павшей звезды из отвергнутых книг? Пусть в узоре времен ни стихов. ни имен, но напомнит забывшим их полуночный крик." Тэм Гринхилл
"Ужели суслик твоего коварства нагадит в плов доверья моего?". Л.Филатов 
PM MAIL WWW ICQ   Вверх
UniBomb
Дата 6.4.2009, 11:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок
***
Награды: 1



Профиль
Группа: Завсегдатай
Сообщений: 1754
Регистрация: 24.10.2006
Где: Санкт-Петербург

Репутация: 9
Всего: 97



Severyanin, мне наоборот кажется диким 

Цитата(Severyanin @  6.4.2009,  07:28 Найти цитируемый пост)
while(1) или for(;;) юзать



Ведь если флаг TWINT не установится, то программа зависнет в этом месте. Придётся заводить ватчдог, придётся после сброса по ватчдогу восстанавливать последнее стабильное состояние программы и т.д. Число 0xFF взято с потолка, хотя я пробовал и 0xFFFF - результат не меняется. Почему я вообще беру конечное число иттераций? Да потому что если за это время флаг не установился, то я могу смело говорить, что возникла какая то ошибка в модуле TWI и продолжать работать дальше...
PM MAIL ICQ Skype   Вверх
Severyanin
Дата 7.4.2009, 07:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Исследователь
**


Профиль
Группа: Участник
Сообщений: 554
Регистрация: 31.7.2007
Где: Россия, Омск

Репутация: нет
Всего: 9



насколько я помню, задержка на TWI может достигать десятков микросекунд, поэтому он может не успеть за 0xFF циклов сработать. Поэтому все-таки вачдог будет корректнее. А адреса между EEPROM и датчиком правильно распределил? Ты точно обращаешься к датчику? может, по ошибке твои посылки принимает память и, видя некорректные для себя кманды, просто забивает на них?


--------------------
"Звонким вереском скроются наши следы, и не вспомнят о них. Кто поверит нам, рыцарям павшей звезды из отвергнутых книг? Пусть в узоре времен ни стихов. ни имен, но напомнит забывшим их полуночный крик." Тэм Гринхилл
"Ужели суслик твоего коварства нагадит в плов доверья моего?". Л.Филатов 
PM MAIL WWW ICQ   Вверх
UniBomb
Дата 7.4.2009, 08:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок
***
Награды: 1



Профиль
Группа: Завсегдатай
Сообщений: 1754
Регистрация: 24.10.2006
Где: Санкт-Петербург

Репутация: 9
Всего: 97



Severyanin, с адресами всё хоршо. Проверено-пеепроверено тыщу раз. Но...

Заметил, что если написать простую программку, которая работает только с температурой. И всё заработало. Значит у меня где то в программе ошибка. Методом исключения я определил, что если убрать одну функцию, то температура работает и в большой программе. Эта функция работы с АЦП. Выяснилось, что если во время преобразования АЦП не уводить мк в спящий режим, то всё работает. Если же как положено перевести мк в режим понижения шумов ацп, то TWI отваливается и программа ведёт себя как написано выше.

Вообще мне это кажется странным, ведь функции работы с ацп и twi никогда не работают в одно время. Имею следующую структуру программы:

Код

 for(;;)
  {
//...
   if(TimrClk) устанавливается в "1" по таймеру с периодом в сто миллисекунд
    {
//...
     read_temp();
     adt75_start();
    }
//.... 
   get_from_adc();  //функция работы с ацп
  }

/***/
ISR(SIG_ADC)
{
 cur_adc_value = ADCW;
}  
/***/  
float get_from_adc(void)
{ 
 static float dU[8];

 cur_adc_value = -999;
 
 ADMUX = 7;
 ADCSR |= 1 << 6;
 
 MCUCR = 0x90;
 asm("sleep"::); //

 register unsigned int i=1024;
 while(i--) //ждём когда сработает прерывание ацп,
            //т.е. когда в cur_adc_value будет текущее значение преобразования
  {
   if(cur_adc_value != -999)
    break;
  } 
   
 dU[0] = dU[1];
 dU[1] = dU[2];
 dU[2] = dU[3];
 dU[3] = dU[4];
 dU[4] = dU[5];
 dU[5] = dU[6];
 dU[6] = dU[7];
 dU[7] = (float)cur_adc_value*5/1024;
  
 return (dU[0]+dU[1]+dU[2]+dU[3]+dU[4]+dU[5]+dU[6]+dU[7])/8; 
}



Это сообщение отредактировал(а) UniBomb - 7.4.2009, 08:38
PM MAIL ICQ Skype   Вверх
Goodefine
Дата 8.4.2009, 21:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 3
Регистрация: 11.2.2009
Где: Приднестровье, Ти располь

Репутация: нет
Всего: нет



Несколько  настораживает массив флоатных чисел в get_from_adc(). Вычислений прибавится на порядок, а то и больше. Это совершенно необязательно:
Код

float get_from_adc(void)
{ 
 static int dU[8];
 //...

 dU[0] = dU[1];
 dU[1] = dU[2];
 dU[2] = dU[3];
 dU[3] = dU[4];
 dU[4] = dU[5];
 dU[5] = dU[6];
 dU[6] = dU[7];
 dU[7] = cur_adc_value;
  
 return (float)(dU[0]+dU[1]+dU[2]+dU[3]+dU[4]+dU[5]+dU[6]+dU[7])*5/8192; 
}
 
Вообще, флоат тоже необязательно - практически всегда можно обойтись без него, с достаточной точностью... Я бы сделал примерно так:
Код

#dedine BUF_SIZE 8
unsigned int buffer[BUFF_SIZE]; //глобальный (быстрый) массив
...
unsigned int read_adc(unsigned int *buffer){
   //запуск АЦП и все такое...
...
 for(unsigned char i=0;i<(BUFF_SIZE-1);i++) buffer[i]=buffer[i+1];
 buffer[BUFF_SIZE-1]=cur_adc_value;
 
return (buffer[0]+buffer[1]+buffer[2]+buffer[3]+buffer[4]+buffer[5]+buffer[6]+buffer[7])>>3;
                       }

И все дальнейшие манипуляции проводить с возвращенным средним значением за восемь последних отсчетов
Кстати, а Вы уверены, что после преобразования мега вообще просыпается?

Это сообщение отредактировал(а) Goodefine - 8.4.2009, 21:22
PM MAIL   Вверх
UniBomb
Дата 8.4.2009, 22:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок
***
Награды: 1



Профиль
Группа: Завсегдатай
Сообщений: 1754
Регистрация: 24.10.2006
Где: Санкт-Петербург

Репутация: 9
Всего: 97



Цитата(Goodefine @  8.4.2009,  21:15 Найти цитируемый пост)
Несколько  настораживает массив флоатных чисел в get_from_adc()

На самом деле это черновой вариант. Конечно там есть много чего, что можно упростить. Я просто не занимался ещё этой функцией)))


Цитата(Goodefine @  8.4.2009,  21:15 Найти цитируемый пост)
Кстати, а Вы уверены, что после преобразования мега вообще просыпается?

Нет, не уверен. С одной  стороны работают все остальные функции, которые вызываются из главного бесконечного цикла, с другой стороны непросыпание меги могло бы объяснить траблы с TWI, т.к. в режиме сна для понижения шумов в моделу TWI работает только блок сравнения адреса. Можно ли как-нибудь проверить выход из спячки?

Это сообщение отредактировал(а) UniBomb - 8.4.2009, 22:15
PM MAIL ICQ Skype   Вверх
Goodefine
Дата 8.4.2009, 22:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 3
Регистрация: 11.2.2009
Где: Приднестровье, Ти располь

Репутация: нет
Всего: нет



Цитата(UniBomb @ 8.4.2009,  22:15)
На самом деле это черновой вариант. 

Из-за этого существует вероятность порчи стека. Попробуйте сделать эту функцию минималистической, не тягающей через стек такое...
Если дальше вечный цикл крутится, то значит просыпается. Кстати, ждать:
Код

 register unsigned int i=1024;
 while(i--) //ждём когда сработает прерывание ацп,
            //т.е. когда в cur_adc_value будет текущее значение преобразования
  {
   if(cur_adc_value != -999)
    break;
  }
 
вроде как ненужно - до прерывания АЦП, счетчик флеши (команд) МК и так тактироваться не будет. Соответственно после sleep-а, проц будет спать до самого прерывания. Поэтому лучше убрать этот кусок, дабы не раскладывать потенциальные грабли...
Возможно, стоит еще попробовать после преобразования переинициализировать TWI...
P.S. Попробуйте также убрать строчку:
Код

ADCSR |= 1 << 6;

Поскольку в режиме подавления шумов, преобразование ацп начнется само, сразу после команды sleep...
Внешние прерывания по входам применяются? Если да, тогда лучше записать не MCUCR=0x90, а MCUCR|=0x90;

Это сообщение отредактировал(а) Goodefine - 8.4.2009, 23:56
PM MAIL   Вверх
UniBomb
Дата 9.4.2009, 13:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок
***
Награды: 1



Профиль
Группа: Завсегдатай
Сообщений: 1754
Регистрация: 24.10.2006
Где: Санкт-Петербург

Репутация: 9
Всего: 97



Сократил вот до такого:

Код

 unsigned int dU[8]; //глобальный массив

/*...*/

EMPTY_INTERRUPT(ADC_vect); //пустой обработчик прерывания, что бы только мк из спячки вывести

/*...*/

float get_from_adc(int num_channel)
{ 
 unsigned int sum = 0;
 
 ADMUX = num_channel;  //выбор канала считывания
 ADCSR |= 1 << 6;      //запуск преобразования АЦП
 
 MCUCR = 0x90;
// asm("sleep"::);

while(ADCSR&(1<<ADIF)) ; //если без слипа

 for(int i = 0; i < 7; i++)
  {  
   dU[i] = dU[i+1];
   sum += dU[i];
  }
 dU[7] = ADCW;
  
 return (float)((((sum+dU[7])>>1)+((sum+dU[7])>>3))/1024.0); 
}

Сократилось всё где то на 600 байт (при -Os).

Цитата(Goodefine @  8.4.2009,  22:59 Найти цитируемый пост)
попробовать после преобразования переинициализировать TWI

непомогает.


Цитата(Goodefine @  8.4.2009,  22:59 Найти цитируемый пост)
Внешние прерывания по входам применяются?

нет.

Пока всё без изменений...
PM MAIL ICQ Skype   Вверх
Goodefine
Дата 9.4.2009, 17:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 3
Регистрация: 11.2.2009
Где: Приднестровье, Ти располь

Репутация: нет
Всего: нет



Посмотрите в асмовском листинге, сохраняется ли регистр SREG в обработчике прерывания ацп.
Если нет, можно попробовать, сразу при входе в обработчик завершения ацп, руками в асмовской вставке сохранить в стеке регистр SREG, а непосредственно при выходе восстановить его...
PM MAIL   Вверх
UniBomb
Дата 13.4.2009, 08:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок
***
Награды: 1



Профиль
Группа: Завсегдатай
Сообщений: 1754
Регистрация: 24.10.2006
Где: Санкт-Петербург

Репутация: 9
Всего: 97



Goodefine, нет, не сохраняется. EMPTY_INTERRUPT - это просто reti. А в принципе зацем сохранять этот регистр, если изменяется только флаг I? При переходе по соответсвующему вектору этот флаг сбрасывается, а при reti восстанавливается....
PM MAIL ICQ Skype   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Микроконтроллеры (MCU) и микропроцессоры (MPU)"
PILOT ManiaK
UniBomb Mazzi

На данный раздел помимо Правил форума распространяются текже следующие правила:


  • Прежде чем создать тему воспользуйтесь поиском или посмотрите в faq. Возможно на форуме уже есть ответ на ваш или близкий к вашему вопрос.
  • В заголовке темы в квадратных скобках обозначьте используемое семейство микроконтроллера: [avr],[pic],[arm].
  • При создании темы с вопросом указывайте участок кода с ошибкой, версию компилятора, схемы подключения, fuse биты и прочие данные, которые помогут найти правильный ответ. Для форматирования текста программ используйте кнопку код.
  • Новое сообщение должно иметь прямое отношение к тематике этого раздела. Для флуда, просьб выполнить задание, поиска партнёров или исполнителей существуют свои разделы.
  • Если вы заметили несовместимое с правилами сообщение, то можете уведомить об этом модератора раздела нажав кнопку Репорт у соответствующего сообщения.

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, PILOT, ManiaK, UniBomb, Mazzi.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Микроконтроллеры (MCU) и микропроцессоры (MPU) | Следующая тема »


 




[ Время генерации скрипта: 0.0534 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.