はじめに
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にコメントいただけるとありがたいです
参考資料
本文中で出てくるので先に書いておきます。
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はありませんでした
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.c
も stm32f7xx.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からコピーしてくるのはダメ 。
他にも一部変更が必要でした。(変更箇所のみ抜粋)
/* 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 以下の必要なファイルをコピー.面倒な場合は全てコピーしてかまわない.
面倒だったので ./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.ld
を stm32f767zi.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_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 その
1
/*
* 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への修正漏れ
その他の修正
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
と機械的に変換していると、はまります 。
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の設定値
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の本田先生 には、最後動かなくて困っていたところを助けていただいただけでなく、途中色々ご助言いただきました。
ありがとうございました!