Update IDE to handle the new bootloader.

This commit is contained in:
iabdalkader 2016-04-25 18:22:41 +02:00
parent 3d38908f2a
commit 54be7503e6
3 changed files with 260 additions and 204 deletions

View File

@ -181,7 +181,7 @@ Kwabena W. Agyeman</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="show_text">True</property>
<property name="text" translatable="yes"> </property>
<property name="text" translatable="yes"> '</property>
</object>
<packing>
<property name="expand">True</property>
@ -194,7 +194,7 @@ Kwabena W. Agyeman</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkEntry" id="fw_entry">
<object class="GtkEntry" id="fw_path_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
@ -211,12 +211,11 @@ Kwabena W. Agyeman</property>
</packing>
</child>
<child>
<object class="GtkButton" id="fw_button">
<object class="GtkButton" id="fw_path_button">
<property name="label" translatable="yes">...</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_fwpath_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@ -447,7 +446,30 @@ Kwabena W. Agyeman</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menuitem4">
<object class="GtkMenuItem" id="menuitem2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Tools</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu" id="menu2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem" id="menuitem4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Bootloader</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_bootloader_clicked" swapped="no"/>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menuitem3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Help</property>
@ -525,20 +547,6 @@ Kwabena W. Agyeman</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<object class="GtkToolButton" id="bootloader_button">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Update Firmware</property>
<property name="use_underline">True</property>
<property name="stock_id">gtk-go-up</property>
<signal name="clicked" handler="on_fwupdate_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<object class="GtkSeparatorToolItem" id="separator1">
<property name="visible">True</property>
@ -1280,7 +1288,6 @@ Kwabena W. Agyeman</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="image_position">right</property>
<property name="active">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>

View File

@ -13,10 +13,6 @@ import glob
import urllib2, json
import numpy as np
#import pydfu on Linux
if platform.system() == "Linux":
import pydfu
try:
# 3.x name
import configparser
@ -44,9 +40,8 @@ UDEV_PATH = "/etc/udev/rules.d/50-openmv.rules"
SCALE =1
RECENT_FILES_LIMIT=5
FLASH_OFFSETS= [0x08000000, 0x08004000, 0x08008000, 0x0800C000,
0x08010000, 0x08020000, 0x08040000, 0x08060000,
0x08080000, 0x080A0000, 0x080C0000, 0x080E0000]
FLASH_SECTOR_START=4
FLASH_SECTOR_END=11
DEFAULT_CONFIG='''\
[main]
@ -61,6 +56,167 @@ CONFIG_KEYS = ['board', 'serial_port', 'recent', 'last_fw_path', 'baudrate', 'en
RELEASE_TAG_NAME = 'v1.3'
RELEASE_URL = 'https://api.github.com/repos/openmv/openmv/releases/latest'
class Bootloader:
def __init__(self, builder, config):
self.config = config
self.port = config.get("main", "serial_port")
self.baud = int(config.get("main", "baudrate"))
self.parent = builder.get_object("top_window")
self.dialog = builder.get_object("fw_dialog")
self.dialog.set_transient_for(self.parent)
self.fw_progress = builder.get_object("fw_progressbar")
self.fw_path_entry = builder.get_object("fw_path_entry")
self.fw_path_button = builder.get_object("fw_path_button")
self.ok_button = builder.get_object("fw_ok_button")
self.cancel_button = builder.get_object("fw_cancel_button")
self.ok_button.set_sensitive(True)
self.cancel_button.set_sensitive(True)
self.fw_path_button.set_sensitive(True)
# Firmware file path
self.last_fw_path = self.config.get("main", "last_fw_path")
if os.path.isfile(self.last_fw_path) == False:
self.last_fw_path = ""
# Connect signals
self.dialog.connect("close", self.on_dialog_close)
self.dialog.connect("delete_event", self.on_dialog_close)
self.dialog.connect("response", self.on_dialog_response)
self.fw_path_button.connect("clicked", self.on_fw_path)
def show_message_dialog(self, msg_type, msg):
message = gtk.MessageDialog(parent=self.parent, flags=gtk.DIALOG_DESTROY_WITH_PARENT,
type=msg_type, buttons=gtk.BUTTONS_OK, message_format=msg)
message.run()
message.destroy()
def on_fw_path(self, widget):
dialog = gtk.FileChooserDialog(title=None,action=gtk.FILE_CHOOSER_ACTION_OPEN,
buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK))
dialog.set_default_response(gtk.RESPONSE_OK)
dialog.set_current_folder(SCRIPTS_DIR)
ff = gtk.FileFilter()
ff.set_name("binary")
ff.add_pattern("*.bin")
dialog.add_filter(ff)
if dialog.run() == gtk.RESPONSE_OK:
self.fw_path_entry.set_text(dialog.get_filename())
dialog.destroy()
# Fake multitasking :P
def task_init(self, state):
state["next"] = self.task_connect
return True
def task_connect(self, state):
try:
# Attempt to connect to bootloader
openmv.init(self.port, self.baud, timeout=0.050)
if openmv.bootloader_start():
state["next"] = self.task_erase
state["bar"].set_text("Erasing...")
self.cancel_button.set_sensitive(False)
except Exception as e:
if self.flash_msg:
state["bar"].set_text("Connecting to bootloader...\
\n ")
else:
state["bar"].set_text("Connecting to bootloader...\
\nDisconnect and re-connect cam!")
self.flash_msg = self.flash_msg ^ 1
sleep(0.100)
return True
def task_erase(self, state):
sector = state["sector"]
state["bar"].set_text("Erasing sector %d/%d"%((sector-FLASH_SECTOR_START+1), FLASH_SECTOR_END-FLASH_SECTOR_START+1))
state["bar"].set_fraction((sector-FLASH_SECTOR_START+1)/float(FLASH_SECTOR_END-FLASH_SECTOR_START+1))
openmv.flash_erase(sector)
sector += 1
if (sector == FLASH_SECTOR_END+1):
state["next"] = self.task_upload
state["sector"] = sector
return True
def task_upload(self, state):
buf = state["buf"]
xfer_bytes = state["xfer_bytes"]
xfer_total = state["xfer_total"]
# Send chunk
chunk = min (60, xfer_total-xfer_bytes)
#openmv.flash_write(buf[xfer_bytes:xfer_bytes+chunk], xfer_bytes)
openmv.flash_write(buf[xfer_bytes : xfer_bytes+chunk])
xfer_bytes += chunk
state["xfer_bytes"] = xfer_bytes
state["bar"].set_text("Uploading %d/%d"%(xfer_bytes, xfer_total))
state["bar"].set_fraction(xfer_bytes/float(xfer_total))
if (xfer_bytes == xfer_total):
openmv.bootloader_reset()
state["dialog"].hide()
return False
return True
def task(self, state):
return self.running and state["next"](state)
def run(self):
self.running = True
self.flash_msg = 1
self.ok_button.set_sensitive(True)
self.cancel_button.set_sensitive(True)
self.fw_path_button.set_sensitive(True)
# Firmware progress
self.fw_progress.set_text("")
self.fw_progress.set_fraction(0.0)
# Last firmware path
self.fw_path_entry.set_text(self.last_fw_path)
# Run bootloader dialog
self.dialog.run()
def on_dialog_close(self, widget, event=None):
self.dialog.hide()
self.running = False
return True
def on_dialog_response(self, dialog, response, *args):
if response == gtk.RESPONSE_OK:
self.ok_button.set_sensitive(False)
self.fw_path_button.set_sensitive(False)
fw_path = self.fw_path_entry.get_text()
try:
with open(fw_path, 'r') as f:
buf= f.read()
except Exception as e:
self.show_message_dialog(gtk.MESSAGE_ERROR, "Failed to open file %s"%str(e))
self.dialog.hide()
return
self.last_fw_path = fw_path
self.config.set("main", "last_fw_path", self.last_fw_path)
self.state={ "next":self.task_init, "buf":buf, "sector":FLASH_SECTOR_START, "running":True,
"bar":self.fw_progress, "dialog":self.dialog, "xfer_bytes":0, "xfer_total":len(buf)}
gobject.gobject.idle_add(self.task, self.state)
else:
self.dialog.hide()
self.running = False
class OMVGtk:
def __init__(self):
#Set the Glade file
@ -79,7 +235,6 @@ class OMVGtk:
self.connect_button = self.builder.get_object('connect_button')
self.exec_button = self.builder.get_object('exec_button')
self.stop_button = self.builder.get_object('stop_button')
self.fwupdate_button = self.builder.get_object('bootloader_button')
self.save_button.set_sensitive(False)
self.exec_button.set_sensitive(False)
@ -89,7 +244,6 @@ class OMVGtk:
# set control buttons
self.controls = [
self.builder.get_object('reset_button'),
self.builder.get_object('bootloader_button'),
self.builder.get_object('exec_button'),
self.builder.get_object('zoomin_button'),
self.builder.get_object('zoomout_button'),
@ -99,10 +253,6 @@ class OMVGtk:
self.connected = False
map(lambda x:x.set_sensitive(False), self.controls)
# Disable dfu button on Windows
if platform.system() == "Windows":
self.controls.pop(1)
# gtksourceview widget
sourceview = gtksourceview.View()
lang_manager = gtksourceview.language_manager_get_default()
@ -176,37 +326,6 @@ class OMVGtk:
self.builder.get_object("saturation_adjust").attr= openmv.ATTR_SATURATION
self.builder.get_object("gainceiling_adjust").attr= openmv.ATTR_GAINCEILING
#connect signals
signals = {
"on_top_window_destroy" : self.quit,
"on_connect_clicked" : self.connect_clicked,
"on_reset_clicked" : self.reset_clicked,
"on_fwupdate_clicked" : self.fwupdate_clicked,
"on_fwpath_clicked" : self.fwpath_clicked,
"on_execute_clicked" : self.execute_clicked,
"on_stop_clicked" : self.stop_clicked,
"on_motion_notify" : self.motion_notify,
"on_button_press" : self.button_pressed,
"on_button_release" : self.button_released,
"on_open_file" : self.open_file,
"on_new_file" : self.new_file,
"on_save_file" : self.save_file,
"on_save_file_as" : self.save_file_as,
"on_about_dialog" : self.about_dialog,
"on_pinout_dialog" : self.pinout_dialog,
"on_copy_color_activate" : self.copy_color,
"on_save_template_activate" : self.save_template,
"on_save_descriptor_activate" : self.save_descriptor,
"on_ctrl_scale_value_changed" : self.on_ctrl_scale_value_changed,
"on_zoomin_clicked" : self.zoomin_clicked,
"on_zoomout_clicked" : self.zoomout_clicked,
"on_bestfit_clicked" : self.bestfit_clicked,
"on_preferences_clicked" : self.preferences_clicked,
"on_updatefb_clicked" : self.updatefb_clicked,
"on_vte_size_allocate" : self.scroll_terminal,
}
self.builder.connect_signals(signals)
# create data directory
if not os.path.isdir(DATA_DIR):
os.makedirs(DATA_DIR)
@ -253,12 +372,8 @@ class OMVGtk:
print ("Failed to open config file %s"%(e))
sys.exit(1)
# current file path
# Current file path
self.file_path= None
self.fw_file_path=""
path = self.config.get("main", "last_fw_path")
if os.path.isfile(path):
self.fw_file_path = path
# Built-in examples menu
submenu = gtk.Menu()
@ -296,6 +411,36 @@ class OMVGtk:
self._load_file(os.path.join(EXAMPLES_DIR, "01-Basics", "helloworld.py"))
self.save_button.set_sensitive(False)
#connect signals
signals = {
"on_top_window_destroy" : self.quit,
"on_connect_clicked" : self.connect_clicked,
"on_reset_clicked" : self.reset_clicked,
"on_execute_clicked" : self.execute_clicked,
"on_stop_clicked" : self.stop_clicked,
"on_bootloader_clicked" : self.bootloader_clicked,
"on_motion_notify" : self.motion_notify,
"on_button_press" : self.button_pressed,
"on_button_release" : self.button_released,
"on_open_file" : self.open_file,
"on_new_file" : self.new_file,
"on_save_file" : self.save_file,
"on_save_file_as" : self.save_file_as,
"on_about_dialog" : self.about_dialog,
"on_pinout_dialog" : self.pinout_dialog,
"on_copy_color_activate" : self.copy_color,
"on_save_template_activate" : self.save_template,
"on_save_descriptor_activate" : self.save_descriptor,
"on_ctrl_scale_value_changed" : self.on_ctrl_scale_value_changed,
"on_zoomin_clicked" : self.zoomin_clicked,
"on_zoomout_clicked" : self.zoomout_clicked,
"on_bestfit_clicked" : self.bestfit_clicked,
"on_preferences_clicked" : self.preferences_clicked,
"on_updatefb_clicked" : self.updatefb_clicked,
"on_vte_size_allocate" : self.scroll_terminal,
}
self.builder.connect_signals(signals)
def show_message_dialog(self, msg_type, msg):
message = gtk.MessageDialog(parent=self.window, flags=gtk.DIALOG_DESTROY_WITH_PARENT,
type=msg_type, buttons=gtk.BUTTONS_OK, message_format=msg)
@ -341,44 +486,30 @@ class OMVGtk:
gobject.gobject.timeout_add(30, omvgtk.update_terminal)
# check firmware version
self.fw_mismatch = False
fw_ver = openmv.fw_version()
ide_ver = (FIRMWARE_VERSION_MAJOR,
FIRMWARE_VERSION_MINOR,
FIRMWARE_VERSION_PATCH)
ide_ver = (FIRMWARE_VERSION_MAJOR, FIRMWARE_VERSION_MINOR, FIRMWARE_VERSION_PATCH)
print("fw_version:" + str(fw_ver))
print("ide_version:" + str(ide_ver))
if (fw_ver[0] != FIRMWARE_VERSION_MAJOR):
# If ABI versions don't match, nothing todo here...
self.show_message_dialog(gtk.MESSAGE_ERROR,
"Firmware ABI version mismatch!\n"
"Please update the IDE and/or FW manually\n")
return
elif (FIRMWARE_VERSION_MINOR > fw_ver[1]
or FIRMWARE_VERSION_PATCH > fw_ver[2]):
print("fw_version:%s\nide_version:%s" %(str(fw_ver), str(ide_ver)))
if (fw_ver[0] != FIRMWARE_VERSION_MAJOR or fw_ver[1] != FIRMWARE_VERSION_MINOR):
self.connected = False
self.fw_mismatch = True
self.connect_button.set_sensitive(False)
self.show_message_dialog(gtk.MESSAGE_ERROR,
"Firmware version mismatch!\n"
"An older firmware version has been detected.\n"
"Please update the firmware!")
"Please update the firmware image and/or the IDE!")
else:
# interrupt any running code
openmv.stop_script()
# set enable JPEG
openmv.enable_jpeg(self.enable_jpeg)
if (self.fw_mismatch):
self.connected = True
self.fw_mismatch = False
self._update_title()
self.connect_button.set_sensitive(False)
self.fwupdate_button.set_sensitive(True)
return
# interrupt any running code
openmv.stop_script()
# set enable JPEG
openmv.enable_jpeg(self.enable_jpeg)
self.connected = True
self._update_title()
self.connect_button.set_sensitive(False)
map(lambda x:x.set_sensitive(True), self.controls)
map(lambda x:x.set_sensitive(True), self.controls)
def disconnect(self):
try:
@ -396,107 +527,6 @@ class OMVGtk:
def connect_clicked(self, widget):
self.connect()
def fwpath_clicked(self, widget):
fw_entry = self.builder.get_object("fw_entry")
dialog = gtk.FileChooserDialog(title=None,action=gtk.FILE_CHOOSER_ACTION_OPEN,
buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK))
dialog.set_default_response(gtk.RESPONSE_OK)
dialog.set_current_folder(SCRIPTS_DIR)
ff = gtk.FileFilter()
ff.set_name("dfu")
ff.add_pattern("*.bin") #TODO change to DFU
dialog.add_filter(ff)
if dialog.run() == gtk.RESPONSE_OK:
fw_entry.set_text(dialog.get_filename())
dialog.destroy()
# Fake multitasking :P
def fwupdate_task(self, state):
if (state["init"]):
pydfu.init()
state["init"]=False
state["erase"]=True
state["bar"].set_text("Erasing...")
return True
elif (state["erase"]):
page = state["page"]
total = len(FLASH_OFFSETS)
pydfu.page_erase(FLASH_OFFSETS[page])
page +=1
state["bar"].set_fraction(page/float(total))
if (page == total):
state["erase"] = False
state["write"] = True
state["bar"].set_text("Uploading...")
state["page"] = page
return True
elif (state["write"]):
buf = state["buf"]
xfer_bytes = state["xfer_bytes"]
xfer_total = state["xfer_total"]
# Send chunk
chunk = min (64, xfer_total-xfer_bytes)
pydfu.write_page(buf[xfer_bytes:xfer_bytes+chunk], xfer_bytes)
xfer_bytes += chunk
state["xfer_bytes"] = xfer_bytes
state["bar"].set_fraction(xfer_bytes/float(xfer_total))
if (xfer_bytes == xfer_total):
pydfu.exit_dfu()
state["dialog"].hide()
self.disconnect()
return False
return True
def fwupdate_clicked(self, widget):
if (self.connected):
dialog = self.builder.get_object("fw_dialog")
fw_entry = self.builder.get_object("fw_entry")
fw_progress = self.builder.get_object("fw_progressbar")
ok_button = self.builder.get_object("fw_ok_button")
cancel_button = self.builder.get_object("fw_cancel_button")
ok_button.set_sensitive(True)
cancel_button.set_sensitive(True)
dialog.set_transient_for(self.window);
# default FW bin path
fw_entry.set_text(self.fw_file_path)
fw_progress.set_text("")
fw_progress.set_fraction(0.0)
if dialog.run() == gtk.RESPONSE_OK:
ok_button.set_sensitive(False)
cancel_button.set_sensitive(False)
fw_path = fw_entry.get_text()
try:
with open(fw_path, 'r') as f:
buf= f.read()
except Exception as e:
dialog.hide()
self.show_message_dialog(gtk.MESSAGE_ERROR, "Failed to open file %s"%str(e))
return
self.fw_file_path = fw_path
self.config.set("main", "last_fw_path", fw_path)
state={"init":True, "erase":False, "write":False,
"page":0, "buf":buf, "bar":fw_progress, "dialog":dialog,
"xfer_bytes":0, "xfer_total":len(buf)}
# call dfu-util
openmv.enter_dfu()
sleep(1.0)
gobject.gobject.idle_add(self.fwupdate_task, state)
else:
dialog.hide()
def reset_clicked(self, widget):
if (self.connected):
openmv.reset()
@ -562,6 +592,10 @@ class OMVGtk:
def updatefb_clicked(self, widget):
openmv.fb_update()
def bootloader_clicked(self, widget):
# Create bootloader object
Bootloader(self.builder, self.config).run()
def button_pressed(self, widget, event):
self.x1 = int(event.x)
self.y1 = int(event.y)

View File

@ -16,7 +16,7 @@ __serial = None
__FB_HDR_SIZE =12
# USB Debug commands
__USBDBG_CMD = 48
__USBDBG_CMD = 48
__USBDBG_FW_VERSION = 0x80
__USBDBG_FRAME_SIZE = 0x81
__USBDBG_FRAME_DUMP = 0x82
@ -40,6 +40,11 @@ ATTR_BRIGHTNESS =1
ATTR_SATURATION =2
ATTR_GAINCEILING=3
__BOOTLDR_START = 0xABCD0001
__BOOTLDR_RESET = 0xABCD0002
__BOOTLDR_ERASE = 0xABCD0004
__BOOTLDR_WRITE = 0xABCD0008
def init(port, baudrate=921600, timeout=0.3):
global __serial
# open CDC port
@ -121,12 +126,22 @@ def get_attr(attr):
__serial.write(struct.pack("<BBIh", __USBDBG_CMD, __USBDBG_ATTR_READ, 1, attr))
return __serial.read(1)
def enter_dfu():
__serial.write(struct.pack("<BBI", __USBDBG_CMD, __USBDBG_SYS_BOOT, 0))
def reset():
__serial.write(struct.pack("<BBI", __USBDBG_CMD, __USBDBG_SYS_RESET, 0))
def bootloader_start():
__serial.write(struct.pack("<I", __BOOTLDR_START))
return struct.unpack("I", __serial.read(4))[0] == __BOOTLDR_START
def bootloader_reset():
__serial.write(struct.pack("<I", __BOOTLDR_RESET))
def flash_erase(sector):
__serial.write(struct.pack("<II", __BOOTLDR_ERASE, sector))
def flash_write(buf):
__serial.write(struct.pack("<I", __BOOTLDR_WRITE) + buf)
def tx_buf_len():
__serial.write(struct.pack("<BBI", __USBDBG_CMD, __USBDBG_TX_BUF_LEN, 4))
return struct.unpack("I", __serial.read(4))[0]