Digital 4: The Timer and ADC

The first part is a re-primer on timers and the ADC. The homework is at the end. Office hours are Wednesday 5-7 in E15-318. However, it will not be as long as yesterday’s one, so please bring questions :-)

IMPORTANT: The datasheet for ATmega88P is slightly different from the one for ATmega88. Please use the one linked from this site: datasheet for ATmega88P.

Sample File

Timer and ADC sample file

Note: this file is incomplete and is not meant to compile. It simply provides some snippets of code to use.

makefile (for OSX only, not for windows) - This contains the fixes I made during lab yesterday, if you need it.

Timer

Today in class, went over the Timer peripheral in detail. Here I wanted to put some extra info about the workings of the timer. The timer needs several things to work:

  • A clock: The clock is set in TCCR0B, using the CS00, CS01, and CS02 bits. The divider can be seen in table 14-9 of the datasheet.How do we decide the divider? Well, let’s say we need to time something that take 0.1 seconds. We have to know the clock rate of the microcontroller, which defaults to 1MHz. So in 0.1 seconds, 1MHz * 0.1 = 100K clock cycles will take place. We can only count to 256 with a 8 bit counter (2^8 = 256), so for each “tick” of the timer, 100K/256 = approx. 391 “ticks” of the microcontroller clock must take place. Now we know the divider (391), but it’s not one of our choices, so we pick the next higher value (1024).
    Another way to think about it:
  • Mode: The mode is specified using the WGM00 and WGM01 bits in TCCR0A and WGM02 bit in TCCR0B. These control the Waveform Generation Mode, a fancy way of answering “what does the timer do?”. They are all listed in table 14-8 of the datasheet. Let’s take a look at the modes:
    • We’ll skip the phase-correct modes (1 and 5)
    • Mode 0 (000): the default. The timer simply counts. It’s like a regular clock. It starts at zero, goes to 255, and then goes back to zero.
    • Mode 2 (010): CTC mode. In this mode, the timer counts from zero to the value set in OCR0A, and then goes back to zero. No special output is produces. However, you can control the frequency at which the counter “overflows” (ie, resets to zero). This is handy with interrupts, something we’ll cover during a lab. If you think of the diagram above, the timer takes 0.291 seconds to count to 255 (that is, 1/0.291 sec = 3.4Hz). If it counted to 98, it would take 0.1 seconds (10Hz). and if it counted to 98*2 = 196, it would take 0.2 seconds (5Hz).
    • Mode 3 (011): Fast PWM. The timer counts like in mode 0, but PWM is enabled. You can set two outputs using OCR0A and OCR0B.
    • Mode 7 (111): Just like CTC mode, but with PWM enabled. This mode lets you select both a frequency (maximum value), and a point inside where the external pin turns on or off. Note that only OCR0B can be used (since OCR0A is used to define the maximum value like in CTC).
  • Output mode: what to do when the counter goes back to zero. In PWM modes, you also get to define what to do when you have a match with OCR0A and OCR0B, which basically set markers on the timer dial as it goes around. You specify what to do with COM01A and COM00A in TCCR0A for OCR0A (the first compare mark). For the second mark, you use COM01B and COM00B, also in TCCR0A. Note: _ is A or B…
    • Setting 00 (both COM01_ and COM00_ are low): do nothing at all (even in PWM mode)
    • Setting 10 (COM01_ is high): set OC0_ LOW when count = OCR0_. In PWM mode, set OC0_ HIGH when count = 0 (starts high at zero, goes low when matched).
    • Setting 11 (both are high): set OC0_ HIGH when count = OCR0_. In PWM mode, set OC0_ LOW when count = 0 (starts low at zero, goes high when matched).
    • Details are given Tables 14-2 to 14-7 of the datasheet.

Note that the outputs of the different Ouput Compares (pins with functions starting with OC, like OC0A, OC2B, etc) are controlled by their respective registers (OCR0A, OCR2B, etc). However, you MUST set the correct DDRs to outputs to get those pins to do anything at all.

The Analog to Digital Converter (ADC)

