TCCR0A = _BV(WGM01); // disable outputs set mode to ctc
A counter is a n-bit register that counts in binary at a fixed rate. Counters that are incremented by a clock signal can be used as timers. Most timer/counters are constructed with a:
The comparator can be used to reset the counter when counter matches the value of the comparison register.
The overflow signal and the output of the comparator can be used to interrupt the microcontroller.
The output of the comparator is often connected to an external output pin. This output is used as frequency or clock signal source.
The 8-bit timer0 supports the following features:
In the AVR timer, the maximum value is called top and the minimum value is called botton.
Timer/Counter Control Register A
COM0A1 and COM0A0 control the OC0A output pin.
In non-PWM mode: (0,0) disconnects the pin from OC0A, (0,1) toggles OC0A on compare match, (1,0) clears (becomes 0) OC0A on compare match, and (1,1) sets (becomes 1) OC0A on compare match.
In fast PWM mode: (0,0) disconnects the pin from OC0A, (0,1) toggles OC0A on compare match if WGM02 = 1, (1,0) clears OC0A on compare match, sets at bottom, (1,1) sets OC0A on compare match, clears at bottom,
In Phase Correct PWM mode: (0,0) disconnects the pin from OC0A, (0,1) toggles OC0A on compare match if WGM02 = 1, (1,0) clears OC0A on compare match when counting up, sets on compare match when counting down, (1,1) sets OC0A on compare match when counting up, clears on compare match when counting down,
COM0B1 and COM0B0 control the OC0B output pin.
In non-PWM mode: (0,0) disconnects the pin from OC0B, (0,1) toggles OC0B on compare match, (1,0) clears (becomes 0) OC0B on compare match, and (1,1) sets (becomes 1) OC0B on compare match.
In fast PWM mode: (0,0) disconnects the pin from OC0B, (0,1) toggles OC0B on compare match if WGM02 = 1, (1,0) clears OC0B on compare match, sets at bottom, (1,1) sets OC0B on compare match, clears at bottom,
In Phase Correct PWM mode: (0,0) disconnects the pin from OC0B, (0,1) toggles OC0B on compare match if WGM02 = 1, (1,0) clears OC0B on compare match when counting up, sets on compare match when counting down, (1,1) sets OC0B on compare match when counting up, clears on compare match when counting down,
WGM02, WGM01, and WGM00 determine the operating mode of timer 0. The modes are:
TCCR0B – Timer/Counter Control Register B
The settings of (CS02,CS01,CS00) select the counters clock source. The settings are:
The counter and comparsion registers can be read and written as 8-bit values. Care needs to be taking when updating the comparison register when the counter is counting.
TCNT0 - Timer/Counter Register
Output Compare Register A
Output Compare Register B
Timer 0 supports the following interrupts:
These interrupts are monitored and controlled with the flag and interrupt enable registers.
Timer/Counter 0 Interrupt Flag Register
Timer/Counter Interrupt Mask Register
Assuming a 8MHz system clock, program timer 0 to generate an interrupt every 10 milliseconds.
The CTC mode is best since the interrupt rate can be easily configured. The timer prescaler can be choosen with the aid of the following table:
| Scale | Precision | Duration |
|---|---|---|
| 1 | 1/8Mhz (0.125 uS) | 256/8Mhz (32 us) |
| 8 | 8/8Mhz (1 uS) | 256/1Mhz (256 uS) |
| 64 | 64/8Mhz (8 uS) | 256/(1/8)Mhz (2048 uS) |
| 256 | 256/8Mhz (32 uS) | 256/(1/32)Mhz (8192 uS) |
| 1024 | 1024/8Mhz (128 uS) | 256/(1/128)Mhz (32768 uS) |
A prescaler of 1024 provides sufficient range. All other prescalers are too short. The OCR0A compare value is calculated with:
10000uS / 128uS = 78.125
Unfortunately OCR0A must be an integer. Choosing 78 gives a period of:
78 * 128uS = 9984 uS
The timing error is 16 uS. Is this a concern? It depends!
TCCR0A sets the output pins and timer mode. The output should be disabled (ie., (COM0A1:COM0A0) and (COM0B1:COM0B0) are set to zero). The mode is set to CTC.
TCCR0A = _BV(WGM01); // disable outputs set mode to ctc
The prescaler is set to 1024, and WGM02 is set to the CTC mode.
TCCR0B = _BV(CS02) | _BV(CS00); // CTC mode and prescaler to 1024
The compare register is set with:
OCR0A = 78; // 9984uS interval
Compare A interrupts are enabled with:
TIMSK0 |= _BV(OCIE0A); // enable compare A interrupts
After 1000000 interrupts the above code has lost 16uS * 1000000 = 16 seconds. The elapsed time should have been 10000 seconds, but is 9984 seconds. 10000 seconds is 2.7 hours. Thus 16 seconds are lost every 2.7 hours.
The interrupt period can be adjusted to eliminate this error with:
#define TEN_MS_UNDER (78) #define TEN_MS_OVER (79) #define ADJUST_VALUE (8) #define ADJUST_VALUE (8) uint8_t timer0_adjust; ISR(TIMER0_COMPA_vect) { if ( timer0_adjust < ADJUST_VALUE ) { timer0_adjust++; OCR0A = TEN_MS_UNDER; } else { OCR0A = TEN_MS_OVER timer0_adjust = 0; } // rest of interrupt }
A prescaler should be chosen such that the error is as small as possible (precision is smallest) and the duration is sufficently long. A prescaler value of 8 yeilds:
2000 uS / 8 uS = 250
A comparison value of 250 gives an exact interval of 2 mS. The initialize code is:
TCCR0A = _BV(WGM01); // disable outputs set mode to ctc TCCR0B = _BV(CS01); // CTC mode and prescaler to 8 OCR0A = 250; // 2 mS interval TIMSK0 |= _BV(OCIE0A); // enable compare A interrupts
What about an interval of 1 mS? What about 100 mS with the fewest interrupts?
The CTC mode is often used to generate an 50% square wave output at a given frequency. Interrupts are not required for this application of timer 0. The range of frequencies is:
| Scale | Precision | Lowest Frequency |
|---|---|---|
| 1 | 1/8Mhz | 31250 Hz |
| 8 | 8/8Mhz (1 uS) | 3906.25 Hz |
| 64 | 64/8Mhz (8 uS) | 488.28 Hz |
| 256 | 256/8Mhz (32 uS) | 122.07 Hz |
| 1024 | 1024/8Mhz (128 uS) | 30.51 Hz |
The prescaler with the acceptable lowest frequency must be choosen.
A 50% square wave changes at a rate of 523.25*2 = 1046.50 Hz. A frequency of 1046.50 is in the range of a prescaler of 64. The period of 1046.50 Hz is (1/1046.50) which equals 955.56 uS. The closest compare value is given by:
955.56 uS / 8 uS = 119.445
The closest frequency is 1 / (119 * 8uS) = 1050.42 Hz. This gives a frequence of 1050.42 / 2 = 525.2 Hz (or 2hz error).
The pin connected to compare A is configured to toggle when the match occurs. The bits (COM0A1,COM0A0) should be set to (1,0).
TCCR0A = _BV(COM0A0) | _BV(WGM01); // toggle output set mode to ctc
The rest of the configuration is:
TCCR0B = _BV(CS01) | _BV(CS00); // CTC mode and prescaler to 64 // a count of 1, takes 2 clock cycles OCR0A = 119 - 1; // 955.6 uS interval
This musical note still requires a prescaler of 64. The compare value is given by:
(1/(2*659.26)) / 0.000008 = 94.8
The closest setting give a value of 1 / (2 * (95 * 0.000008 )) = 657.89 Hz. The timer configuration is:
TCCR0A = _BV(COM0A0) | _BV(WGM01); // toggle output set mode to ctc TCCR0B = _BV(CS01) | _BV(CS00); // CTC mode and prescaler to 64 OCR0A = 95 - 1;
The 4th octave of C is 261.63. How would this be handled?
Experimenting with the timer 0 can be done with the reg_lookup.c application on the microcontroller and the following Python program:
elif cmd == "freq" : freq = 118 range = 3 if len(sys.argv) > 2 : freq = int( sys.argv[2] ) if len(sys.argv) > 3 : range = int( sys.argv[3] ) write_reg( "ddrb", 0x08 ) write_reg( "tccr0a", 0x42 ) write_reg( "tccr0b", range & 0x7 ) write_reg( "ocr0a", freq )
The program accepts a frequency argument and a range argument. The frequency argument sets the OCR0A register, and the counter's clock is selected with the range argument.
A counter that can be cleared a preset count acts like a divider. A counter is clear when it reaches 1 divides the clock by 2. If the input is 1Mhz, the output changes only at 0.5MHz. Consider the following table of a 4-bit counter with a 1Mhz input clock.
| Reset Value | Output Clock Rate |
|---|---|
| 0 | 1000000 |
| 1 | 500000 |
| 2 | 250000 |
| 3 | 125000 |
| 4 | 62500 |
| 5 | 31250 |
| 6 | 15625 |
| 7 | 7812.5 |
| 8 | 3906.2 |
| 9 | 1953.1 |
| 10 | 976.6 |
| 11 | 488.3 |
| 11 | 244.14 |
| 12 | 122.1 |
| 13 | 61.0 |
| 14 | 30.5 |
| 15 | 15.2 |
Notice that only fractions of the base frequence can be generated. Thus 17.0 can not be generated since in falls in between 30.5 and 15.2.
What would be involved in writting a program to output all possible generated frequencies for timer 0 of the atmega644?
prescalar = [1, 8, 64, 256, 1024] freq = 8000000.0 print " ", for pre in prescalar : print "%12d" % (pre), print for x in xrange(0,256) : print "%-4d " % x, for pre in prescalar : print "%12.2f" % (freq / (2*pre*(1+x)) ), print
Write a script that given a target frequency, the script reports the closest prescaler and compare values.
import sys target = float(sys.argv[1]) freq = 8000000.0 scaler = 1 compare = 0 v = freq / (2*scaler*(1+compare)) min = abs(freq-target) prescalar = [1, 8, 64, 256, 1024] for x in xrange(0,256) : for pre in prescalar : v = (freq / (2*pre*(1+x)) ) m = abs(target-v) if m < min : min = m scaler = pre compare = x print 'best: ', scaler, compare, (freq / (2*scaler*(compare+1)) ) # why is this not the best?
import sys target = float(sys.argv[1]) freq = 8000000.0 prescalar = [1, 8, 64, 256, 1024] print "Prescaler Compare Actual Target" for scaler in prescalar : compare = (freq / (2*scaler)) / target icompare = round(compare) act = (freq / (2*scaler)) / icompare print "%9d %8d %8.2f %8.2f" % (scaler, icompare-1, act, target)
The output for 440 is:
Prescaler Compare Actual Target 1 9090 440.00 440.00 8 1135 440.14 440.00 64 141 440.14 440.00 256 35 434.03 440.00 1024 8 434.03 440.00
PWM is a common control technique for setting the intensity of some device (e.g., amount of light, volume of sound, power in a motor). Some examples are:
A PWM is specified as a period and a duty cycle. The period is the interval of time the signal repeats. The duty cycle is the percentage of time the signal is high (on).
Configure timer 0 to generate a 2ms period with a 10% duty cycle.
The fast PWM mode where the period is controlled by OCRA has (WGM2,WGM1,WGM0) = (1,1,1). The compare match B output must be used and it should turn on at TOP and reset at match. This happens when (COM0B1,COM0B0) = (1,0). The prescaler is set to 64.
TCCR0A = _BV(COM0B1) | _BV(WGM01) | _BV(WGB00); TCCR0B = _BV(WGB02) | _BV(CS01) | _BV(CS00); OCR0A = 250;
The duty cycle (period the PWM is on) is controlled by OCR0B. The range of OCR0B is from 0 to OCR0A (or 250 in this case). If OCR0B exceeds OCR0A, then the PWM output will be alway on. A duty cycle of 10% is set by:
OCR0B = OCR0A * 10% = 250 * .1 = 25
The configuration is complete when OCR0B is assigned:
OCR0B = 25;
Changing the output compare control setting (COM0B1,COM0B0) = (0,1) inverts the waveframe (i.e., the output will be off for 10% and on for 90%).
What is the setting for a 75% duty cycle? How would the period be changed to 250 uS, and how would this affect the precison of the duty cycle?
The script that controls the period and duty cycle of the PWM mode is:
elif cmd == "pwm" : period = 250 duty = 25 range = 3 if len(sys.argv) > 2 : period = int( sys.argv[2] ) if len(sys.argv) > 3 : duty = float( sys.argv[3] ) / 100.0 duty = int(duty * period ) if len(sys.argv) > 4 : range = int( sys.argv[4] ) write_reg( "ddrb", 0x18 ) write_reg( "tccr0a", 0x23 ) write_reg( "tccr0b", (range & 0x7) | 0x08 ) write_reg( "ocr0a", period ) write_reg( "ocr0b", duty )
What are the available periods if the maximum is used and the systems clock is 8Mhz?
If the operating mode is set to (WGM2,WGM1,WGM0) = (0,1,1), then the PWM is set/clear when the maximum count value (255) is reached. In this mode, both compare A and compare B outputs can be used.
The following configuration can be used to set output A to 25% duty cycle and output B to 60%.
// fast PWM, compare a and b outputs TCCR0A = _BV(COM0A1) | |_BV(COM0B1) | _BV(WGM01) | _BV(WGB00); TCCR0B = _BV(CS01); // prescale of 8 OCR0A = 64; // 256 * 25% = 64 OCR0B = 154; // 256 * 60% = 153.6
The script that controls the duty cycle for two outputs in PWM mode is:
elif cmd == "pwm2" : dutyA = 64 dutyB = 154 range = 2 if len(sys.argv) > 2 : dutyA = float( sys.argv[2] ) / 100.0 dutyA = int(dutyA * 256 ) if len(sys.argv) > 3 : dutyB = float( sys.argv[3] ) / 100.0 dutyB = int(dutyB * 256 ) if len(sys.argv) > 4 : range = int( sys.argv[4] ) write_reg( "ddrb", 0x18 ) write_reg( "tccr0a", 0xa3 ) write_reg( "tccr0b", range & 0x7 ) write_reg( "ocr0a", dutyA ) write_reg( "ocr0b", dutyB )
The full script is:
import serial import sys def write_reg( reg, value ) : ser.write( "%s %x\n" % (reg,value) ) l = ser.readline() w = l.split() v = int(w[2],16) if v != value : print "register write failed %s %x %x\n" % (reg,value,v) ser = serial.Serial('/dev/ttyS0', 9600) cmd = sys.argv[1] if cmd == "flash" : write_reg( "ddrc", 0xff ); for x in xrange(0,256) : write_reg( "portc", 255-x ); elif cmd == "freq" : freq = 118 range = 3 if len(sys.argv) > 2 : freq = int( sys.argv[2] ) if len(sys.argv) > 3 : range = int( sys.argv[3] ) write_reg( "ddrb", 0x08 ) write_reg( "tccr0a", 0x42 ) write_reg( "tccr0b", range & 0x7 ) write_reg( "ocr0a", freq ) elif cmd == "pwm" : period = 250 duty = 25 range = 3 if len(sys.argv) > 2 : period = int( sys.argv[2] ) if len(sys.argv) > 3 : duty = float( sys.argv[3] ) / 100.0 duty = int(duty * period ) if len(sys.argv) > 4 : range = int( sys.argv[4] ) write_reg( "ddrb", 0x18 ) write_reg( "tccr0a", 0x23 ) write_reg( "tccr0b", (range & 0x7) | 0x08 ) write_reg( "ocr0a", period ) write_reg( "ocr0b", duty ) elif cmd == "pwm2" : dutyA = 64 dutyB = 154 range = 2 if len(sys.argv) > 2 : dutyA = float( sys.argv[2] ) / 100.0 dutyA = int(dutyA * 256 ) if len(sys.argv) > 3 : dutyB = float( sys.argv[3] ) / 100.0 dutyB = int(dutyB * 256 ) if len(sys.argv) > 4 : range = int( sys.argv[4] ) write_reg( "ddrb", 0x18 ) write_reg( "tccr0a", 0xa3 ) write_reg( "tccr0b", range & 0x7 ) write_reg( "ocr0a", dutyA ) write_reg( "ocr0b", dutyB ) ser.close()
Most RC toys are controlled by servo motors. A servo motor can position a shaft with in an arch of 300 degrees. A PWM signal is used to control the position. The pulse width varies from 1mS to 2ms, with a required repeat rate of around 40 times a second. The shaft is at the 0 position for a pulse width of 1mS and at the 300 degree position with a pulse width of 2mS (the degree of rotation varies across server motors).
A repeat rate of around 40 times a second gives a period of 1/40 or 25mS. The closest period is 32mS when the prescaler is set to 1024. The repeat rate is 32 times per second for the 32mS period. If 40 was required, then timer 0 and timer 2 would be required, each timer would handle one output.
How many steps would this setup allow for the 1mS to 2mS period?
The calculation is:
(2mS - 1mS) / (1024/8000000) = 7.812
Thus, this setting only allows 7 different servo motor positions.
(2mS - 1mS) / (256/8000000) = 31.25
What are the pros can cons of the above two techniques?
Use timer 0 to generate the following waveform:
A possible solution approach using timers for waveform generation is:
The times in the example are all multiples of 104. Since 104 is the product of 13 and 8, a timer whose clock is 8 uS can be used to generate all multiples of 104. For a 8Mhz system clock, timer 0 with a prescaler of 64 gives a 8 uS tick rate. The 8-bit timer can measure the time upto 19 multipes of 104 (i.e., 1976uS).
Unfortunately the prescaler on timer 0 cannot be directly controlled. This results in an 8uS uncertainty to when the next tick will occur.
Timer 2 has the ability to reset the prescaler counter, and thus eliminate the uncertainty.
The output driven by the counter must be initialized to the require state. The output is set to a 0 with:
TCCR2A = (TCCR2A & ~0xc0) | 0x80; TCCR2B |= _BV(FOC0A);
The output is driven high with:
//TCCR2A = (TCCR2A & ~0xc0) | 0xc0; TCCR2A |= 0xc0; TCCR2B |= _BV(FOC0A);
Strobing the FOC0A bit causes, a compare match operation to occur.
The prescaler for timer2 is reset with:
GTCCR = _BV(PSRASY);
Timers 0 and 2 are both 8-bit timers with almost identically features.
The differences for timer 2 are:
Timer 2 can be used for a real time clock.
The differences for timer 0 are:
Timer 0 can be used to count the changes for an external signal.
The 16-bit timer in the AVR is very similar to the 8-bit timers.
Since a shared 8-bit temporary register is used to access the upper byte of the 16-bit counter, compare and capture registers, interrupts should be disabled for all accesses. An example of a safe access is:
uint16_t counter = 0xff01; uint8_t sreg = SREG; cli(); TCNT1 = counter; // 16-bit write SREG = sreg;
The connections for this servo motor are:
If the prescaler of timer 1 is set to 8, then the time between increments of the counter is 1 uS for a 8Mhz clock. The maximum duration for this timer is 65,536 uS or 65.536 mS.
A duration of 1 mS is 1000 counts, and 2 mS is 2000 counts. Thus the 16-bit counter can produce 1000 distinct duration from 1 mS to 2 mS.
Timer 1 configuration for a PWM with a period of 20 mS is:
static void timer1_init( void ) { /* Both outputs are clear on match, and set on top, * top is ICR2. */ TCCR1A = 0xa2; // clear /* Mode configuration continued, * prescalar clock set to divide by 8. */ TCCR1B = (3<<3) | (2); /* assuming a 8Mhz system clock, * a 0.020 millisecond period is * 0.020 / 0.000001 = 20000 */ ICR1 = 20000; DDRD |= _BV(5) | _BV(4); // enable timer1 pwm outputs }
The duty cycle of the PWM mode for both outputs is controlled with:
static void timer1_pwm( uint16_t a, uint16_t b ) { OCR1A = a; OCR1B = b; }
For a servo motor with a 1 mS to 2 mS control pulse, the values for A and B should range from 1000 to 2000. Note: that interrupts must be turned off when OCR1A and OCR1B are updated.
A simple demonstration of the servo motor motions can be performed by varying the pulse width from 1 mS to 2 mS and then from 2 mS to 1 mS. If the pulse width is change every 100 mS, then a visible motion of the motors is apparent.
Timer 2 can be configured to generate a 20 mS periodic interrupt. The 100 mS interval is acheived by skipping 5 interrupts. The configuration code for timer 2 is:
static void timer2_init( uint8_t clockCnt ) { OCR2A = clockCnt; // CTC, OCR2A is top, TCNT2 cleared with OCR2A reached // prescaler set to 1024 TCCR2A = _BV(WGM21); TCCR2B = _BV(CS22) | _BV(CS21) | _BV(CS20); // enable compare A interrupts TIMSK2 |= _BV( OCIE2A ); timer2_state.pwm_a = PWM_A_MAX; timer2_state.a_dir = 0; timer2_state.pwm_b = PWM_B_MIN; timer2_state.b_dir = 1; }
The PWM is changed in timer 2's interrupt routine. The code is:
ISR(TIMER2_COMPA_vect) { if ( timer2_state.skips != 0 ) { timer2_state.skips--; return; } cnt++; PORTC = ~cnt; timer2_state.skips = TIMER2_SKIPS; if ( timer2_state.a_dir ) { timer2_state.pwm_a += DELTA; if ( timer2_state.pwm_a > PWM_A_MAX ) { timer2_state.pwm_a = PWM_A_MAX; timer2_state.a_dir = 0; } } else { timer2_state.pwm_a -= DELTA; if ( timer2_state.pwm_a < PWM_A_MIN ) { timer2_state.pwm_a = PWM_A_MIN; timer2_state.a_dir = 1; } } if ( timer2_state.b_dir ) { timer2_state.pwm_b += DELTA; if ( timer2_state.pwm_b > PWM_B_MAX ) { timer2_state.pwm_b = PWM_B_MAX; timer2_state.b_dir = 0; } } else { timer2_state.pwm_b -= DELTA; if ( timer2_state.pwm_b < PWM_B_MIN ) { timer2_state.pwm_b = PWM_B_MIN; timer2_state.b_dir = 1; } } timer1_pwm( timer2_state.pwm_a, timer2_state.pwm_b ); }
How can the code size be reduced? What is the fastest rate that the motor's position can be changed?
#include <avr/io.h> #include <stdint.h> #include <avr/power.h> #include <avr/sleep.h> #include <avr/pgmspace.h> #include <avr/interrupt.h> static void timer1_init( void ) { /* Both outputs are clear on match, and set on top, * top is ICR2. */ TCCR1A = 0xa2; // clear /* Mode configuration continued, * prescalar clock set to divide by 8. */ TCCR1B = (3<<3) | (2); /* assuming a 8Mhz system clock, * a 0.020 millisecond period is * 0.020 / 0.000001 = 20000 */ ICR1 = 20000; DDRD |= _BV(5) | _BV(4); // enable timer1 pwm outputs } static void timer1_pwm( uint16_t a, uint16_t b ) { OCR1A = a; OCR1B = b; } #define TIMER2_SKIPS (5) #define PWM_A_MAX (2000) #define PWM_A_MIN (1000) #define PWM_B_MAX (2000) #define PWM_B_MIN (1000) #define DELTA (25) static struct { uint8_t skips; uint16_t pwm_a; uint8_t a_dir; uint16_t pwm_b; uint8_t b_dir; } timer2_state; static void timer2_init( uint8_t clockCnt ) { OCR2A = clockCnt; // CTC, OCR2A is top, TCNT2 cleared with OCR2A reached // prescaler set to 1024 TCCR2A = _BV(WGM21); TCCR2B = _BV(CS22) | _BV(CS21) | _BV(CS20); // enable compare A interrupts TIMSK2 |= _BV( OCIE2A ); timer2_state.pwm_a = PWM_A_MAX; timer2_state.a_dir = 0; timer2_state.pwm_b = PWM_B_MIN; timer2_state.b_dir = 1; } uint8_t cnt = 0; ISR(TIMER2_COMPA_vect) { if ( timer2_state.skips != 0 ) { timer2_state.skips--; return; } cnt++; PORTC = ~cnt; timer2_state.skips = TIMER2_SKIPS; if ( timer2_state.a_dir ) { timer2_state.pwm_a += DELTA; if ( timer2_state.pwm_a > PWM_A_MAX ) { timer2_state.pwm_a = PWM_A_MAX; timer2_state.a_dir = 0; } } else { timer2_state.pwm_a -= DELTA; if ( timer2_state.pwm_a < PWM_A_MIN ) { timer2_state.pwm_a = PWM_A_MIN; timer2_state.a_dir = 1; } } if ( timer2_state.b_dir ) { timer2_state.pwm_b += DELTA; if ( timer2_state.pwm_b > PWM_B_MAX ) { timer2_state.pwm_b = PWM_B_MAX; timer2_state.b_dir = 0; } } else { timer2_state.pwm_b -= DELTA; if ( timer2_state.pwm_b < PWM_B_MIN ) { timer2_state.pwm_b = PWM_B_MIN; timer2_state.b_dir = 1; } } timer1_pwm( timer2_state.pwm_a, timer2_state.pwm_b ); } int main( void ) { DDRC = 0xff; clock_prescale_set( clock_div_1 ); timer1_init(); timer2_init( 156 ); // approximately 0.02 seconds for 8MHz sei(); // enable interrupts while ( 1 ) { sleep_mode(); } return 0; }
The counter values of the timers can be read to determine the current time. If the timers are read in the pin change interrupt routine, then the time between pin interrupts can be measured. How can this be used to decode an asynchronous serial signal? How should protocol errors be handled?