SNSへはこちら

CH559マイコンをいじってみよう(5) - UART1と腑に落ちない点

ちょっとハマった UART1 です。
未だによく分かっていない挙動があるので(XBUS について)、知っている人はコメントください。

UART1とは

元々 8051 に載っている通常の UART を拡張したものです。このマイコンってこういう機能拡張があるんですよね。使いやすくて助かる。
そして特徴は以下。

  • パリティ、ストップビット(1 or 2)に対応
  • baud rate が高速。3Mbps まで対応。
    • 115200 bps はヨユーです
  • 全二重通信のみならず、半二重や RS485 にも対応
    • 今回は使いませんが...

と、搭載しているコアにしては割とモダンなんですよ。

ペリフェラルの初期化

では初期化コードを書きましょう。まずは全景。

// Fsys = 48MHz を前提
void uart1_init(void) {
    // Clock Configuration
    SER1_LCR |= 1 << 7; // Enable access to Divisor Latch

    SER1_DIV = 4;
    SER1_DLL = 13;
    SER1_DLM = 0;
    SER1_LCR &= ~(1 << 7);

    XBUS_AUX |= 1 << 4; // ハマりポイント!これが必要

    // Pin Assignment can be set in SER1_IER register.
    // いや、なんでピン割当を割り込み有効化レジスタにやらせるし(メモリ空間が足らなかったのだろうけど...)
    SER1_IER |= 1 << 5;
    // TXD1 is P2.7(#28) and RXD1 is P2.6(#27).
    PORT_CFG &= ~(1 << 2); // P2.x: Push-Pull
    PORT_CFG |= (1 << (2+4)); // P2.x: 10mA max
    P2_DIR |= 1 << 7;

    // Data Format Configuration
    SER1_LCR |= 0x3; // 8bit data length
}

日本語コメントでなんか言ってますが、ちょっとずつ見ていきます。

Baud Rateの設定

UART なら基本ですね。こちらはディバイザラッチへのアクセスを許可してやる必要がありますが、そのへんはちゃんと説明してくれているので問題ないと思います。

    // Clock Configuration
    SER1_LCR |= 1 << 7; // Enable access to Divisor Latch

    SER1_DIV = 4;
    SER1_DLL = 13;
    SER1_DLM = 0;
    SER1_LCR &= ~(1 << 7);

SER1_LCR の 7bit 目を1にすると設定可能になります。設定が終わったら最後にアクセス許可を無効化してあります。

ひとまずそれは正しいとして、計算をしていきます。狙いは 115200 bps です。データシートによると、{SER1_DLM:SER1_DLL} の設定値は Fsys * 2 / SER1_DIV / 16 / baud rate で計算できるとしています。これに従っていきましょう。

まず Fsys は 48MHz なので、雑ですがとりあえず 12MHz 程度に落としておきましょう。なので SER1_DIV4 としています。
あとは式に当てはめれば簡単です。48Meg * 2 / 4 / 16 / 115200 とすると 13.02 となります。整数値に近くていいですね。
ということで、{SER1_DLM:SER1_DLL} = 13 としています。

送受信のコード

初期化コードは割り込みを使っていないので分かるとは思いますが、ポーリングで処理する設定にしています。
素直にフラグを待つだけ。

void uart1_send(char ch) {
    while( !(SER1_LSR & (1 << 5)) );
    SER1_THR = ch;
}

char uart1_get(void) {
    while( !(SER1_LSR & (1 << 0)) );
    return SER1_RBR;
}

main関数の実装例

こんな感じ。もうここまでくれば普通ですね。マイコン側では P2.7(#28) が TXD、P2.6(#27) が RXD です。

int main(void) {
    clock_init();
    uart1_init();

    while(1) {
        uart1_send('A');
        ms_wait(100);
    }
}

ハマりポイント:XBUSのクロック出力有効化

ここからはハマリポイントですので、レジスタ設定の説明等はありません。色々試行錯誤した結果をレポートしておきます。

初期化コード中、たった1行の XBUS_AUX |= 1 << 4; (XBUS_AUXbALE_CLK_EN ビットを1に)が個人的にハマリポイントでした。これを設定しないと UART 通信はできません。まあデータシートに隅々まで目を通せよって話ではあるのですが。
これは UART1 のモードに関わってきます。さっき RS485 対応って書きましたよね?それ関連です。

このように書いてあります:

RS485EN = bUH1_DISABLE & ~ ( bXBUS_CS_OE & ~ bXBUS_AL_OE | bALE_CLK_EN )

この RS485EN が 0 であれば良いです。bUH1_DISABLE がデフォで1、あとのビットは0なので、bALE_CLK_EN を 1 にすることで RS485 モードを無効化しています。

あとの初期化コードは見ての通りポートの設定とデータビット長の設定なので、解説は省略します。

それ以外のRS485無効化パターン

これ以外にも RS485 = 0 と出来るビットパターンありそうですよね。ということで列挙してそれぞれやってみました。

No. bUH1_DISABLE bXBUS_CS_OE bXBUS_AL_OE bALE_CLK_EN
On Rst 1 0 0 0
1. 0 x x x
2. 1 x x 1
3. 1 1 0 0

ちなみに、bUH1_DISABLE は USB、bXBUS_CS_OE, bXBUS_AL_OE は IO ポート、bALE_CLK_EN は XBUS のレジスタに存在するビットです。

2.は上でやりましたね。それで実際に 1.と 3.を検証しましたが、動きません。ここがちょっと自分では理解不能な仕様です。

ちょっと腑に落ちないところ

上で議論したように、XBUS_AUX レジスタの bALE_CLK_EN ビットを立てるしか動作しませんでした。
確かにそれ以外のパターンでも RS485 モードは無効化されているかも知れないのですが、そもそも動かないという感じになりました。

...で、ですよ。UART1 と XBUS でなんの関係があるのかって思いました。XBUS はなんとな〜く察するに、外部の SRAM 等に接続する際、アクセスをメモリ空間上にマッピングしてある外部バス(表現が難しいですね)のようです。

そこで「ひょっとして UART1 って外部バス上に存在するペリフェラルなのか?まあ拡張されたペリフェラルだからそうだとしても無理はないよなあ」と思いデータシート上のブロック図を眺めてみましたが、違いそう

そして、XBUS の当該ビットに書かれている説明を読みに行きましたが、なおさら分からないという。

ん、Fsys の 12 分周クロックを ALE ピンに出力???それって UART1 に関係あるの??って。
もし関係しているとしてもこの 12 分周が謎です。さっきの baud rate 設定じゃ別に12分周しないで動いていたよなあ。

...と正直何もわからないので、ご存じの方がいたらコメントでこっそり教えて下さい。
ちなみにこの bALE_CLK_EN ビットを立てると、USB が動かなくなります (USB デバイスの動作で確認)。マジで分からねえ。つまり USB と UART1 の両立は無理ですね。

コメント

  1. ぽよ より:

    んーうちではUSB HOSTの機能を使いつつ、UART0もUART1もどっちも使えてますね

    • shima-529 より:

      そうですか!教えていただきありがとうございます〜

      近いうちにもう一度やってみたいと思います...