// Timer Fast PWM pulse
#define KEY_START 2
#define PIN_START 9 //OC1A
#define PIN_STOP 10 //OC1B
#define start_pulse() PINB = 1<<(PIN_START - 8), PINB = 1<<(PIN_START - 8)
#define get_key(x) !digitalRead(x)
#define start_timer() TCCR1B = 1<<WGM12 | 1<<WGM13 | 1<<CS10
#define stop_timer() TCCR1B = 1<<WGM12 | 1<<WGM13 | 0<<CS10
void setup() {
Serial.begin(9600);
Serial.println("Timer Fast PWM pulse " __DATE__ " " __TIME__);
pinMode(KEY_START, INPUT_PULLUP);
pinMode(PIN_START, OUTPUT);
pinMode(PIN_STOP, OUTPUT);
digitalWrite(PIN_START, HIGH);
digitalWrite(PIN_STOP, HIGH);
cli();
TCCR1A = 0;
TCCR1B = 0;
OCR1A = 55;
OCR1B = 54;
//set to fast PWM mode 15, TOP = OCR1A
//clear OC1B on Compare Match, set OC1B at BOTTOM (inverting mode)
TCCR1A = 1<<COM1B1 | 0<<COM1B0 | 1<<WGM10 | 1<<WGM11;
stop_timer();
TIMSK1 = 1<<TOIE1;
TIFR1 = 1<<TOV1;
sei();
start_timer();
}
void loop() {
if (get_key(KEY_START)) {
TCNT1 = 0;
start_pulse();
start_timer();
delay(500);
}
}
ISR(TIMER1_OVF_vect) {
stop_timer();
}
во, наверно это “оно”, то чего я не нашел
Знаю по СТМ32, что при старте таймера надо флаг прерывания сразу ресетить, чтобы все регистры верно инициализировались… но что-то не нашел этого в АВР.
Спасибо, вечером в железе гляну.
Задача чисто теоретическая или практическая?
Если практическая, по поставить инвертор и дело с концом.
проще поставить пустой стакан … это же классика
Дело в том, что OCR1A обновляется не сразу, а по прерыванию, и поэтому вы используете предыдущее значение OCR1A. При инициализации вы его задаете = 54, оно и используется в первом цикле не зависимо от того, что вы запишите потом в OCR1A. А после первого цикла OCR1A обновляется и становится равным 50, как вы и ожидаете.
В окончательной версии расширьте границы запрета прерываний до конца setup. И добавьте запрет в функции myTest()
Большое спасибо! Надеюсь это был последний не решенный вопрос . Завтра надеюсь потестирую полностью все в сборке и отпишусь, работает ли все как нужно.
То есть мне надо будет остановить таймер > выключить прерывания > задать новое значение для OCR1A > включить опять прерывания?
Про прерывания я писал не применительно к OCR1A, а просто потому, что и в существующем алгоритме их лучше там запретить. Как в данном алгоритме принудительно обновить OCR1A я пока не думал.
@Evgeniy в чём смысл применения таймера в этой задаче, что бы что?
Выскажу свое мнение.
Я тоже предлагал считать циклы. Но решение с таймером пришло быстрее, чем я нашел функцию для формирование произвольной точной задержки в тактах (или убедился бы что не найду, и принялся бы писать сам). Как вариант решения, метод с таймером мне показался интересно - познавательным.
для “познавательности” хотелось бы найти вариант решения только на таймерах. Пока все что я вижу в ветке - у всех один пин переключается через порт и только второй через таймер.
У меня просто не тот уровень, что бы делать это на ассемблере. Его я не знаю. Ну разве что команду nop задействовал.
А таймер как то понятнее и проще и то закопался в деталях
Любое прерывание, с моей точки зрения, это зло в одно процессорной / одно ядерной системе, так что ТС проще выучить stm32 для своей задачи.
Вроде получилось сделать только на compare match выходах, без генерации импульса вручную в прерывании.
Задержка между сигналами стабильна и равна точно периоду таймера , длительность каждого импульса 1 тик. В момент запуска таймера в setup() проскаивают несколько паразитных импульсов, однако далее импульсы генерятся в loop() только по условию (flag == true) строго парами. Порядок импульсов по каналам в каждой паре меняется, AB - BA -AB -BA
// interval between falling edge 1st pulse and
// raising edge 2nd pulse (in timer ticks)
#define AB_INTERVAL 1000
volatile uint8_t cc =0;
void setup() {
DDRB |= (1<<PB1) | (1<<PB2);
PORTB |= (1<<PB1) | (1<<PB2);
cli(); //stop interrupts for till we make the settings
TCCR1A = 0; // Reset entire TCCR1A to 0
TCCR1B = 0; // Reset entire TCCR1B to 0
TCNT1 = 0; //reset clock value
////set to fast PWM mode 14 via WGM bits(count from 0 to ICR1):
////TOP = ICR1
TCCR1A |= (0 << WGM10) | (1 << WGM11) ;
TCCR1B |= (1 << WGM12) | (1 << WGM13) ;
////Timer/Counter1 Interrupt Mask Register:
TIMSK1 |= (1 << OCIE1A); // enable compare OCR1A itrerrupt
TIMSK1 |= (1 << OCIE1B); // enable compare OCR1B itrerrupt
//TIMSK1 |= (1 << TOIE1); //enable overflow vector interrupt
OCR1A = AB_INTERVAL - 1; //match 1 tick before TOP
ICR1 = AB_INTERVAL; //top value
OCR1B = 0xFFFE; // never match value
//Clear OC1A/OC1B on Compare Match, set OC1A/OC1B at
// BOTTOM (non-inverting PWM mode)
TCCR1A |= (1 << COM1A1) | (1 << COM1B1);
// reset all irq flags
TIFR1 = B111; // (1<<TOV1)| (1<< OCFB) |(1<< OCFA);;
sei();
TCCR1B|= 1; //start timer at 16 MHz, no prescaling
}
ISR(TIMER1_COMPB_vect) {
OCR1A = AB_INTERVAL -1; // match 1 tick before TOP
OCR1B = 0xFFFE; // never match value
if (cc==0) TCCR1B &= ~bit(0); // stop the timer
cc = !cc;
}
ISR(TIMER1_COMPA_vect) {
OCR1A = 0xFFFE; // never match value
OCR1B = AB_INTERVAL -1; // match 1 tick before TOP
if (cc==0) TCCR1B &= ~bit(0); // stop the timer
cc = !cc;
}
void loop() {
static bool flag = true;
if (flag)
{
delay(100);
TCCR1B|= 1; //start timer at 16 MHz, no prescaling
flag = false;
}
}
Спасибо за пример. Думаю он пригодится. Так как возник ньюанс с использованием OCR1A как TOP значение.
Согласно даташиту обновление его значения происходит только на следующий такт, после того как TCNT1 поравняется с OCR1A(двойная буферизация), то есть нельзя при остановленном таймере, перезаписать его. По крайней мере мне не удалось. Пробовал перезаписывать tov1 бит, не помогло. Может как то можно напрямую в память записать его по постоянному адресу.
А ICR1 вроде как сразу обновляется сразу при записи. Буду с ним пробовать
Попробуйте код для обновления OCR1A в вашем случае. Не симуляторе работает нормально, но симуляторы в этом случае могут быть не точными.
void myTest() {
//TCNT1 = 0; //reset clock value
cli();
// update OCR1A
TCCR1B &= ~bit(0); // stop timer
TCNT1 = OCR1A; // reset TCNT1 on next clock
TCCR1B |= B1; // start timer
asm volatile("nop\n\t"); //
asm volatile("nop\n\t");
TCCR1B &= ~bit(0); // stop timer
TIFR1 = bit(TOV1); // clear int flag TOV1
TCNT1 = 0; //reset clock value
//посылаем СТАРТ (D9)
PORTB = B101; //8 H; 9 L
PORTB = B111; //8 H; 9 H 2clocks
TCCR1B |= B1; //T1 ON; running at 16 MHz no prescaling; //1 clock
sei();
}
Что то не работает как нужно.я тоже думал просто запускать в холостую таймер1 что бы подготовить нужное значение. Но это лишний импульс.
А при
Стоповый подготовительный импульс выглядит странно. При OCR1A-1 лучше.
Попробую с icr1
Да, предыдущей мой совет был неправильным.
Возможно ли код привести в:
A > задержка > В
Например по нажатию кнопки. И не возникнет ли нюансов, если при остановленном таймере я буду задавать новое значение ICR1, между нажатиями кнопки.
Не дают сегодня покодить, постоянно отвлечение идёт. Хочется быть уверенным, что такое в итоге возможно и я урывками не трачу время впустую
не вполне понимаю.
А сейчас код какой, не А - задержка - В ?
Оно чередуется a>b>a итд
А надо что бы нажали a>b, нажали a>b итд
И предварительно выбрав задержку перед пуском. С wgm15 и OCR1A придется давать “настроечный” стоповый сигнал что бы поменять задержку, если вдруг придется давать отличную от предустановленной задержки, что не очень желательно. Вот и цепляюсь за этот режим