Como atualizar uma variável em um ISR usando Timers

8

Estou tentando verificar a frequência do Timer3 usando um contador. O valor do contador, declarado como volátil, é incrementado no ISR e a cada segundo a soma é mostrada no loop principal e o valor é zerado.

O temporizador foi configurado corretamente. (Se eu escolher um temporizador de 3 Hz, posso ver o led piscando)

O problema

O contador não é incrementado. Aqui está a saída:

Setup Completed
tick: 1
tick: 0
tick: 0
tick: 0

CÓDIGO

volatile int cont = 0;

void setup()
{
  Serial.begin(9600);

  pinMode(13, OUTPUT);

  // Initialize Timer
  cli();          // disable global interrupts
  TCCR3A = 0;     // set entire TCCR3A register to 0
  TCCR3B = 0;     // same for TCCR3B

  // set compare match register to desired timer count: 800 Hz
  OCR3B = 20; // 800Hz 5; // 3 Hz
  // turn on CTC mode:
  TCCR3B |= (1 << WGM12);
  // Set CS10 and CS12 bits for 1024 prescaler:
  TCCR3B |= (1 << CS30) | (1 << CS32);
  // enable timer compare interrupt:
  TIMSK3 |= (1 << OCIE3B);
  // enable global interrupts:
  sei();

  Serial.println("Setup completed");
}

void loop()
{
  if (millis() % 1000 == 0)
  {
    Serial.print(" tick: ");
    Serial.println(cont);
    cont = 0;
  }
}

ISR(TIMER3_COMPB_vect)
{
  //digitalWrite(13, digitalRead(13) ^ 1);
  cont++;
}

EDIT Este timer é usado para ler um valor anlog de um acelerômetro e armazená-lo em uma matriz de flutuação. Mas, no momento, estou preso a esse problema de atualização.

SOLUÇÃO 1 Graças à Gerben

volatile int cont = 0;

void setup()
{
  Serial.begin(9600);
  pinMode(13, OUTPUT);

  // Initialize Timer
  cli();          // disable global interrupts
  TCCR3A = 0;     // set entire TCCR3A register to 0
  TCCR3B = 0;     // same for TCCR3B

  // set compare match register to desired timer count: 800 Hz
  OCR3A = 20; // 20; //800Hz 5; // 3 Hz
  // turn on CTC mode:
  TCCR3B |= (1 << WGM32);
  // Set CS10 and CS12 bits for 1024 prescaler:
  TCCR3B |= (1 << CS30) | (1 << CS32);
  // enable timer compare interrupt:
  TIMSK3 |= (1 << OCIE3B);
  // enable global interrupts:
  sei();
  Serial.println("Setup completed");
}

void loop()
{
  delay(1000);
  Serial.println(cont);
  cont = 0;
}

ISR(TIMER3_COMPB_vect)
{
  cont++;
}

SOLUÇÃO 2 Graças ao BrettM

volatile int cont = 0;

void setup()
{
  Serial.begin(9600);
  pinMode(13, OUTPUT);

  // Initialize Timer
  cli();          // disable global interrupts
  TCCR3A = 0;     // set entire TCCR3A register to 0
  TCCR3B = 0;     // same for TCCR3B

  // set compare match register to desired timer count: 800 Hz
  OCR3B =  20; //800Hz 5; // 3 Hz
  // turn on CTC mode:
  //TCCR3B |= (1 << WGM32);
  // Set CS10 and CS12 bits for 1024 prescaler:
  TCCR3B |= (1 << CS30) | (1 << CS32);
  // enable timer compare interrupt:
  TIMSK3 |= (1 << OCIE3B);
  // enable global interrupts:
  sei();
  Serial.println("Setup completed");
}

void loop()
{
  Serial.println(cont); 
  cont = 0;
  delay(1000);

}

