1

I'm trying to write a library for (rather slow) communication with visible light and have recently tried to include the TimerOne library as a timer. I want to send bits at 2kHz, so with a period of 500µs. I'm initializing the Timer1 with 500(µs) like this and then attach an interrupt to it to call my transmit function:

Timer1.initialize(500);
Timer1.attachInterrupt(transmitBit);

However, when I try to measure the period with a logic analyzer, it tells me that there is actually a period of 2,000µs. Could this be due to some overhead created by the library? Where else could the problem be?

If you need any other information just let me know and I'll try to provide it.

Thanks in advance for your feedback.

EDIT: I've just tried adding the period as a parameter to the attachInterrupt like so:

Timer1.attachInterrupt(transmitBit, 500);

And now I'm measuring a period of 30µs for whatever reason? Can anyone explain me what's going on?

EDIT: This is the rest of my (relevant) code:

Transmitter::Transmitter()
{
  [...]
  Timer1.initialize(500);
}

Transmitter* Transmitter::_instance;

void Transmitter::start() {
  _instance = this;
  Timer1.attachInterrupt(_transmitBit, 500);
  _active = true;
}

static void Transmitter::_transmitBit() {
  _instance->transmitBit();
}

void Transmitter::transmitBit() {
  if(_active) {
    //Preperation
    if(!_manHalf) { //only change the state when the manchester bit is completely transmitted.
      if(_busy) {
        //Serial.println("Transmitting!");
        /** if there is currently a transmission running
          * read the next bit from the frame
          */
        _state = *(_frame + _pos);
        if(_pos >= _frameSize) {
          _busy = false;
          _state = 0;
          _pos = 0;
        }
        _pos++;
      } else { //else transmit the idle pattern
        _state = 0;
      }
    }
    //Transmission
    if (!_manHalf) {
      digitalWrite(_pin, !_state);
    } else {
      digitalWrite(_pin, _state);
    }
    _manHalf = !_manHalf;
  } else {
    digitalWrite(_pin, HIGH);
  }
}
  • please post all the code – jsotola Jun 2 '18 at 16:03
  • That's going to be a lot but alright – Lithimlin Jun 2 '18 at 16:04
  • then post minimal code that demonstrates the problem. – jsotola Jun 2 '18 at 16:06
  • Yes, that's what I did – Lithimlin Jun 2 '18 at 16:09
  • @jsotola got any idea as to why it wouldn't work? – Lithimlin Jun 2 '18 at 16:43
1

Before using libraries, try to see whether you can manipulate the timer registers directly to achieve what you want. You may find it is easier!

In the following example I assume you are using an ATmega328 chip. The code may be slightly different for other microcontrollers. In that case you can read the data sheets for both microcontrollers and work out what you need to change.

I also assume the data you want to transmit is stored in a byte array. Initialize the data how you prefer.

e.g: byte my_data[10] = {0,0,0,0,0,0,0,0,0,0};

Next, you want to initialize timer1 in the main loop

TCCR1A=0;
TCCR1B=0;

To send data at 2000 bits per second, the following settings can be used

ICR1=124; // Timer counter resets to 0 after reaching 124
OCR1A=125;
OCR1B=125; // other counter match registers must be higher than ICR1
TCCR1A=B00000010; // waveform generation mode 14, fast PWM, ICR1=top
TCCR1B=B00011011; // clock scaler 64, 1 count = 4 us.  125*4 us = 500 us
TIFR1 |= (_BV(ICF1) | _BV(OCF1A) | _BV(OCF1B)); // Clear match flags

This will set the timer1 match register bit ICF1 every 500 us. There are several ways of using the timer to send the data. My preference would be to poll the match flag. The timing precision is about as good as you can get - a couple of clock cycles:

loop_until_bit_is_set(TIFR1,ICF1); // wait until TCNT1=ICR1
TIFR1 |= (_BV(ICF1) | _BV(OCF1A) | _BV(OCF1B)); // clear match flags
// switch digital output high or low depending on data bit to be sent

One way is to run a state machine loop like this:

unsigned int state;

void setup(){
state=0;
}

void loop(){
switch (state){
case 0: // idle or receiving data to be sent
TCCR1A=0;
TCCR1B=0;
// insert other code here
// conditional function, when message data received, set state=1;
break;

case 1: // setup timer
ICR1=124; // Timer counter resets to 0 after reaching 124
OCR1A=125;
OCR1B=125; // other counter match registers must be higher than ICR1
TCCR1A=B00000010; // waveform generation mode 14, fast PWM, ICR1=top
TCCR1B=B00011011; // clock scaler 64, 1 count = 4 us.  125*4 us = 500 us
TIFR1 |= (_BV(ICF1) | _BV(OCF1A) | _BV(OCF1B)); // Clear match flags
state=2;
break;

case 2:
loop_until_bit_is_set(TIFR1,ICF1); // wait until TCNT1==ICR1
TIFR1 |= (_BV(ICF1) | _BV(OCF1A) | _BV(OCF1B)); // clear match flags
// switch digital output high or low depending on data bit to be sent

// add conditional function so that when you are finished sending data, reset to state=0;
break;

}
}

As always, please upvote if you find this helpful.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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