Understanding a chunk of counter/timer code for PWM

I'm working on a project that requires a series of PWM pulses at 56Khz. A very kind person out there gave me a chunk of code that he said worked for him. I've tested it, and it does work for my application, but I'd really like to be able to understand all of it. I understand most of it, but the timer initializations and the actual transmission elude me.

I fully grasp the concept of how the counter/timers are used to generate the PWM signal (you are constantly counting upward until you hit a compare marker at which point something is toggled "on", and it stays on until you hit the end of your count and everything is toggled "off"), but I haven't been able to penetrate what these timer variables are actually representing to understand where those steps are occurring. Here's the code:

#define IR_PWM_FREQUENCY 56   // What frequency is the IR
#define TX_IO_Pin 3           // Pin to send data on

#if IR_PWM_FREQUENCY == 56
  #define PWM_DELAY 13
  #define IR_CLOCK_RATE 56000
#endif

int switchState = 0;

void setup() {
  // Serial.begin(9600);
  MT5_TX_init();
  pinMode(2,INPUT);
}

void loop() {
  switchState = digitalRead(2);
  if (switchState == LOW) {
    digitalWrite(TX_IO_Pin,LOW);
  }
  else {
    MT5_TX_shot(1,25,9);
    delay(250);
  }
}

void MT5_TX_init(){
  // toggle on compare, clk/1
  TCCR2A = (1 << WGM21);
  TCCR2B = (1 << CS20);
  OCR2A = (F_CPU/(IR_CLOCK_RATE*2L)-1);
  OCR2B = OCR2A; // / 2;
  pinMode(TX_IO_Pin, OUTPUT);
  digitalWrite(TX_IO_Pin, LOW); 
}

void MT5_TX(long uSecs){
    TCCR2A |= (1 << COM2B0);
    delayMicroseconds(uSecs); //PWM_DELAY - 3);
    TCCR2A &= ~(1 << COM2B0);
}

void MT5_TX_header(){
  MT5_TX(2400);
  delayMicroseconds(600);
}

void MT5_TX_logic1(){
  MT5_TX(1200);
  delayMicroseconds(600);
}

void MT5_TX_logic0(){
  MT5_TX(600);
  delayMicroseconds(600);
}

void MT5_TX_byte(byte data) {
  for (byte mask = 10000000; mask > 0; mask >>= 1) {
    if (data & mask) {
      MT5_TX_logic1();
    } 
    else {
      MT5_TX_logic0();
    }
  }
}

void MT5_TX_shot(byte teamID, byte playerID, byte damage) {
  MT5_TX_header();
  MT5_TX_byte(playerID);
  MT5_TX_byte((teamID << 6) + (damage << 2)); // Shifting the values to the front 6 bits of the byte
}

Obviously this is an implementation of the MilesTag laser tag protocol. I understand everything here except the most critical 2 functions -- MT5_TX_init(), and MT5_TX(). In fact, I rewrote the MT5_TX_shot and MT5_TX_byte functions in a way I could better understand them. Mostly, I'm just trying to figure out where the values are coming from for the TCCR2A, TCCR2B, OCR2A, OCR2B, WGM21, CS20, and COM2B0 variables. I understand that they're pre-defined registers on the chip, but I don't exactly know what that means, and I don't know what these operations do to the registers.

I would also love it if someone could explain the formula for the OCR2A variable. I realize that's probably got something to do with the pre-scaler and where to set the compare register, but I just can't figure out how that works.

If anyone can help explain this, or if anyone can direct me toward a good tutorial that's mostly on-topic with what I'm trying to do here, I would be most appreciative! While it's working now for the ATmega328 chip, I'd love to understand this well enough to implement it on any microcontroller in the future as needed. Thank you!

Download the datasheet from this link:

http://www.atmel.com/images/Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet_Complete.pdf

See specifically the sections on the individual timers.

Many thanks for the reference! I've been reading through it this weekend, and it's a bit dense, but I'm getting a little out of it. If you could bear with me as I write this out and let me know where I go wrong, it will help me understand it in my own head.

As I understand it, the TCCR2A/TCCR2B are the timer and controller registers which are essentially 8-bit binary numbers where each bit has a pre-defined variable name like:

For TCCR2A:

  • COM2A1
  • COM2A0
  • COM2B1
  • COM2B0
  • WGM21
  • WGM20

For TCCR2B:

  • FOC2A
  • FOC2B
  • WGM22
  • CS22
  • CS21
  • CS20

All should be set to 0 by default, and you only set the ones you want to use to 1. Is that right? I'm also guessing that the various bits named in each register have their bit number assigned as a value, is that right? That's the only way I see that the expression (1 << WGM21) makes any sense. It would mean that you're setting the WGM21 bit to 1.

