2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

STM32L4A6を使った自作ボードでMicroPythonを動かす

Posted at

始めに

知り合いがSTM32L4A6を使ったボードを自作し、これにMicroPythonを入れて動かして欲しいと頼まれたので、動かすまでにやったことの備忘録です。といっても、ほとんどこちらを参考にさせていただきました。

MicroPythonビルド環境構築

こちらを参考

自作ボードにMicroPythonを対応させる

今回、自作ボード用にMicroPythonに追加したフォルダ、ファイルは以下の通り。

ports/stm32/
  ├ boards/
  │ └ STM32L4a6GDISC/           -> new folder
  │      ├ mpconfigboard.h      -> new file
  │      ├ mpconfigboard.mk     -> new file
  │      ├ pins.csv             -> new file
  │      └ stm32l4xx_hal_conf.h -> new file
  ├ stm32l4a6_af.csv       -> new file
  └ stm32l4a6xg.ld        -> new file

自作ボードの回路図・ピンアサ、チップのデータシート、すでにMicroPythonでサポートされている自分と似たチップのデータを参考にこれらのファイルを作成、編集します。

stm32l4a6xg.ld (リンカスクリプト)

チップのデータシートのMemory mappingを参考に記述します。

memory_mapping.png

今回は、同チップシリーズのSTM32L496のMemory mappingと見比べたところ、同じメモリ配置であったため、すでにMicroPythonプロジェクトに存在するstm32l496xg.ldをそのままコピーしました。

stm32l4a6xg.ld
/*
    GNU linker script for STM32L4A6XG
*/

/* Specify the memory areas */
MEMORY
{
    FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 612K /* sectors 0-305 */
    FLASH_FS (r)    : ORIGIN = 0x08099000, LENGTH = 412K /* sectors 306-511 412 KiB */
    RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 320K /* SRAM1, 256K + SRAM2, 64K */
}

/* produce a link error if there is not this amount of RAM for these sections */
_minimum_stack_size = 2K;
_minimum_heap_size = 16K;

/* Define the stack.  The stack is full descending so begins just above last byte of RAM,
   or bottom of FS cache..  Note that EABI requires the stack to be 8-byte aligned for a call. */

/* RAM extents for the garbage collector */
_ram_start = ORIGIN(RAM);
_ram_end = ORIGIN(RAM) + LENGTH(RAM);

_ram_fs_cache_end = _ram_end;
_ram_fs_cache_start = _ram_fs_cache_end - 2K; /* fs cache = 2K */

_estack = _ram_fs_cache_start - _estack_reserve;
_sstack = _estack - 16K; /* stack = 16K */

_heap_start = _ebss; /* heap starts just after statically allocated memory */
_heap_end = _sstack; /* bss + heap = 302K, tunable by adjusting stack size */

_flash_fs_start = ORIGIN(FLASH_FS);
_flash_fs_end   = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS);

stm32l4a6_af.csv (Alternate Functionsリスト)

チップのデータシートのAlternate functionテーブルとpin definitionsテーブルを参考に記述します。
今回は、同チップシリーズのSTM32L496のデータシートと見比べたところ、テーブルの内容が同一のものであったため、既に存在するstm32l496_af.csvをそのままコピーしました。

pins.csv

自作ボードの回路図を見ながら、各ピンの名前と信号線の対応を記述します。

pin_csv.png

後ほど、MicroPython上からは以下のように使うことができます。

from machine import Pin

cs = Pin("SPI2_NSS", Pin.OUT)
cs.on()

mpconfigboard.h (ボードのコンフィグ)

自作ボードの回路図、他のボードのmpconfigboard.hを参考にしながら記述します。
今回はこのようになりました。

mpconfigboard.h
# define MICROPY_HW_BOARD_NAME "HOGE_BOARD"
# define MICROPY_HW_MCU_NAME "STM32L4A6"

