google-site-verification: google3bd66dd162ef54c7.html
fc2ブログ

Arduino UNO R4 の内蔵クロック周波数をトリムレジスタで調整

◆まえがき
Arduino UNO R4 MINIMA の記事がまだ続きます。前回の記事では UNO R4 のクロックを IOピンに出して周波数を正確に測定 出来るようになりました。そうやって測定してみると、UNO R4 のクロック周波数の誤差はかなり大きいことが判りました。

UNO R4 のクロック発生回路に水晶やセラミック振動子は使われておらず、チップに内蔵されたCRオシレーターなので仕方ないのですが、もう少し何とかしたくなります。

このクロックの周波数は、トリムレジスタを調整することでユーザーのプログラムから調整出来るようになっているので、実際にやってみました。

◆クロック周波数調整レジスタ
CPUチップ内蔵クロックは下図のように、ユーザートリミングレジスタの値を設定することで調整可能になっています。
HOCOUTCRの説明
上記はHOCOクロックのトリミングレジスタですが、LOCOとMOCOにも同様な調整レジスタがあります。

ユーザートリミングと言う名前で呼ばれているということは、ファクトリートリミングレジスタなんてのが見えない所にあって、チップ製造時にメーカーがトリミングの初期値を書き込んでいるのではないかと思います。

◆トリミング機能付きのクロック周波数測定プログラム
調査用に、トリムレジスタに値を設定し、D11ピンに選択したクロック(を分周した)信号を出力するプログラムを作りました。
// Arduino R4 MINIMA のクロック周波数調整と確認 20240217_UnoR4ClockFreqTrimTest
// クロック トリムレジスタを設定し、D11ピン(P109)に指定したCLKOUT信号を出す

char cbuff[10];  // 文字列操作バッファ

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println();
  Serial.println("Clock out test Start !");

  trimHOCO(3);      // HOCO(48MHz)    トリム量設定 (0.056%/step)
  trimMOCO(-3);     // MOCO(8MHz)     トリム量設定 (1.5%/step)
  trimLOCO(0);      // LOCO(32.768kHz)トリム量設定 (3.77%/step)
  d11AsClockOut();  // 指定クロックをD11ポートから出力
}

void loop() {
}

void d11AsClockOut() {  // D11ピンをクロックアウトで使用
  // CKCORを設定(クロックソースと分周比を設定)
  R_SYSTEM->PRCR = 0xA501;           // レジスタプロテクト解除
  R_SYSTEM->CKOCR = 0;               // CKOCRの全ビットクリア
  R_SYSTEM->CKOCR_b.CKOSEL = 0b000;  // ソース選択 HOCO:000, MOCO:001, LOCO:010, MOSC:011, SOSC:100
  R_SYSTEM->CKOCR_b.CKODIV = 0b011;  // 分周比選択 1分周:000, 001, 010, 011, 100, 101, 110, 128分周:111
  R_SYSTEM->CKOCR_b.CKOEN = 1;       // クロックアウト許可
  R_SYSTEM->PRCR = 0xA500;           // レジスタを再プロテクト
  viewCKOCR();                       // 念のために確認

  // D11(P109)をCLKOUTにアサイン
  R_PMISC->PWPR_b.B0WI = 0;                       // 書き込みプロテクトを、
  R_PMISC->PWPR_b.PFSWE = 1;                      // 外す
  R_PFS->PORT[1].PIN[9].PmnPFS = 0;               // 念のために設定をリセット
  R_PFS->PORT[1].PIN[9].PmnPFS_b.PDR = 1;         // D11(P109)を出力に設定
  R_PFS->PORT[1].PIN[9].PmnPFS_b.PSEL = 0b01001;  // CLKOUTを選択
  R_PFS->PORT[1].PIN[9].PmnPFS_b.PMR = 1;         // 周辺機能をON
  R_PMISC->PWPR_b.PFSWE = 0;                      // 書き込みプロテクトを、
  R_PMISC->PWPR_b.B0WI = 1;                       // セット
  viewD11();                                      // 念のために確認
}

void trimHOCO(int tm) {     // HOCO(48MHz)の周波数をトリム(値は±10程度まで)
  R_SYSTEM->PRCR = 0xA501;  // レジスタプロテクト解除
  R_SYSTEM->HOCOUTCR = tm;  // HOCOの周波数を調整(0.056%/LSB)
  R_SYSTEM->PRCR = 0xA500;  // レジスタを再プロテクト
  sprintf(cbuff, "HOCOUTCR  = 0x%02x", R_SYSTEM->HOCOUTCR);
  Serial.println(cbuff);
}

void trimMOCO(int tm) {     // MOCO(8MHz)の周波数をトリム(値は±10程度まで)
  R_SYSTEM->PRCR = 0xA501;  // レジスタプロテクト解除
  R_SYSTEM->MOCOUTCR = tm;  // HOCOの周波数を調整(0.057%/LSB)
  R_SYSTEM->PRCR = 0xA500;  // レジスタを再プロテクト
  sprintf(cbuff, "MOCOUTCR  = 0x%02x", R_SYSTEM->MOCOUTCR);
  Serial.println(cbuff);
}

void trimLOCO(int tm) {     // LOCO(32.768kHz)の周波数をトリム(値は±10程度まで)
  R_SYSTEM->PRCR = 0xA501;  // レジスタプロテクト解除
  R_SYSTEM->LOCOUTCR = tm;  // HOCOの周波数を調整(3.7%/LSB)
  R_SYSTEM->PRCR = 0xA500;  // レジスタを再プロテクト
  sprintf(cbuff, "LOCOUTCR  = 0x%02x", R_SYSTEM->LOCOUTCR);
  Serial.println(cbuff);
}

void viewCKOCR() {          // CKOCRの内容を出力
  sprintf(cbuff, "CKOCR     = 0x%02x", R_SYSTEM->CKOCR);
  Serial.println(cbuff);
}

void viewD11() {            // D11のポート設定を出力
  sprintf(cbuff, "D11(P109) = 0x%08lx", R_PFS->PORT[1].PIN[9].PmnPFS);
  Serial.println(cbuff);
}
選択できるクロック(と分周率)は一つだけなので、複数のクロックを調整したい場合は設定を変えてその都度確認を行うことになります。トリム量設定のプログラムは単独の関数にしてあるので、その部分を流用すれば別のプログラムからでも簡単に使えるようになっています。

◆測定結果
1)HOCO
HOCOはCPUのメインクロックでR4では標準値は48MHzです。
HOCOtable
トリム量を変えた時の周波数測定結果です。トリム量ゼロの時の測定結果は5.988Mhzなので、8分周する前のクロックの発振周波数は47.904MHzです。標準値は48MHzなので 0.2% 周波数が低いことになります。

HOCOgraph
トリム量を変えた時の周波数変化グラフから近似直線の傾きを調べると、0.0559%でした。これが調整感度になります。

初期の誤差は -0.2% だったので必要なトリム量は +3.58 となります。整数値しか設定出来ないので +4を設定することにしました。この調整を行うことで、-0.2% だったクロック周波数の誤差が +0.024% に改善されるはずです。

2)MOCO
これは8MHzのクロックです。IO周りで使われていると思うのですが詳しい用途は調べていませんが、もしもタイマーに使われているなら、時間精度に直結するので気になります。

MOCOtable
公称値に対して3.25%周波数が高いです。

MocoGraph
調整感度は1.499%なので、3.25/1.499=2.16.つまり、トリム量を-2にしておけば良さそうです。この調整を行えば、+3.25%あったクロック周波数の誤差が +0.25%に減るはずです。