So, with all that, if I read the code and this manual right, TCCR2A has all bits set to 0 except the WGM21 bit, which would mean that "Clear Timer on Compare" or CTC is the mode of operation for that register. Then TCCR2B has all bits set to 0 except the CS20 bit which would mean that no prescaling is being done.

OCR2A and OCR2B are the Output Compare Registers which are compared against the TCCR2A/TCCR2B registers all the time. In my code, OCR2A is set to 16000000/((56000 * 2L)-1) which I'm still a little unsure about. Where does the 2L come from? I've seen some examples reference the data type at the end of the value like "1200000UL" as "unsigned long". Does this mean "56000 * 2" with the resulting value being a LONG?

Then OCR2B is being set to the same thing as OCR2A. This makes no sense to me. I thought there had to be a marking value where to turn things on, and then the top value to turn things off?

Finally, in MT5_TX(), TCCR2A has its COM2B0 bit set to 1 (and preserves all other set values) which toggles OCR2B on compare match for uSecs number of seconds, then the COM2B0 bit is set again (while removing values other than COM2B0).

I still don't quite understand why CTC mode is used and not fast PWM mode, unless I'm just reading that wrong. Am I on the right track, or am I missing something big?

The way I work with the Timer registers is to write down in binary all the required bit settings that I need. For example 0b00100001 I find it much easier to relate to the datasheet that way. Others clearly prefer using the style 1 << WGM21.

Be careful not to change bit values that don't concern you - you may upset something else.

If you want to set (say) bit 3 without changing any of the other bits you can do it with an OR instruction.
TCCR2A |= 0b00001000;
and if you want to clear the bit you can do it with
TCCR2A &= 0b11110111;

The latter can also be done with
TCCR2A &= ~0b00001000;
as the tilde inverts the bits.

By the way those examples are probably meaningless - I did not consult the datasheet before writing them.

...R

Thanks Robin2. That method does help a bit. Although I think I understood after I figured out the (1 << WGM21). The original code used a macro that I was unaware of:

TCCR2A =_BV(WGM21);

So I'm breaking it down bit by bit, if you'll excuse the pun.

Did you have any idea about the 2L in the formula for OCR2A in my code? Is it what I thought, that the resulting value of 56000 * 2 was just a LONG data type?

And do you know why OCR2B is set equivalent to OCR2B?

Timer tutorial with code:

You are on the right track. Here's how I understand it.

Timer 2 has been set up in Timer Mode 2 (010) which is CTC to OCR2A. The timer will count up from 0 to OCR2A and start over again. The code sets up OCR2A =(16000000/112000)-1 which is 141.857 which will be truncated to 141. The timer will count 142 ticks(because it is zero referenced) and reset.
142 x .0625us = 8.875 us. Since the pulse has a high and low the period is 17.75 us which is approximately 56.5 Khz. Close enough to the 56K desired frequency.

Now, to get some useful pulse output from the timer, the code is using output B which is on pin D3 and it is set to toggle every time OCR2B = OCR2A = 141. At the end of every timer cycle, pin 3 will change state so it is high for one cycle and low for the next. 50% duty cycle at 56K.

I'm not sure the timer could have been set up in Mode 7 (111) which is Fast PWM to OCR2A, because the pulse frequency would be 112K. I'm not sure, without checking all the data sheet, how the toggle control works with Mode 5 (101) which is PWM to up and down to/from OCR2A. CTC mode may have been the easier setup.

fr0zone:
Did you have any idea about the 2L

Any time you do math with integer constants the compiler assumes that they are 16 bit integers if they will fit. IF you multiply with them and the product is greater than what will fit in a 16 bit signed integer then there is some rollover and the answer you get is way off. Putting the L or UL behind one of the constants forces the compiler to treat it as a long or unsigned long. Since one of the numbers there is a long, the whole operation gets done with 32 bit longs and there is no worry of it rolling over (unless of course it gets really really big).

When dealing with signed integers, isn't it overflow and underflow?

Unsigned's roll over (to get their bellies scratched).

GoForSmoke:
When dealing with signed integers, isn't it overflow and underflow?

Unsigned's roll over (to get their bellies scratched).

I don't know what the technical term is, but either way the number gets too big to be represented with the number of bits available and some high order bits that don't fit get left off and the number gets truncated to fit into the available space and turns out not to be the number you were expecting.

And when you put a UL on the end of one of the constants in the expression the compiler knows to use a bigger type and the issue is avoided.

I only ever knew a few chips down to registers but the Status Registers always had Carry and Overflow bits. Just looked it up and so does the 328P. These are needed to do multi-word (AVR word length is 8 bits) math.

Thank you very much to everyone for their replies! I think I've just about got it all now. Much appreciated!