Преобразователь частоты для 3х фазового асинхронного двигателя на Arduino UNO

step64
Offline
Зарегистрирован: 01.04.2013

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

andryn
Offline
Зарегистрирован: 08.06.2018

step64 пишет:

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

Да, был такой косячек. При остановке не останавливались ШИМ, поэтому при нажатии на кнопу для остановки могла оставться генерация на ключах из разных фаз. Сейчас это убрал. Под нагрузкой из мотора винчестера проверил, мотор крутится, но работает кривовато, больше 70Гц не раскручивается. Еще мне не нравится механизм плавного увеличения частоты по 1Гц, наверно переделаю.

Изменения такие:

@@ -314,6 +324,13 @@
             Serial.print("Button press");
             if ((run & B00000010)) { //Надо остановить
                 TIMSK2 &= ~(1<<TOIE2); //остановка таймера
+                //Установить все PWM в 0
+                OCR0A = 0;
+                OCR0B = 0;
+                OCR1A = 0;
+                OCR1B = 0;
+                OCR2A = 0;
+                OCR2B = 0;
                 //Вывод на экран F + частота
                 disp.display(0,15);
                 disp.display(1,(dfreq_set%1000)/100);

 

andryn
Offline
Зарегистрирован: 08.06.2018

Для всех интересующихся темой: провел небольшие испытания в железе.

Описание стенда: Arduino Uno, Переменный резистор 10К(подключен к A0), Кнопка без фиксации(подключена к А1), дисплей 1602 с I2C(подключен к A4, A5), силовая часть на ir2101+2SK3569, моторчик от FDD 3''(обмотки соединены звездой). Питание драйверов 12В, питание мотора тоже 12В, питание arduino 5В.

Основной результат испытаний: работает. Покрутил моторчик частотами от 10 до 200Гц - работает стабильно.

Есть некоторые особенности, которе не проявляется на эмуляторах:

После запуска таймера с генерацией нельзя ничего перерисовать на дисплее, для этого нужно таймер остановить, перерисовать, потом таймер запустить. Это плохо сказывается на работе мотора. Может это особенность работы LiquidCrystal_I2C, но пока разбираться неохота.

Частота увеличивается по +5Гц примерно 2 раза в секунду, т.е. 50Гц набирает где-то за 5 секунд.

Кнопка старт/стоп работает. Если покрутить резистор в процессе работы мотора, то генерация останавливается принудительно.

Свежий код:

/* Main.ino
 *
 * Created:   AVS, 2018-06-06
 * Processor: Arduino Uno
 * Compiler:  Arduino AVR
 */

#include "TM1637.h"
#include "LiquidCrystal_I2C.h"


const byte PROGMEM sineP4[]  = { //Константа для первой четверти синуса. 256 значений.
   //Для реализации Dead Time при переключении с Hight pin на Low pin добавим несколько нулевых значений. 
   //Для частоты 125Гц значений должно быть 2, т.к. описана 1/4 периода. Сделаем 3 значения, этого точно хватит :)
   0,0,
   0,2,3,5,6,8,9,11,13,14,16,17,19,20,22,24,25,27,28,30,31,33,34,36,38,39,41,42,44,45,47,48,50,51,53,55,56,58,59,61,62,64,65,67,68,70,71,73,74,76,77,79,80,82,83,85,86,88,89,91,92,94,95,96,
   98,99,101,102,104,105,107,108,109,111,112,114,115,116,118,119,121,122,123,125,126,128,129,130,132,133,134,136,137,138,140,141,142,143,145,146,147,149,150,151,152,154,155,156,157,159,160,161,162,164,165,166,167,168,169,171,172,173,174,175,176,178,179,180,
   181,182,183,184,185,186,187,188,190,191,192,193,194,195,196,197,198,199,200,201,202,203,203,204,205,206,207,208,209,210,211,212,213,213,214,215,216,217,218,218,219,220,221,222,222,223,224,225,225,226,227,228,228,229,230,230,231,232,232,233,234,234,235,235,
   236,237,237,238,238,239,239,240,241,241,242,242,243,243,243,244,244,245,245,246,246,247,247,247,248,248,248,249,249,249,250,250,250,251,251,251,251,252,252,252,252,253,253,253,253,253,254,254,254,254,254,254,254,255,255,255,255,255,255,255,255,255,255,255
};

 

// const double refclk=31372.549;  // =16MHz / 510
const unsigned int refclk = 31374;      // Реальная внутренняя частота

volatile unsigned long phaccu;    // аккумулирующая переменная, 32 бит
volatile unsigned long delta;     // переменная, расчитывающаяся на основе refclk и желаемой частоты работы
volatile unsigned int incr;       // переменная для вычисления итерации(10 бит = 2 бит + 8 бит)
volatile byte dfreq;              //Текущая Частота, Гц
volatile byte dfreq_set;          //Установленная частота, Гц

byte inctmr;             // переменная для реализации таймера в loop

//Управление частотой
const int deltafreq=10;                 // deltafreq пунктов изменения аналогового входа дают изменение частоты на 2Гц. Если deltafreq=20, то изменение до 110Гц
unsigned int setfreq=1024+deltafreq;    // переменная для хранения предыдущего измерения заначения пина. По умолчанию гарантированно больше 1023 на deltafreq.

//******************************************************************
//Для регулировки частоты переменным резистором
#define ADJ_PIN A0 // Пин, к которому подключен переменный резистор, регулирующий частоту
int adj;           // переменная для чтения значения с переменного резистора

//*******************************************************************
//Кнопка старт/стоп
#define BTN_START_PIN A1 // Пин, к которому подключена кнопка старта
byte run = B00000000; //младший бит(B00000001) - нажата ли кнопка, старший(B00000010) на до ли запускать


//******************************************************************
//Индикатор TM1637
#define CLK 8
#define DIO 7
TM1637 disp(CLK, DIO);
//******************************************************************
//Дисплей LiquidCrystal_I2C
LiquidCrystal_I2C lcd(0x27,16,2);   // Задаем адрес и размерность дисплея. 

//******************************************************************
// Настройка таймеров
void Setup_timer() {

    // TIMER0
    TCCR0A |=  (1<<COM0A1); // COM0A1, COM0A0: 1 0 - None-inverted mode для OC0A, для инверсного режима ШИМ нужно 1 1
    TCCR0A &= ~(1<<COM0A0); 
    TCCR0A |=  (1<<COM0B1); // COM0B1, COM0B0: 1 0 - None-inverted mode для OC0B, для инверсного режима ШИМ нужно 1 1
    TCCR0A &= ~(1<<COM0B0); 
    TCCR0A |=  (0<<WGM01);  // WGM02, WGM01, WGM00: 0 1 1 - Fast PWM 
    TCCR0A |=  (1<<WGM00);

    TCCR0B &= ~(1<<CS02);   // CS02, CS01, CS00: 0 0 1 - тактовый генератор CLK
    TCCR0B &= ~(1<<CS01); 
    TCCR0B |=  (1<<CS00); 
    TCCR0B &= ~(1<<WGM02);

    // TIMER1
    TCCR1A |=  (1<<COM1A1); // COM1A1, COM1A0: 1 0 - None-inverted mode для OC1A, для инверсного режима ШИМ нужно 1 1
    TCCR1A &= ~(1<<COM1A0); 
    TCCR1A |=  (1<<COM1B1); // COM1B1, COM1B0: 1 0 - None-inverted mode для OC1B, для инверсного режима ШИМ нужно 1 1
    TCCR1A &= ~(1<<COM1B0); 
    TCCR1A &= ~(0<<WGM11);  // WGM13, WGM12, WGM11, WGM10: 0 1 0 1 - Fast PWM, 8bit 
    TCCR1A |=  (1<<WGM10);

    TCCR1B &= ~(1<<CS12);   // CS12, CS11, CS10: 0 0 1 - тактовый генератор CLK
    TCCR1B &= ~(1<<CS11); 
    TCCR1B |=  (1<<CS10); 
    TCCR1B &= ~(1<<WGM13);
    TCCR1B |=  (1<<WGM12);

    // TIMER2
    TCCR2A |=  (1<<COM2A1); // COM2A1, COM2A0: 1 0 - None-inverted mode для OC2A, для инверсного режима ШИМ нужно 1 1
    TCCR2A &= ~(1<<COM2A0);
    TCCR2A |=  (1<<COM2B1); // COM2B1, COM2B0: 1 0 - None-inverted mode для OC2B, для инверсного режима ШИМ нужно 1 1
    TCCR2A &= ~(1<<COM2B0);
    TCCR2A |=  (1<<WGM21);  // WGM22, WGM21, WGM20: 0 1 1 - Fast PWM
    TCCR2A |=  (1<<WGM20);

    TCCR2B &= ~(1<<CS22);   // CS22, CS21, CS20: 0 0 1 - тактовый генератор CLK
    TCCR2B &= ~(1<<CS21); 
    TCCR2B |=  (1<<CS20); 
    TCCR2B &= ~(1<<WGM22);

    //Установить все PWM в 0
    OCR0A = 0;
    OCR0B = 0;
    OCR1A = 0;
    OCR1B = 0;
    OCR2A = 0;
    OCR2B = 0;
    //Разрешить/запретить прерывания по таймерам
    TIMSK0 &= ~(1<<TOIE0); //Запретить прерывания на таймер 0
    TIMSK1 &= ~(1<<TOIE1); //Запретить прерывания на таймер 1
    TIMSK2 &= ~(1<<TOIE2); //Запретить прерывания на таймер 2

}
 
//******************************************************************
//Прерывание по таймеру 2 (TIMER2)
//Работает с частотой 
ISR(TIMER2_OVF_vect) {
    PIND |= (1 << 2); //Для замера скорости
    //Таймер 0 | выводы 5 и 6  | OC0B, OC0A
    //Таймер 1 | выводы 9 и 10 | OC1A, OC1B
    //Таймер 2 | выводы 3 и 11 | OC2B, OC2A

    //Для теста
    //ШИМ 0, pins 6, 5
    //OCR0A = 128;
    //OCR0B = 128;
    //ШИМ 1, pins 9, 10
    //OCR1A = 128;
    //OCR1B = 128;
    //ШИМ 2, pins 11, 3
    //OCR2A = 128;
    //OCR2B = 128;

    phaccu = phaccu + delta;   // Добавляем дельту(рассчитна по частоте) в аккумулирующую переменную 32 бит
    //Фаза 1
    incr = (phaccu >> 22); //10 бит. Старщие 2 бита(incr & 0x300) - номер четверти, младшие 8 бит(incr & 0xFF) - номер значения из таблицы констант

    switch (incr & 0x300) { //B1100000000=0x300
        case 0x0: //Первая четверть, выбрать константу из массива в пин A.
        {
            OCR0B = 0;
            OCR0A = pgm_read_byte_near(sineP4 + (incr & 0xFF));
            break;
        }
        case 0x100: //Вторая четверть, выбрать обратнную константу из массива в пин A
        {
            OCR0B = 0;
            OCR0A = pgm_read_byte_near(sineP4 + 255 - (incr & 0xFF));
            break;
        }
        case 0x200: //Третья четверть, выбрать константу из массива в пин B
        {
            OCR0A = 0;
            OCR0B = pgm_read_byte_near(sineP4 + (incr & 0xFF));
            break;
        }
        case 0x300: //Четвертая четверть, выбрать обратнную константу из массива в пин B
        {
            OCR0A = 0;
            OCR0B = pgm_read_byte_near(sineP4 + 255 - (incr & 0xFF));
            break;
        }
        default:
        {
            OCR0A = 0;
            OCR0B = 0;
            break;
        }
    }
    //Фаза 2. Смещена относительно фазы 1 на 120. 1/2*Pi = 512; 120 = 2/3*Pi = 512*4/3 = 683. Сдвинем фазу на 683 деления
    incr = incr + 683;
    //Уберем биты выще 10го B001111111111=0x3FF, чтобы попасть в массив констант
    incr &= 0x3FF;

    switch (incr & 0x300) { //B1100000000=0x300
        case 0x0: //Первая четверть, выбрать константу из массива в пин A.
        {
            OCR1B = 0;
            OCR1A = pgm_read_byte_near(sineP4 + (incr & 0xFF));
            break;
        }
        case 0x100: //Вторая четверть, выбрать обратнную константу из массива в пин A
        {
            OCR1B = 0;
            OCR1A = pgm_read_byte_near(sineP4 + 255 - (incr & 0xFF));
            break;
        }
        case 0x200: //Третья четверть, выбрать константу из массива в пин B
        {
            OCR1A = 0;
            OCR1B = pgm_read_byte_near(sineP4 + (incr & 0xFF));
            break;
        }
        case 0x300: //Четвертая четверть, выбрать обратнную константу из массива в пин B
        {
            OCR1A = 0;
            OCR1B = pgm_read_byte_near(sineP4 + 255 - (incr & 0xFF));
            break;
        }
        default:
        {
            OCR1A = 0;
            OCR1B = 0;
            break;
        }
    }
    //Фаза 3. Смещена относительно фазы 2 на 120. 1/2*Pi = 512; 120 = 2/3*Pi = 512*4/3 = 683. Сдвинем фазу на 683 деления
    incr = incr + 683;
    //Уберем биты выще 10го B001111111111=0x3FF, чтобы попасть в массив констант
    incr &= 0x3FF;

    switch (incr & 0x300) { //B1100000000=0x300
        case 0x0: //Первая четверть, выбрать константу из массива в пин A.
        {
            OCR2B = 0;
            OCR2A = pgm_read_byte_near(sineP4 + (incr & 0xFF));
            break;
        }
        case 0x100: //Вторая четверть, выбрать обратнную константу из массива в пин A
        {
            OCR2B = 0;
            OCR2A = pgm_read_byte_near(sineP4 + 255 - (incr & 0xFF));
            break;
        }
        case 0x200: //Третья четверть, выбрать константу из массива в пин B
        {
            OCR2A = 0;
            OCR2B = pgm_read_byte_near(sineP4 + (incr & 0xFF));
            break;
        }
        case 0x300: //Четвертая четверть, выбрать обратнную константу из массива в пин B
        {
            OCR2A = 0;
            OCR2B = pgm_read_byte_near(sineP4 + 255 - (incr & 0xFF));
            break;
        }
        default:
        {
            OCR2A = 0;
            OCR2B = 0;
            break;
        }
    }
}

void setup()
{
    Serial.begin(115200);        // connect to the serial port
    Serial.println("Test begin");

    DDRD = 0xFC;  // весь порт D - выход, кроме RX TX
    DDRB = 0xFF;  // весь порт B - выход

    //Настройка и остановка таймеров
    Setup_timer();
    
    dfreq = 9; //минимальнач частота
    //Дя регулировки частоты
    pinMode(ADJ_PIN, INPUT);

    //Кнопка старта
    pinMode(BTN_START_PIN, INPUT_PULLUP);
    //Индикатор
    disp.init();
    disp.point(false);
    disp.set(BRIGHT_TYPICAL); //яркость
    disp.clearDisplay(); //Очистить
    //LCD дисплей
    lcd.init();                            // Инициализация lcd             
    lcd.backlight();                       // Включаем подсветку
    // Курсор находится в начале 1 строки
    lcd.setCursor(0, 0);                        // Устанавливаем курсор в начало 1 строки
    lcd.print("3phase generator");            // Выводим текст
    lcd.setCursor(0, 1);                        // Устанавливаем курсор в начало 2 строки
    lcd.print("Stop            ");             // Выводим текст
}

void loop() {
    //PIND |= (1 << 2); //Для замера скорости
    //Реализация для изменения частоты переменным резистором
    if ((inctmr++) % 2048 == 0) { //Обрабатывается каждый 512-й вызов loop. Можно, наверно, увеличить.
        adj = (analogRead(ADJ_PIN)+analogRead(ADJ_PIN)+analogRead(ADJ_PIN))/3; //Усредненное значение на пине регулировки частоты
        if ( abs(adj - setfreq) >= deltafreq ){
            //Значение аналогового входа изменилось на deltafreq и более.
            //Сначала останавливаем прерывания по таймеру 2, иначе не работает
            TIMSK2 &= ~(1<<TOIE2);
            //Установить все PWM в 0
            OCR0A = 0;
            OCR0B = 0;
            OCR1A = 0;
            OCR1B = 0;
            OCR2A = 0;
            OCR2B = 0;
            
            //Переведем значение analogRead в частоту
            setfreq = adj;
            dfreq_set = 10 + 2*(setfreq/deltafreq);    // Минимальная частота 10Гц, на каждые 15 пунктов частота увеличивается на 2Гц
            
            //Заполнить значение для LCD частотой dfreq_set. Вывод: Set freq: + частота
            lcd.setCursor(0, 0);                      // Устанавливаем курсор в начало 1 строки
            lcd.print("Set freq:       ");            // Выводим текст
            lcd.setCursor(10, 0);                     // Устанавливаем курсор в 10 позицию
            lcd.print(dfreq_set);                     // Выводим текст
            lcd.setCursor(0, 1);                      // Устанавливаем курсор в начало 2 строки
            lcd.print("Stop            ");            // Выводим текст
            
            run = B00000000;       //Изменение состояния на выкл и запрет обработки кнопки
            run |= B00000001;      //Кнопку можно снова обрабатывать            
        }
    }
    if (inctmr % 256 == 0) { //Обрабатывается каждый 256-й вызов loop. Можно, наверно, увеличить.
        //Нажатие кнопки
        if ( digitalRead(BTN_START_PIN) == LOW && (run & B00000001) ){ //Нажата кнопка и нужно менять состояние
            if ((run & B00000010)) { //Надо остановить
                TIMSK2 &= ~(1<<TOIE2); //остановка таймера
                //Установить все PWM в 0
                OCR0A = 0;
                OCR0B = 0;
                OCR1A = 0;
                OCR1B = 0;
                OCR2A = 0;
                OCR2B = 0;
                //Вывод на экран F + частота
                /*disp.display(0,15);
                disp.display(1,(dfreq_set%1000)/100);
                disp.display(2,(dfreq_set%100)/10);
                disp.display(3,dfreq_set%10);
                */
                //Заполнить значение для LCD частотой dfreq_set. Вывод: Set freq: + частота
                lcd.setCursor(0, 1);          // Устанавливаем курсор в начало 2 строки
                lcd.print("Stop            ");// Выводим текст
                
                run = B00000000;       //Изменение состояния на выкл и запрет обработки кнопки
            }
            else {
                phaccu = 0;
                dfreq = 4; //Минимальная частота для увеличения до рабочей
                //Заполнить значение для LCD частотой dfreq_set. Вывод: Set freq: + частота
                lcd.setCursor(0, 1);          // Устанавливаем курсор в начало 2 строки
                lcd.print("Running         ");// Выводим текст
                run = B00000010;  //Изменение состояния на вкл и запрет обработки кнопки
            }
        }
        else if (digitalRead(BTN_START_PIN) == HIGH) { //кнопка отпущена
            run |= B00000001; //Кнопку можно снова обрабатывать
        }
    }

    
    if (dfreq < dfreq_set && (run & B00000010) && inctmr % 1048576/2 == 0) { //Плавное увеличение частоты, если нужная частота больше текущей. Резкое изменение, если меньше
        dfreq++; //Увеличение частоты

        if (dfreq % 5 == 0 || dfreq == dfreq_set) { //Увеличиваем каждые 5Гц
            delta = 0x100000000 * dfreq / refclk;  // Новое вычисление дельты
            TIMSK2 |=  (1<<TOIE2); //Запуск таймера
        }
        //delta = 0x100000000 * dfreq / refclk;  // Новое вычисление дельты
        //TIMSK2 |=  (1<<TOIE2); //Запуск таймера
    }
}

Дальнейшие планы:

1. Сделать реверс

2. Испытать на чем-то более силовом, вольт на 40. Есть идеи?

3. Сделать токовую защиту. Пока простенькую, типа датчика тока по линии питания мотора.

 

step64
Offline
Зарегистрирован: 01.04.2013

andryn пишет:

После запуска таймера с генерацией нельзя ничего перерисовать на дисплее, для этого нужно таймер остановить, перерисовать, потом таймер запустить. Это плохо сказывается на работе мотора. Может это особенность работы LiquidCrystal_I2C, но пока разбираться неохота.

Частота увеличивается по +5Гц примерно 2 раза в секунду, т.е. 50Гц набирает где-то за 5 секунд.

Кнопка старт/стоп работает. Если покрутить резистор в процессе работы мотора, то генерация останавливается принудительно.

Экран по 6 проводам пробовали подключать, результат тот же? Тоесть динамическая регулировка частоты отсутствует?

andryn
Offline
Зарегистрирован: 08.06.2018

step64 пишет:

Экран по 6 проводам пробовали подключать, результат тот же?

Не пробовал. Я всетаки склоняюсь к семисегентнику и паре светодиодов.

step64 пишет:

Тоесть динамическая регулировка частоты отсутствует?

Да, сейчас так. Не понятно как ее отобразить, если нужно останавливать таймер. Может придумаю что-то.

andryn
Offline
Зарегистрирован: 08.06.2018

Добавлена кнопка реверса на пине A2.

Код:

/* Main.ino
 *
 * Created:   AVS, 2018-06-06
 * Processor: Arduino Uno
 * Compiler:  Arduino AVR
 */

#include "TM1637.h"
#include "LiquidCrystal_I2C.h"


const byte PROGMEM sineP4[]  = { //Константа для первой четверти синуса. 256 значений.
   //Для реализации Dead Time при переключении с Hight pin на Low pin добавим несколько нулевых значений. 
   //Для частоты 125Гц значений должно быть 2, т.к. описана 1/4 периода. Сделаем 3 значения, этого точно хватит :)
   0,0,
   0,2,3,5,6,8,9,11,13,14,16,17,19,20,22,24,25,27,28,30,31,33,34,36,38,39,41,42,44,45,47,48,50,51,53,55,56,58,59,61,62,64,65,67,68,70,71,73,74,76,77,79,80,82,83,85,86,88,89,91,92,94,95,96,
   98,99,101,102,104,105,107,108,109,111,112,114,115,116,118,119,121,122,123,125,126,128,129,130,132,133,134,136,137,138,140,141,142,143,145,146,147,149,150,151,152,154,155,156,157,159,160,161,162,164,165,166,167,168,169,171,172,173,174,175,176,178,179,180,
   181,182,183,184,185,186,187,188,190,191,192,193,194,195,196,197,198,199,200,201,202,203,203,204,205,206,207,208,209,210,211,212,213,213,214,215,216,217,218,218,219,220,221,222,222,223,224,225,225,226,227,228,228,229,230,230,231,232,232,233,234,234,235,235,
   236,237,237,238,238,239,239,240,241,241,242,242,243,243,243,244,244,245,245,246,246,247,247,247,248,248,248,249,249,249,250,250,250,251,251,251,251,252,252,252,252,253,253,253,253,253,254,254,254,254,254,254,254,255,255,255,255,255,255,255,255,255,255,255
};
#define PHASE_OFFSET 683 //Смещение фазы относительно предыдущей на 120 градусов(3/4*Pi). Расчет: 1/2*Pi = 512 пунктов; 2/3*Pi = 512*4/3 = 683 пункта. 
 
// const double refclk=31372.549;  // =16MHz / 510
const unsigned int refclk = 31374;      // Реальная внутренняя частота

volatile unsigned long phaccu;     // аккумулирующая переменная, 32 бит
volatile unsigned long delta;      // переменная, расчитывающаяся на основе refclk и желаемой частоты работы
volatile unsigned int incr;        // переменная для вычисления итерации(10 бит = 2 бит + 8 бит)
volatile unsigned int phaseoffset; // переменная для смещения фазы. PHASE_OFFSET для прямого движения и -PHASE_OFFSET для реверсивного
volatile byte dfreq;               //Текущая Частота, Гц
volatile byte dfreq_set;           //Установленная частота, Гц

byte inctmr;             // переменная для реализации таймера в loop

//Управление частотой
const int deltafreq=10;                 // deltafreq пунктов изменения аналогового входа дают изменение частоты на 2Гц. Если deltafreq=20, то изменение до 110Гц
unsigned int setfreq=1024+deltafreq;    // переменная для хранения предыдущего измерения заначения пина. По умолчанию гарантированно больше 1023 на deltafreq.

//******************************************************************
//Для регулировки частоты переменным резистором
#define ADJ_PIN A0 // Пин, к которому подключен переменный резистор, регулирующий частоту
int adj;           // переменная для чтения значения с переменного резистора

//*******************************************************************
//Кнопка старт/стоп
#define BTN_START_PIN A1 // Пин, к которому подключена кнопка старта
byte run = B00000000; //младший бит(B00000001) - нажата ли кнопка, старший(B00000010) надо ли запускать

//*******************************************************************
//Кнопка реверса
#define BTN_REVERSE A2 // Пин, к которому подключена кнопка реверса
byte reverse = B00000000; //младший бит(B00000001) - нажата ли кнопка, старший(B00000010) надо ли реверсировать

//******************************************************************
//Индикатор TM1637
#define CLK 8
#define DIO 7
TM1637 disp(CLK, DIO);
//******************************************************************
//Дисплей LiquidCrystal_I2C
LiquidCrystal_I2C lcd(0x27,16,2);   // Задаем адрес и размерность дисплея. 

//******************************************************************
// Настройка таймеров
void Setup_timer() {

    // TIMER0
    TCCR0A |=  (1<<COM0A1); // COM0A1, COM0A0: 1 0 - None-inverted mode для OC0A, для инверсного режима ШИМ нужно 1 1
    TCCR0A &= ~(1<<COM0A0); 
    TCCR0A |=  (1<<COM0B1); // COM0B1, COM0B0: 1 0 - None-inverted mode для OC0B, для инверсного режима ШИМ нужно 1 1
    TCCR0A &= ~(1<<COM0B0); 
    TCCR0A |=  (0<<WGM01);  // WGM02, WGM01, WGM00: 0 1 1 - Fast PWM 
    TCCR0A |=  (1<<WGM00);

    TCCR0B &= ~(1<<CS02);   // CS02, CS01, CS00: 0 0 1 - тактовый генератор CLK
    TCCR0B &= ~(1<<CS01); 
    TCCR0B |=  (1<<CS00); 
    TCCR0B &= ~(1<<WGM02);

    // TIMER1
    TCCR1A |=  (1<<COM1A1); // COM1A1, COM1A0: 1 0 - None-inverted mode для OC1A, для инверсного режима ШИМ нужно 1 1
    TCCR1A &= ~(1<<COM1A0); 
    TCCR1A |=  (1<<COM1B1); // COM1B1, COM1B0: 1 0 - None-inverted mode для OC1B, для инверсного режима ШИМ нужно 1 1
    TCCR1A &= ~(1<<COM1B0); 
    TCCR1A &= ~(0<<WGM11);  // WGM13, WGM12, WGM11, WGM10: 0 1 0 1 - Fast PWM, 8bit 
    TCCR1A |=  (1<<WGM10);

    TCCR1B &= ~(1<<CS12);   // CS12, CS11, CS10: 0 0 1 - тактовый генератор CLK
    TCCR1B &= ~(1<<CS11); 
    TCCR1B |=  (1<<CS10); 
    TCCR1B &= ~(1<<WGM13);
    TCCR1B |=  (1<<WGM12);

    // TIMER2
    TCCR2A |=  (1<<COM2A1); // COM2A1, COM2A0: 1 0 - None-inverted mode для OC2A, для инверсного режима ШИМ нужно 1 1
    TCCR2A &= ~(1<<COM2A0);
    TCCR2A |=  (1<<COM2B1); // COM2B1, COM2B0: 1 0 - None-inverted mode для OC2B, для инверсного режима ШИМ нужно 1 1
    TCCR2A &= ~(1<<COM2B0);
    TCCR2A |=  (1<<WGM21);  // WGM22, WGM21, WGM20: 0 1 1 - Fast PWM
    TCCR2A |=  (1<<WGM20);

    TCCR2B &= ~(1<<CS22);   // CS22, CS21, CS20: 0 0 1 - тактовый генератор CLK
    TCCR2B &= ~(1<<CS21); 
    TCCR2B |=  (1<<CS20); 
    TCCR2B &= ~(1<<WGM22);

    //Установить все PWM в 0
    OCR0A = 0;
    OCR0B = 0;
    OCR1A = 0;
    OCR1B = 0;
    OCR2A = 0;
    OCR2B = 0;
    //Разрешить/запретить прерывания по таймерам
    TIMSK0 &= ~(1<<TOIE0); //Запретить прерывания на таймер 0
    TIMSK1 &= ~(1<<TOIE1); //Запретить прерывания на таймер 1
    TIMSK2 &= ~(1<<TOIE2); //Запретить прерывания на таймер 2

}
 
//******************************************************************
//Прерывание по таймеру 2 (TIMER2)
//Работает с частотой 
ISR(TIMER2_OVF_vect) {
    PIND |= (1 << 2); //Для замера скорости
    //Таймер 0 | выводы 5 и 6  | OC0B, OC0A
    //Таймер 1 | выводы 9 и 10 | OC1A, OC1B
    //Таймер 2 | выводы 3 и 11 | OC2B, OC2A

    //Для теста
    //ШИМ 0, pins 6, 5
    //OCR0A = 128;
    //OCR0B = 128;
    //ШИМ 1, pins 9, 10
    //OCR1A = 128;
    //OCR1B = 128;
    //ШИМ 2, pins 11, 3
    //OCR2A = 128;
    //OCR2B = 128;

    phaccu = phaccu + delta;   // Добавляем дельту(рассчитна по частоте) в аккумулирующую переменную 32 бит
    //Фаза 1
    incr = (phaccu >> 22); //10 бит. Старщие 2 бита(incr & 0x300) - номер четверти, младшие 8 бит(incr & 0xFF) - номер значения из таблицы констант

    switch (incr & 0x300) { //B1100000000=0x300
        case 0x0: //Первая четверть, выбрать константу из массива в пин A.
        {
            OCR0B = 0;
            OCR0A = pgm_read_byte_near(sineP4 + (incr & 0xFF));
            break;
        }
        case 0x100: //Вторая четверть, выбрать обратнную константу из массива в пин A
        {
            OCR0B = 0;
            OCR0A = pgm_read_byte_near(sineP4 + 255 - (incr & 0xFF));
            break;
        }
        case 0x200: //Третья четверть, выбрать константу из массива в пин B
        {
            OCR0A = 0;
            OCR0B = pgm_read_byte_near(sineP4 + (incr & 0xFF));
            break;
        }
        case 0x300: //Четвертая четверть, выбрать обратнную константу из массива в пин B
        {
            OCR0A = 0;
            OCR0B = pgm_read_byte_near(sineP4 + 255 - (incr & 0xFF));
            break;
        }
        default:
        {
            OCR0A = 0;
            OCR0B = 0;
            break;
        }
    }
    //Фаза 2. Смещена относительно фазы 1 на phaseoffset
    incr = incr + phaseoffset;
    //Уберем биты выще 10го B001111111111=0x3FF, чтобы попасть в массив констант
    incr &= 0x3FF;

    switch (incr & 0x300) { //B1100000000=0x300
        case 0x0: //Первая четверть, выбрать константу из массива в пин A.
        {
            OCR1B = 0;
            OCR1A = pgm_read_byte_near(sineP4 + (incr & 0xFF));
            break;
        }
        case 0x100: //Вторая четверть, выбрать обратнную константу из массива в пин A
        {
            OCR1B = 0;
            OCR1A = pgm_read_byte_near(sineP4 + 255 - (incr & 0xFF));
            break;
        }
        case 0x200: //Третья четверть, выбрать константу из массива в пин B
        {
            OCR1A = 0;
            OCR1B = pgm_read_byte_near(sineP4 + (incr & 0xFF));
            break;
        }
        case 0x300: //Четвертая четверть, выбрать обратнную константу из массива в пин B
        {
            OCR1A = 0;
            OCR1B = pgm_read_byte_near(sineP4 + 255 - (incr & 0xFF));
            break;
        }
        default:
        {
            OCR1A = 0;
            OCR1B = 0;
            break;
        }
    }
    //Фаза 2. Смещена относительно фазы 1 на phaseoffset
    incr = incr + phaseoffset;
    //Уберем биты выще 10го B001111111111=0x3FF, чтобы попасть в массив констант
    incr &= 0x3FF;

    switch (incr & 0x300) { //B1100000000=0x300
        case 0x0: //Первая четверть, выбрать константу из массива в пин A.
        {
            OCR2B = 0;
            OCR2A = pgm_read_byte_near(sineP4 + (incr & 0xFF));
            break;
        }
        case 0x100: //Вторая четверть, выбрать обратнную константу из массива в пин A
        {
            OCR2B = 0;
            OCR2A = pgm_read_byte_near(sineP4 + 255 - (incr & 0xFF));
            break;
        }
        case 0x200: //Третья четверть, выбрать константу из массива в пин B
        {
            OCR2A = 0;
            OCR2B = pgm_read_byte_near(sineP4 + (incr & 0xFF));
            break;
        }
        case 0x300: //Четвертая четверть, выбрать обратнную константу из массива в пин B
        {
            OCR2A = 0;
            OCR2B = pgm_read_byte_near(sineP4 + 255 - (incr & 0xFF));
            break;
        }
        default:
        {
            OCR2A = 0;
            OCR2B = 0;
            break;
        }
    }
}

void display_status() {
    //Отображение на дисплее текущего статуса
    //Первая строка
    //Заполнить значение для LCD частотой dfreq_set. Вывод: Set freq: + частота
    lcd.setCursor(0, 0);                      // Устанавливаем курсор в начало 1 строки
    lcd.print("Freq:           ");            // Выводим текст
    lcd.setCursor(6, 0);                      // Устанавливаем курсор в 6 позицию
    lcd.print(dfreq_set);                     // Выводим текст
    lcd.setCursor(10, 0);                     // Устанавливаем курсор в 10 позицию
    if ((reverse & B00000010)) {
        lcd.print("Revers");
    }
    else {
        lcd.print("Direct");  
    }
    lcd.setCursor(0, 1);                      // Устанавливаем курсор в начало 2 строки
    if (run & B00000010) {
        lcd.print("Run             ");            // Выводим текст
    }
    else {
        lcd.print("Stop            ");            // Выводим текст
    }
    //Вывод на экран F + частота
    /*disp.display(0,15);
    disp.display(1,(dfreq_set%1000)/100);
    disp.display(2,(dfreq_set%100)/10);
    disp.display(3,dfreq_set%10);
    */
}

void setup()
{
    Serial.begin(115200);        // connect to the serial port
    Serial.println("Test begin");

    DDRD = 0xFC;  // весь порт D - выход, кроме RX TX
    DDRB = 0xFF;  // весь порт B - выход

    //Настройка и остановка таймеров
    Setup_timer();
    
    dfreq = 9; //минимальнач частота
    //Дя регулировки частоты
    pinMode(ADJ_PIN, INPUT);

    //Кнопка старта
    pinMode(BTN_START_PIN, INPUT_PULLUP);
    //Кнопка реверса
    pinMode(BTN_REVERSE, INPUT_PULLUP);
    //Индикатор
    disp.init();
    disp.point(false);
    disp.set(BRIGHT_TYPICAL); //яркость
    disp.clearDisplay(); //Очистить
    //LCD дисплей
    lcd.init();                            // Инициализация lcd             
    lcd.backlight();                       // Включаем подсветку
    display_status();
}

void loop() {
    //PIND |= (1 << 2); //Для замера скорости
    //Реализация для изменения частоты переменным резистором
    if ((inctmr++) % 2048 == 0) { //Обрабатывается каждый 2048-й вызов loop
        adj = (analogRead(ADJ_PIN)+analogRead(ADJ_PIN)+analogRead(ADJ_PIN))/3; //Усредненное значение на пине регулировки частоты
        if ( abs(adj - setfreq) >= deltafreq ){
            //Значение аналогового входа изменилось на deltafreq и более.
            //Сначала останавливаем прерывания по таймеру 2, иначе не работает
            TIMSK2 &= ~(1<<TOIE2); //остановка таймера
            //Установить все PWM в 0
            OCR0A = 0;
            OCR0B = 0;
            OCR1A = 0;
            OCR1B = 0;
            OCR2A = 0;
            OCR2B = 0;
            
            //Переведем значение analogRead в частоту
            setfreq = adj;
            dfreq_set = 10 + 2*(setfreq/deltafreq);    // Минимальная частота 10Гц, на каждые 15 пунктов частота увеличивается на 2Гц

            display_status();
            run = B00000000;       //Изменение состояния кнопки на выкл, запрет обработки(если случайно в этот момент нажата)
        }
    }
    if (inctmr % 256 == 0) { //Обрабатывается каждый 256-й вызов loop. Можно, наверно, увеличить.
        //Нажатие кнопки реверс
        if ( digitalRead(BTN_REVERSE) == LOW && (reverse & B00000001) ){ //Нажата кнопка и нужно менять состояние
            //Останавливаем прерывания по таймеру 2
            TIMSK2 &= ~(1<<TOIE2); //остановка таймера
            //Установить все PWM в 0
            OCR0A = 0;
            OCR0B = 0;
            OCR1A = 0;
            OCR1B = 0;
            OCR2A = 0;
            OCR2B = 0;
            
            if ((reverse & B00000010)) {
                reverse = B00000000;
            }
            else {
                reverse = B00000010;
            }
            display_status();
            run = B00000000;       //Изменение состояния кнопки на выкл, запрет обработки(если случайно в этот момент нажата)
        }
        else if (digitalRead(BTN_REVERSE) == HIGH) { //кнопка отпущена
            reverse |= B00000001; //Кнопку можно снова обрабатывать
        }
        //Нажатие кнопки старт/стоп
        if ( digitalRead(BTN_START_PIN) == LOW && (run & B00000001) ){ //Нажата кнопка и нужно менять состояние
            if ((run & B00000010)) { //Надо остановить
                TIMSK2 &= ~(1<<TOIE2); //остановка таймера
                //Установить все PWM в 0
                OCR0A = 0;
                OCR0B = 0;
                OCR1A = 0;
                OCR1B = 0;
                OCR2A = 0;
                OCR2B = 0;
                //Заполнить значение для LCD частотой dfreq_set. Вывод: Set freq: + частота
                lcd.setCursor(0, 1);          // Устанавливаем курсор в начало 2 строки
                lcd.print("Stop            ");// Выводим текст
                
                run = B00000000;       //Изменение состояния на выкл и запрет обработки кнопки
            }
            else {
                phaccu = 0; //
                dfreq = 4;  //Минимальная частота для увеличения до рабочей
                if (reverse & B00000010) {
                    phaseoffset = - PHASE_OFFSET; //Смещение для фазы для реверса
                }
                else {
                    phaseoffset = PHASE_OFFSET; //Смещение для фазы для прямого вращения
                }
                run = B00000010;  //Изменение состояния на вкл и запрет обработки кнопки
                display_status();
                TIMSK2 |=  (1<<TOIE2); //Запуск таймера
            }
        }
        else if (digitalRead(BTN_START_PIN) == HIGH) { //кнопка отпущена
            run |= B00000001; //Кнопку можно снова обрабатывать
        }
    }

    
    if (dfreq < dfreq_set && (run & B00000010) && inctmr % 1048576/2 == 0) { //Плавное увеличение частоты
        dfreq++; //Увеличение частоты

        if (dfreq % 5 == 0 || dfreq == dfreq_set) { //Увеличиваем каждые 5Гц
            delta = 0x100000000 * dfreq / refclk;  // Новое вычисление дельты
        }
        
        //delta = 0x100000000 * dfreq / refclk;  // Новое вычисление дельты
    }
}

 

step64
Offline
Зарегистрирован: 01.04.2013

Не придумали как динамическую регулировку частоты сделать? На другой контроллер не планируете свой проект переводить?

AV
Offline
Зарегистрирован: 24.11.2018

andryn какая максимальная частота вами достигнута ?

andryn
Offline
Зарегистрирован: 08.06.2018

step64 пишет:

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

Проблема не в динамической регулировке частоты, ее сдеать можно. Проблема в отображении на дисплее текущей частоты. В библиотеке LiquidCrystal_I2C встречается 10 delay. В библиотеке TM1637 тоже есть delay.

Поэтому сейчас искуственно введена остановка генерации сигнала при изменении частоты.

Возможно перепишу либу TM1637, но может это и не нужно.

step64 пишет:

На другой контроллер не планируете свой проект переводить?

Нет, не планирую. На других, более мощных контроллерах, есть куча готовых решений. Сам проект сделан специально на Arduino, т.к. когда я искал готовый начитался много ерунды, типа "ардуина так не сможет".

AV пишет:

andryn какая максимальная частота вами достигнута ?

Гонял до 200Гц, но понял, что больше 100 сильно падает мощность мотора. Поэтому сейчас регулировка от 10 до 110Гц.

 

Ну и, для интересующихся, вести с полей. Испытал работу на маленьком трехфазном моторчике 220Вольт 40Вт(по-моему). Работает устойчиво.

При испытании выявил несколько косяков. Вот свежий код:

/* Main.ino
 *
 * Created:   AVS, 2018-06-06
 * Processor: Arduino Uno
 * Compiler:  Arduino AVR
 */

#include "TM1637.h"
#include "LiquidCrystal_I2C.h"


const byte PROGMEM sineP4[]  = { //Константа для первой четверти синуса. 256 значений.
   //Для реализации Dead Time при переключении с Hight pin на Low pin добавим несколько нулевых значений. 
   //Для частоты 125Гц значений должно быть 2, т.к. описана 1/4 периода. Сделаем 3 значения, этого точно хватит :)
   0,0,
   0,2,3,5,6,8,9,11,13,14,16,17,19,20,22,24,25,27,28,30,31,33,34,36,38,39,41,42,44,45,47,48,50,51,53,55,56,58,59,61,62,64,65,67,68,70,71,73,74,76,77,79,80,82,83,85,86,88,89,91,92,94,95,96,
   98,99,101,102,104,105,107,108,109,111,112,114,115,116,118,119,121,122,123,125,126,128,129,130,132,133,134,136,137,138,140,141,142,143,145,146,147,149,150,151,152,154,155,156,157,159,160,161,162,164,165,166,167,168,169,171,172,173,174,175,176,178,179,180,
   181,182,183,184,185,186,187,188,190,191,192,193,194,195,196,197,198,199,200,201,202,203,203,204,205,206,207,208,209,210,211,212,213,213,214,215,216,217,218,218,219,220,221,222,222,223,224,225,225,226,227,228,228,229,230,230,231,232,232,233,234,234,235,235,
   236,237,237,238,238,239,239,240,241,241,242,242,243,243,243,244,244,245,245,246,246,247,247,247,248,248,248,249,249,249,250,250,250,251,251,251,251,252,252,252,252,253,253,253,253,253,254,254,254,254,254,254,254,255,255,255,255,255,255,255,255,255,255,255
};
#define PHASE_OFFSET 683 //Смещение фазы относительно предыдущей на 120 градусов(3/4*Pi). Расчет: 1/2*Pi = 512 пунктов; 2/3*Pi = 512*4/3 = 683 пункта. 
 
// const double refclk=31372.549;  // =16MHz / 510
const unsigned int refclk = 31374;      // Реальная внутренняя частота

volatile unsigned long phaccu;     // аккумулирующая переменная, 32 бит
volatile unsigned long delta;      // переменная, расчитывающаяся на основе refclk и желаемой частоты работы
volatile unsigned int incr;        // переменная для вычисления итерации(10 бит = 2 бит + 8 бит)
volatile unsigned int phaseoffset; // переменная для смещения фазы. PHASE_OFFSET для прямого двидения и -PHASE_OFFSET для реверсивного
volatile byte dfreq;               //Текущая Частота, Гц
volatile byte dfreq_set;           //Установленная частота, Гц

unsigned int inctmr;             // переменная для реализации таймера в loop
unsigned int inctmr_rev;         // переменная для реализации антидребезга кнопки reverse
unsigned int inctmr_stop;        // переменная для реализации антидребезга кнопки stop

//Управление частотой
const int deltafreq=20;                 // deltafreq пунктов изменения аналогового входа дают изменение частоты на 2Гц. Если deltafreq=20, то изменение до 110Гц
unsigned int setfreq=1024+deltafreq;    // переменная для хранения предыдущего измерения заначения пина. По умолчанию гарантированно больше 1023 на deltafreq.

//******************************************************************
//Для регулировки частоты переменным резистором
#define ADJ_PIN A0 // Пин, к которому подключен переменный резистор, регулирующий частоту
int adj;           // переменная для чтения значения с переменного резистора

//*******************************************************************
//Кнопка старт/стоп
#define BTN_START_PIN A1 // Пин, к которому подключена кнопка старта
byte run = B00000000; //младший бит(B00000001) - нажата ли кнопка, старший(B00000010) надо ли запускать

//*******************************************************************
//Кнопка реверса
#define BTN_REVERSE A2 // Пин, к которому подключена кнопка реверса
byte reverse = B00000000; //младший бит(B00000001) - нажата ли кнопка, старший(B00000010) надо ли реверсировать

//******************************************************************
//Индикатор TM1637
#define CLK A5
#define DIO A4
TM1637 disp(CLK, DIO);
//******************************************************************
//Дисплей LiquidCrystal_I2C
//LiquidCrystal_I2C lcd(0x27,16,2);   // Задаем адрес и размерность дисплея. 

//******************************************************************
// Настройка таймеров
void Setup_timer() {

    // TIMER0
    TCCR0A |=  (1<<COM0A1); // COM0A1, COM0A0: 1 0 - None-inverted mode для OC0A, для инверсного режима ШИМ нужно 1 1
    TCCR0A &= ~(1<<COM0A0); 
    TCCR0A |=  (1<<COM0B1); // COM0B1, COM0B0: 1 0 - None-inverted mode для OC0B, для инверсного режима ШИМ нужно 1 1
    TCCR0A &= ~(1<<COM0B0); 
    TCCR0A |=  (0<<WGM01);  // WGM02, WGM01, WGM00: 0 1 1 - Fast PWM 
    TCCR0A |=  (1<<WGM00);

    TCCR0B &= ~(1<<CS02);   // CS02, CS01, CS00: 0 0 1 - тактовый генератор CLK
    TCCR0B &= ~(1<<CS01); 
    TCCR0B |=  (1<<CS00); 
    TCCR0B &= ~(1<<WGM02);

    // TIMER1
    TCCR1A |=  (1<<COM1A1); // COM1A1, COM1A0: 1 0 - None-inverted mode для OC1A, для инверсного режима ШИМ нужно 1 1
    TCCR1A &= ~(1<<COM1A0); 
    TCCR1A |=  (1<<COM1B1); // COM1B1, COM1B0: 1 0 - None-inverted mode для OC1B, для инверсного режима ШИМ нужно 1 1
    TCCR1A &= ~(1<<COM1B0); 
    TCCR1A &= ~(0<<WGM11);  // WGM13, WGM12, WGM11, WGM10: 0 1 0 1 - Fast PWM, 8bit 
    TCCR1A |=  (1<<WGM10);

    TCCR1B &= ~(1<<CS12);   // CS12, CS11, CS10: 0 0 1 - тактовый генератор CLK
    TCCR1B &= ~(1<<CS11); 
    TCCR1B |=  (1<<CS10); 
    TCCR1B &= ~(1<<WGM13);
    TCCR1B |=  (1<<WGM12);

    // TIMER2
    TCCR2A |=  (1<<COM2A1); // COM2A1, COM2A0: 1 0 - None-inverted mode для OC2A, для инверсного режима ШИМ нужно 1 1
    TCCR2A &= ~(1<<COM2A0);
    TCCR2A |=  (1<<COM2B1); // COM2B1, COM2B0: 1 0 - None-inverted mode для OC2B, для инверсного режима ШИМ нужно 1 1
    TCCR2A &= ~(1<<COM2B0);
    TCCR2A |=  (1<<WGM21);  // WGM22, WGM21, WGM20: 0 1 1 - Fast PWM
    TCCR2A |=  (1<<WGM20);

    TCCR2B &= ~(1<<CS22);   // CS22, CS21, CS20: 0 0 1 - тактовый генератор CLK
    TCCR2B &= ~(1<<CS21); 
    TCCR2B |=  (1<<CS20); 
    TCCR2B &= ~(1<<WGM22);

    //Установить все PWM в 0
    OCR0A = 0;
    OCR0B = 0;
    OCR1A = 0;
    OCR1B = 0;
    OCR2A = 0;
    OCR2B = 0;
    //Разрешить/запретить прерывания по таймерам
    TIMSK0 &= ~(1<<TOIE0); //Запретить прерывания на таймер 0
    TIMSK1 &= ~(1<<TOIE1); //Запретить прерывания на таймер 1
    TIMSK2 &= ~(1<<TOIE2); //Запретить прерывания на таймер 2

}
 
//******************************************************************
//Прерывание по таймеру 2 (TIMER2)
//Работает с частотой 
ISR(TIMER2_OVF_vect) {
    PIND |= (1 << 2); //Для замера скорости
    //Таймер 0 | выводы 5 и 6  | OC0B, OC0A
    //Таймер 1 | выводы 9 и 10 | OC1A, OC1B
    //Таймер 2 | выводы 3 и 11 | OC2B, OC2A

    //Для теста
    //ШИМ 0, pins 6, 5
    //OCR0A = 128;
    //OCR0B = 128;
    //ШИМ 1, pins 9, 10
    //OCR1A = 128;
    //OCR1B = 128;
    //ШИМ 2, pins 11, 3
    //OCR2A = 128;
    //OCR2B = 128;

    phaccu = phaccu + delta;   // Добавляем дельту(рассчитна по частоте) в аккумулирующую переменную 32 бит
    //Фаза 1
    incr = (phaccu >> 22); //10 бит. Старщие 2 бита(incr & 0x300) - номер четверти, младшие 8 бит(incr & 0xFF) - номер значения из таблицы констант

    switch (incr & 0x300) { //B1100000000=0x300
        case 0x0: //Первая четверть, выбрать константу из массива в пин A.
        {
            OCR0B = 0;
            OCR0A = pgm_read_byte_near(sineP4 + (incr & 0xFF));
            break;
        }
        case 0x100: //Вторая четверть, выбрать обратнную константу из массива в пин A
        {
            OCR0B = 0;
            OCR0A = pgm_read_byte_near(sineP4 + 255 - (incr & 0xFF));
            break;
        }
        case 0x200: //Третья четверть, выбрать константу из массива в пин B
        {
            OCR0A = 0;
            OCR0B = pgm_read_byte_near(sineP4 + (incr & 0xFF));
            break;
        }
        case 0x300: //Четвертая четверть, выбрать обратнную константу из массива в пин B
        {
            OCR0A = 0;
            OCR0B = pgm_read_byte_near(sineP4 + 255 - (incr & 0xFF));
            break;
        }
        default:
        {
            OCR0A = 0;
            OCR0B = 0;
            break;
        }
    }
    //Фаза 2. Смещена относительно фазы 1 на phaseoffset
    incr = incr + phaseoffset;
    //Уберем биты выще 10го B001111111111=0x3FF, чтобы попасть в массив констант
    incr &= 0x3FF;

    switch (incr & 0x300) { //B1100000000=0x300
        case 0x0: //Первая четверть, выбрать константу из массива в пин A.
        {
            OCR1B = 0;
            OCR1A = pgm_read_byte_near(sineP4 + (incr & 0xFF));
            break;
        }
        case 0x100: //Вторая четверть, выбрать обратнную константу из массива в пин A
        {
            OCR1B = 0;
            OCR1A = pgm_read_byte_near(sineP4 + 255 - (incr & 0xFF));
            break;
        }
        case 0x200: //Третья четверть, выбрать константу из массива в пин B
        {
            OCR1A = 0;
            OCR1B = pgm_read_byte_near(sineP4 + (incr & 0xFF));
            break;
        }
        case 0x300: //Четвертая четверть, выбрать обратнную константу из массива в пин B
        {
            OCR1A = 0;
            OCR1B = pgm_read_byte_near(sineP4 + 255 - (incr & 0xFF));
            break;
        }
        default:
        {
            OCR1A = 0;
            OCR1B = 0;
            break;
        }
    }
    //Фаза 2. Смещена относительно фазы 1 на phaseoffset
    incr = incr + phaseoffset;
    //Уберем биты выще 10го B001111111111=0x3FF, чтобы попасть в массив констант
    incr &= 0x3FF;

    switch (incr & 0x300) { //B1100000000=0x300
        case 0x0: //Первая четверть, выбрать константу из массива в пин A.
        {
            OCR2B = 0;
            OCR2A = pgm_read_byte_near(sineP4 + (incr & 0xFF));
            break;
        }
        case 0x100: //Вторая четверть, выбрать обратнную константу из массива в пин A
        {
            OCR2B = 0;
            OCR2A = pgm_read_byte_near(sineP4 + 255 - (incr & 0xFF));
            break;
        }
        case 0x200: //Третья четверть, выбрать константу из массива в пин B
        {
            OCR2A = 0;
            OCR2B = pgm_read_byte_near(sineP4 + (incr & 0xFF));
            break;
        }
        case 0x300: //Четвертая четверть, выбрать обратнную константу из массива в пин B
        {
            OCR2A = 0;
            OCR2B = pgm_read_byte_near(sineP4 + 255 - (incr & 0xFF));
            break;
        }
        default:
        {
            OCR2A = 0;
            OCR2B = 0;
            break;
        }
    }
}

void display_status() {
    //Отображение на дисплее текущего статуса
    //Первая строка
    //Заполнить значение для LCD частотой dfreq_set. Вывод: Set freq: + частота
    /*lcd.setCursor(0, 0);                      // Устанавливаем курсор в начало 1 строки
    lcd.print("Freq:           ");            // Выводим текст
    lcd.setCursor(6, 0);                      // Устанавливаем курсор в 6 позицию
    lcd.print(dfreq_set);                     // Выводим текст
    lcd.setCursor(10, 0);                     // Устанавливаем курсор в 10 позицию
    if ((reverse & B00000010)) {
        lcd.print("Revers");
    }
    else {
        lcd.print("Direct");  
    }
    lcd.setCursor(0, 1);                      // Устанавливаем курсор в начало 2 строки
    if (run & B00000010) {
        lcd.print("Run             ");            // Выводим текст
    }
    else {
        lcd.print("Stop            ");            // Выводим текст
    }
    */
    //Вывод на экран первого символа
    if ((reverse & B00000010) && (run & B00000010)) {
        disp.display(0,25);
    }
    else if ((reverse & B00000010) && !(run & B00000010)) {
        disp.display(0,23);      
    }
    else if (!(reverse & B00000010) && (run & B00000010)) {
        disp.display(0,24);
    }
    else {
        disp.display(0,22);
    }
    
    //Вывод на экран частоты
    disp.display(1,(dfreq_set%1000)/100);
    disp.display(2,(dfreq_set%100)/10);
    disp.display(3,dfreq_set%10);
    
}

void setup()
{
    Serial.begin(115200);        // connect to the serial port
    Serial.println("Test begin");

    DDRD = 0xFC;  // весь порт D - выход, кроме RX TX
    DDRB = 0xFF;  // весь порт B - выход

    //Настройка и остановка таймеров
    Setup_timer();
    
    dfreq = 9; //минимальнач частота
    //Дя регулировки частоты
    pinMode(ADJ_PIN, INPUT);

    //Кнопка старта
    pinMode(BTN_START_PIN, INPUT_PULLUP);
    //Кнопка реверса
    pinMode(BTN_REVERSE, INPUT_PULLUP);
    //Индикатор
    disp.init();
    disp.point(false);
    disp.set(BRIGHT_TYPICAL); //яркость
    disp.clearDisplay(); //Очистить
    //LCD дисплей
    //lcd.init();                            // Инициализация lcd             
    //lcd.backlight();                       // Включаем подсветку
    display_status();
}

void loop() {
    //PIND |= (1 << 2); //Для замера скорости
    //Реализация для изменения частоты переменным резистором
    if ((inctmr++) % 2048 == 0) { //Обрабатывается каждый 2048-й вызов loop
        adj = (analogRead(ADJ_PIN)+analogRead(ADJ_PIN)+analogRead(ADJ_PIN))/3; //Усредненное значение на пине регулировки частоты
        if ( (abs(adj - setfreq) >= deltafreq  && !(run & B00000010)) || (abs(adj - setfreq) >= deltafreq*4) ){ //Если (остановлено и есть изменение) или (работает и изменение на 4 пункта)
            //Значение аналогового входа изменилось на deltafreq и более.
            //Сначала останавливаем прерывания по таймеру 2, иначе не работает
            TIMSK2 &= ~(1<<TOIE2); //остановка таймера
            //Установить все PWM в 0
            OCR0A = 0;
            OCR0B = 0;
            OCR1A = 0;
            OCR1B = 0;
            OCR2A = 0;
            OCR2B = 0;
            
            //Переведем значение analogRead в частоту
            setfreq = adj;
            dfreq_set = 10 + 2*(setfreq/deltafreq);    // Минимальная частота 10Гц, на каждые 15 пунктов частота увеличивается на 2Гц

            display_status();
            run = B00000000;       //Изменение состояния кнопки на выкл, запрет обработки(если случайно в этот момент нажата)
        }
    }
    if (inctmr % 256 == 0) { //Обрабатывается каждый 256-й вызов loop
        //Нажатие кнопки реверс
        if ( digitalRead(BTN_REVERSE) == LOW && (reverse & B00000001) ){ //Нажата кнопка и нужно менять состояние
            //Останавливаем прерывания по таймеру 2
            TIMSK2 &= ~(1<<TOIE2); //остановка таймера
            //Установить все PWM в 0
            OCR0A = 0;
            OCR0B = 0;
            OCR1A = 0;
            OCR1B = 0;
            OCR2A = 0;
            OCR2B = 0;
            
            if ((reverse & B00000010)) {
                reverse = B00000000;
            }
            else {
                reverse = B00000010;
            }
            run = B00000000;       //Изменение состояния кнопки на выкл, запрет обработки(если случайно в этот момент нажата)
            display_status();
        }
        else if (digitalRead(BTN_REVERSE) == HIGH) { //кнопка отпущена некоторое время
            reverse |= B00000001; //Кнопку можно снова обрабатывать
        }
        //Нажатие кнопки старт/стоп
        if ( digitalRead(BTN_START_PIN) == LOW && (run & B00000001) ){ //Нажата кнопка и нужно менять состояние
            if ((run & B00000010)) { //Надо остановить
                TIMSK2 &= ~(1<<TOIE2); //остановка таймера
                //Установить все PWM в 0
                OCR0A = 0;
                OCR0B = 0;
                OCR1A = 0;
                OCR1B = 0;
                OCR2A = 0;
                OCR2B = 0;
                run = B00000000;       //Изменение состояния на выкл и запрет обработки кнопки
                display_status();
            }
            else {
                phaccu = 0; //
                dfreq = 4;  //Минимальная частота для увеличения до рабочей
                if (reverse & B00000010) {
                    phaseoffset = - PHASE_OFFSET; //Смещение для фазы для реверса
                }
                else {
                    phaseoffset = PHASE_OFFSET; //Смещение для фазы для прямого вращения
                }
                run = B00000010;  //Изменение состояния на вкл и запрет обработки кнопки
                display_status();
                TIMSK2 |=  (1<<TOIE2); //Запуск таймера
            }
        }
        else if (digitalRead(BTN_START_PIN) == HIGH) { //кнопка отпущена некоторое время
            run |= B00000001; //Кнопку можно снова обрабатывать
        }
    }

    
    if ((dfreq < dfreq_set) && (run & B00000010) && (inctmr % (1048576/2) == 0)) { //Плавное увеличение частоты
        dfreq++; //Увеличение частоты

        if (dfreq % 5 == 0 || dfreq == dfreq_set) { //Увеличиваем каждые 5Гц
            delta = 0x100000000 * dfreq / refclk;  // Новое вычисление дельты
        }
        
        //delta = 0x100000000 * dfreq / refclk;  // Новое вычисление дельты
    }
}

В библиотеку TM1637 добавил несколько символов для индикации в старшем разряде текущего состояния.

--- TM1637.cpp.orig	2018-11-10 11:47:08.181946200 +0300
+++ TM1637.cpp	2018-11-11 14:06:05.698749800 +0300
@@ -39,7 +39,8 @@
                             0x66, 0x6d, 0x7d, 0x07,
                             0x7f, 0x6f, 0x77, 0x7c,
                             0x39, 0x5e, 0x79, 0x71,
-                            0x00, 0x40}; //0~9,A,b,C,d,E,F,CLEAR,-
+                            0x00, 0x40, //0~9,A,b,C,d,E,F,CLEAR,- (1-9, 10-17)
+                            0x76, 0x30, 0x1E, 0x38, 0x54, 0x50, 0x37, 0x31}; //H(18), I(19), J(20), L(21), n(22), r(23), П(24), Г(25) -----------AVS--------------
 
 TM1637::TM1637(uint8_t clk, uint8_t data)
 {

 

step64
Offline
Зарегистрирован: 01.04.2013

andrynРад что проект развивается. Вы двигатель на 220 тестировали, силовую сразу на движок цепляли без фильтров? Вч ШИМ 62кГц может похерить изоляцию обмоток движка, да и будут большие динамические потери на таких частотах в МОЩНЫХ игбт,  не зря в пром частотниках частота шим ДО десятка кГц +/- единицы кГц. А вы не хотите как опцию тормозной резистор приделать? Команда стоп -> лог.1 на свободный порт-> ключ-> Резистор. Рекомендую простенькую но эффективную схему защиты от перегруза и кз, повторялась не однократно, есть печатка если надо.

b707
Offline
Зарегистрирован: 26.05.2017

andryn пишет:

Гонял до 200Гц, но понял, что больше 100 сильно падает мощность мотора. Поэтому сейчас регулировка от 10 до 110Гц.

При испытании выявил несколько косяков.

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

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

andryn
Offline
Зарегистрирован: 08.06.2018

step64 пишет:

Вы двигатель на 220 тестировали, силовую сразу на движок цепляли без фильтров? Вч ШИМ 62кГц может похерить изоляцию обмоток движка, да и будут большие динамические потери на таких частотах в МОЩНЫХ игбт,  не зря в пром частотниках частота шим ДО десятка кГц +/- единицы кГц. 

Подключал напрямую, без фильтров. Про пробой изоляции не задумывался. Пока не понимаю физику пробоя при высокой частоте. Почитаю про это.

step64 пишет:

А вы не хотите как опцию тормозной резистор приделать? Команда стоп -> лог.1 на свободный порт-> ключ-> Резистор. Рекомендую простенькую но эффективную схему защиты от перегруза и кз, повторялась не однократно, есть печатка если надо.

Для борьбы с перегрузом и КЗ думал поставить измеритель тока после выпрямителя 220В. Тогда можно программно регулировать максимальную мощность.

b707 пишет:

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

Амплитуда у меня постоянна и равна выпрямленному напряжению 220В. Т.е. повышать ее не куда, если не делать повышающий преобразователь вольт на 500. Можно было бы уменьшать скважность обратно пропорционально частоте, но тогда на номинальных 50Гц будет не полная мощность. В общем я считаю, что обычные 3-х фазные моторы не предназначены и не рассчитаны для вращения со скоростями в 4 раза, превышающими номинальную.

b707 пишет:

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

Функция генерации работает нормально, в нее исправлений не вносил. Основной косяк был в том, что при работе analogRead начинает прыгать на 25-30 пунктов, в связи с чем мотор останавливался, т.к. алгоритм считал, что повернули ручку регулировки. Еще переделал индикацию на TM1637, т.к. использовать большой экран бессмысленно, потому что не получается его динамически рефрешить. Ну и описки были, конечно :)

 

 

