One of the things that simplifies Arduino development is its bootloader. A bootloader is a small program that resides at the code start point of a microcontroller and manages what happens immediately on startup or reset of the device.
In the case of the Arduino, the bootloader lets you upload new programs over a standard serial interface. This is done in conjunction with the serial-to-USB converter included on the Uno, Nano, and others, or with an external serial-to-USB converter in the case of the Pro Mini and similar. When you power up or reset one of these Arduino boards, the bootloader starts running and checks to see if a new program is coming in on the serial line. If it is, it writes the new program to the “user program” area of flash memory, that is, the area of flash not reserved for the bootloader. If no new program is incoming, it jumps execution to the start of the “user program” area.
But a bootloader isn't the only way to program the microcontrollers on these boards. Instead, you can use an external programming device (a.k.a, programmer) that connects your computer to multi-function programming pins on the microcontroller and uploads code directly into flash storage. In fact, the bootloader is itself nothing more than a small program that needs to be written into the microcontroller's flash. Programming factory-fresh ATmega microcontrollers with an Arduino bootloader is typically done with such a programmer.
So while it's entirely possible to program many Arduino boards without using a bootloader, you would have to tie up one USB port on your computer for the programmer and another for serial communication (if you are using serial communication). You would also need separate hardware for both of these. So for this and other reasons, the bootloader is a great convenience.
In spite of the convenience a bootloader offers, there are times when you might want to forego this and program a microcontroller directly. The most common reason is that you want to be able to use all of the available flash storage for your program. Bootloaders in ATmega chips are stored in the same flash memory as “user programs.” While the amount of required memory is small—for example, the Optiboot bootloader used in common Arduinos is a mere 512 bytes—sometimes you just need to use every last byte of flash on the micro for your program.
Another reason to not use a bootloader is that the startup time is critical. The Arduino bootloader introduces about a second and a half delay between power-up/reset and running your program. Without the bootloader, your code starts running almost instantaneously.
Yet another situation where you might not want a bootloader is if you have lowered or disabled the Brown-Out Detection so you can use every last ounce of battery juice in a portable application.1) Without brown-out detection, the erratic system behavior you can expect at very low voltages could cause a program jump to an arbitrary part of the code in flash storage. This could be a jump into bootloader code if the micro is using a bootloader. This then has the potential to start a cascade of events that leads to the bootloader overwriting your program code. If you're not using a bootloader, the code you have programmed can't be corrupted this way.2)
There are situations apart from these where you wouldn't want to use a bootloader that you might discover as you become more experienced with Arduino and microcontroller programming. For whatever reason, if you want to explore how to upload programs to ATmega-based Arduinos without a bootloader, then read on.
To program an ATmega328P without a bootloader, you're going to need a device you want to program (e.g., an Arduino board) and a device with which you will do the programming (i.e., the programmer). You can see the complete list of programmers supported by the Arduino framework by looking at the list available under Tools > Programmer: “xxxx”. At the time of writing this, the available options were:
That's a lot of options!
Below, I cover a few cases to get you started. Most of these use the USBtinyISP.3) Owing to its low cost and wide availability, the USBtinyISP is one of the most popular options. The original USBtinyISP is available sporadically through Adafruit, and clones derived from its open source design along the lines of this one are widely available at auction sites, Amazon, Banggood, etc. All my testing was done using USBtinyISP clones from two different suppliers.
The original USBtinyISP has a built-in 3×2 IDC cable that carries the signals needed for ISP (in-system programming) and a 5×2 IDC cable that carries the same signals but in a different layout. If you're using a USBtinyISP clone, there's a good chance it has a keyed 3×2 and 5×2 headers instead of build-in cables. The following assumes this is the case and that you are using a 3×2 keyed IDC cable. Such a cable is often included with USBtinyISP clone purchases. Finally, I'm assuming the USBtinyISP works at 5VDC, which is the defacto standard.
Depending on the board you are programming, you may find it easier to use Dupont wires (M to F or F to F) instead of the IDC cable.
If you are a Linux user, see Arduino on Linux for important information related to using the USBtinyISP.
As is the case when uploading a program to your Arduino using a bootloader, when uploading a program using a programmer you must specify what the target board is. So when doing the following, make sure Tools > Board and Tools > Processor (or other board options) are set correctly for your board.
Now is also a good time to mention a significant caveat about using a programmer as described here.
Using a programmer involves making connections between the programmer and the Arduino's RST, SCK, MOSI, and MISO pins. If these pins are not used in your application, then programming should be a breeze. However, if these pins are used (e.g. for SPI), then you will probably need to disconnect those connections when you are uploading programs.
This can be bit of a drag and is yet another way the bootloader makes your life simpler.
With that out of the way, let's move on to some specific cases.
Tested with an Uno R3.
Directly uploading a sketch to an Uno or Nano using a USBtinyISP is pretty straightforward:
A caution about power is in order. The 5V VCC supplied from the USBtinyISP to the Arduino is good for about 100mA. This is enough for many situations, but if you have peripherals or other things sucking a lot of power, it may not be. In this case, be prepared to do a fair amount of hoop-jumping to disconnect the power sucking devices during programming or power the Arduino from a source other than the USBtinyISP during programming.
I have not tested this case yet.
The Pro Mini lacks the ISP header found in the Uno and Nano, so the process is a little more involved, but not much.
To connect the USBtinyISP to the Pro Mini you can use Dupont cables, M to F or F to F depending on your programmer. While Dupont cables work fine, you will probably end up connecting and disconnecting the Arduino to and from the USBtinyISP quite a lot. So you might want to build up a custom 3×2 header that you can leave connected to your system and then use the IDC cable to connect and disconnect the two devices. It's your choice.
In either case, the process is the same:
The caveats mentioned in Case 1 about powering an entire system from the USBtinyISP apply here as well.
Most programmers based on the USBtinyISP assume you are working at 5VDC. This presents a bit of an issue if you are using a 3.3V/8MHz Pro Mini because the I/O of the ATmega328P is not 5V tolerant when the VCC is 3.3V. In spite of this, there are a couple approaches to using an USBtinyISP to program a 3.3V Pro Mini, covered below.
As was the case in Case 2, to connect the USBtinyISP to the Pro Mini you can use Dupont cables, M to F or F to F depending on your programmer. While Dupont cables work fine, you will probably end up connecting and disconnecting the Arduino to and from the USBtinyISP quite a lot. So you might want to build up a custom 3×2 header that you can leave connected to your system and then use the IDC cable to connect and disconnect the two devices.
Version 2 of the Adafruit USBtinyISP as well as many clones have a jumper that disconnects the internal 5V VCC from the programming headers and shifts the level of the programmer's I/O to the VCC applied at the ISP header. This means you can connect the 3.3V VCC used by your 3.3V Pro Mini to the programmer and all will be fine.
Make sure your USBtinyISP level shifts the I/O before attempting this method.
Version 1 of the Adafruit USBtinyISP has a jumper that disconnects the internal VCC from the programming headers, but it does not level-shift the I/O. All the thrid-party USBtinyISP clones I've encountered have the VCC jumper as well as the needed circuitry to level shift the I/O. But it's entirely likely that there are some that don't.
Assuming you have a USBtinyISP that has working level-shifting on the I/O, remove the VCC jumper from the USBtinyISP. This is J3 on the Adafruit USBtinyISP. On typical third party USBtinyISP boards it's adjacent to the 3×2 ISP header.
Then here's what to do:
Because the Pro Mini and peripherals are powered by the whatever supply will be used in normal use, caveats mentioned in Case 1 about powering an entire system from the USBtinyISP do not apply.
If your USBtinyISP doesn't have the option of shifting the I/O to the target's VCC (or if you're not sure), you might still be able to use a 5V USBtinyISP. What we are going to do is program the Pro Mini at a VCC or 5V but operate it at 3.3V. This approach only works fine if everything else connected in your system is OK with 5V VCC. So if you have a peripheral that's 3.3V only, then this approach will not work.
Assuming you're in the green zone for this approach, then here's what you do:
The caveats mentioned in Case 1 about powering an entire system from the USBtinyISP apply here as well and are made more complicated than the “program at 5V, use at 3.3V” protocol. This makes this approach pretty fraught, but it can be a lifesaver if you need it.
I have not tested this case yet.
You can use an Arduino board as a programmer using the “Arduino as ISP” sketch. The process of configuring and connecting an Arduino as a programmer is described in the Arduino as ISP and Arduino Bootloaders tutorial.
You should be able to use a 3.3V/8MHz Pro Mini as the “Arduino as ISP”, thereby providing your target 3.3V/8MHz Pro Mini with voltage levels it will be happy with.
When you upload a program using a programmer, you overwrite the bootloader. This means your board will no longer work with the conventional Sketch > Upload command in the IDE. However, getting the bootloader back is straightforward.
Uploading a bootloader to your micro is almost identical to uploading a program using a programmer. The only difference is that instead of uploading a program using Sketch > Upload Using Programmer, you upload the appropriate bootloader for your board using Tools > Burn Bootloader. As was the case when uploading programs, before you upload the bootloader, you must confirm that Tools > Board and Tools > Processor or other options are set appropriately for your board.
After uploading the bootloader, you should be able to use Sketch > Upload again just as you always have.