SNSへはこちら

Atmel SAMの48MHzクロック設定をする

だいぶ放置していた内容になります。例の秋月の SAM ちゃんでクロック設定をするためのコードを紹介します。この設定、気をつけないとマイコンが HardFault でロックアップしてしまうのでやや面倒でした。何度もレスキューしましたね。
商品ページは↓。

ARMマイコン ATSAMD21G18A-AUT: マイコン関連 秋月電子通商-電子部品・ネット通販

修正: (2018/12/12)
ソースコードで SYSCTRL_DFLLMUL_MUL(1920) となっていた部分を単に 1920 に変更。レジスタを .bit.MUL と指定しているため、ビット用マクロはつけてはいけません。

設定内容

まずクロック周辺の説明についてはこちらの過去の記事を参考にしてください。
本記事では 48MHz クロックを PLL によって生成する DFLL48M を CPU クロックに入力する設定をします。

クロックジェネレーターに、どのクロック源を入れるかを選択するのでした。デフォルトでは内部クロックである OSC8M が入力されています。この OSC8M、実はデフォルトで8分周されているので CPU クロックは 1MHz になっています。通常だとうざったいのでこの分周を 1 にしてしまいますが、今回の設定だとむしろこの方が好都合なのでそのままにしておきましょう。
なお、OSC8M の分周を解除したい場合は以下のように書けばそれで終わりです。

SYSCTRL->OSC8M.bit.PRESC = 0;
SYSCTRL->OSC8M.bit.FRANGE = 1;

また、上の記事でもあるように、クロックジェネレーター0番は CPU クロックに直結されているのでした。そこでいきなりクロックジェネレーター0をいじることはせず、とりあえず別のクロックジェネレーターに入力し、そこで色々設定しましょう。

GCLK->GENDIV.reg = GCLK_GENDIV_DIV(40) | GCLK_GENDIV_ID(1); // ID1 is divided by 40.
GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_OSC8M | GCLK_GENCTRL_ID(1); // ID1 ==> OSC8M
GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_DFLL48M | GCLK_GENCTRL_ID(2); // ID2 ==> DFLL48M

ということで、0番とは別に、OSC8M を1番に入れておきます。これは DFLL48M のクロック源とするためです。2番はとりあえず DFLL48M の出力としておきます。
なお、1行目の40分周ですが、これは DFLL48M の入力最大周波数が 33kHz であるためです。これによって入力周波数は 1MHz / 40 = 25 kHz となりました。

続いて、DFLL48M にクロック供給を開始します。入力するクロックは OSC8M なので、そのように設定しておきましょう。続いて1920倍する設定をします(25 kHz x 1920 = 48MHz)。動作が安定したら、より制度の高いクローズドループに設定します。

GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK1 | GCLK_CLKCTRL_ID_DFLL48;
SYSCTRL->DFLLMUL.bit.MUL = 1920; // x1920

while (!SYSCTRL->PCLKSR.bit.DFLLRDY);
SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_ENABLE | SYSCTRL_DFLLCTRL_MODE; // DFLL48M enabled, closed loop mode.
while (!SYSCTRL->PCLKSR.bit.DFLLRDY);

ごちゃごちゃしてきたので以上をまとめます。

  • クロックジェネレーター0番は CPU クロックに直結 デフォルトで OSC8M を8分周したもの
  • クロックジェネレーター1番は OSC8M を8分周したものを更に40分周。DFLL48M 用のクロック
  • クロックジェネレーター2番は DFLL48M の出力。
    • DFLL48M は入力としてクロックジェネレーター1番を使って、クロックジェネレーター2番に逓倍した出力を供給している

以上、ここまできたら後はクロックジェネレーター0番に DFLL48M を割り当てるだけです。あと、ついでに Flash のアクセスレイテンシー設定もお忘れなく。

GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_DFLL48M | GCLK_GENCTRL_ID(0); // ID0(main) ==> DFLL48M
NVMCTRL->CTRLB.bit.RWS = 1; // flash latency

以上、完成です。

コード全景

果たしてこのコードの需要はあるのでしょうか。レジスタ手打ち教の人にとってはかなり有益なものだと自負していますがさて…

次回は SysTick タイマーを割り込み無しで使ってLチカする予定です。需要完全無視でシリーズ化を目指したいと思います。