RaspberryPiからSTM32のファームウェア更新をする!

製品が顧客の手元に届いた後に、ファームウェアを手動で更新するのは現実的ではありません。そこで必要になるのが、リモートでのファームウェア更新機能です。これにより、製品リリース後も、製品の信頼性を向上させ、メンテナンスコストを削減することができます。このブログでは、Raspberry Pi を使って STM32 マイコンのファームウェアをリモートで更新する方法を紹介します。

リモートでファームウェア更新できる仕組み

STM32には内臓ブートローダーがあり、それを活用してUART経由でファームウェアを書き換えることができます。STM32の内臓ブートローダーにはBOOT0ピンをHighにして再起動させるとブートローダーモードで起動します。

  1. Raspberry Pi の GPIO から BOOT0 を High にし、リセットをかける
  2. STM32 が ブートローダーモード で起動
  3. stm32flash を使ってファームウェアを UART 経由で書き込み
  4. 書き込みが完了したら BOOT0 を Low に戻し、リセット
  5. STM32 は新しいファームウェアで起動

用意したもの

RaspberryPi

今回ファームウェア更新する側です。

STM32

今回ファームウェア更新される側です。

Best Price Square
¥4,380 (2025/02/11 18:45時点 | Amazon調べ)

ジャンパーピン

RaspberryPiとSTM32のピンをつなぎ合わせる用です。

ピン接続

Raspberry Pi と STM32 間の接続を以下のように行います:

Raspberry Pi GPIOSTM32 ピン
GPIO14 (TXD)RX (USART2)
GPIO15 (RXD)TX (USART2)
GNDGND
GPIO17BOOT0
GPIO27RESET

必要なパッケージのインストール

まず、Raspberry Pi に stm32flash をインストールします。このユーティリティは、STM32 マイコンと通信するための軽量で強力なツールです。

sudo apt update
sudo apt install stm32flash

次に、Python 3 と gpiozero ライブラリがインストールされていることを確認します。

sudo apt install python3 python3-gpiozero

プログラム内容

RaspberryPiからgpioを制御して、STM32をブートローダーモードにし、stm32flashでファームウェアを書き換え、完了したらSTM32をアプリケーションモードに戻し、再起動させています。

import subprocess
import gpiozero
import time
import argparse

def setup_gpio(boot_pin, reset_pin):
    """Initialize GPIO pins"""
    bootPin = gpiozero.DigitalOutputDevice(pin=boot_pin)
    resetPin = gpiozero.DigitalOutputDevice(pin=reset_pin)
    return bootPin, resetPin

def enter_bootloader(bootPin, resetPin):
    """Enter bootloader mode on STM32"""
    print("Entering bootloader mode...")
    bootPin.on()  # Set BOOT0 pin to High
    time.sleep(0.1)  # Wait for BOOT0 pin to stabilize
    resetPin.off()  # Set RESET pin to Low (reset)
    time.sleep(0.2)  # Hold RESET signal
    resetPin.on()  # Set RESET pin to High (release reset)
    time.sleep(1)  # Wait for bootloader mode to initialize
    print("STM32 is now in bootloader mode.")

def wait_for_device(device, timeout=5):
    """Wait until the device is ready to respond"""
    print(f"Waiting for {device} to be ready...")
    for _ in range(timeout):
        try:
            with open(device, "r"):
                print(f"{device} is ready.")
                return True
        except OSError:
            time.sleep(1)
    print(f"Timeout waiting for {device}.")
    return False

def flash_firmware(bin_file, device, baudrate):
    """Flash the firmware and monitor progress"""
    print("Flashing firmware...")
    command = [
        "stm32flash",
        "-w", bin_file,
        "-v",
        "-g", "0x08000000",
        "-b", str(baudrate),
        device
    ]

    # Run `stm32flash` asynchronously
    process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
    success = False

    try:
        while True:
            # Read progress line by line
            output = process.stdout.readline()
            if output:
                print(output.strip())  # Display output in real-time
                if "Done." in output:
                    success = True
                    break  # Exit the loop on success

            # Exit the loop if the process has finished
            if process.poll() is not None:
                break

        # Check for "Done." in output to confirm success
        if success:
            print("Firmware flashed successfully!")
        else:
            stderr = process.stderr.read()
            print(f"Error during firmware flashing: {stderr.strip()}")

    finally:
        process.stdout.close()
        process.stderr.close()

    return success

def run_application(bootPin, resetPin):
    """Run the application mode on STM32"""
    print("Running application...")
    bootPin.off()  # Set BOOT0 pin to Low
    resetPin.off()  # Set RESET pin to Low (reset)
    time.sleep(0.1)
    resetPin.on()  # Set RESET pin to High (release reset)
    print("STM32 is now running the application.")

