Высокочастотный 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);              // Дождаться синхронизации


}

, 👍0

Обсуждение

Если вам нужен только ШИМ-сигнал на D11 (PA16 на вашем MCU), то не настраивайте другие контакты, о которых вы знаете, что они не поддерживаются. Возможно, ваша программа выходит из строя. Вы видите "." на последовательном мониторе? Кстати: в примере случайно настраивается D2 вместо D3. Но теперь вам нужен только D11 ;-). Тогда не настраивайте оба TCC, а только тот, который вам нужен., @Peter Paul Kiefer


1 ответ


Лучший ответ:

2

Для этого я создал высокочастотную библиотеку 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).

,