по аппаратному прерыванию

RANDREY
Offline
Зарегистрирован: 10.06.2012

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

Надо - частота от 1 Гц до 300 Гц, и её можно менять с помощью кнопок, и ширину импульса. Операции с меню не должны заикать звук. Пока написал это

 

#include <LiquidCrystal.h>

LiquidCrystal lcd(8, 9, 10, 11, 12, 13); // инициализируем LCD, указывая контакты данных
#define TOGGLE_IO        40  //Arduino pin to toggle in timer ISR

unsigned int latency;
unsigned int latencySum;
unsigned int sampleCount;
unsigned char timerLoadValue;
int freq=500;
int analogkey2;

#define TIMER_CLOCK_FREQ 2000000.0 //2MHz for /8 prescale from 16MHz

unsigned char SetupTimer2(float timeoutFrequency){
  unsigned char result; 
  result=(int)((257.0-(TIMER_CLOCK_FREQ/timeoutFrequency))+0.5); 
  TCCR2A = 0;
  TCCR2B = 0<<CS22 | 1<<CS21 | 0<<CS20; 
  TIMSK2 = 1<<TOIE2;
  TCNT2=result; 
  return(result);
}

 //Timer2 overflow interrupt vector handler
ISR(TIMER2_OVF_vect) {
  digitalWrite(TOGGLE_IO,!digitalRead(TOGGLE_IO));
  latency=TCNT2;
  TCNT2=latency+timerLoadValue; 
  
  digitalWrite(49,HIGH);
  delay (freq);
  digitalWrite(49,LOW);
}

void setup(void) {
  pinMode(TOGGLE_IO,OUTPUT);
  pinMode(49,OUTPUT);  // LED TESTER
  
  lcd.begin(20, 4); // указываем размерность экрана и начинаем работать 
  lcd.clear();
  lcd.setCursor(0, 0); 
  lcd.print("Timer2 Test");
  lcd.setCursor(10, 0); 
  lcd.print(timerLoadValue);
  delay(1000); 
  timerLoadValue=SetupTimer2(44100);
 // Serial.println(timerLoadValue,HEX);
}

void loop(void) {

  delay(1000);   //Accumulate ISR latency every 10ms.
  latencySum+=latency;
  sampleCount++;
  
  if(sampleCount>99) {
    float latencyAverage;
    float loadPercent;
    
    latencyAverage=latencySum/100.0;
    sampleCount=0;
    latencySum=0;
    loadPercent=latencyAverage/(float)(256.0-timerLoadValue);
    loadPercent*=100; //Scale up from ratio to percentage;
    latencyAverage-=(int)latencyAverage;
 spy:    
 analogkey2 = analogRead (2);
   if (analogkey2>650 && analogkey2<750){ // ->>
   if (freq>30) {
    freq=freq-10;
                   };
                   };// if
   
   if (analogkey2>300 && analogkey2<380) { // <--
   if (freq<1000) {
     freq=freq+10;
                     };
                     }; // if
   lcd.setCursor(5,1);
   lcd.print (freq);
   goto spy;
  }
}

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

В Delphi к примеру в основной программе достаточно было вызвать CreateThread и в теле потока написать подобие ВКЛ.LED, пауза1,ВЫКЛ.LED,пауза2. и управляя переменными пауз из основного потока программы достигли бы нужного.

RANDREY
Offline
Зарегистрирован: 10.06.2012

нашел лучший пример,всё обернуто в обертку и вынесено в cpp, хотя не рабочий, с помощью библиотеки MsTimer2.cpp (добавил в либры, плата 2560, среда 1.0.1)

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

 

#include <LiquidCrystal.h>
#include <MsTimer2.h>

LiquidCrystal lcd(8, 9, 10, 11, 12, 13); // инициализируем LCD, указывая контакты данных
int freq=500;
int analogkey2;

void flash_led() //обработчик прерывания 
{ 
  digitalWrite(49,HIGH);
  delay(1000);
  digitalWrite(49,LOW);
}

void setup(void) {
  pinMode(49,OUTPUT);  // LED TESTER

 MsTimer2::set(5000, flash_led); // ms период 
 MsTimer2::start();  //включить таймер
  
  lcd.begin(20, 4); //
  lcd.clear();
  lcd.setCursor(0, 0); 
  lcd.print("Timer2 Test");
  delay(1000); 

}

void loop(void) {
 delay(1000);   //Accumulate ISR latency every 10ms.

 spy:    
 analogkey2 = analogRead (2);
   if (analogkey2>650 && analogkey2<750){ // ->>
   if (freq>30) {
    freq=freq-10;
                   };
                   };// if
   
   if (analogkey2>300 && analogkey2<380) { // <--
   if (freq<1000) {
     freq=freq+10;
                     };
                     }; // if
   lcd.setCursor(5,1);
   lcd.print (freq);
   delay(100);
   goto spy;
}

 