The ADC is basically like a multimeter for a microcontroller. It basically takes a reading from the meter and gives you the voltage reading. The voltage reading is between 0 and a “reference voltage”, which is the maximum allowed value. Values are read in as a 10-bit number between 0 and 1023. To convert back to volts, you can calculate (reading * reference voltage)/1023, though most of the time we don’t actually convert to volts. Unlike a regular multimeter, you can select to read from one of a number of physical inputs pins (like ADC0, ADC1, etc). To use the ADC, you need to do a few things:

  • Enable the ADC by setting bit ADEN in ADCSRA high.
  • The ADC needs a clock (sort of like the timer). This is used internally, but because a MCU can run at many speeds, you have to set it manually. Set the ADC clock by changing the ADPS2, ADPS1, and ADPS0 bits in ADCSRA. The maximum safe speed is 250kHz. Generally, it’s safest to run slower. You can find all the division ratios in table 23-5. So if your MCU runs at 1MHz, then a divisor of 4 (set ADPS1 high as per table 23-5) will give a clock rate of 1,000,000/4 = 250,000, with is the maximum speed allowable.
  • Select a reference (table 23-3). This is done in the the ADMUX register, with bits REFS1 and REFS0.
    • Default (both low) uses the AREF pin on the MCU. You can put any voltage between 0 and VCC here.
    • Setting REFS0 high uses the voltage at AVCC (ie, VCC).
    • Setting both high uses 1.1V as the reference (to read smaller signals).
    • If the voltage is above the reference, the reading is 1023 (ie, max). However, if you exceed VCC, the MCU will fry.
  • Select the pin to read from. These are set with the bits MUX3, MUX2, MUX1, and MUX0 (a 4-bit value, also written as MUX3:0 collectively) in ADMUX register. If you read it as a 4-bit value (remember, the 1 bit is to the right, 3 bit is to the left), then zero is ADC0, and 7 is ADC7. The other values are rarely used. The patterns are in table 23-4.
  • Now you’re ready to do a reading. To read, you have to set the ADSC (start conversion) bit high in ADCSRA. This is both an input and an output. See sample for how to set it and wait for it to switch back to zero. Basically, you make it high to tell the ADC to start. When it’s done, the ADC makes the bit low to let you know it’s done.
  • Read the value in. The value of the ADC is available from the ADC variable, which is of type unsigned int. Note that the result is 16 bit variable (the smallest type capable of holding 10 bits), not a unsigned char (which has only 8 bits). Since the MCU has an easier time with 8 bits, we often only use 8 bits of the result. You can easily make it fit in 8 bits by dividing ADC/4.

And a final note: The ADC input has a finite resistance. So if you plug in a very high-resistance sensor (like a photodiode), it will not read correctly. We generally use a opamp buffer to “condition” and bias the signal correctly before sending it to the ADC.

The Homework

For this homework (due Thursday), please do the following steps:

  1. Complete the work from the in-class lab. Namely, use the PWM code in the sample and write a simple program that blinks the LED.
  2. Modify the code so that the divisor is 1 (turn it into brightness control instead of blink, because it will be going really fast). This is given in the table 14-9 as “no prescalar”.
  3. Complete the ADC init code from the sample.
  4. Hook up the ADC to read the value of the potentiometer (”pot”) I gave out in class. The pot has 3 legs. The left should go to VCC, the right to GND, and the middle one is the output, which goes to ADC0 (PC0). Doesn’t matter which you consider “left”.
  5. Control the blink rate with the ADC value. To do this, you will have to update the PWM value with the ADC reading (divide the ADC reading by 4 to make it 8 bit like the timer).
  6. Hint: if you update the PWM rate constantly, the LED will never have time to blink. Instead, use _delay_loop_2(<integer>) to wait between updates. _delay_loop_2 is a function that takes an unsigned int instead of a unsigned char, or 16 bits instead of 8. The maximum value you can pass to it is 2^16-1 = 65535 or 0xffff. There is a pseudocode main function in the sample file.
  7. IMPORTANT: Prepare for the next class by installing Eagle from here.

About this entry