2014/11/29

[keil]32KBの壁はどこにある?

「keilのフリー版は32KB制限がありますよね」という記事を書いていたのだが、読んでくださった方々から情報をいただいた。
ありがたやありがたや。
私があんまり社交的じゃないためか、このブログはマイナーなんですよ。
ときどき、閉鎖して本業に集中しようかとも思うけど、見てくださる方がいるのであれば、もうちょっと続けようかという感じでやっとります。


さて、本題。

hiro99ma blog: [ble]BLEモジュールを使った場合のがんばりどころは?

コメントをいただいた中で、誰もが「32KBを超えても大丈夫だった」ということだった(2人しかコメントがないが、これを「誰もが」と書くことで、すごく多くの人がコメントをくれたようなイメージを与えるテクニックだ)。

では、試してみなくてはならぬ。

まず、32KB、がどこを指しているのか。
ARM KEIL組み込み開発システム 統合開発環境 <MDK-ARM 正規代理店>|横河ディジタルコンピュータ株式会社
こちらの表を見ると、デバッガ、シミュレータ、そしてコンパイラが32KBとのこと。
しかし、コンパイルのどこら辺が32KBかは、見てもわからない。

セカンド・オピニオン (412) MCUで遊ぼう Part2 (3) | マイナビニュース
これを読むと、コード/データが32KBとある。
プログラムは基本的にROMとRAMの2つに分かれるが、ROMもデータとプログラムに分かれる。
RAMも、初期値があるものと、ないものに分かれる。
初期値があるものは、その初期値自体はROMに置かれることになる。じゃないと、初期値を保持しておくところがないからね。
C言語では、初期値を持つ変数というのは、staticなものしかない。
そのstaticな変数の中でも、初期値を代入しているものは「初期値あり」だが、代入していないものは「ゼロ」になる。
だから、staticな変数であっても、初期値の有無でROMのサイズが変わってくる。

なんでこんなことをごにゃごにゃ書いたかというと、32KBのROMをどうやったら作り出せるか、ということを考えていたからだ。
プログラムを書いて32KB作るのは、けっこう難しい。
できれば、var[32768]みたいな感じで、楽に32KBを作りたい。
でも、staticとかvolatileとかつけても、単に宣言しただけだとRAM領域の話になるので、コードサイズにならない。
初期値がないから、ゼロになるだけだ。
私が見たことのあるコンパイラだと、main()が呼ばれる前にmemset()とか、あるいはアセンブラなどでゼロ埋めするものがほとんどだったが、オプションによってゼロ埋めする処理すら省略するものもあった。

我々が求めているのは、ゼロ以外の初期値を持つ変数だ。
そうなると、初期値の部分がROMに配置されるので、今回のコードサイズ制限を調べるのに役立つ。

#include <nrf51.h>
#include <stdint.h>
#define FILL16 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
#define FILL256 FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16
#define FILL1K FILL256,FILL256,FILL256,FILL256
static const uint8_t mem[256 * 1024 * 1024] = {
    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,
    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K
};
int main(void)
{
    uint8_t jan = mem[256];
    __wfe();
    return 0;
}

16byteのFILL16を、16個並べたFILL256を、4つ配置。これでFILL1K。
とりあえずエラーを見てみたいので、これを16個並べたら、16KBだからよさそうだけど、エラーになった。

Build target 'Target 1'
compiling main_32k.c...
main_32k.c(15): warning:  #177-D: variable "jan" was declared but never referenced
        uint8_t jan = mem[256];
main_32k.c: 1 warning, 0 errors
linking...
.\Objects\test.axf: error: L6047U: The size of this image (268436284 bytes) exceeds the maximum allowed for this version of the linker
Finished: 0 information, 0 warning, 0 error and 1 fatal error messages.
".\Objects\test.axf" - 1 Error(s), 1 Warning(s).
Target not created.

でたー。
これが、maximumなエラーメッセージらしい。
少し満足。

まあ、256 * 1024 * 1024だと、どの環境でもエラーになりそうよねぇ、ということで、32KB制限近くでやってみた。

#include <nrf51.h>
#include <stdint.h>
#define FILL16 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
#define FILL256 FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16
#define FILL1K FILL256,FILL256,FILL256,FILL256
static const uint8_t mem[31 * 1024] = {
    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,
    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K
};
int main(void)
{
    uint8_t jan = mem[256];
    __wfe();
    return 0;
}

これのビルド結果は、こう。

Rebuild target 'Target 1'
compiling main_32k.c...
main_32k.c(15): warning:  #177-D: variable "jan" was declared but never referenced
        uint8_t jan = mem[256];
main_32k.c: 1 warning, 0 errors
assembling arm_startup_nrf51.s...
compiling system_nrf51.c...
linking...
Program Size: Code=598 RO-data=31970 RW-data=4 ZI-data=4196  
FromELF: creating hex file...
".\Objects\test.axf" - 0 Error(s), 1 Warning(s).

この、memの31を32にすると、リンクエラーになった。

Rebuild target 'Target 1'
compiling main_32k.c...
main_32k.c(15): warning:  #177-D: variable "jan" was declared but never referenced
        uint8_t jan = mem[256];
main_32k.c: 1 warning, 0 errors
assembling arm_startup_nrf51.s...
compiling system_nrf51.c...
linking...
.\Objects\test.axf: error: L6047U: The size of this image (33596 bytes) exceeds the maximum allowed for this version of the linker
Finished: 0 information, 0 warning, 0 error and 1 fatal error messages.
".\Objects\test.axf" - 1 Error(s), 1 Warning(s).
Target not created.

こっちが、warningだけだったコード。

#include <nrf51.h>
#include <stdint.h>
#define FILL16 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
#define FILL256 FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16
#define FILL1K FILL256,FILL256,FILL256,FILL256
static const uint8_t mem[31 * 1024] = {
    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,
    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K
};
int main(void)
{
    uint8_t jan = mem[256];
    __wfe();
    return 0;
}

こっちが、エラーになったコード。

#include <nrf51.h>
#include <stdint.h>
#define FILL16 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
#define FILL256 FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16
#define FILL1K FILL256,FILL256,FILL256,FILL256
static const uint8_t mem[32 * 1024] = {
    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,
    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K,    FILL1K
};
int main(void)
{
    uint8_t jan = mem[256];
    __wfe();
    return 0;
}

違いは、memの添字になっている値が、31か32か、だ。

こうやって見る分には、ちゃんと32KBで制限がかかっていると感じます。

2014/11/28

[android]AndroidStudio 1.0 RC2が出ていた

Windowsにも、Android Studio 1.0 RC 2が出ていた。

image

前がどんなのか忘れたけど、あか抜けた感じがしますな。
あか抜けてない私が言っても説得力ないですが。

image

 

既存のプロジェクトを開くと、エラーが出た。
こちらを参考に修正すると、よさそうだった。
Android Studioでmethod not found: 'runProguard()'発生 電脳羊(Android Dream)/ウェブリブログ

ファイルを開いて、runProguard を minifyEnabledに変更し、設定をrecommendのものにしただけで、ビルドは通った。
動かしてはいないけど、まあいいや。

2014/11/26

[c/c++]enumはプリプロセスでは評価できません

「何を当たり前のことを」と言われそうだが、まあ聞いておくれ。

今、BLEの再履修をしているところだ。
一通り小さなサービスを動かしてきたことになるので、始めた当時にはよくわからなかったものが、今では理解できるようになったかもしれない。
私の場合、学習→試作→学習→試作、を繰り返すような感じで物事を把握することが多い。
なんというか、覚えるときは紙に目一杯書き込まないと覚えられないような、体で感じるタイプらしい。

 

まあそんなわけで、BLEのことを調べ直しているのだが、設定できる値の範囲が気になってきた。
例えば、connIntervalは7.5msec~4secの間だ。
これをS110とかでは、こんな表し方になる。

#define CONN_MAX_INTERVAL               MSEC_TO_UNITS(1000, MY_UNIT_1_25_MS)

MSEC_TO_UNITS()はapp_util.hにあるマクロで、こんな定義だ。

