Skip to content
This repository has been archived by the owner on Jan 31, 2019. It is now read-only.

Commit

Permalink
Merge branch 'flasher-tinkering' and release 2.5.0
Browse files Browse the repository at this point in the history
  • Loading branch information
edjubuh committed Mar 31, 2017
2 parents 527040e + 5478afc commit 2a991c5
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 131 deletions.
4 changes: 3 additions & 1 deletion Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ stage('Build') {
}
stage('Dependencies') {
tool 'python3'
sh 'sudo apt-get install -y python3-pip'
if(isUnix()) {
sh 'sudo apt-get install -y python3-pip'
}
venv.create_virtualenv()
venv.run 'pip3 install wheel twine'
}
Expand Down
17 changes: 14 additions & 3 deletions proscli/flasher.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import prosflasher.upload
import prosconfig
from proscli.utils import default_cfg, AliasGroup
from proscli.utils import get_version


@click.group(cls=AliasGroup)
Expand All @@ -24,13 +25,16 @@ def flasher_cli():
help='Specifies a binary file, project directory, or project config file.')
@click.option('-p', '--port', default='auto', metavar='PORT', help='Specifies the serial port.')
@click.option('--no-poll', is_flag=True, default=False)
@click.option('-r', '--retry', default=2,
help='Specify the number of times the flasher should retry the flash when it detects a failure'
' (default two times).')
@default_cfg
# @click.option('-m', '--strategy', default='cortex', metavar='STRATEGY',
# help='Specify the microcontroller upload strategy. Not currently used.')
def flash(ctx, save_file_system, y, port, binary, no_poll):
def flash(ctx, save_file_system, y, port, binary, no_poll, retry):
"""Upload binaries to the microcontroller. A serial port and binary file need to be specified.
By default, the port is automatically selected (if you want to be pendantic, 'auto').
By default, the port is automatically selected (if you want to be pedantic, 'auto').
Otherwise, a system COM port descriptor needs to be used. In Windows/NT, this takes the form of COM1.
In *nx systems, this takes the form of /dev/tty1 or /dev/acm1 or similar.
\b
Expand All @@ -39,6 +43,7 @@ def flash(ctx, save_file_system, y, port, binary, no_poll):
By default, the CLI will look around for a proper binary to upload to the microcontroller. If one was not found, or
if you want to change the default binary, you can specify it.
"""
click.echo(' ====:: PROS Flasher v{} ::===='.format(get_version()))
if port == 'auto':
ports = prosflasher.ports.list_com_ports()
if len(ports) == 0:
Expand Down Expand Up @@ -87,7 +92,13 @@ def flash(ctx, save_file_system, y, port, binary, no_poll):

click.echo('Flashing ' + binary + ' to ' + ', '.join(port))
for p in port:
prosflasher.upload.upload(p, binary, no_poll, ctx)
tries = 1
while tries <= retry:
code = prosflasher.upload.upload(p, y, binary, no_poll, ctx)
if code or code == -1000:
break
click.echo('Retrying...')
tries += 1


def find_binary(path):
Expand Down
21 changes: 1 addition & 20 deletions proscli/main.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import click
#from pkg_resources import get_distribution
import proscli
from proscli.utils import default_options
import os.path
import sys

from proscli.utils import default_options, get_version

def main():
# the program name should always be pros. don't care if it's not...
Expand All @@ -13,22 +10,6 @@ def main():
except KeyboardInterrupt:
click.echo('Aborted!')


def get_version():
try:
if os.path.isfile(os.path.join(__file__, '../../version')):
return open(os.path.join(__file__, '../../version')).read().strip()
except Exception:
pass
try:
if getattr(sys, 'frozen', False):
import BUILD_CONSTANTS
return BUILD_CONSTANTS.CLI_VERSION
except Exception:
pass
return None # Let click figure it out


