I'm using Timer2 to generate a clock signal. I wanted to check that it
was generating at 50% on and 50% off, so I used the code below to try
to check it. I am surprised that the output - although close to 50% - seems
to have a small varying error, whatever the timer counter values are.
Am I using the timer counter and output toggle feature correctly?
I may want to use this to generate a clock signal at about 9600 Hz,
rather than bit-banging the output. I thought it might produce less
jitter.
I even get the error with the timer set to run (prescalar set to max)
as slow as possible, as shown with this output:
ints/ mark/space/ diff /percent error
X31X/41552/41702/-150.00/-0.36/
X31X/40620/42728/-2108.00/-5.19/
X30X/40550/42796/-2246.00/-5.54/
X31X/41274/41980/-706.00/-1.71/
X30X/42266/40978/1288.00/3.05/
X31X/42209/41123/1086.00/2.57/
X31X/41192/42151/-959.00/-2.33/
X30X/40550/42798/-2248.00/-5.54/
X31X/40785/42473/-1688.00/-4.14/
X31X/41718/41531/187.00/0.45/
X30X/42355/40977/1378.00/3.25/
The hardware circuit is just a single wire, connecting pin11 to pin7. Arduino0022, using a Duemilanove (168) clone.
#include <avr/interrupt.h>
#include <avr/io.h>
const int ledPin = 13;
const int ClockPin = 11; //PB3 is OC2A, Arduino pin 11
const int StPin = 7;
void print_hex_byte_leading_zero(byte val)
{
if (val < 0x10)
{
Serial.print('0');
}
Serial.print(val, HEX);
}
byte converthexdigit(char c1)
{
byte retval;
if (c1 >= '0' && c1 <= '9') retval = (c1 - 48 );
if (c1 >= 'A' && c1 <= 'F') retval = (c1 - 65 + 10 );
if (c1 >= 'a' && c1 <= 'f') retval = (c1 - 97 + 10 );
return retval;
}
volatile boolean int_out_rdy;
volatile int int_out_data;
volatile int int_out_bits;
volatile long l_ints = 0L;
long scount, mcount;
ISR(TIMER2_COMPA_vect) {
l_ints++;
if (int_out_rdy)
{
if (int_out_bits--)
{
if (int_out_data & 0x20)
{
//digitalWrite(ledPin, 1);
PORTB |= 0x20;
}
else
{
//digitalWrite(ledPin, 0);
PORTB &= ~(0x20);
}
int_out_data <<= 1;
}
else
{
int_out_rdy = 0;
PORTB &= ~(0x20);
}
}
}
void setup_interrupt()
{
//Timer2 Settings
TCCR2B |= (1<<CS22); // was off
TCCR2B |= (1<<CS21);
TCCR2B |= (1<<CS20); // was off
TCCR2B |= (1<<WGM22);
TCCR2A &= ~(1<<WGM21);
TCCR2A |= (1<<WGM20);
TCCR2A |= (1<<COM2A0); // set up for OCR2A toggle
TCCR2A &= ~(1<<COM2A1);
// Use internal clock - external clock not used in Arduino
ASSR &= ~(1<<AS2);
//Timer2 Overflow Interrupt disable,
TIMSK2 &= ~(1<<TOIE2);
// set one output compare interrupt enables
TIMSK2 |= (1<<OCIE2A);
TIMSK2 &= ~(1<<OCIE2B);
OCR2A = 0x34; // gives as near to 9600 as I can get
sei();
}
unsigned long millinext;
unsigned long milliinterval = 1000L;
void setup() {
Serial.begin(9600);
setup_interrupt();
millinext = millis() + milliinterval;
pinMode(ledPin, OUTPUT);
pinMode(ClockPin, OUTPUT);
pinMode(StPin, INPUT);
digitalWrite(StPin, HIGH);
}
void loop() {
char c;
if (millis() > millinext) {
Serial.print("X");
Serial.print(l_ints);
Serial.print("X");
Serial.print("/"); Serial.print(mcount); Serial.print("/");
Serial.print(scount); Serial.print("/");
float diff = (mcount - scount);
Serial.print(diff); Serial.print("/");
float percent = 100.0*diff/(float)mcount;
Serial.print(percent); Serial.println("/");
mcount = scount = 0L;
l_ints = 0L;
millinext = millis() + milliinterval;
}
digitalRead(StPin) == HIGH ? mcount++: scount++;
if (Serial.available())
{
c = Serial.read();
// to read a command like O1
if (c == 'O' && !int_out_rdy) // O for output
{
while(!Serial.available());
char c1 = Serial.read();
int_out_data = 0;
int_out_data = (c1 << 1) | 0x01; // put stop bit on at end
Serial.print(int_out_data, HEX);
int_out_bits = 6;
int_out_rdy = 1; // flag for interrupt routine
}
// to read a command like Pf0
if (c == 'P')
{
char c1;
byte add;
Serial.print("*");
while(Serial.available() < 2) { // expecting 2 characters
;
}
add = converthexdigit(Serial.read()) * 16 + converthexdigit(Serial.read());
OCR2A = add;
Serial.print("P");
print_hex_byte_leading_zero(add);
Serial.println(";");
}
}
}