#define MSEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000) / (RESOLUTION))

なんでこんな式かというと、connIntervalは7.5 + 1.25n、というBluetoothの仕様になっているからだ。
TIMEはmsec単位なので、それを1000倍して割る。
ということは、全体としてはμsecになってしまうので、そうならないようにRESOLUTIONも1000倍された値を使う。
「どうせconnIntervalなんて固定値でしか持たないだろうから、そこまでやらなくていいんじゃないの」と思ったが、実装にも仕様にも固定じゃないといかんというわけではなく、もしかしたら普通は曜日や時間帯に合わせて可変にするものだけど、サンプルだから固定にしているだけなのかもしれん。

 

前置きが長くなって済まん。
nRF51で使うconnInterval値の範囲は6~3200になるのだが、そういうマクロ値が既に定義されていることを知った。
BLE_GAP_CP_MAX_CONN_INTVL_MINが6で、BLE_GAP_CP_MAX_CONN_INTVL_MAXが3200とか。
ならば、コンパイル前に定義値の範囲チェックをしておけば、変なバグで悩むことが減るんじゃなかろうか、と思ったのだ。

それで、こんなチェックマクロを埋め込んだ。

#if (CONN_MAX_INTERVAL < BLE_GAP_CP_MAX_CONN_INTVL_MIN)
#error connInterval_Max(Connection) too small.
#elif (BLE_GAP_CP_MAX_CONN_INTVL_MAX < CONN_MAX_INTERVAL)
#error connInterval_Max(Connection) too large.
#endif  //CONN_MAX_INTERVAL

 

メモリを消費するでもなく、ビルド段階で範囲チェックができてよかろう、と満足したのだが、これがgccでエラーになるのだ(他のは知らん)。
さんざん悩んだが、結果としてはこのせいだった。

enum {
    UNIT_0_625_MS = 625,
    UNIT_1_25_MS  = 1250,
    UNIT_10_MS    = 10000
};

えっ、ここにenum使ってたの??
なんでまた・・・。

#defineはプリプロセス段階で展開されるけど、enumはコンパイル時に展開される。
だからプリプロセスとしては、MSEC_TO_UNITS(1000, MY_UNIT_1_25_MS)みたいなenumを含む式を見つけると、展開できないという扱いにしてしまい、ここではゼロとみなして「ゼロで割ってます」というエラーになっていたのだ。

まあ、enumにするとデバッガなどで値が見やすくなるのはあるけど、ここでは使ってほしくなかったなぁ。
せっかくdefineがあるけど、ここは自分で7.5msecとか4secとかの定義値を作って、値でチェックする方式に置き換える方がよさそうだ。
時間かかったわー。

2014/11/24

Excel2013のコンテキストメニューが薄い

何も解決しない話になるが、Excel2013のコンテキストメニューの文字色が薄い。

image

使えるのは濃いグレーで、使えないのは薄いグレー。
どうにもコントラストが低く、最初見たときは全部ディセーブルなのかと思ったくらいだ。

テーマを変えても色が変わらないので、どうしようもなさそうだ。
Window自体も、アクティブと非アクティブでタイトル文字色しか変わらないので見分けづらい。
Excelの機能に不満はないけど、ちょっとこれはねぇ・・・。

2014/11/23

[nrf51]iBeaconとNordic

別にNordicに限定したことじゃないが、iOS8からiBeaconの仕様が変わったらしく、今のところNordicのVendor IDはCore Location Frameworkではじかれているらしい。

Nordic beacons and iOS

 

iBeaconのパケット仕様は、もともとAppleからは出てなくて、どちらかというと「こういうAdvertisingデータを流すと反応してくれた」みたいな感じだったのではなかっただろうか。
少なくとも私は、Appleの技術ドキュメントからは見つけられていない。

んで、いつしかMFi対象になってた。
あんまりわかってないけど、Advertisingのデータに書いてあるだけで、それが平文だったら、普通にスキャンしてまねすればだませてしまいますわな。
なので、自由度はなくなってしまうけど、まあAppleなら仕方ないかな、というところか。