ページ更新: 2016-11-04 (金) (347日前)

(2014-05-08 新規作成)

mbed についてのメモ。主に STM32 Nucleo を使う。

目次

[編集]

情報源 #

[編集]

mbed.org, mbed.com #

mbed OS:

mbed.org forum

mbed.org users

[編集]

#

[編集]

NXP #

[編集]

mbed LPC1114FN28 (2016-10-29) #

  • mbed LPC1114FN28 | mbed
    • 信号線, LPC1114FN28チップのデータシートへのリンク
    • dp14, dp28 に LED1, LED2 という別名が付けられているが, LEDは基板には実装されていないことに注意。
[編集]

mbed LPC1768 #

[編集]

STM32 Nucleo #

  • 2014-05-02 mbed祭り2014@春の大阪プレゼン資料 | mbed
    • 「STM32-NUCLEO向けオフライン作業ひととおりやってみました」, 全18ページ
      • 設定を調整してmbed SDKライブラリをビルドした事例
      • 開発環境エクスポート形式の追加など、STM32-NUCLEOをmbedのプラットフォームとして使ってもらうためのオフライン作業結果
[編集]

Nucleo F446RE #

(2015-08-09)

こんなのが増えてた。

2015-08-09 時点では、日本国内では見当たらなかった。

[編集]

Nucleo F411RE #

[編集]

Nucleo F401RE #

related:

[編集]

Nucleo L152RE #

[編集]

RTX #

[編集]

mbos: A Real-Time Operating System for mbed (2015-11-17) #

メモ:

プリエンプティブ・マルチタスク RTOSである。

  • 割り込み機構を使って現在実行中のタスクを中断させ、スケジューラを呼び出して次に実行すべきタスクを決定する。

機能は最小限。タスク生成、リソースの排他、イベント、タイマーイベント 機能だけである。

  • (mbos | mbed より)
  • OS起動
    • mbosクラス, Startメンバ関数
  • タスク
    • Task: CreateTask, GetTask
    • Priority: SetPriority, GetPriority
  • イベント、タイマー
    • Event: WaitEvent, SetEvent, GetEvent
    • Timer: CreateTimer, SetTimer, RedirectTimer, ClearTimer
  • 排他処理
    • CreateResource, LockResource, TestResource, FreeResource

OSEK OS (たとえば OSEK/VDX(Ver2.1) API Service Specifications) よりも機能が少ない。

[編集]

CAN通信 #

MCP2551, MCP2515 を入手したので、そのうち試すかも

[編集]

ScmRTOS (2016-01-31) #

[編集]

メモ #

[編集]

mbed LPC1114FN28 買った (2016-10-29) #

  1. mbed LPC1114FN28 のファームウェアを更新した。
  2. Windows 用シリアルドライバを更新した。
  3. 動作確認のために、LED 2つ、CRD (定電流ダイオード) を外付け。 (GPIOの定格電流を調べるべきだけど、10〜15mA のCRDがあったので、繋いじゃった)

LPC1114FN28_blinky-001-6.jpg

#include "mbed.h"

DigitalOut led1(LED1);
DigitalOut led2(LED2);

int main() {
    for (;;) {
        led1 = 1;
        led2 = 0;
        wait(0.2);
        
        led1 = 0;
        led2 = 1;
        wait(0.2);
    }
}
[編集]

mbos (2015-11-17) #

勉強のために、mbos (#mbos) を調べ始めた。 (作業中)

[編集]

CreaeTimer のサンプルコードの引数が間違っている (2015-12-23) #

mbos.h で宣言された CreateTimer メンバ関数のプロトタイプ宣言の引数は timerid, taskid, event の順である。

    /** Create a mbos timer. Allocates and initialises the data structures for the timer.
     *
     * @param timerid Unique ID for the timer (0 .. ntimers - 1). 
     * @param taskid The ID of the task to which the timer will post events. May not be 
     * the idle task.
     * @param event The event flag(s) that the timer should post on timeout. May not be NULL.
     */
    void CreateTimer(uint timerid, uint taskid, uint event);

mbos.cpp で定義された CreateTimer メンバ関数の引数も、id (timerid), task (taskid), event の順であり、mbos.h のプロトタイプ宣言と一致する:

// Create Timer Function  --------------------------------------------------------------
void mbos::CreateTimer(uint id, uint task, uint event)
{
    // check bounds 
    if(id >= _numtimers) error("mbos::CreateTimer - %i is an invalid timer id\n", id); 
    if(task < 1|| task >= _numtasks) error("mbos::CreateTimer - %i is an invalid task id for Timer %i\n", task, id); 
    if(event == 0) error("mbos::CreateTimer - Can't use null event for Timer %i\n", id); 
  
    // fill tcb 
    _timers[id].timer = 0;
    _timers[id].reload = 0;
    _timers[id].task = task;
    _timers[id].event = event;
}

しかし、mbos.h に書かれているサンプルコードでは、CreateTimer の引数は timerid, event, taskid の順である。

 * int main(void)
 * {
 *     // Configure tasks and timers
 *     os.CreateTask(TASK1_ID, TASK1_PRIO, TASK1_STACK_SZ, task1);
 *     os.CreateTask(TASK2_ID, TASK2_PRIO, TASK2_STACK_SZ, task2);
 *     os.CreateTimer(TIMER0_ID, TIMER0_EVENT, TASK1_ID);  // !!!
 *     // Start mbos
 *     os.Start();
 *     // never  return!
 * }

つまり、サンプルコードが間違っているはず。

[編集]

mbos メモ #

  • 依存部分はmbedライブラリが吸収していて、LPCに依存してない。手持ちの mbed STM32 Nucleo の Cortex-M3,-M4,-M7 で動くのでは。
    • Nucleo には LED2 が存在しないので、サンプルコードは修正するか、LED2を自分で用意する。
    • Cortex-M4(M4F) だと、たぶんタスク切り替え時にVFPとNEONのレジスタの退避と復帰が必要だと思うのだが。
    • Cortex-M7 だと、さらに、キャッシュ対策が必要かも。(メモリバリアとか)。アセンブラの生成コード見て確認すべき。
  • mbos クラス
    • メンバ変数は存在しない。必要な変数は mbos.cpp で定義している。
      • mbos_asm.s から参照されるので、static変数やstaticメンバに出来ないのだろう。
    • OSの呼び出しは全て、このクラスのメンバー関数を呼ぶだけ。
      • メンバ変数が存在しないので、thisポインタが不要だだと思うので、staticメンバ関数にしても良さそうに見える。
    • _swap を呼ぶ(PendSVを呼ぶ?)のは、Systick_handler (1ms周期に設定したシステムタイマーで起動), WaitEvent, SetEvent の3つ。
      • Systick_handler でタスクを切り替えるので、preemptive multitask。
    • リソースの排他は割り込み禁止中に _resource[id].lock メンバを変更して実現している。
      • データキャッシュがあるCPUだとまずいかも。Cortex-M7 にはキャッシュがあった気がする。LDREX/STREX命令とか、メモリバリアを使うようにすれば良いのかな?
  • Systick_Handler, PendSV_Handler, SVC_Handler が mbos で定義されている。mbedの既存の割り込みハンドラは .weak なので、mbosは「extern "C"」な同名のエントリを用意して差し替えている。
  • Systick_handler
    • 1000us (1ms) 周期で割り込んでいる。
    • タイマーを検索して、イベントの発生条件が合致したタスクのeventlistとpriostateを修正し、_swap を呼ぶ。
    • _swap は ICSR (Interrupt Control and State Register) の bit 28 PENDSVSET (SysTick exception set-pending bit) を1にする。(1 = changes SysTick exception state to pending.)。これで、たぶん Systick_Handler が完了した直後に PendSV_Handler が実行される。
  • PendSV_Hanlder
    • タスク切り替え
    • mbos の PendSv_Handler はアセンブラで記述されている。レジスタを差し替えるからだと思う。性能も必要だし。
    • PendSV_Hander の中でスタック消費量を確認している。Abstract_3550-Levido-Second.pdf にもスタックオーバーフローをチェックしている、と記載され ている。
  • SVC_Handler
    • 呼び出し個所は1個所。_startos で svc 0 を実行。最初のタスクを開始するようだ。

情報源 (mbos 以外):

[編集]

mbos のサンプルコードを動かす - NXP mbed LPC1768 #

mbos.h に記載されているサンプルコードを動かしてみた。 処理内容は、タイマー1つとタスクを2つ用意して、OSを起動し、以下の処理を行う。

  • task1: タイマーを起動し、タイマーのイベントを待ち、led1を反転し、task2にイベントを送る。
  • task2: task1 からのイベントを待って、led2を100ms間点灯する。

動作に問題なし。

1. https://developer.mbed.org/compiler/ を開き、(1)空のプロジェクトを作り、(2) ライブラリとして mbos と mbed をインポートする。

mbos-001.png

mbos-002.png mbos-003.png mbos-004.png mbos-005.png

2. main.cpp を作って、mbos のマニュアルからサンプルコードをコピーして、コンパイルして、mbed LPC1768 に書き込む。

mbos-006.png mbos-007.png mbos-008.png mbos-009.png

mbos-010.png

mbos-011.png

3. マニュアルに記載されているサンプルコードは、mbed LPC1768 のLED1, LED2を以下のように制御している。

mbos-LPC-sample.MOV?dl=0

mbos-LPC-sample.png

#include <mbed.h>
#include <mbos.h>

#define TASK1_ID                1       // Id for task 1 (idle task is 0)
#define TASK1_PRIO              50      // priority for task 1
#define TASK1_STACK_SZ          32      // stack size for task 1 in words

#define TASK2_ID                2       // Id for task 2
#define TASK2_PRIO              60      // priority for task 2
#define TASK2_STACK_SZ          32      // stack size for task 2 in words

#define TIMER0_ID               0       // Id for timer 0
#define TIMER0_PERIOD           1000    // Time period in milliseconds
#define TIMER0_EVENT            1       // Event flag (1 << 0)

#define T1_TO_T2_EVENT          2       // Event flag (1 << 1)

#define N_TASKS                 2
#define N_TIMERS                1

void task1(void);                       // task function prototypes
void task2(void);

DigitalOut led1(LED1);  // mbed GPIO-out. 0=Low, 1=High
DigitalOut led2(LED2);  // mbed GPIO-out. 0=Low, 1=High

mbos os(N_TASKS, N_TIMERS);            // Instantiate mbos with 2 tasks & 1 timer

/**
 * mbosのタスクとタイマーを作成して、mbosを起動する。
 */
int main(void)
{
    // Configure tasks and timers
    os.CreateTask(TASK1_ID, TASK1_PRIO, TASK1_STACK_SZ, task1);
    os.CreateTask(TASK2_ID, TASK2_PRIO, TASK2_STACK_SZ, task2);
    os.CreateTimer(TIMER0_ID, TIMER0_EVENT, TASK1_ID);

    // Start mbos
    os.Start();

    // never  return!
}

/**
 * タイマーを起動し、タイマーのイベントを待ち、led1を反転し、task2にイベントを送る。
 */
void task1(void)
{
    os.SetTimer(TIMER0_ID, TIMER0_PERIOD, TIMER0_PERIOD);
    for (;;) {
        os.WaitEvent(TIMER0_EVENT);
        led1 = !led1;
        os.SetEvent(T1_TO_T2_EVENT, TASK2_ID);
    }
}

/**
 * task1 からのイベントを待って、led2を100ms間点灯する。
 */
void task2(void)
{
    for (;;) {
        os.WaitEvent(T1_TO_T2_EVENT);
        led2 = 1;
        wait_ms(100);
        led2 = 0;
    }
}
[編集]

サンプルコードを動かす, STM32 Nucleo (2015-11-18) #

STM32 Nucleo-L152RE (Cortex-M3 24MHz) や F411RE (Cortex-M4F 96MHz) で動かしてみた。

  • LED2 が存在しないので、同名のvolatile変数を用意して動かした。
  • os.WaitEvent(..) から返ってきたときに、シリアルポートにタイムスタンプを表示するようにした。
    • Stackoverflowが出たので、スタックサイズを128wordに増やした。
  • Cortex-M4F でもmbosを変えなくても動作している。VFP/NEONのレジスタが保存されていないか、あるいはタスク切り替えがすでにVFP/NEONに対応していたのか?
#include <mbed.h>
#include <mbos.h>

#define TASK1_ID                1       // Id for task 1 (idle task is 0)
#define TASK1_PRIO              50      // priority for task 1
#define TASK1_STACK_SZ          128     // stack size for task 1 in words

#define TASK2_ID                2       // Id for task 2
#define TASK2_PRIO              60      // priority for task 2
#define TASK2_STACK_SZ          128     // stack size for task 2 in words

#define TIMER0_ID               0       // Id for timer 0
#define TIMER0_PERIOD           1000    // Time period in milliseconds
#define TIMER0_EVENT            1       // Event flag (1 << 0)

#define T1_TO_T2_EVENT          2       // Event flag (1 << 1)

#define N_TASKS                 2
#define N_TIMERS                1

void task1(void);                       // task function prototypes
void task2(void);

Serial pc(USBTX, USBRX);
Timer timer;

DigitalOut led1(LED1);
//DigitalOut led2(LED2);
volatile uint32_t led2 = 0;  // DigitalOut led2の替わり

mbos os(N_TASKS, N_TIMERS);            // Instantiate mbos with 2 tasks & 1 timer

/**
 * mbosのタスクとタイマーを作成して、mbosを起動する。
 */
int main(void)
{
    pc.baud(115200);
    pc.printf("Nucleo_try_mbos\r\n");
    pc.printf("CPU SystemCoreClock is %.2f MHz\r\n", (float)SystemCoreClock/1000.0f/1000.0f);
    timer.start();

    // Configure tasks and timers
    os.CreateTask(TASK1_ID, TASK1_PRIO, TASK1_STACK_SZ, task1);
    os.CreateTask(TASK2_ID, TASK2_PRIO, TASK2_STACK_SZ, task2);
    os.CreateTimer(TIMER0_ID, TIMER0_EVENT, TASK1_ID);

    // Start mbos
    os.Start();

    // never  return!
}

/**
 * タイマーを起動し、タイマーのイベントを待ち、led1を反転し、task2にイベントを送る。
 */
void task1(void)
{
    os.SetTimer(TIMER0_ID, TIMER0_PERIOD, TIMER0_PERIOD);
    for (;;) {
        os.WaitEvent(TIMER0_EVENT);
        pc.printf("[%f] task1\r\n", timer.read());
        led1 = !led1;
        os.SetEvent(T1_TO_T2_EVENT, TASK2_ID);
    }
}

/**
 * task1 からのイベントを待って、led2を100ms間点灯する。
 */
void task2(void)
{
    for (;;) {
        os.WaitEvent(T1_TO_T2_EVENT);
        pc.printf("[%f] task2\r\n", timer.read());
        led2 = 1;
        wait_ms(100);
        led2 = 0;
    }
}

Nucleo-L152RE (Cortex-M4F 96MHz) の結果

TASK1_STACK_SZ = 32, TASK2_STACK_SZ = 32 ★スタックサイズ =32*4 = 128byte

Nucleo_try_mbos
CPU SystemCoreClock is 24.00 MHz
[1.0Stack Overflow on Task 134218288  ★スタックオーバーフロー

TASK1_STACK_SZ = 64, TASK2_STACK_SZ = 64 ★スタックサイズ =64*4 = 256byte

Nucleo_try_mbos
CPU SystemCoreClock is 24.00 MHz
[1.0Stack Overflow on Task   ★スタックオーバーフロー

TASK1_STACK_SZ = 128, TASK2_STACK_SZ = 128 ★スタックサイズ =128*4 = 512byte

Nucleo_try_mbos
CPU SystemCoreClock is 24.00 MHz
[1.000037] task1
[1.002180] task2    ★ イベント送信 task1 -> task2, タスク切り替えで 2.180ms〜2.200ms
[2.000037] task1    ★ 1.000000 秒間隔。かなり精度が良い。
[2.002184] task2
[3.000037] task1
[3.002196] task2
[4.000037] task1
[4.002182] task2
[5.000037] task1
[5.002194] task2
[6.000037] task1
[6.002197] task2
[7.000037] task1
[7.002200] task2
[8.000037] task1

Nucleo-F411RE (Cortex-M4F 96MHz) の結果

TASK1_STACK_SZ = 128, TASK2_STACK_SZ = 128 ★スタックサイズ =128*4 = 512byte

Nucleo_try_mbos
CPU SystemCoreClock is 96.00 MHz
[1.000009] task1
[1.001472] task2    ★ イベント送信 task1 -> task2, タスク切り替えで 1.472ms〜1.482ms
[2.000009] task1    ★ 1.000000 秒間隔。かなり精度が良い。
[2.001473] task2
[3.000009] task1
[3.001482] task2
[4.000009] task1
[4.001474] task2
[5.000009] task1
[5.001475] task2
[6.000009] task1
[6.001476] task2
[7.000009] task1
[7.001476] task2
[8.000009] task1

Nucleo-F401RE (Cortex-M4F 84MHz) の結果

TASK1_STACK_SZ = 128, TASK2_STACK_SZ = 128 ★スタックサイズ =128*4 = 512byte

Nucleo_try_mbos
CPU SystemCoreClock is 84.00 MHz
[1.000010] task1
[1.001497] task2    ★ イベント送信 task1 -> task2, タスク切り替えで 1.497msから1.508ms
[2.000010] task1    ★ 1.000000 秒間隔。かなり精度が良い。
[2.001500] task2
[3.000010] task1
[3.001504] task2
[4.000010] task1
[4.001498] task2
[5.000010] task1
[5.001502] task2
[6.000010] task1
[6.001505] task2
[7.000010] task1
[7.001508] task2
[8.000010] task1
[8.001503] task2
[9.000010] task1
[編集]

DWT CYCCNT (2015-04-26) #

ARM Cortex-M の Data Watchpoint and Trace Unit (DWT) の Cycle counter (CYCCNT) を使って、実行時間を計測してみる。 このカウンタの分解能は、CPUクロックと同じ。

サンプルコード:

#include <mbed.h>

#define DWT_CONTROL ((volatile uint32_t *)0xE0001000)
#define DWT_CYCCNT  ((volatile uint32_t *)0xE0001004)
#define SCB_DEMCR   ((volatile uint32_t *)0xE000EDFC)

static inline uint32_t getDwtCyccnt(void)
{
    return *DWT_CYCCNT;
}

static inline void resetDwtCyccnt()
{
    *DWT_CYCCNT = 0; // reset the counter
}

static inline void enableDwtCyccnt()
{
    *SCB_DEMCR = *SCB_DEMCR | (1u << 24); // TRCENA = 1
    *DWT_CONTROL = *DWT_CONTROL | 1 ; // enable the counter (CYCCNTENA = 1)
    *DWT_CYCCNT = 0; // reset the counter
}

Serial pc(USBTX, USBRX);

// 動作確認 - オーバーヘッドの確認
static void check_overhead() {
    uint32_t count[11];
    enableDwtCyccnt();
    resetDwtCyccnt();
    count[0] = getDwtCyccnt();
    count[1] = getDwtCyccnt();
    count[2] = getDwtCyccnt();
    count[3] = getDwtCyccnt();
    count[4] = getDwtCyccnt();
    count[5] = getDwtCyccnt();
    count[6] = getDwtCyccnt();
    count[7] = getDwtCyccnt();
    count[8] = getDwtCyccnt();
    count[9] = getDwtCyccnt();
    count[10] = getDwtCyccnt();

    for (size_t i = 0; i <= 10; i++) {
        const uint32_t diff = (i >= 1)?(count[i] - count[i - 1]):0;
        pc.printf("%d (%d)\r\n", count[i], diff);
    }
}

// 動作確認 - wait_us と比較
static void check_wait_us_accuracy() {
    uint32_t count[11];
    enableDwtCyccnt();
    resetDwtCyccnt();
    count[0] = getDwtCyccnt();
    wait_us(1000);
    count[1] = getDwtCyccnt();
    wait_us(1000);
    count[2] = getDwtCyccnt();
    wait_us(1000);
    count[3] = getDwtCyccnt();
    wait_us(1000);
    count[4] = getDwtCyccnt();
    wait_us(1000);
    count[5] = getDwtCyccnt();
    wait_us(1000);
    count[6] = getDwtCyccnt();
    wait_us(1000);
    count[7] = getDwtCyccnt();
    wait_us(1000);
    count[8] = getDwtCyccnt();
    wait_us(1000);
    count[9] = getDwtCyccnt();
    wait_us(1000);
    count[10] = getDwtCyccnt();

    for (size_t i = 0; i <= 10; i++) {
        const uint32_t diff = (i >= 1)?(count[i] - count[i - 1]):0;
        pc.printf("%d (%d)\r\n", count[i], diff);
    }
}

int main() {
    pc.baud(115200);
    pc.printf("CPU SystemCoreClock is %.2f MHz\r\n", (float)SystemCoreClock/1000.0f/1000.0f);

    check_overhead();
    check_wait_us_accuracy();
}

Nucleo F411RE での実行結果:

CPU SystemCoreClock is 100.00 MHz
0 (0)
4 (4)  ★オーバーヘッドは4サイクル
8 (4)
12 (4)
16 (4)
20 (4)
24 (4)
28 (4)
32 (4)
36 (4)
40 (4)
0 (0)
100572 (100572)  ★カウンタが少しぶれてる。Cortex-M4のキャッシュが原因か?
200549 (99977)
300555 (100006)
400563 (100008)
500569 (100006)
600547 (99978)
700555 (100008)
800563 (100008)
900569 (100006)
1000547 (99978)

Nucleo L152RE での実行結果:

CPU SystemCoreClock is 24.00 MHz
0 (0)
4 (4)  ★オーバーヘッドは4サイクル
8 (4)
12 (4)
16 (4)
20 (4)
24 (4)
28 (4)
32 (4)
36 (4)
40 (4)
0 (0)
24535 (24535)
48619 (24084)
72667 (24048)  ★カウンタは、ほぼ一定。
96715 (24048)
120763 (24048)
144811 (24048)
168859 (24048)
192907 (24048)
216955 (24048)
241003 (24048)

情報源:

[編集]

SPI SRAM (2015-02-28) #

Microchip 23LC1024 - Memory (23LC1024-I/P RSオンライン) を 購入して、Nucleo で試してみた。問題なし。

Microchip-23LC1024.jpg Microchip-23LC1024_Nucleo.jpg

ピン

CS18Vcc
SO/SIO127~HOLD/SIO3
SIO236SCK
Vss45SI/SIO0
[編集]

開封、IDE設定 (2014-05-22) #

nolink

nolink

nolink

nolink

nolink

nolink

nolink

nolink

nolink