leshak
Offline
Зарегистрирован: 29.09.2011

Бздынь.....

1. Что у вас с форматирование кода? Нажмите хотя-бы CTRL-T в Arduino IDE
2. GOTO - забудте про существование этого оператора ВООБЩЕ. Пишите так как будто не такого слова. Обходитесь другими средствами.
3. Раз нехотите заикание - откажитесь от функции delay(). И уж тем более в обработчике прервыния (тут нет многопоточности). Только millis() и таймеры (и то, аккуратно с ней обработчике). Вообщем начинайте с базовых примеров в разделе ПрограммированиеМигаем светодиодом без delay() , а не сразу "экраны", "генераторы" и т.п.

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

http://arduino.ru/forum/programmirovanie/arduino-push-pull

 

#include <LiquidCrystal.h>
LiquidCrystal lcd(8,9,10,11,12,13);
void (*mas[2]) (void)={poluper1, poluper2}; 	// массив указателей функций       
volatile int val_fr = 533.0; 			// длительность полупериода f=18000000/val_fr/2(Гц),
byte uk=0;
int f_val, d_val, fkr; 
float rpm, frc;
//	********************************************************************
//	********************************************************************
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)	{
OCR1A=val_fr;
(*mas[uk])(); 							// вызываем функцию по указателю
	}
//	********************************************************************
//	********************************************************************
void poluper1(void)	{	
PORTD&=~(1<<5); 						// на пин 5 лог 0
PORTD|=(1<<4); 							// на пин 4 лог 1, формируем первый полупериод
uk=1; 				}

void poluper2(void) {	
PORTD&=~(1<<4); 						// на пин 4 лог 0
PORTD|=(1<<5); 							// на пин 5 лог 1, формируем второй полупериод
uk=0;				}
//	********************************************************************
//	********************************************************************	
void l_c_d() { 
//	работаем с частотой от 15009(533) Гц до 30075(266) Гц (f=F_CPU/OCR1A/2)
//	работаем с частотой от 25000(320) Гц до 30075(266) Гц (f=F_CPU/OCR1A/2)
f_val = analogRead(A3);     
frc = map(f_val, 0, 1023, 320.0, 266.0);
d_val = analogRead(A2); 
fkr = map(d_val, 0, 1023, -10.0, 10.0);

val_fr = frc+fkr;
rpm = 16000000.0/val_fr/2.0; 				//	частота в Герцах

lcd.setCursor(0, 0);
lcd.print("freq          Hz");	
lcd.setCursor(5, 0);
lcd.print(rpm);
lcd.setCursor(0, 1);
lcd.print("correction      ");	
lcd.setCursor(12, 1);
lcd.print(fkr);
//delay(400);
}

 

RANDREY
Offline
Зарегистрирован: 10.06.2012

ага спасибо, чуток доходит, жалко железной мультипоточности нет.

Goto если правильно использовать, то ничего в этом нет,боятся только новички. Заместо него используют команды условного перехода, if (0==0)  проверяя, то что всегда равно чему-то. loop тот же безусловный переход к адресу первого оператора loop.

Puhlyaviy
Puhlyaviy аватар
Offline
Зарегистрирован: 22.05.2013

leshak пишет:

3. Раз нехотите заикание - откажитесь от функции delay(). И уж тем более в обработчике прервыния (тут нет многопоточности). 

таки есть многопоточность :) в DUE можно запустить несколько loop в одной программе..

RANDREY
Offline
Зарегистрирован: 10.06.2012

это уже другая дурина) там чип другой.

Ещё про GOTO - иногда надо часть кода пропустить (перепрыгнуть), в DELPHI можно было ненужный блок взять в {} и он заремится, в этом языке это служебное слово, придется или на каждой строчке печатать // или брать весь ненужный код в блок условного кода тупо if (0==0), а быстрее добавить пару строк

goto pusk;
тут ненужный код на несколько страниц
pusk:

 

Puhlyaviy
Puhlyaviy аватар
Offline
Зарегистрирован: 22.05.2013

ну если вы не обратили внимания то есть еще вариант

/*   .... строчки которые ремарките..... */

и можеет хоть весь код так отметить

RANDREY
Offline
Зарегистрирован: 10.06.2012

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

leshak
Offline
Зарегистрирован: 29.09.2011

Ну вот не нужно тут петь диферамбы GOTO. Как раз новички его и не боятся. Обычно наличие GOTO говорит либо о том что это новичок который плохо знает синтаксис языка и лепить "костыль GOTO" куда угодно. Либо это писал человек привыкший к ASM и опять-таки ленящейся освоится с новым языком. Действующий по принципу "заработало и ладно".

