Improved support for internal-clock, single-chip Arduino

The ATmega328p has a seemingly little-known feature: it has its own internal clock. Apparently this clock can run at either 8MHz (not very accurate though, often not good enough to run serial)*, or at 128KHz, measured at about ~117KHz on my digital oscilloscope. This is quite the useful little tool from what I've seen so far...

Many Arduino applications don't require high speed processing. Many do, granted, but some of us just have stupid little "make something happen when something else happens" applications that don't need high speed responses and sequences. And it'd be cool to run it on its own board without trying to track down two of those impossible-to-find 22pf capacitors! (Seriously, I have not yet been able to find one without special-ordering it online or something).

Last night I took the plunge with ArduinoISP and reprogrammed one of my 328p's to use its 128KHz internal clock, and a modified Optiboot bootloader. WOW, things changed. With some tweaks I was able to upload the "Blink" sketch directly from the Arduino IDE to the breadboarded ATmega chip using its serial bootloader, no external components whatsoever (except the LEDs and power support wiring I added, which aren't really supporting ;)). It blinked. All was well!

... Well, except the fact that... it, eh... blinked very, very, very slowly. Like, "on", 15 seconds, "off", 15 seconds, etc. I could imagine why... I doubt Wiring has calculations and exceptions to set the timer values properly. Sure enough, I dug into it and found that it was set up to calculate 16MHz and 8MHz, that's it. It always uses a prescaler of 64, which almost never overflows at 128KHz (125 times slower than the standard 16MHz), so the calculations it does during compilation just create goofy values.

After a number of "#if F_CPU == 128000" conditionals, I was able to compile the exact same sketch and slowly work my way through the prescaler / comp-match combinations and settled on one perfect trade-off: 8ms resolution timer (with 0.0ms fractional; 128KHz works out pretty well with a 256-value timer overflow) and it now times and delay()'s perfectly on the dot. Blink flashes once a second and Serial.println(millis(),DEC) indicates almost perfect 1000ms between iterations (about 1,012 actually, probably due to the delay it actually takes to write the serial command at 4800 baud).

The analog sampler is slowed to 64KHz instead of 125KHz in the 16MHz version (a rough leap of faith calculation, the lowest prescaler; I'm not sure if this effects execution time or steals clock cycles though), and the Timer 0, 1, and 2 prescalers are set to 8 instead of 64 to compensate for the much slower clock speed. To get the same timings that the Arduino core seems to expect, these would have to be set more to the tune of "1" - that is, getting nothing done but handling interrupt routines ;)

I think it's a pretty viable option for anyone looking to whip up a quick-and-dirty, ultra-low-power Arduino project without the external components... buy an ATmega88/168/328 chip, slap it in a spare Arduino for programming with another Arduino (with ArduinoISP), set the fuses to enable the internal clock, upload the slow bootloader, and voila... a completely standalone Arduino-compatible chip, perfect for low-speed operations.

That said, I just wanted to get some feedback on this idea first. I think I'm not far off from getting the basic Arduino libraries "ported" to 128KHz (some of these I think would be better with 128KHz_ variants instead of "#if"'ing the thing to death). For me, it'd be useful since I have an ATmega48v that came with a little LED-display DIY capacitance meter, that would be perfect to use as an on-breadboard ArduinoISP, or as my keychain-remote-controlled bedroom door lock controller. I got REALLY close with that one... sleep mode worked, wake-on-interrupt worked, lighting the LED on activation worked, detecting the button, etc., but the servo control just didn't do anything. Turns out the Servo library made similar assumptions that didn't work with 128KHz, so that fell apart but I plan on fixing that up when I get back to it today.

Thoughts?

  • - edit: Ah, evidently we can use the CLKDIV8 flag to get 8MHz/8 = 1MHz clock speed, pretty stable and able to be used with serial as well with many fewer issues than 128MHz.

Using the 128 KHz oscillator works best for a tiny niche: Applications that cannot tolerate the processor sleeping and need to use as little power as possible.

