#timer #arduino #hardware #interrupt
#таймер #arduino #аппаратное обеспечение #прерывание
Вопрос:
Я пытаюсь использовать микроконтроллер SAML21G18B и дистрибутив Mattairtech Arduino, мне нужно настроить TC0 для генерации прерываний. К сожалению, ни одна из существующих библиотек таймеров SAMD не работает с этим MCU / дистрибутивом, поэтому я пытался настроить и использовать TC0 с нуля…
Я взял код analogWrite () из ядра Mattairtech и взломал его, я надеюсь, это то, что настраивает TC0 для обычного PWM — так же, как когда он используется для analogWrite () .
Он компилируется (УРА!), Но я попытался прочитать данные из регистра count, чтобы проверить, идет ли подсчет, и ничего не получил. Я также понятия не имею, как настроить его для генерации прерывания при совпадении с регистром CC0.
Весь мой код приведен ниже, буду признателен за любую помощь, указания, советы по обеспечению работы прерываний.
void TC0_Handler() {
//interrupt handler ...
}
static void SYNC_TC(Tc* TCx){
while(TCx->COUNT16.SYNCBUSY.reg amp; (TC_SYNCBUSY_SWRST | TC_SYNCBUSY_ENABLE | TC_SYNCBUSY_CTRLB | TC_SYNCBUSY_STATUS | TC_SYNCBUSY_COUNT));
}
//initialise TCO
void initTc0(bool sixteenBit, uint16_t value){
uint8_t timerCh = 0;
Tc* TCx = (Tc*)TC0;
//GCLK setup
GCLK->PCHCTRL[GCM_TC0_TC1].reg = (GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN_GCLK0);
while ( (GCLK->PCHCTRL[GCM_TC0_TC1].reg amp; GCLK_PCHCTRL_CHEN) == 0 ); // wait for sync
//Disable TC
TCx->COUNT16.CTRLA.bit.ENABLE = 0;
SYNC_TC(TCx);
// Set Timer counter Mode to 16 bits, normal PWM
if(sixteenBit) TCx->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16;
else{
// Set Timer counter Mode to 8 bits, normal PWM
TCx->COUNT8.CTRLA.reg |= TC_CTRLA_MODE_COUNT8;
SYNC_TC(TCx);
// Set PER to maximum counter value
TCx->COUNT8.PER.reg = 0xFF;
}
SYNC_TC(TCx);
// Set TCx as normal PWM
TCx->COUNT16.WAVE.reg = TC_WAVE_WAVEGEN_NPWM;
SYNC_TC(TCx);
// Set the initial value for CC
if(sixteenBit) TCx->COUNT16.CC[timerCh].reg = (uint16_t) value;
else TCx->COUNT8.CC[timerCh].reg = (uint8_t) value;
SYNC_TC(TCx);
// Enable TCx
TCx->COUNT16.CTRLA.bit.ENABLE = 1;
SYNC_TC(TCx);
}
//Set the CCBuf register
void setTc(bool sixteenBit, uint16_t value){
uint8_t timerCh = 0;
Tc* TCx = (Tc*)TC0;
if(sixteenBit) TCx->COUNT16.CCBUF[timerCh].reg = (uint16_t)value;
else TCx->COUNT8.CCBUF[timerCh].reg = (uint8_t)value;
SYNC_TC(TCx);
}
uint16_t getCount(){
Tc* TCx = (Tc*)TC0;
TCx->COUNT16.CTRLBSET.bit.CMD = TC_CTRLBSET_CMD_READSYNC;
return TCx->COUNT16.COUNT.reg;
}
void setup() {
// put your setup code here, to run once:
initTc0(1, 0);
setTc(1, 500);
Serial.begin(1000000);
}
void loop() {
// put your main code here, to run repeatedly:
Serial.println(getCount());
delay(100);
}
Комментарии:
1. В данный момент у меня нет времени просматривать ваш код, но у меня есть рабочий код для TC4 на SAMD21, используемый для отправки байтов в ЦАП с прерыванием, которое должно быть устрашающе похожим, возможно, это может вам помочь. Это здесь: github.com/ocrdu/Arduino_SAMD21_Audio_Player/blob/master/src /…
2. Спасибо — я использовал целые числа таймера на D21, но L21 достаточно отличается, чтобы вызвать у меня головную боль, пытаясь понять, какие регистры установить. Теперь у меня есть рабочий код — я разорвал код для tone () на части, потому что он использует TC1 для генерации прерываний для включения и выключения контактов. Мне удалось заставить его работать для TC0, так что теперь я пойду и отвечу на свой собственный вопрос …!
Ответ №1:
Хорошо, я ответил на свой собственный вопрос, взломав функцию tone(). Он использует TC1 для генерации периодических прерываний, которые включают и выключают контакт для генерации звукового сигнала. Следующий код позволяет настроить таймер с помощью прескалятора часов и значения для CC — таймер подсчитывает, и когда он совпадает с CC, вызывается прерывание, и все начинается сначала.
Это работает для TC0 и легко редактируется для TC1
#include "variant.h"
#include "sam.h"
//***************Use this for TC0
#define TCTimer TC0
#define timerIRQ TC0_IRQn
//Timer handler function if using TC0 - put whatever ISR code you want in here.
void TC0_Handler (void){
//clear the interrupt flag
TCTimer->COUNT16.INTFLAG.bit.MC0 = 1;
isrCounts ;
}
//***************Use this for TC1
/*
#define TCTimer TC1
#define timerIRQ TC1_IRQn
//Timer handler function if using TC1 - put whatever ISR code you want in here.
void TC1_Handler (void){
//clear the interrupt flag
TCTimer->COUNT16.INTFLAG.bit.MC0 = 1;
isrCounts ;
}*/
//Wait for registers to synchronise
#define WAIT_TC16_REGS_SYNC(x) while(x->COUNT16.SYNCBUSY.reg);
#define TONE_TC_TOP 0xFFFF
#define TONE_TC_CHANNEL 0
//Reset the timer hardware
static inline void resetTC (Tc* TCx){
// Disable TCx
TCx->COUNT16.CTRLA.reg amp;= ~TC_CTRLA_ENABLE;
WAIT_TC16_REGS_SYNC(TCx)
// Reset TCx
TCx->COUNT16.CTRLA.reg = TC_CTRLA_SWRST;
WAIT_TC16_REGS_SYNC(TCx)
while (TCx->COUNT16.CTRLA.bit.SWRST);
}
//setup the timer with a prescaler clock value, and the CC value.
//The timer incriments until it hits CC, then fires an interrupt and starts over
//With prescaler at 1 and CC at 65535 it interrupts 733 times per second
//Reduce CC to get a faster interrupt rate
//Prescaler must only be 1,2,4,8,16,64,25 or 1024
void setupTCTimer(uint32_t prescaler, uint32_t ccVal ){
NVIC_DisableIRQ(timerIRQ);
NVIC_ClearPendingIRQ(timerIRQ);
NVIC_SetPriority(timerIRQ, 0);
// Enable GCLK for timer used
GCLK->PCHCTRL[GCM_TC0_TC1].reg = (GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN_GCLK0);
while ( (GCLK->PCHCTRL[GCM_TC0_TC1].reg amp; GCLK_PCHCTRL_CHEN) == 0 ); // wait for sync
uint32_t prescalerConfigBits;
uint32_t ccValue;
ccValue = ccVal;//toneMaxFrequency / frequency - 1;
prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1;
switch(prescaler){
case 0: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1; break;
case 1: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1; break;
case 2: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV2; break;
case 4: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV4; break;
case 8: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV8; break;
case 16: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV16; break;
case 64: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV64; break;
case 256: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV256; break;
case 1024: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1024; break;
default: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1; break;
}
resetTC(TCTimer);
uint16_t tmpReg = 0;
tmpReg |= TC_CTRLA_MODE_COUNT16; // Set Timer counter Mode to 16 bits
tmpReg |= prescalerConfigBits;
TCTimer->COUNT16.CTRLA.reg |= tmpReg;
WAIT_TC16_REGS_SYNC(TCTimer)
TCTimer->COUNT16.WAVE.reg = TC_WAVE_WAVEGEN_MFRQ;
WAIT_TC16_REGS_SYNC(TCTimer)
TCTimer->COUNT16.CC[TONE_TC_CHANNEL].reg = (uint16_t) ccValue;
WAIT_TC16_REGS_SYNC(TCTimer)
// Enable the TCTimer interrupt request
TCTimer->COUNT16.INTENSET.bit.MC0 = 1;
}
//Start the timer
//you need to call setupTCTimer with prescaler and CC values before you start the timer
void startTCTimer(){
// Enable TCTimer
TCTimer->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;
WAIT_TC16_REGS_SYNC(TCTimer)
NVIC_EnableIRQ(timerIRQ);
timerActive = true;
}
//stop the timer
//This resets it so you need to call setupTCTimer with prescaler and CC values before restarting
void stopTCTimer(){
resetTC(TCTimer);
timerActive = false;
}