Я не новичок. Про GOTO знаю. Моим первым языком был GW-BASIC с нумерацией строк. Где-то 1988-1989 году. Там без GOTO - было вообще никак :)
Но с переходом TurboPascal 3.0 (упоминаемая вами Delphi, на котором тоже не один год писали - его потомок) - надобность в GOTO отпала совершенно. Нет никаких "правильных использований GOTO". Само его использование - уже неправильно. Резко падает сопровождаемость и читабельность кода.

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

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

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

Есть еще условная компиляция. Директивы препроцессора #if #ifdef #ifndef. Которая позволяет "включить/выключить" сразу несколько кусков кода. Не занимать не нужным кодом такты процессора и память. А не решать это в runtime

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

Ну и в конце концов. if(0==0) - никак не поможет в вашей ситуации. Код будет выполнен. Эту условие - всегда истино. Вам нужно было if(0==1) или более удобочитаем if(false) или аскетчино if(0)

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

RANDREY
Offline
Зарегистрирован: 10.06.2012

Конечно плохо знаю, в пределах сайта по контроллеру, на другие не захожу - из специфики языка. И учеба по мере задач - если нужно прерывание их и разбираю, а не как работает функция tone().

ASM да было (jnz). BASIC был, ZX-SPECTRUM - там действительно никак, переход на строчку. GOSUB ещё был....Delphi начал с 4.0 (там уже VCL элементы) goto удобно использовать во врезках asm код end; благо язык позволяет в любом месте перейти на asm."существование большого блока кода " - это важный код который охота пропустить при отладке, это меню, вместо нудного и долгого выбора пункта в меню и выбора значения кнопками, достаточно прописать в начале эти настроенные переменные, в том числе номер меню и сделать переход) Но уговаривать не буду, кому как легче, после отладки данные команды затираются.

Кстати на Mega2560 не работает библиотека <MsTimer2.h>

#include <MsTimer2.h>

void flash_led() //обработчик прерывания 
{ 
  digitalWrite(49,HIGH);
}
void setup(void) {
  pinMode(49,OUTPUT);  // LED TESTER

 MsTimer2::set(500, flash_led); // ms период 
 MsTimer2::start();  //включить таймер
}

void loop(void) {
}

не зажигает светодиод, если в сетап вынести - digitalWrite(49,HIGH); лед горит, исправен.

leshak
Offline
Зарегистрирован: 29.09.2011

Залезте в библиотеку, поробуйте найти все условия содержащие

||  defined(__AVR_ATmega1280__) 

И заменить/дописать на

||  defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)

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

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

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

P.S. Стыдно "не отсуствие знание", а упроствование в этом.

RANDREY
Offline
Зарегистрирован: 10.06.2012

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

Уже дошло - взяв библиотеку FlexiTimer2, там ATmega2560 уже прописано, надо заглядывать в *.cpp, родное IDE 1.0.1 не хочет открывать, а в блокноте каша.

Ну что ж с профи говорить, извините.

sva1509
Offline
Зарегистрирован: 07.12.2012

Доброго времени суток !

1) Таймер 2 - 8-ми битный и по этому от 1 до 300 не получится, если только Вы не собираетесь таймером просто вести счет для CPU

2) Оператор GOTO использовать никто не запрещает, НО среди С-шников это считается дурным тоном. Когда это началось я не знаю, но когда я в 1992 переходил на С это уже было.

3) Паралельных потоков на 1 CPU не бывает - это просто быстрое переключение по прерыванию от таймера (сам когдато делал многозадачность 286/386)

4) Вы хотите использовать LCD и генерировать реалтайм сигнал - для этого необходимо хотябы что то одно делать по прерыванию (в фоне). Так как Вы используете LiquidCrystal который использует паузы (delay delayMicroseconds) то остается сделать генератор сигнала в фоне. Так как у Вас 300 градаций то таймеры 0/2 Вам не подходят. Используйте 1/3 они 16-ти битные. К примеру выберите режим FastPWM с ограничением по OCRA настройте делитель на /256 таким образом 1 сек = 62500 счетов скважность в примеру 50% 62500 / 2. Длительность заносим в OCRA скважность в OCRB и запускаем таймер. Более высокую частоту можете расчитать сами.

 

RANDREY
Offline
Зарегистрирован: 10.06.2012

Доброго.

1) оказалось до 100 гц, далее просто не надо. Для LED например такая частота мерцаний сливается в почти ровное свечение.

2) да и в паскале. В бейсике обязательно, т.к там не было функций-процедур (про ZX-BASIC), в надстройках в виде BETA-BASIC,LASER,MEGA-BASIC было слабое подобие, уже не помню. Поэтому там и этот оператор был необходим.

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

3) конечно, это прозрачно для программиста.

4) пока работает. Отдельный генератор помимо программы.