Страница 1 из 2
Обработка значений с АЦП Мега8 не работает
Добавлено: Ср дек 07, 2022 12:34:13
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; // ток берем до ОУ
Re: Обработка значений с АЦП Мега8 не работает
Добавлено: Ср дек 07, 2022 12:49:11
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;
Re: Обработка значений с АЦП Мега8 не работает
Добавлено: Ср дек 07, 2022 12:51:54
Martian
а еще можно разбить на этапы, а не всё в куче, и посмотреть в режиме отладки, на каком происходит бяка.
Re: Обработка значений с АЦП Мега8 не работает
Добавлено: Ср дек 07, 2022 13:39:58
mir0tv0rec
Приведение к типу 32 не помогает - все тоже. Да, разобью на куски для отладки.
Проверил...
voltage = voltADCRes[2] * uRef;
Уже вот это выражение выдает 0
Re: Обработка значений с АЦП Мега8 не работает
Добавлено: Ср дек 07, 2022 14:28:40
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
Re: Обработка значений с АЦП Мега8 не работает
Добавлено: Ср дек 07, 2022 14:41:33
mir0tv0rec
Значения самого АЦП в норме, от 0 до 1023 по всем трем каналам, если выводить только их (3 элемента массива), регулируя положение подстроечника.
Внутренний ИОН, я измерил на AREF выводе, но сейчас все у меня на макетке. В данный момент это не принципиально. Проблема именно в математике.
Re: Обработка значений с АЦП Мега8 не работает
Добавлено: Ср дек 07, 2022 14:55:45
VNS
Ну если с АЦП всё в порядке, то из конкретного результата можно понять где ошибка в математике… задайте в симуляторе конкретное значение ADC и посмотрите, где ошибка…
Re: Обработка значений с АЦП Мега8 не работает
Добавлено: Ср дек 07, 2022 15:03:46
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
Re: Обработка значений с АЦП Мега8 не работает
Добавлено: Ср дек 07, 2022 15:14:46
mir0tv0rec
Вывел пошагово, все в норме, видимо нужно на несколько строк разбить или еще буду пробовать.
Код: Выделить всё
voltage = voltADCRes[2];
amperage = voltage * uRef;
wattage = amperage / 1024 * 100 / kUr;
P.S. Если объединить действия 1ой и 2ой строчек в одну, тут косяк и вылезает...
Re: Обработка значений с АЦП Мега8 не работает
Добавлено: Ср дек 07, 2022 15:28:58
>TEHb<
mir0tv0rec писал(а):uint32_t voltage = 0; // напряжение
mir0tv0rec писал(а):получаем напряжение типа XX.XXX
Это как дробное число пихается в целочисленный тип?
Re: Обработка значений с АЦП Мега8 не работает
Добавлено: Ср дек 07, 2022 15:34:57
mir0tv0rec
Только целые числа 5 знаков. Т.е. точки там нет конечно, 35655, точку буду добавлять при выводе.
Объясните, почему так не работает: voltage = voltADCRes[2] * uRef;
а так работает:
voltage = voltADCRes[2];
voltage = voltage * uRef;
Re: Обработка значений с АЦП Мега8 не работает
Добавлено: Ср дек 07, 2022 16:11:32
>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;
Re: Обработка значений с АЦП Мега8 не работает
Добавлено: Ср дек 07, 2022 19:38:54
OKF
Как то странно вы получаете результат. Для ADC должен быть uint16_t, зачем там массив? Затем расширяете разрядность - множитель uint32_t или UL.
Re: Обработка значений с АЦП Мега8 не работает
Добавлено: Чт дек 08, 2022 00:09:08
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;
}
//==================
Re: Обработка значений с АЦП Мега8 не работает
Добавлено: Чт дек 08, 2022 09:03:56
mir0tv0rec
[uquote="OKF",url="/forum/viewtopic.php?p=4332876#p4332876"]зачем там массив?[/uquote]
Измеряю по 3м входам АЦП, сохраняю в массив (результаты измерения с нужного входа сохраняются в свою ячейку). Потом беру нужную ячейку и дальше вычисляю - мне так удобнее.
Осталось только калибровку придумать...
Re: Обработка значений с АЦП Мега8 не работает
Добавлено: Чт дек 08, 2022 09:12:00
OKF
Да что там думать!)
Код: Выделить всё
#define ADC_RESOLUTION 1024
#define V_REF 955L
#define ADC2MV(adc) (adc) * V_REF / ADC_RESOLUTION
Re: Обработка значений с АЦП Мега8 не работает
Добавлено: Чт дек 08, 2022 10:33:16
Demiurg
Массив нужен для анализа, фильтрации. Если просто измерение, то достаточно суммирования при среднеарифметическое, либо возведение в квадрат результата измерения и дальнейшее суммирование и вычисление корня при среднеквадратичном.
Re: Обработка значений с АЦП Мега8 не работает
Добавлено: Чт дек 08, 2022 11:35:36
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()
Re: Обработка значений с АЦП Мега8 не работает
Добавлено: Чт дек 08, 2022 11:42:53
Starichok51
mir0tv0rec писал(а):Как нужно будет понять. с какого входа, куда данные сохранять...
каждой переменной дать собственное имя, и тогда массив не нужен будет вообще.
Re: Обработка значений с АЦП Мега8 не работает
Добавлено: Чт дек 08, 2022 11:49:07
Demiurg
mir0tv0rec, выше мой пример. Посмотрите, как у меня сделано. В том примере 4 канала измерений.