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

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

Приветствую всех!

Набросал программу для управления 3х фазным асинхронным двигателем через драйвера типа IR2101, IR2106 с двумя входами.

Программа для Arduino на чипе ATMEGA328P, 16МГц

Частота регулируется переменным резистором от 10 до 150 Гц.

В железе пока не проверял, но на эмуляторе синусы выглядит красиво.

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

Картинка с 2-мя фазами(чтобы показать 3-ю нужно еще 2 входа осциллографа):

Схема в симуляторе:

Сам код:

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

const unsigned int 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 inctmr;             // переменная для реализации таймера в loop
volatile unsigned int setfreq;    // переменная для хранения установленной частоты
const int deltafreq=15;		  // 15 пунктов изменения аналогового входа дают изменение частоты на 2Гц

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

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

   dfreq = 50.0;                          //Частота работы по умолчанию
   setfreq = 300;			  //Значение аналогового пина, соответствующее 50Гц
   delta = 0x100000000 * dfreq / refclk;  // Вычисление дельты
   
   //Настройка и запуск таймеров
   Setup_timer();
   pinMode(A0, INPUT);
}

void loop()
{
   PIND |= (1 << 4); //Для замера скорости
   //Сюда нужно вставить обработчик для изменения частоты
   //Например, 2 кнопки + и -, повешенные на выводы A0 и A1
   //Состояние кнопок будем проверять каждые 100 мкС
   //Или можно повесить переменник на A0 и читать аналоговые значения. Однако analogRead - дорогая операция, на порядок по сравненияю с digitalRead
   
   //Ниже реализация для переменного резистора
   if (inctmr++ == 127) { //Середина цикла по полному byte
      if ( abs(analogRead(A0) - setfreq) >= deltafreq ){
	 //Значение аналогового входа изменилось на deltafreq и более.
	 //Нужно пересчитать частоту
	 
	 //Сначала останавливаем прерывания по таймеру 2
	 TIMSK2 &= ~(1<<TOIE2);
	 //Переведем значение analogRead в частоту
	 setfreq = analogRead(A0);
	 dfreq = 10 + (setfreq/deltafreq)*2;    // Минимальная частота 10Гц, на каждые 15 пунктов частота увеличивается на 2Гц
	 delta = 0x100000000 * dfreq / refclk;  // Новое вычисление дельты
	 Serial.print("Current freq: ");
	 Serial.println(dfreq);
	 //Теперь можно запустить прерывания по таймеру 2
	 TIMSK2 |=  (1<<TOIE2); 
      }
   }  
}

//******************************************************************
// Настройка таймеров
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;
      }
   }
}

 

Efim
Offline
Зарегистрирован: 04.05.2018

Красиво.

Отображение текущей частоты только по сериал будет?

Предлагаю изменение частоты реализовать на энкодере, удобно и практично!

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

Efim пишет:

Отображение текущей частоты только по сериал будет?

Можно шкалу переменника отградуировать от 10 до 146. Она линейная.

Мне на станок нужно, там чем меньше наружу торчит, тем лучше :)

Efim
Offline
Зарегистрирован: 04.05.2018

Три сегмета + энкодер, как вариант.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

andryn пишет:

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

Нужно очень глубоко владеть темой, что-бы конструктивно покритиковать. Я владею поверхностно.  :) Из того, что и так все знают - мега 328 для этого  дела весьма неудобна. Удобен специализированный МК типа at90pwm3, который изначально предназначен для управления двигателями. Ну или таже стм32, тоже удобна. У неё на каждом таймере висит по 3 пары комплементарных выходов,  настраиваемые дидтаймы само собой. + так же аппаратная защита, когда по сигналу извне (например от датчика тока) все сигналы резко отключаются.

По программе -вроде ничего глаза не режет, единственно что сразу стало непонятно - раз уж вы храните массив во флеше, а не в оперативе , то какой глубойкий смысл хранить его четвертинку, и потом тратить дополнительные вычислительные ресурсы на восстановление полной синусоиды, когда можно хранить весь килобайт. Жалко что ли места, его ж там ещё "вагон")

SLKH
Offline
Зарегистрирован: 17.08.2015

На кой икс такая точность формирования синусоиды для трехфазного двигателя? Он всё равно не поймет и не оценит.

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

dimax пишет:

По программе -вроде ничего глаза не режет, единственно что сразу стало непонятно - раз уж вы храните массив во флеше, а не в оперативе , то какой глубойкий смысл хранить его четвертинку, и потом тратить дополнительные вычислительные ресурсы на восстановление полной синусоиды, когда можно хранить весь килобайт. Жалко что ли места, его ж там ещё "вагон")

Провел эксперимент по сравнению скорости чтения на эмуляторе.

Код такой:

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

const unsigned int 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 unsigned int sineP4RAM[]  = { //Константа для первой четверти синуса. 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
};
 

unsigned long inctmr;             // переменная для цикла
unsigned long tmr;		  // Начальное значение таймера
unsigned long tmrread;            // Время выполнения цикла

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

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

void loop()
{
   PIND |= (1 << 4); //Для замера скорости loop

   //Скорость чтения из SRAM
   inctmr = 0xFFFF;
   tmr = millis();
   while (inctmr--) {
      PORTB = sineP4RAM[inctmr & 0xFF];
   }
   tmrread = millis() - tmr;
   Serial.print("Time SRAM read: ");
   Serial.println(tmrread);

   //Скорость чтения из PROGMEM
   inctmr = 0xFFFF;
   tmr = millis();
   while (inctmr--) {
      PORTB = pgm_read_byte_near(sineP4 + (inctmr & 0xFF));
   }
   tmrread = millis() - tmr;
   Serial.print("Time PROGMEM read: ");
   Serial.println(tmrread);
}

Результат вот:

Т.е. чтение из PROGMEM процентов на 10 быстрее, чем из SRAM, при прочих равных.

SLKH пишет:

На кой икс такая точность формирования синусоиды для трехфазного двигателя? Он всё равно не поймет и не оценит.

Ну просто "just for fun". Или, чтобы развеять мифы, типа: "Из того, что и так все знают - мега 328 для этого  дела весьма неудобна. "

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

andryn пишет:

Провел эксперимент по сравнению скорости чтения на эмуляторе.

загрузил из любопытства в реальную дуню -соотношение 152/149. Уже не такая броская разница. Кстати хранить в массиве 8-битные данные в 16-битных переменных не слишком ли жирно? )

