I2C NavKey + AD9850

Если кому-то надо срочно проверить эти устройства ниже приведён код, проверен на ESP32S3, собран из примеров, все претензии к авторам

// Использование пинов
// 18 - выходит сигнал для модулятора, 1 килогерц, амплитуда 3 вольта
// 1,3, 4 на модуль I2C NavKey
// 9, 10, 11, 13 на модуль AD9850
// кнопка влево переключает на регулирование энкодером частоты модуляции
// кнопка вправо переключает энкодер для регулирования несущей частоты кратно 1 килогерцу

#include <Wire.h>
#include <i2cNavKey.h>
#include <Ticker.h>
#include "AD985X.h"

#ifndef ESP32
#error ESP32 only example, please select appropriate board
#endif

uint8_t AD_RST = 9;
uint8_t AD_FQUDP = 10;
uint8_t AD_DATA = 11;
uint8_t AD_CLK = 13;
AD9850 freqGen(AD_RST, AD_FQUDP, AD_DATA, AD_CLK);

uint32_t rife_freq = 1100000;
uint32_t rife_prev = 0;
uint32_t rife_maxFreq;


Ticker tNavkey;  // Таймер для обработки NavKey

// Генерируем ШИМ для модулятора
// Конфигурация ШИМ
const int pwmPin = 18;      // GPIO для выхода ШИМ
volatile int freq = 1000;   // Частота 1 кГц
const int ledChannel = 0;   // Канал LEDC (0-7 для S3)
const int resolution = 10;  // Разрешение 10 бит (значения 0-1023)
volatile int fcounter;
volatile int fcounter_old;
volatile bool fflag = true;  // флаг, какую частоту меняем? true - модуляция, false - несущая

// Настройки пинов для ESP32-S3
const int SDA_PIN = 1;
const int SCL_PIN = 3;
const int INT_PIN = 4;

void help() {
  Serial.println();
  Serial.println("+ :  f = f + 1");
  Serial.println("- :  f = f - 1");
  Serial.println("* :  f = f * 10");
  Serial.println("/ :  f = f / 10");
  Serial.println("? :  help");
  Serial.println("R :  AD9850 reset");
  Serial.println("P :  AD9850 power down");
  Serial.println("U :  AD9850 power up");
  Serial.println();
}

// Инициализация объекта. Адрес 0x10 соответствует вашему 0b0010000
i2cNavKey navkey(0x10);

volatile bool eventFlag = false;

// Функция прерывания (обработчик) NavKey
void IRAM_ATTR navkeyISR() {
  eventFlag = true;
}

// Функция опроса срабатывания энкодера
// Если сработало прерывание (нажат пин 4)
void tnav() {
  if (eventFlag) {
    eventFlag = false;
    navkey.updateStatus();  // Эта функция вызовет нужный callback автоматически
  }
}

// --- Функции обратного вызова (Callbacks) ---
void UP_Button_Pressed(i2cNavKey* p) {
  Serial.println("ВВЕРХ нажата");
}
void DOWN_Button_Pressed(i2cNavKey* p) {
  Serial.println("ВНИЗ нажата");
}
void LEFT_Button_Pressed(i2cNavKey* p) {
  Serial.println("ВЛЕВО нажата");
  fflag = true;
}
void RIGHT_Button_Pressed(i2cNavKey* p) {
  Serial.println("ВПРАВО нажата");
  fflag = false;
}
void CENTRAL_Button_Pressed(i2cNavKey* p) {
  Serial.println("ЦЕНТР нажата");
}
void CENTRAL_Button_Double(i2cNavKey* p) {
  Serial.println("Двойное нажатие ЦЕНТР!");
}
void Encoder_Rotate(i2cNavKey* p) {
  Serial.printf("Значение энкодера: %d\n", p->readCounterInt());

  // Меняем частоту если значение изменилось, кратно 10 герц
  if (fflag) {
    fcounter = p->readCounterInt();
    if (fcounter > fcounter_old) {
      freq += 10;
      fcounter_old = fcounter;
      ledcChangeFrequency(pwmPin, freq, resolution);
    }
    if (fcounter < fcounter_old) {
      freq -= 10;
      fcounter_old = fcounter;
      ledcChangeFrequency(pwmPin, freq, resolution);
    }
  } else {
    fcounter = p->readCounterInt();
    if (fcounter > fcounter_old) {
      rife_freq += 1000;
      rife_prev = rife_freq;
      fcounter_old = fcounter;
      freqGen.setFrequency(rife_freq);
      Serial.println(rife_freq);
    }
    if (fcounter < fcounter_old) {
      rife_freq -= 1000;
      rife_prev = rife_freq;
      fcounter_old = fcounter;
      freqGen.setFrequency(rife_freq);
      Serial.println(rife_freq);
    }
  }
}


