Untitled

 avatar
unknown
plain_text
9 months ago
8.8 kB
10
Indexable
#include <avr/sleep.h>
#include <avr/power.h>

#define N_PORTS 10   // 使用的 Arduino PORT 數量(A~J)
#define N_DIVS 10    // 一個 40kHz 週期切成幾個步階(解析度)
#define N_TX 64      // 發射器(Tx)數量

// 將某個 division 的所有 PORT 寫入(一次控制所有 Tx)
#define OUTPUT_WAVE(pointer, d)  \
  PORTA = pointer[d*N_PORTS + 0]; \
  PORTC = pointer[d*N_PORTS + 1]; \
  PORTL = pointer[d*N_PORTS + 2]; \
  PORTB = pointer[d*N_PORTS + 3]; \
  PORTK = pointer[d*N_PORTS + 4]; \
  PORTF = pointer[d*N_PORTS + 5]; \
  PORTH = pointer[d*N_PORTS + 6]; \
  PORTD = pointer[d*N_PORTS + 7]; \
  PORTG = pointer[d*N_PORTS + 8]; \
  PORTJ = pointer[d*N_PORTS + 9];

// 控制指令定義
#define CMD_SHIFT_FORWARD  '1'  // 整體相位右移(+1 division)
#define CMD_SHIFT_BACKWARD '2'  // 整體相位左移(-1 division)
#define CMD_SWITCH_BUFFER  '0'  // 切換使用 buffer(雙緩衝)
#define CMD_CUSTOM_PATTERN 'P'  // 指定某個 Tx 開啟單一 division,例如 P3,5
#define CMD_CUSTOM_WAVE    'W'  // 指定某個 Tx 的整段波形,例如 W3:1110000000
#define CMD_SHIFT_TX       'S'  // 移動某個 Tx 的相位,例如 S3,1
#define CMD_CLEAR_ALL      'C'  // 清空所有 Tx 的 waveform
#define CMD_FILL_PHASE_1   'A'  // 在指定 division 將所有 Tx 設為 1
#define CMD_FILL_PHASE_0   'B'  // 在指定 division 將所有 Tx 設為 0

// 雙緩衝區(避免播放時更新資料造成干擾)
static byte bufferA[N_DIVS * N_PORTS];
static byte bufferB[N_DIVS * N_PORTS];

// 64 顆發射器,每顆 10 division PWM pattern
static byte waveform[N_TX][N_DIVS];

// 將 waveform 資料轉換到 buffer 中,準備輸出
void fillBufferFromWaveform(byte* buffer) {
  for (int i = 0; i < N_DIVS * N_PORTS; ++i) buffer[i] = 0;

  for (int tx = 0; tx < N_TX; ++tx) {
    int port = tx / 8;           // 一個 PORT 控制 8 顆 Tx
    int bit = tx % 8;            // Tx 在該 PORT 的第幾個 bit
    for (int d = 0; d < N_DIVS; ++d) {
      if (waveform[tx][d]) {
        buffer[d * N_PORTS + port] |= (1 << bit);
      } else {
        buffer[d * N_PORTS + port] &= ~(1 << bit);
      }
    }
  }
}

// 對單一 Tx 的 waveform 做相位偏移
void shiftWaveform(int tx, int step) {
  byte temp[N_DIVS];
  for (int d = 0; d < N_DIVS; ++d)
    temp[(d + step + N_DIVS) % N_DIVS] = waveform[tx][d];
  for (int d = 0; d < N_DIVS; ++d)
    waveform[tx][d] = temp[d];
}

// 對所有 Tx 同時偏移相位
void shiftAllWaveform(int step) {
  for (int tx = 0; tx < N_TX; ++tx)
    shiftWaveform(tx, step);
}

// P 指令:設定單一 Tx 在某 division 輸出為 1
void parseCustomPattern() {
  String input = Serial.readStringUntil('\n');
  input.trim();
  int commaIndex = input.indexOf(',');
  if (commaIndex == -1) {
    Serial.println("[ERROR] 格式錯誤,請使用 P<tx>,<division>");
    return;
  }
  int tx = input.substring(0, commaIndex).toInt();
  int p = input.substring(commaIndex + 1).toInt();
  if (tx >= 0 && tx < N_TX && p >= 0 && p < N_DIVS) {
    for (int i = 0; i < N_DIVS; ++i) waveform[tx][i] = 0;
    waveform[tx][p] = 1;
    Serial.print("[INFO] 設定 Tx "); Serial.print(tx);
    Serial.print(" 在 division "); Serial.println(p);
  } else {
    Serial.println("[ERROR] Tx 或 division 無效");
  }
}

// W 指令:設定 Tx 的整段 10-bit 波形
void parseCustomWave() {
  String input = Serial.readStringUntil('\n');
  input.trim();
  int colonIndex = input.indexOf(':');
  if (colonIndex == -1 || input.length() - colonIndex - 1 < 10) {
    Serial.println("[ERROR] 格式錯誤,請使用 W<tx>:<10位元>");
    return;
  }
  int tx = input.substring(0, colonIndex).toInt();
  String bits = input.substring(colonIndex + 1);
  if (tx >= 0 && tx < N_TX) {
    Serial.print("[INFO] 設定 Tx "); Serial.print(tx); Serial.print(" = ");
    for (int i = 0; i < N_DIVS; ++i) {
      waveform[tx][i] = (bits[i] == '1') ? 1 : 0;
      Serial.print(waveform[tx][i]);
    }
    Serial.println();
  } else {
    Serial.println("[ERROR] Tx 無效");
  }
}