andryn пишет:

Или, чтобы развеять мифы, типа: "Из того, что и так все знают - мега 328 для этого  дела весьма неудобна. "

А что, удобна? Заняты все таймеры. Нет никаких защит -если вдруг эта штука от какой нибудь помехи повиснет, то всё сгорит..

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

dimax пишет:

Кстати хранить в массиве 8-битные данные в 16-битных переменных не слишком ли жирно? )

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

dimax пишет:

Заняты все таймеры.

Ну это-то не проблема. Они как раз для этого и нужны.

dimax пишет:

Нет никаких защит -если вдруг эта штука от какой нибудь помехи повиснет, то всё сгорит..

Даже если повиснет, то для того, чтобы полыхнула, нужно чтобы оба входа одного драйвера HIN и LIN оказались в HIGH. Такого состояния в программе нету. Для этого она должна зависнуть и сама перевести оба вывода в HIGH. Такое возможно?

С защитой, согласен. Надо подумать.

Logik
Offline
Зарегистрирован: 05.08.2014

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

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

signed char TabSin(unsigned char x)
{
const byte TabSin[64]={0, 3,  6,  9, 12, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49,
                       51, 54, 57, 60, 63, 65, 68, 71, 73, 76, 78, 81, 83, 85, 88, 90,
                       92, 94, 96, 98,100,102,104,106,107,109,111,112,113,115,116,117,
                      118,120,121,122,122,123,124,125,125,126,126,126,127,127,127};

signed char r=TabSin[((x&0x40)?-x-1:x)&0x3f];
 if(x&0x80)
   return -r;
 return r;
 
  
}

 

SLKH
Offline
Зарегистрирован: 17.08.2015

andryn пишет:

 

SLKH пишет:

На кой икс такая точность формирования синусоиды для трехфазного двигателя? Он всё равно не поймет и не оценит.

Ну просто "just for fun". Или, чтобы развеять мифы, типа: "Из того, что и так все знают - мега 328 для этого  дела весьма неудобна. "

Сдается мне, что получилась программа для эмулятора. А двигателю (реальному) вместо этой точности нужно ещё и напряжение менять в зависимости от частоты и нагрузки.

Efim
Offline
Зарегистрирован: 04.05.2018

SLKH

Скорость асинхронных двигателей регулируют частотой.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Efim пишет:

SLKH

Скорость асинхронных двигателей регулируют частотой.

А момент на валу?

Logik
Offline
Зарегистрирован: 05.08.2014

andriano пишет:

Efim пишет:

SLKH

Скорость асинхронных двигателей регулируют частотой.

А момент на валу?

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

Logik
Offline
Зарегистрирован: 05.08.2014

SLKH пишет:

нужно ещё и напряжение менять в зависимости от частоты и нагрузки.

То вобще нефиг делать. Каждый отсчет из синуса умножать на коэффициент амплитуды. Правда ШИМ 8 бит не позволит делать широкий диапазон.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Logik пишет:

Цитата:

А момент на валу?

максимальный пропорционален квадрату напряжения.

Т.е. ток здесь совсем ни при чем?

Наверное, мы с Вами в разных школах учили разные физики.

Logik
Offline
Зарегистрирован: 05.08.2014

И физику в разных и курс электрических машин в универе учил только один из нас.

 

Ссылка самая что нинаесть первая в гугле http://model.exponenta.ru/electro/0080.htm

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Logik, а Вы не заметили словосочетание "другое выражение" в приведенном Вами тексте? 

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

Но давайте вернемся к Вашей ссылке и посмотрим не на "другое", а на "исходное" выражение, которое приведено как раз в самом начале п.2.6., т.е. примерно полутора экранами выше процитированного Вами фрагмента:

M=CΦI2cosψ2.

это - исходное выражение. Да, при некоторых допущениях из него можно получить приведенное Вами. Но все таки "основа" и "следствие при определенных допущениях" - это немного разные вещи.

 

PS. У меня "по первой ссылке в Гугле" открывается это: http://tehinfor.ru/s_12/elektro_92.html (наверное, у нас с Вами не только школы и физики разные, но и Гуглы - тоже)

и, представьте, там приведена та же самая формула (и также в самом начале раздела):

M = CΦIφ 2cos φ2krs.gif krs.gif krs.gif (122)

т.е. выходит, наши с Вами "разные Гуглы" тоже придерживаются убеждения, что момент в первую очередь зависит от тока, а зависимость от напряжения - опосредованная и обусловлена рядом дополнительных допущений.

PPS. Что касается курса электрических машин, я его не проходил. Из нас готовили физиков, а не инженеров (хотя в дипломе стояло как раз "инженер-физик").

SLKH
Offline
Зарегистрирован: 17.08.2015

Efim пишет:

SLKH

Скорость асинхронных двигателей регулируют частотой.

В небольших пределах - да. 

Например, многократно разгоняли дизель-генераторы до 55 герц - выполнялась задача откачать 11 тысяч тонн воды, и хотелось поскорее закончить с этим скучным делом и перейти к коньяку.

 

А "от 10 до 150 Гц" - для реального двигателя одного изменения частоты недостаточно.

 

Efim
Offline
Зарегистрирован: 04.05.2018

SLKH

Вы понимаете что обмотка двигателя рассчитывается на определенное напряжение которое нельзя превышать? А уменьшение напряжения приведет к потере крутящего момента. Так вот крутящий момент если он нужен можно контролировать с помощью обратной связи. То есть на мотор устанавливается датчик оборотов и при их падение частота подымается. Естейственно все имеет свои пределы.

Efim
Offline
Зарегистрирован: 04.05.2018

SLKH

Часто встечал в нашей промышлености когда инженеры не берут в расчет ограничение по напряжению и не учитывают искровой зазор или рабочее напряжение деталей. Взять допустим обычное сопротивление, расчетное напряжение на нем не должно превышать 200 вольт. Так вот наши горе инженеры в импульсной технике после выпрямителя когда напряжение достигает амплитудного значения сети ставят одно сопротивление в цепях питания шим контроллера.  Что мы получаем? Пробой! Хотя значение сопротивления соответствует необходимому паденю тока. В таких случаях нужно ставить два последовательно соединенных сопротивления.

SLKH
Offline
Зарегистрирован: 17.08.2015

Efim пишет:

SLKH

Вы понимаете что обмотка двигателя рассчитывается на определенное напряжение которое нельзя превышать?

да.

SLKH
Offline
Зарегистрирован: 17.08.2015

Efim пишет:

SLKH

Часто встечал в нашей промышлености когда инженеры не берут в расчет ограничение по напряжению и не учитывают искровой зазор или рабочее напряжение деталей. Взять допустим обычное сопротивление, расчетное напряжение на нем не должно превышать 200 вольт. Так вот наши горе инженеры в импульсной технике после выпрямителя когда напряжение достигает амплитудного значения сети ставят одно сопротивление в цепях питания шим контроллера.  Что мы получаем? Пробой! Хотя значение сопротивления соответствует необходимому паденю тока. В таких случаях нужно ставить два последовательно соединенных сопротивления.

Т.е. вы совершили открытие "нельзя превышать максимально допустимые паспортные параметры" и решили поделиться со мной? Зря - я об этом знал раньше. 

Efim
Offline
Зарегистрирован: 04.05.2018

SLKH

Ну почему бы с хорошим человеком открытием не поделится.

Logik
Offline
Зарегистрирован: 05.08.2014

andriano пишет:

PPS. Что касается курса электрических машин, я его не проходил. Из нас готовили физиков, а не инженеров (хотя в дипломе стояло как раз "инженер-физик").

Хреново готовили. Как инженер (какой именно после тире опустим) вы не на прилагательное "основа" возле формулы должны бы смотреть, а прежде всего разобратся какие величины в ней присутствуют. А как физик - тем более.  Вы решили раз там ток - то оно, даже  не обратив внимание что там за индексы. А там I2 или Iφ2, - а это, цитирую из приведеного вами источника "силы тока в обмотке ротора I2". Это не ток во внешней цепи, это ток индуцированый в роторе. Применение ток ротора для регулировки момента на валу сомнительно ;) , его в АД с КЗ ротором даже померить нельзя, не то что регулировать. Помножив его на косинус фи получаем активную составляющую. А чемуж оно все равно? 

на основании закона Ома

fr_233_1.jpg

"следствие при определенных допущениях" - это закон Ома. Вы с ним  несогласны? E2 (эдс ротора) связана с E1(эдс статора)  через коэффициент трансформации - это закон электромагнитной индукции. Может не признаете такой?  А напряжение питания и E1 - это совсем просто, закон Кирхгофа, в т.ч. и почему там приблизительное равенство поясняет.  Так напряжение питания в формуле момента появляется один раз. Ну а второй - уже ясно, из Ф т.е. из магнитного потока. Не буду себя утруждать дальше, образования Вам оно не добавит, если уж у Вас оно так - то так,  а любой желающий по приведенной ссылке легко убедится сам. На том считаю нужным закончить культпросвет не по теме. Но дайте ответ на один простой вопрос - какой ВУЗ Вам выдал диплом? Это должно быть интересно тем кто решает куда поступить.

 

 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

andriano пишет:

Logik пишет:

Цитата:

А момент на валу?

максимальный пропорционален квадрату напряжения.

Т.е. ток здесь совсем ни при чем?

Наверное, мы с Вами в разных школах учили разные физики.

Влезу. Двигатель сделан так что не может менять активное и реактивное сопротивление обмоток(что-бы ток менять при постояном напряжении). Так что приходится регулировать момент напряжением. Но здесь тоже есть нюансы. Материал сердечника, частота, толщина провода обмотки и изоляции , частота наряжения. Теория это хорошо, а паспортные данные это не от фонаря писали.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

qwone пишет:

Двигатель сделан так что не может менять активное и реактивное сопротивление обмоток(что-бы ток менять при постояном напряжении). Так что приходится регулировать момент напряжением. Но здесь тоже есть нюансы. Материал сердечника, частота, толщина провода обмотки и изоляции , частота наряжения. Теория это хорошо, а паспортные данные это не от фонаря писали.

Так никто против этого и не возражает.

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

А из-за того, что, как Вы совершенно верно отметили, сопротивление имеет как активную, так и реактивную составляющую, для обеспечения одного и того же тока на разных частотах требуется разное напряжение.

Вон, Логик, как грамотный инженер, не понимает физического смысла явления, зато хорошо умеет пользоваться справочниками. И ему невдомек, что закон Ома - это не функдаментальный физический закон, а лишь закон эмпирический, который никогда не выполняется точно. Поэтому ему кажется, что подстановка закона Ома в уравнеие приводит к получению полностью эквивалентного уравнения, тогда как на самом деле она превращает точное уравнение в приближенное. Как раз потому, что не учитывает все сопутствующие факторы, о которых Вы написали: физические свойства метриала сердечника, частота приложенного напряжения и собственные частоты механичесой и электрической системы, толщина провода обмотки, ее материал и зависимость электропроводности этой обмотки от температуры и частоты и т.п.

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

Logik
Offline
Зарегистрирован: 05.08.2014

Излишняя точность — недостаток умаСказано не мной. Прятатся за нефундаментальность закона Ома не стоит, наифундаментальнейшая квантовая электродинамика в инженерном деле не применяется за ненадобностю и сложностю. Вместо неё применяются соответствующие уровню описания матмодели. И наиболее применимая - линейные цепи с законом Ома, гармоническими напряжениями и линейными материалами с нулевыми температурными коэффициентами. Этого достаточно для точности в единицы процентов. Хватает почти всем почти всегда. А для АД в допустимом диапазоне параметров - вооще всегда. Для повышения точности матмодель усложняют. Но никакое уточнение не отменяет закономерности линейной модели. Такие как момент пропорционален квадрату напряжения. Да, при нагреве до состояния плазмы или в сверхсильном поле и т.д. может быть не совсем так - так сами топите урановый лом в ртути ;) А нормальные люди в нормальных условиях законом Ома пользуются. 

ПС. Давайте завяжем с флудом! Был задан короткий вопрос и дан короткий ответ. Здесь больше нечего обсуждать, особенно в разделе программирования ;)

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

Efim пишет:

Отображение текущей частоты только по сериал будет?

Подумал и решил добавить. А то скучно как-то без индикатора.

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

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 inctmr;             // переменная для реализации таймера в loop
volatile unsigned int setfreq;    // переменная для хранения установленной частоты
const int deltafreq=15;		  // 15 пунктов изменения аналогового входа дают изменение частоты на 2Гц


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


//******************************************************************
//Для индикатора

