Счетчик импульса тока на atmega8

Обсуждаем контроллеры компании Atmel.
Ответить
Открыл глаза
Сообщения: 42
Зарегистрирован: Сб ноя 29, 2014 21:20:52

Сообщение panya »

Делаю счетчик импульса тока (I^2)*t;
Картина след: U с шунта через вып. мост и операционник измеряемая величина поступает на АЦП. 10 битный.

Смысл какой, как можно чаще опрашивать АЦП, получать значение, и по формуле "St1t2=((а+в)/2)*(t1-t2)" и делать вот так Sres +=St1t2.

Изначально планировалось так: (ADC - оцифрованный сигнал )
Sres=0;

if(ADC>0){

t++;
if(t%2==0){U=ADC;}else(U2=ADC; t2++;)

St1t1=((U+U2)/2)*(t2-t1);
Sres=Sres+St1t1;

Проблемы:
1. не получается посчитать изменение времени от t1 до t2 (именно не то что они изменились на +1, а на сколько реально изменилось время в нано или микро секундах, для того чтобы посчитать площадь прямоугольной трапеции)
2. Очень быстро переполняются переменные

Помогите пожалуйста если кто знает в чем проблема.

Вот код:

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

int read_ADC(){
    ADCSR|=0x40; // Прерывание
        while ((ADCSRA & 0x10)==0); 
    return ADCW;
    }


void main(void){
char time[32];
char imp[32];
unsigned int a;
float b;
int t1; 
unsigned int St1t2;
int t2; 
unsigned int Sres;
float kt;
    char buffer[32]; 
    
    int ADC; //переменная для хранения значений АЦП
    int U; //переменная для хранения напряжения 
   // float I; //переменная для хранения тока

ADMUX=0xC0; // Номер порта и опорное напряжение
ADCSR=0x85; // Инициализация АЦП и Делитель 32

lcd_init(16); 


a=0;
b=0;
t1=0; 
St1t2=0;
t2=0; 
Sres=0;
  kt=100;   
       
       
while(1){
    ADC=read_ADC();

        if(ADC>0){
        lcd_clear(); 
            U=ADC*kt;
            lcd_gotoxy(0,0);
            sprintf(buffer,"I: %d",(int)(U));
            lcd_puts(buffer);  
            
            t1++;
            lcd_gotoxy(10,0);
            sprintf(time,"t: %ds",(t1/100));
            lcd_puts(time);   
            
            a=(U);             
            St1t2=((a*a)*0.001);
            Sres=Sres+St1t2;
            lcd_gotoxy(0,1);
            sprintf(imp,"B: %dkA2*s",Sres);
            lcd_puts(imp);
                }else{
    
                       
                        lcd_gotoxy(0,0);
                        sprintf(buffer,"I:%d",(int)(U));
                        lcd_puts(buffer);  
            
           
            lcd_gotoxy(10,0);
            sprintf(time,"t:%ds",(t1/100));
            lcd_puts(time);   
            
                        
            
           
            lcd_gotoxy(0,1);
            sprintf(imp,"B:%dkA2*s",sqrt(Sres/t1));
            lcd_puts(imp);
            
            }
           
       
           
        }    
      
           };
Реклама
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

float kt;
Плохая идея
int ADC;
Плохая идея. ADC=ADCW=ADCH:ADCL.
не получается посчитать изменение времени от t1 до t2
А что, таймеров свободных нет? Если и правда нет - один раз замерить сколько идет преобразование (там либо 12-13 тактов, либо 25 в зависимости от режима. Тактов f_adc, а не тактов ядра контроллера) и использовать его. А еще лучше - настроить на непрерывный режим и считывать по прерыванию. Или, еще точнее, по прерыванию таймера, уж его-то можно настроить с точностью до такта.
Очень быстро переполняются переменные
Ну еще бы, между измерениями проходит пол-тысячи тактов, а потом на это умножать 10-битное число 2.5-3 байта на одно измерение. Логично использовать либо uint64_t либо его программную реализацию (на ассемблере это сделать проще), либо сохранять измерения за ~4 млн измерений (если без умножения, как и стоит делать), либо 16-256 (в Вашем случае) измерений в массив uint32_t (в unsigned int = uint16_t не влезет и одно измерение). В uint64_t влезет аж 2*10^16 измерений. При частоте преобразования 12000 измерений в секунду (максимальная рекомендуемая частота) этого хватит на 1.5*10^12 сек. Числа uint32_t хватит всего на 350 сек (почти 6 минут). Надеюсь, не накосячил в арифметике.
Кстати, числа с плавающей точкой использовать бесполезно - точность у них намного ниже, чем у целых.
Реклама
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18783
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

решение задачи, как всегда, нужно начинать с анализа условий.
1. разбираемся с тем, с какой частотой АЦП в состоянии давать отсчеты входного сигнала. допустим, ориентируемся на рекомендованную производителем тактовую частоту АЦП в 200 кГц, что дает нам 15384 семпла в секунду
2. для более-менее нормального вычисления площади импульса (интеграла по времени) нужно, чтобы внуртри импульса было хотя бы 10 семплов, откуда вытекает, что импульс должен быть не короче 650 мкс.
3. в принципе, тактовую АЦП можно слегка поднять и пожертвовать количеством семплов. если это возможно, приходим к выводу, что импульс может быть минимум 3 семпла или порядка 150-200 микросекунд. если импульс короче - задача неразрешима в принципе на AVR
4. теперь следует написать программу, которая будет работать по следующему алгоритму:
а) выделяется массив на 10 отсчетов АЦП (ну или больше - смотря по тому, сколько семплов внутри импульса мы надеемся получить)
б) АЦП настраивается на работу в режиме free-running и по прерываниям заполняет этот массив полученными семплами.
в) после того, как массив заполнен, неспеша производится обсчет всех полученных значений. здесь можно применять как float, так и long int. первый вариант даст более привычные взгляду значения "высокой точности", второй позволит сделать все вычисления очень быстро.

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

