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が
出てきます。
最近のコメント