2018年5月31日木曜日

CubeMXで新規プロジェクト まとめ

 前に書いたやつのまとめ。

 最低限、コンパイラが吐くエラーを自力で修正できる必要がある。あとMakefileをかなりいじるので、そのあたりの知識も。
 それとsyscalls.cというファイルが必要になる。どっかから拾ってきてください。

 Win10のWSLで実行することを前提にしています。まぁmakeとか一通り動いてarm-none-eabi-gccとかも一通り入ってればだいたい問題ない。

1 プロジェクトを作る

File > New Project (Ctrl-N)でNew Projectを表示する。
 使用するMCUを選択する。左側のMCU Filtersでコアやシリーズ、パッケージを選択したり、製品名を入力する。
 今回はSTBee F4miniを使用するので、STM32F405RGを選択する。
 MCUs List n itemsでアイテムをダブルクリックして終了。


 Project > Settings...でProject Settings画面を開く。
 Project Nameでプロジェクト名を入力する。
 Toolchain / IDEをMakefileにする。

 Code GeneratorタブでGenerate peripheral initialization as a pair of '.c/.h' files per peripheral、Set all free pins as analog (to optimize the power consumption)、Enable Full Assertの3個にチェックを入れる。

 OKでProject Settingsダイアログを閉じる。

2 初期設定

GPIOの設定を行う。

 左のツリーからRCCのHigh Speed Clock (HSE)とLow Speed Clocl (LSE)をCrystal/Ceramic Resonatorにする。

 左のツリーからUSB_OGT_FSのModeをDevice_Onlyにする。
 USB_DEVICEのClass For FS IPをCommunication Device Class (Virtual Port Com)にする。

 右のパッケージのイラストからPA0-WKUPをクリック、GPIO_Inputを選択。右クリックでEnter User Labelをクリックし、USER_SWと入力。同じようにPD2をGPIO_Outputにし、USER_LEDと入力。

3 クロックの設定

Clock Configurationタブを開く。
 Clock configuration Do you want to run automatic clock issues solver? というダイアログが出てくるので、Noで閉じる。
 左側のInput frequencyをボードの水晶の値である12にする。
 PLL Source MuxでHSEを選択、System Clock MuxでPLLCLKを選択する。
 右側のHCLK to AHB bus, core, memory and DMA (MHz)に168と入力し、Enterで反映する。少し待たされた後、赤枠のエラーが消えたりして設定が反映される。

4 その他の設定

Configurationタブを開く。
 左側のFREERTOSのEnableをチェックする。


 右側のFREERTOSアイコンをクリックし、FREERTOS Configurationダイアログを開く。
 Task and Queuesのタブを開く(デフォルトでこのタブのはず)。

 TasksのAddでNew Taskダイアログを開く。
 とりあえずLEDを点滅させるタスクをつくる。Task NameにLED_blink、PriorityにosPriorityLow、Stack Sizeに128、Enter FunctionにLED_blink_func、Code Generation OptionにAs externalを設定し、OKでダイアログを閉じる。
 コンソールのタスクも作っておく。Task Nameにconsole、PriorityにosPriorityBelowNormal、Stack Sizeに1024、Entry Functionにconsole_func、Code Generation OptionにAs externalを設定する。

 QueuesのAddでNew Queueダイアログを開く。
 Queue Nameにstdout_queue、Queue Sizeに256、Item Sizeにuint8_tとし、OKで閉じる。
 再びAddでダイアログを開き、stdin_queue、128、uint8_tとして追加。

 Config parametersタブでUSE_IDLE_HOOKとUSE_TRACE_FACILITYとUSE_STATS_FORMATTING_FUNCTIONSをEnableにする。

 OKでFREERTOS Configurationダイアログを閉じる。

5 コード生成

Project > Generate Code (Ctrl-Shift-G)をクリック。
 Warning: Code Generationと警告が出てくるが、Yesで続行する。

 しばらく待つとThe Code is successfulyとダイアログが出るので、Open Folderで出力フォルダを開くか、Closeでダイアログを閉じる。

6 コードの修正と追加

Makefileを開く。

 C_SOURCES =  \の下に Src/syscalls.c \ と Src/CCM.c \ を追加。

 # ASM sourcesの上に以下のコードを追加する。
