Обработка значений с АЦП Мега8 не работает

Обсуждаем контроллеры компании Atmel.
Аватара пользователя
mir0tv0rec
Грызет канифоль
Сообщения: 268
Зарегистрирован: Пн сен 03, 2012 14:46:08
Откуда: Волгоград

Обработка значений с АЦП Мега8 не работает

Сообщение mir0tv0rec »

Всем привет! Делаю вольтамперметр на 8ой меге для лабораторника.
Но возникла загвоздка в преобразовании значений АЦП в вольты:

Код: Выделить всё

uint8_t messCount = 0;      // счетчик кол-во измерений АЦП
uint8_t analogPin = 3;      // номер входа АЦП
uint8_t recMUX = 0;         // переменная для сохранения исходного значения регистра ADMUX
uint16_t voltADCTmp = 0;    // вр. перем. для получения ср. ариф. АЦП

uint32_t voltage = 0;       // напряжение
uint32_t amperage = 0;      // ток
uint32_t wattage = 0;       // мощность
uint16_t uRef = 26300;      // опорное напряжение для мега8а (внутренний ИОН) x10000
uint8_t rSh = 10;           // сопротивление токового шунта x100
uint8_t kUr = 83;           // отношение напряжений входного делителя x1000

uint16_t voltADCRes[3];

voltage = voltADCRes[2] * uRef / 1024 * 100 / kUr;  // получаем напряжение типа XX.XXX

Последняя строка. voltage получается где-то 40-50. а должен получатся пятизначный результат (максимум). Если же просто выводить значение АЦП, то все в норме - до 1023. Считал на калькуляторе (режим programmer), там значения в норме. Вроде размерность переменных взял, чтобы переполнения не было...
С током тоже самое - 2 других входа АЦП: amperage = voltADCRes[0] * uRef / 1024 / rSh; // ток берем до ОУ
Реклама
Аватара пользователя
COKPOWEHEU
Говорящий с текстолитом
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Re: Обработка значений с АЦП Мега8 не работает

Сообщение COKPOWEHEU »

[uquote="mir0tv0rec",url="/forum/viewtopic.php?p=4332632#p4332632"]Вроде размерность переменных взял, чтобы переполнения не было...[/uquote]
Точно? А мне кажется, что переполнение возникает уже на перемножении двух 16-битных чисел voltADCRes и uRef.
/ 1024 * 100
Лучше наоборот, сначала умножить, потом делить.
voltage = voltADCRes[2] * uRef / 1024 * 100 / kUr; // получаем напряжение типа XX.XXX
Попробуйте привести к 32-битному числу на время вычислений:

Код: Выделить всё

voltage = (uint32_t)voltADCRes[2] * uRef * 100 / 1024 / kUr;
Реклама
Martian
Друг Кота
Сообщения: 12867
Зарегистрирован: Сб дек 18, 2021 19:25:32
Контактная информация:

Re: Обработка значений с АЦП Мега8 не работает

Сообщение Martian »

а еще можно разбить на этапы, а не всё в куче, и посмотреть в режиме отладки, на каком происходит бяка.
Аватара пользователя
mir0tv0rec
Грызет канифоль
Сообщения: 268
Зарегистрирован: Пн сен 03, 2012 14:46:08
Откуда: Волгоград

Re: Обработка значений с АЦП Мега8 не работает

Сообщение mir0tv0rec »

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

voltage = voltADCRes[2] * uRef;

Уже вот это выражение выдает 0
Последний раз редактировалось mir0tv0rec Ср дек 07, 2022 14:37:11, всего редактировалось 1 раз.
Реклама
Эиком - электронные компоненты и радиодетали
Аватара пользователя
VNS
Говорящий с текстолитом
Сообщения: 1627
Зарегистрирован: Пт дек 10, 2021 12:48:46
Откуда: Тюмень

Re: Обработка значений с АЦП Мега8 не работает

Сообщение VNS »

