如何使用AVRDUDE强制重置Arduino Leonardo?

10

我想在Leonardo开发板上自己编译和传输Arduino程序。

使用Arduino官方IDE一切都很顺利,我已经启用了编译和字节码传输的详细模式。

我可以看到每个命令行。
我想理解每一行。

除了最后一步AVRDUDE传输之外,一切都很好。如果我输入完全相同的命令,我会收到一个错误:

.avrdude: butterfly_recv(): programmer is not responding

如果我使用Arduino IDE上传代码,这个错误就不会出现。

我能看到一个区别 - Arduino IDE在AVRDUDE调用之前显示了这一行:

通过串口/dev/cu.usbmodem1431使用1200 bps打开/关闭以强制复位

我如何通过命令行进行复位?

7个回答

13

我在 macOS 上也遇到了同样的问题,然后我想出了以下 Bash 脚本:

# Find the Arduino port
ARDUINO_UPLOAD_PORT="$(find /dev/cu.usbmodem* | head -n 1)"

# Reset the Arduino
stty -f "${ARDUINO_UPLOAD_PORT}" 1200

# Wait for it...
while :; do
  sleep 0.5
  [ -c "${ARDUINO_UPLOAD_PORT}" ] && break
done

# ...upload!
avrdude "${OPTIONS[@]}"

while循环是关键!只要Arduino端口恢复在线状态,它就会继续执行。

这是我为项目Sesame编写的Makefile的一部分。


3
不确定这是一个笔误还是与MacOS有关。在Linux下,stty只有一个-F选项,似乎可以实现上述的-f所应该实现的功能... - NichtJens
确实,在某个时空点上,它们分道扬镳了,macOS工具是BSD,而Linux工具是GNU: 了解你的工具:Linux(GNU)与Mac(BSD)命令行实用程序 - gibatronic
哈!你是对的,FreeBSD的手册页也说了-f:https://www.freebsd.org/cgi/man.cgi?query=stty 无论如何,知道这一点很好,也许在回答中指出这一点会很好。 - NichtJens

6

针对从Windows上传的情况,我制作了一个AVRDUDE的批处理文件包装器。

它使用WMI识别Leonardo COM端口,使用MODE命令将此COM端口重置为1200波特率,识别引导加载程序(COM)端口并调用AVRDUDE。

固件应放置在firmware.hex中,但可以更改为从命令行提供。

代码存储在GitHub仓库中:Arduino Leonardo Uploader

或者以下:

@echo off
setlocal

for /f "tokens=1* delims==" %%I in ('wmic path win32_pnpentity get caption  /format:list ^| find "SparkFun Pro Micro"') do (
    call :resetCOM "%%~J"
)

:continue

:: wmic /format:list strips trailing spaces (at least for path win32_pnpentity)
for /f "tokens=1* delims==" %%I in ('wmic path win32_pnpentity get caption  /format:list ^| find "Arduino Leonardo bootloader"') do (
    call :setCOM "%%~J"
)

:: end main batch
goto :EOF

:resetCOM <WMIC_output_line>
:: sets _COM#=line
setlocal
set "str=%~1"
set "num=%str:*(COM=%"
set "num=%num:)=%"
set port=COM%num%
echo %port%
mode %port%: BAUD=1200 parity=N data=8 stop=1
goto :continue

:setCOM <WMIC_output_line>
:: sets _COM#=line
setlocal
set "str=%~1"
set "num=%str:*(COM=%"
set "num=%num:)=%"
set port=COM%num%
echo %port%
goto :flash

:flash
avrdude -v -C./avrdude.conf -patmega32u4 -cavr109 -P%port% -b57600 -D -V -Uflash:w:./firmware.hex:i

我需要在启动时上传我的草图,我正在使用Arduino UNO,这个解决方案适合我吗? - RobertoFRey

5

我曾经有同样的问题。我已经尝试用一个Python脚本在1200波特率下打开和关闭ACM0端口,正如其他人已经提到的。但这对我没有起作用。接着我收到了一半的建议,尝试切换RTS/DTS,这将触发自动重置。所以最终我找到了解决办法(至少对于我来说),它在Linux Mint 18.2(Sonya)上运行:

#! /usr/bin/python

import sys
import serial

com = serial.Serial(sys.argv[1], 1200)
com.dtr=False
com.close()


python ./reset.py "/dev/ttyACM0"

dmesg 显示如下:

[21850.047120] cdc_acm 1-1:1.0: ttyACM0: USB ACM device
[22093.700327] usb 1-1: USB disconnect, device number 53
[22094.034133] usb 1-1: new full-speed USB device number 54 using xhci_hcd
[22094.175377] usb 1-1: New USB device found, idVendor=2341, idProduct=0036
[22094.175381] usb 1-1: New USB device strings: Mfr=2, Product=1, SerialNumber=0
[22094.175384] usb 1-1: Product: Arduino Leonardo
[22094.175387] usb 1-1: Manufacturer: Arduino LLC
[22094.175964] cdc_acm 1-1:1.0: ttyACM0: USB ACM device

对我来说,这个(加上等待2秒)终于让我的atmeg32u4 itsy bitsy编程成功了。哇! - Joshua Clayton
这似乎也是适用于Arduino Nano Every板的方法。 - Paul

