ШИМ частотного сигнала?

pwm

Работаю над своим первым настоящим наброском, поэтому постараюсь быть кратким. При необходимости не стесняйтесь спрашивать дополнительную информацию.

Для питания обратноходового трансформатора мне нужно подать ШИМ-сигнал на ШИМ-сигнал. Самый простой способ описать это так: хотя стандартный ШИМ-сигнал включен в течение некоторого установленного времени, а затем выключен в оставшуюся часть периода, мне нужно отправить сигнал, где «включено» на самом деле является прямоугольным сигналом частотой 14 кГц. с коэффициентом заполнения 50 %... затем подайте импульс этому сигналу с коэффициентом заполнения от ~1 до 100 % с частотой ~500 Гц.

С ШИМ 14 кГц проблем нет — я нашел библиотеку PWM.h и настроил ее.

Но как лучше всего справиться с этим с помощью второго ШИМ, скажем, на частоте 500 Гц?

Смогу ли я, приложив немного усилий, «пошутить» с помощью DO – WHILE? Будет ли WHILE читать переменную вне цикла? Есть ли более простые способы?

i=1;
do {                                // Импульсные микросекундные «кадры» высокочастотного сигнала?
                                    //т.е. несущая 14 кГц ШИМ с частотой 500 Гц = 2000 микросекунд на цикл «кадра»
                                    //при нагрузке 50% будет 1000 микрон в высоту и 1000 микрон в низ

    pwmWrite( outpin, 128 );        //Включаем несущую 14 кГц при нагрузке 50%
    delayMicroseconds(1000);        //Задержка для создания «ВЫСОКОЙ» части кадра
    pwmWrite( outpin, 0 );          //Выключаем несущую 14 кГц
    delayMicroseconds(1000);        //Задержка для создания «LOW» части кадра
   } while (i == 1);                //...и продолжаем выполнять цикл, пока состояние равно '1' или 'ON'

delay(1000);                        //?Позволит ли это повторить приведенный выше цикл, ожидая здесь 1 секунду?
i=0;                                //i != 1, значит, выходим из цикла while выше?
                                    //затем продолжаем делать другие вещи ниже...

Любая помощь приветствуется.

, 👍1

Обсуждение

50% ШИМ 14 кГц — это не ШИМ, это тон 14 кГц. Насколько точными должны быть несущая 14 кГц и частота модуляции 500 Гц? Это возможно с помощью «тона» для 14 кГц и «миллиса» для 500 Гц., @Jot

Ваше do... while здесь представляет собой бесконечный цикл., @Edgar Bonet

Спасибо за вклад. Я также рассмотрел командуtone(). В этом случае они практически взаимозаменяемы. tone() может сэкономить один шаг, потому что это автоматически составляет 50%, а мне приходится указывать 50% (128) при каждом вызове pwmWrite. Точность не критична. Тон/ШИМ 14 кГц, который у меня сейчас есть, более чем достаточно точен. Частота 500 Гц тоже может немного колебаться. Похоже, мне нужно настроить переменную i, входящую в цикл, и увеличить ее изнутри?, @Sam43

Рассмотрите возможность использования библиотеки [TimerOne](https://github.com/PaulStoffregen/TimerOne) для настройки обратного вызова на основе таймера/прерывания для части 500 Гц, которая переключает tone() 14 кГц., @jose can u c

Возможно, это лучший способ атаковать со стороны WHILE? (извините, код не работает) ' я = 1; пока (я<=100) { pwmWriteHR (распиновка, 128); задержкамикросекунды (500); pwmWriteHR (распиновка, 0); задержкамикросекунды (500); (я++); }', @Sam43

Это должно работать, но это надуманный способ написать базовый цикл for., @Edgar Bonet

Использование tone() (который использует Timer2) для сигнала несущей и Timer1 для модуляции позволяет использовать периферийные устройства, которые являются частью микроконтроллера и, следовательно, не нуждаются в цикле while для управления. Вы просто настраиваете/запускаете его и все работает. Если вы используете библиотеку TimerOne, единственным важным дополнительным кодом будет включение и выключение функцииtone(). Функция, которую вы для этого пишете, вызывается повторно со скоростью, установленной вами в TimerOne., @jose can u c


2 ответа


3

Ваша несущая 14 кГц может быть сгенерирована функцией tone(), которая является частью ядра Arduino. Затем вы можете использовать библиотеку TimerOne для включения и выключения звукового сигнала.

Вот непроверенный пример эскиза, демонстрирующий использование:

#include <TimerOne.h>

const uint8_t outpin=4;

void setup() {
  // поместите сюда свой код установки для однократного запуска:
  pinMode(outpin, OUTPUT);
  Timer1.initialize(1000);  // 1000 микросекунд на полупериод
  Timer1.attachInterrupt(toggleCarrier);
}

void loop() {
  // поместите сюда свой основной код для повторного запуска:

}

void toggleCarrier() {
  static uint8_t isPlaying;
  if (isPlaying) {
    noTone(outpin);
    isPlaying=0;
  } else {
    tone(outpin, 14000);
    isPlaying=1;
  }
}
,

Этот код отлично работает, как написано, для случая рабочего цикла 50% при частоте 500 Гц. Спасибо!! Мне предстоит еще многое изучить с таймерами и прерываниями. Последняя часть головоломки заключается в том, что я считываю переменный потенциометр и преобразую это показание 0–1023 в коэффициент заполнения 1–100% для сигнала 500 Гц. Таким образом, учитывая полный цикл 2000 микросекунд, я считывал потенциометр, а затем циклически переключался от 20 мкс вкл./1800 мкс выкл. до полного включения 2000 мкс. Подозреваю, что можно было бы добавить больше кода в раздел toggleCarrier(), но лучший ли это метод? поставить еще один таймер? ТИА!, @Sam43


4

На Arduino Mega (или Uno с заменой ATmega328P на ATmega328PB, но тогда вам придется немного адаптировать код) это можно сделать с помощью модулятора сравнения выходов:

void setup() {
  // CTC, OC0A переключение на совпадение сравнения, предварительный масштаб 1/8
  TCCR0A = _BV(COM0A0)|_BV(WGM01);
  TCCR0B = _BV(CS01);

  // переключение каждые 35,5 мкс при частоте 16 МГц и масштабе 1/8
  OCR0A = 70;

  // ШИМ с корректировкой фазы и частоты, TOP — OCR1A, OC1C очищается при повышении и установке при понижении, без предварительного масштабирования
  TCCR1A = _BV(COM1C1)|_BV(WGM10);
  TCCR1B = _BV(WGM13)|_BV(CS10);

  // период 2 мс при 16 МГц и без предварительного масштабирования
  OCR1A = 16000;

  // время включения 1 мс на частоте 16 МГц и без предварительного масштабирования
  OCR1C = 8000;

  // нет прерываний от таймеров
  TIMSK0 = 0;
  TIMSK1 = 0;

  // выводим 1 только тогда, когда оба таймера выдают 1
  PORTB &= ~_BV(PORTB7);

  // останавливаем таймеры и сбрасываем прескалер
  GTCCR |= _BV(TSM);
  GTCCR |= _BV(PSRSYNC);

  // сбрасываем оба таймера
  TCNT0 = 0;
  TCNT1 = 0;

  // включаем вывод
  DDRB |= _BV(DDB7);

  // позволяем таймерам работать
  GTCCR &= ~_BV(TSM);
}

void loop() {
  // Просто отрегулируйте частоты и рабочий цикл, как требуется здесь.
  // Все обрабатывается аппаратно и вступает в силу сразу после записи в регистр.
  // OCR1A: желаемый период более медленного сигнала, в 8-х микросекундах. Может быть до 65535.
  // OCR1C: желаемое время включения более медленного сигнала в 8-х микросекундах. Может быть до 65535.
  // OCR0A: на 1 меньше желаемого периода более быстрого сигнала в микросекундах. Может быть до 255.
  // Время включения более быстрого сигнала всегда равно половине периода.
  // При необходимости вы можете обменять точность на больший диапазон для любого из вышеуказанных значений, настроив прескалер.
}

В этом эскизе контакт 13 на Mega будет иметь желаемую форму сигнала. У этого метода есть два больших преимущества. Во-первых, после настройки он вообще не требует никаких ресурсов ЦП (никаких циклов или прерываний), поэтому вы можете посвятить весь loop чтению потенциометра и настройке рабочего цикла, не беспокоясь о сбоях. на выходе. Во-вторых, полностью аппаратное управление означает, что его выходной сигнал будет точным и абсолютно последовательным (компонент 500 Гц идеален, а компонент 14 кГц составляет примерно 14085 Гц). Однако единственным недостатком является то, что millis и все остальное в библиотеке Arduino, использующее внутренние таймеры, не будет работать правильно, поскольку мы используем те же аппаратные таймеры, от которых они зависят.

,

@Sam43 Sam43 Теперь это обновлено, чтобы вы могли изменять рабочий цикл сигнала 500 Гц., @Joseph Sible-Reinstate Monica