step64
Offline
Зарегистрирован: 01.04.2013

andryn пишет:

Для борьбы с перегрузом и КЗ думал поставить измеритель тока после выпрямителя 220В. Тогда можно программно регулировать максимальную мощность.

  Идея конечно отличная, поставить хотя бы один модуль например ACS712 20A на шину питания и смотреть/контролировать ток на дисплее, тогда и напряжение шины питания надо выводить на дисплей, что бы всё по взрослому было) Интересно, меге 328 это по силам будет всё сразу делать, да ещё успевать мгновенно среагировать на КЗ.

b707
Offline
Зарегистрирован: 26.05.2017

step64 пишет:

 Интересно, меге 328 это по силам будет всё сразу делать, да ещё успевать мгновенно среагировать на КЗ.

не, я конечно понимаю, что чисто из спортивного интереса это классно делать на атмеге328... но вообще практически любой stm32...  даже самый банальный stm32f103 - справится с этим в разы лучше и будет загружен задачей процентов на 20.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

andryn пишет:

Подключал напрямую, без фильтров. Про пробой изоляции не задумывался. Пока не понимаю физику пробоя при высокой частоте. Почитаю про это.

тут всё просто, механическое повреждение изоляции (перетирает) )))
Вакуумная пропитка кремнийорганическим лаком с последующей сущкой спасает ситуацию

EgorDS
Offline
Зарегистрирован: 08.09.2017

1. Заглянул на данную ветку. На работе занимаюсь ремонтом (модульно) промышленных станций - частотников.  Возможно, я что то упустил, но думаю так: не имеет смысла в таблице синусов первыми выборками назначать 0-0-0. Это всего лишь искривит вашу амлитуду при переходе через 0. Дит-тайм - это пауза в шим-сигнале. Подробнее так: методом шима формируется импульс ( определенной (расчетной) длины в заданное время), после каждого импульса устанавливается Дит-тайм (для IGBT-модулей которые мы применяем 3-3,3 mks) и только после этого может включиться нижний ключ или ключ другой фазы. В промышленных установках часто используют камни TMSxxx где все это реализовано аппаратно. Как сделать это программно на атмеге пока не представляю.

2. Недавно прочитал, правильно использовать "геометрический синус" - или как то так.. Взяв из сети 220В по простой таблице синуса вы обратно не отдадите теже 220/3ф. Как он работает - проще посмотреть картину в гугле. В настоящее время на коленке пишу 3-х фазный генератор синуса (срочно попросили). Если кому нужно - выложу то что получилось...

P/S быть может.. фазный шим поможет (заполнение не должно превышать ~ 99%) . Но кто даст гарантии что все таймеры запустятся синхронно и синронность работы PWM не нарушится... И с другой стороны, в гугле попадались частотники на простеньких камнях - и схемы рабочии (но там точно были драйверы с защитой от сквозных токов). Шим в установках 2500 - 5000 Гц с L-C фильтром. 1500 Гц без фильтра что бы АД не грелся...

