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

mrubyファミリ (組み込み向け軽量Ruby)Advent Calendar 2024

Day 2

mruby/c を、Nucleo-L476RG (STM32L476RG) へ移植する

Last updated at Posted at 2024-11-29

しまねソフト研究開発センター(略称 ITOC)にいます、東です。

この記事は、mrubyファミリ (組み込み向け軽量Ruby) Advent Calendar 2024 の2日目に投稿しています。

前回 に続き、mruby/c を、STマイクロエレクトロニクス社製のマイコンボード「Nucleo-L476RG」へ移植を行ったので、その顛末を記事にします。

目標

  • mruby, mruby/c共通ペリフェラルインターフェースガイドラインに従った I/O クラスライブラリを使えるようにする
  • バイトコードのみを書き込む方法(この記事 の方法1)を実現させる

準備するもの

STM32マイコン評価ボード Nucleo-L476RG
(https://www.st.com/ja/evaluation-tools/nucleo-l476rg.html)
開発環境 STM32CubeIDE (ver 1.15.1)
(https://www.st.com/ja/development-tools/stm32cubeide.html)

202411-1_Nucleo-L476RG.jpeg

作業手順

最初からフルスペックで移植を行うのは、かなりハードルが高いので、やはりある程度ステップバイステップで行うのが良いと思います。いくつかの方法を試した結果、以下の方法がベストプラクティスではないかと思います。

  1. 新規にプロジェクトを作る
  2. Nucleo-F401RE用のソースコードを持ってきてマージし、最低限の構成で、まずはビルドを通す
  3. I/O クラスライブラリを、ひとつひとつ移植する
  4. フラッシュメモリへのバイトコード書き込み機能を移植する

余談ですが、先日STマイクロの方より CubeIDEによるマイコンの変更方法には、まず新規プロジェクトを作ってから、そこへ既存プロジェクトをインポートする方法があることを教えて頂きました。実際にやってみたのですが、あたりまえですが非互換部分の修正はそれなりに必要ですし、結果的に今回の用途では、ステップバイステップで手作業によるマージが安全かつ納得感があるという結論に達しました。

1. 新規にプロジェクトを作る

では実際の作業に入ります。
別記事「mruby/cをSTM32マイコンで動かす Chapter01: 環境構築」 に書いた方法と同様の手順でプロジェクトを作ります。

Board Selector をクリックしてボードセレクタタブに変更し、Commercial Part Number 欄に、"L476" と入力すれば NUCLEO-L476RG が検索されますので、ボードリスト欄で選べばOKです。

202411-1_Scan17.41.19.png

プロジェクト名は、任意でかまいません。今回私は、"mrubyc-nucleo-L476RG" とつけました。
Initialize all peripherals whth their default Mode? ダイアログは、Yes をクリックします。

デフォルトデバイスの確認

プロジェクトが作成されましたら、デフォルトでマップされた以下のデバイスを確認します。

  • オンボードLEDのポート番号 = PA5 (前回F401REでも、PA5)
  • オンボードスイッチのポート番号 = PC13 (前回F401REでも、PC13)
  • USBシリアルにマップされたUART番号 = UART2 (前回F401REでも、UART2)

幸いなことに、今回のボード L476RG は、チュートリアルで使ったボード F401RE と同じポート番号、UART番号でした。

2. Nucleo-F401RE用のソースコードを持ってきてマージし、最低限の構成で、まずはビルドを通す

必要最低限のソースコードのマージ

  1. こちら (https://github.com/HirohitoHigashi/mrubyc-nucleo-F401RE/tree/chapter08_mrbwrite) より、ソースコードを zip でダウンロードし、展開します。
  2. 展開したファイルから、Core/mrubyc_src ディレクトリを、新プロジェクトの同じ場所にコピーします。
  3. 新プロジェクトに、Core/mrubyc ディレクトリを作ります。
  4. 展開したファイルの、mrubyc ディレクトリからファイル makefile, mrbc.exe, start_mrubyc.c を、新プロジェクトの Core/mrubyc ディレクトリへコピーします。

202411-1_Scan18.19.05.png

自動生成されたソースコードへ追記

自動生成されたファイル2つへ、以下の通り追記します。

Cure/Src/main.c
  /* USER CODE BEGIN 2 */
  void start_mrubyc(void);
  start_mrubyc();
  /* USER CODE END 2 */
Core/Src/stm32l4xx_it
  /* USER CODE BEGIN SysTick_IRQn 0 */
  void mrbc_tick(void);
  mrbc_tick();
  /* USER CODE END SysTick_IRQn 0 */

ビルドプロパティーの変更

  1. メニューから、Project > Properties を選び、ダイアログを開きます。
  2. ダイアログ左ペインの C/C++ Build > Settings をクリックします。
  3. 右ペインの Configuration: を、All configurations に変更します。
  4. 右ペインの Tool Settings タブをクリックし、MCU Settings をクリックします。
  5. Use float with printf from newlib-nano 欄のチェックをつけます。
    202411-1_Scan18.25.27.png
  6. 右ペインの Build Steps タブをクリックします。
  7. Pre-build steps の Command: 欄へ、以下の通り入力します。
cd ..\\Core\\mrubyc; make

202411-1_Scan18.27.06.png

start_mrubyc.c ファイルの変更

とにかく最初はビルドを通すことを目的として、ビルドエラーになるところをコメントアウトします。

  • stm32f4_uart.h, mrbc_firm.h の #include
// #include "stm32f4_uart.h"
// #include "mrbc_firm.h"
  • check_boot_mode の中を #if 0 で無効化
int check_boot_mode( void )
{
  const int MAX_WAIT_CYCLE = 256;
  int ret = 0;

#if 0
  for( int i = 0; i < MAX_WAIT_CYCLE; i++ ) {
    HAL_GPIO_WritePin( GPIOA, GPIO_PIN_5,
		       ((i>>4) | (i>>1)) & 0x01 );	// Blink LED1
    if( uart_can_read_line( UART_HANDLE_CONSOLE )) {
      uart_clear_rx_buffer( UART_HANDLE_CONSOLE );
      ret = 1;
      break;
    }
    HAL_Delay( 10 );
  }
  HAL_GPIO_WritePin( GPIOA, GPIO_PIN_5, 0 );
#endif

  return ret;
}
  • uart_init()
//  uart_init();
  • 各クラスの初期化
  // 各クラスの初期化
//  void mrbc_init_class_gpio(void);
//  mrbc_init_class_gpio();
//  void mrbc_init_class_uart(void);
//  mrbc_init_class_uart();
//  void mrbc_init_class_adc(void);
//  mrbc_init_class_adc();
//  void mrbc_init_class_pwm(void);
//  mrbc_init_class_pwm();
//  void mrbc_init_class_i2c(void);
//  mrbc_init_class_i2c();
//  void mrbc_init_class_spi(void);
//  mrbc_init_class_spi();
  • タスクの登録のところは、 #if でバイトコード書き込み機能サポート(前半)と、prepared bytecode(後半)が分けてあるので、#if 0 にして後半を有効化
  // タスクの登録
#if 0
  void *task = 0;
  ...

ビルド!

ビルドしてエラー無く通るかを確認します。
なお、最初の1回目は、task1.rb から task1.c を生成するタイミングの問題により必ず失敗しますが、もう一度ビルドすると成功します。

3. I/O クラスライブラリを、ひとつひとつ移植する

ビルドが通ったら、I/Oクラスライブラリの移植を行います。

GPIOクラス

  • ダウンロードしたファイルの、mrubyc ディレクトリからファイル stm32f4_gpio.h, stm32f4_gpio.c を、新プロジェクトの Core/mrubyc ディレクトリへコピーします。
  • 先ほどコメントアウトした start_mrubyc.c ファイルの GPIO クラスの初期化呼び出しを有効にします。
Core/mrubyc/start_mrubyc.c
  // 各クラスの初期化
  void mrbc_init_class_gpio(void);
  mrbc_init_class_gpio();

ビルドすると成功し、エルチカなども正しく動作します。

UARTクラス

まず、UARTユニットが何個使えそうか、データシートや CubeIDE のコンフィグ画面を使って調べます。
以前は、UART1, UART2, UART6の 3つを有効化していました。L476RGでは、UART, UART2 は、同様に使えそうですが、UART6は存在しませんでした。よって今回は、UART1とUART2だけ使えるようにします。もし不足するようでしたら、UART3,4,5があるので、それを有効にした版を作ることもできると思います。

ピンアサインは、以下の通りです。

CN pin SILK GPIO Usage
CN5 1 D8 PA9 UART1_TX
CN9 3 D2 PA10 UART1_RX
CN9 2 TX/D1 PA2 UART2_TX
CN9 1 RX/D0 PA3 UART2_RX

CubeIDE を使って、UART1および2の設定をします。
Project Explorer から、(プロジェクト名).ioc をダブルクリックし、コンフィグレーション画面を開き、Connectivity > USART1 をクリックします。

USART1 Mode and Configuration 画面
202411-1_Scan10.54.28.png

  1. Mode欄を Asynchronous に変更
  2. Parameter Settings タブ > Basic Parameters > Baud Rate を、9600 Bits/s に変更

次にDMAの設定変更です。
202411-1_Scan10.55.39.png

  1. DMA Settingsタブをクリック
  2. [Add] ボタンをクリック
  3. Select欄が表示されるので、USART1_RXに変更
  4. Mode を、Circularに変更

同様に、UART2も設定をします。
設定を保存することで、コードの自動生成が始まります。

次にダウンロードしたファイルの適用を行います。

  • ダウンロードしたファイルの、mrubyc ディレクトリからファイル stm32f4_uart.h, stm32f4_uart.c を、新プロジェクトの Core/mrubyc ディレクトリへコピーします。
  • start_mrubyc.c ファイルからコメントアウトした UART 関連部分を有効にします。
Core/mrubyc/start_mrubyc.c
#include "stm32f4_uart.h"
...
  uart_init();
...
  void mrbc_init_class_uart(void);
  mrbc_init_class_uart();
  • UART6が無いので、コピーした stm32f4_uart.c から、UART6に関連する部分を削除もしくはコメントアウトします。
Core/mrubyc/stm32f4_uart.c
//extern UART_HandleTypeDef huart6;
...
    // UART6
//  &(UART_HANDLE){
//    .unit_num = 6,
//    .delimiter = '\n',
//    .hal_uart = &huart6,
...
//    case 6:
  • HALライブラリで定義してある構造体メンバ名 NDTR が CNDTR に変わっているので、こちらも変更します。
Core/mrubyc/stm32f4_uart.c
static inline int uart_get_wr_pos( const UART_HANDLE *hndl )
{
  return hndl->rxfifo_size - hndl->hal_uart->hdmarx->Instance->CNDTR;
}

以上で終了です。

ADCクラス

このチップはADCユニットを3つ (ADC1, ADC2, ADC3) 持っていますが、ボードのA0〜A5シルクで示されたピンアサインから、ADC1をマルチプレクサで切り替えて使う事が想定されているようです。

ピン割り当て

ボード上のシルク印刷(=ADピン番号)と、GPIOチャネル、A/Dチャネルの対応は、以下の通りになっています。

CN pin SILK GPIO ADC Channel
CN8 1 A0 PA0 1 ADC_CHANNEL_5
CN8 2 A1 PA1 1 ADC_CHANNEL_6
CN8 3 A2 PA4 1 ADC_CHANNEL_9
CN8 4 A3 PB0 1 ADC_CHANNEL_15
CN8 5 A4 PC1 1 ADC_CHANNEL_2
CN8 6 A5 PC0 1 ADC_CHANNEL_1

お手本にしている F401REとは Channel が異なっていますので、その対応をします。

CubeIDE を使って、ADC1の設定をします。

  • ADC1をクリックして選び、上記表の各チャンネルモードプルダウンで、Single-ended を選びます。

202411-1_Scan11.48.16.png

設定を保存することで、コードの自動生成が始まります。

次にダウンロードしたファイルの適用を行います。

  • ダウンロードしたファイルの、mrubyc ディレクトリからファイル stm32f4_adc.c を、新プロジェクトの Core/mrubyc ディレクトリへコピーします
  • 以下の通り、ADC_CHANNEL_* を変更します
Core/mrubyc/stm32f4_uart.c
} const TBL_ADC_CHANNELS[] = {
                            // GPIO  ADC ch.  silk
  {{1, 0}, ADC_CHANNEL_5},	// PA0   5        A0
  {{1, 1}, ADC_CHANNEL_6},	// PA1   6        A1
  {{1, 4}, ADC_CHANNEL_9},	// PA4   9        A2
  {{2, 0}, ADC_CHANNEL_15},	// PB0   15       A3
  {{3, 1}, ADC_CHANNEL_2},	// PC1   2        A4
  {{3, 0}, ADC_CHANNEL_1},	// PC0   1        A5
};
  • start_mrubyc.c ファイルからコメントアウトした ADC 関連部分を有効にします。
Core/mrubyc/start_mrubyc.c
  void mrbc_init_class_adc(void);
  mrbc_init_class_adc();

ここでビルドをしてみますが、いくつかビルドエラーが発生しました。

../Core/mrubyc/stm32f4_adc.c:99:21: error: 'ADC_SAMPLETIME_3CYCLES' undeclared (first use in this function); did you mean 'ADC_SAMPLETIME_2CYCLE_5'?
   99 |     .SamplingTime = ADC_SAMPLETIME_3CYCLES,
      |                     ^~~~~~~~~~~~~~~~~~~~~~
      |                     ADC_SAMPLETIME_2CYCLE_5

どうやら UART の時と同じく、ADC もHALライブラリの仕様がチップ間で多少違うようです。ここはマルチプレクサを変更したいだけなのですが、ADC_ChannelConfTypeDef 構造体をきちんと作らないと駄目のようです。幸い、Core/Src/main.c の MX_ADC1_Init() 初期化関数の中に、初期パラメータの設定があったのでそれをそのまま使います。

Core/mrubyc/stm32f4_uart.c
static uint32_t read_sub(mrbc_vm *vm, mrbc_value v[], int argc)
{
  int idx = *((int *)(v[0].instance->data));

  ADC_ChannelConfTypeDef sConfig = {
    .Channel = TBL_ADC_CHANNELS[idx].channel,
    .Rank = ADC_REGULAR_RANK_1,
    .SamplingTime = ADC_SAMPLETIME_2CYCLES_5,
    .SingleDiff = ADC_SINGLE_ENDED,
    .OffsetNumber = ADC_OFFSET_NONE,
    .Offset = 0,
  };

これでビルドは、通ります。
が、テストしてみると、どうもADCの値が正しく表示されません。
CubeMX が生成した L476 のコードと、F401 のコードを注意深く見比べてみると、GPIO のアナログモード設定に使う定数が、GPIO_MODE_ANALOG ではなく、 GPIO_MODE_ANALOG_ADC_CONTROL でなければならないようです。命名から類推するに、L476では単にアナログにする場合と ADC で使うためにアナログにする場合とでハード的に分けてあるため、ソフトウェアもそれに従う必要があるようですね。

理由がわかったので、そちらも変更します。GPIO_MODE_ANALOG を設定しているのは、stm32f4_gpio.c です。

Core/mrubyc/stm32f4_gpio.c
int gpio_setmode( const PIN_HANDLE *pin, unsigned int mode )
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  GPIO_InitStruct.Pin = TBL_NUM_TO_STM32PIN[pin->num];

  if( mode & (GPIO_IN|GPIO_OUT|GPIO_ANALOG|GPIO_HIGH_Z|GPIO_OPEN_DRAIN) ) {
    if( mode & GPIO_ANALOG ) {
      GPIO_InitStruct.Mode = GPIO_MODE_ANALOG_ADC_CONTROL;

これでビルドしてテストすると、今度は正しく電圧値が取得できました。

PWMクラス

お手本にしている F401RE で用意した PWM チャネル(タイマー番号とチャネル)は、以下の通りです。

CN pin SILK GPIO Usage (PWM)
CN5 5 MISO/D12 PA6 TIM3_CH1
CN5 4 PWM/MOSI/D11 PA7 TIM3_CH2
CN5 3 PWM/CS/D10 PB6 TIM4_CH1
CN5 2 PWM/D9 PC7 TIM3_CH2
CN9 8 D7 PA8 TIM1_CH1
CN9 7 PWM/D6 PB10 TIM2_CH3
CN9 6 PMW/D5 PB4 TIM3_CH1
CN9 5 D4 PB5 TIM3_CH2
CN8 1 A0 PA0 AD0 TIM2_CH1
CN8 2 A1 PA1 AD1 TIM2_CH2
CN8 4 A3 PB0 AD8 TIM3_CH3

一つ一つタイマー番号とチャネルが、L475REではどうか調べます。幸いなことにすべて合致しています。

CubeIDEを使って、タイマーの設定をします。

  • Timersの中から、TIM1,2,3,4 の各チャンネルプルダウンを、 以下の通り PWM Generation No Output に設定します。
TIM Channel
TIM1 1 をPWM Generation No Outputに設定
TIM2 1 2 3 をPWM Generation No Outputに設定
TIM3 1 2 3 をPWM Generation No Outputに設定
TIM4 1 をPWM Generation No Outputに設定

202411-1_Scan14.16.44.png

設定を保存することで、コードの自動生成が始まります。

次にダウンロードしたファイルの適用を行います。

  • ダウンロードしたファイルの、mrubyc ディレクトリからファイル stm32f4_pwm.c を、新プロジェクトの Core/mrubyc ディレクトリへコピーします。
  • start_mrubyc.c ファイルからコメントアウトした PWM 関連部分を有効にします。
Core/mrubyc/start_mrubyc.c
  void mrbc_init_class_pwm(void);
  mrbc_init_class_pwm();

以上で終了です。

I2Cクラス

I2Cは、シルクにあるSCL/D15, SDA/D14を使います。これも手本にしているF401REと同じ割り当てになっています。

CN pin SILK GPIO Usage
CN5 10 SCL/D15 PB8 I2C1_SCL
CN5 9 SDA/D14 PB9 I2C1_SDA

CubeIDEを使って、I2Cの設定をします。
202411-1_Scan14.38.52.png

  1. Connectivity > I2C1 をクリックします
  2. I2C 欄を、I2C に変更します

デフォルトでは、PB6, PB7 にピンアサインされるので、それぞれ PB8, PB9 に変更します。
202411-1_Scan14.39.11.png

設定を保存することで、コードの自動生成が始まります。

次にダウンロードしたファイルの適用を行います。

  • ダウンロードしたファイルの、mrubyc ディレクトリからファイル stm32f4_i2c.c を、新プロジェクトの Core/mrubyc ディレクトリへコピーします。
  • start_mrubyc.c ファイルからコメントアウトした I2C 関連部分を有効にします。
Core/mrubyc/start_mrubyc.c
  void mrbc_init_class_i2c(void);
  mrbc_init_class_i2c();

以上で終了です。

SPIクラス

SPIは、ボード上にシルク SCK,MISO,MOSI の表示がありますが、オンボードデバイスのLEDとピンアサインが被っています。よって、シルク印刷は無視することにして、以下の通り、手本にした F401REと同じアサインで移植することにします。

CN pin SILK GPIO Usage
CN7 1 PC10 SPI3_SCK
CN7 2 PC11 SPI3_MISO
CN7 3 PC12 SPI3_MOSI

CubeIDEを使って、SPIの設定をします。
202411-1_Scan15.01.33.png

  1. Connectivity > SPI3 をクリックします
  2. Mode 欄を、Full-Duples Master に変更します
  3. Data Size を 8 Bits に変更します
  4. Prescaler を 128 に変更します

設定を保存することで、コードの自動生成が始まります。

次にダウンロードしたファイルの適用を行います。

  • ダウンロードしたファイルの、mrubyc ディレクトリからファイル stm32f4_spi.c を、新プロジェクトの Core/mrubyc ディレクトリへコピーします。
  • start_mrubyc.c ファイルからコメントアウトした SPI 関連部分を有効にします。
Core/mrubyc/start_mrubyc.c
  void mrbc_init_class_spi(void);
  mrbc_init_class_spi();

以上で終了です。

ここまでで CubeIDE で設定したピンアサインのスクリーンショットです。
202411-1_Scan12.01.18.png

4. フラッシュメモリへのバイトコード書き込み機能を移植する

最後は、バイトコード書き込み機能の移植です。
リファレンスマニュアル(RM0351) を確認すると、今回ターゲットとしている L476RGと前回の F401RE とは、フラッシュメモリバンクの考え方が違います。
F401REはセクターという単位でメモリが管理されていましたが、L476RGはより一般的と思われる 2KBごとのページで管理されています。

202411-1_Scan18.23.19.png

HALライブラリでは、消去はページ単位で行う事ができます。
書き込み単位は、8バイトです。

MPU 管理単位 消去 書き込み
F401RE セクタ (16〜128KB) セクタ単位 1,2,4 バイトずつ
L476RG ページ (2KB) ページ単位 8バイトずつ

これらを元に、以下のとおり設計しました。

  • サイズは、F401REと同じ 128KBとする
  • アドレス範囲は、0x80E0000 - 80FFFFF
  • ページでいうと、448 〜 551 の 64ページ

リンカファイルの編集

バイトコード用のアドレス範囲をリンカが誤って使用しないように、予約します。

CubeIDEの左ペイン ProjectExplorer から、STM32L476RGTX_FLASH.ld をダブルクリックして開きます
メモリ定義の行を探し、以下の通り書き換えて保存します

STM32L476RGTX_FLASH.ld
/* Memories definition */
MEMORY
{
  RAM     (xrw)    : ORIGIN = 0x20000000,   LENGTH = 96K
  RAM2    (xrw)    : ORIGIN = 0x10000000,   LENGTH = 32K
  FLASH   (rx)     : ORIGIN = 0x8000000,    LENGTH = 896K
  IREP    (r)      : ORIGIN = 0x80E0000,    LENGTH = 128K
}

バイトコード受信・書き込みプログラムの移植

次にダウンロードしたファイルの適用を行います。

ダウンロードしたファイルの、mrubyc ディレクトリからファイル mrbc_firm.h mrbc_firm.h を、新プロジェクトの Core/mrubyc ディレクトリへコピーします。

mrbc_firm.c を、上記の違いを反映して変更します。

アドレス範囲

Core/mrubyc/mrbc_firm.c
const uint32_t IREP_START_ADDR = 0x080E0000;	// 128KB
const uint32_t IREP_END_ADDR   = 0x080FFFFF;	//  (see: cmd_clear function)

clearコマンド

Core/mrubyc/mrbc_firm.c
static int cmd_clear(void)
{
  HAL_FLASH_Unlock();

  FLASH_EraseInitTypeDef erase = {
    .TypeErase = FLASH_TYPEERASE_PAGES,
    .Banks = FLASH_BANK_2,
    .Page = 448,
    .NbPages = 64,
  };
...

writeコマンド

Core/mrubyc/mrbc_firm.c
static int cmd_write( void *buffer, int buffer_size )
{
  char *token = strtok( NULL, WHITE_SPACE );
  if( token == NULL ) {
    STRM_PUTS("-ERR\r\n");
    return -1;
  }

  // check size
  int size = mrbc_atoi(token, 10);
  uint32_t irep_write_end = irep_write_addr_ + size;
  if( (irep_write_end > IREP_END_ADDR) || (size > buffer_size) ) {
    STRM_PUTS("-ERR IREP file size overflow.\r\n");
    return -1;
  }

  STRM_PUTS("+OK Write bytecode.\r\n");

  // get a bytecode.
  uint8_t *p = buffer;
  int n = size;
  while (n > 0) {
    int readed_size = STRM_READ( p, size );
    p += readed_size;
    n -= readed_size;
  }

  // check 'RITE' magick code.
  p = buffer;
  if( strncmp( (const char *)p, RITE, sizeof(RITE)) != 0 ) {
    STRM_PUTS("-ERR No RITE code received.\r\n");
    return -1;
  }

  // Write bytecode to FLASH.
  HAL_FLASH_Unlock();

  size += (-size & 7);		// align 8 byte.
  irep_write_end = irep_write_addr_ + size;

  while( irep_write_addr_ < irep_write_end ) {
    uint64_t data = 0;
    for( int i = 7; i >= 0; i-- ) {
      data <<= 8;
      data |= p[i];
    }

    HAL_StatusTypeDef sts;
    sts = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, irep_write_addr_, data);

    if( sts != HAL_OK ) {
      STRM_PUTS("-ERR Flash write error.\r\n");
      HAL_FLASH_Lock();
      return -1;
    }

    p += 8;
    irep_write_addr_ += 8;
  }
  HAL_FLASH_Lock();

  STRM_PUTS("+DONE\r\n");

  return 0;
}

showprogコマンド

Core/mrubyc/mrbc_firm.c
...
    addr += size + (-size & 7);	// align 8 byte.
...

pickup_task関数

Core/mrubyc/mrbc_firm.c
...
    addr += size + (-size & 7);	// align 8 byte.
...

start_mrubyc.c ファイルを元に戻す

最初にコメントアウトした部分を全て元に戻します。面倒なら、もう一度ダウンロードしたファイルを使って上書きしても良いです。

以上で移植作業はすべて終了です。

おわりに

ファイル全体は、github リポジトリにありますので、そちらをご覧ください。

今回は、Nucleo-F401RE 用に移植した mruby/c の VM、ペリフェラルクラスライブラリ、バイトコード書き込み機能を、Nucleo-L476RG へ移植しました。
同じ ST 社製のマイコンですが、FシリーズからLシリーズへと、シリーズを超えての移植だったため、いくつか引っかかるポイントがありました。
HAL ライブラリを使っていれば、移植作業は簡単になるといううたい文句ですが、アンドキュメンテッドなことも多く、CubeMX で生成されたコードを読み解きながらの作業も必要でした。やはり HALでプロセッサの変更は怖くないといった理想には、なかなか届かないですね。

私のコードにも、何カ所かもう少し工夫をすれば移植が簡単になるポイントが発見できました。たとえば、バイトコード書き込み単位に由来するアドレスアラインが、今はマジックナンバーで数カ所に書いてありますが、マクロ化するなどして変更を1カ所にまとめる方が良さそうです。
色々なマイコンに移植するとこのような改良点が見つかって良いですが、あまりやり過ぎると抽象的になりすぎてしまい、かえって読みづらくなったり、パフォーマンスに問題が発生したりと、バランスが難しいところです。

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