void setup(void) {
  Serial.begin(115200);

  // Инициализация I2C на пинах 1 и 3 для ESP32-S3
  Wire.begin(SDA_PIN, SCL_PIN);

  // Настройка прерывания на ESP32
  pinMode(INT_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(INT_PIN), navkeyISR, FALLING);

  Serial.println("**** I2C navkey V2 ESP32-S3 Ready ****");

  navkey.reset();

  // Конфигурация NavKey
  navkey.begin(i2cNavKey::INT_DATA | i2cNavKey::WRAP_ENABLE | i2cNavKey::DIRE_RIGHT | i2cNavKey::IPUP_ENABLE);

  navkey.writeCounter((int32_t)0);
  navkey.writeMax((int32_t)256);
  navkey.writeMin((int32_t)-256);
  navkey.writeStep((int32_t)1);
  navkey.writeDoublePushPeriod(50);  // 300ms

  // Привязка событий
  navkey.onUpPush = UP_Button_Pressed;
  navkey.onDownPush = DOWN_Button_Pressed;
  navkey.onRightPush = RIGHT_Button_Pressed;
  navkey.onLeftPush = LEFT_Button_Pressed;
  navkey.onCentralPush = CENTRAL_Button_Pressed;
  navkey.onCentralDoublePush = CENTRAL_Button_Double;
  navkey.onChange = Encoder_Rotate;

  // Активация прерываний в самом устройстве
  navkey.autoconfigInterrupt();

  Serial.print("ID CODE: 0x");
  Serial.println(navkey.readIDCode(), HEX);

  // Включим опрашивание энкодра каждые 5 миллисекунд
  tNavkey.attach(0.005, tnav);

  // Для ядра менее 3.0
  // Настройка таймера LEDC
  // ledcSetup(ledChannel, freq, resolution);
  // Привязка канала к GPIO
  // ledcAttachPin(pwmPin, ledChannel);
  ledcAttach(pwmPin, freq, resolution);

  // Вывод сигнала ШИМ
  ledcWrite(pwmPin, 512);  // PWM 50%
  Serial.println("PWM on pin18 is ON");

  Serial.println();
  Serial.println(__FILE__);
  Serial.print("AD985X_LIB_VERSION: \t");
  Serial.println(AD985X_LIB_VERSION);
  Serial.println();

  freqGen.begin();

  freqGen.powerUp();
  rife_maxFreq = freqGen.getMaxFrequency();
  Serial.println(rife_maxFreq);

  help();
}



void loop() {
  if (Serial.available() > 0) {
    int c = Serial.read();
    switch (c) {
      case '?':
        help();
        break;
      case 'R':
        freqGen.reset();
        rife_freq = freqGen.getFrequency();
        break;
      case 'P':
        freqGen.powerDown();
        break;
      case 'U':
        freqGen.powerUp();
        break;
      case '+':
        rife_freq += 1;
        break;
      case '-':
        rife_freq -= 1;
        break;
      case '*':
        rife_freq *= 10;
        break;
      case '/':
        rife_freq /= 10;
        break;
    }
    if (rife_freq > rife_maxFreq) rife_freq = rife_maxFreq;
  }

  // UPDATE AD985X IF NEW VALUE
  if (rife_prev != rife_freq) {
    rife_prev = rife_freq;
    freqGen.setFrequency(rife_freq);
    Serial.println(rife_freq);
  }
}

