KTR-40C3用スケッチ

この記事は約22分で読めます。

//KTR-40C3用 Arduino pro mini(5V/16MHz) 2025年6月4日
// VFO with Si5351A and OLED display for CW_Transceiver
// Version 2.4.14 (VFO制御とスクイーズキーヤー・手動電鍵統合版)
#include
#include //https://github.com/greiman/SSD1306Ascii
#include // https://github.com/buxtronix/arduino/tree/master/libraries/Rotary
#include
#include
#include

// — Global setup —
const unsigned long DEFAULT_FREQ = 19796940UL;
const int EEPROM_ADDR = 0;
unsigned long lastFreqWriteTime = 0;
bool freqChanged = false;
bool freqWritten = true;
unsigned long eeprom_cached_FREQ; // ★ 追加: EEPROMに書き込む予定の周波数を一時的に保持する変数

// TX/RX 状態遷移管理
unsigned long txDelayStartTime = 0; // 送信開始待ち時間計測用
bool txTransitionActive = false; // 送信への移行中フラグ
bool txActive = false; // VFO側がTX状態(RF出力ON)であることを示すフラグ

unsigned long rxDelayStartTime = 0; // 受信開始待ち時間計測用
bool rxTransitionActive = false; // 受信への移行中フラグ

// Loop interval control for bar graph
unsigned long previousMillis = 0;
const long interval = 100;
int previousBarLength = -1;

// OLED and encoder
SSD1306AsciiAvrI2c oled;
Rotary r = Rotary(2, 3);

// Si5351A definitions
#define Si5351A_ADDR 0x60
#define MSNA_ADDR 26
#define MSNB_ADDR 34
#define MS0_ADDR 42
#define MS1_ADDR 50
#define MS2_ADDR 58
#define CLK0_CTRL 16
#define CLK1_CTRL 17
#define CLK2_CTRL 18
#define OUTPUT_CTRL 3
#define XTAL_LOAD_C 183
#define PLL_RESET 177

// I/O pins
#define SW_STEP 5
#define TX_SW 9 // 手動電鍵およびキーヤーからの送信制御入力
#define TX_ON 7
#define AF_mute 8 // AFミュート制御

// Frequency setup
const long LOW_FREQ = 19796940L;
const long HI_FREQ = 19996940L;
const long IF_FREQ = 12796940L;
unsigned long FREQ = DEFAULT_FREQ;
unsigned long FREQ_OLD = FREQ;
unsigned long txFreq_OLD = 0;
int dF = 120;
int RX_dF = 340;
int STEP = 100;

// — キーヤーピンアサイン —
#define DOT_PIN 11 // 短点パドル入力
#define DASH_PIN 12 // 長点パドル入力
#define SIDETONE_PIN 10 // サイドトーン出力 (D10)
#define SPEED_POT_PIN A6 // スピードコントロール用入力

// — キーヤー定数 —
const int MIN_WPM = 12;
const int MAX_WPM = 35;
const int SIDETONE_FREQ = 800; // サイドトーン周波数 (Hz)
const unsigned long DEBOUNCE_DELAY = 10; // パドル入力のデバウンス時間 (ms)
const unsigned long TX_ON_DELAY_MS = 3; // clk1 オンからTX_ON ONまでの遅延 (ms)
const unsigned long RX_ON_DELAY_MS = 5; // TX_ON OFFからAF_mute 解除までの遅延 (ms)

// — キーヤー グローバル変数 —
volatile int currentWpm = 15;
volatile unsigned long ditTimeMs = 0;
volatile unsigned long dahTimeMs = 0;
volatile unsigned long elementSpaceTimeMs = 0;

// キーヤーの内部動作状態 (割り込み内で更新されるので volatile)
enum KeyerState {
KEYER_IDLE, // アイドル状態 (TX OFF)
KEYER_SENDING_ON, // 符号のON期間送出中 (TX ON)
KEYER_SENDING_OFF // 符号間のOFF期間送出中 (TX OFF)
};
volatile KeyerState currentKeyerState = KEYER_IDLE;

// 現在送出している符号のタイプ (ISRで更新、loopで参照)
enum CurrentlySendingSymbol {
NONE_SENDING,
DOT_SENDING,
DASH_SENDING
};
volatile CurrentlySendingSymbol currentlySendingSymbol = NONE_SENDING;

