328P at 1 MHz

Apologies if this isn't the correct sub-forum.

I am interfacing an ATmega328P (standalone, not on the Arduino board, though I am programming it with the Arduino IDE) with Adafruit's nRF8001 Bluetooth breakout board. I am using Adafruit's code for Arduino, which uses SPI and a few extra pins to setup a UART interface.

Now, everything works perfectly fine at 16 MHz (with external crystal) and at 8 MHz using the "Atmega on a breadboard" code which you can find in the Arduino online tutorials. So next, I recompiled the bootloader code and changed the fuse settings in boards.txt and the makefile so that it would run at 1 MHz. I use "upload using programmer" to load the code which I think overwrites the bootloader anyway, but that's fine... I really just care about the fuse settings being correct and Arduino getting the right timing for the delay() and millis() functions without me having to think about it. When I put together code that does something simple like blink an LED, it works fine at 1 MHz with my new settings. Timing is perfect and everything. But when I try to interface it with the nRF8001 using the same code as before, the code just doesn't execute! Then, if I set CLKPR in my setup( ) function to use a clock divider of 16 instead of 8, making the system clock run at 500 kHz, everything runs fine again.

Any ideas what is going on? Anything I should try? I have a work-around (choose a different clock scaler) but it's frustrating that I don't understand why it isn't working at 1 MHz. I would like to understand.

robustability:
Apologies if this isn't the correct sub-forum.

I am interfacing an ATmega328P (standalone, not on the Arduino board, though I am programming it with the Arduino IDE) with Adafruit's nRF8001 Bluetooth breakout board. I am using Adafruit's code for Arduino, which uses SPI and a few extra pins to setup a UART interface.

Now, everything works perfectly fine at 16 MHz (with external crystal) and at 8 MHz using the "Atmega on a breadboard" code which you can find in the Arduino online tutorials. So next, I recompiled the bootloader code and changed the fuse settings in boards.txt and the makefile so that it would run at 1 MHz. I use "upload using programmer" to load the code which I think overwrites the bootloader anyway, but that's fine... I really just care about the fuse settings being correct and Arduino getting the right timing for the delay() and millis() functions without me having to think about it. When I put together code that does something simple like blink an LED, it works fine at 1 MHz with my new settings. Timing is perfect and everything. But when I try to interface it with the nRF8001 using the same code as before, the code just doesn't execute! Then, if I set CLKPR in my setup( ) function to use a clock divider of 16 instead of 8, making the system clock run at 500 kHz, everything runs fine again.

Any ideas what is going on? Anything I should try? I have a work-around (choose a different clock scaler) but it's frustrating that I don't understand why it isn't working at 1 MHz. I would like to understand.

Leave CKDIV8 unprogrammed (boot at 16 mhz) and leave the bootloader alone, then set the divider in your code. By the way, you don't have to mess with CLKPR... there's a macro in
** **<avr/power.h>** **
called "
** **clock_prescale_set(clock_div_t)** **
" which does it all for you. The dividers are also pre-defined for you (clk_div_1, 2, 4, 8 etc...).

You would call it as such:

#include <avr/power.h> // don't forget this! 
    clock_prescale_set (clock_div_8); // divide F_CPU by 8

