« CQ ham radio 5月号に「14年ぶりに改正されたJIS規格」の記事 | トップページ | トラ技原稿執筆時の注意点 »

2025年4月28日 (月)

Arduino UNO R3のクロック精度を1MHzパルスで確かめる

ラジオペンチさんが、
Arduinoのmicros()を使ったLチカでCPUのクロック精度を測定 
という記事を書かれていたんで、別のアプローチで迎撃してみます。
こんなスケッチ。

・16ビットタイマーをCTCモードにしてOC1A(PB1:D9)にジャスト
 1MHzの方形波を出力。
   このパルス出力はATmega328Pのハードが出してくれるので
   ジッターは無し。
・おまけで、
   loopの毎サイクルでLEDポート(D13)をトグル。
   millis()値の変化でD8ポートをトグル。

//  Arduino UNO R3 発振周波数確認
// PB1:OC1A(D9)に1MHz方形波を出力
// 16MHz / 8 / 2 で1MHz
// PB0(D8) millis()で1msをチェックして方形波出力
// PB5(D13) LEDポート loopで方形波出力
/***** セットアップ *****/
void setup()
{
pinMode( 9, OUTPUT); // PB1 OC1A 1MHz出力
pinMode( 8, OUTPUT); // PB0 1msごとにトグル出力
pinMode(13, OUTPUT); // PB5 LED loopでトグル出力
// タイマー1 16MHz/8 COM1Aトグル出力
TCCR1A = 0b01000000;
// |||| ++--- WGM OCR1AでCTC
// ||++------- COM1B
// ++--------- COM1A トグル出力
TCCR1B = 0b00001001;
// || ||+++--- CS 16MHz入力
// || ++------ WGM OCR1AでCTC
// |+--------- ICES1
// |---------- ICNC1
TIMSK1 = 0b00000000; // 割り込みオフ
// | ||+--- TOIE1
// | |+---- OCIE1A
// | +----- OCIE1B
// +-------- ICIE1
OCR1A = 8 - 1; // 16MHz/8 2MHz, OC1Aトグルで1MHzに
}

/***** LOOP *****/
void loop()
{
uint32_t ms , a;
ms = millis(); // 現1ms値
while(1){
PINB |= (1 << PB5); // LEDポートトグル
a = millis();
if(ms != a){ // 1ms変化?
PINB |= (1 << PB0); // PB0 (D8)ポートトグル
ms = a; // 次の1msを待つ
}
}
}

これで、
(a) PB1の周波数を読めば、クロックの誤差がわかる。
(b) millis()値の変化は1msサイクルじゃないことがわかる。
(c) タイマー0割り込みの処理時間がわかる。
というのを確かめられるかと。

この命令、
  PINB |= (1 << PB5); // LEDポートトグル
AVRマイコンデータシートのポート解説のところを見てください。
 「PINxnへの論理1書き込みはDDRxnの値によらず
  PORTxnの値を反転切り替えします。」
となっていて、SBI命令ひとつで出力ビットの反転操作が
できるのです。

BLINKの応用で、LEDを点滅させることが
あるかと思いますが、
  現在のon/offを判断
  digitalWriteでon/offを設定
なんてまどろっこしいことをしなくても
最短の時間、最小の命令バイト数でポートを
トグルできるのです。

どうか、覚えておいてください。

※関連
2020年2月4日:Arduinoのタイマー OCRレジスタは「n」じゃなく「n - 1」の値を設定せよ


*追記:Raspberry Pi Picoだと

//  ラズパイピコの周波数確認
// GP2 1MHz方形波 PWMで出力
// GP4 LOOPでトグル出力
// GP6 millisによる1msごとにトグル(500Hz)
#define GP2 2 // PWM出力
#define SN2 (GP2 / 2) // PWMスライスナンバー
/***** SETUP *****/
void setup(){
pinMode(25, OUTPUT); // LED
pinMode( 4, OUTPUT); // loopで反転
pinMode( 6, OUTPUT); // 1msで反転
// PWMの設定
gpio_set_function(GP2, GPIO_FUNC_PWM); // GP2をPWM出力に
pwm_set_clkdiv(SN2, 12.5); // 125MHz / 12.5 = 10MHz
pwm_set_wrap(SN2, 10 - 1); // PWM 分解能
pwm_set_chan_level(SN2, GP2 & 1, 5); // ch-A PWM値
pwm_set_enabled(SN2, 1); // PWMスタート
}

/***** LOOP *****/
void loop(){
uint32_t ms, a;
byte f_xLED = 0; // LEDポート反転フラグ
byte f_xGP6 = 0; // GP6ポート反転フラグ
ms = millis(); // ms現在値
while(1){
f_xLED ^= 1; // LEDポートトグル
gpio_put(25, f_xLED); // LED出力
gpio_put( 4, f_xLED); // GP4出力
a = millis();
if(ms != a){ // 1ms変化?
f_xGP6 ^= 1; // GP6ポートトグル
gpio_put( 6, f_xGP6); // GP6出力
ms = a; // 次の1msを待つ
}
}
}

GP2ポートをPWM出力にして1MHzを出すようにしてみました。
こちらも搭載マイコンRP2040のハードウェアを使って
パルスを出していますんで、走っているソフトの影響で
パルスが遅れるとか進むとかはありません。

  ※RP2040のPWMユニットの分周器、n・m/16で
   設定できるのが面白い。
   主クロックの125MHzを1/12.5とすれば10MHzが
   出てきます。

|

« CQ ham radio 5月号に「14年ぶりに改正されたJIS規格」の記事 | トップページ | トラ技原稿執筆時の注意点 »

Arduino」カテゴリの記事

ラズパイ・ピコ」カテゴリの記事

コメント

コメントを書く



(ウェブ上には掲載しません)




« CQ ham radio 5月号に「14年ぶりに改正されたJIS規格」の記事 | トップページ | トラ技原稿執筆時の注意点 »