Skip to content

Commit 62296d2

Browse files
author
Nathan Seidle
committed
Working v1.0
1 parent cd66698 commit 62296d2

File tree

9 files changed

+135
-31
lines changed

9 files changed

+135
-31
lines changed

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
SparkFun Artemis Firmware Uploader GUI
2+
========================================
3+
4+
![Artemis Firmware Uploader GUI](https://cdn.sparkfun.com/assets/home_page_posts/3/2/4/5/Artemis_Firmware_Uploader_GUI.jpg)
5+
6+
The Artemis Firmware Uploader (AFU) is a simple to use Windows GUI for updating firmware and the bootloader on Artemis based products.
7+
8+
To use:
9+
10+
* Download the repo ZIP
11+
* Run artemis_firmware_uploader_gui.exe
12+
* Select the firmware file you'd like to upload (should end in *.bin*)
13+
* Attach the Artemis target board over USB
14+
* Select the COM port (hit Refresh to refresh the list of USB devices)
15+
* Press Upload
16+
17+
Be sure you are loading firmware for your board or product. While it's unlikely to damage Artemis by loading incorrect firmware it will erase the pre-existing firmware and may lead to the peripherals being controlled incorrectly.
18+
19+
A *Blink.bin* firmware file is included. This will cause pin 5 to blink at 1Hz.
20+
21+
Pressing the 'Update Bootloader' button will erase all firmware on the Artemis and load the latest bootloader firmware. This is handy when SparkFun releases updates to the [SVL](https://github.com/sparkfun/SparkFun_Apollo3_AmbiqSuite_BSPs/blob/master/common/examples/artemis_svl/src/main.c).
22+
23+
SparkFun labored with love to create this code. Feel like supporting open source hardware?
24+
Buy a [breakout board](https://www.sparkfun.com/products/15444) from SparkFun!
25+
26+
Repository Contents
27+
-------------------
28+
29+
* **/tools** - The python source files and executables
30+
31+
License Information
32+
-------------------
33+
34+
This product is _**open source**_!
35+
36+
If you have any questions or concerns on licensing, please contact [email protected].
37+
38+
Please use, reuse, and modify these files as you see fit. Please maintain attribution to SparkFun Electronics and release any derivative under the same license.
39+
40+
Distributed as-is; no warranty is given.
41+
42+
- Your friends at SparkFun.

Together/AFU.exe

-28.8 MB
Binary file not shown.

Together/artemis_svl.exe

-5.45 MB
Binary file not shown.
Binary file not shown.

AFU.py renamed to artemis_firmware_uploader_gui.py

Lines changed: 93 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,24 @@
1010
TODO:
1111
1212
Push user to upgrade bootloader as needed
13+
Bootloader from CLI:
14+
ambiq_bin2board.exe --bin "artemis_svl.bin" --load-address-blob 0x20000 --magic-num 0xCB --version 0x0 --load-address-wired 0xC000 -i 6 --options 0x1 -b 115200 -port "COM3" -r 2 -v
15+
16+
ambiq_bin2board.exe --bin "artemis_svl.bin" --load-address-blob 0x20000 --magic-num 0xCB --version 0x0 --load-address-wired 0xC000 -i 6 --options 0x1 -b 115200 -port "COM3" -r 2 -v
1317
1418
"""
1519
from typing import Iterator, Tuple
1620
from serial.tools.list_ports import comports
17-
from PyQt5.QtCore import QSettings, QProcess
21+
from PyQt5.QtCore import QSettings, QProcess, QTimer
1822
from PyQt5.QtWidgets import QWidget, QLabel, QComboBox, QGridLayout, \
19-
QPushButton, QApplication, QLineEdit, QFileDialog
20-
from PyQt5.QtGui import QCloseEvent
21-
22-
# import artemis_svl
23+
QPushButton, QApplication, QLineEdit, QFileDialog, QPlainTextEdit
24+
from PyQt5.QtGui import QCloseEvent, QTextCursor
2325

2426
# Setting constants
2527
SETTING_PORT_NAME = 'port_name'
2628
SETTING_FILE_LOCATION = 'message'
2729
SETTING_BAUD_RATE = '921600'
2830

29-
progressCount = 1
30-
3131
guiVersion = 'v1.0'
3232

3333

@@ -69,7 +69,7 @@ def __init__(self, parent: QWidget = None) -> None:
6969
self.refresh_btn.pressed.connect(self.on_refresh_btn_pressed)
7070

7171
# Baudrate Combobox
72-
self.baud_label = QLabel(self.tr('Baud:'))
72+
self.baud_label = QLabel(self.tr('Baud Rate:'))
7373
self.baud_combobox = QComboBox()
7474
self.baud_label.setBuddy(self.baud_combobox)
7575
self.update_baud_rates()
@@ -78,10 +78,24 @@ def __init__(self, parent: QWidget = None) -> None:
7878
self.upload_btn = QPushButton(self.tr('Upload'))
7979
self.upload_btn.pressed.connect(self.on_upload_btn_pressed)
8080

81+
# Upload Button
82+
self.updateBootloader_btn = QPushButton(self.tr('Update Bootloader'))
83+
self.updateBootloader_btn.pressed.connect(
84+
self.on_update_bootloader_btn_pressed)
85+
8186
# Status bar
8287
self.status_label = QLabel(self.tr('Status:'))
8388
self.status = QLabel(self.tr(' '))
8489

90+
# Messages Bar
91+
self.messages_label = QLabel(self.tr('Status / Warnings:'))
92+
93+
# Messages Window
94+
self.messages = QPlainTextEdit()
95+
# Attempting to reduce window size
96+
#self.messages.setMinimumSize(1, 2)
97+
#self.messages.resize(1, 2)
98+
8599
# Arrange Layout
86100
layout = QGridLayout()
87101
layout.addWidget(self.msg_label, 0, 0)
@@ -94,14 +108,30 @@ def __init__(self, parent: QWidget = None) -> None:
94108

95109
layout.addWidget(self.baud_label, 2, 0)
96110
layout.addWidget(self.baud_combobox, 2, 1)
97-
layout.addWidget(self.upload_btn, 3, 2)
98111

99-
layout.addWidget(self.status_label, 3, 0)
100-
layout.addWidget(self.status, 3, 1)
112+
#layout.addWidget(self.status_label, 3, 0)
113+
#layout.addWidget(self.status, 3, 1)
114+
115+
layout.addWidget(self.messages_label, 3, 0)
116+
layout.addWidget(self.messages, 4, 0, 4, 3)
117+
118+
layout.addWidget(self.upload_btn, 15, 2)
119+
layout.addWidget(self.updateBootloader_btn, 15, 1)
120+
101121
self.setLayout(layout)
102122

103123
self._load_settings()
104124

125+
# Make the text edit window read-only
126+
self.messages.setReadOnly(True)
127+
self.messages.clear() # Clear the message window
128+
129+
def addMessage(self, msg: str) -> None:
130+
self.messages.moveCursor(QTextCursor.End)
131+
self.messages.ensureCursorVisible()
132+
self.messages.appendPlainText(msg)
133+
self.messages.ensureCursorVisible()
134+
105135
def _load_settings(self) -> None:
106136
"""Load settings on startup."""
107137
settings = QSettings()
@@ -139,11 +169,26 @@ def show_error_message(self, msg: str) -> None:
139169
def update_com_ports(self) -> None:
140170
"""Update COM Port list in GUI."""
141171
self.port_combobox.clear()
172+
173+
index = 0
174+
indexOfCH340 = -1
142175
for name, device in gen_serial_ports():
143176
self.port_combobox.addItem(name, device)
177+
if("CH340" in name):
178+
if(indexOfCH340 == -1): # Select the first available
179+
indexOfCH340 = index
180+
#print("CH340 found at index " + str(indexOfCH340))
181+
index = index + 1
182+
183+
# If we have no previous settings, and CH340 is in the list, auto-select it
184+
if indexOfCH340 > -1:
185+
settings = QSettings()
186+
port_name = settings.value(SETTING_PORT_NAME)
187+
if port_name is not None:
188+
self.port_combobox.setCurrentIndex(indexOfCH340)
144189

145190
def update_baud_rates(self) -> None:
146-
"""Update COM Port list in GUI."""
191+
"""Update baud rate list in GUI."""
147192
self.baud_combobox.addItem("921600", 921600)
148193
self.baud_combobox.addItem("460800", 460800)
149194
self.baud_combobox.addItem("115200", 115200)
@@ -167,6 +212,7 @@ def closeEvent(self, event: QCloseEvent) -> None:
167212

168213
def on_refresh_btn_pressed(self) -> None:
169214
self.update_com_ports()
215+
self.addMessage("Ports Refreshed")
170216

171217
def on_upload_btn_pressed(self) -> None:
172218
"""Check if port is available"""
@@ -176,7 +222,7 @@ def on_upload_btn_pressed(self) -> None:
176222
if (p.device == self.port):
177223
portAvailable = True
178224
if (portAvailable == False):
179-
self.status.setText("Port No Longer Available")
225+
self.addMessage("Port No Longer Available")
180226
return
181227

182228
"""Check if file exists"""
@@ -188,46 +234,62 @@ def on_upload_btn_pressed(self) -> None:
188234
fileExists = False
189235
finally:
190236
if (fileExists == False):
191-
self.status.setText("File Not Found")
237+
self.addMessage("File Not Found")
192238
return
193239
f.close()
194240

195-
global progressCount
196-
progressCount = 0
241+
self.addMessage("Uploading")
242+
243+
self.process = QProcess()
244+
self.process.readyReadStandardError.connect(
245+
self.onReadyReadStandardError)
246+
self.process.readyReadStandardOutput.connect(
247+
self.onReadyReadStandardOutput)
248+
249+
commandString = "tools/artemis_svl.exe " + \
250+
str(self.port) + " -f\"" + self.fileLocation_lineedit.text() + \
251+
"\"" + " -b" + str(self.baudRate)
252+
print(commandString)
253+
self.process.start(commandString)
254+
255+
def on_update_bootloader_btn_pressed(self) -> None:
256+
"""Check if port is available"""
257+
portAvailable = False
258+
ports = comports()
259+
for p in ports:
260+
if (p.device == self.port):
261+
portAvailable = True
262+
if (portAvailable == False):
263+
self.addMessage("Port No Longer Available")
264+
return
197265

198-
self.status.setText("Uploading ")
266+
self.addMessage("Updating bootloader")
199267

200268
self.process = QProcess()
201269
self.process.readyReadStandardError.connect(
202270
self.onReadyReadStandardError)
203271
self.process.readyReadStandardOutput.connect(
204272
self.onReadyReadStandardOutput)
205273

206-
self.process.start("artemis_svl.exe " + str(self.port) +
207-
" -f\"" + self.fileLocation_lineedit.text() + "\"" + " -b" + str(self.baudRate))
274+
self.process.start("tools/ambiq_bin2board.exe --bin tools/artemis_svl.bin --load-address-blob 0x20000 --magic-num 0xCB --version 0x0 --load-address-wired 0xC000 -i 6 --options 0x1 -b 115200 -port " + str(self.port) +
275+
" -r 2 -v")
208276

209277
def onReadyReadStandardError(self):
210278
error = self.process.readAllStandardError().data().decode()
211279
# print(error)
212-
self.status.setText(error)
280+
self.addMessage(error)
213281

214282
def onReadyReadStandardOutput(self):
215283
"""Parse the output from the process. Update our status as we go."""
216284
result = self.process.readAllStandardOutput().data().decode()
217285
# print(result)
286+
self.addMessage(result)
218287
if ("complete" in result):
219-
self.status.setText("Complete")
288+
self.addMessage("Complete")
220289
elif ("failed" in result):
221-
self.status.setText("Upload Failed")
290+
self.addMessage("Upload Failed")
222291
elif ("open" in result):
223-
self.status.setText("Port In Use / Please Close")
224-
else: # The '#' is displayed 50 times until completion
225-
global progressCount
226-
progressCount = progressCount + result.count("#")
227-
# print(progressCount)
228-
for i in range(int(progressCount / 3)):
229-
current = self.status.text() + "."
230-
self.status.setText(current)
292+
self.addMessage("Port In Use / Please Close")
231293

232294
def on_browse_btn_pressed(self) -> None:
233295
"""Open dialog to select bin file."""
@@ -246,7 +308,7 @@ def on_browse_btn_pressed(self) -> None:
246308
import sys
247309
app = QApplication([])
248310
app.setOrganizationName('SparkFun')
249-
app.setApplicationName('Artemis Firmware Uploader')
311+
app.setApplicationName('Artemis Firmware Uploader ' + guiVersion)
250312
w = RemoteWidget()
251313
w.show()
252314
sys.exit(app.exec_())
File renamed without changes.

tools/ambiq_bin2board.exe

6.68 MB
Binary file not shown.

tools/artemis_svl.bin

12.6 KB
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)