STM32CubeIDEでMbedする Ticker編

STM32CubeIDEでMbedする Ticker編

前回までで、Mbedの DigitalOut と Serialクラスを動かすことができました。
今回は Tickerクラスを動かしてみます。

投稿時の開発環境を記しておきます。

PC:Windows10 OS
IDE: STM32CubeIDE Version1.1.0
Configurator: STM32CubeMX Version5.4.0
Board: STM32Nucleo-F401RE

Mbed の Tickerクラスとは?

英語ですけれど こちら を見ていただくと概要がわかると思います。
割り込みで使うインターバルタイマーのように定期的に関数を呼び出すことができます。
関数には特定のオブジェクトのメンバ関数も指定することができます。

コーディングしてみる

Tickerのコードはclock系の初期化処理のSystemClock_Config()より後に書いてください。

/* USER CODE BEGIN 0 */
DigitalOut led(LED1);

void led_ticker();
void led_ticker()
{
	led = !led;
}
/* USER CODE END 0 */
/* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  Ticker tick;
  tick.attach(&led_ticker, 1);

  /* USER CODE END SysInit */

mbed-dev/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F401xE/device/cmsis_nvic.h を以下のように編集します。

#ifndef MBED_CMSIS_NVIC_H
#define MBED_CMSIS_NVIC_H

// STM32F401RE
// CORE: 16 vectors = 64 bytes from 0x00 to 0x3F
// MCU Peripherals: 85 vectors = 340 bytes from 0x40 to ...
// Total: 101 vectors = 404 bytes (0x194) to be reserved in RAM
#define NVIC_NUM_VECTORS        101
#define NVIC_RAM_VECTOR_ADDRESS 0x20000000    // Vectors positioned at start of RAM

#include "cmsis.h"

#ifdef __cplusplus
extern "C" {
#endif

void NVIC_SetVector(IRQn_Type IRQn, uint32_t vector);
uint32_t NVIC_GetVector(IRQn_Type IRQn);

#ifdef __cplusplus
}

#endif
#endif

cmsis_nvic.cを追加、編集する

Project Explorer から

mbed-dev/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F401xE/device を右クリックして、New – File を選択し

cmsis_nvic.c というファイルを作成します。
cmsis_nvic.c を以下のように編集します。

#include "cmsis_nvic.h"

#define NVIC_RAM_VECTOR_ADDRESS   (0x20000000)  // Vectors positioned at start of RAM
#define NVIC_FLASH_VECTOR_ADDRESS (0x08000000)  // Initial vector position in flash

void NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) {
    uint32_t *vectors = (uint32_t *)SCB->VTOR;
    uint32_t i;

    // Copy and switch to dynamic vectors if the first time called
    if (SCB->VTOR == NVIC_FLASH_VECTOR_ADDRESS) {
        uint32_t *old_vectors = vectors;
        vectors = (uint32_t*)NVIC_RAM_VECTOR_ADDRESS;
        for (i = 0; i < NVIC_NUM_VECTORS; i++) {
            vectors[i] = old_vectors[i];
        }
        SCB->VTOR = (uint32_t)NVIC_RAM_VECTOR_ADDRESS;
    }
    vectors[IRQn + NVIC_USER_IRQ_OFFSET] = vector;
}

uint32_t NVIC_GetVector(IRQn_Type IRQn) {
    uint32_t *vectors = (uint32_t*)SCB->VTOR;
    return vectors[IRQn + NVIC_USER_IRQ_OFFSET];
}

NVIC_SetVetor , NVIC_GetVector を実装したので、
Drivers/CMSIS/Include/core_cm4.h の 1632, 1633行目をコメントにします。

1632 // #define NVIC_SetVector __NVIC_SetVector
1633 // #define NVIC_GetVector __NVIC_GetVector

ビルドして実行してみる

ここで一度、ビルドして実行してみます。
うまく動かないはずです。

リンカースクリプトを編集する

Project Explorer で F401mbed… の直下にある STM32F401RETX_FLASH.ld の以下の部分(64行目付近)の RAM の行を以下のように編集します。

/* Memories definition */
MEMORY
{
  RAM	(xrw)	: ORIGIN = 0x20000200,	LENGTH = 96K-512
  FLASH	(rx)	: ORIGIN = 0x8000000,	LENGTH = 512K
}

これがなかなか気がつきませんでした。
どこかでRAMの値が書き換わってしまうことに気がついて、IDEで追いかけて原因を見つけることができました。
IDEさまさま ですね。

もう一度ビルドして実行してみる

ビルドして実行してみましょう。

こちらではLチカが確認できました。
皆さまの環境ではいかがでしたでしょうか?

今回は技術的に難しいところがあったので Ticker が動くようになるまでに時間がかかりました。

それでは、どんなことをやったのか、おさらいしておきます。

Tickerを使うにはタイマー割り込みが必要になり、ハンドラの中身を変更したいのでベクタテーブルを書きかえています。

変更前のNVIC_SetVector()は__NVIC_SetVecotr()が呼ばれるわけですが、以下のコードになっています。

__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector)
{
  uint32_t *vectors = (uint32_t *)SCB->VTOR;
  vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector;
}

ベクタテーブルのIRQn番目のベクタを書き換えるコードになっています。
ところが、これでは書きかわらないのです。

なぜかというとベクタテーブルはフラッシュメモリの領域にあるからです。
RAMと違ってフラッシュメモリは簡単に書き込みができません。

そこで先ほど追加・編集した cmsis_nvic.c のコードを準備しました。
もちろん私自身が書いたコードではなくネットで探してきたものです。

フラッシュメモリーでは単純な書き換えができないので、ベクタテーブルをRAM領域にコピーしてから一部分を書き換えるようにしています。

VTOR がベクタテーブルの先頭番地を指し示すレジスタなのでコピー後に書き換えて下駄を履かせています。

それからRAM領域の先頭にベクタテーブルを配置したので、その部分をRAMとして使わないようにリンカスクリプトを編集しました。

使っているマイコンのベクタ数であるNVIC_NUM_VECTORS が 101 なので16進数で切りの良い値の 128 として アドレス4バイトをかけた 512 バイト分をベクタテーブルとして消費します。RAMの先頭アドレスにはその分の下駄を履かせたというわけです。

RAMを512バイト消費してしまいますがメリットもあります。ベクタテーブルがRAM上にあるので割り込み処理のアクセス速度が速くなります。
RAM 512バイトとの取引なら充分元を取っているでしょう。

クラスのメンバ関数をアタッチしてみる

せっかくですから、もうひとつ attach_us のコード例を紹介しておきます。

/* USER CODE BEGIN 0 */
class LedTicker {
public:
	LedTicker(PinName pin) : led(pin) {
		led = 0;
	}
	void toggle() {
		led = !led;
	}
private:
DigitalOut led;
};
LedTicker l(LED1);
/* USER CODE END 0 */

Ticker t;
t.attach_us(callback(&l, &LedTicker::toggle), 500000.0);	// 500msec

while (1)

クラスのメンバ関数をアタッチする例です。
コンストラクタとattach_us()はSystemClock_Config()より後に書いてください。

ビルドして実行し、無事にLチカが確認できました。

Mbedカテゴリの最新記事