[uquote="mir0tv0rec",url="/forum/viewtopic.php?p=4332632#p4332632"]uint16_t uRef = 26300; // опорное напряжение для мега8а (внутренний ИОН) x10000[/uquote]
Внутренний ИОН у меги8 равен 2,56 В… а у Вас как я понял 2,63 В.
Ну и хотелось бы точно знать при каком входном значении какой результат, чтобы понять где ошибка…
Ещё иногда хорошо бы проверить какое значение получается у АЦП без преобразования при определённом входном напряжении…

Ну и формула ИМХО лучше когда выглядит так: ADC * Vtef / 10240… как заметили делим не на 1024, а на 10240…
Vtef = 2560
Последний раз редактировалось VNS Ср дек 07, 2022 14:50:19, всего редактировалось 1 раз.
Реклама
Аватара пользователя
mir0tv0rec
Грызет канифоль
Сообщения: 268
Зарегистрирован: Пн сен 03, 2012 14:46:08
Откуда: Волгоград

Re: Обработка значений с АЦП Мега8 не работает

Сообщение mir0tv0rec »

Значения самого АЦП в норме, от 0 до 1023 по всем трем каналам, если выводить только их (3 элемента массива), регулируя положение подстроечника.
Внутренний ИОН, я измерил на AREF выводе, но сейчас все у меня на макетке. В данный момент это не принципиально. Проблема именно в математике.
Реклама
Аватара пользователя
VNS
Говорящий с текстолитом
Сообщения: 1627
Зарегистрирован: Пт дек 10, 2021 12:48:46
Откуда: Тюмень

Re: Обработка значений с АЦП Мега8 не работает

Сообщение VNS »

Ну если с АЦП всё в порядке, то из конкретного результата можно понять где ошибка в математике… задайте в симуляторе конкретное значение ADC и посмотрите, где ошибка…
veso74
Поставщик валерьянки для Кота
Сообщения: 1916
Зарегистрирован: Сб май 05, 2012 20:24:52
Откуда: KN34PC, Болгария
Контактная информация:

Re: Обработка значений с АЦП Мега8 не работает

Сообщение veso74 »

для тест, с GCC:
voltADCRes[2] = 512;

при
voltage = (uint32_t)voltADCRes[2] * uRef / 1024 * 100 / kUr;
36

при
voltage = (uint32_t)voltADCRes[2] * uRef / 1024 * 100 / kUr;
15843

можете для теста преобразовать в всех местах:
voltage = (uint32_t)voltADCRes[2] * (uint32_t)uRef / 1024UL * 100UL / (uint32_t)kUr;
мой результат: 15843
Аватара пользователя
mir0tv0rec
Грызет канифоль
Сообщения: 268
Зарегистрирован: Пн сен 03, 2012 14:46:08
Откуда: Волгоград

Re: Обработка значений с АЦП Мега8 не работает

Сообщение mir0tv0rec »

Вывел пошагово, все в норме, видимо нужно на несколько строк разбить или еще буду пробовать.

Код: Выделить всё

voltage = voltADCRes[2];
        
amperage = voltage * uRef;
        
wattage =  amperage / 1024 * 100 / kUr;
P.S. Если объединить действия 1ой и 2ой строчек в одну, тут косяк и вылезает...
Последний раз редактировалось mir0tv0rec Ср дек 07, 2022 15:30:33, всего редактировалось 1 раз.
Аватара пользователя
>TEHb<
Друг Кота
Сообщения: 5749
Зарегистрирован: Ср ноя 11, 2009 17:19:30
Откуда: Воронеж
Контактная информация:

Re: Обработка значений с АЦП Мега8 не работает

Сообщение >TEHb< »

mir0tv0rec писал(а):uint32_t voltage = 0;       // напряжение
mir0tv0rec писал(а):получаем напряжение типа XX.XXX
Это как дробное число пихается в целочисленный тип?
"Привет!" - соврал он.
Аватара пользователя
mir0tv0rec
Грызет канифоль
Сообщения: 268
Зарегистрирован: Пн сен 03, 2012 14:46:08
Откуда: Волгоград

Re: Обработка значений с АЦП Мега8 не работает

Сообщение mir0tv0rec »

Только целые числа 5 знаков. Т.е. точки там нет конечно, 35655, точку буду добавлять при выводе.

