Arduino Push-pull

renoshnik
Offline
Зарегистрирован: 11.04.2013

Нужна помощь. Если где-то уже есть решения подскажите пожалуйста.

за основу было взято 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...

но что-то не могу понять как сделать двухтактный выход .... 

 

renoshnik
Offline
Зарегистрирован: 11.04.2013


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 Гц, уже лучше но недостаточно...

Evgen
Evgen аватар
Offline
Зарегистрирован: 10.06.2011

Даже при использований прерываний и таймера 1 в режиме СТС, шаг регулировки частоты, при 19.36 кГц, будет около 50 Гц, и чем выше частота тем он будет больше. Соответственно чем ниже частота тем шаг меньше. Так что 100 Гц это нормальный шаг.

renoshnik
Offline
Зарегистрирован: 11.04.2013

вот такой код получился

#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 пине...

если бы сделать как-то расчёт частоты для отображения на экране, но пока не придумал как....

Evgen
Evgen аватар
Offline
Зарегистрирован: 10.06.2011

Кароч всетаки придется использовать прерывания и таймер 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 (убираем еденицу).

 

renoshnik
Offline
Зарегистрирован: 11.04.2013

 ---  что-то не хочет компилироваться ...

sketch_jun30a.ino: In function 'void __vector_11()':
sketch_jun30a:19: error: 'PD4' was not declared in this scope

 

 ---  на счёт второго полупериода вы правы, недосмотрел. Хотя в моём случае это не критично. Во время генерации мерял прибором отдельно полупериоды, показания идентичны. При удержании кнопки на 5 пине держится единица пока кручу резистор но на работу устройства это особо не влияет (хотя можно попробовать обнулить этот пин).

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

Evgen
Evgen аватар
Offline
Зарегистрирован: 10.06.2011

renoshnik пишет:

 ---  что-то не хочет компилироваться ...

Замените в 19 строке PD4 на 4.

renoshnik
Offline
Зарегистрирован: 11.04.2013

всё, запустил !!!!

у вас просто опечатка...  if(PORTD & (1<<PIND4))PORTD = B00100100;

Отлично, шаг тут получается 30 Гц .....  

Теперь нужно прикрутить регулировку и индикацию - продолжаю мучать прерывания.

renoshnik
Offline
Зарегистрирован: 11.04.2013

попытался сделать так 

#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; ?????

Evgen
Evgen аватар
Offline
Зарегистрирован: 10.06.2011

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

ISR(TIMER1_COMPA_vect)
{
  if(PORTD & (1<<4))PORTD = B00100100; 
  else PORTD = B00010100; // если пин4 установлен формируем второй полупериод, если нет, первый
  OCR1A=val_fr;
}

 

renoshnik
Offline
Зарегистрирован: 11.04.2013

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() в обработчике прерывания с пропорциональным увеличением частоты, но что-то в цифрах запутался....

 

 

 

Evgen
Evgen аватар
Offline
Зарегистрирован: 10.06.2011

renoshnik пишет:
может ещё подскажете как сюда добавить регулировку скважности ...

Вы хотите задавать разную длительность первого и второго полупериода?

renoshnik пишет:
delayMicroseconds() в обработчике прерывания

Не не, так делать нельзя, работать не будет.

renoshnik
Offline
Зарегистрирован: 11.04.2013

Evgen пишет:

renoshnik пишет:
может ещё подскажете как сюда добавить регулировку скважности ...

Вы хотите задавать разную длительность первого и второго полупериода?

несовсем. нужно сделать dead time - задержки между полупериодами

На рис. 4 показана форма импульсов с малой скважностью, на рис. 5 - с большей скважностью, а на рис. 6 - с максимальной скважностью.

только просто сделать задержку между полупериодами наверное не получится т.к. за время переполнения таймера будеет разное колличество импульсов (будет меняться частота).

нужно сохранять частоту  при изменении скважности .

renoshnik
Offline
Зарегистрирован: 11.04.2013

Вот реализация того, что сделано насегодня...

http://www.youtube.com/watch?v=rQ368CWKoCU&feature=youtu.be

Evgen
Evgen аватар
Offline
Зарегистрирован: 10.06.2011

Вот. Можно регулировать скважность переменной 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;
}
renoshnik
Offline
Зарегистрирован: 11.04.2013

Огромное спасибо !!!!

если я правильно понял, то переменные

volatile int val_fr = 500; // длительность полупериода, не должна быть меньше 50
volatile int dead_time; // пауза между периодами, не должна быть меньше 50 и больше (val_fr*2)-100

void loop()
{
 // основная программа 
}

можно просто изменять в основной программе.

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

Evgen
Evgen аватар
Offline
Зарегистрирован: 10.06.2011

Да, все так. Только я этот код не проверял, пробуйте сами.

renoshnik
Offline
Зарегистрирован: 11.04.2013

Проверил, но что-то не то получается....

вопервых при значении val_fr = 500 в "базовом" варианте реальная частота равна 16 кГц и соответствует расчётам f=16000/500/2

