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"
// 定义宏,简化寄存器配置
#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);
}
}

View File

@ -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后作者会添加更多内容。

View File

@ -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)