def main():
    # Parse command-line arguments
    parser = argparse.ArgumentParser(description="STM32 Firmware Update Tool")
    parser.add_argument("--bin", required=True, help="Path to the firmware binary file")
    parser.add_argument("--device", required=True, help="UART device (e.g., /dev/ttyAMA0)")
    parser.add_argument("--baudrate", type=int, default=115200, help="Baud rate for stm32flash (default: 115200)")
    parser.add_argument("--boot-pin", type=int, default=17, help="GPIO pin for BOOT0 (default: 17)")
    parser.add_argument("--reset-pin", type=int, default=27, help="GPIO pin for RESET (default: 27)")
    args = parser.parse_args()

    # Initialize GPIO
    bootPin, resetPin = setup_gpio(args.boot_pin, args.reset_pin)

    try:
        # Switch to bootloader mode
        enter_bootloader(bootPin, resetPin)

        # Check if the device is ready
        if not wait_for_device(args.device):
            print("Device not ready. Aborting.")
            return

        # Flash the firmware
        success = flash_firmware(args.bin, args.device, args.baudrate)

        # Only switch to application mode if the update succeeded
        if success:
            print("Firmware update succeeded.")
            run_application(bootPin, resetPin)
        else:
            print("Firmware update failed.")
    finally:
        # Clean up GPIO resources
        bootPin.close()
        resetPin.close()

if __name__ == "__main__":
    main()

実行結果

上記のプログラムを使用してSTM32のファームウェアを更新します。

進捗状況が返され、100%まで進んでいたらうまくいっています!

$ python3 updater.py --bin test.bin --device /dev/ttyAMA0 --baudrate 115200
Entering bootloader mode...
STM32 is now in bootloader mode.
Waiting for /dev/ttyAMA0 to be ready...
/dev/ttyAMA0 is ready.
Flashing firmware...
stm32flash 0.7

http://stm32flash.sourceforge.net/

Using Parser : Raw BINARY
Size         : 11680
Interface serial_posix: 115200 8E1
Version      : 0x31
Option 1     : 0x00
Option 2     : 0x00
Device ID    : 0x0439 (STM32F301xx/F302x4(6/8)/F318xx)
- RAM        : Up to 16KiB  (6144b reserved by bootloader)
- Flash      : Up to 64KiB (size first sector: 2x2048)
- Option RAM : 16b
- System RAM : 8KiB
Write to memory
Erasing memory

Wrote and verified address 0x08000100 (2.19%)
Wrote and verified address 0x08000200 (4.38%)
Wrote and verified address 0x08000300 (6.58%)
Wrote and verified address 0x08000400 (8.77%)
Wrote and verified address 0x08000500 (10.96%)
Wrote and verified address 0x08000600 (13.15%)
Wrote and verified address 0x08000700 (15.34%)
Wrote and verified address 0x08000800 (17.53%)
Wrote and verified address 0x08000900 (19.73%)
Wrote and verified address 0x08000a00 (21.92%)
Wrote and verified address 0x08000b00 (24.11%)
Wrote and verified address 0x08000c00 (26.30%)
Wrote and verified address 0x08000d00 (28.49%)
Wrote and verified address 0x08000e00 (30.68%)
Wrote and verified address 0x08000f00 (32.88%)
Wrote and verified address 0x08001000 (35.07%)
Wrote and verified address 0x08001100 (37.26%)
Wrote and verified address 0x08001200 (39.45%)
Wrote and verified address 0x08001300 (41.64%)
Wrote and verified address 0x08001400 (43.84%)
Wrote and verified address 0x08001500 (46.03%)
Wrote and verified address 0x08001600 (48.22%)
Wrote and verified address 0x08001700 (50.41%)
Wrote and verified address 0x08001800 (52.60%)
Wrote and verified address 0x08001900 (54.79%)
Wrote and verified address 0x08001a00 (56.99%)
Wrote and verified address 0x08001b00 (59.18%)
Wrote and verified address 0x08001c00 (61.37%)
Wrote and verified address 0x08001d00 (63.56%)
Wrote and verified address 0x08001e00 (65.75%)
Wrote and verified address 0x08001f00 (67.95%)
Wrote and verified address 0x08002000 (70.14%)
Wrote and verified address 0x08002100 (72.33%)
Wrote and verified address 0x08002200 (74.52%)
Wrote and verified address 0x08002300 (76.71%)
Wrote and verified address 0x08002400 (78.90%)
Wrote and verified address 0x08002500 (81.10%)
Wrote and verified address 0x08002600 (83.29%)
Wrote and verified address 0x08002700 (85.48%)
Wrote and verified address 0x08002800 (87.67%)
Wrote and verified address 0x08002900 (89.86%)
Wrote and verified address 0x08002a00 (92.05%)
Wrote and verified address 0x08002b00 (94.25%)
Wrote and verified address 0x08002c00 (96.44%)
Wrote and verified address 0x08002d00 (98.63%)
Wrote and verified address 0x08002da0 (100.00%) Done.
Firmware flashed successfully!
Firmware update succeeded.
Running application...
STM32 is now running the application.

まとめ

この手法を使用すれば、製品が顧客の手元にある状態でもリモートで STM32 のファームウェア更新を安全かつ効率的に行うことができます。この仕組みを活用して、より信頼性の高い製品を提供しましょう!