// 次に送出する符号のタイプ (メモリに記憶された符号) (volatile)
enum BufferedSymbol {
NONE_BUFFERED,
DOT_BUFFERED,
DASH_BUFFERED
};
volatile BufferedSymbol bufferedSymbol = NONE_BUFFERED; // 1つだけバッファする符号

// 割り込みハンドラ内でカウントするティック数
volatile unsigned long currentTickCount = 0;
volatile unsigned long requiredTicks = 0;

// パドル入力の状態 (デバウンス後) – volatileを追加
volatile bool dotPaddleState = false;
volatile bool dashPaddleState = false;

// デバウンス処理のための内部変数
static unsigned long lastDotReadTime = 0;
static unsigned long lastDashReadTime = 0;
static bool rawDotState = false;
static bool rawDashState = false;
static int lastPotValue = -1; // ポテンショメータの値を監視するための変数

// TX_SW (手動電鍵) の状態を保持する変数
volatile bool manualTxSwitchPressed = false; // ISRからも参照される可能性があるのでvolatileに
unsigned long lastManualTxReadTime = 0;
// Note: rawManualTxStateはloop内でローカル変数として処理する
const unsigned long MANUAL_TX_DEBOUNCE_DELAY = 10; // 手動電鍵のデバウンス

// — 関数 (Si5351A / OLED / VFO) —
void rotary_encoder();
void Fnc_Stp();
void drawBar(int currentBarLength);
void Set_CLK_Enable(uint8_t clk0, uint8_t clk1, uint8_t clk2);
void Freq_Disp(unsigned long Fre);
void Step_Disp(int Stp);
void Si5351_write(byte Reg, byte Data);
void Si5351_init();
void PLL_Set(char pll, uint32_t freq);
void MS_Set(uint8_t clk_no);
void Parameter_write(uint8_t REG_ADDR, uint32_t Pa1, uint32_t Pa2, uint32_t Pa3);

// — 関数 (キーヤー) —
void updateKeyerTiming();
void processPaddleInput();
void setTxOnState(bool on); // TX_ON制御を専用の関数に統一
void setupTimer1();

// — Setup —
void setup() {
// Rotary Encoder setup
r.begin();
attachInterrupt(0, rotary_encoder, CHANGE);
attachInterrupt(1, rotary_encoder, CHANGE);

// Pin Modes
pinMode(SW_STEP, INPUT_PULLUP);
pinMode(TX_SW, INPUT_PULLUP); // TX_SW (手動電鍵 & キーヤーからの制御用)
pinMode(TX_ON, OUTPUT);
pinMode(AF_mute, OUTPUT);

// Keyer related pin modes
pinMode(DOT_PIN, INPUT_PULLUP);
pinMode(DASH_PIN, INPUT_PULLUP);
pinMode(SIDETONE_PIN, OUTPUT); // サイドトーン出力ピンを設定
pinMode(SPEED_POT_PIN, INPUT);

// Initial output states
setTxOnState(false); // TX_ONを初期オフ (専用関数経由)
digitalWrite(AF_mute, HIGH); // AF_muteを(受信状態)

// EEPROM read for frequency
EEPROM.get(EEPROM_ADDR, FREQ);
if (FREQ < LOW_FREQ || FREQ > HI_FREQ) FREQ = DEFAULT_FREQ;
FREQ_OLD = FREQ;
eeprom_cached_FREQ = FREQ; // ★ 追加: 初期値を同期

oled.begin(&Adafruit128x64, 0x3C); //0.96インチ128×64用、1.3インチは(&SH1106_128x64, 0x3C)に変更

Si5351_init();

// Initial PLL/MS settings for RX and TX
PLL_Set(‘A’, FREQ + RX_dF);
MS_Set(0); // CLK0 (受信) のマルチシンセサイザ設定

unsigned long initialTxFreq = FREQ + dF – IF_FREQ;
PLL_Set(‘B’, initialTxFreq);
MS_Set(1); // CLK1 (送信) のマルチシンセサイザ設定
txFreq_OLD = initialTxFreq; // 初期送信周波数を記録
Si5351_write(CLK1_CTRL, 0x6D); //CLK1を4mAにおまじない

// Display initial frequency and step
Freq_Disp(FREQ);
Step_Disp(STEP);

// OLED bar graph label
oled.setCursor(0, 4);
oled.setFont(font5x7);
oled.write(“1__3__5__7_9+20+40+60”);

Set_CLK_Enable(1, 0, 0); // 初期はCLK0 (受信) のみ有効

// — キーヤー初期化 —
updateKeyerTiming(); // 初期スピードの計算とタイミング変数の設定
setupTimer1(); // Timer1割り込みを設定 (1ms周期)

// グローバル割り込みを有効化 (setupTimer1でも行うが念のため)
sei();
}

// — Loop —
void loop() {
unsigned long now = millis();

if (digitalRead(SW_STEP) == LOW) Fnc_Stp();

// 手動電鍵のデバウンス処理
bool currentRawManualTx = (digitalRead(TX_SW) == LOW);
// rawManualTxStateはここでローカル変数として定義
static bool rawManualTxState = false; // staticにして値を保持

if (currentRawManualTx != rawManualTxState) {
lastManualTxReadTime = now;
rawManualTxState = currentRawManualTx;
}
if (now – lastManualTxReadTime > MANUAL_TX_DEBOUNCE_DELAY) {
manualTxSwitchPressed = rawManualTxState; // volatile変数に最終状態を書き込み
}

// — TX/RX 状態遷移処理 (VFO側とキーヤー側の両方から制御を統合) —
// 手動電鍵が押されていれば、キーヤーの状態にかかわらず送信要求を優先する
bool requestTx = manualTxSwitchPressed || (currentKeyerState != KEYER_IDLE);

if (requestTx) {
if (!txTransitionActive) {
// 受信から送信への移行開始
txDelayStartTime = now;
txTransitionActive = true;
rxTransitionActive = false; // 受信への移行をリセット

digitalWrite(AF_mute, LOW); // AFミュート解除

// 送信周波数に切り替え
Set_CLK_Enable(0, 1, 0); // CLK1 (送信) を有効
}

// TX_ONはキーヤーISRと手動電鍵の状態に基づいて、setTxOnState()で制御される
// ここでは直接digitalWrite(TX_ON)はしない
txActive = true; // TXがアクティブであることをマーク
} else { // 送信要求がない場合 (キーヤーも手動電鍵もTXを要求していない)
if (!rxTransitionActive) {
rxDelayStartTime = now;
rxTransitionActive = true;
txTransitionActive = false; // 送信への移行をリセット

setTxOnState(false); // 送信要求がなくなった時点でTX_ONをLOWにする
txActive = false; // TX非アクティブをマーク

// TX_ONがオフになったら直ちにRX周波数に切り替え
Set_CLK_Enable(1, 0, 0); // CLK0 (受信) を有効
// AF_muteはまだONにしない (遅延させるため)
}

// RX_ON_DELAY_MS の遅延後にAF_muteをONにする
if (rxTransitionActive && (now – rxDelayStartTime >= RX_ON_DELAY_MS)) {
digitalWrite(AF_mute, HIGH); // AFミュートオン
rxTransitionActive = false; // 受信への移行完了
}
}

// — 周波数変更検出 —
if (FREQ != FREQ_OLD) {
PLL_Set(‘A’, FREQ + RX_dF);
MS_Set(0); // CLK0 (受信) に紐づくマルチシンセサイザ

unsigned long txFreq_new = FREQ + dF – IF_FREQ;
PLL_Set(‘B’, txFreq_new);
MS_Set(1); // CLK1 (送信) に紐づくマルチシンセサイザ
txFreq_OLD = txFreq_new; // 送信周波数を更新

Freq_Disp(FREQ);
FREQ_OLD = FREQ;
freqChanged = true;
lastFreqWriteTime = now;
freqWritten = false;
eeprom_cached_FREQ = FREQ; // ★ 修正: EEPROMに書き込む予定の値を更新
}

// — バーグラフ —
if (now – previousMillis >= interval) {
previousMillis = now;
int sensorValue = analogRead(A0);
const int MIN_ADC = 190;
const int MAX_ADC = 588;
sensorValue = constrain(sensorValue, MIN_ADC, MAX_ADC);
int currentBarLength = map(sensorValue, MAX_ADC, MIN_ADC, 0, 128);

if (currentBarLength != previousBarLength) {
drawBar(currentBarLength);
previousBarLength = currentBarLength;
}
}

// — EEPROM書き込み処理 —
if (freqChanged && !freqWritten && (now – lastFreqWriteTime >= 2000)) {
unsigned long storedFreq;
EEPROM.get(EEPROM_ADDR, storedFreq);
if (storedFreq != eeprom_cached_FREQ) { // ★ 修正: 保持している値と比較
EEPROM.put(EEPROM_ADDR, eeprom_cached_FREQ); // ★ 修正: 保持している値を書き込み
}
freqWritten = true;
freqChanged = false;
}

// — キーヤー関連処理 —
updateKeyerTiming(); // WPMを常に更新し、それに伴うタイミング変数を再計算
processPaddleInput(); // パドル入力を処理 (デバウンスと状態更新)

// サイドトーン制御: TX_ONピンの状態に連動させる
if (digitalRead(TX_ON) == HIGH) { // TX_ONがHIGHの時
tone(SIDETONE_PIN, SIDETONE_FREQ);
} else { // TX_ONがLOWの時
noTone(SIDETONE_PIN);
}

// ここではbufferedSymbolを直接設定せず、ISRに任せる。
// ISRがdotPaddleState/dashPaddleStateとcurrentKeyerState/currentlySendingSymbolを見て
// 適切にbufferedSymbolを更新するロジックを持つ。
}

