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を返さないと、気がついたらソフトウェアがクラッシュしてる。
 メッセージ自体はそんなに複雑ではないので、モードを切り替えたりしながらメッセージを見てみると、だいたいフォーマットがわかってくると思う。

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

2018年4月6日金曜日

BLH/UTM/MGRSデータセット

 緯度経度高度(BLH/LLH)とUTM、MGRSのデータセット。
 適当に乱数で作った8地点と、UTM/MGRS的にヤバそうな地点のフチの部分。数が少ないのであんまりテストデータには向かないかも。

 データは以下の構造体に入ってる。

typedef struct Position_data
{
    struct
    {
        double latitude;
        double longitude;
        double height;
    } LLH;
    struct
    {
        double x;
        double y;
        double z;
    } ECEF;
    const char *const MGRS;
    // if MGRS is null, UTM is invalid
    struct
    {
        uint8_t zone_lon;
        bool is_north_side;
        double east;
        double north;
    } UTM;
} Position_data;

 LLHはLatLonが度単位、heightがメートル単位、ECEFはすべてメートル、MGRSはdigit10(5:5)でゾーンはゼロ埋め、UTMは1メートル分解能。
 MGRSは文字リテラルへのポインタだが、UTM/MGRSに変換できない地域(北緯84度以上の北極周辺と南緯80度以上の南極周辺)の場合はMGRSにヌルポインタがセットされる。


 LLHとECEFの変換には以下のサイトを使った。
 Latitude/Longitude/Height <--> ECEF via J-Script BLH

 LLHとUTM/MGRSの変換には以下のサイトを使った。
 Convert between Latitude/Longitude & UTM coordinates


 とりあえずLLHとECEFは「GPSのための実用プログラミング」という本のソースコードを参考にしたプログラムでチェックしている。LLHとUTMはwikipediaの式を参考にしたプログラムでチェックしている。UTMとMGRSは上記リンクのJavaScriptコードを参考にしたプログラムでチェックしている。

 この本のプログラムでは、ECEF→LLHで、sqrt(x*x+y*y)<1の条件の際にheightの値が不正になる不具合がある(正確には、sqrtが数十cmを下回ると急速にheightのエラーが大きくなる)。某F氏が作者の承諾を得てC++に移植したコードでもそのまま残ってる気がする(未確認)。
 上記LLH-ECEF変換のサイトのJavaScriptコードを見てみると、sqrt(x*x+y*y)が一定以下の場合は特殊な処理を行っているらしい。とりあえずsqrt(x*x+y*y)<1e0の場合はheight = fabs(z) - a * (1 - f)で対応した。
 データセットでは(0オリジンで)2番めがx=1程度になるようにしている。3番目と4番目は地軸直上のそれぞれ北極側、南極側にしている。データセット2をPASSして3でFAILするならこの部分の問題がある。


 UTM/MGRSではノルウェー南西付近とノルウェー領スヴァールバル諸島の付近に特殊な処理をしている。データセットの内10セットはこの地域の経度方向の区切り付近のデータになっている。


 日本で位置情報を扱う国土地理院としては、災害時の地図情報としてMGRSを使いたいらしい。ただし、なぜかMGRSとは言わず、UTMと言っている。「昔はUTMを使っていたから、MGRSもUTMと言い張る」みたいな理論らしい。ミリタリーと名の付くものを嫌ったのか? とにかく、諸外国のUTMと地理院のUTMは別物なので要注意。
 地理院が防災・災害時にMGRSを使いたがっている割には、MGRSの計算方法とかの情報が極めて少ない。今回一番参考になったのはwikipediaの計算式と先のリンクのJavaScriptコードだった。大丈夫か国土地理院(地理院のWebサイトにあるPDFにUTM変換のJavaScriptコードが有るんだけど、非常にわかりずらい)。


/***

 最近、何もやる気でないけど、何もしないには暇すぎる、という状況。ということで暇つぶしに位置座標関係のソースコードを書いたりしてる。
 JSF++のコーディングルールを意識したコードを書いて、CppUnitでテストしている。TDDという程じゃないが。

 CppUnitはかなり使いづらい。基本的にCPPUNIT_ASSERT_EQUALを使って、多重定義で各種の型に対応しているようだが、例えばuint8_tを渡すと文字として処理される。結果、FAILすると期待値や結果がASCIIで表示される。期待値や結果が2とか8だったりすると、文字としては何も表示されない。わけがわからない。
 他にも、const char*と別のconst char*を渡すと、常にFAILする。これは「ポインタが渡されたんだな!よし、ポインタを比較するぞ!。別のアドレスを指しているな?FAILだ!!」みたいなことらしい。文字リテラルと文字リテラル同士の比較なら文字列として比較されるが、そんなもんはテストする意味ない。結局、trueと0 == strcmp(expected, actual)の比較を行うしか無い。ただし、boolの比較の場合、trueとfalseの表示ではなく、0と1で表示される。わけがわからないよ。

 テストと並行して開発していくと、コーディングよりもテストケースを作るほうがはるかに時間がかかる。コーディングのほうが時間がかかる場合は、そもそもコード規模が大きすぎる(あるいはテストが少なすぎる)ので、プログラムとして不適切。
 結果、コードの品質を保証するにはテストをするべきだが、テストケースを作る行為に非常にコストがかかる。テストをしたからと言って、テストした点以外の場所にでかい虫が隠れてる可能性もあるし。
 そりゃF-35も納期伸びてコスト上がるわけだ、みたいな感想。
 F-35はEO-DASで敵機や見方機の追跡、EOTSで地上目標の探知・識別、AESAで空中/地上目標の探知・識別ができるらしい。ということは、様々なデータを処理するためのソースコードが有るわけで、それについてもちゃんとテストが行われている。はず。テストデータだけでもいったいどれだけ有るのか。
 F-35のソフトウェアは開発中だから、「値下げしろ」と言われれば、ソースコード開発のどこかを切るしか無い(ハードウェアの設計は大体終わってるはずで、そこでコストカットは厳しいはず)。となると、「値下げしろ」と言われて以降のソースコードは、結構ヤバイのかも知れない。

 JSF++を読んでると、コード例で<LM_string.h>というヘッダがインクルードされてる。おそらくロッキード・マーティンが作成した文字列処理に関するライブラリなのだろう。こういうレベルから作っていくんだから、防衛装備品は大変だなぁ、と思う次第。
 例えばJSF++ではstdio.hは使用が禁止されている。他にも時間処理系のライブラリも使用禁止。そういうのを全部作らなきゃいけないんだから、大変だよなぁ。

***/


 データセットは500行くらい有るので以下。


2018年3月31日土曜日

日本周辺LatLon/MGRSデータセット

 久しぶりの更新です。年度末駆け込みエントリ。駆け込むような内容じゃないけど。


 LatLonとMGRSのデータセットを作ってみました。LatLonとMGRSの変換コードとかを書く人はプログラムのチェックとかに使ってみてください。後述するように、日本周辺しかカバーしていないので、ノルウェーとかあの辺りはカバーしていません。

 変換は地理院地図で手作業でやってます。なのでどっかミスってるかも。簡単にチェックした限りでは大丈夫だと思いますが。

 地理院地図


 * データ範囲
 16Nから56Nまで、120Eから156Eまでの矩形。
 GZD(grid zone designator)で51から56、QからUの範囲です。

 1列目:データ番号
 2列目:北緯
 3列目:東経
 4列目:MGRSコード(4+4digit)

