Генератор плавающей частоты

Kollega
Offline
Зарегистрирован: 21.02.2015

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

Возникла необходимость в генераторе "плавающей" частоты.

Суть такова:

На одном выходе должна быть частота 10кГц-20кГц, изменение частоты должно наростать в течение 1 секунду от 10кГц до 20кГц, одновременно на другом выходе должно происходить обратное от 20кГц до 10кГц. Процесс бесконечный, начинается с включения питания.

Пытался взять за основу сирену

http://arduino.ru/forum/proekty/imitatsiya-sireny-grazhdanskoi-oborony, но что-то на два выхода не получается.

Если кто-то делал подобное, поделитесь скетчем с пенсионером :)

 

Kollega
Offline
Зарегистрирован: 21.02.2015

Немного не верно написал.

На одном выходе частота втечение 1 секунды изменяется от 10кГц до 20кГц и снова от 20кГц до 10кГц,  а на втором выходе обратная картина, от 20 до 10 и обратно от 10 до 20.

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

Kollega, это довольно непростая задача.  Фактически нужно задействовать 3 таймера, на одном  считать время (или использовать millis() ) , а на ещё 2х сделать 2 генератора.   На 16-битном таймере не проблема,  Он может считать отрезками по 0,0625 us, то есть на диапазон 10kHz-20kHz выпадет на 800...400 отсчётов таймера, вполне плавно. А на оставшимся  8-битном таймере плавной регулировки просто так уже не сделать, тут придётся мудрить что-то..

Вот для одного выхода -довольно просто сделать :)

Kollega
Offline
Зарегистрирован: 21.02.2015

Да, dimax, вот по этому и не получается у меня... Я думал может у кого-то есть решения, но наверное нет. Вся затея в том, чтобы ограничиться одной ардуинкой, конечно если пристроить генераторы, то проблем нет, но нужно именно одной. :)

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

.

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

Kollega, ну как же нет решения? Я вам  его написал в общих чертах. Но привёл информацию для мк типа ардуино-уно, т.к. я только с ними работаю. Но возьмите ардуино- леонардо (микро) или ардуино-мега 2560, там по нескольку  16-битных счётчиков, без проблем реализуете свою задумку.

Alex_Sk
Offline
Зарегистрирован: 06.01.2015

Есть готовая библиотека позволяющая на ATmega168/328 генерировать до 3 независимых частот одновременно с использованием таймеров - т.е. во время генерации может исполняться другой код не задействующий таймеры. А при двух частотах таймер 0 свободен и можно использовать и millis(), micros() и ШИМ.

Сама библиотека от того же автора что и функция tone() в стандартном наборе.

https://code.google.com/p/rogue-code/wiki/ToneLibraryDocumentation

dmitriykisliy
Offline
Зарегистрирован: 03.03.2015

или есть библиотека

MirmPS.h

многозадачность на основе таймера 2 

вот инфо, вот библиотека

Kollega
Offline
Зарегистрирован: 21.02.2015

Спасибо Всем за полезную инфу, приму к сведению!

Вопрос решился немного по другому, на первом выходе действительно нужна была плавающая частота, а на втором достаточно было только постоянную середину между 10кГц  и 20, т.е. 15кГц, поэтому работает на втором выходе функция tone с постоянной частотой.

В заблуждение иногда вводит proteus, не всегда корректно в нем работает то, что нормально на железе, и наоборот...

Kollega
Offline
Зарегистрирован: 21.02.2015

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

#define Speaker_pin 10 // пин к которому подключен выход
#define Speed_up_down_frequency 1000 // скорость нарастания и убывания частоты

void setup() {
	pinMode(Speaker_pin, OUTPUT);// выход сигнала
	
}

void loop()
{
	for(int f = 88; f >= 44; f--){ // нарастание частоты
		Vooooo(f,Speed_up_down_frequency);
	}
		
	for(int f = 44; f <= 88; f++){ // убывание частоты
		Vooooo(f,Speed_up_down_frequency);
	}
		
}

void Vooooo(int freq, long duration){
	long time = duration/1/freq;
	for(long t = 0; t < time; t++)
	{
		digitalWrite(Speaker_pin, HIGH);
		delayMicroseconds(freq);
		digitalWrite(Speaker_pin, LOW);
		delayMicroseconds(freq);
	}
}

Но не могу прикрутить к скетчу функцию котроля заряда батареи. По принципу, если батарея разряжена на 50%, то зажигается светодиод. Отдельно такой скетч работает, а как совместить, не разберусь...

void setup() {
	// инициализируем пин, подключенный к светодиоду, как выход
	pinMode(ledPin, OUTPUT);
	// инициализируем пин, подключенный к делителю, как вход
	pinMode(buttonPin, INPUT);
}

void loop(){
	
	// считываем значения с делителя
	buttonState = digitalRead(buttonPin);
	
	// проверяем напряжение
	
	if (buttonState == HIGH) {
		
		digitalWrite(ledPin, LOW);
	}
	else {
		
		digitalWrite(ledPin, HIGH);
	}
}

 

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

Kollega, а где вы позаимствовали такую форму детектора напряжения? Он конечно будет работать, только идеологически неверно использовать цифровой вход для контроля уровня напряжения, для этого есть ацп или компаратор.  И по первому скетчу -генерировать частоту в функции loop это очень плохая затея, меандр будет очень нестабилен, и любое изменение скетча сразу вызовет уход всех ваших настроек, . Вот набросал, посмотрите.  На выводе D9 генератор качающейся частоты 10..20кГц, вывод неизменяем. Вывод 13 -светодиод, вывод A1 -вход для измерения напряжения, к нему подключите ваш делитель. Задействовано 2 таймера, таймер 1 генерит сигнал, от 10 до 20кГц с дискретностью 25 Гц., А что бы удобно обслуживать регулировку частоты используется таймер 2, он 400 раз в секунду вызывает прерывание, в котором увеличивается или уменьшается значение регистраOCR1 на единицу, и сответственно меняется частота на 25Гц. Порог срабатывания светодиода задаётся в строке 24.

void setup(){
pinMode (9,OUTPUT); //выход частоты 10..20кГц
pinMode (13,OUTPUT); //светодиод
TCCR2A=(1<<WGM21); //CTC
TCCR2B=(1<<CS21)|(1<<CS22); //div to 256
OCR2A=155; 
TIMSK2=1<<OCIE2A; //enable interrapt
TCCR1A= (1<<COM1A0)|(1<<COM1B0);
TCCR1B= (1<<CS10)|(1<<WGM12);
OCR1A=800; 
}

ISR (TIMER2_COMPA_vect) { 
static int times=800;
static bool freq_up=1;
if(freq_up) times --;
if(!freq_up) times ++; 
OCR1A=times;
if (times <= 400) freq_up=0; 
if (times >= 800) freq_up=1; 
}

void loop() { 
if (analogRead(A0) < 512 )digitalWrite(13,HIGH);
else digitalWrite(13,LOW);
}



 

Kollega
Offline
Зарегистрирован: 21.02.2015

Здравствуйте, dimax! Респект Вам и Уважуха, что отвечете мне и не отправляете, как на многих форумах сначала в гугел, потом в "автошколу" :).

Радиолюбительством я занимался с детства, еще с семидесятых, в восьмидесятых работал на радиозаводе. Но в те времена был "аналог", сейчас времена и технологии совершенно другие. Меня вот на пенсии снова потянуло к паяльнику и запаху канифоли. Я раньше понятия не имел, что такое arduino, случайно в иннете наткнулся на рекламу, когда искал осциллограх.

Тема показалась интересная, теперь пытаюсь постигать азы.

Еще раз Спасибо Вам!

Kollega
Offline
Зарегистрирован: 21.02.2015

dimax пишет:

Kollega, а где вы позаимствовали такую форму детектора напряжения? Он конечно будет работать, только идеологически неверно использовать цифровой вход для контроля уровня напряжения, для этого есть ацп или компаратор.  И по первому скетчу -генерировать частоту в функции loop это очень плохая затея, меандр будет очень нестабилен, и любое изменение скетча сразу вызовет уход всех ваших настроек, . Вот набросал, посмотрите.  На выводе D9 генератор качающейся частоты 10..20кГц, вывод неизменяем. Вывод 13 -светодиод, вывод A1 -вход для измерения напряжения, к нему подключите ваш делитель. Задействовано 2 таймера, таймер 1 генерит сигнал, от 10 до 20кГц с дискретностью 25 Гц., А что бы удобно обслуживать регулировку частоты используется таймер 2, он 400 раз в секунду вызывает прерывание, в котором увеличивается или уменьшается значение регистраOCR1 на единицу, и сответственно меняется частота на 25Гц. Порог срабатывания светодиода задаётся в строке 24.

void setup(){
pinMode (9,OUTPUT); //выход частоты 10..20кГц
pinMode (13,OUTPUT); //светодиод
TCCR2A=(1<<WGM21); //CTC
TCCR2B=(1<<CS21)|(1<<CS22); //div to 256
OCR2A=155; 
TIMSK2=1<<OCIE2A; //enable interrapt
TCCR1A= (1<<COM1A0)|(1<<COM1B0);
TCCR1B= (1<<CS10)|(1<<WGM12);
OCR1A=800; 
}

ISR (TIMER2_COMPA_vect) { 
static int times=800;
static bool freq_up=1;
if(freq_up) times --;
if(!freq_up) times ++; 
OCR1A=times;
if (times <= 400) freq_up=0; 
if (times >= 800) freq_up=1; 
}

void loop() { 
if (analogRead(A0) < 512 )digitalWrite(13,HIGH);
else digitalWrite(13,LOW);
}

У меня еще такой вопрос. Просимулировал в proteus_е, получается, что время изменения частоы несколько секунд. Мне не обходимо время от низкой к высокой и обратно за одну секунду. Подскажите, каким образом изменить время, и как можно изменить верхнюю и нижнюю частоту, и как можно зафиксировать (на время настройки верхнюю и нижнюю частоту, по тому, что proteus не успевает считывать.)

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

Kollega,  Думаю вы не правильно что-то задали в протеусе. Возможно татовую частоту. (Я не пользуюсь этой прогой, так что не скажу точно)  Время  считается так - частота процессора (16 000 000 ) / делитель (задано 256), =  тактовая частоту таймера, т.е. 62500Гц. Переведём их сразу в секунды для удобства,  1/62500 = 16uS. Далее  в 6 строке скетча задаётся количество тактов, которые нужно пропустить перед прерыванием. 16 * 155= 2480 uS. Т.е. каждые 2,48 ms запускается прерывание. Цикл увеличения или уменьшения частоты состоит из 400 прерываний,  2,48 * 400 =992 миллисекунды .  Если хотите за секунду и увеличить и уменьшить, то  придётся поменять делитель на /1024 TCCR2B=(1<<CS20)|(1<<CS21)|(1<<CS20); и  задержку OCR2A=20;

Проверяем: 16 000 000 / 1024 = 15 625 ; 1/15 625 =  64us ; 64*20= 1280us прерывание. Нам нужно сделать их 800 за секунду, 1280*800 = 1,024 s

Зафиксировать частоту -закомментируйте тело прерывания или можно только 16 и 17 строчку.

Изменить частоту - в регистре OCR1A записывать другие значения, рассчитывать можно по аналогии, делитель там 1. 

Kollega
Offline
Зарегистрирован: 21.02.2015

Да. Сейчас понятно. Я протеусом пользуюсь, чтобы хоть как-то сначала получить эмуляцию.

Более лучшей программы для эмуляции не нашел, или плохо искал. У меня стоит еще multisim, но этот эмулятор больше для аналога.

Я в скетч прикрутил еще один светодиод, на эмуляторе заработало.

Т.е. напряжение от 9 до 7 вольт - светится "высокий" заряд батарейки, от 7 до 5 - два светодиода, типа середина, и от 5 вольт и ниже типа "низкий" заряд батарейки.

Вы пользуетесь каким либо эмулятором?

Kollega
Offline
Зарегистрирован: 21.02.2015

dimax пишет:

Kollega,  Думаю вы не правильно что-то задали в протеусе. Возможно татовую частоту. (Я не пользуюсь этой прогой, так что не скажу точно)  Время  считается так - частота процессора (16 000 000 ) / делитель (задано 256), =  тактовая частоту таймера, т.е. 62500Гц. Переведём их сразу в секунды для удобства,  1/62500 = 16uS. Далее  в 6 строке скетча задаётся количество тактов, которые нужно пропустить перед прерыванием. 16 * 155= 2480 uS. Т.е. каждые 2,48 ms запускается прерывание. Цикл увеличения или уменьшения частоты состоит из 400 прерываний,  2,48 * 400 =992 миллисекунды .  Если хотите за секунду и увеличить и уменьшить, то  придётся поменять делитель на /1024 TCCR2B=(1<<CS20)|(1<<CS21)|(1<<CS20); и  задержку OCR2A=20;

Проверяем: 16 000 000 / 1024 = 15 625 ; 1/15 625 =  64us ; 64*20= 1280us прерывание. Нам нужно сделать их 800 за секунду, 1280*800 = 1,024 s

Зафиксировать частоту -закомментируйте тело прерывания или можно только 16 и 17 строчку.

Изменить частоту - в регистре OCR1A записывать другие значения, рассчитывать можно по аналогии, делитель там 1. 

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

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

Kollega, эмуляторами не пользуюсь, как-то всё не возникало надобности, сам удивляюсь. Но протеус давно скачал и установил, осталось научиться пользоваться)

Частота первый раз задаётся в 10 строке, в обаботчик вносится в регистр таймера в 18 строке. Инициализируется переменная в 14 строке, там задаётся с чего начинать движение частоты туда/сюда.Но на частоту влияет ещё делитель (который  впрочем отключен -бит CS10 в 9 строке)  Вычислить что писать в регистр для получения частоты можно так:

OCR1A= (16 000 000 /n  /2 /div) -1       где n-заданная частота в Гц, div -делитель (стоит на 1)

Проверям для 10kHz  (16000000 /10000 /2 /1) -1 = 799.   Т.е. я на единицу в скетче ошибся, типа округлил..

Кстати уберите из 8 строки вот это, оно лишнее, не заметил сразу. |(1<<COM1B0)

Kollega
Offline
Зарегистрирован: 21.02.2015

dimax пишет:

Kollega, эмуляторами не пользуюсь, как-то всё не возникало надобности, сам удивляюсь. Но протеус давно скачал и установил, осталось научиться пользоваться)

Частота первый раз задаётся в 10 строке, в обаботчик вносится в регистр таймера в 18 строке. Инициализируется переменная в 14 строке, там задаётся с чего начинать движение частоты туда/сюда.Но на частоту влияет ещё делитель (который  впрочем отключен -бит CS10 в 9 строке)  Вычислить что писать в регистр для получения частоты можно так:

OCR1A= (16 000 000 /n  /2 /div) -1       где n-заданная частота в Гц, div -делитель (стоит на 1)

Проверям для 10kHz  (16000000 /10000 /2 /1) -1 = 799.   Т.е. я на единицу в скетче ошибся, типа округлил..

Спасибо!

Kollega
Offline
Зарегистрирован: 21.02.2015

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

Kollega
Offline
Зарегистрирован: 21.02.2015

Kollega]</p> <p>[quote=dimax пишет:

Kollega, эмуляторами не пользуюсь, как-то всё не возникало надобности, сам удивляюсь. Но протеус давно скачал и установил, осталось научиться пользоваться)

Частота первый раз задаётся в 10 строке, в обаботчик вносится в регистр таймера в 18 строке. Инициализируется переменная в 14 строке, там задаётся с чего начинать движение частоты туда/сюда.Но на частоту влияет ещё делитель (который  впрочем отключен -бит CS10 в 9 строке)  Вычислить что писать в регистр для получения частоты можно так:

OCR1A= (16 000 000 /n  /2 /div) -1       где n-заданная частота в Гц, div -делитель (стоит на 1)

Проверям для 10kHz  (16000000 /10000 /2 /1) -1 = 799.   Т.е. я на единицу в скетче ошибся, типа округлил..

Спасибо!