//Подключение 74HC595
#define CLOCK_PIN  7  // SHCP отвечает за ШИМ. Когда 1 из данных забирается бит, 0 пропускается.Синхронизация потока. На Схеме 595 пин 11
#define DATA_PIN   8  // DS отвечает за поток битов(данные). На Схеме 595 пин 14
#define LATCH_PIN  12 //STCP  отвечает за так называемое защелкивание. Когда мы все свои биты передали, посылаем сигнал, что окончили передачу. На Схеме 595 пин 12

#define INDICATOR_DIGITS 4 //Количество разрядов индикатора

const int digitsPins[INDICATOR_DIGITS] = {A4, A3, A2, A1}; //Пины для управления разрядами индикатора

byte values[INDICATOR_DIGITS+1]; //Массив для значений индикатора по разрядам + 1 значение, соотв. разряду с точкой

byte currentDigit = 0; //Текущий отображаемый разряд индикатора
byte countDigitInication = 0; //Количество показов подряд одного разряда. Нужно, чтобы частота выключения была много меньше частоты свечения.


byte symbols[15] = { //Массив значений для различных символов, подключение q1->A,..q7->G,q0->DP, индикатор с общим катодом
   //ABCDEFG  //8-й разряд это точка(DP)
   B01111110, //0 
   B00110000, //1
   B01101101, //2
   B01111001, //3
   B00110011, //4
   B01011011, //5
   B01011111, //6
   B01110000, //7
   B01111111, //8
   B01111011, //9 
   B00000000, //пусто 
   B00000001, //- (минус) 
   B01001111, //E 
   B00000101, //r 
   B00110111  //H 
};

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

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

   dfreq = 50.0;                          //Частота работы по умолчанию
   setfreq = 300;			  //Значение аналогового пина, соответствующее 50Гц
   delta = 0x100000000 * dfreq / refclk;  // Вычисление дельты
   
   //Настройка и запуск таймеров
   Setup_timer();

   //Дя регулировки частоты
   pinMode(ADJ_PIN, INPUT);

   //Пины управления разрядами индикатора переведем в output
   byte i = INDICATOR_DIGITS;
   while (i--) {
      pinMode(digitsPins[i], OUTPUT);
      pinMode(digitsPins[i], HIGH);
   }
   calculateValue(dfreq); //Заполнить значение для индикатора частотой по умолчанию
}

void loop()
{
   PIND |= (1 << 4); //Для замера скорости
   //Сюда нужно вставить обработчик для изменения частоты
   //Например, 2 кнопки + и -, повешенные на выводы A0 и A1
   //Состояние кнопок будем проверять каждые 100 мкС
   //Или можно повесить переменник на A0 и читать аналоговые значения. Однако analogRead - дорогая операция, на порядок по сравненияю с digitalRead
   
   //Ниже реализация для переменного резистора
   if (inctmr++ == 127) { //Середина цикла по полному byte
      if ( abs(analogRead(ADJ_PIN) - setfreq) >= deltafreq ){
	 //Значение аналогового входа изменилось на deltafreq и более.
	 //Нужно пересчитать частоту
	 
	 //Сначала останавливаем прерывания по таймеру 2
	 TIMSK2 &= ~(1<<TOIE2);
	 //Переведем значение analogRead в частоту
	 setfreq = analogRead(ADJ_PIN);
	 dfreq = 10 + (setfreq/deltafreq)*2;    // Минимальная частота 10Гц, на каждые 15 пунктов частота увеличивается на 2Гц
	 delta = 0x100000000 * dfreq / refclk;  // Новое вычисление дельты
	 Serial.print("Current freq: ");
	 Serial.println(dfreq);
	 calculateValue(dfreq); //Заполнить значение для индикатора
	 //Теперь можно запустить прерывания по таймеру 2
	 TIMSK2 |=  (1<<TOIE2); 
      }
   }
   
   indicateValue();
}

//******************************************************************
// Настройка таймеров
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;
      }
   }
}


//******************************************************************
// Функция для работы с индикатором с общим катодом и сдвиговым регистром 74НС595
// Должна вызываться регулярно в loop
void indicateValue()
{
   if (!countDigitInication) {
      //
      countDigitInication++;
      return;
   }
   byte symbol = symbols[values[currentDigit]]; //Выбрать символ для отображения из таблицы символов
   if (values[INDICATOR_DIGITS] && values[INDICATOR_DIGITS] == currentDigit) { //Нужно поставить точку в текущем разряде
      symbol |= B10000000;
   }
   byte i = INDICATOR_DIGITS;
   //Погасить все разряды
   while (i--) {
      digitalWrite(digitsPins[i],  HIGH);
   }
   //Для начала записи в 74HC595 нужно подать 0 на STCP (открыть)
   digitalWrite(LATCH_PIN, LOW);
   // С данными выпускаем поток битов на DS  синхроннированные с SHCP
   shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, symbol);
   // Когда последний бит числа передали закрываем
   digitalWrite(LATCH_PIN, HIGH);
   
   // Зажечь нужный разряд
   digitalWrite(digitsPins[currentDigit], LOW);
   // Перейти к следующему разряду
   currentDigit++;
   currentDigit = currentDigit % INDICATOR_DIGITS;
}

// Функция для расчета значениий по разрядам. Вызывается только при изменении отображаемого значения. Точность по умолчанию 0.
void calculateValue(float val)
{
   //Точность по умолчанию равна 0
   calculateValue(val, 0);
}

void calculateValue(float val, byte prec)
{
   byte i = INDICATOR_DIGITS;
   //Зачистить все значения
   while (i--) {
      values[i] = 10;
   }
   
   long intval = (long)(val * pow(10, prec));
   
   //Проверим влезает точность и число в разряды индикатора
   if (prec >= INDICATOR_DIGITS || intval > (long)pow(10, INDICATOR_DIGITS)) {
      values[2] = 12;
      values[1] = 13;
      values[0] = 13;
      return;
   }
   
   values[INDICATOR_DIGITS] = prec; //Поставить точку в нужном месте
   
   //Если все влезло, распределим число по разрядам
   byte prevval = 10; //Значение предыдущего старшего разряда, чтобы убрать ведущие нули
   i = INDICATOR_DIGITS;
   while (i--) {
      values[i] = intval / pow(10, i);
      intval = intval - values[i] * pow(10, i);
      //Уберем ведущие нули
      if (prevval == 10 && values[i] == 0) {
	 values[i] = 10;
      }
      prevval = values[i];
   }
}