Странная причина открыть ветку на форуме -
“я скопировал чужой код, для чего не знаю, если что - я не виноват”… ?

я то точно знаю для чего, если у тебя не хватает IQ (ну нету красного диплома) я то тут при чём?

И после этого кода не понятно?

// Использование пинов
// 15 - выходит сигнал для модулятора, 1 килогерц, амплитуда 3 вольта
// 18 - выходит сигнал для модулятора, 1 килогерц, амплитуда 0.2 вольта
// 17 - CLK к PDM сигналу DATA на выводе 18
// 1,3, 4 на модуль I2C NavKey
// 9, 10, 11, 13 на модуль AD9850
// кнопка влево переключает на регулирование энкодером частоты модуляции
// кнопка вправо переключает энкодер для регулирования несущей частоты кратно 1 килогерцу
// вывод меандра на пин 15
// вывод синусоидального сигнала на пин 18, заняты 2 пина 17 и 18, сигнал снимать через RC цепь
// не забываем, на выводе 18 будет присутствовать постоянная составляющая, сигнал на модулятор
// подаём через конденсатор, чтобы полевой транзистор остался в рабочем режиме смесителя

#include <Wire.h>
#include <i2cNavKey.h>
#include <Ticker.h>
#include "AD985X.h"

#ifndef ESP32
#error ESP32 only example, please select appropriate board
#endif

uint8_t AD_RST = 9;
uint8_t AD_FQUDP = 10;
uint8_t AD_DATA = 11;
uint8_t AD_CLK = 13;
AD9850 freqGen(AD_RST, AD_FQUDP, AD_DATA, AD_CLK);

uint32_t rife_freq = 1100000;
uint32_t rife_prev = 0;
uint32_t rife_maxFreq;


Ticker tNavkey;  // Таймер для обработки NavKey

// Генерируем ШИМ для модулятора
// Конфигурация ШИМ
const int pwmPin = 15;      // GPIO для выхода ШИМ
volatile int freq = 1000;   // Частота 1 кГц
const int ledChannel = 0;   // Канал LEDC (0-7 для S3)
const int resolution = 10;  // Разрешение 10 бит (значения 0-1023)
volatile int fcounter;
volatile int fcounter_old;
volatile bool fflag = true;  // флаг, какую частоту меняем? true - модуляция, false - несущая
volatile bool fmflag = false;

// Настройки пинов для ESP32-S3
const int SDA_PIN = 1;
const int SCL_PIN = 3;
const int INT_PIN = 4;

// Генерация сигнала методом PDM (I2S), фильтр 1к + 0.1мкф
#include <driver/i2s_pdm.h>

// Настройки
#define I2S_BCLK_PIN 17    // PDM CLK
#define I2S_DOUT_PIN 18    // PDM DATA
#define SAMPLE_RATE 48000  // Частота PCM (выбирается под нужный диапазон)
#define BUFFER_SIZE 1024

i2s_chan_handle_t tx_handle;

// Буфер для PCM-данных (16 бит на сэмпл)
int16_t pcm_buffer[BUFFER_SIZE];

// Функция заполнения буфера синусом заданной частоты
void fill_sine_buffer(int16_t* buffer, int samples, float freq_hz, int sample_rate) {
  for (int i = 0; i < samples; i++) {
    float phase = 2.0 * 3.14159 * freq_hz * i / sample_rate;
    buffer[i] = (int16_t)(32767 * sin(phase));
  }
}


void help() {
  Serial.println();
  Serial.println("+ :  f = f + 1");
  Serial.println("- :  f = f - 1");
  Serial.println("* :  f = f * 10");
  Serial.println("/ :  f = f / 10");
  Serial.println("? :  help");
  Serial.println("R :  AD9850 reset");
  Serial.println("P :  AD9850 power down");
  Serial.println("U :  AD9850 power up");
  Serial.println();
}

// Инициализация объекта. Адрес 0x10 соответствует вашему 0b0010000
i2cNavKey navkey(0x10);

volatile bool eventFlag = false;

// Функция прерывания (обработчик) NavKey
void IRAM_ATTR navkeyISR() {
  eventFlag = true;
}

