Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
nanovna committed Aug 29, 2020
2 parents 96a5313 + 4127df2 commit ef6a58d
Show file tree
Hide file tree
Showing 7 changed files with 298 additions and 153 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

This repository contains the source code to build the firmware for the NanoVNA2 (or SAA2)
See https://www.nanorfe.com for more info.
See https://nanorfe.com/nanovna-v2.html for more info.

Below is information for building the firmware on Linux.

Expand Down
4 changes: 4 additions & 0 deletions bootloader/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,10 @@ extern "C" int atoi(const char* s) {
// TODO: implement
return 0;
}
extern "C" long atoll(const char* s) {
// TODO: implement
return 0;
}
extern "C" void __aeabi_atexit(void * arg , void (* func ) (void *)) {
// Leave this function empty. Program never exits.
}
Expand Down
2 changes: 1 addition & 1 deletion common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ static constexpr uint32_t FREQUENCY_CHANGE_OVER = 140000000;
#define USB_POINTS_MAX 1024
#define TRACES_MAX 4
#define MARKERS_MAX 4
#define FFT_SIZE 512
#define FFT_SIZE 256
#define ECAL_PARTIAL

#ifdef ECAL_PARTIAL
Expand Down
305 changes: 188 additions & 117 deletions dfu.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,119 +1,190 @@
#!/usr/bin/env python3
import serial, tty, sys
import argparse

# returns true if device is in dfu mode
def detectDFU(ser):
# reset protocol to known state
ser.write([0,0,0,0,0,0,0,0])

# read register 0xf3 (firmware major version)
cmd = b"\x10\xf3"
ser.write(cmd)

resp = ser.read(1)
if len(resp) < 1:
print('Read timeout')
return False

# firmware major version is always 0xff in dfu mode
if resp[0] == 0xff:
return True

print('Firmware major version: 0x%02x' % resp[0])
return False

# sets the flash address to begin writing at
def sendWriteAddr(ser, addr):
# write register 0xe0, 4 bytes
cmd = b"\x22\xe0" + int.to_bytes(addr, 4, 'little')
ser.write(cmd)

def sendBytes(ser, data):
cmd = bytearray()
offs = 0
while offs < len(data):
sendLen = len(data) - offs
if sendLen > 255:
sendLen = 255

# cmd: write FIFO, addr 0xe4
cmd += (b'\x28\xe4')
cmd.append(sendLen)
cmd += (data[offs:offs+sendLen])
offs += sendLen

# cmd: echo version
cmd.append(0x0d)
ser.write(cmd)

# wait for one sent buffer to complete
def waitSend(ser):
ser.read(1)
sys.stdout.write('.')
sys.stdout.flush()




parser = argparse.ArgumentParser()
parser.add_argument('-s', '--serial', default='/dev/ttyACM0',
help="Path to USB serial device")
parser.add_argument('-f', '--file',
help="Path to .bin image file")
parser.add_argument('-r', '--reboot', action='store_true',
help="Reset device without flashing any image")
parser.add_argument('-a', '--argument', type=lambda x:int(x,0), default=-1,
help="User argument (u32) passed to application after reset")
args = parser.parse_args()

if args.file != None and args.reboot:
print('-f and -r can not both be specified at the same time')
exit(1)

if args.file == None and not args.reboot:
print('Either -f or -r required')
exit(1)


ser = serial.Serial(args.serial)
tty.setraw(ser.fd)
ser.timeout = 0.1
ser.read(4096)
ser.timeout = 2

if not detectDFU(ser):
print('Device not in DFU mode')
print('Please enter DFU mode through menu CONFIG -> DFU or hold down the JOG LEFT button and power cycle the device.')
exit(1)

if args.file != None:
sendWriteAddr(ser, 0x08004000)

src = open(args.file)
outstanding = 0
while True:
buf = src.buffer.read(1200)
if len(buf) == 0:
break
sendBytes(ser, buf)
outstanding += 1
if outstanding >= 5:
waitSend(ser)
outstanding -= 1

while outstanding > 0:
waitSend(ser)
outstanding -= 1

print('')
print('done. Resetting')

# set user argument
if args.argument >= 0:
cmd = b"\x22\xe8" + int.to_bytes(args.argument, 4, 'little')
ser.write(cmd)

# reboot device
ser.write([0x20, 0xef, 0x5e])

import os
from sys import exit
import time
import tty

import serial

try:
from progress.bar import Bar
except ImportError:
Bar = None


