In building one’s own MCU board, one often does not use a crystal or resonator. Instead, the internal RC-oscillator is employed, which can be quite inaccurate. Similarly, if one wants to use the internal reference voltage to measure the supply voltage, it turns out that the reference voltage can deviate from its nominal value quite a lot. Both, the RC-oscillator and the internal reference voltage can be calibrated, though. In this blog post, I describe a simple method to calibrate both using only a UNO board and a multimeter employing the avrCalibrate library.
In particular, when it comes to the design of battery-based low-power systems, the issue of calibrating the RC-oscillator and the internal reference voltage arises.
Why calibrate the RC-oscillator?
Employing the internal RC-oscillator reduces the part count, saves space on the PCB, allows the use of more MCU pins as GPIOs, and, last but not least, reduces supply current and minimizes wake-up time from a power-down state. All in all, it has a lot of advantages, in particular for low-power applications. The main disadvantage is low accuracy. While quartz crystal oscillators have a maximal deviation of around ± 50 ppm, and ceramic resonators of around ± 1000-5000 ppm or ± 0.1 to 0.5 %, the factory calibration of RC-oscillators in AVR chips is specified as accurate up to ±3 % or ±10 % (depending on the device).
As mentioned in the blog post on asynchronous communication, a 3 % deviation can already be beyond the acceptable limit. 10 % definitely is! Similarly, if you want to do some timing, a 10 % error does not look very convincing. However, one can bring the error down to 1 % by tweaking the OSCCAL register of AVR MCUs. It should be noted, though, that the calibration is supply voltage and temperature dependent. In other words, the 1 % error is guaranteed only as long as these two parameters do not change too much.
Why calibrate the internal reference voltage?
In low-power applications, one often wants to monitor the supply voltage (coming from a battery) in order to be able to stop operation before the display cannot be read anymore or the EEPROM becomes corrupted. One can leave this task to the brown-out detection mechanism of the MCU. However, this mechanism draws a considerable amount of current in the power-down mode, there are only a few fixed voltage levels, it is not very accurate, and, most importantly, it will reset the chip and does not permit the output of a low-voltage message on a display.
One way to measure the supply voltage quite accurately is to measure the internal reference voltage (most often 1.1 volts) by using the ADC with the supply voltage as the reference. The only problem is that the internal voltage is also only accurate up to 10 %. As in the case above, one can improve the accuracy. If the supply voltage is known, it is easy to estimate the true internal reference voltage with an error of less than 1 %, provided the temperature does not change too much.
You need a server board for the calibration process, which will generate the reference frequency. This can be a UNO board or similar, and its clock frequency should be ideally controlled by a quartz crystal, but a ceramic resonator is also acceptable. Usually, these resonators have an accuracy better than 0.1%. However, sometimes it can get as bad as 0.9 % deviation. So, if you want to be sure, you should measure the frequency generated at the MOSI pin (digital pin 11 on a UNO). It should be 10 Hz. Instead of a UNO board, also a MEGA(2560) or a Nano board will do.
You can calibrate almost all classic AVR MCUs that are supported by ATTinyCore, MicroCore, MiniCore, and MightyCore. Additionally, the procedure also works for some MCUs supported by MegaCore. Voltage calibration is not supported for the ATtiny2313(A) and ATtiny4313, however, because both do not have an ADC. Similarly, it does not work for the ATtiny13 because the internal reference voltage cannot be routed to the ADC. Furthermore, the ATtiny43U is not supported at all, since I was not able to get reasonable values when measuring the supply voltage. Please check the list of supported MCUs in the
readme file of the avrCalibrate library.
The easiest way to connect the server board to the target board is to use an ICSP cable, assuming that your target board has an ICSP connector. This will connect the ground line and the signal lines (
RESET) of the two boards. Further, it also connects the supply voltage of the server board to the supply voltage rail of the target board. If the target board is already powered by a separate source, or if a different supply voltage than the 5 V coming from the server board is expected for the target board, then the Vcc line of the ICSP cable needs to be broken out.
If you wonder, which of the pins have to be connected, let us take a look at the connector looking from above.
When you have keyed connectors, then it is clear which way you should plug them in. Often, however, the boards only contain 6 pins, and it is unclear which way you should plug the sockets in. In this case, watch out for a marker, often a “1”, that marks pin 1. On Arduino boards, pin 1 is always oriented toward the USB connector. If in doubt, use an Ohm meter to identify the GND pin. In any case, I have not managed to burn any board by plugging in the socket the wrong way around.
To make life easier, I built an ICSP cable with broken-out Vcc and RESET line (similar to what dmjlambert described in his Instructable), which one can also use to program a target chip or to debug it using debugWIRE. Connecting the server board to the target then looks as follows.
Once you have connected the two boards and the target board is supplied with power, you should measure the target supply voltage with a multimeter. And make sure that the supply voltage is close to the voltage the board will later use because the calibration is supply voltage dependent.
After having installed the avrCalibrate library (using the library manager or downloading the library from GitHub) together with the Vcc library, you need to upload the calibration sketches that are stored in the
- Correct the value of the compile-time constant TRUEMILLIVOLT in the calibTarget sketch in the
extrasfolder according to what you have measured as the supply voltage for the target board.
- Select the right target MCU under the
Toolsmenu, select as the clock frequency either
8 MHz(whatever you will use in the deployed MCU) and use as the clock source
internal. In the case of the ATtiny13, it is 1.2 or 9.6 MHz. If you are dealing with a 2 K byte MCU, disable
millis()/micros(). And you need to set the right programming
port, of course.
- Upload the calibTarget sketch in the
extrasfolder to your target board, using either a separate ISP programmer or turning an Arduino board into such a programmer (Arduino as ISP).
- Upload the calibServer sketch to a UNO board (or similar).
- Open the Arduino monitor window.
- Press the reset button on the server board.
- Now, the line “Press any key to start calibration” will be shown in the monitor window. Press any key and send it to the server board by clicking the “send” field.
- Now the calibration takes place. As mentioned, the calibration is temperature dependent and the operating temperature of the target chip changes after powered on. For this reason, it is a good idea to repeat steps 5-7 a couple of times until the values stabilize.
The output of the calibration process could look as follows.
First, the factory-determined value of the OSCCAL register is systematically changed towards the direction of 100,000 ticks, and the closest value is then stored in EEPROM. Afterward, the correct value of the internal reference voltage is determined, which is stored in EEPROM as well. If the process is carried out on a chip with 2K bytes, the output is a bit less verbose in order to be able to fit everything into the 2K bytes flash. Furthermore, one needs to disable the millis/micros timekeeping in the
Tools menu for these MCUs. With the ATtiny13, which has only 1K byte flash, the output is even sparser (see below).
In this case, only the starting and final OSCCAL values are given and no calibration of the internal reference voltage is performed, because the latter is not possible on this chip.
Using the calibration values
Once, the chip has been calibrated, the values can be used in the
setup function of a sketch, by calling the
init method of the avrCalibrate library as follows.
void setup ()
init method retrieves the OSCCAL and internal reference voltage value from the EEPROM and sets the OSCCAL register and an internal calibration variable, respectively. However, this works only if the
EESAVE fuse has been set, which protects the EEPROM from clearing when the flash memory is reprogrammed. You can set this fuse either by using a fuse programmer or by selecting
EEPROM retained in the
Tools menu and then executing
If you plan to use the calibration values for only one chip, then instead of fiddling around with fuses, you can call the init method with the appropriate calibration values, which had been displayed during the calibration process. The first parameter is the OSCCAL value, and the second one is the internal reference voltage in millivolts, as in the next sketch.
void setup ()
If you do not want to change the OSCCAL register, use the constant
NOOSCCAL. If you do not want to set the reference voltage, then use the constant
The calibration process is no rocket science. However, you may be interested in what goes on behind the curtain in order to get an idea in how much you can trust the entire procedure.
How to calibrate the RC-oscillator
As mentioned above, the factory calibration of the RC oscillator is guaranteed to be within either ±3 % or ±10 %, depending on the MCU type. However, this applies to a fixed temperature and supply voltage. When you look at the variability caused by temperature and supply voltage, things can get worse (see application note AVR053):
As you can see, the clock frequency can actually vary (after calibration to 8 MHz at room temperature with 5 V) between 6.7 MHz (at 85° C with 2.7 V supply voltage) and 8.4 MHz (at -40° C with 5.5 V supply voltage). This is more than ±16 % with respect to the nominal frequency of 8 MHz.
In other words, the promise to tighten the accuracy to less than 1 % is only guaranteed for a fixed temperature and voltage. When one looks at the frequency curve based on the OSCCAL values (at fixed temperature and supply voltage) from the same application note, it seems that a better accuracy could be expected.
In the area around 8 MHz, each step is roughly 0.05 MHz (or 0.6 % of 8 MHz). In the area with the highest frequency, each step is around 0.1 MHz (or 1.2 % of the nominal frequency). This seems to suggest that one should be able to find a value with an error less than 0.6 %. However, things are not as straightforward. When measuring the clock frequency over a few minutes, I noted a variation of the clock frequency up to ±0.5 % for some chips. In other words, a worst-case error of 1.1 % seems unavoidable.
The general approach to calibrate the OSCCAL value is to use an accurate external frequency. In our case, a 10 Hz signal is generated on a UNO or similar board. In the cases I encountered, the accuracy was always better than 0.1 %. However, it may be worthwhile checking that before starting the calibration procedure. If the deviation is larger than 0.1 %, you can adjust the compile-time constant
The calibTarget sketch simply counts the number of clock ticks between two falling edges of the 10 Hz signal (divided by 8, if 8 MHz has been selected). That is, we want to find an OSCCAL value that results in a value as close to 100,000 as possible,
The standard Microchip calibration procedure, which requires you to install Atmel Studio and to use one of the Microchip AVR programmers, uses binary search over the possible OSCCAL values (see AVR053), which gives you an upper bound of 8 search steps plus 4 additional comparisons to account for what Microchip calls a pseudo-monotonic behavior of the OSCCAL value. This means that frequency does not strictly monotonically change with the OSCCAL value. However, every second step is guaranteed to be strictly higher or lower.
I decided to search linearly towards the optimal value starting at the factory calibration value, which should mean that usually 6 (when off by 3 %) and not more than 18 steps (when off by 10 %) are enough.
The final value is stored in EEPROM at
E2END. In addition, the byte at the address E2END-1 is set to zero when the calibration process is successful.
How to calibrate the internal reference voltage
Calibration of the internal reference voltage is much easier. However, again, it is dependent on temperature and supply voltage. An example graph for the ATmega328P is given below.
Calibration is performed by estimating the supply voltage using the nominal internal reference voltage of 1.1 V. By using the supplied TRUEMILLIVOLT value, one can then easily estimate the true internal reference value, which is stored in EEPROM (two bytes starting at
E2END-3. This can be later used to measure the supply voltage using the class method
Vcc::measure from the Vcc library. If you want, you can also turn any value measured by using
analogRead into a voltage value. Well, saying that the nominal internal reference voltage is 1.1 V is only partially true. There are a few older MCUs that have a different internal reference voltage (see