Объясните, почему так не работает: voltage = voltADCRes[2] * uRef;
а так работает:
voltage = voltADCRes[2];
voltage = voltage * uRef;
Аватара пользователя
>TEHb<
Друг Кота
Сообщения: 5749
Зарегистрирован: Ср ноя 11, 2009 17:19:30
Откуда: Воронеж
Контактная информация:

Re: Обработка значений с АЦП Мега8 не работает

Сообщение >TEHb< »

mir0tv0rec писал(а):Объясните, почему так не работает: voltage = voltADCRes[2] * uRef;
а так работает:
voltage = voltADCRes[2];
voltage = voltage * uRef;
Что ж, тогда согласен с
veso74 писал(а):можете для теста преобразовать в всех местах:
voltage = (uint32_t)voltADCRes[2] * (uint32_t)uRef / 1024UL * 100UL / (uint32_t)kUr;
"Привет!" - соврал он.
OKF
Это не хвост, это антенна
Сообщения: 1405
Зарегистрирован: Вт июн 07, 2011 08:03:18

Re: Обработка значений с АЦП Мега8 не работает

Сообщение OKF »

Как то странно вы получаете результат. Для ADC должен быть uint16_t, зачем там массив? Затем расширяете разрядность - множитель uint32_t или UL.
Demiurg
Это не хвост, это антенна
Сообщения: 1480
Зарегистрирован: Ср июн 25, 2008 15:19:44
Контактная информация:

Re: Обработка значений с АЦП Мега8 не работает

Сообщение Demiurg »

Я сейчас занят. Поднял из одного проекта. Проект не выстрелил, клиент был сельский, дорого ему было. Выдаю из наработок. Пока сами разбирайтесь. Занят. Помню, что были схожие проблемы, как у ТС, с математикой на С. Пришлось применить костыли. Результат выдал через long. А там через симулятор вычислил соответствие результата, чтобы вывести на дисплей.

В выходные попробую поднять проект, чтобы детальнее разобраться.

В проекте было измерение переменного тока (трансформаторы тока) и измерение переменного напряжения. Среднеквадратичное значение.

На тот момент я остановился на коэфициентах.

В общем, разбирайтесь, пробуйте. Работало, оставалось только допилить, чтобы более-менее соответствовало показаниям мультиметра. Особой точности в проекте не требовалось, так что...

Upd. Грубо. Суть такова. У avr плохо с float. Все упирается в ресурсы. Память, время исполнения. Переменку нужно успеть обработать. 10 мс. Поэтому нужно было успевать по максимуму работать с целочисленными значениями.

Схема измерения переменного тока была следующей. ИОН на 2,5 V. Один конец вторичной катушки на выход ИОН, второй конец через резистор 10 кОм на вход АЦП.

Схема измерения переменного напряжения. Делители напряжения. И резистор на выход ИОН для создания средней точки. Виртуальный нуль, что ли, не помню, как правильно называется эта схема.

Описание работы вкратце. Так как частота сети стабильная, детектора перехода через нуль нет. Таймер и АЦП настроены так, чтобы за 10 мс было 100 измерений. Для большей точности нужно сделать замеры нескольких периодов. Результаты измерений суммируются, затем вычисляется среднеквадратичное значение.

Так как в моем проекте было измерение трехфазной сети, каждый такт входы АЦП циклически переключаются.
Спойлер

Код: Выделить всё

//==================
#ifndef METER_H
#define METER_H

#include "meter.h"

#include "main_def_func.h"
//==================

//==================
#define METER_TCNT         TCNT0
#define METER_TIMSK        TIMSK
#define METER_OCIE         OCIE0
#define METER_OCR          OCR0
#define METER_TCCR         TCCR0
#define METER_CS0          CS00
#define METER_CS1          CS01
#define METER_CS2          CS02

#define METER_OCR_VAL      25
//==================

//==================
#define V_REF   5.00
#define ADC_RES 1023.0
#define K_1     1.089
#define K_2     1.061
#define K_3     1.033
//==================

//==================
#define SAMPLES 400
//==================