Ну и картинка, как все подкючено:

Efim
Offline
Зарегистрирован: 04.05.2018

andryn

То что надо)

dmitron1036
Offline
Зарегистрирован: 10.01.2016

Напомните мне,

изменение ШИМ на лету - у какой-нибудь ардуины есть?

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

dmitron1036 пишет:

Напомните мне,

изменение ШИМ на лету - у какой-нибудь ардуины есть?

А что вы хотите менять "на лету"? Частоту? Скважность? Включать-выключать? Что-то еще?

Что вы имеете ввиду под определением "на лету"?

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

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

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

step64 пишет:

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

Я тоже подумал о доп. фукциях, и решил освободить 4 пина, сделав индикатор частоты на двух HC595. В таком раскладе нужно всего 3 пина. Выводить можно до 8-ми разрядов.

На освободившиеся пины можно повесить старт/стоп, реверс и что-то еще. Вот картинка:

И код:

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

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 inctmr;             // переменная для реализации таймера в loop
volatile unsigned int setfreq;    // переменная для хранения установленной частоты
const int deltafreq=15;		  // 15 пунктов изменения аналогового входа дают изменение частоты на 2Гц


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


//******************************************************************
//Для индикатора

//Подключение 74HC595
#define CLOCK_PIN  7  // SHCP отвечает за ШИМ. Когда 1 из данных забирается бит, 0 пропускается.Синхронизация потока. На Схеме 595 пин 11
#define DATA_PIN   8  // DS отвечает за поток битов(данные). На Схеме 595 пин 14
#define LATCH_PIN  12 //STCP  отвечает за так называемое защелкивание. Когда мы все свои биты передали, посылаем сигнал, что окончили передачу. На Схеме 595 пин 12

#define INDICATOR_DIGITS 4 //Количество разрядов индикатора

const byte digits[INDICATOR_DIGITS] = {  //Массив значений для разрядов. Подключение q1->1 ... q4->4. Разряд зажигается низким уровнем.
   //1234   // Выводы 1,2,3,4. Если будет 8-разрядный индикатор, то нужно добатить еще 4 значения
   B11110111,
   B11101111,
   B11011111,
   B10111111
}; //Пины для управления разрядами индикатора

byte values[INDICATOR_DIGITS+1]; //Массив для значений индикатора по разрядам + 1 значение, соотв. разряду с точкой

byte currentDigit = 0; //Текущий отображаемый разряд индикатора
byte countDigitInication = 0; //Количество показов подряд одного разряда. Нужно, чтобы частота выключения была много меньше частоты свечения.


const byte symbols[15] = { //Массив значений для различных символов, подключение q1->A,..q7->G,q0->DP, индикатор с общим катодом
   //ABCDEFG  //8-й разряд это точка(DP)
   B01111110, //0 
   B00110000, //1
   B01101101, //2
   B01111001, //3
   B00110011, //4
   B01011011, //5
   B01011111, //6
   B01110000, //7
   B01111111, //8
   B01111011, //9 
   B00000000, //пусто 
   B00000001, //- (минус) 
   B01001111, //E 
   B00000101, //r 
   B00110111  //H 
};

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

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

   dfreq = 50.0;                          //Частота работы по умолчанию
   setfreq = 300;			  //Значение аналогового пина, соответствующее 50Гц
   delta = 0x100000000 * dfreq / refclk;  // Вычисление дельты
   
   //Настройка и запуск таймеров
   Setup_timer();

   //Дя регулировки частоты
   pinMode(ADJ_PIN, INPUT);

   calculateValue(dfreq); //Заполнить значение для индикатора частотой по умолчанию
}

void loop()
{
   PIND |= (1 << 4); //Для замера скорости
   //Сюда нужно вставить обработчик для изменения частоты
   //Например, 2 кнопки + и -, повешенные на выводы A0 и A1
   //Состояние кнопок будем проверять каждые 100 мкС
   //Или можно повесить переменник на A0 и читать аналоговые значения. Однако analogRead - дорогая операция, на порядок по сравненияю с digitalRead
   
   //Ниже реализация для переменного резистора
   if (inctmr++ == 127) { //Середина цикла по полному byte
      if ( abs(analogRead(ADJ_PIN) - setfreq) >= deltafreq ){
	 //Значение аналогового входа изменилось на deltafreq и более.
	 //Нужно пересчитать частоту
	 
	 //Сначала останавливаем прерывания по таймеру 2
	 TIMSK2 &= ~(1<<TOIE2);
	 //Переведем значение analogRead в частоту
	 setfreq = analogRead(ADJ_PIN);
	 dfreq = 10 + (setfreq/deltafreq)*2;    // Минимальная частота 10Гц, на каждые 15 пунктов частота увеличивается на 2Гц
	 delta = 0x100000000 * dfreq / refclk;  // Новое вычисление дельты
	 Serial.print("Current freq: ");
	 Serial.println(dfreq);
	 calculateValue(dfreq); //Заполнить значение для индикатора
	 //Теперь можно запустить прерывания по таймеру 2
	 TIMSK2 |=  (1<<TOIE2); 
      }
   }
   
   indicateValue();
}

//******************************************************************
// Настройка таймеров
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;
      }
   }
}


//******************************************************************
// Функция для работы с индикатором с общим катодом и сдвиговым регистром 74НС595
// Должна вызываться регулярно в loop
void indicateValue()
{
   if (!countDigitInication) {
      //
      countDigitInication++;
      return;
   }
   byte symbol = symbols[values[currentDigit]]; //Выбрать символ для отображения из таблицы символов
   if (values[INDICATOR_DIGITS] && values[INDICATOR_DIGITS] == currentDigit) { //Нужно поставить точку в текущем разряде
      symbol |= B10000000;
   }
   //Погасить все разряды
   shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, B11111111); //Разряды
   shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, B00000000); // Символы

   //Для начала записи в 74HC595 нужно подать 0 на STCP (открыть)
   digitalWrite(LATCH_PIN, LOW);

   // С данными выпускаем поток битов на DS  синхроннированные с SHCP
   // Зажечь нужный разряд
   shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, digits[currentDigit]);
   // Показать нужный символ
   shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, symbol);
   // Когда последний бит передали, закрываем
   digitalWrite(LATCH_PIN, HIGH);
   
   // Перейти к следующему разряду
   currentDigit++;
   currentDigit = currentDigit % INDICATOR_DIGITS;
}

