Высокочастотный PWM на Adafruit Feather M0
Я пытаюсь заставить Bluefruit Feather M0, у которого есть SAMD21G, выполнять высокочастотную ШИМ, напрямую обращаясь к регистрам таймера, вместо того, чтобы использовать API Arduino для выполнения ШИМ на частоте ~ 1 кГц или ~ 500 Гц. Фактическая частота на данном этапе не важна, я просто хочу намного больше, чем 1 кГц.
Я нашел пример MKR1000, основанный на аналогичном процессоре. Я адаптировал его, как показано ниже. Сравнивая распиновку Adafruit с исходным кодом и с таблицейданных, становится очевидным, что нумерация выводов отличается и что вывод 11, который является единственным доступным из двух примеров выводов, находится здесь на TCC0, а не на TCC1. Единственное изменение, которое я вижу, которое мне нужно было внести, - это дублировать все настройки TCC1 в setupTimers()
и управление рабочим циклом TCC1 в loop()
для TCC0. Тем не менее, на прицеле я не вижу ШИМ-выхода. Затем я попробовал также установить вывод 13 на ШИМ, чтобы увидеть его на встроенном светодиоде. По-прежнему ничего. Сейчас я в некотором замешательстве.
У кого-нибудь есть какие-нибудь идеи?
void setup() {
// поместите сюда свой установочный код, чтобы запустить его один раз:
setupTimers();
Serial.begin(9600);
}
void loop() {
// поместите сюда свой основной код для повторного запуска:
int t = 0;
Serial.println("\nRunning PWM 0 to 100%");
for (t = 0; t < 1000; t = t + 1) {
REG_TCC1_CC1 = t; // TCC1 CC1 - на D3 - ШИМ-сигнализация
while (TCC1->SYNCBUSY.bit.CC1); // Дождаться синхронизации
REG_TCC0_CC1 = t; // TCC1 CC1 - на D3 - ШИМ-сигнализация
while (TCC0->SYNCBUSY.bit.CC1); // Дождаться синхронизации
delay(1);
Serial.print(".");
}
delay(2000);
}
// Вывод ШИМ 24 кГц на цифровой вывод D3 и D11 с помощью таймера TCC1 (разрешение 10 бит)
void setupTimers()
{
REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) | // Разделите источник тактовой частоты 48 МГц на делитель N= 1: 48 МГц / 1=48 МГц
GCLK_GENDIV_ID(4); // Выберите общие часы (GCLK) 4
while (GCLK->STATUS.bit.SYNCBUSY); // Дождаться синхронизации
REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC | // Установите рабочий цикл на 50/50 ВЫСОКИЙ / НИЗКИЙ
GCLK_GENCTRL_GENEN | // Включить GCLK4
GCLK_GENCTRL_SRC_DFLL48M | // Установить источник синхронизации 48 МГц
GCLK_GENCTRL_ID(4); // Выберите GCLK4
while (GCLK->STATUS.bit.SYNCBUSY); // Дождаться синхронизации
// Включить мультиплексор портов для цифровых выводов D3 и D11 **** g_APinDescription() преобразует вывод Arduino в вывод SAMD21
PORT->Group[g_APinDescription[3].ulPort].PINCFG[g_APinDescription[3].ulPin].bit.PMUXEN = 1;
PORT->Group[g_APinDescription[11].ulPort].PINCFG[g_APinDescription[11].ulPin].bit.PMUXEN = 1;
PORT->Group[g_APinDescription[13].ulPort].PINCFG[g_APinDescription[13].ulPin].bit.PMUXEN = 1;
// Подключите таймер TCC1 к цифровому выходу D3 и D11 - контакты портов сопряжены с нечетным PMUO и четным PMUXE
// F & E укажите таймеры: TCC0, TCC1 и TCC2
PORT->Group[g_APinDescription[2].ulPort].PMUX[g_APinDescription[2].ulPin >> 1].reg = PORT_PMUX_PMUXO_E; // D3 находится на PA11 = нечетно, используйте устройство E на TCC1/ WO[1]
PORT->Group[g_APinDescription[11].ulPort].PMUX[g_APinDescription[11].ulPin >> 1].reg = PORT_PMUX_PMUXE_F; // D11 находится на PA08 = четно, используйте устройство F на TCC1 / WO[0]
PORT->Group[g_APinDescription[13].ulPort].PMUX[g_APinDescription[13].ulPin >> 1].reg = PORT_PMUX_PMUXE_E; // Попробуйте индикатор .. не работает с E или F
//На самом деле, контакт 11 на Пере находится на PA16. Все еще даже, не уверен в значимости, но теперь это TCC0/WO[6] и все еще порт F.
// Все, что дальше, касается TCC1!
// Передать GCLK4 в TCC0 и TCC1
REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | // Включить GCLK4 для TCC0 и TCC1
GCLK_CLKCTRL_GEN_GCLK4 | // Выбрать GCLK4
GCLK_CLKCTRL_ID_TCC0_TCC1; // Передать GCLK4 в TCC0 и TCC1
while (GCLK->STATUS.bit.SYNCBUSY); // Дождаться синхронизации
// Работа ШИМ с двойным наклоном: таймеры ведут счетный отсчет до значения каждого регистра, а затем до 0
REG_TCC1_WAVE |= TCC_WAVE_POL(0xF) | // Изменить полярность вывода на всех выходах TCC0
TCC_WAVE_WAVEGEN_DSBOTH; // Настройка ШИМ с двойным наклоном на TCC0
while (TCC1->SYNCBUSY.bit.WAVE); // Ждать синхронизации
// Каждый таймер отсчитывает до максимального или ВЕРХНЕГО значения, установленного регистром PER,
// это определяет частоту работы ШИМ: Freq = 48 МГц/ (2*N*PER)
REG_TCC1_PER = 1000; // Установите частоту ШИМ на TCC1 равной 24 кГц
while (TCC1->SYNCBUSY.bit.PER); // Ждать синхронизации
// Установите выходной сигнал PWM, PWM ds = 2 * N (TOP-CCx) / Freqtcc => PWM = 0 => CCx = PER, PWM = 50% => CCx = PER / 2
REG_TCC1_CC1 = 500; // TCC1 CC1 - на D3 50%
while (TCC1->SYNCBUSY.bit.CC1); // Дождаться синхронизации
REG_TCC1_CC0 = 500; // TCC1 CC0 - на D11 50%
while (TCC1->SYNCBUSY.bit.CC0); // Ждать синхронизации
// Разделите сигнал GCLOCK на 1, давая в этом случае 48 МГц (20,83 нс) тик таймера TCC1, и включите выходы
REG_TCC1_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 | // Разделить GCLK4 на 1
TCC_CTRLA_ENABLE; // Включить вывод TCC0
while (TCC1->SYNCBUSY.bit.ENABLE); // Дождаться синхронизации
//Сделайте все снова для TCC0
// Работа ШИМ с двойным наклоном: таймеры ведут счетный отсчет до значения каждого регистра, а затем до 0
REG_TCC0_WAVE |= TCC_WAVE_POL(0xF) | // Изменить полярность вывода на всех выходах TCC0
TCC_WAVE_WAVEGEN_DSBOTH; // Настройка ШИМ с двойным наклоном на TCC0
while (TCC0->SYNCBUSY.bit.WAVE); // Ждать синхронизации
// Каждый таймер отсчитывает до максимального или ВЕРХНЕГО значения, установленного регистром PER,
// это определяет частоту работы ШИМ: Freq = 48 МГц/ (2*N*PER)
REG_TCC0_PER = 1000; // Установите частоту ШИМ на TCC1 равной 24 кГц
while (TCC0->SYNCBUSY.bit.PER); // Ждать синхронизации
// Установите выходной сигнал PWM, PWM ds = 2 * N (TOP-CCx) / Freqtcc => PWM = 0 => CCx = PER, PWM = 50% => CCx = PER / 2
REG_TCC0_CC1 = 500; // TCC1 CC1 - на D3 50%
while (TCC0->SYNCBUSY.bit.CC1); // Дождаться синхронизации
REG_TCC0_CC0 = 500; // TCC1 CC0 - на D11 50%
while (TCC0->SYNCBUSY.bit.CC0); // Ждать синхронизации
// Разделите сигнал GCLOCK на 1, давая в этом случае 48 МГц (20,83 нс) тик таймера TCC1, и включите выходы
REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 | // Разделить GCLK4 на 1
TCC_CTRLA_ENABLE; // Включить вывод TCC0
while (TCC0->SYNCBUSY.bit.ENABLE); // Дождаться синхронизации
}
@Craig Graham, 👍0
Обсуждение1 ответ
Лучший ответ:
Для этого я создал высокочастотную библиотеку PWM (называемую Arduino_SAMD21_turbo_PWM), и теперь я реализовал таблицу выводов для Adafruit Feather M0.
Однако я не тестировал его, главным образом потому, что у меня нет Adafruit Feather M0. Тем не менее, не стесняйтесь попробовать.
Сама таблица сопоставлений выводов для Adafruit Feather M0, которая AFAIK имеет те же сопоставления выводов, что и Arduino Zero, находится здесь, на Github; возможно, это может помочь.
Для вашей конкретной проблемы: D11 пера
- это PA16 SAMD и на
TCC0 /WO[6]
(используйте REG_TCC0_CCB2
и PORT_PMUX_PMUXE_F
), а также на TCC2 / WO [0]
(используйте REG_TCC2_CCB0
и PORT_PMUX_PMUXE_E
).
- Как изменить скорость нескольких сервоприводов, работающих одновременно с модулем PCA9685?
- Seeeduino СЯО запись и чтение ШИМ продолжительность (период) с помощью таймеров
- Одновременное использование SDA и SCL с аналоговыми входами
- Не удается успешно реализовать синглтон
- Используйте Adafruit 16-Channel 12-bit PWM/Servo Shield для переключения реле
- Несколько записей variant.cpp для одного и того же физического контакта
- Сигнализация Arduino Zero RTC не работает в цикле
- Проблемы загрузки нулевого загрузчика arduino в atsamd21g18a
Если вам нужен только ШИМ-сигнал на D11 (PA16 на вашем MCU), то не настраивайте другие контакты, о которых вы знаете, что они не поддерживаются. Возможно, ваша программа выходит из строя. Вы видите "." на последовательном мониторе? Кстати: в примере случайно настраивается D2 вместо D3. Но теперь вам нужен только D11 ;-). Тогда не настраивайте оба TCC, а только тот, который вам нужен., @Peter Paul Kiefer