Arduino Push-pull
- Войдите на сайт для отправки комментариев
Сб, 29/06/2013 - 19:10
Нужна помощь. Если где-то уже есть решения подскажите пожалуйста.
за основу было взято http://www.kerrywong.com/2010/03/12/a-power-inverter-with-arduino-pulse-source/
изменил немного скетч
void setup() {
DDRD = B11111110;
PORTD = B00000000;
}
void loop() {
PORTD = B00010000;
delayMicroseconds(25); // 19.36 кГц
PORTD = B00100000;
delayMicroseconds(25);
}
получилось запустить устройство
http://www.youtube.com/watch?v=gz3YcAGV678
В перспективе добавить обратную связь для автоподстройки частоты на резонансе. Но проблема в том, что очень большой шаг изменения частоты.
25 = 19,36 кГЦ
26 = 18,62 кГц
27 = 17,93 кГц
28 = 17,29 кГц
смотрел темы :
http://cxem.net/arduino/arduino62.php
http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM
https://sites.google.com/site/vanyambauseslinux/arduino/tajmery-sceetcik...
но что-то не могу понять как сделать двухтактный выход ....
void setup() { DDRD = B11111110; PORTD = B00000000; } void loop(){ int i = 80; // 13,68 кГц for (int k=0;k<i;k++) PORTD = B00010000; for (int k=0;k<i;k++) PORTD = B00100000; }так, шаг уменьшился до 100 Гц, уже лучше но недостаточно...
Даже при использований прерываний и таймера 1 в режиме СТС, шаг регулировки частоты, при 19.36 кГц, будет около 50 Гц, и чем выше частота тем он будет больше. Соответственно чем ниже частота тем шаг меньше. Так что 100 Гц это нормальный шаг.
вот такой код получился
#include <LineDriver.h> #include <LiquidCrystalExt.h> #include <LiquidCrystalRus.h> LiquidCrystalRus lcd(8,9,10,11,12,13); int val = 0; int val_fr = 100; // 10,96 кГц void setup() { DDRD = B11111000; PORTD = B00000100; lcd.begin(16, 2); } void loop(){ for (int k=0; k<val_fr; k++) PORTD = B00010100; for (int k=0; k<val_fr; k++) PORTD = B00100100; if (PIND == B00100010) l_c_d(); } void l_c_d() { val = analogRead(A3); val_fr = map(val, 0, 1023, 1, 200); lcd.clear(); lcd.setCursor(0, 0); lcd.print("знач. 1 "); lcd.setCursor(8, 0); lcd.print(val); lcd.setCursor(0, 1); lcd.print("знач. 2 "); lcd.setCursor(8, 1); lcd.print(val_fr); delay(500); }изменение частоты производится переменным резистором тоько при низком уровне на 3 пине...
если бы сделать как-то расчёт частоты для отображения на экране, но пока не придумал как....
Кароч всетаки придется использовать прерывания и таймер 1 в режиме СТС. Объясняю почему. В строке 16 100 раз подряд будет выполняться команда PORTD=B00010100. Пока она выполняется на pin4 высокий уровень, формируется первый полупериод. В 17 строке 100 раз выполняется команда PORTD=B00100100, на pin5 высокий уровень, формируется второй полупериод. И вот формироваться он будет пока не отработает 18 строка, а она вызывает функцию l_c_d(), а в ней есть задержка пол секунды. Надеюсь понятно , что второй полупериод будет слишком долгий.
Вот пример как получить генерацию заданной частоты используя прерывания и таймер 1 в режиме СТС:
void setup() { DDRD = B11111000; // нужные пины на выход PORTD = B00000100; TCCR1A=0; TIMSK1=0; // сбрасываем на всякий эти регистры TCCR1B=0; // мало ли что arduino IDE туда записало TCNT1=0; // сбрасываем счетный регистр таймера 1 OCR1A=413; // задаем частоту, в Герцах, по формуле f=F_CPU/OCR1A/2 где F_CPU тактовая частота ардуины TIMSK1|=(1<<OCIE1A); // разрешаем генерацию прерывания таймера 1, по совпадению с регистром OCR1A TCCR1B|=((1<<CS10)|(1<<WGM12)); // запускаем таймер 1 без предделителя в режиме СТС } void loop() { // основная программа } ISR(TIMER1_COMPA_vect) { if(PORTD & (1<<PD4))PORTD = B00100100; else PORTD = B00010100; // если пин4 установлен формируем второй полупериод, если нет, первый }Значения меньше 30 в OCR1A лучше не записывать. Так как были изменены значения регистров по умолчанию, функция analogWrite() будет работать неправильно. Скетч будет работать на дуинах с atmega168/328. При использовании atmega8 меняем все TIMSK1 на TIMSK (убираем еденицу).
--- что-то не хочет компилироваться ...
sketch_jun30a.ino: In function 'void __vector_11()':
sketch_jun30a:19: error: 'PD4' was not declared in this scope
--- на счёт второго полупериода вы правы, недосмотрел. Хотя в моём случае это не критично. Во время генерации мерял прибором отдельно полупериоды, показания идентичны. При удержании кнопки на 5 пине держится единица пока кручу резистор но на работу устройства это особо не влияет (хотя можно попробовать обнулить этот пин).
больше напрягает настройка и индикация частоты..... хочу попробовать запустить ваш вариант, но что-то запутался в этих таймерах....
--- что-то не хочет компилироваться ...
Замените в 19 строке PD4 на 4.
всё, запустил !!!!
у вас просто опечатка... if(PORTD & (1<<PIND4))PORTD = B00100100;
Отлично, шаг тут получается 30 Гц .....
Теперь нужно прикрутить регулировку и индикацию - продолжаю мучать прерывания.
попытался сделать так
#include <LineDriver.h> #include <LiquidCrystalExt.h> #include <LiquidCrystalRus.h> LiquidCrystalRus lcd(8,9,10,11,12,13); int val, z; volatile int val_fr = 500; // 15,65 кГц float rpm; void setup() { DDRD = B11111000; // нужные пины на выход PORTD = B00000100; TCCR1A=0; TIMSK1=0; // сбрасываем на всякий эти регистры TCCR1B=0; // мало ли что arduino IDE туда записало TCNT1=0; // сбрасываем счетный регистр таймера 1 OCR1A=val_fr; // задаем частоту, в Герцах, по формуле f=F_CPU/OCR1A/2 где F_CPU тактовая частота ардуины TIMSK1|=(1<<OCIE1A); // разрешаем генерацию прерывания таймера 1, по совпадению с регистром OCR1A TCCR1B|=((1<<CS10)|(1<<WGM12)); // запускаем таймер 1 без предделителя в режиме СТС lcd.begin(16, 2); } void loop(){ if (PIND == B00100010) l_c_d(); } void l_c_d() { val = analogRead(A3); val_fr = map(val, 0, 1023, 400, 700); rpm = 16000000/val_fr/2; //lcd.clear(); lcd.setCursor(0, 0); lcd.print("freq Гц"); lcd.setCursor(5, 0); lcd.print(rpm); lcd.setCursor(0, 1); lcd.print("вх. А3 "); lcd.setCursor(9, 1); lcd.print(val_fr); //delay(500); } ISR(TIMER1_COMPA_vect) { if(PORTD & (1<<4))PORTD = B00100100; else PORTD = B00010100; // если пин4 установлен формируем второй полупериод, если нет, первый }при нажатии кнопки включается индикатор и показывает изменение val_fr но потом изменённое значение НЕ передаётся в генератор...
подскажите как изменить OCR1A=val_fr; ?????
Регистр OCR1A лучше изменять в обработчике прерывания
ISR(TIMER1_COMPA_vect) { if(PORTD & (1<<4))PORTD = B00100100; else PORTD = B00010100; // если пин4 установлен формируем второй полупериод, если нет, первый OCR1A=val_fr; }Evgen, с меня пиво, всё работает отлично !!!!!!!!!!
#include <LineDriver.h> #include <LiquidCrystalExt.h> #include <LiquidCrystalRus.h> LiquidCrystalRus lcd(8,9,10,11,12,13); int val, z; volatile int val_fr = 500; // 15,65 кГц float rpm; void setup() { DDRD = B11111000; // нужные пины на выход PORTD = B00000100; TCCR1A=0; TIMSK1=0; // сбрасываем на всякий эти регистры TCCR1B=0; // мало ли что arduino IDE туда записало TCNT1=0; // сбрасываем счетный регистр таймера 1 // OCR1A=val_fr; // задаем частоту, в Герцах, по формуле f=F_CPU/OCR1A/2 где F_CPU тактовая частота ардуины TIMSK1|=(1<<OCIE1A); // разрешаем генерацию прерывания таймера 1, по совпадению с регистром OCR1A TCCR1B|=((1<<CS10)|(1<<WGM12)); // запускаем таймер 1 без предделителя в режиме СТС lcd.begin(16, 2); } void loop(){ if (PIND == B00100010) l_c_d(); } void l_c_d() { val = analogRead(A3); val_fr = map(val, 0, 1023, 200, 600); rpm = 16000000/val_fr/2; //lcd.clear(); lcd.setCursor(0, 0); lcd.print("freq Гц"); lcd.setCursor(5, 0); lcd.print(rpm); lcd.setCursor(0, 1); lcd.print("вх. А3 "); lcd.setCursor(9, 1); lcd.print(val_fr); //delay(500); } ISR(TIMER1_COMPA_vect) { if(PORTD & (1<<4))PORTD = B00100100; else PORTD = B00010100; // если пин4 установлен формируем второй полупериод, если нет, первый OCR1A=val_fr; }прицепил на 4 и 5 пины прибор мерять частоту, посмотрел при замкнутой и разомкнутой кнопке на пин 3, частота не уходит. возможно настройку частоты можно поместит в loop() без "нулевого" прерывания. проверю это на готовом изделии.
Не сочтите за наглость, но может ещё подскажете как сюда добавить регулировку скважности ...
пока только в качестве идеи - delayMicroseconds() в обработчике прерывания с пропорциональным увеличением частоты, но что-то в цифрах запутался....
Вы хотите задавать разную длительность первого и второго полупериода?
Не не, так делать нельзя, работать не будет.
Вы хотите задавать разную длительность первого и второго полупериода?
несовсем. нужно сделать dead time - задержки между полупериодами
На рис. 4 показана форма импульсов с малой скважностью, на рис. 5 - с большей скважностью, а на рис. 6 - с максимальной скважностью.
только просто сделать задержку между полупериодами наверное не получится т.к. за время переполнения таймера будеет разное колличество импульсов (будет меняться частота).
нужно сохранять частоту при изменении скважности .
Вот реализация того, что сделано насегодня...
http://www.youtube.com/watch?v=rQ368CWKoCU&feature=youtu.be
Вот. Можно регулировать скважность переменной dead_time.
void (*mas[]) (void)={poluper1, dead_time1, poluper2, dead_time2}; // массив указателей функций volatile int val_fr = 500; // длительность полупериода, не должна быть меньше 50 volatile int dead_time; // пауза между периодами, не должна быть меньше 50 и больше (val_fr*2)-100 byte uk=0; void setup() { DDRD = B11111000; // нужные пины на выход TCCR1A=0; TIMSK1=0; // сбрасываем на всякий эти регистры TCCR1B=0; // мало ли что arduino IDE туда записало TCNT1=0; // сбрасываем счетный регистр таймера 1 OCR1A=val_fr; // задаем частоту, в Герцах, по формуле f=F_CPU/OCR1A/2 где F_CPU тактовая частота ардуины TIMSK1|=(1<<OCIE1A); // разрешаем генерацию прерывания таймера 1, по совпадению с регистром OCR1A TCCR1B|=((1<<CS10)|(1<<WGM12)); // запускаем таймер 1 без предделителя в режиме СТС } void loop() { // основная программа } ISR(TIMER1_COMPA_vect) { (*mas[uk])(); // вызываем функцию по указателю } void poluper1(void) // функция 0 { PORTD&=~(1<<5); // на пин 5 лог 0 PORTD|=(1<<4); // на пин 4 лог 1, формируем первый полупериод if(dead_time<50) {OCR1A=val_fr; uk=2;} // если пауза меньше 50 тогда она формироваться не будет else {OCR1A=val_fr-(dead_time/2); uk=1;} // иначе уменьшаем полупериод на половину длительности паузы чтоб сохранить частоту } void dead_time1(void) // функция 1 { PORTD&=~(1<<4); // на пинах 4 и 5 лог 0, формируем dead_time паузу OCR1A=dead_time; uk=2; } void poluper2(void) // функция 2 { PORTD&=~(1<<4); // на пин 4 лог 0 PORTD|=(1<<5); // на пин 5 лог 1, формируем второй полупериод if(dead_time<50) {OCR1A=val_fr; uk=0;} else {OCR1A=val_fr-(dead_time/2); uk=3;} } void dead_time2(void) // функция 3 { PORTD&=~(1<<5); // на пинах 4 и 5 лог 0, формируем dead_time паузу OCR1A=dead_time; uk=0; }Огромное спасибо !!!!
если я правильно понял, то переменные
volatile int val_fr = 500; // длительность полупериода, не должна быть меньше 50 volatile int dead_time; // пауза между периодами, не должна быть меньше 50 и больше (val_fr*2)-100 void loop() { // основная программа }можно просто изменять в основной программе.
тоесть как раньше сделать прерывание на кнопку и двумя переменниками устанавливаем значения на аналоговых пинах, затем читаем их и используем в генераторе...
Да, все так. Только я этот код не проверял, пробуйте сами.
Проверил, но что-то не то получается....
вопервых при значении val_fr = 500 в "базовом" варианте реальная частота равна 16 кГц и соответствует расчётам f=16000/500/2
в новом скетче, с частотой что-то не понятное и скважность ведёт себя странно...
На последних двух скриншотах пауза между периодами задана не правильно. Я же писал там в комментах, она не должна быть больше чем (val_fr*2)-100. А вот почему остальное не работает счас буду думать.
А что за программа осциллогрф? Дайте ссылочку где скачать можно.
не помню где я её скачал, вот залил на обменник.
http://zalil.ru/34619125
а щуп делал такой http://www.illari.ru/electro/osc/
только какая-то фигня с этим осцилографом, цепляю один щуп на пин, второй на массу а на осцилографе всёравно ДВЕ полуволны.....
А если вот так попробывать ??? Только сейчас не могу проверить на железе...
void (*mas[]) (void)={poluper1, dead_time1, poluper2, dead_time2}; // массив указателей функций volatile int val_fr = 500; // длительность полупериода, не должна быть меньше 50 volatile int dead_time = 50; // пауза между периодами, не должна быть меньше 50 и больше (val_fr*2)-100 // скважность (dead_time) задаю в процентах, для наглядности byte uk=0; void setup() { DDRD = B11111000; // нужные пины на выход PORTD = B00000100; TCCR1A=0; TIMSK1=0; // сбрасываем на всякий эти регистры TCCR1B=0; // мало ли что arduino IDE туда записало TCNT1=0; // сбрасываем счетный регистр таймера 1 OCR1A=0; // задаем частоту, в Герцах, по формуле f=F_CPU/OCR1A/2 где F_CPU тактовая частота ардуины TIMSK1|=(1<<OCIE1A); // разрешаем генерацию прерывания таймера 1, по совпадению с регистром OCR1A TCCR1B|=((1<<CS10)|(1<<WGM12)); // запускаем таймер 1 без предделителя в режиме СТС } void loop() { // основная программа } ISR(TIMER1_COMPA_vect) { if(uk==0 || uk==2) OCR1A=val_fr-((val_fr/100)*dead_time); if(uk==1 || uk==3) OCR1A=(val_fr/100)*dead_time; (*mas[uk])(); // вызываем функцию по указателю } void poluper1(void) // функция 0 { PORTD = B00010000; // на пин 5 лог 0 на пин 4 лог 1, формируем первый полупериод uk=1; } void dead_time1(void) // функция 1 { PORTD = B00000000; // на пинах 4 и 5 лог 0, формируем dead_time паузу uk=2; } void poluper2(void) // функция 2 { PORTD = B00100000; // на пин 4 лог 0 на пин 5 лог 1, формируем второй полупериод uk=3; } void dead_time2(void) // функция 3 { PORTD = B00000000; // на пинах 4 и 5 лог 0, формируем dead_time паузу uk=0; }Разобрался я почему неправильно работало. У дуины банально не хватает быстродействия.
Значения этих переменных, если другими словами, это такты процессора. Если какаянить из переменных равна 500, то таймер 1 отсчитает 500 тактов проца и вызовет прерывание, если 300 значит 300 тактов. Это значит что, то что написано в обработчике прерывания должно выполнится за меньшее количество тактов, а вот это как раз делаться не успевалось. Только один вход в обработчик это, я думаю, 20-30 тактов, да плюс еще там операция деления присутствует (если пауза между периодами формируется), а она много времени отнимает. К тому-же у дуины еще свои прерывания есть, например функции millis() и micros() используют таймер 0.
Для повышения быстродействия можно заменить операцию деления на два, на сдвиг вправо на один разряд (сдвиг вправо на один разряд это деление на два, сдвиг влево на один разряд это умножение на два).
else {OCR1A=val_fr-(dead_time>>1); uk=3;}Ну и нужно задать другие ограничения для переменных val_fr и dead_time. В общем вот немного подправленный скетч.
void (*mas[]) (void)={poluper1, dead_time1, poluper2, dead_time2}; // массив указателей функций volatile int val_fr = 500; // длительность полупериода, не должна быть меньше 200+(dead_time/2) volatile int dead_time; // пауза между периодами, не должна быть меньше 200 и больше (val_fr*2)-200 byte uk=0; void setup() { DDRD = B11111000; // нужные пины на выход TCCR1A=0; TIMSK1=0; // сбрасываем на всякий эти регистры TCCR1B=0; // мало ли что arduino IDE туда записало TCNT1=0; // сбрасываем счетный регистр таймера 1 OCR1A=val_fr; // задаем частоту, в Герцах, по формуле f=F_CPU/OCR1A/2 где F_CPU тактовая частота ардуины TIMSK1|=(1<<OCIE1A); // разрешаем генерацию прерывания таймера 1, по совпадению с регистром OCR1A TCCR1B|=((1<<CS10)|(1<<WGM12)); // запускаем таймер 1 без предделителя в режиме СТС } void loop() { // основная программа } ISR(TIMER1_COMPA_vect) { (*mas[uk])(); // вызываем функцию по указателю } void poluper1(void) // функция 0 { PORTD&=~(1<<5); // на пин 5 лог 0 PORTD|=(1<<4); // на пин 4 лог 1, формируем первый полупериод if(dead_time<200) {OCR1A=val_fr; uk=2;} // если пауза меньше 50 тогда она формироваться не будет else {OCR1A=val_fr-(dead_time>>1); uk=1;} // иначе уменьшаем полупериод на половину длительности паузы чтоб сохранить частоту } void dead_time1(void) // функция 1 { PORTD&=~(1<<4); // на пинах 4 и 5 лог 0, формируем dead_time паузу OCR1A=dead_time; uk=2; } void poluper2(void) // функция 2 { PORTD&=~(1<<4); // на пин 4 лог 0 PORTD|=(1<<5); // на пин 5 лог 1, формируем второй полупериод if(dead_time<200) {OCR1A=val_fr; uk=0;} else {OCR1A=val_fr-(dead_time>>1); uk=3;} } void dead_time2(void) // функция 3 { PORTD&=~(1<<5); // на пинах 4 и 5 лог 0, формируем dead_time паузу OCR1A=dead_time; uk=0; }Теперь вопрос. Вы откуда осциллограммы снимаете, с пинов ардуины? С четвертого и пятого или только одного? У вас на осциллограммах только один канал показан, должно быть два. Вы второй специально отключили? Должно быть вот так:
у меня штекер "моно" ....
подключался к пин 4 и пин 5 ... хотя ессли цеплял пин+масса картинка не менялась...
А, почему у вас разная чувствительность на каналах на первом 1000, а на втором 500 ?
Так было по умолчанию при включении программы, я не трогал эту настройку.
Если вы будете снимать сигнал только с одного пина то, при формировании паузы между полупериодами, осциллограф будет неправильно высчитывать частоту. Он будет считать частоту, замеряя длительность периода, вот так
А в конечном устройстве период будет вот такой
Самое странное, что у меня картинка сигнала НЕ меняется хоть с одного пина снимаю, хоть с двух ?!?!?!
вот пример, с двух пинов ....
вот с одного пина и массы
Запустил последний скетч, вот что получилось (сигнал снимал с двух пинов)
с такими настройками на картинке колличество импульсов не меняется, а частота (в цифрах) скачет.
Но непонятно другое !!!!! почему "мёртвая зона" только с одной стороны ????
по идее и по правилам "мёртвая зона" должна быть сдвух сторон каждой полуволны...
Вы каим оброзом осциллограммы снимаете? Надо один канал линейного входа звуковой карты подключить к четвертому пину, другой к пятому. Я подключал через резисторы 100 Ом (можно побольше поставить) и ничего не сгорело. Для этого нужен разъем мини-джек "стерео". Дуину нужно от того-же компа запитывать которым осциллограммы снимаете.
У меня джек "моно" поэтому входы карты объеденены, и получается, что пин 4 на входы, пин 5 на массу.
Помогите, я кажется запутался...
void dead_time1(void) // функция 1 { PORTD&=~(1<<4); // на пинах 4 и 5 лог 0, формируем dead_time паузу OCR1A=dead_time; uk=2; } void poluper2(void) // функция 2 { PORTD&=~(1<<4); // на пин 4 лог 0 PORTD|=(1<<5); // на пин 5 лог 1, формируем второй полупериод if(dead_time<200) {OCR1A=val_fr; uk=0;} else {OCR1A=val_fr-(dead_time>>1); uk=3;} }выполняется функция 1 (дедтайм) по её завершении таймеру задаётся значение OCR1A=dead_time; тогда получается, что следующая функция 2 (формирование полупериода) будет выполняться в течении таймера "мёртвой зоны"...
Немного не так. Строка OCR1A=dead_time задает сколько будет длиться пауза между полупериодами. Эта функция не выполняется сколько-то долго, она просто устанавливает лог 0 на пинах 4 и 5 (ну в данном случае только на пине 4 потому, что 5 пин и так в нуле), потм задает через сколько тактов процессора должно вызваться это-же прерывание (OCR1A=dead_time) и изменяет переменную uk=2, чтобы в следующий раз в обработчике прерывания вызвалась не опять она-же, а уже другая функция, которая формирует полупериод.
Evgen, спасибо всё понял, а то, что-то запутался....
ещё.
я немного изменил строки с массивом функций, всё работает но хочется узнать на сколько это правильно.
void (*mas[4]) (void)={poluper1, dead_time1, poluper2, dead_time2}; // массив указателей функций int uk=0;вчера взял осцилограф (не Бог весть что, но лучше чем прога в компе)... Вот что получается.
volatile int val_fr = 900;
volatile int dead_time = 500;
volatile int val_fr = 900;
volatile int dead_time = 250;
теперь нормально видно, что "мёртвая зона" отрабатывается правильно, с двух сторон от полупериода.
Только при изменении дедтайма изменяется частота...
Думаю, что частоту (полупериод) и скважность (дедтайм) нужно расчитывать от одного числа. Тоесть "полупериод" плюс "дедтайм" должно быть равно при изменении одного из слагаемых.
В общем есть чем заниматься на выходных...
Ещё раз ОГРОМНОЕ СПАСИБО !!! за помощь...
В первой строке вы явно указали размер массива. Если все элементы массива указаны, а в данном случае они указаны в фигурных скобках, то компилятор выставит размер массива сам. Так что можно указывать, а можно не указывать, правильно и так и так.
Во второй стрчке лучше оставить тип byte. Зачем отводить под переменную два байта когда хватит и одного? Хотя конечно работать будет, но старший байт будет не задействован.
А вот на счет ухода частоты это надо подумать.
Подумал:) Получается, что-бы частота не уходила, пауза дедтайма должна полностью вычитаться из полупериода. Тогда изменим функцию формирования полупериода вот так
void poluper1(void) // функция 0 { PORTD&=~(1<<5); // на пин 5 лог 0 PORTD|=(1<<4); // на пин 4 лог 1, формируем первый полупериод if(dead_time<200) {OCR1A=val_fr; uk=2;} // если пауза меньше 200 тогда она формироваться не будет else {OCR1A=val_fr-dead_time; uk=1;} // иначе уменьшаем полупериод на длительность паузы чтоб сохранить частоту }Это значит что дедтайм не может быть меньше 200 и больше длительности полупериода - 200
Как-то так.
Пока не могу проверить в "железе" ....
#include <LiquidCrystal.h> LiquidCrystal lcd(8,9,10,11,12,13); volatile int val_fr = 533; // длительность полупериода, не должна быть меньше 200+(dead_time/2) volatile int dead_time = 199; // пауза между периодами, не должна быть меньше 200 и больше val_fr-200 int f_val, d_val; float rpm, dtm; // ******************************************************************** // ******************************************************************** void loop(){ if (PIND&=~(1<<2)) l_c_d(); // если на пин 2 лог 0 // if ((PIND&(1<<2)) == 0) l_c_d(); } // ******************************************************************** // ******************************************************************** void l_c_d() { // работаем с частотой от 15009 Гц до 30075 Гц (f=F_CPU/OCR1A/2) // длительность полупериода, не должна быть меньше 200+(dead_time/2) f_val = analogRead(A3); val_fr = map(f_val, 0, 1023, 533, 266); rpm = 16000000/val_fr/2; // частота в Герцах // работаем со скважностью // пауза между периодами, не должна быть меньше 200 и больше val_fr-200 d_val = analogRead(A2); int m_dt = val_fr-200; dead_time = map(d_val, 0, 1023, 199, m_dt); dtm = map(dead_time, 199, m_dt, 0, 100); // скважность в процентах //lcd.clear(); lcd.setCursor(0, 0); lcd.print("freq Hz"); lcd.setCursor(5, 0); lcd.print(rpm); lcd.setCursor(0, 1); lcd.print("dead time %"); lcd.setCursor(10, 1); lcd.print(dtm); //delay(400); }Это значит что дедтайм не может быть меньше 200 и больше длительности полупериода - 200volatile int dead_time; // пауза между периодами, не должна быть меньше 200 и больше val_fr-200
Как-то так.
Вот как-то так ....
volatile int dead_time = 91; // пауза между периодами, не должна быть меньше 92 и больше val_fr-92 void poluper1(void) { PORTD&=~(1<<5); // на пин 5 лог 0 PORTD|=(1<<4); // на пин 4 лог 1, формируем первый полупериод if(dead_time<92) {OCR1A=val_fr; uk=2;} // если пауза меньше 92 тогда она формироваться не будет else {OCR1A=val_fr-dead_time; uk=1;} // иначе уменьшаем полупериод на длительность паузы чтоб сохранить частотуВот полный скетч...
#include <LiquidCrystal.h> LiquidCrystal lcd(8,9,10,11,12,13); void (*mas[4]) (void)={poluper1, dead_time1, poluper2, dead_time2}; // массив указателей функций volatile int val_fr = 533; // длительность полупериода f=18000000/val_fr/2(Гц), volatile int dead_time = 91; // пауза между периодами, не должна быть меньше 92 и больше val_fr-92 byte uk=0; int f_val, d_val; float rpm, dtm; // ******************************************************************** // ******************************************************************** void setup() { DDRD = B11111000; // нужные пины на выход PORTD = B00000100; // на втором пине устанавливаем "единицу" TCCR1A=0; TIMSK1=0; // сбрасываем на всякий эти регистры // TCCR1A=0; TIMSK=0; TCCR1B=0; // мало ли что arduino IDE туда записало TCNT1=0; // сбрасываем счетный регистр таймера 1 OCR1A=0; // задаем частоту, в Гц, по формуле f=F_CPU/OCR1A/2 где F_CPU тактовая частота TIMSK1|=(1<<OCIE1A); // разрешаем генерацию прерывания таймера 1, по совпадению с регистром OCR1A // TIMSK|=(1<<OCIE1A); // Скетч будет работать на дуинах с atmega168/328. // При использовании atmega8 меняем все TIMSK1 на TIMSK (убираем еденицу). TCCR1B|=((1<<CS10)|(1<<WGM12)); // запускаем таймер 1 без предделителя в режиме СТС lcd.begin(16, 2); lcd.print("arduino PushPull"); } // ******************************************************************** // ******************************************************************** void loop(){ if ((PIND&(1<<2)) == 0) l_c_d(); // если на пин 2 лог 0 } // ******************************************************************** // ******************************************************************** ISR(TIMER1_COMPA_vect) { (*mas[uk])(); // вызываем функцию по указателю } // ******************************************************************** // ******************************************************************** // ******************************************************************** void poluper1(void) { PORTD&=~(1<<5); // на пин 5 лог 0 PORTD|=(1<<4); // на пин 4 лог 1, формируем первый полупериод if(dead_time<92) {OCR1A=val_fr; uk=2;} // если пауза меньше 92 тогда она формироваться не будет else {OCR1A=val_fr-dead_time; uk=1;} // иначе уменьшаем полупериод на длительность паузы чтоб сохранить частоту } void dead_time1(void) { PORTD&=~(1<<4); // на пинах 4 и 5 лог 0, формируем dead_time паузу OCR1A=dead_time; uk=2; } void poluper2(void) { PORTD&=~(1<<4); // на пин 4 лог 0 PORTD|=(1<<5); // на пин 5 лог 1, формируем второй полупериод if(dead_time<92) {OCR1A=val_fr; uk=0;} else {OCR1A=val_fr-dead_time; uk=3;} } void dead_time2(void) { PORTD&=~(1<<5); // на пинах 4 и 5 лог 0, формируем dead_time паузу OCR1A=dead_time; uk=0; } // ******************************************************************** // ******************************************************************** void l_c_d() { // работаем с частотой от 15009(533) Гц до 30075(266) Гц (f=F_CPU/OCR1A/2) // работаем с частотой от 20000(400) Гц до 30075(266) Гц (f=F_CPU/OCR1A/2) f_val = analogRead(A3); val_fr = map(f_val, 0, 1023, 533, 266); rpm = 16000000.0/val_fr/2.0; // частота в Герцах // работаем со скважностью // пауза между периодами, не должна быть меньше 92 и больше val_fr-91 d_val = analogRead(A2); int max_dt = val_fr-92; dead_time = map(d_val, 0, 1023, 91, max_dt); dtm = dead_time/(val_fr/100.0); // скважность в процентах if (dead_time < 92) dtm=0; lcd.setCursor(0, 0); lcd.print("freq Hz"); lcd.setCursor(5, 0); lcd.print(rpm); lcd.setCursor(0, 1); lcd.print("dead time %"); lcd.setCursor(10, 1); lcd.print(dtm, 1); //delay(400); }Ну вроде все так. Должно работать.
Это уже проверено - работает.... СПАСИБО ЗА ПОМОЩЬ !!!!!!!
минимальный дедтайм равен 92 - установлено экспериментально .
не помню где я её скачал, вот залил на обменник.
http://zalil.ru/34619125
а щуп делал такой http://www.illari.ru/electro/osc/
Можешь перезалить?
не помню где я её скачал, вот залил на обменник.
http://zalil.ru/34619125
а щуп делал такой http://www.illari.ru/electro/osc/
Можешь перезалить?
http://radioradar.net/programms/radiomeasurements/osc.html
Спасибо, но уже нашёл получше http://www.x-io.co.uk/serial-oscilloscope/
В принципе неплохо рабтает:
http://www.youtube.com/watch?v=zDzTNyC4qJQ
Ну вроде все так. Должно работать.
Здравствуйте, вы могли бы подсказать как запустить этот скейтч на Arduino DUE ? Спасибо.
Никак. На Arduino DUE стоит совсем другой микроконтроллер на базе ARM Cortex-M3. Там совсем другие регистры и работа с портами ввода-вывода, аппаратными таймерами и прерываниями отличается от AVR.
Получается нужно с нуля писать код под DUE? А я обрадовался... Я только начинаю с ардуино, поэтому пока для меня тёмный лес.
Получается нужно с нуля писать код под DUE?
Да, код будет практически с нуля.
Спасибо. Не подскажите как правильно начать обучаться программированию именно DUE?
Сложный вопрос. Приведенный выше код выходит за рамки концепции Arduino (скажем так, ардуино это упрощение для новчиков). Там идёт работа с микроконтроллером на более низком уровне. Поэтому нужно изучать не ардуино, а сам микроконтроллер. На Arduino DUE стоит микроконтроллер фирмы Atmel, а именно SAM3X8E. Чтобы писать подобный код, как выше - надо качать даташит на микроконтроллер (который, к слову, на английском языке) и читать, изучать. Можно ещё поискать какие-нибудь книжки или статьи на русском, но по МК на базе ARM Cortex-M от Atmel я практически ничего не встречал... Для начала научитесь программировать как Ардуино, без низкоуровневого обращения к микроконтроллеру. Потому как освоить это будет очень тяжело с наскоку.
Вам проще будет взять другую ардуину, где МК ATmega (ядро AVR). Они кстати тоже разные бывают и не всегда совместимы по низкоуровневому коду. Код выше вроде подходит для МК ATmega328P, который стоит на Arduino UNO, NANO, Pro Mini.
Ну или найти/написать другой код, который не будет использовать низкоуровневую работу с микроконтроллером.
Спасибо вам.
Всем привет. Вопрос: заметил что фронты иногда скачут, то тут то там, увеличивая и уменьшая скважность От этого как можно избавиться или это обычное дело ?
я так понял, никто не в курсе ???
вот даже снял видео, https://youtu.be/ET0zkCjuRsg
скрипт по сути тот же что в шапке.
Осциллограф програмный, через звуковуху? На меньшей частоте 1-2 кГц тоже самое?
3dmax -код в шапке слишком примитивен, на длительность фронтов влияет прерывание таймера0. Если нужен сигнал стабильной формы - к вашим услугам три аппаратных таймера.