Interface 2009/5月号の付録基板の続き。
ロムモニタができました。H8用のと同じくモトローラSフォーマットのローダです。
ロムモニタができました。H8用のと同じくモトローラSフォーマットのローダです。
$ tip umon connected stack_start: 0x40010000 ROM data: 0x3b70-0x3ff4 RAM data: 0x4000f800-0x4000fc84 1156byte bss: 0x4000fc84-0x4000fd28 164byte Clock: IRC, PLL NOT connected, PLL M:0 N:0, CCLKCFG 0 Clock: MAIN, PLL connected, PLL M:11 N:0, CCLKCFG 3 Checking RAM 40000000-4000f800 bss RAM check passed. LPC2388 Simple Monitor Build Jun 17 2009 21:27:41 >> l Send S-record file. ~>Local file name? a.mot 1544 lines transferred in 4 seconds ! Read 36221 byte. success Start address: 0x40000200 data: 0x40002d98-0x400031f0 1112byte bss: 0x400031f0-0x40003270 128byte Hello world Clock: MAIN, PLL connected, PLL M:11 N:0, CCLKCFG 3 user> user> led user> led user> led user> reset stack_start: 0x40010000 ROM data: 0x3b70-0x3ff4 RAM data: 0x4000f800-0x4000fc84 1156byte bss: 0x4000fc84-0x4000fd28 164byte Clock: MAIN, PLL connected, PLL M:11 N:0, CCLKCFG 3 Clock: MAIN, PLL connected, PLL M:11 N:0, CCLKCFG 3 Checking RAM 40000000-4000f800 bss RAM check passed. LPC2388 Simple Monitor Build Jun 17 2009 21:27:41 >>これでいくらでもガツガツとトライ&エラーができる。実はスタックがBSS潰し たりとか、BSS初期化忘れてたりと、そっと秘密にしておきたいような失敗をし て時間がかかってしまいました。
クロックについて。
LPC2388のクロックソースには、外部発振子(メイン 〜25MHz)、内部RC発振子
4MHz、外部RTCの3つが選べる。
リセット後には内部発振子の4MHzで駆動する。
リセット後に実行されるブートローダ(ISP)はこれをベースにPLLでクロックを
設定する。
ISPでボーレートの合わせ込みをした後にクロックを設定するのはこれのため。
UARTのクロックはCPUクロックをベースにして設定され、最低でも10MHzはない
と38400bpsで通信できない。実際のとこ12MHzに設定して115200bpsでいける。
PLLの入力は32kHzから25MHzまで。32kHzというのはRTCをソースにできるという
こと。
PLLを使わない場合は入力ソースがそのままシステムクロックになる。
入力->PLL->Divider->C(CPU)CLK->Divider->P(Peripheral)CLK
ユーザプログラムが直接起動した場合、内部RC発振子でPLLが接続されていない
状態になるので4MHzで動くことになる。
まずCPUクロックの設定。最大の72MHzにします。クロックを外部発振子12MHzに
変更して、PLLを設定し(288MHzにする)、Dividerを設定し(1/4にする)72MHzに
します。LPC23XXのユーザマニュアル6.14章 PLL setup sequenceをそのまま実
装します。
static inline void
__pll_feed ()
{
*PLLFEED = 0xaa;
*PLLFEED = 0x55;
}
#define PLL_MVAL 11
#define PLL_NVAL 0
#define CCLKDIV 3
void
mcu_speed ()
{
// User manual 6.14 PLL setup sequence.
// 1. Disconnect the PLL with one feed sequence if PLL is already connected.
ソースクロックとCPUクロックはPLLを介して接続されるか、そのまま接続され
るかの二択。PLLを介している時にPLLの設定を変えることはできないので、ま
ずPLLの接続を外してソースクロック直結にします。
ユーザプログラムがリセット後直接起動された場合はISP(ブートローダ)によっ
てPLLは接続されていないけれども、ISPから直接ユーザプログラムにジャンプ
することもできる。その場合PLLが接続された状態で実行される。なので確認
する必要がある。
if (*PLLSTAT & PLLSTAT_PLLC) // PLL connected?
{
// Disconnect PLL. (Enable)
*PLLCON = PLLCON_PLLE;
__pll_feed ();
PLLCON, PLLCFGのレジスタの設定はフィードすることで初めて実行される。
}
// 2. Disbale the PLL with one feed sequence.
PLLを無効化します。PLLCFGはPLLが無効の時しか設定できないためです。
*PLLCON = 0;
__pll_feed ();
// 3. Change the CPU Clock Divider setting to speed up operation
// without the PLL, If desired
よくわからず。
// 4. Write to the Clock Source Selection Control register to change
// clock source.
// Set main oscillator as PLL clock source.
ソースクロックをメイン(このボードの場合12MHz)に変更します。変更する
前に発振子が安定するのを確認して。
// Enable main oscillator.
*SCS |= SCS_OSCEN;
// Wait until oscillator ready.
while (!(*SCS & SCS_OSCSTAT))
;
*CLKSRCSEL = CLKSRCSEL_OSC;
// 5. Write to the PLLCFG and make it effective with one feed
// sequence. The PLLCFG can only be updated when the PLL is disabled.
このPLLCFGの設定します。逓倍された周波数は275MHzから550MHzの範囲で
ないといけない。
*PLLCFG = PLL_MVAL | (PLL_NVAL << PLLCFG_NSEL_SHIFT);
// 2 * (MVAL+1) * Fin(12MHz) / (NVAL + 1)
// 2 * 12 * 12000000 / 1 = 288MHz
MVALとNVALの値は上記のコメントのように設定した。いくつかのパターンは存
在するけれど、NVALを小さくするのがよいとデータシートにはあるのでこの設
定。周辺機器のクロックはCPUクロックからの分周で与えられ、その分周は
1/1,1/4,1/8に限られる(RTC,CAN1,CAN2に例外あり)。USBは48MHzでないといけ
ない。そのあたりを考慮して設定する。UARTは計算によってそこそこ自由に設
定できる。
__pll_feed ();
// 6. Enable the PLL with one feed sequence.
PLLの設定ができたのでPLLを動かします。まだソースとCPUの間には接続されて
いません。
// Enable PLL, Disconnected.
*PLLCON = PLLCON_PLLE;
__pll_feed ();
// 7. Change the CPU Clock Divider setting for the operation with
// the PLL. It's critical to do this before connecting PLL.
// CPU clock divider.
ソースクロックがPLLによって逓倍(この場合24倍で288MHz)されたのから、
何分周したのをCPUクロックとするかを設定します。72MHzにしたいので4。
// 1/4 divider 288/4 = 72
*CCLKCFG = 3;
// 1/8 divider 288/8 = 36
// CCLKCFG = 7;
// 8. Wait for PLL to achive lock.
PLLが安定するのを待ちます。
while ((*PLLSTAT & PLLSTAT_PLOCK) == 0)
;
// Now PLL is requested frequency.
// 9. Connect PLL with one feed sequence.
PLLが安定したところで、ソースとCPUの間にPLLを接続します。
*PLLCON = PLLCON_PLLE | PLLCON_PLLC; /* enable and connect */
__pll_feed ();
while ((*PLLSTAT & PLLSTAT_PLLC) == 0)
;
// Now PLL is connected.
}
これでCPUを72MHzで設定できました。そしてUARTの設定。UARTのクロックはCPUの
クロックの分周から設定されるので、ここからの影響を受けます。
void
com0_init () // UART0
{
// Pin select
ピンはGPIOを多重化されているのでUARTに使うように設定します。
mcu_pin_select (0, 2, 1); // Port0.2 : TXD0
mcu_pin_select (0, 3, 1); // Port0.3 : RXD0
/* Clock supply */
// CCLK 72MHz PCLK 18Hz
CPUクロックを72MHzにしたので1/4の18MHzをPCKにします。
mcu_peripheral_clock (PCLK_UART0, CCLK4); // 72/4=18
// CCLK 36MHz PCLK 18Hz
CPUクロックを36MHzにしたのであれば、
//mcu_peripheral_clock (PCLK_UART0, CCLK2); // 36/2=18
/* Power supply */
電源を入れます。
mcu_peripheral_power (PCONP_PCUART0, TRUE);
/* Line speed. */
パラメタの設定について。
/*
PCLK 18MHz 115200bps
に設定したいとする。
DLest = 18000000/(16*115200) = 9.77
を計算する。これが整数でないので
iDLest = PCLK / (16 * 115200 * 1.5);
を計算する。(1.5がFRest)
FRest = PCLK / (16 * BR * iDLest);
を計算する。
FRest 1.5 がguessで FRest = 1.627になった。
この計算が1.1から1.9に収まらなければFRestのguessを変化させて収まるようにする。
ここでユーザマニュアルの16章テーブル363を参照する。
From Table 16.363
1.625 DivAddVal/MulVal = 5/8
このテーブルにある一番近い値は1.625なので、これを参照して
MULVAL=8,DIVADDVAL=5にする。
DLMとDLLはどうやって決定するか。
18000000/(16*(256 * DLM + DLM)* (1+ 5/8))
この値がbpsになるので、いいところを探す。
18000000/(16*(256 * 0 + 6)* (1+ 5/8))=115384
このあたりだろう。なので
DLM = 0, DLL = 6
これなら誤差2%程度。
115384/115200=1.002
というように決める。
PCLK 18MHz 57600bps
DLest = 18000000/(16*57600) = 19.53
FRest = 1.5 -> FRest = 1.5024
1.500 DivAddVal/MulVal = 1/2
18000000/(16*(256 * 0 + 13)* (1+ 1/2)) = 57692
DLM = 0, DLL = 13
57692/57600=1.002
PCLK 18MHz 9600bps
DLest = 18000000/(16*9600) = 117.2
FRest = 1.500000 =>1.502404
1.500 DivAddVal/MulVal = 1/2
18000000/(16*(256 * 0 + 78)* (1+ 1/2))=9615
DLM = 0, DLL = 78
9615/9600=1.002
*/
// Line Control Register
*U0LCR = ULCR_DLAB; // Enable Divisor Latche access.
#define B_115200
#ifdef B_115200
// 115200bps
// DLM=0, DLL=4, DIVADDVAL=5, MULVAL=8
*U0DLM = 0; // Divisor Latch MSB
*U0DLL = 6; // Divisor Latch LSB
*U0FDR = 5 | (8 << 4); // Fractional Divider Register
#elif defined B_57600
// 57600bps
*U0DLM = 0; // Divisor Latch MSB
*U0DLL = 13; // Divisor Latch LSB
*U0FDR = 1 | (2 << 4); // Fractional Divider Register
#elif defined B_9600
// 9600bps
*U0DLM = 0; // Divisor Latch MSB
*U0DLL = 78; // Divisor Latch LSB
*U0FDR = 1 | (2 << 4); // Fractional Divider Register
#else
#error
#endif
// Set line format. 8N1.
*U0LCR = ULCR_8BIT | ULCR_PARITY_NONE | ULCR_STOP1 | ULCR_BREAK_DISABLE;
*U0IER = 0; // Interrupt disable.
}
