main: detect specific serial port IDs based on USB vid/pid
This makes it possible to flash a board even when there are multiple different kinds of boards attached, e.g. an Arduino Uno and a Circuit Playground Express. You can find the VID/PID pair in several ways: 1. By running `lsusb` before and after attaching the board and looking at the new USB device. 2. By grepping for `usb_PID` and `usb_VID` in the TinyGo source code. 3. By checking the Arduino IDE boards.txt from the vendor. Note that one board may have multiple VID/PID pairs: * The bootloader and main program may have a different PID, so far I've seen that the main program generally has the bootloader PID with 0x8000 added. * The software running on the board may have an erroneous PID, for example from a different board. I've seen this happen a few times. * A single board may have had some revisions which changed the PID. This is particularly true for the Arduino Uno. As a fallback, if the given VID/PID pair isn't found, the whole set of serial ports will be used. There are many boards which I haven't included yet simply because I couldn't test them.
Этот коммит содержится в:
родитель
8e33f1c9eb
коммит
e107efa63f
10 изменённых файлов: 75 добавлений и 7 удалений
|
@ -45,6 +45,7 @@ type TargetSpec struct {
|
||||||
FlashCommand string `json:"flash-command"`
|
FlashCommand string `json:"flash-command"`
|
||||||
GDB []string `json:"gdb"`
|
GDB []string `json:"gdb"`
|
||||||
PortReset string `json:"flash-1200-bps-reset"`
|
PortReset string `json:"flash-1200-bps-reset"`
|
||||||
|
SerialPort []string `json:"serial-port"` // serial port IDs in the form "acm:vid:pid" or "usb:vid:pid"
|
||||||
FlashMethod string `json:"flash-method"`
|
FlashMethod string `json:"flash-method"`
|
||||||
FlashVolume string `json:"msd-volume-name"`
|
FlashVolume string `json:"msd-volume-name"`
|
||||||
FlashFilename string `json:"msd-firmware-name"`
|
FlashFilename string `json:"msd-firmware-name"`
|
||||||
|
|
73
main.go
73
main.go
|
@ -15,6 +15,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
@ -296,7 +297,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||||
return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
|
return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
|
||||||
// do we need port reset to put MCU into bootloader mode?
|
// do we need port reset to put MCU into bootloader mode?
|
||||||
if config.Target.PortReset == "true" && flashMethod != "openocd" {
|
if config.Target.PortReset == "true" && flashMethod != "openocd" {
|
||||||
port, err := getDefaultPort(strings.FieldsFunc(port, func(c rune) bool { return c == ',' }))
|
port, err := getDefaultPort(port, config.Target.SerialPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -321,7 +322,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||||
|
|
||||||
if strings.Contains(flashCmd, "{port}") {
|
if strings.Contains(flashCmd, "{port}") {
|
||||||
var err error
|
var err error
|
||||||
port, err = getDefaultPort(strings.FieldsFunc(port, func(c rune) bool { return c == ',' }))
|
port, err = getDefaultPort(port, config.Target.SerialPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -718,7 +719,8 @@ func windowsFindUSBDrive(volume string, options *compileopts.Options) (string, e
|
||||||
}
|
}
|
||||||
|
|
||||||
// getDefaultPort returns the default serial port depending on the operating system.
|
// getDefaultPort returns the default serial port depending on the operating system.
|
||||||
func getDefaultPort(portCandidates []string) (port string, err error) {
|
func getDefaultPort(portFlag string, usbInterfaces []string) (port string, err error) {
|
||||||
|
portCandidates := strings.FieldsFunc(portFlag, func(c rune) bool { return c == ',' })
|
||||||
if len(portCandidates) == 1 {
|
if len(portCandidates) == 1 {
|
||||||
return portCandidates[0], nil
|
return portCandidates[0], nil
|
||||||
}
|
}
|
||||||
|
@ -734,13 +736,70 @@ func getDefaultPort(portCandidates []string) (port string, err error) {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range portsList {
|
var preferredPortIDs [][2]uint16
|
||||||
if p.IsUSB {
|
for _, s := range usbInterfaces {
|
||||||
ports = append(ports, p.Name)
|
parts := strings.Split(s, ":")
|
||||||
|
if len(parts) != 3 || (parts[0] != "acm" && parts[0] == "usb") {
|
||||||
|
// acm and usb are the two types of serial ports recognized
|
||||||
|
// under Linux (ttyACM*, ttyUSB*). Other operating systems don't
|
||||||
|
// generally make this distinction. If this is not one of the
|
||||||
|
// given USB devices, don't try to parse the USB IDs.
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
vid, err := strconv.ParseUint(parts[1], 16, 16)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("could not parse USB vendor ID %q: %w", parts[1], err)
|
||||||
|
}
|
||||||
|
pid, err := strconv.ParseUint(parts[2], 16, 16)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("could not parse USB product ID %q: %w", parts[1], err)
|
||||||
|
}
|
||||||
|
preferredPortIDs = append(preferredPortIDs, [2]uint16{uint16(vid), uint16(pid)})
|
||||||
}
|
}
|
||||||
|
|
||||||
if ports == nil || len(ports) == 0 {
|
var primaryPorts []string // ports picked from preferred USB VID/PID
|
||||||
|
var secondaryPorts []string // other ports (as a fallback)
|
||||||
|
for _, p := range portsList {
|
||||||
|
if !p.IsUSB {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if p.VID != "" && p.PID != "" {
|
||||||
|
foundPort := false
|
||||||
|
vid, vidErr := strconv.ParseUint(p.VID, 16, 16)
|
||||||
|
pid, pidErr := strconv.ParseUint(p.PID, 16, 16)
|
||||||
|
if vidErr == nil && pidErr == nil {
|
||||||
|
for _, id := range preferredPortIDs {
|
||||||
|
if uint16(vid) == id[0] && uint16(pid) == id[1] {
|
||||||
|
primaryPorts = append(primaryPorts, p.Name)
|
||||||
|
foundPort = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if foundPort {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
secondaryPorts = append(secondaryPorts, p.Name)
|
||||||
|
}
|
||||||
|
if len(primaryPorts) == 1 {
|
||||||
|
// There is exactly one match in the set of preferred ports. Use
|
||||||
|
// this port, even if there may be others available. This allows
|
||||||
|
// flashing a specific board even if there are multiple available.
|
||||||
|
return primaryPorts[0], nil
|
||||||
|
} else if len(primaryPorts) > 1 {
|
||||||
|
// There are multiple preferred ports, probably because more than
|
||||||
|
// one device of the same type are connected (e.g. two Arduino
|
||||||
|
// Unos).
|
||||||
|
ports = primaryPorts
|
||||||
|
} else {
|
||||||
|
// No preferred ports found. Fall back to other serial ports
|
||||||
|
// available in the system.
|
||||||
|
ports = secondaryPorts
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ports) == 0 {
|
||||||
// fallback
|
// fallback
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "darwin":
|
case "darwin":
|
||||||
|
|
|
@ -2,5 +2,6 @@
|
||||||
"inherits": ["atsamd21g18a"],
|
"inherits": ["atsamd21g18a"],
|
||||||
"build-tags": ["arduino_nano33"],
|
"build-tags": ["arduino_nano33"],
|
||||||
"flash-command": "bossac -i -e -w -v -R -U --port={port} --offset=0x2000 {bin}",
|
"flash-command": "bossac -i -e -w -v -R -U --port={port} --offset=0x2000 {bin}",
|
||||||
|
"serial-port": ["acm:2341:8057", "acm:2341:0057"],
|
||||||
"flash-1200-bps-reset": "true"
|
"flash-1200-bps-reset": "true"
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,5 +6,6 @@
|
||||||
"-Wl,--defsym=_stack_size=512"
|
"-Wl,--defsym=_stack_size=512"
|
||||||
],
|
],
|
||||||
"flash-command": "avrdude -c arduino -p atmega328p -P {port} -U flash:w:{hex}:i",
|
"flash-command": "avrdude -c arduino -p atmega328p -P {port} -U flash:w:{hex}:i",
|
||||||
|
"serial-port": ["acm:2341:0043", "acm:2341:0001", "acm:2a03:0043", "acm:2341:0243"],
|
||||||
"emulator": ["simavr", "-m", "atmega328p", "-f", "16000000"]
|
"emulator": ["simavr", "-m", "atmega328p", "-f", "16000000"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
"build-tags": ["circuitplay_bluefruit","nrf52840_reset_uf2", "softdevice", "s140v6"],
|
"build-tags": ["circuitplay_bluefruit","nrf52840_reset_uf2", "softdevice", "s140v6"],
|
||||||
"flash-1200-bps-reset": "true",
|
"flash-1200-bps-reset": "true",
|
||||||
"flash-method": "msd",
|
"flash-method": "msd",
|
||||||
|
"serial-port": ["acm:239a:8045", "acm:239a:45"],
|
||||||
"msd-volume-name": "CPLAYBTBOOT",
|
"msd-volume-name": "CPLAYBTBOOT",
|
||||||
"msd-firmware-name": "firmware.uf2",
|
"msd-firmware-name": "firmware.uf2",
|
||||||
"uf2-family-id": "0xADA52840",
|
"uf2-family-id": "0xADA52840",
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
"build-tags": ["circuitplay_express"],
|
"build-tags": ["circuitplay_express"],
|
||||||
"flash-1200-bps-reset": "true",
|
"flash-1200-bps-reset": "true",
|
||||||
"flash-method": "msd",
|
"flash-method": "msd",
|
||||||
|
"serial-port": ["acm:239a:8018", "acm:239a:18"],
|
||||||
"msd-volume-name": "CPLAYBOOT",
|
"msd-volume-name": "CPLAYBOOT",
|
||||||
"msd-firmware-name": "firmware.uf2"
|
"msd-firmware-name": "firmware.uf2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
"build-tags": ["itsybitsy_m4"],
|
"build-tags": ["itsybitsy_m4"],
|
||||||
"flash-1200-bps-reset": "true",
|
"flash-1200-bps-reset": "true",
|
||||||
"flash-method": "msd",
|
"flash-method": "msd",
|
||||||
|
"serial-port": ["acm:239a:802b", "acm:239a:002b"],
|
||||||
"msd-volume-name": "ITSYM4BOOT",
|
"msd-volume-name": "ITSYM4BOOT",
|
||||||
"msd-firmware-name": "firmware.uf2"
|
"msd-firmware-name": "firmware.uf2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
"inherits": ["nrf52840"],
|
"inherits": ["nrf52840"],
|
||||||
"build-tags": ["nano_33_ble", "nrf52840_reset_bossa"],
|
"build-tags": ["nano_33_ble", "nrf52840_reset_bossa"],
|
||||||
"flash-command": "bossac_arduino2 -d -i -e -w -v -R --port={port} {bin}",
|
"flash-command": "bossac_arduino2 -d -i -e -w -v -R --port={port} {bin}",
|
||||||
|
"serial-port": ["acm:2341:805a", "acm:2341:005a"],
|
||||||
"flash-1200-bps-reset": "true",
|
"flash-1200-bps-reset": "true",
|
||||||
"linkerscript": "targets/nano-33-ble.ld"
|
"linkerscript": "targets/nano-33-ble.ld"
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
"build-tags": ["pybadge"],
|
"build-tags": ["pybadge"],
|
||||||
"flash-1200-bps-reset": "true",
|
"flash-1200-bps-reset": "true",
|
||||||
"flash-method": "msd",
|
"flash-method": "msd",
|
||||||
|
"serial-port": ["acm:239a:8033", "acm:239a:33"],
|
||||||
"msd-volume-name": "PYBADGEBOOT",
|
"msd-volume-name": "PYBADGEBOOT",
|
||||||
"msd-firmware-name": "arcade.uf2"
|
"msd-firmware-name": "arcade.uf2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
"build-tags": ["pyportal"],
|
"build-tags": ["pyportal"],
|
||||||
"flash-1200-bps-reset": "true",
|
"flash-1200-bps-reset": "true",
|
||||||
"flash-method": "msd",
|
"flash-method": "msd",
|
||||||
|
"serial-port": ["acm:239a:8035", "acm:239a:35", "acm:239a:8036"],
|
||||||
"msd-volume-name": "PORTALBOOT",
|
"msd-volume-name": "PORTALBOOT",
|
||||||
"msd-firmware-name": "firmware.uf2"
|
"msd-firmware-name": "firmware.uf2"
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче