Arduino Forum

Using Arduino => Microcontrollers => Topic started by: pito on May 18, 2011, 01:05 am

Title: Running 328p&1284p with internal oscillator (8.000MHz tuning)
Post by: pito on May 18, 2011, 01:05 am
as somebody may try to use those chips with internal 8MHz oscillator (no external crystal needed then and 2 additional pins free) this is a small intro into the topic - maybe useful for somebody (disclaimer - only for advanced users, do it at your own risk, no responsibility for any direct or indirect damages or losses!!   ]:) ). The burning was done with ICSP.

This is basically a chiken-egg situation as the internal frequency is not know precisely, so your bootloader may not work even you compiled and set everything properly!

Why? The reason is Atmel is "calibrating" the internal oscilator at 3.0V(!!) Vcc and at temperature=25C (see the datasheet). Moreover, the internal oscillator depends on the voltage and temperature, USART's baudrate depends on internal oscillator and Arduino likes 8.000MHz..

I needed to run an atmega328p and atmega1284p with the internal 8MHz oscillator (not really, though). Therefore I compiled and burned the bootlaoder for 8MHz internal oscillator into 328p (@3.3V) and it worked. But not with 1284p (@5V).

So the first step is to measure the internal oscillator frequency. This is easy as the output of the internal oscillator can be passed to the pin PB1 with 1284p (the pin PB0 at 328p). You have to burn the new fuses set to internal 8MHz RC oscillator, its clock output to the respective pin, not divided by 8.

Then you must possess a frequency meter, connect it to the pin and ground and measure the frequency. Do it at the temperature typical for the environment you plan to operate the chip in, and at the voltage you will run the chip (!!).

I measured 8080kHz at 3.3V for my 328p, and 8400kHz at 5V for my 1284p (your frequencies may vary significantly!).

The next step is to compile the bootlader for above frequencies (set above measured frequencies in the Makefile and in boards.txt in order to compile and run the stuff), and run the arduino. It will work, however you will be off the recommended 8.000MHz for Arduino. Now we want to run the stuff at 8.000MHz. We need to recalibrate manually.

So I loaded the command line interpreter&shell for Arduino - Bitlash (the latest), the Bitlashdemo into the sketch and uploaded to Arduino. Then you may write at the promt following into the command line (e.g. via a terminal "connected" to Arduino)- like this:

> print inb(0x66)

You will get the Atmel's factory OSCCAL value, for example "144". This is the value which the MCU reads from a specific location, not accessible to the user! This value has been loaded by the MCU from this unaccessible location into the OSCCAL register as the first operation after the MCU's reset. So in order to set a new value you have to put the new value into OSCCAL (address 0x66) at the _begining_ of the bootloader code (MCU will run it immediately after an reset) in order to set everything to 8.000MHz.

What value?? You cannot experiment with it in Arduino sketch easily (without writing a new sketch from scratch with tackling the OSCCAL) as you may loose the UART connectivity at certain point- as the new OSCCAL may set the clock to a value off the baudrate setting.

You can fill the OSCCAL with a XX value anytime, the MCU clock frequency will change immediately.

So you may use Bitlash again to find the value for 8.000MHz:

> outb(0x66,XX); delay(10000); outb(0x66,144)

Do it in one line as you may loose connectivity after the first command when done command by command, please!

Above command line code will set the OSCCAL to XX (of course you must put there a number from  0-255, I will not use 0 and 255 as I do not like such extremes (;-)) and then you have e.g. 10seconds for measuring the new clock frequency at PB1 or PB0. The Bitlash itself then returns the OSCCAL value back to the number set by Atmel. And you will not loose the USART connectivity as you run it as a "script". You have not changed the fuses so far so you can see the frequency, of course.

My 328p gave me 5.15MHz at XX=1 and 15.9MHz at XX=254 !!

So by repeating this process (MSA - manual successive approximation) you can find 8.000MHz. The  frequency is not a linear function of XX - there is an overlap! You may see a jitter of +/- 10Khz, so do your best. You have to remember the number XX, best practise is to write it down on a piece of paper.

Then you have to add the OSCCAL value at the beginning of the bootloader code, I did it e.g.:

* main program starts here */
int main(void)
   uint8_t ch,ch2;
   uint16_t w;
   #ifdef OSC_CAL   
   // @@@@@@@@@@@ OSCCAL calibration for 8.000MHz internal osc, Pito 29/4/2011


and in the Makefile I added following switch:

atmega328_uno8Mint3V3: CFLAGS += '-DMAX_TIME_COUNT=F_CPU>>4' '-DOSC_CAL=142' '-DNUM_LED_FLASHES=2' -DBAUD_RATE=38400

As you can see I needed 142 in '-DOSC_CAL=142' (Atmel's setting was 144). The difference was much bigger with 1284p, but I lost the sticker with the XX unfortunately..

So.. the next step is to recompile the bootlader again with the new OSCCAL value and with frequency 8000000, and to edit the boards.txt to 8000000 as well. Of course you may set the fuses to No Clock output at PB1 or PB0, when required.

After this excercise you get a 328p calibrated manually to Arduino's 8.000MHz at specific Voltage and at specific Temperature.

BTW, you can measure the voltage and the temperature directly on the chip, thus you may create an Arduino function, which will be setting the proper OSCCAL value for 8.000MHz, based on the actual voltage and chip temperature measured.

Good luck!

PS: there is an application note on an automatic calibration so you may try it as well.
Title: Re: Running at328p, 1284p on internal oscillator (8.0000MHz)
Post by: billroy on May 18, 2011, 03:02 am
Great use of Bitlash to illustrate your point, Pito.  Thanks for sharing.


Title: Re: Running at328p, 1284p on internal oscillator (8.0000MHz)
Post by: pito on May 18, 2011, 04:17 am
..and maybe for people who do not have access to a terminal with a command line history buffer:

> function fq {outb(0x66,x); delay(10000); outb(0x66,144)}
> x = 133
> fq
> x = 130
> fq
and so on. However not tested this way.P.