//==================
enum
{
   PHASE_A,   //фаза A
   PHASE_B,   //фаза B
   PHASE_C,   //фаза C
   PHASE_Usm,
};
//==================

//==================
void meter_timer_init (void);
void meter_timer_off (void);

void meter_init (void);
void meter_off (void);

u16 read_adc (u08 channel);

void set_proc_meter_on (void);
void set_proc_meter_off (void);
void proc_meter (void);

u32 calc_adc_result (u32 sqr_summ);

bool get_measure_complete (void);
//==================

#endif
Спойлер

Код: Выделить всё

//==================
#include "meter.h"
//==================

//==================
bool adc_complete;

static u08 phase;
static u16 sample;

static u32 phase_a_summ_sqr;
static u32 phase_b_summ_sqr;
static u32 phase_c_summ_sqr;

static bool measure_complete;
//==================

//==================
#pragma vector = TIMER0_COMP_vect
__interrupt void meter_timer_comp (void)
{
   u16 adc = 0;
   u16 temp = 0;

   u32 a = 0;

   METER_OCR += METER_OCR_VAL;

   adc = read_adc (PHASE_Usm);

   temp = adc;

   adc = read_adc (phase);

   if (adc >= temp)
      adc -= temp;
   else
      adc = (temp - adc);

   a = (long) (adc * adc);

   switch (phase)
   {
      case PHASE_A:
         phase_a_summ_sqr += a;
         phase = PHASE_B;
         break;

      case PHASE_B:
         phase_b_summ_sqr += a;
         phase = PHASE_C;
         break;

      case PHASE_C:
         phase_c_summ_sqr += a;
         phase = PHASE_A;

         if (++sample > SAMPLES)
         {
            meter_off ();

            measure_complete = true;
         }
         break;
   }
}
//==================

//==================
#pragma inline = forced
void meter_timer_init (void)
{
   METER_TCNT = 0;
   METER_OCR = METER_OCR_VAL;
   set_bit (METER_TIMSK, METER_OCIE);
   set_bit (SFIOR, PSR10);
   METER_TCCR |= ((1<<METER_CS1) | (1<<METER_CS0));
}
//------------------------------------------------------------------------
#pragma inline = forced
void meter_timer_off (void)
{
   METER_TCCR = 0;
   clr_bit (METER_TIMSK, METER_OCIE);
   METER_TCNT = 0;
   METER_OCR = 0;
}
//==================

//==================
//#pragma inline = forced
u16 read_adc (u08 channel)
{
   ADMUX = ((0<<REFS1) | (0<<REFS0) | (0<<ADLAR) | channel);

   set_bit (ADCSRA, ADSC);

   while (1)
   {
      __enable_interrupt ();

      sleep_mode_enable ();

      if (adc_complete)
      {
         adc_complete = false;
         break;
      }
   }

   return ADC;
}
//==================

//==================
#pragma vector = ADC_vect
__interrupt void adc_interrupt_handler (void)
{
   adc_complete = true;
}
//==================

//==================
void meter_init (void)
{
   phase = PHASE_A;

   phase_a_summ_sqr = 0;
   phase_b_summ_sqr = 0;
   phase_c_summ_sqr = 0;

   sample = 0;

   measure_complete = false;

   meter_timer_init ();
}
//------------------------------------------------------------------------
#pragma inline = forced
void meter_off (void)
{
//   ADCSRA = 0;
//   ADMUX = 0;

   meter_timer_off ();
}
//==================

//==================
static u08 _proc_meter;