For reducing part count, the internal 1 MH / 8 MHz oscillator works well. At 8 MHz, no software changes are necessary. Tuning it is not required but the tolerance is ±10% from the factory. Tuning is fairly easily to perform, brings the tolerance to ±1%, and requires no special hardware…
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1287558192

For reducing power consumption, the 1 MH / 8 MHz oscillator works well when coupled with putting the processor to sleep…
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1273507808

I’m impressed with what you have done!

Very interesting indeed! In fact, I had first tried the internal 8MHz mode, but it just crapped itself… I don’t know if it was a bootloader issue, or that I had the indicator LED plugged in backwards (I had a good handful of trouble keeping the pin assignment straight, then later found my red indicator LED had two equal-length leads - d’oh.), but it just didn’t want to play nice. And with the huge gap between 128KHz and 8MHz, I thought the 8MHz mode was required to be “tuned” by some sort of proper AVR programming rig, from what I’d gathered.

I’ll definitely have to play around with the 8MHz clock (and tuning), because I really think this could be quite valuable! Both to me and to others that stumble upon the passing thought of “just using the chip”… because really, if you don’t need to update the code, all you need is the chip in this setup (provided timing accuracy isn’t needed).

I’ve always been amazed at how fast the ATmega chips run programs at 16MHz… to the point of being downright excessive, sometimes! Case in point, I wrote an LED animation using a block of “if (x <= y && x > z) abc… else if (…)” statements of analogWrite values within a “while” loop, iterating through about 1,400 states during the 1.5-second animation… and when I first tested it after spending about a half-hour manually calculating the various state-values through the course of the program, I switched it on and… blinknothing. Wuh? Was that the startup blip? Or was that all 1,400 states cycled through in the blink of an eye? LOL. It’s really impressive how fast the thing is, almost to a flaw, even. It’d be interesting to slow things down to 128KHz and watch the bits fly in real-time :wink:

Definitely going to check out the 8MHz tuning thing when I get back to the toys!

Very impressive! I always want to try these but fail to find time and confidence to get started. Oh, if you need some 22pF caps and 16MHz crystals, I'll stuff them in the envelop. I have an overflowing amount of 22pF caps. Last time I bought 1,000 of them ;)

or that I had the indicator LED plugged in backwards (I had a good handful of trouble keeping the pin assignment straight, then later found my red indicator LED had two equal-length leads - d'oh.)

Yeah, I have trouble remembering how to wire LEDs so I soldered resistors to the positive side of a bunch of LEDs and formed the pair into a lowercase-h. Slapping an LED into a breadboard circuit is now fast and trouble-free! One of the best ideas I've ever borrowed!

I thought the 8MHz mode was required to be "tuned" by some sort of proper AVR programming rig, from what I'd gathered.

Nope. In fact, it is meant to be "tuned" over a fairly wide range of frequencies so the user can get just the right frequency for their application.

I'll definitely have to play around with the 8MHz clock (and tuning), because I really think this could be quite valuable!

These may help...

Search for "Minimal Circuit (Eliminating the External Clock)"... http://arduino.cc/en/Tutorial/ArduinoToBreadboard

Search for "Circuit (targeting an AVR on a breadboard)"... http://arduino.cc/en/Tutorial/ArduinoISP

I love you guys so much more than the Android community. No homo. =) Seriously, I’ve done exactly the same thing on that side, posing real-world suggestions and solutions, and it seems like every time someone goes out of their way to just make me feel like crap. Here, it’s the exact opposite… you guys go out of your way to help each other out! <3 Arduino. Sure as hell isn’t helping my recent Arduino addiction, though… sheesh, all I keep thinking about is going home (from work) and playing with that stuff again. xD

@liudr: Amusingly, I bought a pack of 5x 16MHz crystals on eBay when I was building my S3V3 (which is now permanently installed in my turntable), and only then realized that I couldn’t nearest-round-swap them for 100pF (“102”? Or is that 1000?) caps, which were the lowest I had. Spent hours debugging that thing, going over every trace with solder and an ohmmeter, swapping components, on and on. If it just had an internal oscillator fallback mode, I’d’ve been able to pin it down to those darn capacitors… but now I have 4 of 'em in a bag and no capacitors to go with them! So yeah, I could really use a few - THANKS!! :grin:

As to the minimal Arduino tutorial, oh man oh man, I’d seen that article about 10 times while searching for the “way to actually get it into that mode”… problem is, it doesn’t describe the fundamental chicken-and-egg problem of programming a clock-less ATmega chip that was already fused for an external oscillator. It just sits there on the SPI pins looking dumb and timing out avrdude… until it gets a clock.

Now, maybe that’s something that can be added to the ArduinoISP sketch… switching on the external “clock echo” on digital 8 instead of an indicator LED. Then, that can be attached to a newly-breadboarded ATmega chip on XTAL1, and from what I understand (although I wasn’t able to make it work with the 500hz test signal from my oscilloscope), it should “bootstrap” the chip into SPI mode so the fuses can be changed. Maybe worth a try…

problem is, it doesn't describe the fundamental chicken-and-egg problem of programming a clock-less ATmega chip that was already fused for an external oscillator. It just sits there on the SPI pins looking dumb and timing out avrdude... until it gets a clock.

I've been burned by that as well. I truly don't understand why the problem exists. During programming, the target processor is held in reset so the CPU does not run. The programmer provides the SPI clock. Why does the processor need the external crystal? What does it do with it?

Oh well. It is what it is.

switching on the external "clock echo" on digital 8 instead of an indicator LED. Then, that can be attached to a newly-breadboarded ATmega chip on XTAL1

Wouldn't that cause a conflict with the crystal driver?

Not sure… I was just suggesting that based on this:
"Especially if it’s a “modern” AVR with a CLKOUT fuse and pin - set the fuse and suddenly you have a 1MHz square wave oscillator.

For older models like mega8 that don’t have this facility then just take a second chip and put this in it…
code
now all the IO ins on that chip are square wave oscillators any of which can be connected to XTAL1 on the “locked” chip to perform ISP and undo the damage."

That’s what I was thinking of… don’t know much about crystals myself (always been a dark art to me), but it sounds like it should work :wink:

FalconFour: So yeah, I could really use a few - THANKS!! :grin:

I'll add 10 22pf caps to your package :) Do you need 0.1uF too? I have like another 1,000 of them ]:D

Wouldn’t that cause a conflict with the crystal driver? Not sure… I was just suggesting that based on this:

Based on the AVR Freaks post and this “Pins XTAL1 and XTAL2 are input and output, respectively,…” from the datasheet that XTAL1 can safely be fed a clock signal. I think I’ll add a clock output to the Arduino ISP.

Augh. I'm now finding how much a mistake putting my ATmega328p into 128KHz mode was. Spent the entire evening trying to get it out of there, or rather, trying to find SOME way to calibrate my ATmega48 at 8MHz internal clock (for lack of an 8MHz crystal this time), so it can be low enough to run the SCK at CPUCLK/128 (62.5 KHz) and maybe the 128MHz chip can understand it. Right now all I'm getting is the dreaded "Invalid signature: 0x000000" error, no matter what.

I even used my patch shield combined with my USB BoArduino to create a 3-way flashing station between my 3 ATmega chips, now all in 3 different clock configurations. The Duemilanove (328) is the only one still working at proper spec... the JYETech capacitance meter (after obtaining the hex code from the manufacturer via email - woohoo!) with its 48V is my clock test chip with the CLKO flag set in the fuse, being monitored by my digital oscilloscope (frequency counter mode). It shows 8.338 MHz at the moment, and the chip just will not respond to serial. I managed to fit ArduinoISP exactly into its 4.0KB of Flash (at 97% usage) and 512 bytes of RAM (91%) by cutting out the LED functions (pulsing heartbeat LED, etc) and EEPROM functions.

All 3 chips can be accessed by flipping a few cables around - I have a ribbon cable set up from the Patch Shield pins to 6-pin ICSP format, that plugs into the BoArduino. Then I have a raw jumper layout set up on the breadboard for flashing free-standing chips from either the Patch Shield or the BoArduino (just plug the two 8-pin patch panel headers into each other).

