Running the Mega 2560 on 3.3v at 2MHz

Spent a while searching for guidance on this and could not find much. So I'm posting here hoping it helps others.

I am going to attempt to dump the contents of a parallel flash chip that works at 3.3v logic levels. The absolute maximum for these chips is Vcc = 4V, pins = Vcc + 0.5V. So, the 5V the Arduino is running at will wreck the flash chip. The ATMEGA2560 exits in a couple of speed grades. The plain 2560 is spec'd to run at 16MHz as low as 4.5V

The V variant maxes out at 8MHz, but will operate down to 1.8V, with a speed reduction.

The Arduino MEGA boards have the plain 2560, but YOLO. I need 3.3V and don't have a 2560V. Sticking well within the Safe Operating Area is the best chance of getting something working.

The Arduino MEGA board has a 16MHz crystal, and the 2560 can operate with an 8MHz internal oscillator. The internal oscillator is not as accurate as a crystal, which I read can cause issues with UART timing. It would also be good to push further into the safe operating area considering we're not using a V variant.

Like most AVRs, the 2560 has a System Clock Prescaler, which normally operates at 1

This prescaler can be set to factors that allow us to retain the 16MHz crystal, but achieve clock frequencies below 8MHz

However, setting this register is a little annoying to set in software, it would be better to do it permanently. Fortunately, there is a fuse bit we can set that divides the clock by 8

We can set that bit with an ISP programmer. You can quite easily look at the tables and convert to hex values, but there is an excelent online tool for doing this which is fantastic while you are working out the process. It helps you get it right first time. https://www.engbedded.com/fusecalc/

I used an Arduino UNO as an ISP programmer
https://support.arduino.cc/hc/en-us/articles/4841602539164-Burn-the-bootloader-on-UNO-Mega-and-classic-Nano-using-another-Arduino


Note: The capacitor is absolutely required, else the programmer resets and you get the 'programmer not responding' error.

To get the current state of the fuses, we use avrdude. It is included in the IDE, but it was complaining of missing a config file (it must be stored elsewhere and passed the path by the IDE). I found the easiest thing to do was just to grab the latest release, unzip to a folder, then open a command line to that folder.
https://github.com/avrdudes/avrdude/releases
In my examples, I just unzipped it all into my downloads folder.

To test its working:

avrdude -c arduino -p m2560 -P COM5 -b 19200 -v

which is

avrdude -c <programmer> -p <target MCU model> -P <The COM port its on> -b <baudrate> -v <verbose output>
C:\Users\EliteBook2570p\Downloads>avrdude -c arduino -p m2560 -P COM5 -b 19200 -v
Avrdude version 8.1
Copyright see https://github.com/avrdudes/avrdude/blob/main/AUTHORS

System wide configuration file is C:\Users\EliteBook2570p\Downloads\avrdude.conf

Using port            : COM5
Using programmer      : arduino
Setting baud rate     : 19200
AVR part              : ATmega2560
Programming modes     : SPM, ISP, HVPP, JTAG
Programmer type       : Arduino
Description           : Arduino bootloader using STK500 v1 protocol
HW Version            : 2
FW Version            : 1.18
Topcard               : Unknown

AVR device initialized and ready to accept instructions
Device signature = 1E 98 01 (ATmega2560)

Avrdude done.  Thank you.

To get the fuses, we read the memory addresses using the -U command.

C:\Users\EliteBook2570p\Downloads>avrdude -c arduino -p m2560 -P COM5 -b 19200 -U lfuse:r:-:h -U hfuse:r:-:h -U efuse:r:-:h

Processing -U lfuse:r:-:h
Reading lfuse memory ...
Writing 1 byte to output file <stdout>
0xff

Processing -U hfuse:r:-:h
Reading hfuse memory ...
Writing 1 byte to output file <stdout>
0xd8

Processing -U efuse:r:-:h
Reading efuse memory ...
Writing 1 byte to output file <stdout>
0xfd

Avrdude done.  Thank you.

These commands mean:

-U <Address>:<r=read, w=write>:<destination -=stdout>:<format h=hex>

Note: Setting the baudrate appears to be required, and the correct setting is in the Arduino ISP sketch at line 144:

// Configure the baud rate:

#define BAUDRATE 19200
// #define BAUDRATE	115200
// #define BAUDRATE	1000000

Putting these into the online tool


shows us what is currently set

Here, we can set the changes. I am going to select CKDIV8 to divide the clock by 8.
Pre-empting one issue I had, I Deselect BODLEVEL1 and select BODLEVEL0. The BODLEVEL change is to change the Brown-Out Detection (BOD) level to the lowest level, 1.8V. By default, it is set to 2.7. Brown out detection resets the chip if Vcc goes below the set level. And, 3.3V gives us less headroom. Deselecting all BOD bits disables BOD, but I had issues where the chip would not boot on power up (It would after reset was pressed). So, setting it to 1.8V gives us enough overhead and has the chip booting OK.


The new HEX values for the fuse bytes are then displayed.
image

to write them, run the avrdude command with write instead of read arguments

C:\Users\EliteBook2570p\Downloads>avrdude -c arduino -p m2560 -P COM5 -b 19200 -U lfuse:w:0x7f:m -U hfuse:w:0xD8:m -U efuse:w:0xfe:m

Processing -U lfuse:w:0x7f:m
Reading 1 byte for lfuse from input file 0x7f
Writing 1 byte (0x7F) to lfuse, 1 byte written, 1 verified

Processing -U hfuse:w:0xD8:m
Reading 1 byte for hfuse from input file 0xD8
Writing 1 byte (0xD8) to hfuse, 1 byte written, 1 verified

Processing -U efuse:w:0xfe:m
Reading 1 byte for efuse from input file 0xfe
Writing 1 byte (0xFE) to efuse, 1 byte written, 1 verified

Avrdude done.  Thank you.

Here:

-U <Address>:<w=write>:<source data>:<format m=??>

(I am not sure why you use 'm' instead of 'h')

Now, the chip is running at 2MHz with a lower BOD voltage. But, it is impossible to flash using the Arduino IDE because the baudrates will now be a factor of 8 out. And if it did flash, timings for things like delay() will be a factor of 8 out. To fix this, we make a custom board.

Navigate to where the board header files are. There is a list of folders, one per board model, with 'pins_arduino.h' inside. On my PC the path is:

C:\Users\EliteBook2570p\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\variants\

Copy the existing mega folder and name it something unique. I called it mega-2Mhz


There is no need to change anything inside the .h file.

Next, find the boards.txt file. On my PC the path is:

C:\Users\EliteBook2570p\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\boards.txt

Here, there is a list of all the boards, their names, their CPU frequencies ect. Scroll down untill you find the entry for the Arduino Mega.

##############################################################

mega.name=Arduino Mega or Mega 2560

mega.vid.0=0x2341
mega.pid.0=0x0010
mega.vid.1=0x2341
mega.pid.1=0x0042
mega.vid.2=0x2A03
mega.pid.2=0x0010
...

Copy that section of the file (All the lines that begin with mega., and the associated comments) and paste a copy below (Or, at the bottom of the file).

In the new, duplicated section, change the lines so that instead of starting with mega., they start with the name of the folder we made earlier. In my case, I change mega. to mega-2MHz.

Change the line 'mega.name' to something unique. This is what will show in the 'Boards' menu. I used:

mega-2MHz.name=Arduino Mega or Mega 2560 @ 2 MHz

Change the low fuse byte from 0xFF to what we set earlier, 0x7F

mega-2MHz.bootloader.low_fuses=0x7F

Change the CPU frequency from 16000000L to 2000000L

mega-2MHz.build.f_cpu=2000000L

Change the other fuse bits in the 2560 section to what we set earlier. Extended fuses change from 0xFD to 0xFE (we didn't change the high fuse bits)

mega-2MHz.menu.cpu.atmega2560.bootloader.high_fuses=0xD8
mega-2MHz.menu.cpu.atmega2560.bootloader.extended_fuses=0xFE

IMPORTANT: The upload baudrate needs to be set 1/8 the speed of the listed upload speed, to match the CPU speed which is now running at 1/8 of its original speed. Originally, the baudrate is 115000. This needs to be set to 115000/8 = 14400. This is not a 'standard' UART speed, but it seems to work and through trial and error, this was the only setting that allowed uploading of sketches.

mega-2MHz.menu.cpu.atmega2560.upload.speed=14400

I suppose the settings for the 1280 can be left alone as we are not using that CPU, but in case, I have calculated what they should be.

mega-2MHz.menu.cpu.atmega1280.upload.speed=7200
mega-2MHz.menu.cpu.atmega1280.bootloader.high_fuses=0xDA
mega-2MHz.menu.cpu.atmega1280.bootloader.extended_fuses=0xFE

The full section with the changes then looks like this: (at least with my version of Arduino IDE)

##############################################################

mega-2MHz.name=Arduino Mega or Mega 2560 @ 2 MHz

mega-2MHz.vid.0=0x2341
mega-2MHz.pid.0=0x0010
mega-2MHz.vid.1=0x2341
mega-2MHz.pid.1=0x0042
mega-2MHz.vid.2=0x2A03
mega-2MHz.pid.2=0x0010
mega-2MHz.vid.3=0x2A03
mega-2MHz.pid.3=0x0042
mega-2MHz.vid.4=0x2341
mega-2MHz.pid.4=0x0210
mega-2MHz.vid.5=0x2341
mega-2MHz.pid.5=0x0242
mega-2MHz.upload_port.0.vid=0x2341
mega-2MHz.upload_port.0.pid=0x0010
mega-2MHz.upload_port.1.vid=0x2341
mega-2MHz.upload_port.1.pid=0x0042
mega-2MHz.upload_port.2.vid=0x2A03
mega-2MHz.upload_port.2.pid=0x0010
mega-2MHz.upload_port.3.vid=0x2A03
mega-2MHz.upload_port.3.pid=0x0042
mega-2MHz.upload_port.4.vid=0x2341
mega-2MHz.upload_port.4.pid=0x0210
mega-2MHz.upload_port.5.vid=0x2341
mega-2MHz.upload_port.5.pid=0x0242
mega-2MHz.upload_port.6.board=mega

mega-2MHz.upload.tool=avrdude
mega-2MHz.upload.tool.default=avrdude
mega-2MHz.upload.tool.network=arduino_ota
mega-2MHz.upload.maximum_data_size=8192

mega-2MHz.bootloader.tool=avrdude
mega-2MHz.bootloader.tool.default=avrdude
mega-2MHz.bootloader.low_fuses=0x7F
mega-2MHz.bootloader.unlock_bits=0x3F
mega-2MHz.bootloader.lock_bits=0x0F

mega-2MHz.build.f_cpu=2000000L
mega-2MHz.build.core=arduino
mega-2MHz.build.variant=mega
# default board may be overridden by the cpu menu
mega-2MHz.build.board=AVR_MEGA2560

## Arduino Mega w/ ATmega2560
## -------------------------
mega-2MHz.menu.cpu.atmega2560=ATmega2560 (Mega 2560)

mega-2MHz.menu.cpu.atmega2560.upload.protocol=wiring
mega-2MHz.menu.cpu.atmega2560.upload.maximum_size=253952
mega-2MHz.menu.cpu.atmega2560.upload.speed=14400

mega-2MHz.menu.cpu.atmega2560.bootloader.high_fuses=0xD8
mega-2MHz.menu.cpu.atmega2560.bootloader.extended_fuses=0xFE
mega-2MHz.menu.cpu.atmega2560.bootloader.file=stk500v2/stk500boot_v2_mega2560.hex

mega-2MHz.menu.cpu.atmega2560.build.mcu=atmega2560
mega-2MHz.menu.cpu.atmega2560.build.board=AVR_MEGA2560

## Arduino Mega w/ ATmega1280
## -------------------------
mega-2MHz.menu.cpu.atmega1280=ATmega1280

mega-2MHz.menu.cpu.atmega1280.upload.protocol=arduino
mega-2MHz.menu.cpu.atmega1280.upload.maximum_size=126976
mega-2MHz.menu.cpu.atmega1280.upload.speed=7200
mega-2MHz.menu.cpu.atmega1280.bootloader.high_fuses=0xDA
mega-2MHz.menu.cpu.atmega1280.bootloader.extended_fuses=0xFE
mega-2MHz.menu.cpu.atmega1280.bootloader.file=atmega/ATmegaBOOT_168_atmega1280.hex

mega-2MHz.menu.cpu.atmega1280.build.mcu=atmega1280
mega-2MHz.menu.cpu.atmega1280.build.board=AVR_MEGA

##############################################################

This gives a new listing in the boards menu:

Uploading blink reveals that uploads work and that the Arduino libraries are correcting for the slower CPU clock. Blink frequency is still one Hz

The LED is pretty dim at 3.3V, but still visible.

NOTE: When supplying 3.3V power to the board, do it on the '5V' pin. Putting it on Vin passes the current through a 5V linear regulator, leading to a voltage drop of of up to 1V. Putting it on the '3.3V' pin puts it on the wrong side of a 3.3V linear regulator. The '5V' pin is connected directly to the ATMEGA's Vcc pin.

It appears to me it would much simpler and much more cost effective to use voltage translator chips for the interface. You will have a much more stable system.

I don't think it is. Because:

  1. I don't have a chip and would need to order it
  2. It will need to go in between about 30 signal lines :stuck_out_tongue: Would rather not make a PCB or an unmanageable rats nest

Do you need a 3.3v on all these lines? Or, as I understand, you need to change voltage on flash chip connections only?

The other way - change the board. There are a lot of Arduino compatible boards, natively works on 3.3v level - ESP32, RP2040, STM32 etc..

Yep. The absolute voltage limits for the flash chip are Vcc=4.0V, most pins Vcc+0.5V

Not really interested in obtaining a new board just for this if i can make the mega work

Thanks for taking the time to share this @snuffwooter!

An alternative approach would be to use the excellent 3rd party MegaCore boards platform:

This platform allows you to configure the clock source, clock speed, and BOD:

  1. Install MegaCore via the Arduino IDE Boards Manager:
    https://github.com/MCUdude/MegaCore/tree/master?tab=readme-ov-file#boards-manager-installation
  2. Select Tools > Board > MegaCore > ATmega2560 from the Arduino IDE menus.
  3. Select Tools > Clock > Internal 2 MHz from the Arduino IDE menus.
  4. Select Tools > BOD > 1.8V from the Arduino IDE menus.
  5. Configure the other custom board option menus under the Tools menu according to your preferences.
  6. Connect an ISP programmer between the PC and the target Mega 2560 board.
    Select the appropriate programmer from Arduino IDE's Tools > Programmer menu.
  7. Select Tools > Burn Bootloader from the Arduino IDE menus.
  8. Wait for the "Burn Bootloader" operation to finish successfully.

In addition to the obvious utility of flashing the bootloader binary to the target (which you can actually disable if you like when using MegaCore via the Tools > Bootloader menu), the "Burn Bootloader" operation sets the configuration fuses according to the board configuration. So this will configured the ATmega2560 to use the internal clock and a BOD level of 1.8 V.

Check out this alternative tool:

https://eleccelerator.com/fusecalc/fusecalc.php?chip=atmega2560&LOW=FF&HIGH=DE&EXTENDED=FD&LOCKBIT=0F

Was going to try that, but for some reason couldn't find it in the Boards Manager downloads section. Wish it was there, because it would have been easier than my first principles approach!

Only thing is 2MHz internal oscillator isn't as stable as the external crystal, from what I've read. If you can flash fuses with MCUCore then that's not a problem. Just do what I did through MCUCore!

Does the serial communications still work?

You must add a URL to Arduino IDE's "Additional Boards Manager URLs" preference before MegaCore will be listed in Boards Manager:

https://github.com/MCUdude/MegaCore/tree/master?tab=readme-ov-file#boards-manager-installation

  • Open the File > Preferences menu item.
  • Enter the following URL in Additional Boards Manager URLs:
    https://mcudude.github.io/MegaCore/package_MCUdude_MegaCore_index.json

Yeah serial worked great. Can't remember if I needed to divide the baud rate though.

That was kinda the point of using the crystal and not the internal 8MHz oscillator, it is more stable for timing the UART

Couldn't use the on-board UART->USB chip obviously, as that adds 5v on the board. In my case an RS232 level shifter did the trick, even though it is meant for 5V. A 3.3v FTDI chip would also work