в новом скетче, с частотой что-то не понятное и скважность ведёт себя странно...

 

Evgen
Evgen аватар
Offline
Зарегистрирован: 10.06.2011

На последних двух скриншотах пауза между периодами задана не правильно. Я же писал там в комментах, она не должна быть больше чем (val_fr*2)-100. А вот почему остальное не работает счас буду думать.

А что за программа осциллогрф? Дайте ссылочку где скачать можно.

renoshnik
Offline
Зарегистрирован: 11.04.2013

не помню где я её скачал, вот залил на обменник.

http://zalil.ru/34619125

а щуп делал такой http://www.illari.ru/electro/osc/

только какая-то фигня с этим осцилографом, цепляю один щуп на пин, второй на массу а на осцилографе всёравно ДВЕ полуволны..... 

 

 

renoshnik
Offline
Зарегистрирован: 11.04.2013

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



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;
}

 

Evgen
Evgen аватар
Offline
Зарегистрирован: 10.06.2011

Разобрался я почему неправильно работало. У дуины банально не хватает быстродействия.

volatile int val_fr = 500; 
volatile int dead_time = 300;

Значения этих переменных, если другими словами, это такты процессора. Если какаянить из переменных равна 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;
}

Теперь вопрос. Вы откуда осциллограммы снимаете, с пинов ардуины? С четвертого и пятого или только одного? У вас на осциллограммах только один канал показан, должно быть два. Вы второй специально отключили? Должно быть вот так:

renoshnik
Offline
Зарегистрирован: 11.04.2013

у меня штекер "моно" .... 

подключался к пин 4 и пин 5 ... хотя ессли цеплял пин+масса картинка не менялась... 

 

А, почему у вас разная чувствительность на каналах на первом 1000, а на втором 500 ?

Evgen
Evgen аватар
Offline
Зарегистрирован: 10.06.2011

renoshnik пишет:
А, почему у вас разная чувствительность на каналах на первом 1000, а на втором 500 ?

Так было по умолчанию при включении программы, я не трогал эту настройку.

renoshnik пишет:
у меня штекер "моно" ....

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

А в конечном устройстве период будет вот такой

renoshnik
Offline
Зарегистрирован: 11.04.2013

 

Самое странное, что у меня картинка сигнала НЕ меняется хоть с одного пина снимаю, хоть с двух ?!?!?!

вот пример, с двух пинов ....

вот с одного пина и массы

 

Запустил последний скетч, вот что получилось (сигнал снимал с двух пинов)

 

с такими настройками на картинке колличество импульсов не меняется, а частота (в цифрах) скачет.

 

Но непонятно другое !!!!! почему "мёртвая зона" только с одной стороны ???? 

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

 

 

 

Evgen
Evgen аватар
Offline
Зарегистрирован: 10.06.2011

Вы каим оброзом осциллограммы снимаете? Надо один канал линейного входа звуковой карты подключить к четвертому пину, другой к пятому. Я подключал через резисторы 100 Ом (можно побольше поставить) и ничего не сгорело. Для этого нужен разъем мини-джек "стерео". Дуину нужно от того-же компа запитывать которым осциллограммы снимаете.

renoshnik
Offline
Зарегистрирован: 11.04.2013

У меня джек "моно" поэтому входы карты объеденены, и получается, что пин 4 на входы, пин 5 на массу.

renoshnik
Offline
Зарегистрирован: 11.04.2013

Помогите, я кажется запутался...

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 (формирование полупериода) будет выполняться в течении таймера "мёртвой зоны"...

Evgen
Evgen аватар
Offline
Зарегистрирован: 10.06.2011

Немного не так. Строка OCR1A=dead_time задает сколько будет длиться пауза между полупериодами. Эта функция не выполняется сколько-то долго, она просто устанавливает лог 0 на пинах 4 и 5 (ну в данном случае только на пине 4 потому, что 5 пин и так в нуле), потм задает через сколько тактов процессора должно вызваться это-же прерывание (OCR1A=dead_time) и изменяет переменную uk=2, чтобы в следующий раз в обработчике прерывания вызвалась не опять она-же, а уже другая функция, которая формирует полупериод.

renoshnik
Offline
Зарегистрирован: 11.04.2013

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;

теперь нормально видно, что "мёртвая зона" отрабатывается правильно, с двух сторон от полупериода.

Только при изменении дедтайма изменяется частота... 

Думаю, что частоту (полупериод) и скважность (дедтайм) нужно расчитывать от одного числа. Тоесть "полупериод" плюс "дедтайм" должно быть равно при изменении одного из слагаемых.

В общем есть чем заниматься на выходных...

Ещё раз ОГРОМНОЕ СПАСИБО !!! за помощь...

Evgen
Evgen аватар
Offline
Зарегистрирован: 10.06.2011

renoshnik пишет:
хочется узнать на сколько это правильно

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