Мой уютный бложик... заходите!
Контактная информация:
Электрический кот
Аватара пользователя
Сообщения: 1031
Зарегистрирован: Чт июн 20, 2013 00:00:58
Откуда: москва, м.Сходненская

Сообщение Mishany »

главное чтобы авр успевал просчитать все до следующего заполнения массива и если еще учесть работу остальной переферии подключенной к мк....
Реклама
Эиком - электронные компоненты и радиодетали
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

Пусть тактовая частота контроллера 8 МГц (довольно типичная на мой взгляд), частота преобразования 200 кГц (максимальная без потери точности). Согласно даташиту, одно преобразование занимает 13 тактов АЦП, то етсь 520 тактов ядра. На обработку float этого может и не хватит, но какой дурак будет использовать числа с плавающей точкой в таком применении. А вот сложить с 4-8 байтным числом времени хватит с огромным запасом. У меня на работу с uint64_t должно уйти не более 80 тактов (в реальности не проверял), включая вызов обработчика прерывания и сохранение всех переменных. Для uint32_t тактов на 12 меньше.
здесь можно применять как float, так и long int. первый вариант даст более привычные взгляду значения "высокой точности", второй позволит сделать все вычисления очень быстро.
float точнее long int? Для avr это может и так (вроде там long int==int) но если брать нормальный 32-разрядный int, то он на целый байт больше float'а, у которого в этом байте хранится порядок. Это не говоря о приведениях порядков при арифметических операциях и скорости работы с дробными числами.
4. теперь следует написать программу, которая будет работать по следующему алгоритму:
У AVR хватит скорости считать все это в реальном времени, незачем извращаться, тем более, что если импульс требуется измерять, вряд ли его длительность известна заранее.
3. в принципе, тактовую АЦП можно слегка поднять
Тут вопрос точности, но это надо узнать у автора. Максимальная частота оцифровки - F_CPU / 26 измерений в секунду, но при типичных тактовых частотах точность будет небольшой.
Реклама
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18783
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

COKPOWEHEU писал(а):в реальности не проверял
COKPOWEHEU писал(а):Для avr это может и так (вроде там long int==int)
что-то слишком много неуверенности и догадок. для AVR int == int16_t, а long == int32_t
COKPOWEHEU писал(а):float точнее long int?
внимательно читайте, что я написал и обращайте внимание на кавычки - как вы думаете, зачем я их ставил? ;)
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Реклама
Электрический кот
Аватара пользователя
Сообщения: 1031
Зарегистрирован: Чт июн 20, 2013 00:00:58
Откуда: москва, м.Сходненская

Сообщение Mishany »

при использовании asm наверное возможно уложиться в расчетное число тактов, но при использовании СИ не думаю, что можно успевать обрабатывать числа длинее int.
Открыл глаза
Сообщения: 42
Зарегистрирован: Сб ноя 29, 2014 21:20:52

Сообщение panya »

Спасибо, за участие. Скажите пожалуйста таким вот методом получится выполнить правельные вычисления:

int read_ADC(){
ADCSR|=0x40;
while ((ADCSRA & 0x10)==0);
return ADCW;
}

interrupt [TIM0_OVF] void timer0_ovf_isr(void){
TCCR0|=0х2; // Предделитель 8 - - - 4000000(или здесь частота АЦП в 125кГц??)/8/256(FF вместимость счетчика)

count++;
ADC=read_ADC();

if(count%2==0){u2=ADC; count2++;}else{u1=ADC;}

\\ Выполняю необходимые преобразования с числами с частотой TCCR0|=0х2; // Предделитель 8 - - - 4000000/8/256(FF вместимость счетчика)
Выгружаю данные за пределы timer0_ovf_isr, и дальше изощряюсь над числами в то время как в timer0_ovf_isr заново началось веселье.


}

D=
Последний раз редактировалось panya Вт дек 02, 2014 18:49:20, всего редактировалось 1 раз.
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18783
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

panya писал(а):Спасибо, за участие. Скажите пожалуйста таким вот методом получится выполнить правельные вычисления
нет.

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

Мой уютный бложик... заходите!
Контактная информация:
Открыл глаза
Сообщения: 42
Зарегистрирован: Сб ноя 29, 2014 21:20:52

Сообщение panya »

ARV писал(а):
panya писал(а):Спасибо, за участие. Скажите пожалуйста таким вот методом получится выполнить правельные вычисления
нет.

