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(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.

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.

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

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()) {
}

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;
}
}
``````