void set_proc_meter_on (void)
{
   ACSR = (1<<ACD);

   ADCSRA = ((1<<ADEN) | (0<<ADSC) | (0<<ADATE) | (1<<ADIF) | (1<<ADIE) | (1<<ADPS2) | (0<<ADPS1) | (0<<ADPS0));

   _proc_meter = 1;
}
//------------------------------------------------------------------------
void set_proc_meter_off (void)
{
   meter_off ();

   _proc_meter = 0;
}
//------------------------------------------------------------------------
void proc_meter (void)
{
   switch (_proc_meter)
   {
      case 0:
         break;

      case 1:
         meter_init ();
         _proc_meter = 2;
         break;

      case 2:
         if (get_measure_complete ())
         {
            phase = PHASE_A;
            _proc_meter = 3;
         }
         break;

      case 3:
         switch (phase)
         {
            case PHASE_A:
               set_i_ph_a (calc_adc_result (phase_a_summ_sqr));
               phase = PHASE_B;
               break;

            case PHASE_B:
               set_i_ph_b (calc_adc_result (phase_b_summ_sqr));
               phase = PHASE_C;
               break;

            case PHASE_C:
               set_i_ph_c (calc_adc_result (phase_c_summ_sqr));
               Set_Event (EV_ID_MEASURE_I_ABC_COMPLETE, USER_EVENT);
               _proc_meter = 1;
               break;
         }
         break;
   }
}
//==================

//==================
u32 calc_adc_result (u32 sqr_summ)
{
   float a;
   u16 temp;

   sqr_summ /= sample;

   a = sqrt (sqr_summ);
   temp = (int) a;

   a = (float) a * (V_REF / ADC_RES);

   if (temp <= 50)
      a = (float) a * K_1;
   if (temp > 50 && temp < 100)
      a = (float) a * K_2;
   if (temp >= 100)
      a = (float) a * K_3;

   a = a * 10000;
   return (long) a;
}
//==================

//==================
bool get_measure_complete (void)
{
   if (measure_complete == true)
      {
         measure_complete = false;
         return true;
      }
   else
      return false;
}
//==================
Аватара пользователя
mir0tv0rec
Грызет канифоль
Сообщения: 268
Зарегистрирован: Пн сен 03, 2012 14:46:08
Откуда: Волгоград

Re: Обработка значений с АЦП Мега8 не работает

Сообщение mir0tv0rec »

[uquote="OKF",url="/forum/viewtopic.php?p=4332876#p4332876"]зачем там массив?[/uquote]
Измеряю по 3м входам АЦП, сохраняю в массив (результаты измерения с нужного входа сохраняются в свою ячейку). Потом беру нужную ячейку и дальше вычисляю - мне так удобнее.
Осталось только калибровку придумать...
OKF
Это не хвост, это антенна
Сообщения: 1405
Зарегистрирован: Вт июн 07, 2011 08:03:18

Re: Обработка значений с АЦП Мега8 не работает

Сообщение OKF »

Да что там думать!)

Код: Выделить всё

#define ADC_RESOLUTION    1024
#define V_REF             955L
#define ADC2MV(adc)       (adc) * V_REF / ADC_RESOLUTION
Demiurg
Это не хвост, это антенна
Сообщения: 1480
Зарегистрирован: Ср июн 25, 2008 15:19:44
Контактная информация:

Re: Обработка значений с АЦП Мега8 не работает

Сообщение Demiurg »

Массив нужен для анализа, фильтрации. Если просто измерение, то достаточно суммирования при среднеарифметическое, либо возведение в квадрат результата измерения и дальнейшее суммирование и вычисление корня при среднеквадратичном.
Аватара пользователя
mir0tv0rec
Грызет канифоль
Сообщения: 268
Зарегистрирован: Пн сен 03, 2012 14:46:08
Откуда: Волгоград

Re: Обработка значений с АЦП Мега8 не работает

Сообщение mir0tv0rec »

Мне так удобнее. Можно и без массива, но здесь нужны будут "if" или "switch". Как нужно будет понять. с какого входа, куда данные сохранять...

Код: Выделить всё

