update new plot program

This commit is contained in:
yutaov5 2025-06-10 19:27:56 +08:00
parent ba095322e1
commit fc7f542f3b
11 changed files with 75 additions and 45 deletions

View File

@ -2,7 +2,7 @@
#include "esp_timer.h" #include "esp_timer.h"
// 定义宏,简化寄存器配置 // 定义宏,简化寄存器配置
#define CONFIG_1 0xC5 #define CONFIG_1 0xD5
#define CONFIG_2 0xC0 #define CONFIG_2 0xC0
#define CONFIG_3 0xEC #define CONFIG_3 0xEC
#define CHnSET 0x60 #define CHnSET 0x60
@ -81,7 +81,7 @@ void setup() {
SPI.begin(SCLK_PIN, MISO_PIN, MOSI_PIN, CS_PIN); SPI.begin(SCLK_PIN, MISO_PIN, MOSI_PIN, CS_PIN);
SPI.setBitOrder(MSBFIRST); SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE1); SPI.setDataMode(SPI_MODE1);
SPI.setClockDivider(SPI_CLOCK_DIV4); // 约 5 MHz SPI.setClockDivider(SPI_CLOCK_DIV8); // 约 5 MHz
// 初始化ADS1299 // 初始化ADS1299
initADS1299(); initADS1299();
@ -175,9 +175,9 @@ void startSelfTestMode() {
sendCommand(RESET); sendCommand(RESET);
delay(100); delay(100);
sendCommand(SDATAC); sendCommand(SDATAC);
// 配置CONFIG2寄存器启用测试信号 // 配置CONFIG2寄存器启用测试信号 1af 2hz
writeRegister(0x01, 0xD4); writeRegister(0x01, 0xD5);
writeRegister(0x02, 0x10); writeRegister(0x02, 0xD1);
writeRegister(0x03, 0xEC); writeRegister(0x03, 0xEC);
writeRegister(0x04, 0x00); // MISC1寄存器启用SRB1 writeRegister(0x04, 0x00); // MISC1寄存器启用SRB1
// 配置所有通道输入为内部测试信号 // 配置所有通道输入为内部测试信号
@ -212,11 +212,12 @@ void initADS1299() {
void readData() { void readData() {
byte data[27]; byte data[27];
digitalWrite(CS_PIN, LOW); digitalWrite(CS_PIN, LOW);
delayMicroseconds(1); // 确保 tSDSU
for (int i = 0; i < 27; i++) { for (int i = 0; i < 27; i++) {
data[i] = SPI.transfer(0x00); data[i] = SPI.transfer(0x00);
} }
digitalWrite(CS_PIN, HIGH); digitalWrite(CS_PIN, HIGH);
delayMicroseconds(1); // 确保 tDSHD
// 转换数据,并将结果保存在全局缓冲区 // 转换数据,并将结果保存在全局缓冲区
convertData(data, channelDataBuffer); convertData(data, channelDataBuffer);
@ -258,50 +259,63 @@ void measureImpedance() {
} }
void convertData(byte *data, double *channelData) { void convertData(byte *data, double *channelData) {
// 解析 STATUS 寄存器 // 解析STATUS寄存器
long statusValue = ((long)data[0] << 16) | ((long)data[1] << 8) | data[2]; 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++) { 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]; long value = ((long)data[3*i+3] << 16) | ((long)data[3*i+4] << 8) | data[3*i+5];
// if (value & 0x800000) {
// value |= 0xFF000000; // 符号扩展 // 24位符号扩展
// } if (value & 0x800000) {
channelData[i + 1] = (double)value * 4.5 / (double)0x800000; value |= 0xFF000000; // 负数扩展
}
// 电压计算 (考虑增益)
double vPerLSB = 4.5 / (24 * 8388608.0); // 1 LSB对应电压
channelData[i+1] = (double)value * vPerLSB;
} }
} }
void sendCommand(byte cmd) { void sendCommand(byte cmd) {
digitalWrite(CS_PIN, LOW); digitalWrite(CS_PIN, LOW);
delayMicroseconds(1); // 确保 tSDSU
SPI.transfer(cmd); SPI.transfer(cmd);
digitalWrite(CS_PIN, HIGH); digitalWrite(CS_PIN, HIGH);
delayMicroseconds(1); // 确保 tSDSU
} }
void writeRegister(byte reg, byte value) { void writeRegister(byte reg, byte value) {
digitalWrite(CS_PIN, LOW); digitalWrite(CS_PIN, LOW);
delayMicroseconds(1); // 确保 tSDSU
SPI.transfer(WREG | reg); SPI.transfer(WREG | reg);
SPI.transfer(0x00); SPI.transfer(0x00);
SPI.transfer(value); SPI.transfer(value);
digitalWrite(CS_PIN, HIGH); digitalWrite(CS_PIN, HIGH);
delayMicroseconds(1); // 确保 tSDSU
} }
byte readRegister(byte reg) { byte readRegister(byte reg) {
digitalWrite(CS_PIN, LOW); digitalWrite(CS_PIN, LOW);
delayMicroseconds(1); // 确保 tSDSU
SPI.transfer(RREG | reg); SPI.transfer(RREG | reg);
SPI.transfer(0x00); SPI.transfer(0x00);
byte value = SPI.transfer(0x00); byte value = SPI.transfer(0x00);
digitalWrite(CS_PIN, HIGH); digitalWrite(CS_PIN, HIGH);
delayMicroseconds(1); // 确保 tSDSU
return value; return value;
} }
void getDeviceID() { void getDeviceID() {
digitalWrite(CS_PIN, LOW); digitalWrite(CS_PIN, LOW);
delayMicroseconds(1); // 确保 tSDSU
SPI.transfer(SDATAC); SPI.transfer(SDATAC);
SPI.transfer(RREG | 0x00); SPI.transfer(RREG | 0x00);
SPI.transfer(0x00); SPI.transfer(0x00);
byte data = SPI.transfer(0x00); byte data = SPI.transfer(0x00);
digitalWrite(CS_PIN, HIGH); digitalWrite(CS_PIN, HIGH);
delayMicroseconds(1); // 确保 tSDSU
Serial.print("Device ID: "); Serial.print("Device ID: ");
Serial.println(data, BIN); Serial.println(data, BIN);
} }