void drawBar(int currentBarLength) {
oled.setFont(font5x7);

if (currentBarLength > previousBarLength) {
for (int i = previousBarLength; i < currentBarLength; i++) { oled.setCursor(i, 5); oled.print("L"); } } else if (currentBarLength < previousBarLength) { for (int i = currentBarLength; i < previousBarLength; i++) { oled.setCursor(i, 5); oled.print(" "); } } } void Set_CLK_Enable(uint8_t clk0, uint8_t clk1, uint8_t clk2) { uint8_t val = 0; if (!clk0) val |= (1 << 0); if (!clk1) val |= (1 << 1); if (!clk2) val |= (1 << 2); Si5351_write(OUTPUT_CTRL, val); } void Freq_Disp(unsigned long Fre) { unsigned long displayFreq = Fre - IF_FREQ; String freqt = String(displayFreq); oled.setFont(fixednums8x16); oled.setCursor(8, 1); oled.print(freqt.substring(0, 1) + "." + freqt.substring(1, 4) + "." + freqt.substring(4)); } void Step_Disp(int Stp) { oled.setFont(font5x7); oled.setCursor(100, 2); oled.print((Stp == 1000) ? " 1K" : (Stp == 100) ? "100" : " 10"); } void Si5351_write(byte Reg, byte Data) { Wire.beginTransmission(Si5351A_ADDR); Wire.write(Reg); Wire.write(Data); Wire.endTransmission(); // delayMicroseconds(10); } //---初期化--- void Si5351_init() { Si5351_write(OUTPUT_CTRL, 0xFF); // All outputs disabled Si5351_write(CLK0_CTRL, 0x80); // Power down CLK0 Si5351_write(CLK1_CTRL, 0x80); // Power down CLK1 Si5351_write(CLK2_CTRL, 0x80); // Power down CLK2 Si5351_write(XTAL_LOAD_C, 0x92); // Set crystal load capacitance Si5351_write(PLL_RESET, 0xA0); // Reset all PLLs Si5351_write(CLK0_CTRL, 0x4D); // CLK0: PLLA, 4mA, (RX) Si5351_write(CLK1_CTRL, 0x6D); // CLK1: PLLB, 4mA, (TX) Si5351_write(CLK2_CTRL, 0x6C); // CLK2: PLLB, 2mA, (使わないから何でもいい) Si5351_write(OUTPUT_CTRL, 0xFC); // Enable CLK0 CLK1 at init } void PLL_Set(char pll, uint32_t freq) { uint8_t addr = (pll == 'A') ? MSNA_ADDR : MSNB_ADDR; uint64_t pll_freq = (uint64_t)freq * 32; uint32_t mult = pll_freq / 25000000; uint32_t l = pll_freq % 25000000; uint32_t num = (uint64_t)l * 1048575 / 25000000; uint32_t denom = 1048575; uint32_t P1 = 128 * mult + ((128 * num) / denom) - 512; uint32_t P2 = (128 * num) % denom; uint32_t P3 = denom; Parameter_write(addr, P1, P2, P3); } void MS_Set(uint8_t clk_no) { uint8_t addr; if (clk_no == 0) addr = MS0_ADDR; else if (clk_no == 1) addr = MS1_ADDR; else addr = MS2_ADDR; uint32_t P1 = 128 * 32 - 512; // Divide by 32 uint32_t P2 = 0; uint32_t P3 = 1; Parameter_write(addr, P1, P2, P3); } void Parameter_write(uint8_t REG_ADDR, uint32_t Pa1, uint32_t Pa2, uint32_t Pa3) { Si5351_write(REG_ADDR, (Pa3 >> 8) & 0xFF);
Si5351_write(REG_ADDR + 1, Pa3 & 0xFF);
Si5351_write(REG_ADDR + 2, (Pa1 >> 16) & 0x03);
Si5351_write(REG_ADDR + 3, (Pa1 >> 8) & 0xFF);
Si5351_write(REG_ADDR + 4, Pa1 & 0xFF);
Si5351_write(REG_ADDR + 5, ((Pa3 >> 12) & 0xF0) | ((Pa2 >> 16) & 0x0F));
Si5351_write(REG_ADDR + 6, (Pa2 >> 8) & 0xFF);
Si5351_write(REG_ADDR + 7, Pa2 & 0xFF);
}

