Arduino解析

  Arduino是一个基于开源代码的快速电子原型开发平台,其由各种型号的Arduino开发板及Arduino IDE组成(官网中文社区)。目前的Arduino开发板是基于AVR单片机系统开发,并在其基础上作了较完善的软硬件封装,目的是尽量屏蔽底层硬件的影响便于快速开发。
 
1. Arduino开发板硬件解析,基于Arduino UNO R3.
  原理图下载:点我
  实物图如下:
  

Arduino UNO R3

  
  开发板硬件主要由 USB 控制器,AVR单片机 Atmega 328P 系统,电源系统及相应的一些外围电路组成。
  1. 电源系统,开发板可采用“DC-5”或“USB接口”供电。
  2. USB控制器,从原理图可以看出其与Atmega 328P的USART串口相连,用于传输数据;端口PD7用于控制Atmega 328P的复位,已便在Arduino IDE下载程序时,使单片机复位进入Bootloader程序。
  通常的AVR单片机开发采用ISP接口下载程序,需要专门的ISP下载器。Arduino为了简化外围设备,采用USB串口下载程序。其原理是将AVR单片机系统程序分为“Bootloader”和“APP”两部分,“Bootloader”负责将USB串口接收的程序通过自编程(IAP)烧写至Flash,并在一定条件下控制程序跳转至“APP”区执行;“APP”是真正用户编写的程序。(PS: 有些单片机厂商在出厂前已将“Bootloader”固化到单片机,如 STM32 系列单片机,其可通过串口,I2C 等接口程接收用户程序,用户通过控制 BOOT 相关引脚的电平,控制程序跳转。)
  3. AVR 单片机 Atmega 328P 系统,含时钟电路、复位电路、I/O端口。
  
  待续。。。
  
2. Arduino Bootloader源码分析
  在Ardunino IDE的安装目录下,个人目录:“E:\Arduino\hardware\arduino\avr\bootloaders\atmega”。
  Bootloader主要实现功能:1. 接收外部发送来的程序;2. 将程序烧写到Flash;3. 控制程序跳转。
  
  待续。。。
 

/**********************************************************/
/* Serial Bootloader for Atmel megaAVR Controllers        */
/*                                                        */
/* tested with ATmegarduino8, ATmega128 and ATmega168  */
/* should work with other mega's, see code for details    */
/*                                                        */
/* ATmegaBOOT.c                                           */
/*                                                        */
/*                                                        */
/* 20090308: integrated Mega changes into main bootloader */
/*           source by D. Mellis                          */
/* 20080930: hacked for Arduino Mega (with the 1280       */
/*           processor, backwards compatible)             */
/*           by D. Cuartielles                            */
/* 20070626: hacked for Arduino Diecimila (which auto-    */
/*           resets when a USB connection is made to it)  */
/*           by D. Mellis                                 */
/* 20060802: hacked for Arduino by D. Cuartielles         */
/*           based on a previous hack by D. Mellis        */
/*           and D. Cuartielles                           */
/*                                                        */
/* Monitor and debug functions were added to the original */
/* code by Dr. Erik Lins, chip45.com. (See below)         */
/*                                                        */
/* Thanks to Karl Pitrich for fixing a bootloader pin     */
/* problem and more informative LED blinking!             */
/*                                                        */
/* For the latest version see:                            */
/* http://www.chip45.com/                                 */
/*                                                        */
/* ------------------------------------------------------ */
/*                                                        */
/* based on stk500boot.c                                  */
/* Copyright (c) 2003, Jason P. Kyle                      */
/* All rights reserved.                                   */
/* see avr1.org for original file and information         */
/*                                                        */
/* 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                    */
/*                                                        */
/* Target = Atmel AVR m128,m64,m32,m16,m8,m162,m163,m169, */
/* m8515,m8535. ATmega161 has a very small boot block so  */
/* isn't supported.                                       */
/*                                                        */
/* Tested with m168                                       */
/**********************************************************/


/* some includes */
#include <inttypes.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <util/delay.h>

/* the current avr-libc eeprom functions do not support the ATmega168 */
/* own eeprom write/read functions are used instead */
#if !defined(__AVR_ATmega168__) || !defined(__AVR_ATmega328P__) || !defined(__AVR_ATmega328__)
#include <avr/eeprom.h>
#endif

/* Use the F_CPU defined in Makefile */

/* 20060803: hacked by DojoCorp */
/* 20070626: hacked by David A. Mellis to decrease waiting time for auto-reset */
/* set the waiting time for the bootloader */
/* get this from the Makefile instead */
/* #define MAX_TIME_COUNT (F_CPU>>4) */