View File

@ -33,7 +33,10 @@
> 【注意】请勿焊接TPS模块直接使用USB供电原因如下 > 【注意】请勿焊接TPS模块直接使用USB供电原因如下
> ESP的Vbus引脚直连USB电源并没有引脚二极管处理供电冲突所以请不要同时使用两种供电 > ESP的Vbus引脚直连USB电源并没有引脚二极管处理供电冲突所以请不要同时使用两种供电
> [!NOTE] > [!NOTE] 更新日志 2025/6/10
> 更新波形显示界面`FIFO_Plot.py`,添加去基线等滤波功能。修改部分驱动寄存器配置。
> [!NOTE] 更新日志 2024/12/7
> 项目已更新【SSVEP 范式】后续还会添加APP以及网页波形显示建议 Star 或者Fork后作者会添加更多内容。 > 项目已更新【SSVEP 范式】后续还会添加APP以及网页波形显示建议 Star 或者Fork后作者会添加更多内容。

View File

@ -1,5 +1,7 @@
import sys import sys
import time import time
from collections import deque
import numpy as np import numpy as np
import re import re
from PyQt5 import QtWidgets, QtCore, QtGui from PyQt5 import QtWidgets, QtCore, QtGui
@ -17,12 +19,13 @@ FFT_LENGTH = 500 # FFT分析长度
UPDATE_INTERVAL = 60 # 界面刷新间隔(ms) UPDATE_INTERVAL = 60 # 界面刷新间隔(ms)
SAMPLE_RATE = 500 # 采样率 SAMPLE_RATE = 500 # 采样率
# 新增滤波器参数 # 修改滤波器参数
NOTCH_FREQ = 50.0 # 陷波频率 NOTCH_FREQ = 50.0 # 陷波频率
QUALITY_FACTOR = 10 # 品质因数(决定带宽) QUALITY_FACTOR = 10 # 品质因数
FILTER_CUTOFF = 45 # 低通滤波截止频率 LOW_CUTOFF = 2.0 # 带通滤波低频截止
BUTTER_ORDER = 4 # 巴特沃兹滤波器阶数 HIGH_CUTOFF = 40.0 # 带通滤波高频截止
BUTTER_CUTOFF = 45 # 巴特沃兹截止频率 FIR_ORDER = 101 # FIR滤波器阶数
MEDIAN_WINDOW = 800 # 中值滤波窗口大小
class SerialThread(QtCore.QThread): class SerialThread(QtCore.QThread):
data_received = QtCore.pyqtSignal(str) data_received = QtCore.pyqtSignal(str)
@ -33,26 +36,29 @@ class SerialThread(QtCore.QThread):
self.filter_flag = True self.filter_flag = True
self.serial_port = None self.serial_port = None
self.running = False self.running = False
self.data_buffer = [[] for _ in range(9)] # 动态数据池 self.data_buffer = [[] for _ in range(9)]
self.timestamps = [] # 时间戳记录 self.timestamps = []
self.write_index = 0 self.write_index = 0
self.max_data_length = 1_000_000 # 最大数据量限制 self.max_data_length = 1_000_000
# 初始化滤波器参数 # 初始化滤波器
self.b_notch = [1] * 9 # 滤波器分子系数 self._init_filters()
self.a_notch = [1] * 9 # 滤波器分母系数
self.zi_notch = [np.zeros(2) for _ in range(9)] # 滤波器状态
def _init_filters(self):
# 设计50Hz陷波滤波器 # 设计50Hz陷波滤波器
nyquist = 0.5 * SAMPLE_RATE nyquist = 0.5 * SAMPLE_RATE
freq = NOTCH_FREQ / nyquist freq = NOTCH_FREQ / nyquist
self.b_notch, self.a_notch = iirnotch(freq, QUALITY_FACTOR) 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)] self.zi_notch = [lfilter_zi(self.b_notch, self.a_notch) for _ in range(9)]
# 巴特沃兹滤波器初始化 # 设计FIR带通滤波器
butter_cutoff = BUTTER_CUTOFF / nyquist from scipy.signal import firwin
self.b_butter, self.a_butter = butter(BUTTER_ORDER, butter_cutoff, btype='low') self.fir_coeff = firwin(FIR_ORDER,
self.zi_butter = [lfilter_zi(self.b_butter, self.a_butter) for _ in range(9)] [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): def run(self):
self.running = True self.running = True
@ -75,27 +81,34 @@ class SerialThread(QtCore.QThread):
self.serial_port.close() self.serial_port.close()
def _update_buffer(self, values): def _update_buffer(self, values):
# 应用50Hz陷波滤波
filtered_values = [] filtered_values = []
for i in range(9): for i in range(9):
# 使用lfilter进行实时滤波处理 # 1. 50Hz陷波滤波
filtered_value, self.zi_notch[i] = lfilter( notch_filtered, self.zi_notch[i] = lfilter(
self.b_notch, self.b_notch,
self.a_notch, self.a_notch,
[values[i]], [values[i]],
zi=self.zi_notch[i] zi=self.zi_notch[i]
) )
filtered_values.append(filtered_value[0])
# 第二级4阶巴特沃兹低通滤波 # 2. FIR带通滤波
for i in range(9): fir_filtered, self.zi_fir[i] = lfilter(
filtered, self.zi_butter[i] = lfilter( self.fir_coeff,
self.b_butter, [1.0],
self.a_butter, notch_filtered,
[filtered_values[i]], zi=self.zi_fir[i]
zi=self.zi_butter[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: while len(self.timestamps) >= self.max_data_length:
for ch in self.data_buffer: for ch in self.data_buffer:
ch.pop(0) ch.pop(0)