diff --git a/Firmware/ads1299_driver/ads1299_driver.ino b/Firmware/ads1299_driver/ads1299_driver.ino index 1b10b65..6824052 100644 --- a/Firmware/ads1299_driver/ads1299_driver.ino +++ b/Firmware/ads1299_driver/ads1299_driver.ino @@ -2,7 +2,7 @@ #include "esp_timer.h" // 定义宏,简化寄存器配置 -#define CONFIG_1 0xC5 +#define CONFIG_1 0xD5 #define CONFIG_2 0xC0 #define CONFIG_3 0xEC #define CHnSET 0x60 @@ -81,7 +81,7 @@ void setup() { SPI.begin(SCLK_PIN, MISO_PIN, MOSI_PIN, CS_PIN); SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE1); - SPI.setClockDivider(SPI_CLOCK_DIV4); // 约 5 MHz + SPI.setClockDivider(SPI_CLOCK_DIV8); // 约 5 MHz // 初始化ADS1299 initADS1299(); @@ -175,9 +175,9 @@ void startSelfTestMode() { sendCommand(RESET); delay(100); sendCommand(SDATAC); - // 配置CONFIG2寄存器启用测试信号 - writeRegister(0x01, 0xD4); - writeRegister(0x02, 0x10); + // 配置CONFIG2寄存器启用测试信号 1af 2hz + writeRegister(0x01, 0xD5); + writeRegister(0x02, 0xD1); writeRegister(0x03, 0xEC); writeRegister(0x04, 0x00); // MISC1寄存器启用SRB1 // 配置所有通道输入为内部测试信号 @@ -212,11 +212,12 @@ void initADS1299() { void readData() { byte data[27]; digitalWrite(CS_PIN, LOW); + delayMicroseconds(1); // 确保 tSDSU for (int i = 0; i < 27; i++) { data[i] = SPI.transfer(0x00); } digitalWrite(CS_PIN, HIGH); - + delayMicroseconds(1); // 确保 tDSHD // 转换数据,并将结果保存在全局缓冲区 convertData(data, channelDataBuffer); @@ -258,50 +259,63 @@ void measureImpedance() { } void convertData(byte *data, double *channelData) { - // 解析 STATUS 寄存器 + // 解析STATUS寄存器 long statusValue = ((long)data[0] << 16) | ((long)data[1] << 8) | data[2]; - channelData[0] = (double)statusValue; // 将 STATUS 存储在第一个位置 + channelData[0] = (double)statusValue; - // 解析 8 个通道的数据 + // 转换通道数据 for (int i = 0; i < 8; i++) { - long value = ((long)data[3 * i + 3] << 16) | ((long)data[3 * i + 4] << 8) | data[3 * i + 5]; - // if (value & 0x800000) { - // value |= 0xFF000000; // 符号扩展 - // } - channelData[i + 1] = (double)value * 4.5 / (double)0x800000; + long value = ((long)data[3*i+3] << 16) | ((long)data[3*i+4] << 8) | data[3*i+5]; + + // 24位符号扩展 + if (value & 0x800000) { + value |= 0xFF000000; // 负数扩展 + } + + // 电压计算 (考虑增益) + double vPerLSB = 4.5 / (24 * 8388608.0); // 1 LSB对应电压 + channelData[i+1] = (double)value * vPerLSB; } } void sendCommand(byte cmd) { digitalWrite(CS_PIN, LOW); + delayMicroseconds(1); // 确保 tSDSU SPI.transfer(cmd); digitalWrite(CS_PIN, HIGH); + delayMicroseconds(1); // 确保 tSDSU } void writeRegister(byte reg, byte value) { digitalWrite(CS_PIN, LOW); + delayMicroseconds(1); // 确保 tSDSU SPI.transfer(WREG | reg); SPI.transfer(0x00); SPI.transfer(value); digitalWrite(CS_PIN, HIGH); + delayMicroseconds(1); // 确保 tSDSU } byte readRegister(byte reg) { digitalWrite(CS_PIN, LOW); + delayMicroseconds(1); // 确保 tSDSU SPI.transfer(RREG | reg); SPI.transfer(0x00); byte value = SPI.transfer(0x00); digitalWrite(CS_PIN, HIGH); + delayMicroseconds(1); // 确保 tSDSU return value; } void getDeviceID() { digitalWrite(CS_PIN, LOW); + delayMicroseconds(1); // 确保 tSDSU SPI.transfer(SDATAC); SPI.transfer(RREG | 0x00); SPI.transfer(0x00); byte data = SPI.transfer(0x00); digitalWrite(CS_PIN, HIGH); + delayMicroseconds(1); // 确保 tSDSU Serial.print("Device ID: "); Serial.println(data, BIN); -} +} \ No newline at end of file diff --git a/Paradigm/data_save/20241013_10.0Hz.npy b/Paradigm/data_save/20241013_10.0Hz.npy deleted file mode 100644 index 076cc19..0000000 Binary files a/Paradigm/data_save/20241013_10.0Hz.npy and /dev/null differ diff --git a/Paradigm/data_save/20241013_11.0Hz.npy b/Paradigm/data_save/20241013_11.0Hz.npy deleted file mode 100644 index 076cc19..0000000 Binary files a/Paradigm/data_save/20241013_11.0Hz.npy and /dev/null differ diff --git a/Paradigm/data_save/20241013_12.0Hz.npy b/Paradigm/data_save/20241013_12.0Hz.npy deleted file mode 100644 index 076cc19..0000000 Binary files a/Paradigm/data_save/20241013_12.0Hz.npy and /dev/null differ diff --git a/Paradigm/data_save/20241013_13.0Hz.npy b/Paradigm/data_save/20241013_13.0Hz.npy deleted file mode 100644 index 7a84e6d..0000000 Binary files a/Paradigm/data_save/20241013_13.0Hz.npy and /dev/null differ diff --git a/Paradigm/data_save/20241013_14.0Hz.npy b/Paradigm/data_save/20241013_14.0Hz.npy deleted file mode 100644 index ef6f1f1..0000000 Binary files a/Paradigm/data_save/20241013_14.0Hz.npy and /dev/null differ diff --git a/Paradigm/data_save/20241013_15.0Hz.npy b/Paradigm/data_save/20241013_15.0Hz.npy deleted file mode 100644 index 38a89b0..0000000 Binary files a/Paradigm/data_save/20241013_15.0Hz.npy and /dev/null differ diff --git a/Paradigm/data_save/20241013_8.0Hz.npy b/Paradigm/data_save/20241013_8.0Hz.npy deleted file mode 100644 index dc1aaaa..0000000 Binary files a/Paradigm/data_save/20241013_8.0Hz.npy and /dev/null differ diff --git a/Paradigm/data_save/20241013_9.0Hz.npy b/Paradigm/data_save/20241013_9.0Hz.npy deleted file mode 100644 index c3ce3a1..0000000 Binary files a/Paradigm/data_save/20241013_9.0Hz.npy and /dev/null differ diff --git a/README.md b/README.md index 7bf2e25..ec53882 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,10 @@ > 【注意】请勿焊接TPS模块,直接使用USB供电,原因如下: > ESP的Vbus引脚直连USB电源,并没有引脚二极管处理供电冲突所以请不要同时使用两种供电 -> [!NOTE] +> [!NOTE] 更新日志 2025/6/10 +> 更新波形显示界面`FIFO_Plot.py`,添加去基线等滤波功能。修改部分驱动寄存器配置。 + +> [!NOTE] 更新日志 2024/12/7 > 项目已更新【SSVEP 范式】,后续还会添加APP以及网页波形显示,建议 Star 或者Fork后,作者会添加更多内容。 diff --git a/Software/FIFO_plot.py b/Software/FIFO_plot.py index fb67750..ad6ef57 100644 --- a/Software/FIFO_plot.py +++ b/Software/FIFO_plot.py @@ -1,5 +1,7 @@ import sys import time +from collections import deque + import numpy as np import re from PyQt5 import QtWidgets, QtCore, QtGui @@ -17,12 +19,13 @@ FFT_LENGTH = 500 # FFT分析长度 UPDATE_INTERVAL = 60 # 界面刷新间隔(ms) SAMPLE_RATE = 500 # 采样率 -# 新增滤波器参数 +# 修改滤波器参数 NOTCH_FREQ = 50.0 # 陷波频率 -QUALITY_FACTOR = 10 # 品质因数(决定带宽) -FILTER_CUTOFF = 45 # 低通滤波截止频率 -BUTTER_ORDER = 4 # 巴特沃兹滤波器阶数 -BUTTER_CUTOFF = 45 # 巴特沃兹截止频率 +QUALITY_FACTOR = 10 # 品质因数 +LOW_CUTOFF = 2.0 # 带通滤波低频截止 +HIGH_CUTOFF = 40.0 # 带通滤波高频截止 +FIR_ORDER = 101 # FIR滤波器阶数 +MEDIAN_WINDOW = 800 # 中值滤波窗口大小 class SerialThread(QtCore.QThread): data_received = QtCore.pyqtSignal(str) @@ -33,26 +36,29 @@ class SerialThread(QtCore.QThread): self.filter_flag = True self.serial_port = None self.running = False - self.data_buffer = [[] for _ in range(9)] # 动态数据池 - self.timestamps = [] # 时间戳记录 + self.data_buffer = [[] for _ in range(9)] + self.timestamps = [] self.write_index = 0 - self.max_data_length = 1_000_000 # 最大数据量限制 + self.max_data_length = 1_000_000 - # 初始化滤波器参数 - self.b_notch = [1] * 9 # 滤波器分子系数 - self.a_notch = [1] * 9 # 滤波器分母系数 - self.zi_notch = [np.zeros(2) for _ in range(9)] # 滤波器状态 + # 初始化滤波器 + self._init_filters() + def _init_filters(self): # 设计50Hz陷波滤波器 nyquist = 0.5 * SAMPLE_RATE freq = NOTCH_FREQ / nyquist self.b_notch, self.a_notch = iirnotch(freq, QUALITY_FACTOR) self.zi_notch = [lfilter_zi(self.b_notch, self.a_notch) for _ in range(9)] - # 巴特沃兹滤波器初始化 - butter_cutoff = BUTTER_CUTOFF / nyquist - self.b_butter, self.a_butter = butter(BUTTER_ORDER, butter_cutoff, btype='low') - self.zi_butter = [lfilter_zi(self.b_butter, self.a_butter) for _ in range(9)] + # 设计FIR带通滤波器 + from scipy.signal import firwin + self.fir_coeff = firwin(FIR_ORDER, + [LOW_CUTOFF/nyquist, HIGH_CUTOFF/nyquist], + pass_zero=False) + self.zi_fir = [np.zeros(FIR_ORDER - 1) for _ in range(9)] + # 中值滤波缓冲区 + self.median_buffers = [deque(maxlen=MEDIAN_WINDOW) for _ in range(9)] def run(self): self.running = True @@ -75,27 +81,34 @@ class SerialThread(QtCore.QThread): self.serial_port.close() def _update_buffer(self, values): - # 应用50Hz陷波滤波 filtered_values = [] for i in range(9): - # 使用lfilter进行实时滤波处理 - filtered_value, self.zi_notch[i] = lfilter( + # 1. 50Hz陷波滤波 + notch_filtered, self.zi_notch[i] = lfilter( self.b_notch, self.a_notch, [values[i]], zi=self.zi_notch[i] ) - filtered_values.append(filtered_value[0]) - # 第二级:4阶巴特沃兹低通滤波 - for i in range(9): - filtered, self.zi_butter[i] = lfilter( - self.b_butter, - self.a_butter, - [filtered_values[i]], - zi=self.zi_butter[i] + + # 2. FIR带通滤波 + fir_filtered, self.zi_fir[i] = lfilter( + self.fir_coeff, + [1.0], + notch_filtered, + zi=self.zi_fir[i] ) - filtered_values[i] = filtered[0] - # 将滤波后的数据存入缓冲区(替代原始数据) + + # 3. 中值滤波去基线 + self.median_buffers[i].append(fir_filtered[0]) + # 当缓冲区满时计算中值 + if len(self.median_buffers[i]) >= MEDIAN_WINDOW: + median = np.median(self.median_buffers[i]) + filtered_values.append(fir_filtered[0] - median) + else: + filtered_values.append(fir_filtered[0]) + + # 更新数据缓冲区 while len(self.timestamps) >= self.max_data_length: for ch in self.data_buffer: ch.pop(0)