« UNO R4はanalogWrite(n,128)でデュティー50%の方形波が出るぞ | トップページ | Better Power Battery社の単3ニッ水「Pool」2150mAh »

2025年6月 6日 (金)

Arduino UNO R4で2つの1Hz信号の位相変化を見る

まさにラジオペンチさんに試してもらいたい内容です。

Arduino UNO R4に搭載のマイコンRA4M1のタイマカウンタに
は贅沢な機能が搭載されています。

周波数や周期の測定で活用できるのがインプットキャプチャ。
各タイマ、2つのキャプチャ入力を持っています。
2つあれば、2つの信号の位相差変化を観察することができます。

48MHzのメインクロックでタイマを走らせ、A入力とB入力で
タイマカウント値をキャプチャさせます。
2つのカウント値の差の変化が位相差変化。
48MHzが分解能。

さらに面白いことに、キャプチャ信号でカウンタの
クリアもできるのです。

キャプチャAを「正の1Hz」につなぎ、キャプチャさせる
とともにクリアの機能も有効に。
するとキャプチャAのデータには1Hzのカウント値が
ラッチされ、同時にゼロクリアしてカウントが
再スタートします。
ラッチされたデータは1Hz間のクロック数ですので、
そのまま48000000が出てきます。
32bitカウンタだからできる技。

キャプチャBは比較したいクロック源につなぎます。

1.1Hzを入れるとこんな感じでデータが得られます。
   cap A    cap B    D=A-B   Dn-D(n-1)
  48000407   3824229  44176178   3261976
  48000277  45300801   2699476   3261599
  48000397  42039033   5961364   3261888
  48000381  38776951   9223430   3262066
  48000361  35515063  12485298   3261868
  48000385  32253111  15747274   3261976
  48000393  28991307  19009086   3261812
  48000349  25729291  22271058   3261972
  48000313  22467319  25532994   3261936
  48000473  19205553  28794920   3261926
  48000277  15943485  32056792   3261872
  48000485  12681605  35318880   3262088
  48000329   9419641  38580688   3261808
  48000335   6157735  41842600   3261912
  48000457  47634349   366108   3262053

cap Aは48MHz付近をうろうろ。
  ※ジッタによるクロック誤差が憎い!
cap Bは1.1Hz(およそ)でキャプチャ値がゼロのほうに
進みます。

右端の数字が差の差でおよそ一定。
  差のオーバーフロ発生時、偏差として突拍子も
  ない数字が出ないよう、押さえつけています。
  前回値を保存するややこしい処理はこれのため。

cap A検出のタイミングでデータを出力していますが、
その直前に新たなcap Bのデータが確定してないとい
けません。
B波形の変動によってこれが抜けることがあります。
  BのエッジがAのエッジを左から右に
  通り過ぎた時。
  Aの2つのエッジ間にBのエッジが一つも
  入らないタイミング。
現スケッチは、このタイミングの計数を無視し
ちゃってます。

/**********************************************/
/* Arduino UNO R4 minima, 1Hz位相差検出 */
/**********************************************/
// 入力ピン
// D2 P105 GPT1A キャプチャ入力A 外部から1Hzを入力 ↓を検出
// キャプチャと同時にGTCNTクリア
// D3 P104 GPT1B キャプチャ入力B 比較入力 ↓を検出
// タイマー1インプットキャプチャフラグ
#define CK_TCFA (R_GPT1->GTST_b.TCFA) // GPT1インプットキャプチャAチェック
#define CLR_TCFA (R_GPT1->GTST_b.TCFA = 0) // インプットキャプチャAクリア
#define CK_TCFB (R_GPT1->GTST_b.TCFB) // GPT1インプットキャプチャBチェック
#define CLR_TCFB (R_GPT1->GTST_b.TCFB = 0) // インプットキャプチャBクリア
// 文字出力バッファ
char str_bff[80]; // sprintfで使用
/***** SETUP *****/
void setup() {
// モジュールストップ解除
R_MSTP->MSTPCRD_b.MSTPD5 = 0; // 32bit PWMモジュールストップ解除
R_MSTP->MSTPCRD_b.MSTPD6 = 0; // 16bit PWMモジュールストップ解除
R_MSTP->MSTPCRD_b.MSTPD14 = 0; // POEGモジュールストップ解除
// D2:P105 GPT1A キャプチャ入力A
R_PFS->PORT[1].PIN[5].PmnPFS_b.PCR = 1; // P105入力pullup
R_PFS->PORT[1].PIN[5].PmnPFS_b.PSEL = 0b00010; // GTETRGA入力に
R_PFS->PORT[1].PIN[5].PmnPFS_b.PMR = 1; // 周辺機能有効
R_GPT1->GTICASR_b.ASGTRGAF = 1; // A↓でキャプチャ
R_GPT1->GTCSR_b.CSGTRGAF = 1; // A↓でクリア
// D3:P104 GPT1B キャプチャ入力B
R_PFS->PORT[1].PIN[4].PmnPFS_b.PCR = 1; // P105入力pullup
R_PFS->PORT[1].PIN[4].PmnPFS_b.PSEL = 0b00010; // GTETRGB入力に
R_PFS->PORT[1].PIN[4].PmnPFS_b.PMR = 1; // 周辺機能有効
R_GPT1->GTICBSR_b.BSGTRGBF = 1; // B↓でキャプチャ
R_GPT1->GTCR_b.CST = 1; // GPT1カウント開始
// Serialでデータ出力
Serial.begin(9600); // 9600BPSで
while (!Serial); // USB接続チェック
}

/***** キャプチャデータ *****/
int32_t d_cap[2]; // キャプチャA,B値
// Aにジャスト1Hzを入れるとcap[0]は48000000となる
int32_t d_ab; // 差分(A-B)データ プラスの値
int32_t m_ab; // 前回の差分保存データ
int32_t d_dev; // 差分からの偏差
int32_t m_dev; // 偏差保存データ
int32_t m_cap0; // cap A保存データ
byte f_mab; // A-B差分保存済みフラグ
/***** LOOP *****/
void loop() {
Serial.println("1Hz_PH01b.ino"); // タイトル
while(1){
if(CK_TCFA){ // キャプチャA発生
CLR_TCFA; // Aフラグクリア
if(CK_TCFB){ // キャプチャBあり?
CLR_TCFB; // Bフラグクリア
d_cap[0] = R_GPT1->GTCCR[0]; // CAP Aデータ保存
d_cap[1] = R_GPT1->GTCCR[1]; // CAP Bデータ保存
d_ab = d_cap[0] - d_cap[1]; // A-B 差分
if(f_mab == 0){ // 初めての差分
f_mab = 1;
m_ab = d_ab; // 差分を保存
m_cap0 = d_cap[0]; // Aキャプチャ値を保存
}
else{ // 差分2回目以降
d_dev = d_ab - m_ab; // 偏差:差分の差
if(abs(d_dev) >= (d_cap[0] / 2)){ // 前回との差が48Mの半分越え
if(d_dev >= 0){ // 差がプラス
d_dev = d_dev - d_cap[0]; // 偏差を修正
}
else{ // 差がマイナス
d_dev = d_dev - m_dev + m_cap0; // 偏差を修正
}
}
sprintf(str_bff, "%11u %11u %11d %11d",
d_cap[0], d_cap[1], // A,Bキャプチャ値
d_ab, // A-B差分
d_dev); // 偏差
Serial.println(str_bff);
m_ab = d_ab; // 差分を保存
m_dev = d_dev; // 偏差を保存
m_cap0 = d_cap[0]; // Aキャプチャ値を保存
}
}
else{ // ★ capAが来た時にcapBが無かったら
// Serial.println(); // 改行で知らせる
}
}
}
}


※関連
GPSとArduinoでRTCの誤差(確度)を精密測定

実用するには、UNO R4に水晶発振子をくっつけて
クロックを安定化しないといけません。
クロック精度が悪くてもかまわないのですが、
(UNO R3のように)、UNO R4のジッタがイケません。

計測データ、右端cap A値、ほんとなら48000000近辺で
止まっていてくれなくちゃいけません。

※実験中の様子
1hzz
左から
・DDS IC AD9833を使って1.0Hzとかを発生。
   0.1Hzを調整できる。
・UNO R4基板
・32.768kHz時計用水晶を使った1Hz発生回路
・12.8MHz発振子を使った1Hz発生回路

※追記:RA4M1コアボードで試す
RA4M1コアボードの水晶(16MHz)+PLLを使って、
1Hzの位相差検出を試してみました。
 ・スケッチ:
    ダウンロード - 1hz_phx01.txt
すると・・・

cap Aが12.8MHz発振器を使った1Hz発生回路
cap Bが32.768kHz時計用水晶を使った1Hz発生回路

 cap A  cap B  D=A-B Dn-D(n-1)
48001139 34840845 13160294 194
48001141 34840651 13160490 196
48001139 34840455 13160684 194
48001141 34840261 13160880 196
48001139 34840065 13161074 194
48001139 34839871 13161268 194
48001141 34839677 13161464 196
 :

・AとBを入れ換えると

48000945 13164763 34836182 -196
48000947 13164957 34835990 -192
48000945 13165149 34835796 -194
48000947 13165343 34835604 -192
48000947 13165537 34835410 -194
48000947 13165729 34835218 -192
48000947 13165921 34835026 -192
48000947 13166115 34834832 -194
 :

・PLLを使うことによりcap Aの値が安定。
  飛びが2~3に
・偏差も安定。
・cap AとcapBを入れ換えると偏差の+/-が逆に。
・RA4M1コアボードの水晶発振+PLL周波数がちょい高め。

このくらいだと位相変化が安心して読め、
1Hz発生回路の違いが見えます。

|

« UNO R4はanalogWrite(n,128)でデュティー50%の方形波が出るぞ | トップページ | Better Power Battery社の単3ニッ水「Pool」2150mAh »

Arduino UNO R4」カテゴリの記事

コメント

32ビットカウンタなので48MHzクロックを直接計測しても約90秒くらいの時間は測れるということですね。
こりゃ水晶クロックが実装されているRA4M1のコアボードが欲しくなります。ただあのボードはブレッドボードに挿さらないのが残念なんですよね。

投稿: ラジオペンチ | 2025年6月 9日 (月) 07時21分

32ビットカウンタは偉大です。

投稿: 居酒屋ガレージ店主(JH3DBO) | 2025年6月 9日 (月) 14時11分

UNO R4 minimaに12MHz水晶を。
http://igarage.cocolog-nifty.com/blog/2025/06/post-500e2e.html
大きいけれど、HC-49USのをハンダ付け。

投稿: 居酒屋ガレージ店主(JH3DBO) | 2025年6月10日 (火) 08時46分

コメントを書く



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




« UNO R4はanalogWrite(n,128)でデュティー50%の方形波が出るぞ | トップページ | Better Power Battery社の単3ニッ水「Pool」2150mAh »