Генератор меандра с изменяемой скважностью сигнала

Begemot
Offline
Зарегистрирован: 28.04.2016

Здравствуйте, задача проекта: нужен генератор сигнала, в котором пачки импульсов с частотой 32 кГц генерируются с частотой 15 Гц. Длительность импульсов в пачке 400 Нс.

генерацию сигнала написал используя материалы форума:

boolean t=0;
boolean t1=0;
  
void setup()
{
    Serial.begin(115200);
pinMode(11, OUTPUT);
pinMode(3, OUTPUT);
pinMode(13, OUTPUT);
pinMode(9, OUTPUT);
pinMode(5, OUTPUT);
static uint32_t enc;
  uint32_t ocr; uint32_t divider; float freq;
  
//------ Timer1 ----------
TCCR1A=0;
TCCR1B=0;

TCCR1A |=1<<COM1A0;
TCCR1B |= (1<<WGM12);    // Режим CTC (сброс по совпадению)

//расчёт прескалера и OCR по нужной частоте
   
   enc=25;
  
   Serial.println(F_CPU);
   divider=1; ocr = (F_CPU / enc /2 /divider);
   if (ocr >65536) { divider=8; ocr = F_CPU / enc /2 /divider;
       if (ocr >65536) { divider=64; ocr = F_CPU / enc /2 /divider;
           if (ocr >65536)  {divider=256; ocr = F_CPU / enc /2 /divider;
               if (ocr >65536) { divider=1024; ocr = F_CPU / enc /2 /divider;
                   if (ocr >65536){ocr=65536; }}}}} OCR1A=ocr-1; 
  //запись в регистр прескалера            
   switch (divider) {
     case 1: TCCR1B=1|(1<<WGM12); break;
      case 8: TCCR1B=2|(1<<WGM12); break;
       case 64: TCCR1B=3|(1<<WGM12); break;
        case 256: TCCR1B=4|(1<<WGM12); break;
         case 1024: TCCR1B=5|(1<<WGM12); break;  }
freq= (float) F_CPU/2 / (OCR1A+1) /divider;
      if (freq <10000) { Serial.print(freq,1);Serial.println(" Hz "); }
      if (freq >10000) { Serial.print(freq/1000,3);Serial.println(" kHz");}

TIMSK1 = (1<<OCIE1A);   // Разрешить прерывание по совпадению



//---------Timer 2-------
TCCR2A = 0;// set entire TCCR2A register to 0
TCCR2B = 0;// same for TCCR2B
TCNT2  = 0;



TCCR2A |=1<<COM2A0;
TCCR2B |= (1<<WGM12);

enc=3200;
   Serial.println(F_CPU);
   divider=1; ocr = (F_CPU / enc /2 /divider);
   if (ocr >255) { divider=8; ocr = F_CPU / enc /2 /divider;
    if (ocr >255) { divider=32; ocr = F_CPU / enc /2 /divider;
       if (ocr >255) { divider=64; ocr = F_CPU / enc /2 /divider;
        if (ocr >255) { divider=128; ocr = F_CPU / enc /2 /divider;
           if (ocr >255)  {divider=256; ocr = F_CPU / enc /2 /divider;
               if (ocr >255) { divider=1024; ocr = F_CPU / enc /2 /divider;
                   if (ocr >255){ocr=256; }}}}}}} OCR2A=ocr-1; 
  //запись в регистр прескалера            
freq= (float) F_CPU/2 / (OCR2A+1) /divider;
      if (freq <10000) { Serial.print(freq,1);Serial.println(" Hz "); }
      if (freq >10000) { Serial.print(freq/1000,3);Serial.println(" kHz");}
Serial.println(divider);
 switch (divider) {
     case 1: TCCR2B = (1<<CS20); break;
      case 8: TCCR2B = (1<<CS21); break;
       case 32: TCCR2B = (1<<CS20)|(1<<CS21); break;
         case 64: TCCR2B = (1<<CS22); break;
           case 128: TCCR2B = (1<<CS20)|(1<<CS22); break;
            case 256: TCCR2B = (1<<CS21)|(1<<CS22); break;
             case 1024: TCCR2B = (1<<CS20)|(1<<CS21)|(1<<CS22); break;  }



//TIMSK2 = (1<<OCIE2A);   // Разрешить прерывание по совпадению

sei ();                 // Глобально разрешить прерывания
}



ISR (TIMER1_COMPA_vect)
{
  t1=!t1;
 TCCR2A = (t1<<COM2A0);
}
void loop() {
  // put your main code here, to run repeatedly:

}