/* 20070707: hacked by David A. Mellis - after this many errors give up and launch application */
#define MAX_ERROR_COUNT 5

/* set the UART baud rate */
/* 20060803: hacked by DojoCorp */
//#define BAUD_RATE   115200
#ifndef BAUD_RATE
#define BAUD_RATE   19200
#endif


/* SW_MAJOR and MINOR needs to be updated from time to time to avoid warning message from AVR Studio */
/* never allow AVR Studio to do an update !!!! */
#define HW_VER   0x02
#define SW_MAJOR 0x01
#define SW_MINOR 0x10


/* Adjust to suit whatever pin your hardware uses to enter the bootloader */
/* ATmega128 has two UARTS so two pins are used to enter bootloader and select UART */
/* ATmega1280 has four UARTS, but for Arduino Mega, we will only use RXD0 to get code */
/* BL0... means UART0, BL1... means UART1 */
#ifdef __AVR_ATmega128__
#define BL_DDR  DDRF
#define BL_PORT PORTF
#define BL_PIN  PINF
#define BL0     PINF7
#define BL1     PINF6
#elif defined __AVR_ATmega1280__ 
/* we just don't do anything for the MEGA and enter bootloader on reset anyway*/
#else
/* other ATmegas have only one UART, so only one pin is defined to enter bootloader */
#define BL_DDR  DDRD
#define BL_PORT PORTD
#define BL_PIN  PIND
#define BL      PIND6
#endif


/* onboard LED is used to indicate, that the bootloader was entered (3x flashing) */
/* if monitor functions are included, LED goes on after monitor was entered */
#if defined __AVR_ATmega128__ || defined __AVR_ATmega1280__
/* Onboard LED is connected to pin PB7 (e.g. Crumb128, PROBOmega128, Savvy128, Arduino Mega) */
#define LED_DDR  DDRB
#define LED_PORT PORTB
#define LED_PIN  PINB
#define LED      PINB7
#else
/* Onboard LED is connected to pin PB5 in Arduino NG, Diecimila, and Duomilanuove */ 
/* other boards like e.g. Crumb8, Crumb168 are using PB2 */
#define LED_DDR  DDRB
#define LED_PORT PORTB
#define LED_PIN  PINB
#define LED      PINB5
#endif


/* monitor functions will only be compiled when using ATmega128, due to bootblock size constraints */
#if defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__)
#define MONITOR 1
#endif


/* define various device id's */
/* manufacturer byte is always the same */
#define SIG1    0x1E    // Yep, Atmel is the only manufacturer of AVR micros.  Single source :(

#if defined __AVR_ATmega1280__
#define SIG2    0x97
#define SIG3    0x03
#define PAGE_SIZE   0x80U   //128 words

#elif defined __AVR_ATmega1281__
#define SIG2    0x97
#define SIG3    0x04
#define PAGE_SIZE   0x80U   //128 words

#elif defined __AVR_ATmega128__
#define SIG2    0x97
#define SIG3    0x02
#define PAGE_SIZE   0x80U   //128 words

#elif defined __AVR_ATmega64__
#define SIG2    0x96
#define SIG3    0x02
#define PAGE_SIZE   0x80U   //128 words

#elif defined __AVR_ATmega32__
#define SIG2    0x95
#define SIG3    0x02
#define PAGE_SIZE   0x40U   //64 words

#elif defined __AVR_ATmega16__
#define SIG2    0x94
#define SIG3    0x03
#define PAGE_SIZE   0x40U   //64 words

#elif defined __AVR_ATmega8__
#define SIG2    0x93
#define SIG3    0x07
#define PAGE_SIZE   0x20U   //32 words

#elif defined __AVR_ATmega88__
#define SIG2    0x93
#define SIG3    0x0a
#define PAGE_SIZE   0x20U   //32 words

#elif defined __AVR_ATmega168__
#define SIG2    0x94
#define SIG3    0x06
#define PAGE_SIZE   0x40U   //64 words

#elif defined __AVR_ATmega328P__
#define SIG2    0x95
#define SIG3    0x0F
#define PAGE_SIZE   0x40U   //64 words

#elif defined __AVR_ATmega328__
#define SIG2    0x95
#define SIG3    0x14
#define PAGE_SIZE   0x40U   //64 words

#elif defined __AVR_ATmega162__
#define SIG2    0x94
#define SIG3    0x04
#define PAGE_SIZE   0x40U   //64 words