// Функция опроса срабатывания энкодера
// Если сработало прерывание (нажат пин 4)
void tnav() {
  if (eventFlag) {
    eventFlag = false;
    navkey.updateStatus();  // Эта функция вызовет нужный callback автоматически
  }
}

// --- Функции обратного вызова (Callbacks) ---
void UP_Button_Pressed(i2cNavKey* p) {
  Serial.println("ВВЕРХ нажата");
}
void DOWN_Button_Pressed(i2cNavKey* p) {
  Serial.println("ВНИЗ нажата");
}
void LEFT_Button_Pressed(i2cNavKey* p) {
  Serial.println("ВЛЕВО нажата");
  fflag = true;
}
void RIGHT_Button_Pressed(i2cNavKey* p) {
  Serial.println("ВПРАВО нажата");
  fflag = false;
}
void CENTRAL_Button_Pressed(i2cNavKey* p) {
  Serial.println("ЦЕНТР нажата");
}
void CENTRAL_Button_Double(i2cNavKey* p) {
  Serial.println("Двойное нажатие ЦЕНТР!");
}
void Encoder_Rotate(i2cNavKey* p) {
  Serial.printf("Значение энкодера: %d\n", p->readCounterInt());

  // Меняем частоту если значение изменилось, кратно 10 герц
  if (fflag) {
    fcounter = p->readCounterInt();
    if (fcounter > fcounter_old) {
      freq += 10;
      fcounter_old = fcounter;
      fmflag = true;
      ledcChangeFrequency(pwmPin, freq, resolution);
    }
    if (fcounter < fcounter_old) {
      freq -= 10;
      fcounter_old = fcounter;
      fmflag = true;
      ledcChangeFrequency(pwmPin, freq, resolution);
    }
  } else {
    fcounter = p->readCounterInt();
    if (fcounter > fcounter_old) {
      rife_freq += 1000;
      rife_prev = rife_freq;
      fcounter_old = fcounter;
      freqGen.setFrequency(rife_freq);
      Serial.println(rife_freq);
    }
    if (fcounter < fcounter_old) {
      rife_freq -= 1000;
      rife_prev = rife_freq;
      fcounter_old = fcounter;
      freqGen.setFrequency(rife_freq);
      Serial.println(rife_freq);
    }
  }
}