Buldakov
Offline
Зарегистрирован: 17.01.2016

По поводу ШИМ для двигателя может быть еще несколько проблем.

Первая -  как уже писалось Если верхний транзистор открыт мы сначала подаем сигнал на закрытие верхнего транзистора. Ждем когда он закроется. И после этого только подаем сигнал на открытие нижнего транзистора. Так сделано в типовой схеме компьютерного блока питания на микросхеме TL494. Иначе пойдет ток короткого замыкания через 2 одновременно открытых транзистора и они сгорят. Время закрытого состояния обоих транзисторов около 4% длительности.

Второе - Допустим мы подаем идеальный ШИМ на двигатель.Но двигатель имеет неидеальные характеристики. Напимер индуктивность одной фазы на 0.0001% больше чем другой. В итоге сердечник двигателя войдет в насыщение по этой фазе и переодически ток насышения будет по этой фазе нарастать (например 1 раз в секунду для одного периода импульса) гораздо быстрее чем на остальных. Допустим частота ШИМ = 1 кгц. ток по истечении 0.5 мс с момента начала импульса ток должен быть 10 А. Но из за насыщения сердечника 1 раз в несколько секунд ток по истечении 0.5 мс   будет 100 А. После этого насыщение убирается и все повторяется заново. Так вот этот ток и палит транзисторы IGBT. Либо ставить транзисторы с импульсным током гораздо большим среднего тока. Либо как то бороться с этим. Но бороться специальными схемными решениями. Контроллер в этом не поможет. Врят ли он сумеет обработать датчик тока и закрыть транзистор. При использовании двигателей маленькой мошности средний ток около 0.5А. Импульсный ток может быть 10А. Такой режим в основном держат все мощные полевые транзисторы или IGBT. При этом проблем не будет. Проблемы будут при мощных движках. Если не делать защит то транзисторы надо будет выбирать на импульсные токи 300 - 500А.

