Использование TC0 на SAML21G18B с дистрибутивом Arduino Mattairtech

#timer #arduino #hardware #interrupt

#таймер #arduino #аппаратное обеспечение #прерывание

Вопрос:

Я пытаюсь использовать микроконтроллер SAML21G18B и дистрибутив Mattairtech Arduino, мне нужно настроить TC0 для генерации прерываний. К сожалению, ни одна из существующих библиотек таймеров SAMD не работает с этим MCU / дистрибутивом, поэтому я пытался настроить и использовать TC0 с нуля…

Mattairtech core на Github

Я взял код 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;
}