#elif defined __AVR_ATmega163__
#define SIG2    0x94
#define SIG3    0x02
#define PAGE_SIZE   0x40U   //64 words

#elif defined __AVR_ATmega169__
#define SIG2    0x94
#define SIG3    0x05
#define PAGE_SIZE   0x40U   //64 words

#elif defined __AVR_ATmega8515__
#define SIG2    0x93
#define SIG3    0x06
#define PAGE_SIZE   0x20U   //32 words

#elif defined __AVR_ATmega8535__
#define SIG2    0x93
#define SIG3    0x08
#define PAGE_SIZE   0x20U   //32 words
#endif


/* function prototypes */
void putch(char);
char getch(void);
void getNch(uint8_t);
void byte_response(uint8_t);
void nothing_response(void);
char gethex(void);
void puthex(char);
void flash_led(uint8_t);

/* some variables */
union address_union {
    uint16_t word;
    uint8_t  byte[2];
} address;

union length_union {
    uint16_t word;
    uint8_t  byte[2];
} length;

struct flags_struct {
    unsigned eeprom : 1;
    unsigned rampz  : 1;
} flags;

uint8_t buff[256];
uint8_t address_high;

uint8_t pagesz=0x80;

uint8_t i;
uint8_t bootuart = 0;

uint8_t error_count = 0;

void (*app_start)(void) = 0x0000;


/* main program starts here */
int main(void)
{
    uint8_t ch,ch2;
    uint16_t w;

#ifdef WATCHDOG_MODS
    ch = MCUSR;
    MCUSR = 0;

    WDTCSR |= _BV(WDCE) | _BV(WDE);
    WDTCSR = 0;

    // Check if the WDT was used to reset, in which case we dont bootload and skip straight to the code. woot.
    if (! (ch &  _BV(EXTRF))) // if its a not an external reset...
        app_start();  // skip bootloader
#else
    asm volatile("nop\n\t");
#endif

    /* set pin direction for bootloader pin and enable pullup */
    /* for ATmega128, two pins need to be initialized */
#ifdef __AVR_ATmega128__
    BL_DDR &= ~_BV(BL0);
    BL_DDR &= ~_BV(BL1);
    BL_PORT |= _BV(BL0);
    BL_PORT |= _BV(BL1);
#else
    /* We run the bootloader regardless of the state of this pin.  Thus, don't
    put it in a different state than the other pins.  --DAM, 070709
    This also applies to Arduino Mega -- DC, 080930
    BL_DDR &= ~_BV(BL);
    BL_PORT |= _BV(BL);
    */
#endif


#ifdef __AVR_ATmega128__
    /* check which UART should be used for booting */
    if(bit_is_clear(BL_PIN, BL0)) {
        bootuart = 1;
    }
    else if(bit_is_clear(BL_PIN, BL1)) {
        bootuart = 2;
    }
#endif

#if defined __AVR_ATmega1280__
    /* the mega1280 chip has four serial ports ... we could eventually use any of them, or not? */
    /* however, we don't wanna confuse people, to avoid making a mess, we will stick to RXD0, TXD0 */
    bootuart = 1;
#endif

    /* check if flash is programmed already, if not start bootloader anyway */
    if(pgm_read_byte_near(0x0000) != 0xFF) {

#ifdef __AVR_ATmega128__
    /* no UART was selected, start application */
    if(!bootuart) {
        app_start();
    }
#else
    /* check if bootloader pin is set low */
    /* we don't start this part neither for the m8, nor m168 */
    //if(bit_is_set(BL_PIN, BL)) {
    //      app_start();
    //    }
#endif
    }

#ifdef __AVR_ATmega128__    
    /* no bootuart was selected, default to uart 0 */
    if(!bootuart) {
        bootuart = 1;
    }
#endif


    /* initialize UART(s) depending on CPU defined */
#if defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__)
    if(bootuart == 1) {
        UBRR0L = (uint8_t)(F_CPU/(BAUD_RATE*16L)-1);
        UBRR0H = (F_CPU/(BAUD_RATE*16L)-1) >> 8;
        UCSR0A = 0x00;
        UCSR0C = 0x06;
        UCSR0B = _BV(TXEN0)|_BV(RXEN0);
    }
    if(bootuart == 2) {
        UBRR1L = (uint8_t)(F_CPU/(BAUD_RATE*16L)-1);
        UBRR1H = (F_CPU/(BAUD_RATE*16L)-1) >> 8;
        UCSR1A = 0x00;
        UCSR1C = 0x06;
        UCSR1B = _BV(TXEN1)|_BV(RXEN1);
    }
#elif defined __AVR_ATmega163__
    UBRR = (uint8_t)(F_CPU/(BAUD_RATE*16L)-1);
    UBRRHI = (F_CPU/(BAUD_RATE*16L)-1) >> 8;
    UCSRA = 0x00;
    UCSRB = _BV(TXEN)|_BV(RXEN);    
#elif defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || defined (__AVR_ATmega328__)

#ifdef DOUBLE_SPEED
    UCSR0A = (1<<U2X0); //Double speed mode USART0
    UBRR0L = (uint8_t)(F_CPU/(BAUD_RATE*8L)-1);
    UBRR0H = (F_CPU/(BAUD_RATE*8L)-1) >> 8;
#else
    UBRR0L = (uint8_t)(F_CPU/(BAUD_RATE*16L)-1);
    UBRR0H = (F_CPU/(BAUD_RATE*16L)-1) >> 8;
#endif

    UCSR0B = (1<<RXEN0) | (1<<TXEN0);
    UCSR0C = (1<<UCSZ00) | (1<<UCSZ01);

    /* Enable internal pull-up resistor on pin D0 (RX), in order
    to supress line noise that prevents the bootloader from
    timing out (DAM: 20070509) */
    DDRD &= ~_BV(PIND0);
    PORTD |= _BV(PIND0);
#elif defined __AVR_ATmega8__
    /* m8 */
    UBRRH = (((F_CPU/BAUD_RATE)/16)-1)>>8;  // set baud rate
    UBRRL = (((F_CPU/BAUD_RATE)/16)-1);
    UCSRB = (1<<RXEN)|(1<<TXEN);  // enable Rx & Tx
    UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);  // config USART; 8N1