// Функция для расчета значениий по разрядам. Вызывается только при изменении отображаемого значения. Точность по умолчанию 0.
void calculateValue(float val)
{
   //Точность по умолчанию равна 0
   calculateValue(val, 0);
}

void calculateValue(float val, byte prec)
{
   byte i = INDICATOR_DIGITS;
   //Зачистить все значения
   while (i--) {
      values[i] = 10;
   }
   
   long intval = (long)(val * pow(10, prec));
   
   //Проверим влезает точность и число в разряды индикатора
   if (prec >= INDICATOR_DIGITS || intval > (long)pow(10, INDICATOR_DIGITS)) {
      values[2] = 12;
      values[1] = 13;
      values[0] = 13;
      return;
   }
   
   values[INDICATOR_DIGITS] = prec; //Поставить точку в нужном месте
   
   //Если все влезло, распределим число по разрядам
   byte prevval = 10; //Значение предыдущего старшего разряда, чтобы убрать ведущие нули
   i = INDICATOR_DIGITS;
   while (i--) {
      values[i] = intval / pow(10, i);
      intval = intval - values[i] * pow(10, i);
      //Уберем ведущие нули
      if (prevval == 10 && values[i] == 0) {
	 values[i] = 10;
      }
      prevval = values[i];
   }
}

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

 

Efim
Offline
Зарегистрирован: 04.05.2018

Если хотите освободить пины то можно воспользоватся индикаторами типа К490ИП1

Понадобится всего один вывод, индикатор имеет сдвиговый регистр для вывода на следующий элемент.

Микросхемы десятичного счетчика знакосинтезирующего индикатора. Содержит КМОП-схему управления со счетчиком импульсов и семисегментный знакосинтезирующий индикатор с децимальной точкой. Применяются для счета по модулю 10 импульсов положительной полярности, отображения их числа в виде цифр от 0 до 9 и формирования импульса переноса на старший разряд. 
Выпускаются в пластмассовом корпусе. 
Высота знака 2,5 мм. 
Масса прибора не более 1,5 г. 

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

http://dotdisplay.blogspot.com/2017/12/490ip1-russian-led-display-for-avionics.html

SLKH
Offline
Зарегистрирован: 17.08.2015

andryn пишет:

 Надо попробовать в железе.

 

"О, сколько нам открытий чудных..."

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

Как движется мега-проект?)  Могу предложить задействовать свободные пины так:  1й свободный пин  – тумблер вкл/выкл(старт/стоп);  2й свободный пин с ацп – задавать время разгона от 0 до выставленной частоты; 3й свободный пин – команда стоп при подаче на него сигнала с датчика постоянного тока с шины плюса питания(по хорошему нужен датчик тока на каждую фазу, но это же мега 328... ).  4й свободный пин – тумблер вперд /назад (можно сделать аппаратно на 2х контакторах). И расширить диапазон регулирования до Герц 600 хотя бы. А ещё было бы супер заменить симисигментники на жк 4*20 и выводить напряжение постоянное и ток общий и каждой фазы, но это уже мега 328 не осилит, надо стм32f103 хотя бы.

 

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

step64 пишет:

Как движется мега-проект?)  

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

Развел печатную плату.

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

В железе еще не проверял, тяжко со временем.

Схема одной фазы такая:

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

Можете выложить код последней Вашей версии частотника? Думаю собрать, но переделать под экран 1602, а не семисигментники и "разрешение" синуса уменьшить, дабы контроллер разгрузить немного под другие нужды. Может подскажите, насколько ресурсоёмкий и влияет на общее быстродействие вывод инфы на экран 1602  в отличии от семисигментников или это без разницы?

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

step64 пишет:

Можете выложить код последней Вашей версии частотника? 

Нет, проблем. Код ниже. Однако, оказалось, что динамическая индикация ведет себя плохо. Поэтому ее буду переделывать на что-то вроде TM1637. Для подключения экрана 1602 нужно 6 свободных пинов. По скорости - нужно попробовать.

Текущий вариант кода:

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

#include "SevenSegmentDigitsDisplay.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

//Управление частотой
unsigned int setfreq;    // переменная для хранения предыдущего измерения заначения пина
const int deltafreq=15;		  // 15 пунктов изменения аналогового входа дают изменение частоты на 2Гц


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

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


//******************************************************************
//Для индикатора
SevenSegmentDigitsDisplay disp(8, 7, 4);

//******************************************************************
// Настройка таймеров
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 - выход

   dfreq = 9;                             //Частота работы по умолчанию
   delta = 0x100000000 * dfreq / refclk;  // Вычисление дельты
   
   //Настройка и остановка таймеров
   Setup_timer();

   //Дя регулировки частоты
   pinMode(ADJ_PIN, INPUT);
   
   //Кнопка старта
   pinMode(BTN_START_PIN, INPUT_PULLUP);
   disp.setDiditValue(0, 15); //Заполнить значение для индикатора частотой по умолчанию
}

void loop()
{
   //PIND |= (1 << 2); //Для замера скорости
   
   //Реализация для изменения частоты переменным резистором
   if ((inctmr++) % 64 == 0) { //Обрабатывается каждый 64-й вызов loop. Можно, наверно, увеличить.
      //Изменили сопротивление резистора
      if ( abs(analogRead(ADJ_PIN) - setfreq) >= deltafreq ){
	 //Значение аналогового входа изменилось на deltafreq и более.
	 //Нужно пересчитать частоту

	 //Переведем значение analogRead в частоту
	 setfreq = analogRead(ADJ_PIN);
	 dfreq_set = 10 + 2*(setfreq/deltafreq);    // Минимальная частота 10Гц, на каждые 15 пунктов частота увеличивается на 2Гц
	 Serial.print("Current setting freq: ");
	 Serial.println(dfreq_set);
	 disp.calculateValue(dfreq_set); //Заполнить значение для индикатора
      }
      
      //Нажатие кнопки
      if ( digitalRead(BTN_START_PIN) == LOW && (run & B00000001) ){ //Нажата кнопка и нужно менять состояние
	 Serial.print("Button press");
	 if ((run & B00000010)) { //Надо остановить
	    TIMSK2 &= ~(1<<TOIE2); //остановка таймера
	    run = B00000000;       //Изменение состояния на выкл и запрет обработки кнопки
	 }
	 else { 
	    //Запустить прерывания по таймеру 2
	    dfreq = 9; //Минимальная частота для увеличения до рабочей
	    TIMSK2 |=  (1<<TOIE2);
	    run = B00000010;	//Изменение состояния на вкл и запрет обработки кнопки
	 }
      }
      else if (digitalRead(BTN_START_PIN) == HIGH) { //кнопка отпущена
	 run |= B00000001; //Кнопку можно снова обрабатывать
      }
   }

   if (dfreq != dfreq_set && (run & B00000010) && inctmr % 16 == 0) { //Плавное увеличение частоты, если нужная частота больше текущей. Резкое изменение, если меньше
      //Сначала останавливаем прерывания по таймеру 2
      TIMSK2 &= ~(1<<TOIE2);
      if (dfreq < dfreq_set) {
	 dfreq++; //Увеличели частоту
      }
      else {
	 dfreq = dfreq_set;
      }
      Serial.print("!the freq: ");
      Serial.println(dfreq);
      delta = 0x100000000 * dfreq / refclk;  // Новое вычисление дельты
      //Теперь можно запустить прерывания по таймеру 2
      TIMSK2 |=  (1<<TOIE2); 
   }
   
   disp.indicateValue(); //Обновить экран
}

 

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

andryn пишет:

 Для подключения экрана 1602 нужно 6 свободных пинов. По скорости - нужно попробовать.

Спасибо за код, потестирую как появится время. Дисплей по i2c можно подключить:)  Опять же всё в ресурс меги 328 упирается, надо тестить...  Эх, вот бы эту программу да на стм32 переписать.

step64
Offline
Зарегистрирован: 01.04.2013
 
Прогнал ваш последний код, только поудалял из него всё от семисигментников, печально что частоту нельзя в реальном времени регулировать, а только при перезагрузке ардуины(( Плавный разгон порадовал. Возможно ли вообще в этом коде реализовать изменение частоты в реальном времени?  Частота шима 61,5 кГц не многовато ли?
andryn
Offline
Зарегистрирован: 08.06.2018

step64 пишет:

печально что частоту нельзя в реальном времени регулировать, а только при перезагрузке ардуины((
Почему нельзя? Вроде в протеусе работает. Переменник, подключенный к ADJ_PIN регулирует частоту.
В этой стороке обработка:
if ( abs(analogRead(ADJ_PIN) - setfreq) >= deltafreq )
 
Сейчас как раз код на TM1637 переделываю, проверю еще раз.
 
nevkon
Offline
Зарегистрирован: 20.01.2015

На реальном двигателе проверяли?

Есть ПЧ заводские, там целая туча настроек, в том числе и зависимость напряжения от частоты и защита по превышению тока. Напряжение можно регулировать одним ШИМ каналом со сглаживающим выходом.

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

Переделал на индикатор TM1637. Работает регулировка частоты и кнопка старт/стоп.

Схема такая:

Код:

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

#include "TM1637.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=15;                 // 15 пунктов изменения аналогового входа дают изменение частоты на 2Гц
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);
//******************************************************************
// Настройка таймеров
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(); //Очистить
}

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

    //Реализация для изменения частоты переменным резистором
    if ((inctmr++) % 512 == 0) { //Обрабатывается каждый 512-й вызов loop. Можно, наверно, увеличить.
        adj = (analogRead(ADJ_PIN)+analogRead(ADJ_PIN)+analogRead(ADJ_PIN)+analogRead(ADJ_PIN))/4; //Усредненное значение на пине регулировки частоты по 4-м чтениям
        //Изменили сопротивление резистора        
        if ( abs(adj - setfreq) >= deltafreq ){
            //Значение аналогового входа изменилось на deltafreq и более.
            //Нужно пересчитать частоту

            //Переведем значение analogRead в частоту
            setfreq = adj;
            dfreq_set = 10 + 2*(setfreq/deltafreq);    // Минимальная частота 10Гц, на каждые 15 пунктов частота увеличивается на 2Гц
            Serial.print("Current setting freq: ");
            Serial.println(dfreq_set);
            //Заполнить значение для индикатора частотой dfreq_set. Вывод: 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);
        }
      
        //Нажатие кнопки
        if ( digitalRead(BTN_START_PIN) == LOW && (run & B00000001) ){ //Нажата кнопка и нужно менять состояние
            Serial.print("Button press");
            if ((run & B00000010)) { //Надо остановить
                TIMSK2 &= ~(1<<TOIE2); //остановка таймера
                //Вывод на экран 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);
                run = B00000000;       //Изменение состояния на выкл и запрет обработки кнопки
            }
            else { 
                //Запустить прерывания по таймеру 2
                dfreq = 9; //Минимальная частота для увеличения до рабочей
                TIMSK2 |=  (1<<TOIE2);
                run = B00000010;  //Изменение состояния на вкл и запрет обработки кнопки
            }
        }
        else if (digitalRead(BTN_START_PIN) == HIGH) { //кнопка отпущена
            run |= B00000001; //Кнопку можно снова обрабатывать
        }
    }

    if (dfreq != dfreq_set && (run & B00000010) && inctmr % 128 == 0) { //Плавное увеличение частоты, если нужная частота больше текущей. Резкое изменение, если меньше
        //Сначала останавливаем прерывания по таймеру 2
        TIMSK2 &= ~(1<<TOIE2);
        if (dfreq < dfreq_set) {
            dfreq++; //Увеличели частоту
        }
        else {
            dfreq = dfreq_set;
        }
        Serial.print("!the freq: ");
        Serial.println(dfreq);
        //Вывод на экран C + частота
        disp.display(0,12);
        disp.display(1,(dfreq%1000)/100);
        disp.display(2,(dfreq%100)/10);
        disp.display(3,dfreq%10);
        delta = 0x100000000 * dfreq / refclk;  // Новое вычисление дельты
        
        //Теперь можно запустить прерывания по таймеру 2
        TIMSK2 |=  (1<<TOIE2); 
    }   
}

 

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

step64 пишет:

Дисплей по i2c можно подключить:)  

Пока уж взялся за переделку, попробовал подключить 1602 через i2c. В первом приближении вроде работает.

Схема:

Код:

/* 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=15;                 // 15 пунктов изменения аналогового входа дают изменение частоты на 2Гц
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++) % 512 == 0) { //Обрабатывается каждый 512-й вызов loop. Можно, наверно, увеличить.
        adj = (analogRead(ADJ_PIN)+analogRead(ADJ_PIN)+analogRead(ADJ_PIN)+analogRead(ADJ_PIN))/4; //Усредненное значение на пине регулировки частоты по 4-м чтениям
        //Изменили сопротивление резистора        
        if ( abs(adj - setfreq) >= deltafreq ){
            //Значение аналогового входа изменилось на deltafreq и более.
            //Нужно пересчитать частоту

            //Переведем значение analogRead в частоту
            setfreq = adj;
            dfreq_set = 10 + 2*(setfreq/deltafreq);    // Минимальная частота 10Гц, на каждые 15 пунктов частота увеличивается на 2Гц
            Serial.print("Current setting freq: ");
            Serial.println(dfreq_set);
            //Заполнить значение для индикатора частотой dfreq_set. Вывод: 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, 0);                      // Устанавливаем курсор в начало 1 строки
            lcd.print("Set freq:       ");            // Выводим текст
            lcd.setCursor(10, 0);                     // Устанавливаем курсор в 10 позицию
            lcd.print(dfreq_set);                     // Выводим текст

        }
      
        //Нажатие кнопки
        if ( digitalRead(BTN_START_PIN) == LOW && (run & B00000001) ){ //Нажата кнопка и нужно менять состояние
            Serial.print("Button press");
            if ((run & B00000010)) { //Надо остановить
                TIMSK2 &= ~(1<<TOIE2); //остановка таймера
                //Вывод на экран 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 { 
                //Запустить прерывания по таймеру 2
                dfreq = 9; //Минимальная частота для увеличения до рабочей
                TIMSK2 |=  (1<<TOIE2);
                run = B00000010;  //Изменение состояния на вкл и запрет обработки кнопки
            }
        }
        else if (digitalRead(BTN_START_PIN) == HIGH) { //кнопка отпущена
            run |= B00000001; //Кнопку можно снова обрабатывать
        }
    }

    if (dfreq != dfreq_set && (run & B00000010) && inctmr % 128 == 0) { //Плавное увеличение частоты, если нужная частота больше текущей. Резкое изменение, если меньше
        //Сначала останавливаем прерывания по таймеру 2
        TIMSK2 &= ~(1<<TOIE2);
        if (dfreq < dfreq_set) {
            dfreq++; //Увеличели частоту
        }
        else {
            dfreq = dfreq_set;
        }
        delta = 0x100000000 * dfreq / refclk;  // Новое вычисление дельты
        

        Serial.print("!the freq: ");
        Serial.println(dfreq);
        //Вывод на экран C + частота
        disp.display(0,12);
        disp.display(1,(dfreq%1000)/100);
        disp.display(2,(dfreq%100)/10);
        disp.display(3,dfreq%10);
        //Заполнить значение для LCD частотой dfreq Вывод: Set freq: + частота
        lcd.setCursor(0, 1);                  // Устанавливаем курсор в начало 1 строки
        lcd.print("Current freq:  ");        // Выводим текст
        lcd.setCursor(13, 1);                 // Устанавливаем курсор в 10 позицию
        lcd.print(dfreq);                     // Выводим текст

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

    }   
}

Надо попробовать на железяке, но у меня, по-моему, нету 1602 с i2c.

Индикация на TM1637 работает нормально, проверил.

mrtester
Offline
Зарегистрирован: 26.02.2015

Интересная тема можно что то вроде этого поставить STK5F1U3E2D-E и защиту по току и температуре не сложно сделать.

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

В принципе можно и без i2c подключить 1602 по 6 проводам, портов хватает, к тому же можно аналоговые входы использовать как цифровые порты ввода/вывода.  В предыдущем вашем коде я подал питание на ардуино, выставил частоту переменником, нажимал кнопку - пошла генерация на заданной частоте, но во время работы частота не менялась переменником, осцилом контролирую всё, у вас в железе всё работает как надо? И ещё не помешало бы организовать самую простую защиту, в начале цикла дописать : if digitalread свободный пин == 1 , то генерация запустилась, иначе аварийная остановка и надпись на экране Over Current .  Можно и нужно на каждую фазу и шину питания организовать самую простую тригерную защиту, защита сработала - логическая 1 на контроллер и аварийная остановка генерации до перезапуска. Хотя проще STK5Q4U362J-E и им подобные модули юзать, но их надо покупать и стоят они не дёшево((  Погоняю ещё на днях ваш код с экраном 1602 без i2c и без сериала, борьба за ресурс контроллера для защиты как бы:))

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

step64 пишет:

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

Последний код http://arduino.ru/forum/programmirovanie/preobrazovatel-chastoty-dlya-3k... загрузил в UNO, подключил 1602 по i2c и TM1637, кнопку, переменник. Первое нажатие кнопки - старт, второе стоп и т.д. На 1602 отображается установленная частота и текущая, в процессе увеличения. Регулировка частоты вроде в обоих режимах(старт и стоп) работает.

Осциллограф лень было доставать, поэтому за генерацией не смотрел, но на эмуляторе генерация идет нормально. Постараюсь в ближайшее время доделать "высоковольтную"(12В или 24В) часть и проверить на моторе.

and@rey
Offline
Зарегистрирован: 11.09.2018

Здравствуйте Все!
Andryn,как дела в железе?

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

and@rey пишет:
Здравствуйте Все! Andryn,как дела в железе?

С железом пока не очень.

Протестировал без силовой части, нашел, что, как и писал step64, динамически частота регулируется странно и нестабильно. Поправил немного код, теперь вроде нормально:

--- ..\3PhaseFreq2-old\3PhaseFreq2.ino	2018-09-05 11:10:34.219816500 +0300
+++ 3PhaseFreq2.ino	2018-09-11 01:22:08.047006700 +0300
@@ -289,7 +289,10 @@
         if ( abs(adj - setfreq) >= deltafreq ){
             //Значение аналогового входа изменилось на deltafreq и более.
             //Нужно пересчитать частоту
-
+            
+            //Сначала останавливаем прерывания по таймеру 2, иначе не работает
+            TIMSK2 &= ~(1<<TOIE2);
+            
             //Переведем значение analogRead в частоту
             setfreq = adj;
             dfreq_set = 10 + 2*(setfreq/deltafreq);    // Минимальная частота 10Гц, на каждые 15 пунктов частота увеличивается на 2Гц

С силовой частью пока вот так:

  

Еще не допаял :)