Да и посмотрите какой материал используется в двигателе. Обычно пластинчатое железо. У него на большой частоте свойства сильно падают.

SergeiNSK
Offline
Зарегистрирован: 11.03.2015

Всем привет. Ну как ? Работает схема??

SergeiNSK
Offline
Зарегистрирован: 11.03.2015

Выкладывай что получилось.. и продублируй мне на почту пожалуйста!   spgazsystem@yandex.ru 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

ты забыл ключевое слово "ЩАЖЖЭ!!!"

SergeiNSK
Offline
Зарегистрирован: 11.03.2015

Я тама прописал.. Пажалуйста !!

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

DetSimen пишет:

ты забыл ключевое слово "ЩАЖЖЭ!!!"

вот вот, подскажи ссылку на частотник где PWM реализован программно, что-то не могу найти, там еще силовая на IRAMS10UP60B была сделана - "ЩАЖЖЭ!!!" )))

b707
Offline
Зарегистрирован: 26.05.2017

ua6em пишет:

подскажи ссылку на частотник где PWM реализован программно, что-то не могу найти, там еще силовая на IRAMS10UP60B была сделана - "ЩАЖЖЭ!!!" )))

держи

http://arduino.ru/forum/pesochnitsa-razdel-dlya-novichkov/proshu-pomoch-uskorit-rabotu-sketcha-programmnyi-shim-6-kana

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

b707 пишет:

ua6em пишет:

подскажи ссылку на частотник где PWM реализован программно, что-то не могу найти, там еще силовая на IRAMS10UP60B была сделана - "ЩАЖЖЭ!!!" )))

держи

http://arduino.ru/forum/pesochnitsa-razdel-dlya-novichkov/proshu-pomoch-uskorit-rabotu-sketcha-programmnyi-shim-6-kana

данке щён

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Что-то по проекту больше никакой информации!?

serega-4789
Offline
Зарегистрирован: 06.05.2020

Добрый день! Как успехи по проекту ?В протеусе посмотрел работу программы на ардуино 5 и 6 порты не выдают импульсы шимм

 

b707
Offline
Зарегистрирован: 26.05.2017

serega-4789 пишет:

Добрый день! Как успехи по проекту ?В протеусе посмотрел работу программы на ардуино 5 и 6 порты не выдают импульсы шимм

 

делайте на СТМ32

Oleg.normalniy
Offline
Зарегистрирован: 10.06.2020

привет народ , замутил свой частотник на XMEGA32D4

https://studio.youtube.com/video/fY6xBOhNq6Q/edit

nevkon
Offline
Зарегистрирован: 20.01.2015

Ну кто так ссылки то выкладывает.

Правильно вот так: https://www.youtube.com/watch?v=fY6xBOhNq6Q

ну и ссылки на кота можно было тоже привести, а то ни схемы ни прошивки, как будто похвастаться:

https://radiokot.ru/forum/viewtopic.php?p=3814020#p3814020

https://radiokot.ru/forum/viewtopic.php?p=3851152#p3851152

 

Вот только одно из видео не понял - частотник на 1 или 3 фазы?

Oleg.normalniy
Offline
Зарегистрирован: 10.06.2020

nevkon пишет:

Ну кто так ссылки то выкладывает.

Правильно вот так: https://www.youtube.com/watch?v=fY6xBOhNq6Q

ну и ссылки на кота можно было тоже привести, а то ни схемы ни прошивки, как будто похвастаться:

https://radiokot.ru/forum/viewtopic.php?p=3814020#p3814020

https://radiokot.ru/forum/viewtopic.php?p=3851152#p3851152

 

Вот только одно из видео не понял - частотник на 1 или 3 фазы?

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

Oleg.normalniy
Offline
Зарегистрирован: 10.06.2020

нашел IR2308  но активный уровень высокий , схему управления изменил , микруху 540 заменил на 245 , если есть предложения по драйверам тогда предлагайте буду  ОШЕНЬ  благодарен

Oleg.normalniy
Offline
Зарегистрирован: 10.06.2020

привед народ , выкладываю новые приключения моторчика с датчиком тока ACS758

https://youtu.be/aoLlJcYoZYQ

Oleg.normalniy
Offline
Зарегистрирован: 10.06.2020

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

https://youtu.be/QnE6-JBv4kQ

Alex_Ferrum
Offline
Зарегистрирован: 04.05.2015

Кстати по поводу предложений, под управление трёхфазными двигателями, вернее под формирование трёхфазной синусоиды больше заточена Attiny861, у неё есть таймер счётчик работающий на частоте до 64 МГц (есть внутри ФАПЧ), есть 3 канала шим с 2 выходами каждый, с возможностью установки мёртвого времени между закрытием верхнего ключа и открытием нижнего и наоборот.
Кстати, в этом контроллере есть одна хитрость, связанная с включением каналов. Если мы моделируем в Протеусе, то каналы A, B и D работают как надо, в железе запускается только канал A. Некоторое время пришлось поламать голову над этой задачкой, но все три канала запустил, причём это связано не с тем что чип китайский якобы и это не является глюком контроллера (не описано в erratasheet). Тут скорее как говорил Козьма Прутков: "Зри в даташник."

Oleg.normalniy
Offline
Зарегистрирован: 10.06.2020

XMEGA и не надо искать профильный проц , XMEGA A1 если верить DATE держит 128 мгц , а таймеров на 5 движков

serega-4789
Offline
Зарегистрирован: 06.05.2020

Добрый день! Загрузил код в ардуино, синус не меняется
Его частота на порте а0 и кнопка старт стоп только работает, реверс просто выключает шимм, у меня на осциллографе дсо лес из шимм сигналов , с конденсатором появляется половина синуса но 20 мс развёртка там полу период на целую секунду растянут

vahnik60
Offline
Зарегистрирован: 07.07.2020

Господа, не могу не вмешаться. Только что сын защитил диплом, собрали учебный стенд с трёхфазным векторным ШИМ с контроллером Arduino UNO. Пусть вас не пугают "двугорбые верблюды", "титьки" или "жопы", как их называют специалисты по электроприводу, это математически единственно верный метод создания трёхфазного векторного ШИМ.

 Программу варганил профи. Начальная частота пуска 5 Гц, Максимальная частота 50 Гц. Разгон и торможение в функции U/f, зависимость линейная. Частота ШИМ 5 кГц. Задание частоты потенциометром, кнопки пуск и стоп. назначение входов в программе подписаны: #define key_stop A1 //вывод для кнопки стоп: #define key_start A2 //вывод для кнопки старт: #define f_zad_pin A3 //вход для задатчика выходной частоты.

//расчет шим и метод формирования по книге \диплом\Помощь\книга с. 460.djvu

//раздел 20.1

 

//таблица времени T1

//по формуле 20.1.6 - cos(a+30) в относительных единицах (0-1 соотв. 0-255)

const unsigned int PROGMEM time1[]  = {

220,220,219,219,218,218,217,217,216,215,215,214,214,213,213,212,212,211,210,210,209,209,208,207,207,206,206,205,204,204,203,202,202

,201,201,200,199,199,198,197,197,196,195,195,194,193,193,192,191,191,190,189,188,188,187,186,186,185,184,183,183,182,181,181,180,179

,178,178,177,176,175,175,174,173,172,172,171,170,169,168,168,167,166,165,164,164,163,162,161,160,160,159,158,157,156,156,155,154,153

,152,151,151,150,149,148,147,146,145,145,144,143,142,141,140,139,139,138,137,136,135,134,133,132,131,131,130,129,128,127,126,125,124

,123,122,122,121,120,119,118,117,116,115,114,113,112,111,110,109,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92

,91,90,89,88,87,86,85,84,83,82,81,80,79,78,78,77,76,75,74,73,72,71,70,69,68,67,65,64,63,62,61,60,59,58,57,56,55,54,53

,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,24,23,22,21,20,19,18,17,16,15,14,13

,12,11,10,9,8,7,6,5,4,3,2,1 };

 

//таблица времени T2

//по формуле 20.1.7 - sin(a) в относительных единицах (0-1 соотв. 0-255)

const unsigned int PROGMEM time2[]  = {

0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41

,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,67,68,69,70,71,72,73,74,75,76,77,78,78,79,80

,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,109,110,111,112,113,114,115

,116,117,118,119,120,121,122,122,123,124,125,126,127,128,129,130,131,131,132,133,134,135,136,137,138,139,139,140,141,142,143,144,145

,145,146,147,148,149,150,151,151,152,153,154,155,156,156,157,158,159,160,160,161,162,163,164,164,165,166,167,168,168,169,170,171,172

,172,173,174,175,175,176,177,178,178,179,180,181,181,182,183,183,184,185,186,186,187,188,188,189,190,191,191,192,193,193,194,195,195

,196,197,197,198,199,199,200,201,201,202,202,203,204,204,205,206,206,207,207,208,209,209,210,210,211,212,212,213,213,214,214,215,215

,216,217,217,218,218,219,219,220 };

 

//по предварительным опытам частота шим получилась 7692,30769Гц

volatile unsigned long f_zad=0x00FFFFFF;//задание на частоту в отн.единицах 0..0x00FFFFFF, что соотв. 0-50 Гц

const unsigned long f_zad_max=0x00FFFFFF;//максимум задание на частоту в отн.единицах, что соотв. 50 Гц

const unsigned long f_zad_min=0x19999A;//минимум задание на частоту в отн.единицах, что соотв. 5 Гц

volatile unsigned long f_out;//частота на выход в отн.единицах 0..0x00FFFFFF, что соотв. 0-50 Гц

const unsigned long df=218;//шаг приращения вых.частоты до задания, подобрать для получения разгона 50Гц/10сек

 

volatile byte u_out=1;//амплитуда на выход в отн.единицах 0-255, что соотв. 0-100%

 

//делим полный оборот вектора напряжения на сектора

//в каждом секторе отдельно накапливаем фазу до максимума и переходим к след.сектору

//при накоплении до 6 сектора начинаем с начала

volatile unsigned long faza=0; //угол вектора в секторе 0..0x00FFFFFF

volatile byte sector=1;//сектор 1..6

const unsigned long dt=2566;//коэф.интегрирования частоты для получения фазы. подобрать для получения 50Гц при f_zad=0x00FFFFFF

 

volatile boolean start=false; //разрешение работы инвертора

 

volatile unsigned int tim=0;//счетчик времени

unsigned long f=5;//задание частоты

unsigned long fz;//задание частоты для шим

unsigned int ttim;//временная tim

unsigned int tims;//таймер подачи силового питания

volatile unsigned long fout;//частота на выход в отн.единицах 0..0x00FFFFFF, что соотв. 0-50 Гц

 

#define key_stop A1 //вывод для кнопки стоп

#define key_start A2 //вывод для кнопки старт

#define f_zad_pin A3 //вход для задатчика выходной частоты

 

//процедура выполняется перед основным циклом

void setup()

{

//настройка выхода для управления подачей силового питания

   pinMode(A0, OUTPUT); digitalWrite(A0,LOW);

 

//настройка входов кнопок с подтяжкой к +5

   pinMode(key_stop, INPUT_PULLUP);

   pinMode(key_start, INPUT_PULLUP);

 

//настройка портов вывода

   DDRD = 0xFC;  // весь порт D - выход, кроме RX TX

   DDRB = 0xFF;  // весь порт B - выход

 

//задание начальных значений частоты и таймера питания

   f_out=f_zad_min;

   tims=0;

   

//Настройка шим и прерываний

   Setup_timer();

}

 

//основной цикл программы, исполняется бесконечно

void loop(){

 

  //считываем значение времени во временную переменную

TIMSK2 &= ~(1<<TOIE2);//Сначала останавливаем прерывания по таймеру 2

ttim=tim;

TIMSK2 |= (1<<TOIE2);//Теперь можно запустить прерывания по таймеру 2

 

//если прошло полсекунды, то делаем опрос кнопок, обновление индикации

if(ttim>3500){//каждые 0.5сек

  //читаем задание частоты

  int f_pin=analogRead(f_zad_pin);

  //изменяем задание на частоту

  f=f_pin/20;

  if(f<5)f=5; //если получилось меньше 5, то установить 5

  if(f>50)f=50; //если больше 50, то установить 50

  //пересчет задания частоы из Гц в относительные единицы

  fz=f*20*0x4189;

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

  TIMSK2 &= ~(1<<TOIE2);//Сначала останавливаем прерывания по таймеру 2

  f_zad=fz; //новое задание

  tim=0;

  TIMSK2 |= (1<<TOIE2);//Теперь можно запустить прерывания по таймеру 2

 

  //проверяем нажатие кнопок старт и стоп

  if(digitalRead(key_start)==LOW){// если старт

    start=true; //устанавливаем флаг разрешения работы

  }

  if(digitalRead(key_stop)==LOW){// если стоп

    start=false;//сбрасываем флаг разрешения работы

  }

 

  //счет выдержки для подачи силового питания

  tims++; //увеличить счетчик на 1

  if(tims>4){//если прошло 2 сек

    tims=0;

    digitalWrite(A0,HIGH); //лог.1 на выход управления питанием

  }

}

 

}

 

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

// Настройка таймеров

void Setup_timer() {

//настройка регистров таймеров и шим согласно документации на контроллер

//за каждую фазу отвечает свой таймер (0-ф.А,1-ф.В,2-ф.С)

// у каждого таймера два компаратора А и В, которые отвечают каждый за свой ключ в фазе (А-верх, В-нижн.)

//т.к. ключи в одном плече работают противофазно, то сомпараторы А настраиваем в неинвертирующий режим, В - в инвертирующий

//таймеры настраиваем в режим Fast PWM, 8bit

   // TIMER0

   TCCR0A |=  (1<<COM0A1); // COM0A1, COM0A0: 1 0 - None-inverted mode для OC0A, для инверсного режима ШИМ нужно 1 1

   TCCR0A &= ~(1<<COM0A0);

   TCCR0A |=  (1<<COM0B1); // COM0B1, COM0B0: 1 0 - None-inverted mode для OC0B, для инверсного режима ШИМ нужно 1 1

   TCCR0A |=  (1<<COM0B0);

   TCCR0A |=  (0<<WGM01);  // WGM02, WGM01, WGM00: 0 1 1 - Fast PWM

   TCCR0A |=  (1<<WGM00);

 

   TCCR0B &= ~(1<<CS02);   // CS02, CS01, CS00: 0 0 1 - тактовый генератор CLK

   TCCR0B &= ~(1<<CS00);

   TCCR0B |=  (1<<CS01);

   TCCR0B &= ~(1<<WGM02);

 

   // TIMER1

   TCCR1A |=  (1<<COM1A1); // COM1A1, COM1A0: 1 0 - None-inverted mode для OC1A, для инверсного режима ШИМ нужно 1 1

   TCCR1A &= ~(1<<COM1A0);

   TCCR1A |=  (1<<COM1B1); // COM1B1, COM1B0: 1 0 - None-inverted mode для OC1B, для инверсного режима ШИМ нужно 1 1

   TCCR1A |=  (1<<COM1B0);

   TCCR1A &= ~(0<<WGM11);  // WGM13, WGM12, WGM11, WGM10: 0 1 0 1 - Fast PWM, 8bit

   TCCR1A |=  (1<<WGM10);

 

   TCCR1B &= ~(1<<CS12);   // CS12, CS11, CS10: 0 0 1 - тактовый генератор CLK

   TCCR1B &= ~(1<<CS10);

   TCCR1B |=  (1<<CS11);

   TCCR1B &= ~(1<<WGM13);

   TCCR1B |=  (1<<WGM12);

 

   // TIMER2

   TCCR2A |=  (1<<COM2A1); // COM2A1, COM2A0: 1 0 - None-inverted mode для OC2A, для инверсного режима ШИМ нужно 1 1

   TCCR2A &= ~(1<<COM2A0);

   TCCR2A |=  (1<<COM2B1); // COM2B1, COM2B0: 1 0 - None-inverted mode для OC2B, для инверсного режима ШИМ нужно 1 1

   TCCR2A |=  (1<<COM2B0);

   TCCR2A |=  (1<<WGM21);  // WGM22, WGM21, WGM20: 0 1 1 - Fast PWM

   TCCR2A |=  (1<<WGM20);

 

   TCCR2B &= ~(1<<CS22);   // CS22, CS21, CS20: 0 0 1 - тактовый генератор CLK

   TCCR2B &= ~(1<<CS20);

   TCCR2B |=  (1<<CS21);

   TCCR2B &= ~(1<<WGM22);

 

   //Установить все PWM в 0

   OCR0A = 0;

   OCR0B = 0;

   OCR1A = 0;

   OCR1B = 0;

   OCR2A = 0;

   OCR2B = 0;

   //Разрешить/запретить прерывания по таймерам

   TIMSK0 &= ~(1<<TOIE0); //Запретить прерывания на таймер 0

   TIMSK1 &= ~(1<<TOIE1); //Запретить прерывания на таймер 1

   TIMSK2 |=  (1<<TOIE2); //Разрешить прерывания на таймер 2

 

}

  

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

//Прерывание по таймеру 2 (TIMER2)

//Работает с частотой шим

//оформляется отдельной блок-схемой, т.к. работает параллельно основной программе

ISR(TIMER2_OVF_vect) {

   //Таймер 0 | выводы 5 и 6  | OC0B, OC0A

   //Таймер 1 | выводы 9 и 10 | OC1A, OC1B

   //Таймер 2 | выводы 3 и 11 | OC2B, OC2A

 

//расчет выходной частоты (реализация разгона и торможения по рампе с заданным темпом)

if(start){//если есть разрешение работы

  if(f_out<f_zad_min)f_out=f_zad_min;//если частота ниже минимума, то ставим минимум

  if(f_out<f_zad){//если частота меньше заданной, то 

    f_out=f_out+df;//увеличиваем с заданным темпом

    if(f_out>f_zad)f_out=f_zad;//если превысили задание, то ставим частоту равной заданию

  }

  if(f_out>f_zad){//если частота больше заданной, то

    f_out=f_out-df;//уменьшаем частоту с заданным темпом

    if(f_out<f_zad)f_out=f_zad;//если стало меньше задания, то ставим частоту равной заданию

  }

}else{//иначе нет разрешения работы (стоп)

  if(f_out<f_zad_min){f_out=0;}//если выходная частота меньше минимума, то ставим ноль

  else{f_out=f_out-df;}//иначе тормозим с заданным темпом

}

//расчет амплитуды выходного напряжения пропорционально частоте

//т.к. принята линейная зависимость, то в относительных единицах они равны

//просто приводим к размеру представления амплитуды

u_out=(byte)(f_out>>16);

//расчет угла обобщенного вектора выходного напряжения

faza=faza+dt*(f_out>>16);//(фаза увеличивается как интеграл частоты)

if(faza>0x00FFFFFF){//если угол вышел за сектор в 60град

  faza=faza-0x00FFFFFF;//находим угол вектора в новом секторе

  sector++;//увеличиваем номер сектора на 1

  if(sector>6)sector=1;//если сектор больше 6, то начинаем с первого

}

//расчет времени импульсов управления ключами

unsigned int t1=pgm_read_byte_near(time1+(faza>>16));//считываем из таблицы Т1

unsigned int t2=pgm_read_byte_near(time2+(faza>>16));//считываем из таблицы Т2

//умножаем Т1 и Т2 на относительную амплитуду напряжения

t1=(t1*u_out)>>8;

t2=(t2*u_out)>>8;

byte t0=255-t1-t2;//расчет времени нулевого вектора

byte fa,fb,fc;//временные переменные для компараторов фазы А,В,С

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

//см.рис.20.5

switch (sector) {

  case 1:{ //t1-100 t2-110 t0-111

    PORTD |= (1 << 4); //Для контроля вых.частоты

    fa=255;

    fb=t0+t2;

    fc=t0;

    break;

  }

  case 2:{ //t1-110 t2-010 t0-111

    fb=255;

    fa=t0+t1;

    fc=t0;

    break;

  }

  case 3:{ //t1-010 t2-011 t0-111

    fb=255;

    fc=t0+t2;

    fa=t0;

    break;

  }

  case 4:{ //t1-011 t2-001 t0-111

    PORTD &= ~(1 << 4); //Для контроля вых.частоты

    fc=255;

    fb=t0+t1;

    fa=t0;

    break;

  }

  case 5:{ //t1-001 t2-101 t0-111

    fc=255;

    fa=t0+t2;

    fb=t0;

    break;

  }

  case 6:{ //t1-101 t2-100 t0-111

    fa=255;

    fc=t0+t1;

    fb=t0;

    break;

  }

}

if(!start){//стоп - формируем ноль на выход

  if(f_out<f_zad_min){

    fa=255;fb=255;fc=255;

  }

}

//запись значений в компараторы

OCR0A = fa;

OCR0B = fa;

OCR1A = fb;

OCR1B = fb;

OCR2A = fc;

OCR2B = fc;

 

//приращение счетчика времени

tim++;

 

}

 

следующий вариант этой проги для тех, кто имеет МКИ на базе TM1638 с двумя четырёхразрядными индикаторами семисегментного кода с общим катодом LED & KEY 

http://arduinoprom.ru/obzory-modulej/289-obzor-displeja-led-and-key-na-c...

Слева заданная частота, справа текущая

Интенсивность разгона около 50 сек. до 50 Гц.

сейчас df=100

меняйте и смотрите

 

А3 - STB

А2 - CLK

А1 - DIO

 

A0  - готовность появляется через 2 секунды после включения, подача силового питания на ключи.

D6  - HU, D5  - LU фаза А

D10 - HV, D9  - LV фаза В

D11 - HW, D3  - LW фаза С

А4 - меандр, контроль частоты.

 

Для повышения максимальной частоты менять константы пересчета туда и обратно

const unsigned long dt=2566;//коэф.интегрирования частоты для получения фазы. подобрать для получения 50Гц при f_zad=0x00FFFFFF

  fz=f*20*0x4189;

  ff=ff/float(0x00FFFFFF); ff=ff*50.0; условия ограничения задания с кнопок

 

не забудьте добавить в библиотеку TM1638

 

#include <TM1638.h>

 

//таблица времени T1

const unsigned int PROGMEM time1[]  = {

220,220,219,219,218,218,217,217,216,215,215,214,214,213,213,212,212,211,210,210,209,209,208,207,207,206,206,205,204,204,203,202,202

,201,201,200,199,199,198,197,197,196,195,195,194,193,193,192,191,191,190,189,188,188,187,186,186,185,184,183,183,182,181,181,180,179

,178,178,177,176,175,175,174,173,172,172,171,170,169,168,168,167,166,165,164,164,163,162,161,160,160,159,158,157,156,156,155,154,153

,152,151,151,150,149,148,147,146,145,145,144,143,142,141,140,139,139,138,137,136,135,134,133,132,131,131,130,129,128,127,126,125,124

,123,122,122,121,120,119,118,117,116,115,114,113,112,111,110,109,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92

,91,90,89,88,87,86,85,84,83,82,81,80,79,78,78,77,76,75,74,73,72,71,70,69,68,67,65,64,63,62,61,60,59,58,57,56,55,54,53

,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,24,23,22,21,20,19,18,17,16,15,14,13

,12,11,10,9,8,7,6,5,4,3,2,1 };

 

//таблица времени T2

const unsigned int PROGMEM time2[]  = {

0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41

,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,67,68,69,70,71,72,73,74,75,76,77,78,78,79,80

,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,109,110,111,112,113,114,115

,116,117,118,119,120,121,122,122,123,124,125,126,127,128,129,130,131,131,132,133,134,135,136,137,138,139,139,140,141,142,143,144,145

,145,146,147,148,149,150,151,151,152,153,154,155,156,156,157,158,159,160,160,161,162,163,164,164,165,166,167,168,168,169,170,171,172

,172,173,174,175,175,176,177,178,178,179,180,181,181,182,183,183,184,185,186,186,187,188,188,189,190,191,191,192,193,193,194,195,195

,196,197,197,198,199,199,200,201,201,202,202,203,204,204,205,206,206,207,207,208,209,209,210,210,211,212,212,213,213,214,214,215,215

,216,217,217,218,218,219,219,220 };

 

//по предварительным опытам частота шим получилась 7692,30769Гц

volatile unsigned long f_zad=0x00FFFFFF;//задание на частоту в отн.единицах 0..0x00FFFFFF, что соотв. 0-50 Гц

const unsigned long f_zad_max=0x00FFFFFF;//максимум задание на частоту в отн.единицах, что соотв. 50 Гц

const unsigned long f_zad_min=0x19999A;//минимум задание на частоту в отн.единицах, что соотв. 5 Гц

volatile unsigned long f_out;//частота на выход в отн.единицах 0..0x00FFFFFF, что соотв. 0-50 Гц

