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

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

Сообщение panya »

Пришел за оптимизацией:
Попытаюсь объяснить что тут я накрутил.

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

С немецким наигрался в доволь ) :music:

Да, помогите пожалуйста нормально посчитать числа. а то неполучается (

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

/*****************************************************
Chip type               : ATmega8
Program type            : Application
AVR Core Clock frequency: 4,000000 MHz
Memory model            : Small
External RAM size       : 0
Data Stack size         : 256
*****************************************************/

#include <mega8.h>

#asm
   .equ __lcd_port=0x18 ;PORTB
#endasm

#include <lcd.h>
#include <delay.h>
#include <stdio.h>
#include <math.h>

//************************************ Статус системы ****************************************//

_Bool R_Umwandlung=0; // режим преобразования да/нет
_Bool Arbeit_END=0; // работа окончена да/нет
_Bool LCD_UP=0; // вывод на экран да/нет

//**************************************** Переменные ****************************************//

volatile float S_RES=0.00;	// результат S_RES
volatile float S_INT=0.00; // S_intermediate (Проможуточное S)
volatile float CURRENT=0.00; // ток

unsigned int ACP_Anzahl=0; // DAS IST ACP_Anzahl
   
//********************************* Переменные времени ***************************************//

volatile unsigned int KOL=0; // кол-во положительных подсчетов (синусоида наростает либо спадает, но недоходя до граничной зоны)
volatile unsigned int MS=0; // дискретность 0.2 секунды, хватит на 2621 секунду работы
volatile unsigned int SCHLECHT_Anzahl=0; // число плохих подсчетов

unsigned unsigned int WATCH=0; // Выводимое время, хватит на 13105 секунд но count'а хватит на 2621 секунд, поэтому всеравно 2621

//*************************************** Макросы ********************************************//
       
#define ADC_MIN	2 // порог срабатывания АЦП
#define WAIT_EN	250	// сколько измерений должно быть ниже порога, чтобы импульс считать закончившимся
#define WAIT_ST	250	// сколько измерений должно быть, включить FLAG=Warten_START
#define Kt 0.1	//коэффициент преобразования из значения АЦП в ток.

// Массивы

char TIME[12];
char RES_IMP[12];
char STROM[12];

//********************************************************************************************//

interrupt[TIM2_COMP] void timer2_comp_isr(void){ // прерывание по совпадению 5кГц

    ACP_Anzahl=ADCW; // Anzahl
    
        if(ACP_Anzahl<ADC_MIN){  // если с АЦП пришло маленькое число, ну прям очень маленькое

            SCHLECHT_Anzahl++; // считаем сколько раз отдаваемое с АЦП число плохое
            
            if((SCHLECHT_Anzahl==WAIT_EN) && (R_Umwandlung==1)){ // если режим (R_Umwandlung=тру) а это возможно когда хоть раз проскочил импульс выше чем минимальная уставка (это в свою очередь означает что аппарат работал (если конечно не помешала наводка или шумы, ну тут на практике загрубим и будет норм), а если работал то при ожидании 250 прерываний (тоесть синусоида пришла и больше не возвращается) отключаемся и выводим флаг окончания измерений)

                SCHLECHT_Anzahl=0;
                
                Arbeit_END=1; // Arbeit_END вывод на экран окончания результата измерения
                
                 lcd_clear(); 
                
                TIMSK=0; // больше нету прерываний, переменные остаются неизменными 
                
                    TCCR2=0; // больше нету таймера по совпадению OCR2
                    
                        ADCSR=0;
            }else{
            
                if(SCHLECHT_Anzahl==1000){SCHLECHT_Anzahl=0;}} // это режим ожидания запуска. Сброс счетчика обусловлено (шоб не крутил много)    
        }else{
        
            R_Umwandlung=1; // режим преобразования
            
            KOL++; // считаем кол-во
            
                if(SCHLECHT_Anzahl>0){KOL=KOL+SCHLECHT_Anzahl; SCHLECHT_Anzahl=0;} // если есть плохое время плюсуем его к времени текущих полож. подсчетов. Прикол в том что В ДВУХПОЛУПЕРИОДНОМ выпрямлении синусоида спадает до 0 а потом нарастает. И если считать только время положительных значений то можно потерять реальное время наличия синусоиды. Визуальное обьяснение в приложеной картинке 
                
                if(KOL==1000){MS++; KOL=0; LCD_UP=1;}   // 0.2 секунды 
                
                CURRENT=(float) ACP_Anzahl/100; // (ACP_Anzahl/10) дело в том что в реальности есть подстроечный резюк в цепи ООС операционника, а это значит что могу подганять аналоговую часть к цифровой. Другими словами посчитаю как здесь будет удобно, а потом подберу коэф. усиления.
                
                S_INT=CURRENT*CURRENT;
                
                S_RES=S_RES+S_INT;            
}}

void Update_LCD(){

 lcd_gotoxy(0,0);
 sprintf(STROM,"im:%d02A",ACP_Anzahl);
 lcd_puts(STROM);  
            
 if(MS%5==0){WATCH++;} // 1 секунда
 lcd_gotoxy(10,0);
 sprintf(TIME,"t:%ds",WATCH);
 lcd_puts(TIME);
 
 S_RES=S_RES/5000; // 5 кГ
 lcd_gotoxy(0,1);
 sprintf(RES_IMP,"A:%dA2*s",S_RES);
 lcd_puts(RES_IMP); 

}

void end_LCD(){
    CURRENT=sqrt((S_RES/5000)/WATCH);
    
    lcd_gotoxy(0,0);
    sprintf(STROM,"I:%dA",CURRENT);
    lcd_puts(STROM); 
   
    lcd_gotoxy(10,0);
    sprintf(TIME,"t:%ds",WATCH);
    lcd_puts(TIME);
     
    lcd_gotoxy(0,1);
    sprintf(RES_IMP,"A:%dA2*s",S_RES);
    lcd_puts(RES_IMP);

}

void main(void){

//****************************** Системные настройки ******************************//

    ADMUX=0xC0; // Номер порта и опорное напряжение 2.56
    ADCSR=(1<<ADEN | 1<<ADSC | 1<<ADFR | 0<<ADIF | 0<<ADIE | 1<<ADPS2 | 1<<ADPS1 | 0<<ADPS0); // Настройки АЦП
    
    TCCR2=(0<<CS22 | 1<<CS21 | 0<<CS22 | 1<<WGM21); // 5 kHz и по совпадению OCR2
    OCR2=0x64; // число 100
    TIMSK=(1<<OCIE2); // включили счетчик
    
    lcd_init(16); // инициализация экрана
    
    #asm("sei") // разрешение прерывания

//*********************************************************************************//

// первый запуск (режим ожидания)
    lcd_gotoxy(0,0);
    lcd_puts("I:0A");  

    lcd_gotoxy(10,0);
    lcd_puts("t:0s");
            
    lcd_gotoxy(0,1);
    lcd_puts("A:0kA2*s");


while(1){
    if(LCD_UP==1){Update_LCD(); LCD_UP=0;};
        if(Arbeit_END==1){end_LCD();}}
}
23456789.png
(64.56 КБ) 284 скачивания
Реклама
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18783
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

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

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

Сообщение panya »

ARV писал(а):судя по картинке вам нужно интегрировать не абы какой импульс, а полупериод сетевого напряжения? это так?
Да родненькие 50 Гц. Немного подвыпрямленные.
ARV писал(а):импульс одиночный или периодический, как на картинке? может быть, вы вообще действующее значение тока хотите получить?
1. Импульс одиночный. Сначала небыло, потом появился секунд так на 20 (притом непрерывно), потом пропал.
2. Меня интересует ИМПУЛЬС, он складывается с мгновенных значений тока

3. Притом немного интересует и ток. Но получить я его желаю из формулы: A=(I^2)*t;

Хотя в режиме (когда изделие работает), я вывожу мгновенное значение, так для красоты
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18783
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

давайте по порядку, ибо если бы вы с самого начала ПРАВИЛЬНО ЗАДАЛИ ВОПРОС, решение было бы давным-давно найдено.

1. термин "импульс" плохо согласуется с "подвыпрямленные 50 Гц" и "появляется секунд на 20". тут надо корректно сформулировать, что вам надо: то ли выбрать любой полупериод из этих 20 секунд и его измерить, то ли каждый полупериод из 2000, то ли все 2000 полупериодов проинтегрировать, то ли еще как-то иначе.

2. прежде чем говорить про мгновенные значения, ответьте четко на первый вопрос.

3. квадрат тока, умноженный на время - это что в итоге должно быть? что за величина, характеристика, параметр? я весьма далек от всех прикладных областей науки и техники и чего-то могу не знать в принципе... потому и интересуюсь.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

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

Сообщение COKPOWEHEU »

Отформатируйте наконец код.
Идентификатор из заглавных букв в Си приняты для констант, а не для переменных.
Названия переменных, процедур и прочего лучше делать на английском, а не на немецком или, скажем, транслите. Комментарии - по желанию, но лучше на английском, либо родном.
Стоит выработать свой стиль форматирования или, лучше, взять готовый, и пользоваться им. Отступ от предыдущего блока до следующего должен быть фиксированным, а не как у вас: тут пробел, там четыре.
OCR2=0x64; // число 100
Почему 100 а не 99? Почему шестнадцатеричная запись а не десятичная?
S_RES=S_RES/5000; // 5 кГ
Килограмм пишется кг (маленькими буквами), а килогерц - кГц.
volatile float S_INT=0.00; // S_intermediate (Проможуточное S)
Про точность чисел м плавающей точкой и скорость работы с ними уже говорили.
sprintf(RES_IMP,"A:%dA2*s",S_RES);
Про вывод числа с плавающей точкой как целое промолчим.
3. квадрат тока, умноженный на время - это что в итоге должно быть? что за величина, характеристика, параметр? я весьма далек от всех прикладных областей науки и техники и чего-то могу не знать в принципе... потому и интересуюсь.
Работа тока, умноженная на сопротивление, но сопротивление константа, так что ее можно пока опустить. А вообще, это способ найти среднеквадратичное (действующее) значение тока.
A=U^2*t/R, точнее, интеграл (U^2*dt/R), потом вычисляется постоянный ток, соответствующий той же работе по первой формуле.
Хотя в режиме (когда изделие работает), я вывожу мгновенное значение, так для красоты
В данном случае это бесполезно и некрасиво - выводятся случайные значения от 0 до максимума. Хаотично мигающие цифры мало кому нравятся.
.
К сожалению код плохо читаем, чтобы сказать что-то более конкретное.
Реклама
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18783
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

COKPOWEHEU писал(а):Работа тока, умноженная на сопротивление, но сопротивление константа, так что ее можно пока опустить. А вообще, это способ найти среднеквадратичное (действующее) значение тока.A=U^2*t/R, точнее, интеграл (U^2*dt/R), потом вычисляется постоянный ток, соответствующий той же работе по первой формуле.
я, конечно, академиев не кончал, но RMS вычисляется как корень из интеграла квадратов... для знакопостоянного сигнала можно и без квадратов обойтись просто усреднением... нет?
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

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

Сообщение panya »

ARV писал(а):давайте по порядку, ибо если бы вы с самого начала ПРАВИЛЬНО ЗАДАЛИ ВОПРОС, решение было бы давным-давно найдено.

1. термин "импульс" плохо согласуется с "подвыпрямленные 50 Гц" и "появляется секунд на 20". тут надо корректно сформулировать, что вам надо: то ли выбрать любой полупериод из этих 20 секунд и его измерить, то ли каждый полупериод из 2000, то ли все 2000 полупериодов проинтегрировать, то ли еще как-то иначе.

2. прежде чем говорить про мгновенные значения, ответьте четко на первый вопрос.

3. квадрат тока, умноженный на время - это что в итоге должно быть? что за величина, характеристика, параметр? я весьма далек от всех прикладных областей науки и техники и чего-то могу не знать в принципе... потому и интересуюсь.
Ответ на 1 вопрос:
Я неоднократно писал про инвертор. Предполагалось что из постоянки делают переменку. Думал что словом инвертор все сказано.

Нет любой полупериод выбирать не надо. Надо считать (I^2*t) с тех пор как: Подвыпрямленное появилось, до момента когда оно пропадет (притом он будет только один раз).

Ответ на 3-й:
Есть ИНВЕРТОР способный выдавать (200А при 220В переменки) при 234В постоянки. И есть его параметры. Одним из параметров которые гарантирует изготовитель это "Интегрально зависимое время отключения при перегрузке".

Другими словами: чем выше ток, тем быстрее отключаеться. А это связано с нагревом силовых транзисторов на которых тепло (I^2*R*t). Но так как производитель побеспокоился про нас, что б мы не лезли и не измеряли сопротивление открытого транзистора, то они говорят:

Пацаны:
Просто умножьте квадрат тока на время, и когда вы получите таким образом число ХХХ, то инвертор отключиться.
Исходя из этого:
чем выше ток, тем быстрее наступает число ХХХ и тем быстрее отключаеться инвертор.
чем меньше ток, тем медленее отключаеться инвертор.

Вот и необходимо сопоставить заявленное число с реальным!

отсюда и получаеться интегрально зависимая выдержка времени

ток в норме (номинальный или выше) ему вообще по барабану, хоть трое суток работать


==============
И еще. мне нравится как оно работает, только вот проблема с числами. Помогите или отправьте почитать где-то как вывести толково.
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18783
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

если у вас инвертор выдает синусоиду, то:
1. вам не надо вычислять кучу мгновенных значений полупериодов! достаточно определить МАКСИМУМ (амплитудное значение) и по известной формуле ВЫЧИСЛИТЬ значение RMS, которое затем возвести в квадрат и умножить на время. сделать это можно элементарно, просто в обработчике прерывания АЦП сравнивая новый отсчет с предыдущим:

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

if(new_adc > old_adc) old_adc = new_adc;
else {
   peak_value = old_adc;
   // прекратить замеры и начать вычисления
}
2. вам нужно просто измерить ДЛИТЕЛЬНОСТЬ вашей перегрузки инвертора, причем с довольно некритичной точностью. для этого вам достаточно ПОДСЧИТАТЬ количество полупериодов. сделать это можно элементарно.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

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

Сообщение panya »

ARV писал(а):если у вас инвертор выдает синусоиду, то:
1. вам не надо вычислять кучу мгновенных значений полупериодов! достаточно определить МАКСИМУМ (амплитудное значение) и по известной формуле ВЫЧИСЛИТЬ значение RMS, которое затем возвести в квадрат и умножить на время. сделать это можно элементарно, просто в обработчике прерывания АЦП сравнивая новый отсчет с предыдущим:

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

if(new_adc > old_adc) old_adc = new_adc;
else {
   peak_value = old_adc;
   // прекратить замеры и начать вычисления
}
Как показывает практика, ток всетаки имеет свойство изменяться в некоторых приделах, как правило уменьшаться. Транзисторы греються и увел. свое сопративление. Поэтому думаю лучше измерять кучу мгновенных значений
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

ARV писал(а):я, конечно, академиев не кончал, но RMS вычисляется как корень из интеграла квадратов... для знакопостоянного сигнала можно и без квадратов обойтись просто усреднением... нет?
А попробуйте честно взять интеграл. Мощность на активной нагрузке не зависит от полярности и будет одинаковой как для переменного, так и для пульсирующего токов.
И еще. мне нравится как оно работает, только вот проблема с числами. Помогите или отправьте почитать где-то как вывести толково.
Про *printf("%i",(float)x) я уже писал. Вообще, всю математику лучше привести к целочисленному виду - и точнее и быстрее будет. Но надо осторожно с точностью. Более точно смогу сказать, когда будет возможно проанализировать код.
Есть ИНВЕРТОР способный выдавать (200А и 220В переменки) при 234В постоянки.
Странное построение предложения. Выходное напряжение у него константа? Одного тока для определения выходной мощности недостаточно, нужно знать полное сопротивление нагрузки, либо напряжение на ней.
Ответ на 1 вопрос:
Я неоднократно писал про инвертор. Предполагалось что из постоянки делают переменку. Думал что словом инвертор все сказано.
Есть схемы инверторов, выдающие постоянное напряжение. В радиолюбительской литературе неоднократно писали про бесперебойники для ИИП с выходным напряжением 310 В постоянки. Внутри, разумеется, инвертор. А под импульсом действительно понимается одиночный импульс, от 0 (или около того) до максимума и обратно до нуля.
Открыл глаза
Сообщения: 42
Зарегистрирован: Сб ноя 29, 2014 21:20:52

Сообщение panya »

COKPOWEHEU писал(а):
Есть ИНВЕРТОР способный выдавать (200А и 220В переменки) при 234В постоянки.
Странное построение предложения. Выходное напряжение у него константа? Одного тока для определения выходной мощности недостаточно, нужно знать полное сопротивление нагрузки, либо напряжение на ней.
)) Согласен. Но когда он работает на сеть (к примеру на секцию потребителей). Можно сказать что он способен выдать потребителям 200А при напряжении на его шинах 220В. Хотя справедливо подмечено что сформулировано некорректно.

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

