USBホストになれるCH559入門 その4 UART0振り返り

概要

前回はGPIOを使ったLチカのコードをレジスタレベルで復習してみました。今回はUART0もレジスタレベルからコードを復習していきたいと思います。

初期化部分 クロック設定

void main()
{
    unsigned long x;
    uint32_t baud = 115200;

    // Clock Setting
    SAFE_MOD = 0x55;
    SAFE_MOD = 0xAA;
    CLOCK_CFG &= ~MASK_SYS_CK_DIV;
    CLOCK_CFG |= 6;
    PLL_CFG = ((24 << 0) | (6 << 5)) & 255;
    SAFE_MOD = 0xFF;
    delay();

セーフモードに入る

    SAFE_MOD = 0x55;
    SAFE_MOD = 0xAA;

まず最初にSAFE_MODに0x55と0xAAを書き込んでいます。これはセーフモードに入るための手順となります。

上記の一番下に記述がありますが、SAFE_MODに0x55と0xAAを書き込んでから約13~23回のシステム主周波数サイクルの間はセーフモードになって、特殊なレジスタを書き換えられるみたいです。

システムクロック分周率

    CLOCK_CFG &= ~MASK_SYS_CK_DIV;
    CLOCK_CFG |= 6;

システムクロックのドキュメントを見ると、システムクロック分周率の設定をしているようです。&= ~MASK_SYS_CK_DIVで該当ビットを0に落としています。その後6を設定していますね。

PLLクロック制御レジスタ

    PLL_CFG = ((24 << 0) | (6 << 5)) & 255;

ちょっとわかりにくい表記ですが、2つの設定をしています。0ビットから4ビットまでの5ビットがPLL基準クロック乗算器で、5ビットから7ビットまでの3ビットがUSBクロックの分周率になります。

(24 << 0)はシフト量0なのでPLL基準クロック乗算器は24になります。(6 << 5)は5ビット目からのUSBクロックの分周率に6をセットする意味となります。

計算してみる

  • オリジナルクロック周波数 Fosc = bOSC_EN_INT? 12MHz: Fxt
  • PLL周波数 Fpll = Fosc * (PLL_CFG & MASK_PLL_MULT)
  • USBクロック分周率 Kusb = (PLL_CFG & MASK_USB_4X_DIV) » 5
  • USBクロック周波数 Fusb4x = Fpll / (Kusb? Kusb: 8)
  • システムクロック分周率 Ksys = CLOCK_CFG & MASK_SYS_CK_DIV
  • システム周波数 Fsys = Fpll / (Ksys? Ksys: 32)
  • リセット直後, Fosc = 12MHz, Fpll = 288MHz, Fusb4x = 48MHz, Fsys = 12MHz.

データシートでは上記の式が書いてありました。デフォルトの値が一番下になりますね。

オリジナルクロック周波数 Fosc = bOSC_EN_INT? 12MHz: Fxt

今回bOSC_EN_INTを変更していませんので12MHzになります。これは内蔵の12MHzをクロック源に利用していることを表します。CH559は外部のもう少し早いクロックを使うこともできますが、一般的には内蔵のクロックで十分な気もします。

PLL周波数 Fpll = Fosc * (PLL_CFG & MASK_PLL_MULT)

PLL_CFGを今回編集していますね。PLL_CFG & MASK_PLL_MULTですので24になります。値を計算すると12MHz * 24ですので288MHzですね。あれ、この値はリセット値と変わっていませんね。

USBクロック分周率 Kusb = (PLL_CFG & MASK_USB_4X_DIV) » 5

こちらも変更しています。(6 << 5)を>>5で元の数値に戻していますね。6になります。そもそもMASK_USB_4X_DIVがリセット値6なのでPLL_CFGの変更は実は意味がなかったみたいです。ただし、システムクロックを設定する手順としてPLL_CFGを書き換えると書いてあったので、書き換えているのだと思います。

USBクロック周波数 Fusb4x = Fpll / (Kusb? Kusb: 8)

Kusbは6なので288MHz/6で48MHzになります。リセット値のままですね。

システムクロック分周率 Ksys = CLOCK_CFG & MASK_SYS_CK_DIV

MASK_SYS_CK_DIVの部分はリセット値24から6に変更されています。

システム周波数 Fsys = Fpll / (Ksys? Ksys: 32)

Ksysは6なので288MHz/6になり、48MHzになります。デフォルトが24なので12MHzなので高速化していますね。

変更点一覧

項目初期値設定値
Fosc12MHz12MHz
Fpll288MHz288MHz
Kusb66
Fusb4x48MHz48MHz
Ksys246
Fsys12MHz48MHz

システム周波数を12MHzから48MHzに高速化していますね。低速なUARTだと遅いままでも動くのでしょうが、ある程度以上の速度は高速化が必須みたいですね。

セーフモードから抜ける

    SAFE_MOD = 0xFF;

任意の数値を書き込むとセーフモードから抜け出すとのことなので、0xFFで抜けているようです。このコードは忘れていても自動的にセーフモードから復帰しますが安全のため不要になったら抜けたほういいですね。

反映待ち

    delay();

上記で反映待ちをしています。

static inline void delay()
{
    uint32_t i;
    for (i = 0; i < (120000UL); i++)
    {
        __asm__("nop");
    }
}

中身はこんな感じで12万回nopを実行しています。nopの行は;で処理無しにしても同じ結果になったと思います。今回は48MHzなので2.5ミリ秒ぐらいの遅延になるのでしょうか?

UART0ポート設定

    PORT_CFG |= bP0_OC;
    P0_DIR |= bTXD_;
    P0_PU |= bTXD_ | bRXD_;
    PIN_FUNC |= bUART0_PIN_X;

上記はおそらくポート系だと思われます。

上記で調べていきます。

PORT_CFG |= bP0_OC

bP0_OCなのでP0ポートをオープンドレイン出力許可に設定しています。

P0_DIR |= bTXD_

SFR(P0_DIR,	0xC4);	// port 0 direction
#define bUDCD             0x80      // DCD input for UART1
#define bURI              0x40      // RI input for UART1
#define bUDSR             0x20      // DSR input for UART1
#define bUCTS             0x10      // CTS input for UART1
#define bTXD_             0x08      // alternate pin for TXD of UART0
#define bRXD_             0x04      // alternate pin for RXD of UART0
#define bURTS             0x02      // RTS output for UART1
#define bUDTR             0x01      // DTR output for UART1

宣言をみると、TXDの出力を許可しているようです。

P0_PU |= bTXD_ | bRXD_

P0のプルアップ設定です。宣言値はP0_DIRと同じですね。TXDとRXDをプルアップしています。内部プロアップしているのでTXDはオープンドレイン出力なんですね。

PIN_FUNC |= bUART0_PIN_X

bUART0_PIN_Xを設定していますのでRXD0/TXD0はP0.2/P0.3を使用する設定になります。

UART0設定

    SM0 = 0;
    SM1 = 1;
    SM2 = 0;
    REN = 1;
    PCON |= SMOD;
    x = (((unsigned long)FREQ_SYS / 8) / baud + 1) / 2;

    TMOD = TMOD & ~bT1_GATE & ~bT1_CT & ~MASK_T1_MOD | bT1_M1;
    T2MOD = T2MOD | bTMR_CLK | bT1_CLK;
    TH1 = (256 - x) & 255;
    TR1 = 1;
    TI = 1;

上記でUART0の設定をしています。

上記を見ながら確認していきます。

SM0 = 0

8ビットデータの非同期通信が選択しています。1だと9ビットのようです。

SM1 = 1

1: T1またはT2によって生成される可変ボーレートを設定。これはタイマーでボーレートを指定する設定になります。

モード確認

SM0SM1備考
00モード0。シフトレジスタモード、固定ボーレートはFsys / 12
01モード1。8ビットの非同期通信モード。可変ボーレート、タイマーT1またはT2で生成されます。
10モード2。9ビットの非同期通信モード。ボーレートは、Fsys / 128(SMOD = 0)またはFsys / 32(SMOD = 1)です。
11モード3。9ビット非同期通信。可変ボーレート。タイマーT1またはT2で生成。
表13.2.1.1 UART0作業モード選択

上記で確認するとSM0 = 0, SM1 = 1はモード1になります。

SM2 = 0

UART0マルチマシン通信制御ビットで、モード1の場合は有効なストップビットを受信したときのみ、受信が有効になるかのフラグになります。0なので特殊な制御はなく受信可能です。

REN = 1

UART0 許可受信制御ビットで1で受信可能になります。

PCON |= SMOD

電源周りの設定です。上記によるとTimer1を利用してUART0のボーレートを生成するときに高速モードに設定をしているようです。

x = (((unsigned long)FREQ_SYS / 8) / baud + 1) / 2

ボーレートを指定する数値を事前に計算しています。FREQ_SYSはビルド時に外部から渡している値ですが、48MHzです。

set dfreq_sys=48000000

baudは今回設定する転送速度です。

    uint32_t baud = 115200;

こちらの計算式はのちほど設定をするときに確認していきたいと思います。計算結果自体は26になっていました。

TMOD = TMOD & ~bT1_GATE & ~bT1_CT & ~MASK_T1_MOD | bT1_M1

いろいろ設定していますね。タイマーの設定でした。

ビット名前アクセス概要リセット値
7bT1_GATERWゲート許可ビットは、タイマ1の起動が外部割込み信号INT1の影響を受けるかどうかを制御します。
0: Timer/Counter1が起動するかどうかはINT1とは関係ありません。
1: INT1ピンがHIGHでTR1が1の時にのみ起動できます。
0
6bT1_CTRWタイミングモードまたはカウントモード選択ビットです。T1ピンの立ち下がりエッジをクロックとし、カウントモードで動作する場合は1となります。0
5bT1_M1RWTimer/Counter1モード選択上位0
4bT1_M0RWTimer/Counter1モード選択下位0

抜粋ですが、上記となります。bT1_M1を1に、他は0に指定しています。ゲートはINT1の影響を受けず、立ち上がりエッジをクロックとしています。Timer1モードは10となります。

bTn_M1bTn_M0Timern working mode (n = 0, 1)
10モード2: 8ビットリロードTimer/Counter nの場合、リロードカウント単位としてTLn, THnを使用します。カウントが8ビットからオール0に変化すると、オーバーフローフラグTFnがセットされ、初期値が自動的にTHnからロードされます。

モード2になりますね。

T2MOD = T2MOD | bTMR_CLK | bT1_CLK

タイマー2のT2ですが他のタイマー設定もしているレジスタです。bTMR_CLKを選択しているので周波数分割のないシステムクロックFsysがカウントクロックとして使用されます。bT1_CLKで高速クロックFsys/4(bTMR_CLK = 0)またはFsys(bTMR_CLK = 1)を選択するとあります。

今回はbTMR_CLK = 1なのでFsysがクロック源として選択されました。

ボーレートの決定方法

bTMR_CLKbT1_CLKSMOD備考
110TH1 = 256-Fsys/32/ボーレート
111TH1 = 256-Fsys/16/ボーレート
010TH1 = 256-Fsys/4/32/ボーレート
011TH1 = 256-Fsys/4/16/ボーレート
X00TH1 = 256-Fsys/12/32/ボーレート
X01TH1 = 256-Fsys/12/16/ボーレート
表13.2.1.2 T1で生成されるUART0ボーレートの計算式

上記の式で計算できるようです。bTMR_CLK = 1, bT1_CLK = 1, SMOD = 1なのでTH1 = 256-Fsys/16/ボーレートの計算式になります。

Fsysは48MHzでボーレートは115200になりますので、TH1 = 256-(48000000/16/115200)になり、TH1 = 256-(26)になりました。同じ値ですがxとは計算式がちょっと違いますね。。。

Fsysボーレート設定値
480000009600312.5
4800000019200156.3
480000003840078.1
480000005760052.1
480000007488040.1
4800000011520026.0
4800000023040013.0
4800000025000012.0
480000005000006.0
4800000010000003.0
4800000015000002.0
4800000020000001.5

Fsys/16/ボーレートの計算値です。1から256までの必要があるので9600のボーレートは選択できません。2000000も整数値じゃないので誤差が多すぎて使えません。そのため一番早いボーレートは1.5Mbpsになります。3Mは設定可能なようにみえますが、たぶん安定しないはずです。。。

ちなみに9600などの遅いボーレートを使いたい場合には/16より遅い設定にするか、クロックを下げれば使えるはずです。クロックは他への影響が大きいのでbTMR_CLKを0にするとかの方が良さそうですね。

TH1 = (256 – x) & 255

タイマーの上位カウントを設定しています。xは26なので230が指定されました。

TR1 = 1

1を指定するとタイマー1が起動するようです。

TI = 1

UART0の送信割り込みフラグビットをセットしているようです。

受信

int UART0Receive()
{
    while (!RI)
        ;
    RI = 0;
    return SBUF;
}

これは前回と同じですがRIは受信割り込みフラグビットで1になるまで待機してから0クリア後に、SBUFから受信データと取り込みます。

送信

int UART0Send(int c)
{
    while (!TI)
        ;
    TI = 0;
    SBUF = c & 0xFF;
    return c;
}

同じように、送信割り込みフラグビットであるTIが1になるまで待機してから0クリア後に、SBUFに送信データを入れます。

まとめ

データシートを確認しながらUART0の設定を確認しました。ボーレートによって設定値や計算が変わるのがちょっと嫌ですね。1.5Mの高速通信をしないのであれば低速側の設定を使ったほうがいいのかもしれません。

コメント