const unsigned long df=050;//шаг приращения вых.частоты до задания, подобрать для получения разгона 50Гц/100сек

 

volatile byte u_out=1;//амплитуда на выход в отн.единицах 0-255, что соотв. 0-100%

 

//делим полный оборот вектора напряжения на сектора

//в каждом секторе отдельно накапливаем фазу до максимума и переходим к след.сектору

//при накоплении до 6 сектора начинаем с начала

volatile unsigned long faza=0; //угол вектора в секторе 0..0x00FFFFFF

volatile byte sector=1;//сектор 1..6

const unsigned long dt=2566;//коэф.интегрирования частоты для получения фазы. подобрать для получения 50Гц при f_zad=0x00FFFFFF

 

//TM1638 module(8, 9, 7);// define a module on data pin 8, clock pin 9 and strobe pin 7

TM1638 tm(A1, A2, A3);//подключаем кнопки и индикатор DATA-A1 CLK-A2 STRB-A3

 

volatile boolean start=false; //разрешение работы инвертора

 

volatile unsigned int tim=0;//счетчик времени

unsigned long f=5;//задание частоты

unsigned long fz;//задание частоты для шим

unsigned int ttim,tims;

volatile unsigned long fout;//частота на выход в отн.единицах 0..0x00FFFFFF, что соотв. 0-50 Гц

 

void setup()

{

   Serial.begin(115200);        // connect to the serial port

   Serial.println("Test begin");

 

//управление подачей силового питания

   pinMode(A0, OUTPUT);

   digitalWrite(A0,LOW);

 

   DDRD = 0xFC;  // весь порт D - выход, кроме RX TX

   DDRB = 0xFF;  // весь порт B - выход

 

   f_out=f_zad_min;

  

   //Настройка и запуск таймеров

   Setup_timer();

  

   tm.clearDisplay();

   tims=0;

}

 

void loop(){

byte keys;

 

TIMSK2 &= ~(1<<TOIE2);//Сначала останавливаем прерывания по таймеру 2

ttim=tim;

TIMSK2 |= (1<<TOIE2);//Теперь можно запустить прерывания по таймеру 2

if(ttim>3500){//каждые 0.5сек

  //читаем кнопки

  keys = tm.getButtons();

  //Serial.print("keys=");Serial.println(keys);

  //читаем задание на частоту

  if(keys==4){// уменьшить задание

   f--; if(f<5)f=5;

  }else{

    if(keys==8){// увеличить задание

     f++; if(f>50)f=50;

    }

  }

  fz=f*20*0x4189;

  TIMSK2 &= ~(1<<TOIE2);//Сначала останавливаем прерывания по таймеру 2

  f_zad=fz; //новое задание

  tim=0;

  TIMSK2 |= (1<<TOIE2);//Теперь можно запустить прерывания по таймеру 2

 

  if(keys==2){// start

    start=true;

  }

  if(keys==1){// stop

    start=false;

  }

 

  //вывод задания и выхода на индикатор Гц

  //tm.setDisplayToDecNumber( f*10000+fout, 0, false);

  //tm.setNumber(f,3);

  TIMSK2 &= ~(1<<TOIE2);//Сначала останавливаем прерывания по таймеру 2

  fout=f_out;

  TIMSK2 |= (1<<TOIE2);//Теперь можно запустить прерывания по таймеру 2

  //Serial.print("fout_1=");Serial.println(fout);

  //fout=fout*50;

  //Serial.print("fout_2=");Serial.println(fout);

  //fout=fout/0x00FFFFFF;

  //Serial.print("fout_3=");Serial.println(fout);

  float ff=float(fout);

  ff=ff/float(0x00FFFFFF); ff=ff*50.0;

  fout=round(ff);

  String s1,s2;

  s1=String(f);while(s1.length()<4){s1=" "+s1;}

  s2=String(fout);while(s2.length()<4){s2=" "+s2;}

  s1=s1+s2;

  tm.setDisplayToString(s1);

 

  //счет выдержки для подачи силового питания

  tims++;

  if(tims>4){//2 сек

    tims=0;

    digitalWrite(A0,HIGH);

  }

}

 

}

 

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

// Настройка таймеров

void Setup_timer() {

 

   // TIMER0

   TCCR0A |=  (1<<COM0A1); // COM0A1, COM0A0: 1 0 - None-inverted mode для OC0A, для инверсного режима ШИМ нужно 1 1

   TCCR0A &= ~(1<<COM0A0);

   TCCR0A |=  (1<<COM0B1); // COM0B1, COM0B0: 1 0 - None-inverted mode для OC0B, для инверсного режима ШИМ нужно 1 1

   TCCR0A |=  (1<<COM0B0);

   TCCR0A |=  (0<<WGM01);  // WGM02, WGM01, WGM00: 0 1 1 - Fast PWM

   TCCR0A |=  (1<<WGM00);

 

   TCCR0B &= ~(1<<CS02);   // CS02, CS01, CS00: 0 0 1 - тактовый генератор CLK

   TCCR0B &= ~(1<<CS00);

   TCCR0B |=  (1<<CS01);

   TCCR0B &= ~(1<<WGM02);

 

   // TIMER1

   TCCR1A |=  (1<<COM1A1); // COM1A1, COM1A0: 1 0 - None-inverted mode для OC1A, для инверсного режима ШИМ нужно 1 1

   TCCR1A &= ~(1<<COM1A0);

   TCCR1A |=  (1<<COM1B1); // COM1B1, COM1B0: 1 0 - None-inverted mode для OC1B, для инверсного режима ШИМ нужно 1 1

   TCCR1A |=  (1<<COM1B0);

   TCCR1A &= ~(0<<WGM11);  // WGM13, WGM12, WGM11, WGM10: 0 1 0 1 - Fast PWM, 8bit

   TCCR1A |=  (1<<WGM10);

 

   TCCR1B &= ~(1<<CS12);   // CS12, CS11, CS10: 0 0 1 - тактовый генератор CLK

   TCCR1B &= ~(1<<CS10);

   TCCR1B |=  (1<<CS11);

   TCCR1B &= ~(1<<WGM13);

   TCCR1B |=  (1<<WGM12);

 

   // TIMER2

   TCCR2A |=  (1<<COM2A1); // COM2A1, COM2A0: 1 0 - None-inverted mode для OC2A, для инверсного режима ШИМ нужно 1 1

   TCCR2A &= ~(1<<COM2A0);

   TCCR2A |=  (1<<COM2B1); // COM2B1, COM2B0: 1 0 - None-inverted mode для OC2B, для инверсного режима ШИМ нужно 1 1

   TCCR2A |=  (1<<COM2B0);

   TCCR2A |=  (1<<WGM21);  // WGM22, WGM21, WGM20: 0 1 1 - Fast PWM

   TCCR2A |=  (1<<WGM20);

 

   TCCR2B &= ~(1<<CS22);   // CS22, CS21, CS20: 0 0 1 - тактовый генератор CLK

   TCCR2B &= ~(1<<CS20);

   TCCR2B |=  (1<<CS21);

   TCCR2B &= ~(1<<WGM22);

 

   //Установить все PWM в 0

   OCR0A = 0;

   OCR0B = 0;

   OCR1A = 0;

   OCR1B = 0;

   OCR2A = 0;

   OCR2B = 0;

   //Разрешить/запретить прерывания по таймерам

   TIMSK0 &= ~(1<<TOIE0); //Запретить прерывания на таймер 0

   TIMSK1 &= ~(1<<TOIE1); //Запретить прерывания на таймер 1

   TIMSK2 |=  (1<<TOIE2); //Разрешить прерывания на таймер 2

 

}

 

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

//Прерывание по таймеру 2 (TIMER2)

//Работает с частотой

ISR(TIMER2_OVF_vect) {

   PORTD |= (1 << 2); //Для замера скорости

   //Таймер 0 | выводы 5 и 6  | OC0B, OC0A

   //Таймер 1 | выводы 9 и 10 | OC1A, OC1B

   //Таймер 2 | выводы 3 и 11 | OC2B, OC2A

 

//разгон к заданию

if(start){//работаем

  if(f_out<f_zad_min)f_out=f_zad_min;

  if(f_out<f_zad){

    f_out=f_out+df; if(f_out>f_zad)f_out=f_zad;

  }

  if(f_out>f_zad){

    f_out=f_out-df; if(f_out<f_zad)f_out=f_zad;

  }

}else{//стоп

  if(f_out<f_zad_min){f_out=0;}

  else{f_out=f_out-df;}

}

//амплитуда пропорционально частоте

u_out=(byte)(f_out>>16);

//крутим вектор

faza=faza+dt*(f_out>>16);

if(faza>0x00FFFFFF){

  faza=faza-0x00FFFFFF;

  sector++;

  if(sector>6)sector=1;

}

//расчет времени импульсов

unsigned int t1=pgm_read_byte_near(time1+(faza>>16));

unsigned int t2=pgm_read_byte_near(time2+(faza>>16));

t1=(t1*u_out)>>8;

t2=(t2*u_out)>>8;

byte t0=255-t1-t2;

byte fa,fb,fc;

//выдаем сигналы управления от сектора

switch (sector) {

  case 1:{ //t1-100 t2-110 t0-111

    PORTD |= (1 << 4); //Для контроля вых.частоты

    fa=255;

    fb=t0+t2;

    fc=t0;

    break;

  }

  case 2:{ //t1-110 t2-010 t0-111

    fb=255;

    fa=t0+t1;

    fc=t0;

    break;

  }

  case 3:{ //t1-010 t2-011 t0-111

    fb=255;

    fc=t0+t2;

    fa=t0;

    break;

  }

  case 4:{ //t1-011 t2-001 t0-111

    PORTD &= ~(1 << 4); //Для контроля вых.частоты

    fc=255;

    fb=t0+t1;

    fa=t0;

    break;

  }

  case 5:{ //t1-001 t2-101 t0-111

    fc=255;

    fa=t0+t2;

    fb=t0;

    break;

  }

  case 6:{ //t1-101 t2-100 t0-111

    fa=255;

    fc=t0+t1;

    fb=t0;

    break;

  }

}

if(!start){//стоп - формируем ноль на выход

  if(f_out<f_zad_min){

    fa=255;fb=255;fc=255;

  }

}

OCR0A = fa;

OCR0B = fa;

OCR1A = fb;

OCR1B = fb;

OCR2A = fc;

OCR2B = fc;

 

/*

   //Для теста

   //ШИМ 0, pins 6, 5

   OCR0A = 0;

   OCR0B = 64;

   //ШИМ 1, pins 9, 10

   OCR1A = 128;

   OCR1B = 255;

   //ШИМ 2, pins 11, 3

   OCR2A = 3;

   OCR2B = 250;

*/ 

 

  tim++;

 

   PORTD &= ~(1 << 2); //Для замера скорости

}

 

Когда станете цеплять ключи, не забудьте на выходы ШИМ поставить схемы задержки времени, чтобы избежать сквозных токов через силовые ключи.

Задержка на включение до 3 мксек (в данном случае 2,5 мксек).

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

К сожалению не умею вставлять рисунки и файлы

Вроде бы всё!!!

 

b707
Offline
Зарегистрирован: 26.05.2017

vahnik60 пишет:

К сожалению не умею вставлять рисунки и файлы

а так же код.

Может, прежде чем лезть на форум. правила надо было почитать? или так душа горела "вмешаться" ?:)

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

ну он же должен был нам причинить радость и нанести щастье.  Вот причинил и нанёс, без спроса.

serega-4789
Offline
Зарегистрирован: 06.05.2020

добрый день!

/расчет шим и метод формирования по книге \диплом\Помощь\книга с. 460.djvu

как книга называется?

vahnik60
Offline
Зарегистрирован: 07.07.2020

добрый день!

/расчет шим и метод формирования по книге \диплом\Помощь\книга с. 460.djvu

как книга называется?

vahnik60@mail.ru пошли мне пробное письмо, я тебе перешлю все материалы, какие пожелаешь, а там вставляй куда хочешь сам. Надоела эта ребятня с пацанскими манерами, мало мне полторашку к пенсии добавили, ещё ваши выпендроны выслушивать. Возьми на себя распространение.

vahnik60
Offline
Зарегистрирован: 07.07.2020

/расчет шим и метод формирования по книге \диплом\Помощь\книга с. 460.djvu как книга называется?

В. Мелешин Д. Овчинников
Управление транзисторными преобразователями электроэнергии
стр. 460
но вам лучше в эти дебри не лезть, лучше воткните первую прогу в Arduino UNO и наслаждайтесь. Не забудьте про задержки на включение ключей (в моём IGBT-модуле время открытия IGBT-транзистора 0,4 мксек, а время закрытия - 3,4 мксек при 50 А 600 В), сквозной ток, однако!!!
У кого есть МКИ указанного типа (индикаторы с общим катодом ТМ1638, если индикаторы с общим анодом - замучаетесь программировать, нестандартный вариант), запускайте вторую прогу, ещё приятнее.
Если нужны исходники для Arduino UNO (R3) или схемы - запрос на почту vahnik60@mail.ru
Есть файл маткада с наглядным пояснением, как из двух двугорбых верблюдов получается синусоида линейного напряжения, это классическая раскладка 3-фазного векторного ШИМ (извините за повторения).
serega-4789
Offline
Зарегистрирован: 06.05.2020


Можно ли использовать триггер шмидта для задачи дет тайма? На осциллографе в протеусе вроде получается ..
serega-4789
Offline
Зарегистрирован: 06.05.2020
/*
Программу варганил профи. Начальная частота пуска 5 Гц, Максимальная частота 50 Гц. Разгон и торможение в функции U/f, зависимость линейная. Частота ШИМ 5 кГц.
Задание частоты потенциометром, кнопки пуск и стоп. назначение входов в программе подписаны: #define key_stop A1 //вывод для кнопки стоп:
#define key_start A2 //вывод для кнопки старт: #define f_zad_pin A3 //вход для задатчика выходной частоты.
*/
//расчет шим и метод формирования по книге \диплом\Помощь\книга с. 460.djvu

 

#include "LiquidCrystal_I2C.h"

//раздел 20.1
//таблица времени T1

//по формуле 20.1.6 - cos(a+30) в относительных единицах (0-1 соотв. 0-255)

const unsigned int PROGMEM time1[]  = {

220,220,219,219,218,218,217,217,216,215,215,214,214,213,213,212,212,211,210,210,209,209,208,207,207,206,206,205,204,204,203,202,202

,201,201,200,199,199,198,197,197,196,195,195,194,193,193,192,191,191,190,189,188,188,187,186,186,185,184,183,183,182,181,181,180,179

,178,178,177,176,175,175,174,173,172,172,171,170,169,168,168,167,166,165,164,164,163,162,161,160,160,159,158,157,156,156,155,154,153

,152,151,151,150,149,148,147,146,145,145,144,143,142,141,140,139,139,138,137,136,135,134,133,132,131,131,130,129,128,127,126,125,124

,123,122,122,121,120,119,118,117,116,115,114,113,112,111,110,109,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92

,91,90,89,88,87,86,85,84,83,82,81,80,79,78,78,77,76,75,74,73,72,71,70,69,68,67,65,64,63,62,61,60,59,58,57,56,55,54,53

,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,24,23,22,21,20,19,18,17,16,15,14,13

,12,11,10,9,8,7,6,5,4,3,2,1 };

 

//таблица времени T2

//по формуле 20.1.7 - sin(a) в относительных единицах (0-1 соотв. 0-255)

const unsigned int PROGMEM time2[]  = {

0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41

,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,67,68,69,70,71,72,73,74,75,76,77,78,78,79,80

,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,109,110,111,112,113,114,115

,116,117,118,119,120,121,122,122,123,124,125,126,127,128,129,130,131,131,132,133,134,135,136,137,138,139,139,140,141,142,143,144,145

,145,146,147,148,149,150,151,151,152,153,154,155,156,156,157,158,159,160,160,161,162,163,164,164,165,166,167,168,168,169,170,171,172

,172,173,174,175,175,176,177,178,178,179,180,181,181,182,183,183,184,185,186,186,187,188,188,189,190,191,191,192,193,193,194,195,195

,196,197,197,198,199,199,200,201,201,202,202,203,204,204,205,206,206,207,207,208,209,209,210,210,211,212,212,213,213,214,214,215,215

,216,217,217,218,218,219,219,220 };

 

//по предварительным опытам частота шим получилась 7692,30769Гц

volatile unsigned long f_zad=0x00FFFFFF;//задание на частоту в отн.единицах 0..0x00FFFFFF, что соотв. 0-50 Гц
const unsigned long f_zad_max=0x00FFFFFF;//максимум задание на частоту в отн.единицах, что соотв. 50 Гц
const unsigned long f_zad_min=0x19999A;//минимум задание на частоту в отн.единицах, что соотв. 5 Гц
volatile unsigned long f_out;//частота на выход в отн.единицах 0..0x00FFFFFF, что соотв. 0-50 Гц
const unsigned long df=218;//шаг приращения вых.частоты до задания, подобрать для получения разгона 50Гц/10сек
volatile byte u_out=1;//амплитуда на выход в отн.единицах 0-255, что соотв. 0-100%

 //делим полный оборот вектора напряжения на сектора

//в каждом секторе отдельно накапливаем фазу до максимума и переходим к след.сектору

//при накоплении до 6 сектора начинаем с начала

volatile unsigned long faza=0; //угол вектора в секторе 0..0x00FFFFFF
volatile byte sector=1;//сектор 1..6
const unsigned long dt=2566;//коэф.интегрирования частоты для получения фазы. подобрать для получения 50Гц при f_zad=0x00FFFFFF
volatile boolean start=false; //разрешение работы инвертора
volatile unsigned int tim=0;//счетчик времени
unsigned long f=5;//задание частоты
volatile unsigned long f_old;
unsigned long fz;//задание частоты для шим
unsigned int ttim;//временная tim
unsigned int tims;//таймер подачи силового питания
volatile unsigned long fout;//частота на выход в отн.единицах 0..0x00FFFFFF, что соотв. 0-50 Гц

#define key_stop A1 //вывод для кнопки стоп
#define key_start A2 //вывод для кнопки старт
#define f_zad_pin A3 //вход для задатчика выходной частоты

LiquidCrystal_I2C lcd(0x27,16,2);   // Задаем адрес и размерность дисплея.

//процедура выполняется перед основным циклом

void setup()

{
Serial.begin(9600);        // connect to the serial port
Serial.println("Test begin");
//настройка выхода для управления подачей силового питания

   pinMode(A0, OUTPUT); digitalWrite(A0,LOW);

//настройка входов кнопок с подтяжкой к +5

   pinMode(key_stop, INPUT_PULLUP);
   pinMode(key_start, INPUT_PULLUP);
//настройка портов вывода

   DDRD = 0xFC;  // весь порт D - выход, кроме RX TX

   DDRB = 0xFF;  // весь порт B - выход

//задание начальных значений частоты и таймера питания

   f_out=f_zad_min;

   tims=0;

//Настройка шим и прерываний

   Setup_timer();
    
    
    lcd.setCursor(0, 0);                      // Устанавливаем курсор в начало 1 строки
    lcd.print("Freq:           ");            // Выводим текст
    lcd.setCursor(6, 0);                      // Устанавливаем курсор в 6 позицию
    lcd.print(f);        // Выводим текст
    Serial.println( f);
}

 

//основной цикл программы, исполняется бесконечно

void loop(){
//считываем значение времени во временную переменную
TIMSK2 &= ~(1<<TOIE2);//Сначала останавливаем прерывания по таймеру 2
ttim=tim;
TIMSK2 |= (1<<TOIE2);//Теперь можно запустить прерывания по таймеру 2
//если прошло полсекунды, то делаем опрос кнопок, обновление индикации
if(ttim>3500){//каждые 0.5сек

  //читаем задание частоты

  int f_pin=analogRead(f_zad_pin);

  //изменяем задание на частоту

  f=f_pin/20;
  if(f<5)f=5; //если получилось меньше 5, то установить 5
  if(f>50)f=50; //если больше 50, то установить 50

  //пересчет задания частоы из Гц в относительные единицы

  fz=f*20*0x4189;

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

  TIMSK2 &= ~(1<<TOIE2);//Сначала останавливаем прерывания по таймеру 2

  f_zad=fz; //новое задание..............................................................
  
  if( f!= f_old){f_old=f;  
    lcd.setCursor(6, 0);                      // Устанавливаем курсор в 6 позицию
    lcd.print(f);                         // Выводим текст        
    Serial.println(f);
  }
  tim=0;

  TIMSK2 |= (1<<TOIE2);//Теперь можно запустить прерывания по таймеру 2

 

  //проверяем нажатие кнопок старт и стоп

  if(digitalRead(key_start)==LOW){// если старт

    start=true; //устанавливаем флаг разрешения работы

  }

  if(digitalRead(key_stop)==LOW){// если стоп

    start=false;//сбрасываем флаг разрешения работы

  }

 

  //счет выдержки для подачи силового питания

  tims++; //увеличить счетчик на 1

  if(tims>4){//если прошло 2 сек

    tims=0;

    digitalWrite(A0,HIGH); //лог.1 на выход управления питанием

  }

}

 

}

 

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

// Настройка таймеров

void Setup_timer() {

//настройка регистров таймеров и шим согласно документации на контроллер

//за каждую фазу отвечает свой таймер (0-ф.А,1-ф.В,2-ф.С)

// у каждого таймера два компаратора А и В, которые отвечают каждый за свой ключ в фазе (А-верх, В-нижн.)

//т.к. ключи в одном плече работают противофазно, то сомпараторы А настраиваем в неинвертирующий режим, В - в инвертирующий

//таймеры настраиваем в режим Fast PWM, 8bit

   // TIMER0

   TCCR0A |=  (1<<COM0A1); // COM0A1, COM0A0: 1 0 - None-inverted mode для OC0A, для инверсного режима ШИМ нужно 1 1
   TCCR0A &= ~(1<<COM0A0);
   TCCR0A |=  (1<<COM0B1); // COM0B1, COM0B0: 1 0 - None-inverted mode для OC0B, для инверсного режима ШИМ нужно 1 1
   TCCR0A |=  (1<<COM0B0);
   TCCR0A |=  (0<<WGM01);  // WGM02, WGM01, WGM00: 0 1 1 - Fast PWM
   TCCR0A |=  (1<<WGM00);
   TCCR0B &= ~(1<<CS02);   // CS02, CS01, CS00: 0 0 1 - тактовый генератор CLK
   TCCR0B &= ~(1<<CS00);
   TCCR0B |=  (1<<CS01);
   TCCR0B &= ~(1<<WGM02);

  // TIMER1

   TCCR1A |=  (1<<COM1A1); // COM1A1, COM1A0: 1 0 - None-inverted mode для OC1A, для инверсного режима ШИМ нужно 1 1
   TCCR1A &= ~(1<<COM1A0);
   TCCR1A |=  (1<<COM1B1); // COM1B1, COM1B0: 1 0 - None-inverted mode для OC1B, для инверсного режима ШИМ нужно 1 1
   TCCR1A |=  (1<<COM1B0);
   TCCR1A &= ~(0<<WGM11);  // WGM13, WGM12, WGM11, WGM10: 0 1 0 1 - Fast PWM, 8bit
   TCCR1A |=  (1<<WGM10);

   TCCR1B &= ~(1<<CS12);   // CS12, CS11, CS10: 0 0 1 - тактовый генератор CLK
   TCCR1B &= ~(1<<CS10);
   TCCR1B |=  (1<<CS11);
   TCCR1B &= ~(1<<WGM13);
   TCCR1B |=  (1<<WGM12);

   // TIMER2

   TCCR2A |=  (1<<COM2A1); // COM2A1, COM2A0: 1 0 - None-inverted mode для OC2A, для инверсного режима ШИМ нужно 1 1
   TCCR2A &= ~(1<<COM2A0);
   TCCR2A |=  (1<<COM2B1); // COM2B1, COM2B0: 1 0 - None-inverted mode для OC2B, для инверсного режима ШИМ нужно 1 1
   TCCR2A |=  (1<<COM2B0);
   TCCR2A |=  (1<<WGM21);  // WGM22, WGM21, WGM20: 0 1 1 - Fast PWM
   TCCR2A |=  (1<<WGM20);

 

   TCCR2B &= ~(1<<CS22);   // CS22, CS21, CS20: 0 0 1 - тактовый генератор CLK
   TCCR2B &= ~(1<<CS20);
   TCCR2B |=  (1<<CS21);
   TCCR2B &= ~(1<<WGM22);
 
   //Установить все PWM в 0

   OCR0A = 0;
   OCR0B = 0;// инверс

   OCR1A = 0;
   OCR1B = 0;

   OCR2A = 0;
   OCR2B = 0;

   //Разрешить/запретить прерывания по таймерам

   TIMSK0 &= ~(1<<TOIE0); //Запретить прерывания на таймер 0
   TIMSK1 &= ~(1<<TOIE1); //Запретить прерывания на таймер 1
   TIMSK2 |=  (1<<TOIE2); //Разрешить прерывания на таймер 2

 

}

  

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

//Прерывание по таймеру 2 (TIMER2)

//Работает с частотой шим

//оформляется отдельной блок-схемой, т.к. работает параллельно основной программе

ISR(TIMER2_OVF_vect) {

   //Таймер 0 | выводы 5 и 6  | OC0B, OC0A

   //Таймер 1 | выводы 9 и 10 | OC1A, OC1B

   //Таймер 2 | выводы 3 и 11 | OC2B, OC2A

 

//расчет выходной частоты (реализация разгона и торможения по рампе с заданным темпом)

if(start){//если есть разрешение работы

  if(f_out<f_zad_min)f_out=f_zad_min;//если частота ниже минимума, то ставим минимум

  if(f_out<f_zad){//если частота меньше заданной, то 

    f_out=f_out+df;//увеличиваем с заданным темпом

    if(f_out>f_zad)f_out=f_zad;//если превысили задание, то ставим частоту равной заданию

  }

  if(f_out>f_zad){//если частота больше заданной, то

    f_out=f_out-df;//уменьшаем частоту с заданным темпом

    if(f_out<f_zad)f_out=f_zad;//если стало меньше задания, то ставим частоту равной заданию

  }

}else{//иначе нет разрешения работы (стоп)

  if(f_out<f_zad_min){f_out=0;}//если выходная частота меньше минимума, то ставим ноль

  else{f_out=f_out-df;}//иначе тормозим с заданным темпом

}

//расчет амплитуды выходного напряжения пропорционально частоте

//т.к. принята линейная зависимость, то в относительных единицах они равны

//просто приводим к размеру представления амплитуды

u_out=(byte)(f_out>>16);

//расчет угла обобщенного вектора выходного напряжения

faza=faza+dt*(f_out>>16);//(фаза увеличивается как интеграл частоты)

if(faza>0x00FFFFFF){//если угол вышел за сектор в 60град

  faza=faza-0x00FFFFFF;//находим угол вектора в новом секторе

  sector++;//увеличиваем номер сектора на 1

  if(sector>6)sector=1;//если сектор больше 6, то начинаем с первого

}

//расчет времени импульсов управления ключами

unsigned int t1=pgm_read_byte_near(time1+(faza>>16));//считываем из таблицы Т1

unsigned int t2=pgm_read_byte_near(time2+(faza>>16));//считываем из таблицы Т2

//умножаем Т1 и Т2 на относительную амплитуду напряжения

t1=(t1*u_out)>>8;

t2=(t2*u_out)>>8;

byte t0=255-t1-t2;//расчет времени нулевого вектора

byte fa,fb,fc;//временные переменные для компараторов фазы А,В,С

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

//см.рис.20.5

switch (sector) {

  case 1:{ //t1-100 t2-110 t0-111

    PORTD |= (1 << 4); //Для контроля вых.частоты

    fa=255;

    fb=t0+t2;

    fc=t0;

    break;

  }

  case 2:{ //t1-110 t2-010 t0-111

    fb=255;

    fa=t0+t1;

    fc=t0;

    break;

  }

  case 3:{ //t1-010 t2-011 t0-111

    fb=255;

    fc=t0+t2;

    fa=t0;

    break;

  }

  case 4:{ //t1-011 t2-001 t0-111

    PORTD &= ~(1 << 4); //Для контроля вых.частоты

    fc=255;

    fb=t0+t1;

    fa=t0;

    break;

  }

  case 5:{ //t1-001 t2-101 t0-111

    fc=255;

    fa=t0+t2;

    fb=t0;

    break;

  }

  case 6:{ //t1-101 t2-100 t0-111

    fa=255;

    fc=t0+t1;

    fb=t0;

    break;

  }

}

if(!start){//стоп - формируем ноль на выход

  if(f_out<f_zad_min){

    fa=255;fb=255;fc=255;

  }

}

//запись значений в компараторы

OCR0A = fa;
OCR0B = fa;

OCR1A = fb;
OCR1B = fb;

OCR2A = fc;
OCR2B = fc;

 //приращение счетчика времени

tim++;

 }

 

serega-4789
Offline
Зарегистрирован: 06.05.2020

Добавил дисплей для первого варианта 

Oleg.normalniy
Offline
Зарегистрирован: 10.06.2020
vahnik60
Offline
Зарегистрирован: 07.07.2020

Уважаемый Oleg.normalniy, недоумеваю от Ваших коментариев в роликах по поводу сравнения потребления тока при векторном (двугорбые полусинусоиды) и при самопальном (одногорбые полусинусоиды). Для двигателя разницы в способе формирования синусоиды напряжения нет, а вот амплитуда модулируемого напряжения синусоиды для него имеет значение, в первом случае формируются линейные напряжения 3-х фазной сети чуть не в два раза выше, чем при втором при "одинаковой", вроде бы, амплитуде на RC-цепочке. Первый классический способ гарантируется 100% использования ресурса постоянного напряжения источника, а второй менее 70%. Вы можете в этом убедиться, если подцепите на силовые выходы инвертора "звезду" из 3-х RC-цепей (например 1 мкф, 30k), образующих искусственную среднюю точку. Увидите фвзные напряжения в масштабе в обоих случаях. Успехов в экспериментах, берегите себя.

borlik5
Offline
Зарегистрирован: 15.01.2018

 пост  #94

доброго времени .   собрал на макетке , прошил код  ,

все 3 верхних шим просто включены и не управляются ,

управляются только нижние .

это так специально или у меня при загрузке косяк ?

vahnik60
Offline
Зарегистрирован: 07.07.2020

При не запущенном ШИМ открыты три верхние ключи, нижние отключены. При включении ШИМ в каждом плече один из ключей включен, второй выключен и наоборот. Вероятно, у Вас неверный код программы. Пришлитемне письмо на почту vahnik60mail.ru , я вышлю исходники для двух вариантов программы. Первый вариант с заданием частоты резистивным задатчиком, второй - с МКИ на TM1638.

Вариант и LCD-дисплеем не смотрел (не моя переделка), он оказался недоделанным в плане индикации.

serega-4789
Offline
Зарегистрирован: 06.05.2020

Дипломная работа частотный привод

https://yadi.sk/d/hFfTlAGVB6TIBw