Generating squarewave output with variable wavelength

Hello,

I´m pretty new to using timers but I have do generate an Digital Squarewave Outputsignal.
In this Signal a zero is represented by 10µs high an 10µs low and a one is represented by 50µs high and 50µs low. So the attached picture would be the signal of “010”
I`m able to generate both frequencys by using the fast-pwm mode. But I´m struggeling, when ist comes to changing the frequency. In this Code is was trying to generate a Signal with alternating frequencies:

boolean fast = 1;
void setup() {

pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
TCCR1A = 0;
TCCR1B = 0;
TCCR1A |= (1<<WGM11) | (1<<COM1A1) | (1<<COM1B1);
TCCR1B |= (1<<CS11) | (1<<WGM12) | (1<<WGM13);
//ICR1 = 99; //20kHz
//OCR1A = 49;//20kHz
ICR1 = 19; //100KHz
OCR1A = 9; //100kHz
}

void loop() {
if(digitalRead(10) == LOW){
if(fast){
ICR1 = 99; //20KHz
OCR1A = 49;
}else{
ICR1 = 19; //100KHz
OCR1A = 9;
}
fast = !fast;
}

}

But as you can guess it it is not working at all. Later on I want to read the Signal, wich is to be generated from an Array.
If your could help me with this problem I would be very thankful.

Timers are good for repetetive pulses. They can make the same size pulse over and over with no interaction with your main sketch.

To change the pulse width on every pulse takes a lot of work. I would not use the hardware timer for this. Use the approach taken by SoftwareSerial when it transmits: turn off interrupts and bit-bang the timing directly in your code.

Hmm ok, I dont know how to do that. I guess I have to learn a lot more right now. :smiley:

I thought maybe I could use the CTC Mode and have two different interrupts and depending on the current Bit I will output the Fast Signal or the slow signal.

But I’m facing so many problems with this Idea right now

Vykynger:
Hmm ok, I dont know how to do that. I guess I have to learn a lot more right now. :smiley:

I thought maybe I could use the CTC Mode and have two different interrupts and depending on the current Bit I will output the Fast Signal or the slow signal.

But I'm facing so many problems with this Idea right now

If you don't use the method Morgan suggested, you will create "glitches" in your pulse train every time you change from 0 to 1 and back to 0.

Paul

It can be done with the timers without glitches. But not in loop().

You need an interrupt which fires at the end of each bit to allow you to put the time for the next bit into the register, without stopping the timer from counting.

loop() will then just put data into a buffer that the interrupt can pull from. If you make a class that inherits from the Stream class then you only need to implement a .write() method and the Stream class will allow you to use .println() and all those other useful methods in your new class.

Study how Serial does it. That uses bytes instead of bits but the principle is the same.

Classes may be too advanced for this project. It can be done without a class.

Should be able to do it with the timer’s output compare feature. Here’s a skeletal example. It only send one byte, but can be expanded upon. It’s untested, but compiles. I might be able to check it on a scope in the next couple of days.

#include "Arduino.h"

bool sendByte();

const uint16_t pulseTimes[] = { 160, 800 };

enum State  : uint8_t {Idle, WaitForRising, WaitForFalling};

volatile State currentState = Idle;
volatile uint8_t byteToSend;
volatile uint8_t bitsRemaining;
volatile uint16_t nextEdgeTime;

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println("Starting");

  TCCR1B = 0;         // stop counter waveform generator mode 0
  TCCR1A = 0;             // waveform generator Mode 0
  TCNT1 = 0;              // reset counter
  OCR1A = 0xFFFF;         // park output compare value here for now
  TCCR1A = (1 << COM1A1);   // OC1A low on A-match, waveform generator Mode 0
  TCCR1C = (1 << FOC1A);    // Force A-match, OC1A Low
  DDRB |= (1 << DDB1);     // PB1 (Pin 9) as output
  TIMSK1 = 0;
  TCCR1B = 1 << CS10;    // waveform generator mode 0, pre-scaler = 1


  byteToSend = 0b1101001; // test byte;
  sendByte();

}

void loop() {
}

bool sendByte() {
  const uint8_t initialDelay = 20;
  if (currentState != Idle) {
    return false;
  }

  bitsRemaining = 8;
  currentState = WaitForRising;

  // critical section
  noInterrupts();
  nextEdgeTime = TCNT1 + initialDelay;
  OCR1A = nextEdgeTime;
  TCCR1A = (1 << COM1A1) | (1 << COM1A0);   // rising edge after short delay
  TIMSK1 = (1 << OCIE1A);           // interrupt at timer match
  interrupts();

  return true;
}

ISR(TIMER1_COMPA_vect) {
  nextEdgeTime += pulseTimes[byteToSend & 0x1]; // set time for next edge
  OCR1A = nextEdgeTime;
  if (currentState == WaitForRising) {    // just sent rising edge
    currentState = WaitForFalling;
    TCCR1A = (1 << COM1A1);   // OC1A low on A-match
  } else {    // just sent falling edge
    bitsRemaining--;
    if (bitsRemaining == 0) {  // we're done
      TIMSK1 = 0;
      currentState = Idle;
    } else {  // more bits to send
      currentState = WaitForRising;
      TCCR1A = (1 << COM1A1) | (1 << COM1A0);  // OC1A high on A-match
      byteToSend >>= 1;
    }
  }
}

EDIT:
The output signal will be on Pin 9.

Thank you very much! It works :slight_smile:

Glad it works, I didn’t get around to testing it. I did think of one refinement. After the falling edge of the last bit, it needs to wait the required time (10us, 50us) before going back to the idle state. Otherwise, transmission of a subsequent byte could start before the “LOW” time of the final bit is finished.

#include "Arduino.h"

bool sendByte();

const uint16_t pulseTimes[] = { 160, 800 };

enum State
  : uint8_t {Idle, WaitForRising, WaitForFalling, final
          };

volatile State currentState = Idle;
volatile uint8_t byteToSend;
volatile uint8_t bitsRemaining;
volatile uint16_t nextEdgeTime;

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println("Starting");

  TCCR1B = 0;         // stop counter, set waveform generator mode 0
  TCCR1A = 0;             // waveform generator Mode 0
  TCNT1 = 0;              // reset counter
  OCR1A = 0xFFFF;         // park output compare value here for now
  TCCR1A = (1 << COM1A1);   // OC1A low on A-match, waveform generator Mode 0
  TCCR1C = (1 << FOC1A);    // Force A-match, OC1A Low
  DDRB |= (1 << DDB1);     // PB1 (Pin 9) as output
  TIMSK1 = 0;
  TCCR1B = 1 << CS10;    // waveform generator mode 0, pre-scaler = 1
}

void loop() {
  char c = '\0';

  while (Serial.available()) {
    c = Serial.read();
  }

  if (c) {
    Serial.println("Sending");
    byteToSend = 0b1101001; // test byte;
    sendByte();
  }
}

bool sendByte() {
  const uint8_t initialDelay = 20;
  if (currentState != Idle) {
    return false;
  }

  bitsRemaining = 8;
  currentState = WaitForRising;

  // critical section
  noInterrupts();
  nextEdgeTime = TCNT1 + initialDelay;
  OCR1A = nextEdgeTime;
  TCCR1A = (1 << COM1A1) | (1 << COM1A0);   // rising edge after 'initialDelay' clock cycle delay
  TIFR1 = (1 << ICF1) | (1 << OCF1B) | (1 << OCF1A) | (1 << TOV1);             // clear interrupt flags
  TIMSK1 = (1 << OCIE1A);           // interrupt at timer match
  interrupts();

  return true;
}

ISR(TIMER1_COMPA_vect) {
  nextEdgeTime += pulseTimes[byteToSend & 0x1]; // set time for next edge
  OCR1A = nextEdgeTime;
  switch (currentState) {

    // LOW period of final bit ended, we're done
    case final:
      TIMSK1 = 0;
      currentState = Idle;
      break;

    // just sent rising edge
    case WaitForRising:
      currentState = WaitForFalling;
      TCCR1A = (1 << COM1A1);         // OC1A low on A-match
      break;

    // just sent falling edge
    case WaitForFalling:
      bitsRemaining--;
      if (bitsRemaining == 0) {     // Wait for LOW period so next byte doesn't start too soon
        currentState = final;
      } else {
        currentState = WaitForRising;       // more bits to send. send rising edge after LOW period
        TCCR1A = (1 << COM1A1) | (1 << COM1A0);   // OC1A high on A-match
        byteToSend >>= 1;
      }
      break;

    default:
      break;
  }
}