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

от автора

В статье будет рассмотрен и описан простой процесс сборки полезного генератора импульсов для изучения катушек индуктивности и других экспериментов.

Отличие такого генератора от распространённых ШИМ-генераторов в том, что у данного генератора имеется возможность задавать отдельно длительность импульса и отдельно длительность паузы, а также количество последовательно идущих импульсов и пауз. Имеется также возможность получения полноценного регулируемого повторяющегося во времени сигнала.
Все перечисленные возможности реализованы через код. Для создания генератора понадобится Arduino Nano/Uno, две тактильные кнопки, LCD-дисплей и установленная на ПК программа Arduino IDE.
Схема подключения изображена на рисунке 1.

рис.1

рис.1

Для удобства подключения и использования Arduino Nano можно применить такую плату расширения (рис. 2). Очень удобно, есть рядом расположенные необходимые выводы для подключения LCD-дисплея. У каждого сигнального пина рядом есть отдельный пин GND и +5V.

рис.2

рис.2

Вначале необходимо скачать и установить Arduino IDE. Далее необходима библиотека LiquidCrystal I2C, автор Frank de Brabander, библиотека необходима для работы дисплея. Это можно сделать, зайдя в Tools — Manage Libraries…. (рис.3).

рис.3

рис.3

На данном этапе у вас должны быть в наличии: I2C LCD 2004 дисплей, две тактильные кнопки, Arduino Nano или Uno. И установлена на компьютер Arduino IDE, добавлена библиотека LiquidCrystal I2C.

Рассмотрим возможности генератора импульсов (рис.4) и (рис.1).

рис.4

рис.4

Кнопка 1. Длинное нажатие (0,4 сек) служит для выбора цифровой ячейки по горизонтали.
Кнопка 1. Короткое нажатие задаёт цифровое значение в выделенной ячейке.

Кнопка 2. По короткому нажатию идёт выбор строки. Выделение текущей ячейки опускается сверху в низ, дойдя до самого низа, выделение возвращается в верхнюю ячейку.
Кнопка 2. Длинное нажатие выдаёт сигнал или пачку сигналов в режиме Burst на вывод 9 Arduino.

Pulse. Задаётся длительность импульса в микросекундах. Рабочий диапазон от единиц микросекунд до 16000 микросекунд.

Pause. Задаётся длительность паузы в микросекундах. Рабочий диапазон от единиц микросекунд до 16000 микросекунд.

Mode. Здесь задаётся один из двух режимов. В данном примере это Burst, то есть режим одиночного импульса или последовательности из нескольких импульсов и пауз. Переключение режимов происходит по быстрому нажатию на кнопку 1.

Burst. В этой строчке задаётся количество последовательно идущих импульсов и пауз. По умолчанию стоит 1, это значит, что данный режим работы, — это генератор одиночного импульса. Если поставить 2, то мы получим два импульса и две паузы. Если 3, то три импульса и три паузы и т.д. При этом после последней паузы генерация выключается, а значит, отчётливой границы последней паузы не будет видно, пример: (рис.6,7).

Для переключения к настройкам повторяющегося сигнала необходимо опуститься в то место дисплея, где написано Mode, и далее короткое нажатие на кнопку 1. Режим Burst изменится на Continuous (рис.5).

рис.5

рис.5

В режиме Continuous имеется возможность задавать длительность импульсов и отдельно пауз. И выводить повторяющийся сигнал.
Режим Continuous работает в реальном времени. Без необходимости запускать повторяющийся сигнал и изменения кнопкой.

Данный генератор создавался с целью изучения катушек индуктивности. Он создавался и рассчитан не на прямое подключение к полевому транзистору, а на подключение через оптопару и драйвер затвора. В моём случае это 6N137 и TC4420. Поэтому логика работы генератора инвертированная: то есть когда MOSFET закрыт, на выводе 9 Arduino — 5 вольт.

Пример работы. Генератор выдаёт 1 импульс (Pulse) длительностью 100 микросекунд (рис6).

рис.6

рис.6

Пример работы. Генератор выдаёт 3 последовательных импульсов и пауз. Длительность импульса (Pulse) 100 микросекунд, длительность паузы (Pause) 50 микросекунд. При этом после последней паузы генерация выключается, поэтому последняя пауза всегда не разделяется (рис. 7).

рис.7

рис.7

Пример работы. Генератор в режиме Continuous. 200 микросекунд длительность импульса (Pulse) и 450 микросекунд длительность паузы (Pause), — это бесконечно повторяющийся режим (рис. 8).

рис.8

рис.8

Для завершения создания этого генератора импульсов достаточно загрузить предоставленный код в Arduino IDE. Для этого подключите Arduino Nano через USB-кабель к компьютеру, откройте Arduino IDE, выберите Arduino Nano/Uno, выберите COM-порт к которому подключена Arduino Nano/Uno. Необходимое выделено на (рис. 9). Загрузите код, нажмите вверху «Загрузка» (Upload).