void ReadADC()
{
  uint16_t result = ADCL | ADCH << 8;    // Получаем 10-битный результат
  
  if (ADC_read)
  {
    // 10 измерений для исключения скачков
    if (messCount < 10)
    {        
      voltADCTmp += result; // аккумулируем показания АЦП канала
      messCount++;  // инкрементируем счетчик кол-во снятых показаний
    }// end if 
    else
    {
      voltADCRes[analogPin-3] = voltADCTmp / messCount; // усредняем результат, присваиваем значения с АЦП ячейкам массива 
      voltADCTmp = 0;  // обнуляем  
      messCount = 0;   // обнуляем
      analogPin++;     // Перебираем входные пины по кругу (А3...А5 - их может быть больше)
      if (analogPin > 5)
      {
        voltage = voltADCRes[2];
        voltage = voltage * uRef / 1024 * 100 / kUr;    // получаем напряжение типа XX.XXX
                                
        uint16_t resultTmp = voltage % 1000;  // получаем тысячные 
        
        //округляем до сотых
        if (resultTmp % 10 < 5) resultTmp = resultTmp / 10;  // если тысячные меньше 5 - просто делим на 10 
        else if (resultTmp < 995) resultTmp = resultTmp / 10 + 1; // если тысяные меньше 945 и тысячные больше 4, округляем сотые в большуюю сторону
        else
        { // если > 995
          resultTmp = 0; // обнуляем сотые
          voltage += 1000;  // увеличиваем целые
        }//end else  
        voltage = voltage / 1000 * 100 + resultTmp; // приводим в виду XXXX (2 знака целые, 2 знака сотые) 
        
        // если напряжение после ОУ вышло из диапазона опорного (напряжение на шунте больше ~0.5V) 
        if (voltADCRes[1] >= 1023)
        {
          amperage = voltADCRes[0];
          amperage = amperage * uRef / 1024 / rSh;  // ток берем до ОУ
        }//end if  
        
        // если напряжение после ОУ в диапазоне опорного
        else
        {
          amperage = voltADCRes[1];
          amperage = amperage * uRef * 10 / 1024 / kOA / rSh;  // ток берем с выхода ОУ (ку ОУ рассчитываем относительно друго входа без усиления)  
          //amperage = amperage * uRef / 1024 / (voltADCRes[1] / voltADCRes[0]) / rSh;  // ток берем с выхода ОУ (ку ОУ рассчитываем относительно друго входа без усиления)
        }//end else
        
        resultTmp = amperage % 1000;  // получаем тысячные 
        //округляем до сотых
        if (resultTmp % 10 < 5) resultTmp = resultTmp / 10;  // если тысячные меньше 5 - просто делим на 10 
        else if (resultTmp < 995) resultTmp = resultTmp / 10 + 1; // если тысяные меньше 945 и тысячные больше 4, округляем сотые в большуюю сторону
        else
        { // если > 995
          resultTmp = 0; // обнуляем сотые
          amperage += 1000;  // увеличиваем целые
        }//end else  
        amperage = amperage / 1000 * 100 + resultTmp; // приводим в виду XXXX (2 знака целые, 2 знака сотые) 
                
        //if (uSw_1 && uSw_2 > uSw_1 && uSw_3 > uSw_2) SwitchRel(); // не переключаем реле, поку не установлены пороги переключения
        
        analogPin = 3; // Все нужные перебрали...возвращаемся к первому
        resultReady = true; 
      }//end if
      ADMUX = recMUX | (analogPin & 0x07);  // Устанавливаем новый вход для преобразования
      ADC_read = false; // Устанавливаем флаг смены входного пина - следующее прерывание пропускаем
    }// end else
  }// end if (ADC_read)
  else ADC_read = true;   // Первый раз пропускаем считывание и устанавливаем флаг на чтение в следующий раз
}// end void ReadADC()
Аватара пользователя
Starichok51
Модератор
Сообщения: 19055
Зарегистрирован: Сб авг 14, 2010 15:05:51
Откуда: г. Озерск, Челябинская обл.

Re: Обработка значений с АЦП Мега8 не работает

Сообщение Starichok51 »

mir0tv0rec писал(а):Как нужно будет понять. с какого входа, куда данные сохранять...
каждой переменной дать собственное имя, и тогда массив не нужен будет вообще.
Мудрость приходит вместе с импотенцией...
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Demiurg
Это не хвост, это антенна
Сообщения: 1480
Зарегистрирован: Ср июн 25, 2008 15:19:44
Контактная информация:

Re: Обработка значений с АЦП Мега8 не работает

Сообщение Demiurg »

mir0tv0rec, выше мой пример. Посмотрите, как у меня сделано. В том примере 4 канала измерений.
Ответить

Вернуться в «AVR»