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

Проблема с входами АЦП ATmega8


Рекомендуемые сообщения

Доброго времени всем. Описую проблему. Есть МК Atmega8, входы АЦП 0 и 1 подключены к двум точкам для измерения напряжения (вход и выход автотрансформатора). Измеряю по очереди и вывожу по SPI на другой МК с индикаторами. Первые показания выводяться на дисплей №1 и после сравнения с таблицей управляют портом D.

  Volt_Input();                                              // измеряем напряжение на входе  
  Data_Write(1,resultat);                                // выводим результат на первый индикатор
  Rele();                                            // ЕСЛИ СТАБИЛИЗАТОР ВКЛЮЧЕН cравниваем напряжение с таблицей

void Rele(void){
      
       if((resultat >= 121) && (resultat <= 149)){        //  
          Data_Write(10,0b11101111); 
          delay_ms(5);  
          PORTD = 0b00110000;
          delay_ms(20);
          return;
         };
       if((resultat >= 150) && (resultat <= 164)){        //
          Data_Write(10,0b11110111);
          delay_ms(5);    
          PORTD = 0b00101000; 
          delay_ms(20);
          return;
         };
       if((resultat >= 165) && (resultat <= 189)){        //  
          Data_Write(10,0b11111011);
          delay_ms(5); 
          PORTD = 0b00100100; 
          delay_ms(20);
          return;
         };
       if((resultat >= 190) && (resultat <= 209)){        // 
          Data_Write(10,0b11111101);
          delay_ms(5);    
          PORTD = 0b00100010;
          delay_ms(20);
          return;
         };
       if((resultat >= 210) && (resultat <= 259)){        // 
          Data_Write(10,0b11111110); 
          delay_ms(5);   
          PORTD = 0b00100001;
          delay_ms(20);
          return;
         }; 
                                                                
    
    }               

  

 

Вторые показания просто выводяться на 2 дисплей

      Volt_Output();                                      // измеряем напряжение на выходе
      Data_Write(2,result_out);                           // выводим на второй дисплей

 

 

Проблема в том что если работать только с одним входом, то он работает нормально. Но когда начинаю проводить измерение поочередно, то в обоих случаях измерение проходит со второго входа. Показания второго измерения выводяться на оба индикатора и также управляют реле.

 

Вначале измерял вот так, указывая номер входа

void Volt_Input(unsigned char input){              
                                
unsigned char counter_1;                      // счетчик колличества сэмплов
unsigned long int  buffer_A;                  // буффер для суммы сэмплов
unsigned char flag_O = 1;

 while(flag_O){                               // ждем переход через 0   
    if(CROSS_ZERO == 1) flag_O = 0;  
    };

 flag_A = 1;                                    //  флаг - начало измеряемого полупериода
 flag_T = 0;                                    //  флаг - конец измеряемого полупериода
 counter_1 = 0;                                 //  обнуляем счетчик колличества сэмплов
 buffer_A = 0;                                  //  обнуляем буффер суммы всех сэмплов    
 TCCR1B = 0x03;                                 //  включаем таймер, Clock value: 187,500 kHz
 TCNT1H = 0xF8;                                 //  начальное значение до переполнения 0xf8ac
 TCNT1L = 0xAC;
 while(flag_A){
   ADC = ADC_result(input);                     //  запускаем преобразование  
   buffer_A = buffer_A + ADC;                   //  считаем сумму сэмплов
   counter_1++;                                 //  увеличиваем счетчик на 1
     if(flag_T == 1){                           //  если полупериод закончился - то
        flag_A = 0;                             //  конец измеряемого полупериода
        resultat = buffer_A / counter_1;        //  получаем среднее значение ADC 
        };
   }
 
            resultat = resultat * 0.695;        //  коеффициент 0,695 приводит значение 1023 = 660 вольт
 
}

 

После зделал две отдельных функции

//======================= измеряем напряжение до трансформатора ================================
void Volt_Input(void){              
                                
unsigned char counter_1;                      // счетчик колличества сэмплов
unsigned long int  buffer_A;                  // буффер для суммы сэмплов
unsigned char flag_O = 1;

 while(flag_O){                               // ждем переход через 0   
    if(CROSS_ZERO == 1) flag_O = 0;  
    };

 flag_A = 1;                                    //  флаг - начало измеряемого полупериода
 flag_T = 0;                                    //  флаг - конец измеряемого полупериода
 counter_1 = 0;                                 //  обнуляем счетчик колличества сэмплов
 buffer_A = 0;                                  //  обнуляем буффер суммы всех сэмплов    
 TCCR1B = 0x03;                                 //  включаем таймер, Clock value: 187,500 kHz
 TCNT1H = 0xF8;                                 //  начальное значение до переполнения 0xf8ac
 TCNT1L = 0xAC;
 while(flag_A){
   ADC = ADC_result(0x00);                      //  запускаем преобразование  
   buffer_A = buffer_A + ADC;                   //  считаем сумму сэмплов
   counter_1++;                                 //  увеличиваем счетчик на 1
     if(flag_T == 1){                           //  если полупериод закончился - то
        flag_A = 0;                             //  конец измеряемого полупериода
        resultat = buffer_A / counter_1;        //  получаем среднее значение ADC 
        };
   }
            
        resultat = resultat * 0.695;            //  коеффициент 0,695 приводит значение 1023 = 660 вольт                                            
 
}


//======================= измеряем напряжение после трансформатора ================================
void Volt_Output(void){              
                                
unsigned char counter_1;                      // счетчик колличества сэмплов
unsigned long int  buffer_A;                  // буффер для суммы сэмплов
unsigned char flag_O = 1;

 while(flag_O){                               // ждем переход через 0   
    if(CROSS_ZERO == 1) flag_O = 0;  
    };

 flag_A = 1;                                    //  флаг - начало измеряемого полупериода
 flag_T = 0;                                    //  флаг - конец измеряемого полупериода
 counter_1 = 0;                                 //  обнуляем счетчик колличества сэмплов
 buffer_A = 0;                                  //  обнуляем буффер суммы всех сэмплов    
 TCCR1B = 0x03;                                 //  включаем таймер, Clock value: 187,500 kHz
 TCNT1H = 0xF8;                                 //  начальное значение до переполнения 0xf8ac
 TCNT1L = 0xAC;
 while(flag_A){
   ADC = ADC_result(0x01);                      //  запускаем преобразование  
   buffer_A = buffer_A + ADC;                   //  считаем сумму сэмплов
   counter_1++;                                 //  увеличиваем счетчик на 1
     if(flag_T == 1){                           //  если полупериод закончился - то
        flag_A = 0;                             //  конец измеряемого полупериода
        resultat = buffer_A / counter_1;        //  получаем среднее значение ADC 
        };
   }

                                                
        result_out = resultat * 0.695;          //  напряжение на выходе                                            
 
}

 

Стопроцентно ясно, что после измерения по второму входу все последующие измерения проводяться по нему и указание номера при измерении этого неменяет. Хотя после включения/выключения питания все может работать нормально какоето время либо до следующего рестарта.

 

Что может быть с АЦП?

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

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

Вот как раз этого-то куска кода вы и не показали. А ошибка, наиболее вероятно, как раз там.

Да и реализацию ADC_result неплохо бы привести.

Изменено пользователем Lexter
Ссылка на комментарий
Поделиться на другие сайты

Вот как раз этого-то куска кода вы и не показали. А ошибка, наиболее вероятно, как раз там.

Да и реализацию ADC_result неплохо бы привести.

unsigned int ADC_result(unsigned char adc_input){

  ADMUX=adc_input | (ADC_VREF_TYPE & 0xff);
  delay_us(10);
  ADCSRA|=0x40;                    // Start the AD conversion
  while ((ADCSRA & 0x10)==0);
  ADCSRA|=0x10;
  return ADCW; 
  
} 

этот код генерирует CodeWizard, сомневаюсь что здесь может быть ошибка.

Изменено пользователем химик
Ссылка на комментарий
Поделиться на другие сайты

... этот код генерирует CodeWizard ...

