|
@@ -0,0 +1,963 @@
|
|
|
+#define FUNC_READ 1
|
|
|
+#define FUNC_WRITE 1
|
|
|
+/**********************************************************/
|
|
|
+/* Optiboot bootloader for Arduino */
|
|
|
+/* */
|
|
|
+/* http://optiboot.googlecode.com */
|
|
|
+/* */
|
|
|
+/* Arduino-maintained version : See README.TXT */
|
|
|
+/* http://code.google.com/p/arduino/ */
|
|
|
+/* It is the intent that changes not relevant to the */
|
|
|
+/* Arduino production envionment get moved from the */
|
|
|
+/* optiboot project to the arduino project in "lumps." */
|
|
|
+/* */
|
|
|
+/* Heavily optimised bootloader that is faster and */
|
|
|
+/* smaller than the Arduino standard bootloader */
|
|
|
+/* */
|
|
|
+/* Enhancements: */
|
|
|
+/* Fits in 512 bytes, saving 1.5K of code space */
|
|
|
+/* Higher baud rate speeds up programming */
|
|
|
+/* Written almost entirely in C */
|
|
|
+/* Customisable timeout with accurate timeconstant */
|
|
|
+/* Optional virtual UART. No hardware UART required. */
|
|
|
+/* Optional virtual boot partition for devices without. */
|
|
|
+/* */
|
|
|
+/* What you lose: */
|
|
|
+/* Implements a skeleton STK500 protocol which is */
|
|
|
+/* missing several features including EEPROM */
|
|
|
+/* programming and non-page-aligned writes */
|
|
|
+/* High baud rate breaks compatibility with standard */
|
|
|
+/* Arduino flash settings */
|
|
|
+/* */
|
|
|
+/* Fully supported: */
|
|
|
+/* ATmega168 based devices (Diecimila etc) */
|
|
|
+/* ATmega328P based devices (Duemilanove etc) */
|
|
|
+/* */
|
|
|
+/* Beta test (believed working.) */
|
|
|
+/* ATmega8 based devices (Arduino legacy) */
|
|
|
+/* ATmega328 non-picopower devices */
|
|
|
+/* ATmega644P based devices (Sanguino) */
|
|
|
+/* ATmega1284P based devices */
|
|
|
+/* ATmega1280 based devices (Arduino Mega) */
|
|
|
+/* */
|
|
|
+/* Alpha test */
|
|
|
+/* ATmega32 */
|
|
|
+/* */
|
|
|
+/* Work in progress: */
|
|
|
+/* ATtiny84 based devices (Luminet) */
|
|
|
+/* */
|
|
|
+/* Does not support: */
|
|
|
+/* USB based devices (eg. Teensy, Leonardo) */
|
|
|
+/* */
|
|
|
+/* Assumptions: */
|
|
|
+/* The code makes several assumptions that reduce the */
|
|
|
+/* code size. They are all true after a hardware reset, */
|
|
|
+/* but may not be true if the bootloader is called by */
|
|
|
+/* other means or on other hardware. */
|
|
|
+/* No interrupts can occur */
|
|
|
+/* UART and Timer 1 are set to their reset state */
|
|
|
+/* SP points to RAMEND */
|
|
|
+/* */
|
|
|
+/* Code builds on code, libraries and optimisations from: */
|
|
|
+/* stk500boot.c by Jason P. Kyle */
|
|
|
+/* Arduino bootloader http://arduino.cc */
|
|
|
+/* Spiff's 1K bootloader http://spiffie.org/know/arduino_1k_bootloader/bootloader.shtml */
|
|
|
+/* avr-libc project http://nongnu.org/avr-libc */
|
|
|
+/* Adaboot http://www.ladyada.net/library/arduino/bootloader.html */
|
|
|
+/* AVR305 Atmel Application Note */
|
|
|
+/* */
|
|
|
+
|
|
|
+/* Copyright 2013-2015 by Bill Westfield. */
|
|
|
+/* Copyright 2010 by Peter Knight. */
|
|
|
+/* */
|
|
|
+/* This program is free software; you can redistribute it */
|
|
|
+/* and/or modify it under the terms of the GNU General */
|
|
|
+/* Public License as published by the Free Software */
|
|
|
+/* Foundation; either version 2 of the License, or */
|
|
|
+/* (at your option) any later version. */
|
|
|
+/* */
|
|
|
+/* This program is distributed in the hope that it will */
|
|
|
+/* be useful, but WITHOUT ANY WARRANTY; without even the */
|
|
|
+/* implied warranty of MERCHANTABILITY or FITNESS FOR A */
|
|
|
+/* PARTICULAR PURPOSE. See the GNU General Public */
|
|
|
+/* License for more details. */
|
|
|
+/* */
|
|
|
+/* You should have received a copy of the GNU General */
|
|
|
+/* Public License along with this program; if not, write */
|
|
|
+/* to the Free Software Foundation, Inc., */
|
|
|
+/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|
|
+/* */
|
|
|
+/* Licence can be viewed at */
|
|
|
+/* http://www.fsf.org/licenses/gpl.txt */
|
|
|
+/* */
|
|
|
+/**********************************************************/
|
|
|
+
|
|
|
+
|
|
|
+/**********************************************************/
|
|
|
+/* */
|
|
|
+/* Optional defines: */
|
|
|
+/* */
|
|
|
+/**********************************************************/
|
|
|
+/* */
|
|
|
+/* BIGBOOT: */
|
|
|
+/* Build a 1k bootloader, not 512 bytes. This turns on */
|
|
|
+/* extra functionality. */
|
|
|
+/* */
|
|
|
+/* BAUD_RATE: */
|
|
|
+/* Set bootloader baud rate. */
|
|
|
+/* */
|
|
|
+/* SOFT_UART: */
|
|
|
+/* Use AVR305 soft-UART instead of hardware UART. */
|
|
|
+/* */
|
|
|
+/* LED_START_FLASHES: */
|
|
|
+/* Number of LED flashes on bootup. */
|
|
|
+/* */
|
|
|
+/* LED_DATA_FLASH: */
|
|
|
+/* Flash LED when transferring data. For boards without */
|
|
|
+/* TX or RX LEDs, or for people who like blinky lights. */
|
|
|
+/* */
|
|
|
+/* SUPPORT_EEPROM: */
|
|
|
+/* Support reading and writing from EEPROM. This is not */
|
|
|
+/* used by Arduino, so off by default. */
|
|
|
+/* */
|
|
|
+/* TIMEOUT_MS: */
|
|
|
+/* Bootloader timeout period, in milliseconds. */
|
|
|
+/* 500,1000,2000,4000,8000 supported. */
|
|
|
+/* */
|
|
|
+/* UART: */
|
|
|
+/* UART number (0..n) for devices with more than */
|
|
|
+/* one hardware uart (644P, 1284P, etc) */
|
|
|
+/* */
|
|
|
+/**********************************************************/
|
|
|
+
|
|
|
+/**********************************************************/
|
|
|
+/* Version Numbers! */
|
|
|
+/* */
|
|
|
+/* Arduino Optiboot now includes this Version number in */
|
|
|
+/* the source and object code. */
|
|
|
+/* */
|
|
|
+/* Version 3 was released as zip from the optiboot */
|
|
|
+/* repository and was distributed with Arduino 0022. */
|
|
|
+/* Version 4 starts with the arduino repository commit */
|
|
|
+/* that brought the arduino repository up-to-date with */
|
|
|
+/* the optiboot source tree changes since v3. */
|
|
|
+/* Version 5 was created at the time of the new Makefile */
|
|
|
+/* structure (Mar, 2013), even though no binaries changed*/
|
|
|
+/* It would be good if versions implemented outside the */
|
|
|
+/* official repository used an out-of-seqeunce version */
|
|
|
+/* number (like 104.6 if based on based on 4.5) to */
|
|
|
+/* prevent collisions. */
|
|
|
+/* */
|
|
|
+/**********************************************************/
|
|
|
+
|
|
|
+/**********************************************************/
|
|
|
+/* Edit History: */
|
|
|
+/* */
|
|
|
+/* Aug 2014 */
|
|
|
+/* 6.2 WestfW: make size of length variables dependent */
|
|
|
+/* on the SPM_PAGESIZE. This saves space */
|
|
|
+/* on the chips where it's most important. */
|
|
|
+/* 6.1 WestfW: Fix OPTIBOOT_CUSTOMVER (send it!) */
|
|
|
+/* Make no-wait mod less picky about */
|
|
|
+/* skipping the bootloader. */
|
|
|
+/* Remove some dead code */
|
|
|
+/* Jun 2014 */
|
|
|
+/* 6.0 WestfW: Modularize memory read/write functions */
|
|
|
+/* Remove serial/flash overlap */
|
|
|
+/* (and all references to NRWWSTART/etc) */
|
|
|
+/* Correctly handle pagesize > 255bytes */
|
|
|
+/* Add EEPROM support in BIGBOOT (1284) */
|
|
|
+/* EEPROM write on small chips now causes err */
|
|
|
+/* Split Makefile into smaller pieces */
|
|
|
+/* Add Wicked devices Wildfire */
|
|
|
+/* Move UART=n conditionals into pin_defs.h */
|
|
|
+/* Remove LUDICOUS_SPEED option */
|
|
|
+/* Replace inline assembler for .version */
|
|
|
+/* and add OPTIBOOT_CUSTOMVER for user code */
|
|
|
+/* Fix LED value for Bobuino (Makefile) */
|
|
|
+/* Make all functions explicitly inline or */
|
|
|
+/* noinline, so we fit when using gcc4.8 */
|
|
|
+/* Change optimization options for gcc4.8 */
|
|
|
+/* Make ENV=arduino work in 1.5.x trees. */
|
|
|
+/* May 2014 */
|
|
|
+/* 5.0 WestfW: Add support for 1Mbps UART */
|
|
|
+/* Mar 2013 */
|
|
|
+/* 5.0 WestfW: Major Makefile restructuring. */
|
|
|
+/* See Makefile and pin_defs.h */
|
|
|
+/* (no binary changes) */
|
|
|
+/* */
|
|
|
+/* 4.6 WestfW/Pito: Add ATmega32 support */
|
|
|
+/* 4.6 WestfW/radoni: Don't set LED_PIN as an output if */
|
|
|
+/* not used. (LED_START_FLASHES = 0) */
|
|
|
+/* Jan 2013 */
|
|
|
+/* 4.6 WestfW/dkinzer: use autoincrement lpm for read */
|
|
|
+/* 4.6 WestfW/dkinzer: pass reset cause to app in R2 */
|
|
|
+/* Mar 2012 */
|
|
|
+/* 4.5 WestfW: add infrastructure for non-zero UARTS. */
|
|
|
+/* 4.5 WestfW: fix SIGNATURE_2 for m644 (bad in avr-libc) */
|
|
|
+/* Jan 2012: */
|
|
|
+/* 4.5 WestfW: fix NRWW value for m1284. */
|
|
|
+/* 4.4 WestfW: use attribute OS_main instead of naked for */
|
|
|
+/* main(). This allows optimizations that we */
|
|
|
+/* count on, which are prohibited in naked */
|
|
|
+/* functions due to PR42240. (keeps us less */
|
|
|
+/* than 512 bytes when compiler is gcc4.5 */
|
|
|
+/* (code from 4.3.2 remains the same.) */
|
|
|
+/* 4.4 WestfW and Maniacbug: Add m1284 support. This */
|
|
|
+/* does not change the 328 binary, so the */
|
|
|
+/* version number didn't change either. (?) */
|
|
|
+/* June 2011: */
|
|
|
+/* 4.4 WestfW: remove automatic soft_uart detect (didn't */
|
|
|
+/* know what it was doing or why.) Added a */
|
|
|
+/* check of the calculated BRG value instead. */
|
|
|
+/* Version stays 4.4; existing binaries are */
|
|
|
+/* not changed. */
|
|
|
+/* 4.4 WestfW: add initialization of address to keep */
|
|
|
+/* the compiler happy. Change SC'ed targets. */
|
|
|
+/* Return the SW version via READ PARAM */
|
|
|
+/* 4.3 WestfW: catch framing errors in getch(), so that */
|
|
|
+/* AVRISP works without HW kludges. */
|
|
|
+/* http://code.google.com/p/arduino/issues/detail?id=368n*/
|
|
|
+/* 4.2 WestfW: reduce code size, fix timeouts, change */
|
|
|
+/* verifySpace to use WDT instead of appstart */
|
|
|
+/* 4.1 WestfW: put version number in binary. */
|
|
|
+/**********************************************************/
|
|
|
+
|
|
|
+#define OPTIBOOT_MAJVER 6
|
|
|
+#define OPTIBOOT_MINVER 2
|
|
|
+
|
|
|
+/*
|
|
|
+ * OPTIBOOT_CUSTOMVER should be defined (by the makefile) for custom edits
|
|
|
+ * of optiboot. That way you don't wind up with very different code that
|
|
|
+ * matches the version number of a "released" optiboot.
|
|
|
+ */
|
|
|
+
|
|
|
+#if !defined(OPTIBOOT_CUSTOMVER)
|
|
|
+#define OPTIBOOT_CUSTOMVER 0
|
|
|
+#endif
|
|
|
+
|
|
|
+unsigned const int __attribute__((section(".version")))
|
|
|
+optiboot_version = 256*(OPTIBOOT_MAJVER + OPTIBOOT_CUSTOMVER) + OPTIBOOT_MINVER;
|
|
|
+
|
|
|
+
|
|
|
+#include <inttypes.h>
|
|
|
+#include <avr/io.h>
|
|
|
+#include <avr/pgmspace.h>
|
|
|
+#include <avr/eeprom.h>
|
|
|
+
|
|
|
+/*
|
|
|
+ * Note that we use our own version of "boot.h"
|
|
|
+ * <avr/boot.h> uses sts instructions, but this version uses out instructions
|
|
|
+ * This saves cycles and program memory. Sorry for the name overlap.
|
|
|
+ */
|
|
|
+#include "boot.h"
|
|
|
+
|
|
|
+
|
|
|
+// We don't use <avr/wdt.h> as those routines have interrupt overhead we don't need.
|
|
|
+
|
|
|
+/*
|
|
|
+ * pin_defs.h
|
|
|
+ * This contains most of the rather ugly defines that implement our
|
|
|
+ * ability to use UART=n and LED=D3, and some avr family bit name differences.
|
|
|
+ */
|
|
|
+#include "pin_defs.h"
|
|
|
+
|
|
|
+/*
|
|
|
+ * stk500.h contains the constant definitions for the stk500v1 comm protocol
|
|
|
+ */
|
|
|
+#include "stk500.h"
|
|
|
+
|
|
|
+#ifndef LED_START_FLASHES
|
|
|
+#define LED_START_FLASHES 0
|
|
|
+#endif
|
|
|
+
|
|
|
+/* set the UART baud rate defaults */
|
|
|
+#ifndef BAUD_RATE
|
|
|
+#if F_CPU >= 8000000L
|
|
|
+#define BAUD_RATE 115200L // Highest rate Avrdude win32 will support
|
|
|
+#elif F_CPU >= 1000000L
|
|
|
+#define BAUD_RATE 9600L // 19200 also supported, but with significant error
|
|
|
+#elif F_CPU >= 128000L
|
|
|
+#define BAUD_RATE 4800L // Good for 128kHz internal RC
|
|
|
+#else
|
|
|
+#define BAUD_RATE 1200L // Good even at 32768Hz
|
|
|
+#endif
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifndef UART
|
|
|
+#define UART 0
|
|
|
+#endif
|
|
|
+
|
|
|
+#define BAUD_SETTING (( (F_CPU + BAUD_RATE * 4L) / ((BAUD_RATE * 8L))) - 1 )
|
|
|
+#define BAUD_ACTUAL (F_CPU/(8 * ((BAUD_SETTING)+1)))
|
|
|
+#if BAUD_ACTUAL <= BAUD_RATE
|
|
|
+ #define BAUD_ERROR (( 100*(BAUD_RATE - BAUD_ACTUAL) ) / BAUD_RATE)
|
|
|
+ #if BAUD_ERROR >= 5
|
|
|
+ #error BAUD_RATE error greater than -5%
|
|
|
+ #elif BAUD_ERROR >= 2
|
|
|
+ #warning BAUD_RATE error greater than -2%
|
|
|
+ #endif
|
|
|
+#else
|
|
|
+ #define BAUD_ERROR (( 100*(BAUD_ACTUAL - BAUD_RATE) ) / BAUD_RATE)
|
|
|
+ #if BAUD_ERROR >= 5
|
|
|
+ #error BAUD_RATE error greater than 5%
|
|
|
+ #elif BAUD_ERROR >= 2
|
|
|
+ #warning BAUD_RATE error greater than 2%
|
|
|
+ #endif
|
|
|
+#endif
|
|
|
+
|
|
|
+#if (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 > 250
|
|
|
+#error Unachievable baud rate (too slow) BAUD_RATE
|
|
|
+#endif // baud rate slow check
|
|
|
+#if (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 < 3
|
|
|
+#if BAUD_ERROR != 0 // permit high bitrates (ie 1Mbps@16MHz) if error is zero
|
|
|
+#error Unachievable baud rate (too fast) BAUD_RATE
|
|
|
+#endif
|
|
|
+#endif // baud rate fastn check
|
|
|
+
|
|
|
+/* Watchdog settings */
|
|
|
+#define WATCHDOG_OFF (0)
|
|
|
+#define WATCHDOG_16MS (_BV(WDE))
|
|
|
+#define WATCHDOG_32MS (_BV(WDP0) | _BV(WDE))
|
|
|
+#define WATCHDOG_64MS (_BV(WDP1) | _BV(WDE))
|
|
|
+#define WATCHDOG_125MS (_BV(WDP1) | _BV(WDP0) | _BV(WDE))
|
|
|
+#define WATCHDOG_250MS (_BV(WDP2) | _BV(WDE))
|
|
|
+#define WATCHDOG_500MS (_BV(WDP2) | _BV(WDP0) | _BV(WDE))
|
|
|
+#define WATCHDOG_1S (_BV(WDP2) | _BV(WDP1) | _BV(WDE))
|
|
|
+#define WATCHDOG_2S (_BV(WDP2) | _BV(WDP1) | _BV(WDP0) | _BV(WDE))
|
|
|
+#ifndef __AVR_ATmega8__
|
|
|
+#define WATCHDOG_4S (_BV(WDP3) | _BV(WDE))
|
|
|
+#define WATCHDOG_8S (_BV(WDP3) | _BV(WDP0) | _BV(WDE))
|
|
|
+#endif
|
|
|
+
|
|
|
+
|
|
|
+/*
|
|
|
+ * We can never load flash with more than 1 page at a time, so we can save
|
|
|
+ * some code space on parts with smaller pagesize by using a smaller int.
|
|
|
+ */
|
|
|
+#if SPM_PAGESIZE > 255
|
|
|
+typedef uint16_t pagelen_t ;
|
|
|
+#define GETLENGTH(len) len = getch()<<8; len |= getch()
|
|
|
+#else
|
|
|
+typedef uint8_t pagelen_t;
|
|
|
+#define GETLENGTH(len) (void) getch() /* skip high byte */; len = getch()
|
|
|
+#endif
|
|
|
+
|
|
|
+
|
|
|
+/* Function Prototypes
|
|
|
+ * The main() function is in init9, which removes the interrupt vector table
|
|
|
+ * we don't need. It is also 'OS_main', which means the compiler does not
|
|
|
+ * generate any entry or exit code itself (but unlike 'naked', it doesn't
|
|
|
+ * supress some compile-time options we want.)
|
|
|
+ */
|
|
|
+
|
|
|
+int main(void) __attribute__ ((OS_main)) __attribute__ ((section (".init9")));
|
|
|
+
|
|
|
+void __attribute__((noinline)) putch(char);
|
|
|
+uint8_t __attribute__((noinline)) getch(void);
|
|
|
+void __attribute__((noinline)) verifySpace();
|
|
|
+void __attribute__((noinline)) watchdogConfig(uint8_t x);
|
|
|
+
|
|
|
+static inline void getNch(uint8_t);
|
|
|
+static inline void flash_led(uint8_t);
|
|
|
+static inline void watchdogReset();
|
|
|
+static inline void writebuffer(int8_t memtype, uint8_t *mybuff,
|
|
|
+ uint16_t address, pagelen_t len);
|
|
|
+static inline void read_mem(uint8_t memtype,
|
|
|
+ uint16_t address, pagelen_t len);
|
|
|
+
|
|
|
+#ifdef SOFT_UART
|
|
|
+void uartDelay() __attribute__ ((naked));
|
|
|
+#endif
|
|
|
+void appStart(uint8_t rstFlags) __attribute__ ((naked));
|
|
|
+
|
|
|
+/*
|
|
|
+ * RAMSTART should be self-explanatory. It's bigger on parts with a
|
|
|
+ * lot of peripheral registers. Let 0x100 be the default
|
|
|
+ * Note that RAMSTART (for optiboot) need not be exactly at the start of RAM.
|
|
|
+ */
|
|
|
+#if !defined(RAMSTART) // newer versions of gcc avr-libc define RAMSTART
|
|
|
+#define RAMSTART 0x100
|
|
|
+#if defined (__AVR_ATmega644P__)
|
|
|
+// correct for a bug in avr-libc
|
|
|
+#undef SIGNATURE_2
|
|
|
+#define SIGNATURE_2 0x0A
|
|
|
+#elif defined(__AVR_ATmega1280__)
|
|
|
+#undef RAMSTART
|
|
|
+#define RAMSTART (0x200)
|
|
|
+#endif
|
|
|
+#endif
|
|
|
+
|
|
|
+/* C zero initialises all global variables. However, that requires */
|
|
|
+/* These definitions are NOT zero initialised, but that doesn't matter */
|
|
|
+/* This allows us to drop the zero init code, saving us memory */
|
|
|
+#define buff ((uint8_t*)(RAMSTART))
|
|
|
+
|
|
|
+/* Virtual boot partition support */
|
|
|
+#ifdef VIRTUAL_BOOT_PARTITION
|
|
|
+#define rstVect0_sav (*(uint8_t*)(RAMSTART+SPM_PAGESIZE*2+4))
|
|
|
+#define rstVect1_sav (*(uint8_t*)(RAMSTART+SPM_PAGESIZE*2+5))
|
|
|
+#define saveVect0_sav (*(uint8_t*)(RAMSTART+SPM_PAGESIZE*2+6))
|
|
|
+#define saveVect1_sav (*(uint8_t*)(RAMSTART+SPM_PAGESIZE*2+7))
|
|
|
+// Vector to save original reset jump:
|
|
|
+// SPM Ready is least probably used, so it's default
|
|
|
+// if not, use old way WDT_vect_num,
|
|
|
+// or simply set custom save_vect_num in Makefile using vector name
|
|
|
+// or even raw number.
|
|
|
+#if !defined (save_vect_num)
|
|
|
+#if defined (SPM_RDY_vect_num)
|
|
|
+#define save_vect_num (SPM_RDY_vect_num)
|
|
|
+#elif defined (SPM_READY_vect_num)
|
|
|
+#define save_vect_num (SPM_READY_vect_num)
|
|
|
+#elif defined (WDT_vect_num)
|
|
|
+#define save_vect_num (WDT_vect_num)
|
|
|
+#else
|
|
|
+#error Cant find SPM or WDT interrupt vector for this CPU
|
|
|
+#endif
|
|
|
+#endif //save_vect_num
|
|
|
+// check if it's on the same page (code assumes that)
|
|
|
+#if (SPM_PAGESIZE <= save_vect_num)
|
|
|
+#error Save vector not in the same page as reset!
|
|
|
+#endif
|
|
|
+#if FLASHEND > 8192
|
|
|
+// AVRs with more than 8k of flash have 4-byte vectors, and use jmp.
|
|
|
+// We save only 16 bits of address, so devices with more than 128KB
|
|
|
+// may behave wrong for upper part of address space.
|
|
|
+#define rstVect0 2
|
|
|
+#define rstVect1 3
|
|
|
+#define saveVect0 (save_vect_num*4+2)
|
|
|
+#define saveVect1 (save_vect_num*4+3)
|
|
|
+#define appstart_vec (save_vect_num*2)
|
|
|
+#else
|
|
|
+// AVRs with up to 8k of flash have 2-byte vectors, and use rjmp.
|
|
|
+#define rstVect0 0
|
|
|
+#define rstVect1 1
|
|
|
+#define saveVect0 (save_vect_num*2)
|
|
|
+#define saveVect1 (save_vect_num*2+1)
|
|
|
+#define appstart_vec (save_vect_num)
|
|
|
+#endif
|
|
|
+#else
|
|
|
+#define appstart_vec (0)
|
|
|
+#endif // VIRTUAL_BOOT_PARTITION
|
|
|
+
|
|
|
+
|
|
|
+/* main program starts here */
|
|
|
+int main(void) {
|
|
|
+ uint8_t ch;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Making these local and in registers prevents the need for initializing
|
|
|
+ * them, and also saves space because code no longer stores to memory.
|
|
|
+ * (initializing address keeps the compiler happy, but isn't really
|
|
|
+ * necessary, and uses 4 bytes of flash.)
|
|
|
+ */
|
|
|
+ register uint16_t address = 0;
|
|
|
+ register pagelen_t length;
|
|
|
+
|
|
|
+ // After the zero init loop, this is the first code to run.
|
|
|
+ //
|
|
|
+ // This code makes the following assumptions:
|
|
|
+ // No interrupts will execute
|
|
|
+ // SP points to RAMEND
|
|
|
+ // r1 contains zero
|
|
|
+ //
|
|
|
+ // If not, uncomment the following instructions:
|
|
|
+ // cli();
|
|
|
+ asm volatile ("clr __zero_reg__");
|
|
|
+#if defined(__AVR_ATmega8__) || defined (__AVR_ATmega32__) || defined (__AVR_ATmega16__)
|
|
|
+ SP=RAMEND; // This is done by hardware reset
|
|
|
+#endif
|
|
|
+
|
|
|
+ /*
|
|
|
+ * modified Adaboot no-wait mod.
|
|
|
+ * Pass the reset reason to app. Also, it appears that an Uno poweron
|
|
|
+ * can leave multiple reset flags set; we only want the bootloader to
|
|
|
+ * run on an 'external reset only' status
|
|
|
+ */
|
|
|
+#if !defined(__AVR_ATmega16__)
|
|
|
+ ch = MCUSR;
|
|
|
+ MCUSR = 0;
|
|
|
+#else
|
|
|
+ ch = MCUCSR;
|
|
|
+ MCUCSR = 0;
|
|
|
+#endif
|
|
|
+ if (ch & (_BV(WDRF) | _BV(BORF) | _BV(PORF)))
|
|
|
+ appStart(ch);
|
|
|
+
|
|
|
+#if LED_START_FLASHES > 0
|
|
|
+ // Set up Timer 1 for timeout counter
|
|
|
+ TCCR1B = _BV(CS12) | _BV(CS10); // div 1024
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifndef SOFT_UART
|
|
|
+#if defined(__AVR_ATmega8__) || defined (__AVR_ATmega32__) || defined (__AVR_ATmega16__)
|
|
|
+ UCSRA = _BV(U2X); //Double speed mode USART
|
|
|
+ UCSRB = _BV(RXEN) | _BV(TXEN); // enable Rx & Tx
|
|
|
+ UCSRC = _BV(URSEL) | _BV(UCSZ1) | _BV(UCSZ0); // config USART; 8N1
|
|
|
+ UBRRL = (uint8_t)( (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 );
|
|
|
+#else
|
|
|
+ UART_SRA = _BV(U2X0); //Double speed mode USART0
|
|
|
+ UART_SRB = _BV(RXEN0) | _BV(TXEN0);
|
|
|
+ UART_SRC = _BV(UCSZ00) | _BV(UCSZ01);
|
|
|
+ UART_SRL = (uint8_t)( (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 );
|
|
|
+#endif
|
|
|
+#endif
|
|
|
+
|
|
|
+ // Set up watchdog to trigger after 1s
|
|
|
+ watchdogConfig(WATCHDOG_1S);
|
|
|
+
|
|
|
+#if (LED_START_FLASHES > 0) || defined(LED_DATA_FLASH)
|
|
|
+ /* Set LED pin as output */
|
|
|
+ LED_DDR |= _BV(LED);
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef SOFT_UART
|
|
|
+ /* Set TX pin as output */
|
|
|
+ UART_DDR |= _BV(UART_TX_BIT);
|
|
|
+#endif
|
|
|
+
|
|
|
+#if LED_START_FLASHES > 0
|
|
|
+ /* Flash onboard LED to signal entering of bootloader */
|
|
|
+ flash_led(LED_START_FLASHES * 2);
|
|
|
+#endif
|
|
|
+
|
|
|
+ /* Forever loop: exits by causing WDT reset */
|
|
|
+ for (;;) {
|
|
|
+ /* get character from UART */
|
|
|
+ ch = getch();
|
|
|
+
|
|
|
+ if(ch == STK_GET_PARAMETER) {
|
|
|
+ unsigned char which = getch();
|
|
|
+ verifySpace();
|
|
|
+ /*
|
|
|
+ * Send optiboot version as "SW version"
|
|
|
+ * Note that the references to memory are optimized away.
|
|
|
+ */
|
|
|
+ if (which == 0x82) {
|
|
|
+ putch(optiboot_version & 0xFF);
|
|
|
+ } else if (which == 0x81) {
|
|
|
+ putch(optiboot_version >> 8);
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * GET PARAMETER returns a generic 0x03 reply for
|
|
|
+ * other parameters - enough to keep Avrdude happy
|
|
|
+ */
|
|
|
+ putch(0x03);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if(ch == STK_SET_DEVICE) {
|
|
|
+ // SET DEVICE is ignored
|
|
|
+ getNch(20);
|
|
|
+ }
|
|
|
+ else if(ch == STK_SET_DEVICE_EXT) {
|
|
|
+ // SET DEVICE EXT is ignored
|
|
|
+ getNch(5);
|
|
|
+ }
|
|
|
+ else if(ch == STK_LOAD_ADDRESS) {
|
|
|
+ // LOAD ADDRESS
|
|
|
+ uint16_t newAddress;
|
|
|
+ newAddress = getch();
|
|
|
+ newAddress = (newAddress & 0xff) | (getch() << 8);
|
|
|
+#ifdef RAMPZ
|
|
|
+ // Transfer top bit to RAMPZ
|
|
|
+ RAMPZ = (newAddress & 0x8000) ? 1 : 0;
|
|
|
+#endif
|
|
|
+ newAddress += newAddress; // Convert from word address to byte address
|
|
|
+ address = newAddress;
|
|
|
+ verifySpace();
|
|
|
+ }
|
|
|
+ else if(ch == STK_UNIVERSAL) {
|
|
|
+ // UNIVERSAL command is ignored
|
|
|
+ getNch(4);
|
|
|
+ putch(0x00);
|
|
|
+ }
|
|
|
+ /* Write memory, length is big endian and is in bytes */
|
|
|
+ else if(ch == STK_PROG_PAGE) {
|
|
|
+ // PROGRAM PAGE - we support flash programming only, not EEPROM
|
|
|
+ uint8_t desttype;
|
|
|
+ uint8_t *bufPtr;
|
|
|
+ pagelen_t savelength;
|
|
|
+
|
|
|
+ GETLENGTH(length);
|
|
|
+ savelength = length;
|
|
|
+ desttype = getch();
|
|
|
+
|
|
|
+ // read a page worth of contents
|
|
|
+ bufPtr = buff;
|
|
|
+ do *bufPtr++ = getch();
|
|
|
+ while (--length);
|
|
|
+
|
|
|
+ // Read command terminator, start reply
|
|
|
+ verifySpace();
|
|
|
+
|
|
|
+#ifdef VIRTUAL_BOOT_PARTITION
|
|
|
+#if FLASHEND > 8192
|
|
|
+/*
|
|
|
+ * AVR with 4-byte ISR Vectors and "jmp"
|
|
|
+ * WARNING: this works only up to 128KB flash!
|
|
|
+ */
|
|
|
+ if (address == 0) {
|
|
|
+ // This is the reset vector page. We need to live-patch the
|
|
|
+ // code so the bootloader runs first.
|
|
|
+ //
|
|
|
+ // Save jmp targets (for "Verify")
|
|
|
+ rstVect0_sav = buff[rstVect0];
|
|
|
+ rstVect1_sav = buff[rstVect1];
|
|
|
+ saveVect0_sav = buff[saveVect0];
|
|
|
+ saveVect1_sav = buff[saveVect1];
|
|
|
+
|
|
|
+ // Move RESET jmp target to 'save' vector
|
|
|
+ buff[saveVect0] = rstVect0_sav;
|
|
|
+ buff[saveVect1] = rstVect1_sav;
|
|
|
+
|
|
|
+ // Add jump to bootloader at RESET vector
|
|
|
+ // WARNING: this works as long as 'main' is in first section
|
|
|
+ buff[rstVect0] = ((uint16_t)main) & 0xFF;
|
|
|
+ buff[rstVect1] = ((uint16_t)main) >> 8;
|
|
|
+ }
|
|
|
+
|
|
|
+#else
|
|
|
+/*
|
|
|
+ * AVR with 2-byte ISR Vectors and rjmp
|
|
|
+ */
|
|
|
+ if ((uint16_t)(void*)address == rstVect0) {
|
|
|
+ // This is the reset vector page. We need to live-patch
|
|
|
+ // the code so the bootloader runs first.
|
|
|
+ //
|
|
|
+ // Move RESET vector to 'save' vector
|
|
|
+ // Save jmp targets (for "Verify")
|
|
|
+ rstVect0_sav = buff[rstVect0];
|
|
|
+ rstVect1_sav = buff[rstVect1];
|
|
|
+ saveVect0_sav = buff[saveVect0];
|
|
|
+ saveVect1_sav = buff[saveVect1];
|
|
|
+
|
|
|
+ // Instruction is a relative jump (rjmp), so recalculate.
|
|
|
+ uint16_t vect=(rstVect0_sav & 0xff) | ((rstVect1_sav & 0x0f)<<8); //calculate 12b displacement
|
|
|
+ vect = (vect-save_vect_num) & 0x0fff; //substract 'save' interrupt position and wrap around 4096
|
|
|
+ // Move RESET jmp target to 'save' vector
|
|
|
+ buff[saveVect0] = vect & 0xff;
|
|
|
+ buff[saveVect1] = (vect >> 8) | 0xc0; //
|
|
|
+ // Add rjump to bootloader at RESET vector
|
|
|
+ vect = ((uint16_t)main) &0x0fff; //WARNIG: this works as long as 'main' is in first section
|
|
|
+ buff[0] = vect & 0xFF; // rjmp 0x1c00 instruction
|
|
|
+ buff[1] = (vect >> 8) | 0xC0;
|
|
|
+ }
|
|
|
+#endif // FLASHEND
|
|
|
+#endif // VBP
|
|
|
+
|
|
|
+ writebuffer(desttype, buff, address, savelength);
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+ /* Read memory block mode, length is big endian. */
|
|
|
+ else if(ch == STK_READ_PAGE) {
|
|
|
+ uint8_t desttype;
|
|
|
+ GETLENGTH(length);
|
|
|
+
|
|
|
+ desttype = getch();
|
|
|
+
|
|
|
+ verifySpace();
|
|
|
+
|
|
|
+ read_mem(desttype, address, length);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Get device signature bytes */
|
|
|
+ else if(ch == STK_READ_SIGN) {
|
|
|
+ // READ SIGN - return what Avrdude wants to hear
|
|
|
+ verifySpace();
|
|
|
+ putch(SIGNATURE_0);
|
|
|
+ putch(SIGNATURE_1);
|
|
|
+ putch(SIGNATURE_2);
|
|
|
+ }
|
|
|
+ else if (ch == STK_LEAVE_PROGMODE) { /* 'Q' */
|
|
|
+ // Adaboot no-wait mod
|
|
|
+ watchdogConfig(WATCHDOG_16MS);
|
|
|
+ verifySpace();
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // This covers the response to commands like STK_ENTER_PROGMODE
|
|
|
+ verifySpace();
|
|
|
+ }
|
|
|
+ putch(STK_OK);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void putch(char ch) {
|
|
|
+#ifndef SOFT_UART
|
|
|
+ while (!(UART_SRA & _BV(UDRE0)));
|
|
|
+ UART_UDR = ch;
|
|
|
+#else
|
|
|
+ __asm__ __volatile__ (
|
|
|
+ " com %[ch]\n" // ones complement, carry set
|
|
|
+ " sec\n"
|
|
|
+ "1: brcc 2f\n"
|
|
|
+ " cbi %[uartPort],%[uartBit]\n"
|
|
|
+ " rjmp 3f\n"
|
|
|
+ "2: sbi %[uartPort],%[uartBit]\n"
|
|
|
+ " nop\n"
|
|
|
+ "3: rcall uartDelay\n"
|
|
|
+ " rcall uartDelay\n"
|
|
|
+ " lsr %[ch]\n"
|
|
|
+ " dec %[bitcnt]\n"
|
|
|
+ " brne 1b\n"
|
|
|
+ :
|
|
|
+ :
|
|
|
+ [bitcnt] "d" (10),
|
|
|
+ [ch] "r" (ch),
|
|
|
+ [uartPort] "I" (_SFR_IO_ADDR(UART_PORT)),
|
|
|
+ [uartBit] "I" (UART_TX_BIT)
|
|
|
+ :
|
|
|
+ "r25"
|
|
|
+ );
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+uint8_t getch(void) {
|
|
|
+ uint8_t ch;
|
|
|
+
|
|
|
+#ifdef LED_DATA_FLASH
|
|
|
+#if defined(__AVR_ATmega8__) || defined (__AVR_ATmega32__) || defined (__AVR_ATmega16__)
|
|
|
+ LED_PORT ^= _BV(LED);
|
|
|
+#else
|
|
|
+ LED_PIN |= _BV(LED);
|
|
|
+#endif
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef SOFT_UART
|
|
|
+ watchdogReset();
|
|
|
+ __asm__ __volatile__ (
|
|
|
+ "1: sbic %[uartPin],%[uartBit]\n" // Wait for start edge
|
|
|
+ " rjmp 1b\n"
|
|
|
+ " rcall uartDelay\n" // Get to middle of start bit
|
|
|
+ "2: rcall uartDelay\n" // Wait 1 bit period
|
|
|
+ " rcall uartDelay\n" // Wait 1 bit period
|
|
|
+ " clc\n"
|
|
|
+ " sbic %[uartPin],%[uartBit]\n"
|
|
|
+ " sec\n"
|
|
|
+ " dec %[bitCnt]\n"
|
|
|
+ " breq 3f\n"
|
|
|
+ " ror %[ch]\n"
|
|
|
+ " rjmp 2b\n"
|
|
|
+ "3:\n"
|
|
|
+ :
|
|
|
+ [ch] "=r" (ch)
|
|
|
+ :
|
|
|
+ [bitCnt] "d" (9),
|
|
|
+ [uartPin] "I" (_SFR_IO_ADDR(UART_PIN)),
|
|
|
+ [uartBit] "I" (UART_RX_BIT)
|
|
|
+ :
|
|
|
+ "r25"
|
|
|
+);
|
|
|
+#else
|
|
|
+ while(!(UART_SRA & _BV(RXC0)))
|
|
|
+ ;
|
|
|
+ if (!(UART_SRA & _BV(FE0))) {
|
|
|
+ /*
|
|
|
+ * A Framing Error indicates (probably) that something is talking
|
|
|
+ * to us at the wrong bit rate. Assume that this is because it
|
|
|
+ * expects to be talking to the application, and DON'T reset the
|
|
|
+ * watchdog. This should cause the bootloader to abort and run
|
|
|
+ * the application "soon", if it keeps happening. (Note that we
|
|
|
+ * don't care that an invalid char is returned...)
|
|
|
+ */
|
|
|
+ watchdogReset();
|
|
|
+ }
|
|
|
+
|
|
|
+ ch = UART_UDR;
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef LED_DATA_FLASH
|
|
|
+#if defined(__AVR_ATmega8__) || defined (__AVR_ATmega32__) || defined (__AVR_ATmega16__)
|
|
|
+ LED_PORT ^= _BV(LED);
|
|
|
+#else
|
|
|
+ LED_PIN |= _BV(LED);
|
|
|
+#endif
|
|
|
+#endif
|
|
|
+
|
|
|
+ return ch;
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef SOFT_UART
|
|
|
+// AVR305 equation: #define UART_B_VALUE (((F_CPU/BAUD_RATE)-23)/6)
|
|
|
+// Adding 3 to numerator simulates nearest rounding for more accurate baud rates
|
|
|
+#define UART_B_VALUE (((F_CPU/BAUD_RATE)-20)/6)
|
|
|
+#if UART_B_VALUE > 255
|
|
|
+#error Baud rate too slow for soft UART
|
|
|
+#endif
|
|
|
+
|
|
|
+void uartDelay() {
|
|
|
+ __asm__ __volatile__ (
|
|
|
+ "ldi r25,%[count]\n"
|
|
|
+ "1:dec r25\n"
|
|
|
+ "brne 1b\n"
|
|
|
+ "ret\n"
|
|
|
+ ::[count] "M" (UART_B_VALUE)
|
|
|
+ );
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+void getNch(uint8_t count) {
|
|
|
+ do getch(); while (--count);
|
|
|
+ verifySpace();
|
|
|
+}
|
|
|
+
|
|
|
+void verifySpace() {
|
|
|
+ if (getch() != CRC_EOP) {
|
|
|
+ watchdogConfig(WATCHDOG_16MS); // shorten WD timeout
|
|
|
+ while (1) // and busy-loop so that WD causes
|
|
|
+ ; // a reset and app start.
|
|
|
+ }
|
|
|
+ putch(STK_INSYNC);
|
|
|
+}
|
|
|
+
|
|
|
+#if LED_START_FLASHES > 0
|
|
|
+void flash_led(uint8_t count) {
|
|
|
+ do {
|
|
|
+ TCNT1 = -(F_CPU/(1024*16));
|
|
|
+ TIFR1 = _BV(TOV1);
|
|
|
+ while(!(TIFR1 & _BV(TOV1)));
|
|
|
+#if defined(__AVR_ATmega8__) || defined (__AVR_ATmega32__) || defined (__AVR_ATmega16__)
|
|
|
+ LED_PORT ^= _BV(LED);
|
|
|
+#else
|
|
|
+ LED_PIN |= _BV(LED);
|
|
|
+#endif
|
|
|
+ watchdogReset();
|
|
|
+ } while (--count);
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+// Watchdog functions. These are only safe with interrupts turned off.
|
|
|
+void watchdogReset() {
|
|
|
+ __asm__ __volatile__ (
|
|
|
+ "wdr\n"
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+void watchdogConfig(uint8_t x) {
|
|
|
+ WDTCSR = _BV(WDCE) | _BV(WDE);
|
|
|
+ WDTCSR = x;
|
|
|
+}
|
|
|
+
|
|
|
+void appStart(uint8_t rstFlags) {
|
|
|
+ // save the reset flags in the designated register
|
|
|
+ // This can be saved in a main program by putting code in .init0 (which
|
|
|
+ // executes before normal c init code) to save R2 to a global variable.
|
|
|
+ __asm__ __volatile__ ("mov r2, %0\n" :: "r" (rstFlags));
|
|
|
+
|
|
|
+ watchdogConfig(WATCHDOG_OFF);
|
|
|
+ // Note that appstart_vec is defined so that this works with either
|
|
|
+ // real or virtual boot partitions.
|
|
|
+ __asm__ __volatile__ (
|
|
|
+ // Jump to 'save' or RST vector
|
|
|
+ "ldi r30,%[rstvec]\n"
|
|
|
+ "clr r31\n"
|
|
|
+ "ijmp\n"::[rstvec] "M"(appstart_vec)
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * void writebuffer(memtype, buffer, address, length)
|
|
|
+ */
|
|
|
+static inline void writebuffer(int8_t memtype, uint8_t *mybuff,
|
|
|
+ uint16_t address, pagelen_t len)
|
|
|
+{
|
|
|
+ switch (memtype) {
|
|
|
+ case 'E': // EEPROM
|
|
|
+#if defined(SUPPORT_EEPROM) || defined(BIGBOOT)
|
|
|
+ while(len--) {
|
|
|
+ eeprom_write_byte((uint8_t *)(address++), *mybuff++);
|
|
|
+ }
|
|
|
+#else
|
|
|
+ /*
|
|
|
+ * On systems where EEPROM write is not supported, just busy-loop
|
|
|
+ * until the WDT expires, which will eventually cause an error on
|
|
|
+ * host system (which is what it should do.)
|
|
|
+ */
|
|
|
+ while (1)
|
|
|
+ ; // Error: wait for WDT
|
|
|
+#endif
|
|
|
+ break;
|
|
|
+ default: // FLASH
|
|
|
+ /*
|
|
|
+ * Default to writing to Flash program memory. By making this
|
|
|
+ * the default rather than checking for the correct code, we save
|
|
|
+ * space on chips that don't support any other memory types.
|
|
|
+ */
|
|
|
+ {
|
|
|
+ // Copy buffer into programming buffer
|
|
|
+ uint8_t *bufPtr = mybuff;
|
|
|
+ uint16_t addrPtr = (uint16_t)(void*)address;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Start the page erase and wait for it to finish. There
|
|
|
+ * used to be code to do this while receiving the data over
|
|
|
+ * the serial link, but the performance improvement was slight,
|
|
|
+ * and we needed the space back.
|
|
|
+ */
|
|
|
+ __boot_page_erase_short((uint16_t)(void*)address);
|
|
|
+ boot_spm_busy_wait();
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Copy data from the buffer into the flash write buffer.
|
|
|
+ */
|
|
|
+ do {
|
|
|
+ uint16_t a;
|
|
|
+ a = *bufPtr++;
|
|
|
+ a |= (*bufPtr++) << 8;
|
|
|
+ __boot_page_fill_short((uint16_t)(void*)addrPtr,a);
|
|
|
+ addrPtr += 2;
|
|
|
+ } while (len -= 2);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Actually Write the buffer to flash (and wait for it to finish.)
|
|
|
+ */
|
|
|
+ __boot_page_write_short((uint16_t)(void*)address);
|
|
|
+ boot_spm_busy_wait();
|
|
|
+#if defined(RWWSRE)
|
|
|
+ // Reenable read access to flash
|
|
|
+ boot_rww_enable();
|
|
|
+#endif
|
|
|
+ } // default block
|
|
|
+ break;
|
|
|
+ } // switch
|
|
|
+}
|
|
|
+
|
|
|
+static inline void read_mem(uint8_t memtype, uint16_t address, pagelen_t length)
|
|
|
+{
|
|
|
+ uint8_t ch;
|
|
|
+
|
|
|
+ switch (memtype) {
|
|
|
+
|
|
|
+#if defined(SUPPORT_EEPROM) || defined(BIGBOOT)
|
|
|
+ case 'E': // EEPROM
|
|
|
+ do {
|
|
|
+ putch(eeprom_read_byte((uint8_t *)(address++)));
|
|
|
+ } while (--length);
|
|
|
+ break;
|
|
|
+#endif
|
|
|
+ default:
|
|
|
+ do {
|
|
|
+#ifdef VIRTUAL_BOOT_PARTITION
|
|
|
+ // Undo vector patch in bottom page so verify passes
|
|
|
+ if (address == rstVect0) ch = rstVect0_sav;
|
|
|
+ else if (address == rstVect1) ch = rstVect1_sav;
|
|
|
+ else if (address == saveVect0) ch = saveVect0_sav;
|
|
|
+ else if (address == saveVect1) ch = saveVect1_sav;
|
|
|
+ else ch = pgm_read_byte_near(address);
|
|
|
+ address++;
|
|
|
+#elif defined(RAMPZ)
|
|
|
+ // Since RAMPZ should already be set, we need to use EPLM directly.
|
|
|
+ // Also, we can use the autoincrement version of lpm to update "address"
|
|
|
+ // do putch(pgm_read_byte_near(address++));
|
|
|
+ // while (--length);
|
|
|
+ // read a Flash and increment the address (may increment RAMPZ)
|
|
|
+ __asm__ ("elpm %0,Z+\n" : "=r" (ch), "=z" (address): "1" (address));
|
|
|
+#else
|
|
|
+ // read a Flash byte and increment the address
|
|
|
+ __asm__ ("lpm %0,Z+\n" : "=r" (ch), "=z" (address): "1" (address));
|
|
|
+#endif
|
|
|
+ putch(ch);
|
|
|
+ } while (--length);
|
|
|
+ break;
|
|
|
+ } // switch
|
|
|
+}
|