マイクロコントローラ(MCU)では、汎用非同期レシーバ/トランスミッタ(UART)ペリフェラルが普及しているにもかかわらず、ビットバングされたUARTアルゴリズムが依然として使用されています。この理由は、アプリケーションごとに異なります。場合によっては、選択したデバイスが提供するよりも多くの UART が必要であることが原因である場合があります。アプリケーションやレイアウトの制限により、UART機能に特定のピンを使用する必要があるのに、デバイスはUARTピンを必要なパッケージピンに配線しないかもしれません。おそらく、アプリケーションには非標準または独自のUARTスキームが必要です。
理由が何であれ、ビットバンギングされたUARTが使用され、通常は純粋なソフトウェア実装であるアプリケーションがあります(タイマーが使用され、MCUコアがGPIOピンを直接制御します)。より良い代替手段は、Flextimer(FTM)またはTimer/PWM Module(TPM)を使用して、これらの周辺機器の機能を利用し、CPUの負荷を軽減することです。このドキュメントでは、FTM または TPM ペリフェラルを使用して UART をエミュレートする方法を説明し、サンプル アプリケーションを提供します。ここでは、Kinetis SDKの例(TWR-K22F120MおよびFRDM-K22Fプラットフォーム用)とベアメタル・レガシー・コード例(FRDM-KL26Z用)を示します。
UART をエミュレートするアプリケーションを作成する前に、UART プロトコルとエンコードを理解する必要があります。UARTプロトコルは、通常、スタートビット、 ペイロード(7〜10データビット)、および ストップビット を含む非同期プロトコルですが、ストップビットの数とデータの転送内容/方法に関する多くのバリエーションが可能です。このドキュメントとアプリケーション例では、1つのスタートビット、 8つのデータビット、 1つのストップビット、パリティなし、フロー制御なしのUART伝送に焦点を当てます。データは最初に最下位ビット(LSB)で送信されます。次の図は、この送信のブロック図です。
ただし、これはトランスミッションが電気的にどのように見えるかを特定するものではありません。次の図は、オシロスコープでUART通信をキャプチャしたスクリーンショットです。送信されるデータは 0x55 または ASCII 表現の「U」です。
伝送ラインは最初はロジックハイであり、その後ローに遷移して伝送の開始を通知します。伝送ラインは、レシーバが検出するために1ビット幅の間ローに保たれる必要があります。次に、8つのデータビットがあり、その後に1つのストップビットが続きます。上記の場合、データビットは0x55または0b0101_0101です。送信は最初にLSBで送信されるため、スクリーンショットには1-0-1-0-1-0-1-0が示されていることに注意してください。最後の遷移ハイはストップビットの開始を示し、ラインは次の送信の開始までその状態のままです。レシーバは非同期であるため、ストップビットの終わりをマークするための識別遷移のタイプは必要ありません。
このようなプロジェクトを始めるときに多くの人が最初に尋ねる質問は、「UART をエミュレートするときに FTM/TPM をどのように構成するか」です。これに対する答えは、解決しようとしているこの問題の側面によって異なります。文字の送受信には、2 つの異なる構成が必要です。伝送には、特定の時点で出力ピンを操作する構成が必要です。文字を受信するには、受信ピンをサンプリングし、ピンの遷移間隔を測定する構成が必要です。FTM と TPM には、次の表に示すモードがあります。
FTMとTPMには、出力を操作する4つの異なるモードがあります:出力比較(パルスなし)、出力比較(パルス付き)、エッジ整列PWM、およびセンター整列PWM。どちらのPWMモードも、アプリケーションの要件には適していません。これは、PWMモードが連続的な波形を生成するように設計されており、波形のサイクル中に常に初期化された状態に戻るためです。ただし、UART プロトコルでは、データに連続した 1 または 0 があり、ピン間の遷移がない場合があります。
出力比較モード (ハイ トゥルー パルス モードまたはロー トゥルー パルス モード) は、ピンを 1 回だけ操作し、1 FTM/TPM クロック サイクルのパルスのみを生成するように設計されています。したがって、これは明らかにアプリケーションにとって望ましくありません。出力比較モード(Set/Clear/Toggle on match)は期待できます。このモードでは、サイクルごとに出力ピンが操作されます。3 つの異なるオプションがあります: 一致時に出力をクリアする、一致時に出力を設定、一致時に出力を切り替えます。「一致時に出力をクリア」も「一致時に出力を設定」も、文字の送信中に設定変更が必要になるため、理想的ではありません。ただし、「トグル出力オンマッチ」は使用でき、このサンプルアプリケーションで選択された設定モードです。
文字を受け取るには、直感的に操作できるモードが 1 つだけあります: "入力キャプチャ モード"。このモードは、選択した入力ピンのエッジ遷移にタイマーカウント値を記録します。送信機能に選択された出力比較モードと同様に、入力キャプチャ モードには、立ち上がりエッジでのキャプチャ、立ち下がりエッジのキャプチャ、いずれかのエッジでのキャプチャの 3 つのサブモードがあります。説明から、どちらのエッジでもキャプチャを選択する必要があることは明らかです。
FTM/TPMモードの選択は適度に直感的ですが、このモードを使用してUART伝送をエミュレートすることはできません。これには2つの問題があり、少し厄介です。
1) 出力ピンがローに初期化されている。ただし、UART プロトコルでは、ピンが論理的な High 状態で開始する必要があります。
2) ピンは、チャネル値が MOD レジスタの値より小さい場合、各サイクルで遷移します。1 または 0 の文字列が連続しているため、ピンが遷移しない期間が必要です。
これらの点には両方とも回避策があります。
最初の問題では、チャネル割り込みが最初にイネーブルされ、チャネル値レジスタに MOD レジスタの値よりもはるかに小さい値が読み込まれます。次に、チャネル割り込みサービス ルーチンで、ピンがサンプリングされ、ロジック ハイ ステートになり、チャネル割り込みが無効になっている (アプリケーションのライフ ライフ中は再度有効にならない) ことを確認します。この割り込みサービス・ルーチンのコードは次のとおりです。
2 番目の問題については、ピンの値を遷移させず、タイマーが通常どおりにカウントを続行できるようにする方法が必要です。出力比較モードでは、チャネル値レジスタを使用して、ピン遷移がいつ発生するかを判断します。MODより大きい値がチャネル値レジスタに書き込まれた場合、チャネル値はカウントレジスタと一致しないため、ピン遷移は発生しません。したがって、連続した 1 または 0 を送信する必要がある場合、MOD レジスタの値より大きい値をチャネル値レジスタに書き込んで、出力ピンを現在の状態に保つことができます。ただし、MOD より大きい値がチャネル値レジスタに書き込まれると、チャネルの一致は発生しません (つまり、チャネル割り込みは発生しません)。したがって、値の書き込みを続行するには、タイマーオーバーフロー割り込みを使用する必要があります。そのため、出力ピンの更新を事前に計画する必要があり、伝送アルゴリズムが少し難しくなります。次の図は、適切なパルスを生成するために、どの値をチャネル値レジスタに書き込むタイミングを示しています。
数値を適切な一連の MOD/2 値と MOD+1 値に変換する関数を記述するのは、少し難しい場合があります。これを行うには、まず、伝送ピンの変更が必要なときに MOD/2 を書き込む必要があり、ピンの伝送が望ましくない場合は MOD+1 を書き込む必要があることに注意する必要があります。では、変更がいつ発生したかを判断するために、どのような論理関数を使用できるのでしょうか。XORが正解です。では、どの2つの値をXORで結合する必要があるのでしょうか?1つの値は、明らかに送信したい値です。しかし、2番目の値は何ですか?2 番目の値は、送信する値のシフト バージョンであることがわかります。具体的には、2 番目の値は、左に 1 シフトして送信する目的の値です。(これは、目的の値の「将来」の値のようなものと考えることができます)。次の図は、送信に使用するキューを決定する方法を示しています。
受信機能は、文字の受信にDMAを使用できるという点で、送信機能よりも優れています。これは、受信機能がFTM/TPMの入力キャプチャ機能を利用しているため、チャネルマッチ割り込みを使用できるためです。このドキュメントで提供されるサンプル アプリケーションは、受信用の DMA メソッドと非 DMA メソッドを実装します。まず、非DMA方式について説明します。入力パルス幅の収集の詳細について説明する前に、受信ピンの詳細について説明する必要があります。
受信ピンは、パケット送信の開始がいつ開始されるかを判断できる必要があります。これを行うには、受信ピンをFTM / TPMピンとして構成します。同時に、GPIO割り込み機能は、立ち下がりエッジ割り込みの同じピンに構成されます。GPIO割り込み機能はどのデジタルモードでも有効になっているため、GPIO割り込みはネストされたベクトル割り込みコントローラー(NVIC)にルーティングできます。ピン割り込みは、新しい文字の受信が開始されたときにFTM/TPMクロックを開始するために使用されます。このピンのGPIO割り込みでは、FTM/TPMカウンタレジスタがリセットされ、FTM/TPMへのクロックがオンになります。 GPIO割り込みサービスルーチンのコードを以下に示します。
ここで、文字を受信して DMA を使用しない場合、最初に理解すべきことは、割り込みサービス ルーチン (ISR) が使用され、主にキャプチャされたカウント値を記録するために使用されるということです。割り込みサービス・ルーチンは、現在の受信文字長も追跡し、カウンタ・レジスタをリセットします。これは、受信キューの値に最後のピン遷移からの時間が反映されるようにするためです。非DMAアプリケーションの割り込み機能を以下に示します。
ISR の最初の 2 つのアクションは、カウント レジスタのリセットとチャネル イベント割り込みフラグのクリアであることに注意してください。次に、チャネル値は受信パルス幅配列に格納されます(これは単に、受信されている現在の文字の受信パルス幅を保持する配列です)。次に、受信される文字の現在の長さを保持する変数である recvQueueLength は、最新の文字の長さを反映するように更新されます。次のステップは、完全な文字が受信されたかどうかを判断することです。これは、 recvQueueLength を RECV_QUEUE_THRESH (予想されるビット数に予想されるビット幅に別のビット幅 (開始ビット用) を掛けて決定されるしきい値) と比較することによって決定されます。recvQueueLength が RECV_QUEUE_THRESH より大きい場合は、完全な文字が受信されたことを示すセマフォ recvdChar が設定されます。FTM/TPMクロックがオフになり、受信ピンのピン割り込み機能が有効になります。割り込みルーチンの最後のステップは、受信キュー インデックス recvQueueIndex をインクリメントすることです。この変数は、受信キュー配列の現在のエントリを指します。
DMAを使用する場合、受信FTM/TPM割り込みは大きく異なります。割り込みルーチンは、チャネル割り込みフラグをクリアし、FTM / TPMタイマーを停止し、DMAチャネルを無効にし、受信した文字セマフォを設定するだけです。その後、文字は割り込みルーチンの外部でデコードされます。DMA使用時の割り込み機能を以下に示します。
パルス幅の配列が入力されたら、受信した文字を 1 つの数値に変換する必要があります。これは、DMA を使用する場合と使用しない場合で若干異なります。ただし、基本的な原理は同じです。1 つのエントリのビット数は、予想されるビット幅で除算することによって決定され、これは 1 と 0 を含む一時配列に変換され、それを使用して適切な数の 1 と 0 が返される char 変数にシフトされます。一時配列が必要になるのは、値が最初に UART LSB にシフトされるため、ビットを最初のエントリから最後のエントリに物理的に反転させる必要があるためです。これを自動的に行う論理演算はありません。この変換を実行するアルゴリズムを以下に示します。このアルゴリズムでは、recvPulseWidth はパルス幅の生のカウント値を含む配列であることに注意してください。配列 tempRxChar はデコードされた文字を逆の順序で保持し、rxChar は受信した文字を保持する char 変数です。
このドキュメントでは、UART プロトコルの概要を説明し、FTM または TPM ペリフェラルのタイミング機能を使用してソフトウェア UART を作成する方法について説明します。この方法では、正確なタイミングが可能になり、CPU や割り込みピンと GPIO ピンに関連するレイテンシに完全に依存することはありません。受信機能は、DMA を使用してさらに最適化できるため、CPU のさらなるアンロードを提供できます。