Нужна помощь по генератору сдвоенных импульсов
- Войдите на сайт для отправки комментариев
Сб, 02/11/2019 - 09:07
Всем Доброго Времени Суток!
Стоит задача, сделать генератор сдвоенных и гальванически развязанных, прямоугольных импульсов и регулировкой частоты следования, длительностей и задержки между ними. Благодаря чтению форума и особенно авторам нужных тем ЕвгенияП и dimax почти готов нужный скетч.
Программа работает только на Arduino Mega2560 v.R3, т.к. только там нашлась пара свободных 16-ти битных таймеров (можно взять Mega2560 и другой ревизии, но тогда придется переопределить в программе выходные пины таймеров). Импульсы задаются таймерами 3 и 5, (пины 2 и 45 соответственно, можно переопределить в небольших пределах). Повторю программа работает, импульсы наблюдаю на осциллографе. Частоту можно переопределять через Serial. С длительностью импульсов тоже всё в порядке. Наблюдается что-то непонятное с задержкой между импульсами.
Проявляется это следующим образом: при переходе с частоты нескольких килогерц, на новое значение, например в 15 кГц, происходит скачок задержки с 70 мкс до 35 мкс, и при возврате частоты на исходную задержка остается такой же (35 мкс). При изменении частоты в небольших пределах (например с 2 кГц на 3, 4 или 5 кГц) этого не происходит, всё работает правильно. За эту задержку в программе отвечают строки 51 (в разделе setup) и 130 (в разделе loop ). Не могу понять почему так происходит и поэтому прошу помощи.
Заранее всем спасибо за участие.
/* Генератор гальванически развязанных сдвоенных импульсов ( импульсы на разных пинах ). Программа только для Arduino Mega2560 v.R3, т.к. только там нашлась пара свободных 16-ти битных таймеров (можно взять Mega2560 и другой ревизии, но тогда придется переопределить в программе выходные пины таймеров). Импульсы задаются таймерами 3 и 5, (пины 2 и 45 соответственно, можно переопределить в небольших пределах). При необходимости, можно использовать еще один таймер (4). При необходимости, изменить в программе значения длительность импульсов и задержку между ними. Изменение частоты реализовано через монитор монитор порта (serial) При написании программы использовались идеи представленные в: 1.http://arduino.ru/forum/programmirovanie/etyudy-dlya-nachinayushchikh-blink-i-bez-delay-i-bez-millis 2.http://arduino.ru/forum/proekty/generator-s-reguliruemoei-chastotoi-na-arduino 3.библиотека Timer.h Загружаете скетч в Мегу 2560 (R3) и на пинах 2 и 45 появляются импульсы. */ #include <limits.h> #define setDuty() {OCR3B=dutyOnePulse;OCR5B=dutyTwoPulse;} unsigned char clockSelectBits; // переменная для записи в управляющий регистр таймера unsigned long dutyOnePulse = 56; // 56 = 7 microSeconds, это длительность первого импульса unsigned long dutyTwoPulse = 8; // 8 = 1 microSeconds, это длительность второго импульса int16_t PulseDelay = 210; // 210 это примерно 70 мкс задержки между импульсами int16_t prescaler[6] = {1,1,8,64,256,1024}; // 5 значений прескалера + оптимальное(нулевое для записи) uint16_t counter[6]; // переменная для счетчика, можно обойтись без массива double frequency[6]; // переменная для частоты, можно обойтись без массива double difference[6]; // переменная для ошибки, можно обойтись без массива bool valid; // двоичная переменная.. double freq = 2000; // начальное значение частоты следования импульсов в Герцах void setup(){ TCCR3A = 0; // обнуление управляющего регистра А Таймера3 TCCR5A = 0; // обнуление управляющего регистра А Таймера5 setPreScaler(freq); // расчет и установка делителей Таймеров 3 и 5 setDuty(); // установка длительности импульсов DDRE |= _BV(PORTE4); TCCR3A |= _BV(COM3B1); // Устанавливаем пин 2 как выход Таймера3 TCCR3B |= clockSelectBits; // запуск режима PWM на Таймере3 for ( int16_t i=0; i <= PulseDelay; i++){ asm("nop"); } // задержка между импульсами DDRL |= _BV(PORTL4); TCCR5A |= _BV(COM3B1); // Устанавливаем пин 45 как выход Таймера5 TCCR5B |= clockSelectBits; // запуск режима PWM на Таймере5 Serial.begin(115200); Serial.setTimeout(LONG_MAX); } void setPreScaler(double freq) { difference[0] = 200; // это значение "начальной" ошибки между заданной и генерируемой частотой for ( int8_t i=1; i<6; i++ ){ // в этом цикле производится перебор делителей и выбирается вариант с наименьшей ошибкой double f0 = floor( F_CPU / ( 2.0 * prescaler[i] * freq ) - 0.5 ); if ( f0 < 0.0 || f0 > UINT_MAX ) { valid = false; } else { valid = true; } counter[i] = (uint16_t) f0; frequency[i] = F_CPU / ( 2.0 * prescaler[i] * ( 1.0 + counter[i] ) ); difference[i] = fabs( frequency[i] - freq ); if ( (difference[i] < difference[0] ) && ( valid == true ) ) { // вариант с наименьшей ошибкой запоминается в нулевом номере массива difference[0] = difference[i]; prescaler[0] = prescaler[i]; counter[0] = counter[i]; frequency[0] = frequency[i]; } } TCCR3B = _BV(WGM13); // остановка Таймеров TCCR5B = _BV(WGM13); switch (prescaler[0]) // запись выбранного значения делителя в clockSelectBits { case 1: //выполняется когда prescaler[0] равно 1 clockSelectBits = _BV(CS10); break; case 8: //выполняется когда prescaler[0] равно 8 clockSelectBits = _BV(CS11); break; case 64: //выполняется когда prescaler[0] равно 64 clockSelectBits = _BV(CS11) | _BV(CS10); break; case 256: //выполняется когда prescaler[0] равно 256 clockSelectBits = _BV(CS12); break; case 1024: //выполняется когда prescaler[0] равно 1024 clockSelectBits = _BV(CS12) | _BV(CS10); break; } ICR3 = counter[0]; // запись выбранного значения счетчика в ICR3 ICR5 = counter[0]; // у нас импульсы с одинаковой частотой.. TCCR3B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12)); TCCR5B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12)); // обнуление в Таймере5 управляющего регистра TCCR5B } void loop() { Serial.println("\nЧастота следования импульсов " + String(frequency[0]) + "Гц"); Serial.println("\nВведите новое значение частоты"); freq = Serial.parseFloat(); if (freq < 0) { Serial.println("\nЧастота не может быть отрицательной\n"); } else if (freq > 20000) { Serial.println("\nЧастота не может быть слишком большой\n"); } else {setPreScaler(freq); // расчет и уставка новых значений делителей TCCR3B = _BV(WGM13); // остановка Таймеров TCCR5B = _BV(WGM13); TCCR3B |= clockSelectBits; for ( int16_t i=0; i <= PulseDelay; i++){ asm("nop"); } // задержка между импульсами TCCR5B |= clockSelectBits;} delay(1000); }
Так когда частоту перенастраиваишь надо же таймерам счетчики сбросить.
Vlad1976, а вы можете описать точно что вам нужно? Из того, что видно по программе -дельта между таймерами не делается ноп-ами, а делается загрузкой в счётный регистр CNT одного из таймеров некого смещения, от которого начнётся счёт. Потом таймеры синхронно запускаются регистром GTCCR
А на stm32 всё делается гораздо проще и удобнее :)
Отвечаю asam.
Так я считаю, что сбрасываю счетчик, это строки в программе № 107 и 108.
Для Dimax.
1. stm32 - еще едут ко мне из Китая.
2. Из того что от этой программы мне осталось добиться, это чтобы при перестройки частоты, задержка между импульсами не изменялась.
3. не понял из того, что вы написали, что задержка задается у меня не нопами!? в управляющие регистры счетчиков (TCCR3B, ICR3 TCCR5B, ICR5), я записываю одни и те же значения, для того чтобы импульсы шли синхронно, записываю разные значения в регистры (OCR3B и OCR5B), так как нужна разная длительность у этих импульсов, и считал, что задержку формирую нопами.. А почему тогда она меняется --- если менять значение PulseDelay? Не могли бы вы разъяснить мне подробнее, пожалуйста.
два 16 битных таймера есть в 328PB
Vlad1976,
Вот так надо задержки делать. Думаю разберётесь как это работает.
dimax - большое спасибо.
Доделал программу. Всё работает как надо. Выкладываю, вдруг кому-нибудь пригодиться. Коротко о схеме. Пины 2 и 45 - выходы генераторов. Дисплей двухстрочных (16х2) подключен через i2c, конкретно у моего адрес 0х3f, у вас может быть другой. Кнопка энкодера - земля и пин 3, меняет режим энкодера (М1 - изменение частоты по 100Гц, М2 - изменение частоты по 1 Гц, М3 - изменение длительности первого импульса, М4 - изменение задержки между импульсами.). Средний контакт энкодера - на земле, два других подключены через триггер Шмитта 74HC14p (антидребезг контактов) к пинам 18,19