Я сейчас занят. Поднял из одного проекта. Проект не выстрелил, клиент был сельский, дорого ему было. Выдаю из наработок. Пока сами разбирайтесь. Занят. Помню, что были схожие проблемы, как у ТС, с математикой на С. Пришлось применить костыли. Результат выдал через 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;
}
//==================