@click.command('pros',
cls=click.CommandCollection,
context_settings=dict(help_option_names=['-h', '--help']),
Expand Down
3 changes: 2 additions & 1 deletion proscli/terminal.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import click
import proscli.serial_terminal
import prosflasher.ports
import serial
import signal
import sys
import time
Expand Down Expand Up @@ -28,7 +29,7 @@ def terminal(port):
click.echo('No ports were found.')
click.get_current_context().abort()
sys.exit()
ser = prosflasher.ports.create_serial(port)
ser = prosflasher.ports.create_serial(port, serial.PARITY_NONE)
term = proscli.serial_terminal.Terminal(ser)
signal.signal(signal.SIGINT, term.stop)
term.start()
Expand Down
17 changes: 17 additions & 0 deletions proscli/utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
import os

import click
import sys

from proscli.state import State

pass_state = click.make_pass_decorator(State)

def get_version():
try:
if os.path.isfile(os.path.join(__file__, '../../version')):
return open(os.path.join(__file__, '../../version')).read().strip()
except Exception:
pass
try:
if getattr(sys, 'frozen', False):
import BUILD_CONSTANTS
return BUILD_CONSTANTS.CLI_VERSION
except Exception:
pass
return None # Let click figure it out

def verbosity_option(f):
"""
Expand Down
128 changes: 78 additions & 50 deletions prosflasher/bootloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@ def debug_response(command, response, fmt='STM BL RESPONSE TO 0x{}: {}'):

def send_bootloader_command(port, command, response_size=1):
port.write([command, 0xff - command])
port.flush()
time.sleep(0.01)
response = port.read(response_size)
debug_response(command, response)
return response


Expand All @@ -44,22 +43,32 @@ def compute_address_commandable(address):


def prepare_bootloader(port):
click.echo('Preparing bootloader...', nl=False)
prosflasher.upload.configure_port(port, serial.PARITY_EVEN)
port.flush()
port.write([0x7f])
response = port.read(1)
debug_response(0x07f, response)
if response is None or len(response) != 1 or response[0] != ACK:
click.echo('failed')
return False
click.echo('complete')
if click.get_current_context().obj.verbosity > 1:
click.echo('Extra commands:')
send_bootloader_command(port, 0x00, 15)
send_bootloader_command(port, 0x01, 5)
send_bootloader_command(port, 0x02, 5)
return True
click.echo('Preparing bootloader... ', nl=False)
# for _ in range(0, 3):
# response = send_bootloader_command(port, 0x00, 15)
# if response is not None and len(response) == 15 and response[0] == ACK and response[-1] == ACK:
# click.echo('complete')
# return True
time.sleep(0.01)
port.rts = 0
time.sleep(0.01)
for _ in range(0,3):
port.write([0x7f])
response = port.read(1)
debug_response(0x7f, response)
if not (response is None or len(response) != 1 or response[0] != ACK):
time.sleep(0.01)
response = send_bootloader_command(port, 0x00, 15)
debug_response(0x00, response)
if response is None or len(response) != 15 or response[0] != ACK or response[-1] != ACK:
click.echo('failed (couldn\'t verify commands)')
return False
# send_bootloader_command(port, 0x01, 5)
# send_bootloader_command(port, 0x02, 5)
click.echo('complete')
return True
click.echo('failed')
return False


def read_memory(port, start_address, size=0x100):
Expand Down Expand Up @@ -95,59 +104,73 @@ def read_memory(port, start_address, size=0x100):


def erase_flash(port):
click.echo('Erasing user flash...', nl=False)
prosflasher.upload.configure_port(port, serial.PARITY_EVEN)
click.echo('Erasing user flash... ', nl=False)
port.flush()
response = send_bootloader_command(port, 0x43, 1)
if response is None or response[0] != 0x79:
if response is None or len(response) < 1 or response[0] != 0x79:
click.echo('failed')
return False
time.sleep(0.01)
response = send_bootloader_command(port, 0xff, 1)
if response is None or response[0] != 0x79:
click.echo('failed')
debug_response(0xff, response)
if response is None or len(response) < 1 or response[0] != 0x79:
click.echo('failed (address unacceptable)')
return False
click.echo('complete')
return True


def write_flash(port, start_address, data):
def write_flash(port, start_address, data, retry=2):
data = bytearray(data)
if len(data) > 256:
click.echo('Tried writing too much data at once! ({} bytes)'.format(len(data)))
return False
port.read_all()
start_address = compute_address_commandable(start_address)
debug('Writing {} bytes to {}'.format(len(data), adr_to_str(start_address)))
c_addr = compute_address_commandable(start_address)
debug('Writing {} bytes to {}'.format(len(data), adr_to_str(c_addr)))
response = send_bootloader_command(port, 0x31)
if response is None or response[0] != ACK:
if response is None or len(response) < 1 or response[0] != ACK:
click.echo('failed (write command not accepted)')
return False
port.write(start_address)
port.write(c_addr)
port.flush()
response = port.read(1)
debug_response(adr_to_str(start_address), response)
if response is None or response[0] != ACK:
click.echo('failed (address not accepted)')
return False
debug_response(adr_to_str(c_addr), response)
if response is None or len(response) < 1 or response[0] != ACK:
if retry > 0:
debug('RETRYING PACKET')
write_flash(port, start_address, data, retry=retry - 1)
else:
click.echo('failed (address not accepted)')
return False
checksum = len(data) - 1
for x in data:
checksum ^= x
data.insert(0, len(data) - 1)
data.append(checksum)
port.write(data)
send_data = data
send_data.insert(0, len(send_data) - 1)
send_data.append(checksum)
port.write(send_data)
time.sleep(0.005)
response = port.read(1)
if response is None or response[0] != ACK:
port.write(data)
time.sleep(20)
response = port.read(1)
if response is None or response[0] != ACK:
debug('STM BL RESPONSE TO WRITE: {}'.format(response))
if response is None or len(response) < 1 or response[0] != ACK:
if retry > 0:
debug('RETRYING PACKET')
write_flash(port, start_address, data, retry=retry - 1)
else:
click.echo('failed (could not complete upload)')
return False
port.flush()
port.reset_input_buffer()
return True


def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(l), n):
yield l[i:i + n]


def upload_binary(port, file):
address = 0x08000000
with open(file, 'rb') as f:
Expand All @@ -161,18 +184,23 @@ def upload_binary(port, file):
return True


def send_go_command(port, address):
click.echo('Executing binary...', nl=False)
address = compute_address_commandable(address)
debug('Executing binary at {}'.format(adr_to_str(address)))
def send_go_command(port, address, retry=3):
click.echo('Executing user code... ', nl=False)
c_addr = compute_address_commandable(address)
debug('Executing binary at {}'.format(adr_to_str(c_addr)))

response = send_bootloader_command(port, 0x21, 1)
if response is None or response[0] != ACK:
debug_response(0x21, response)
if response is None or len(response) < 1 or response[0] != ACK:
click.echo('failed (execute command not accepted)')
return False
port.write(address)
port.flush()
click.echo('complete')
time.sleep(0.01)
port.write(c_addr)
time.sleep(0.01)
response = port.read(1)
debug_response(adr_to_str(c_addr), response)
if response is None or len(response) < 1 or response[0] != ACK:
click.echo('user code might not have started properly. May need to restart Cortex')
else:
click.echo('complete')
return True


28 changes: 12 additions & 16 deletions prosflasher/ports.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,42 +16,38 @@ def list_com_ports():
return [x for x in serial.tools.list_ports.comports() if x.vid is not None and (x.vid in USB_VID or 'vex' in x.product.lower())]


def create_serial(port):
def create_serial(port, parity):
"""
Creates and/or configures a serial port to communicate with the Cortex Microcontroller
:param port: A serial.Serial object, a device string identifier will create a corresponding serial port.
Anything elsse will create a default serial port with no device assigned.
Anything else will create a default serial port with no device assigned.
:return: Returns a correctly configured instance of a serial.Serial object, potentially with a correctly configured
device iff a correct port value was passed in
"""
# port_str = ''
if isinstance(port, str):
try:
port = serial.Serial(port)
# port_str = port
port = serial.Serial(port, baudrate=BAUD_RATE, bytesize=serial.EIGHTBITS, parity=parity, stopbits=serial.STOPBITS_ONE)
except serial.SerialException as e:
click.echo('WARNING: {}'.format(e))
port = serial.Serial()
port = serial.Serial(baudrate=BAUD_RATE, bytesize=serial.EIGHTBITS, parity=parity, stopbits=serial.STOPBITS_ONE)
elif not isinstance(port, serial.Serial):
port = serial.Serial()
click.echo('port was not string, send help')
port = serial.Serial(baudrate=BAUD_RATE, bytesize=serial.EIGHTBITS, parity=parity, stopbits=serial.STOPBITS_ONE)

assert isinstance(port, serial.Serial)

port.baudrate = BAUD_RATE
port.bytesize = serial.EIGHTBITS
port.parity = serial.PARITY_EVEN
port.stopbits = serial.STOPBITS_ONE
port.timeout = 5.0
port.xonxoff = False
port.rtscts = False
port.dsrdtr = False
# port.port = port_str if port_str != '' else None
port.timeout = 0.5
# port.write_timeout = 5.0
# port.inter_byte_timeout = 0.005 # todo make sure this is seconds

port.inter_byte_timeout = 0.1 # todo make sure this is seconds
return port


def create_port_list(verbose=False):
"""
Returns a formatted string of all COM ports we believe are valid Cortex ports, delimted by \n
Returns a formatted string of all COM ports we believe are valid Cortex ports, delimited by \n
:param verbose: If True, then the hwid will be added to the end of each device
:return: A formatted string for printing describing the COM ports
"""
Expand Down
Loading

0 comments on commit 2a991c5

Please sign in to comment.