// S 指令:對單一 Tx 移動相位(左或右)
void parseShiftTx() {
  String input = Serial.readStringUntil('\n');
  input.trim();
  int commaIndex = input.indexOf(',');
  if (commaIndex == -1) {
    Serial.println("[ERROR] 格式錯誤,請使用 S<tx>,<dir>");
    return;
  }
  int tx = input.substring(0, commaIndex).toInt();
  int dir = input.substring(commaIndex + 1).toInt();
  if (tx >= 0 && tx < N_TX) {
    shiftWaveform(tx, dir == 1 ? 1 : -1);
    Serial.print("[INFO] 移動 Tx "); Serial.print(tx);
    Serial.println(dir == 1 ? " 向右" : " 向左");
  } else {
    Serial.println("[ERROR] Tx 無效");
  }
}

// 清空所有 waveform
void clearAllWaveform() {
  for (int i = 0; i < N_TX; ++i)
    for (int d = 0; d < N_DIVS; ++d)
      waveform[i][d] = 0;
  Serial.println("[INFO] 已清空所有 waveform");
}

// 將所有 Tx 在指定 division 設為 1
void fillPhaseOne(int div) {
  if (div >= 0 && div < N_DIVS) {
    for (int i = 0; i < N_TX; ++i)
      waveform[i][div] = 1;
    Serial.print("[INFO] 所有 Tx 在 division ");
    Serial.print(div);
    Serial.println(" 設為 1");
  } else {
    Serial.println("[ERROR] division 無效 (0~9)");
  }
}

// 將所有 Tx 在指定 division 設為 0
void fillPhaseZero(int div) {
  if (div >= 0 && div < N_DIVS) {
    for (int i = 0; i < N_TX; ++i)
      waveform[i][div] = 0;
    Serial.print("[INFO] 所有 Tx 在 division ");
    Serial.print(div);
    Serial.println(" 設為 0");
  } else {
    Serial.println("[ERROR] division 無效 (0~9)");
  }
}

void setup() {
  // 設定所有 PORT 為輸出模式
  DDRA = DDRC = DDRL = DDRB = DDRK = DDRF = DDRH = DDRD = DDRG = DDRJ = 0xFF;
  // 預設所有輸出為低電位
  PORTA = PORTC = PORTL = PORTB = PORTK = PORTF = PORTH = PORTD = PORTG = PORTJ = 0x00;

  // 初始 waveform 設定為每個 Tx 的 division 對應位置為 1
  for (int i = 0; i < N_TX; ++i)
    for (int d = 0; d < N_DIVS; ++d)
      waveform[i][d] = (d == (i % N_DIVS)) ? 1 : 0;

  fillBufferFromWaveform(bufferA);
  fillBufferFromWaveform(bufferB);

  Serial.begin(115200);

  // 關閉不需要的模組省電
  ADCSRA = 0;
  power_adc_disable();
  power_spi_disable();
  power_twi_disable();
  power_timer0_disable();
  power_usart1_disable();
  power_usart2_disable();
  power_usart3_disable();

  // 設定 40kHz 同步輸出訊號在 pin2
  pinMode(2, OUTPUT);
  TCCR3A = bit(WGM10) | bit(WGM11) | bit(COM1B1);
  TCCR3B = bit(WGM12) | bit(WGM13) | bit(CS10);
  OCR3A = (F_CPU / 40000L) - 5;
  OCR3B = (F_CPU / 40000L) / 2;

  pinMode(3, INPUT_PULLUP);  // 同步輸入腳位
  interrupts();
}

void loop() {
  static bool emittingA = true;
  byte* currentBuffer = emittingA ? bufferA : bufferB;

  // 每個週期輸出 10 個步階(每步 2.5us)
  for (int i = 0; i < N_DIVS; ++i) {
    while (PINE & 0b00100000); // 等待同步訊號為 LOW
    OUTPUT_WAVE(currentBuffer, i); // 輸出當前步階
  }

  // 指令處理
  if (Serial.available()) {
    char cmd = Serial.read();
    if (cmd == CMD_SWITCH_BUFFER) {
      emittingA = !emittingA;
      Serial.print("[INFO] Switched to buffer ");
      Serial.println(emittingA ? "A" : "B");
    } 
    else if (cmd == CMD_SHIFT_FORWARD) {
      shiftAllWaveform(1);
      fillBufferFromWaveform(emittingA ? bufferB : bufferA);
      Serial.println("[INFO] All phase +1");
    } 
    else if (cmd == CMD_SHIFT_BACKWARD) {
      shiftAllWaveform(-1);
      fillBufferFromWaveform(emittingA ? bufferB : bufferA);
      Serial.println("[INFO] All phase -1");
    } 
    else if (cmd == CMD_CUSTOM_PATTERN) {
      parseCustomPattern();
      fillBufferFromWaveform(emittingA ? bufferB : bufferA);
    } 
    else if (cmd == CMD_CUSTOM_WAVE) {
      parseCustomWave();
      fillBufferFromWaveform(emittingA ? bufferB : bufferA);
    } 
    else if (cmd == CMD_SHIFT_TX) {
      parseShiftTx();
      fillBufferFromWaveform(emittingA ? bufferB : bufferA);
    }
    else if (cmd == CMD_CLEAR_ALL) {
      clearAllWaveform();
      fillBufferFromWaveform(emittingA ? bufferB : bufferA);
    } 
    else if (cmd == CMD_FILL_PHASE_1) {
      while (!Serial.available());
      int div = Serial.parseInt();
      fillPhaseOne(div);
      fillBufferFromWaveform(emittingA ? bufferB : bufferA);
    } 
    else if (cmd == CMD_FILL_PHASE_0) {
      while (!Serial.available());
      int div = Serial.parseInt();
      fillPhaseZero(div);
      fillBufferFromWaveform(emittingA ? bufferB : bufferA);
    }
  }
}
Editor is loading...
Leave a Comment