Untitled
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