Про *printf("%i",(float)x) я уже писал. Вообще, всю математику лучше привести к целочисленному виду - и точнее и быстрее будет. Но надо осторожно с точностью. Более точно смогу сказать, когда будет возможно проанализировать код.
Если я правильно понимаю если целочисленную переменную 749/10= то будет 74. А не 74.9 Я правильно понимаю? Если 74 то точность здесь не такая уж и большая
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

Можно сказать что он способен выдать потребителям 200А при напряжении на его шинах 220В. Хотя справедливо подмечено что сформулировано некорректно.
Даже если выдает идеальную синусоиду ровно 220 В, придется измерять и напряжение и ток, так как фаза заранее неизвестна, а только тока для этого недостаточно. Вдруг сопротивление потребителя обратно пропорционально напряжению?
Если я правильно понимаю если целочисленную переменную 749/10= то будет 74. А не 74.9 Я правильно понимаю? Если 74 то точность здесь не такая уж и большая
Про числа с фиксированной точкой я уже упоминал. В stdio вроде нет стандартных средств для их вывода, но все равно лучше писать вручную.
Открыл глаза
Сообщения: 42
Зарегистрирован: Сб ноя 29, 2014 21:20:52

Сообщение panya »

COKPOWEHEU писал(а):
Можно сказать что он способен выдать потребителям 200А при напряжении на его шинах 220В. Хотя справедливо подмечено что сформулировано некорректно.
Даже если выдает идеальную синусоиду ровно 220 В, придется измерять и напряжение и ток, так как фаза заранее неизвестна, а только тока для этого недостаточно. Вдруг сопротивление потребителя обратно пропорционально напряжению?
Вот в том числе и для этого и нужна интегральная зависимость времени отключения от тока. Ну всеже как преобразововать числа. 444/10 и получить 44.4 а не 44
а про 777*0.04567=?? я вообще молчу. Так как организовать то?
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