# define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0)
# define MICROPY_HW_ENABLE_RNG (1)
# define MICROPY_HW_ENABLE_RTC (1)
# define MICROPY_HW_ENABLE_TIMER (1)
# define MICROPY_HW_ENABLE_SDCARD (1)

// MSI is used and is 4MHz,
// Resulting core frequency is 80MHz:
# define MICROPY_HW_CLK_PLLM (1)
# define MICROPY_HW_CLK_PLLN (40)
# define MICROPY_HW_CLK_PLLP (RCC_PLLP_DIV7)
# define MICROPY_HW_CLK_PLLR (RCC_PLLR_DIV2)
# define MICROPY_HW_CLK_PLLQ (RCC_PLLQ_DIV2)
# define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_4

// The board has an external 32kHz crystal
# define MICROPY_HW_RTC_USE_LSE (1)

// USART config
# define MICROPY_HW_UART1_TX (pin_A9)
# define MICROPY_HW_UART1_RX (pin_A10)
# define MICROPY_HW_UART2_TX (pin_A2)
# define MICROPY_HW_UART2_RX (pin_A3)
// USART 1 is connected to the virtual com port on the ST-LINK
# define MICROPY_HW_UART_REPL PYB_UART_1
# define MICROPY_HW_UART_REPL_BAUD 115200

// I2C buses
# define MICROPY_HW_I2C1_SCL (pin_B6)
# define MICROPY_HW_I2C1_SDA (pin_B7)
# define MICROPY_HW_I2C2_SCL (pin_B10)
# define MICROPY_HW_I2C2_SDA (pin_B11)

// SPI buses
# define MICROPY_HW_SPI1_NSS (pin_A4)
# define MICROPY_HW_SPI1_SCK (pin_A5)
# define MICROPY_HW_SPI1_MISO (pin_A6)
# define MICROPY_HW_SPI1_MOSI (pin_A7)
# define MICROPY_HW_SPI2_NSS (pin_B12)
# define MICROPY_HW_SPI2_SCK (pin_B13)
# define MICROPY_HW_SPI2_MISO (pin_B14)
# define MICROPY_HW_SPI2_MOSI (pin_B15)

// LED (The orange LED is controlled over MFX)
# define MICROPY_HW_LED1 (pin_B0)
# define MICROPY_HW_LED2 (pin_B1)
# define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin))
# define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin))

// SD card
# define MICROPY_HW_SDCARD_SDMMC (1)
# define MICROPY_HW_SDCARD_CK (pin_C12)
# define MICROPY_HW_SDCARD_CMD (pin_D2)
# define MICROPY_HW_SDCARD_D0 (pin_C8)
# define MICROPY_HW_SDCARD_D1 (pin_C9)
# define MICROPY_HW_SDCARD_D2 (pin_C10)
# define MICROPY_HW_SDCARD_D3 (pin_C11)
# define MICROPY_HW_SDCARD_DETECT_PIN (pin_A8)
# define MICROPY_HW_SDCARD_DETECT_PULL (GPIO_PULLUP)
# define MICROPY_HW_SDCARD_DETECT_PRESENT (GPIO_PIN_RESET)

mpconfigboard.mk

mpconfigboard.mk
MCU_SERIES = l4
CMSIS_MCU = STM32L4A6xx
AF_FILE = boards/stm32l4a6_af.csv
LD_FILES = boards/stm32l4a6xg.ld boards/common_basic.ld
OPENOCD_CONFIG = boards/openocd_stm32l4.cfg

stm32l4xx_hal_conf.h

こちらもSTM32L496用のものをそのまま流用しました。

stm32l4xx_hal_conf.h
/* This file is part of the MicroPython project, http://micropython.org/
 * The MIT License (MIT)
 * Copyright (c) 2019 Damien P. George
 */
# ifndef MICROPY_INCLUDED_STM32L4XX_HAL_CONF_H
# define MICROPY_INCLUDED_STM32L4XX_HAL_CONF_H

# include "boards/stm32l4xx_hal_conf_base.h"

// Oscillator values in Hz
# define HSE_VALUE (8000000)
# define LSE_VALUE (32768)
# define EXTERNAL_SAI1_CLOCK_VALUE (48000)
# define EXTERNAL_SAI2_CLOCK_VALUE (48000)

// Oscillator timeouts in ms
# define HSE_STARTUP_TIMEOUT (100)
# define LSE_STARTUP_TIMEOUT (5000)

# endif // MICROPY_INCLUDED_STM32L4XX_HAL_CONF_H

その他 ports/stm32/adc.c

上記ファイルを追加し、ビルドを実行したところ、adc.cでエラーが発生したので、必要箇所に、

defined(STM32L4A6xx)

を追記しました。

ビルド

ports/stm32ディレクトリに移動し、

$ make BOARD=STM32L4a6GDISC

作成されたbuild-STM32L4a6GDISC/firmware.hexをチップに書き込むと、無事MicroPythonが起動しました。

おまけ

SDカードへのアクセス

SD内のファイルを確認

>>> import os
>>> os.listdir("/sd")
['appl.mot', 'kernel_config.txt', 'System Volume Information', 'hello.py', 'tmp', 'spi.py', 'spi_bmx160.py']
>>>

SD内のPythonスクリプトを実行

>>> exec(open("/sd/hello.py").read())
Hello World
>>>

SPI通信でセンサの情報を取得する

自作ボードにはボッシュ社のBMX160 9軸センサが載っており、チップとはSPIでつながっています。MicroPythonからセンサの情報を取得するスクリプトは以下のようになります。

spi_bmx160.py
from struct import unpack
import time

from machine import SPI, Pin


WRITE_BIT = 0 << 7
READ_BIT = 1 << 7

CHIP_ID_ADDRESS = 0x00
MAG_CONF_ADDRESS = 0x44
MAG_IF_0_ADDRESS = 0x4C
MAG_IF_1_ADDRESS = 0x4D
MAG_IF_2_ADDRESS = 0x4E
MAG_IF_3_ADDRESS = 0x4F
CMD_REG_ADDRESS = 0x7E

DATA_START_ADDRESS = 0x04
DATA_END_ADDRESS = 0x1A
DATA_NUM = DATA_END_ADDRESS - DATA_START_ADDRESS + 1

spi = SPI(2)
cs = Pin("SPI2_NSS", Pin.OUT)
cs.on()
time.sleep(0.5)


def write_spi(reg_address: int, write_data: int) -> None:
    send_data = WRITE_BIT | reg_address
    buf = bytearray([send_data, write_data])
    print(f"Write: {buf}")
    cs.off()
    spi.write(buf)
    cs.on()
    time.sleep(0.1)


def read_spi_1byte(reg_address: int) -> None:
    send_data = READ_BIT | reg_address
    print(f"Send: 0x{send_data:x}")
    cs.off()
    recv = spi.read(2, send_data)
    cs.on()
    print(f"Recv: 0x{recv[1]:x}\n")


def read_spi_multiple(start_address: int, nbytes: int) -> bytearray:
    write_data = READ_BIT | start_address
    cs.off()
    read_buf = spi.read(nbytes + 1, write_data)
    cs.on()
    return read_buf[1:]


def convert_mag_data(mag_data_byte: bytearray) -> float:
    mag_data_raw = unpack("<h", mag_data_byte)[0]
    return mag_data_raw / 16.0


def convert_gyro_data(gyro_data_byte: bytearray) -> float:
    gyro_data_raw = unpack("<h", gyro_data_byte)[0]
    return gyro_data_raw * 61.0 / 1000


def convert_acc_data(acc_data_byte: bytearray) -> float:
    acc_data_raw = unpack("<h", acc_data_byte)[0]
    return acc_data_raw * 2 / 0x7FFF


def convert_sensor_time(sensor_time_byte: bytearray) -> float:
    sensor_time_raw = int.from_bytes(sensor_time_byte, "little")
    return sensor_time_raw * 39 / 1000000


print("CHIP ID")
read_spi_1byte(CHIP_ID_ADDRESS)

write_spi(CMD_REG_ADDRESS, 0x11)
write_spi(CMD_REG_ADDRESS, 0x15)
write_spi(CMD_REG_ADDRESS, 0x19)

write_spi(MAG_IF_0_ADDRESS, 0x80)
write_spi(MAG_IF_3_ADDRESS, 0x01)
write_spi(MAG_IF_2_ADDRESS, 0x4B)
write_spi(MAG_IF_3_ADDRESS, 0x01)
write_spi(MAG_IF_2_ADDRESS, 0x51)
write_spi(MAG_IF_3_ADDRESS, 0x0E)
write_spi(MAG_IF_2_ADDRESS, 0x52)
write_spi(MAG_IF_3_ADDRESS, 0x02)
write_spi(MAG_IF_2_ADDRESS, 0x4C)
write_spi(MAG_IF_1_ADDRESS, 0x42)
write_spi(MAG_CONF_ADDRESS, 0x05)
write_spi(MAG_IF_0_ADDRESS, 0x00)
write_spi(CMD_REG_ADDRESS, 0x1A)


while True:
    data_bytearray = read_spi_multiple(DATA_START_ADDRESS, DATA_NUM)

    mag_x = convert_mag_data(data_bytearray[0:2])
    mag_y = convert_mag_data(data_bytearray[2:4])
    mag_z = convert_mag_data(data_bytearray[4:6])
    gyro_x = convert_gyro_data(data_bytearray[8:10])
    gyro_y = convert_gyro_data(data_bytearray[10:12])
    gyro_z = convert_gyro_data(data_bytearray[12:14])
    acc_x = convert_acc_data(data_bytearray[14:16])
    acc_y = convert_acc_data(data_bytearray[16:18])
    acc_z = convert_acc_data(data_bytearray[18:20])
    sensor_time = convert_sensor_time(data_bytearray[20:23])

    print(f"Mag X: {mag_x:.2f}, Mag Y: {mag_y:.2f}, Mag Z: {mag_z:.2f} [μT]")
    print(
        f"Gyro X: {gyro_x:.2f}, Gyro Y: {gyro_y:.2f}, Gyro Z: {gyro_z:.2f} [deg/s]"
    )
    print(f"Acc X: {acc_x:.2f}, Acc Y: {acc_y:.2f}, Acc Z: {acc_z:.2f} [g]")
    print(f"Sensor time: {sensor_time:.3f} [sec]")
    print(data_bytearray)
    print("")

    time.sleep(1)

実行すると1秒間隔でセンサの情報を取ってくれました。

Mag X: 29.56, Mag Y: -4.94, Mag Z: -4.19 [μT]
Gyro X: -0.12, Gyro Y: 0.18, Gyro Z: 0.06 [deg/s]
Acc X: 0.99, Acc Y: -0.02, Acc Z: 0.05 [g]
Sensor time: 156.778 [sec]
b'\xd9\x01\xb1\xff\xbd\xff\rZ\xfe\xff\x03\x00\x01\x00)?\xc5\xfe\x03\x03\xecV='

Mag X: 30.56, Mag Y: -5.94, Mag Z: -3.81 [μT]
Gyro X: -0.06, Gyro Y: 0.18, Gyro Z: 0.18 [deg/s]
Acc X: 0.99, Acc Y: -0.02, Acc Z: 0.05 [g]
Sensor time: 157.816 [sec]
b'\xe9\x01\xa1\xff\xc3\xff\rZ\xff\xff\x03\x00\x03\x00$?\xc4\xfe\xe5\x02\xe7\xbe='

参考URL

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?