Using Arduino IDE for standalone ATMega328P board

I am exploring buying ATMega328P chips and using them natively on a PCB. I have an ASIX Presto programmer is able to load the hex file built in the Arduino IDE. I am considering running the board using the internal 8 mHZ clock and have a question about changes needed to accommodate that. I see that in the boards.txt file the fuse bits are defined, as is the clock speed. Should I be editing uno.bootloader.low_fuses to disable the divide-by-eight feature, and uno.build.f_cpu to 8000000L? I have noticed in my preliminary testing that the compiled code seems to believe the clock is running at 16 mHZ. If I do go with the external crystal, are there any recommendations as to specific chips? My initial attempt seemed to fail and I wonder if I purchased low quality crystals/caps.

Hi @couldabin. For this use case I recommend the excellent MiniCore boards platform.

  1. Install the platform by following the instructions provided here:
    https://github.com/MCUdude/MiniCore#boards-manager-installation
  2. Select Tools > Board > MiniCore > ATmega328 from the Arduino IDE menus.
  3. Select Tools > Variant > 328P / 328PA from the Arduino IDE menus.
  4. Select the appropriate clock speed from Arduino IDE's Tools > Clock menu.
  5. Select your preferred bootloader configuration from Arduino IDE's Tools > Bootloader menu.
  6. Select the appropriate programmer from Arduino IDE's Tools > Programmer menu.
  7. Select Tools > Burn Bootloader from the Arduino IDE menus.

The "Burn Bootloader" operation will do two things:

  • Configure the fuses on the chip according to the configuration you selected from the Tools menus.
  • Flash the bootloader binary to the chip, which will allow you to upload via the serial port. If you selected "No bootloader" from the Tools > Bootloader menu, then no bootloader binary will be flashed (which is appropriate if you are going to use the ISP programmer to flash application binaries to the chip.

If it's upload behaviour, you will have to go into a bit more detail on what happens that.

If it's runtime behaviour, the problem is that the code that you compiled using the Uno option is based on a 16 MHz clock. So all your timings will be of by a factor 2 if you run it on a 8 MHz clock. So e.g. blink will go twice as slow and serial baudrate needs to be adjusted.

You can program your board with the Arduino Pro or Pro Mini option. This will enable a processor menu under tools/processor where you can select the clock frequency (8 MHz or 16MHz). You can ignore the fact that you have options for 5V and 3.3V.

After starting with an Uno starter kit, I now design/build custom controllers based on the ATMega328P. I put an ICSP header on the boards I design and have them manufactured by JLCPCB. You can use Arduino IDE, Platform.IO or Microchip Studio to write and compile your code. I use an ICSP programmer between computer and board for programming.

More likely, lack of decoupling caps or pullup resistor on RESET, incorrect component values, wiring errors, etc.

This is a great tutorial, and if you follow it carefully, it should just work.

Thanks for the link. I will read it closely. I certainly don't have a pullup resistor on RESET. Apart from that, I am hoping I don't have any wiring errors on the crystal and caps -- I simply put the crystal across XTAL1 and XTAL 2, and connected 22 pF caps to ground.

Very helpful. I had no problems read/writing using the Presto programmer. The main issue was what you described -- delay() taking twice as long, etc. I will take a look at the Pro/Pro Mini option. Thanks much!

The Gammon post was very helpful. I am particularly interested in the external clock feature. My main hangup on switching to an external clock is that after setting the clock source fuse bits, if the external clock doesn't work I have no way to get back to change the clock source fuse bits so that it once again uses the internal clock. If I connect my Uno to my board to provide a clock signal on D9, and I use a common ground between the Uno and my standalone board, should I then be able to reset the clock source fuse bits using my Presto programmer?

Setting the bits to what? Please provide more detail about what you are trying to do.

A crystal is not an "external clock". If you have set the fuses for an external clock, then you need to provide that clock signal from some other device. In addition, for the ISP programmer to work, the programming frequency must be set to less than 1/4 of the applied external clock frequency.

Re: “Setting the bits to what?” Oh sorry, I assumed you were familiar with the way the ATMega 328 selects its main clock. The bootloader configuration includes what are referred to as high and low fuse bytes (see boards.txt). The low fuse byte determines a number of things, including which clock will be used, and whether the frequency will be divided by 8. Bit7 determines whether divide-by-8 is enabled, while bits 3:0 (CKSEL in the datasheet) determine the clock source: 0011 selects the internal 128 kHZ RC oscillator; 0010 selects the internal 8 mHZ RC oscillator; and 0000 selects the external clock, which I believe is to be connected across XTAL1 and XTAL2 (pins 9 and 10 respectively).

Re: “a crystal is not an external clock.” Correct me if I am wrong, but I thought that putting a crystal across those pins, along with the requisite capacitors, created an external clock. If I am understanding the documentation, setting the CKSEL bits to 0000 will cause the board to treat the crystal (or resonator) connected across XTAL1 and XTAL2 as an external clock.

I did just that with my project – installed the crystal and the caps – and then set the CKSEL fuse bits to 0000. That left the board dead – all subsequent attempts to read/write the board failed with a message that said the Presto could no longer communicate with the device. I have read numerous warnings that if you don’t have a working external clock, setting the fuse to make the board rely on an external clock will leave you stranded because you can no longer go back in and make changes (nor, of course, will your board run).

But then I read the Gammon post you provided. I believe he says that you can use an Arduino Uno to supply the clock signal on pin 9 (XTAL1). Specifically: “As an alternative to a crystal or resonator, the revised version of the sketch outputs a 8 MHz clock on pin D9 of the programming board. Thus you just need to connect D9 on the programming board to pin 9 of the target board and this will provide a clock signal, enough for you to upload the bootloader and change the fuses.”

I believe that my earlier failure with an external clock (crystal, if you prefer) was that either the crystal or the caps were bad (I could have damaged the crystal; not sure) or both, and I knew of no way to change the fuse back to the internal clock. My prior post was asking whether a person could use the Uno as described to provide the clock signal, while still using a standalone programmer (I use a Presto) to read/write to the target board. I was assuming the Uno would have to share the target board ground.

Hope this is clearer.

set the CKSEL fuse bits to 0000.

Thank you for answering the question. That is the problem.

CKSEL = 0 selects "external clock", which is an external active device generating the digital clock signal, NOT a crystal.

To use a crystal, select fuse bits according to this table, following the instructions in Section 9 of the ATmega328 data sheet "System Clock and Clock Options". You will need to provide an external clock signal to the chip, in order to program the fuses. Be sure to connect the grounds between the external clock generator and the ATmega328 chip.

Thank you for this. I have a lot to digest ...