Таймер 2 не верно работает с частотой, например, вместо 3200 выдает 976, как изменить длительность более чем 1/255 не знаю.

когда делаю другим способом

boolean t=0;
 uint16_t x = 3200;
 float y = 0.065*5;
void setup() {
Serial.begin(9600);
pinMode(9,OUTPUT);
TCCR1A=(1<<COM1A1)|(1<<WGM11);
TCCR1B=(1<<WGM13)|(1<<WGM12)|(1<<CS10);
OCR1A=0; ICR1=0;

   ICR1= (F_CPU/x)-1; 
   OCR1A=  ((float) y / 0.0625 ) -1  ; 
}

void loop() {
 t=!t;
TCCR1A=t<<COM1A0;

delay(20);
 }

строчка TCCR1A=t<<COM1A0; убивает изменение длительности, она становится равной 50% и частота вместо 3200 Гц, становится 1600 Гц. 

Как исправить изменение частоты в первом случае и как добиться требуемой скважности и частоты во втором?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Begemot пишет:

Генератор меандра с изменяемой скважностью сигнала

Так не бывает. Скважность меандра всегда равна двум и изменяться не может.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ни в коей мере не критикую (т.к. не моё дело) но, возможно, Вы опечатались. Если Ваш ник должен означать библейское Чудовище, то правильное написание "Behemoth".

Begemot
Offline
Зарегистрирован: 28.04.2016

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

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

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Begemot пишет:

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

:-))))

Ну, наверняка навеяно булгаковским котярой! Такой милый персонаж! 

– Это водка? – слабо спросила Маргарита.
Кот подпрыгнул на стуле от обиды.
– Помилуйте, королева, – прохрипел он, – разве я позволил бы себе налить даме водки? Это чистый спирт!
 
:)))))
ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

самый простой способ - две ардуины на логический элемент И )))

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

Begemot пишет:

строчка TCCR1A=t<<COM1A0; убивает изменение длительности, она становится равной 50% и частота вместо 3200 Гц, становится 1600 Гц. 

Похоже не знакомы с битовыми операциями, лучше не откладывать это, а выучить сразу как таблицу умножения. TCCR1A= Этой командой вы сбрасываете ранее сделанные настройки регистра конфигурации таймера. В данном случае удобно использовать команду инверсии бита, тогда и t=!t не понадобится.

Begemot
Offline
Зарегистрирован: 28.04.2016

dimax пишет:

 Похоже не знакомы с битовыми операциями, лучше не откладывать это, а выучить сразу как таблицу умножения. TCCR1A= Этой командой вы сбрасываете ранее сделанные настройки регистра конфигурации таймера. В данном случае удобно использовать команду инверсии бита, тогда и t=!t не понадобится.

нет возможности проверить сейчас, но следует писать "TCCR1A=TCCR1A^COM1A0;" ? про регистры смотрел на http://www.gaw.ru/html.cgi/txt/doc/micros/avr/arh/mega103_38.htm, но как правильно битовые операции записывать в arduino ide? не могли бы помочь справочными материалами? что долговечнее будет - использовать ардуино в таком режиме генератора или осбрать генератор на логических элементах? 

 

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

Begemot, нет. Правильно будет "TCCR1A^=1<<COM1A0;"  Чтива везде полно, ищите по словам "битовые операции" "двоичные операции" "булева арифметика".  Как лучше сделать -только вам решать, т.к. нам неизвестно что вы хотите получить, информация в топике не позволяет определить лучший метод, т.к. непонятно для чего это, нужно ли управление генерацией, какая требуется точность, итп.   Лично я бы в общем случае взял тини13 и написал код на ассемблере, что вышло бы по-любому компактнее и проще схемотехнически, чем на "логических элементах"  :)

Begemot
Offline
Зарегистрирован: 28.04.2016

dimax пишет:

Begemot, нет. Правильно будет "TCCR1A^=1<<COM1A0;"  Чтива везде полно, ищите по словам "битовые операции" "двоичные операции" "булева арифметика".  Как лучше сделать -только вам решать, т.к. нам неизвестно что вы хотите получить, информация в топике не позволяет определить лучший метод, т.к. непонятно для чего это, нужно ли управление генерацией, какая требуется точность, итп.   Лично я бы в общем случае взял тини13 и написал код на ассемблере, что вышло бы по-любому компактнее и проще схемотехнически, чем на "логических элементах"  :)

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

Использовал команду "TCCR1A=TCCR1A^COM1A0;" и получил то, что нужно, привожу примеры сигналов, скважность увеличил для наглядности.