0 44.96186516 153.1542827 56TNQ12167872
1 18.7714042 143.0560952 54QYF16737678
2 46.43152517 123.8722651 51TWM67014236
3 34.57617141 151.1546819 56SLD30732759
4 46.80607313 138.1273513 54TTS80828762
5 50.97522775 150.444036 56ULB20565018
6 42.34817779 153.0308327 56TNM02538843
7 28.306578 123.9823699 51RWM96313155
8 34.93075751 146.0695517 55SDU15026575
9 40.8256208 149.5492312 55TGF14962252
10 51.74925858 131.2469221 52UFC55103553
11 45.85395448 134.5753307 53TML67037790
12 18.35453617 136.3214108 53QPA39612991
13 32.71391875 123.9806842 51SWS91901999
14 22.55759173 143.1845675 54QYK24639619
15 28.52762554 139.3176863 54RUS35395680
16 24.13443419 153.2556589 56RNM25976913
17 30.44526514 132.6153275 53RKP71007054
18 39.47509494 126.0155309 52SBJ43287375
19 26.53095451 140.4341724 54RVQ43623461
20 16.44206381 124.1708383 51QXU24991819
21 17.78883466 147.1096265 55QEV11616682
22 32.24670767 152.1072662 56SMA15906813
23 26.37228208 135.0063959 53RNK00631691
24 52.45606586 142.9168586 54UXD30241349
25 34.61370299 150.7056936 56SKD89643259
26 43.2473821 125.2140275 51TXH79739066
27 21.25670397 134.2805044 53QMD25355072
28 50.14047509 125.5887281 51UXR84975745
29 38.2773181 120.8249588 51SUC09743882
30 44.53458017 129.6540982 52TEQ51963145
31 20.37395729 135.9317315 53QNC97235313
32 19.98191373 143.7686473 54QYH89731187
33 55.45592321 135.3999101 53UNB25294560
34 46.61256741 135.1153209 53TNM08836211
35 16.49164717 131.7868028 52QGD97522537
36 42.99508508 128.4849595 52TDN58016039
37 46.84641436 144.9882321 55TCM46619006
38 41.34109508 128.2280872 52TDL35417691
39 29.76365353 143.5512955 54RYT46689532
40 53.87005009 154.8711544 56UPE23027068
41 49.49386466 144.6834442 55UCQ32248493
42 43.36724466 129.3866424 52TEP31320167
43 34.01364544 139.2004572 54SUC33836512
44 39.56023913 137.6020481 53SQD23548218
45 26.59305316 142.3502875 54RXQ34454207
46 50.01263422 147.4384549 55UER31414012
47 33.34293228 123.3076479 51SWS28628934
48 29.11023704 122.2793269 51RVN29882041
49 35.32740487 126.8219815 52SCE02031152
50 54.11905341 143.0157117 54UXE31749864
51 28.36128872 120.203023 51RTM25854040
52 35.67839253 122.7087131 51SVV73644831
53 18.86014498 125.0677693 51QYA17858662
54 51.57016518 141.9936974 54UWC68861370
55 53.49512464 139.52631 54UVE02232836
56 30.7216343 153.6808136 56RNU65189895
57 38.19484205 135.3814567 53SNC33402750
58 46.71081244 143.8655927 54TYS19027701
59 22.80537597 126.2490146 52QBL17592460
60 23.64109735 145.6131361 55QCG58541517
61 21.97749607 122.8904952 51QVE88693034
62 40.97157846 155.1397791 56TPL80043780
63 55.63164644 150.4769347 56ULG41166797
64 34.11977345 141.4619827 54SWC42607553
65 37.60524134 126.0650445 52SBG40916606
66 28.02268182 136.9628807 53RPM92970126
67 40.45923319 148.6484249 55TFE39768003
68 38.9547951 128.7149847 52SDJ75301179
69 16.36678463 148.7955161 55QFU91771035
70 43.17482058 122.3411788 51TVH46458043
71 32.27967715 127.3031004 52SCA40197270
72 31.18582796 147.4932757 55REQ46995030
73 52.48425027 120.1984457 51UUU09771859
74 30.2980949 150.3743381 56RKU47475473
75 39.22704779 143.4443602 54SYJ10994482
76 28.08577903 140.7503122 54RVS75460672
77 23.51874727 131.4197272 52QGM47070302
78 17.16803188 147.2879577 55QEU30629816
79 45.07334959 122.4427623 51TVK56139124
80 43.36859054 142.8083557 54TXP46510333
81 26.76202605 130.7391816 52RFQ72936125
82 26.2274406 154.6776849 56RPQ67580195
83 49.09472783 128.2637125 52UDV46243824
84 31.29198709 155.4945263 56RQV37446464
85 51.58023875 121.8532846 51UVT20541497
86 48.23330635 123.6334546 51UWP47034242
87 45.35690253 135.3736399 53TNL29262266
88 55.94809064 145.7523634 55UDC22080100
89 42.65429001 150.3310178 56TKN81232587
90 47.6126432 128.498543 52TDT62317337
91 47.05889011 135.313476 53TNN23801175
92 19.56330254 135.301159 53QNB31586318
93 20.41021724 151.3785403 56QLH30815771
94 21.36599532 133.9048001 53QLD86456304
95 44.69100228 129.04793 52TEQ03794862
96 17.15687852 133.3283063 53QLU22189767
97 34.47526915 150.9530766 56SLD12011675
98 43.6421101 153.4284252 56TNP34553221
99 54.50784753 151.1125419 56ULF77794166

2018年3月14日水曜日

UARTのHALで直接割り込み

 久しぶりにHALでUSART割り込み使おうとして覚えてなかったのでメモ。

 HALのUSARTではTXE(Transmit Empty)とRXNE(Read Not Empty)のコールバック関数はない。これは、HALではHandleにバッファのポインタを渡し、HALの中でそこに(/から)転送する、という処理になっているため。
 なので、FreeRTOSのQueueとかで転送したい場合は、自分でUSARTx_IRQHandlerの中身を書く必要がある。

if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE))
{
    extern osMessageQId stdin_queueHandle;
    uint8_t data = huart1.Instance->DR;

    xQueueSendFromISR(stdin_queueHandle, &data, 0);

    return;
}

if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TXE))
{
    extern osMessageQId stdout_queueHandle;
    uint8_t data = 0;
    BaseType_t status = xQueueReceiveFromISR(stdout_queueHandle, &data, 0);

    if (status == pdPASS)
    {
        huart1.Instance->DR = data;
    }
    else
    {
        __HAL_UART_DISABLE_IT(&huart1, UART_IT_TXE);
    }

    return;
}

 この処理はおそらくHAL_UART_IRQHandler(&huartx);の前に書く必要がある。
 また、TXEをチェックしてからRXNEをチェックすると、すでにRXNEがクリアされてる、という謎の挙動があるので、RXNEを先にチェックする必要がある。


 送信の場合は、xQueueSendでデータを入れた後に__HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE);でTXEを有効化し、割り込みの中でDRに転送している。TXEが有効でかつDRにデータがない場合、たぶんずーっと割り込みが回り続ける。なのでQueueが空の場合はDISABLEで停止し、QueueSendの後に都度ENABLEする、という手順にしている。

 受信の場合は、適当な箇所で__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);で割り込みを有効化しておく。RXNEは、受信しない限り割り込みが発生することもないので、ずっと有効状態でいい。


 注意点はあまり多くないが、割り込みのチェック(__HAL_UART_GET_FLAG)に渡す値と、割り込みの有効化/無効化(__HAL_UART_ENABLE_IT等)に渡す値は別の値であることに注意する必要がある。