Easyelectronics.ru

Электроника для всех
Текущее время: 26 ноя 2020, 19:36

Часовой пояс: UTC + 5 часов



JLCPCB – Прототипы печатных плат за $2/5шт. два слоя. $5/5шт. четыре слоя
Крупнейший производитель печатных плат и прототипов. Более 600000 клиентов и свыше 10000 заказов в день!
Получите скидку на почтовую отправку при первом заказе в JLCPCB!

Начать новую тему Ответить на тему  [ Сообщений: 7 ] 
Автор Сообщение
 Заголовок сообщения: Проблема с точностью измерений АЦП ATmega8
СообщениеДобавлено: 07 сен 2020, 12:25 
Только пришел

Зарегистрирован: 07 сен 2020, 11:53
Сообщения: 7
Добрый день, уважаемые форумчане!
Есть задача - измеряю напряжение с помощью АЦП, используя при этом два канала. С одного канала измеряю напряжение от 0 до 12 вольт, с другого - от 0 до 30 вольт. Использую ИОН 2.56 вольт, соответственно подобрал делители, понижающие напряжение до требуемого уровня. Показания вывожу на семисегментный индикатор. Каналы АЦП переключаю с помощью кнопки, при этом на плате есть два светодиода, которые переключаются по нажатию кнопки и показывают с какого канала производится измерение. Для фильтрации измерений использую фильтр скользящего среднего.
Суть проблемы заключается в том, что когда я начинаю увеличивать напряжение на блоке питания, то вижу, что показания на БП и на семисегментном индикаторе отличаются на 8-10 отсчетов.
Пример: при измерении 9 вольт с канала 30 вольт должно быть 308-309 отсчетов, по факту получается 302.
Если переключить с одного канала на другой,а потом обратно - покажет 309. Увеличу напряжение, а потом обратно - покажет 309. Отключу фильтр - покажет 309.
Внизу привожу код программы:
Код:
#define  F_CPU 1000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdio.h>

unsigned int R0 = 0;
unsigned int R1 = 0, R2 = 0, R3 = 0;
unsigned int n_count;
int u1 = 0;
int u2 = 0;

int u3 = 0;
int u4 = 0;

int value = 0;

void segchar(unsigned char seg){
   switch(seg){
      case 0: PORTD = 0b00111111; break;
      case 1: PORTD = 0b00000110; break;
      case 2: PORTD = 0b01011011; break;
      case 3: PORTD = 0b01001111; break;
      case 4: PORTD = 0b01100110; break;
      case 5: PORTD = 0b01101101; break;
      case 6: PORTD = 0b01111101; break;
      case 7: PORTD = 0b00000111; break;
      case 8: PORTD = 0b01111111; break;
      case 9: PORTD = 0b01101111; break;
   }
}

void segchar_pnt (unsigned char seg_pnt)
{
   switch(seg_pnt)
   {
      case 0: PORTD = 0b10111111; break;
      case 1: PORTD = 0b10000110; break;
      case 2: PORTD = 0b11011011; break;
      case 3: PORTD = 0b11001111; break;
      case 4: PORTD = 0b11100110; break;
      case 5: PORTD = 0b11101101; break;
      case 6: PORTD = 0b11111101; break;
      case 7: PORTD = 0b10000111; break;
      case 8: PORTD = 0b11111111; break;
      case 9: PORTD = 0b11101111; break;

   }
}

void ADC_1_init(){
   ADCSRA |= (1<<ADEN)|(0<<ADPS2)|(1<<ADPS2)|(1<<ADPS2);
   ADMUX |= (1 << REFS1)|(1 << REFS0)|(1 << MUX0)|(0 << MUX1)|(1 << MUX2)|(0 << MUX3);
}

void timer_ini(){
   TCCR1B |= (1<<WGM12);
   TIMSK |= (1<<OCIE1A);
   OCR1A = 200;
   TCCR1B |= (1<<CS11);
}

ISR (TIMER1_COMPA_vect){

   R0 = value;
   R1 = R0%10;
   R2 = R0%100/10;
   R3 = R0%1000/100;
   
   if (n_count==0) {
      segchar(R1);
      PORTB = 0b00000100;
   }
   if (n_count==1) {
      segchar_pnt(R2);
      PORTB = 0b00000010;
   }
   if (n_count==2) {
      segchar(R3);
      PORTB = 0b00000001;
   }
   n_count++;
   if (n_count>2) {
      n_count = 0;
   }

   if ((ADMUX&(1 << MUX0))==0)
   {
      PORTB |= (0<<PB3);
      PORTB |= (1<<PB4);
   }
   
   else {
      PORTB |= (1<<PB3);
      PORTB |= (0<<PB4);
   }
}

