FSK下位レイヤ
出典: Wikimura
DDSをマイコン内のDMAやタイマから構築することができた。これにより、CPUパワーを全く使わずに500kHz程度のサンプリングレートを実現した。これを使って、FSK変調でデータを送るソフトウェアモデム(というか変調だけ)を作る。
ここでは、コーディングに入る前に色々と考えたことをメモする。
目次 |
FSK下位レイヤの必要性
FSKでは、デジタルの1,0を周波数の高低に対応付け、一定間隔に送り出すことでデータを伝送する。 ソフトウェアで実装する場合、データレートの間隔でDDSの周期を切り替えれば良い。
しかし、1200bpsなら1200回/sで割り込みがかかることを意味する。大したことはないが、このオーバーヘッドはもったいない。
それなら数ビットまとめて、1200Hzのタイマ更新イベントからのDMA要求により、周期レジスタの値を更新していった方がよいと考えた。USARTの場合、スタート、データ、ストップのフォーマットで10ビット必要となる。これなら割り込みは1秒に120回まで減らすことができる。バッファを増やせばもっと余裕が出るだろう。
ただ、ビットごとの転送でバッファを使う場合に、周期データバッファの容量が問題になる。全ての転送データを予め周期データにしておくと、ビット数 × 2バイトのメモリが必要となってしまう。これはタイマの周期レジスタ値が16ビットであるため。
そこで全送信データの内、先頭の数バイトを周期データに直そうと考えた。周期データバッファが空になったら、送信データバッファから補充する。
バイトからビットストリームへの変換
送信データバッファには送信したいデータそのものを入れる。しかし、FSK変調をかける際には、これをビットストリームに変換しなくてはならない。この際、上位レイヤのプロトコルに応じた処理が必要になる。
USARTの場合、スタートビットやストップビット、場合によってはパリティビットなどが付加されなくてはならない。
AX.25の場合、bit-stuffing("01111110"はスタート/ストップフラグであるため、この並びが他の場所で現れないように防ぐ)という処理をしなくてはならない。Bit-stuffingでは、'1'が5つ連続したら、その次に問答無用で'0'を付加しなくてはならないと定められている。また、フレームを構成するフィールドの中で、FCS(Frame Check Sequence)以外はLSBから送り、FCSだけはMSBから送らなくてはならないと定められている。
送信データバッファからデータを取り出し、ビットストリームへ変換する処理は、上位レイヤにお願いしなくてはならない。
コールバックで実装
上位レイヤが逐次下位レイヤを監視していては意味が無い。必要な時に上位レイヤの提供する関数が呼ばれるようにすれば良いのだから、これはコールバックで実装すべきと考えた。
コールバック関数にはいくつか引数が必要になると考えている。 分割して処理するため、コンテキストを保持するためにもデータが必要となる。 今のところ2つ考えている。 「ビットストリームバッファ」の情報構造体と、上位レイヤの情報構造体である。 両方ともポインタで渡せばよい。特に後者はvoid*を使うことで、どんな用途にも対応できるはず。 上位レイヤの構造体は「コンテキスト情報構造体」とでも名付ける。
イメージ
- ビットストリームバッファが空になる
- 上位レイヤに新たなデータを要求する
- バッファサイズと先頭アドレスの入った「バッファ情報構造体」を渡す
- 「コンテキスト構造体」を渡す
- コールバック関数が、バッファからデータを取り出しビットストリームバッファへデータを格納する
- 下位レイヤの提供する関数を使ってストリームへデータを格納...以下のような関数を使う
- Fill(Bit,n): 指定したビット(1/0)がn個連続するビット列をストリームへ
- MSB(data):MSBからビットストリームへ
- LSB(data):LSBからビットストリームへ
- 下位レイヤの提供する関数を使ってストリームへデータを格納...以下のような関数を使う
後で実行
ビットストリームバッファ(周期レジスタの値が入っている)にデータを書き込む処理は大して難しくない。しかし、バッファを大きくして一気に処理したい場合もある。 割り込みハンドラで長時間の処理になるのは好ましくないため、後で実行したい場合もあるかもしれない。
割り込みハンドラで呼び出す関数を後で呼び出しても問題ない。 ただ、呼び出されずに終わっても普通に続行されたら困る。そこで、まだデータをセットし終えていない状態に移行することが必要となりそう。
データをセットし終えていないにも拘らず、次のデータを要求されたら、それはエラーになる。 エラーになったらそれを通知しなくてはならない。