А как мы получаем 20 кГц, и что прописано в 19 и 20 строке

     19   if (times <= 400) freq_up=0;
     20   if (times >= 800) freq_up=1;

 

Kollega
Offline
Зарегистрирован: 21.02.2015

dimax, можно вашу электронку?

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

Kollega пишет:

А как мы получаем 20 кГц, и что прописано в 19 и 20 строке

     19   if (times <= 400) freq_up=0;
     20   if (times >= 800) freq_up=1; 

Что бы разделить повышение частоты от понижения я сделал флаг freq_up, когда счётчик "times" доходит до нужного конца флаг меняется, и в следущий заход частота начинает меняться в другую сторону. 20 кГц при значении OCR1A=400; Я ж вам формулу дал.

Kollega
Offline
Зарегистрирован: 21.02.2015

Здравствуйте, dimax!

Не могу понять, почему в протеусе нарастание и убывание частоты длится почти полторы минуты. Частота стоит правильно 16мГц, верхняя и нижняя частота совпадает.

Посмотреть можно здесь https://www.youtube.com/watch?v=m6gaJbh1Opc

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

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

Kollega
Offline
Зарегистрирован: 21.02.2015

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

Может позже разберусь.

У меня к Вам еще такой вопрос по светодиоду, я так понимаю, что значение задается от 1023, т.е. в вашем скетче 512 -это половина, начиная от половины заданного светодиод зажигается.

Как задать два условия, например выше 512 не светится, ниже 512 зажигается, а при понижении до 400 гаснет.

Так же хотелось узнать хорошие источники по изучению кода. На официальном сайте ардуино, как-то не совсем удобно написано...

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

void loop() { 
int aread=analogRead(A0);
  if (aread >=400 && aread <= 512 )digitalWrite(13,HIGH);
    else digitalWrite(13,LOW);
}

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

http://we.easyelectronics.ru/AVR/dokumentaciya-po-avr-mikrokontrolleram-vse-na-russkom.html

PS: думаю вам стоит начать с вот этого http://robocraft.ru/files/books/arduino_notebook_rus_v1-1.pdf

Kollega
Offline
Зарегистрирован: 21.02.2015

dimax пишет:


void loop() { 
int aread=analogRead(A0);
  if (aread >=400 && aread <= 512 )digitalWrite(13,HIGH);
    else digitalWrite(13,LOW);
}

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

http://we.easyelectronics.ru/AVR/dokumentaciya-po-avr-mikrokontrolleram-vse-na-russkom.html

PS: думаю вам стоит начать с вот этого http://robocraft.ru/files/books/arduino_notebook_rus_v1-1.pdf

Спасибо!

Kollega
Offline
Зарегистрирован: 21.02.2015

Правильно ли я мыслю, если мне например нужно изменить частоту. Начальная частота 20кГц

16000000/20000/2/div = 400

Верхняя частота 30кГц

16000000/30000/2/div = 266

(время 1 сек. оставляем без изменения)

Или я накосячил?

void setup(){
pinMode (9,OUTPUT); //выход частоты 20..30кГц
pinMode (13,OUTPUT); //светодиод
TCCR2A=(1<<WGM21); //CTC
TCCR2B=(1<<CS21)|(1<<CS22); //div to 256
OCR2A=155; 
TIMSK2=1<<OCIE2A; //enable interrapt
TCCR1A= (1<<COM1A0)|(1<<COM1B0);
TCCR1B= (1<<CS10)|(1<<WGM12);
OCR1A=400; // Заданная частота
}

ISR (TIMER2_COMPA_vect) { 
static int times=400;// начинаем с заданной
static bool freq_up=1;
if(freq_up) times --;
if(!freq_up) times ++; 
OCR1A=times;
if (times <= 266) freq_up=0; //верхняя частота
if (times >= 400) freq_up=1; //нижняя частота
}

void loop() { 
if (analogRead(A0) < 512 )digitalWrite(13,HIGH);
else digitalWrite(13,LOW);
}



 

Kollega
Offline
Зарегистрирован: 21.02.2015

Здравствуйте, dimax! Я тут спрашивал у Вас по поводу частоы, правильно ли я изменил код.

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

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