"
CPP_SOURCES = \
Src/LED_blink.cpp \
Src/Console.cpp \
"

 CC = とかAS = とかの$(BINPATH)/$(PREFIX)を$(BINPATH)$(PREFIX)にする(スラッシュを消す)。
 CCの下にCPP = $(BINPATH)$(PREFIX)g++を追加する。

 LDFLAGS = にある-specs=nano.specsを削除する。

 CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" を CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)" に変更。

 # list of objectsの OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o))) を OBJECTS = $(addprefix $(BUILD_DIR)/,$(C_SOURCES:.c=.o)) に変更。

 # list of ASM program objectsの上に以下のコードを追加。
"
OBJECTS += $(addprefix $(BUILD_DIR)/,$(CPP_SOURCES:.cpp=.o))
vpath %.cpp $(sort $(dir $(CPP_SOURCES)))
"

 vpath %.s $(sort $(dir $(ASM_SOURCES))) の下に以下のコードを追加。
"
DEPS = $(OBJECTS:%.o=%.d)
-include $(DEPS)
"

 OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.s=.o))) を OBJECTS += $(addprefix $(BUILD_DIR)/,$(ASM_SOURCES:.s=.o)) に変更。

 $(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR) の下に mkdir -p $(dir $@); を追加。

 $(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@ を $(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(<:.c=.lst) $< -o $@ に変更。

 $(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR)の上に以下のコードを追加。
"
$(BUILD_DIR)/%.o: %.cpp Makefile | $(BUILD_DIR)
mkdir -p $(dir $@);
$(CPP) -std=c++11 -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(<:.cpp=.lst) $< -o $@
"

 $(CC) $(OBJECTS) $(LDFLAGS) -o $@を$(CPP) $(OBJECTS) $(LDFLAGS) -o $@に変更する。


 STM32F405RGTx_FLASH.ldを開く。
 _Min_Stack_Size=の下に_heap_end = ORIGIN(RAM)+LENGTH(RAM)-4;を追加する。
 *(.ccmram*)の下に*CCM.oを追加する


 Inc/FreeRTOSConfig.hのUSER CODE END Definesの上に #define configAPPLICATION_ALLOCATED_HEAP 1 を追加する。


 SrcフォルダにCCM.cを追加。以下の行を追加。
"
#include <FreeRTOS.h>

#if( configAPPLICATION_ALLOCATED_HEAP == 1 )
uint8_t ucHeap[configTOTAL_HEAP_SIZE];
#endif
"


 SrcフォルダにLED_blink.cppを追加。以下の行を追加。
"
#include <cmsis_os.h>
#include <main.h>
#include <stm32f4xx_hal.h>

extern "C" void LED_blink_func(void const *argument)
{
    while (1)
    {
        HAL_GPIO_TogglePin(USER_LED_GPIO_Port, USER_LED_Pin);
        osDelay(HAL_GPIO_ReadPin(USER_SW_GPIO_Port, USER_SW_Pin) == GPIO_PIN_SET
                    ? 100
                    : 500);
    }
}
"


 SrcフォルダにConsole.cppを追加。以下のコードを追加。
"
#include <cmsis_os.h>
#include <stm32f4xx_hal.h>
#include <string.h>

extern "C" void console_func(void const *argument)
{
    while (1)
    {
        char str[100];
        scanf("%99[^\n]%*[^\n]", str);
        getchar();

        {
            char *const p(strchr(str, '\r'));

            if (p)
            {
                *p = '\0';
            }
        }

        if (!strcmp(str, "tasklist"))
        {
            char buff[10 * 45];
            osThreadList((uint8_t *)buff);
            printf(
                "Name            State   Priorty Stack   Num\n"
                "*******************************************\n"
                "%s"
                "*******************************************\n",
                buff);
        }
    }
}
"



 Srcにどこかから持ってきたsyscalls.cをコピーして入れておく。

 _read_rの中を以下のようにする。
"
uint8_t *p = (uint8_t *)ptr;
extern osMessageQId stdin_queueHandle;

while (len--)
{
    uint8_t data;
    xQueueReceive(stdin_queueHandle, &data, portMAX_DELAY);
    *p++ = data;

    if (data == '\n')
    {
        break;
    }
}

_ssize_t i = (uint32_t)p - (uint32_t)ptr;

return (i);
"

 _write_rの中を以下のようにする。
"
uint8_t *p = (uint8_t *)ptr;
int i = 0;
extern osMessageQId stdout_queueHandle;

if (__get_IPSR() == 0)
{
    for (i = 0; i < len; i++)
    {
        xQueueSend(stdout_queueHandle, p++, portMAX_DELAY);
    }
}
else
{
    for (i = 0; i < len; i++)
    {
        xQueueSendFromISR(stdout_queueHandle, p++, 0);
    }
}

return (i);
"


 Src/freertos.cを開く。
 #include <usbd_cdc_if.h> を追加する。

 vApplicationIdleHookに HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); を追加する。

 StartDefaultTaskの無限ループの前に osDelay(500); を追加する。
 無限ループの中を以下のように書き換える。
"
static uint8_t buffer[32];
uint16_t count = 0;
BaseType_t dequeue_state = pdPASS;

while (dequeue_state == pdPASS && count < sizeof(buffer))
{
    dequeue_state = xQueueReceive(stdout_queueHandle, &buffer[count], 1);

    if (dequeue_state)
    {
        count++;
    }
}

if (count > 0)
{
    CDC_Transmit_FS(buffer, count);
}
osDelay(2);
"



 Src/usbd_cdc_if.cを開く。
 #include <cmsis_os.h> を追加。

 CDC_Control_FS関数のスイッチのケースCDC_GET_LINE_CODINGに以下のコードを追加。
"
*((uint32_t*)&pbuf[0]) = 115200;
pbuf[4] = 1;
pbuf[5] = 0;
pbuf[6] = 8;
"

 CDC_Receive_FS関数のreturnの前に以下のコードを追加。
"
extern osMessageQId stdin_queueHandle;
uint32_t i = 0;
for (i = 0; i < *Len; i++)
{
    xQueueSendFromISR(stdin_queueHandle, &Buf[i], 0);
}
"


 あとはmakeすればbuildフォルダにelfとかhexとかbinとかが作られてるので、任意のツールでマイコンに書き込めばOK。

 注意点として、このmakefileはヘッダの変更を認識しない。ヘッダファイルを書き換えた場合は、責任を持ってそれに依存するすべてのソースファイルをタッチするか、あるいはmake cleanでオブジェクトファイルを削除して再びビルドすること。

 Cubeが吐いたmakefileにはC_SOURCESに絶対パスを指定するファイルが含まれる場合がある。その場合は最初のスラッシュを削除し、相対パスで認識されるようにすること。

 未確認だが、割り込みの中でHALのタイムアウトを指定する関数を呼ぶ場合、うまく動かないと思う(タイムアウトしないはず)。対策はいろいろあるが、そもそも割り込みの中で何ミリ秒もポーリングするような処理はするな、ということで。
 ほぼ同じところが原因で、FreeRTOSのコンテキストスイッチを1kHz以外に設定するといろいろ不具合を起こすはず。


* WSLでGCCが古い問題
 Win10 WSLのUbuntuのaptで入れたarm-none-eabi-gccはバージョン4.9.3で更新が止まってる。新しいGCCを使いたい場合は、GNU Arm Embedded Toolchain | Downloads – Arm DeveloperからWindows ZIPをダウンロードしてきてスペースや全角文字を含まないディレクトリに解凍しておく。binフォルダにarm-none-eabi-gcc.exe等一式が入っているので、MakefileのBINPATHにこのフォルダのパスを入れておく。MakefileはWSLで実行するので、ディレクトリ名はC:\等ではなく/mnt/c/のようになる点に注意。そしてCC = $(BINPATH)$(PREFIX)gcc.exeのように拡張子を追加する。
 あとはmakeすれば新しいgccでビルドできる。

 C:\Devz\ARM\launchpad\bin\arm-none-eabi-gcc.exe のようなディレクトリ構成の場合、 BINPATHは /mnt/c/Devz/ARM/launchpad/bin/ のようになるはず。


更新:2018/06/15
 [update]依存関係を正常に処理できるように
 [add]BINPATHの例を追加

マッチドフィルタ/パルス圧縮/FFT相関




 上がマッチドフィルタ風、下がパルス圧縮風。
 受信信号と想定した正弦波は-1から+1の範囲を取る。ノイズと想定した疑似乱数は-1から+1の範囲を取る。

 マッチドフィルタ風は「ΔFが0なパルス圧縮」、つまり圧縮比が0のパルス圧縮と見ることができる。実際、上記のグラフはΔFの違いしか無い。

 マッチドフィルタでは近い範囲にエコーがあると判別ができない。パルス圧縮では十分に見分けることができる。

 レーダーの探知性能は送信出力とパルス幅の、いわゆる力積と呼ばれるもので、ざっくり言えばパルスの振幅とパルス幅の積がレーダー波のエネルギーになる。エネルギーが大きいほど遠距離の、あるいは小型な目標のエコーを拾うことができる(受信感度等は割愛)。
 この図ではマッチドフィルタもパルス圧縮も同じ振幅、同じパルス幅なので、どちらも探知性能としては同様。ただしパルス圧縮のほうが距離分解能が高い。また、パルス圧縮のほうが立ち上がり・立ち下がりが急峻であり、距離精度が改善する(目標のエコーが遠近方向に広がらずに表示される)。

***


 この図は、上がチャープ信号をFFT解析したもの、下はそれをIFFTに通したもの。

 FFT解析には下記ブログのソースコードを流用した。
 C#で画像処理 高速フーリエ変換(FFT)
 C#で画像処理 逆高速フーリエ変換(IFFT)

 FFT結果は凄まじいことになっているが、IFFTを通すとちゃんと元通りになる。

***


この図は、パルス圧縮の波形を単純な積で計算したものと、FFTを使って計算したものの比較。赤がfor2重ループで計算したもの、緑がFFTで計算したものだが、どちらも同じ結果になっている(最後の方が一致していないのはサンプル数の問題)。
 正弦波が±1なのに対し、ノイズが±5程度あるが、十分な強度としてパルスが見えており、狭い範囲にある2つのパルスを判別することができる。


 以下のページを参考にした
 離散フーリエ変換(3) - 畳み込み定理 - uhiaha888’s diary

 for2重ループは時間領域、FFTは周波数領域、というタイプなのかな?
 とある本によると、入力信号と基準波形をDFT計算し、その結果を乗算後に逆DFTに通す、というふうに書いてある。処理としては積をDFTに通せばいいらしい。
 それと、入力信号が1024サンプル、基準波形が128サンプル、といった場合、基準波形を1024サンプルになるようにゼロ埋めし、それらをFFT解析してから積をとり再びFFTに入れる、という処理になるらしい。
 また、次の入力信号の解析を行うときは、基準波形分の128サンプルの領域が重なるように処理する必要がありそうな気がする。とはいえ、これに関してはfor2重でも同じような問題が出てくるので、欠点と言えるほどではないかも。

 基準信号・入力信号ともにスペクトルの大部分が低周波領域に集中しているから、乗算を行うのは全体の2%程度で済んでいる(サンプリング周波数と目的の周波数の関係で変わるはず)。今回はサンプリング周波数とチャープ信号の中心周波数の比が200なので2%で済んでいるが、実際はこの比は20とかそれ未満の場合もあるだろうから、本来であればFFTのかなりの領域の積を取る必要があるはず。

 基本的にレーダーであれば基準信号がコロコロ変わることは少なく、近距離と遠距離で送信波を変える場合でも数種類で済むはず。ということは、基準波形のFFTは最初の1回行えば済むわけだから、データ解析のときは入力信号のFFTを行い、その積を再びFFTに入れる、という処理だけでいいはず。とはいえ、FFT2回分の計算量がどの程度になるのか。
 実際のレーダーでは50MサンプルくらいのFFT解析になるのかもしれない。とてつもない分量。

 非常にざっくりとした数値だけど、今回の16384ポイントの相関処理では、forだと220msec、FFTだと8msec程度で終わっている。かなり早い。

 入力信号、基準信号、出力波形は実部のみだが、入力信号と基準波形の結果、それとその積は虚部も必要になる。PCで処理するなら問題にならないデータ量だが、組み込み機器、特にワンチップマイコンで外付けRAMなしの場合は非常に厳しいと思う。
 必要なメモリは6x入力信号くらいか。入力信号が16384サンプルなら、必要なメモリは384KiB程度(float32の場合)。他に一時的なテーブルが必要になる。FFTのIN/OUTを16bitで計算するならその半分としても200KiB程度必要になる。
 STBeeF4miniだと100KiB程度しか使えないので、8192ポイント程度しかできないか? 8kポイントだと距離にして1.5m程度のゲート幅しか得られない。ただし8kポイントを取得するのに10msecかかるから、FFT2回が6msec程度で終わるなら結果を記録するメモリが足りる分だけ延々とサンプリングできる。


 まずはARMコアでFFTがどれくらい動くかを確かめる必要があるか。DSP_LibにFFTっぽいライブラリがあるけど、使ったことがないのでちゃんと動くかどうかをテストしなければ。それがちゃんと動けば、適当な値でFFTを通して処理時間を測って。十分に短い時間で処理できるならいろいろ遊べそうだ。
 FFTとかちゃんと動くなら、適当にパッケージの大きなF4を買ってきてSRAMを外においたほうが使いやすいかもしれない。
 別件でFFTがあると便利そうだと思ってる部分があるので、それも含めてFFTの評価は軽くやっておきたい。

2018年5月27日日曜日

独自float16

 マイコンで生成したint64_tの配列をPCに転送したいので、独自の浮動小数点を考えてみた。

 似たような規格として、既存の半精度浮動小数点数があるが、マイコンでのサポートがない(多分)、表現できる範囲が狭い、といった問題がある。
 前者に関しては、それっぽい値に変換するライブラリをかけば済む話だが、後者が問題で、IEEE 754の半精度浮動小数点数は10進でたかだか4.8桁程度しか表現できない。int64_tは10進で18.9桁ほどあるので、圧倒的に足りない。
 また、一般的な浮動小数点は1未満かつ-1より大きい値を表現できるが、あくまで整数の中間表現がほしいだけなので、1未満の分解能は必要ない。ということで、符号1bit、指数6bit、仮数9bit、の16bit浮動小数点を考えた次第。

 これにより、8バイトの64bit整数を2バイトで表現できるようになったので、4分の1のメモリサイズで済む。

 値が小さい時(<1024)は少し特殊な処理をしている。
 たぶん最小値(-2^63)を与えるとバグる。
 今回はそんな端っこまで使う予定はなかったので省いた。というかint32でも足りるんじゃないか、程度の範囲しか使っていないんだよなぁ。浮動仮数幅浮動小数点、なんてのもありかもしれない。

using original_float_16 = uint16_t;

original_float_16 convert_to_BIN(int64_t value)
{
    const bool is_negative(value < 0);

    if (is_negative)
    {
        value = -value;
    }

    uint16_t bit_data(0);

    if (value < 1024)
    {
        bit_data = value;
    }
    else
    {
        uint_fast8_t i(53);
        while (i > 0) // exit with break
        {
            if (value & ((uint64_t)1 << (i + 9)))
            {
                bit_data = ((i + 1) << 9) | ((value >> i) & 0x01FF);
                break;
            }
            i--;
        }
    }

    if (is_negative)
    {
        bit_data |= 0x8000;
    }

    return (bit_data);
}

int64_t convert_from_BIN(original_float_16 BIN)
{
    const uint_fast8_t sign((BIN >> 15) & 0x01);
    const uint_fast8_t exponent((BIN >> 9) & 0x3F);
    const uint_fast16_t fraction(BIN & 0x01FF);

    const int64_t result(exponent
                             ? (uint64_t)(fraction | 0x0200) << (exponent - 1)
                             : fraction);

    return (!sign ? result : -result);
}

 もうちょっとうまくかけそうな気もするけど、とりあえず動いてるので。

 fp→intは各ビットを取り出してビットシフトするだけなので比較的簡単。
 int→fpは最上位ビットの位置を探す必要があるのでループしてる。最上位ビットの位置を1クロックで返す命令とか無いものか。

2018年5月22日火曜日

1Wireで複数デバイス

 温度センサが欲しくなったので、1Wireで遊んでみました。
 基本的には以前のエントリで書いた方法と同じです。

 STM32F4で、通信にはUART TX/RXを使い、通信時はTXをAF_ODに設定、ストロングプルアップはTXをOUT_PPに設定、0を出す時は0x00を送信、1を出す時は0xFFを送信、受信時は0xFFを送信し、0xFFなら1、それ以外なら0、リセットは0x00を送信、という感じです。
 ボーレートはリセット時が18.75kbaud、送信時が125kbaud、受信時が180kbaudです。送受信は100kbaud固定でも大丈夫っぽいです。
 プルアップ抵抗は4.7kですが、ストロングプルアップの動作確認のために47kでプルダウンしています。電源は3.3V系で、通常は4.7kと47kの分圧抵抗で3.0V付近ですが、ストロングプルアップが有効になると3.3Vになります。
 ストロングプルアップの動作確認にはオシロスコープが必須です。ストロングプルアップは、仕様では10usec以内に有効にしろ、とのことですが、これはかなり厳しいです。無いよりは良いでしょ、くらいの気休め程度です。
 バスのデコードにはZEROPLUSのLAPが使えます。バスプロトコルでLSBファーストに設定すること、サンプリングポイントを適切に設定すること(今回は12us)、あたりに気をつければ大丈夫なはずです。

 今回、テストにDS18B20を使いましたが、デバイス側から0を送信する時のパルス幅がかなり狭いです。だいたい15usくらいしかプルしてくれません。そのため、ZEROPLUSデフォルトのサンプリングポイント30usだと適切にデコードできません。

 リセット時は、0x00を送信し、2バイトを受信できれば正常です。1バイトだけの場合はデバイスが接続されていません。1バイトも受信できない場合は回路に異常があります(TXとRXが接続されておらず、ループバックしていない)。


 最初、SEARCH ROM COMMANDの実装を自分で考えていたのですが、うまく動かないので、"マイコンの1線2線3線インターフェース活用入門"という本の28ページに有る図1-8のフローチャートを実装しました。


 とりあえず、DS18B20を7個、ブレッドボードに配置してテストしてみました。起動後にSEARCH ROM COMMANDでアドレスをスキャンし、その後温度変換コマンドを打ってメモリを読み出します。

"
search ROM:7
Device  0 OK  28 90 F2 6D 08 00 00 A2
Device  1 OK  28 68 D7 6E 08 00 00 D3
Device  2 OK  28 84 87 6E 08 00 00 12
Device  3 OK  28 62 FB 6C 08 00 00 C2
Device  4 OK  28 F2 68 6D 08 00 00 05
Device  5 OK  28 F1 63 DE 03 00 00 F4
Device  6 OK  28 09 B8 6E 08 00 00 59
---
Device  0 OK  CE 01 4B 46 7F FF 02 10 4B +28.9 degC
Device  1 OK  CE 01 4B 46 7F FF 02 10 4B +28.9 degC
Device  2 OK  CE 01 4B 46 7F FF 02 10 4B +28.9 degC
Device  3 OK  CD 01 4B 46 7F FF 03 10 4A +28.8 degC
Device  4 OK  CE 01 4B 46 7F FF 02 10 4B +28.9 degC
Device  5 OK  BC 01 4B 46 7F FF 04 10 D2 +27.8 degC
Device  6 OK  CC 01 4B 46 7F FF 04 10 67 +28.8 degC
"
 という感じの結果が得られました。DS18B20のファミリーコードは28hです。OKとあるのは、CRCチェックをパスした、という意味です。
 正常にアドレスが読み出され、メモリ読み出しも正常です。
 7個中、Device 5だけ温度が大きく異なります。他の6個はTO-92パッケージですが、Device 5はステンレス管に入れた耐候性の高いタイプです。設置場所はほぼ変わらないので、ここまで温度が変わるのは謎です。

 今回はパラサイトパワーを使っているので、温度変換中はストロングプルアップが必要になります。温度変換には1秒弱かかるので、センサ1つから温度を読み出すのにおよそ1秒かかります。7個あるので、7秒です。
 1Wireの温度センサはそれほど高速に温度が変わるような場所に使うものではありませんが、それでも1組のスキャンにデバイスの数だけ時間がかかるので、センサが何十個も有るような構成には向きません。とはいえ、I2CやSPIの温度センサを何十個も並べるのは大変ですから、そういうところこそ1Wireを使うべきなのかも知れませんが。

 1Wireの欠点の1つは、そのアドレスのデバイスがどこに有るかがわからない、という点です。このあたりは、活線挿抜で増えた/消えたデバイスを探し、そのアドレスと設置場所を紐付ける、といった事が必要になると思います。


 とりあえず、件の温度センサを使いたいというヤツは、センサ1個あれば十分なので、SEARCH ROMとかも不要だったのですが、興味本位で実装してみました。と言っても本のコピーですけども。

 さて、次は久しぶりに超音波距離計とかやりたいな。暖かくなってきたので、外に出て長距離のテストとかもやりやすい季節です。


2018年5月12日土曜日

ペパクラAIM-9/AIM-120

 AIM-9のペーパークラフトを作ってみた。
 Luaスクリプトで図形を書いて印刷できるソフトウェアを作っているので、AIM-120はスクリプトで書いた。AIM-9も似た形なので、スクリプトを流用できる。


 下が前に作った1/5スケールのAIM-120C/D live ammo、上が今回試作した1/5スケールのAIM-9M。今回は試作なのでフィンは2組4枚しか作っていない。
 AIM-9Mの動翼(前側)は、実際にはくびれた形状だが、スクリプトの関係で今回はくびれのないデルタ翼になっている。
 フィン(後ろ側)はクリップドデルタなので、簡単につくれる。が、ローレロンとかは作れていない。そのあたりは追々。

 AIM-9Mの図面は、なぜかロシア語サイトに詳しい寸法が入った図があった。ロシア語に翻訳されているから、寸法もミリメートルになってる。注釈は全く読めないけど。


 実際のAIM-9とAIM-120の写真を見てみると、確かにこれくらいのスケール感なのがわかる。長さはAIM-120が12ft、AIM-9は9.3ftなので、1.3倍くらいしか違わない。
 直径では、AIM-120が7in、AIM-9が5inで、こちらも1.4倍程度。ただし、円の直径での1.4倍は面積で2倍の差になる。
 実際には、AIM-120C/Dは356lb、AIM-9Mは188lbで、約1.9倍の差になる。直径で2倍、かつ長さが短いので、AIM-9はもっと軽いはずだが、若干密度が高い。

 こうしてみると、AIM-9のフィンが異様に大きい。AIM-120C/Dのウイングスパンは19in、AIM-9は25inなので、120C/Dに比べて9のほうが幅が広い。
 AIM-120C/Dのデルタ翼を切り落としたのはF-22のウェポンベイに入るように、という事だが、逆に言えばAIM-120C/Dより幅が広いと、ウェポンベイに6本入れることができない。つまり、F-22のウェポンベイにAIM-9Mを6本入れることはできない。まぁ、ドロップランチャーでAIM-9Mを撃てるのか(撃つ意味があるか)という問題があるが。
 F-22のウェポンベイに9Xを入れたら、空対地ミサイルとして使用できる、という利点はあるかもしれない。地上車両や小型ボートを攻撃できるから、SDBと似たような使い方ができ、かつ、GBU-39 SDBと違って、外部からの支援の必要なく移動目標に対して攻撃することができる。そして、9Xは元々がAAMだから、自衛用にも転用できる。ま、それこそ「意味があるか」という話になるが。ただでさえ高コストなF-22を空対地攻撃専用に割り振るには、ねぇ。

***

 5分の1スケールのAIM-120はそれなりに大きくて、愛でていても楽しいのだが、大きさがいまいちわからない。ということで、試しに7分の1スケールのAIM-120を作ってみた。


 同じスケールの人間(身長約170cm)と比べてみると、かなりデカい。
 長さが人間の2倍くらいある。アニメキャラは身長は忠実だけど、太さ方向はあんまり実際の人間に忠実ではないので、そのあたりは参考の程度に。


 1/5のAIM-120はA4用紙4枚が必要になるが、1/7のAIM-120はA4用紙2枚に収まる。切り出すのも大きな定規が必要ないので、かなり作りやすい。
 直径的には、モデルロケットのモーターより1cm近く太いので、モデルロケットとしても良さそう。
 ちなみに、1/5 AIM-9MはA4用紙4枚の見込み。フィンが大きいので、AIM-120より小さいのを相殺してしまう。

2018年5月7日月曜日

ST17のネットワークのデータフォーマット

 ST17のネットワークモードのデータフォーマットを調べてみた。
 バージョン1.0.8.7のソフトウェアによる。
 ST32は別のプロトコルかも。バージョン変わるとフォーマットも変わるかも

 通信プロトコル(TCP, UDP等)とポートの確認にはWiresharkを使った(久しぶりに使ったけど、日本語化されてて便利)。

 通信はポート62739番のTCPで行われていて、このポートでTCPサーバーを立てればST17をクライアントとして接続できる。
 基本的にプリフィックス"@"で始まり\r\nで終わる文字列が与えられる。
 適切にACKを返さないと、クライアントがメモリリークしてしぬ。ACKは固定パターンで良いっぽい。詳しくはWiresharkで参照のこと。

 .Netの特性なのか、ネットワークドライバの特性なのか、Wiresharkの特性なのか、同じIPアドレス間ではWiresharkでキャプチャできない。PCを2台用意して、1台でST17をサーバーとして動かし、もう1台でクライアントとして動かし、PC間の通信をWiresharkでフィルタリングするとやりやすい。
 サーバー側を自分で作るなら、データを受信したら固定パターンのACKを返すだけでとりあえず動作するので、あとは得られた文字列をデバッグダンプしておけば、データを人力デコードできる。


 ポーリングデータのフォーマットは以下の通り。

 とりあえず大体のデータは得られるが、移動する的の場合、的の位置といった情報は得られない。固定的でも、的の大きさと言った情報は得られないはず。

 他に、"@D"で始まる弾着データが送られてくる。これには何発目か、スコアはどれくらいか、時間はいつか、エネルギーや弾速はどれくらいだったか、座標はどこか、といったデータが含まれる。位置情報は液晶のピクセル位置で得られる。

 ST17のネットワークモードはよくわからない。ソフトウェアを片方サーバー、片方クライアントにして接続してみても、何ができるかよくわからない。

***

 以前、ST17の的を自分で作ろうと思った時は、hWind総当たりでST17のターゲット画面をキャプチャして、それを画像解析して弾着位置を調べよう、と思ったことがある。
 確かノートPCで15fpsくらいしか処理できなかった気がする。冷却ファンの音も凄まじかった。

 TCPで情報が得られるのがわかったので、画像処理より格段に簡単な方法で情報を得ることができる。

 エアガンショップのレンジにST17が置いてあるくらいなら、ブルズアイターゲットとかタクティカルターゲットでも良いのだけど、頻繁にST17を使える環境にいると、ST17の的は致命的なまでにバリエーションが少ない(遊び方の問題かも。精密射撃しかしないなら足りるだろうし)。
 
 ということで、気が向いたらST17のサーバーを作って、自由に的を表示できるソフトを作りたいな、と思っている次第。
 いつになるやら。冬のインドアシューティングがメインになる時期までにはどうにかしたい。

2018年5月2日水曜日

ST32のドライバ

 名前が似てるけど、STM32ではなく、Stealth-Target ST32のドライバの話。

 ST32には、ST32と、ST32Rの2種類がある。
 現行品はST32Rかな? どちらも注文できるのかも。
 オンラインサポートからST17やST32のソフトウェア/ドライバをダウンロードすることができるが、現時点ではST32Rのソフトウェア/ドライバをダウンロードすることができない。

 そのため、ST32Rを使う場合は、付属のCDからインストールする必要がある。

 また、場合によってはソフトウェアをインストールしただけでは接続できないかもしれない。その時は、デバイスマネージャからドライバの更新を行い、フォルダを選択してインストールする。

***

 ST17とST32、違いは大きさくらいかな。ソフトウェアに関してはレイアウトの違いはあれど、主な機能の違いは無いはず。
 射距離10m程度で精密射撃を行うなら、ST17で十分だと思う。
 タクティカルターゲット(greenとかthreeとかcircleとか言われるのでそれを撃つ)をやるなら、ST32のほうが面白いと思う。

 10mレンジくらいで使うなら、HDMIはただの長いケーブルで接続できる(相性問題はあるだろうが)。VGAの場合は、あまり長いと画質が悪くなるだろうが、同時に見る人間も遠いから、あまり気にならない。
 USBの方は、5m程度を境に、それより遠くに置きたいなら、レピータを使う必要がある。素のケーブルを伸ばすと「デバイスの接続は正常なのにソフトウェアから認識できない」みたいな中途半端な問題を起こす。

***

 STシリーズは、自分で的を作れれば面白いと思うんだが、そういう機能はなさそう。

 ネットワークモードを使えば、ソフトウェアをTCP/IPクライアントとして使うことができる。C#とかでサーバーを作れば、ASCII文字列が送られてくる。
 通信自体は簡単だが、メッセージ1個を受信するたびにACKを返さないと、気がついたらソフトウェアがクラッシュしてる。
 メッセージ自体はそんなに複雑ではないので、モードを切り替えたりしながらメッセージを見てみると、だいたいフォーマットがわかってくると思う。

 適当なスクリプトを作れば的を表示したり得点を表示したり、というプログラムを作りたいけど、ちょっと別件が忙しくてそっちにかかりっきり。