3) LOCO
時計などに使われる32.768kHzのクロックです。
発振周波数の測定結果は33kHzちょうどだったので、0.708%周波数が高いことが判りました。

20240217LocoTable.jpg


LocoGraph
トリム感度は 3.77%とびっくりするくらい大きな値でした。こんなに大きな値だと現状より正確な周波数に合わせ込むことは出来ません。そんなことで、LOCOのトリム量はゼロのままにするしかありませんでした。

◆電源電圧の影響
電源電の変化でクロック周波数が大きく変わってしまうようでは、クロックのトリムを細かく調整してもあまり意味が無くなります。そこで、電源電圧変化に対する、クロック周波数の変化を測定してみました。方法としては、USBコネクタから電源供給し、その電圧を安定化電源を使って3Vから5.5Vまで変化させました。

結果は、大きく電源電圧変化させても、HOCO, MOCO, LOCO の全てで周波数の変化はほとんどありませんでした(ばらつきの範囲内でした)。つまり通常の使用範囲での電源電圧の変化に対して、クロック周波数の変化量は無視できる程度でした。

想像ですが、内部オシレーターは、チップ内のローカルレギュレーターから安定化された電源電圧を供給されて動いているような気がします。

◆まとめ
クロック周波数のユーザートリミングレジスタを設定して、その効果を定量的に測定してみました。

HOCOについては0.056%ステップで調整が可能であることが判りました。もう少し細かく調整が出来ると良いのですが、内蔵クロックは周波数の下の4桁目あたりが常にふらふらと変動しているので、これ以上細かいことを言っても仕方が無い感じです。

LOCOはリアルタイムクロック(RTC)の信号源なので、正確な周期を供給出来るようにしておいて欲しいところです。しかし、元々その誤差が大きく、さらにトリミング量のステップが3.77%と大きいので、正確に調整することは不可能でした。とても残念な結果です。

正確な時計が必要な場合は水晶オシレーターを使うべきなんでしょう。ただ、UNO R4 MINIMA には水晶振動子は付いていないので改造が必要です。(私の買ったR4の互換品の基板には部品の取り付けパッドも省略されていました)。そういう面倒臭いことになるなら、外付けのRTCを使った方が手っ取り早い気がします。

Arduino UNO R4 のクロックを IOピンに出して周波数を測定

◆まえがき
前の記事で、CPUのレジスタの設定方法を理解した のでその使用例として、チップのクロックをIOピンに出す設定を行ってみます。

実はこれをやりたかったので、レジスタの操作方法を調べていました。

◆効果
クロックの信号をIOピンに出せば、周波数カウンタを使った周波数測定が可能になるので、その誤差を正確に測定することが可能となります。また、いろいろな信号源として使えるので便利だと思います。

◆CLKOUT信号
マニュアルを読むと、クロックを外に出す方法として CLKOUT信号が使えそうです。というか名前の通りです。下記はデーターシートの図8.1の一部です。

CLKOUTの回路図

セレクタでクロックソースを切り替え、分周器を通した信号を出力出来るようになっています。(赤丸部)

◆CKOCRレジスタ
上の回路図のセレクタと分周器の設定は CKOCRレジスタで行います。
CKOCRの設定

◆D11ピンへ出力
後は実際に CLKOUT を CPU のピンに出す方法を調べないといけません。データシートの IOポートの設定表を読むと、CLKOUT 信号は以下に示すように P109 のポートから出力可能です。さらに、このポートはボードの D11 ピンに接続されているので楽に測定出来そうです。

・CLKOUTを出せるポートはP109
CLKOUT
PSEL を 01001b に設定すれば CLKOUT が選択されて、ポートから出力されることが判ります。

なお、CLKOUT は P205 のポートからも出力可能で、このポートは CPUパッケージの23番ピンに接続されています。ただ残念なことに、ボードのIOピンには接続されていないので今回は使わないことにしました。もし D11ピンが使えない場合は何とかして線を引き出して P205 の方を使えば良いでしょう。

