5.5 Creating a Bootloader Client for AVR® Devices
This section demonstrates how to configure a bootloader for 8-bit AVR devices using UART, SPI, or I2C for communication.
Description: This section details a basic bootloader use case on the AVR128DA48 microcontroller, which applies to many variants of the AVR architecture and that can also be extrapolated to other 8-bit AVR MCUs. This example uses the AVR128DA48 Curiosity Nano board for demonstration. Refer to the board schematics for configuration information.
-
Create a new MPLAB® X IDE project.
Note: If “Simulator” is the only option displayed in the Tools combo box while selecting the device, then the target board is not plugged into the computer or it is not communicating with MPLAB® X IDE properly. -
MCC opens up automatically when a new project is created. If not, click the MCC button in the menu bar to start the MPLAB Code Configurator.
-
Click Next on MCC Melody in the MCC Content Type Wizard window.
-
Click Finish in the Content Download Required window and wait for all the required content to be downloaded.
- Open the MCC Content Manager from the Device Resources tab and make sure the latest version of 8-Bit MDFU Client is selected under Libraries>Bootloader
-
Add the 8-Bit MDFU Client library into the Project Resources tab from Device Resources. In previous versions of MCC, a user needed to manually attach all peripheral drivers required by their library, using the Device Resources tab. The new MCC Melody allows users to select the library needed for the project from the Device Resources and Melody can bring in all the dependencies.
-
Confirm that the 8-Bit MDFU Client library has been included into the Application Builder window. The Application Builder UI will import all the required peripheral driver dependencies such as the NVM and Timer Delay modules and link them to the 8-Bit MDFU Client library.
-
One of the modules that cannot be configured automatically is the communication peripheral. All of the communication protocols that are supported by the 8-Bit MDFU Client are shown below. Navigate to the section for the preferred communication peripheral:
-
UART:
For UART communication, this example will configure the UART peripheral to route data through the CDC ports of the on-board debugger. This is the simplest UART configuration that we can create using the Curiosity Nano platform.
Select UART from the Communication Protocol drop-down list in the 8-Bit MDFU Client Easy View tab.
For the bootloader to communicate with the host application (PyMDFU) over USART, a driver instance needs to be configured to exchange data through the serial ports on the target device. To understand which communication ports are required to be opened for the device in use, check the schematics and look for the Serial or CDC Ports definition for the Curiosity Nano board, as shown in the image below. Using this definition, the MCU is communicating through the PC0 pin for transmitting data serially and the PC1 pin for receiving data serially.
A communication driver version that communicates through the PC0 and PC1 pins is needed. For the AVR128DA48 Curiosity Nano, attach the USART1 driver, since that is the UART driver that can communicate through those pins. Select USART1 from the “UART PLIB Selector” in the SERCOM(None) tab. The rest of the SERCOM settings will become visible.
For AVR devices, enabling a software pull-up on the RX helps with the stability of UART. Navigate to the Pins tab and check the box for Weak pull-up on the UART RX pin.
Note: Some devices will automatically select the required UART pins. Open the Pin Grid View tab to make sure the pins are configured correctly.SPI:
For SPI communication, this example will utilize the MCP2210 Breakout Module to send SPI signals through a USB port. This breakout module is one of the ways we can allow PyMDFU to send SPI signals through a USB port.
Select SPI from the Communication Protocol drop-down list in the 8-Bit MDFU Client Easy View tab.Select an instance of the SPI driver from the SPI Client PLIB Selector drop-down list. For example, SPI0 and the driver settings will open up.Make sure the correct SDI (MOSI), SDO (MISO), SS and SCK pins are selected and rename the SS pin to CHIP_SELECT.Note: For some devices, the SPI PLIB will automatically select the proper pins. Validate that what has been set is correct for your hardware configuration.I2C:
For I2C communication, this example will utilize a USB-I2C bridge, like the MCP2221A or an Aardvark Total Phase I2C/SPI Host to send I2C signals through a USB port. This method allows PyMDFU to send I2C signals through a USB port.
Select I2C from the Communication Protocol drop-down list in the 8-Bit MDFU Client Easy View tab.
Select an instance of the I2C driver from the I2C Client PLIB Selector drop-down list. For example, TWI0 and the driver settings will open up. Set the Client Address and Client Mask appropriately.
Note: If the device only has one I2C module, then the PLIB will be added automatically.Make sure the correct SCL, and SDA pins.
Important: It is strongly suggested to use interrupts for I2C communication. Follow the steps provided in the Supporting Interrupts for AVR® Devices section to finish setting up interrupts in the ecosystem. - In the CLKCTRL module, configure the oscillator and clock divider. In general, a faster clock is better for more reliable communication. For this AVR128DA48 example, 24 MHz internal oscillator is selected with Prescaler Disabled.
- In the System/Configuration Bits
module, set the External Oscillator Selection to “Oscillator not enabled”. Tip: Matching the clock settings and oscillator configuration bits settings in the bootloader client and the end application can help to create similar behavior when testing the application alone and when ran through a bootloader.
-
There are several advantages to configuring I/O pins in a bootloader, but the most important one is the ability to receive an operating state back from the MCU based on an LED status. In this bootloader module, the LED will be illuminated when in a Boot state, and it will turn off when the application state takes control of the MCU. This helps to track the program flow during the development process and helps to identify the Active state of the device.
Another helpful feature we can configure using the I/O pins is a hardware entry mechanism. In many development scenarios, users will want to be able to switch from the running application back to the Boot mode in order to load a new application. Providing a push button that can give a signal to the MCU that a bootload is being requested by the user is a very helpful feature for testing the ecosystem. In this bootloader module, the Entry pin can force entry into the Boot mode when an expected voltage level is observed on the pin at the time of a device reset.
Configure the bootloader module to utilize both of the I/O options. Click the switches on the Easy View to activate the I/O Pins features.
Check the schematics to configure the I/O pins for using the LED and switch on the board. Find the I/O interface declaration, similar to the image below. For the AVR128DA48, make sure the RC6 port is connected to control the LED and RC7 is connected to control the switch.
Go back to MPLAB X IDE and navigate to the Pin Grid View tab in the bottom center of the screen. Then select the RC6 as the output pin and RC7 as the input pin for BOOT INDICATE and BOOT ENTRY in the grid, respectively. Open the Project Resources> System>Pins to view the specific GPIO pin settings.
Note: The output pin is assumed to start in an Off state and the starting voltage level of the pin must be configured to follow the required activation level of the pin. For example, most Curiosity Nano devices have an Active-Low setting for the LED pins. Since, in this scenario, the LED pin will read a low voltage when enabled, it will start in the Off state, meaning the voltage will start high, and the bootloader will set that pin low once it starts running.Note: The input pin must be configured to use a Pull-up to help stabilize the signal during a power cycle or other reset. - Device ID is automatically
populated for the device used in the current project.
-
Before moving on, address the warnings in the Notifications [MCC] tab if there are any. For example, NVM options must be enabled and a notification will be present if it was not set automatically.
Note: Ignore the notifications for the application start address and linker settings for now. - For AVR devices, setting the
application start address is not as trivial as just setting the address. AVR
device must set the BOOTSIZE in the Configurations Bits section to the maximum
size - 1 to reserve one page for the application image. The actual value of the
BOOTSIZE can be calculated after compiling the project for the first time.Tip: For example, the AVR128DA48 has a total of 256 pages (range is 0 to 255) so set the value to 254.
- For this example, the
verification scheme is set to Checksum.
-
Now, with the basic configurations complete, click the Generate button in the Project Resources tab to generate the project code.
- The
bl_example.c
file provides routines for running a basic bootloader update process. Themain.c
file will be automatically updated to call theBL_ExampleInitialize()
andBL_Example()
. If not, open themain.c
file, and add the code shown below. -
Click the “Clean and Build” icon drop-down option and select Clean and Build Main Project to compile the bootloader code.
- With the device programmed, the
next step is to calculate the FUSE size. As seen in the image below, the
bootloader code is taking up memory from 0x0 to 0x123B. The Boot section of
memory needs to be configured at a minimum 0x2000 bytes. The range must be
pushed slightly higher to account for the RAM needed during operation. In this
example, extra space is added to the bootloader to mimic a production design
choice suitable for several solutions. There can also be a design where extra
space is added to the Boot section to house an internal library within the Boot
section of Flash.Note: Adding extra space in the Boot section of Flash is a design decision that must be considered on an as-needed basis.
The device used in this example, AVR128DA48, has the Flash memory divided into 100h pages with each page consisting of 200h bytes. Allocating 10h pages will provide the bootloader section with 2000h bytes (10h pages x 200h bytes) of Program Flash Memory and the remaining 0x1E000 for the user application.
Note: Refer to the Memory Organization section of the device data sheet for more information on memory partition.Note: The “BOOTSIZE” field in the Configuration Bit Setup uses decimal values. Make sure to convert the calculated hex value to decimal value.Setting the CODESIZE fuse is an optional action that can be done in instances where a user wants more control over the various portions of memory. If this value is set to 0x00, the bootloader will still operate normally, but it is not possible to specify an AppData section of memory without first configuring the Codesize fuse. Refer to the memory map presented in the data sheet on how to configure this option.Note: The “CODESIZE” field in the Configuration Bit Setup uses decimal values. Make sure to convert the calculated hex value to decimal value. - Click Generate after updating the BOOT and CODE FUSE values in the MCC UI and merge all of the file changes if the manual merge window comes up.
-
Add the linker settings for the desired compiler. The following example is using XC8. Copy the Additional Options under the Linker Option>XC8 Linker Settings. Right click the project and open project properties. Go to XC8 Global Options>XC8 Linker>Additional Options>Extra Linker Options and apply the copied value.
Note: Make sure to uncheck “Map 32K segments of FLASH into the RAM address space” under the Project Project>XC8 Global Options>Global Options, if available, to make sure that the const data is written into Flash memory. -
To prepare the chip to bootload, click the Make and Program Device button. After this programming process ends, the LED on the AVR128DA48 Curiosity Nano board will stay ON continuously.