To make your delays and such work right, also do this (obviously the F_CPU must match the frequency the clock divider gives you.

#ifdef F_CPU
#undef F_CPU
#define F_CPU 1000000UL
#endif

Lastly, here's a little "wrapper" I made for that function:

///////////////////////////////////////////////////////////////////////////////
//
//  CPU clock speed divider control function for Arduino
//  Copyright (c) 2015 Roger A. Krupski <rakrupski@verizon.net>
//
//  Last update: 24 August 2015
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program. If not, see <http://www.gnu.org/licenses/>.
//
///////////////////////////////////////////////////////////////////////////////

#include "wiring_private.h"

uint8_t clockDivide (uint16_t div)
{
    uint8_t n = 16;

    div = div < 1 ? 1 : div > 256 ? 256 : div; // constrain value to legal

    while (! (div & 0x8000)) {
        div *= 2;
        n--;
    }

    if (! ((div << 1) & 0x8000)) {
        n--;
    }

    clock_prescale_set ((clock_div_t) n);

    return n; // return divider number for other possible uses
}

//// end of clock_divide.cpp ////

With the wrapper, you can put in any number you want and the closest speed will be chosen.

Hi Robustability,
I am also in need to run an ATMega328P at 1MHz. I tried all post i found but i still get delay/millis running 16 times slower despite using downloaded bootloaders that were supposedly recompiled for f_cpu=1000000L.
It looks like you had more luck.
Any chances you could share your bootloader (HEX file) and your board.txt?
Thanks a million!

delay and millis timing are determined by the speed your sketch is compiled for (as defined by the selected board), not what the bootloader is compiled for. If you select a 16mhz board and build your sketch, it'll be built for 16mhz, and if the board you point it at isn't 16mhz, it won't run at the right speed.

The bootloader itself ONLY deals with uploading to board, not anything else.
However, since 'burn bootloader' also sets the fuses to set what speed to run at, the confusion is understandable. That sets the speed the chip will run at - but it's up to you to select a board that matches what you're uploading to.

OP: Your problem is really weird. Does it work if you change the FCPU in boards.txt to 999999L? (this will check if there's a #if (F_CPU >=1000000) that's tripping you up - I don't know that there is - I'm grasping at straws - but it's an easy test)

Krupski:
To make your delays and such work right, also do this (obviously the F_CPU must match the frequency the clock divider gives you.

#ifdef F_CPU

#undef F_CPU
#define F_CPU 1000000UL
#endif

Ah, I didn't realize you could do this. I think this is what I was missing! Why do you have 1000000UL instead of just L? Thanks for your help, I will probably use this in my code. I'm still curious WHY my original approach didn't work though.

DrAzzy:
OP: Your problem is really weird. Does it work if you change the FCPU in boards.txt to 999999L? (this will check if there's a #if (F_CPU >=1000000) that's tripping you up - I don't know that there is - I'm grasping at straws - but it's an easy test)

I tried it and no, it didn't work. Is it supposed to work? Will FCPU be forced to 1,000,000 if you do this?

mfini:
Hi Robustability,
I am also in need to run an ATMega328P at 1MHz. I tried all post i found but i still get delay/millis running 16 times slower despite using downloaded bootloaders that were supposedly recompiled for f_cpu=1000000L.
It looks like you had more luck.
Any chances you could share your bootloader (HEX file) and your board.txt?
Thanks a million!

It sounds like you still have f_cpu set to 16000000 somewhere. I would suggest you do what Krupski suggested in his post. Of course instead of 16 MHz use the 8 MHz internal oscillator configuration as shown here, then set the clock prescaler to 8. However, if you would still like to try my code, I've attached it. Make sure to put boards.txt in your \Documents\Arduino\Hardware\breadboard\avr\ folder and the .hex file in \Documents\Arduino\Hardware\breadboard\avr\bootloaders\atmega and restart Arduino. Then in tools-> board the new 1 MHz configuration should show up at the bottom, and you should select that and click burn bootloader. Keep in mind I'm some random guy on the internet and I don't entirely know what I'm doing, so use at your own risk. But I have tested this and it works for basic programs. If you find a reproducible bug in it, please let me know.

Edit: oops, looks like the forum didn't let me attach the .hex file for the bootloader, so I added it inside a zip file.

boards.txt (1.7 KB)

ATmegaBOOT_168_atmega328_1MHz.zip (2.2 KB)

robustability:
I tried it and no, it didn't work. Is it supposed to work? Will FCPU be forced to 1,000,000 if you do this?

The hope there was that 1,000,000 was the tipping point for some #if (somewhere) that set something one way at 1mhz or more, or something else below 1mhz - yet at 1mhz, maybe the setting it picked wasn't a good one (afaict, Arduino isn't well tested at anything other than 8 and 16mhz)

Hi, I do have f_cpu=1000000L in my boards.txt but it Looks like it is not picked up.
In many other threads I had read that the bootloader itself needs to be compiled with that option, while you now say it only influences the baud rate (make sense to me btw).
I do compile my sketch targeting the right board, so my takeaway is that Arduino only handles correctly 8 and 16mhz, hence some changes in my own code are needed to adjust the delays...
Unless you confirm you really got the delays working @1mhz as the original post seems to suggest. Thank you all.

@Robustability: thank you, I did not notice the files attached from the phone.
will try this evening and let you know!

An interesting challenge. I do confirm that the chip works fine at 1MHz as described by Robustability (I actually verified it also worked in my setup).
HOWEVER, things go south when I try to use Timer1. In that context, the timer does not work as expected and the Arduino timings are incorrect (e.g. Delay 1000 = 16 Sec).
It is not my programming mistake, as even the various TimerOne Libraries dont work.
Anyone has a clue why?