ここまで判れば後はプログラムを書いて確認です。

◆プログラム
// 20240214_UnoR4ClockOutTest
// D11ピン(P109)にCLKOUT信号を出す

char cbuff[10];  // 文字列操作バッファ

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println();
  Serial.println("Start !");

  // CKCORを設定(クロックソースと分周比を設定)
  R_SYSTEM->PRCR = 0xA501;           // レジスタプロテクト解除
  R_SYSTEM->CKOCR = 0;               // CKOCRの全ビットクリア
  R_SYSTEM->CKOCR_b.CKOSEL = 0b000;  // HOCO:000, MOCO:001, LOCO:010, MOSC:011, SOSC:100
  R_SYSTEM->CKOCR_b.CKODIV = 0b011;  // 1分周:000, 001, 010, 011, 100, 101, 110, 128分周:111
  R_SYSTEM->CKOCR_b.CKOEN = 1;       // クロックアウト許可
  R_SYSTEM->PRCR = 0xA500;           // レジスタを再プロテクト
  viewCKOCR();

  // D11ポートの設定(CKOUTが出力出来るように設定)
  R_PMISC->PWPR_b.B0WI = 0;                       // 書き込みプロテクトを、
  R_PMISC->PWPR_b.PFSWE = 1;                      // 外す
  R_PFS->PORT[1].PIN[9].PmnPFS = 0;               // 念のために設定をリセット
  R_PFS->PORT[1].PIN[9].PmnPFS_b.PDR = 1;         // D11(P109)を出力に設定
  R_PFS->PORT[1].PIN[9].PmnPFS_b.PSEL = 0b01001;  // CLKOUTを選択
  R_PFS->PORT[1].PIN[9].PmnPFS_b.PMR = 1;         // 周辺機能をON
  R_PMISC->PWPR_b.PFSWE = 0;                      // 書き込みプロテクトを、
  R_PMISC->PWPR_b.B0WI = 1;                       // 掛ける
  viewD11();                                      // ちゃんと設定出来たか確認
}

void loop() {
}

void viewCKOCR() {  // CKOCRの内容を出力
  sprintf(cbuff, "CKOCR     = 0x%02x", R_SYSTEM->CKOCR);
  Serial.println(cbuff);
}

void viewD11() {  // D11のポート設定を出力
  sprintf(cbuff, "D11(P109) = 0x%08lx", R_PFS->PORT[1].PIN[9].PmnPFS);
  Serial.println(cbuff);
}
HOCOを8分周で出力する設定になっています。変更したい時は、CKOSEL と CKODIV の設定値を変えてください。
レジスタの操作の前後にプロテクトを外す(設定する)操作が必要になります。詳しくはプログラムを参照ください。

・実行結果
シリアルモニタに下記のような表示が出ればうまく動いています。
シリアルモニタ
CKOCR と P109 のポートが正しく設定されていることが確認出来ます。

・周波数測定結果の例
32.768kHzの測定結果
ボードのD11ピンに出力される信号の周波数を測っている様子です。
LOCO を分周比=1で出力しているので本来なら32.768kHzになっていて欲しいですが、えらくズレています。値については事項でまた触れます。

◆周波数測定結果
下表はクロックソース毎の周波数の測定結果で、CLKOUTの項が測定結果です。

測定結果
MOSCを選択すると、出力が出なかったので空欄になっています。

分周比から計算した元の周波数の値(原発)も記載しています。それらの標準値は上から48MHz, 8MHz, 32.768kHzなのでその値に対する誤差を表の右に表示しています。(SOSCの標準値は判らなかったです)

HOCO周波数の誤差は-0.19%でそこそこ小さいですが、水晶オシレーターと比べると100倍くらい悪い値です。また、LOCOはRTCのクロックとして使われますが、こんなに誤差が大くては、時計としては使えないと思います。

