I'm working on a project that presently uses the standard 16MHz clock. The sketch they provided uses timer2 as a millisecond timer that drives a real-time clock. Obviously, this means that the system is running at 16MHZ continuously. The client wants to reduce long-term power consumption to be able to use a couple of AAA cells that would last for several months., so I'm thinking of changing things so that it goes to sleep when no activity is detected, then timer2 is clocked with a 32.768 KHz crystal, with the prescaler for a 1 sec interrupt time to update the clock registers, and at those 1 second intervals, check the status of the system by running the CPU using the internal 8MHz (or possibly 1MHz) RC clock.
What i snot clear to me is how I tell the compiler that the CPU clock is no longer 16MHz, so that the other functions (delays & whatnot) will be handled properly. I have googled & found references to setting F_CPU to different values, or changing "Arduino.h" to specify a different clock, but I cannot find, at least not on my computer, any instance of #define F_CPU 16000000 or any file named Arduino.h
So, how do I tell the compiler that my system is running at another clock frequency without screwing up the bootloader or anything else? I'm using Arduino 1.6.9 at the moment.
I would be tempted to use an external RTC chip, which can keep time at much lower standby power consumption than you can get if you're using the '328p to keep the time - you're not going to get good power usage if you can't put the chip to sleep, and I don't think you can keep a timer running if you do that...
Plus you get a battery backup.
Actually, running the processor at the maximum speed when it has something
to do may actually use less total power.
Remember, it is watts/hr that count, not just watts.
Of course, if you have delays in your code, it may be
desired to slow things down when not actually doing something.
Dwight
F_CPU is set by boards.txt
DrAzzy:
I would be tempted to use an external RTC chip, which can keep time at much lower standby power consumption than you can get if you're using the '328p to keep the time - you're not going to get good power usage if you can't put the chip to sleep, and I don't think you can keep a timer running if you do that...
This is why datasheets exist, so you don't have to guess, you can know.
The -P series of microcontrollers (like the Uno's ATmega328P) have more than the usual amount of sleep modes, including a Power-save mode that leave Timer2 running. If you have Timer2 clocked off a 32 kHz watch crystal the datasheet says the current draw in Power-save mode is 1 uA, which is very competitive with some of the RTC chips I spot checked on Mouser. The couple cheap Microchip ones were 1 uA, and the NXP one was a couple hundred nA. It's not worth fussing over nanoamps when you've got AAAs.
Bottom line, I wouldn't recommend using a separate RTC chip unless you're timing calendar events. If you're just wanting to keep time while sleeping, I'd just hook the watch crystal to Timer2.
KeithRB:
Plus you get a battery backup.
Not all RTC chips have that. It might not be necessary for Dave's application.
Couple additional things about OP's post. If you want to run the batteries flat down to the 328P's lowest operating voltage (1.8V), you can only go up to 4 MHz, not 8. That means you will need to leave the DIV8 fuse programmed and adjust the prescaler on powerup if you want to boost it to 4MHz. You will probably also want to adjust the BOD level, or turn it off completely.
This will definitely upset the bootloader, since it is compiled for a specific clock frequency. You have two options: recompile the bootloader, or leave it off. I recommend the second, unless the client specifically requires an Optibootloaded chip in the deliverable. I would hope you have enough skill to figure out how to load sketches onto an unbootloaded chip.
One other thing, I've actually been trying to make my own set of breadboard Arduino settings with various different CPU and fuse settings, but I've been unsuccessful. The official documentation suggests putting stuff in a hardware//avr folder in the sketch directory, but when I put just a boards.txt file the new "board" showed up in the IDE, but when I tried to compile for it it complained about a while bunch of stuff missing. But when I put the exact same entry in the IDE's boards.txt file, it didn't show up in the menu. So I'm stumped.
Since TOSC1/TOSC2 (the crystal pins for Timer2) are the same pins as XTAL1 and XTAL2 (the crystal pins for the system clock) you will want to select the 8 MHz RC clock option in the config fuses.
And like always, 10 freakin' seconds after I post about the damn thing, I manage to find my solution on Google, even though multiple searches 2 days ago couldn't find it.
In you sketch folder, make a folder called hardware. Inside that folder, make another folder with any name you want. I used my initials. Inside that folder, make another folder called avr. Inside that folder, make a boards.txt file and paste this in it:
maxlowpower.name=Ardiuno on Breadboard, Max Low Power
maxlowpower.upload.tool=avrdude
maxlowpower.upload.protocol=arduinoisp
maxlowpower.upload.maximum_size=32256
maxlowpower.upload.maximum_data_size=2048
maxlowpower.upload.speed=19200
maxlowpower.bootloader.tool=avrdude
maxlowpower.bootloader.low_fuses=62
maxlowpower.bootloader.high_fuses=DF
maxlowpower.bootloader.extended_fuses=FF
maxlowpower.bootloader.unlock_bits=0x3F
maxlowpower.bootloader.lock_bits=0x0F
maxlowpower.build.board=AVR_UNO
maxlowpower.build.core=arduino:arduino
maxlowpower.build.variant=arduino:standard
maxlowpower.build.mcu=atmega328p
maxlowpower.build.f_cpu=4000000L
##############################################################
Load up your IDE, and you'll see it way down at the bottom of the boards menu, with the name "Ardiuno on Breadboard, Max Low Power".
For the curious, it was the arduino: part that I needed to add to reference the official core and variant files.
OK, thanks for all the replies. I'll try to deal with the various suggestions below:
- I did think about a RTC chip, but the customer is very concerned about additional cost. The option of using timer2 as a RTC is very doable, and should result in lower power. However:
- The use of the 32.768KHz crystal means that the normal 16MHz crystal can't be used, so I have to use the internal RC clock for the various "housekeeping" functions. If I could run everything at 1 MHz, that woul dbe even better, and I think I might be able to do that, so I can use the /8 option.
- The customer is not married to the Arduino bootloader as the only programming source, so an AVRISPII or AVR Dragon should be a way to load the program.
@jiggy-ninja, you just answered a very important question for me, as I did just what you did, and couldn't figure out why my "new" board designation wasn't working!
OK, so it looks as if I can specify a custom board for this project, and use the internal RC clock. I'll make the necessary folder additions & see what happens.
Thanks again to all!
Well... I got the IDE to recognize the new board description. However, when I try to compile, I get this error message, which doesn't really help me much:
recipe.preproc.macros pattern is missing
I updated the board files, using board manager, but no luck. Selecting the "standard" Arduino/Genuino Uno board works fine, it is only the modified board file that causes this error. The only thing I changed in the file was the name prefix (from uno.xxxx to uno8.xxxx) and the f_cpu from 16000000 to 8000000
Any ideas?
You gain next to nothing in terms of power consumption by running at a reduced clock rate, because, with the exception of external signals, the majority of power us consumed when signals switch state, and nearly zero power when they are in a stable state. Most external signals will draw more power the longer they are active, which means minimizing their active time will save a lot of power. Because of this, in many real-world cases, running on a slower clock will consume more power than running on a faster clock. It is much simpler, and probably just as efficient, to simply run full speed whenever you're doing useful work. This means the work will get done much faster, and you'll spend more total time in sleep.
Regards,
Ray L.
@RayLivingston - Since the crystal pins will have a 32.768 KHz timebase crystal across them, I cannot run the system at 16MHz, I have to use something from the internal clock selection (8MHz or 1MHz).
I have to use something from the internal clock selection (8MHz or 1MHz)
Use the 8 MHz internal clock, and a 32768 Hz crystal on Timer2 to make an RTC. A neat trick is to calibrate the internal oscillator using the crystal.
The following code is the heart of a self-contained RTC remote RF datalogger, which demonstrates these techniques. It runs on a bare bones ATmega328 board designed by CrossRoads on this forum, powered by a Supercap and 2x2 cm2 solar cell.
//
// ATmega328p Solar/SuperCap powered Real Time Clock and voltage monitor.
// VirtualWire data transmission
//
// sjames dot remington / gmail
//
// This code incorporates an RTC formed by Timer2 and a 32768 xtal on OSC pins
// The internal 8MHz RC oscillator is calibrated by comparison with the 32768 Hz standard
//
// Mini-uino board from http://crossroadsfencing.com/BobuinoRev17/index.html
// fuses: 8 MHz internal RC clock, LP xtal (32768 Hz) on OSC pins
//
// Binary BCD HH:MM:SS time version, RTC maintained in global array RTC_buf[]
// Data output on PORTD,3 (VirtualWire TX)
// Power output on PORTD,2 (TX module)
#include <avr/sleep.h>
#include <avr/power.h>
#include <util/delay.h>
#include <VirtualWire.h>
// Global variables for RTC, time and day
// set the time here, if you like
volatile unsigned char RTC_buf[]={
0,0,0,0,0,0}; //binary BCD hh:mm:ss (24 hour clock)
volatile unsigned int dayno=0; //days since startup
char buf[40]; //sprintf buffer, about 30 chars actually used
int VW_PWR=2; //433 MHz TX power
int VW_TX=3; //Virtual Wire TX
/*
** approximate 1ms delays (can't use Arduino delay - timer disabled)
*/
void delay1ms(unsigned int ms)
{
while (ms--) _delay_ms(1);
}
// everything happens in setup()
void setup()
{
char t,zero=0;
unsigned int batt,h,m,s;
pinMode(VW_PWR,OUTPUT); //transmitter power
digitalWrite(VW_PWR,LOW); // TX off
pinMode(VW_TX,OUTPUT);
digitalWrite(VW_TX,LOW);
//PRR Power Reduction Register (set PRADC after ADCSRA=0)
//Bit 7 - PRTWI: Power Reduction TWI
//Bit 6 - PRTIM2: Power Reduction Timer/Counter2
//Bit 5 - PRTIM0: Power Reduction Timer/Counter0
//Bit 3 - PRTIM1: Power Reduction Timer/Counter1
//Bit 2 - PRSPI: Power Reduction Serial Peripheral Interface
//Bit 1 - PRUSART0: Power Reduction USART0
//Bit 0 - PRADC: Power Reduction ADC
ADCSRA = 0; //disable ADC
PRR |= (1<<PRTWI)|(1<<PRSPI)|(1<<PRTIM0)|(1<<PRUSART0)|(1<<PRADC); //need Timers 1 and 2
OSCCAL_calibrate(); //calibrate the RC osc for accurate timing using the 32 kHz crystal
timer2_init(); //setup timer2 interrupts for RTC
vw_set_tx_pin(VW_TX); //initialize VirtualWire TX
vw_setup(2000); // and bits per sec
sei(); //enable interrupts
t=255; //initialize loop timer
while(1) {
// wake up on Timer2 overflow (1/sec)
// output day, time and cpu voltage via VirtualWire every 10 minutes
if(t != RTC_buf[3]) {
t=RTC_buf[3];
s=10*RTC_buf[4]+RTC_buf[5]; //format time (seconds)
m=10*RTC_buf[2]+RTC_buf[3]; //minutes
h=10*RTC_buf[0]+RTC_buf[1]; //hours
digitalWrite(VW_PWR,HIGH); //power on TX
PRR &= ~(1<<PRADC); //turn ADC back on
ADCSRA = (1<<ADEN); //enable and
ADCSRA |= (1<<ADPS0) | (1<<ADPS1) | (1<<ADPS2); // set prescaler to 128
delay1ms(10); //let ADC and TX stabilize
batt = readVcc(); //get Vcc voltage
// format days, time and battery voltage
sprintf(buf,"%0u,%0d,%0d,%0d,%0u%c",dayno,h,m,s,batt,zero); //max ~16 characters
vw_send((uint8_t *)buf, strlen(buf)); //send it
vw_wait_tx(); // Wait until message is sent
digitalWrite(VW_TX,LOW); //power off TX
digitalWrite(VW_PWR,LOW);
ADCSRA = 0; //ADC off
PRR |= (1<<PRADC);
} //end if (t)
//go back to sleep
set_sleep_mode(SLEEP_MODE_PWR_SAVE);
sleep_enable();
cli(); //time critical steps follow
MCUCR = (1<<BODS) | (1<<BODSE); // turn on brown-out enable select
MCUCR = (1<<BODS); //Brown out off. This must be done within 4 clock cycles of above
sei();
sleep_cpu();
} //end while(1)
}
void loop() {
} //do everything in setup()
//******************************************************************
// Timer2 Interrupt Service
// 32 kKz / 256 = 1 Hz with prescaler 128
// provides binary BCD Real Time Clock
// no check for illegal values of RTC_buffer upon startup!
ISR (TIMER2_OVF_vect) {
// RTC function
RTC_buf[5]++; // increment second
if (RTC_buf[5] > 9)
{
RTC_buf[5]=0; // increment ten seconds
RTC_buf[4]++;
if ( RTC_buf[4] > 5)
{
RTC_buf[4]=0;
RTC_buf[3]++; // increment minutes
if (RTC_buf[3] > 9)
{
RTC_buf[3]=0;
RTC_buf[2]++; // increment ten minutes
if (RTC_buf[2] > 5)
{
RTC_buf[2]=0;
RTC_buf[1]++; // increment hours
char b = RTC_buf[0]; // tens of hours, handle rollover at 19 or 23
if ( ((b < 2) && (RTC_buf[1] > 9)) || ((b==2) && (RTC_buf[1] > 3)) )
{
RTC_buf[1]=0;
RTC_buf[0]++; // increment ten hours and day number, if midnight rollover
if (RTC_buf[0] > 2) {
RTC_buf[0]=0;
dayno++; //one day at a time...
}
}
}
}
}
}
}
/*
// initialize Timer2 as asynchronous 32768 Hz timing source
*/
void timer2_init(void) {
TCCR2B = 0; //stop Timer 2
TIMSK2 = 0; // disable Timer 2 interrupts
ASSR = (1<<AS2); // select asynchronous operation of Timer2
TCNT2 = 0; // clear Timer 2 counter
TCCR2A = 0; //normal count up mode, no port output
TCCR2B = (1<<CS22) | (1<<CS20); // select prescaler 128 => 1 sec between each overflow
while (ASSR & ((1<<TCN2UB)|(1<<TCR2BUB))); // wait for TCN2UB and TCR2BUB to clear
TIFR2 = 0xFF; // clear all interrupt flags
TIMSK2 = (1<<TOIE2); // enable Timer2 overflow interrupt
}
// Read 1.1V reference against AVcc
// return battery voltage in millivolts
// must be individually calibrated for each CPU
unsigned int readVcc(void) {
unsigned int result;
// set the reference to Vcc and the measurement to the internal 1.1V reference
ADMUX = (1<<REFS0) | (1<<MUX3) | (1<<MUX2) | (1<<MUX1);
delay1ms(2); // Wait for Vref to settle
ADCSRA |= (1<<ADSC); // Start conversion
while (bit_is_set(ADCSRA,ADSC)); // wait until done
result = ADC;
// second time is a charm
ADCSRA |= (1<<ADSC); // Start conversion
while (bit_is_set(ADCSRA,ADSC)); // wait until done
result = ADC;
// calibrated for my Miniduino
result = 1195700UL / (unsigned long)result; //1126400 = 1.1*1024*1000
return result; // Vcc in millivolts
}
//
// Calibrate the internal OSCCAL byte, using the external 32768 Hz crystal as reference.
//
void OSCCAL_calibrate(void) //This version specific to ATmegaXX8
{
unsigned char calibrate = 0; //not calibrated;
unsigned int temp;
TIMSK1 = 0; //disable Timer1,2 interrupts
TIMSK2 = 0;
ASSR = (1<<AS2); //select asynchronous operation of timer2 (32,768kHz)
OCR2A = 200; // set timer2 compare value
TCCR1A = 0;
TCCR1B = (1<<CS11); // start timer1 with prescaler 8
TCCR2B = (1<<CS20); // start timer2 with no prescaling (ATmega169 use TCCR2A!)
while (ASSR & ((1<<TCN2UB)|(1<<TCR2BUB))); //wait for TCN2UB and TCR2BUB to be cleared
delay1ms(2000); //allow xtal osc to stabilize
while(!calibrate)
{
cli(); // disable global interrupt
TIFR1 = 0xFF; // clear Timer1 flags
TIFR2 = 0xFF; // clear Timer2 flags
TCNT1 = 0; // clear timer1 counter
TCNT2 = 0; // clear timer2 counter
TCCR1B = (1<<CS11); // start timer1 (again)
while ( !(TIFR2&(1<<OCF2A)) ); // wait for timer2 compareflag
TCCR1B = 0; // stop timer1
sei(); // reenable global interrupts
if ( TIFR1&(1<<TOV1) ) temp = 0xFFFF; //overflow, load max
else temp = TCNT1;
//expect about (1e6/32768)*201 = 6134 ticks
if ( (temp >= 6125) && (temp <= 6145) ) calibrate=1;
if (temp > 6145) OSCCAL--; //RC oscillator runs too fast, decrease OSCCAL
if (temp < 6125) OSCCAL++; //RC oscillator runs too slow, increase OSCCAL
} //end while(!calibrate)
} //return
davetelling:
Well... I got the IDE to recognize the new board description. However, when I try to compile, I get this error message, which doesn't really help me much:
recipe.preproc.macros pattern is missing
I updated the board files, using board manager, but no luck. Selecting the "standard" Arduino/Genuino Uno board works fine, it is only the modified board file that causes this error. The only thing I changed in the file was the name prefix (from uno.xxxx to uno8.xxxx) and the f_cpu from 16000000 to 8000000
Any ideas?
Compile or upload? Change the avrdude entries to arduino:avrdude. I also made it to upload by default with an ArduinoISP. I tried a test compile with it, but I couldn't try a test upload right away since I didn't have my board available so there was still some bugs.
Try this:
breadboard.name=Standalone ATmega328P (low power)
breadboard.upload.tool=arduino:avrdude
breadboard.upload.protocol=stk500v1
breadboard.upload.maximum_size=32768
breadboard.upload.maximum_data_size=2048
breadboard.upload.speed=19200
breadboard.bootloader.tool=arduino:avrdude
breadboard.bootloader.low_fuses=0x62
breadboard.bootloader.high_fuses=0xD9
breadboard.bootloader.extended_fuses=0xFF
breadboard.bootloader.unlock_bits=0x3F
breadboard.bootloader.lock_bits=0x0F
breadboard.build.board=AVR_UNO
breadboard.build.core=arduino:arduino
breadboard.build.variant=arduino:standard
breadboard.build.mcu=atmega328p
breadboard.build.f_cpu=4000000UL
Use the "burn bootloader" command and it should set up the chip's fuses to use the Internal Oscillator with the CKDIV8 fuse programmed. F_CPU is 4 MHz, the highest you can have at the lowest operating voltage, and will require changing the prescaler in setup to keep timing correct. Or you can change F_CPU to 1 MHz and use it like that.
Before you burn the fuses though, make sure for yourself that I have not made a mistake in setting the value of the RSTDSBL and SPIEN values.
J-N, I was just trying verify/compile. I also found that there are at least two "platforms.txt" files that have an effect on the compile process, depending upon which one is placed where, so at this point, I may just move over to the Atmel Studio 7 and continue from there. I'll have to see if the customer has an ISP header on their board.
Then, I just need to set up the clock dividers to make the delay and timer functions work. (I hope!)
@J-N , OK, I removed the "platform.txt" files, tried your latest code & compiled & loaded. I am running the "blink" sketch, as it is pretty straightforward, and the code runs, but the blink frequency seems to be 1 second still, so I guess I need to change the clock prescaler now & see what happens.
Thanks much!
davetelling:
@J-N , OK, I removed the "platform.txt" files, tried your latest code & compiled & loaded. I am running the "blink" sketch, as it is pretty straightforward, and the code runs, but the blink frequency seems to be 1 second still, so I guess I need to change the clock prescaler now & see what happens.
Thanks much!
You need to use the burn bootloader command with that board selected to write the fuse settings. The bootloader can't change fuses so you need an ICSP programmer to do that. The ArduinoISP sketch can make an Arduino board act like an ICSP board for another chip, use stk500v1 protocol as the avrdude option. Then, if you upload a default Blink sketch with that board selected it should be 4x slower.
@Jiggy-Ninja OK, that last bit of code you provided almost worked, it compiled, but when I went to burn the bootloader, I got a whole bunch of error message about avrdude not being in sync:
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 1 of 10: not in sync: resp=0x03
So, I'm back to ?????
How's your hardware hooked up? You can't just plug in an Arduino board and burn the bootloader on that board, you need to use an ICSP. An Arduino board running the ArduinoISP example can do this, and you can program another chip with it.
@Jiggy-Ninja, OK, I see that we were cross-posting!
So, Iam a bit confused - I have an AVRISPmkII that I can hook up to my Uno board, but it brings the question as to why have fuse settings in the board file?
At any rate - it sounds as if I need to use my AVRISPmkII to burn the bootloader, then try the "blink"sketch, using the normal Arduino upload process, is this correct?
One major issue is that, since I have the Atmel Studio installed, it also installs the Jungo driver, which isn't compatible with the AVRISPmkII in teh Arduino environment. I tried installing the generic libusb driver, but my Windows 10 nanny won't allow me to change the driver. Grrrrrr!
Is this something with which you are familiar? I found a post online here: http://www.arduinoos.com/2015/08/connecting-avrisp-to-arduino-part-4/
that almost worked, but if I select the 64 bit option, W10 says it can't run on my system. If I select the 32 bit option, it says errors were made in the installation, and it won't run.
I suppose the only option would be to remove the AVRISPmkII from the device manager & try again.