С вашей командой  "TCCR1A^=1<<COM1A0;" получился вот такой сигнал, получается инверсия работы, а не отключение выводов.

Привожу окончательный вариант программы.

boolean t=0;
 uint16_t x = 3200;
void setup() {
pinMode(9,OUTPUT);
TCCR1A=(1<<COM1A1)|(1<<WGM11);
TCCR1B=(1<<WGM13)|(1<<WGM12)|(1<<CS10);
OCR1A=0; ICR1=0;

   ICR1= (F_CPU/x)-1; //частота
   OCR1A=  4  ; //длительность
}

void loop() {
TCCR1A=TCCR1A^COM1A0;//отключаем 9 вывод

delay(20);
 }

И спасибо Вам за помощь и за те программы, что выкладываете.

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

Begemot пишет:

Использовал команду "TCCR1A=TCCR1A^COM1A0;" и получил то, что нужно, привожу примеры сигналов, скважность увеличил для наглядности. С вашей командой  "TCCR1A^=1<<COM1A0;" получился вот такой сигнал, получается инверсия работы, а не отключение выводов.

С "моей" командой происходит ровно то действие которое хотели вы -то есть инверсия бита COM1A0. То, что это не давало нужный результат -следствие вашей же ошибки, т.к. инвертировать нужно было тот бит, который устанавливался ещё в сетапе -т.е. COM1A1. Т.е. то, что у вас заработало -лишь следствие случайного везения. Фактически вы не коммутировали пин, как хотели, а делали  операцию исключающее ИЛИ  одновременно с 1(WGM11) и 2(не используется) битом, тем самым вызывая отключение бита WGM11, что в свою очередь переключало таймер в другой режим :) Вряд ли в другой раз вам так же улыбнётся удача.. так что двоичные операции нужно подучить :)

Begemot
Offline
Зарегистрирован: 28.04.2016

[quote=dimax]

/quote]