◆まとめ
CPUのレジスタを操作して Arduino UNO R4 のクロックをボードのピンに出して、クロック周波数を測定してみました。

チップ内蔵のクロック源はCRオシレーターなので仕方無いのでしょうが、周波数の誤差が大きいので、正確な時間精度が必要な場合は注意が必要だと思います。安く作るために割り切ったのでしょうが、ここは水晶を使って欲しかったです。

ここまで Arduino UNO R4 をいじってきて、前回の記事のアナログ入力インピーダンスの問題などいろいろな不満点が目に付きます。ただ、チップのマニュアルを読むと、色んな機能や配慮が各所にあってなかなか良く出来ていると感じます。マニュアル読んでいると、なんだかこのチップが好きになっちゃいました。

非難されるのを覚悟で書くと、Arduino UNO R4 ボードのいろいろな性能上の不具合は、チップのせいでは無くボードとしてのまとめ方に問題がある気がします。

◆おまけ
CLKOUTで出力可能な周波数を計算してみました。

オシレーター別出力周波数(Typ.)
緑色部は出力可能な周波数の上限(16MHz)を超えているので使用不可です。
ブザーの音源として使うことを想定すると300〜3kHzの範囲になりますが、この範囲は黄色で示した設定が該当しています。これ、覚えておくと役に立つことがあるかも知れません。

◆追記(2024/2/18)
CLKOUTの周波数の上限は16MHzと書きましたが、実際にはもっと高い周波数でも出力可能でした。下記はHOCOを分周率1で出力した波形です。
HOCO 48MHz クロック
D11から出力した48MHzのクロック波形。
最近うちのオシロの調子が悪いので、使えるうちに写真撮っときます。430Ω→50Ω同軸→オシロ内50Ω終端で観察、テクトロ 2465B (アナログ,400MHz)

Arduino UNO R4 のレジスタ操作解説とピン設定ダンププログラム

◆まえがき
Arduino UNO R4 いじりの話の3回目。前回は アナログポートの挙動を詳しく調査 しましたが、今回はCPUのレジスタ操作方法についてまとめます。

CPUのIO機能の細かい設定を行うためには、内部のレジスタを操作する必要があります。ソフトからはレジスタの名前でアクセスする訳ですが、その指定方法が UNO R3 とは少し違っていたので、そのあたりを整理しておきたいと思います。なお、C言語をちゃんと習った方には当たり前の話が多いのかも知れません。

◆参考URL
このあたりの話、私は素人同然なのでネットの情報を頼りに調べて行きました。参考にさせていただいたのは主に下記サイトです。ありがとうございました。
Arduino UNO R4でポートレジスタ直接制御 (小倉 キャッスル 一馬さん)などの一連の記事
RAマイコンを試食(7) - ピン設定 (なんとなく活動記録。さん)

◆レジスタの指定方法
上記にURLを書いた小倉キャッスルさんが丁寧に解説されていますが、復讐を兼ねて私が知らなかったことを中心に振り返ってみます。以下の例は、小倉キャッスルさんの記事に最初に出て来る、D13ピンを出力にアサインするコードです。

例1 R_PORT1->PCNTR1 |= 1 << 11;
・最初の R_ から見たことが無い表現です。これは、レジスタはグループ分けされていて最上位のレジスタグループ名に  R_ を付けるというルールがあるようです(違うかも)。つまりこの例では、PORT1というレジスタグループ名を示しています。このCPUには大量のレジスタがあるので、グループ分けしておかないといろいろ不便なんだと思います。

・R_PORT1の次の -> はアロー演算子で構造体の構成要素を指定するために使われています。
ちなみに、R言語では <- は代入(向きが逆)。pythonだと -> はアノテーションなのでこれを最初に見た時はピンと来なかったです。

Arduino IDE の 2.0以降なら、アロー演算子の > を入力した直後に下図のように
PORT1の選択肢
そのレジスタで選択可能な項目がプルダウンで表示されるので、そこから選べば間違いを防ぐことが出来ます。これ以降の階層でも、そこで選択可能な項目名が列挙されます。

・PCNTR1は32ビットのポートコントロールレジスタになります。(PCNTR1の上位16ビットにはPODR、下位16ビットにはPDRという名前が付いているので、そちらの名前で呼び出すことも可能です。)
この例ではPCNTR1の11ビット目に1を書いてP111ポート(D13ピン)を出力にアサインしています。

例2 R_PORT1->PDR |= 1 << 11;
前項はPCNTRを指定していたのに対し、この例では下位の16ビットのPDRを呼び出しています。指定方法は前項と同じでビット操作で行っています。

例3 R_PORT1->PDR_b.PDR11 = 1;
ここで新しいルールの登場です。前項のPDRの代わりにPDR_bを指定すると、そのレジスタの中の要素名を指定出来るようになります。_bはビットあるいはバイナリー要素と言う意味でしょうか。この例ではPDRレジスタの中の11ビット目であるPDR11が選択されています。

例3+ R_PORT1->PCNTR1_b.PDR11=1;
引用元には書かれていませんが、PDR11はPCNTR1の要素に含まれているので、上記のようにPCNTR1_b.PDR11;と書く手もあると思います。

例4 R_PFS->PORT[1].PIN[11].PmnPFS |= 1 << 3;
PmnPFSレジスタでポートの機能を詳細に指定する方法です。この方法ではより細かいポートの機能の指定が可能になります。内容としては、
R_PFSでレジスタグループを指定。->のアロー演算子の後にポートとピン番号を指定。最後に辿り着いたPmnPFSレジスタの下から3つ目のビットを立てることで出力ピンに指定しています。

例5 R_PFS->PORT[1].PIN[11].PmnPFS_b.PDR = 1;
この例ではPmnPFS_bと指定することでビット要素での指定を可能とし、その要素名のPDRに1を書くことで出力を指定しています。

◆データーシートとの関係
例4と5で指定しているPFS->PmnPFSレジスタのデーターシートは下記です
PmnPfsSetting

アドレス名の先頭がPFSなので R_PFSと書いてレジスタグループを指定します。ポートとピン名は配列と構造を組み合わせて指定されているようです。R_PFS->PORT[1].PIN[11].PmnPFS_b.PDR = 1; と書けばこのレジスタのPDRに1を書くことが出来ます。

PDRのビットはオレンジ色の矢印で示すようにPmnPFS_BYに含まれているので、
例5は、R_PFS->PORT[1].PIN[11].PmnPFS_BY_b.PDR = 1; と書いても大丈夫だと思います。

◆ポートの設定状態を読み取り
指定したレジスタの記述を代入するだけで読めるので、例えば
x = R_PFS->PORT[1].PIN[11].PmnPFS;
と書けばOKです。上記の例では32ビットの値が返ってくるので xは uint32_t で定義。

レジスタによってはプロテクトが掛かっているために、正しく書けていない場合があります。本当に書けている、そのレジスタを読み出して確認した方が良いです。

◆プログラム例
ポート操作の話の締め括りとして、Arduino UNO R4 MINIMA のボードの全IOピンの設定状態をダンプするプログラムを作ってみました。上に書いたように思った通りに書けているかを確認することが重要だと思います。IOピンのピン毎に関数を分けてあるので必要部分をコピペして使うと便利だと思います。また、他のレジスタの確認プログラムも、これを雛形にして作れば楽に出来ると思います。
注:これはR4 MINIMA用です。R4 WiFi はピンアサインが異なる部分があるのでこのままでは使えません。
// Arduino UNO R4 MINIMAのIOピンの初期状態をダンプ
// 20240213_UnoR4MinimaPinDeffDump
// ラジオペンチ http://radiopench.blog96.fc2.com/