#else
    /* m16,m32,m169,m8515,m8535 */
    UBRRL = (uint8_t)(F_CPU/(BAUD_RATE*16L)-1);
    UBRRH = (F_CPU/(BAUD_RATE*16L)-1) >> 8;
    UCSRA = 0x00;
    UCSRC = 0x06;
    UCSRB = _BV(TXEN)|_BV(RXEN);
#endif

#if defined __AVR_ATmega1280__
    /* Enable internal pull-up resistor on pin D0 (RX), in order
    to supress line noise that prevents the bootloader from
    timing out (DAM: 20070509) */
    /* feature added to the Arduino Mega --DC: 080930 */
    DDRE &= ~_BV(PINE0);
    PORTE |= _BV(PINE0);
#endif


    /* set LED pin as output */
    LED_DDR |= _BV(LED);


    /* flash onboard LED to signal entering of bootloader */
#if defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__)
    // 4x for UART0, 5x for UART1
    flash_led(NUM_LED_FLASHES + bootuart);
#else
    flash_led(NUM_LED_FLASHES);
#endif

    /* 20050803: by DojoCorp, this is one of the parts provoking the
         system to stop listening, cancelled from the original */
    //putch('\0');

    /* forever loop */
    for (;;) {

    /* get character from UART */
    ch = getch();

    /* A bunch of if...else if... gives smaller code than switch...case ! */

    /* Hello is anyone home ? */ 
    if(ch=='0') {
        nothing_response();
    }


    /* Request programmer ID */
    /* Not using PROGMEM string due to boot block in m128 being beyond 64kB boundry  */
    /* Would need to selectively manipulate RAMPZ, and it's only 9 characters anyway so who cares.  */
    else if(ch=='1') {
        if (getch() == ' ') {
            putch(0x14);
            putch('A');
            putch('V');
            putch('R');
            putch(' ');
            putch('I');
            putch('S');
            putch('P');
            putch(0x10);
        } else {
            if (++error_count == MAX_ERROR_COUNT)
                app_start();
        }
    }


    /* AVR ISP/STK500 board commands  DON'T CARE so default nothing_response */
    else if(ch=='@') {
        ch2 = getch();
        if (ch2>0x85) getch();
        nothing_response();
    }


    /* AVR ISP/STK500 board requests */
    else if(ch=='A') {
        ch2 = getch();
        if(ch2==0x80) byte_response(HW_VER);        // Hardware version
        else if(ch2==0x81) byte_response(SW_MAJOR); // Software major version
        else if(ch2==0x82) byte_response(SW_MINOR); // Software minor version
        else if(ch2==0x98) byte_response(0x03);     // Unknown but seems to be required by avr studio 3.56
        else byte_response(0x00);               // Covers various unnecessary responses we don't care about
    }


    /* Device Parameters  DON'T CARE, DEVICE IS FIXED  */
    else if(ch=='B') {
        getNch(20);
        nothing_response();
    }


    /* Parallel programming stuff  DON'T CARE  */
    else if(ch=='E') {
        getNch(5);
        nothing_response();
    }


    /* P: Enter programming mode  */
    /* R: Erase device, don't care as we will erase one page at a time anyway.  */
    else if(ch=='P' || ch=='R') {
        nothing_response();
    }


    /* Leave programming mode  */
    else if(ch=='Q') {
        nothing_response();
#ifdef WATCHDOG_MODS
        // autoreset via watchdog (sneaky!)
        WDTCSR = _BV(WDE);
        while (1); // 16 ms
#endif
    }


    /* Set address, little endian. EEPROM in bytes, FLASH in words  */
    /* Perhaps extra address bytes may be added in future to support > 128kB FLASH.  */
    /* This might explain why little endian was used here, big endian used everywhere else.  */
    else if(ch=='U') {
        address.byte[0] = getch();
        address.byte[1] = getch();
        nothing_response();
    }


    /* Universal SPI programming command, disabled.  Would be used for fuses and lock bits.  */
    else if(ch=='V') {
        if (getch() == 0x30) {
            getch();
            ch = getch();
            getch();
            if (ch == 0) {
                byte_response(SIG1);
            } else if (ch == 1) {
                byte_response(SIG2); 
            } else {
                byte_response(SIG3);
            } 
        } else {
            getNch(3);
            byte_response(0x00);
        }
    }


    /* Write memory, length is big endian and is in bytes  */
    else if(ch=='d') {
        length.byte[1] = getch();
        length.byte[0] = getch();
        flags.eeprom = 0;
        if (getch() == 'E') flags.eeprom = 1;
        for (w=0;w<length.word;w++) {
            buff[w] = getch();                          // Store data in buffer, can't keep up with serial data stream whilst programming pages
        }
        if (getch() == ' ') {
            if (flags.eeprom) {                     //Write to EEPROM one byte at a time
                address.word <<= 1;
                for(w=0;w<length.word;w++) {
#if defined(__AVR_ATmega168__)  || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)
                    while(EECR & (1<<EEPE));
                    EEAR = (uint16_t)(void *)address.word;
                    EEDR = buff[w];
                    EECR |= (1<<EEMPE);
                    EECR |= (1<<EEPE);
#else
                    eeprom_write_byte((void *)address.word,buff[w]);
#endif
                    address.word++;
                }           
            }
            else {                          //Write to FLASH one page at a time
                if (address.byte[1]>127) address_high = 0x01;   //Only possible with m128, m256 will need 3rd address byte. FIXME
                else address_high = 0x00;
#if defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega1281__)
                RAMPZ = address_high;
#endif
                address.word = address.word << 1;           //address * 2 -> byte location
                /* if ((length.byte[0] & 0x01) == 0x01) length.word++;  //Even up an odd number of bytes */
                if ((length.byte[0] & 0x01)) length.word++; //Even up an odd number of bytes
                cli();                  //Disable interrupts, just to be sure
#if defined(EEPE)
                while(bit_is_set(EECR,EEPE));           //Wait for previous EEPROM writes to complete
#else
                while(bit_is_set(EECR,EEWE));           //Wait for previous EEPROM writes to complete
#endif
                asm volatile(
                     "clr   r17     \n\t"   //page_word_count
                     "lds   r30,address \n\t"   //Address of FLASH location (in bytes)
                     "lds   r31,address+1   \n\t"
                     "ldi   r28,lo8(buff)   \n\t"   //Start of buffer array in RAM
                     "ldi   r29,hi8(buff)   \n\t"
                     "lds   r24,length  \n\t"   //Length of data to be written (in bytes)
                     "lds   r25,length+1    \n\t"
                     "length_loop:      \n\t"   //Main loop, repeat for number of words in block                                                         
                     "cpi   r17,0x00    \n\t"   //If page_word_count=0 then erase page
                     "brne  no_page_erase   \n\t"                        
                     "wait_spm1:        \n\t"
                     "lds   r16,%0      \n\t"   //Wait for previous spm to complete
                     "andi  r16,1           \n\t"
                     "cpi   r16,1           \n\t"
                     "breq  wait_spm1       \n\t"
                     "ldi   r16,0x03    \n\t"   //Erase page pointed to by Z
                     "sts   %0,r16      \n\t"
                     "spm           \n\t"                            
#ifdef __AVR_ATmega163__
                     ".word 0xFFFF      \n\t"
                     "nop           \n\t"
#endif
                     "wait_spm2:        \n\t"
                     "lds   r16,%0      \n\t"   //Wait for previous spm to complete
                     "andi  r16,1           \n\t"
                     "cpi   r16,1           \n\t"
                     "breq  wait_spm2       \n\t"                                    

                     "ldi   r16,0x11    \n\t"   //Re-enable RWW section
                     "sts   %0,r16      \n\t"                                    
                     "spm           \n\t"
#ifdef __AVR_ATmega163__
                     ".word 0xFFFF      \n\t"
                     "nop           \n\t"
#endif
                     "no_page_erase:        \n\t"                            
                     "ld    r0,Y+       \n\t"   //Write 2 bytes into page buffer
                     "ld    r1,Y+       \n\t"                            

                     "wait_spm3:        \n\t"
                     "lds   r16,%0      \n\t"   //Wait for previous spm to complete
                     "andi  r16,1           \n\t"
                     "cpi   r16,1           \n\t"
                     "breq  wait_spm3       \n\t"
                     "ldi   r16,0x01    \n\t"   //Load r0,r1 into FLASH page buffer
                     "sts   %0,r16      \n\t"
                     "spm           \n\t"

                     "inc   r17     \n\t"   //page_word_count++
                     "cpi r17,%1            \n\t"
                     "brlo  same_page   \n\t"   //Still same page in FLASH
                     "write_page:       \n\t"
                     "clr   r17     \n\t"   //New page, write current one first
                     "wait_spm4:        \n\t"
                     "lds   r16,%0      \n\t"   //Wait for previous spm to complete
                     "andi  r16,1           \n\t"
                     "cpi   r16,1           \n\t"
                     "breq  wait_spm4       \n\t"
#ifdef __AVR_ATmega163__
                     "andi  r30,0x80    \n\t"   // m163 requires Z6:Z1 to be zero during page write
#endif                                                       
                     "ldi   r16,0x05    \n\t"   //Write page pointed to by Z
                     "sts   %0,r16      \n\t"
                     "spm           \n\t"
#ifdef __AVR_ATmega163__
                     ".word 0xFFFF      \n\t"
                     "nop           \n\t"
                     "ori   r30,0x7E    \n\t"   // recover Z6:Z1 state after page write (had to be zero during write)
#endif
                     "wait_spm5:        \n\t"
                     "lds   r16,%0      \n\t"   //Wait for previous spm to complete
                     "andi  r16,1           \n\t"
                     "cpi   r16,1           \n\t"
                     "breq  wait_spm5       \n\t"                                    
                     "ldi   r16,0x11    \n\t"   //Re-enable RWW section
                     "sts   %0,r16      \n\t"                                    
                     "spm           \n\t"                            
#ifdef __AVR_ATmega163__
                     ".word 0xFFFF      \n\t"
                     "nop           \n\t"
#endif
                     "same_page:        \n\t"                            
                     "adiw  r30,2       \n\t"   //Next word in FLASH
                     "sbiw  r24,2       \n\t"   //length-2
                     "breq  final_write \n\t"   //Finished
                     "rjmp  length_loop \n\t"
                     "final_write:      \n\t"
                     "cpi   r17,0       \n\t"
                     "breq  block_done  \n\t"
                     "adiw  r24,2       \n\t"   //length+2, fool above check on length after short page write
                     "rjmp  write_page  \n\t"
                     "block_done:       \n\t"
                     "clr   __zero_reg__    \n\t"   //restore zero register
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega1281__)
                     : "=m" (SPMCSR) : "M" (PAGE_SIZE) : "r0","r16","r17","r24","r25","r28","r29","r30","r31"
#else
                     : "=m" (SPMCR) : "M" (PAGE_SIZE) : "r0","r16","r17","r24","r25","r28","r29","r30","r31"
#endif
                     );
                /* Should really add a wait for RWW section to be enabled, don't actually need it since we never */
                /* exit the bootloader without a power cycle anyhow */
            }
            putch(0x14);
            putch(0x10);
        } else {
            if (++error_count == MAX_ERROR_COUNT)
                app_start();
        }       
    }


    /* Read memory block mode, length is big endian.  */
    else if(ch=='t') {
        length.byte[1] = getch();
        length.byte[0] = getch();
#if defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__)
        if (address.word>0x7FFF) flags.rampz = 1;       // No go with m256, FIXME
        else flags.rampz = 0;
#endif
        address.word = address.word << 1;           // address * 2 -> byte location
        if (getch() == 'E') flags.eeprom = 1;
        else flags.eeprom = 0;
        if (getch() == ' ') {                       // Command terminator
            putch(0x14);
            for (w=0;w < length.word;w++) {             // Can handle odd and even lengths okay
                if (flags.eeprom) {                         // Byte access EEPROM read
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)
                    while(EECR & (1<<EEPE));
                    EEAR = (uint16_t)(void *)address.word;
                    EECR |= (1<<EERE);
                    putch(EEDR);
#else
                    putch(eeprom_read_byte((void *)address.word));
#endif
                    address.word++;
                }
                else {

                    if (!flags.rampz) putch(pgm_read_byte_near(address.word));
#if defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__)
                    else putch(pgm_read_byte_far(address.word + 0x10000));
                    // Hmmmm, yuck  FIXME when m256 arrvies
#endif
                    address.word++;
                }
            }
            putch(0x10);
        }
    }


    /* Get device signature bytes  */
    else if(ch=='u') {
        if (getch() == ' ') {
            putch(0x14);
            putch(SIG1);
            putch(SIG2);
            putch(SIG3);
            putch(0x10);
        } else {
            if (++error_count == MAX_ERROR_COUNT)
                app_start();
        }
    }


    /* Read oscillator calibration byte */
    else if(ch=='v') {
        byte_response(0x00);
    }


#if defined MONITOR 

    /* here come the extended monitor commands by Erik Lins */

    /* check for three times exclamation mark pressed */
    else if(ch=='!') {
        ch = getch();
        if(ch=='!') {
        ch = getch();
        if(ch=='!') {
            PGM_P welcome = "";
#if defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__)
            uint16_t extaddr;
#endif
            uint8_t addrl, addrh;

#ifdef CRUMB128
            welcome = "ATmegaBOOT / Crumb128 - (C) J.P.Kyle, E.Lins - 050815\n\r";
#elif defined PROBOMEGA128
            welcome = "ATmegaBOOT / PROBOmega128 - (C) J.P.Kyle, E.Lins - 050815\n\r";
#elif defined SAVVY128
            welcome = "ATmegaBOOT / Savvy128 - (C) J.P.Kyle, E.Lins - 050815\n\r";
#elif defined __AVR_ATmega1280__ 
            welcome = "ATmegaBOOT / Arduino Mega - (C) Arduino LLC - 090930\n\r";
#endif

            /* turn on LED */
            LED_DDR |= _BV(LED);
            LED_PORT &= ~_BV(LED);

            /* print a welcome message and command overview */
            for(i=0; welcome[i] != '\0'; ++i) {
                putch(welcome[i]);
            }

            /* test for valid commands */
            for(;;) {
                putch('\n');
                putch('\r');
                putch(':');
                putch(' ');

                ch = getch();
                putch(ch);

                /* toggle LED */
                if(ch == 't') {
                    if(bit_is_set(LED_PIN,LED)) {
                        LED_PORT &= ~_BV(LED);
                        putch('1');
                    } else {
                        LED_PORT |= _BV(LED);
                        putch('0');
                    }
                } 

                /* read byte from address */
                else if(ch == 'r') {
                    ch = getch(); putch(ch);
                    addrh = gethex();
                    addrl = gethex();
                    putch('=');
                    ch = *(uint8_t *)((addrh << 8) + addrl);
                    puthex(ch);
                }

                /* write a byte to address  */
                else if(ch == 'w') {
                    ch = getch(); putch(ch);
                    addrh = gethex();
                    addrl = gethex();
                    ch = getch(); putch(ch);
                    ch = gethex();
                    *(uint8_t *)((addrh << 8) + addrl) = ch;
                }

                /* read from uart and echo back */
                else if(ch == 'u') {
                    for(;;) {
                        putch(getch());
                    }
                }
#if defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__)
                /* external bus loop  */
                else if(ch == 'b') {
                    putch('b');
                    putch('u');
                    putch('s');
                    MCUCR = 0x80;
                    XMCRA = 0;
                    XMCRB = 0;
                    extaddr = 0x1100;
                    for(;;) {
                        ch = *(volatile uint8_t *)extaddr;
                        if(++extaddr == 0) {
                            extaddr = 0x1100;
                        }
                    }
                }
#endif

                else if(ch == 'j') {
                    app_start();
                }

            } /* end of monitor functions */

        }
        }
    }
    /* end of monitor */
#endif
    else if (++error_count == MAX_ERROR_COUNT) {
        app_start();
    }
    } /* end of forever loop */

}


char gethexnib(void) {
    char a;
    a = getch(); putch(a);
    if(a >= 'a') {
        return (a - 'a' + 0x0a);
    } else if(a >= '0') {
        return(a - '0');
    }
    return a;
}


char gethex(void) {
    return (gethexnib() << 4) + gethexnib();
}


void puthex(char ch) {
    char ah;

    ah = ch >> 4;
    if(ah >= 0x0a) {
        ah = ah - 0x0a + 'a';
    } else {
        ah += '0';
    }

    ch &= 0x0f;
    if(ch >= 0x0a) {
        ch = ch - 0x0a + 'a';
    } else {
        ch += '0';
    }

    putch(ah);
    putch(ch);
}


void putch(char ch)
{
#if defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__)
    if(bootuart == 1) {
        while (!(UCSR0A & _BV(UDRE0)));
        UDR0 = ch;
    }
    else if (bootuart == 2) {
        while (!(UCSR1A & _BV(UDRE1)));
        UDR1 = ch;
    }
#elif defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || defined (__AVR_ATmega328__)
    while (!(UCSR0A & _BV(UDRE0)));
    UDR0 = ch;
#else
    /* m8,16,32,169,8515,8535,163 */
    while (!(UCSRA & _BV(UDRE)));
    UDR = ch;
#endif
}


char getch(void)
{
#if defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__)
    uint32_t count = 0;
    if(bootuart == 1) {
        while(!(UCSR0A & _BV(RXC0))) {
            /* 20060803 DojoCorp:: Addon coming from the previous Bootloader*/               
            /* HACKME:: here is a good place to count times*/
            count++;
            if (count > MAX_TIME_COUNT)
                app_start();
        }

        return UDR0;
    }
    else if(bootuart == 2) {
        while(!(UCSR1A & _BV(RXC1))) {
            /* 20060803 DojoCorp:: Addon coming from the previous Bootloader*/               
            /* HACKME:: here is a good place to count times*/
            count++;
            if (count > MAX_TIME_COUNT)
                app_start();
        }

        return UDR1;
    }
    return 0;
#elif defined(__AVR_ATmega168__)  || defined(__AVR_ATmega328P__) || defined (__AVR_ATmega328__)
    uint32_t count = 0;
    while(!(UCSR0A & _BV(RXC0))){
        /* 20060803 DojoCorp:: Addon coming from the previous Bootloader*/               
        /* HACKME:: here is a good place to count times*/
        count++;
        if (count > MAX_TIME_COUNT)
            app_start();
    }
    return UDR0;
#else
    /* m8,16,32,169,8515,8535,163 */
    uint32_t count = 0;
    while(!(UCSRA & _BV(RXC))){
        /* 20060803 DojoCorp:: Addon coming from the previous Bootloader*/               
        /* HACKME:: here is a good place to count times*/
        count++;
        if (count > MAX_TIME_COUNT)
            app_start();
    }
    return UDR;
#endif
}