4
在Windows上,在命令提示符中,使用略有不同的批处理文件来确定引导加载程序COM端口。请注意,只有要刷写的Leonardo板应该连接!
@echo off
echo Upgrade procedure starting.
if %1.==. goto error
set hexfile=%1
set comportA=NONE
set comportB=NONE
if not exist %hexfile% goto error
for /f "usebackq" %%B in (`wmic path Win32_SerialPort Where "Caption LIKE '%%Leonardo%%'" Get DeviceID ^| FIND "COM"`) do set comportA=%%B
if %comportA%==NONE goto nodevice
echo COM port for Arduino device is detected as %comportA%.
echo Reset Arduino into bootloader
mode %comportA%: baud=12 > nul
timeout 2 > nul
for /f "usebackq" %%B in (`wmic path Win32_SerialPort Where "Caption LIKE '%%Leonardo%%'" Get DeviceID ^| FIND "COM"`) do set comportB=%%B
if %comportB%==NONE goto nobldevice
echo COM port for Arduino bootloader device is detected as %comportB%.
echo.
echo Starting AVR Downloader/UploaDEr.....
avrdude -pm32u4 -cavr109 -D -P%comportB% -b57600 -Uflash:w:%hexfile%
goto upgradedone
:nodevice
echo No matching module found, you should connect the module you want to upgrade.
goto end
:nobldevice
echo Reset into bootloader failed, please try again...
goto end
:error
Echo Missing parameter or file, you should provide the full filename of an existing .hex file you want to use.
goto end
:upgradedone
echo.
echo Upgrade done!
:end

1

我用这个步骤来配置Nano-every:

  1. 以1200波特率和DTR=ON配置适当的串口。
  2. 关闭DTR。
  3. 使用正确版本的avrdude和avrdude.config烧录设备。我的版本是随着Nano-every设备安装的。 对于其他开发板,可能会有所不同。第1、2行代码重置了串口,我认为这与开发板无关,但请更改串口号!

c:\windows\system32\mode.com com6: baud=1200 dtr=on

c:\windows\system32\mode.com com6: dtr=off

C:\Users\Ludo\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17/bin/avrdude -C C:\Users\Ludo\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17/etc/avrdude.conf -v -p atmega4809 -c jtag2updi -P COM6 -b 115200 -e -D -U flash:w:C:\Users\Ludo\AppData\Local\Temp\arduino_build_385557/sketch_dec11a.ino.hex:i -U fuse2:w:0x01:m -U fuse5:w:0xC9:m -U fuse8:w:0x00:m {upload.extra_files}


0

嗯,你基本上已经自己写出了答案。

你需要以1200波特率打开与Arduino的串行连接,然后关闭连接。Arduino将进入SAM-BA模式,重置自身,并准备好接收新程序。


我不明白Arduino如何通过打开/关闭端口来理解重置。 - Bob5421
2
这里有一些阅读材料供您参考,https://www.arduino.cc/en/main/arduinoBoardLeonardo - 请查看“自动(软件)复位和引导加载程序初始化”部分。 - XerXeX

0

我在使用Arduino Leonardo时也遇到了同样的问题。

我想分享我的解决方案,供那些想要使用Qt解决这个问题的人参考。

以下是一个已经准备好可以复制粘贴的解决方案,同时还考虑了超时问题:


#include <QtCore/QString>
#include <QtCore/QDebug>
#include <QtCore/QThread>
#include <QtCore/QElapsedTimer>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>

bool forceResetComPort(const QString& portName)
{
    QSerialPort serial;
    serial.setPortName(portName);
    serial.setBaudRate(1200);

    qDebug() << "Forcing reset using 1200bps open/close on port ") << portName;
    if(!serial.open(QIODevice::ReadWrite))
        return false;

    // This seems optional
    serial.setDataTerminalReady(false);

    qDebug() << "Waiting for the new upload port...";

    QElapsedTimer timeoutWatcher;
    qint64 timeoutMs = 10000;
    bool isPortPresent = true;

    const auto fetchIsPortPresent = [&]() -> bool
    {
        const auto ports = QSerialPortInfo::availablePorts();
        for(const auto& info : ports)
        {
            if(info.portName() == serial.portName())
                return true;
        }
        return false;
    };

    timeoutWatcher.start();

    // Wait for port to disconnect
    while(isPortPresent && !timeoutWatcher.hasExpired(timeoutMs))
    {
        isPortPresent = fetchIsPortPresent();

        if(isPortPresent)
            QThread::msleep(1);
    }

    serial.close();

    // Wait for port to reconnect
    while(!isPortPresent && !timeoutWatcher.hasExpired(timeoutMs))
    {
        isPortPresent = fetchIsPortPresent();

        if(!isPortPresent)
            QThread::msleep(1);
    }
 
    return !timeoutWatcher.hasExpired(timeoutMs);
}

一切都取决于时间。我真的需要等待串口断开才能关闭串口。

  • 关闭得太快没有任何效果。
  • 关闭得太慢,USB串口会重新连接,但端口名称不同。(例如从/dev/ttyACM0到/dev/ttyACM1)。
  • 在我的情况下,等待端口重新连接非常重要,因为我之后会调用QProcess中的avrdude。

这是dmesg:

[ 8566.623621] cdc_acm 1-8:1.0: failed to set dtr/rts
[ 8566.979697] usb 1-8: new full-speed USB device number 21 using xhci_hcd
[ 8567.133193] usb 1-8: New USB device found, idVendor=2341, idProduct=0036, bcdDevice= 0.01
[ 8567.133197] usb 1-8: New USB device strings: Mfr=2, Product=1, SerialNumber=0
[ 8567.133199] usb 1-8: Product: Arduino Leonardo
[ 8567.133200] usb 1-8: Manufacturer: Arduino LLC
[ 8567.134820] cdc_acm 1-8:1.0: ttyACM0: USB ACM device

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接