char cbuff[10];  // 文字列操作バッファ

void setup() {
  Serial.begin(115200);
  delay(1000);  // while(!Serial){} は使えなかった
}

void loop() {
  Serial.println();
  Serial.println("Arduino UNO R4 MINIMA PFS dump");
  viewD0();
  viewD1();
  viewD2();
  viewD3();
  viewD4();
  viewD5();
  viewD6();
  viewD7();
  viewD8();
  viewD9();
  viewD10();
  viewD11();
  viewD12();
  viewD13();
  viewA0();
  viewA1();
  viewA2();
  viewA3();
  viewA4();
  viewA5();
  delay(200);
}

void viewD0() {                                                          // D0
  sprintf(cbuff, "D0  (P301) = 0x%08lx", R_PFS->PORT[3].PIN[1].PmnPFS);  // 対象ポートのPFSを16進8桁の文字列に変換
  Serial.println(cbuff);
}
void viewD1() {  // D1
  sprintf(cbuff, "D1  (P302) = 0x%08lx", R_PFS->PORT[3].PIN[2].PmnPFS);
  Serial.println(cbuff);
}
void viewD2() {  // D2
  sprintf(cbuff, "D2  (P105) = 0x%08lx", R_PFS->PORT[1].PIN[5].PmnPFS);
  Serial.println(cbuff);
}
void viewD3() {  // D3
  sprintf(cbuff, "D3  (P104) = 0x%08lx", R_PFS->PORT[1].PIN[4].PmnPFS);
  Serial.println(cbuff);
}
void viewD4() {  // D4
  sprintf(cbuff, "D4  (P103) = 0x%08lx", R_PFS->PORT[1].PIN[3].PmnPFS);
  Serial.println(cbuff);
}
void viewD5() {  // D5
  sprintf(cbuff, "D5  (P102) = 0x%08lx", R_PFS->PORT[1].PIN[2].PmnPFS);
  Serial.println(cbuff);
}
void viewD6() {  // D6
  sprintf(cbuff, "D6  (P106) = 0x%08lx", R_PFS->PORT[1].PIN[6].PmnPFS);
  Serial.println(cbuff);
}
void viewD7() {  // D7
  sprintf(cbuff, "D7  (P107) = 0x%08lx", R_PFS->PORT[1].PIN[7].PmnPFS);
  Serial.println(cbuff);
}
void viewD8() {  // D8
  sprintf(cbuff, "D8  (P304) = 0x%08lx", R_PFS->PORT[3].PIN[4].PmnPFS);
  Serial.println(cbuff);
}
void viewD9() {  // D9
  sprintf(cbuff, "D9  (P303) = 0x%08lx", R_PFS->PORT[3].PIN[3].PmnPFS);
  Serial.println(cbuff);
}
void viewD10() {  // D10
  sprintf(cbuff, "D10 (P112) = 0x%08lx", R_PFS->PORT[1].PIN[12].PmnPFS);
  Serial.println(cbuff);
}
void viewD11() {  // D11
  sprintf(cbuff, "D11 (P109) = 0x%08lx", R_PFS->PORT[1].PIN[9].PmnPFS);
  Serial.println(cbuff);
}
void viewD12() {  // D12
  sprintf(cbuff, "D12 (P110) = 0x%08lx", R_PFS->PORT[1].PIN[10].PmnPFS);
  Serial.println(cbuff);
}
void viewD13() {  // D13
  sprintf(cbuff, "D13 (P111) = 0x%08lx", R_PFS->PORT[1].PIN[11].PmnPFS);
  Serial.println(cbuff);
}
void viewA0() {  // A0
  sprintf(cbuff, "A0  (P014) = 0x%08lx", R_PFS->PORT[0].PIN[14].PmnPFS);
  Serial.println(cbuff);
}
void viewA1() {  // A1
  sprintf(cbuff, "A1  (P000) = 0x%08lx", R_PFS->PORT[0].PIN[0].PmnPFS);
  Serial.println(cbuff);
}
void viewA2() {  // A2
  sprintf(cbuff, "A2  (P001) = 0x%08lx", R_PFS->PORT[0].PIN[1].PmnPFS);
  Serial.println(cbuff);
}
void viewA3() {  // A3
  sprintf(cbuff, "A3  (P002) = 0x%08lx", R_PFS->PORT[0].PIN[2].PmnPFS);
  Serial.println(cbuff);
}
void viewA4() {  // A4
  sprintf(cbuff, "A4  (P101) = 0x%08lx", R_PFS->PORT[1].PIN[1].PmnPFS);
  Serial.println(cbuff);
}
void viewA5() {  // A5
  sprintf(cbuff, "A5  (P100) = 0x%08lx", R_PFS->PORT[1].PIN[0].PmnPFS);
  Serial.println(cbuff);
}

