mirror of
https://github.com/esp8266/Arduino.git
synced 2025-06-16 11:21:18 +03:00
Updates for esp8266 platform
This commit is contained in:
252
hardware/arduino/esp8266/cores/esp8266/HardwareSerial.cpp
Normal file
252
hardware/arduino/esp8266/cores/esp8266/HardwareSerial.cpp
Normal file
@ -0,0 +1,252 @@
|
||||
/*
|
||||
HardwareSerial.cpp - Hardware serial library for Wiring
|
||||
Copyright (c) 2006 Nicholas Zambetti. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Modified 23 November 2006 by David A. Mellis
|
||||
Modified 28 September 2010 by Mark Sproul
|
||||
Modified 14 August 2012 by Alarus
|
||||
Modified 3 December 2013 by Matthijs Kooijman
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "HardwareSerial.h"
|
||||
#include "HardwareSerial_private.h"
|
||||
|
||||
// this next line disables the entire HardwareSerial.cpp,
|
||||
// this is so I can support Attiny series and any other chip without a uart
|
||||
#if defined(HAVE_HWSERIAL0) || defined(HAVE_HWSERIAL1) || defined(HAVE_HWSERIAL2) || defined(HAVE_HWSERIAL3)
|
||||
|
||||
// SerialEvent functions are weak, so when the user doesn't define them,
|
||||
// the linker just sets their address to 0 (which is checked below).
|
||||
// The Serialx_available is just a wrapper around Serialx.available(),
|
||||
// but we can refer to it weakly so we don't pull in the entire
|
||||
// HardwareSerial instance if the user doesn't also refer to it.
|
||||
#if defined(HAVE_HWSERIAL0)
|
||||
void serialEvent() __attribute__((weak));
|
||||
bool Serial0_available() __attribute__((weak));
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_HWSERIAL1)
|
||||
void serialEvent1() __attribute__((weak));
|
||||
bool Serial1_available() __attribute__((weak));
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_HWSERIAL2)
|
||||
void serialEvent2() __attribute__((weak));
|
||||
bool Serial2_available() __attribute__((weak));
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_HWSERIAL3)
|
||||
void serialEvent3() __attribute__((weak));
|
||||
bool Serial3_available() __attribute__((weak));
|
||||
#endif
|
||||
|
||||
void serialEventRun(void)
|
||||
{
|
||||
#if defined(HAVE_HWSERIAL0)
|
||||
if (Serial0_available && serialEvent && Serial0_available()) serialEvent();
|
||||
#endif
|
||||
#if defined(HAVE_HWSERIAL1)
|
||||
if (Serial1_available && serialEvent1 && Serial1_available()) serialEvent1();
|
||||
#endif
|
||||
#if defined(HAVE_HWSERIAL2)
|
||||
if (Serial2_available && serialEvent2 && Serial2_available()) serialEvent2();
|
||||
#endif
|
||||
#if defined(HAVE_HWSERIAL3)
|
||||
if (Serial3_available && serialEvent3 && Serial3_available()) serialEvent3();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Actual interrupt handlers //////////////////////////////////////////////////////////////
|
||||
|
||||
void HardwareSerial::_tx_udr_empty_irq(void)
|
||||
{
|
||||
// If interrupts are enabled, there must be more data in the output
|
||||
// buffer. Send the next byte
|
||||
unsigned char c = _tx_buffer[_tx_buffer_tail];
|
||||
_tx_buffer_tail = (_tx_buffer_tail + 1) % SERIAL_TX_BUFFER_SIZE;
|
||||
|
||||
*_udr = c;
|
||||
|
||||
// clear the TXC bit -- "can be cleared by writing a one to its bit
|
||||
// location". This makes sure flush() won't return until the bytes
|
||||
// actually got written
|
||||
sbi(*_ucsra, TXC0);
|
||||
|
||||
if (_tx_buffer_head == _tx_buffer_tail) {
|
||||
// Buffer empty, so disable interrupts
|
||||
cbi(*_ucsrb, UDRIE0);
|
||||
}
|
||||
}
|
||||
|
||||
// Public Methods //////////////////////////////////////////////////////////////
|
||||
|
||||
void HardwareSerial::begin(unsigned long baud, byte config)
|
||||
{
|
||||
// Try u2x mode first
|
||||
uint16_t baud_setting = (F_CPU / 4 / baud - 1) / 2;
|
||||
*_ucsra = 1 << U2X0;
|
||||
|
||||
// hardcoded exception for 57600 for compatibility with the bootloader
|
||||
// shipped with the Duemilanove and previous boards and the firmware
|
||||
// on the 8U2 on the Uno and Mega 2560. Also, The baud_setting cannot
|
||||
// be > 4095, so switch back to non-u2x mode if the baud rate is too
|
||||
// low.
|
||||
if (((F_CPU == 16000000UL) && (baud == 57600)) || (baud_setting >4095))
|
||||
{
|
||||
*_ucsra = 0;
|
||||
baud_setting = (F_CPU / 8 / baud - 1) / 2;
|
||||
}
|
||||
|
||||
// assign the baud_setting, a.k.a. ubrr (USART Baud Rate Register)
|
||||
*_ubrrh = baud_setting >> 8;
|
||||
*_ubrrl = baud_setting;
|
||||
|
||||
_written = false;
|
||||
|
||||
//set the data bits, parity, and stop bits
|
||||
#if defined(__AVR_ATmega8__)
|
||||
config |= 0x80; // select UCSRC register (shared with UBRRH)
|
||||
#endif
|
||||
*_ucsrc = config;
|
||||
|
||||
sbi(*_ucsrb, RXEN0);
|
||||
sbi(*_ucsrb, TXEN0);
|
||||
sbi(*_ucsrb, RXCIE0);
|
||||
cbi(*_ucsrb, UDRIE0);
|
||||
}
|
||||
|
||||
void HardwareSerial::end()
|
||||
{
|
||||
// wait for transmission of outgoing data
|
||||
while (_tx_buffer_head != _tx_buffer_tail)
|
||||
;
|
||||
|
||||
cbi(*_ucsrb, RXEN0);
|
||||
cbi(*_ucsrb, TXEN0);
|
||||
cbi(*_ucsrb, RXCIE0);
|
||||
cbi(*_ucsrb, UDRIE0);
|
||||
|
||||
// clear any received data
|
||||
_rx_buffer_head = _rx_buffer_tail;
|
||||
}
|
||||
|
||||
int HardwareSerial::available(void)
|
||||
{
|
||||
return ((unsigned int)(SERIAL_RX_BUFFER_SIZE + _rx_buffer_head - _rx_buffer_tail)) % SERIAL_RX_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
int HardwareSerial::peek(void)
|
||||
{
|
||||
if (_rx_buffer_head == _rx_buffer_tail) {
|
||||
return -1;
|
||||
} else {
|
||||
return _rx_buffer[_rx_buffer_tail];
|
||||
}
|
||||
}
|
||||
|
||||
int HardwareSerial::read(void)
|
||||
{
|
||||
// if the head isn't ahead of the tail, we don't have any characters
|
||||
if (_rx_buffer_head == _rx_buffer_tail) {
|
||||
return -1;
|
||||
} else {
|
||||
unsigned char c = _rx_buffer[_rx_buffer_tail];
|
||||
_rx_buffer_tail = (rx_buffer_index_t)(_rx_buffer_tail + 1) % SERIAL_RX_BUFFER_SIZE;
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
int HardwareSerial::availableForWrite(void)
|
||||
{
|
||||
#if (SERIAL_TX_BUFFER_SIZE>256)
|
||||
uint8_t oldSREG = SREG;
|
||||
cli();
|
||||
#endif
|
||||
tx_buffer_index_t head = _tx_buffer_head;
|
||||
tx_buffer_index_t tail = _tx_buffer_tail;
|
||||
#if (SERIAL_TX_BUFFER_SIZE>256)
|
||||
SREG = oldSREG;
|
||||
#endif
|
||||
if (head >= tail) return SERIAL_TX_BUFFER_SIZE - 1 - head + tail;
|
||||
return tail - head - 1;
|
||||
}
|
||||
|
||||
void HardwareSerial::flush()
|
||||
{
|
||||
// If we have never written a byte, no need to flush. This special
|
||||
// case is needed since there is no way to force the TXC (transmit
|
||||
// complete) bit to 1 during initialization
|
||||
if (!_written)
|
||||
return;
|
||||
|
||||
while (bit_is_set(*_ucsrb, UDRIE0) || bit_is_clear(*_ucsra, TXC0)) {
|
||||
if (bit_is_clear(SREG, SREG_I) && bit_is_set(*_ucsrb, UDRIE0))
|
||||
// Interrupts are globally disabled, but the DR empty
|
||||
// interrupt should be enabled, so poll the DR empty flag to
|
||||
// prevent deadlock
|
||||
if (bit_is_set(*_ucsra, UDRE0))
|
||||
_tx_udr_empty_irq();
|
||||
}
|
||||
// If we get here, nothing is queued anymore (DRIE is disabled) and
|
||||
// the hardware finished tranmission (TXC is set).
|
||||
}
|
||||
|
||||
size_t HardwareSerial::write(uint8_t c)
|
||||
{
|
||||
// If the buffer and the data register is empty, just write the byte
|
||||
// to the data register and be done. This shortcut helps
|
||||
// significantly improve the effective datarate at high (>
|
||||
// 500kbit/s) bitrates, where interrupt overhead becomes a slowdown.
|
||||
if (_tx_buffer_head == _tx_buffer_tail && bit_is_set(*_ucsra, UDRE0)) {
|
||||
*_udr = c;
|
||||
sbi(*_ucsra, TXC0);
|
||||
return 1;
|
||||
}
|
||||
tx_buffer_index_t i = (_tx_buffer_head + 1) % SERIAL_TX_BUFFER_SIZE;
|
||||
|
||||
// If the output buffer is full, there's nothing for it other than to
|
||||
// wait for the interrupt handler to empty it a bit
|
||||
while (i == _tx_buffer_tail) {
|
||||
if (bit_is_clear(SREG, SREG_I)) {
|
||||
// Interrupts are disabled, so we'll have to poll the data
|
||||
// register empty flag ourselves. If it is set, pretend an
|
||||
// interrupt has happened and call the handler to free up
|
||||
// space for us.
|
||||
if(bit_is_set(*_ucsra, UDRE0))
|
||||
_tx_udr_empty_irq();
|
||||
} else {
|
||||
// nop, the interrupt handler will free up space for us
|
||||
}
|
||||
}
|
||||
|
||||
_tx_buffer[_tx_buffer_head] = c;
|
||||
_tx_buffer_head = i;
|
||||
|
||||
sbi(*_ucsrb, UDRIE0);
|
||||
_written = true;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
#endif // whole file
|
@ -62,7 +62,7 @@ recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compil
|
||||
recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {compiler.c.elf.flags} {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" -Wl,--start-group {object_files} "{build.path}/{archive_file}" {compiler.c.elf.libs} -Wl,--end-group "-L{build.path}"
|
||||
|
||||
## Create eeprom
|
||||
recipe.objcopy.eep.pattern=echo EEP
|
||||
recipe.objcopy.eep.pattern=
|
||||
|
||||
## Create hex
|
||||
#recipe.objcopy.hex.pattern="{compiler.path}{compiler.elf2hex.cmd}" {compiler.elf2hex.flags} {compiler.elf2hex.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.hex"
|
||||
@ -76,32 +76,11 @@ recipe.size.regex=^(?:\.text|\.data|\.bootloader)\s+([0-9]+).*
|
||||
recipe.size.regex.data=^(?:\.data|\.bss|\.noinit)\s+([0-9]+).*
|
||||
recipe.size.regex.eeprom=^(?:\.eeprom)\s+([0-9]+).*
|
||||
|
||||
|
||||
# AVR Uploader/Programmers tools
|
||||
# ------------------------------
|
||||
|
||||
tools.avrdude.cmd.path={runtime.ide.path}/hardware/tools/avr/bin/avrdude
|
||||
tools.avrdude.config.path={runtime.ide.path}/hardware/tools/avr/etc/avrdude.conf
|
||||
tools.esptoolpy.cmd.path={runtime.ide.path}/hardware/tools/esptool.py
|
||||
|
||||
tools.avrdude.upload.params.verbose=-v
|
||||
tools.avrdude.upload.params.quiet=-q -q
|
||||
tools.avrdude.upload.pattern="{cmd.path}" "-C{config.path}" {upload.verbose} -p{build.mcu} -c{upload.protocol} -P{serial.port} -b{upload.speed} -D "-Uflash:w:{build.path}/{build.project_name}.hex:i"
|
||||
tools.esptoolpy.program.params.verbose=
|
||||
tools.esptoolpy.program.params.quiet=
|
||||
tools.esptoolpy.program.pattern="python {cmd.path}" --port={serial.port} write_flash 0x00000 "{build.path}/{build.project_name}._00000.bin"
|
||||
|
||||
tools.avrdude.program.params.verbose=-v
|
||||
tools.avrdude.program.params.quiet=-q -q
|
||||
tools.avrdude.program.pattern="{cmd.path}" "-C{config.path}" {program.verbose} -p{build.mcu} -c{protocol} {program.extra_params} "-Uflash:w:{build.path}/{build.project_name}.hex:i"
|
||||
|
||||
tools.avrdude.erase.params.verbose=-v
|
||||
tools.avrdude.erase.params.quiet=-q -q
|
||||
tools.avrdude.erase.pattern="{cmd.path}" "-C{config.path}" {erase.verbose} -p{build.mcu} -c{protocol} {program.extra_params} -e -Ulock:w:{bootloader.unlock_bits}:m -Uefuse:w:{bootloader.extended_fuses}:m -Uhfuse:w:{bootloader.high_fuses}:m -Ulfuse:w:{bootloader.low_fuses}:m
|
||||
|
||||
tools.avrdude.bootloader.params.verbose=-v
|
||||
tools.avrdude.bootloader.params.quiet=-q -q
|
||||
tools.avrdude.bootloader.pattern="{cmd.path}" "-C{config.path}" {bootloader.verbose} -p{build.mcu} -c{protocol} {program.extra_params} "-Uflash:w:{runtime.platform.path}/bootloaders/{bootloader.file}:i" -Ulock:w:{bootloader.lock_bits}:m
|
||||
|
||||
|
||||
# USB Default Flags
|
||||
# Default blank usb manufacturer will be filled it at compile time
|
||||
# - from numeric vendor ID, set to Unknown otherwise
|
||||
build.usb_manufacturer=
|
||||
build.usb_flags=-DUSB_VID={build.vid} -DUSB_PID={build.pid} '-DUSB_MANUFACTURER={build.usb_manufacturer}' '-DUSB_PRODUCT={build.usb_product}'
|
||||
|
@ -2,5 +2,5 @@ esptoolpy.name=esptool
|
||||
esptoolpy.communication=serial
|
||||
esptoolpy.protocol=esp
|
||||
esptoolpy.program.protocol=esp
|
||||
esptoolpy.program.tool=esptool.py
|
||||
esptoolpy.program.extra_params=--port {serial.port}
|
||||
esptoolpy.program.tool=esptoolpy
|
||||
esptoolpy.program.extra_params=
|
||||
|
@ -1,383 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# ESP8266 ROM Bootloader Utility
|
||||
# https://github.com/themadinventor/esptool
|
||||
#
|
||||
# Copyright (C) 2014 Fredrik Ahlberg
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
|
||||
# Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
import sys
|
||||
import struct
|
||||
import serial
|
||||
import math
|
||||
import time
|
||||
import argparse
|
||||
|
||||
class ESPROM:
|
||||
|
||||
# These are the currently known commands supported by the ROM
|
||||
ESP_FLASH_BEGIN = 0x02
|
||||
ESP_FLASH_DATA = 0x03
|
||||
ESP_FLASH_END = 0x04
|
||||
ESP_MEM_BEGIN = 0x05
|
||||
ESP_MEM_END = 0x06
|
||||
ESP_MEM_DATA = 0x07
|
||||
ESP_SYNC = 0x08
|
||||
ESP_WRITE_REG = 0x09
|
||||
ESP_READ_REG = 0x0a
|
||||
|
||||
# Maximum block sized for RAM and Flash writes, respectively.
|
||||
ESP_RAM_BLOCK = 0x1800
|
||||
ESP_FLASH_BLOCK = 0x400
|
||||
|
||||
# Default baudrate used by the ROM. Don't know if it is possible to change.
|
||||
ESP_ROM_BAUD = 57600
|
||||
|
||||
# First byte of the application image
|
||||
ESP_IMAGE_MAGIC = 0xe9
|
||||
|
||||
# Initial state for the checksum routine
|
||||
ESP_CHECKSUM_MAGIC = 0xef
|
||||
|
||||
def __init__(self, port = 0):
|
||||
self._port = serial.Serial(port, self.ESP_ROM_BAUD)
|
||||
|
||||
""" Read bytes from the serial port while performing SLIP unescaping """
|
||||
def read(self, length = 1):
|
||||
b = ''
|
||||
while len(b) < length:
|
||||
c = self._port.read(1)
|
||||
if c == '\xdb':
|
||||
c = self._port.read(1)
|
||||
if c == '\xdc':
|
||||
b = b + '\xc0'
|
||||
elif c == '\xdd':
|
||||
b = b + '\xdb'
|
||||
else:
|
||||
raise Exception('Invalid SLIP escape')
|
||||
else:
|
||||
b = b + c
|
||||
return b
|
||||
|
||||
""" Write bytes to the serial port while performing SLIP escaping """
|
||||
def write(self, packet):
|
||||
buf = '\xc0'
|
||||
for b in packet:
|
||||
if b == '\xc0':
|
||||
buf += '\xdb\xdc'
|
||||
elif b == '\xdb':
|
||||
buf += '\xdb\xdd'
|
||||
else:
|
||||
buf += b
|
||||
buf += '\xc0'
|
||||
self._port.write(buf)
|
||||
|
||||
""" Calculate checksum of a blob, as it is defined by the ROM """
|
||||
@staticmethod
|
||||
def checksum(data, state = ESP_CHECKSUM_MAGIC):
|
||||
for b in data:
|
||||
state ^= ord(b)
|
||||
return state
|
||||
|
||||
""" Send a request and read the response """
|
||||
def command(self, op = None, data = None, chk = 0):
|
||||
if op:
|
||||
# Construct and send request
|
||||
pkt = struct.pack('<BBHI', 0x00, op, len(data), chk) + data
|
||||
self.write(pkt)
|
||||
|
||||
# Read header of response and parse
|
||||
if self._port.read(1) != '\xc0':
|
||||
raise Exception('Invalid head of packet')
|
||||
hdr = self.read(8)
|
||||
(resp, op_ret, len_ret, val) = struct.unpack('<BBHI', hdr)
|
||||
if resp != 0x01 or (op and op_ret != op):
|
||||
raise Exception('Invalid response')
|
||||
|
||||
# The variable-length body
|
||||
body = self.read(len_ret)
|
||||
|
||||
# Terminating byte
|
||||
if self._port.read(1) != chr(0xc0):
|
||||
raise Exception('Invalid end of packet')
|
||||
|
||||
return val, body
|
||||
|
||||
""" Perform a connection test """
|
||||
def sync(self):
|
||||
self.command(ESPROM.ESP_SYNC, '\x07\x07\x12\x20'+32*'\x55')
|
||||
for i in xrange(7):
|
||||
self.command()
|
||||
|
||||
""" Try connecting repeatedly until successful, or giving up """
|
||||
def connect(self):
|
||||
print 'Connecting'
|
||||
self._port.timeout = 0.2
|
||||
for i in xrange(10):
|
||||
try:
|
||||
#self._port.setBreak(True)
|
||||
#time.sleep(0.1)
|
||||
#self._port.setDTR(False)
|
||||
#time.sleep(0.1)
|
||||
#self._port.setDTR(True)
|
||||
#time.sleep(0.7)
|
||||
#self._port.setBreak(False)
|
||||
#time.sleep(0.1)
|
||||
self._port.flushInput()
|
||||
self._port.flushOutput()
|
||||
self.sync()
|
||||
self._port.timeout = 1
|
||||
return
|
||||
except:
|
||||
time.sleep(0.1)
|
||||
raise Exception('Failed to connect')
|
||||
|
||||
""" Read memory address in target """
|
||||
def read_reg(self, addr):
|
||||
res = self.command(ESPROM.ESP_READ_REG, struct.pack('<I', addr))
|
||||
if res[1] != "\0\0":
|
||||
raise Exception('Failed to read target memory')
|
||||
return res[0]
|
||||
|
||||
""" Write to memory address in target """
|
||||
def write_reg(self, addr, value, mask, delay_us = 0):
|
||||
if self.command(ESPROM.ESP_WRITE_REG,
|
||||
struct.pack('<IIII', addr, value, mask, delay_us))[1] != "\0\0":
|
||||
raise Exception('Failed to write target memory')
|
||||
|
||||
""" Start downloading an application image to RAM """
|
||||
def mem_begin(self, size, blocks, blocksize, offset):
|
||||
if self.command(ESPROM.ESP_MEM_BEGIN,
|
||||
struct.pack('<IIII', size, blocks, blocksize, offset))[1] != "\0\0":
|
||||
raise Exception('Failed to enter RAM download mode')
|
||||
|
||||
""" Send a block of an image to RAM """
|
||||
def mem_block(self, data, seq):
|
||||
if self.command(ESPROM.ESP_MEM_DATA,
|
||||
struct.pack('<IIII', len(data), seq, 0, 0)+data, ESPROM.checksum(data))[1] != "\0\0":
|
||||
raise Exception('Failed to write to target RAM')
|
||||
|
||||
""" Leave download mode and run the application """
|
||||
def mem_finish(self, entrypoint = 0):
|
||||
if self.command(ESPROM.ESP_MEM_END,
|
||||
struct.pack('<II', int(entrypoint == 0), entrypoint))[1] != "\0\0":
|
||||
raise Exception('Failed to leave RAM download mode')
|
||||
|
||||
""" Start downloading to Flash (performs an erase) """
|
||||
def flash_begin(self, size, offset):
|
||||
old_tmo = self._port.timeout
|
||||
self._port.timeout = 10
|
||||
if self.command(ESPROM.ESP_FLASH_BEGIN,
|
||||
struct.pack('<IIII', size, 0x200, 0x400, offset))[1] != "\0\0":
|
||||
raise Exception('Failed to enter Flash download mode')
|
||||
self._port.timeout = old_tmo
|
||||
|
||||
""" Write block to flash """
|
||||
def flash_block(self, data, seq):
|
||||
if self.command(ESPROM.ESP_FLASH_DATA,
|
||||
struct.pack('<IIII', len(data), seq, 0, 0)+data, ESPROM.checksum(data))[1] != "\0\0":
|
||||
raise Exception('Failed to write to target Flash')
|
||||
|
||||
""" Leave flash mode and run/reboot """
|
||||
def flash_finish(self, reboot = False):
|
||||
if self.command(ESPROM.ESP_FLASH_END,
|
||||
struct.pack('<I', int(not reboot)))[1] != "\0\0":
|
||||
raise Exception('Failed to leave Flash mode')
|
||||
|
||||
|
||||
class ESPFirmwareImage:
|
||||
|
||||
def __init__(self, filename = None):
|
||||
self.segments = []
|
||||
self.entrypoint = 0
|
||||
|
||||
if filename is not None:
|
||||
f = file(filename, 'rb')
|
||||
(magic, segments, _, _, self.entrypoint) = struct.unpack('<BBBBI', f.read(8))
|
||||
|
||||
# some sanity check
|
||||
if magic != ESPROM.ESP_IMAGE_MAGIC or segments > 16:
|
||||
raise Exception('Invalid firmware image')
|
||||
|
||||
for i in xrange(segments):
|
||||
(offset, size) = struct.unpack('<II', f.read(8))
|
||||
if offset > 0x40200000 or offset < 0x3ffe0000 or size > 65536:
|
||||
raise Exception('Suspicious segment %x,%d' % (offset, size))
|
||||
self.segments.append((offset, size, f.read(size)))
|
||||
|
||||
# Skip the padding. The checksum is stored in the last byte so that the
|
||||
# file is a multiple of 16 bytes.
|
||||
align = 15-(f.tell() % 16)
|
||||
f.seek(align, 1)
|
||||
|
||||
self.checksum = ord(f.read(1))
|
||||
|
||||
def add_segment(self, addr, data):
|
||||
self.segments.append((addr, len(data), data))
|
||||
|
||||
def save(self, filename):
|
||||
f = file(filename, 'wb')
|
||||
f.write(struct.pack('<BBBBI', ESPROM.ESP_IMAGE_MAGIC, len(self.segments), 0, 0, self.entrypoint))
|
||||
|
||||
checksum = ESPROM.ESP_CHECKSUM_MAGIC
|
||||
for (offset, size, data) in self.segments:
|
||||
f.write(struct.pack('<II', offset, size))
|
||||
f.write(data)
|
||||
checksum = ESPROM.checksum(data, checksum)
|
||||
|
||||
align = 15-(f.tell() % 16)
|
||||
f.seek(align, 1)
|
||||
f.write(struct.pack('B', checksum))
|
||||
|
||||
def arg_auto_int(x):
|
||||
return int(x, 0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description = 'ESP8266 ROM Bootloader Utility', prog = 'esptool')
|
||||
|
||||
parser.add_argument(
|
||||
'--port', '-p',
|
||||
help = 'Serial port device',
|
||||
default = '/dev/ttyUSB0')
|
||||
|
||||
subparsers = parser.add_subparsers(
|
||||
dest = 'operation',
|
||||
help = 'Run esptool {command} -h for additional help')
|
||||
|
||||
parser_load_ram = subparsers.add_parser(
|
||||
'load_ram',
|
||||
help = 'Download an image to RAM and execute')
|
||||
parser_load_ram.add_argument('filename', help = 'Firmware image')
|
||||
|
||||
parser_dump_mem = subparsers.add_parser(
|
||||
'dump_mem',
|
||||
help = 'Dump arbitrary memory to disk')
|
||||
parser_dump_mem.add_argument('address', help = 'Base address', type = arg_auto_int)
|
||||
parser_dump_mem.add_argument('size', help = 'Size of region to dump', type = arg_auto_int)
|
||||
parser_dump_mem.add_argument('filename', help = 'Name of binary dump')
|
||||
|
||||
parser_read_mem = subparsers.add_parser(
|
||||
'read_mem',
|
||||
help = 'Read arbitrary memory location')
|
||||
parser_read_mem.add_argument('address', help = 'Address to read', type = arg_auto_int)
|
||||
|
||||
parser_write_mem = subparsers.add_parser(
|
||||
'write_mem',
|
||||
help = 'Read-modify-write to arbitrary memory location')
|
||||
parser_write_mem.add_argument('address', help = 'Address to write', type = arg_auto_int)
|
||||
parser_write_mem.add_argument('value', help = 'Value', type = arg_auto_int)
|
||||
parser_write_mem.add_argument('mask', help = 'Mask of bits to write', type = arg_auto_int)
|
||||
|
||||
parser_write_flash = subparsers.add_parser(
|
||||
'write_flash',
|
||||
help = 'Write a binary blob to flash')
|
||||
parser_write_flash.add_argument('address', help = 'Base address, 4KiB-aligned', type = arg_auto_int)
|
||||
parser_write_flash.add_argument('filename', help = 'Binary file to write')
|
||||
|
||||
parser_image_info = subparsers.add_parser(
|
||||
'image_info',
|
||||
help = 'Dump headers from an application image')
|
||||
parser_image_info.add_argument('filename', help = 'Image file to parse')
|
||||
|
||||
parser_make_image = subparsers.add_parser(
|
||||
'make_image',
|
||||
help = 'Create an application image from binary files')
|
||||
parser_make_image.add_argument('output', help = 'Output image file')
|
||||
parser_make_image.add_argument('--segfile', '-f', action = 'append', help = 'Segment input file')
|
||||
parser_make_image.add_argument('--segaddr', '-a', action = 'append', help = 'Segment base address', type = arg_auto_int)
|
||||
parser_make_image.add_argument('--entrypoint', '-e', help = 'Address of entry point', type = arg_auto_int, default = 0)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Create the ESPROM connection object, if needed
|
||||
esp = None
|
||||
if args.operation not in ('image_info','make_image'):
|
||||
esp = ESPROM(args.port)
|
||||
esp.connect()
|
||||
|
||||
# Do the actual work. Should probably be split into separate functions.
|
||||
if args.operation == 'load_ram':
|
||||
image = ESPFirmwareImage(args.filename)
|
||||
|
||||
print 'RAM boot...'
|
||||
for (offset, size, data) in image.segments:
|
||||
print 'Downloading %d bytes at %08x...' % (size, offset),
|
||||
sys.stdout.flush()
|
||||
esp.mem_begin(size, math.ceil(size / float(esp.ESP_RAM_BLOCK)), esp.ESP_RAM_BLOCK, offset)
|
||||
|
||||
seq = 0
|
||||
while len(data) > 0:
|
||||
esp.mem_block(data[0:esp.ESP_RAM_BLOCK], seq)
|
||||
data = data[esp.ESP_RAM_BLOCK:]
|
||||
seq += 1
|
||||
print 'done!'
|
||||
|
||||
print 'All segments done, executing at %08x' % image.entrypoint
|
||||
esp.mem_finish(image.entrypoint)
|
||||
|
||||
elif args.operation == 'read_mem':
|
||||
print '0x%08x = 0x%08x' % (args.address, esp.read_reg(args.address))
|
||||
|
||||
elif args.operation == 'write_mem':
|
||||
esp.write_reg(args.address, args.value, args.mask, 0)
|
||||
print 'Wrote %08x, mask %08x to %08x' % (args.value, args.mask, args.address)
|
||||
|
||||
elif args.operation == 'dump_mem':
|
||||
f = file(args.filename, 'wb')
|
||||
for i in xrange(args.size/4):
|
||||
d = esp.read_reg(args.address+(i*4))
|
||||
f.write(struct.pack('<I', d))
|
||||
if f.tell() % 1024 == 0:
|
||||
print '\r%d bytes read... (%d %%)' % (f.tell(), f.tell()*100/args.size),
|
||||
sys.stdout.flush()
|
||||
print 'Done!'
|
||||
|
||||
elif args.operation == 'write_flash':
|
||||
image = file(args.filename, 'rb').read()
|
||||
print 'Erasing flash...'
|
||||
esp.flash_begin(len(image), args.address)
|
||||
seq = 0
|
||||
blocks = math.ceil(len(image)/esp.ESP_FLASH_BLOCK)
|
||||
while len(image) > 0:
|
||||
print '\rWriting at 0x%08x... (%d %%)' % (args.address + seq*esp.ESP_FLASH_BLOCK, 100*seq/blocks),
|
||||
sys.stdout.flush()
|
||||
esp.flash_block(image[0:esp.ESP_FLASH_BLOCK], seq)
|
||||
image = image[esp.ESP_FLASH_BLOCK:]
|
||||
seq += 1
|
||||
print '\nLeaving...'
|
||||
esp.flash_finish(False)
|
||||
|
||||
elif args.operation == 'image_info':
|
||||
image = ESPFirmwareImage(args.filename)
|
||||
print ('Entry point: %08x' % image.entrypoint) if image.entrypoint != 0 else 'Entry point not set'
|
||||
print '%d segments' % len(image.segments)
|
||||
print
|
||||
checksum = ESPROM.ESP_CHECKSUM_MAGIC
|
||||
for (idx, (offset, size, data)) in enumerate(image.segments):
|
||||
print 'Segment %d: %5d bytes at %08x' % (idx+1, size, offset)
|
||||
checksum = ESPROM.checksum(data, checksum)
|
||||
print
|
||||
print 'Checksum: %02x (%s)' % (image.checksum, 'valid' if image.checksum == checksum else 'invalid!')
|
||||
|
||||
elif args.operation == 'make_image':
|
||||
image = ESPFirmwareImage()
|
||||
if len(args.segfile) == 0:
|
||||
raise Exception('No segments specified')
|
||||
if len(args.segfile) != len(args.segaddr):
|
||||
raise Exception('Number of specified files does not match number of specified addresses')
|
||||
for (seg, addr) in zip(args.segfile, args.segaddr):
|
||||
data = file(seg, 'rb').read()
|
||||
image.add_segment(addr, data)
|
||||
image.entrypoint = args.entrypoint
|
||||
image.save(args.output)
|
Reference in New Issue
Block a user