1069 lines
38 KiB
C
1069 lines
38 KiB
C
|
//////////////////////////////////////////////////////////////////////////
|
|||
|
//
|
|||
|
// DualOptiboot
|
|||
|
// Optiboot based custom bootloader for Moteino
|
|||
|
// Enables wireless programming of Moteino wireless arduino clone
|
|||
|
// Copyright Felix Rusu (2013), felix@lowpowerlab.com
|
|||
|
// http://lowpowerlab.com/Moteino
|
|||
|
//
|
|||
|
//////////////////////////////////////////////////////////////////////////
|
|||
|
//
|
|||
|
// 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
|
|||
|
//
|
|||
|
// Please maintain this license information along with authorship
|
|||
|
// and copyright notices in any redistribution of this code
|
|||
|
//
|
|||
|
//////////////////////////////////////////////////////////////////////////
|
|||
|
//
|
|||
|
// This Optiboot version is modified to add the capability of reflashing
|
|||
|
// from an external SPI flash memory chip. As configured this will work
|
|||
|
// with Moteino (www.lowpowerlab.com/Moteino) provided a SPI flash chip
|
|||
|
// is present on the dedicated onboard footprint.
|
|||
|
// Summary of how this Optiboot version works:
|
|||
|
// - it looks for an external flash chip
|
|||
|
// - if one is found (SPI returns valid data) it will further look
|
|||
|
// for a new sketch flash image signature and size
|
|||
|
// starting at address 0: FLXIMG:NN:XXXXXXXXXXX
|
|||
|
// where: - 'FLXIMG' is fixed signature indicating FLASH chip
|
|||
|
// contains a valid new flash image to be burned
|
|||
|
// - 'NN' are 2 size bytes (uint_16) indicating how long the
|
|||
|
// new flash image is (how many bytes to read), max 65536Bytes
|
|||
|
// - 'XXXXXX' are the de-hexified bytes of the flash
|
|||
|
// pages to be burned
|
|||
|
// - ':' colons have fixed positions (delimiters)
|
|||
|
// - if no valid signature/size are found, it will skip and
|
|||
|
// function as it normally would (listen to STK500 protocol on serial port)
|
|||
|
//
|
|||
|
// The added code will result in a compiled size of just under 1kb
|
|||
|
// (Originally Optiboot takes just under 0.5kb)
|
|||
|
// - Examples at: http://lowpowerlab.com/blog/category/moteino/wireless-programming/
|
|||
|
// - Tested on atmega328P and atmega644/1284P
|
|||
|
// - Limited to 31K sketches for atmega328p and 64K sketches for atmega1284P.
|
|||
|
//////////////////////////////////////////////////////////////////////////
|
|||
|
/**********************************************************/
|
|||
|
/* 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 */
|
|||
|
/* Background page erasing speeds up programming */
|
|||
|
/* 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 */
|
|||
|
/* */
|
|||
|
/* 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: */
|
|||
|
/* */
|
|||
|
/**********************************************************/
|
|||
|
/* */
|
|||
|
/* BIG_BOOT: */
|
|||
|
/* Build a 1k bootloader, not 512 bytes. This turns on */
|
|||
|
/* extra functionality. */
|
|||
|
/* */
|
|||
|
/* BAUD_RATE: */
|
|||
|
/* Set bootloader baud rate. */
|
|||
|
/* */
|
|||
|
/* LUDICROUS_SPEED: */
|
|||
|
/* 230400 baud :-) */
|
|||
|
/* */
|
|||
|
/* 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: */
|
|||
|
/* */
|
|||
|
/* 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 LED_DATA_FLASH 1
|
|||
|
#define OPTIBOOT_MAJVER 5
|
|||
|
#define OPTIBOOT_MINVER 0
|
|||
|
|
|||
|
#define MAKESTR(a) #a
|
|||
|
#define MAKEVER(a, b) MAKESTR(a*256+b)
|
|||
|
|
|||
|
asm(" .section .version\n"
|
|||
|
"optiboot_version: .word " MAKEVER(OPTIBOOT_MAJVER, OPTIBOOT_MINVER) "\n"
|
|||
|
" .section .text\n");
|
|||
|
|
|||
|
#include <inttypes.h>
|
|||
|
#include <avr/io.h>
|
|||
|
#include <avr/pgmspace.h>
|
|||
|
|
|||
|
// <avr/boot.h> uses sts instructions, but this version uses out instructions
|
|||
|
// This saves cycles and program memory.
|
|||
|
#include "boot.h"
|
|||
|
|
|||
|
|
|||
|
// We don't use <avr/wdt.h> as those routines have interrupt overhead we don't need.
|
|||
|
|
|||
|
#include "pin_defs.h"
|
|||
|
#include "stk500.h"
|
|||
|
|
|||
|
#ifndef LED_START_FLASHES
|
|||
|
#define LED_START_FLASHES 1
|
|||
|
#endif
|
|||
|
|
|||
|
#ifdef LUDICROUS_SPEED
|
|||
|
#define BAUD_RATE 230400L
|
|||
|
#endif
|
|||
|
|
|||
|
/* set the UART baud rate defaults */
|
|||
|
#ifndef BAUD_RATE
|
|||
|
#if F_CPU >= 8000000L
|
|||
|
#define BAUD_RATE 115200L // Highest rate Avrdude win32 will support
|
|||
|
#elsif F_CPU >= 1000000L
|
|||
|
#define BAUD_RATE 9600L // 19200 also supported, but with significant error
|
|||
|
#elsif 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)))
|
|||
|
#define BAUD_ERROR (( 100*(BAUD_RATE - BAUD_ACTUAL) ) / BAUD_RATE)
|
|||
|
|
|||
|
#if BAUD_ERROR >= 5
|
|||
|
#error BAUD_RATE error greater than 5%
|
|||
|
#elif BAUD_ERROR <= -5
|
|||
|
#error BAUD_RATE error greater than -5%
|
|||
|
#elif BAUD_ERROR >= 2
|
|||
|
#warning BAUD_RATE error greater than 2%
|
|||
|
#elif BAUD_ERROR <= -2
|
|||
|
#warning BAUD_RATE error greater than -2%
|
|||
|
#endif
|
|||
|
|
|||
|
#if 0
|
|||
|
/* Switch in soft UART for hard baud rates */
|
|||
|
/*
|
|||
|
* I don't understand what this was supposed to accomplish, where the
|
|||
|
* constant "280" came from, or why automatically (and perhaps unexpectedly)
|
|||
|
* switching to a soft uart is a good thing, so I'm undoing this in favor
|
|||
|
* of a range check using the same calc used to config the BRG...
|
|||
|
*/
|
|||
|
#if (F_CPU/BAUD_RATE) > 280 // > 57600 for 16MHz
|
|||
|
#ifndef SOFT_UART
|
|||
|
#define SOFT_UART
|
|||
|
#endif
|
|||
|
#endif
|
|||
|
#else // 0
|
|||
|
#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
|
|||
|
#error Unachievable baud rate (too fast) BAUD_RATE
|
|||
|
#endif // baud rate fastn check
|
|||
|
#endif
|
|||
|
|
|||
|
/* 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
|
|||
|
|
|||
|
/* Function Prototypes */
|
|||
|
/* The main function is in init9, which removes the interrupt vector table */
|
|||
|
/* we don't need. It is also 'naked', which means the compiler does not */
|
|||
|
/* generate any entry or exit code itself. */
|
|||
|
int main(void) __attribute__ ((OS_main)) __attribute__ ((section (".init9")));
|
|||
|
void putch(char);
|
|||
|
uint8_t getch(void);
|
|||
|
static inline void getNch(uint8_t); /* "static inline" is a compiler hint to reduce code size */
|
|||
|
void verifySpace();
|
|||
|
static inline void flash_led(uint8_t);
|
|||
|
uint8_t getLen();
|
|||
|
static inline void watchdogReset();
|
|||
|
void watchdogConfig(uint8_t x);
|
|||
|
#ifdef SOFT_UART
|
|||
|
void uartDelay() __attribute__ ((naked));
|
|||
|
#endif
|
|||
|
void appStart(uint8_t rstFlags) __attribute__ ((naked));
|
|||
|
|
|||
|
/*
|
|||
|
* NRWW memory
|
|||
|
* Addresses below NRWW (Non-Read-While-Write) can be programmed while
|
|||
|
* continuing to run code from flash, slightly speeding up programming
|
|||
|
* time. Beware that Atmel data sheets specify this as a WORD address,
|
|||
|
* while optiboot will be comparing against a 16-bit byte address. This
|
|||
|
* means that on a part with 128kB of memory, the upper part of the lower
|
|||
|
* 64k will get NRWW processing as well, even though it doesn't need it.
|
|||
|
* That's OK. In fact, you can disable the overlapping processing for
|
|||
|
* a part entirely by setting NRWWSTART to zero. This reduces code
|
|||
|
* space a bit, at the expense of being slightly slower, overall.
|
|||
|
*
|
|||
|
* RAMSTART should be self-explanatory. It's bigger on parts with a
|
|||
|
* lot of peripheral registers.
|
|||
|
*/
|
|||
|
#if defined(__AVR_ATmega168__)
|
|||
|
#define RAMSTART (0x100)
|
|||
|
#define NRWWSTART (0x3800)
|
|||
|
#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega32__)
|
|||
|
#define RAMSTART (0x100)
|
|||
|
#define NRWWSTART (0x7000)
|
|||
|
#elif defined (__AVR_ATmega644P__)
|
|||
|
#define RAMSTART (0x100)
|
|||
|
#define NRWWSTART (0xE000)
|
|||
|
// correct for a bug in avr-libc
|
|||
|
#undef SIGNATURE_2
|
|||
|
#define SIGNATURE_2 0x0A
|
|||
|
#elif defined (__AVR_ATmega1284P__)
|
|||
|
#define RAMSTART (0x100)
|
|||
|
#define NRWWSTART (0xE000)
|
|||
|
#elif defined(__AVR_ATtiny84__)
|
|||
|
#define RAMSTART (0x100)
|
|||
|
#define NRWWSTART (0x0000)
|
|||
|
#elif defined(__AVR_ATmega1280__)
|
|||
|
#define RAMSTART (0x200)
|
|||
|
#define NRWWSTART (0xE000)
|
|||
|
#elif defined(__AVR_ATmega8__) || defined(__AVR_ATmega88__)
|
|||
|
#define RAMSTART (0x100)
|
|||
|
#define NRWWSTART (0x1800)
|
|||
|
#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))
|
|||
|
#ifdef VIRTUAL_BOOT_PARTITION
|
|||
|
#define rstVect (*(uint16_t*)(RAMSTART+SPM_PAGESIZE*2+4))
|
|||
|
#define wdtVect (*(uint16_t*)(RAMSTART+SPM_PAGESIZE*2+6))
|
|||
|
#endif
|
|||
|
|
|||
|
/*
|
|||
|
* Handle devices with up to 4 uarts (eg m1280.) Rather inelegantly.
|
|||
|
* Note that mega8/m32 still needs special handling, because ubrr is handled
|
|||
|
* differently.
|
|||
|
*/
|
|||
|
#if UART == 0
|
|||
|
# define UART_SRA UCSR0A
|
|||
|
# define UART_SRB UCSR0B
|
|||
|
# define UART_SRC UCSR0C
|
|||
|
# define UART_SRL UBRR0L
|
|||
|
# define UART_UDR UDR0
|
|||
|
#elif UART == 1
|
|||
|
#if !defined(UDR1)
|
|||
|
#error UART == 1, but no UART1 on device
|
|||
|
#endif
|
|||
|
# define UART_SRA UCSR1A
|
|||
|
# define UART_SRB UCSR1B
|
|||
|
# define UART_SRC UCSR1C
|
|||
|
# define UART_SRL UBRR1L
|
|||
|
# define UART_UDR UDR1
|
|||
|
#elif UART == 2
|
|||
|
#if !defined(UDR2)
|
|||
|
#error UART == 2, but no UART2 on device
|
|||
|
#endif
|
|||
|
# define UART_SRA UCSR2A
|
|||
|
# define UART_SRB UCSR2B
|
|||
|
# define UART_SRC UCSR2C
|
|||
|
# define UART_SRL UBRR2L
|
|||
|
# define UART_UDR UDR2
|
|||
|
#elif UART == 3
|
|||
|
#if !defined(UDR1)
|
|||
|
#error UART == 3, but no UART3 on device
|
|||
|
#endif
|
|||
|
# define UART_SRA UCSR3A
|
|||
|
# define UART_SRB UCSR3B
|
|||
|
# define UART_SRC UCSR3C
|
|||
|
# define UART_SRL UBRR3L
|
|||
|
# define UART_UDR UDR3
|
|||
|
#endif
|
|||
|
|
|||
|
/******************* SPI FLASH Code **********************************/
|
|||
|
// This code will handle the reading/erasing the external SPI FLASH memory
|
|||
|
// assumed to have the SPI_CS on D8 on Moteino (Atmega328P)
|
|||
|
#define SPI_MODE0 0x00
|
|||
|
#define SPI_MODE_MASK 0x0C // CPOL = bit 3, CPHA = bit 2 on SPCR
|
|||
|
#define SPI_CLOCK_MASK 0x03 // SPR1 = bit 1, SPR0 = bit 0 on SPCR
|
|||
|
#define SPI_2XCLOCK_MASK 0x01 // SPI2X = bit 0 on SPSR
|
|||
|
#define SPI_CLOCK_DIV2 0x04
|
|||
|
|
|||
|
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega88) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega88__)
|
|||
|
#define FLASHSS_DDR DDRB
|
|||
|
#define FLASHSS_PORT PORTB
|
|||
|
#define FLASHSS PINB0
|
|||
|
#define SS PINB2
|
|||
|
#elif defined (__AVR_ATmega1284P__) || defined (__AVR_ATmega644P__)
|
|||
|
#define FLASHSS_DDR DDRC
|
|||
|
#define FLASHSS_PORT PORTC
|
|||
|
#define FLASHSS PINC7
|
|||
|
#define SS PINB4
|
|||
|
#endif
|
|||
|
|
|||
|
#define FLASH_SELECT { FLASHSS_PORT &= ~(_BV(FLASHSS)); }
|
|||
|
#define FLASH_UNSELECT { FLASHSS_PORT |= _BV(FLASHSS); }
|
|||
|
|
|||
|
#define SPIFLASH_STATUSWRITE 0x01 // write status register
|
|||
|
#define SPIFLASH_STATUSREAD 0x05 // read status register
|
|||
|
#define SPIFLASH_WRITEENABLE 0x06 // write enable
|
|||
|
#define SPIFLASH_ARRAYREADLOWFREQ 0x03 // read array (low frequency)
|
|||
|
#define SPIFLASH_BLOCKERASE_32K 0x52 // erase one 32K block of flash memory
|
|||
|
#define SPIFLASH_BLOCKERASE_64K 0xD8 // erase one 32K block of flash memory
|
|||
|
#define SPIFLASH_JEDECID 0x9F // read JEDEC ID
|
|||
|
//#define DEBUG_ON // uncomment to enable Serial debugging
|
|||
|
// (will output different characters depending on which path the bootloader takes)
|
|||
|
|
|||
|
uint8_t SPI_transfer(uint8_t _data) {
|
|||
|
SPDR = _data;
|
|||
|
while (!(SPSR & _BV(SPIF)));
|
|||
|
return SPDR;
|
|||
|
}
|
|||
|
|
|||
|
uint8_t FLASH_busy()
|
|||
|
{
|
|||
|
FLASH_SELECT;
|
|||
|
SPI_transfer(SPIFLASH_STATUSREAD);
|
|||
|
uint8_t status = SPI_transfer(0);
|
|||
|
FLASH_UNSELECT;
|
|||
|
return status & 1;
|
|||
|
}
|
|||
|
|
|||
|
void FLASH_command(uint8_t cmd, uint8_t isWrite){
|
|||
|
if (isWrite)
|
|||
|
{
|
|||
|
FLASH_command(SPIFLASH_WRITEENABLE, 0); // Write Enable
|
|||
|
FLASH_UNSELECT;
|
|||
|
}
|
|||
|
while(FLASH_busy()); //wait for chip to become available
|
|||
|
FLASH_SELECT;
|
|||
|
SPI_transfer(cmd);
|
|||
|
}
|
|||
|
|
|||
|
uint8_t FLASH_readByte(uint32_t addr) {
|
|||
|
FLASH_command(SPIFLASH_ARRAYREADLOWFREQ, 0);
|
|||
|
SPI_transfer(addr >> 16);
|
|||
|
SPI_transfer(addr >> 8);
|
|||
|
SPI_transfer(addr);
|
|||
|
//SPI.transfer(0); //"dont care", needed with SPIFLASH_ARRAYREAD command only
|
|||
|
uint8_t result = SPI_transfer(0);
|
|||
|
FLASH_UNSELECT;
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
void CheckFlashImage() {
|
|||
|
#ifdef DEBUG_ON
|
|||
|
putch('F');
|
|||
|
#endif
|
|||
|
watchdogConfig(WATCHDOG_OFF);
|
|||
|
|
|||
|
//SPI INIT
|
|||
|
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega88) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega88__)
|
|||
|
DDRB |= _BV(FLASHSS) | _BV(SS) | _BV(PB3) | _BV(PB5); //OUTPUTS for FLASH_SS and SS, MOSI, SCK
|
|||
|
FLASH_UNSELECT; //unselect FLASH chip
|
|||
|
PORTB |= _BV(SS); //set SS HIGH
|
|||
|
#elif defined (__AVR_ATmega1284P__) || defined (__AVR_ATmega644P__)
|
|||
|
DDRC |= _BV(FLASHSS); //OUTPUT for FLASH_SS
|
|||
|
DDRB |= _BV(SS) | _BV(PB5) | _BV(PB7); //OUTPUTS for SS, MOSI, SCK
|
|||
|
FLASH_UNSELECT; //unselect FLASH chip
|
|||
|
PORTB |= _BV(SS); //set SS HIGH
|
|||
|
#endif
|
|||
|
|
|||
|
//SPCR &= ~(_BV(DORD)); //MSB first
|
|||
|
//SPCR = (SPCR & ~SPI_MODE_MASK) | SPI_MODE0 ; //SPI MODE 0
|
|||
|
//SPCR = (SPCR & ~SPI_CLOCK_MASK) | (SPI_CLOCK_DIV2 & SPI_CLOCK_MASK); //clock divider = 2
|
|||
|
//SPSR = (SPSR & ~SPI_2XCLOCK_MASK) | ((SPI_CLOCK_DIV2 >> 2) & SPI_2XCLOCK_MASK);
|
|||
|
|
|||
|
// Warning: if the SS pin ever becomes a LOW INPUT then SPI automatically switches to Slave, so the data direction of the SS pin MUST be kept as OUTPUT.
|
|||
|
SPCR |= _BV(MSTR) | _BV(SPE); //enable SPI and set SPI to MASTER mode
|
|||
|
|
|||
|
//read first byte of JEDECID, if chip is present it should return a non-0 and non-FF value
|
|||
|
FLASH_SELECT;
|
|||
|
SPI_transfer(SPIFLASH_JEDECID);
|
|||
|
uint8_t deviceId = SPI_transfer(0);
|
|||
|
FLASH_UNSELECT;
|
|||
|
if (deviceId==0 || deviceId==0xFF) return;
|
|||
|
|
|||
|
//global unprotect
|
|||
|
FLASH_command(SPIFLASH_STATUSWRITE, 1);
|
|||
|
SPI_transfer(0);
|
|||
|
FLASH_UNSELECT;
|
|||
|
|
|||
|
//check if any flash image exists on external FLASH chip
|
|||
|
if (FLASH_readByte(0)=='F' && FLASH_readByte(1)=='L' && FLASH_readByte(2)=='X' && FLASH_readByte(6)==':' && FLASH_readByte(9)==':')
|
|||
|
{
|
|||
|
#ifdef DEBUG_ON
|
|||
|
putch('L');
|
|||
|
#endif
|
|||
|
|
|||
|
uint16_t imagesize = (FLASH_readByte(7)<<8) | FLASH_readByte(8);
|
|||
|
if (imagesize%2!=0) return; //basic check that we got even # of bytes
|
|||
|
|
|||
|
uint16_t b, i, nextAddress=0;
|
|||
|
|
|||
|
LED_PIN |= _BV(LED);
|
|||
|
for (i=0; i<imagesize; i+=2)
|
|||
|
{
|
|||
|
#ifdef DEBUG_ON
|
|||
|
putch('*');
|
|||
|
#endif
|
|||
|
|
|||
|
//read 2 bytes (16 bits) from flash image, transfer them to page buffer
|
|||
|
b = FLASH_readByte(i+10); // flash image starts at position 10 on the external flash memory: FLX:XX:FLASH_IMAGE_BYTES_HERE...... (XX = two size bytes)
|
|||
|
b |= FLASH_readByte(i+11) << 8; //bytes are stored big endian on external flash, need to flip the bytes to little endian for transfer to internal flash
|
|||
|
__boot_page_fill_short((uint16_t)(void*)i,b);
|
|||
|
|
|||
|
//when 1 page is full (or we're on the last page), write it to the internal flash memory
|
|||
|
if ((i+2)%SPM_PAGESIZE==0 || (i+2==imagesize))
|
|||
|
{
|
|||
|
__boot_page_erase_short((uint16_t)(void*)nextAddress); //(i+2-SPM_PAGESIZE)
|
|||
|
boot_spm_busy_wait();
|
|||
|
// Write from programming buffer
|
|||
|
__boot_page_write_short((uint16_t)(void*)nextAddress ); //(i+2-SPM_PAGESIZE)
|
|||
|
boot_spm_busy_wait();
|
|||
|
nextAddress += SPM_PAGESIZE;
|
|||
|
}
|
|||
|
}
|
|||
|
LED_PIN &= ~_BV(LED);
|
|||
|
|
|||
|
#if defined(RWWSRE)
|
|||
|
// Reenable read access to flash
|
|||
|
boot_rww_enable();
|
|||
|
#endif
|
|||
|
|
|||
|
#ifdef DEBUG_ON
|
|||
|
putch('E');
|
|||
|
#endif
|
|||
|
|
|||
|
//erase the first 32/64K block where flash image resided (atmega328 should be less than 31K, and atmega1284 can be up to 64K)
|
|||
|
if (imagesize+10<=32768) FLASH_command(SPIFLASH_BLOCKERASE_32K, 1);
|
|||
|
else FLASH_command(SPIFLASH_BLOCKERASE_64K, 1);
|
|||
|
SPI_transfer(0);
|
|||
|
SPI_transfer(0);
|
|||
|
SPI_transfer(0);
|
|||
|
FLASH_UNSELECT;
|
|||
|
|
|||
|
//now trigger a watchdog reset
|
|||
|
watchdogConfig(WATCHDOG_16MS); // short WDT timeout
|
|||
|
while (1); // and busy-loop so that WD causes a reset and app start
|
|||
|
}
|
|||
|
#ifdef DEBUG_ON
|
|||
|
putch('X');
|
|||
|
#endif
|
|||
|
}
|
|||
|
/******************* END SPI FLASH Code ****************************/
|
|||
|
|
|||
|
|
|||
|
/* 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 uint8_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__)
|
|||
|
SP=RAMEND; // This is done by hardware reset
|
|||
|
#endif
|
|||
|
|
|||
|
// Adaboot no-wait mod
|
|||
|
ch = MCUSR;
|
|||
|
MCUSR = 0;
|
|||
|
|
|||
|
#ifdef DEBUG_ON
|
|||
|
putch('S');
|
|||
|
#endif
|
|||
|
|
|||
|
if (!(ch & _BV(EXTRF))) //if not external reset
|
|||
|
{
|
|||
|
if (ch & _BV(WDRF)) //if reset by watchdog
|
|||
|
CheckFlashImage();
|
|||
|
#ifdef DEBUG_ON
|
|||
|
putch('A');
|
|||
|
#endif
|
|||
|
SPCR &= ~_BV(SPE) & ~_BV(MSTR);
|
|||
|
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__)
|
|||
|
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 500ms
|
|||
|
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 */
|
|||
|
for (;;) {
|
|||
|
/* get character from UART */
|
|||
|
ch = getch();
|
|||
|
|
|||
|
if(ch == STK_GET_PARAMETER) {
|
|||
|
unsigned char which = getch();
|
|||
|
verifySpace();
|
|||
|
if (which == 0x82) {
|
|||
|
/*
|
|||
|
* Send optiboot version as "minor SW version"
|
|||
|
*/
|
|||
|
putch(OPTIBOOT_MINVER);
|
|||
|
} else if (which == 0x81) {
|
|||
|
putch(OPTIBOOT_MAJVER);
|
|||
|
} 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 *bufPtr;
|
|||
|
uint16_t addrPtr;
|
|||
|
|
|||
|
getch(); /* getlen() */
|
|||
|
length = getch();
|
|||
|
getch();
|
|||
|
|
|||
|
// If we are in RWW section, immediately start page erase
|
|||
|
if (address < NRWWSTART) __boot_page_erase_short((uint16_t)(void*)address);
|
|||
|
|
|||
|
// While that is going on, read in page contents
|
|||
|
bufPtr = buff;
|
|||
|
do *bufPtr++ = getch();
|
|||
|
while (--length);
|
|||
|
|
|||
|
// If we are in NRWW section, page erase has to be delayed until now.
|
|||
|
// Todo: Take RAMPZ into account (not doing so just means that we will
|
|||
|
// treat the top of both "pages" of flash as NRWW, for a slight speed
|
|||
|
// decrease, so fixing this is not urgent.)
|
|||
|
if (address >= NRWWSTART) __boot_page_erase_short((uint16_t)(void*)address);
|
|||
|
|
|||
|
// Read command terminator, start reply
|
|||
|
verifySpace();
|
|||
|
|
|||
|
// If only a partial page is to be programmed, the erase might not be complete.
|
|||
|
// So check that here
|
|||
|
boot_spm_busy_wait();
|
|||
|
|
|||
|
#ifdef VIRTUAL_BOOT_PARTITION
|
|||
|
if ((uint16_t)(void*)address == 0) {
|
|||
|
// This is the reset vector page. We need to live-patch the code so the
|
|||
|
// bootloader runs.
|
|||
|
//
|
|||
|
// Move RESET vector to WDT vector
|
|||
|
uint16_t vect = buff[0] | (buff[1]<<8);
|
|||
|
rstVect = vect;
|
|||
|
wdtVect = buff[8] | (buff[9]<<8);
|
|||
|
vect -= 4; // Instruction is a relative jump (rjmp), so recalculate.
|
|||
|
buff[8] = vect & 0xff;
|
|||
|
buff[9] = vect >> 8;
|
|||
|
|
|||
|
// Add jump to bootloader at RESET vector
|
|||
|
buff[0] = 0x7f;
|
|||
|
buff[1] = 0xce; // rjmp 0x1d00 instruction
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
// Copy buffer into programming buffer
|
|||
|
bufPtr = buff;
|
|||
|
addrPtr = (uint16_t)(void*)address;
|
|||
|
ch = SPM_PAGESIZE / 2;
|
|||
|
do {
|
|||
|
uint16_t a;
|
|||
|
a = *bufPtr++;
|
|||
|
a |= (*bufPtr++) << 8;
|
|||
|
__boot_page_fill_short((uint16_t)(void*)addrPtr,a);
|
|||
|
addrPtr += 2;
|
|||
|
} while (--ch);
|
|||
|
|
|||
|
// Write from programming buffer
|
|||
|
__boot_page_write_short((uint16_t)(void*)address);
|
|||
|
boot_spm_busy_wait();
|
|||
|
|
|||
|
#if defined(RWWSRE)
|
|||
|
// Reenable read access to flash
|
|||
|
boot_rww_enable();
|
|||
|
#endif
|
|||
|
|
|||
|
}
|
|||
|
/* Read memory block mode, length is big endian. */
|
|||
|
else if(ch == STK_READ_PAGE) {
|
|||
|
// READ PAGE - we only read flash
|
|||
|
getch(); /* getlen() */
|
|||
|
length = getch();
|
|||
|
getch();
|
|||
|
|
|||
|
verifySpace();
|
|||
|
do {
|
|||
|
#ifdef VIRTUAL_BOOT_PARTITION
|
|||
|
// Undo vector patch in bottom page so verify passes
|
|||
|
if (address == 0) ch=rstVect & 0xff;
|
|||
|
else if (address == 1) ch=rstVect >> 8;
|
|||
|
else if (address == 8) ch=wdtVect & 0xff;
|
|||
|
else if (address == 9) ch=wdtVect >> 8;
|
|||
|
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);
|
|||
|
}
|
|||
|
|
|||
|
/* 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__)
|
|||
|
LED_PORT ^= _BV(LED);
|
|||
|
#else
|
|||
|
LED_PIN |= _BV(LED);
|
|||
|
#endif
|
|||
|
#endif
|
|||
|
|
|||
|
#ifdef SOFT_UART
|
|||
|
__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__)
|
|||
|
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__)
|
|||
|
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);
|
|||
|
__asm__ __volatile__ (
|
|||
|
#ifdef VIRTUAL_BOOT_PARTITION
|
|||
|
// Jump to WDT vector
|
|||
|
"ldi r30,4\n"
|
|||
|
"clr r31\n"
|
|||
|
#else
|
|||
|
// Jump to RST vector
|
|||
|
"clr r30\n"
|
|||
|
"clr r31\n"
|
|||
|
#endif
|
|||
|
"ijmp\n"
|
|||
|
);
|
|||
|
}
|