0

So I want to use this library, that uses timer1 to fetch samples from analog input on pin A0. It works great, and so after proper detection I'd like to perform all kinds of different tasks. But for those I need timer1 again and I think this library keeps it's timer1 routine continuously running. (Am I right?)

(EDIT: clarification) I think therefore a different library using timer1 won't work as expected. That's why after the first library's task is done, I'd like to free up timer1 for the second library.)

/*
  Original text created by Jacob Rosenthal:

  The Goertzel algorithm is long standing so see
  http://en.wikipedia.org/wiki/Goertzel_algorithm for a full description.
  It is often used in DTMF tone detection as an alternative to the Fast
  Fourier Transform because it is quick with low overheard because it
  is only searching for a single frequency rather than showing the
  occurrence of all frequencies.

  This work is entirely based on the Kevin Banks code found at
  http://www.embedded.com/design/configurable-systems/4024443/The-Goertzel-Algorithm
  so full credit to him for his generic implementation and breakdown. I've
  simply massaged it into an Arduino library. I recommend reading his article
  for a full description of whats going on behind the scenes.

  Created by Jacob Rosenthal, June 20, 2012.
  Released into the public domain.

  Modifications 6v6gt 09.09.2019
  1. Restructure instance variables to permit multiple instances of class.
  2. Make sample array static to share it between instances
  3. Pass in sample array as pointer so it can be reused outside class.
  4. Drive ADC by timer1 instead of polling ADC in loop()
      and reduce resolution to 8 bits.
  5. Separate coeeficient calculation from constructor because it
       now relies on data unknown at invocation (sample rate)
  5. Some consolidation of methods.
  6. Software "as is". No claims made about its suitability for any use.
       Use at your own risk. Special care required if you use other
       analog inputs or an AVR chip other than ATmega328P (Uno etc.).
*/


#include "Arduino.h"
#include "Goertzel.h"

// set by Goertzel::init()
uint8_t * Goertzel::testData;             // static declaration in .h
uint16_t Goertzel::N ;                    // ditto
uint16_t Goertzel::SAMPLING_FREQUENCY ;   // ditto

volatile bool Goertzel::testDataReady = false ; // static declaration in .h


//ADC interrupt service routine
ISR(ADC_vect) {
  // load sample buffer on sample conversion.
  static uint16_t sampleIndex = 0 ;
  if ( ! Goertzel::testDataReady   ) {
    if ( sampleIndex < Goertzel::N  ) {
      *(Goertzel::testData + sampleIndex++ )  = ADCH ; // 8 bits. Direct adressing into byte buffer
    }
    else  {
      Goertzel::testDataReady = true ;    // make buffer available to consumer
      sampleIndex = 0 ;
    }
  }
  TIFR1 = _BV(ICF1); // reset interrupt flag
}


// constructor
Goertzel::Goertzel(float TARGET_FREQUENCY  )
{
  _TARGET_FREQUENCY = TARGET_FREQUENCY; //should be integer of SAMPLING_RATE/N
}


//static method
void Goertzel::init( uint8_t *sampleArray , uint16_t sampleArraySize, uint16_t sampleFrequency ) {
  // set up sample array, number of samples and sample frequency.

  // load class static variables
  testData = sampleArray ;
  N = sampleArraySize ;
  SAMPLING_FREQUENCY = sampleFrequency ;


  // initialise ADC and Timer1. Timer1 triggers ADC at frequency SAMPLING_FREQUENCY.
  // ISR(ADC_vect) called when conversion complete.
  cli() ;

  // Setup Timer1 for chosen sampling frequency.
  TCCR1A = 0;
  TCCR1B = _BV(CS10)  |    // Bit 2:0 – CS12:0: Clock Select =  no prescaler
           _BV(WGM13) |    // WGM 12 = CTC ICR1 Immediate MAX
           _BV(WGM12);     // WGM 12 ditto
  ICR1 = ( (F_CPU ) / SAMPLING_FREQUENCY ) - 1;


  // Setup ADC for triggering by timer1; 8bit resolution; Analog Port PC0 (pin A0) ADMUX
  ADMUX =  _BV(REFS0) ;    // Fixed AVcc reference voltage for ATMega328P
  ADMUX |= _BV(ADLAR) ;    // left adjust conversion result in ADCH (8bit)
  DIDR0 |= _BV(ADC0D);     // DIDR0  Digital Input Disable Register 0
  ADCSRB = _BV(ADTS2) |    // Bit 2:0  ADTS[2:0]: ADC Auto Trigger Source
           _BV(ADTS1) |    // Timer/Counter1 Capture Event
           _BV(ADTS0);     // ditto


  ADCSRA = _BV(ADEN) |      // Bit 7   ADEN: ADC Enable
           _BV(ADSC) |      // Bit 6   ADSC: ADC Start Conversion
           _BV(ADATE) |     // Bit 5   ADATE: ADC Auto Trigger Enable
           _BV(ADIE) |      //
           _BV(ADPS0) |     // Bits 2:0  ADPS[2:0]: ADC Prescaler Select Bits  (div 8 )
           _BV(ADPS1);      // ditto
  sei() ;

}



// instance method
void Goertzel::getCoefficient( void ) {
  // previously in constructor. Now SAMPLING_FREQUENCY unknown at invocation time.
  float omega = (2.0 * PI * _TARGET_FREQUENCY) / SAMPLING_FREQUENCY;
  coeff = 2.0 * cos(omega);
}



// instance method
float Goertzel::detect()
{
  Q2 = 0;
  Q1 = 0;
  for ( uint16_t index = 0; index < N; index++)
  {
    // byte sample is ( *( testData + index ) );
    float Q0;
    Q0 = coeff * Q1 - Q2 + (float) ( *( testData + index ) - 128 ) ; //  128 for 8bit; 512 for 10bit resolution.
    Q2 = Q1;
    Q1 = Q0;
  }

  /* standard Goertzel processing. */
  float magnitude = sqrt(Q1 * Q1 + Q2 * Q2 - coeff * Q1 * Q2);
  return magnitude  ;
}

I'm new to using timers on Arduino, but I want to understand and learn more about it. So I'm reading up on it. Seeing that it's not the easiest subject to wrap my head around I'd like to ask for some directions.

On this splendid page I gathered some examples of stopping / disabling timer1.

  TCCR1B = 0;    
  TIMSK1 = 0;    // disable Timer1 Interrupt

Nick Gammons Timer info page

However I don't completely recognise the usage of the library's timer names, registers, variables yet. And so I'm unsure what part of the code is doing what exactly, ie where, how and when the timer starts. And therefore I'm equally unsure how to go about stopping it when the task is done, and to start it again when a new detection is needed. It seems there is TCCR1A, TCCR1B, cli() and sei() that have something to do with it and ISR(ADC_vect). One of you probably knows ;)

EDIT: Reading the suggested ATmega328p datasheet I found out a little bit more about the following abbreviations:

cli // disable interrupts during timed sequence __disable_interrupt();
sei // Set global Interrupt Enable __enable_interrupt();
TCNT1 // Timer/Counter (setting 0 will reset)
TIFR1 // Timer Interrupt Flag Register
TCCR1A // Timer/Counter Control Register 1a (setting 0 will reset)
TCCR1B // Timer/Counter Control Register 1b (setting 0 will reset)
TIMSK1 // Timer Interrupt Mask Register
ICR1 // Input Capture Register
ICF1 // Input Capture Flag
ICP1 // Input Capture Pin
WGM // Waveform Generation Mode
CS // Clock Select Bits (prescaler??)
F_CPU // The CPU speed (What is the default value? 8000000??? 1000000??? 16000000???)
DIDR1/DIDR0 // Digital Input Disable Registers (if left enabled they will use excessive power when Analog input is floating or close to VCC/2)
ADMUX // ADC Multiplexer Selection Register
REFS1 & REFS0 // Voltage Reference Selection
ADLAR // ADC Left Adjust Result
MUX
ADEN // ADC Enable
ADSC // ADC Start Conversion
ADATE // ADC Auto Trigger Enable
ADTS // ADC Trigger Source Select 
ADCSRB // 
ADIE // ADC Conversion Complete Interrupt Enable
ADPS0 & ADPS0 // ADC Prescaler Select Bits

But hasn't really lightened things up entirely for me. What is _BV? for example.

EDIT: I think I'm starting to make sense of it more. Am I right here(??):

ADMUX = (0b00 << REFS0) | (1 << ADLAR) | (0b00000 << MUX0);
Is the same as:
ADMUX = (0b00 << REFS0); // 0 shift has no effect so can be omitted
ADMUX |= (1 << ADLAR); 
ADMUX |= (0b00000 << MUX0); // 0 shift has no effect so can be omitted
Is de same as:
ADMUX = (1 << REFS0); // can be omitted
ADMUX |= (1 << ADLAR);
ADMUX |= (1 << MUX0); // can be omitted
is the same as:
ADMUX = _BV(REFS0); // can be omitted
ADMUX |= _BV(ADLAR);
ADMUX |= _BV(MUX0); // can be omitted
Is the same as:
ADMUX = (1 << ADLAR); 
Is the same as: 
ADMUX = _BV(ADLAR);

Understanding it all would be great, but may be unnecessary. If only I'd know how I can stop and free Timer1 usage for other purposes. Starting it all up again would be easier I guess ;)

7
  • 2
    Did you try looking at the datasheet of the Atmega328p (which is the microcontroller on the Uno/Nano)? The register names (like TCCR1A) are from there. It also describes the function of each bit in each register.
    – chrisl
    Sep 25, 2022 at 18:34
  • Doing that now.
    – Gaai
    Sep 26, 2022 at 9:22
  • Re “What is _BV?”: As stated in the documentation:, #define _BV(bit) (1 << (bit)) Sep 26, 2022 at 11:31
  • What is the difference between: TIFR1 = _BV(ICF1); And TIFR1 |= bit (OCF1A); In code comments they both are said to clear the Input Capture Flag or the Interrupt Flag register...
    – Gaai
    Sep 26, 2022 at 11:42
  • Re _BV() vs bit(): _BV() comes from the avr-libc, whereas bit() is from the Arduino core. Otherwise they are basically the same, except that bit() returns an unsigned long. Sep 27, 2022 at 11:16

3 Answers 3

2

I'm not sure why you need to stop the timer in the first place. Timers are just things that count "ticks" which can be programmed to arrive at various speeds (usually some division of the processor speed).

Imagine you have a clock in your kitchen. And imagine you are baking a cake. You observe the time that you put the cake into the oven. Then you note at what time the cake will be done. Meanwhile that same clock can be used to time boiling an egg. You don't need to stop or "reset" the clock to achieve this.

Just let the clock run and do its thing, and meanwhile make a note of what time needs to arrive for things to be "done".

Meanwhile, the Uno (and Nano) have other timers. You can use those. It's like owning three clocks, that can run at different speeds — or perhaps more accurately — different levels of precision.

2
  • That's because I'd like to use another (more complex) library not written by me, that uses timer1. And it seems their usage collide. Because they both want to use their favourite kitchen clock I guess ;) And I'm not yet the coding diplomat needed to convince them otherwise.
    – Gaai
    Sep 26, 2022 at 11:31
  • in short: I didn't want to stop the timer (as in clock) but rather the task (interrupt / isr) associated with it. I could've worded my question better perhaps, but I couldn't before because I didn't know the right words. It seems people use 'setup a timer' when they actually mean: 'setup an ISR interrupt routine' or whatever.
    – Gaai
    Oct 17, 2022 at 10:16
2

I am answering here only a tiny part of the question:

ADMUX = (0b00 << REFS0); // 0 shift has no effect so can be omitted
ADMUX |= (1 << ADLAR); 
ADMUX |= (0b00000 << MUX0); // 0 shift has no effect so can be omitted

Is de same as:

ADMUX = (1 << REFS0); // can be omitted
ADMUX |= (1 << ADLAR);
ADMUX |= (1 << MUX0); // can be omitted

No, it is not the same.

Starting from the first line: (0b00 << REFS0) means “zero left-shifted by REFS0 (i.e. six) bits”. It is not a zero shift, as stated in the comment: it is a 6-bit shift. The resulting value is zero, because the number being shifted is zero to start with, and because vacated bits are zero-filled during a left-shift.

In contrast, (1 << REFS0) means “one left-shifted by REFS0 bits”. The resulting value is:

2REFS0 = 26 = 0b01000000 = 64

In the first code snippet, ADMUX ends up with only the ADLAR bit being set. In the second snippet, all three bits (REFS0, ADLAR and MUX0) are set.

Edit: explaining the notion that “vacated bits are zero-filled during a left-shift”. Let's assume x = 0bABCDEFGH is a byte, where A…H are the individual bits (“0b” is a prefix meaning “what follows is in binary”). Left-shifting this byte gives the following:

x << 0 = 0bABCDEFGH
x << 1 = 0bBCDEFGH0
x << 2 = 0bCDEFGH00
x << 3 = 0bDEFGH000
...

By “vacated bits are zero-filled” I mean that the bits that are beyond the last original bit (beyond H) are replaced by zeros. Note that a zero shift “has no effect” in the sense that x<<0 is the same as x. Note also that shifting the value zero by any amount always yields zero.

In this particular code, the value being written to ADMUX can be computed as follows:

1 << REFS0 = 1 << 6 = 0b00000001 << 6 = 0b01000000
1 << ADLAR = 1 << 5 = 0b00000001 << 5 = 0b00100000
1 << MUX0  = 1 << 0 = 0b00000001 << 0 = 0b00000001
──────────────────────────────────────────────────
(1<<REFS0) | (1<<ADLAR) | (1<<MUX0)   = 0b01100001
8
  • Thanks for taking the time to look at this. I have some more reading to do I suppose. And see if I can find back the source of that comment.
    – Gaai
    Sep 27, 2022 at 12:22
  • could you elaborate on this: "vacated bits are zero-filled during a left-shift". I'm confused because I thought a shift operation only affects the shifted bit, but now I'm reading the whole row of bits might be shifted left (or right). Then there's also the difference between the logical OR or AND. In case of a zero shift with an OR, does it have an effect at all? And a 1 shift with an OR? Does it have an effect on vacated bits?
    – Gaai
    Sep 28, 2022 at 10:47
  • @Gaai: What do you mean by “zero shift”? Shifting some number by zero bits or shifting the number zero by some number of bits? What do you mean by “have an effect”? Every expression has the effect or returning some value. Re OR vs AND: please look up the truth tables of these operators yourself. For the rest, see amended answer. Sep 28, 2022 at 11:30
  • With having an effect I meant: actually setting/changing/writing to the bit. Instead of just reading, which is an effect aswell, as you noted, of course.
    – Gaai
    Sep 28, 2022 at 12:10
  • I found this line of code with the comment on a forum. I didn't use it right. But I was struggling to understand why I didn't see a bit setting for A0 as the input pin. While when other pins where used (elsewhere by people) they set these with (MUX0, MUX1, MUX2 or other ways even as it seems there are always more ways to do the same in bit settings). Anyway, I tought this might be because A0 was set by some zero setting elsewhere (called a zero shift), which meant it could be left out... I know better now how this is done, thanks to you and some further reading. It's a confusing journey ;)
    – Gaai
    Sep 28, 2022 at 12:18
0

Time hasn't been on my side lately. But here is the code I've come up with so far. Needs testing still to see if it solves all my problems but in regards to my original question this seems to hold the answer I was looking for. Later on I might add some more info/links to register settings, hex/binary/ascii tables, bitwise operations and what they all do. But the manual is your friend pretty much.

Alright then. First of all this print function comes in handy:

// Handy while experimenting with all different bitwise operations and settings
void printRegSettings() {
  const String regNames[8] = {"TCCR1A", "TCCR1B", "ICR1", "ADMUX", "DIDR0", "ADCSRA", "ADCSRB", "TIFR1"}; //other: "TCNT1", "OCR1A", "OCR1B", "TIMSK1"
  uint8_t regVars[8] = {TCCR1A, TCCR1B, ICR1, ADMUX, DIDR0, ADCSRA, ADCSRB, TIFR1}; //other: TCNT1, OCR1A, OCR1B, TIMSK1

    for (uint8_t index = 0; index < sizeof(regVars)/sizeof(regVars[0]); index++) {
      Serial.print(regNames[index]+" = 0b");
      for (uint8_t i = 8; i > 0; i--) {
        Serial.print(bitRead(regVars[index], i-1));
      }
    Serial.write('\n');
    }
}

Next put all library register settings in function, notice the boolean in the end, for emptying the ISR (later described):

void dtmfTimerSetup () {

  TCCR1A = 0;
  TCCR1B = _BV(CS10) | // Bit 2:0 – CS12:0: Clock Select = no prescaler
           _BV(WGM13) | // WGM 12 = CTC ICR1 Immediate MAX
           _BV(WGM12); // WGM 12 ditto
  ICR1 = ( (F_CPU ) / 9600 ) - 1;

  ADMUX = _BV(REFS0) ; // Fixed AVcc reference voltage for ATMega328P
  ADMUX |= _BV(ADLAR) ; // left adjust conversion result in ADCH (8bit)
  DIDR0 |= _BV(ADC0D); // DIDR0 Digital Input Disable Register 0
  ADCSRB = _BV(ADTS2) | // Bit 2:0 ADTS[2:0]: ADC Auto Trigger Source
           _BV(ADTS1) | // Timer/Counter1 Capture Event
           _BV(ADTS0); // ditto
  ADCSRA = _BV(ADEN) | // Bit 7 ADEN: ADC Enable
           _BV(ADSC) | // Bit 6 ADSC: ADC Start Conversion
           _BV(ADATE) | // Bit 5 ADATE: ADC Auto Trigger Enable
           //_BV(ADIE) | //
           _BV(ADPS0) | // Bits 2:0 ADPS[2:0]: ADC Prescaler Select Bits (div 8 )
           _BV(ADPS1); // ditto

  Goertzel::dtmfListenerISR = true;
}

Next add a function to clear all these settings. Initial values of registers is 0 as far as I checked. So setting them zero does the job. Again notice the boolean false this time.

void clearTimerSettings() {
  //Empty Goertzel dtmf interrupt ISR routine
  Goertzel::dtmfListenerISR = false;
  
  //reset all altered registers to initial value 0
  Serial.println(F("***CLEARING ALL BIT SETTINGS***"));
  TCCR1A = TCCR1B = ICR1 = ADMUX = DIDR0 = ADCSRA = ADCSRB = TIFR1 = 0;

// pauze but don't erase current interrupt settings:
// bitClear(ADCSRA, ADIE); //risk interference when different timer adc library sets bit again
}

In Goertzel.h Add this static bool declaration to class Goertzel public:

static volatile bool Goertzel::dtmfListenerISR;

Add boolean declaration to Goertzel.cpp and add 'if statement' inside ISR(ADC_vect) to only execute interrupt if dtmfListenerISR is true.

volatile bool Goertzel::dtmfListenerISR; 

//ADC interrupt service routine
 
ISR(ADC_vect) {
  //if dtmfListenerISR is false, this code is skipped, freeing timer1 for other purposes
  if (Goertzel::dtmfListenerISR) {
  
    // load sample buffer on sample conversion.
    static uint16_t sampleIndex = 0 ;
    if ( ! Goertzel::testDataReady  ) {
      if ( sampleIndex < Goertzel::N  ) {
        *(Goertzel::testData + sampleIndex++ )  = ADCH ; // 8 bits. Direct adressing into byte buffer
      }
      else  {
        Goertzel::testDataReady = true ;    // make buffer available to consumer
        sampleIndex = 0 ;
      }
    }
    TIFR1 = _BV(ICF1); // reset interrupt flag
  }
}

Finally use these functions like this in your sketch. The setup can be done in setup but the clearing should take place in your void loop of course, once the library and timer interrupt have finished their job and you want another library / timer routine to take over. Just for testing I put them all inside a setup and a simple sketch now.

void setup() {
  Serial.begin(115200);
  //setup/initialize timer interrupt routine:
  dtmfTimerSetup();
  //FOR TESTING: check and print current settings:
  printRegSettings();
  //clear timer interrupt routine;
  clearTimerSettings();
  printRegSettings(); //only for testing purposes
  
}

void loop() {
  // put your main code here, to run repeatedly:
}

Ofcourse for the other libraries using timer1 you should do the same: setup functions with register settings / cleanup and declare/set boolean for ISR part. There might be some overkill here, but so far I didn't find a more elegant way of achieving all this.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.