From a3d9874432d1c5fed98d10a0d33f788c1ecdc4cf Mon Sep 17 00:00:00 2001 From: Simon Kirby Date: Thu, 31 May 2012 02:49:02 -0700 Subject: [PATCH] Add stk500v2 boot loader with Turnigy USB Linker signalling on PWM input. Fuses must be set to 512-word boot loader (0xe00), and BOOTRST enabled. This works with the Turnigy USB linker...or mine, at least. Reading/writing of flash and EEPROM is supported with a avrdude; eg: avrdude -p m8 -b 9600 -c stk500v2 -D -P /dev/ttyUSB0 -u Chip erase (avrdude -e) does not actually chip erase, but just erases the EEPROM instead. Higher signalling rates should work over the wire, but the Turnigy USB linker only appears to support 9600 baud. We support flashing over the boot loader by running out of the way if we are erasing the page that would know how to flash it back after erasing. There are some nops for future expansion, but significantly changing the boot loader in-place is tricky. Hopefully this will work for most cases, and make updates much easier! --- tgy.asm | 712 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 703 insertions(+), 9 deletions(-) diff --git a/tgy.asm b/tgy.asm index e2eb68d..1bdfa3a 100644 --- a/tgy.asm +++ b/tgy.asm @@ -58,18 +58,23 @@ ; ;-- Fuses ----------------------------------------------------------------- ; -; Old fuses for internal RC oscillator at 8 MHz were lfuse=0xa4 hfuse=0xdf, -; but since we now set OSCCAL to actually run at about 16 MHz, we'd better -; set brown-out detection to 4.0V. This code should work without changes on -; boards with external 16MHz crystals / resonators; just set lfuse=0x3f. +; Old fuses for internal RC oscillator at 8MHz were lfuse=0xa4 hfuse=0xdf, +; but since we now set OSCCAL to 0xff (about 16MHz), running under 4.5V is +; officially out of spec. We'd better set the brown-out detection to 4.0V. +; The resulting code works with or without external 16MHz oscillators. +; Boards with external oscillators can use lfuse=0x3f. +; +; If the boot loader is enabled, the last nibble of the hfuse should be set +; to 'a' or '2' to also enable EESAVE - save EEPROM on chip erase. This is +; a 512-word boot flash section (0xe00), and enable BOOTRST to jump to it. +; Setting these fuses actually has no harm even without the boot loader, +; since 0xffff is nop, and it will just nop-sled around into normal code. ; ; Suggested fuses with 4.0V brown-out voltage: -; Without external crystal: avrdude -U lfuse:w:0x24:m -U hfuse:w:0xd7:m -; With external crystal: avrdude -U lfuse:w:0x3f:m -U hfuse:w:0xc7:m +; Without external oscillator: avrdude -U lfuse:w:0x24:m -U hfuse:w:0xda:m +; With external oscillator: avrdude -U lfuse:w:0x3f:m -U hfuse:w:0xca:m ; -; Testing fuses with 2.7V brown-out voltage (unsafe at 16MHz): -; Without external crystal: avrdude -U lfuse:w:0xa4:m -U hfuse:w:0xd7:m -; With external crystal: avrdude -U lfuse:w:0xbf:m -U hfuse:w:0xc7:m +; Don't set WDTON if using the boot loader. We will enable it on start. ; ;-- Board ----------------------------------------------------------------- ; @@ -109,6 +114,8 @@ .include "tgy.inc" ; TowerPro/Turnigy Basic/Plush "type 2" (INT0 PWM) .endif +.equ BOOT_LOADER = 1 ; Enable or disable boot loader + .equ I2C_ADDR = 0x50 ; MK-style I2C address .equ MOTOR_ID = 1 ; MK-style I2C motor ID, or UART motor number @@ -1952,4 +1959,691 @@ com1com6: ; An on, Cn off sei ret +.if BOOT_LOADER +;-----bko----------------------------------------------------------------- +; Simple boot loader on PWM input pin. +; +; We stay here as long as the input pin is pulled high, which is typical +; for the Turnigy USB Linker. The Turnigy USB Linker sports a SiLabs MCU +; (5V tolerant I/O) which converts 9600baud serial output from a SiLabs +; CP2102 USB-to-serial converter to a half duplex wire encoding which +; avoids signalling that can look like valid drive pulses. All bits are +; either one or two pulses, as opposed to a serial UART which could go +; high or low for a long time. This means it _should_ be safe to signal +; even to an armed ESC, as long as the low end has not been calibrated +; or set to start at pulses shorter than the linker timing. +; +; All transmissions have a leader of three 0xff bytes (last bit is 0). +; Bit encoding starts at the least significant bit and is 8 bits wide. +; 1-bits are encoded as 64.0us high, 72.8us low (135.8us total). +; 0-bits are encoded as 27.8us high, 34.5us low, 34.4us high, 37.9 low +; (134.6us total) +; End of encoding adds 34.0us high, then return to input mode. +; The last 0-bit low time is 32.6us instead of 37.9us, for some reason. +; +; We always learn the actual timing from the host's leader. It seems to +; be possible to respond a faster or slower, but faster will cause drops +; between the host and its serial-to-USB conversion at 9600baud. It does +; seem to work to use an average of high and low times as the actual bit +; timing, but since it doesn't quite fit in one byte at clk/8 at 16MHz, +; we store the high and low times separately, and copy the same timings. +; We should still work even at many times the bit rate. +; +; We support self-flashing ourselves (yo dawg), but doing so in a way +; that can still respond after each page update is a bit tricky. Some +; nops are present for future expansion without bumping addresses. +; +; We implement STK500v2, as recommended by the avrdude author, rather +; than implementing a random new protocol. STK500v2 protocol is the only +; serial protocol that passes the chip signature bytes directly instead +; of using a lookup table. However, avrdude uses CMD_SPI_MULTI to get +; these, which is for direct SPI access. We have to catch this and fake +; the response. We respond to CMD_SIGN_ON with "AVRISP_2", which keeps +; all messaes in the same format and with xor-checksums. We could say +; "AVRISP_MK2" and drop the message structure after sign-on, but then +; there is nothing to synchronize messages or do checksums. +; +; Note that to work with the Turnigy USB linker, the baud rate must be +; set to 9600. +; +; Registers: +; r0: Temporary, spm data (temp5) +; r1: Temporary, spm data (temp6) +; r2: Half-bit low time in timer2 ticks +; r3: Half-bit high time in timer2 ticks +; r4: Quarter-bit average time in timer2 ticks +; r5: stk500v2 message checksum (xor) +; r6: stk500v2 message length low +; r7: stk500v2 message length high +; r8: 7/8th bit time in timer2 ticks +; r9: Unused +; r10: Doubled (word) address l +; r11: Doubled (word) address h +; r12: Address l +; r13: Address h +; r14: Temporary (for checking TIFR, Z storage) (temp7) +; r15: Temporary (Z storage) +; r16: Zero +; r17: EEPROM read/write flags +; r18: Unused +; r19: Unused +; r20: Set for clearing TOV2/OCF2 flags +; r21: Timeout +; r22: Byte storage for bit shifting rx/tx (temp3) +; r23: Temporary (temp4) +; r24: Loop counter (temp1) +; r25: Loop counter (temp2) +; X: TX pointer +; Y: RX pointer +; Z: RX jump state pointer +; +; We keep the RX buffer just past start of RAM, +; and start building the response at the start of ram. +; The whole RAM area is used as the RX/TX buffer. +.equ RX_BUFFER = SRAM_START + 32 +.equ TX_BUFFER = SRAM_START + +; Number of RX timeouts / unsuccessful restarts before exiting boot loader +; If we get stray pulses or continuous high/low with no successful bytes +; received, we will exit the boot loader after this many tries. +.equ BOOT_RX_TRIES = 20 + +; STK message constants +.equ MESSAGE_START = 0x1b +.equ TOKEN = 0x0e + +; STK general command constants +.equ CMD_SIGN_ON = 0x01 +.equ CMD_SET_PARAMETER = 0x02 +.equ CMD_GET_PARAMETER = 0x03 +.equ CMD_SET_DEVICE_PARAMETERS = 0x04 +.equ CMD_OSCCAL = 0x05 +.equ CMD_LOAD_ADDRESS = 0x06 +.equ CMD_FIRMWARE_UPGRADE = 0x07 +.equ CMD_CHECK_TARGET_CONNECTION = 0x0d +.equ CMD_LOAD_RC_ID_TABLE = 0x0e +.equ CMD_LOAD_EC_ID_TABLE = 0x0f + +; STK ISP command constants +.equ CMD_ENTER_PROGMODE_ISP = 0x10 +.equ CMD_LEAVE_PROGMODE_ISP = 0x11 +.equ CMD_CHIP_ERASE_ISP = 0x12 +.equ CMD_PROGRAM_FLASH_ISP = 0x13 +.equ CMD_READ_FLASH_ISP = 0x14 +.equ CMD_PROGRAM_EEPROM_ISP = 0x15 +.equ CMD_READ_EEPROM_ISP = 0x16 +.equ CMD_PROGRAM_FUSE_ISP = 0x17 +.equ CMD_READ_FUSE_ISP = 0x18 +.equ CMD_PROGRAM_LOCK_ISP = 0x19 +.equ CMD_READ_LOCK_ISP = 0x1a +.equ CMD_READ_SIGNATURE_ISP = 0x1b +.equ CMD_READ_OSCCAL_ISP = 0x1c +.equ CMD_SPI_MULTI = 0x1d + +; STK status constants +.equ STATUS_CMD_OK = 0x00 +.equ STATUS_CMD_TOUT = 0x80 +.equ STATUS_RDY_BSY_TOUT = 0x81 +.equ STATUS_SET_PARAM_MISSING = 0x82 +.equ STATUS_CMD_FAILED = 0xc0 +.equ STATUS_CKSUM_ERROR = 0xc1 +.equ STATUS_CMD_UNKNOWN = 0xc9 +.equ STATUS_CMD_ILLEGAL_PARAMETER = 0xca + +; STK parameter constants +.equ PARAM_BUILD_NUMBER_LOW = 0x80 +.equ PARAM_BUILD_NUMBER_HIGH = 0x81 +.equ PARAM_HW_VER = 0x90 +.equ PARAM_SW_MAJOR = 0x91 +.equ PARAM_SW_MINOR = 0x92 +.equ PARAM_VTARGET = 0x94 +.equ PARAM_VADJUST = 0x95 ; STK500 only +.equ PARAM_OSC_PSCALE = 0x96 ; STK500 only +.equ PARAM_OSC_CMATCH = 0x97 ; STK500 only +.equ PARAM_SCK_DURATION = 0x98 ; STK500 only +.equ PARAM_TOPCARD_DETECT = 0x9a ; STK500 only +.equ PARAM_STATUS = 0x9c ; STK500 only +.equ PARAM_DATA = 0x9d ; STK500 only +.equ PARAM_RESET_POLARITY = 0x9e ; STK500 only, and STK600 FW version <= 2.0.3 +.equ PARAM_CONTROLLER_INIT = 0x9f + +; Support listening on ICP pin (on AfroESCs) +.if defined(USE_ICP) && USE_ICP +.equ RCP_PORT = PORTB +.equ RCP_DDR = DDRB +.else +.equ RCP_PORT = PORTD +.equ RCP_DDR = DDRD +.endif + +; THIRDBOOTSTART on the ATmega8 is 0xe00. +; Fuses shold have BOOTSZ1 set, BOOTSZ0 unset, BOOTRST set. +; Last nibble of hfuse should be A or 2 to save EEPROM on chip erase. +; Do not set WTDON. Implementing support for it here is big/difficult. +.equ BOOT_START = THIRDBOOTSTART +.org BOOT_START +boot_reset: ldi ZL, high(RAMEND) ; Set up stack + ldi ZH, low(RAMEND) + out SPH, ZH + out SPL, ZL + ldi r16, 0 ; Use r16 as zero + ldi ZL, low(stk_rx_start) + ldi ZH, high(stk_rx_start) + ldi YL, low(RX_BUFFER) + ldi YH, high(RX_BUFFER) + ldi XL, low(TX_BUFFER) + ldi XH, high(TX_BUFFER) + ldi r20, (1<