Точная задержка между двумя короткими импульсами

Джиттер из за прерываний - так и должно быть.
С точностью до такта - либо из слипа, либо без прерываний.

а если листинг глянуть?

Всем привет! Меня 707-ой в тему позвал. Бегло ознакомился.

Я правильно понял, что нужно сделать два импульса с точной задержкой между ними с точностью до одного такта процессора?
Если правильно понял, то на прерываниях теоретически можно сделать точность только в два такта. Задержку с точностью в один такт нужно делать на прямом цикле NOP-ов.

Я о том, что нужно написал? Или что-то не так понял?

1 лайк

Да. В догонку. Может точность иногда и три такта быть. Так как есть команды длиной в три такта, а команда всегда заканчивается перед переходом по прерыванию. Но тут подумать нужно… может и не попасть такое при прерывании от таймера. Надежнее брать 4, как основу - вот с точностью 4 такта можно все спокойно делать.

Некоторые даже в 4.) Просто человек хочет аппаратно сформировать задержку и минимальный одиночный импульс. И вроде как Fast PWM, с виду, должен подходить, но…
Я бы запрещал прерывания и формировал всё вручную, считая такты. Правда, я не знаю всех условий ТС.

не совсем.
Я про сообщение 21 спрашивал - что нужно сделать, что при записи битов

пин не выставлялся принудительно в ЛОУ?
Возможности явно задать состояние выхода при старте таймера я не нашел. В даташите об этой особенности ни слова.

Я не пробовал такого. но соображения такие:

  1. Самое простое пины не подключать пинМодом (или регистром) пока все не настроишь
  2. Задать ситуацию при которой они в 1. То есть сперва установить все правильно и режим и значение таймера и OCRx а потом установить биты и запустить таймер.
  3. Попробовать Форс оутпут компаре, но тут не выйдет хорошо, ИМХО. но попробовать можно.

С такими таймингами нет смысла городить не на таймере, не на прерываниях. Собссно вот топорное решение на асме, задержка в 32uS сделана на декременте счётного регистра.

"cli" "\n\t" 
"ldi r20,171"    "\n\t" //171 петля, каждая петля занимает 3 такта
"ldi r19,6"    "\n\t" // b0110   PB1 high, PB2 high
"ldi r18,4"    "\n\t" // b0100  PB1 low, PB2 high
"ldi r17,2"    "\n\t" // b0010 PB1 high, PB2 low
"out 0x05,r18;"    "\n\t" //PB1 low  
"out 0x05,r19;"    "\n\t" //PB1 high  
"loop:"       "\n\t" //0
"dec r20"         "\n\t" //1 такт
"brne loop"   "\n\t" //2  
"out 0x05,r17;"    "\n\t" //PB2 low  
"out 0x05,r19;"    "\n\t" //PB2 high  
"sei" "\n\t"
:::);
2 лайка

не влияет

пробовал.
Похоже что состояние пина ПО НАСТРОЙКАМ ТАЙМЕРА обновляется только в момент достижения счетчика значений TOP или BOTTOM. А биты COMnAx действуют сразу. Так что с момента выставления этих бит до следующего переполнения пин будет в низком уровне НЕЗАВИСИМО от любых других настроек.
Даже если выставить начальное значение TCNT1 на TOP-1, получается одиночный отрицательный импульс, а если начать с TOP, то сигнал переполнения вообще не генерится и все откладывается на целый цикл.

эти биты не работают в в PWM режимах

Не обязательно что бы сразу в момент включения сразу все работало. Можно сделать определенную инициализацию первоначальную.

Идея такая, что по нажатию кнопки происходит генерация сигналов. Так же парой кнопок можно увеличить/уменьшить задержку, сдвигая на ее по одному такту(±62нс.Выбрана именно отрицательная полярность, так как она нужна для запуска схемы усиления сигналов, которая идёт сразу после ардуинки.

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

Я в последнем коде, что я писал в setup добавил

TCCR1B |= B1; //on
TCCR1B &= ~bit(0); //off

Что бы включить и выключить таймер. Тем самым сделать уровень D10 высоким. Далее он всегда висит High даже при остановленном таймере.

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

А так увеличение работает плавно, можно добавлять по 1 такту за раз

1 лайк

Можете выложить последний вариант кода, почистив его от неиспользуемого мусора?

интересная идея

я тут недавно баловался, продолжение темы
И снова таймеры - импульс по счетчику - Песочница. Раздел для новичков - Arduino.ru
столкнулся с аналогичным поведением

	//TCCR1A |= 1 << COM1A1; // 10 - сброс вывода OC1A (PB1 pin 9 Nano) при совпадении с A // почему то не работает, отключает пин сразу при запуске таймера
	TCCR1A |= 1 << COM1A0; // 01 - изменение состояния вывода OC1A (PB1 pin 9 Nano) на противоположное при совпадении с A

не знаю как объяснить, пришлось сделать переключение состояние пина

/*
 Name:		NanoTimers01.ino
 Created:	09.11.2022 13:03:46
 Author:	Andrey
*/

static constexpr unsigned char pin_input_step = 5; // T1 PD5 pin 5 Nano
static constexpr unsigned char pin_mixer = 9; // OC1A PB1 pin 9 Nano

/*ISR(TIMER1_COMPA_vect) { // прерывание Таймер1 по совпадению
	digitalWrite(pin_mixer, !digitalRead(pin_mixer));
}*/

/*(target time) = (timer resolution) * (# timer counts + 1)
(# timer counts + 1) = (target time) / (timer resolution) -> 16000000/1024
(# timer counts + 1) = (1 s) / (6.4e-5 s)
(# timer counts + 1) = 15625
(# timer counts) = 15625 - 1 = 15624*/

/*void startTimer1comp1sec(void) { // ежесекундный вызов прерывания по совпадению счетчика
	cli(); // отключить глобальные прерывания
	TCCR1A = 0b00000000; // очищаем все настройки таймера
	TCCR1B = 0b00000000; // очищаем все настройки таймера
	TCCR1B |= 1 << CS12; // clk_io/1024 (делитель частоты)
	TCCR1B |= 1 << CS10; // clk_io/1024 (делитель частоты)
	TIMSK1 |= (1 << TOIE1); // включить прерывание Timer1 overflow
	TCCR1B |= (1 << WGM12); // включение в CTC режим
	TIMSK1 |= (1 << OCIE1A);  // включение прерываний по совпадению
	OCR1A = 15624; // установка регистра совпадения
	sei();  // включить глобальные прерывания
}*/

/*void startTimer1compext(void) { // вызов прерывания по совпадению счетчика от внешних импульсов
	cli(); // отключить глобальные прерывания
	TCCR1A = 0b00000000; // очищаем все настройки таймера
	TCCR1B = 0b00000000; // очищаем все настройки таймера
	TCCR1B |= 1 << CS12; // 110 - внешний источник на выводе T1 (T1 PD5 pin 5 Nano) по спаду сигнала
	TCCR1B |= 1 << CS11; // 110 - внешний источник на выводе T1 (11 ножка) по спаду сигнала
	TIMSK1 |= (1 << TOIE1); // включить прерывание Timer1 overflow
	TCCR1B |= (1 << WGM12); // включение в CTC режим // сброс при совпадении
	TIMSK1 |= (1 << OCIE1A);  // включение прерываний по совпадению
	OCR1A = 5; // установка регистра совпадения
	sei();  // включить глобальные прерывания
}*/

/*void startTimer1compOutOCR1Atrig(void) { // изменение полярности выходного пина по совпадению счетчика от внешних импульсов
	cli(); // отключить глобальные прерывания
	TCCR1A = 0b00000000; // очищаем все настройки таймера
	TCCR1B = 0b00000000; // очищаем все настройки таймера
	TCCR1A |= 1 << COM1A0; // 01 - изменение состояния вывода OC1A (PB1 pin 9 Nano) на противоположное при совпадении с A
	TCCR1B |= 1 << CS12; // 110 - внешний источник на выводе T1 (T1 PD5 pin 5 Nano) по спаду сигнала
	TCCR1B |= 1 << CS11; // 110 - внешний источник на выводе T1 (11 ножка) по спаду сигнала
	TCCR1B |= (1 << WGM12); // включение в CTC режим // сброс при совпадении
	OCR1A = 5; // установка регистра совпадения
	sei();  // включить глобальные прерывания
}*/

void startTimer1singleONcomp(void) { // разовое включение выходного пина по совпадению счетчика входных импульсов
	cli(); // отключить глобальные прерывания
	TCCR1A = 0x00; // очищаем все настройки таймера
	TCCR1B = 0x00; // очищаем все настройки таймера
	TCNT1 = (unsigned short)0; // сброс счетчика
	TCCR1A |= 1 << COM1A1; // 11 - включение вывода OC1A (PB1 pin 9 Nano) при совпадении с A
	TCCR1A |= 1 << COM1A0; // 11 - включение вывода OC1A (PB1 pin 9 Nano) при совпадении с A
	TCCR1B |= 1 << CS12; // 110 - внешний источник на выводе T1 (T1 PD5 pin 5 Nano) по спаду сигнала
	TCCR1B |= 1 << CS11; // 110 - внешний источник на выводе T1 (11 ножка) по спаду сигнала
	TCCR1B |= (1 << WGM12); // включение в CTC режим // сброс при совпадении
	OCR1A = (unsigned short)10; // установка регистра совпадения
	sei();  // включить глобальные прерывания
}

void startTimer1pin1secOFF(void) { // отключение пина через секунду
	cli(); // отключить глобальные прерывания
	TCCR1A = 0x00; // очищаем все настройки таймера
	TCCR1B = 0x00; // очищаем все настройки таймера
	TCNT1 = (unsigned short)0; // сброс счетчика
	//TCCR1A |= 1 << COM1A1; // 10 - сброс вывода OC1A (PB1 pin 9 Nano) при совпадении с A // почему то не работает, отключает пин сразу при запуске таймера
	TCCR1A |= 1 << COM1A0; // 01 - изменение состояния вывода OC1A (PB1 pin 9 Nano) на противоположное при совпадении с A
	TCCR1B |= 1 << CS12; // 101 - clk_io/1024 (делитель частоты)
	TCCR1B |= 1 << CS10; // 101 - clk_io/1024 (делитель частоты)
	TCCR1B |= (1 << WGM12); // включение в CTC режим // сброс при совпадении
	OCR1A = (unsigned short)15624; // установка регистра совпадения
	sei();  // включить глобальные прерывания
}

void initPins(void) {
	pinMode(LED_BUILTIN, OUTPUT);
	digitalWrite(LED_BUILTIN, LOW);
	pinMode(pin_input_step, INPUT_PULLUP);
	pinMode(pin_mixer, OUTPUT);
	digitalWrite(pin_mixer, LOW);
}

// the setup function runs once when you press reset or power the board
void setup() {
	initPins();
	//startTimer1comp1sec();
	//startTimer1compext();
	//startTimer1compOutOCR1Atrig();
	startTimer1singleONcomp();
	//startTimer1pin1secOFF();
}

// the loop function runs over and over again until power down or reset
void loop() {
	static unsigned char mixerState = 0;
	if (!(mixerState)) {
		if (digitalRead(pin_mixer)) {
			startTimer1pin1secOFF();
			mixerState = 1;
		}
	} else if (!digitalRead(pin_mixer)) {
		mixerState = 0;
		startTimer1singleONcomp();
	}
}

что-то все комментарии /* */ съехали в коде, непонятно куда смотреть

выкинул все лишнее
строки 23 и 24

static constexpr unsigned char pin_input_step = 5; // T1 PD5 pin 5 Nano
static constexpr unsigned char pin_mixer = 9; // OC1A PB1 pin 9 Nano

void startTimer1singleONcomp(void) { // разовое включение выходного пина по совпадению счетчика входных импульсов
	cli(); // отключить глобальные прерывания
	TCCR1A = 0x00; // очищаем все настройки таймера
	TCCR1B = 0x00; // очищаем все настройки таймера
	TCNT1 = (unsigned short)0; // сброс счетчика
	TCCR1A |= 1 << COM1A1; // 11 - включение вывода OC1A (PB1 pin 9 Nano) при совпадении с A
	TCCR1A |= 1 << COM1A0; // 11 - включение вывода OC1A (PB1 pin 9 Nano) при совпадении с A
	TCCR1B |= 1 << CS12; // 110 - внешний источник на выводе T1 (T1 PD5 pin 5 Nano) по спаду сигнала
	TCCR1B |= 1 << CS11; // 110 - внешний источник на выводе T1 (11 ножка) по спаду сигнала
	TCCR1B |= (1 << WGM12); // включение в CTC режим // сброс при совпадении
	OCR1A = (unsigned short)10; // установка регистра совпадения
	sei();  // включить глобальные прерывания
}

void startTimer1pin1secOFF(void) { // отключение пина через секунду
	cli(); // отключить глобальные прерывания
	TCCR1A = 0x00; // очищаем все настройки таймера
	TCCR1B = 0x00; // очищаем все настройки таймера
	TCNT1 = (unsigned short)0; // сброс счетчика
	//TCCR1A |= 1 << COM1A1; // 10 - сброс вывода OC1A (PB1 pin 9 Nano) при совпадении с A // почему то не работает, отключает пин сразу при запуске таймера
	TCCR1A |= 1 << COM1A0; // 01 - изменение состояния вывода OC1A (PB1 pin 9 Nano) на противоположное при совпадении с A
	TCCR1B |= 1 << CS12; // 101 - clk_io/1024 (делитель частоты)
	TCCR1B |= 1 << CS10; // 101 - clk_io/1024 (делитель частоты)
	TCCR1B |= (1 << WGM12); // включение в CTC режим // сброс при совпадении
	OCR1A = (unsigned short)15624; // установка регистра совпадения
	sei();  // включить глобальные прерывания
}

void initPins(void) {
	pinMode(LED_BUILTIN, OUTPUT);
	digitalWrite(LED_BUILTIN, LOW);
	pinMode(pin_input_step, INPUT_PULLUP);
	pinMode(pin_mixer, OUTPUT);
	digitalWrite(pin_mixer, LOW);
}

// the setup function runs once when you press reset or power the board
void setup() {
	initPins();
	startTimer1singleONcomp();
}

// the loop function runs over and over again until power down or reset
void loop() {
	static unsigned char mixerState = 0;
	if (!(mixerState)) {
		if (digitalRead(pin_mixer)) {
			startTimer1pin1secOFF();
			mixerState = 1;
		}
	} else if (!digitalRead(pin_mixer)) {
		mixerState = 0;
		startTimer1singleONcomp();
	}
}

Насколько я вижу. у вас импульс положительный

да, нужен отрицательный?

b707, я попробовал в эмуляторе и у меня всё красиво. Никаких LOW импульсов при старте.

Да. ТС-у нужен отрицательный.

В случае положительного проблема сброса пина в низкий уровень не такая острая - он и так там.
А с отрицательным импульсом пин должен быть ХАЙ все остальное время - поэтому сброс его в ЛОУ при включении таймера портит всю картину. Вчера полночи просидел, так решения и не нашел.

Почистил, но оставил кнопку. Можно убрать кнопку, просто наверное сделать какой то цикл вместо нее.
При запуске на осциллографе оба уровня становятся высокими. После при первом нажатии кнопки появляются 2 импульса, если нажать еще раз, то видим опять их но второй на НА 2 ТАКТА ближе, последующие нажатия не приводят к изменению картины.
2 такта как раз занимает с изменением состояния пина, может как то с этим связанно первое нажатие?

#define btnStart 16                      // Кнопка   A2 - СТАРТ
#include <Wire.h>

bool flagBtn = false; //high or low for btn
uint32_t btnTimer = 0;

void setup() {
  pinMode(btnStart, INPUT_PULLUP); //btn pinMode

  pinMode(9, OUTPUT); //START
  pinMode(10, OUTPUT); //STOP

  digitalWrite(9, HIGH);
  digitalWrite(10, HIGH);

  ////FAST PWM SET UP:
  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

  //WGM 15
  TCCR1A |= (1 << WGM10) | (1 << WGM11) ;
  TCCR1B |= (1 << WGM12) | (1 << WGM13) ;


  TIMSK1 |= (1 << TOIE1); //OVERFLOW INTERRUPT
  sei();       //Enable back the interrupts


  OCR1B = 1;
  OCR1A = 54;
  //11 - SET ON COMPARE; OCR1B =1; OCR1A = 1000;
  TCCR1A = B00110011; //7-COM1A1; 6-COM1A0; 5-COM1B1; 4-COM1B0; 3-;2-;1-WGM11;0-WGM10

  //ВКЛЮЧАЕМ И СРАЗУ ВЫКЛЮЧАЕМ Т1
  TCCR1B |= B1; //ВКЛЮЧИТЬ
  TCCR1B &= ~bit(0); //ВЫКЛЮЧИТЬ Т1 ЧТО ВЫКЛЮЧИТЬ ТАЙМЕР, ЧТО БЫ D10 > HIGH
}

void loop() {
  //КНОПКА С АНТИДРЕБЕЗГОМ
  //START pressed
  if (!digitalRead(btnStart) && !flagBtn && millis() - btnTimer > 150) { //if btwn is pressed with debouncing
    flagBtn = true; //btn pressed flag
    btnTimer = millis();
    OCR1A = 50; //~50 ТАКТОВ
    myTest();
  }
  //START RELEASE
  if (digitalRead(btnStart) && flagBtn && millis() - btnTimer > 150) { //release with debouncing
    flagBtn = false; //btn released flag
    btnTimer = millis();
  }
}
void myTest() {
  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
}

ISR(TIMER1_OVF_vect) { //OVERFLOW VECTOR
  TCCR1B &= ~bit(0); //turn off timer1 (CS10 = 0) 18 clocks
}

а код покажете?