正常に動くと、シリアルにポートのPFSの32ビットを16進形式でダンプします。
Arduino UNO R4 MINIMA PFS dump
D0 (P301) = 0x00000000
D1 (P302) = 0x00000000
D2 (P105) = 0x00000000
D3 (P104) = 0x00000000
D4 (P103) = 0x00000000
D5 (P102) = 0x00000000
D6 (P106) = 0x00000000
D7 (P107) = 0x00000000
D8 (P304) = 0x00000000
D9 (P303) = 0x00000000
D10 (P112) = 0x00000000
D11 (P109) = 0x00010000
D12 (P110) = 0x00010012
D13 (P111) = 0x00000004
A0 (P014) = 0x00000000
A1 (P000) = 0x00000000
A2 (P001) = 0x00000000
A3 (P002) = 0x00000000
A4 (P101) = 0x00000002
A5 (P100) = 0x00000002

このダンプは繰り返し表示します。
リセット直後はD13以外は入力ピンになっているので、IOピンに電圧を加えると状態が変化する様子を見ることが出来ます。

これ見ると、D11とD12には特別な機能が割り付けられているようです。
また、D12は入力のプルアップに指定されています。ということは、

・D12でLEDが光った
D12でLEDが光った
リセット後の UNO R4 の D12ピンはプルアップ(約20kΩ)されているのでLEDを繋ぐと光る。

◆まとめ
これで UNO R4 のレジスタの操作方法をマスターしました(したと思う)。設定の確認プログラムも出来たので、次は気になっているクロック周波数の確認と調整にチャレンジする予定です。

記事中に書き忘れましたが、操作するビットの要素はその名前を書いておいた方がコードが読み易くなると思います。つまり、例1や例2のように11ビット目と書くのではなく、PDR11と明記した方が判り易いコードになると思います。

◆話の整理
CPUレジスタのアクセス方法を UNO R3 などと比較すると、
1. R_周辺機能名->レジスタ名.[レジスタ名] の階層構造で目的のレジスタを指定する。
2. レジスタ内のビット(集合)を指定したい場合は、レジスタ名_b.ビット(集合)名の形式で指定する。
3. 入力可能な項目名はプルダウンメニューに出て来るので、その中から選択するとミスを防げる。(IDE2.0以上の場合)

カレンダー
01 | 2024/02 | 03
- - - - 1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 - -
プロフィール

ラジオペンチ

Author:ラジオペンチ
電子工作を中心としたブログです。たまに近所(東京都稲城市)の話題など。60過ぎて視力や器用さの衰えを感じつつ日々挑戦!
コメントを入れる時にメールアドレスの記入は不要です。なお、非公開コメントは受け付けていません。
記事の内容のご利用は読者の自己責任でお願いします。

記事が気に入ったらクリックを!
最新記事
カテゴリ
最新コメント
リンク
FC2カウンター
検索フォーム
月別アーカイブ
RSSリンクの表示
QRコード
QRコード