int main(void) {
   DDRC = 0x00;
   PORTC |= (1<<PC0);
   DDRB = 0xFF;
   DDRD = 0xFF;

   timer_ini();
   ADC_1_init();
   sei();
   while (1) {

      if (((PINC&(1 << PC0))) == 0) {
         _delay_ms(10);
         if (((PINC&(1 << PC0))) == 0) {
            if (((PINB&(1 << PB3))) == 0)
            {
               ADMUX ^= (1 << MUX0);
            }
            else{
               ADMUX ^= (1 << MUX0);
            }
            while (((PINC&(1 << PC0))) == 0){};
         }
      }

      if ((ADMUX&(1 << MUX0))==0) {
         ADCSRA |= (1<<ADSC);
         while((ADCSRA&(1<<ADIF))==0){   
            u1 = (ADCL|ADCH << 8);

            u2 = u2*9;
            u2 = u2 + u1;
            u2 = u2/10;
            u1 = u2;
            value = (2.56/1024)*u1*11.57*10;
         }
         _delay_ms(10);   
      }
      else
      {
         ADCSRA |= (1<<ADSC);
         while((ADCSRA&(1<<ADIF))==0){
            u3 = (ADCL|ADCH << 8);
            
            u4 = u4*9;
            u4 = u4 + u3;
            u4 = u4/10;
            u3 = u4;
            value = (2.56/1024)*u3*4.6875*10;      
         }
         _delay_ms(10);   
      }   
   }
}


Подскажите в чем проблема, возможно кто-то сталкивался с подобным?
Заранее спасибо!


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Проблема с точностью измерений АЦП ATmega8
СообщениеДобавлено: 07 сен 2020, 22:28 
Старожил
Аватара пользователя

Зарегистрирован: 11 авг 2016, 20:52
Сообщения: 776
Откуда: GMT+6
(u2*9+u1)/10 = (302*9+309)/10 = 302.7, что при целочисленном делении = 302.

Код:
while((ADCSRA&(1<<ADIF))==0){   
            u1 = (ADCL|ADCH << 8);
         }
Нехорошо так делать.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Проблема с точностью измерений АЦП ATm
СообщениеДобавлено: 08 сен 2020, 11:03 
Только пришел

Зарегистрирован: 07 сен 2020, 11:53
Сообщения: 7
С целочисленным делением разобрался, просто не совсем могу понять, почему в коде переменная u2 не досчитывает до значения АЦП?
u2 = u2*9;
u2 = u2 + u1;
u2 = u2/10;
u1 = u2;
Я предполагал, что с помощью этого фильтра переменная u2 постепенно сравняется с переменной u1, то есть u1 = u2 = 309 при 9 вольтах.
Это косяк в математике фильтра или что-то другое?


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Проблема с точностью измерений АЦП ATmega8
СообщениеДобавлено: 08 сен 2020, 20:25 
Заглядывает иногда

Зарегистрирован: 25 авг 2014, 12:12
Сообщения: 104
Вам же написали:
Kelvin писал(а):
(u2*9+u1)/10 = (302*9+309)/10 = 302.7, что при целочисленном делении = 302
По крайней мере для пары значений 302 - 309 ваше вычисление скользящего среднего "залипает" на значении 302


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Проблема с точностью измерений АЦП ATm
СообщениеДобавлено: 08 сен 2020, 20:59 
Заглядывает иногда

Зарегистрирован: 26 фев 2018, 03:06
Сообщения: 131
Bogdan94 писал(а):
С целочисленным делением разобрался, просто не совсем могу понять, почему в коде переменная u2 не досчитывает до значения АЦП?
u2 = u2*9;
u2 = u2 + u1;
u2 = u2/10;
u1 = u2;
Я предполагал, что с помощью этого фильтра переменная u2 постепенно сравняется с переменной u1, то есть u1 = u2 = 309 при 9 вольтах.
Это косяк в математике фильтра или что-то другое?
это не совсем средние это апериодичное звено или простейший фильтр НЧ , и он имеет зону не чувствительности, скользящие средние делается через массив, код для ПЫКов , ну думаю разберетесь.
Код:
unsigned int ReadADC_AN0(void)
{ static unsigned char s_ucCount;
  static unsigned int  m_TempResalt[32];
  static unsigned int  s_uiSymm;
 
  s_ucCount++;
  s_ucCount&=0b00011111;
  s_uiSymm -= m_TempResalt[s_ucCount];
  __delay_us(20); 
  ADCON0bits.GO_DONE=1;
  while(ADCON0bits.GO_DONE) continue;
  __delay_us(5);
  m_TempResalt[s_ucCount] = ADRESH;
  m_TempResalt[s_ucCount]<<=8;
  m_TempResalt[s_ucCount]+= ADRESL;
  s_uiSymm += m_TempResalt[s_ucCount];
  return s_uiSymm/32;   
}


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Проблема с точностью измерений АЦП ATmega8
СообщениеДобавлено: 09 сен 2020, 11:37 
Только пришел

Зарегистрирован: 07 сен 2020, 11:53
Сообщения: 7
Попробовал сделать скользящее среднее через массив, все заработало как надо.
Спасибо всем за ответы!


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Проблема с точностью измерений АЦП ATmega8
СообщениеДобавлено: 09 сен 2020, 16:53 
Старожил
Аватара пользователя

Зарегистрирован: 11 авг 2016, 20:52
Сообщения: 776
Откуда: GMT+6
Код:
while((ADCSRA&(1<<ADIF))==0){   
            u1 = (ADCL|ADCH << 8);
         }
Вложение:
ADC.png
ADC.png [ 3.59 Кб | Просмотров: 304 ]
Тут Вы читаете АЦП пока ADIF==0, таким образом в u1 чаще всего будет результат предыдущего преобразования АЦП.


Show P.S.


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 7 ] 


Часовой пояс: UTC + 5 часов


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  

Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
Русская поддержка phpBB