class DummyBar:
"""Dummy progress bar, to be used if progress is not available"""
def __init__(self, max=100, **kwargs):
self.max = max
self.index = 0

def start(self):
pass

def next(self, n=1):
self.index += n
print(".", sep="", end="", flush=True)

def finish(self):
print(flush=True)


if Bar is None:
Bar = DummyBar


class DummySerial:
"""Dummy serial port, useful for testing purpose"""
def read(self, size=1):
time.sleep(0.05)
# return 0xFF so detect_fpu() is happy
return b"\xff" * size

def write(self, data):
pass


def detect_dfu(ser):
"""returns true if device is in dfu mode"""

# reset protocol to known state
ser.write([0] * 8)

# read registers 0xf1->0xf4
# f0: device variant
# f1: protocol version (01)
# f2: hardware revision (always 0 in dfu mode)
# f3: firmware major version (ff => dfu mode)
# f4: firmware minor version (bootloader version)

cmd = b"\x10\xf0\x10\xf1\x10\xf2\x10\xf3\x10\xf4"
ser.write(cmd)
resp = ser.read(5)

if resp and len(resp) == 5:
# firmware major version is always 0xff in dfu mode
if resp[3] == 0xFF:
return True
print(f"Informations:")
print(f" Device variant: 0x{resp[0]:02x}")
print(f" Protocol version: 0x{resp[1]:02x}")
print(f" Hardware version: 0x{resp[2]:02x}")
print(f" Firmware major version: 0x{resp[3]:02x}")
print(f" Firmware minor version: 0x{resp[4]:02x}")
else:
print("Read timeout")
return False


def send_write_addr(addr):
"returns the cmd to set the flash address to begin writing at"
# write register 0xe0, 4 bytes
return b"\x22\xe0" + addr.to_bytes(length=4, byteorder="little")


def send_bytes(data):
"returns the bytes to send as block of data"
cmd = []
while data:
# send data by blocks of 255 bytes
block = data[:255]
cmd.append(b"\x28\xe4")
cmd.append(bytes([len(block)]))
cmd.append(block)
data = data[255:] # remaining data to send, if any
# cmd: echo version
cmd.append(b"\x0d")
return b"".join(cmd)


def block_reader(fobj, blocksize=1275):
"generates blocks of data of size blocksize reading from fobj"
while True:
data = fobj.read(blocksize)
if data:
yield data
else:
break


def main():
parser = argparse.ArgumentParser()
parser.add_argument(
"-s", "--serial", default="/dev/ttyACM0", help="Path to USB serial device"
)
parser.add_argument(
"-f", "--file", type=argparse.FileType("rb"), help="Path to .bin image file"
)
parser.add_argument(
"-r",
"--reboot",
action="store_true",
help="Reset device without flashing any image",
)
parser.add_argument(
"-a",
"--argument",
type=int,
default=-1,
help="User argument (u32) passed to application after reset",
)
parser.add_argument(
"-d", "--dummy", default=False, action="store_true", help="Dummy mode",
)
args = parser.parse_args()
if args.file is not None and args.reboot:
print("-f and -r can not both be specified at the same time")
exit(1)

if args.file is None and not args.reboot:
print("Either -f or -r required")
exit(1)

if not args.dummy:
ser = serial.Serial(args.serial)
tty.setraw(ser.fd)
# flush the serial port buffer, if any
ser.timeout = 0.1
ser.read(4096)

ser.timeout = 2
else:
ser = DummySerial()

if not detect_dfu(ser):
print("Device not in DFU mode")
print(
"Please enter DFU mode through menu CONFIG -> DFU or hold down "
"the JOG LEFT button and power cycle the device."
)
exit(1)

if args.file is not None:
print(f"Uploading firmware file {args.file.name}")
# set the flash address where the firmware is to be written
ser.write(send_write_addr(0x08004000))
size = os.stat(args.file.name).st_size
pbar = Bar(message=os.path.basename(args.file.name), max=size)
pbar.start()
# write the firmware
for data in block_reader(args.file, blocksize=1200):
if data:
ser.write(send_bytes(data))
pbar.next(len(data))
ser.read(1) # wait for block write to complete
pbar.finish()

# set user argument
if args.argument >= 0:
print("Sending user argument")
cmd = b"\x22\xe8" + int.to_bytes(args.argument, 4, "little")
ser.write(cmd)

print("Resetting")
# reboot device
ser.write([0x20, 0xEF, 0x5E])


if __name__ == "__main__":
main()
Loading

0 comments on commit ef6a58d

Please sign in to comment.