правильный метод вам был объяснен ранее
Я по причине незнанию железа не могу представить как это написать((
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

ARV писал(а):что-то слишком много неуверенности и догадок. для AVR int == int16_t, а long == int32_t
Перепутал с float==double. Только что перепроверил. Работа в Си с 64-битной переменной выглядит длиннее, чем у меня на ассемблере, там при входе в прерывание сохраняются едва ли не все регистры. Кроме того, вместо использования ld / st с постинкрементом зачем-то используется lds / sts по 4 байта. Впрочем, ассемблерные вставки пока никто не запрещал, да и времени это займет ненамного больше. Навскидку - тактов 200. Все равно укладывается в 520. Сейчас симулятора под рукой нет, только avr-gcc, поэтому точных цифр не называю.
ARV писал(а):внимательно читайте, что я написал и обращайте внимание на кавычки - как вы думаете, зачем я их ставил? ;)
Ок :beer:
при использовании asm наверное возможно уложиться в расчетное число тактов, но при использовании СИ не думаю, что можно успевать обрабатывать числа длинее int.
Вычисление с 64-разрядными числами еще влезает, а с 32-разрядными занимает порядка 50 тактов (оно почему-то решило не сохранять ничего), так что запас приличный. Да и с 32-разрядным числом у меня получилось почти 6 минут (на частоте 200 кГц - 4.5 минуты), для "импульса" вполне може тхватить. uint64_t хватает аж на 37 тысяч лет. 128-разрядные числа компилятор почему-то не поддерживает, а жаль.
Я по причине незнанию железа не могу представить как это написать((
Откройте Евстифеева на разделе об АЦП и почитайте про режим непрерывного преобразования. Ну и про таймеры заодно.
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18783
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

avr-gcc реализует крайне неоптимально математику с long long int, то есть uint64_t, это известный трабл.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Открыл глаза
Сообщения: 42
Зарегистрирован: Сб ноя 29, 2014 21:20:52

Сообщение panya »

На верном пути?

ADMUX=0xC0; // Номер порта и опорное напряжение
ADCSR=0xE5; // Инициализация АЦП и Делитель 32 (125кГц) и непрерывное преобразование и запуск первого преобразования

к ADCW обращаться каждые 14 тактов f_acp и выдергивать из него значения?

А 14 тактов щитаем таймером?

И дальше щитать. Зачем тогда массив?
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

Не угадали. Посмотрите что такое режим непрерывного преобразования АЦП и прерывания. Массив не нужен.
avr-gcc реализует крайне неоптимально математику с long long int, то есть uint64_t, это известный трабл
В данном случае этого хватит, кроме того никто не мешает сделать ассемблерную вставку и сделать как нравится.
Открыл глаза
Сообщения: 42
Зарегистрирован: Сб ноя 29, 2014 21:20:52

Сообщение panya »

COKPOWEHEU писал(а):Не угадали. Посмотрите что такое режим непрерывного преобразования АЦП и прерывания. Массив не нужен.
1. За непрерывное преобразование отвечает ADFR окончание преобразования узнаем по "1" в ADIF.
2. Останавливаем все на 500мкс (Прерываемся);
3. щитаем что получилось
4. запускаемся (Заводим АЦП)
5. окончание преобразования узнаем по "1" в ADIF.
6. Останавливаем все на 500мкс+время 13 тактов от f_acp (Прерываемся);
while(4,5,6)

я так представляю работу. Поправьте если что-то не верно.
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

2. Останавливаем все на 500мкс (Прерываемся);
Да почитайте уже что такое прерывания! Они вызываются аппаратно каждым модулем, не надо так извращений с задержками.
3. щитаем что получилось
Коверканье слов давно уже не смешно.
4. запускаемся (Заводим АЦП)
5. окончание преобразования узнаем по "1" в ADIF.
6. Останавливаем все на 500мкс+время 13 тактов от f_acp (Прерываемся);
while(4,5,6)
Снова мимо. Как войти в режим непрерывного преобразования нашли, это хорошо. Осталось выяснить, чем же он отличается от одиночного преобразования. А еще за что отвечает флаг ADIE и что же такое прерывания, как их обрабатывает ядро.
я так представляю работу. Поправьте если что-то не верно.
Попробуйте пока без сложения отдельных измерений просто вывести результат АЦП в UART например или еще куда, воспользовавшись прерыванием и не перезапуская АЦП после окончания измерения. Добавить остальную функциональность будет несложно.
.
В процессе беседы уже несколько раз написал аналогичный код, проверяя быстродействие и прочие параметры. Но будет лучше если все-таки разберетесь в теме, от готового решения толку немного.
Открыл глаза
Сообщения: 42
Зарегистрирован: Сб ноя 29, 2014 21:20:52

Сообщение panya »

// Обработка прерывания по переполнению
interrupt [TIM0_OVF] void timer0_ovf(void){
while ((ADCSRA & 0x10)==0);
U=ADCW;
S+=U*U;
}

main (volid){
ADCSR=0xE5; // Инициализация АЦП и Делитель 32 (125кГц) и непрерывное преобразование и запуск первого преобразования
TCCR0=0хХХХ //запускаем таймер
TIMSK=0хХХХ //разрешаем прерывание по переполнению

while(1){
echo S;
}
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

Очень хочется через прерывания таймера, а не АЦП? Ладно.
ADCSR=0xE5; // Инициализация АЦП и Делитель 32 (125кГц) и непрерывное преобразование и запуск первого преобразования
Запись плохая, лучше
ADCSR = (1<<ADEN | 1<<ADSC | 1<<ADATE | 0b101<<ADPS0); и выставлять его последним, после инициализации таймера и прочих настроек АЦП, потому что преобразование начнется сразу, а ведь нужно еще выставить ADMUX.
S+=U*U;
А это зачем? Или считаете мощность на известном сопротивлении?
echo S;
Не припомню в Си такой команды.
while ((ADCSRA & 0x10)==0);
Снова плохая запись, лучше
while(!(ADCSRA & (1<<ADIF))); и, наверное, стоит потом сбросить флаг ADIF.
А еще лучше - подобрать время таймера, чтобы прерывания гарантированно происходили реже, чем завершается преобразование, это лучше с точки зрения точности и можно не проверять флаг ADIF. Или, если проверять, то на возникновение ошибки. А именно в Вашем варианте не понятно, с чем синхронизация: и не с таймером (потому что ждем АЦП), и не с АЦП (потому что обработка по таймеру).
TIMSK=0хХХХ //разрешаем прерывание по переполнению
0xXXX - 12-битное число :shock: ? Согласно документации, время преобразования составляет ровно 13 тактов. Это не менее точно, чем таймер, но проще реализуется.
Открыл глаза
Сообщения: 42
Зарегистрирован: Сб ноя 29, 2014 21:20:52

Сообщение panya »

COKPOWEHEU писал(а):
echo S;
Не припомню в Си такой команды.
Да, нету, она из php.

Так будет похоже на прерывание по таймеру?

// Обработка прерывания по переполнению
interrupt [TIM0_OVF] void timer0_ovf(void){
count++;
U=ADCW;
if(U>0){
if(count%2==0){A2=U;}else{A1=U;}}
else{
if(count%2==0){A2=U;}else{A1=U;}}

if(A1==0 && A2==0){S_res=S_res;}else{St1t2=((A1+A2)/2)*((A1+A2)/2)*t1t2; S_res+=St1t2;}

}

main (volid){

TCCR0=0хХХ //запускаем таймер
TIMSK=0хХХ //разрешаем прерывание по переполнению
ADCSR=0xE5; // Инициализация АЦП и Делитель 32 (125кГц) и непрерывное преобразование и запуск первого преобразования

while(1){
echo S;
}
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

До редактирования было лучше. Можно было неспешно добить код до рабочего состояния и уже потом навешивать остальную функциональность. Надо подобрать значения таймера, настроить ADMUX, привести настройки к человеческому виду (см. мой предыдущий пост), убрать php-шную функцию из main'а (что она там делает?).
По добавленному:
if(U>0)
Плохая идея: помехи плюс точность АЦП могут приводить к ложным срабатываниям. Лучше подобрать положительное число большее уровня шумов.
if(count%2==0)
Плохая идея использовать деление для определения четности, есть гораздо более оптимальные и простые способы.
if(A1==0 && A2==0){S_res=S_res;}
Зачем?
if(count%2==0){A2=U;}else{A1=U;}}
else{
if(count%2==0){A2=U;}else{A1=U;}}
if(A1==0 && A2==0){S_res=S_res;}else{St1t2=((A1+A2)/2)*((A1+A2)/2)*t1t2; S_res+=St1t2;}
Что это за колдунство? Зачем код повторяется в обеих ветках if'a? Форматирование, мягко говоря, странное. Я так понял, Вы хотите проинтегрировать работу? Извращения с временем лишние. Прерывание по таймеру использовано именно для того, чтобы все интервалы времени быля одинаковыми и умножить на них можно было один раз, в конце. Две переменные здесь тоже не нужны, если так уж хочется усреднять по трапеции (не уверен, что точнее), достаточно одной.
interrupt [TIM0_OVF] void timer0_ovf(void){
Обработчик прерывания должен занимать как можно меньше времени. Поэтому от ненужных вычислений стоит избавиться. В данном случае ненужные - остаток от деления и деление. Учите булеву алгебру, оба эти действия делаются гораздо проще!
Но сначала все-таки исправьте начальный вариант, который без интегрирования и определения начала и конца импульса.
Ответить

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