|
| 1 | +/****************************************************************************** |
| 2 | + * © 2018 Microchip Technology Inc. and its subsidiaries. |
| 3 | + * |
| 4 | + * Subject to your compliance with these terms, you may use Microchip software |
| 5 | + * and any derivatives exclusively with Microchip products. It is your |
| 6 | + * responsibility to comply with third party license terms applicable to your |
| 7 | + * use of third party software (including open source software) that may |
| 8 | + * accompany Microchip software. |
| 9 | + * |
| 10 | + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER |
| 11 | + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY |
| 12 | + * IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A |
| 13 | + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, |
| 14 | + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR |
| 15 | + * EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, |
| 16 | + * EVEN IF MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE |
| 17 | + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL |
| 18 | + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED |
| 19 | + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR |
| 20 | + * THIS SOFTWARE. |
| 21 | + * |
| 22 | + *****************************************************************************/ |
| 23 | + |
| 24 | +/* |
| 25 | + * UART Bootloader for tinyAVR 0- and 1-series, and megaAVR 0-series |
| 26 | + * Each byte received is echoed to confirm reception. |
| 27 | + * |
| 28 | + * For the code to be placed in the constructors section it is necessary |
| 29 | + * to disable standard startup files in Toolchain->AVR/GNU Linker->General. |
| 30 | + * |
| 31 | + * The example is written for ATtiny817 with the following pinout: |
| 32 | + * USART0 TxD PB2 |
| 33 | + * USART0 RxD PB3 |
| 34 | + * LED0 PB4 |
| 35 | + * SW1 PC5 (external pull-up) |
| 36 | + */ |
| 37 | +#define F_CPU_RESET (16E6/6) |
| 38 | + |
| 39 | +#include <avr/io.h> |
| 40 | +#include <assert.h> |
| 41 | +#include <stdbool.h> |
| 42 | + |
| 43 | +/* Baud rate configuration */ |
| 44 | +#define BOOT_BAUD (115200) |
| 45 | + |
| 46 | +/* Memory configuration |
| 47 | + * BOOTEND_FUSE * 256 must be above Bootloader Program Memory Usage, |
| 48 | + * this is 194 bytes at optimization level -O3, so BOOTEND_FUSE = 0x01 |
| 49 | + */ |
| 50 | +#define BOOTEND_FUSE (0x01) |
| 51 | +#define BOOT_SIZE (BOOTEND_FUSE * 0x100) |
| 52 | +#define MAPPED_APPLICATION_START (MAPPED_PROGMEM_START + BOOT_SIZE) |
| 53 | +#define MAPPED_APPLICATION_SIZE (MAPPED_PROGMEM_SIZE - BOOT_SIZE) |
| 54 | + |
| 55 | +/* Fuse configuration |
| 56 | + * BOOTEND sets the size (end) of the boot section in blocks of 256 bytes. |
| 57 | + * APPEND = 0x00 defines the section from BOOTEND*256 to end of Flash as application code. |
| 58 | + * Remaining fuses have default configuration. |
| 59 | + */ |
| 60 | +FUSES = { |
| 61 | + .OSCCFG = FREQSEL_16MHZ_gc, |
| 62 | + .SYSCFG0 = CRCSRC_NOCRC_gc | RSTPINCFG_UPDI_gc, |
| 63 | + .SYSCFG1 = SUT_64MS_gc, |
| 64 | + .APPEND = 0x00, |
| 65 | + .BOOTEND = BOOTEND_FUSE |
| 66 | +}; |
| 67 | + |
| 68 | +/* Define application pointer type */ |
| 69 | +typedef void (*const app_t)(void); |
| 70 | + |
| 71 | +/* Interface function prototypes */ |
| 72 | +static bool is_bootloader_requested(void); |
| 73 | +static void init_uart(void); |
| 74 | +static uint8_t uart_receive(void); |
| 75 | +static void uart_send(uint8_t byte); |
| 76 | +static void init_status_led(void); |
| 77 | +static void toggle_status_led(void); |
| 78 | + |
| 79 | +/* |
| 80 | + * Main boot function |
| 81 | + * Put in the constructors section (.ctors) to save Flash. |
| 82 | + * Naked attribute used since function prologue and epilogue is unused |
| 83 | + */ |
| 84 | +__attribute__((naked)) __attribute__((section(".ctors"))) void boot(void) |
| 85 | +{ |
| 86 | + /* Initialize system for AVR GCC support, expects r1 = 0 */ |
| 87 | + asm volatile("clr r1"); |
| 88 | + |
| 89 | + /* Check if entering application or continuing to bootloader */ |
| 90 | + if(!is_bootloader_requested()) { |
| 91 | + /* Enable Boot Section Lock */ |
| 92 | + NVMCTRL.CTRLB = NVMCTRL_BOOTLOCK_bm; |
| 93 | + |
| 94 | + /* Go to application, located immediately after boot section */ |
| 95 | + app_t app = (app_t)(BOOT_SIZE / sizeof(app_t)); |
| 96 | + app(); |
| 97 | + } |
| 98 | + |
| 99 | + /* Initialize communication interface */ |
| 100 | + init_uart(); |
| 101 | + init_status_led(); |
| 102 | + |
| 103 | + VPORTD.OUT |= PIN6_bm; |
| 104 | + |
| 105 | + /* |
| 106 | + * Start programming at start for application section |
| 107 | + * Subtract MAPPED_PROGMEM_START in condition to handle overflow on large flash sizes |
| 108 | + */ |
| 109 | + uint8_t *app_ptr = (uint8_t *)MAPPED_APPLICATION_START; |
| 110 | + while(app_ptr - MAPPED_PROGMEM_START <= (uint8_t *)PROGMEM_END) { |
| 111 | + /* Receive and echo data before loading to memory */ |
| 112 | + uint8_t rx_data = uart_receive(); |
| 113 | + uart_send(rx_data); |
| 114 | + |
| 115 | + /* Incremental load to page buffer before writing to Flash */ |
| 116 | + *app_ptr = rx_data; |
| 117 | + app_ptr++; |
| 118 | + if(!((uint16_t)app_ptr % MAPPED_PROGMEM_PAGE_SIZE)) { |
| 119 | + /* Page boundary reached, Commit page to Flash */ |
| 120 | + _PROTECTED_WRITE_SPM(NVMCTRL.CTRLA, NVMCTRL_CMD_PAGEERASEWRITE_gc); |
| 121 | + while(NVMCTRL.STATUS & NVMCTRL_FBUSY_bm); |
| 122 | + |
| 123 | + toggle_status_led(); |
| 124 | + } |
| 125 | + } |
| 126 | + |
| 127 | + /* Issue system reset */ |
| 128 | + _PROTECTED_WRITE(RSTCTRL.SWRR, RSTCTRL_SWRE_bm); |
| 129 | +} |
| 130 | + |
| 131 | +/* |
| 132 | + * Boot access request function |
| 133 | + */ |
| 134 | +static bool is_bootloader_requested(void) |
| 135 | +{ |
| 136 | + /* Check if SW1 (PA0) is low */ |
| 137 | + if(VPORTA.IN & PIN0_bm) { |
| 138 | + return false; |
| 139 | + } |
| 140 | + return true; |
| 141 | +} |
| 142 | + |
| 143 | +/* |
| 144 | + * Communication interface functions |
| 145 | + */ |
| 146 | +static void init_uart(void) |
| 147 | +{ |
| 148 | + /* Configure UART */ |
| 149 | + USART1.CTRLB = USART_RXEN_bm | USART_TXEN_bm; |
| 150 | + |
| 151 | + /* From datasheet: |
| 152 | + * Baud rate compensated with factory stored frequency error |
| 153 | + * Asynchronous communication without Auto-baud (Sync Field) |
| 154 | + * 20MHz Clock, 3V |
| 155 | + */ |
| 156 | + int32_t baud_reg_val = (F_CPU_RESET * 64) / (BOOT_BAUD * 16); // ideal BAUD register value |
| 157 | + assert(baud_reg_val >= 0x4A); // Verify legal min BAUD register value with max neg comp |
| 158 | + int8_t sigrow_val = SIGROW.OSC16ERR5V; // read signed error |
| 159 | + baud_reg_val *= (1024 + sigrow_val); // sum resolution + error |
| 160 | + baud_reg_val += 512; // compensate for rounding error |
| 161 | + baud_reg_val /= 1024; // divide by resolution |
| 162 | + USART1.BAUD = (int16_t) baud_reg_val; // set adjusted baud rate |
| 163 | + |
| 164 | + PORTMUX.USARTROUTEA |= PORTMUX_USART1_ALT1_gc; |
| 165 | + |
| 166 | + /* Set TxD (PB2) as output */ |
| 167 | + VPORTC.DIR |= PIN4_bm; |
| 168 | +} |
| 169 | + |
| 170 | +static uint8_t uart_receive(void) |
| 171 | +{ |
| 172 | + /* Poll for data received */ |
| 173 | + while(!(USART1.STATUS & USART_RXCIF_bm)); |
| 174 | + return USART1.RXDATAL; |
| 175 | +} |
| 176 | + |
| 177 | +static void uart_send(uint8_t byte) |
| 178 | +{ |
| 179 | + /* Data will be sent when TXDATA is written */ |
| 180 | + USART1.TXDATAL = byte; |
| 181 | +} |
| 182 | + |
| 183 | +static void init_status_led(void) |
| 184 | +{ |
| 185 | + /* Set LED0 (PB4) as output */ |
| 186 | + VPORTD.DIR |= PIN6_bm; |
| 187 | +} |
| 188 | + |
| 189 | +static void toggle_status_led(void) |
| 190 | +{ |
| 191 | + /* Toggle LED0 (PB4) */ |
| 192 | + VPORTD.OUT ^= PIN6_bm; |
| 193 | +} |
0 commit comments