void setup(void) {
  Serial.begin(115200);

  // Инициализация I2C на пинах 1 и 3 для ESP32-S3
  Wire.begin(SDA_PIN, SCL_PIN);

  // Настройка прерывания на ESP32
  pinMode(INT_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(INT_PIN), navkeyISR, FALLING);

  Serial.println("**** I2C navkey V2 ESP32-S3 Ready ****");

  navkey.reset();

  // Конфигурация NavKey
  navkey.begin(i2cNavKey::INT_DATA | i2cNavKey::WRAP_ENABLE | i2cNavKey::DIRE_RIGHT | i2cNavKey::IPUP_ENABLE);

  navkey.writeCounter((int32_t)0);
  navkey.writeMax((int32_t)256);
  navkey.writeMin((int32_t)-256);
  navkey.writeStep((int32_t)1);
  navkey.writeDoublePushPeriod(50);  // 300ms

  // Привязка событий
  navkey.onUpPush = UP_Button_Pressed;
  navkey.onDownPush = DOWN_Button_Pressed;
  navkey.onRightPush = RIGHT_Button_Pressed;
  navkey.onLeftPush = LEFT_Button_Pressed;
  navkey.onCentralPush = CENTRAL_Button_Pressed;
  navkey.onCentralDoublePush = CENTRAL_Button_Double;
  navkey.onChange = Encoder_Rotate;

  // Активация прерываний в самом устройстве
  navkey.autoconfigInterrupt();

  Serial.print("ID CODE: 0x");
  Serial.println(navkey.readIDCode(), HEX);

  // Включим опрашивание энкодра каждые 5 миллисекунд
  tNavkey.attach(0.005, tnav);

  // Для ядра менее 3.0
  // Настройка таймера LEDC
  // ledcSetup(ledChannel, freq, resolution);
  // Привязка канала к GPIO
  // ledcAttachPin(pwmPin, ledChannel);
  ledcAttach(pwmPin, freq, resolution);

  // Вывод сигнала ШИМ
  ledcWrite(pwmPin, 512);  // PWM 50%
  Serial.println("PWM on pin18 is ON");

  Serial.println();
  Serial.println(__FILE__);
  Serial.print("AD985X_LIB_VERSION: \t");
  Serial.println(AD985X_LIB_VERSION);
  Serial.println();

  freqGen.begin();

  freqGen.powerUp();
  rife_maxFreq = freqGen.getMaxFrequency();
  Serial.println(rife_maxFreq);

  help();

  // Вывод синуса через PDM
  // 1. Конфигурация канала
  i2s_chan_config_t chan_cfg = {
    .id = I2S_NUM_AUTO,
    .role = I2S_ROLE_MASTER,
    .dma_desc_num = 6,
    .dma_frame_num = 256,
    .auto_clear_after_cb = false,
    .auto_clear_before_cb = false,
  };
  ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle, NULL));

  // 2. Конфигурация PDM TX
  i2s_pdm_tx_clk_config_t clk_cfg = {
    .sample_rate_hz = SAMPLE_RATE,
    .clk_src = I2S_CLK_SRC_DEFAULT,
    .mclk_multiple = I2S_MCLK_MULTIPLE_256,
    .up_sample_fp = 960,  // числитель oversampling
    .up_sample_fs = 480,  // знаменатель (960/480 = 2 – фикс. коэф.)
    .bclk_div = 8,
  };

  i2s_pdm_tx_slot_config_t slot_cfg = {
    .data_bit_width = I2S_DATA_BIT_WIDTH_16BIT,
    .slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO,
    .slot_mode = I2S_SLOT_MODE_MONO,
  };

  i2s_pdm_tx_gpio_config_t gpio_cfg = {
    .clk = (gpio_num_t)I2S_BCLK_PIN,
    .dout = (gpio_num_t)I2S_DOUT_PIN,
    .invert_flags = {
      .clk_inv = false,
    },
  };

  i2s_pdm_tx_config_t pdm_tx_cfg = {
    .clk_cfg = clk_cfg,
    .slot_cfg = slot_cfg,
    .gpio_cfg = gpio_cfg,
  };

  ESP_ERROR_CHECK(i2s_channel_init_pdm_tx_mode(tx_handle, &pdm_tx_cfg));
  ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));

  // 3. Заполняем буфер синусом, например, 1000 Гц
  fill_sine_buffer(pcm_buffer, BUFFER_SIZE, freq, SAMPLE_RATE);
}



void loop() {
  if (Serial.available() > 0) {
    int c = Serial.read();
    switch (c) {
      case '?':
        help();
        break;
      case 'R':
        freqGen.reset();
        rife_freq = freqGen.getFrequency();
        break;
      case 'P':
        freqGen.powerDown();
        break;
      case 'U':
        freqGen.powerUp();
        break;
      case '+':
        rife_freq += 1;
        break;
      case '-':
        rife_freq -= 1;
        break;
      case '*':
        rife_freq *= 10;
        break;
      case '/':
        rife_freq /= 10;
        break;
    }
    if (rife_freq > rife_maxFreq) rife_freq = rife_maxFreq;
  }

  // UPDATE AD985X IF NEW VALUE
  if (rife_prev != rife_freq) {
    rife_prev = rife_freq;
    freqGen.setFrequency(rife_freq);
    Serial.println(rife_freq);
  }

  size_t bytes_written;
  i2s_channel_write(tx_handle, pcm_buffer, BUFFER_SIZE * sizeof(int16_t), &bytes_written, portMAX_DELAY);
  if (fmflag) {
    fmflag = false;
    fill_sine_buffer(pcm_buffer, BUFFER_SIZE, freq, SAMPLE_RATE);
    Serial.printf("Freq changed to %d Hz\n", freq);
  }
}

Ой. Не компилируется! :roll_eyes:

даже у девочек компилируется, а у тебя нет, грустно однако…