Это замечательно. А самый интересный код "когда начинаю проводить измерение поочередно", без которого ошибки нет, по-прежнему, секрет?"

Изменено пользователем Lexter
Ссылка на комментарий
Поделиться на другие сайты

void main(void){

unsigned char DS1820;                                  // наличие датчика
                                     
flag_P = eep_flag_P;                                   // проверяем чему был равен флаг до отключения

PORTB=0x14;
DDRB=0x28;        //0x2C

PORTC=0x00;
DDRC=0x20;

PORTD=0x00;
DDRD=0x7F;

TCCR0=0x00;
TCNT0=0x00;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer1 Stopped
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer2 Stopped
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;

// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
MCUCR=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x05;

// Analog Comparator initialization
ACSR=0x4a;    //4A;
SFIOR=0x00;

// ADC initialization
// ADC Clock frequency: 187,500 kHz
// ADC Voltage Reference: AVCC pin
ADMUX=0x00;
ADCSRA=0x86;

// SPI initialization
// SPI Type: Master
// SPI Clock Rate: 3000,000 kHz
// SPI Clock Phase: Cycle Half
// SPI Clock Polarity: Low
// SPI Data Order: MSB First
SPCR=0x52;
SPSR=0x00;

#asm("sei")

//delay_ms(1000);                                           // задержка 1 секунда
Data_Write(5,666);                                        // очищаем второй индикатор
Data_Write(6,666);                                        // очищаем третий индикатор
Alarm();                                                  // проверяем напряжение сети и включаем   
DDRB.2 = 1;                                               // переключаем как выход
  
while (1){
 
 
 //=================== кнопка №1 ==================
 if(PINB.0 == 0){                                         // если нажата кнопка
    key_1++;                                              // начинаем считать такты
    if(key_1 == 1){                                       // если такт первый
       TCCR0=0x05;                                        // запустить таймер 
       TCNT0=0x50;                                        // Clock value: 11,719 kHz 
       };
    };
 
 if(flag_V == 1) Alarm();                                 // если сработал аналоговый компаратор
 
 //=================== Барьер =====================  
 Volt_Input();                                           // измеряем напряжение на входе  
 if((resultat >= 260) || (resultat <= 120)){              // если напряжение за пределами допустимого  
    PORTD = 0b00000000;                                   // отключаем все реле
    Data_Write(10,0b10011111);                            // включаем сигнализацию
    Alarm();                                              // переходим в цикл ожидания
    };  
      
 if(flag_V == 1) Alarm();                                 // если сработал аналоговый компаратор
 
 
 //================== Индикатор №1 ================
   Data_Write(1,resultat);                                // выводим результат на первый индикатор
 
 

     
 if(flag_P == 0){                                         // ЕСЛИ СТАБИЛИЗАТОР ВЫКЛЮЧЕН  
    Data_Write(5,211);                                    // на второй индикатор выводим  OFF  
    Data_Write(10,0b11111111);  
    PORTD = 0b01000000;                                   // все теристоры 0, реле 1 - включено  
    delay_ms(20);                                         
    } else {                                              
       Rele();                                            // ЕСЛИ СТАБИЛИЗАТОР ВКЛЮЧЕН cравниваем напряжение с таблицей
       };
    

   
   delay_ms(200);
   
   if(flag_V == 1) Alarm();                               // если сработал аналоговый компаратор
  
  
  //================ индикатор №2 ============== 
   if(flag_P == 1){                                       // если стабилизатор включен
      Volt_Output();                                      // измеряем напряжение на выходе
      Data_Write(2,result_out);                           // выводим на второй дисплей
      }else{
         Data_Write(5,211);                               // выводим на второй дисплей         
     }
   
   if(flag_V == 1) Alarm();                               // если сработал аналоговый компаратор
   
   
   //===================== термометр ===========
   DS1820 = w1_init();                                    // проверяем подключение датчика 
   if(DS1820 > 0){                                        // если датчик есть
      term = ds1820_temperature_10(0)/10;                 // измеряем температуру
      Data_Write(3,term);                                 // выводим температуру на 3 дисплей
        if(term >= 40){
           PORTC.5 = 0;                                   // вентиятор вкючен
           }else{
           PORTC.5 = 1;                                   // вентиятор выкючен
           };                                       
      }else{                                              // если датчик отсутствует
         PORTC.5 = 1;                                     // вентиятор выкючен
         Data_Write(6,555);                               // выводим ---
      }
        
      
      
   delay_ms(500);


 

     };
}

Stabilizator_ver_1.0.5.rar

Изменено пользователем химик
Ссылка на комментарий
Поделиться на другие сайты

Для начала я бы заменил Volt_Input() и Volt_Output() на заглушки, которые тупо возвращают фиксированные числа. Проверил бы, что при длительной работе программы, перезапусках и любых манипуляциях с кнопками отображаются на индикаторах именно эти числа. Если ошибки в коде main не обнаружится, разобрал бы ADC_result до уровня ассемблера и сравнил бы с описанием работы и настроек АЦП в даташите на эту Atmega8. Просчитал бы и проверил необходимые задержки. Возможно, там не хватает какой-нибудь инициализации или перед вызовом ADC_result требуется произвести какие-нибудь действия.

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

Вобщем что получаеться.

unsigned int ADC_result(unsigned char adc_input){

  ADMUX=adc_input | (ADC_VREF_TYPE & 0xff);
...
сделал вот так 
ADMUX=adc_input;

ADMUX в первом случае равен 0 во втором 1. Работает все нормально длительное время, пока не происходит переход в функцию

    if(flag_V == 1) Alarm();                               // если сработал аналоговый компаратор

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

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

Кажиться проблема решена.

unsigned int ADC_result(unsigned char adc_input){

  ADMUX=adc_input | (ADC_VREF_TYPE & 0xff);
  delay_us(10);
  ADCSRA|=0x40;                    // Start the AD conversion
  while ((ADCSRA & 0x10)==0);
  ADCSRA|=0x10;
  return ADCW; 
  
} 

А по даташиту задержка должна быть не менее 125мкС. Пока работает, дальше будем посмотреть. Так что на CodeWizard больше надеяться небуду.

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

Вобщем проверил весь код, путем подстановок и проверок отдельных частей программы. Также пробывал изменять некоторые функции разными способами. После недельных мучений убрал из программы измерение по второму каналу, и вот здесь самое интересное - входы продолжают переключаться самопроизвольно. Даже если ADMUX устанавливаю напрямую в 0x00, то всеравно происходит самопроизвольная смена входа.

ADMUX &= 0;

Какие могут быть преположения?

Кстати, может я и ошибаюсь, но эта гадость начала выскакивать после шунтирования входов АЦП кондерами на 0,15мкФ. Хотя врятли это может повлиять на регистр мультиплексора контроллера.

Изменено пользователем химик
Ссылка на комментарий
Поделиться на другие сайты

...Какие могут быть преположения?

Кстати, может я и ошибаюсь, но эта гадость начала выскакивать после шунтирования входов АЦП кондерами на 0,15мкФ. Хотя врятли это может повлиять на регистр мультиплексора контроллера.

Шунтирования входов АЦП кондерами на 0,15мкФ, действительно, вряд ли может повлиять на регистр мультиплексора контроллера. Точнее - вообще не может.

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

И перед этим всё-таки проверить, нет ли "иголок" по питанию. Не происходит ли сбой/перезапуск контроллера.

У него, кстати, выводы питания ёмкостями хорошо шунтированы? "Земля" не тонким проводом?

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

... эта гадость начала выскакивать после шунтирования входов АЦП кондерами на 0,15мкФ. ...

Стоп. То есть, до установки конденсаторов всё работало, как положено?

Так это совсем меняет дело. Может чего коротнули при пайке?

Попробуйте их снять, посмотреть, восстановится ли нормальное функционирование.

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

Для исключения сбоев по питанию необходимо добавить пару конденсаторов емкостью 0,1 - 10 μF между выводами 7 - 8 и 20 - 22.

На Вашей схеме их нет, а без них возможны сбои как в цифровой, так и в аналоговой работе микросхемы Atmega8.

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

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти
  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...