LoginSignup
13
4

More than 3 years have passed since last update.

STM32 NUCLEOボードにTOPPERS/ASP3をポーティングする

Posted at

はじめに

TOPPERS/ASP3には現在、Nucleo F401RE簡易パッケージとNucleo L552ZE-Q簡易パッケージがあります。
Nucleo F401RE簡易パッケージのtarget/nucleo_f401re_gcc/target_user.txt には (8) 他のNUCLEOボードのサポート という章があり、ここにポーティングの手順が書いてあります。

本パッケージはSTM32FCUBEに含まれているファイルを用いて,デバイス等の初期化を行っているため,他のNUCLEOも容易にサポート可能である.

これを参考にしてNUCLEO F767ZIというボードにポーティングしてみたので、その手順を書いてみました。
他のNUCLEOボードでもTOPPERS使いたい!という方の参考になれば幸いです。

ポーティングしたものは https://github.com/yurie/asp3-f767zi に公開しています。
コミットログはポーティング手順に合わせて書いているので、何をしようとして変更したのかわかると思います。(自分でも、何がしたかったんだ??と後から悩みそうだったので…)
ただし、時々リネームのタイミング等でhistoryをうまく引き継げていないファイルがあったり、途中で間違いに気づいて最終的には別のコミットで修正されている場所もあるのでご注意ください。
もし、コミットログを見て、「意図してたことと、修正内容違うで!」ということに気づかれた方がいらっしゃったらissueにコメントいただけるとありがたいです :bow:

参考資料

本文中で出てくるので先に書いておきます。

Cortex-M7アーキテクチャ用 TOPPERS ASP

ARM Cortex-M7アーキテクチャ・GCC依存部パッケージ に asp_arch_arm_m7_gcc-1.9.5.tar.gz があります。
こちらはSTM32Cubeを使用していないものですが、各種設定値は同じなので。
他にもM4, M0向けがあるので、他のボードへのポーティング時にも参考になりそうです。

STM32CubeF7

GitHubに公開されています。
https://github.com/STMicroelectronics/STM32CubeF7
STM32FCUBE関連はこれを使用しています。

特に、STM32F767ZI-Nucleoのサンプルコードは実装の参考にしました。
USARTに関しては、HALの実装例がなかったので、LLの設定を参考にしています。

ポーティング

変更箇所

arch/arm_m_gcc 以下に不足しているものの追加 と target/nucleo_f401re_gccを複製して必要なところを書き換えるのが主な作業です。

F401REとF767ZIの大きな違い

USARTのポート

F401REはUSART2、F767ZIはUSART3です。
(STM32CubeIDEのGUIで設定できたので使えるかと思ったのですが、ダメっぽい)

NUCLEO-64 vs NUCLEO-144

ピンの数が違います。だから何?という感じですが、後述のようにファイル名に影響してくる場合もあるので注意。
直接関係ないけど、144本の方もArduino Unoとピン互換です。(Arduino Uno用拡張ボードがNUCLEOのピンが余る感じで挿すことができます)

UserLEDの数

F401REは1個ですが、F767ZIには3個あります。せっかくなので3つとも使えるようにしておきました。

Cortex-Mx

Cortex-M4, M0+, M0, M3は対応済みでしたが、M7はありませんでした :cry:

arch以下がポーティングしたいボードに対応されているかどうかでポーティング難易度が変わってきます。

さっそく始めていきましょう。
引用文はtarget_user.txt(8) 他のNUCLEOボードのサポートからの引用です。

STM32Cubeのファイルの取得 & 変更

./target/nucleo_f401re_gcc/stm32fcube に以下のファイルをコピー
\STM32Cube_FW_F4_V1.9.0\Projects\STM32F401RE-Nucleo\Templates\Inc\stm32f4xx_hal_conf.h
\STM32Cube_FW_F4_V1.9.0\Projects\STM32F401RE-Nucleo\Templates\Src\system_stm32f4xx.c
\STM32Cube_FW_F4_V1.9.0\Projects\STM32F411RE-Nucleo\Templates\Src\main.c
\STM32Cube_FW_F4_V1.9.0\Drivers\BSP\STM32F4xx-Nucleo\stm32f4xx_nucleo.h
\STM32Cube_FW_F4_V1.9.0\Drivers\BSP\STM32F4xx-Nucleo\stm32f4xx_nucleo.c

それぞれ次のように変更

F7用STM32Cubeは以下にあります。
https://github.com/STMicroelectronics/STM32CubeF7

target用ディレクトリの作成

./target/(ターゲット名)_gcc/stm32fcube というディレクトリをつくります。
私の場合は、 ./target/nucleo_f767zi_gcc/stm32fcube としました。

stm32f4xx_hal_conf.hの修正

stm32f4xx_hal_conf.h
・必要なHALのコメントアウトを外す

私の場合は、 stm32f7xx_hal_conf.h をそのまま使いました。
(後から何か追加した際に、このコメントアウトを外すのを思い出さなくてハマりそうな気がしたので…)

system_stm32f4xx.c(system_stm32f7xx.c)の修正

system_stm32f4xx.c
・SystemInitの最初にあるCPACRの初期化(FPUの初期化)をコメントアウト

TODO:system_stm32f7xx.cの場所
target/nucleo_f767zi_gcc/stm32fcube/system_stm32f7xx.cのような感じで、同様の修正をしました。
ここは stm32f4xx.cstm32f7xx.cも同じでした。

void SystemInit(void)
{
  /* FPU settings ------------------------------------------------------------*/
  #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
//    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */
  #endif

systemclock_config.cの作成

main.c
・SystemClock_Config()関数のみ残して,ファイル名をsystemclock_config.cに変更.

こちらも同様の修正をしました。
名前は同じですが、F401REとは関数の中身が違うので、F401REからコピーしてくるのはダメ :sweat_smile:

他にも一部変更が必要でした。(変更箇所のみ抜粋)

  /* Enable Power Control clock */  
//  __HAL_RCC_PWR_CLK_ENABLE();
  /* The voltage scaling allows optimizing the power consumption when the device is     
     clocked below the maximum system frequency, to update the voltage scaling value    
     regarding system frequency refer to product datasheet.  */ 
//  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); 

RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS;
RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
RCC_OscInitStruct.PLL.PLLM = 8;

stm32f4xx_nucleo.cの修正

stm32f4xx_nucleo.c
・必要でない機能をコメントアウト

TODO

arch/arm_m_gccにファイルの追加

./arch/arm_m_gcc/stm32f4xx_stm32cube/ 以下に
STM32Cube_FW_F4_V1.9.0\Drivers 以下の必要なファイルをコピー.面倒な場合は全てコピーしてかまわない.

面倒だったので :sweat_smile: ./arch/arm_m_gcc/stm32f7xx_stm32cube/ ディレクトリを作成して、STM32CubeF7/Driversを全コピーしました。

Makefile.targetの修正

./target/nucleo_f401re_gcc/ 以下のファイルを変更

Makefile.target
22行目 : 使用するチップの型番を指定
CDEFS := $(CDEFS) -DSTM32F401xE

./target/nucleo_f767zi_gcc/ の Makefile.targetを修正

CDEFS := $(CDEFS) -DSTM32F767ZITx -DSTM32F767xx

(なぜ2つ書いたのかは失念しました…。思い出したら補足します)

リンカスクリプトの修正

stm32f401re.ld
メモリマップを使用するチップに合わせて変更.
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
SRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 96K
}

stm32f401re.ldstm32f767zi.ld にリネームして上記修正

target/nucleo_f767zi_gcc/stm32f767zi.ld

MEMORY
{
  FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 2048K
  SRAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 512K
}

target_config.hの修正

target_config.h
割込みの最大数を使用するチップに合わせて変更(必ず+16すること).
#define TMAX_INTNO (84 + 16)

#define TMAX_INTNO (109 + 16)

設定値はASPを参考にしました。

クロック周波数を使用するチップに合わせて変更.
#define SYS_CLOCK 84000000

SYS_CLOCKは現在使われていないようです。84000000が設定されているものにCPU_CLOCK_HZがありました。
それっぽかったので、これを変更。

#define CPU_CLOCK_HZ    216000000

target_config.c target_kernel_impl.cの修正

target_config.c
target_initialize() を使用するチップに合わせて初期化処理を追加.
usart_early_init() を使用するチップに合わせて初期化処理を追加.

usart_early_init()target_initialize()target_config.c ではなくて、target/nucleo_f401re_gcc/target_kernel_impl.cにあるようです。

target_initialize()

HALによる初期化をF767ZIにあわせた

__HAL_FLASH_ART_ENABLE();
__HAL_FLASH_PREFETCH_BUFFER_ENABLE();

キャッシュの有効化の設定を追加

  /* Enable I-Cache */
  SCB_EnableICache();

  /* Enable D-Cache */
  SCB_EnableDCache();

LEDは3つついているので、全て使えるようにしました。

    /*
     *  UserLEDの初期化
     */
    BSP_LED_Init(LED1);
    BSP_LED_Init(LED2);
    BSP_LED_Init(LED3);

usart_early_init()

void
usart_early_init()
{
  usart_low_init();

  UartHandle.Instance          = USART_NAME; 
  UartHandle.Init.BaudRate     = BPS_SETTING;
  UartHandle.Init.WordLength   = UART_WORDLENGTH_8B;
  UartHandle.Init.StopBits     = UART_STOPBITS_1;
  UartHandle.Init.Parity       = UART_PARITY_NONE;
  UartHandle.Init.HwFlowCtl    = UART_HWCONTROL_NONE;
  UartHandle.Init.Mode         = UART_MODE_TX_RX;
  UartHandle.Init.OverSampling = UART_OVERSAMPLING_16;
  UartHandle.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  UartHandle.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;

  if(HAL_UART_Init(&UartHandle) != HAL_OK) {
    Error_Handler();
  }

};

USART関連

usart_early_init()内で呼ばれているusart_low_init()も関連して修正する必要があります。
F401REではUSART2を使っています。
F767ZIでは、USART3なので、こちらも変更する必要があります。
GPIOもF401REと異なっていたので、変更しました。

関連してtarget/nucleo_f767zi_gcc/nucleo_f767zi.hも修正します。

target/nucleo_f767zi_gcc/nucleo_f767zi その1

/*
 *  USART関連の定義
 */
#define USART_INTNO (USART3_IRQn + 16)
#define USART_NAME  USART3
#define USART_BASE  USART3_BASE 

target/nucleo_f767zi_gcc/nucleo_f767zi その2

/*
 *  UsartのクロックとIOの初期化
 */
Inline void
usart_low_init(void) {
  GPIO_InitTypeDef  GPIO_InitStruct;

  /* Enable Clock */
  __HAL_RCC_USART3_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  /* UART TX GPIO pin configuration  */
  GPIO_InitStruct.Pin       = GPIO_PIN_8|GPIO_PIN_9;
  GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull      = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF7_USART3;

  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
}

target/nucleo_f767zi_gcc/nucleo_f767zi その3

#ifndef TECSGEN
#include "stm32f7xx_nucleo_144.h"
#else /* !TECSGEN */
#define USART3_BASE  0x40004800U
#define USART3_IRQn  39
#endif /* TECSGEN */
#endif /* TOPPERS_MACRO_ONLY */

USARTの変更は、Syslog周りはTECSで実装されているため、そちらの修正も必要になります。

arch/arm_m_gcc/stm32f7xx_stm32cube/tUsart.c その

/*
 * USARTレジスタ定義
 */
#define USART_CR1(x)    (x + 0x00)
#define USART_CR2(x)    (x + 0x04)
#define USART_CR3(x)    (x + 0x08)
#define USART_BRR(x)    (x + 0x0C)
#define USART_GTPR(x)   (x + 0x10)
#define USART_ISR(x)    (x + 0x1C)
#define USART_ICR(x)    (x + 0x20)
#define USART_RDR(x)    (x + 0x24)
#define USART_TDR(x)    (x + 0x28)

arch/arm_m_gcc/stm32f7xx_stm32cube/tUsart.c その2

/*
 *  受信バッファに文字があるか?
 */
Inline bool_t
usart_getready(CELLCB *p_cellcb)
{
    return (sil_rew_mem((void*)USART_ISR(ATTR_baseAddress)) & USART_FLAG_RXNE) != 0;

}

/*
 *  送信バッファに空きがあるか?
 */
Inline bool_t
usart_putready(CELLCB *p_cellcb)
{
    return (sil_rew_mem((void*)USART_ISR(ATTR_baseAddress)) & USART_FLAG_TXE) != 0;
}

/*
 *  受信した文字の取出し
 */
Inline char
usart_getchar(CELLCB *p_cellcb)
{
    return((char) sil_rew_mem((void*)USART_RDR(ATTR_baseAddress)) & 0xFF);
}

/*
 *  送信する文字の書込み
 */
Inline void
usart_putchar(CELLCB *p_cellcb, char c)
{
    sil_wrw_mem((void*)USART_TDR(ATTR_baseAddress), c);
}

型番の変更

全ファイル
F401RE を,使用するチップの型番にリプレース.

これはそのまま、F401RE -> F767ZI という感じで。
F401RE を,使用するチップの型番にリプレースに対応
F4xxからF7xxへの修正漏れ:sweat_smile:

その他の修正

F767ZIの場合はこれ以外にも変更が必要でした。

Makefile.target

「型番の変更」で察してねということでしょう。
差分はこちらで。

以下、変更箇所の抜粋です。

BOARD = nucleo_f767zi
CHIP  = stm32f7xx_stm32cube

KERNEL_COBJS := $(KERNEL_COBJS) target_kernel_impl.o \
                system_stm32f7xx.o stm32f7xx_nucleo.o systemclock_config.o \
                stm32f7xx_hal_gpio.o stm32f7xx_hal_uart.o \
                stm32f7xx_hal_rcc.o stm32f7xx_hal_dma.o  stm32f7xx_hal_pwr_ex.o stm32f7xx_hal.o

KERNEL_COBJS := $(KERNEL_COBJS) stm32f7xx_hal_tim.o

LDSCRIPT = $(TARGETDIR)/stm32f767zi.ld

144pinボードの罠

stm32f4xxだからstm32f7xxと機械的に変換していると、はまります :sweat_smile:
https://github.com/yurie/asp3-f767zi/blob/a368a1d6a523294d4a181d6bd0eecabf769cfb6d/target/nucleo_f767zi_gcc/nucleo_f767zi.h#L69

#ifndef TECSGEN
#include "stm32f7xx_nucleo_144.h"
#else /* !TECSGEN */

F401REの場合はstm32f4xx_nucleo.hでしたが、F767ZIの場合はstm32f7xx_nucleo_144.hという感じで _144 がついている
ので注意。
そもそも基板の大きさが違う(144ピン)なのですね。

TIMxの設定値

target_timer.c

Tim2Handle.Init.Prescaler = ((CPU_CLOCK_HZ/2)/1000000);
Tim5Handle.Init.Prescaler = ((CPU_CLOCK_HZ/2)/1000000);

Cortex-M7対応

残念ながら未実装でした…。

arch/arm_m_gcc/common/Makefile.core

else ifeq ($(CORE_TYPE),CORTEX_M7)
  ARM_ARCH = ARMV7M
  COPTS := $(COPTS) -mcpu=cortex-m7
  CDEFS := $(CDEFS) -DTOPPERS_CORTEX_M7
  ifeq ($(FPU_ARCH_MACRO),__TARGET_FPU_FPV5_DP)
    FPU_ARCH_OPT   = fpv5-d16
  else
    FPU_ARCH_MACRO = __TARGET_FPU_FPV4_SP
    FPU_ARCH_OPT   = fpv4-sp-d16
  endif
# CORTEX_M7のFPU_ARCH_MACROはASPのターゲット依存部を参考にした

arch/arm_m_gcc/common/core_insn.h

#if defined(TOPPERS_CORTEX_M4) || defined(TOPPERS_CORTEX_M3) || defined(TOPPERS_CORTEX_M0) || defined(TOPPERS_CORTEX_M0PLUS) || defined(TOPPERS_CORTEX_M7)

target/nucleo_f767zi_gcc/Makefile.target

#
#  FPUを使用するか
#
FPU_USAGE = FPU_LAZYSTACKING
FPU_ARCH_MACRO := __TARGET_FPU_FPV5_DP
# FPU_ABI := hard // TODO : ASPにはついていた

実はちょっと困っているのが、FPU_ABIってhard?softfp?どっちなの!?というところです。

arch/arm_m_gcc/common/Makefile.coreの中で

#
#  FPUの設定
#
ifeq ($(FPU_ABI),)
    FPU_ABI = softfp
endif

となっていたのと、ASP3のF401REではnucleo_f401re_gcc/Makefile.targetで設定していなかったので、それを踏襲しています。

さいごに

本記事では、Nucleo F401RE簡易パッケージのtarget/nucleo_f401re_gcc/target_user.txtを参考にポーティングした内容をまとめました。
他のボードに移植してみようかなという方の参考になれば幸いです。
その際、もし、間違っているところ、不明なところなどあれば教えてください。

GitHubのcommitにもあるように、NUCLEO F401RE簡易パッケージを作成された TOPPERSの本田先生 には、最後動かなくて困っていたところを助けていただいただけでなく、途中色々ご助言いただきました。
ありがとうございました!

13
4
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
13
4