рис.9

рис.9

Если после загрузки кода на дисплее не появилась информация, то в начале кода измените адрес на «0x3F» (без кавычек). Смотрите (рис. 10).

рис.10

рис.10
#include <Wire.h>#include <LiquidCrystal_I2C.h>LiquidCrystal_I2C lcd(0x27, 20, 4);  // Адрес I2C дисплея (подставьте свой при необходимости)// ---------- Пины ----------const int button1Pin = 3;  // Кнопка 1 (короткое: изменение цифры, длинное: переход на следующую цифру)const int button2Pin = 4;  // Кнопка 2 (короткое: выбор раздела, длинное: запуск Burst)const int outputPin = 9;   // Выход на оптопару (ШИМ/импульсы)// ---------- Параметры генерации ----------unsigned long pulseWidth = 100;  // Длительность импульса (мкс)unsigned long pauseWidth = 100;  // Длительность паузы (мкс)unsigned long burstCount = 1;    // Число импульсов в пачке (Burst)// Символьные представления для ввода (6 цифр)char pulseDigits[6] = {'0', '0', '0', '1', '0', '0'};  // "000100" -> 100 мксchar pauseDigits[6] = {'0', '0', '0', '1', '0', '0'};char burstDigits[6] = {'0', '0', '0', '0', '0', '1'};  // "000001" -> 1 импульс// ---------- Интерфейс ----------int currentSection = 0;  // 0 = pulse, 1 = pause, 2 = mode, 3 = burst count (только в Burst-режиме)int currentDigit = 0;    // Индекс редактируемой цифры (0-5)int mode = 1;            // 0 = Continuous, 1 = Burstvolatile bool generating = false;  // Флаг активной генерации пачки// ---------- Дребезг кнопок ----------int button1State = HIGH, lastButton1Reading = HIGH;unsigned long lastDebounceTime1 = 0, button1PressTime = 0;int button2State = HIGH, lastButton2Reading = HIGH;unsigned long lastDebounceTime2 = 0, button2PressTime = 0;const unsigned long debounceDelay = 50;       // мсconst unsigned long longPressThreshold = 400; // мс (удержание кнопки)// ---------- Вспомогательные функции ----------unsigned long getValue(char* digits) {  unsigned long val = 0;  for (int i = 0; i < 6; i++) {    val = val * 10 + (digits[i] - '0');  }  return (val == 0) ? 1 : val;  // 000000 интерпретируется как 1 мкс}// Настройка аппаратного ШИМ (режим Continuous)void configureTimer() {  unsigned long period_us = pulseWidth + pauseWidth;  if (period_us > 1000000) period_us = 1000000;  // ограничение 1 сек  uint16_t prescaler;  uint8_t cs_bits;  double tick_us;  if (period_us <= 4096) {    prescaler = 1;     cs_bits = 0b001; tick_us = 0.0625;  } else if (period_us <= 32768) {    prescaler = 8;     cs_bits = 0b010; tick_us = 0.5;  } else if (period_us <= 262144) {    prescaler = 64;    cs_bits = 0b011; tick_us = 4.0;  } else if (period_us <= 1048576) {    prescaler = 256;   cs_bits = 0b100; tick_us = 16.0;  } else {    prescaler = 1024;  cs_bits = 0b101; tick_us = 64.0;  }  double clock_mhz = 16.0;  double ticks_period = (period_us * clock_mhz) / prescaler;  uint16_t icr = round(ticks_period) - 1;  if (icr < 1) icr = 1;  double ticks_high = (pulseWidth * clock_mhz) / prescaler;  uint16_t ocr = round(ticks_high) - 1;  if (ocr > icr) ocr = icr;  if (ocr < 0) ocr = 0;  noInterrupts();  TCCR1A = 0;  TCCR1B = 0;  TCNT1  = 0;  ICR1   = icr;  OCR1A  = ocr;  // Инвертированный fast PWM (чтобы HIGH на выходе оптопары соответствовал открытому MOSFET)  TCCR1A = (1 << COM1A1) | (1 << COM1A0) | (1 << WGM11);  TCCR1B = (1 << WGM13) | (1 << WGM12) | cs_bits;  interrupts();}// Обновление дисплея с мигающим курсоромvoid updateDisplay() {  lcd.clear();  lcd.setCursor(0, 0);  lcd.print("Pulse: ");  for (int i = 0; i < 6; i++) lcd.print(pulseDigits[i]);  lcd.print(" us");  lcd.setCursor(0, 1);  lcd.print("Pause: ");  for (int i = 0; i < 6; i++) lcd.print(pauseDigits[i]);  lcd.print(" us");  lcd.setCursor(0, 2);  lcd.print("Mode: ");  if (mode == 0) lcd.print("Continuous");  else lcd.print("Burst");  if (mode == 1) {    lcd.setCursor(0, 3);    lcd.print("Burst: ");    for (int i = 0; i < 6; i++) lcd.print(burstDigits[i]);  }  // Мигающий курсор  int row, col;  if (currentSection == 0) { row = 0; col = 7 + currentDigit; }  else if (currentSection == 1) { row = 1; col = 7 + currentDigit; }  else if (currentSection == 2) { row = 2; col = 6; }  else { row = 3; col = 7 + currentDigit; }  lcd.setCursor(col, row);  lcd.blink();}// ---------- Инициализация ----------void setup() {  pinMode(button1Pin, INPUT_PULLUP);  pinMode(button2Pin, INPUT_PULLUP);  pinMode(outputPin, OUTPUT);  // Начальное состояние: светодиод оптопары горит -> выход LOW -> MOSFET закрыт  digitalWrite(outputPin, HIGH);  lcd.init();  lcd.backlight();  pulseWidth = getValue(pulseDigits);  pauseWidth = getValue(pauseDigits);  burstCount = getValue(burstDigits);  if (mode == 0) configureTimer();  // Если Continuous, запускаем таймер  updateDisplay();}// ---------- Основной цикл ----------void loop() {  // 🔘 Кнопка 1 (изменение значений)  int reading1 = digitalRead(button1Pin);  if (reading1 != lastButton1Reading) {    lastDebounceTime1 = millis();  }  if ((millis() - lastDebounceTime1) > debounceDelay) {    if (reading1 != button1State) {      button1State = reading1;      if (button1State == LOW) {        button1PressTime = millis();  // нажали      } else {        // Отпустили кнопку        unsigned long duration = millis() - button1PressTime;        bool paramsChanged = false;        if (currentSection == 0 || currentSection == 1 || (currentSection == 3 && mode == 1)) {          char* digits = (currentSection == 0) ? pulseDigits :                         (currentSection == 1) ? pauseDigits : burstDigits;          if (duration < longPressThreshold) {            // Короткое нажатие: увеличить цифру            digits[currentDigit]++;            if (digits[currentDigit] > '9') digits[currentDigit] = '0';            paramsChanged = true;          } else {            // Длинное нажатие: переход к следующей цифре            currentDigit = (currentDigit + 1) % 6;          }        } else if (currentSection == 2) {          // Раздел Mode: короткое нажатие переключает Continuous/Burst          if (duration < longPressThreshold) {            mode = (mode + 1) % 2;            paramsChanged = true;          }        }        // Если параметры изменились, пересчитываем и сбрасываем генератор        if (paramsChanged) {          // Принудительно закрываем MOSFET          digitalWrite(outputPin, HIGH);          noInterrupts();          TCCR1A = 0;   // остановка таймера          TCCR1B = 0;          interrupts();          generating = false;          pulseWidth = getValue(pulseDigits);          pauseWidth = getValue(pauseDigits);          burstCount = getValue(burstDigits);          if (mode == 0) configureTimer();  // перезапустить Continuous, если нужно        }        updateDisplay();      }    }  }  lastButton1Reading = reading1;  // 🔘 Кнопка 2 (переключение разделов и запуск Burst)  int reading2 = digitalRead(button2Pin);  if (reading2 != lastButton2Reading) {    lastDebounceTime2 = millis();  }  if ((millis() - lastDebounceTime2) > debounceDelay) {    if (reading2 != button2State) {      button2State = reading2;      if (button2State == LOW) {        button2PressTime = millis();      } else {        unsigned long duration = millis() - button2PressTime;        if (duration < longPressThreshold) {          // Короткое нажатие: выбор следующего раздела меню          currentSection = (currentSection + 1) % (mode == 0 ? 3 : 4);          currentDigit = 0;        } else {          // Длинное нажатие: запустить пачку (только в режиме Burst)          if (mode == 1 && !generating && burstCount > 0) {            generating = true;            // Задаём длительности без коррекции (6N137 быстрая)            unsigned long pulseEffective = pulseWidth;            unsigned long pauseEffective = pauseWidth;            for (unsigned long i = 0; i < burstCount; i++) {              // Быстрый LOW: сбрасываем бит 1 порта B              PORTB &= ~(1 << PB1);              delayMicroseconds(pulseEffective);                            // Быстрый HIGH: устанавливаем бит 1 порта B              PORTB |= (1 << PB1);                            if (i < burstCount - 1) {                delayMicroseconds(pauseEffective);              }            }            generating = false;          }        }        updateDisplay();      }    }  }  lastButton2Reading = reading2;  // Если режим Continuous и таймер остановлен (например, был сброс параметров),  // он автоматически не перезапустится, но в setup() и при изменении настроек мы его запускаем.}

ссылка на оригинал статьи https://habr.com/ru/articles/1064696/