HarfUART - askn37/askn37.github.io GitHub Wiki
最小フットプリントの、二線式半二重UART通信HarfUART_Class
基本クラス。
Arduino互換APIの<HardwareSerial.h>
を代替する。
プロトコルは 8N1 専用。
割込も計時器周辺機能もFIFOバッファも使用しない。
従って全体割り込み禁止中でも、割込ハンドラの中からでも使用できる。
reduceAVR は未対応
有効ならENABLE_CLASS_HARFUART
マクロに1が定義される。
クラスインスタンスはスケッチ.ino
では自動で設定され、無宣言で使用できる。
そうでなければ<peripheral.h>
({build.variant}
に置かれている)
をインクルードすることで準備される。
/* #include <peripheral.h> */ /* *.ino 以外 */
void setup (void) {
Serial.begin(CONSOLE_BAUD); /* {build.console_baud} */
}
void loop (void) {
if (Serial.available()) Serial.write( Serial.read() );
}
このクラスは半二重通信用なので、送信と受信は同時には行えない。 ただし送信も受信も USART周辺機能が1文字分の緩衝バッファをハードウェア的に持つため、 1文字分の送受信時間内に限って全二重動作となる。 受信についてはその緩衝バッファが損なわれる猶予時間内に受信文字を読み出せなければ、受信落ちを経験するだろう。
行単位で着実に送信受信方向動作を切り替えるならreadBytes
を用いるハンドシェイク動作とするのが良い。
前述の用例よりこちらのほうが実用にはより適する。
void setup (void) {
Serial.begin(CONSOLE_BAUD);
}
void loop (void) {
size_t length;
char buff[INTERNAL_SRAM_SIZE / 4]; // 適当な大きさの1行ぶんのバッファメモリ
length = Serial.readBytes(&buff, sizeof(buff), '\n'); // `\n` 受信で方向切替
if (length) Serial.write(&buff, length);
}
組込用途での UART利用はあらかたハンドシェイク動作なので全二重対応が必要な場面は少ない。
依存性:<avr/io.h>
<Portmux.h>
<UsartBaseClass.h>
<peripheral.h>
デフォルトインスタンスを指すSerial
は通常、Serial0
を指すエイリアスである。
更にSerial0
は普通Serial0A
を指すように定義されている。
Serial
はボードサブメニューやブートローダー選択に応じて Arduino IDE によって書換えられる。
<peripheral.h>
で定まる事前定義は次のようになっている。
対応する TX/RX 入出力外部端子ペアは一意に決まっているので、
そのバリアントで使えない定義は存在しない。
定義されていても実体が使われないインスタンスは
最終出力 HEX/bin には書き出されない。
#ifndef Serial /* {build.console_select} がない場合 */
#define Serual Serial0 /* 代表エイリアス */
#endif
#define Serial0 Serial0A
#define Serial1 Serial1A
#define Serial2 Serial2A
/* ~ */
/* インスタンス宣言 */
HarfUART_Class Serial0A {&USART0, &_portmux_usart0a}; /* USART0_DEFAULT */
HarfUART_Class Serial0B {&USART0, &_portmux_usart0b}; /* USART0_ALT_1 */
HarfUART_Class Serial0C {&USART0, &_portmux_usart0c}; /* USART0_ALT_2 */
/* ~ */
/* 実際に参照したインスタンスだけが最終バイナリに結合される */
Serial.begin(CONSOLE_BAUD);
プライマリが
0
、以後追加ポート以降に1
2
... の識別子が付く。
PORTMUX の DEFAULT選択がA
、ALTERNATE_1がB
、以後C
D
... の接尾子が付く。
インスタンスが複数あっても同一周辺機能なら同時使用できないことに注意。
どの型番でどの周辺機能が使用できるかは [modernAVR 周辺機能比較一覧] を参照のこと。
UARTの使用を、任意のボーレート値で開始する。
自身のオブジェクトを返すので、メソッドチェーンを後続できる。
ボーレート値から実際に必要な設定値を計算してinitiate
を呼び出すため、
事前コンパイル済定数を与えないと除算ライブラリが結合されることに注意。
Serial.begin(CONSOLE_BAUD);
F_CPU
によって動作可能速度が定まるため、計算範囲外となる値では正常動作しない。
CPU主クロックは設定ボーレートの 8倍以上 8192倍未満でなければならない。
ただし限界付近ではマージンが不足するため安定しない。
UARTの使用を、指定の定数ラベルで開始する。
自身のオブジェクトを返すので、メソッドチェーンを後続できる。
定数ラベルは<Portmux_private.h>
で事前計算され定義されている。
除算は使われない。
Serial.initiate(UART_115200);
/* etc */
Serial.initiate(UART_CONSOLE_BAUD);
F_CPU
によって動作可能速度は定まるため、計算範囲外となる定数ラベルは定義されない。
UART_CONSOLE_BAUD
は事前定義マクロCONSOLE_BAUD
を元に生成されるが、
OSC-ULP 動作時は最大 2400bps、1200bps、あるいは 300bps の設定で上書きされるだろう。
F_CPU
が 2400 未満では、Arduino IDE のシリアルモニター(下限300bps)とは通信できない。
UART装置の使用を停止し、専有していた外部端子を開放する。 RX線の内蔵プルアップも無効になる。
- 一般に、UART対向機器の電源が落ちている場合は TX線が Hi-Zか LOWになるだろう。
これを調べるには TX端子をデジタル入力許可に変えなければならない。
end
後はそれが可能な状態になる。
1キャラクタを出力する。 送信緩衝バッファが空いていなければ空くまで待つ。 常に1を返す。 全体割込が禁止されていても動作する。
指定のバッファから指定の量を書き出す。書けた量を返す。 全体割込が禁止されていても動作する。
1キャラクタを入力する。 入力が空かフレームエラーがあれば -1 を返す。 全体割込が禁止されていても動作するが、 他の割込動作が2キャラクタ時間(20bit相当)以上継続すると、その間の入力は失われる。
指定の_buffer
に、最大_limit
文字数、
あるいは_terminate
指定キャラクタ出現までを取得する。取得されたキャラクタ数を返す。
_buffer
の最後に\0
は補完されないことに注意。
_terminate
指定キャラクタは取得内容に含まれる。しかし\0
は指定できない。
受信キャラクタが\0
であればそれは含まれる。
そしてBREAK と見做して受信待機を打ち切るだろう。
このメソッドが呼ばれると、少なくとも 20bit時間のあいだ通信線を監視して
IDLE 状態が続いたなら何も取得せずに終了し0
を返す。
つまりNOBLOCK動作であるから、必要に応じて繰り返し呼び出さなければならない。
逆に_buffer
に格納される最初の文字はその前に 約20bit長以上の IDLE 状態が検出されなければならない。
従って例えば シリアルGPSの NMEA出力は、概ね新たな行頭から採取されるだろう。
_swevent
が0
以外であるなら、それは事象システム EVSYS ストローブ指令と解釈され、
最初の受信キャラクタ検出の瞬間に以下の IOレジスタへ代入し、
任意の事象イベントを発火することが出来る。
tinyAVR-0/1 | megaAVR-0 | その他のmodernAVR |
---|---|---|
EVSYS_ASYNCSTROBE | EVSYS_STROBE | EVSYS_SWEVENTA |
各ビットが
EVSYS
チャネル番号[0-7]に対応。チャネル8番以上には対応していない。
発火イベントが割込を惹起する場合はそちらの実行が優先され、そして受信落ちを経験するだろう。 だが全体割込禁止中でもこのメソッドが機能することは留意されたい。
この機能は例えば 1PPS割込のない シリアルGPSで、秒タイミングを測るのに使える。
受信緩衝バッファに1キャラクタが揃っていれば(read可能なら)1を返す。 そうでなければ0を返す。 全体割込が禁止されていても動作するが、 他の割込動作が2キャラクタ時間(20bit相当)以上継続すると、その前の入力は失われる。
受信緩衝バッファが 1Byteしかないので、ある程度のバッファ量を溜めてから動作を進める実装は簡単に書けない。 自ずからそれは自前のバッファを用意して蓄積するコードを書く必要がある。
送信緩衝バッファが空いていれば(待たずにwrite可能なら)1を返す。 空いていなければ(前の送信実行中であれば)0を返す。 全体割込が禁止されていても動作する。
送信緩衝バッファが空でかつ送信完了になるのを待つ。 全体割込が禁止されていても動作する。
機能しない。常に -1 を返す。
最後に更新されたUSARTn_DATAH
の値を返す。
print文とその派生メソッドは<api/Print.h>
を参照のこと。
....
この他の、特にタイムアウトを伴うストリーム読込系の機能は用意されていない。