Скетч использует 391906 байт (18%) памяти устройства. Всего доступно 2097152 байт.
Глобальные переменные используют 25452 байт (7%) динамической памяти, оставляя 302228 байт для локальных переменных. Максимум: 327680 байт.

“Это опять-таки случай так называемого вранья”(с)
image

Ещё раз, компилируется даже у девочек - проверен на ESP32S3
Для этого камня и пишется
Мантру прочитал перед компилированием?

09:16:53.968 -> rst:0x1 (POWERON),boot:0x2b (SPI_FAST_FLASH_BOOT)
09:16:53.968 -> SPIWP:0xee
09:16:53.968 -> mode:DIO, clock div:1
09:16:53.968 -> load:0x3fce2820,len:0x118c
09:16:53.968 -> load:0x403c8700,len:0x4
09:16:53.968 -> load:0x403c8704,len:0xc20
09:16:53.968 -> load:0x403cb700,len:0x30e0
09:16:53.968 -> entry 0x403c88b8
09:16:54.043 -> **** I2C navkey V2 ESP32-S3 Ready ****
09:16:54.075 -> ID CODE: 0x5B
09:16:54.075 -> PWM on pin18 is ON
09:16:54.075 -> 
09:16:54.075 -> C:\ARDUINO\2026\I2C_NavKey_V03\I2C_NavKey_V03.ino
09:16:54.075 -> AD985X_LIB_VERSION: 	0.7.3
09:16:54.075 -> 
09:16:54.075 -> 40000000
09:16:54.075 -> 
09:16:54.075 -> + :  f = f + 1
09:16:54.075 -> - :  f = f - 1
09:16:54.075 -> * :  f = f * 10
09:16:54.075 -> / :  f = f / 10
09:16:54.075 -> ? :  help
09:16:54.075 -> R :  AD9850 reset
09:16:54.075 -> P :  AD9850 power down
09:16:54.075 -> U :  AD9850 power up
09:16:54.118 -> 
09:16:54.118 -> 110000

Ну не всем же звёздами быть. Ещё раз
image
Может, кроме мантры, ещё чего не хватает!? :roll_eyes:

ну не знаю…ядро 3.0.5 ты же ошибку не выкладываешь, да и зачем оно тебе, у тебя есть I2CNavKey?

Вдруг война, а я уставший Вдруг появится, а я и проверить не смогу! :sob: Вот готовлюсь!

ты в каком бункере сидишь, оглянись вокруг…

Используем библиотеку Wire версии 3.2.0:  
Используем библиотеку DuPPa Library версии 1.2.0: 
Используем библиотеку Ticker версии 3.2.0: 
Используем библиотеку AD985X версии 0.7.3: 
Используем библиотеку SPI версии 3.2.0: 
"C:\\Users\\***\\AppData\\Local\\Arduino15\\packages\\esp32\\tools\\esp-x32\\2411/bin/xtensa-esp32s3-elf-size" 
-A "C:\\Users\\***\\AppData\\Local\\arduino\\sketches\\96166668E8C2972A1ABC3B889063B617/I2C_NavKey_V03.ino.elf"
Скетч использует 391906 байт (18%) памяти устройства. Всего доступно 2097152 байт.
Глобальные переменные используют 25452 байт (7%) динамической памяти, оставляя 302228 байт для локальных переменных. Максимум: 327680 байт.

Ух тыж, а что это?

#include [<i2cNavKey.h>](https://github.com/DuPPadotnet/ArduinoDuPPaLib)

Я рад, что хотя бы ты знаешь

Нет. И для чего же ты выложил этот код на форум?

А-а-а. А такого в скейтче не было. :face_with_raised_eyebrow: И там эта библиотека есть?

слона то я и не приметил…

В смысле в

Вроде по-русски всё написано!? Как можно не понять!?

Да как бы не до улыбок , когда обещают быстроту, а на поверку выходит, что не выходит.

так я тут при чем, что у тебя “академическим виделся текст, а на поверку - про секс”, видимо карма у тебя такая, не к производителю идти за библиотекой, а лазить по помойкам, других вариантов не вижу

Ндя! Вот и поговорили. Полезная тема, может в проекты её!? :face_with_raised_eyebrow: