├── toolchain ├── Arduino_Library ├── images │ └── output.png ├── keywords.txt ├── library.properties ├── src │ ├── AirBootGSMLib.h │ ├── eeprom_addr.h │ └── AirBootGSMLib.cpp ├── LICENSE ├── examples │ └── SimpleOTA │ │ └── SimpleOTA.ino └── README.md ├── debug.h ├── program.sh ├── sbit.h ├── cmds ├── watchdog.c ├── gsm_at_cmds.h ├── gsm.h ├── watchdog.h ├── eeprom_addr.h ├── LICENSE ├── optiboot.h ├── uart.h ├── README.md ├── optiboot_stk500.h ├── main.c ├── uart.c ├── gsm.c ├── Makefile ├── pin_defs.h ├── optiboot.c └── boot.h /toolchain: -------------------------------------------------------------------------------- 1 | sudo apt-get install binutils gcc-avr avr-libc uisp avrdude flex byacc bison 2 | -------------------------------------------------------------------------------- /Arduino_Library/images/output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadlyd15/AirBootGSM/HEAD/Arduino_Library/images/output.png -------------------------------------------------------------------------------- /Arduino_Library/keywords.txt: -------------------------------------------------------------------------------- 1 | startOta KEYWORD1 2 | getOtaStatus KEYWORD1 3 | setGsmEnablePin KEYWORD1 4 | setOtaServer KEYWORD1 -------------------------------------------------------------------------------- /debug.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_H 2 | #define DEBUG_H 3 | #ifdef ENABLE_DEBUG 4 | #define DEBUG(x) x 5 | #else 6 | #define DEBUG(x) 7 | #endif 8 | #endif -------------------------------------------------------------------------------- /program.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Select Your Programmer 4 | PROGRAMMER="usbasp" 5 | avrdude -c $PROGRAMMER -p $TARGET_MCU -u -U flash:w:"AirBootGSM"_$TARGET.hex -------------------------------------------------------------------------------- /Arduino_Library/library.properties: -------------------------------------------------------------------------------- 1 | name=AirBootGSMLib 2 | version=1.0 3 | author=Shadly Salahuddin 4 | maintainer=Shadly Salahuddin 5 | sentence=A Library to use OTA features of AirBootGSM 6 | paragraph=A Library to use OTA features of AirBootGSM 7 | category=Other 8 | url=https://github.com/shadlyd15/AirBootGSM 9 | architectures=* -------------------------------------------------------------------------------- /sbit.h: -------------------------------------------------------------------------------- 1 | #ifndef _sbit_h_ 2 | #define _sbit_h_ 3 | 4 | struct bits { 5 | uint8_t b0:1; 6 | uint8_t b1:1; 7 | uint8_t b2:1; 8 | uint8_t b3:1; 9 | uint8_t b4:1; 10 | uint8_t b5:1; 11 | uint8_t b6:1; 12 | uint8_t b7:1; 13 | } __attribute__((__packed__)); 14 | 15 | #define SBIT(port,pin) ((*(volatile struct bits*)&port).b##pin) 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /cmds: -------------------------------------------------------------------------------- 1 | avrdude -c usbasp -p atmega328p -u -U flash:w:gsm_bootloader_atmega328p_16Mhz.hex 2 | 3 | avrdude -p m128 -c stk500 -e -U flash:w:diag.hex 4 | 5 | avrdude -c usbasp -p atmega328p -u -U flash:r:filename.hex:i 6 | 7 | sudo picocom /dev/ttyACM0 -b 115200 8 | 9 | make atmega328p_16MHz 10 | 11 | avrdude -c usbasp -p atmega328p -U lfuse:w:0xff:m -U hfuse:w:0xd0:m -U efuse:w:0xfd:m -------------------------------------------------------------------------------- /watchdog.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "pin_defs.h" 4 | #include "watchdog.h" 5 | 6 | /* Disable the watchdog timer to prevent 7 | * eternal reset loop of doom and despair */ 8 | void watchdogDisable(void){ 9 | watchdogConfig(WATCHDOG_OFF); 10 | } 11 | 12 | // Watchdog functions. These are only safe with interrupts turned off. 13 | void watchdogReset() { 14 | __asm__ __volatile__ ( 15 | "wdr\n" 16 | ); 17 | } 18 | 19 | void watchdogConfig(uint8_t x){ 20 | WDTCSR = _BV(WDCE) | _BV(WDE); 21 | WDTCSR = x; 22 | } 23 | -------------------------------------------------------------------------------- /gsm_at_cmds.h: -------------------------------------------------------------------------------- 1 | #ifndef GSM_AT_CMDS_H 2 | #define GSM_AT_CMDS_H 3 | 4 | #define NEOWAY 5 | #if defined(NEOWAY) 6 | const char AT_CHECK_PIN[] = "AT+CPIN?\r\n"; 7 | const char AT_ECHO_OFF[] = "ATE0\r\n"; 8 | const char AT_CHECK_SIGNAL[] = "AT+CSQ\r\n"; 9 | const char AT_CHECK_GSM[] = "AT+CREG?\r\n"; 10 | const char AT_CHECK_GPRS[] = "AT+CGATT?\r\n"; 11 | const char AT_SET_PPP_LINK[] = "AT+XIIC=1\r\n"; 12 | const char AT_CHECK_PPP_LINK[] = "AT+XIIC?\r\n"; 13 | const char AT_TCP_TRANS[] = "AT+TCPTRANS="; 14 | const char TCP_CLOSE[] = "AT+TCPCLOSE=0\r\n" ; 15 | #endif // defined(NEOWAY) 16 | #endif // GSM_AT_CMDS_H -------------------------------------------------------------------------------- /gsm.h: -------------------------------------------------------------------------------- 1 | #ifndef GSM_SERIAL_H 2 | #define GSM_SERIAL_H 3 | 4 | #include 5 | 6 | #define SIZE_RX_BUFFER 32 7 | #define SIZE_RESPONSE SIZE_RX_BUFFER 8 | 9 | #define OK 1 10 | #define NOT_OK 0 11 | #define UNKNOWN 255 12 | 13 | #define UART_BUFFER_EMPTY 0 14 | #define UART_NO_CHARACTER -1 15 | 16 | #define INITIAL 0 17 | #define CHECK_PIN 1 18 | #define TURN_OFF_ECHO 2 19 | #define CHECK_SIGNAL 3 20 | #define CHECK_GPRS 4 21 | #define CHECK_GSM 5 22 | #define SET_PPP_LINK 6 23 | #define CHECK_PPP_LINK 7 24 | #define CONNECT_TCP 8 25 | #define IDLE 9 26 | 27 | uint8_t gsm_init(void); 28 | 29 | #endif // GSM_SERIAL_H -------------------------------------------------------------------------------- /Arduino_Library/src/AirBootGSMLib.h: -------------------------------------------------------------------------------- 1 | #ifndef GSM_OTA_H 2 | #define GSM_OTA_H 3 | 4 | #define OTA_COMPLETED ((uint8_t)(32)) 5 | 6 | #define OTA_INIT_SIG_ADDR ((uint8_t*)0) 7 | #define OTA_STAUS_ADDR ((uint8_t*)2) 8 | 9 | typedef enum otaStatus{ 10 | NO_OTA_PARAMETER = -1 11 | OTA_FAILED = 0, 12 | OTA_SUCCESS = 1, 13 | FIRMWARE_OK = 2 14 | } otaStatus_t; 15 | 16 | class AirBoot{ 17 | public: 18 | void stopOta(); 19 | int getOtaStatus(); 20 | int startOta(uint8_t * ip_addr, uint16_t port); 21 | void setGsmEnablePin(uint8_t * port, uint8_t pin); 22 | void setOtaServer(uint8_t * ip_addr, uint16_t port); 23 | private: 24 | uint8_t isGsmEnablePinSet = 0; 25 | uint8_t isOtaServerSet = 0; 26 | }; 27 | 28 | #endif // GSM_OTA_H -------------------------------------------------------------------------------- /watchdog.h: -------------------------------------------------------------------------------- 1 | #ifndef WATCHDOG_H 2 | #define WATCHDOG_H 3 | 4 | /* Watchdog settings */ 5 | #define WATCHDOG_OFF (0) 6 | #define WATCHDOG_16MS (_BV(WDE)) 7 | #define WATCHDOG_32MS (_BV(WDP0) | _BV(WDE)) 8 | #define WATCHDOG_64MS (_BV(WDP1) | _BV(WDE)) 9 | #define WATCHDOG_125MS (_BV(WDP1) | _BV(WDP0) | _BV(WDE)) 10 | #define WATCHDOG_250MS (_BV(WDP2) | _BV(WDE)) 11 | #define WATCHDOG_500MS (_BV(WDP2) | _BV(WDP0) | _BV(WDE)) 12 | #define WATCHDOG_1S (_BV(WDP2) | _BV(WDP1) | _BV(WDE)) 13 | #define WATCHDOG_2S (_BV(WDP2) | _BV(WDP1) | _BV(WDP0) | _BV(WDE)) 14 | #ifndef __AVR_ATmega8__ 15 | #define WATCHDOG_4S (_BV(WDP3) | _BV(WDE)) 16 | #define WATCHDOG_8S (_BV(WDP3) | _BV(WDP0) | _BV(WDE)) 17 | #endif 18 | 19 | void watchdogDisable(void); 20 | void watchdogReset(); 21 | void watchdogConfig(uint8_t x); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /eeprom_addr.h: -------------------------------------------------------------------------------- 1 | #ifndef EEPROM_ADDR_H 2 | #define EEPROM_ADDR_H 3 | 4 | /*-----------------------------------------------------*/ 5 | 6 | #define GSM_BOOTLOADER_MAJVER (E2END - 0) 7 | #define GSM_BOOTLOADER_MINVER (E2END - 1) 8 | 9 | #define OTA_INIT_SIG_ADDR (E2END - 2) 10 | #define OTA_ATTEMPTED_ADDR (E2END - 3) 11 | 12 | #define OTA_STAUS_ADDR (E2END - 4) 13 | 14 | #define GSM_REG_PORT_ADDR (E2END - 5) 15 | #define GSM_REG_PIN_ADDR (E2END - 6) 16 | #define GSM_REG_PIN_ON_LOGIC_ADDR (E2END - 7) 17 | 18 | #define OTA_SERVER_IP_0 (E2END - 8) 19 | #define OTA_SERVER_IP_1 (E2END - 9) 20 | #define OTA_SERVER_IP_2 (E2END - 10) 21 | #define OTA_SERVER_IP_3 (E2END - 11) 22 | 23 | #define OTA_SERVER_PORT_H (E2END - 12) 24 | #define OTA_SERVER_PORT_L (E2END - 13) 25 | 26 | /*-----------------------------------------------------*/ 27 | 28 | #define OTA_START_SIG (uint8_t)(0xBE) 29 | #define OTA_ATTEMPTED_SIG (uint8_t)(0xBE) 30 | #define OTA_COMPLETED (uint8_t)(0xBE) 31 | 32 | /*-----------------------------------------------------*/ 33 | 34 | #endif -------------------------------------------------------------------------------- /Arduino_Library/src/eeprom_addr.h: -------------------------------------------------------------------------------- 1 | #ifndef EEPROM_ADDR_H 2 | #define EEPROM_ADDR_H 3 | 4 | /*-----------------------------------------------------*/ 5 | 6 | #define GSM_BOOTLOADER_MAJVER (E2END - 0) 7 | #define GSM_BOOTLOADER_MINVER (E2END - 1) 8 | 9 | #define OTA_INIT_SIG_ADDR (E2END - 2) 10 | #define OTA_ATTEMPTED_ADDR (E2END - 3) 11 | 12 | #define OTA_STAUS_ADDR (E2END - 4) 13 | 14 | #define GSM_REG_PORT_ADDR (E2END - 5) 15 | #define GSM_REG_PIN_ADDR (E2END - 6) 16 | #define GSM_REG_PIN_ON_LOGIC_ADDR (E2END - 7) 17 | 18 | #define OTA_SERVER_IP_0 (E2END - 8) 19 | #define OTA_SERVER_IP_1 (E2END - 9) 20 | #define OTA_SERVER_IP_2 (E2END - 10) 21 | #define OTA_SERVER_IP_3 (E2END - 11) 22 | 23 | #define OTA_SERVER_PORT_H (E2END - 12) 24 | #define OTA_SERVER_PORT_L (E2END - 13) 25 | 26 | /*-----------------------------------------------------*/ 27 | 28 | #define OTA_START_SIG (uint8_t)(0xBE) 29 | #define OTA_ATTEMPTED_SIG (uint8_t)(0xBE) 30 | #define OTA_COMPLETED (uint8_t)(0xBE) 31 | 32 | /*-----------------------------------------------------*/ 33 | 34 | #endif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 shadlyd15 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Arduino_Library/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 shadlyd15 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Arduino_Library/examples/SimpleOTA/SimpleOTA.ino: -------------------------------------------------------------------------------- 1 | #include "AirBootGSMLib.h" 2 | 3 | AirBoot airboot; 4 | 5 | int x = 0; 6 | char buff[128]; 7 | uint16_t ota_port = 8888; 8 | uint8_t ota_server[4] = { 192, 56, 12, 5 }; 9 | 10 | // the setup function runs once when you press reset or power the board 11 | void setup() { 12 | pinMode(LED_BUILTIN, OUTPUT); 13 | delay(100); 14 | Serial.begin(115200); 15 | delay(100); 16 | Serial.println("\r\n-- Application Starts --"); 17 | airboot.stop_ota(); 18 | airboot.setGsmEnablePin(PORTB, PB3); 19 | memset(buff, 0x00, 128); 20 | } 21 | 22 | // the loop function runs over and over again forever 23 | void loop() { 24 | Serial.println("Loop Starts"); 25 | digitalWrite(LED_BUILTIN, HIGH); 26 | delay(1000); // wait for a second 27 | digitalWrite(LED_BUILTIN, LOW); 28 | delay(1000); // wait for a second 29 | digitalWrite(LED_BUILTIN, HIGH); 30 | delay(1000); // wait for a second 31 | digitalWrite(LED_BUILTIN, LOW); 32 | delay(1000); // wait for a second 33 | 34 | if(Serial.available()){ 35 | char c = (char)Serial.read(); 36 | buff[x] = c; 37 | x = ((x+1) % 128); 38 | } 39 | 40 | if(strstr(buff, "OTA")){ 41 | Serial.println("Starting OTA .....\r\n"); 42 | airboot.startOta(ota_server, ota_port); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /optiboot.h: -------------------------------------------------------------------------------- 1 | #ifndef optiboot_h 2 | #define optiboot_h 3 | 4 | #if defined(__AVR_ATmega168__) 5 | #define RAMSTART (0x100) 6 | #define NRWWSTART (0x3800) 7 | #elif defined(__AVR_ATmega328P__) 8 | #define RAMSTART (0x100) 9 | #define NRWWSTART (0x7000) 10 | #elif defined (__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) 11 | #define RAMSTART (0x100) 12 | #define NRWWSTART (0xE000) 13 | #elif defined(__AVR_ATtiny84__) 14 | #define RAMSTART (0x100) 15 | #define NRWWSTART (0x0000) 16 | #elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) 17 | // #define RAMSTART (0x200) 18 | #define NRWWSTART (0xE000) 19 | #elif defined(__AVR_ATmega8__) || defined(__AVR_ATmega88__) 20 | #define RAMSTART (0x100) 21 | #define NRWWSTART (0x1800) 22 | #endif 23 | 24 | /* This kind of define'd pointer to memory cause 25 | * the bootloader to hang during flashing 26 | * so it has been replaced with an uint8_t array of size 256 27 | * inside the programming phase 28 | */ 29 | //#define buff ((uint8_t*)(RAMSTART)) 30 | 31 | /* C zero initialises all global variables. However, that requires 32 | * These definitions are NOT zero initialised, but that doesn't matter 33 | * This allows us to drop the zero init code, saving us memory 34 | */ 35 | #ifdef VIRTUAL_BOOT_PARTITION 36 | #define rstVect (*(uint16_t*)(RAMSTART+SPM_PAGESIZE*2+4)) 37 | #define wdtVect (*(uint16_t*)(RAMSTART+SPM_PAGESIZE*2+6)) 38 | #endif 39 | 40 | /* 41 | * Jump into the serial flashing subsystem 42 | */ 43 | uint8_t processOptiboot(void); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /Arduino_Library/README.md: -------------------------------------------------------------------------------- 1 | ## How To Use 2 | To use the OTA firmware upgrade capabilities from application layer, you can use the provided Arduino library. The library contains a simple example to demonstrate OTA firmware upgrade from application layer. It triggers OTA firmware upgrade if it gets input string "OTA" the from Serial. 3 | 4 | ```cpp 5 | #include "AirBootGSMLib.h" 6 | 7 | void start_ota(){ 8 | eeprom_write_byte(OTA_INIT_SIG_ADDR, 15); 9 | wdt_enable(WDTO_15MS); 10 | delay(1000); 11 | } 12 | 13 | void stop_ota(){ 14 | if(eeprom_read_byte(OTA_INIT_SIG_ADDR) == 15){ 15 | if(eeprom_read_byte(OTA_STAUS_ADDR) == OTA_COMPLETED){ 16 | Serial.println("Firmware Upgraded"); 17 | } 18 | else{ 19 | Serial.println("Firmware Upgrade Failed"); 20 | } 21 | eeprom_write_byte(OTA_INIT_SIG_ADDR, 0xFF); 22 | } 23 | } 24 | 25 | int x = 0; 26 | char buff[128]; 27 | 28 | // the setup function runs once when you press reset or power the board 29 | void setup() { 30 | pinMode(LED_BUILTIN, OUTPUT); 31 | delay(100); 32 | Serial.begin(115200); 33 | delay(100); 34 | Serial.println("\r\n-- Application Starts --"); 35 | stop_ota(); 36 | memset(buff, 0x00, 128); 37 | } 38 | 39 | // the loop function runs over and over again forever 40 | void loop() { 41 | Serial.println("Loop Starts"); 42 | digitalWrite(LED_BUILTIN, HIGH); 43 | delay(1000); // wait for a second 44 | digitalWrite(LED_BUILTIN, LOW); 45 | delay(1000); // wait for a second 46 | digitalWrite(LED_BUILTIN, HIGH); 47 | delay(1000); // wait for a second 48 | digitalWrite(LED_BUILTIN, LOW); 49 | delay(1000); // wait for a second 50 | 51 | if(Serial.available()){ 52 | char c = (char)Serial.read(); 53 | buff[x] = c; 54 | x = ((x+1) % 128); 55 | } 56 | 57 | if(strstr(buff, "OTA")){ 58 | Serial.println("Starting OTA .....\r\n"); 59 | start_ota(); 60 | } 61 | } 62 | ``` -------------------------------------------------------------------------------- /uart.h: -------------------------------------------------------------------------------- 1 | /* 2 | * pin_defs.h 3 | * This contains most of the rather ugly defines that implement our 4 | * ability to use UART=n and LED=D3, and some avr family bit name differences. 5 | */ 6 | #include "pin_defs.h" 7 | #include 8 | 9 | /* set the UART baud rate defaults */ 10 | #ifndef BAUD_RATE 11 | #if F_CPU >= 8000000L 12 | #define BAUD_RATE 115200L // Highest rate Avrdude win32 will support 13 | #elif F_CPU >= 1000000L 14 | #define BAUD_RATE 9600L // 19200 also supported, but with significant error 15 | #elif F_CPU >= 128000L 16 | #define BAUD_RATE 4800L // Good for 128kHz internal RC 17 | #else 18 | #define BAUD_RATE 1200L // Good even at 32768Hz 19 | #endif 20 | #endif 21 | 22 | #ifndef UART 23 | #define UART 0 24 | #endif 25 | 26 | #define BAUD_SETTING (( (F_CPU + BAUD_RATE * 4L) / ((BAUD_RATE * 8L))) - 1 ) 27 | #define BAUD_ACTUAL (F_CPU/(8 * ((BAUD_SETTING)+1))) 28 | 29 | #if BAUD_ACTUAL <= BAUD_RATE 30 | #define BAUD_ERROR (( 100*(BAUD_RATE - BAUD_ACTUAL) ) / BAUD_RATE) 31 | #if BAUD_ERROR >= 5 32 | #error BAUD_RATE error greater than -5% 33 | #elif BAUD_ERROR >= 2 34 | #warning BAUD_RATE error greater than -2% 35 | #endif 36 | #else 37 | #define BAUD_ERROR (( 100*(BAUD_ACTUAL - BAUD_RATE) ) / BAUD_RATE) 38 | #if BAUD_ERROR >= 5 39 | #error BAUD_RATE error greater than 5% 40 | #elif BAUD_ERROR >= 2 41 | #warning BAUD_RATE error greater than 2% 42 | #endif 43 | #endif 44 | 45 | #if (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 > 250 46 | #error Unachievable baud rate (too slow) BAUD_RATE 47 | #endif // baud rate slow check 48 | 49 | #if (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 < 3 50 | #if BAUD_ERROR != 0 // permit high bitrates (ie 1Mbps@16MHz) if error is zero 51 | #error Unachievable baud rate (too fast) BAUD_RATE 52 | #endif 53 | #endif // baud rate fastn check 54 | 55 | void __attribute__((noinline)) putch(char); 56 | uint8_t __attribute__((noinline)) getch(void); 57 | uint8_t uart_puts(const char * buf); 58 | int get_non_blocking_ch(); 59 | 60 | #ifdef SOFT_UART 61 | void uartDelay() __attribute__ ((naked)); 62 | #endif -------------------------------------------------------------------------------- /Arduino_Library/src/AirBootGSMLib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "AirBootGSMLib.h" 4 | #include "eeprom_addr.h" 5 | 6 | void AirBoot::setGsmEnablePin(uint8_t * port, uint8_t pin){ 7 | this->isGsmEnablePinSet = 1; 8 | if(eeprom_read_byte(GSM_REG_PORT_ADDR) != port){ 9 | eeprom_write_byte(GSM_REG_PORT_ADDR, port); 10 | } 11 | 12 | if(eeprom_read_byte(GSM_REG_PIN_ADDR) != pin){ 13 | eeprom_write_byte(GSM_REG_PIN_ADDR, pin); 14 | } 15 | } 16 | 17 | void AirBoot::setOtaServer(uint8_t * ip_addr, uint16_t port){ 18 | this->isOtaServerSet = 1; 19 | if(eeprom_read_byte(OTA_SERVER_IP_0) != ip_addr[0]) 20 | eeprom_write_byte(OTA_SERVER_IP_0, ip_addr[0]); 21 | 22 | if(eeprom_read_byte(OTA_SERVER_IP_1) != ip_addr[1]) 23 | eeprom_write_byte(OTA_SERVER_IP_1, ip_addr[1]); 24 | 25 | if(eeprom_read_byte(OTA_SERVER_IP_2) != ip_addr[2]) 26 | eeprom_write_byte(OTA_SERVER_IP_2, ip_addr[2]); 27 | 28 | if(eeprom_read_byte(OTA_SERVER_IP_3) != ip_addr[3]) 29 | eeprom_write_byte(OTA_SERVER_IP_3, ip_addr[3]); 30 | 31 | if(eeprom_read_byte(OTA_SERVER_PORT_H) != (port & 0xFF)) 32 | eeprom_write_byte(OTA_SERVER_PORT_H, port & 0xFF); 33 | 34 | if(eeprom_read_byte(OTA_SERVER_PORT_L) != (port >> 8)) 35 | eeprom_write_byte(OTA_SERVER_PORT_L, port >> 8); 36 | } 37 | 38 | int AirBoot::startOta(uint8_t * ip_addr, uint16_t port){ 39 | this->setOtaServer(ip_addr, port); 40 | if(isOtaServerSet && isGsmEnablePinSet){ 41 | if(eeprom_read_byte(OTA_INIT_SIG_ADDR) != OTA_START_SIG){ 42 | eeprom_write_byte(OTA_INIT_SIG_ADDR, OTA_START_SIG); 43 | } 44 | wdt_enable(WDTO_15MS); 45 | delay(1000); 46 | } 47 | return -1; 48 | } 49 | 50 | 51 | void AirBoot::stopOta(){ 52 | if(eeprom_read_byte(OTA_INIT_SIG_ADDR) == OTA_START_SIG){ 53 | // if(eeprom_read_byte(OTA_STAUS_ADDR) == OTA_COMPLETED){ 54 | // Serial.println("Firmware Upgraded"); 55 | // } 56 | // else{ 57 | // Serial.println("Firmware Upgrade Failed"); 58 | // } 59 | eeprom_write_byte(OTA_INIT_SIG_ADDR, 0xFF); 60 | } 61 | } 62 | 63 | 64 | int AirBoot::getOtaStatus(){ 65 | if(eeprom_read_byte(OTA_INIT_SIG_ADDR) == OTA_START_SIG){ 66 | eeprom_write_byte(OTA_INIT_SIG_ADDR, 0xFF); 67 | return OTA_SUCCESS; 68 | } 69 | return FIRMWARE_OK; 70 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## AirBootGSM : An AVR & Arduino GSM OTA Bootloader 2 | 3 | AirBootGSM is a AVR & Arduino bootloader for upgrading firmware over the air using GSM modem. It is kind of fail-safe. If anything wrong happens and the chip does not boot after the firmware upgrade, the bootloader automatically retries to download and reinstall new firmware. AirBootGSM is based on highly optimized [Optiboot Bootloader.](https://github.com/Optiboot/optiboot) 4 | 5 | ## Setup Toolchain to Compile : 6 | ```bash 7 | sudo apt-get update 8 | sudo apt-get install binutils gcc-avr avr-libc uisp avrdude flex byacc bison 9 | ``` 10 | 11 | ## Compile : 12 | Run build.sh 13 | ```bash 14 | ./build.sh 15 | ``` 16 | Or select your traget mcu and run the following commands. 17 | ```bash 18 | # Select Your Target MCU 19 | TARGET_SPEED="8MHz" 20 | # TARGET_SPEED="16MHZ" 21 | 22 | TARGET_MCU="atmega16" 23 | # TARGET_MCU="atmega168" 24 | # TARGET_MCU="atmega328p" #TESTED 25 | # TARGET_MCU="atmega1284p" 26 | TARGET=$TARGET_MCU"_"$TARGET_SPEED 27 | 28 | make clean 29 | make $TARGET 30 | ``` 31 | 32 | ## To Pragram : 33 | Run build.sh 34 | ```bash 35 | ./build.sh 36 | ``` 37 | Or select your programmer and run the following commands. 38 | ```bash 39 | PROGRAMMER="usbasp" 40 | # PROGRAMMER="arduino" 41 | # PROGRAMMER="avrisp" 42 | # PROGRAMMER="avrispmkII" 43 | avrdude -c $PROGRAMMER -p $TARGET_MCU -u -U flash:w:"AirBootGSM"_$TARGET.hex 44 | ``` 45 | ## How To Use 46 | To use the OTA firmware upgrade capabilities from application layer, you can use the provided Arduino library. The library contains a simple example to demonstrate OTA firmware upgrade from application layer. It triggers OTA firmware upgrade if it gets input string "OTA" the from Serial. 47 | 48 | ```cpp 49 | #include "AirBootGSMLib.h" 50 | 51 | AirBoot airboot; 52 | 53 | int x = 0; 54 | char buff[128]; 55 | uint16_t ota_port = 8888; 56 | uint8_t ota_server[4] = { 192, 56, 12, 5 }; 57 | 58 | // the setup function runs once when you press reset or power the board 59 | void setup() { 60 | pinMode(LED_BUILTIN, OUTPUT); 61 | delay(100); 62 | Serial.begin(115200); 63 | delay(100); 64 | Serial.println("\r\n-- Application Starts --"); 65 | airboot.stop_ota(); 66 | airboot.setGsmEnablePin(PORTB, PB3); 67 | memset(buff, 0x00, 128); 68 | } 69 | 70 | // the loop function runs over and over again forever 71 | void loop() { 72 | Serial.println("Loop Starts"); 73 | digitalWrite(LED_BUILTIN, HIGH); 74 | delay(1000); // wait for a second 75 | digitalWrite(LED_BUILTIN, LOW); 76 | delay(1000); // wait for a second 77 | digitalWrite(LED_BUILTIN, HIGH); 78 | delay(1000); // wait for a second 79 | digitalWrite(LED_BUILTIN, LOW); 80 | delay(1000); // wait for a second 81 | 82 | if(Serial.available()){ 83 | char c = (char)Serial.read(); 84 | buff[x] = c; 85 | x = ((x+1) % 128); 86 | } 87 | 88 | if(strstr(buff, "OTA")){ 89 | Serial.println("Starting OTA .....\r\n"); 90 | airboot.startOta(ota_server, ota_port); 91 | } 92 | } 93 | 94 | ``` 95 | 96 | ## Limitations : 97 | Currently it only supports Neoway GSM modems that support Transparet TCP mode. To keep compiled binary as small as possible to fit with a few KBs, readability of the code is greatly hampared. Still any other modems can easily be adopted. 98 | -------------------------------------------------------------------------------- /optiboot_stk500.h: -------------------------------------------------------------------------------- 1 | // /* STK500 constants list, from AVRDUDE */ 2 | // #define STK_OK 0x10 3 | // #define STK_FAILED 0x11 // Not used 4 | // #define STK_UNKNOWN 0x12 // Not used 5 | // #define STK_NODEVICE 0x13 // Not used 6 | // #define STK_INSYNC 0x14 // ' ' 7 | // #define STK_NOSYNC 0x15 // Not used 8 | // #define ADC_CHANNEL_ERROR 0x16 // Not used 9 | // #define ADC_MEASURE_OK 0x17 // Not used 10 | // #define PWM_CHANNEL_ERROR 0x18 // Not used 11 | // #define PWM_ADJUST_OK 0x19 // Not used 12 | // #define CRC_EOP 0x20 // 'SPACE' 13 | // #define STK_GET_SYNC 0x30 // '0' 14 | // #define STK_GET_SIGN_ON 0x31 // '1' 15 | // #define STK_SET_PARAMETER 0x40 // '@' 16 | // #define STK_GET_PARAMETER 0x41 // 'A' 17 | // #define STK_SET_DEVICE 0x42 // 'B' 18 | // #define STK_SET_DEVICE_EXT 0x45 // 'E' 19 | // #define STK_ENTER_PROGMODE 0x50 // 'P' 20 | // #define STK_LEAVE_PROGMODE 0x51 // 'Q' 21 | // #define STK_CHIP_ERASE 0x52 // 'R' 22 | // #define STK_CHECK_AUTOINC 0x53 // 'S' 23 | // #define STK_LOAD_ADDRESS 0x55 // 'U' 24 | // #define STK_UNIVERSAL 0x56 // 'V' 25 | // #define STK_PROG_FLASH 0x60 // '`' 26 | // #define STK_PROG_DATA 0x61 // 'a' 27 | // #define STK_PROG_FUSE 0x62 // 'b' 28 | // #define STK_PROG_LOCK 0x63 // 'c' 29 | // #define STK_PROG_PAGE 0x64 // 'd' 30 | // #define STK_PROG_FUSE_EXT 0x65 // 'e' 31 | // #define STK_READ_FLASH 0x70 // 'p' 32 | // #define STK_READ_DATA 0x71 // 'q' 33 | // #define STK_READ_FUSE 0x72 // 'r' 34 | // #define STK_READ_LOCK 0x73 // 's' 35 | // #define STK_READ_PAGE 0x74 // 't' 36 | // #define STK_READ_SIGN 0x75 // 'u' 37 | // #define STK_READ_OSCCAL 0x76 // 'v' 38 | // #define STK_READ_FUSE_EXT 0x77 // 'w' 39 | // #define STK_READ_OSCCAL_EXT 0x78 // 'x' 40 | 41 | /* STK500 constants list, from AVRDUDE 42 | * 43 | * Trivial set of constants derived from Atmel App Note AVR061 44 | * Not copyrighted. Released to the public domain. 45 | */ 46 | 47 | #define STK_OK 0x10 48 | #define STK_FAILED 0x11 // Not used 49 | #define STK_UNKNOWN 0x12 // Not used 50 | #define STK_NODEVICE 0x13 // Not used 51 | #define STK_INSYNC 0x14 // ' ' 52 | #define STK_NOSYNC 0x15 // Not used 53 | #define ADC_CHANNEL_ERROR 0x16 // Not used 54 | #define ADC_MEASURE_OK 0x17 // Not used 55 | #define PWM_CHANNEL_ERROR 0x18 // Not used 56 | #define PWM_ADJUST_OK 0x19 // Not used 57 | #define CRC_EOP 0x20 // 'SPACE' 58 | #define STK_GET_SYNC 0x30 // '0' 59 | #define STK_GET_SIGN_ON 0x31 // '1' 60 | #define STK_SET_PARAMETER 0x40 // '@' 61 | #define STK_GET_PARAMETER 0x41 // 'A' 62 | #define STK_SET_DEVICE 0x42 // 'B' 63 | #define STK_SET_DEVICE_EXT 0x45 // 'E' 64 | #define STK_ENTER_PROGMODE 0x50 // 'P' 65 | #define STK_LEAVE_PROGMODE 0x51 // 'Q' 66 | #define STK_CHIP_ERASE 0x52 // 'R' 67 | #define STK_CHECK_AUTOINC 0x53 // 'S' 68 | #define STK_LOAD_ADDRESS 0x55 // 'U' 69 | #define STK_UNIVERSAL 0x56 // 'V' 70 | #define STK_PROG_FLASH 0x60 // '`' 71 | #define STK_PROG_DATA 0x61 // 'a' 72 | #define STK_PROG_FUSE 0x62 // 'b' 73 | #define STK_PROG_LOCK 0x63 // 'c' 74 | #define STK_PROG_PAGE 0x64 // 'd' 75 | #define STK_PROG_FUSE_EXT 0x65 // 'e' 76 | #define STK_READ_FLASH 0x70 // 'p' 77 | #define STK_READ_DATA 0x71 // 'q' 78 | #define STK_READ_FUSE 0x72 // 'r' 79 | #define STK_READ_LOCK 0x73 // 's' 80 | #define STK_READ_PAGE 0x74 // 't' 81 | #define STK_READ_SIGN 0x75 // 'u' 82 | #define STK_READ_OSCCAL 0x76 // 'v' 83 | #define STK_READ_FUSE_EXT 0x77 // 'w' 84 | #define STK_READ_OSCCAL_EXT 0x78 // 'x' 85 | #define STK_SW_MAJOR 0x81 // ' ' 86 | #define STK_SW_MINOR 0x82 // ' ' 87 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "gsm.h" 6 | #include "eeprom_addr.h" 7 | #include "watchdog.h" 8 | #include "debug.h" 9 | 10 | int main(void) __attribute__ ((section (".init9"))); 11 | void appStart(void) __attribute__ ((naked)); 12 | 13 | int main(void){ 14 | watchdogDisable(); 15 | uint8_t ch; 16 | DDRD &= ~(_BV(PD7)); 17 | PORTD |= (1 << PD7); 18 | 19 | /* This code makes the following assumptions: 20 | * No interrupts will execute 21 | * SP points to RAMEND 22 | * r1 contains zero 23 | * If not, uncomment the following instructions. */ 24 | 25 | // cli(); 26 | asm volatile("clr __zero_reg__"); 27 | #if defined(__AVR_ATmega8__) 28 | SP = RAMEND; // This is done by hardware reset 29 | #endif 30 | 31 | #if !defined(__AVR_ATmega16__) 32 | ch = MCUSR; 33 | MCUSR = 0; 34 | #else 35 | ch = MCUCSR; 36 | MCUCSR = 0; 37 | #endif 38 | 39 | uart_init(); 40 | DEBUG(uart_puts("\r\n-- Bootloader Starts --\r\n")); 41 | 42 | if(ch & _BV(WDRF)){ 43 | watchdogDisable(); 44 | DEBUG(uart_puts("Watchdog Reset\r\n")); 45 | if(eeprom_read_byte(OTA_INIT_SIG_ADDR) == OTA_START_SIG){ 46 | DEBUG(uart_puts("OTA Signal Found\r\n")); 47 | if(eeprom_read_byte(OTA_ATTEMPTED_ADDR) == OTA_ATTEMPTED_SIG){ 48 | DEBUG(uart_puts("Optiboot Attempted Before\r\n")); 49 | eeprom_write_byte(OTA_ATTEMPTED_ADDR, 0xFF); 50 | DEBUG(uart_puts("Starting Application\r\n")); 51 | appStart(); 52 | } 53 | else{ 54 | eeprom_write_byte(OTA_ATTEMPTED_ADDR, OTA_ATTEMPTED_SIG); 55 | if(eeprom_read_byte(OTA_STAUS_ADDR) == OTA_COMPLETED){ 56 | eeprom_write_byte(OTA_STAUS_ADDR, 0xFF); 57 | } 58 | DEBUG(uart_puts("Attempting Optiboot\r\n")); 59 | DEBUG(uart_puts("GSM Loop\r\n")); 60 | gsm_loop(); 61 | watchdogConfig(WATCHDOG_1S); 62 | } 63 | } 64 | else{ // on wdt 65 | DEBUG(uart_puts("OTA Signal Not Found\r\n")); 66 | DEBUG(uart_puts("Starting Application\r\n")); 67 | appStart(); 68 | } 69 | } 70 | else if(ch & _BV(BORF)){ 71 | watchdogConfig(WATCHDOG_1S); 72 | DEBUG(uart_puts("Brown Out Reset\r\n")); 73 | } 74 | else if(ch & _BV(EXTRF)){ 75 | watchdogConfig(WATCHDOG_1S); 76 | DEBUG(uart_puts("External Reset\r\n")); 77 | DEBUG(uart_puts("Starting OTA\r\n")); 78 | if(eeprom_read_byte(OTA_INIT_SIG_ADDR) == OTA_START_SIG){ 79 | DEBUG(uart_puts("OTA Signal Found\r\n")); 80 | if(bit_is_clear(PIND, PD7)) { 81 | while(bit_is_clear(PIND, PD7)); 82 | uart_puts("-- Clearing GSM OTA Flag --"); 83 | eeprom_write_byte(OTA_INIT_SIG_ADDR, 0xFF); 84 | watchdogConfig(WATCHDOG_16MS); 85 | _delay_ms(1000); 86 | } 87 | if(eeprom_read_byte(OTA_ATTEMPTED_ADDR) == OTA_ATTEMPTED_SIG){ 88 | DEBUG(uart_puts("Optiboot Attempted Before\r\n")); 89 | eeprom_write_byte(OTA_ATTEMPTED_ADDR, 0xFF); 90 | DEBUG(uart_puts("Starting Application\r\n")); 91 | appStart(); 92 | } 93 | else{ 94 | eeprom_write_byte(OTA_ATTEMPTED_ADDR, OTA_ATTEMPTED_SIG); 95 | if(eeprom_read_byte(OTA_STAUS_ADDR) == OTA_COMPLETED){ 96 | eeprom_write_byte(OTA_STAUS_ADDR, 0xFF); 97 | } 98 | DEBUG(uart_puts("Attempting Optiboot\r\n")); 99 | DEBUG(uart_puts("GSM Loop\r\n")); 100 | gsm_loop(); 101 | watchdogConfig(WATCHDOG_1S); 102 | } 103 | } 104 | else{ // on wdt 105 | DEBUG(uart_puts("OTA Signal Not Found\r\n")); 106 | DEBUG(uart_puts("Starting Application\r\n")); 107 | appStart(); 108 | } 109 | } 110 | else if(ch & _BV(PORF)){ 111 | watchdogConfig(WATCHDOG_1S); 112 | DEBUG(uart_puts("Power Reset\r\n")); 113 | } 114 | else{ 115 | // watchdogConfig(WATCHDOG_1S); 116 | DEBUG(uart_puts("Invalid Firmware\r\n")); 117 | } 118 | 119 | // DEBUG(uart_puts("-- Optiboot Starts Now --\r\n")); 120 | optiboot(); 121 | 122 | DEBUG(uart_puts("Bootloader Ends Here. Starting App\r\n")); 123 | /* Exit to user application */ 124 | appStart(); 125 | 126 | DEBUG(uart_puts("Main Exited\r\n")); 127 | return(0); //never reached 128 | } 129 | 130 | void appStart(void) { 131 | watchdogDisable(); 132 | asm volatile( 133 | "clr r30 \n\t" 134 | "clr r31 \n\t" 135 | "ijmp \n\t" 136 | ); 137 | } 138 | -------------------------------------------------------------------------------- /uart.c: -------------------------------------------------------------------------------- 1 | #include "uart.h" 2 | #include "watchdog.h" 3 | 4 | void uart_init(){ 5 | #ifndef SOFT_UART 6 | 7 | #if defined(__AVR_ATmega8__) || defined (__AVR_ATmega32__) || defined (__AVR_ATmega16__) 8 | UCSRA = _BV(U2X); //Double speed mode USART 9 | UCSRB = _BV(RXEN) | _BV(TXEN); // enable Rx & Tx 10 | UCSRC = _BV(URSEL) | _BV(UCSZ1) | _BV(UCSZ0); // config USART; 8N1 11 | UBRRL = (uint8_t)( (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 ); 12 | #else 13 | UART_SRA = _BV(U2X0); //Double speed mode USART0 14 | UART_SRB = _BV(RXEN0) | _BV(TXEN0); 15 | UART_SRC = _BV(UCSZ00) | _BV(UCSZ01); 16 | UART_SRL = (uint8_t)( (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 ); 17 | #endif 18 | 19 | #else 20 | // AVR305 equation: #define UART_B_VALUE (((F_CPU/BAUD_RATE)-23)/6) 21 | // Adding 3 to numerator simulates nearest rounding for more accurate baud rates 22 | #define UART_B_VALUE (((F_CPU/BAUD_RATE)-20)/6) 23 | #if UART_B_VALUE > 255 24 | #error Baud rate too slow for soft UART 25 | #endif 26 | /* Set TX pin as output */ 27 | UART_DDR |= _BV(UART_TX_BIT); 28 | 29 | #endif 30 | } 31 | 32 | void putch(char ch) { 33 | #ifndef SOFT_UART 34 | while (!(UART_SRA & _BV(UDRE0))); 35 | UART_UDR = ch; 36 | #else 37 | __asm__ __volatile__ ( 38 | " com %[ch]\n" // ones complement, carry set 39 | " sec\n" 40 | "1: brcc 2f\n" 41 | " cbi %[uartPort],%[uartBit]\n" 42 | " rjmp 3f\n" 43 | "2: sbi %[uartPort],%[uartBit]\n" 44 | " nop\n" 45 | "3: rcall uartDelay\n" 46 | " rcall uartDelay\n" 47 | " lsr %[ch]\n" 48 | " dec %[bitcnt]\n" 49 | " brne 1b\n" 50 | : 51 | : 52 | [bitcnt] "d" (10), 53 | [ch] "r" (ch), 54 | [uartPort] "I" (_SFR_IO_ADDR(UART_PORT)), 55 | [uartBit] "I" (UART_TX_BIT) 56 | : 57 | "r25" 58 | ); 59 | #endif 60 | } 61 | 62 | uint8_t getch(void) { 63 | uint8_t ch; 64 | 65 | #ifdef LED_DATA_FLASH 66 | #if defined(__AVR_ATmega8__) || defined (__AVR_ATmega32__) || defined (__AVR_ATmega16__) 67 | LED_PORT ^= _BV(LED); 68 | #else 69 | LED_PIN |= _BV(LED); 70 | #endif 71 | #endif 72 | 73 | #ifdef SOFT_UART 74 | watchdogReset(); 75 | __asm__ __volatile__ ( 76 | "1: sbic %[uartPin],%[uartBit]\n" // Wait for start edge 77 | " rjmp 1b\n" 78 | " rcall uartDelay\n" // Get to middle of start bit 79 | "2: rcall uartDelay\n" // Wait 1 bit period 80 | " rcall uartDelay\n" // Wait 1 bit period 81 | " clc\n" 82 | " sbic %[uartPin],%[uartBit]\n" 83 | " sec\n" 84 | " dec %[bitCnt]\n" 85 | " breq 3f\n" 86 | " ror %[ch]\n" 87 | " rjmp 2b\n" 88 | "3:\n" 89 | : 90 | [ch] "=r" (ch) 91 | : 92 | [bitCnt] "d" (9), 93 | [uartPin] "I" (_SFR_IO_ADDR(UART_PIN)), 94 | [uartBit] "I" (UART_RX_BIT) 95 | : 96 | "r25" 97 | ); 98 | #else 99 | while(!(UART_SRA & _BV(RXC0))) 100 | ; 101 | if (!(UART_SRA & _BV(FE0))) { 102 | /* 103 | * A Framing Error indicates (probably) that something is talking 104 | * to us at the wrong bit rate. Assume that this is because it 105 | * expects to be talking to the application, and DON'T reset the 106 | * watchdog. This should cause the bootloader to abort and run 107 | * the application "soon", if it keeps happening. (Note that we 108 | * don't care that an invalid char is returned...) 109 | */ 110 | watchdogReset(); 111 | } 112 | 113 | ch = UART_UDR; 114 | #endif 115 | 116 | #ifdef LED_DATA_FLASH 117 | #if defined(__AVR_ATmega8__) || defined (__AVR_ATmega32__) || defined (__AVR_ATmega16__) 118 | LED_PORT ^= _BV(LED); 119 | #else 120 | LED_PIN |= _BV(LED); 121 | #endif 122 | #endif 123 | 124 | return ch; 125 | } 126 | 127 | #ifdef SOFT_UART 128 | // AVR305 equation: #define UART_B_VALUE (((F_CPU/BAUD_RATE)-23)/6) 129 | // Adding 3 to numerator simulates nearest rounding for more accurate baud rates 130 | #define UART_B_VALUE (((F_CPU/BAUD_RATE)-20)/6) 131 | #if UART_B_VALUE > 255 132 | #error Baud rate too slow for soft UART 133 | #endif 134 | 135 | void uartDelay() { 136 | __asm__ __volatile__ ( 137 | "ldi r25,%[count]\n" 138 | "1:dec r25\n" 139 | "brne 1b\n" 140 | "ret\n" 141 | ::[count] "M" (UART_B_VALUE) 142 | ); 143 | } 144 | #endif 145 | 146 | uint8_t uart_puts(const char * buf){ 147 | while (*buf) { 148 | putch(*buf++); 149 | } 150 | } 151 | 152 | int get_non_blocking_ch(){ 153 | if(UART_SRA & _BV(RXC0)){ 154 | if (!(UART_SRA & _BV(FE0))) { 155 | /* 156 | * A Framing Error indicates (probably) that something is talking 157 | * to us at the wrong bit rate. Assume that this is because it 158 | * expects to be talking to the application, and DON'T reset the 159 | * watchdog. This should cause the bootloader to abort and run 160 | * the application "soon", if it keeps happening. (Note that we 161 | * don't care that an invalid char is returned...) 162 | */ 163 | watchdogReset(); 164 | } 165 | return(UART_UDR); 166 | } 167 | return -1; 168 | } -------------------------------------------------------------------------------- /gsm.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include "gsm.h" 7 | #include "uart.h" 8 | #include "watchdog.h" 9 | #include "eeprom_addr.h" 10 | #include "gsm_at_cmds.h" 11 | 12 | volatile unsigned long g_seconds; 13 | //NOTE: A unsigned long holds values from 0 to 4,294,967,295 (2^32 - 1). It will roll over to 0 after reaching its maximum value. 14 | 15 | uint8_t get_tcp_trans_cmd(uint8_t * cmd){ 16 | if(cmd){ 17 | char ip_str[32]; 18 | char port_str[12]; 19 | memset(ip_str, 0x00, 32); 20 | memset(port_str, 0x00, 12); 21 | 22 | get_ota_server_ip_str(ip_str); 23 | get_server_port_str(port_str); 24 | 25 | sprintf(cmd, "%s%s,%s\r\n", AT_TCP_TRANS, ip_str, port_str); 26 | return 1; 27 | } 28 | return 0; 29 | } 30 | 31 | uint8_t set_gsm_enable_config(uint8_t * port, uint8_t pin){ 32 | if(port){ 33 | eeprom_write_byte(GSM_REG_PORT_ADDR, port); 34 | eeprom_write_byte(GSM_REG_PIN_ADDR, pin); 35 | return 1; 36 | } 37 | return 0; 38 | } 39 | 40 | void set_ota_server_config(uint8_t * ip_addr, uint16_t port){ 41 | eeprom_write_byte(OTA_SERVER_IP_0, ip_addr[0]); 42 | eeprom_write_byte(OTA_SERVER_IP_1, ip_addr[1]); 43 | eeprom_write_byte(OTA_SERVER_IP_2, ip_addr[2]); 44 | eeprom_write_byte(OTA_SERVER_IP_3, ip_addr[3]); 45 | 46 | eeprom_write_byte(OTA_SERVER_PORT_H, port & 0xFF); 47 | eeprom_write_byte(OTA_SERVER_PORT_L, port >> 8); 48 | } 49 | 50 | void change_pin_state(uint8_t *port, uint8_t pin, uint8_t value){ 51 | unsigned char * ddr = port - 1; 52 | *ddr |= (1 << pin); 53 | if(value){ 54 | *port |= (1 << pin);//make pin 13 high and power on the led 55 | } 56 | else{ 57 | *port &= ~(1 < F_CPU / 1024){ 68 | g_seconds = g_seconds + 1; 69 | TCNT1 = 0; 70 | } 71 | return g_seconds; 72 | } 73 | 74 | uint8_t find_sub_string(char* mainStr, uint8_t mainLen, char* subStr, uint8_t subLen){ 75 | uint8_t ans = NOT_OK; 76 | uint8_t count = 0; 77 | uint8_t offset = 0; 78 | uint8_t i = 0; 79 | 80 | for(i = 0; i < mainLen; i++){ 81 | if(mainStr[i] == subStr[count]){ 82 | count++; 83 | offset++; 84 | } 85 | else{ 86 | count = 0; 87 | } 88 | 89 | if(count == (subLen-1)){ 90 | ans = OK; 91 | return ans; 92 | } 93 | } 94 | return ans; 95 | } 96 | 97 | uint8_t send_at_command(char * ATcommand, char * expected_resp, unsigned long timeout){ 98 | uint8_t x=0; 99 | uint8_t answer = NOT_OK; 100 | char response[SIZE_RESPONSE]; 101 | memset(response,'\0',SIZE_RESPONSE); 102 | unsigned long start_time = elapsed_seconds(); 103 | 104 | uart_puts(ATcommand); // Send the AT command 105 | do{ // this loop waits for the answer until timeouts 106 | int ch = get_non_blocking_ch(); 107 | if(ch > -1){ 108 | char c = (char)ch; 109 | response[x] = c; 110 | x = ((x+1) % SIZE_RESPONSE); 111 | if(strstr(response, expected_resp)){ 112 | answer = OK; 113 | return answer; 114 | } 115 | else if(strstr(response, "ERROR")){ 116 | answer = NOT_OK; 117 | return answer; 118 | } 119 | } 120 | } 121 | while(((unsigned long)(elapsed_seconds() - start_time) < timeout) || answer); 122 | return answer; 123 | } 124 | 125 | void turn_modem_on(void){ 126 | uint8_t * port_addr = eeprom_read_byte(GSM_REG_PORT_ADDR); 127 | uint8_t port_pin = eeprom_read_byte(GSM_REG_PIN_ADDR); 128 | uint8_t high_level = eeprom_read_byte(GSM_REG_PIN_ON_LOGIC_ADDR); 129 | 130 | change_pin_state(port_addr, port_pin, high_level); 131 | } 132 | 133 | void turn_modem_off(void){ 134 | uint8_t * port_addr = eeprom_read_byte(GSM_REG_PORT_ADDR); 135 | uint8_t port_pin = eeprom_read_byte(GSM_REG_PIN_ADDR); 136 | uint8_t high_level = eeprom_read_byte(GSM_REG_PIN_ON_LOGIC_ADDR); 137 | 138 | change_pin_state(port_addr, port_pin, !high_level); 139 | } 140 | 141 | void get_ota_server_ip_str(char * ip_str){ 142 | uint8_t ip_1 = eeprom_read_byte(OTA_SERVER_IP_0); 143 | uint8_t ip_2 = eeprom_read_byte(OTA_SERVER_IP_1); 144 | uint8_t ip_3 = eeprom_read_byte(OTA_SERVER_IP_2); 145 | uint8_t ip_4 = eeprom_read_byte(OTA_SERVER_IP_3); 146 | 147 | sprintf(ip_str, "%d.%d.%d.%d", ip_1, ip_2, ip_3, ip_4); 148 | } 149 | 150 | void get_server_port_str(char * port_str){ 151 | uint8_t port_h = eeprom_read_byte(OTA_SERVER_PORT_H); 152 | uint8_t port_l = eeprom_read_byte(OTA_SERVER_PORT_L); 153 | uint16_t port = ((uint16_t)port_l << 8) | port_h; 154 | 155 | sprintf(port_str, "%d", port); 156 | } 157 | 158 | uint8_t gsm_loop(void){ 159 | init_millis(); 160 | uint8_t ret_val = NOT_OK; 161 | uint8_t gsm_state = INITIAL; 162 | while(gsm_state != IDLE){ 163 | watchdogReset(); 164 | switch(gsm_state){ 165 | case INITIAL:{ 166 | turn_modem_on(); 167 | send_at_command(TCP_CLOSE, "OK", 2); 168 | gsm_state = CHECK_PIN; 169 | break; 170 | } 171 | 172 | case CHECK_PIN:{ 173 | if(send_at_command(AT_CHECK_PIN, "+CPIN: READY", 2)){ 174 | gsm_state = TURN_OFF_ECHO; 175 | } 176 | 177 | _delay_ms(1000); 178 | break; 179 | } 180 | 181 | case TURN_OFF_ECHO:{ 182 | if(send_at_command(AT_ECHO_OFF, "OK", 2)){ 183 | gsm_state = CHECK_GSM; 184 | } 185 | 186 | _delay_ms(1000); 187 | break; 188 | } 189 | 190 | case CHECK_SIGNAL:{ 191 | if(send_at_command(AT_CHECK_SIGNAL, "OK", 2)){ 192 | gsm_state = CHECK_GSM; 193 | } 194 | 195 | _delay_ms(1000); 196 | break; 197 | } 198 | 199 | case CHECK_GSM:{ 200 | if(send_at_command(AT_CHECK_GSM, "+CREG: 0,1", 2)){ 201 | gsm_state = CHECK_GPRS; 202 | } 203 | 204 | _delay_ms(1000); 205 | break; 206 | } 207 | 208 | case CHECK_GPRS:{ 209 | if(send_at_command(AT_CHECK_GPRS, "+CGATT: 1", 2)){ 210 | gsm_state = SET_PPP_LINK; 211 | } 212 | 213 | _delay_ms(1000); 214 | break; 215 | } 216 | 217 | case SET_PPP_LINK:{ 218 | if(send_at_command(AT_SET_PPP_LINK, "OK", 2)){ 219 | gsm_state = CONNECT_TCP; 220 | } 221 | 222 | _delay_ms(1000); 223 | break; 224 | } 225 | 226 | case CHECK_PPP_LINK:{ 227 | if(send_at_command(AT_CHECK_PPP_LINK, "+XIIC: 1,", 2)){ 228 | gsm_state = CONNECT_TCP; 229 | } 230 | 231 | _delay_ms(1000); 232 | break; 233 | } 234 | 235 | case CONNECT_TCP:{ 236 | char AT_TCP_TRANS[128]; 237 | memset(AT_TCP_TRANS, 0x00, 128); 238 | if(get_tcp_trans_cmd(AT_TCP_TRANS)){ 239 | if(send_at_command(AT_TCP_TRANS, "+TCPTRANS: OK", 5)){ 240 | gsm_state = IDLE; 241 | return OK; 242 | } 243 | } 244 | turn_modem_off(); 245 | _delay_ms(1000); 246 | gsm_state = INITIAL; 247 | break; 248 | } 249 | 250 | case IDLE:{ 251 | // uart_puts("GSM Loop Exits !!!\r\n"); 252 | ret_val = OK; 253 | break; 254 | } 255 | 256 | default:{ 257 | break; 258 | } 259 | } 260 | } 261 | 262 | watchdogReset(); 263 | return NOT_OK; 264 | } 265 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # program name should not be changed ... 2 | PROGRAM = AirBootGSM 3 | DEBUG = enable 4 | # DEBUG = disable 5 | 6 | # The default behavior is to build using tools that are in the users 7 | # current path variables, but we can also build using an installed 8 | # Arduino user IDE setup, or the Arduino source tree. 9 | # Uncomment this next lines to build within the arduino environment, 10 | # using the arduino-included avrgcc toolset (mac and pc) 11 | # ENV ?= arduino 12 | # ENV ?= arduinodev 13 | # OS ?= macosx 14 | # OS ?= windows 15 | 16 | MCU_TARGET = atmega328p 17 | LDSECTIONS = -Wl,--section-start=.text=0x7000 #-Wl,--section-start=.version=0x7ffe 18 | 19 | # Build environments 20 | # Start of some ugly makefile-isms to allow optiboot to be built 21 | # in several different environments. See the README.TXT file for 22 | # details. 23 | 24 | # default 25 | fixpath = $(1) 26 | 27 | ifeq ($(ENV), arduino) 28 | # For Arduino, we assume that we're connected to the optiboot directory 29 | # included with the arduino distribution, which means that the full set 30 | # of avr-tools are "right up there" in standard places. 31 | TOOLROOT = ../../../tools 32 | GCCROOT = $(TOOLROOT)/avr/bin/ 33 | AVRDUDE_CONF = -C$(TOOLROOT)/avr/etc/avrdude.conf 34 | 35 | ifeq ($(OS), windows) 36 | # On windows, SOME of the tool paths will need to have backslashes instead 37 | # of forward slashes (because they use windows cmd.exe for execution instead 38 | # of a unix/mingw shell?) We also have to ensure that a consistent shell 39 | # is used even if a unix shell is installed (ie as part of WINAVR) 40 | fixpath = $(subst /,\,$1) 41 | SHELL = cmd.exe 42 | endif 43 | 44 | else ifeq ($(ENV), arduinodev) 45 | # Arduino IDE source code environment. Use the unpacked compilers created 46 | # by the build (you'll need to do "ant build" first.) 47 | ifeq ($(OS), macosx) 48 | TOOLROOT = ../../../../build/macosx/work/Arduino.app/Contents/Resources/Java/hardware/tools 49 | endif 50 | ifeq ($(OS), windows) 51 | TOOLROOT = ../../../../build/windows/work/hardware/tools 52 | endif 53 | 54 | GCCROOT = $(TOOLROOT)/avr/bin/ 55 | AVRDUDE_CONF = -C$(TOOLROOT)/avr/etc/avrdude.conf 56 | 57 | else 58 | GCCROOT = 59 | AVRDUDE_CONF = 60 | endif 61 | 62 | # End of build environment code. 63 | 64 | OBJ = main.o watchdog.o gsm.o optiboot.o uart.o 65 | OPTIMIZE = -Os -flto -fuse-linker-plugin -fno-inline-small-functions -fno-split-wide-types -mno-interrupts -mrelax 66 | OPTIMIZE += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -fno-jump-tables -std=gnu99 #-save-temps 67 | 68 | DEFS = 69 | LIBS = 70 | 71 | CC = $(GCCROOT)avr-gcc 72 | 73 | # Override is only needed by avr-lib build system. 74 | 75 | override CFLAGS = -g -Wall -w -Wextra -Wstrict-prototypes $(OPTIMIZE) -mmcu=$(MCU_TARGET) -DF_CPU=$(AVR_FREQ) $(DEFS) 76 | override LDFLAGS = $(LDSECTIONS) -Wl,--relax -Wl,--gc-sections -flto -fuse-linker-plugin 77 | 78 | OBJCOPY = $(GCCROOT)avr-objcopy 79 | OBJDUMP = $(call fixpath,$(GCCROOT)avr-objdump) 80 | 81 | SIZE = $(GCCROOT)avr-size 82 | 83 | # Build with the debug messages enabled 84 | # Use it only if you have already seen the source code 85 | 86 | ifeq ($(DEBUG), enable) 87 | override CFLAGS += -DENABLE_DEBUG 88 | endif 89 | 90 | ## ATmega8 91 | atmega8_8MHz: TARGET = atmega8_8MHz 92 | atmega8_8MHz: MCU_TARGET = atmega8 93 | atmega8_8MHz: CFLAGS += 94 | atmega8_8MHz: AVR_FREQ = 8000000L 95 | atmega8_8MHz: LDSECTIONS = -Wl,--section-start=.text=0x1e00 -Wl,--section-start=.version=0x1ffe 96 | atmega8_8MHz: $(PROGRAM)_atmega8_8MHz.hex 97 | atmega8_8MHz: $(PROGRAM)_atmega8_8MHz.lst 98 | 99 | atmega8_16MHz: TARGET = atmega8_16MHz 100 | atmega8_16MHz: MCU_TARGET = atmega8 101 | atmega8_16MHz: CFLAGS += 102 | atmega8_16MHz: AVR_FREQ = 16000000L 103 | atmega8_16MHz: LDSECTIONS = -Wl,--section-start=.text=0x1e00 -Wl,--section-start=.version=0x1ffe 104 | atmega8_16MHz: $(PROGRAM)_atmega8_16MHz.hex 105 | atmega8_16MHz: $(PROGRAM)_atmega8_16MHz.lst 106 | 107 | ## ATmega168 108 | atmega16_8MHz: TARGET = atmega16_8MHz 109 | atmega16_8MHz: MCU_TARGET = atmega16 110 | atmega16_8MHz: CFLAGS += 111 | atmega16_8MHz: AVR_FREQ = 8000000L 112 | atmega16_8MHz: LDSECTIONS = -Wl,--section-start=.text=0x1e00 -Wl,--section-start=.version=0x1ffe 113 | atmega16_8MHz: $(PROGRAM)_atmega16_8MHz.hex 114 | atmega16_8MHz: $(PROGRAM)_atmega16_8MHz.lst 115 | 116 | atmega16_16MHz: TARGET = atmega16_16MHz 117 | atmega16_16MHz: MCU_TARGET = atmega16 118 | atmega16_16MHz: CFLAGS += 119 | atmega16_16MHz: AVR_FREQ = 16000000L 120 | atmega16_16MHz: LDSECTIONS = -Wl,--section-start=.text=0x1e00 -Wl,--section-start=.version=0x1ffe 121 | atmega16_16MHz: $(PROGRAM)_atmega16_16MHz.hex 122 | atmega16_16MHz: $(PROGRAM)_atmega16_16MHz.lst 123 | 124 | ## ATmega168 125 | atmega168_8MHz: TARGET = atmega168_8MHz 126 | atmega168_8MHz: MCU_TARGET = atmega168 127 | atmega168_8MHz: CFLAGS += 128 | atmega168_8MHz: AVR_FREQ = 8000000L 129 | atmega168_8MHz: LDSECTIONS = -Wl,--section-start=.text=0x1e00 -Wl,--section-start=.version=0x1ffe 130 | atmega168_8MHz: $(PROGRAM)_atmega168_8MHz.hex 131 | atmega168_8MHz: $(PROGRAM)_atmega168_8MHz.lst 132 | 133 | atmega168_16MHz: TARGET = atmega168_16MHz 134 | atmega168_16MHz: MCU_TARGET = atmega168 135 | atmega168_16MHz: CFLAGS += 136 | atmega168_16MHz: AVR_FREQ = 16000000L 137 | atmega168_16MHz: LDSECTIONS = -Wl,--section-start=.text=0x1e00 -Wl,--section-start=.version=0x1ffe 138 | atmega168_16MHz: $(PROGRAM)_atmega168_16MHz.hex 139 | atmega168_16MHz: $(PROGRAM)_atmega168_16MHz.lst 140 | 141 | ## ATmega328p 142 | atmega328p_8MHz: TARGET = atmega328p_8MHz 143 | atmega328p_8MHz: MCU_TARGET = atmega328p 144 | atmega328p_8MHz: CFLAGS += 145 | atmega328p_8MHz: AVR_FREQ = 8000000L 146 | atmega328p_8MHz: LDSECTIONS = -Wl,--section-start=.text=0x7000 #-Wl,--section-start=.version=0x7ffe 147 | atmega328p_8MHz: $(PROGRAM)_atmega328p_8Mhz.hex 148 | atmega328p_8MHz: $(PROGRAM)_atmega328p_8Mhz.lst 149 | 150 | atmega328p_16MHz: TARGET = atmega328p_16MHz 151 | atmega328p_16MHz: MCU_TARGET = atmega328p 152 | atmega328p_16MHz: CFLAGS += 153 | atmega328p_16MHz: AVR_FREQ = 16000000L 154 | atmega328p_16MHz: LDSECTIONS = -Wl,--section-start=.text=0x7000 #-Wl,--section-start=.version=0x7ffe 155 | atmega328p_16MHz: $(PROGRAM)_atmega328p_16Mhz.hex 156 | atmega328p_16MHz: $(PROGRAM)_atmega328p_16Mhz.lst 157 | 158 | ## Atmega1284p 159 | atmega1284p_8MHz: TARGET = _atmega1284p_8MHz 160 | atmega1284p_8MHz: MCU_TARGET = atmega1284p 161 | atmega1284p_8MHz: CFLAGS += 162 | atmega1284p_8MHz: AVR_FREQ = 8000000L 163 | atmega1284p_8MHz: LDSECTIONS = -Wl,--section-start=.text=0x1e000 164 | atmega1284p_8MHz: $(PROGRAM)_atmega1284p_8MHz.hex 165 | atmega1284p_8MHz: $(PROGRAM)_atmega1284p_8MHz.lst 166 | 167 | atmega1284p_16MHz: TARGET = atmega1284p_16MHz 168 | atmega1284p_16MHz: MCU_TARGET = atmega1284p 169 | atmega1284p_16MHz: CFLAGS += 170 | atmega1284p_16MHz: AVR_FREQ = 16000000L 171 | atmega1284p_16MHz: LDSECTIONS = -Wl,--section-start=.text=0x1e000 172 | atmega1284p_16MHz: $(PROGRAM)_atmega1284p_8MHz.hex 173 | atmega1284p_16MHz: $(PROGRAM)_atmega1284p_8MHz.lst 174 | 175 | ## Generic build instructions 176 | 177 | %.elf: $(OBJ) 178 | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) 179 | $(SIZE) $@ 180 | 181 | clean: 182 | rm -rf *.o *.i *.s *.elf *.lst *.map *.sym *.lss *.eep *.srec *.bin *.hex *~ 183 | 184 | %.lst: %.elf 185 | $(OBJDUMP) -h -S $< > $@ 186 | 187 | %.hex: %.elf 188 | $(OBJCOPY) -j .text -j .data -j .version --set-section-flags .version=alloc,load -O ihex $< $@ 189 | 190 | %.srec: %.elf 191 | $(OBJCOPY) -j .text -j .data -j .version --set-section-flags .version=alloc,load -O srec $< $@ 192 | 193 | %.bin: %.elf 194 | $(OBJCOPY) -j .text -j .data -j .version --set-section-flags .version=alloc,load -O binary $< $@ 195 | -------------------------------------------------------------------------------- /pin_defs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * pin_defs.h 3 | * optiboot helper defining the default pin assignments (LED, SOFT_UART) 4 | * for the various chips that are supported. This also has some ugly macros 5 | * for selecting among various UARTs and LED possibilities using command-line 6 | * defines like "UART=2 LED=B5" 7 | * 8 | * Copyright 2013-2015 by Bill Westfield. 9 | * Copyright 2010 by Peter Knight. 10 | * This software is licensed under version 2 of the Gnu Public Licence. 11 | * See optiboot.c for details. 12 | */ 13 | 14 | /*------------------------------------------------------------------------ */ 15 | #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega88) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega88__) 16 | /* Onboard LED is connected to pin PB5 in Arduino NG, Diecimila, and Duemilanove 17 | */ 18 | #define LED C1 19 | /* Ports for soft UART */ 20 | #ifdef SOFT_UART 21 | #define UART_PORT PORTD 22 | #define UART_PIN PIND 23 | #define UART_DDR DDRD 24 | #define UART_TX_BIT 1 25 | #define UART_RX_BIT 0 26 | #endif 27 | #endif 28 | 29 | /* 30 | * Handle devices with up to 4 uarts (eg m1280.) Rather inelegantly. 31 | * Note that mega8/m32 still needs special handling, because ubrr is handled 32 | * differently. 33 | */ 34 | #if UART == 0 35 | # define UART_SRA UCSR0A 36 | # define UART_SRB UCSR0B 37 | # define UART_SRC UCSR0C 38 | # define UART_SRL UBRR0L 39 | # define UART_UDR UDR0 40 | #elif UART == 1 41 | #if !defined(UDR1) 42 | #error UART == 1, but no UART1 on device 43 | #endif 44 | # define UART_SRA UCSR1A 45 | # define UART_SRB UCSR1B 46 | # define UART_SRC UCSR1C 47 | # define UART_SRL UBRR1L 48 | # define UART_UDR UDR1 49 | #elif UART == 2 50 | #if !defined(UDR2) 51 | #error UART == 2, but no UART2 on device 52 | #endif 53 | # define UART_SRA UCSR2A 54 | # define UART_SRB UCSR2B 55 | # define UART_SRC UCSR2C 56 | # define UART_SRL UBRR2L 57 | # define UART_UDR UDR2 58 | #elif UART == 3 59 | #if !defined(UDR3) 60 | #error UART == 3, but no UART3 on device 61 | #endif 62 | # define UART_SRA UCSR3A 63 | # define UART_SRB UCSR3B 64 | # define UART_SRC UCSR3C 65 | # define UART_SRL UBRR3L 66 | # define UART_UDR UDR3 67 | #endif 68 | 69 | #if defined(__AVR_ATmega8__) || defined (__AVR_ATmega32__) || defined (__AVR_ATmega16__) 70 | //Name conversion R.Wiersma 71 | #define UCSR0A UCSRA 72 | #define UDR0 UDR 73 | #define UDRE0 UDRE 74 | #define RXC0 RXC 75 | #define FE0 FE 76 | #define TIFR1 TIFR 77 | #define WDTCSR WDTCR 78 | #endif 79 | 80 | #if defined (__AVR_ATmega32__) || defined (__AVR_ATmega16__) 81 | #define WDCE WDTOE 82 | #endif 83 | 84 | /* Luminet support */ 85 | /*------------------------------------------------------------------------ */ 86 | #if defined(__AVR_ATtiny84__) 87 | /* Red LED is connected to pin PA4 */ 88 | #define LED A4 89 | /* Ports for soft UART - left port only for now. TX/RX on PA2/PA3 */ 90 | #ifdef SOFT_UART 91 | #define UART_PORT PORTA 92 | #define UART_PIN PINA 93 | #define UART_DDR DDRA 94 | #define UART_TX_BIT 2 95 | #define UART_RX_BIT 3 96 | #endif 97 | #endif 98 | 99 | /*------------------------------------------------------------------------ */ 100 | /* Sanguino support (and other 40pin DIP cpus) */ 101 | #if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega32__) || defined (__AVR_ATmega16__) 102 | /* Onboard LED is connected to pin PB0 on Sanguino */ 103 | #define LED B0 104 | /* Ports for soft UART */ 105 | #ifdef SOFT_UART 106 | #define UART_PORT PORTD 107 | #define UART_PIN PIND 108 | #define UART_DDR DDRD 109 | #define UART_TX_BIT 1 110 | #define UART_RX_BIT 0 111 | #endif 112 | #endif 113 | 114 | /*------------------------------------------------------------------------ */ 115 | /* Mega support */ 116 | #if defined(__AVR_ATmega1280__) 117 | /* Onboard LED is connected to pin PB7 on Arduino Mega */ 118 | #define LED B7 119 | /* Ports for soft UART */ 120 | #ifdef SOFT_UART 121 | #define UART_PORT PORTE 122 | #define UART_PIN PINE 123 | #define UART_DDR DDRE 124 | #define UART_TX_BIT 1 125 | #define UART_RX_BIT 0 126 | #endif 127 | #endif 128 | 129 | /* 130 | * ------------------------------------------------------------------------ 131 | * A bunch of macros to enable the LED to be specifed as "B5" for bit 5 132 | * of port B, and similar. 133 | */ 134 | 135 | #define A0 0x100 136 | #define A1 0x101 137 | #define A2 0x102 138 | #define A3 0x103 139 | #define A4 0x104 140 | #define A5 0x105 141 | #define A6 0x106 142 | #define A7 0x107 143 | 144 | #define B0 0x200 145 | #define B1 0x201 146 | #define B2 0x202 147 | #define B3 0x203 148 | #define B4 0x204 149 | #define B5 0x205 150 | #define B6 0x206 151 | #define B7 0x207 152 | 153 | #define C0 0x300 154 | #define C1 0x301 155 | #define C2 0x302 156 | #define C3 0x303 157 | #define C4 0x304 158 | #define C5 0x305 159 | #define C6 0x306 160 | #define C7 0x307 161 | 162 | #define D0 0x400 163 | #define D1 0x401 164 | #define D2 0x402 165 | #define D3 0x403 166 | #define D4 0x404 167 | #define D5 0x405 168 | #define D6 0x406 169 | #define D7 0x407 170 | 171 | #define E0 0x500 172 | #define E1 0x501 173 | #define E2 0x502 174 | #define E3 0x503 175 | #define E4 0x504 176 | #define E5 0x505 177 | #define E6 0x506 178 | #define E7 0x507 179 | 180 | #define F0 0x600 181 | #define F1 0x601 182 | #define F2 0x602 183 | #define F3 0x603 184 | #define F4 0x604 185 | #define F5 0x605 186 | #define F6 0x606 187 | #define F7 0x607 188 | 189 | #define G0 0x700 190 | #define G1 0x701 191 | #define G2 0x702 192 | #define G3 0x703 193 | #define G4 0x704 194 | #define G5 0x705 195 | #define G6 0x706 196 | #define G7 0x707 197 | 198 | #define H0 0x800 199 | #define H1 0x801 200 | #define H2 0x802 201 | #define H3 0x803 202 | #define H4 0x804 203 | #define H5 0x805 204 | #define H6 0x806 205 | #define H7 0x807 206 | 207 | #define J0 0xA00 208 | #define J1 0xA01 209 | #define J2 0xA02 210 | #define J3 0xA03 211 | #define J4 0xA04 212 | #define J5 0xA05 213 | #define J6 0xA06 214 | #define J7 0xA07 215 | 216 | #define K0 0xB00 217 | #define K1 0xB01 218 | #define K2 0xB02 219 | #define K3 0xB03 220 | #define K4 0xB04 221 | #define K5 0xB05 222 | #define K6 0xB06 223 | #define K7 0xB07 224 | 225 | #define L0 0xC00 226 | #define L1 0xC01 227 | #define L2 0xC02 228 | #define L3 0xC03 229 | #define L4 0xC04 230 | #define L5 0xC05 231 | #define L6 0xC06 232 | #define L7 0xC07 233 | 234 | #if LED == B0 235 | #undef LED 236 | #define LED_DDR DDRB 237 | #define LED_PORT PORTB 238 | #define LED_PIN PINB 239 | #define LED PINB0 240 | #elif LED == B1 241 | #undef LED 242 | #define LED_DDR DDRB 243 | #define LED_PORT PORTB 244 | #define LED_PIN PINB 245 | #define LED PINB1 246 | #elif LED == B2 247 | #undef LED 248 | #define LED_DDR DDRB 249 | #define LED_PORT PORTB 250 | #define LED_PIN PINB 251 | #define LED PINB2 252 | #elif LED == B3 253 | #undef LED 254 | #define LED_DDR DDRB 255 | #define LED_PORT PORTB 256 | #define LED_PIN PINB 257 | #define LED PINB3 258 | #elif LED == B4 259 | #undef LED 260 | #define LED_DDR DDRB 261 | #define LED_PORT PORTB 262 | #define LED_PIN PINB 263 | #define LED PINB4 264 | #elif LED == B5 265 | #undef LED 266 | #define LED_DDR DDRB 267 | #define LED_PORT PORTB 268 | #define LED_PIN PINB 269 | #define LED PINB5 270 | #elif LED == B6 271 | #undef LED 272 | #define LED_DDR DDRB 273 | #define LED_PORT PORTB 274 | #define LED_PIN PINB 275 | #define LED PINB6 276 | #elif LED == B7 277 | #undef LED 278 | #define LED_DDR DDRB 279 | #define LED_PORT PORTB 280 | #define LED_PIN PINB 281 | #define LED PINB7 282 | 283 | #elif LED == C0 284 | #undef LED 285 | #define LED_DDR DDRC 286 | #define LED_PORT PORTC 287 | #define LED_PIN PINC 288 | #define LED PINC0 289 | #elif LED == C1 290 | #undef LED 291 | #define LED_DDR DDRC 292 | #define LED_PORT PORTC 293 | #define LED_PIN PINC 294 | #define LED PINC1 295 | #elif LED == C2 296 | #undef LED 297 | #define LED_DDR DDRC 298 | #define LED_PORT PORTC 299 | #define LED_PIN PINC 300 | #define LED PINC2 301 | #elif LED == C3 302 | #undef LED 303 | #define LED_DDR DDRC 304 | #define LED_PORT PORTC 305 | #define LED_PIN PINC 306 | #define LED PINC3 307 | #elif LED == C4 308 | #undef LED 309 | #define LED_DDR DDRC 310 | #define LED_PORT PORTC 311 | #define LED_PIN PINC 312 | #define LED PINC4 313 | #elif LED == C5 314 | #undef LED 315 | #define LED_DDR DDRC 316 | #define LED_PORT PORTC 317 | #define LED_PIN PINC 318 | #define LED PINC5 319 | #elif LED == C6 320 | #undef LED 321 | #define LED_DDR DDRC 322 | #define LED_PORT PORTC 323 | #define LED_PIN PINC 324 | #define LED PINC6 325 | #elif LED == C7 326 | #undef LED 327 | #define LED_DDR DDRC 328 | #define LED_PORT PORTC 329 | #define LED_PIN PINC 330 | #define LED PINC7 331 | 332 | #elif LED == D0 333 | #undef LED 334 | #define LED_DDR DDRD 335 | #define LED_PORT PORTD 336 | #define LED_PIN PIND 337 | #define LED PIND0 338 | #elif LED == D1 339 | #undef LED 340 | #define LED_DDR DDRD 341 | #define LED_PORT PORTD 342 | #define LED_PIN PIND 343 | #define LED PIND1 344 | #elif LED == D2 345 | #undef LED 346 | #define LED_DDR DDRD 347 | #define LED_PORT PORTD 348 | #define LED_PIN PIND 349 | #define LED PIND2 350 | #elif LED == D3 351 | #undef LED 352 | #define LED_DDR DDRD 353 | #define LED_PORT PORTD 354 | #define LED_PIN PIND 355 | #define LED PIND3 356 | #elif LED == D4 357 | #undef LED 358 | #define LED_DDR DDRD 359 | #define LED_PORT PORTD 360 | #define LED_PIN PIND 361 | #define LED PIND4 362 | #elif LED == D5 363 | #undef LED 364 | #define LED_DDR DDRD 365 | #define LED_PORT PORTD 366 | #define LED_PIN PIND 367 | #define LED PIND5 368 | #elif LED == D6 369 | #undef LED 370 | #define LED_DDR DDRD 371 | #define LED_PORT PORTD 372 | #define LED_PIN PIND 373 | #define LED PIND6 374 | #elif LED == D7 375 | #undef LED 376 | #define LED_DDR DDRD 377 | #define LED_PORT PORTD 378 | #define LED_PIN PIND 379 | #define LED PIND7 380 | 381 | #elif LED == E0 382 | #undef LED 383 | #define LED_DDR DDRE 384 | #define LED_PORT PORTE 385 | #define LED_PIN PINE 386 | #define LED PINE0 387 | #elif LED == E1 388 | #undef LED 389 | #define LED_DDR DDRE 390 | #define LED_PORT PORTE 391 | #define LED_PIN PINE 392 | #define LED PINE1 393 | #elif LED == E2 394 | #undef LED 395 | #define LED_DDR DDRE 396 | #define LED_PORT PORTE 397 | #define LED_PIN PINE 398 | #define LED PINE2 399 | #elif LED == E3 400 | #undef LED 401 | #define LED_DDR DDRE 402 | #define LED_PORT PORTE 403 | #define LED_PIN PINE 404 | #define LED PINE3 405 | #elif LED == E4 406 | #undef LED 407 | #define LED_DDR DDRE 408 | #define LED_PORT PORTE 409 | #define LED_PIN PINE 410 | #define LED PINE4 411 | #elif LED == E5 412 | #undef LED 413 | #define LED_DDR DDRE 414 | #define LED_PORT PORTE 415 | #define LED_PIN PINE 416 | #define LED PINE5 417 | #elif LED == E6 418 | #undef LED 419 | #define LED_DDR DDRE 420 | #define LED_PORT PORTE 421 | #define LED_PIN PINE 422 | #define LED PINE6 423 | #elif LED == E7 424 | #undef LED 425 | #define LED_DDR DDRE 426 | #define LED_PORT PORTE 427 | #define LED_PIN PINE 428 | #define LED PINE7 429 | 430 | #elif LED == F0 431 | #undef LED 432 | #define LED_DDR DDRF 433 | #define LED_PORT PORTF 434 | #define LED_PIN PINF 435 | #define LED PINF0 436 | #elif LED == F1 437 | #undef LED 438 | #define LED_DDR DDRF 439 | #define LED_PORT PORTF 440 | #define LED_PIN PINF 441 | #define LED PINF1 442 | #elif LED == F2 443 | #undef LED 444 | #define LED_DDR DDRF 445 | #define LED_PORT PORTF 446 | #define LED_PIN PINF 447 | #define LED PINF2 448 | #elif LED == F3 449 | #undef LED 450 | #define LED_DDR DDRF 451 | #define LED_PORT PORTF 452 | #define LED_PIN PINF 453 | #define LED PINF3 454 | #elif LED == F4 455 | #undef LED 456 | #define LED_DDR DDRF 457 | #define LED_PORT PORTF 458 | #define LED_PIN PINF 459 | #define LED PINF4 460 | #elif LED == F5 461 | #undef LED 462 | #define LED_DDR DDRF 463 | #define LED_PORT PORTF 464 | #define LED_PIN PINF 465 | #define LED PINF5 466 | #elif LED == F6 467 | #undef LED 468 | #define LED_DDR DDRF 469 | #define LED_PORT PORTF 470 | #define LED_PIN PINF 471 | #define LED PINF6 472 | #elif LED == F7 473 | #undef LED 474 | #define LED_DDR DDRF 475 | #define LED_PORT PORTF 476 | #define LED_PIN PINF 477 | #define LED PINF7 478 | 479 | #elif LED == G0 480 | #undef LED 481 | #define LED_DDR DDRG 482 | #define LED_PORT PORTG 483 | #define LED_PIN PING 484 | #define LED PING0 485 | #elif LED == G1 486 | #undef LED 487 | #define LED_DDR DDRG 488 | #define LED_PORT PORTG 489 | #define LED_PIN PING 490 | #define LED PING1 491 | #elif LED == G2 492 | #undef LED 493 | #define LED_DDR DDRG 494 | #define LED_PORT PORTG 495 | #define LED_PIN PING 496 | #define LED PING2 497 | #elif LED == G3 498 | #undef LED 499 | #define LED_DDR DDRG 500 | #define LED_PORT PORTG 501 | #define LED_PIN PING 502 | #define LED PING3 503 | #elif LED == G4 504 | #undef LED 505 | #define LED_DDR DDRG 506 | #define LED_PORT PORTG 507 | #define LED_PIN PING 508 | #define LED PING4 509 | #elif LED == G5 510 | #undef LED 511 | #define LED_DDR DDRG 512 | #define LED_PORT PORTG 513 | #define LED_PIN PING 514 | #define LED PING5 515 | #elif LED == G6 516 | #undef LED 517 | #define LED_DDR DDRG 518 | #define LED_PORT PORTG 519 | #define LED_PIN PING 520 | #define LED PING6 521 | #elif LED == G7 522 | #undef LED 523 | #define LED_DDR DDRG 524 | #define LED_PORT PORTG 525 | #define LED_PIN PING 526 | #define LED PING7 527 | 528 | #elif LED == H0 529 | #undef LED 530 | #define LED_DDR DDRH 531 | #define LED_PORT PORTH 532 | #define LED_PIN PINH 533 | #define LED PINH0 534 | #elif LED == H1 535 | #undef LED 536 | #define LED_DDR DDRH 537 | #define LED_PORT PORTH 538 | #define LED_PIN PINH 539 | #define LED PINH1 540 | #elif LED == H2 541 | #undef LED 542 | #define LED_DDR DDRH 543 | #define LED_PORT PORTH 544 | #define LED_PIN PINH 545 | #define LED PINH2 546 | #elif LED == H3 547 | #undef LED 548 | #define LED_DDR DDRH 549 | #define LED_PORT PORTH 550 | #define LED_PIN PINH 551 | #define LED PINH3 552 | #elif LED == H4 553 | #undef LED 554 | #define LED_DDR DDRH 555 | #define LED_PORT PORTH 556 | #define LED_PIN PINH 557 | #define LED PINH4 558 | #elif LED == H5 559 | #undef LED 560 | #define LED_DDR DDRH 561 | #define LED_PORT PORTH 562 | #define LED_PIN PINH 563 | #define LED PINH5 564 | #elif LED == H6 565 | #undef LED 566 | #define LED_DDR DDRH 567 | #define LED_PORT PORTH 568 | #define LED_PIN PINH 569 | #define LED PINH6 570 | #elif LED == H7 571 | #undef LED 572 | #define LED_DDR DDRH 573 | #define LED_PORT PORTH 574 | #define LED_PIN PINH 575 | #define LED PINH7 576 | 577 | #elif LED == J0 578 | #undef LED 579 | #define LED_DDR DDRJ 580 | #define LED_PORT PORTJ 581 | #define LED_PIN PINJ 582 | #define LED PINJ0 583 | #elif LED == J1 584 | #undef LED 585 | #define LED_DDR DDRJ 586 | #define LED_PORT PORTJ 587 | #define LED_PIN PINJ 588 | #define LED PINJ1 589 | #elif LED == J2 590 | #undef LED 591 | #define LED_DDR DDRJ 592 | #define LED_PORT PORTJ 593 | #define LED_PIN PINJ 594 | #define LED PINJ2 595 | #elif LED == J3 596 | #undef LED 597 | #define LED_DDR DDRJ 598 | #define LED_PORT PORTJ 599 | #define LED_PIN PINJ 600 | #define LED PINJ3 601 | #elif LED == J4 602 | #undef LED 603 | #define LED_DDR DDRJ 604 | #define LED_PORT PORTJ 605 | #define LED_PIN PINJ 606 | #define LED PINJ4 607 | #elif LED == J5 608 | #undef LED 609 | #define LED_DDR DDRJ 610 | #define LED_PORT PORTJ 611 | #define LED_PIN PINJ 612 | #define LED PINJ5 613 | #elif LED == J6 614 | #undef LED 615 | #define LED_DDR DDRJ 616 | #define LED_PORT PORTJ 617 | #define LED_PIN PINJ 618 | #define LED PINJ6 619 | #elif LED == J7 620 | #undef LED 621 | #define LED_DDR DDRJ 622 | #define LED_PORT PORTJ 623 | #define LED_PIN PINJ 624 | #define LED PINJ7 625 | 626 | #elif LED == K0 627 | #undef LED 628 | #define LED_DDR DDRK 629 | #define LED_PORT PORTK 630 | #define LED_PIN PINK 631 | #define LED PINK0 632 | #elif LED == K1 633 | #undef LED 634 | #define LED_DDR DDRK 635 | #define LED_PORT PORTK 636 | #define LED_PIN PINK 637 | #define LED PINK1 638 | #elif LED == K2 639 | #undef LED 640 | #define LED_DDR DDRK 641 | #define LED_PORT PORTK 642 | #define LED_PIN PINK 643 | #define LED PINK2 644 | #elif LED == K3 645 | #undef LED 646 | #define LED_DDR DDRK 647 | #define LED_PORT PORTK 648 | #define LED_PIN PINK 649 | #define LED PINK3 650 | #elif LED == K4 651 | #undef LED 652 | #define LED_DDR DDRK 653 | #define LED_PORT PORTK 654 | #define LED_PIN PINK 655 | #define LED PINK4 656 | #elif LED == K5 657 | #undef LED 658 | #define LED_DDR DDRK 659 | #define LED_PORT PORTK 660 | #define LED_PIN PINK 661 | #define LED PINK5 662 | #elif LED == K6 663 | #undef LED 664 | #define LED_DDR DDRK 665 | #define LED_PORT PORTK 666 | #define LED_PIN PINK 667 | #define LED PINK6 668 | #elif LED == K7 669 | #undef LED 670 | #define LED_DDR DDRK 671 | #define LED_PORT PORTK 672 | #define LED_PIN PINK 673 | #define LED PINK7 674 | 675 | #elif LED == L0 676 | #undef LED 677 | #define LED_DDR DDRL 678 | #define LED_PORT PORTL 679 | #define LED_PIN PINL 680 | #define LED PINL0 681 | #elif LED == L1 682 | #undef LED 683 | #define LED_DDR DDRL 684 | #define LED_PORT PORTL 685 | #define LED_PIN PINL 686 | #define LED PINL1 687 | #elif LED == L2 688 | #undef LED 689 | #define LED_DDR DDRL 690 | #define LED_PORT PORTL 691 | #define LED_PIN PINL 692 | #define LED PINL2 693 | #elif LED == L3 694 | #undef LED 695 | #define LED_DDR DDRL 696 | #define LED_PORT PORTL 697 | #define LED_PIN PINL 698 | #define LED PINL3 699 | #elif LED == L4 700 | #undef LED 701 | #define LED_DDR DDRL 702 | #define LED_PORT PORTL 703 | #define LED_PIN PINL 704 | #define LED PINL4 705 | #elif LED == L5 706 | #undef LED 707 | #define LED_DDR DDRL 708 | #define LED_PORT PORTL 709 | #define LED_PIN PINL 710 | #define LED PINL5 711 | #elif LED == L6 712 | #undef LED 713 | #define LED_DDR DDRL 714 | #define LED_PORT PORTL 715 | #define LED_PIN PINL 716 | #define LED PINL6 717 | #elif LED == L7 718 | #undef LED 719 | #define LED_DDR DDRL 720 | #define LED_PORT PORTL 721 | #define LED_PIN PINL 722 | #define LED PINL7 723 | 724 | #elif LED == A0 725 | #undef LED 726 | #define LED_DDR DDRA 727 | #define LED_PORT PORTA 728 | #define LED_PIN PINA 729 | #define LED PINA0 730 | #elif LED == A1 731 | #undef LED 732 | #define LED_DDR DDRA 733 | #define LED_PORT PORTA 734 | #define LED_PIN PINA 735 | #define LED PINA1 736 | #elif LED == A2 737 | #undef LED 738 | #define LED_DDR DDRA 739 | #define LED_PORT PORTA 740 | #define LED_PIN PINA 741 | #define LED PINA2 742 | #elif LED == A3 743 | #undef LED 744 | #define LED_DDR DDRA 745 | #define LED_PORT PORTA 746 | #define LED_PIN PINA 747 | #define LED PINA3 748 | #elif LED == A4 749 | #undef LED 750 | #define LED_DDR DDRA 751 | #define LED_PORT PORTA 752 | #define LED_PIN PINA 753 | #define LED PINA4 754 | #elif LED == A5 755 | #undef LED 756 | #define LED_DDR DDRA 757 | #define LED_PORT PORTA 758 | #define LED_PIN PINA 759 | #define LED PINA5 760 | #elif LED == A6 761 | #undef LED 762 | #define LED_DDR DDRA 763 | #define LED_PORT PORTA 764 | #define LED_PIN PINA 765 | #define LED PINA6 766 | #elif LED == A7 767 | #undef LED 768 | #define LED_DDR DDRA 769 | #define LED_PORT PORTA 770 | #define LED_PIN PINA 771 | #define LED PINA7 772 | 773 | #else 774 | #error ------------------------------------------- 775 | #error Unrecognized LED name. Should be like "B5" 776 | #error ------------------------------------------- 777 | #endif 778 | -------------------------------------------------------------------------------- /optiboot.c: -------------------------------------------------------------------------------- 1 | #define FUNC_READ 1 2 | #define FUNC_WRITE 1 3 | /**********************************************************/ 4 | /* Optiboot bootloader for Arduino */ 5 | /* */ 6 | /* http://optiboot.googlecode.com */ 7 | /* */ 8 | /* Arduino-maintained version : See README.TXT */ 9 | /* http://code.google.com/p/arduino/ */ 10 | /* It is the intent that changes not relevant to the */ 11 | /* Arduino production envionment get moved from the */ 12 | /* optiboot project to the arduino project in "lumps." */ 13 | /* */ 14 | /* Heavily optimised bootloader that is faster and */ 15 | /* smaller than the Arduino standard bootloader */ 16 | /* */ 17 | /* Enhancements: */ 18 | /* Fits in 512 bytes, saving 1.5K of code space */ 19 | /* Higher baud rate speeds up programming */ 20 | /* Written almost entirely in C */ 21 | /* Customisable timeout with accurate timeconstant */ 22 | /* Optional virtual UART. No hardware UART required. */ 23 | /* Optional virtual boot partition for devices without. */ 24 | /* */ 25 | /* What you lose: */ 26 | /* Implements a skeleton STK500 protocol which is */ 27 | /* missing several features including EEPROM */ 28 | /* programming and non-page-aligned writes */ 29 | /* High baud rate breaks compatibility with standard */ 30 | /* Arduino flash settings */ 31 | /* */ 32 | /* Fully supported: */ 33 | /* ATmega168 based devices (Diecimila etc) */ 34 | /* ATmega328P based devices (Duemilanove etc) */ 35 | /* */ 36 | /* Beta test (believed working.) */ 37 | /* ATmega8 based devices (Arduino legacy) */ 38 | /* ATmega328 non-picopower devices */ 39 | /* ATmega644P based devices (Sanguino) */ 40 | /* ATmega1284P based devices */ 41 | /* ATmega1280 based devices (Arduino Mega) */ 42 | /* */ 43 | /* Alpha test */ 44 | /* ATmega32 */ 45 | /* */ 46 | /* Work in progress: */ 47 | /* ATtiny84 based devices (Luminet) */ 48 | /* */ 49 | /* Does not support: */ 50 | /* USB based devices (eg. Teensy, Leonardo) */ 51 | /* */ 52 | /* Assumptions: */ 53 | /* The code makes several assumptions that reduce the */ 54 | /* code size. They are all true after a hardware reset, */ 55 | /* but may not be true if the bootloader is called by */ 56 | /* other means or on other hardware. */ 57 | /* No interrupts can occur */ 58 | /* UART and Timer 1 are set to their reset state */ 59 | /* SP points to RAMEND */ 60 | /* */ 61 | /* Code builds on code, libraries and optimisations from: */ 62 | /* stk500boot.c by Jason P. Kyle */ 63 | /* Arduino bootloader http://arduino.cc */ 64 | /* Spiff's 1K bootloader http://spiffie.org/know/arduino_1k_bootloader/bootloader.shtml */ 65 | /* avr-libc project http://nongnu.org/avr-libc */ 66 | /* Adaboot http://www.ladyada.net/library/arduino/bootloader.html */ 67 | /* AVR305 Atmel Application Note */ 68 | /* */ 69 | 70 | /* Copyright 2013-2015 by Bill Westfield. */ 71 | /* Copyright 2010 by Peter Knight. */ 72 | /* */ 73 | /* This program is free software; you can redistribute it */ 74 | /* and/or modify it under the terms of the GNU General */ 75 | /* Public License as published by the Free Software */ 76 | /* Foundation; either version 2 of the License, or */ 77 | /* (at your option) any later version. */ 78 | /* */ 79 | /* This program is distributed in the hope that it will */ 80 | /* be useful, but WITHOUT ANY WARRANTY; without even the */ 81 | /* implied warranty of MERCHANTABILITY or FITNESS FOR A */ 82 | /* PARTICULAR PURPOSE. See the GNU General Public */ 83 | /* License for more details. */ 84 | /* */ 85 | /* You should have received a copy of the GNU General */ 86 | /* Public License along with this program; if not, write */ 87 | /* to the Free Software Foundation, Inc., */ 88 | /* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 89 | /* */ 90 | /* Licence can be viewed at */ 91 | /* http://www.fsf.org/licenses/gpl.txt */ 92 | /* */ 93 | /**********************************************************/ 94 | 95 | 96 | /**********************************************************/ 97 | /* */ 98 | /* Optional defines: */ 99 | /* */ 100 | /**********************************************************/ 101 | /* */ 102 | /* BIGBOOT: */ 103 | /* Build a 1k bootloader, not 512 bytes. This turns on */ 104 | /* extra functionality. */ 105 | /* */ 106 | /* BAUD_RATE: */ 107 | /* Set bootloader baud rate. */ 108 | /* */ 109 | /* SOFT_UART: */ 110 | /* Use AVR305 soft-UART instead of hardware UART. */ 111 | /* */ 112 | /* LED_START_FLASHES: */ 113 | /* Number of LED flashes on bootup. */ 114 | /* */ 115 | /* LED_DATA_FLASH: */ 116 | /* Flash LED when transferring data. For boards without */ 117 | /* TX or RX LEDs, or for people who like blinky lights. */ 118 | /* */ 119 | /* SUPPORT_EEPROM: */ 120 | /* Support reading and writing from EEPROM. This is not */ 121 | /* used by Arduino, so off by default. */ 122 | /* */ 123 | /* TIMEOUT_MS: */ 124 | /* Bootloader timeout period, in milliseconds. */ 125 | /* 500,1000,2000,4000,8000 supported. */ 126 | /* */ 127 | /* UART: */ 128 | /* UART number (0..n) for devices with more than */ 129 | /* one hardware uart (644P, 1284P, etc) */ 130 | /* */ 131 | /**********************************************************/ 132 | 133 | /**********************************************************/ 134 | /* Version Numbers! */ 135 | /* */ 136 | /* Arduino Optiboot now includes this Version number in */ 137 | /* the source and object code. */ 138 | /* */ 139 | /* Version 3 was released as zip from the optiboot */ 140 | /* repository and was distributed with Arduino 0022. */ 141 | /* Version 4 starts with the arduino repository commit */ 142 | /* that brought the arduino repository up-to-date with */ 143 | /* the optiboot source tree changes since v3. */ 144 | /* Version 5 was created at the time of the new Makefile */ 145 | /* structure (Mar, 2013), even though no binaries changed*/ 146 | /* It would be good if versions implemented outside the */ 147 | /* official repository used an out-of-seqeunce version */ 148 | /* number (like 104.6 if based on based on 4.5) to */ 149 | /* prevent collisions. */ 150 | /* */ 151 | /**********************************************************/ 152 | 153 | /**********************************************************/ 154 | /* Edit History: */ 155 | /* */ 156 | /* Aug 2014 */ 157 | /* 6.2 WestfW: make size of length variables dependent */ 158 | /* on the SPM_PAGESIZE. This saves space */ 159 | /* on the chips where it's most important. */ 160 | /* 6.1 WestfW: Fix OPTIBOOT_CUSTOMVER (send it!) */ 161 | /* Make no-wait mod less picky about */ 162 | /* skipping the bootloader. */ 163 | /* Remove some dead code */ 164 | /* Jun 2014 */ 165 | /* 6.0 WestfW: Modularize memory read/write functions */ 166 | /* Remove serial/flash overlap */ 167 | /* (and all references to NRWWSTART/etc) */ 168 | /* Correctly handle pagesize > 255bytes */ 169 | /* Add EEPROM support in BIGBOOT (1284) */ 170 | /* EEPROM write on small chips now causes err */ 171 | /* Split Makefile into smaller pieces */ 172 | /* Add Wicked devices Wildfire */ 173 | /* Move UART=n conditionals into pin_defs.h */ 174 | /* Remove LUDICOUS_SPEED option */ 175 | /* Replace inline assembler for .version */ 176 | /* and add OPTIBOOT_CUSTOMVER for user code */ 177 | /* Fix LED value for Bobuino (Makefile) */ 178 | /* Make all functions explicitly inline or */ 179 | /* noinline, so we fit when using gcc4.8 */ 180 | /* Change optimization options for gcc4.8 */ 181 | /* Make ENV=arduino work in 1.5.x trees. */ 182 | /* May 2014 */ 183 | /* 5.0 WestfW: Add support for 1Mbps UART */ 184 | /* Mar 2013 */ 185 | /* 5.0 WestfW: Major Makefile restructuring. */ 186 | /* See Makefile and pin_defs.h */ 187 | /* (no binary changes) */ 188 | /* */ 189 | /* 4.6 WestfW/Pito: Add ATmega32 support */ 190 | /* 4.6 WestfW/radoni: Don't set LED_PIN as an output if */ 191 | /* not used. (LED_START_FLASHES = 0) */ 192 | /* Jan 2013 */ 193 | /* 4.6 WestfW/dkinzer: use autoincrement lpm for read */ 194 | /* 4.6 WestfW/dkinzer: pass reset cause to app in R2 */ 195 | /* Mar 2012 */ 196 | /* 4.5 WestfW: add infrastructure for non-zero UARTS. */ 197 | /* 4.5 WestfW: fix SIGNATURE_2 for m644 (bad in avr-libc) */ 198 | /* Jan 2012: */ 199 | /* 4.5 WestfW: fix NRWW value for m1284. */ 200 | /* 4.4 WestfW: use attribute OS_main instead of naked for */ 201 | /* main(). This allows optimizations that we */ 202 | /* count on, which are prohibited in naked */ 203 | /* functions due to PR42240. (keeps us less */ 204 | /* than 512 bytes when compiler is gcc4.5 */ 205 | /* (code from 4.3.2 remains the same.) */ 206 | /* 4.4 WestfW and Maniacbug: Add m1284 support. This */ 207 | /* does not change the 328 binary, so the */ 208 | /* version number didn't change either. (?) */ 209 | /* June 2011: */ 210 | /* 4.4 WestfW: remove automatic soft_uart detect (didn't */ 211 | /* know what it was doing or why.) Added a */ 212 | /* check of the calculated BRG value instead. */ 213 | /* Version stays 4.4; existing binaries are */ 214 | /* not changed. */ 215 | /* 4.4 WestfW: add initialization of address to keep */ 216 | /* the compiler happy. Change SC'ed targets. */ 217 | /* Return the SW version via READ PARAM */ 218 | /* 4.3 WestfW: catch framing errors in getch(), so that */ 219 | /* AVRISP works without HW kludges. */ 220 | /* http://code.google.com/p/arduino/issues/detail?id=368n*/ 221 | /* 4.2 WestfW: reduce code size, fix timeouts, change */ 222 | /* verifySpace to use WDT instead of appstart */ 223 | /* 4.1 WestfW: put version number in binary. */ 224 | /**********************************************************/ 225 | 226 | #define OPTIBOOT_MAJVER 6 227 | #define OPTIBOOT_MINVER 2 228 | 229 | /* 230 | * OPTIBOOT_CUSTOMVER should be defined (by the makefile) for custom edits 231 | * of optiboot. That way you don't wind up with very different code that 232 | * matches the version number of a "released" optiboot. 233 | */ 234 | 235 | #if !defined(OPTIBOOT_CUSTOMVER) 236 | #define OPTIBOOT_CUSTOMVER 0 237 | #endif 238 | 239 | unsigned const int __attribute__((section(".version"))) 240 | optiboot_version = 256*(OPTIBOOT_MAJVER + OPTIBOOT_CUSTOMVER) + OPTIBOOT_MINVER; 241 | 242 | #include 243 | #include 244 | #include 245 | #include 246 | 247 | /* 248 | * Note that we use our own version of "boot.h" 249 | * uses sts instructions, but this version uses out instructions 250 | * This saves cycles and program memory. Sorry for the name overlap. 251 | */ 252 | #include "boot.h" 253 | #include "uart.h" 254 | #include "watchdog.h" 255 | 256 | // We don't use as those routines have interrupt overhead we don't need. 257 | 258 | /* 259 | * pin_defs.h 260 | * This contains most of the rather ugly defines that implement our 261 | * ability to use UART=n and LED=D3, and some avr family bit name differences. 262 | */ 263 | #include "pin_defs.h" 264 | 265 | /* 266 | * stk500.h contains the constant definitions for the stk500v1 comm protocol 267 | */ 268 | #include "optiboot_stk500.h" 269 | 270 | #ifndef LED_START_FLASHES 271 | #define LED_START_FLASHES 0 272 | #endif 273 | 274 | /* 275 | * We can never load flash with more than 1 page at a time, so we can save 276 | * some code space on parts with smaller pagesize by using a smaller int. 277 | */ 278 | #if SPM_PAGESIZE > 255 279 | typedef uint16_t pagelen_t ; 280 | #define GETLENGTH(len) len = getch()<<8; len |= getch() 281 | #else 282 | typedef uint8_t pagelen_t; 283 | #define GETLENGTH(len) (void) getch() /* skip high byte */; len = getch() 284 | #endif 285 | 286 | 287 | /* Function Prototypes 288 | * The main() function is in init9, which removes the interrupt vector table 289 | * we don't need. It is also 'OS_main', which means the compiler does not 290 | * generate any entry or exit code itself (but unlike 'naked', it doesn't 291 | * supress some compile-time options we want.) 292 | */ 293 | 294 | 295 | void __attribute__((noinline)) verifySpace(); 296 | void __attribute__((noinline)) watchdogConfig(uint8_t x); 297 | 298 | 299 | static inline void getNch(uint8_t); 300 | void getNch(uint8_t count) { 301 | do getch(); while (--count); 302 | verifySpace(); 303 | } 304 | 305 | #if LED_START_FLASHES > 0 306 | static inline void flash_led(uint8_t); 307 | #endif 308 | static inline void writebuffer(int8_t memtype, uint8_t *mybuff, 309 | uint16_t address, pagelen_t len); 310 | static inline void read_mem(uint8_t memtype, 311 | uint16_t address, pagelen_t len); 312 | 313 | 314 | /* 315 | * RAMSTART should be self-explanatory. It's bigger on parts with a 316 | * lot of peripheral registers. Let 0x100 be the default 317 | * Note that RAMSTART (for optiboot) need not be exactly at the start of RAM. 318 | */ 319 | #if !defined(RAMSTART) // newer versions of gcc avr-libc define RAMSTART 320 | #define RAMSTART 0x100 321 | #if defined (__AVR_ATmega644P__) 322 | // correct for a bug in avr-libc 323 | #undef SIGNATURE_2 324 | #define SIGNATURE_2 0x0A 325 | #elif defined(__AVR_ATmega1280__) 326 | #undef RAMSTART 327 | #define RAMSTART (0x200) 328 | #endif 329 | #endif 330 | 331 | /* C zero initialises all global variables. However, that requires */ 332 | /* These definitions are NOT zero initialised, but that doesn't matter */ 333 | /* This allows us to drop the zero init code, saving us memory */ 334 | #define buff ((uint8_t*)(RAMSTART)) 335 | 336 | /* Virtual boot partition support */ 337 | #ifdef VIRTUAL_BOOT_PARTITION 338 | #define rstVect0_sav (*(uint8_t*)(RAMSTART+SPM_PAGESIZE*2+4)) 339 | #define rstVect1_sav (*(uint8_t*)(RAMSTART+SPM_PAGESIZE*2+5)) 340 | #define saveVect0_sav (*(uint8_t*)(RAMSTART+SPM_PAGESIZE*2+6)) 341 | #define saveVect1_sav (*(uint8_t*)(RAMSTART+SPM_PAGESIZE*2+7)) 342 | // Vector to save original reset jump: 343 | // SPM Ready is least probably used, so it's default 344 | // if not, use old way WDT_vect_num, 345 | // or simply set custom save_vect_num in Makefile using vector name 346 | // or even raw number. 347 | #if !defined (save_vect_num) 348 | #if defined (SPM_RDY_vect_num) 349 | #define save_vect_num (SPM_RDY_vect_num) 350 | #elif defined (SPM_READY_vect_num) 351 | #define save_vect_num (SPM_READY_vect_num) 352 | #elif defined (WDT_vect_num) 353 | #define save_vect_num (WDT_vect_num) 354 | #else 355 | #error Cant find SPM or WDT interrupt vector for this CPU 356 | #endif 357 | #endif //save_vect_num 358 | // check if it's on the same page (code assumes that) 359 | #if (SPM_PAGESIZE <= save_vect_num) 360 | #error Save vector not in the same page as reset! 361 | #endif 362 | #if FLASHEND > 8192 363 | // AVRs with more than 8k of flash have 4-byte vectors, and use jmp. 364 | // We save only 16 bits of address, so devices with more than 128KB 365 | // may behave wrong for upper part of address space. 366 | #define rstVect0 2 367 | #define rstVect1 3 368 | #define saveVect0 (save_vect_num*4+2) 369 | #define saveVect1 (save_vect_num*4+3) 370 | #define appstart_vec (save_vect_num*2) 371 | #else 372 | // AVRs with up to 8k of flash have 2-byte vectors, and use rjmp. 373 | #define rstVect0 0 374 | #define rstVect1 1 375 | #define saveVect0 (save_vect_num*2) 376 | #define saveVect1 (save_vect_num*2+1) 377 | #define appstart_vec (save_vect_num) 378 | #endif 379 | #else 380 | #define appstart_vec (0) 381 | #endif // VIRTUAL_BOOT_PARTITION 382 | 383 | 384 | /* main program starts here */ 385 | int optiboot(void) { 386 | uint8_t ch; 387 | 388 | /* 389 | * Making these local and in registers prevents the need for initializing 390 | * them, and also saves space because code no longer stores to memory. 391 | * (initializing address keeps the compiler happy, but isn't really 392 | * necessary, and uses 4 bytes of flash.) 393 | */ 394 | register uint16_t address = 0; 395 | register pagelen_t length; 396 | 397 | /* Forever loop: exits by causing WDT reset */ 398 | for (;;) { 399 | /* get character from UART */ 400 | ch = getch(); 401 | 402 | if(ch == STK_GET_PARAMETER) { 403 | unsigned char which = getch(); 404 | verifySpace(); 405 | /* 406 | * Send optiboot version as "SW version" 407 | * Note that the references to memory are optimized away. 408 | */ 409 | if (which == STK_SW_MINOR) { 410 | putch(optiboot_version & 0xFF); 411 | } else if (which == STK_SW_MAJOR) { 412 | putch(optiboot_version >> 8); 413 | } else { 414 | /* 415 | * GET PARAMETER returns a generic 0x03 reply for 416 | * other parameters - enough to keep Avrdude happy 417 | */ 418 | putch(0x03); 419 | } 420 | } 421 | else if(ch == STK_SET_DEVICE) { 422 | // SET DEVICE is ignored 423 | getNch(20); 424 | } 425 | else if(ch == STK_SET_DEVICE_EXT) { 426 | // SET DEVICE EXT is ignored 427 | getNch(5); 428 | } 429 | else if(ch == STK_LOAD_ADDRESS) { 430 | // LOAD ADDRESS 431 | uint16_t newAddress; 432 | newAddress = getch(); 433 | newAddress = (newAddress & 0xff) | (getch() << 8); 434 | #ifdef RAMPZ 435 | // Transfer top bit to RAMPZ 436 | RAMPZ = (newAddress & 0x8000) ? 1 : 0; 437 | #endif 438 | newAddress += newAddress; // Convert from word address to byte address 439 | address = newAddress; 440 | verifySpace(); 441 | } 442 | else if(ch == STK_UNIVERSAL) { 443 | // UNIVERSAL command is ignored 444 | getNch(4); 445 | putch(0x00); 446 | } 447 | /* Write memory, length is big endian and is in bytes */ 448 | else if(ch == STK_PROG_PAGE) { 449 | // PROGRAM PAGE - we support flash programming only, not EEPROM 450 | uint8_t desttype; 451 | uint8_t *bufPtr; 452 | pagelen_t savelength; 453 | 454 | GETLENGTH(length); 455 | savelength = length; 456 | desttype = getch(); 457 | 458 | // read a page worth of contents 459 | bufPtr = buff; 460 | do *bufPtr++ = getch(); 461 | while (--length); 462 | 463 | // Read command terminator, start reply 464 | verifySpace(); 465 | 466 | #ifdef VIRTUAL_BOOT_PARTITION 467 | #if FLASHEND > 8192 468 | /* 469 | * AVR with 4-byte ISR Vectors and "jmp" 470 | * WARNING: this works only up to 128KB flash! 471 | */ 472 | if (address == 0) { 473 | // This is the reset vector page. We need to live-patch the 474 | // code so the bootloader runs first. 475 | // 476 | // Save jmp targets (for "Verify") 477 | rstVect0_sav = buff[rstVect0]; 478 | rstVect1_sav = buff[rstVect1]; 479 | saveVect0_sav = buff[saveVect0]; 480 | saveVect1_sav = buff[saveVect1]; 481 | 482 | // Move RESET jmp target to 'save' vector 483 | buff[saveVect0] = rstVect0_sav; 484 | buff[saveVect1] = rstVect1_sav; 485 | 486 | // Add jump to bootloader at RESET vector 487 | // WARNING: this works as long as 'main' is in first section 488 | buff[rstVect0] = ((uint16_t)main) & 0xFF; 489 | buff[rstVect1] = ((uint16_t)main) >> 8; 490 | } 491 | 492 | #else 493 | /* 494 | * AVR with 2-byte ISR Vectors and rjmp 495 | */ 496 | if ((uint16_t)(void*)address == rstVect0) { 497 | // This is the reset vector page. We need to live-patch 498 | // the code so the bootloader runs first. 499 | // 500 | // Move RESET vector to 'save' vector 501 | // Save jmp targets (for "Verify") 502 | rstVect0_sav = buff[rstVect0]; 503 | rstVect1_sav = buff[rstVect1]; 504 | saveVect0_sav = buff[saveVect0]; 505 | saveVect1_sav = buff[saveVect1]; 506 | 507 | // Instruction is a relative jump (rjmp), so recalculate. 508 | uint16_t vect=(rstVect0_sav & 0xff) | ((rstVect1_sav & 0x0f)<<8); //calculate 12b displacement 509 | vect = (vect-save_vect_num) & 0x0fff; //substract 'save' interrupt position and wrap around 4096 510 | // Move RESET jmp target to 'save' vector 511 | buff[saveVect0] = vect & 0xff; 512 | buff[saveVect1] = (vect >> 8) | 0xc0; // 513 | // Add rjump to bootloader at RESET vector 514 | vect = ((uint16_t)main) &0x0fff; //WARNIG: this works as long as 'main' is in first section 515 | buff[0] = vect & 0xFF; // rjmp 0x1c00 instruction 516 | buff[1] = (vect >> 8) | 0xC0; 517 | } 518 | #endif // FLASHEND 519 | #endif // VBP 520 | 521 | writebuffer(desttype, buff, address, savelength); 522 | 523 | 524 | } 525 | /* Read memory block mode, length is big endian. */ 526 | else if(ch == STK_READ_PAGE) { 527 | uint8_t desttype; 528 | GETLENGTH(length); 529 | 530 | desttype = getch(); 531 | 532 | verifySpace(); 533 | 534 | read_mem(desttype, address, length); 535 | } 536 | 537 | /* Get device signature bytes */ 538 | else if(ch == STK_READ_SIGN) { 539 | // READ SIGN - return what Avrdude wants to hear 540 | verifySpace(); 541 | putch(SIGNATURE_0); 542 | putch(SIGNATURE_1); 543 | putch(SIGNATURE_2); 544 | } 545 | else if (ch == STK_LEAVE_PROGMODE) { /* 'Q' */ 546 | // Adaboot no-wait mod 547 | watchdogConfig(WATCHDOG_16MS); 548 | verifySpace(); 549 | } 550 | else { 551 | // This covers the response to commands like STK_ENTER_PROGMODE 552 | verifySpace(); 553 | } 554 | putch(STK_OK); 555 | } 556 | } 557 | 558 | void verifySpace() { 559 | if (getch() != CRC_EOP) { 560 | // watchdogConfig(WATCHDOG_16MS); // shorten WD timeout 561 | // while (1) // and busy-loop so that WD causes 562 | // ; // a reset and app start. 563 | } 564 | putch(STK_INSYNC); 565 | } 566 | 567 | #if LED_START_FLASHES > 0 568 | void flash_led(uint8_t count) { 569 | do { 570 | TCNT1 = -(F_CPU/(1024*16)); 571 | TIFR1 = _BV(TOV1); 572 | while(!(TIFR1 & _BV(TOV1))); 573 | #if defined(__AVR_ATmega8__) || defined (__AVR_ATmega32__) || defined (__AVR_ATmega16__) 574 | LED_PORT ^= _BV(LED); 575 | #else 576 | LED_PIN |= _BV(LED); 577 | #endif 578 | watchdogReset(); 579 | } while (--count); 580 | } 581 | #endif 582 | 583 | /* 584 | * void writebuffer(memtype, buffer, address, length) 585 | */ 586 | static inline void writebuffer(int8_t memtype, uint8_t *mybuff, 587 | uint16_t address, pagelen_t len) 588 | { 589 | switch (memtype) { 590 | case 'E': // EEPROM 591 | #if defined(SUPPORT_EEPROM) || defined(BIGBOOT) 592 | while(len--) { 593 | eeprom_write_byte((uint8_t *)(address++), *mybuff++); 594 | } 595 | #else 596 | /* 597 | * On systems where EEPROM write is not supported, just busy-loop 598 | * until the WDT expires, which will eventually cause an error on 599 | * host system (which is what it should do.) 600 | */ 601 | while (1) 602 | ; // Error: wait for WDT 603 | #endif 604 | break; 605 | default: // FLASH 606 | /* 607 | * Default to writing to Flash program memory. By making this 608 | * the default rather than checking for the correct code, we save 609 | * space on chips that don't support any other memory types. 610 | */ 611 | { 612 | // Copy buffer into programming buffer 613 | uint8_t *bufPtr = mybuff; 614 | uint16_t addrPtr = (uint16_t)(void*)address; 615 | 616 | /* 617 | * Start the page erase and wait for it to finish. There 618 | * used to be code to do this while receiving the data over 619 | * the serial link, but the performance improvement was slight, 620 | * and we needed the space back. 621 | */ 622 | __boot_page_erase_short((uint16_t)(void*)address); 623 | boot_spm_busy_wait(); 624 | 625 | /* 626 | * Copy data from the buffer into the flash write buffer. 627 | */ 628 | do { 629 | uint16_t a; 630 | a = *bufPtr++; 631 | a |= (*bufPtr++) << 8; 632 | __boot_page_fill_short((uint16_t)(void*)addrPtr,a); 633 | addrPtr += 2; 634 | } while (len -= 2); 635 | 636 | /* 637 | * Actually Write the buffer to flash (and wait for it to finish.) 638 | */ 639 | __boot_page_write_short((uint16_t)(void*)address); 640 | boot_spm_busy_wait(); 641 | #if defined(RWWSRE) 642 | // Reenable read access to flash 643 | boot_rww_enable(); 644 | #endif 645 | } // default block 646 | break; 647 | } // switch 648 | } 649 | 650 | static inline void read_mem(uint8_t memtype, uint16_t address, pagelen_t length) 651 | { 652 | uint8_t ch; 653 | 654 | switch (memtype) { 655 | 656 | #if defined(SUPPORT_EEPROM) || defined(BIGBOOT) 657 | case 'E': // EEPROM 658 | do { 659 | putch(eeprom_read_byte((uint8_t *)(address++))); 660 | } while (--length); 661 | break; 662 | #endif 663 | default: 664 | do { 665 | #ifdef VIRTUAL_BOOT_PARTITION 666 | // Undo vector patch in bottom page so verify passes 667 | if (address == rstVect0) ch = rstVect0_sav; 668 | else if (address == rstVect1) ch = rstVect1_sav; 669 | else if (address == saveVect0) ch = saveVect0_sav; 670 | else if (address == saveVect1) ch = saveVect1_sav; 671 | else ch = pgm_read_byte_near(address); 672 | address++; 673 | #elif defined(RAMPZ) 674 | // Since RAMPZ should already be set, we need to use EPLM directly. 675 | // Also, we can use the autoincrement version of lpm to update "address" 676 | // do putch(pgm_read_byte_near(address++)); 677 | // while (--length); 678 | // read a Flash and increment the address (may increment RAMPZ) 679 | __asm__ ("elpm %0,Z+\n" : "=r" (ch), "=z" (address): "1" (address)); 680 | #else 681 | // read a Flash byte and increment the address 682 | __asm__ ("lpm %0,Z+\n" : "=r" (ch), "=z" (address): "1" (address)); 683 | #endif 684 | putch(ch); 685 | } while (--length); 686 | break; 687 | } // switch 688 | } 689 | -------------------------------------------------------------------------------- /boot.h: -------------------------------------------------------------------------------- 1 | /* Modified to use out for SPM access 2 | ** Peter Knight, Optiboot project http://optiboot.googlecode.com 3 | ** 4 | ** Todo: Tidy up 5 | ** 6 | ** "_short" routines execute 1 cycle faster and use 1 less word of flash 7 | ** by using "out" instruction instead of "sts". 8 | ** 9 | ** Additional elpm variants that trust the value of RAMPZ 10 | */ 11 | 12 | /* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007 Eric B. Weddington 13 | All rights reserved. 14 | 15 | Redistribution and use in source and binary forms, with or without 16 | modification, are permitted provided that the following conditions are met: 17 | 18 | * Redistributions of source code must retain the above copyright 19 | notice, this list of conditions and the following disclaimer. 20 | * Redistributions in binary form must reproduce the above copyright 21 | notice, this list of conditions and the following disclaimer in 22 | the documentation and/or other materials provided with the 23 | distribution. 24 | * Neither the name of the copyright holders nor the names of 25 | contributors may be used to endorse or promote products derived 26 | from this software without specific prior written permission. 27 | 28 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 29 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 32 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 33 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 34 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 35 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 36 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 37 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 38 | POSSIBILITY OF SUCH DAMAGE. */ 39 | 40 | /* $Id: boot.h,v 1.27.2.3 2008/09/30 13:58:48 arcanum Exp $ */ 41 | 42 | #ifndef _AVR_BOOT_H_ 43 | #define _AVR_BOOT_H_ 1 44 | 45 | /** \file */ 46 | /** \defgroup avr_boot : Bootloader Support Utilities 47 | \code 48 | #include 49 | #include 50 | \endcode 51 | 52 | The macros in this module provide a C language interface to the 53 | bootloader support functionality of certain AVR processors. These 54 | macros are designed to work with all sizes of flash memory. 55 | 56 | Global interrupts are not automatically disabled for these macros. It 57 | is left up to the programmer to do this. See the code example below. 58 | Also see the processor datasheet for caveats on having global interrupts 59 | enabled during writing of the Flash. 60 | 61 | \note Not all AVR processors provide bootloader support. See your 62 | processor datasheet to see if it provides bootloader support. 63 | 64 | \todo From email with Marek: On smaller devices (all except ATmega64/128), 65 | __SPM_REG is in the I/O space, accessible with the shorter "in" and "out" 66 | instructions - since the boot loader has a limited size, this could be an 67 | important optimization. 68 | 69 | \par API Usage Example 70 | The following code shows typical usage of the boot API. 71 | 72 | \code 73 | #include 74 | #include 75 | #include 76 | 77 | void boot_program_page (uint32_t page, uint8_t *buf) 78 | { 79 | uint16_t i; 80 | uint8_t sreg; 81 | 82 | // Disable interrupts. 83 | 84 | sreg = SREG; 85 | cli(); 86 | 87 | eeprom_busy_wait (); 88 | 89 | boot_page_erase (page); 90 | boot_spm_busy_wait (); // Wait until the memory is erased. 91 | 92 | for (i=0; i 116 | #include 117 | #include 118 | #include 119 | 120 | /* Check for SPM Control Register in processor. */ 121 | #if defined (SPMCSR) 122 | # define __SPM_REG SPMCSR 123 | #elif defined (SPMCR) 124 | # define __SPM_REG SPMCR 125 | #else 126 | # error AVR processor does not provide bootloader support! 127 | #endif 128 | 129 | 130 | /* Check for SPM Enable bit. */ 131 | #if defined(SPMEN) 132 | # define __SPM_ENABLE SPMEN 133 | #elif defined(SELFPRGEN) 134 | # define __SPM_ENABLE SELFPRGEN 135 | #else 136 | # error Cannot find SPM Enable bit definition! 137 | #endif 138 | 139 | /** \ingroup avr_boot 140 | \def BOOTLOADER_SECTION 141 | 142 | Used to declare a function or variable to be placed into a 143 | new section called .bootloader. This section and its contents 144 | can then be relocated to any address (such as the bootloader 145 | NRWW area) at link-time. */ 146 | 147 | #define BOOTLOADER_SECTION __attribute__ ((section (".bootloader"))) 148 | 149 | /* Create common bit definitions. */ 150 | #ifdef ASB 151 | #define __COMMON_ASB ASB 152 | #else 153 | #define __COMMON_ASB RWWSB 154 | #endif 155 | 156 | #ifdef ASRE 157 | #define __COMMON_ASRE ASRE 158 | #else 159 | #define __COMMON_ASRE RWWSRE 160 | #endif 161 | 162 | /* Define the bit positions of the Boot Lock Bits. */ 163 | 164 | #define BLB12 5 165 | #define BLB11 4 166 | #define BLB02 3 167 | #define BLB01 2 168 | 169 | /** \ingroup avr_boot 170 | \def boot_spm_interrupt_enable() 171 | Enable the SPM interrupt. */ 172 | 173 | #define boot_spm_interrupt_enable() (__SPM_REG |= (uint8_t)_BV(SPMIE)) 174 | 175 | /** \ingroup avr_boot 176 | \def boot_spm_interrupt_disable() 177 | Disable the SPM interrupt. */ 178 | 179 | #define boot_spm_interrupt_disable() (__SPM_REG &= (uint8_t)~_BV(SPMIE)) 180 | 181 | /** \ingroup avr_boot 182 | \def boot_is_spm_interrupt() 183 | Check if the SPM interrupt is enabled. */ 184 | 185 | #define boot_is_spm_interrupt() (__SPM_REG & (uint8_t)_BV(SPMIE)) 186 | 187 | /** \ingroup avr_boot 188 | \def boot_rww_busy() 189 | Check if the RWW section is busy. */ 190 | 191 | #define boot_rww_busy() (__SPM_REG & (uint8_t)_BV(__COMMON_ASB)) 192 | 193 | /** \ingroup avr_boot 194 | \def boot_spm_busy() 195 | Check if the SPM instruction is busy. */ 196 | 197 | #define boot_spm_busy() (__SPM_REG & (uint8_t)_BV(__SPM_ENABLE)) 198 | 199 | /** \ingroup avr_boot 200 | \def boot_spm_busy_wait() 201 | Wait while the SPM instruction is busy. */ 202 | 203 | #define boot_spm_busy_wait() do{}while(boot_spm_busy()) 204 | 205 | #define __BOOT_PAGE_ERASE (_BV(__SPM_ENABLE) | _BV(PGERS)) 206 | #define __BOOT_PAGE_WRITE (_BV(__SPM_ENABLE) | _BV(PGWRT)) 207 | #define __BOOT_PAGE_FILL _BV(__SPM_ENABLE) 208 | #define __BOOT_RWW_ENABLE (_BV(__SPM_ENABLE) | _BV(__COMMON_ASRE)) 209 | #define __BOOT_LOCK_BITS_SET (_BV(__SPM_ENABLE) | _BV(BLBSET)) 210 | 211 | #define __boot_page_fill_short(address, data) \ 212 | (__extension__({ \ 213 | __asm__ __volatile__ \ 214 | ( \ 215 | "movw r0, %3\n\t" \ 216 | "out %0, %1\n\t" \ 217 | "spm\n\t" \ 218 | "clr r1\n\t" \ 219 | : \ 220 | : "i" (_SFR_IO_ADDR(__SPM_REG)), \ 221 | "r" ((uint8_t)__BOOT_PAGE_FILL), \ 222 | "z" ((uint16_t)address), \ 223 | "r" ((uint16_t)data) \ 224 | : "r0" \ 225 | ); \ 226 | })) 227 | 228 | #define __boot_page_fill_normal(address, data) \ 229 | (__extension__({ \ 230 | __asm__ __volatile__ \ 231 | ( \ 232 | "movw r0, %3\n\t" \ 233 | "sts %0, %1\n\t" \ 234 | "spm\n\t" \ 235 | "clr r1\n\t" \ 236 | : \ 237 | : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ 238 | "r" ((uint8_t)__BOOT_PAGE_FILL), \ 239 | "z" ((uint16_t)address), \ 240 | "r" ((uint16_t)data) \ 241 | : "r0" \ 242 | ); \ 243 | })) 244 | 245 | #define __boot_page_fill_alternate(address, data)\ 246 | (__extension__({ \ 247 | __asm__ __volatile__ \ 248 | ( \ 249 | "movw r0, %3\n\t" \ 250 | "sts %0, %1\n\t" \ 251 | "spm\n\t" \ 252 | ".word 0xffff\n\t" \ 253 | "nop\n\t" \ 254 | "clr r1\n\t" \ 255 | : \ 256 | : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ 257 | "r" ((uint8_t)__BOOT_PAGE_FILL), \ 258 | "z" ((uint16_t)address), \ 259 | "r" ((uint16_t)data) \ 260 | : "r0" \ 261 | ); \ 262 | })) 263 | 264 | #define __boot_page_fill_extended(address, data) \ 265 | (__extension__({ \ 266 | __asm__ __volatile__ \ 267 | ( \ 268 | "movw r0, %4\n\t" \ 269 | "movw r30, %A3\n\t" \ 270 | "sts %1, %C3\n\t" \ 271 | "sts %0, %2\n\t" \ 272 | "spm\n\t" \ 273 | "clr r1\n\t" \ 274 | : \ 275 | : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ 276 | "i" (_SFR_MEM_ADDR(RAMPZ)), \ 277 | "r" ((uint8_t)__BOOT_PAGE_FILL), \ 278 | "r" ((uint32_t)address), \ 279 | "r" ((uint16_t)data) \ 280 | : "r0", "r30", "r31" \ 281 | ); \ 282 | })) 283 | 284 | #define __boot_page_fill_extended_short(address, data) \ 285 | (__extension__({ \ 286 | __asm__ __volatile__ \ 287 | ( \ 288 | "movw r0, %4\n\t" \ 289 | "movw r30, %A3\n\t" \ 290 | "out %1, %C3\n\t" \ 291 | "out %0, %2\n\t" \ 292 | "spm\n\t" \ 293 | "clr r1\n\t" \ 294 | : \ 295 | : "i" (_SFR_IO_ADDR(__SPM_REG)), \ 296 | "i" (_SFR_IO_ADDR(RAMPZ)), \ 297 | "r" ((uint8_t)__BOOT_PAGE_FILL), \ 298 | "r" ((uint32_t)address), \ 299 | "r" ((uint16_t)data) \ 300 | : "r0", "r30", "r31" \ 301 | ); \ 302 | })) 303 | 304 | #define __boot_page_erase_short(address) \ 305 | (__extension__({ \ 306 | __asm__ __volatile__ \ 307 | ( \ 308 | "out %0, %1\n\t" \ 309 | "spm\n\t" \ 310 | : \ 311 | : "i" (_SFR_IO_ADDR(__SPM_REG)), \ 312 | "r" ((uint8_t)__BOOT_PAGE_ERASE), \ 313 | "z" ((uint16_t)address) \ 314 | ); \ 315 | })) 316 | 317 | 318 | #define __boot_page_erase_normal(address) \ 319 | (__extension__({ \ 320 | __asm__ __volatile__ \ 321 | ( \ 322 | "sts %0, %1\n\t" \ 323 | "spm\n\t" \ 324 | : \ 325 | : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ 326 | "r" ((uint8_t)__BOOT_PAGE_ERASE), \ 327 | "z" ((uint16_t)address) \ 328 | ); \ 329 | })) 330 | 331 | #define __boot_page_erase_alternate(address) \ 332 | (__extension__({ \ 333 | __asm__ __volatile__ \ 334 | ( \ 335 | "sts %0, %1\n\t" \ 336 | "spm\n\t" \ 337 | ".word 0xffff\n\t" \ 338 | "nop\n\t" \ 339 | : \ 340 | : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ 341 | "r" ((uint8_t)__BOOT_PAGE_ERASE), \ 342 | "z" ((uint16_t)address) \ 343 | ); \ 344 | })) 345 | 346 | #define __boot_page_erase_extended(address) \ 347 | (__extension__({ \ 348 | __asm__ __volatile__ \ 349 | ( \ 350 | "movw r30, %A3\n\t" \ 351 | "sts %1, %C3\n\t" \ 352 | "sts %0, %2\n\t" \ 353 | "spm\n\t" \ 354 | : \ 355 | : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ 356 | "i" (_SFR_MEM_ADDR(RAMPZ)), \ 357 | "r" ((uint8_t)__BOOT_PAGE_ERASE), \ 358 | "r" ((uint32_t)address) \ 359 | : "r30", "r31" \ 360 | ); \ 361 | })) 362 | #define __boot_page_erase_extended_short(address) \ 363 | (__extension__({ \ 364 | __asm__ __volatile__ \ 365 | ( \ 366 | "movw r30, %A3\n\t" \ 367 | "out %1, %C3\n\t" \ 368 | "out %0, %2\n\t" \ 369 | "spm\n\t" \ 370 | : \ 371 | : "i" (_SFR_IO_ADDR(__SPM_REG)), \ 372 | "i" (_SFR_IO_ADDR(RAMPZ)), \ 373 | "r" ((uint8_t)__BOOT_PAGE_ERASE), \ 374 | "r" ((uint32_t)address) \ 375 | : "r30", "r31" \ 376 | ); \ 377 | })) 378 | 379 | #define __boot_page_write_short(address) \ 380 | (__extension__({ \ 381 | __asm__ __volatile__ \ 382 | ( \ 383 | "out %0, %1\n\t" \ 384 | "spm\n\t" \ 385 | : \ 386 | : "i" (_SFR_IO_ADDR(__SPM_REG)), \ 387 | "r" ((uint8_t)__BOOT_PAGE_WRITE), \ 388 | "z" ((uint16_t)address) \ 389 | ); \ 390 | })) 391 | 392 | #define __boot_page_write_normal(address) \ 393 | (__extension__({ \ 394 | __asm__ __volatile__ \ 395 | ( \ 396 | "sts %0, %1\n\t" \ 397 | "spm\n\t" \ 398 | : \ 399 | : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ 400 | "r" ((uint8_t)__BOOT_PAGE_WRITE), \ 401 | "z" ((uint16_t)address) \ 402 | ); \ 403 | })) 404 | 405 | #define __boot_page_write_alternate(address) \ 406 | (__extension__({ \ 407 | __asm__ __volatile__ \ 408 | ( \ 409 | "sts %0, %1\n\t" \ 410 | "spm\n\t" \ 411 | ".word 0xffff\n\t" \ 412 | "nop\n\t" \ 413 | : \ 414 | : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ 415 | "r" ((uint8_t)__BOOT_PAGE_WRITE), \ 416 | "z" ((uint16_t)address) \ 417 | ); \ 418 | })) 419 | 420 | #define __boot_page_write_extended(address) \ 421 | (__extension__({ \ 422 | __asm__ __volatile__ \ 423 | ( \ 424 | "movw r30, %A3\n\t" \ 425 | "sts %1, %C3\n\t" \ 426 | "sts %0, %2\n\t" \ 427 | "spm\n\t" \ 428 | : \ 429 | : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ 430 | "i" (_SFR_MEM_ADDR(RAMPZ)), \ 431 | "r" ((uint8_t)__BOOT_PAGE_WRITE), \ 432 | "r" ((uint32_t)address) \ 433 | : "r30", "r31" \ 434 | ); \ 435 | })) 436 | #define __boot_page_write_extended_short(address) \ 437 | (__extension__({ \ 438 | __asm__ __volatile__ \ 439 | ( \ 440 | "movw r30, %A3\n\t" \ 441 | "out %1, %C3\n\t" \ 442 | "out %0, %2\n\t" \ 443 | "spm\n\t" \ 444 | : \ 445 | : "i" (_SFR_IO_ADDR(__SPM_REG)), \ 446 | "i" (_SFR_IO_ADDR(RAMPZ)), \ 447 | "r" ((uint8_t)__BOOT_PAGE_WRITE), \ 448 | "r" ((uint32_t)address) \ 449 | : "r30", "r31" \ 450 | ); \ 451 | })) 452 | 453 | #define __boot_rww_enable_short() \ 454 | (__extension__({ \ 455 | __asm__ __volatile__ \ 456 | ( \ 457 | "out %0, %1\n\t" \ 458 | "spm\n\t" \ 459 | : \ 460 | : "i" (_SFR_IO_ADDR(__SPM_REG)), \ 461 | "r" ((uint8_t)__BOOT_RWW_ENABLE) \ 462 | ); \ 463 | })) 464 | 465 | #define __boot_rww_enable() \ 466 | (__extension__({ \ 467 | __asm__ __volatile__ \ 468 | ( \ 469 | "sts %0, %1\n\t" \ 470 | "spm\n\t" \ 471 | : \ 472 | : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ 473 | "r" ((uint8_t)__BOOT_RWW_ENABLE) \ 474 | ); \ 475 | })) 476 | 477 | #define __boot_rww_enable_alternate() \ 478 | (__extension__({ \ 479 | __asm__ __volatile__ \ 480 | ( \ 481 | "sts %0, %1\n\t" \ 482 | "spm\n\t" \ 483 | ".word 0xffff\n\t" \ 484 | "nop\n\t" \ 485 | : \ 486 | : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ 487 | "r" ((uint8_t)__BOOT_RWW_ENABLE) \ 488 | ); \ 489 | })) 490 | 491 | /* From the mega16/mega128 data sheets (maybe others): 492 | 493 | Bits by SPM To set the Boot Loader Lock bits, write the desired data to 494 | R0, write "X0001001" to SPMCR and execute SPM within four clock cycles 495 | after writing SPMCR. The only accessible Lock bits are the Boot Lock bits 496 | that may prevent the Application and Boot Loader section from any 497 | software update by the MCU. 498 | 499 | If bits 5..2 in R0 are cleared (zero), the corresponding Boot Lock bit 500 | will be programmed if an SPM instruction is executed within four cycles 501 | after BLBSET and SPMEN (or SELFPRGEN) are set in SPMCR. The Z-pointer is 502 | don't care during this operation, but for future compatibility it is 503 | recommended to load the Z-pointer with $0001 (same as used for reading the 504 | Lock bits). For future compatibility It is also recommended to set bits 7, 505 | 6, 1, and 0 in R0 to 1 when writing the Lock bits. When programming the 506 | Lock bits the entire Flash can be read during the operation. */ 507 | 508 | #define __boot_lock_bits_set_short(lock_bits) \ 509 | (__extension__({ \ 510 | uint8_t value = (uint8_t)(~(lock_bits)); \ 511 | __asm__ __volatile__ \ 512 | ( \ 513 | "ldi r30, 1\n\t" \ 514 | "ldi r31, 0\n\t" \ 515 | "mov r0, %2\n\t" \ 516 | "out %0, %1\n\t" \ 517 | "spm\n\t" \ 518 | : \ 519 | : "i" (_SFR_IO_ADDR(__SPM_REG)), \ 520 | "r" ((uint8_t)__BOOT_LOCK_BITS_SET), \ 521 | "r" (value) \ 522 | : "r0", "r30", "r31" \ 523 | ); \ 524 | })) 525 | 526 | #define __boot_lock_bits_set(lock_bits) \ 527 | (__extension__({ \ 528 | uint8_t value = (uint8_t)(~(lock_bits)); \ 529 | __asm__ __volatile__ \ 530 | ( \ 531 | "ldi r30, 1\n\t" \ 532 | "ldi r31, 0\n\t" \ 533 | "mov r0, %2\n\t" \ 534 | "sts %0, %1\n\t" \ 535 | "spm\n\t" \ 536 | : \ 537 | : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ 538 | "r" ((uint8_t)__BOOT_LOCK_BITS_SET), \ 539 | "r" (value) \ 540 | : "r0", "r30", "r31" \ 541 | ); \ 542 | })) 543 | 544 | #define __boot_lock_bits_set_alternate(lock_bits) \ 545 | (__extension__({ \ 546 | uint8_t value = (uint8_t)(~(lock_bits)); \ 547 | __asm__ __volatile__ \ 548 | ( \ 549 | "ldi r30, 1\n\t" \ 550 | "ldi r31, 0\n\t" \ 551 | "mov r0, %2\n\t" \ 552 | "sts %0, %1\n\t" \ 553 | "spm\n\t" \ 554 | ".word 0xffff\n\t" \ 555 | "nop\n\t" \ 556 | : \ 557 | : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ 558 | "r" ((uint8_t)__BOOT_LOCK_BITS_SET), \ 559 | "r" (value) \ 560 | : "r0", "r30", "r31" \ 561 | ); \ 562 | })) 563 | 564 | /* 565 | Reading lock and fuse bits: 566 | 567 | Similarly to writing the lock bits above, set BLBSET and SPMEN (or 568 | SELFPRGEN) bits in __SPMREG, and then (within four clock cycles) issue an 569 | LPM instruction. 570 | 571 | Z address: contents: 572 | 0x0000 low fuse bits 573 | 0x0001 lock bits 574 | 0x0002 extended fuse bits 575 | 0x0003 high fuse bits 576 | 577 | Sounds confusing, doesn't it? 578 | 579 | Unlike the macros in pgmspace.h, no need to care for non-enhanced 580 | cores here as these old cores do not provide SPM support anyway. 581 | */ 582 | 583 | /** \ingroup avr_boot 584 | \def GET_LOW_FUSE_BITS 585 | address to read the low fuse bits, using boot_lock_fuse_bits_get 586 | */ 587 | #define GET_LOW_FUSE_BITS (0x0000) 588 | /** \ingroup avr_boot 589 | \def GET_LOCK_BITS 590 | address to read the lock bits, using boot_lock_fuse_bits_get 591 | */ 592 | #define GET_LOCK_BITS (0x0001) 593 | /** \ingroup avr_boot 594 | \def GET_EXTENDED_FUSE_BITS 595 | address to read the extended fuse bits, using boot_lock_fuse_bits_get 596 | */ 597 | #define GET_EXTENDED_FUSE_BITS (0x0002) 598 | /** \ingroup avr_boot 599 | \def GET_HIGH_FUSE_BITS 600 | address to read the high fuse bits, using boot_lock_fuse_bits_get 601 | */ 602 | #define GET_HIGH_FUSE_BITS (0x0003) 603 | 604 | /** \ingroup avr_boot 605 | \def boot_lock_fuse_bits_get(address) 606 | 607 | Read the lock or fuse bits at \c address. 608 | 609 | Parameter \c address can be any of GET_LOW_FUSE_BITS, 610 | GET_LOCK_BITS, GET_EXTENDED_FUSE_BITS, or GET_HIGH_FUSE_BITS. 611 | 612 | \note The lock and fuse bits returned are the physical values, 613 | i.e. a bit returned as 0 means the corresponding fuse or lock bit 614 | is programmed. 615 | */ 616 | #define boot_lock_fuse_bits_get_short(address) \ 617 | (__extension__({ \ 618 | uint8_t __result; \ 619 | __asm__ __volatile__ \ 620 | ( \ 621 | "ldi r30, %3\n\t" \ 622 | "ldi r31, 0\n\t" \ 623 | "out %1, %2\n\t" \ 624 | "lpm %0, Z\n\t" \ 625 | : "=r" (__result) \ 626 | : "i" (_SFR_IO_ADDR(__SPM_REG)), \ 627 | "r" ((uint8_t)__BOOT_LOCK_BITS_SET), \ 628 | "M" (address) \ 629 | : "r0", "r30", "r31" \ 630 | ); \ 631 | __result; \ 632 | })) 633 | 634 | #define boot_lock_fuse_bits_get(address) \ 635 | (__extension__({ \ 636 | uint8_t __result; \ 637 | __asm__ __volatile__ \ 638 | ( \ 639 | "ldi r30, %3\n\t" \ 640 | "ldi r31, 0\n\t" \ 641 | "sts %1, %2\n\t" \ 642 | "lpm %0, Z\n\t" \ 643 | : "=r" (__result) \ 644 | : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ 645 | "r" ((uint8_t)__BOOT_LOCK_BITS_SET), \ 646 | "M" (address) \ 647 | : "r0", "r30", "r31" \ 648 | ); \ 649 | __result; \ 650 | })) 651 | 652 | /** \ingroup avr_boot 653 | \def boot_signature_byte_get(address) 654 | 655 | Read the Signature Row byte at \c address. For some MCU types, 656 | this function can also retrieve the factory-stored oscillator 657 | calibration bytes. 658 | 659 | Parameter \c address can be 0-0x1f as documented by the datasheet. 660 | \note The values are MCU type dependent. 661 | */ 662 | 663 | #define __BOOT_SIGROW_READ (_BV(__SPM_ENABLE) | _BV(SIGRD)) 664 | 665 | #define boot_signature_byte_get_short(addr) \ 666 | (__extension__({ \ 667 | uint16_t __addr16 = (uint16_t)(addr); \ 668 | uint8_t __result; \ 669 | __asm__ __volatile__ \ 670 | ( \ 671 | "out %1, %2\n\t" \ 672 | "lpm %0, Z" "\n\t" \ 673 | : "=r" (__result) \ 674 | : "i" (_SFR_IO_ADDR(__SPM_REG)), \ 675 | "r" ((uint8_t) __BOOT_SIGROW_READ), \ 676 | "z" (__addr16) \ 677 | ); \ 678 | __result; \ 679 | })) 680 | 681 | #define boot_signature_byte_get(addr) \ 682 | (__extension__({ \ 683 | uint16_t __addr16 = (uint16_t)(addr); \ 684 | uint8_t __result; \ 685 | __asm__ __volatile__ \ 686 | ( \ 687 | "sts %1, %2\n\t" \ 688 | "lpm %0, Z" "\n\t" \ 689 | : "=r" (__result) \ 690 | : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ 691 | "r" ((uint8_t) __BOOT_SIGROW_READ), \ 692 | "z" (__addr16) \ 693 | ); \ 694 | __result; \ 695 | })) 696 | 697 | /** \ingroup avr_boot 698 | \def boot_page_fill(address, data) 699 | 700 | Fill the bootloader temporary page buffer for flash 701 | address with data word. 702 | 703 | \note The address is a byte address. The data is a word. The AVR 704 | writes data to the buffer a word at a time, but addresses the buffer 705 | per byte! So, increment your address by 2 between calls, and send 2 706 | data bytes in a word format! The LSB of the data is written to the lower 707 | address; the MSB of the data is written to the higher address.*/ 708 | 709 | /** \ingroup avr_boot 710 | \def boot_page_erase(address) 711 | 712 | Erase the flash page that contains address. 713 | 714 | \note address is a byte address in flash, not a word address. */ 715 | 716 | /** \ingroup avr_boot 717 | \def boot_page_write(address) 718 | 719 | Write the bootloader temporary page buffer 720 | to flash page that contains address. 721 | 722 | \note address is a byte address in flash, not a word address. */ 723 | 724 | /** \ingroup avr_boot 725 | \def boot_rww_enable() 726 | 727 | Enable the Read-While-Write memory section. */ 728 | 729 | /** \ingroup avr_boot 730 | \def boot_lock_bits_set(lock_bits) 731 | 732 | Set the bootloader lock bits. 733 | 734 | \param lock_bits A mask of which Boot Loader Lock Bits to set. 735 | 736 | \note In this context, a 'set bit' will be written to a zero value. 737 | Note also that only BLBxx bits can be programmed by this command. 738 | 739 | For example, to disallow the SPM instruction from writing to the Boot 740 | Loader memory section of flash, you would use this macro as such: 741 | 742 | \code 743 | boot_lock_bits_set (_BV (BLB11)); 744 | \endcode 745 | 746 | \note Like any lock bits, the Boot Loader Lock Bits, once set, 747 | cannot be cleared again except by a chip erase which will in turn 748 | also erase the boot loader itself. */ 749 | 750 | /* Normal versions of the macros use 16-bit addresses. 751 | Extended versions of the macros use 32-bit addresses. 752 | Alternate versions of the macros use 16-bit addresses and require special 753 | instruction sequences after LPM. 754 | 755 | FLASHEND is defined in the ioXXXX.h file. 756 | USHRT_MAX is defined in . */ 757 | 758 | #if defined(__AVR_ATmega161__) || defined(__AVR_ATmega163__) \ 759 | || defined(__AVR_ATmega323__) 760 | 761 | /* Alternate: ATmega161/163/323 and 16 bit address */ 762 | #define boot_page_fill(address, data) __boot_page_fill_alternate(address, data) 763 | #define boot_page_erase(address) __boot_page_erase_alternate(address) 764 | #define boot_page_write(address) __boot_page_write_alternate(address) 765 | #define boot_rww_enable() __boot_rww_enable_alternate() 766 | #define boot_lock_bits_set(lock_bits) __boot_lock_bits_set_alternate(lock_bits) 767 | 768 | #elif (FLASHEND > USHRT_MAX) 769 | 770 | /* Extended: >16 bit address */ 771 | #define boot_page_fill(address, data) __boot_page_fill_extended_short(address, data) 772 | #define boot_page_erase(address) __boot_page_erase_extended_short(address) 773 | #define boot_page_write(address) __boot_page_write_extended_short(address) 774 | #define boot_rww_enable() __boot_rww_enable_short() 775 | #define boot_lock_bits_set(lock_bits) __boot_lock_bits_set_short(lock_bits) 776 | 777 | #else 778 | 779 | /* Normal: 16 bit address */ 780 | #define boot_page_fill(address, data) __boot_page_fill_short(address, data) 781 | #define boot_page_erase(address) __boot_page_erase_short(address) 782 | #define boot_page_write(address) __boot_page_write_short(address) 783 | #define boot_rww_enable() __boot_rww_enable_short() 784 | #define boot_lock_bits_set(lock_bits) __boot_lock_bits_set_short(lock_bits) 785 | 786 | #endif 787 | 788 | /** \ingroup avr_boot 789 | 790 | Same as boot_page_fill() except it waits for eeprom and spm operations to 791 | complete before filling the page. */ 792 | 793 | #define boot_page_fill_safe(address, data) \ 794 | do { \ 795 | boot_spm_busy_wait(); \ 796 | eeprom_busy_wait(); \ 797 | boot_page_fill(address, data); \ 798 | } while (0) 799 | 800 | /** \ingroup avr_boot 801 | 802 | Same as boot_page_erase() except it waits for eeprom and spm operations to 803 | complete before erasing the page. */ 804 | 805 | #define boot_page_erase_safe(address) \ 806 | do { \ 807 | boot_spm_busy_wait(); \ 808 | eeprom_busy_wait(); \ 809 | boot_page_erase (address); \ 810 | } while (0) 811 | 812 | /** \ingroup avr_boot 813 | 814 | Same as boot_page_write() except it waits for eeprom and spm operations to 815 | complete before writing the page. */ 816 | 817 | #define boot_page_write_safe(address) \ 818 | do { \ 819 | boot_spm_busy_wait(); \ 820 | eeprom_busy_wait(); \ 821 | boot_page_write (address); \ 822 | } while (0) 823 | 824 | /** \ingroup avr_boot 825 | 826 | Same as boot_rww_enable() except waits for eeprom and spm operations to 827 | complete before enabling the RWW mameory. */ 828 | 829 | #define boot_rww_enable_safe() \ 830 | do { \ 831 | boot_spm_busy_wait(); \ 832 | eeprom_busy_wait(); \ 833 | boot_rww_enable(); \ 834 | } while (0) 835 | 836 | /** \ingroup avr_boot 837 | 838 | Same as boot_lock_bits_set() except waits for eeprom and spm operations to 839 | complete before setting the lock bits. */ 840 | 841 | #define boot_lock_bits_set_safe(lock_bits) \ 842 | do { \ 843 | boot_spm_busy_wait(); \ 844 | eeprom_busy_wait(); \ 845 | boot_lock_bits_set (lock_bits); \ 846 | } while (0) 847 | 848 | #endif /* _AVR_BOOT_H_ */ 849 | --------------------------------------------------------------------------------