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

製品が顧客の手元に届いた後に、ファームウェアを手動で更新するのは現実的ではありません。そこで必要になるのが、リモートでのファームウェア更新機能です。これにより、製品リリース後も、製品の信頼性を向上させ、メンテナンスコストを削減することができます。このブログでは、Raspberry Pi を使って STM32 マイコンのファームウェアをリモートで更新する方法を紹介します。
リモートでファームウェア更新できる仕組み
STM32には内臓ブートローダーがあり、それを活用してUART経由でファームウェアを書き換えることができます。STM32の内臓ブートローダーにはBOOT0ピンをHighにして再起動させるとブートローダーモードで起動します。
- Raspberry Pi の GPIO から BOOT0 を High にし、リセットをかける
- STM32 が ブートローダーモード で起動
- stm32flash を使ってファームウェアを UART 経由で書き込み
- 書き込みが完了したら BOOT0 を Low に戻し、リセット
- STM32 は新しいファームウェアで起動
用意したもの
RaspberryPi
今回ファームウェア更新する側です。

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

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

ピン接続
Raspberry Pi と STM32 間の接続を以下のように行います:
Raspberry Pi GPIO | STM32 ピン |
---|---|
GPIO14 (TXD) | RX (USART2) |
GPIO15 (RXD) | TX (USART2) |
GND | GND |
GPIO17 | BOOT0 |
GPIO27 | RESET |
必要なパッケージのインストール
まず、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 のファームウェア更新を安全かつ効率的に行うことができます。この仕組みを活用して、より信頼性の高い製品を提供しましょう!