Зарегистрирован: Сб сен 10, 2016 08:54:49 Сообщений: 32
Рейтинг сообщения:0
Доброго времени суток.
Понадобился генератор прямоугольных импульсов с независимой регулировкой частоты и скважности.
Диапазон 0,1 - 1000 Гц, шаг регулировки 0,1 Гц. Длительность импульсов от 10 мкс, шаг регулировки 10 мкс. Частота и длительность выводятся на дисплей, в герцах и миллисекундах. Макс. скважность 50%.
Хотел все реализовать в Atmega328, поскольку она все равно есть в устройстве, хотя и до этого я никогда не программировал вообще.
Все, что я смог придумать - это сделать предделитель таймера на 1, установить прерывание по совпадению на 16 (отсчитали 1 микросекунду) и в прерывании изменять переменную х+1. Соответственно сравнивая с двумя другими переменными - подавать 1 или 0 на ножку МК.
Вроде бы худо-бедно работает, но при этом дисплей жутко тупит - секунд по 5 выводятся данные. Если вызывать прерывание по совпадению на 126 (160-34 цикла) - тогда все приемлемо, но длительность импульса конечно будет меняться с шагом 100 мкс. а не 10. Режим СТС не подходит, да и шаг изменения будет 16000000\256= 62500; 1\62500=0,000016 мкс., а хочется кратно10-ти.
Собственно, вопрос: как реализовать такой генератор на МК? Поможет ли ускорить работу МК такие ухищрения как собственный кварц для таймера, или подключение дисплея напрямую без регистра сдвига?
Гуглится очень много подобных тем, но решения вопроса нет ни в одной.
Карма: 29
Рейтинг сообщений: 651
Зарегистрирован: Сб май 14, 2011 21:16:04 Сообщений: 2708 Откуда: г. Чайковский
Рейтинг сообщения:0 Медали: 1
Конечно такие вещи лучше всего делать на периферии, чтобы на тратить ресурсы ядра. AVR конечно в плане периферии, уже устарела.
Я Вам предложу идею, но возможность ее осуществления проверьте сами, не хочу считать и читать.
16 битный таймер подключаете на выход. Он будет задавать частоту. Этот таймер тактируется от ядра. Т.к. таймер 16 битный. С помощью предделителя и регистров сравнения можно будет задавать частоту в широком диапазоне и хорошей дискретностью.
8 битный таймер тактируется с внешнего пина. Выход 16битного таймера соединен с входом 8 битного. На 8 битном настраиваете скважность и подключаете его тоже на выход. Это уже будет полезный выход.
Все это будет работать без участия программы, кроме моментов изменения режима. А вот возможность установки частот и скважностей посчитайте сами, может получится.
_________________ Добро всегда побеждает зло. Поэтому кто победил - тот и добрый.
Зарегистрирован: Сб сен 10, 2016 08:54:49 Сообщений: 32
Рейтинг сообщения:0
Z_h_e писал(а):
8 битный таймер тактируется с внешнего пина. Выход 16битного таймера соединен с входом 8 битного. На 8 битном настраиваете скважность и подключаете его тоже на выход. Это уже будет полезный выход.
Все это будет работать без участия программы, кроме моментов изменения режима. А вот возможность установки частот и скважностей посчитайте сами, может получится.
Впринципе, тоже думал о таком, жаль программирование начал осваивать с 0 всего неделю назад. Понадобится ещё 1 кварц и куча расчетов. Но надеюсь, осилю такой апгрейд прошивки. Спасибо за совет! Смотрел характеристики stm32, выглядят прельстиво, но увы нет таких подробных примеров и исходников какими завален гугл для avr.
Использование модульных источников питания открытого типа широко распространено в современных устройствах. Присущие им компактность, гибкость в интеграции и высокая эффективность делают их отличным решением для систем промышленной автоматизации, телекоммуникационного оборудования, медицинской техники, устройств «умного дома» и прочих приложений. Рассмотрим подробнее характеристики и особенности трех самых популярных вариантов AC/DC-преобразователей MW открытого типа, подходящих для применения в промышленных устройствах - серий EPS, EPP и RPS представленных на Meanwell.market.
Достаточно одного 16-разрядного таймера. Режим PWM TOP=OCR1A или ICR1. Через предделитель и TOP задаём период/частоту, через OCR скважность. Ровно 10мкс может не получится - виноват делитель на 8, но близко к этому значению вполне реально. Это чисто аппаратный вариант.
Программный вариант. Рассчитываем число периодов(переполнений) таймера, на последнем включаем PWM или CTC с управлением выводом по сравнению(если неполный период), вывод устанавливается в 1. Ждём сколько-то переполнений и часть периода таймера до окончания периода синтезируемого колебания, вывод устанавливается в 0. Повторяем цикл снова. Недостаток - джиттер в небольших пределах и надо уделять внимание тем случаям когда сравнение происходит близко к вершине таймера TOP.
А там и до ПЛИСов не далеко. Или на обычной логике собирать. Или как советовали другой контроллер с хорошим и быстрым таймером ставить. Шаг по 10мкс - это чтоб просто так было или реальные потребности какого-то процесса?
Карма: 29
Рейтинг сообщений: 651
Зарегистрирован: Сб май 14, 2011 21:16:04 Сообщений: 2708 Откуда: г. Чайковский
Рейтинг сообщения:1 Медали: 1
Все зависит от задачи. Пускай частота ядра 16МГц. Предделитель на 1024. Частота тактирования счетчика будет 15625Гц. 16 битны счетчик будет переполняться с частотой 0,25Гц.
Понижаете частоту тактирования в два раза. Где то вроде Ваша частота. Но это частота 16 битного счетчика. Делите ее еще на 256, 8битного, будет еще меньше.
Вариаций много. Не утверждаю МК способен выполнить Вашу задачу. но весьма вероятно.
_________________ Добро всегда побеждает зло. Поэтому кто победил - тот и добрый.
Зарегистрирован: Сб сен 10, 2016 08:54:49 Сообщений: 32
Рейтинг сообщения:0
uk8amk писал(а):
А там и до ПЛИСов не далеко. Или на обычной логике собирать. Или как советовали другой контроллер с хорошим и быстрым таймером ставить. Шаг по 10мкс - это чтоб просто так было или реальные потребности какого-то процесса?
Если другой это stm то боюсь не осилю. Шаг именно 10 мкс. Уже работает на моем индусском коде с шагом в 100 мкс и не хватает точности, потому сюда и написал. Пусть нижний предел будет 100 мкс и 0,5 гц, но шаг изменения именно такой, даже если максимальная частота станет ниже, до 50гц а не 1 кгц.
Карма: 29
Рейтинг сообщений: 651
Зарегистрирован: Сб май 14, 2011 21:16:04 Сообщений: 2708 Откуда: г. Чайковский
Рейтинг сообщения:0 Медали: 1
Hel писал(а):
Вроде бы худо-бедно работает, но при этом дисплей жутко тупит - секунд по 5 выводятся данные
Может получится оптимизировать Ваш алгоритм и будет работать приемлемо. Вам нужно минимизировать время выполнения обработчика прерывания ISR (TIMER0_COMPA_vect). 1. Используйте режим СТС. Счетчик будет сам сбрасываться при совпадении. 2. Переключайте порт записью 1 в регистр PIN. Отпадет необходимость использовать |=. PINB = 1<<3; 3. Попробуйте разные уровни оптимизации при компиляции. 4. Возможно у Вас неудачный тип данных для переменных, измените. 5. Разместите эти переменные в регистрах, а не в памяти. Но к сожалению это кажется можно только для локальной переменной. Тогда плохой это совет. Надо будет уточнить. 6. Переписать весь код на ассемблере. 7. Тактировать МК на максимальной частоте.
Тут у Вас вроде как фигурная скобка лишняя
Код:
PORTB &= ~(1<<3); }// Конец импульса.
_________________ Добро всегда побеждает зло. Поэтому кто победил - тот и добрый.
FCPU=16000000 FTIM0=FCPU Режим CTC для TIM0, TOP=OCRA. OCR0A=160-1; Получим интервал сравнений и вызова прерываний ровно 10мкс. В обработчике COMPA: Timer0Count=Timer0Count+1; если (Timer0Count == (DelayTime-1)) разрешаем вывод OCR0A как SET on compare match. если (Timer0Count == (DelayTime)) PORTB |= 1<<3; Normal port operation, OC0A disconnected. если (Timer0Count == (PeriodTime-1)) разрешаем вывод OCR0A как CLEAR on compare match. если (Timer0Count == PeriodTime) PORTB &= ~(1<<3);Timer0Count=0;Normal port operation, OC0A disconnected.
Таким образом, управление выводом в нужные моменты производится аппаратно, без джиттера. 160 тактов(10мкс) должно хватить на обработчик(я так думаю). С тормозами фоновых процессов придётся смириться потому что программный DDS пожирает много ресурсов. Или повышать FCPU.
Одиночную генерацию импульса заданной длительности можно реализовать с помощью недокументированной возможности таймеров AVR в PWM режиме. Здесь подробное описание: https://wp.josh.com/2015/03/12/avr-time ... explained/ Удачно выбрав кварц и предделитель изменение длительности импульса на 10 мкс будет делаться изменением содержимого регистра счётчика на единицу. Сам такое пробовал — работает.
Правда запускается всё это дело программно. Чтобы не было джиттера запускать следует в прерывании от другого таймера, отлавливая длительность задержки входа в прерывание. Это вполне реально реализовать, я даже где-то здесь на форуме встречал синтез видеосигнала для телевизора на какой-то меге. Надеюсь знатоки подскажут. Там джиттер приводит к прыганию строк вправо-влево на один-два пикселя. Но с помощью специальной последовательности команд задержку можно сделать фиксированной. К сожалению сам не пробовал и подробностей не помню. Точно могу сказать, что там нужен ассемблер, на си не факт что можно сделать.
Если джиттер не страшен, то вообще никаких проблем.
Карма: 29
Рейтинг сообщений: 651
Зарегистрирован: Сб май 14, 2011 21:16:04 Сообщений: 2708 Откуда: г. Чайковский
Рейтинг сообщения:0 Медали: 1
Сегодня посидел над Вашей задачей. Сделал генератор полностью аппаратный, но от 1.6Гц. Ниже можно будет тоже, но надо будет несколько поизвращаться с подсчетом переполнений таймера1.
МК - мега328 Тактовая частота- 8МГц На выходе PD5 100кГц. Ее задает таймер0, эта же нога вход тактирования для таймера 1. На выходе PB2 частота от 1.6Гц до 50кГц. Спойлер
Зарегистрирован: Сб сен 10, 2016 08:54:49 Сообщений: 32
Рейтинг сообщения:0
Огромное спасибо всем, кто откликнулся, сам укатил в небольшую командировку на неделю и потому молчал. Уважаемый uk8amk, можно мне как новичку-гуманитарию пояснить пару строчек? Как они должны правильно выглядеть на си?
uk8amk писал(а):
if (Timer0Count == (DelayTime-1)) разрешаем вывод OCR0A как SET on compare match.
if (Timer0Count == (PeriodTime-1)) разрешаем вывод OCR0A как CLEAR on compare match. .
Как я вас понял: void WorkOn() { OCR0A=159; //Количество импульсов соответствующих 10 микросекундам. 16 МГц /160 - 1 (число циклов потраченных на выполнение комманд) TIMSK0=(1<<OCIE0A); TCNT0 = 0; TCCR0B |= (1<<WGM00)|(0<<WGM01)|(0<<CS02)|(1<<CS01)|(1<<CS00); //Предделитель тактовой частоты - 1. режим СТС. UpdateF(); } ISR (TIMER0_COMPA_vect) // Генератор импульсов. { Timer0Count=Timer0Count+1;
if (Timer0Count == (DelayTime-1)){ TCCR0A |= (1<<COM0A0)|(1<<COM0A1); }
if (Timer0Count == (DelayTime)) {PIND = 1<<6;}
if (Timer0Count == (PeriodTime-1)){ TCCR0A |= (1<<COM0A0)|(0<<COM0A1); }
if (Timer0Count == PeriodTime){PIND = 0<<6;}
Timer0Count=0; // Сброс счетчика. }
UPD проверил, не работает на выходе при запуске появляется +5 в и больше ничего. не снимается даже когда PORTD &= ~(1<<6); Видимо, я где-то напортачил в коде или же не так понял идею.
Z_h_e спасибо, тоже хорошая идея, единственное, что может серьезно помешать - нижний предел 1,6 гц. Эх, не была бы нужна низкая частота - был бы, пожалуй, лучший вариант.
if (Timer0Count == (DelayTime-1)){ TCCR0A |= (1<<COM0A0)|(1<<COM0A1); } if (Timer0Count == (PeriodTime-1)){ TCCR0A |= (1<<COM0A0)|(0<<COM0A1); }
Это значит что в итоге всегда будет COM0A0=1, COM0A1=1(Set OC0A on Compare Match).
Перед тем как делать {PIND = 1<<6;} Надо линию порта переключить с альтернативной функции на GPIO(опять же через COM0A0, COM0A1).
Строка if (Timer0Count == PeriodTime){PIND = 0<<6;} Не имеет смысла потому что
Цитата:
14.2.2 Toggling the Pin Writing a logic one to PINxn toggles the value of PORTxn, independent on the value of DDRxn. Note that the SBI instruction can be used to toggle one single bit in a port.
Иными словами смена состояния возможна только при записи 1. И применять данную функцию вам надо с осторожностью, поскольку результат зависит от предыдущего состояния линии порта.
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 7
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения