Таймеры ESP32 — контроллер выходит из строя при частоте выше 240 кГц, рабочий цикл становится нестабильным
Я пытаюсь сгенерировать три синхронизированных сигнала на ESP32 для устройства, требующего точной синхронизации. Однако при увеличении частоты выше 240 кГц контроллер зависает, а скважность сигналов становится крайне нестабильной.


Требуемые сроки следующие:
- MainClock: прямоугольный сигнал с частотой 1–2 МГц или выше.
- SH должен перейти в высокий уровень с задержкой от 100 до 1000 нс после ICG
- SH должен оставаться высоким в течение как минимум 1000 нс.
- ICG должен быть высоким, когда MainClock высокий
Пока что я застрял на первой точке, чтобы получить стабильные 2 МГц...
#include "Arduino.h"
#define MCLK_PIN 25 // MCLK (главный тактовый генератор) для CCD GPIO.out ^= (1 << MCLK_PIN);
volatile int interruptCounter; // для подсчета прерываний
int totalInterruptCounter; // общий подсчет прерываний
hw_timer_t *timer = NULL; // Определение аппаратного таймера (Указатель на структуру)
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
void IRAM_ATTR onTimer()
{ // Определение функции прерывания с помощью IRAM_ATTR для более быстрого доступа
portENTER_CRITICAL_ISR(&timerMux);
interruptCounter++;
GPIO.out ^= (1 << MCLK_PIN); // инвертировать состояние MCLK_PIN
portEXIT_CRITICAL_ISR(&timerMux);
}
unsigned long prescaler = 2; // если делитель HW TIMER выходит за пределы диапазона [2, 65536] ошибка
unsigned long timerValue = 162;
void setup()
{
Serial.begin(115200);
pinMode(MCLK_PIN, OUTPUT);
timer = timerBegin(1, prescaler, true); // таймер 0, предделитель: 40, счет вверх
timerAttachInterrupt(timer, &onTimer, true); // Присоединить прерывание
timerAlarmWrite(timer, timerValue, true); // Соответствует значению = 1000000 для задержки в 1 секунду.
timerAlarmEnable(timer); // Включить таймер с прерыванием (Alarm Enable)
}
unsigned long lastTime = 0, currentTime = 0;
float frequency = 0, period = 0;
void loop()
{
if (interruptCounter > 0)
{
portENTER_CRITICAL(&timerMux);
interruptCounter--;
portEXIT_CRITICAL(&timerMux);
totalInterruptCounter++;
period = (timerValue * prescaler) / 80.0;
frequency = 1000.0 / period;
if (millis() > currentTime)
{
lastTime = currentTime;
printf("An interrupt has occurred. Total number: %d\n", totalInterruptCounter);
printf("Frequency: %.2f kHz\n", frequency);
printf("Period: %.2f µs\n", period);
currentTime = millis() + 1000;
}
}
}
В чём причина этой нестабильности? Какие методы можно использовать для повышения стабильности?
РЕДАКТИРОВАТЬ
Если я попытаюсь сделать это с помощью ledc, прерывание тоже не будет работать должным образом :(

#include <Arduino.h>
// Расчет настроек для каждой полосы частот
// Разрешение = log2(Частота(80 МГц)/f) + 1 например: 50,000 Гц = 80,0000/50,000 = 1,600 log2(1600) = 10 + 1 = 11
// Обязанность 50% = (2**Разрешение)/2 например: 2**11 = 2048 2048/2 = 1024
#include "driver/ledc.h"
#define LEDC_HS_CH0_CHANNEL LEDC_CHANNEL_0 // канал LEDC 0
#define LEDC_HS_MODE LEDC_HIGH_SPEED_MODE // LEDC на высокой скорости
#define LEDC_HS_TIMER LEDC_TIMER_0 // Использовать timer0ledc
#define MCLK_PIN 25
#define SH_PIN 26
uint32_t resolution = 0; // Разрешение var
uint32_t oscilator = 2000000; // Частота Гц
uint32_t mDuty = 0; // Вычислить нагрузку ledc var
volatile int counter = 0;
void IRAM_ATTR onFallingEdge() {
counter++;
if (counter >= 4) { // 4 падающих ребра
GPIO.out ^= (1 << SH_PIN); // Інверсія стану
counter = 0;
}
}
//-----------------------------------------------------------------------
void ledcInit()
{
resolution = log((80000000 / oscilator) + 1) / log(2); // Расчет разрешения светодиода
if (resolution > 20)
resolution = 20; // Максимальное разрешение 20
mDuty = (pow(2, resolution)) / 2; // Расчет коэффициента заполнения ledc 50%
// Serial.println(mDuty); // Печать
ledc_timer_config_t ledc_timer = {}; // Создаём экземпляр таймера LEDC
ledc_timer.duty_resolution = (ledc_timer_bit_t) + resolution; // Разрешение конфигурации
ledc_timer.freq_hz = oscilator; // Частота
ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE; // Рабочий режим: высокая скорость
ledc_timer.timer_num = LEDC_TIMER_0; // Используем таймер0
ledc_timer_config(&ledc_timer); // Конфигурация таймера ledc
ledc_channel_config_t ledc_channel = {}; // Создает экземпляр конфигурации канала LEDC
ledc_channel.channel = LEDC_HS_CH0_CHANNEL; // Конфигурация канала
ledc_channel.duty = mDuty; // Расчет пошлины %
ledc_channel.gpio_num = MCLK_PIN; // Выходной GPIO
ledc_channel.intr_type = LEDC_INTR_DISABLE; // Отключено прерывание светодиода
ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE; // Работает на высокой скорости
ledc_channel.timer_sel = LEDC_TIMER_0; // Используем таймер0
ledc_channel_config(&ledc_channel); // Конфигурация канала ledc
}
//-----------------------------------------------------------------------
void setup()
{
Serial.begin(115200);
pinMode(SH_PIN, OUTPUT);
ledcInit();
attachInterrupt(digitalPinToInterrupt(MCLK_PIN), onFallingEdge, FALLING);
}
//-----------------------------------------------------------------------
void loop() {}
@Ihor T., 👍0
Обсуждение1 ответ
По моему опыту, он не справляется с высокочастотным взаимодействием. В вашем случае, похоже, вы переусердствовали с его технической стороной. Возможно, стоит просто использовать аппаратные средства, например, делители тактовой частоты, для формирования сигнала.
- Создание регулируемого низкочастотного ШИМ-контроллера на Arduino
- Можно ли сгенерировать точный тактовый импульс 15 кГц с помощью ардуино?
- ESP 32 изменение частоты вместо рабочего цикла
- Настройка вывода на непрерывный выходной сигнал
- Установите частоту ШИМ на 25 кГц.
- Как настроить PWM-сигнал 25 кГц на ESP-WROOM-32?
- Генерировать 1,7 МГц с PWM в Uno?
- Сдвиг уровня ШИМ от 3,3 В до 5 В
Вы измеряли время выполнения вашего ISR? Пожалуйста, [отредактируйте] свой вопрос, добавив это. Скорее всего, ваш ISR использует все тактовые частоты процессора на этой частоте, и это вызывает наблюдаемое поведение., @the busybee