今さらLPCXpressoメモ

ひさしぶりにLPCXpresso使ったら、使い方忘れてたのでメモ。

IDEのインストール

  • 最新版のLPCXpresso IDE (今日現在でv8.2.2_650) をダウンロードしてインストールする。
  • Windowsでは、通常は C:\nxp\LPCXpresso_8.2.2_650 にインストールされる。

IDEの起動

  • LPCXpresso IDEを起動するとワークスペースが開く。
  • デフォルトのワークスペースはWindows10では下記のファルダである。
    %USERPROFILE%\Documents\LPCXpresso_8.2.2_650\workspace である。

ライブラリのインポート

  • ここではLPC812-LPCXpressoボードをLPCOpenライブラリで開発するものとして必要なライブラリをインポートする。
  • Quickstart Panelの「Import project(s)」で「Project archive」の「Browse」から下記ファイルを選択する。
    C:\nxp\LPCXpresso_8.2.2_650\lpcxpresso\Examples\LPCOpen\
    lpcopen_2_19_lpcxpresso_nxp_lpcxpresso_812.zip
  • このZIPファイルにはチップライブラリ(lpc_chip_8xx)、ボードライブラリ(lpc_board_nxp_lpcxpresso_812)、およびサンプルコードが含まれるので、とりあえず全てをインポートする。

プロジェクトの作成

  • Quickstart Panelの「New project」からファミリ名「LPC81x」の「LPCOpen - C Project」を選択
  • プロジェクト名を入力
  • ターゲットMCUとして「LPC812」を選択
  • チップライブラリとして「lpc_chip_8xx」、ボードライブラリとして「lpc_board_nxp_lpcxpresso_812」を選択
  • その他のオプションを適宜設定
  • プロジェクトが作成される

プロジェクトのビルド

  • Quickstart Panelの「Build 'プロジェクト名'」でビルドが実行される。

プロジェクトのデバッグ実行

  • Quickstart Panelの「Debug 'プロジェクト名'」でデバッグ実行が開始される。
  • 最初に「Connect to emulator」でデバイスを選択する。
  • main関数の冒頭でブレークするので、Resume(F8)なりStep Into(F5)なりStep Over(F6)なりする。

固定小数点の再発明

Adafruit TrinketのようなROMが数キロバイトしかないマイコンでは、float型を用いると浮動小数点のライブラリがROM容量を圧迫してしまう。そこで固定小数点クラスを作ってみた。

【2019/05/24 追記】
Arduino IDEでビルドしてみたところ、AVRマイコンではfloat使うよりROMサイズが増えてしまった。クラス化はあきらめてCだけで書いたらいちおうfloatよりROM削減にはなったけど、それも思ったほどの効果でもなかった。8ビットマイコンで64ビットの乗算をするためけっこうROMを食うようだ。残念な結果である。
固定小数点計算ふたたび - 滴了庵日録

fixed24クラスの仕様

  • 内部表現は符号1ビット、整数部7ビット、小数部24ビットの全32ビット。
  • 代入(=)、比較(==, != , >, <, >=, <=)、四則演算(+, -, *, /) の演算子が使える。
  • +=のような糖衣な演算子はサポートしない。
  • 生値(32ビットの内部表現) または (分子, 分母) からインスタンス生成する。
  • 整数への変換にはtoInt()メソッドを用いる。(キャスト演算子は暗黙のキャストが危険なため)
  • デバッグ用にdoubleへのキャスト演算子を用意。(通常は無効)
  • 生値(32ビットの内部表現)へのアクセスを許す。(どうせこんなクラス使うのは泥臭い世界)

fixed24.h

#ifndef _FIXED24_H
#define _FIXED24_H

#include<stdint.h>

class fixed24
{
public:
    // constructors
    fixed24(void) {
        value = 0;
    }
    fixed24(int32_t rawval){
        value = rawval;
    }
    fixed24(int32_t numer, int32_t denom){
        int64_t x = ((int64_t)numer << 24) / (int64_t)denom;
        value = (int32_t)x;
    }

    // operators
    void  operator =  (fixed24 x){ value = x.value; }
    bool  operator == (fixed24 x) const { return (value == x.value); }
    bool  operator != (fixed24 x) const { return (value != x.value); }
    bool  operator >  (fixed24 x) const { return (value >  x.value); }
    bool  operator <  (fixed24 x) const { return (value <  x.value); }
    bool  operator >= (fixed24 x) const { return (value >= x.value); }
    bool  operator <= (fixed24 x) const { return (value <= x.value); }
    fixed24 operator + (fixed24 x) const { return fixed24(value + x.value); }
    fixed24 operator - (fixed24 x) const { return fixed24(value - x.value); }
    fixed24 operator * (fixed24 x) const {
        int64_t a = (int64_t)value;
        int64_t b = (int64_t)x.value;
        a *= b;
        a >>= 24;
        return fixed24((int32_t)a);
    }
    fixed24 operator * (int32_t x) const {
        int32_t y = value * x;
        return fixed24((int32_t)y);
    }
    fixed24 operator / (fixed24 x) const {
        int64_t a = (int64_t)value << 32;
        int64_t b = (int64_t)x.value;
        a /= b;
        a >>= 8;
        return fixed24((int32_t)a);
    }
    fixed24 operator / (int32_t x) const {
        int32_t y = value / x;
        return fixed24((int32_t)y);
    }
    
    // convert to integer (-128 to +127)
    // not cast operator because implicit cast is dangerous
    int toInt() const { return (int)(value >> 24); }

#ifdef _DEBUG_FIXED24
    // cast to double (for debug)
    operator double () const {
        double x = (double)value / (double)(1UL << 24);
        return x;
    }
#endif
    
    // raw value
    int32_t    value;
};

#endif

テスト

#include <stdio.h>

#define _DEBUG_FIXED24
#include "fixed24.h"

int main(void)
{
    fixed24 x = fixed24(10, 1);   // 10/1 = 10
    fixed24 y = fixed24(20, 100); // 20/100 = 0.20
    fixed24 z = fixed24(0x01000000 / 2); // 1/2 = 0.5

    printf("x = %d (%08X)\n", x.toInt(), x.value);
    printf("y = %f (%08X)\n", (double)y, y.value);
    printf("z = %f (%08X)\n", (double)z, z.value);

    z = x + y;
    printf("%f + %f = %f (%08X)\n", (double)x, (double)y, (double)z, z.value);
    z = x - y;
    printf("%f - %f = %f (%08X)\n", (double)x, (double)y, (double)z, z.value);
    z = x * y;
    printf("%f * %f = %f (%08X)\n", (double)x, (double)y, (double)z, z.value);
    z = x / y;
    printf("%f / %f = %f (%08X)\n", (double)x, (double)y, (double)z, z.value);
    z = x * 2;
    printf("%f * 2 = %f (%08X)\n", (double)x, (double)z, z.value);
    z = x / 2;
    printf("%f / 2 = %f (%08X)\n", (double)x, (double)z, z.value);

    x = fixed24(1, 1);
    y = fixed24(1, 1);
    printf("%f == %f : %s\n", (double)x, (double)y, (x == y) ? "TRUE" : "FALSE");
    printf("%f != %f : %s\n", (double)x, (double)y, (x != y) ? "TRUE" : "FALSE");
    y = fixed24(2, 1);
    printf("%f == %f : %s\n", (double)x, (double)y, (x == y) ? "TRUE" : "FALSE");
    printf("%f != %f : %s\n", (double)x, (double)y, (x != y) ? "TRUE" : "FALSE");

    return 0;
}

日本語の「ん」には何種類の発音があるか?

日本語の「ん」にはじつは何種類もの発音があり、無意識に使い分けています。というより、発音しやすいように自然に発音が変化します。

f:id:licheng:20190521213707p:plain:w500

[n] : 歯茎鼻音

  • タ行、ダ行、ナ行、ザ行、ラ行に続く場合
  • 舌の先が上あごの前歯の歯ぐきに触れる
  • 例: 安泰(アンタイ)、安打(アンダ)、案内(アンナイ)、うんざり(ウンザリ)、安楽(アンラク)
  • ただし、イ段(チ、ヂ、ニ、ジ、リ)に続く場合は後述する [ɲ] になる? (個人差あり)
  • ザ行は「ん」の後では [z] ではなく [dz] で発音されることに注意。 (個人差あり)
  • ラ行の発音は特に個人差が大きく、舌の位置に違いがありうる。

[m] : 両唇鼻音

  • パ行、バ行、マ行に続く場合
  • 上下のが閉じる
  • 例: 安パイ(アンパイ)、塩梅(アンバイ)、あんまり(アンマリ)

[ŋ] : 軟口蓋鼻音

  • カ行、ガ行に続く場合
  • 舌の奥が盛り上がって上あごの奥の軟らかい部分に触れる
  • 例: 参加(サンカ)、案外(アンガイ)

[ɲ] : 硬口蓋鼻音

  • チ、ヂ、ニ、ジ、リに続く場合? (個人差あり)
  • 舌の腹が上あごの硬い部分に触れる
  • 例: 安置(アンチ)、暗に(アンニ)、暗示(アンジ)、

[ɴ] : 口蓋垂鼻音

  • 後に何も続かない場合
  • のどちんこが下がって舌のいちばん奥に触れる
  • 例: あかん。(アカ)

[ã ĩ ɯ̃ ẽ õ] : 鼻母音

  • ア行、ヤ行、ワ行、サ行、ハ行に続く場合
  • 前の母音の口の形のまま、のどちんこが少し下がって鼻にも息が抜ける
  • 例: 安易(アンイ)、暗躍(アンヤク)、やんわり(ヤンワリ)、暗殺(アンサツ)、アンハッピー
  • 他の「ん」と異なり、このタイプの「ん」は母音であって子音ではない。
  • 例えば、健一(ケンイチ) と 敬一(ケーイチ) の差は、のどちんこの動きの有無でしかない。
  • 前述の[ɴ]のようにのどちんこが完全にのどをフタすることなく、鼻と口の両方に息が流れる。

まとめ

  • 日本語では息が鼻に抜ける音(鼻音)をすべて「ん」という同じ音と見なす。
  • 実際にどの音で発音されるかは、前後の音のつながり(特に後に続く音)で決まる。
  • 息が鼻と口の両方に流れる鼻母音で発音される場合もある。

Arduinoのしょうもない落とし穴

踏んでしまったのでメモ。

操作ミスまたは一時的なバックアップのつもりでうっかり下図のようなファイルコピーをおこなったとする。

f:id:licheng:20190520143745p:plain

このとき、test.ino を開いてもArduino IDE上に test - コピー.ino は表示されない。空白や日本語を含むファイル名のソースは表示されないようだ。

f:id:licheng:20190520143757p:plain:w500

ところが、ビルドすると test - コピー.inoコンパイルされる。その結果、関数の再定義エラーが発生する。

f:id:licheng:20190520143808p:plain:w600

パーソナルEEGデバイス(脳波計)の選び方?

手軽なヘッドセットで測定した脳波データをスマホに送って、分析したりアプリ開発したりできるやつ欲しい。どれを選んだらいいのか調べていたら↓の動画を見つけた。

以下に要約する。(※内容の妥当性は僕には保証できません。)

今はMuseとEmotivが双璧

  • NeuroSky MindWave Moble, Muse, Emotiv EPOC / Insight, BrainLink Pro / Liteと
    いろいろあるがどれを買えばいいのか?
  • NeuroSky MindWave Mobleは今となってはちと古いのでおすすめしない (2011年発売)。
    最初にパーソナルEEGバイス市場に進出した画期的なデバイスだった。
    (※ MindWave Moble2というのが出てるようだがマイナーチェンジ?)
  • 後発のMuseやEmotivのほうがセンサ技術が改良されている。
  • BrainLink Pro / Liteとかいうのも近日発売になる。(※ すでに発売されてます)
  • 現時点(2018年)ではMuseとEmotivがいちばんよく見かけるしコミュニティー/エコシステムが大きい。
  • どちらを選ぶべきかはあなたが何をしたいかによる。

用途の3つのカテゴリー

  1. 瞑想/セラピー/ヘルスケア (ヨガとか)
  2. ブレイン・マシン・インタフェース (脳波でパソコンを操作したり、ロボットを操縦したり)
  3. 神経科学の研究 (認知機能と脳波の研究、ニューラル・マーケティングなど)

瞑想/セラピー/ヘルスケア用途ではMuse

ブレイン・マシン・インタフェース用途ではEmotiv

  • EmotivはGitHubなどで開発者コミュニティーが大きい
  • センサは多いほうが良いが、価格とのトレードオフ
  • Emotiv EPOC Flex, Emotiv EPOC, Emotiv Insightの順にセンサが多く高価

研究用途

  • ガチの研究にはセンサがたくさんあるやつが必要
  • 脳波の生データの利用
    • Emotivは有償版ソフトのEmotiv PROでないと脳波の生データは扱えない
    • Museは無償のソフトで脳波の生データが扱える
    • EEG LAB:脳波信号のデータ処理用のMATLABツールボックス
    • Muse Direct:脳波信号のグラフ表示&記録ができるアプリ

Arduino系マイコンで超いいかげんなprintf

Arduino系のマイコンでprintfをシリアルに出力するためのいいかげんなマクロ

static char print_buff[256]; // ←メモリが許すなら十分大きくとる
#define printf(...)  sprintf(print_buff, __VA_ARGS__), Serial.print(print_buff)

sprintf()とSerial.print()の区切りがなぜ ; でなく , かというと、下記のようなif文の書き方があった場合にエラーになるため。

if(hoge)
    printf("hoge");
else
    printf("piyo");

printf()の戻り値を受け取るような場合にはエラーになるが、そんなことは滅多にしないだろう。

また、見ての通りリエントラントではないので割り込みやマルチタスクで使う場合は要注意。あくまでもテキトーにコピペしたコードを動かすための、間に合わせの超いいかげんなマクロである。

ハッコーのイベント限定品レビュー

Maker Faire Kyotoで限定販売されたハッコーのキャップセットと折りたたみこて台をゲットしました。プロトタイプ段階のもので今のところ一般販売は未定のようです。

f:id:licheng:20190511211440j:plain:w500

キャップセット

ハッコーの温調はんだごてFX-600にキャップを取り付けるためのアダプタとキャップのセットです。キャップはPRESTOに付属するものと同等品です。アダプタにはキャップをしめるためのネジが切ってあり、付属のビス3本で簡単にこてに取り付けることができます。FX-600を携帯するときに便利です。今まではこてカバー605Mを使ってましたが、キャップのほうがスッキリしますね。

f:id:licheng:20190511211803j:plain:w500

…ていうか、FX-600は最初からキャップを取り付けられるようにしてほしかったです。

折りたたみこて台

折りたためるこて台です。こて台を携帯するときに便利です。同社のこて台633-02に折りたたみ機構がついたようなかんじですね。関節部分が良く出来ていると思います。昔愛用していたgootのこて台ST-77は関節を蝶ネジでしめる機構ですが、蝶ネジがゆるむとぐらつくのが難点でした。本製品は関節がロックするのでネジは軽くしめるだけでOKです。また、底面の滑り止めのゴムが大きくて、軽くてもしっかり机にグリップする感じがします。

f:id:licheng:20190511212807j:plain:w500

携帯用のこて台にはこてを寝かせて置くものが従来からありコンパクトではありますが安定が悪く、ケーブルを引っ張るとこてがころがり落ちる危険がありました。本製品は安定感があって出先でもガッツリはんだづけ作業ができると思います。

f:id:licheng:20190511213219j:plain:w500

欲を言えば、エンジニアのSS-05みたいなフタ付きのクリーナーが欲しいです。クリーナーをチャック袋に入れて携帯するのはスッキリしないので。とりあえず、SS-05とセットで運用しようかなと思います。

その他

Maker Faire Kyotoでは他にUSBはんだごても限定販売されてました。いずれもイベント限定販売で終わっては惜しいアイテムだと思います。(あ、私はべつにハッコーの回し者ではありません。FX-888Dのボタンの操作性はほんとクソ。ほんとクソ。)