Sadly, I haven't even gotten past setting up the 8MHz clock on the 48, because the TinyTuner code just refuses to function! I've gone over the code in excruciating detail, and it just won't work... I found that it gets past the first 2-LED-flash... ehh...

OK, I keep alt-tabbing back to the code every time I try to get a word out here. It gets past setup() (I flash the LED twice at 500ms, but it stays on despite the last statement being "low" - weird). It makes it past the first calculation and adjustment (LED flashes 100ms and again stays "on" despite being set "low" - I triple checked it, it's an output, and the LED is wired right, but still saying high. VERY strange.

I think it's having a low-memory problem... the compilers insist it's only using 10 bytes RAM (yeah... right), but the structs insist otherwise. Anyway, after it finishes the first byte (after I finally managed to get it to read, after about 2...3 hours of debugging to find that I used the PINB address (reading the clock line on PB0!), instead of PIND... oops. Oscilloscope is still sitting here reading ~8,275,000Hz, and after the first byte it goes down to ~8,200,000, but then the next byte resets it back to ~8,275,000 again. Ick.

Think I'll try using the cap-meter's 12MHz oscillator piggy-backed on the bare setup and see how that goes...

Thought I’d post the night’s work… here’s how to recover from a bad external clock source flub.

// this sketch turns the Arduino into a AVRISP
// using the following pins:
// 10: slave reset
// 11: MOSI
// 12: MISO
// 13: SCK

// for a clock-bricked ATMegaX8 chip, attach the following:
// 9: oscillator, attach to pin 10 of the ATMega chip (NOT digital pin 10, there is no pin for it)
//     - best to do this on a breadboard with this guide: http://arduino.cc/en/Tutorial/ArduinoToBreadboard

// Put an LED (with resistor) on the following pins:
// 6: Heartbeat - flashes to show the programmer is running
// 8: Error - Lights up if something goes wrong (use red if that makes sense)
// 7: Programming - In communication with the slave
//
// June 2011 by Matt Falcon
// - Provided an 8MHz clock to recover a bricked chip, all functions still perform the same, since the oscillation is all hardware-based in Timer 1.
//

In ArduinoISP, replace setup() with this:

void setup() {
  unsigned char sreg;
  Serial.begin(19200);
  pinMode(LED_HB, OUTPUT);
  pulse(LED_HB, 2);
  pinMode(LED_ERR, OUTPUT);
  pulse(LED_ERR, 2);
  pinMode(LED_PMODE, OUTPUT);
  pulse(LED_PMODE,2);
  pinMode(9,OUTPUT); // Oscillator output needs to be set for timer to work
  digitalWrite(9,LOW); // Also needs to be set low for waveform generator (digitalWrite ^ waveform = pin; dW(1) + wgm(1) = pin(0); dW(0) + wgm(0) = 0; dW(0) + wgm(1) = 1)
  sreg = SREG; // save CPU registers just in case...
  cli();
  TCCR1A = _BV(COM1A0); // toggle OC1A on Compare Match
  TCCR1B = _BV(WGM12) | _BV(CS10); // no prescaling, use 16mhz clock = 8mhz output (yay!) but very weak due to high power drivers in ATMega... YMMV.
  OCR1A = 0; // very, very tight loop, but this is hardware. VERY important that no analogWrites are done in this program as they'll likely smother timer1 and hang the chip.
  TIMSK1 = 0; // disable all interrupts on timer1
  sei();
  SREG = sreg; // restore registers
}

… heartBeat() with this - we need to get rid of the analogWrite:

// this provides a heartbeat on pin 6, so you can tell the software is running.
uint8_t hbval=128;
int8_t hbdelta=8;
void heartbeat() {
  if (hbval > 192) hbdelta = -hbdelta;
  if (hbval < 32) hbdelta = -hbdelta;
  hbval += hbdelta;
  digitalWrite(LED_HB, hbval >> 7);
  delay(40);
}

… and be sure we free up pin 9 (triple-check that nothing at all is touching pin 9 except in setup()!):

#define LED_HB 6
#define LED_ERR 8
#define LED_PMODE 7

Bam… 8MHz external oscillator. And a few fixed chips. :wink: