Seeeduino СЯО запись и чтение ШИМ продолжительность (период) с помощью таймеров
Я пытаюсь установить связь между двумя Seeeduino XIAO (чип: ATSAMD21G18A-MU) с помощью захвата TCC с использованием регистровых таймеров.
Длительность импульса прямоугольной волны варьируется от 200 нс до 4 мс.
Я нашел эти два примера кода и теперь пытаюсь изменить их, чтобы они работали так, как я намерен читать и записывать эти короткие импульсы.
1) СЯО пишет квадратную волну (генерирует вывод)
Этот код работает, но он изменяет ширину импульса, а не период импульса. Выход находится на выводах D2
и D3
. Как я могу заставить его изменить период поля вместо ширины поля? Источник
// Output 300kHz dual slope PWM on TCC0 with complementary outputs and dead time insertion
void setup()
{
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | // Enable GCLK0
GCLK_CLKCTRL_GEN_GCLK0 | // Select GCLK0 at 48MHz
GCLK_CLKCTRL_ID_TCC0_TCC1; // Route GCLK0 to TCC0 and TCC1
PORT->Group[PORTA].PINCFG[10].bit.PMUXEN = 1; // Enable the port multiplexer for port pins PA10 and PA11
PORT->Group[PORTA].PINCFG[11].bit.PMUXEN = 1;
// Select the port pin multiplexer switch to option F for TCC0/WO[2] and TCC0/WO[3] on
// port pins PA10 and PA11 respectively
PORT->Group[PORTA].PMUX[10 >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F;
TCC0->WAVE.reg = TCC_WAVE_POL2 | // Reverse the signal polarity on channel 2
TCC_WAVE_WAVEGEN_DSBOTTOM; // Dual slope PWM on TCC0
while (TCC0->SYNCBUSY.bit.WAVE); // Wait for synchronization
// 48,000,000 / 300,000 = 160
TCC0->PER.reg = 79; // Set the frequency of the PWM on TCC0 to 300kHz
while(TCC0->SYNCBUSY.bit.PER); // Wait for synchronization
TCC0->CC[2].reg = 40; // Output a 50% duty-cycle
while(TCC0->SYNCBUSY.bit.CC2); // Wait for synchronization
TCC0->CC[3].reg = 35; // Output a 43% duty-cycle
while(TCC0->SYNCBUSY.bit.CC3); // Wait for synchronization
TCC0->CTRLA.bit.ENABLE = 1; // Enable TCC0
while (TCC0->SYNCBUSY.bit.ENABLE); // Wait for synchronization
}
void loop(){
TCC0->CTRLBSET.reg = TCC_CTRLBSET_LUPD; // Set the Lock Update (LUPD) bit
while (TCC0->SYNCBUSY.bit.CTRLB); // Wait for synchroniztion
TCC0->CCB[2].reg = 40; // Output a 50% duty-cycle
while(TCC0->SYNCBUSY.bit.CCB2); // Wait for synchronization
TCC0->CCB[3].reg = 35; // Output a 43% duty-cycle
while(TCC0->SYNCBUSY.bit.CCB3); // Wait for synchronization
TCC0->CTRLBCLR.reg = TCC_CTRLBCLR_LUPD; // Clear the Lock Update (LUPD) bit
while (TCC0->SYNCBUSY.bit.CTRLB); // Wait for synchroniztion
delay(1000); // Wait for 1 second
TCC0->CTRLBSET.reg = TCC_CTRLBSET_LUPD; // Set the Lock Update (LUPD) bit
while (TCC0->SYNCBUSY.bit.CTRLB); // Wait for synchroniztion
TCC0->CCB[2].reg = 20; // Output a 25% duty-cycle
while(TCC0->SYNCBUSY.bit.CCB2); // Wait for synchronization
TCC0->CCB[3].reg = 15; // Output a 18% duty-cycle
while(TCC0->SYNCBUSY.bit.CCB3); // Wait for synchronization
TCC0->CTRLBCLR.reg = TCC_CTRLBCLR_LUPD; // Clear the Lock Update (LUPD) bit
while (TCC0->SYNCBUSY.bit.CTRLB); // Wait for synchroniztion
delay(1000);
}
2) Квадратная волна считывания СЯО (захват входного сигнала); Захват периода и ширины импульса (PPW)
Этот код работает, но он считывает частоту в течение 1 секунды, а не только продолжительность периода времени. Вход находится на D6
. Как я могу внести эти изменения в код, чтобы он работал? Источник
// Подсчитайте количество импульсов на выводе D6 (PB08) за 1 секунду
void setup()
{
SerialUSB.begin(115200); // Инициализируйте собственный последовательный порт
while(!SerialUSB); // Дождитесь открытия консоли
PM->APBCMASK.reg |= PM_APBCMASK_EVSYS; // Включите периферийное устройство системы событий
////////////////////////////////////////////////////////////////////////////////////////
// Общая Инициализация Clock
GCLK->GENDIV.reg = GCLK_GENDIV_DIV(1) | // Выберите делитель часов на 1
GCLK_GENDIV_ID(4); // Выберите GLCK4
GCLK->GENCTRL.reg = GCLK_GENCTRL_IDC | // Установите рабочий цикл на 50/50 ВЫСОКИЙ/НИЗКИЙ
GCLK_GENCTRL_GENEN | // Включить GCLK
GCLK_GENCTRL_SRC_XOSC32K | // Выбрать источник GCLK в качестве
// внешнего кристалла 32,768 кГц (XOSC32K)
GCLK_GENCTRL_ID(4); // Выберите GCLK4
while (GCLK->STATUS.bit.SYNCBUSY); // Дождитесь синхронизации
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | // Включить общие часы
GCLK_CLKCTRL_GEN_GCLK0 | // GCLK0 на 48MHz
GCLK_CLKCTRL_ID_TCC0_TCC1; // В качестве источника тактовой частоты для TCC0 и TCC1
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | // Включить общие часы
GCLK_CLKCTRL_GEN_GCLK4 | // GCLK4 на частоте 32,768 кГц
GCLK_CLKCTRL_ID_TCC2_TC3; // В качестве источника синхронизации для TCC2 и TC3
//////////////////////////////////////////////////////////////////////////////////////////////
// Инициализация TCC2 - опорный таймер: измеряет период 1 с
TCC2->PER.reg = 32767; // Установите регистр периода (PER) для
// периода ШИМ 1
while (TCC2->SYNCBUSY.bit.PER); // Дождитесь синхронизации
TCC2->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM; // Установите таймер в нормальный режим ШИМ (NPWM)
while (TCC2->SYNCBUSY.bit.WAVE); // Дождитесь синхронизации
TCC2->CTRLBSET.reg = TCC_CTRLBSET_ONESHOT; // Включите операцию oneshot
while(TCC2->SYNCBUSY.bit.CTRLB); // Дождитесь синхронизации
NVIC_SetPriority(TCC2_IRQn, 0); // Установите вложенный векторный контроллер прерываний
// (NVIC) приоритет для TCC2 равным 0 (самый высокий)
NVIC_EnableIRQ(TCC2_IRQn); // Подключите TCC2 к вложенному векторному прерыванию
// Контроллер (NVIC)
TCC2->INTENSET.reg = TCC_INTENSET_OVF; // Включение прерываний переполнения (OVF) на TCC2
TCC2->CTRLA.bit.ENABLE = 1; // Включить TCC2
while (TCC2->SYNCBUSY.bit.ENABLE); // Дождитесь синхронизации
////////////////////////////////////////////////////////////////////////////////////////
// Инициализация TCC0 - счетчик измерений: подсчитывает количество входящих импульсов
PORT->Group[PORTB].PINCFG[8].bit.PMUXEN = 1; // Включить мультиплексор портов
// на выводе порта PB08 (D6)
PORT->Group[PORTB].PMUX[8 >> 1].reg |= PORT_PMUX_PMUXE_A; // Настройка PB08 (D6) в качестве
// EIC (прерывание)
EIC->EVCTRL.reg |= EIC_EVCTRL_EXTINTEO8; // Включить вывод событий при внешнем прерывании 8
EIC->CONFIG[1].reg |= EIC_CONFIG_SENSE0_HIGH; // Установите прерывание для обнаружения ВЫСОКОГО уровня
EIC->INTENCLR.reg = EIC_INTENCLR_EXTINT8; // Снимите флаг прерывания на канале 8
EIC->CTRL.bit.ENABLE = 1; // Включить периферийное устройство EIC
while (EIC->STATUS.bit.SYNCBUSY); // Дождитесь синхронизации
EVSYS->USER.reg = EVSYS_USER_CHANNEL(1) | // Подключите пользователя события (приемник) к
// каналу 0 (n + 1)
EVSYS_USER_USER(EVSYS_ID_USER_TCC0_EV_0); // Установите пользователя события (получателя)
// как таймер TCC0, событие 0
EVSYS->CHANNEL.reg = EVSYS_CHANNEL_EDGSEL_NO_EVT_OUTPUT | // Обнаружение границ событий отсутствует
EVSYS_CHANNEL_PATH_ASYNCHRONOUS | // Задать путь к событию как асинхронный
EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_8) | // установить генератор событий
// (отправитель) в качестве внешнего прерывания 8
EVSYS_CHANNEL_CHANNEL(0); // Подключить генератор (отправитель)
// на канал 0
TCC0->EVCTRL.reg = TCC_EVCTRL_TCEI0 | // Включить входы события 0 TCC0
TCC_EVCTRL_EVACT0_INC; // Увеличить счетчик TCC0 при получении события 0
TCC0->CTRLA.bit.ENABLE = 1; // Включить TCC0
while (TCC0->SYNCBUSY.bit.ENABLE); // Дождитесь синхронизации
}
void loop()
{
startConversion(); // Начните преобразование с задержкой окна интеграции в 1 секунду
delay(3500); // Подождите 1 секунду
}
void startConversion()
{
TCC2->CTRLBSET.reg = TCC_CTRLBSET_CMD_RETRIGGER; // Повторно запустите таймер TCC2, чтобы
// запустить окно интеграции
while (TCC2->SYNCBUSY.bit.CTRLB); // Дождитесь синхронизации
TCC0->CTRLBSET.reg = TCC_CTRLBSET_CMD_RETRIGGER; // Повторно запустите таймер TCC0, чтобы
// начать подсчет
while (TCC0->SYNCBUSY.bit.CTRLB); // Дождитесь синхронизации
}
void TCC2_Handler()
{
TCC2->INTFLAG.bit.OVF = 1; // Снимите флаг прерывания переполнения TCC2
TCC0->CTRLBSET.reg = TCC_CTRLBSET_CMD_READSYNC; // включить синхронизацию чтения
// на счет зарегистрируйтесь
while (TCC0->SYNCBUSY.bit.CTRLB); // ждать реестр CTRLB написать синхронизации
while (TCC0->SYNCBUSY.bit.COUNT); // ждать рассчитывать прочитать регистр синхронизации
SerialUSB.println(TCC0->COUNT.reg); // Распечатать результат
}
Какая-нибудь полезная страница, объясняющая эти таймеры
https://www.picotech.com/support/topic24051.html
https://shawnhymel.com/1710/arduino-zero-samd21-raw-pwm-using-cmsis/
@MeSo2, 👍0
1 ответ
Лучший ответ:
1) Запись коротких импульсов прямоугольной волны на вывод D3
с помощью Seeeduino XIAO
Настройка таймера, полученная из этого сообщения на github и включающая предложение об обновлении от MartinL.
void setup() //TCC1 Timer-Setup AT-SAMD21-G18 ARM Cortex M0
{
setupTimers();
changePer(125);
}
void loop() {
// testing
changePer(20);
delay(3000);
for (byte i = 0; i < 15; i++) {
changePer(40 + (i+1)*4);
delay(1000);
}
delay(2000);
}
// Change Pulse Period
void changePer(uint16_t myPer) {
TCC1->CTRLBSET.reg = TCC_CTRLBSET_LUPD; // Set the Lock Update bit
while (TCC1->SYNCBUSY.bit.CTRLB); // Wait for synchronization
TCC1->PERB.reg = myPer; // Set period
while(TCC1->SYNCBUSY.bit.PERB); // Wait for synchronization
TCC1->CCB[1].reg = myPer/2; // Set duty-cycle to 50%
while(TCC1->SYNCBUSY.bit.CCB0); // Wait for synchronization
TCC1->CTRLBCLR.reg = TCC_CTRLBCLR_LUPD; // Clear the Lock Update bit
while (TCC1->SYNCBUSY.bit.CTRLB); // Wait for synchronization
}
// Output PWM on digital pin D3 using timer TCC1 (10-bit resolution)
void setupTimers() {
REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) | // Divide the 48MHz clock source by divisor N=1: 48MHz/1=48MHz
GCLK_GENDIV_ID(4); // Select Generic Clock (GCLK) 4
while (GCLK->STATUS.bit.SYNCBUSY) ; // Wait for synchronization
REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC | // Set the duty cycle to 50/50 HIGH/LOW
GCLK_GENCTRL_GENEN | // Enable GCLK4
GCLK_GENCTRL_SRC_DFLL48M | // Set the 48MHz clock source
GCLK_GENCTRL_ID(4); // Select GCLK4
while (GCLK->STATUS.bit.SYNCBUSY) ; // Wait for synchronization
// Enable the port multiplexer for the digital pin D3 and D11 **** g_APinDescription() converts Arduino Pin to SAMD21 pin
PORT->Group[g_APinDescription[3].ulPort].PINCFG[g_APinDescription[3].ulPin].bit.PMUXEN = 1;
// Connect the TCC1 timer to digital output D3 - port pins are paired odd PMUO and even PMUXE
// F & E specify the timers: TCC0, TCC1 and TCC2
// D2 is on PA10 = even, use Device E for TCC1
PORT->Group[g_APinDescription[2].ulPort].PMUX[g_APinDescription[2].ulPin >> 1].reg |= PORT_PMUX_PMUXO_E;
// Feed GCLK4 to TCC0 and TCC1
REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | // Enable GCLK4 to TCC0 and TCC1
GCLK_CLKCTRL_GEN_GCLK4 | // Select GCLK4
GCLK_CLKCTRL_ID_TCC0_TCC1; // Feed GCLK4 to TCC0 and TCC1
while (GCLK->STATUS.bit.SYNCBUSY) ; // Wait for synchronization
// Dual slope PWM operation: timers countinuously count up to PER register value then down 0
REG_TCC1_WAVE |= TCC_WAVE_POL(0xF) | // Reverse the output polarity on all TCC0 outputs
TCC_WAVE_WAVEGEN_DSBOTH; // Setup dual slope PWM on TCC0
while (TCC1->SYNCBUSY.bit.WAVE) ; // Wait for synchronization
// Each timer counts up to a maximum or TOP value set by the PER register,
// this determines the frequency of the PWM operation: Freq = 48Mhz/(2*N*PER)
REG_TCC1_PER = 256; // Set the FreqTcc of the PWM on TCC1 to 24Khz
while (TCC1->SYNCBUSY.bit.PER) ; // Wait for synchronization
// Divide the GCLOCK signal by 1 giving in this case 48MHz (20.83ns) TCC1 timer tick and enable the outputs
REG_TCC1_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 | // Divide GCLK4 by 1
TCC_CTRLA_ENABLE; // Enable the TCC0 output
while (TCC1->SYNCBUSY.bit.ENABLE) ; // Wait for synchronization
}
2) Считывание коротких прямоугольных волн с помощью вывода Seeeduino XIAO D2
Именно благодаря Мартинлю я заставил это работать. Источник
// Setup TC4 to capture pulse-width and period on digital pin D2 on Seeeduino Xiao
volatile boolean periodComplete;
volatile uint32_t isrPeriod;
volatile uint32_t isrPulsewidth;
uint32_t period;
uint32_t pulsewidth;
void setup() {
SerialUSB.begin(115200); // Initialise the native serial port
while(!SerialUSB); // Wait for the console to open
PM->APBCMASK.reg |= PM_APBCMASK_EVSYS; // Switch on the event system peripheral
GCLK->GENDIV.reg = GCLK_GENDIV_DIV(1) | // Divide the 48MHz system clock by 1 = 48MHz
GCLK_GENDIV_ID(4); // Set division on Generic Clock Generator (GCLK) 4
GCLK->GENCTRL.reg = GCLK_GENCTRL_IDC | // Set the duty cycle to 50/50 HIGH/LOW
GCLK_GENCTRL_GENEN | // Enable GCLK 4
GCLK_GENCTRL_SRC_DFLL48M | // Set the clock source to 48MHz
GCLK_GENCTRL_ID(4); // Set clock source on GCLK 4
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | // Route GCLK4 to TC4 and TC5
GCLK_CLKCTRL_GEN_GCLK4 |
GCLK_CLKCTRL_ID_TC4_TC5;
// Enable the port multiplexer on port pin PA10
PORT->Group[PORTA].PINCFG[10].bit.PMUXEN = 1;
// Set-up the pin as an EIC (interrupt) on port pin PA10
PORT->Group[PORTA].PMUX[10 >> 1].reg |= PORT_PMUX_PMUXE_A;
EIC->EVCTRL.reg |= EIC_EVCTRL_EXTINTEO10; // Enable event output on external interrupt 10
EIC->CONFIG[1].reg |= EIC_CONFIG_SENSE2_HIGH; // Set event detecting a HIGH level
EIC->INTENCLR.reg = EIC_INTENCLR_EXTINT10; // Disable interrupts on external interrupt 10
EIC->CTRL.reg |= EIC_CTRL_ENABLE; // Enable EIC peripheral
while (EIC->STATUS.bit.SYNCBUSY); // Wait for synchronization
EVSYS->USER.reg = EVSYS_USER_CHANNEL(1) | // Attach the event user (receiver) to channel 0 (n + 1)
EVSYS_USER_USER(EVSYS_ID_USER_TC4_EVU); // Set the event user (receiver) as timer TC4
EVSYS->CHANNEL.reg = EVSYS_CHANNEL_EDGSEL_NO_EVT_OUTPUT | // No event edge detection
EVSYS_CHANNEL_PATH_ASYNCHRONOUS | // Set event path as asynchronous
EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_10) | // Set event generator (sender) as external interrupt 10
EVSYS_CHANNEL_CHANNEL(0); // Attach the generator (sender) to channel 0
TC4->COUNT32.EVCTRL.reg = TC_EVCTRL_TCEI | // Enable the TC event input
//TC_EVCTRL_TCINV | // Invert the event input
TC_EVCTRL_EVACT_PPW; // Set up the timer for capture: CC0 period, CC1 pulsewidth
TC4->COUNT32.CTRLC.reg = TC_CTRLC_CPTEN1 | // Enable capture on CC1
TC_CTRLC_CPTEN0; // Enable capture on CC0
while (TC4->COUNT32.STATUS.bit.SYNCBUSY); // Wait for synchronization
NVIC_SetPriority(TC4_IRQn, 0); // Set the Nested Vector Interrupt Controller (NVIC) priority for TC4 to 0 (highest)
NVIC_EnableIRQ(TC4_IRQn); // Connect the TC4 timer to the Nested Vector Interrupt Controller (NVIC)
TC4->COUNT32.INTENSET.reg = TC_INTENSET_MC1 | // Enable compare channel 1 (CC1) interrupts
TC_INTENSET_MC0; // Enable compare channel 0 (CC0) interrupts
TC4->COUNT32.CTRLA.reg = //TC_CTRLA_PRESCSYNC_PRESC | // Overflow on precaler clock, (rather than the GCLK)
TC_CTRLA_PRESCALER_DIV1 | // Set prescaler to 1, 48MHz/1 = 48MHz
TC_CTRLA_MODE_COUNT32; // Set TC4/TC5 to 32-bit timer mode
TC4->COUNT32.CTRLA.bit.ENABLE = 1; // Enable TC4
while (TC4->COUNT32.STATUS.bit.SYNCBUSY); // Wait for synchronization
}
void loop() {
if (periodComplete) // Check if the period is complete
{
noInterrupts(); // Read the new period and pulse-width
period = isrPeriod;
pulsewidth = isrPulsewidth;
interrupts();
SerialUSB.print("PW: ");
SerialUSB.print(pulsewidth);
SerialUSB.print(F(" "));
SerialUSB.print("P: ");
SerialUSB.println(period);
periodComplete = false; // Start a new period
}
}
void TC4_Handler() // Interrupt Service Routine (ISR) for timer TC4
{
// Check for match counter 0 (MC0) interrupt
if (TC4->COUNT32.INTFLAG.bit.MC0)
{
TC4->COUNT32.READREQ.reg = TC_READREQ_RREQ | // Enable a read request
TC_READREQ_ADDR(0x18); // Offset address of the CC0 register
while (TC4->COUNT32.STATUS.bit.SYNCBUSY); // Wait for (read) synchronization
isrPeriod = TC4->COUNT32.CC[0].reg; // Copy the period
periodComplete = true; // Indicate that the period is complete
}
// Check for match counter 1 (MC1) interrupt
if (TC4->COUNT32.INTFLAG.bit.MC1)
{
TC4->COUNT32.READREQ.reg = TC_READREQ_RREQ | // Enable a read request
TC_READREQ_ADDR(0x1A); // Offset address of the CC1 register
while (TC4->COUNT32.STATUS.bit.SYNCBUSY); // Wait for (read) synchronization
isrPulsewidth = TC4->COUNT32.CC[1].reg; // Copy the pulse-width
}
}
- Можно ли сгенерировать точный тактовый импульс 15 кГц с помощью ардуино?
- Почему IDE не может найти плату, несмотря на то, что она четко видна и выбрана?
- Генерация сигнала частотой 38 кГц без таймеров
- Светодиод Arduino PWM с замиранием в сборке
- Отрегулируйте расчет времени после изменения частоты Timer0
- Высокочастотный PWM на Adafruit Feather M0
- Как измерить ультразвуковой датчик без импульсного метода?
- Teensy 4.1 / 4.0 Когда использовать контакты FlexPWM и QuadTimer для стробирования светодиодов