void getNch(uint8_t count)
{
    while(count--) {
#if defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__)
        if(bootuart == 1) {
            while(!(UCSR0A & _BV(RXC0)));
            UDR0;
        } 
        else if(bootuart == 2) {
            while(!(UCSR1A & _BV(RXC1)));
            UDR1;
        }
#elif defined(__AVR_ATmega168__)  || defined(__AVR_ATmega328P__) || defined (__AVR_ATmega328__)
        getch();
#else
        /* m8,16,32,169,8515,8535,163 */
        /* 20060803 DojoCorp:: Addon coming from the previous Bootloader*/               
        //while(!(UCSRA & _BV(RXC)));
        //UDR;
        getch(); // need to handle time out
#endif      
    }
}


void byte_response(uint8_t val)
{
    if (getch() == ' ') {
        putch(0x14);
        putch(val);
        putch(0x10);
    } else {
        if (++error_count == MAX_ERROR_COUNT)
            app_start();
    }
}


void nothing_response(void)
{
    if (getch() == ' ') {
        putch(0x14);
        putch(0x10);
    } else {
        if (++error_count == MAX_ERROR_COUNT)
            app_start();
    }
}

void flash_led(uint8_t count)
{
    while (count--) {
        LED_PORT |= _BV(LED);
        _delay_ms(100);
        LED_PORT &= ~_BV(LED);
        _delay_ms(100);
    }
}


/* end of file ATmegaBOOT.c */
发布了24 篇原创文章 · 获赞 68 · 访问量 15万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 精致技术 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览