// — 補助関数 (キーヤー関連) —

// Rotary encoder ISR (ISR内でmillis()は使用しない)
void rotary_encoder() {
unsigned char result = r.process();
if (result) {
FREQ += (result == DIR_CW) ? STEP : -STEP;
FREQ = constrain(FREQ, LOW_FREQ, HI_FREQ);
freqChanged = true; // millis()の呼び出しを避ける
}
}

void Fnc_Stp() {
STEP = (STEP == 10) ? 1000 : ((STEP == 1000) ? 100 : 10); // 10 -> 1000 -> 100 -> 10 の順に切り替え
delay(10);
Step_Disp(STEP);
while (digitalRead(SW_STEP) == LOW) delay(10); // ボタンが離れるまで待機
}

// WPMに基づいてキーヤーのタイミング変数を更新する関数
void updateKeyerTiming() {
int sensorValue = analogRead(SPEED_POT_PIN);
int newWpm = map(sensorValue, 0, 1023, MIN_WPM, MAX_WPM);

if (newWpm != currentWpm || lastPotValue == -1) {
currentWpm = newWpm;
if (currentWpm == 0) currentWpm = MIN_WPM;

ditTimeMs = 1200L / currentWpm;
dahTimeMs = ditTimeMs * 3;
elementSpaceTimeMs = ditTimeMs * 1;
}
lastPotValue = sensorValue;
}

// パドル入力のデバウンス処理と状態更新
void processPaddleInput() {
unsigned long now = millis();

bool currentRawDot = !digitalRead(DOT_PIN);
if (currentRawDot != rawDotState) {
lastDotReadTime = now;
rawDotState = currentRawDot;
}
if (now – lastDotReadTime > DEBOUNCE_DELAY) {
dotPaddleState = rawDotState;
}

bool currentRawDash = !digitalRead(DASH_PIN);
if (currentRawDash != rawDashState) {
lastDashReadTime = now;
rawDashState = currentRawDash;
}
if (now – lastDashReadTime > DEBOUNCE_DELAY) {
dashPaddleState = rawDashState;
}
}

// TX_ON制御を行うヘルパー関数 – ISRからも呼ばれる
void setTxOnState(bool on) {
if (on) {
digitalWrite(TX_ON, HIGH);
} else {
digitalWrite(TX_ON, LOW);
}
}

