概要
前回は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なので高速化していますね。
変更点一覧
項目 | 初期値 | 設定値 |
Fosc | 12MHz | 12MHz |
Fpll | 288MHz | 288MHz |
Kusb | 6 | 6 |
Fusb4x | 48MHz | 48MHz |
Ksys | 24 | 6 |
Fsys | 12MHz | 48MHz |
システム周波数を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によって生成される可変ボーレートを設定。これはタイマーでボーレートを指定する設定になります。
モード確認
SM0 | SM1 | 備考 |
0 | 0 | モード0。シフトレジスタモード、固定ボーレートはFsys / 12 |
0 | 1 | モード1。8ビットの非同期通信モード。可変ボーレート、タイマーT1またはT2で生成されます。 |
1 | 0 | モード2。9ビットの非同期通信モード。ボーレートは、Fsys / 128(SMOD = 0)またはFsys / 32(SMOD = 1)です。 |
1 | 1 | モード3。9ビット非同期通信。可変ボーレート。タイマーT1またはT2で生成。 |
上記で確認すると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
いろいろ設定していますね。タイマーの設定でした。
ビット | 名前 | アクセス | 概要 | リセット値 |
7 | bT1_GATE | RW | ゲート許可ビットは、タイマ1の起動が外部割込み信号INT1の影響を受けるかどうかを制御します。 0: Timer/Counter1が起動するかどうかはINT1とは関係ありません。 1: INT1ピンがHIGHでTR1が1の時にのみ起動できます。 | 0 |
6 | bT1_CT | RW | タイミングモードまたはカウントモード選択ビットです。T1ピンの立ち下がりエッジをクロックとし、カウントモードで動作する場合は1となります。 | 0 |
5 | bT1_M1 | RW | Timer/Counter1モード選択上位 | 0 |
4 | bT1_M0 | RW | Timer/Counter1モード選択下位 | 0 |
抜粋ですが、上記となります。bT1_M1を1に、他は0に指定しています。ゲートはINT1の影響を受けず、立ち上がりエッジをクロックとしています。Timer1モードは10となります。
bTn_M1 | bTn_M0 | Timern working mode (n = 0, 1) |
1 | 0 | モード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_CLK | bT1_CLK | SMOD | 備考 |
1 | 1 | 0 | TH1 = 256-Fsys/32/ボーレート |
1 | 1 | 1 | TH1 = 256-Fsys/16/ボーレート |
0 | 1 | 0 | TH1 = 256-Fsys/4/32/ボーレート |
0 | 1 | 1 | TH1 = 256-Fsys/4/16/ボーレート |
X | 0 | 0 | TH1 = 256-Fsys/12/32/ボーレート |
X | 0 | 1 | TH1 = 256-Fsys/12/16/ボーレート |
上記の式で計算できるようです。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 | ボーレート | 設定値 |
48000000 | 9600 | 312.5 |
48000000 | 19200 | 156.3 |
48000000 | 38400 | 78.1 |
48000000 | 57600 | 52.1 |
48000000 | 74880 | 40.1 |
48000000 | 115200 | 26.0 |
48000000 | 230400 | 13.0 |
48000000 | 250000 | 12.0 |
48000000 | 500000 | 6.0 |
48000000 | 1000000 | 3.0 |
48000000 | 1500000 | 2.0 |
48000000 | 2000000 | 1.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の高速通信をしないのであれば低速側の設定を使ったほうがいいのかもしれません。
コメント