Увы, я неправильно понял описание COM1A0:(

Теперь всё работает как надо, но на осцилограмме проскальзывают вот такие импульсы

[

Не подскажете почему так происходит и как избавиться?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Begemot пишет:

Не подскажете почему так происходит и как избавиться?

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

Избавиться? Ну, не знаю, есть 100500 способов. По мне так, лучше всего сделать собственную службу времени и отказаться от ардуиновского сервиса времени на нулевом таймере.

Begemot
Offline
Зарегистрирован: 28.04.2016

ЕвгенийП пишет:

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

Избавиться? Ну, не знаю, есть 100500 способов. По мне так, лучше всего сделать собственную службу времени и отказаться от ардуиновского сервиса времени на нулевом таймере.

Длительность импульса равна примерно 0,32 мс, частота импульсов заданная 3.2 кГц, т.е. этот импульс занимает время между двумя заданными. Появляется он редко, 1 раз из 100, но может сильно навредить. Вернулся к ошибочному варианту пока, костыль, но такого импульса там нет, долго писал сигнал осццилографом в режиме послесвечения и он не проявлялся.

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

Begemot, правильно проскальзывают :) Если бы скважность была больше, то проскальзовали бы чаще, вы же не отслеживаете перед паузой в каком состоянии был выход, в единице или в ноле. Выходы из ситуации разные есть, начиная от вашего "счастливого" костыля до более логичных решений. Например вообще отказаться от практики несинхронного наступления паузы. Паузу можно сделать контролируемой, отсчитав нужное количество импульсов в прерывании. Например так:

#define x 3200
void setup() {
pinMode(9,OUTPUT);
TCCR1A=(1<<COM1A1)|(1<<WGM11);
TCCR1B=(1<<WGM13)|(1<<WGM12)|(1<<CS10);
OCR1A=0; ICR1=0;
ICR1= (F_CPU/x)-1; //частота
OCR1A=  4  ; //длительность
TIMSK1|=1<<TOIE1;
}

ISR (TIMER1_OVF_vect) { 
static byte n;
if (n++ == 64) { TCCR1A^=1<<COM1A1; n=0; } 
}

void loop() { }

 

Begemot
Offline
Зарегистрирован: 28.04.2016

dimax пишет:

Например так

Большое спасибо за объяснение и предложенный вариант решения, всё работает корректно.

RuslanX
RuslanX аватар
Offline
Зарегистрирован: 20.05.2017

dimax здравствуйте. У меня есть вопрос к вам, как специалисту. Но как я понял в этом форуме личных сообщений нет. 

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

Напишите как можно отаправить вам информацию.  Мой email: nignitron@mail.com  скайп. ruslabx

Спасибо

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

RuslanX, день добрый! Если специфический, то наверное лучше что-б было 3 независимых 16-битных аппаратных канала. Это либо мега2560, либо уже МК на ARM архитектуре. Я не программист, поэтому на заказ не пишу, обращайтесь в специализированный раздел Ищу исполнителя.

RuslanX
RuslanX аватар
Offline
Зарегистрирован: 20.05.2017

dimax cпасибо за ответ. Я понял вас. Но хотел бы узнать от веас для ардуинки это сложно?

Вот ссылка на файл что должен сделать генератор.

http://zfile.in.ua/download?file=1605e8b0dab3a20eef8e5f93fdc7689d

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

RuslanX, посмотрел . Всё зависит от того, какая частота вам нужна. И насколько плавно её нужно менять. Судя по картинке исключительно аппаратными средствами таймеров не обойтись, нужно задействовать прерывания. А значит опорная частота будет исчисляться не более десятка килогерц. В принципе можно и  одним таймером обойтись, если все регулировки будут равны минимальному "кванту" -опорной частоте. Т.е. если прерывание будет строчить 1000 раз/сек, то минимальный квант времени будет 1мс . Т.е. ширина импульсов, движение фазы -всё будет кратно  1мс.

RuslanX
RuslanX аватар
Offline
Зарегистрирован: 20.05.2017

Спасибо dimax. Сейчас прикину. Пуш-пул (длинный импульс ключа) в районе 12-40 кГц. Пакеты нужны для компенсации реактивного сопротивления в индуктивности. Хотя правильней сказать реактивного сопротивления  индуктивной емкости. Там частота нужна в пределах от 120 кГц до 6 мГц.  И количество импульсов имеет значение в пакете. Дело в том что если будет их много то они уже зарядат внутреннюю емкость индуктивности и магнитный поток начнет двигаться от короткого импульса и соответственно пройдет свой путь по катушке, после чего прервется и начнется длинный импульс. А это не допустимо, потому что начнется новый переходной процесс (самоиндукция). А нужно поймать однонаправленный магнитный поток.  Тесла должна сработать на определенном угле фазового сдвига магнитного потока. Если у Теслы имеется большое реактивное сопротивление, то понадобятся пакет с большим количеством импульсов, а если малое, то с малым. Частота Теслы тоже варируется как и пуш-пул ударами его в полувонну. Тоесть она работает в каждую полуволну верхнего и нижнего плеча. А от длительность импульсов в пакете в районе 900 кГц-1.6 Мгц.

dimax я просто хочу попробовать ардуино в этом деле. Хотя не уверен выживет ли она у меня вообще.

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

Сдесь я показываю людям работу качера от аккумулятора. Обрати внимание на DC/DC конвертер. Он выдерживал и до сих пор в здравии, но вот с Ардуиной надо проверить.

 https://www.youtube.com/watch?v=2qKkqzOuAOU

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

RuslanX, посмотрел, интересное у вас хобби :) По поводу частот от 120кГц  -это уже за  гранью возможностей арудин,  тут нужно делать на ARM контроллерах, у них и скорость выше  и возможностей у таймеров гораздо больше. В общем задача очень сложная.

RuslanX
RuslanX аватар
Offline
Зарегистрирован: 20.05.2017

Спасибо Дима что уделил мне время. Буду думать с что то логикой. Еще раз спасибо и удачи вам.

mir12
Offline
Зарегистрирован: 11.09.2016

dimax пишет:

Begemot, правильно проскальзывают :) Если бы скважность была больше, то проскальзовали бы чаще, вы же не отслеживаете перед паузой в каком состоянии был выход, в единице или в ноле. Выходы из ситуации разные есть, начиная от вашего "счастливого" костыля до более логичных решений. Например вообще отказаться от практики несинхронного наступления паузы. Паузу можно сделать контролируемой, отсчитав нужное количество импульсов в прерывании. Например так:

#define x 3200
void setup() {
pinMode(9,OUTPUT);
TCCR1A=(1<<COM1A1)|(1<<WGM11);
TCCR1B=(1<<WGM13)|(1<<WGM12)|(1<<CS10);
OCR1A=0; ICR1=0;
ICR1= (F_CPU/x)-1; //частота
OCR1A=  4  ; //длительность
TIMSK1|=1<<TOIE1;
}

ISR (TIMER1_OVF_vect) { 
static byte n;
if (n++ == 64) { TCCR1A^=1<<COM1A1; n=0; } 
}

void loop() { }

 

А как для второго таймера сделать генератор мендра?