Во второй стрчке лучше оставить тип byte. Зачем отводить под переменную два байта когда хватит и одного? Хотя конечно работать будет, но старший байт будет не задействован.

renoshnik пишет:
при изменении дедтайма изменяется частота...

А вот на счет ухода частоты это надо подумать.

Evgen
Evgen аватар
Offline
Зарегистрирован: 10.06.2011

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

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

volatile int dead_time; // пауза между периодами, не должна быть меньше 200 и больше val_fr-200

Как-то так.

renoshnik
Offline
Зарегистрирован: 11.04.2013

Пока не могу проверить в "железе" ....





#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); 	// &nbsp; &nbsp;скважность в процентах


//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);
}

 

renoshnik
Offline
Зарегистрирован: 11.04.2013

Evgen пишет:

Это значит что дедтайм не может быть меньше 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;} 	// иначе уменьшаем полупериод на длительность паузы чтоб сохранить частоту


 

renoshnik
Offline
Зарегистрирован: 11.04.2013

Вот полный скетч...

#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);
}

 

Evgen
Evgen аватар
Offline
Зарегистрирован: 10.06.2011

Ну вроде все так. Должно работать.

renoshnik
Offline
Зарегистрирован: 11.04.2013

Это уже проверено - работает....  СПАСИБО ЗА ПОМОЩЬ !!!!!!!

минимальный дедтайм равен 92 - установлено экспериментально .

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

renoshnik пишет:

не помню где я её скачал, вот залил на обменник.

http://zalil.ru/34619125

а щуп делал такой http://www.illari.ru/electro/osc/

Можешь перезалить?

renoshnik
Offline
Зарегистрирован: 11.04.2013

HWman пишет:

renoshnik пишет:

не помню где я её скачал, вот залил на обменник.

http://zalil.ru/34619125

а щуп делал такой http://www.illari.ru/electro/osc/

Можешь перезалить?

 

http://radioradar.net/programms/radiomeasurements/osc.html

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Спасибо, но уже нашёл получше http://www.x-io.co.uk/serial-oscilloscope/

В принципе неплохо рабтает:

http://www.youtube.com/watch?v=zDzTNyC4qJQ

 

 

sparco19
Offline
Зарегистрирован: 27.06.2016

Evgen пишет:

Ну вроде все так. Должно работать.

Здравствуйте, вы могли бы подсказать как запустить этот скейтч на Arduino DUE ? Спасибо.

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

Никак. На Arduino DUE стоит совсем другой микроконтроллер на базе ARM Cortex-M3. Там совсем другие регистры и работа с портами ввода-вывода, аппаратными таймерами и прерываниями отличается от AVR.

sparco19
Offline
Зарегистрирован: 27.06.2016

Получается нужно с нуля писать код под DUE? А я обрадовался... Я только начинаю с ардуино, поэтому пока для меня тёмный лес.

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

sparco19 пишет:

Получается нужно с нуля писать код под DUE?

Да, код будет практически с нуля.

sparco19
Offline
Зарегистрирован: 27.06.2016

Спасибо. Не подскажите как правильно начать обучаться программированию именно DUE?

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

Сложный вопрос. Приведенный выше код выходит за рамки концепции Arduino (скажем так, ардуино это упрощение для новчиков). Там идёт работа с микроконтроллером на более низком уровне. Поэтому нужно изучать не ардуино, а сам микроконтроллер. На Arduino DUE стоит микроконтроллер фирмы Atmel, а именно SAM3X8E. Чтобы писать подобный код, как выше - надо качать даташит на микроконтроллер (который, к слову, на английском языке) и читать, изучать. Можно ещё поискать какие-нибудь книжки или статьи на русском, но по МК на базе ARM Cortex-M от Atmel я практически ничего не встречал... Для начала научитесь программировать как Ардуино, без низкоуровневого обращения к микроконтроллеру. Потому как освоить это будет очень тяжело с наскоку.

Вам проще будет взять другую ардуину, где МК ATmega (ядро AVR). Они кстати тоже разные бывают и не всегда совместимы по низкоуровневому коду. Код выше вроде подходит для МК ATmega328P, который стоит на Arduino UNO, NANO, Pro Mini.

Ну или найти/написать другой код, который не будет использовать низкоуровневую работу с микроконтроллером.

sparco19
Offline
Зарегистрирован: 27.06.2016

Спасибо вам.

3dmax
Offline
Зарегистрирован: 09.05.2016

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

3dmax
Offline
Зарегистрирован: 09.05.2016

я так понял, никто не в курсе ???

вот даже снял видео, https://youtu.be/ET0zkCjuRsg

скрипт по сути тот же что в шапке.

Evgen
Evgen аватар
Offline
Зарегистрирован: 10.06.2011

Осциллограф програмный, через звуковуху? На меньшей частоте 1-2 кГц тоже самое?

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

3dmax -код в шапке слишком примитивен, на длительность фронтов влияет прерывание таймера0.   Если нужен сигнал стабильной формы - к вашим услугам три аппаратных таймера.