// Timer1をセットアップする関数 (1ミリ秒ごとに割り込み)
void setupTimer1() {
TCCR1A = 0;
TCCR1B = 0;
TCCR1B |= (1 << WGM12); // CTCモード TCCR1B |= (1 << CS11); // プリスケーラを8に設定 (16MHz / 8 = 2MHz) OCR1A = 1999; // 1ms周期 (0から1999までカウント) TIMSK1 |= (1 << OCIE1A); // Timer1 Compare Match A 割り込みを有効化 } // Timer1 割り込みサービスルーチン (ISR) ISR(TIMER1_COMPA_vect) { currentTickCount++; // 現在のON/OFF期間のティック数をインクリメント // キーヤーの現在の状態とパドル入力に基づいて次に送る符号を決定 // manualTxSwitchPressed が優先されるため、ここではキーヤーの符号決定に集中 BufferedSymbol nextSymbolFromKeyerInput = NONE_BUFFERED; // バッファが空の場合、リアルタイム入力を確認 if (bufferedSymbol == NONE_BUFFERED) { if (dotPaddleState) { nextSymbolFromKeyerInput = DOT_BUFFERED; } else if (dashPaddleState) { nextSymbolFromKeyerInput = DASH_BUFFERED; } } else { // バッファに符号がある場合はそれを優先 nextSymbolFromKeyerInput = bufferedSymbol; } switch (currentKeyerState) { case KEYER_IDLE: currentlySendingSymbol = NONE_SENDING; // IDLEなので送出符号なし // アイドル時は manualTxSwitchPressed またはキーヤーの符号入力があればTX_ONをHIGHにする if (manualTxSwitchPressed || nextSymbolFromKeyerInput != NONE_BUFFERED) { setTxOnState(true); // TX_ONをHIGHにする // キーヤーが符号を送出する場合のみ、状態を遷移 if (nextSymbolFromKeyerInput != NONE_BUFFERED) { currentTickCount = 0; // ティックカウントをリセット currentKeyerState = KEYER_SENDING_ON; // 次に送出すべきON期間のティック数を設定 if (nextSymbolFromKeyerInput == DOT_BUFFERED) { requiredTicks = ditTimeMs; currentlySendingSymbol = DOT_SENDING; } else if (nextSymbolFromKeyerInput == DASH_BUFFERED) { requiredTicks = dahTimeMs; currentlySendingSymbol = DASH_SENDING; } bufferedSymbol = NONE_BUFFERED; // 送出を開始したらバッファをクリア } } else { setTxOnState(false); // 送信要求がなければTX_ONをLOWにする } break; case KEYER_SENDING_ON: // ON期間が終了したら、OFF期間へ移行 if (currentTickCount >= requiredTicks) {
setTxOnState(false); // TX_ONをLOWにする
currentTickCount = 0; // ティックカウントをリセット
currentKeyerState = KEYER_SENDING_OFF;
requiredTicks = elementSpaceTimeMs; // 符号間スペース

// 先打ちロジック: 現在送出中の符号と異なるパドルが押されたらバッファ
if (currentlySendingSymbol == DOT_SENDING && dashPaddleState && !dotPaddleState) {
bufferedSymbol = DASH_BUFFERED;
} else if (currentlySendingSymbol == DASH_SENDING && dotPaddleState && !dashPaddleState) {
bufferedSymbol = DOT_BUFFERED;
}
}
break;

case KEYER_SENDING_OFF:
// OFF期間(符号素間スペース)が終了したら
if (currentTickCount >= requiredTicks) {
// 次に送出する符号があれば、そのON期間を開始
if (nextSymbolFromKeyerInput != NONE_BUFFERED) { // bufferedSymbolに加えてリアルタイム入力も考慮
setTxOnState(true); // TX_ONをHIGHにする
currentTickCount = 0; // ティックカウントをリセット
currentKeyerState = KEYER_SENDING_ON;

// 次に送出すべきON期間のティック数を設定
if (nextSymbolFromKeyerInput == DOT_BUFFERED) {
requiredTicks = ditTimeMs;
currentlySendingSymbol = DOT_SENDING;
} else if (nextSymbolFromKeyerInput == DASH_BUFFERED) {
requiredTicks = dahTimeMs;
currentlySendingSymbol = DASH_SENDING;
}
bufferedSymbol = NONE_BUFFERED; // 送出を開始したらバッファをクリア
} else if (manualTxSwitchPressed) {
// キーヤーからの符号入力がなくても、手動電鍵が押されていればTXを維持
setTxOnState(true);
currentlySendingSymbol = NONE_SENDING; // キーヤーは符号を出していない
currentKeyerState = KEYER_IDLE; // キーヤー自体はアイドル状態に戻す
}
else {
// バッファもリアルタイム入力もなければ、IDLE状態に戻る
currentlySendingSymbol = NONE_SENDING;
currentKeyerState = KEYER_IDLE;
bufferedSymbol = NONE_BUFFERED; // IDLEに戻る際もバッファをクリア
}
}
break;
}
}

コメント