GR-SAKURA
GR-KURUMI
GR-COTTON
GR-CITRUS
GR-PEACH
GR-KAEDE
GR-ADZUKI
GR-LYCHEE
GR-ROSE
GR-MANGO(*)
SNShield
Web Compiler
IDE for GR
TOPPERS関連
女子美コラボ
その他
※プロデューサミーティング中
作り方使い方資料
イベント関連
作品記事
体験記事
ライブラリ
ツール
その他・過去ファイル
デジタルに限らず信号を視覚的に確認してえとかそういう要求に対しては、オシロスコープとかなんかそういう専門的な道具を使うのはプロのアレでは普通のことなのかもしれませんが、自分のような素人が自宅にそんなもんあるわけもないのでなんとか手軽にそういうことできないかしらということで、GR-SAKURA に載ってる RX63N のインプットキャプチャ機能を使って試してみた也。
GR-SAKURA に下記のスケッチをビルドして書き込む。
/*GR-SAKURA Sketch Template Version: V1.07*/ #include <rxduino.h> #include <iodefine_gcc63n.h> #include <intvect63n.h> static const int Channels = 2; static const int BufferSize = 10000; static const unsigned long CaptureTime = 5000; static const unsigned long PCLK = 48000000; typedef struct { int8_t state[Channels]; uint16_t clockLow; uint32_t clockHigh; } BufferType; static volatile BufferType buffer[BufferSize]; static volatile int bufferIndex; static volatile uint32_t tcntHigh; void MyExcep_TPU0_TGIC0(void) { if (bufferIndex < BufferSize) { int i = bufferIndex++; uint16_t low = TPU0.TGRC; buffer[i].state[0] = HIGH; buffer[i].clockLow = low; buffer[i].clockHigh = tcntHigh + (ICU.GRP[3].BIT.IS0 && low < 0x8000); } } void MyExcep_TPU0_TGID0(void) { if (bufferIndex < BufferSize) { int i = bufferIndex++; uint16_t low = TPU0.TGRD; buffer[i].state[0] = LOW; buffer[i].clockLow = low; buffer[i].clockHigh = tcntHigh + (ICU.GRP[3].BIT.IS0 && low < 0x8000); } } void MyExcep_TPU3_TGIA3(void) { if (bufferIndex < BufferSize) { int i = bufferIndex++; uint16_t low = TPU3.TGRA; buffer[i].state[1] = HIGH; buffer[i].clockLow = low; buffer[i].clockHigh = tcntHigh + (ICU.GRP[3].BIT.IS0 && low < 0x8000); } } void MyExcep_TPU3_TGIB3(void) { if (bufferIndex < BufferSize) { int i = bufferIndex++; uint16_t low = TPU3.TGRB; buffer[i].state[1] = LOW; buffer[i].clockLow = low; buffer[i].clockHigh = tcntHigh + (ICU.GRP[3].BIT.IS0 && low < 0x8000); } } void MyExcep_GROUP_E3(void) { if (ICU.GRP[3].BIT.IS0) { // TPU0.TCNT オーバフロー(TCI0V) tcntHigh++; ICU.GCR[3].BIT.CLR0 = 1; // 割り込み要因クリア } } static int compBuffer(const BufferType* pa, const BufferType* pb) { uint64_t a = (pa->clockHigh << 16) | pa->clockLow; uint64_t b = (pb->clockHigh << 16) | pb->clockLow; return (a < b) ? -1 : ((a > b) ? 1 : 0); } void setup() { // Serial.begin(230400, SCI_SCI1JTAG); Serial.begin(230400, SCI_AUTO); Serial.setDefault(); setvbuf(stdout, NULL, _IONBF, 0); sci_convert_crlf_ex(Serial.get_handle(), CRLF_CRLF, CRLF_NONE); pinMode(PIN_SW, INPUT); reset: printf("\x1b[2J\x1b[H"); bufferIndex = 0; for (int i = 0; i < BufferSize; i++) { for (int j = 0; j < Channels; j++) { buffer[i].state[j] = -1; } buffer[i].clockLow = 0; buffer[i].clockHigh = 0; } tcntHigh = 0; SYSTEM.MSTPCRA.BIT.MSTPA13 = 0; // TPU0 ~TPU5のモジュールストップを解除 TPUA.TSTR.BYTE &= 0x36; // TPU0,TPU3カウント停止 MPC.P32PFS.BIT.PSEL = 3; // TIOCC0 PORT3.PMR.BIT.B2 = 1; // P32を周辺機能とする TPU0.TCR.BIT.TPSC = 0; // PCLK / 1 TPU0.TMDR.BIT.MD = 0; // 通常動作 TPU0.TMDR.BIT.ICSELD = 1; // インプットキャプチャ入力元はTIOCCn端子 TPU0.TIORL.BIT.IOC = 8; // インプットキャプチャは立上がり TPU0.TIORL.BIT.IOD = 9; // インプットキャプチャは立下がり TPU0.TIER.BIT.TGIEC = 1; // TGRC 割り込み許可 TPU0.TIER.BIT.TGIED = 1; // TGRD 割り込み許可 TPU0.TIER.BIT.TCIEV = 1; // オーバーフロー割り込み許可 IPR(TPU0, TGI0C) = 15; // 割り込み要因プライオリティ IEN(TPU0, TGI0C) = 1; // 割り込み要求許可 IPR(TPU0, TGI0D) = 15; // 割り込み要因プライオリティ IEN(TPU0, TGI0D) = 1; // 割り込み要求許可 IPR(ICU, GROUPE3) = 14; // 割り込み要因プライオリティ IEN(ICU, GROUPE3) = 1; // 割り込み要求許可 ICU.GEN[3].BIT.EN0 = 1; // 割り込み要求許可 MPC.P21PFS.BIT.PSEL = 3; // TIOCA3 PORT2.PMR.BIT.B1 = 1; // P21を周辺機能とする TPU3.TCR.BIT.TPSC = 0; // PCLK / 1 TPU3.TMDR.BIT.MD = 0; // 通常動作 TPU3.TMDR.BIT.ICSELB = 1; // インプットキャプチャ入力元はTIOCAn端子 TPU3.TIORH.BIT.IOA = 8; // インプットキャプチャは立上がり TPU3.TIORH.BIT.IOB = 9; // インプットキャプチャは立下がり TPU3.TIER.BIT.TGIEA = 1; // TGRA 割り込み許可 TPU3.TIER.BIT.TGIEB = 1; // TGRB 割り込み許可 IPR(TPU3, TGI3A) = 15; // 割り込み要因プライオリティ IEN(TPU3, TGI3A) = 1; // 割り込み要求許可 IPR(TPU3, TGI3B) = 15; // 割り込み要因プライオリティ IEN(TPU3, TGI3B) = 1; // 割り込み要求許可 if (bufferIndex < BufferSize) { int i = bufferIndex++; for (int j = 0; j < Channels; j++) { switch (j) { case 0: buffer[i].state[j] = PORT3.PIDR.BIT.B2 ? HIGH : LOW; break; case 1: buffer[i].state[j] = PORT2.PIDR.BIT.B1 ? HIGH : LOW; break; } } buffer[i].clockLow = 0; buffer[i].clockHigh = 0; } TPU0.TCNT = 0; // TPU0 カウント値初期化 TPU3.TCNT = 0; // TPU3 カウント値初期化 TPUA.TSTR.BYTE |= 0x09; // TPU0,TPU3カウント動作 // 決められた時間キャプチャを行う { unsigned long start = millis(); while ((millis() - start) < CaptureTime) { if (!digitalRead(PIN_SW)) { goto reset; } } } IEN(TPU0, TGI0C) = 0; // 割り込み要求禁止 IEN(TPU0, TGI0D) = 0; // 割り込み要求禁止 IEN(TPU3, TGI3A) = 0; // 割り込み要求禁止 IEN(TPU3, TGI3B) = 0; // 割り込み要求禁止 TPUA.TSTR.BYTE &= 0x36; // TPU0,TPU3カウント停止 if (bufferIndex < BufferSize) { int i = bufferIndex++; for (int j = 0; j < Channels; j++) { switch (j) { case 0: buffer[i].state[j] = PORT3.PIDR.BIT.B2 ? HIGH : LOW; break; case 1: buffer[i].state[j] = PORT2.PIDR.BIT.B1 ? HIGH : LOW; break; } } uint16_t low = TPU0.TCNT; buffer[i].clockLow = low; buffer[i].clockHigh = tcntHigh + (ICU.GRP[3].BIT.IS0 && low < 0x8000); } // キャプチャした値について、同タイミングで複数チャネルの立ち上がり/立下りが // 発生した場合にクロックの値が前後する場合があるので並び替える qsort((void*)buffer, bufferIndex, sizeof(BufferType), (int(*)(const void*, const void*))compBuffer); // 同タイミングで複数チャネルの立ち上がり/立下りがあった場合の内容を // マージする { int dst = 0; for (int src = 0; src < bufferIndex;) { BufferType t = *(const_cast<BufferType*>(&buffer[src])); int i; for (i = src + 1; i < bufferIndex; i++) { if (t.clockHigh != buffer[i].clockHigh || t.clockLow != buffer[i].clockLow) { break; } for (int j = 0; j < Channels; j++) { if (buffer[i].state[j] >= 0) { t.state[j] = buffer[i].state[j]; } } } *(const_cast<BufferType*>(&buffer[dst++])) = t; src = i; } bufferIndex = dst; } // 出力 for (int i = 0; i < (bufferIndex - 1); i++) { if (!digitalRead(PIN_SW)) { goto reset; } #define clock2nsec(high, low) (1000 * (long long)((high << 16) | low) / (PCLK / 1000000)) long long start = clock2nsec(buffer[i].clockHigh, buffer[i].clockLow); printf("%8d.%03d: ", int(start / 1000), int(start % 1000)); for (int j = 0; j < Channels; j++) { printf("%c", buffer[i].state[j] < 0 ? '|' : (buffer[i].state[j] ? 'H' : 'L')); if (buffer[i].state[j] >= 0 && (i == 0 || buffer[i].state[j] != buffer[i - 1].state[j])) { long long end = start; for (int k = i + 1; k < bufferIndex; k++) { end = clock2nsec(buffer[k].clockHigh, buffer[k].clockLow); if (buffer[k].state[j] >= 0 && buffer[i].state[j] != buffer[k].state[j]) { break; } } printf("[%8d.%03d]", int((end - start) / 1000), int((end - start) % 1000)); } else { printf("%14s", ""); } printf(" "); } printf("\n"); } // リセット待ち for (;;) { if (!digitalRead(PIN_SW)) { goto reset; } } } void loop() { }
ビルドするに当たっては、gr_common/intvect63n.h の
293:void Excep_GROUP_E3(void) __INTTERUPT_FUNC ; 336:void Excep_TPU0_TGIC0(void) __INTTERUPT_FUNC ; 339:void Excep_TPU0_TGID0(void) __INTTERUPT_FUNC ; 354:void Excep_TPU3_TGIA3(void) __INTTERUPT_FUNC ; 357:void Excep_TPU3_TGIB3(void) __INTTERUPT_FUNC ;
各行を
293:void MyExcep_GROUP_E3(void) __INTTERUPT_FUNC ; 336:void MyExcep_TPU0_TGIC0(void) __INTTERUPT_FUNC ; 339:void MyExcep_TPU0_TGID0(void) __INTTERUPT_FUNC ; 354:void MyExcep_TPU3_TGIA3(void) __INTTERUPT_FUNC ; 357:void MyExcep_TPU3_TGIB3(void) __INTTERUPT_FUNC ;
に、
gr_common/intvect.c の
987: Excep_GROUP_E3, 1006: Excep_TPU0_TGIC0, 1007: Excep_TPU0_TGID0, 1012: Excep_TPU3_TGIA3, 1013: Excep_TPU3_TGIB3,
987: MyExcep_GROUP_E3, 1006: MyExcep_TPU0_TGIC0, 1007: MyExcep_TPU0_TGID0, 1012: MyExcep_TPU3_TGIA3, 1013: MyExcep_TPU3_TGIB3,
に書き換える必要がある。
以上の使い方で、例えば以下のようなのが端末ソフトに出力される筈。
0.000: L[ 7827.541] L[ 8380.166] 7827.541: H[ 543.938] | 8371.479: L[ 19443.354] | 8380.166: | H[ 543.938] 8924.104: | L[ 19443.604] 27814.833: H[ 544.208] | 28359.041: L[ 19443.229] | 28367.708: | H[ 543.687] 28911.395: | L[ 19443.730] 47802.270: H[ 544.188] | 48346.458: L[ 19443.354] | 48355.125: | H[ 543.708] 48898.833: | L[ 19443.583] 67789.812: H[ 543.938] | 68333.750: L[ 19443.354] | 68342.416: | H[ 543.834] 68886.250: | L[ 19443.604]
出力の書式は、
時間(単位μ秒): 信号0のロー/ハイ[信号幅(単位μ秒)] 信号1のロー/ハイ[信号幅(単位μ秒)]
だいたいこんな感じ。
テストとして、Arduino Uno R3 上にて、下記のスケッチを試してみた。
内容は、Arduino の DIGITAL ピン 0 と 1 にそれぞれパルス周期 20ms、パルス幅 544μs のサーボ信号を出力するといういうもの。
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega32U4__) #include <Arduino.h> #include <Servo.h> #elif defined(__RL78__) #include <RLduino78.h> #include <Servo.h> #else #error incorrect target. #endif static const int Channels = 2; static Servo servo[Channels]; void setup() { servo[0].attach(0); servo[0].write(0); servo[1].attach(1); servo[1].writeMicroseconds(544); } void loop() { }
結果は以下の通り(抜粋)で、まあそれらしい信号が出力されてるのが確認できた。
0.000: L[ 7827.541] L[ 8380.166] 7827.541: H[ 543.938] | 8371.479: L[ 19443.354] | 8380.166: | H[ 543.938] 8924.104: | L[ 19443.604] 27814.833: H[ 544.208] | 28359.041: L[ 19443.229] | 28367.708: | H[ 543.687] 28911.395: | L[ 19443.730] 47802.270: H[ 544.188] | 48346.458: L[ 19443.354] | 48355.125: | H[ 543.708] 48898.833: | L[ 19443.583] 67789.812: H[ 543.938] | 68333.750: L[ 19443.354] | 68342.416: | H[ 543.834] 68886.250: | L[ 19443.604] 87777.104: H[ 544.062] | 88321.166: L[ 19443.354] | 88329.854: | H[ 543.937] 88873.791: | L[ 19443.479]
ちなみに、GR-KURUMI で同じスケッチを試してみたところ、
0.000: L[ 2627.020] L[ 3169.312] 2627.020: H[ 547.959] | 3169.312: | H[ 1087.792] 3174.979: L[ 21130.666] | 4257.104: | L[ 20590.958] 24305.645: H[ 548.105] | 24848.062: | H[ 1087.896] 24853.750: L[ 21131.125] | 25935.958: | L[ 20591.458] 45984.875: H[ 548.208] | 46527.416: | H[ 1087.813] 46533.083: L[ 21131.208] | 47615.229: | L[ 20591.521] 67664.291: H[ 548.125] | 68206.750: | H[ 1087.916] 68212.416: L[ 21131.667] | 69294.666: | L[ 20591.729] 89344.083: H[ 547.979] | 89886.395: | H[ 1087.980] 89892.062: L[ 21130.688] | 90974.375: | L[ 20593.812]
あれ? なんか信号0 のパルス周期、信号1 のパルス幅、周期がおかしいぞ?
以上、こんな感じ。
今んとこ、
欠点も多いと思うのだけど、実用と遊びの中間くらいのとこで使えればいいと思ってます。