444/10 так и выводить как 444, только перед последним разрядом точку поставить => 44.4
777*0.04567 = 777*4.567/100 -> 3548, точка перед двумя последними разрядами (потому что /100), точность выбирается по желанию => 35.48, но лучше для вычислений выбрать число побольше: 777*0.4567 = 777*4.567/100 (перевод числам с фиксированной точкой) = 777*1169,152/100/256 ~ 777*1169/100/256 (округляем до целых) = 35,48 (во 2-3 разряде после запятой, правда, точность потерялась, но это подбирается множителем побольше чем 256).
Для правильного округления вроде можно делить на половину делимого, прибавлять 1 и потом делить еще на 2блибо умножать на удвоенное число. Но тут надо проверять, как-то так:

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

x=x*y; //что надо получить
x=x*(2*y); x=x+1; x/=2; // так как y - дробное, последний разряд не потеряем, а 2*y - константа.
Либо
x=x/y; //что надо получить
x=2*x/y; x=x+1; x/=2; //тут y может быть целым и даже переменным.
Открыл глаза
Сообщения: 42
Зарегистрирован: Сб ноя 29, 2014 21:20:52

Сообщение panya »

Как более точно записать, в отношении точности. Сделал так:
========
ACP_Anzahl - число с АЦП
========

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

// - - Напряжение (2.56 == числу 1023 == 128А мгновенного тока в первичной цепи - - //
            // Исходя из того что ((1023)/8)=128
            CURRENT=ACP_Anzahl>>3; // делим на 8
    

При напряжении на АЦП 2.56 МК выдает на экран 127 А. 1 ампер всетаки жалко


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

Сообщение COKPOWEHEU »

А учитываете, что опорное напряжение не 2.56 В, а от 2.3 до 2.7 В согласно документации? В реальности разброс намеряли и больше.
1023 == 128А
Скорее, 127,875. Насчет округления я уже говорил. I = ((U/4)+1)/2 = (U+4)/8 = (U+4)>>3.
Открыл глаза
Сообщения: 42
Зарегистрирован: Сб ноя 29, 2014 21:20:52

Сообщение panya »

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

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

#include <mega8.h>

#include <alcd.h>
#include <stdio.h>
#include <delay.h>
#include <math.h>


//#################### ZHELEZYAKA ################################################################
//                                                                                               #
// ATmega8                                                                                       #
//                                                                                               #
//################################################################################################

//#################### Настройки проекта #########################################################
// setting для atmel studio 6.0

//#define F_CPU 1000000 // частота камня 8.000.000 Гц

//#include <avr/io.h>

//#include <util/delay.h>

//#include <math.h>

//#include <avr/interrupt.h>

//#include "hd44780.h" // библиотека экрана


//#################### Статус системы ############################################################

_Bool conversion=0;                                                // режим преобразования да/нет   #

_Bool work_end=0;                                                // работа окончена да/нет        #

_Bool uploading_lcd=0;                                            // вывод на экран да/нет         #

//#################### Числовые переменные #######################################################

volatile unsigned long res=0;                                    // результат res (ресурс 266с)   #

volatile unsigned long un_res=0;                                // промежуточный результат       #

volatile unsigned long res_residue=0;                            // результирующая погрешность    #

volatile unsigned short current=0;                                // ток                           #

volatile unsigned short acp_number=0;                            // число с АЦП                   #

volatile unsigned char residue=0;                                // остаток отцелочис. деления    #

float hh=0.0;

float qq=0.0;
//#################### Переменные времени ########################################################

volatile unsigned short kol=0;                                    // кол-во полож. подсчетов       #

volatile unsigned int time=0;                                    // время работы системы          #

volatile unsigned short poor_number=0;                            // число плохих подсчетов        #

//#################### Макросы ###################################################################

#define ADC_MIN 4                                                // порог срабатывания АЦП        #

#define WAIT_EN 500                                                // кол-во измерений ниже порога  #

#define WAIT_ST 500                                                // ол-во измерений выше порога   #

#define F_ISR 1000                                                // частота прерываний в Гц       #

//#################### Массивы ###################################################################

char output_time[12]; char output_res[12]; char output_current[12];

// начало прерывания с частотой F_ISR
interrupt[TIM2_COMP] void timer2_comp_isr(void){

    acp_number=ADCW;

        if(acp_number<ADC_MIN){

            poor_number++;

                if((poor_number==WAIT_EN) && (conversion==1)){

                    TIMSK=0x00; 
                    TCCR2=0x00; 
                    ADCSR=0x00;
                    
                        poor_number=0;
                        work_end=1; // work_end флаг для вызова функции work_done

                        lcd_clear();
                        
                }else{

                            if(poor_number==WAIT_ST){poor_number=0;}} // режим ожидания запуска, обнуление счетчика
        }else{

            conversion=1; // режим преобразования
            time++;
            kol++;

                if(poor_number>0){time=time+poor_number; poor_number=0;} // если есть плохое время плюсуем его к текущему времени

                    if(kol==F_ISR){kol=0; uploading_lcd=1;}
                        
                        // вычисляем ток
                        current=(acp_number>>3);
                        
                        // вычисляем остаток (первое приближение)
                        residue=(acp_number-(current<<3));
                        
                        // результат
                        un_res=(current*current);
                        un_res+=(((2*acp_number*residue)+(residue*residue))>>6); // (a+b)^2=(a^2+2*a*b+b^2)
                        res=res+un_res;
                                                
}}

void Update_LCD(){

lcd_gotoxy(0,0); 
sprintf(output_current,"i:%dA",current);
lcd_puts(output_current);

lcd_gotoxy(8,0); 
sprintf(output_time,"t:%ds",(time/F_ISR));
lcd_puts(output_time);

lcd_gotoxy(0,1);
lcd_puts("A:Obrabotka");}

void work_done(){

qq=res/time;
hh=sqrt(qq);
    
lcd_gotoxy(0,0);
sprintf(output_current,"I:%.2fA",hh);
lcd_puts(output_current);

lcd_gotoxy(8,0); 
sprintf(output_time,"t:%d,%ds",(time/F_ISR),((time%F_ISR))/100);
lcd_puts(output_time);

lcd_gotoxy(0,1);
sprintf(output_res,"A:%lu,%dA2*s",(res/F_ISR),((res%1000))/100);
lcd_puts(output_res);

}

void main(void){

//****************************** Системные настройки ******************************//

ADMUX=0x40; // Номер порта и опорное напряжение AREF

ADCSR=0xE4; // Настройки АЦП

TCCR2|=(0<<CS20 | 1<<CS21 | 0<<CS22 | 1<<WGM21); // 1 kHz и по совпадению OCR2

OCR2=0x7C; // число 124

TIMSK=0x80; // включили счетчик

lcd_init(16); // инициализация экрана
    
#asm("sei") // разрешение прерывания

//*********************************************************************************//

// первый запуск (режим ожидания)
lcd_gotoxy(0,0);
lcd_puts("I:0A");

lcd_gotoxy(8,0);
lcd_puts("t:0s");

lcd_gotoxy(0,1);
lcd_puts("A:0A2*s");

while(1){
    
if(uploading_lcd==1){lcd_clear(); Update_LCD(); uploading_lcd=0;};
if(work_end==1){work_done();}}
}



Открыл глаза
Сообщения: 42
Зарегистрирован: Сб ноя 29, 2014 21:20:52

Сообщение panya »

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

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

un_res=(current*current);
un_res+=(((2*acp_number*residue)+(residue*residue))>>6); // (a+b)^2=(a^2+2*a*b+b^2)
Я так понимаю начинать надо как-то так:
#asm

LDS R30, _current
MUL R30,R30
STS _un_res,R0
STS _un_res+1,R1

// хочется не грузить результат в переменную а пересохранить значения в регистры:

#asm

LDS R30, _current
MUL R30,R30
MOVW R15:R16,R0:R1

#endasm

// но пока все перемножу (a+b)^2=a^2+2ab+b^2 (это так реализована точность вычисления) используется много регистров в итоге на экране ерунда выводится на экран в протеусе, какието иероглифы. Я так понимаю затронул какието регистры которые не стоит трогать

Помогите кто знает) использовал любые регистры от R2 до R31
Держит паяльник хвостом
Сообщения: 933
Зарегистрирован: Ср апр 13, 2011 11:09:20
Откуда: Екатеринбург

Сообщение Alkul »

panya писал(а): Я так понимаю начинать надо как-то так:

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

#asm
LDS R30, _current
MUL R30,R30
STS _un_res,R0
STS _un_res+1,R1
Вот если честно - всю Вашу программу лучше сразу на ассемблере и писать, она ж простая.
Я на Си под МК практически не программирую, но мне кажется, что проблема тут вот в чем:
Си-шный компилятор использует регистры МК по своему усмотрению. Поэтому при вызове ассемблерной вставки в Си-шный код я бы сохранил на стеке все используемые регистры, а перед выходом из ассемблерной вставки - восстановил. как-то так:

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

#asm
push R30
push R0
push R1
LDS R30, _current
MUL R30,R30
STS _un_res,R0
STS _un_res+1,R1
pop R1
pop R0
pop R30
#endasm
Вторая проблема, как мне кажется, может быть в следующем:
команды LDS и STS в качестве операндов имеют регистр и адрес в ОЗУ, например:

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

lds R30,_current
R30 - это регистр, в который заносятся данные, хранящиеся в ячейке памяти с адресом _current, при этом _current - это 16-ти битное число. Так вот, не факт, что объявляя Си-шные переменные

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

volatile unsigned long un_res=0;
volatile unsigned short current=0; 
Вы определяете имена un_rez и current как адреса, "понятные" ассемблерным командам lds и sts.
То есть, нужно в симуляторе проверить, действительно ли команды lds и sts читают/пишут именно в переменные un_rez и current, а не в произвольные места в ОЗУ.
Ответить

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