ISR(TIMER3_COMPB_vect)
{
  TCNT3 = 0;
  cont++;
}
UserK
fonte
E se você descomentar a digitalWritelinha, vê o LED piscar uma vez por segundo (a cada 0,66s)?
Ricardo
Sim, se eu descomentar digitalWritee definir OCR3B = 5;o led pisca aproximadamente nessa frequência.
UserK
Então é um mistério. Você já tentou comentar o cont = 0;interior do loop? O que acontece depois?
Ricardo
11
Tente aumentar a frequência. Eu acho que sua declaração if pode estar limpando o contador com mais frequência do que a interrupção é chamada, de alguma forma. Mas então você deve ver mais na saída. Além disso, deixe-o correr por mais tempo (digamos 1 minuto) e cole os resultados. Além disso, quando você atualizar a pergunta, deixe a saída antiga para que ela faça sentido (sem o histórico de edições).
Ricardo
11
Suspeito que a rotina de interrupção esteja sendo chamada apenas uma vez e depois seja desativada. Li em algum lugar que as interrupções são desativadas quando um código de interrupção está sendo executado e, em alguns casos, você precisa reativá-lo, mas não tenho certeza se esse é o caso. Esperemos que alguém mais entendido virá em nosso socorro ...
Ricardo

Respostas:

5

No modo CTC, o topo é OCR3A, não OCR3B!

Depois disso TIMSK3 |= (1 << OCIE3B);, também deve ser alterado para TIMSK3 |= (1 << OCIE3A);e ISR(TIMER3_COMPB_vect)paraISR(TIMER3_COMPA_vect)

Para 3Hz, OCR3Adeve ser 5208, não 20.

Tecnicamente TCCR3B |= (1 << WGM12);deve serTCCR3B |= (1 << WGM32);

Gerben
fonte
Com a sua configuração, o contador não é atualizado e a cada segundo é exibida a frase "Instalação concluída" (escrita na função setup ()!). Comportamento realmente estranho.
UserK
Resolvido usando TIMSK3 |= (1 << OCIE3B);. Obrigado Gerben! Modifique sua resposta e eu a aceitarei como solução.
UserK
11
Esqueci a menção de que você também precisa alterar o vetor ISR. ISR(TIMER3_COMPB_vect)deveria ser ISR(TIMER3_COMPA_vect). Se um ISR não estiver definido, o AVR será redefinido automaticamente, como você estava enfrentando. Feliz por você fazer isso funcionar.
Gerben
3

Parece que minha resposta a esta pergunta estava incompleta anteriormente, obrigado por apontar que o modo CTC só funciona com o OCR3A Gerben. Peço desculpas por não testar uma resposta antes de publicá-la.

Dadas as informações apenas nesta pergunta, a resposta de Gerben está completa, mas como sua outra pergunta implica que você não pode usar o OCR3A devido à biblioteca Servo, adicionarei um pouco. (Eu também editei essa resposta)

você pode emular o comportamento do modo CTC, definindo TCNT3 como 0 na sua rotina de interrupção. Lembre-se de remover a linha que ativa o modo CTC no seu código.

Testei seu código com este ISR:

ISR(TIMER3_COMPB_vect)
{
  TCNT3 = 0;
  cont++;
}

e essa configuração do timer registra

OCR3B = 5208; // 800Hz 5; // 3 Hz
// Set CS10 and CS12 bits for 1024 prescaler:
TCCR3B |= (1 << CS30) | (1 << CS32);
// enable timer compare interrupt:
TIMSK3 |= (1 << OCIE3B);

Isso pode ser um pouco menos preciso em altas frequências do que o CTC, não tenho certeza, mas a 3Hz funcionou perfeitamente. Observe que 5208 era o valor correto do OCR, não 20 (novamente graças à Gerben).

BrettAM
fonte
Eu tentei seu código, mas o contador não é incrementado. Eu adicionei o TCNT3=0;no ISR () e removi //TCCR3B |= (1 << WGM32);o setup () como você disse. Eu também tentei comentar a cont=0;linha, mas nada mudou.
UserK
11
Verifique se o código corresponde ao que foi postado na pergunta de qualquer outra maneira. Tente alterar seu loop para apenas println(cont); delay(1000);. Além disso, você ainda está incluindo os bits com cli () e TCCR3A etc, correto?
BrettAM
Ok obrigado. A 800 Hz ainda é preciso!
UserK