目录
一、前期准备
我们在官网可以找到GPD系列的数据手册,在数据手册能找到该系列电源的控制方式,我们的程序也就据此展开:
二、代码部分
1、串口设置
根据数据手册说明,我们可完成对串口的基本设置
该部分界面代码示例:
def SerialSetting(self):
self.GropuBox_SerialSetting=QGroupBox('串口设置')
self.FormLayout_SerialSetting=QFormLayout()
self.ComboBox_SerialPort=MyComboBox()
self.ComboBox_SerialPort.clicked.connect(self.DetectSerialPort)
self.FormLayout_SerialSetting.addRow('串口选择:',self.ComboBox_SerialPort)
self.ComboBox_BaudRate=QComboBox()
self.ComboBox_BaudRate.addItems(['9600','57600','115200'])
self.FormLayout_SerialSetting.addRow('波特率:',self.ComboBox_BaudRate)
self.PushButton_ToggleSerial=QPushButton('打开串口')
self.PushButton_ToggleSerial.setCheckable(True) # 设置按钮为切换按钮
self.PushButton_ToggleSerial.clicked.connect(self.ToggleSerialPort)
self.FormLayout_SerialSetting.addRow(self.PushButton_ToggleSerial)
self.GropuBox_SerialSetting.setLayout(self.FormLayout_SerialSetting)
return self.GropuBox_SerialSetting
界面实际效果图:
串口功能函数,主要用于获取可用串口以及按照配置打开串口。
def DetectSerialPort(self):
self.ComboBox_SerialPort.clear()
#获取可用串口
SerialPortList=[x.portName()+' '+x.description() for x in QSerialPortInfo.availablePorts()]
for port in SerialPortList:
self.ComboBox_SerialPort.addItem(port)
def ToggleSerialPort(self):
if self.PushButton_ToggleSerial.isChecked():
try:
# 打开串口
port_name = self.ComboBox_SerialPort.currentText().split()[0] # 获取选中的串口名称
baud_rate = int(self.ComboBox_BaudRate.currentText())
self.SerialPort.setPortName(port_name)
self.SerialPort.setBaudRate(baud_rate)
self.SerialPort.setDataBits(QSerialPort.Data8)
self.SerialPort.setStopBits(QSerialPort.OneStop)
self.SerialPort.setParity(QSerialPort.NoParity)
self.SerialPort.setFlowControl(QSerialPort.NoFlowControl)
if self.SerialPort.open(QSerialPort.ReadWrite):
print("串口打开成功")
self.ComboBox_SerialPort.setEnabled(False)
self.ComboBox_BaudRate.setEnabled(False)
self.PushButton_ToggleSerial.setText("关闭串口")
self.PushButton_ToggleSerial.setStyleSheet("background-color: red; color: black;")
else:
print("无法打开串口")
QMessageBox.critical(self, "错误", "无法打开串口")
except Exception as e:
QMessageBox.critical(self, "错误", "请选择串口")
else:
# 关闭串口
self.SerialPort.close()
self.ComboBox_SerialPort.setEnabled(True)
self.ComboBox_BaudRate.setEnabled(True)
self.PushButton_ToggleSerial.setText("打开串口")
self.PushButton_ToggleSerial.setStyleSheet("background-color: white; color: black;")
2、输出电流/电压的设置和读取
在数据手册里,我们能找到控制用的指令,图中红框所圈出来就是接下来所需用到的指令。
该部分界面代码示例:
def CurrentControl(self):
self.GropuBox_CurrentControl=QGroupBox('电流控制')
self.FormLayout_CurrentControl=QFormLayout()
self.ComboBox_CurrentChannel=QComboBox()
self.ComboBox_CurrentChannel.addItems(['CH1','CH2'])
self.FormLayout_CurrentControl.addRow('电流通道',self.ComboBox_CurrentChannel)
self.LineEdit_CurrentSet=QLineEdit()
self.LineEdit_CurrentSet.setAlignment(QtCore.Qt.AlignCenter)
self.PushButton_CurrentSet=QPushButton('设置')
self.PushButton_CurrentSet.clicked.connect(self.SetCurrent)
self.hbox1 = QHBoxLayout()
self.hbox1.addWidget(self.LineEdit_CurrentSet)
self.hbox1.addWidget(QLabel('A'))
self.hbox1.addWidget(self.PushButton_CurrentSet)
self.FormLayout_CurrentControl.addRow('电流设置',self.hbox1)
self.LineEdit_CurrentRead=QLineEdit()
self.LineEdit_CurrentRead.setAlignment(QtCore.Qt.AlignCenter)
self.PushButton_CurrentRead=QPushButton('读取')
self.PushButton_CurrentRead.clicked.connect(self.ReadCurrent)
self.hbox2 = QHBoxLayout()
self.hbox2.addWidget(self.LineEdit_CurrentRead)
self.hbox2.addWidget(QLabel('A'))
self.hbox2.addWidget(self.PushButton_CurrentRead)
self.FormLayout_CurrentControl.addRow('电流读取',self.hbox2)
self.GropuBox_CurrentControl.setLayout(self.FormLayout_CurrentControl)
return self.GropuBox_CurrentControl
def VoltageControl(self):
self.GropuBox_VoltageControl=QGroupBox('电压控制')
self.FormLayout_VoltageControl=QFormLayout()
self.ComboBox_VoltageChannel=QComboBox()
self.ComboBox_VoltageChannel.addItems(['CH1','CH2'])
self.FormLayout_VoltageControl.addRow('电压通道',self.ComboBox_VoltageChannel)
self.LineEdit_VoltageSet=QLineEdit()
self.LineEdit_VoltageSet.setAlignment(QtCore.Qt.AlignCenter)
self.PushButton_VoltageSet=QPushButton('设置')
self.PushButton_VoltageSet.clicked.connect(self.SetVoltage)
self.hbox1 = QHBoxLayout()
self.hbox1.addWidget(self.LineEdit_VoltageSet)
self.hbox1.addWidget(QLabel('V'))
self.hbox1.addWidget(self.PushButton_VoltageSet)
self.FormLayout_VoltageControl.addRow('电压设置',self.hbox1)
self.LineEdit_VoltageRead=QLineEdit()
self.LineEdit_VoltageRead.setAlignment(QtCore.Qt.AlignCenter)
self.PushButton_VoltageRead=QPushButton('读取')
self.PushButton_VoltageRead.clicked.connect(self.ReadVoltage)
self.hbox2 = QHBoxLayout()
self.hbox2.addWidget(self.LineEdit_VoltageRead)
self.hbox2.addWidget(QLabel('V'))
self.hbox2.addWidget(self.PushButton_VoltageRead)
self.FormLayout_VoltageControl.addRow('电压读取',self.hbox2)
self.GropuBox_VoltageControl.setLayout(self.FormLayout_VoltageControl)
return self.GropuBox_VoltageControl
实际效果图:
在设置输出电流/电压时,我们需要提取对应下拉中的内容,并按上面指令的格式进行发送:
def SetCurrent(self):
if self.SerialPort.isOpen():
try:
current = self.LineEdit_CurrentSet.text()
if current:
channel_text = self.ComboBox_CurrentChannel.currentText()
channel_mapping = {"CH1": 1, "CH2": 2}
channel = channel_mapping[channel_text]
# 正确格式化字符串,并进行编码
data = f"ISET{channel}:{current}\r\n".encode('utf-8')
print(data)
# 确保使用的是正确的方法名
self.SerialPort.write(data)
else:
QMessageBox.warning(self, "警告", "电流设置不能为空")
except Exception as e:
print(e)
QMessageBox.critical(self, "错误", "设置电流值时出错")
def SetVoltage(self):
if self.SerialPort.isOpen():
try:
voltage = self.LineEdit_VoltageSet.text()
if voltage:
channel_text = self.ComboBox_VoltageChannel.currentText()
channel_mapping = {"CH1": 1, "CH2": 2}
channel = channel_mapping[channel_text]
# 正确格式化字符串,并进行编码
data = f"VSET{channel}:{voltage}\r\n".encode('utf-8')
print(data)
# 确保使用的是正确的方法名
self.SerialPort.write(data)
else:
QMessageBox.warning(self, "警告", "电压设置不能为空")
except Exception as e:
print(e)
QMessageBox.critical(self, "错误", "设置电压值时出错")
在读取电源实际输出电流/电压时,我们首先需要通过串口发送前文中的查询指令,同时开启串口接收进程,待接收到电源发过来对应的数据后,再将其处理后显示在对应的框中:
def ReadCurrent(self):
if self.SerialPort.isOpen():
try:
channel_text = self.ComboBox_CurrentChannel.currentText()
channel_mapping = {"CH1": 1, "CH2": 2}
channel = channel_mapping[channel_text]
# 正确格式化字符串,并进行编码
data = f"IOUT{channel}?\r\n".encode('utf-8')
print(data)
# 确保使用的是正确的方法名
self.SerialPort.write(data)
self.Thead.start()
self.GPDThead.ReceiveCurrent_Start()
self.SerialPort.clear()
except Exception as e:
print(e)
QMessageBox.critical(self, "错误", "读取电流值时出错")
def ReadCurrentProcess(self):
if self.SerialPort.isOpen():
try:
data = self.SerialPort.readAll().data().decode('utf-8')
if data:
print(data)
self.LineEdit_CurrentRead.clear()
self.LineEdit_CurrentRead.setText(data)
self.GPDThead.ReceiveCurrent_Stop()
self.Thead.quit()
except Exception as e:
print(e)
QMessageBox.critical(self, "错误", "读取电流值时出错")
def ReadVoltage(self):
if self.SerialPort.isOpen():
try:
channel_text = self.ComboBox_VoltageChannel.currentText()
channel_mapping = {"CH1": 1, "CH2": 2}
channel = channel_mapping[channel_text]
# 正确格式化字符串,并进行编码
data = f"VOUT{channel}?\r\n".encode('utf-8')
print(data)
# 确保使用的是正确的方法名
self.SerialPort.write(data)
self.SerialPort.clear()
self.Thead.start()
self.GPDThead.ReceiveVoltage_Start()
except Exception as e:
print(e)
QMessageBox.critical(self, "错误", "读取电压值时出错")
def ReadVoltageProcess(self):
if self.SerialPort.isOpen():
try:
data = self.SerialPort.readAll().data().decode('utf-8')
if data:
print(data)
self.LineEdit_VoltageRead.clear()
self.LineEdit_VoltageRead.setText(data)
self.GPDThead.ReceiveVoltage_Stop()
self.Thead.quit()
except Exception as e:
print(e)
QMessageBox.critical(self, "错误", "读取电压值时出错")
3、 定制功能——电流脉冲
为了精确控制输出对应电流的时间,我们可以在程序上做一个简单的集成:
如上图所示的界面实现代码示例:
def CurrentPulse_ModeOne(self):
self.GropuBox_CurrentPulse_ModeOne=QGroupBox('电流脉冲模式一')
self.FormLayout_CurrentPulse_ModeOne=QFormLayout()
self.LineEdit_PreheatTime=QLineEdit()
self.LineEdit_PreheatTime.setAlignment(QtCore.Qt.AlignCenter)
self.LineEdit_PreheatCurrent=QLineEdit()
self.LineEdit_PreheatCurrent.setAlignment(QtCore.Qt.AlignCenter)
self.hbox1 = QHBoxLayout()
self.hbox1.addWidget(QLabel('预热时间:'))
self.hbox1.addWidget(self.LineEdit_PreheatTime)
self.hbox1.addWidget(QLabel('s'))
self.hbox1.addWidget(QLabel('预热电流:'))
self.hbox1.addWidget(self.LineEdit_PreheatCurrent)
self.hbox1.addWidget(QLabel('A'))
self.FormLayout_CurrentPulse_ModeOne.addRow('',self.hbox1)
self.LineEdit_RestTime=QLineEdit()
self.LineEdit_RestTime.setAlignment(QtCore.Qt.AlignCenter)
self.hbox2 = QHBoxLayout()
self.hbox2.addWidget(QLabel('休息时间:'))
self.hbox2.addWidget(self.LineEdit_RestTime)
self.hbox2.addWidget(QLabel('s'))
self.FormLayout_CurrentPulse_ModeOne.addRow('',self.hbox2)
self.LineEdit_WorkTime=QLineEdit()
self.LineEdit_WorkTime.setAlignment(QtCore.Qt.AlignCenter)
self.LineEdit_WorkCurrent=QLineEdit()
self.LineEdit_WorkCurrent.setAlignment(QtCore.Qt.AlignCenter)
self.hbox3 = QHBoxLayout()
self.hbox3.addWidget(QLabel('工作时间:'))
self.hbox3.addWidget(self.LineEdit_WorkTime)
self.hbox3.addWidget(QLabel('s'))
self.hbox3.addWidget(QLabel('工作电流:'))
self.hbox3.addWidget(self.LineEdit_WorkCurrent)
self.hbox3.addWidget(QLabel('A'))
self.FormLayout_CurrentPulse_ModeOne.addRow('',self.hbox3)
self.LineEdit_CircleTime=QLineEdit()
self.LineEdit_CircleTime.setAlignment(QtCore.Qt.AlignCenter)
self.hbox4 = QHBoxLayout()
self.hbox4.addWidget(QLabel('循环次数:'))
self.hbox4.addWidget(self.LineEdit_CircleTime)
self.FormLayout_CurrentPulse_ModeOne.addRow('',self.hbox4)
self.ComboBox_ChannelOne=QComboBox()
self.ComboBox_ChannelOne.addItems(['CH1','CH2'])
self.hbox5 = QHBoxLayout()
self.hbox5.addWidget(QLabel('通道选择:'))
self.hbox5.addWidget(self.ComboBox_ChannelOne)
self.FormLayout_CurrentPulse_ModeOne.addRow('',self.hbox5)
self.PushButton_CurrentPulse_ModeOne=QPushButton('开始')
self.PushButton_CurrentPulse_ModeOne.clicked.connect(self.CurrentPulse_ModeOne_Init)
self.PushButton_CurrentPulse_ModeOne.setCheckable(True)
self.FormLayout_CurrentPulse_ModeOne.addRow('',self.PushButton_CurrentPulse_ModeOne)
self.GropuBox_CurrentPulse_ModeOne.setLayout(self.FormLayout_CurrentPulse_ModeOne)
return self.GropuBox_CurrentPulse_ModeOne
def CurrentPulse_ModeTwo(self):
self.GropuBox_CurrentPulse_ModeTwo=QGroupBox('电流脉冲模式二')
self.FormLayout_CurrentPulse_ModeTwo=QFormLayout()
self.LineEdit_InitialCurrent=QLineEdit()
self.LineEdit_InitialCurrent.setAlignment(QtCore.Qt.AlignCenter)
self.hbox1 = QHBoxLayout()
self.hbox1.addWidget(QLabel('初始电流:'))
self.hbox1.addWidget(self.LineEdit_InitialCurrent)
self.hbox1.addWidget(QLabel('A'))
self.FormLayout_CurrentPulse_ModeTwo.addRow('',self.hbox1)
self.LineEdit_HeatRate=QLineEdit()
self.LineEdit_HeatRate.setAlignment(QtCore.Qt.AlignCenter)
self.hbox2 = QHBoxLayout()
self.hbox2.addWidget(QLabel('升温速率:'))
self.hbox2.addWidget(self.LineEdit_HeatRate)
self.hbox2.addWidget(QLabel('A/s'))
self.FormLayout_CurrentPulse_ModeTwo.addRow('',self.hbox2)
self.LineEdit_EndCurrent=QLineEdit()
self.LineEdit_EndCurrent.setAlignment(QtCore.Qt.AlignCenter)
self.hbox3 = QHBoxLayout()
self.hbox3.addWidget(QLabel('结束电流:'))
self.hbox3.addWidget(self.LineEdit_EndCurrent)
self.hbox3.addWidget(QLabel('A'))
self.FormLayout_CurrentPulse_ModeTwo.addRow('',self.hbox3)
self.ComboBox_ChannelTwo=QComboBox()
self.ComboBox_ChannelTwo.addItems(['CH1','CH2'])
self.PushButton_CurrentPulse_ModeTwo=QPushButton('开始')
self.PushButton_CurrentPulse_ModeTwo.clicked.connect(self.ModeTwo_Init)
self.PushButton_CurrentPulse_ModeTwo.setCheckable(True)
self.hbox4 = QHBoxLayout()
self.hbox4.addWidget(QLabel('通道选择:'))
self.hbox4.addWidget(self.ComboBox_ChannelTwo)
self.hbox4.addWidget(self.PushButton_CurrentPulse_ModeTwo)
self.FormLayout_CurrentPulse_ModeTwo.addRow('',self.hbox4)
self.FormLayout_CurrentPulse_ModeTwo.addRow('',self.PushButton_CurrentPulse_ModeTwo)
self.GropuBox_CurrentPulse_ModeTwo.setLayout(self.FormLayout_CurrentPulse_ModeTwo)
return self.GropuBox_CurrentPulse_ModeTwo
对于上图所示功能,我们可以通过一个定时进程定时向串口发送输出电流设置指令实现。
def ModeTwo_Init(self):
if self.SerialPort.isOpen() and self.PushButton_CurrentPulse_ModeTwo.isChecked():
try:
self.current=float(self.LineEdit_InitialCurrent.text())
self.Thead.start()
self.GPDThead.ModeTwo_Start()
self.GPDThead.ReceiveData_Start()
self.SerialPort.clear()
self.PushButton_CurrentPulse_ModeTwo.setText('停止')
self.PushButton_CurrentPulse_ModeTwo.setStyleSheet("background-color: red; color: black;")
except Exception as e:
print(e)
QMessageBox.critical(self, "错误", "模式二启动失败")
else:
self.Thead.quit()
self.GPDThead.ModeTwo_Stop()
self.GPDThead.ReceiveData_Stop()
self.PushButton_CurrentPulse_ModeTwo.setText('开始')
self.PushButton_CurrentPulse_ModeTwo.setStyleSheet("background-color: white; color: black;")
def ModeTwo_Send(self):
try:
heat_rate = float(self.LineEdit_HeatRate.text())
end_current = float(self.LineEdit_EndCurrent.text())
if self.current+heat_rate<end_current:
self.current+=heat_rate
else:
self.current=end_current
channel_text = self.ComboBox_ChannelTwo.currentText()
channel_mapping = {"CH1": 1, "CH2": 2}
channel = channel_mapping[channel_text]
data = f"ISET{channel}:{self.current}\r\n".encode('utf-8')
self.SerialPort.write(data)
except Exception as e:
print(e)
这里我们给出了电流脉冲模式二的部分代码,模式一的代码也同理。
4、实时输出数据图绘制
在上面的基础上,我们虽然定时发送了输出控制指令,但我们不知道电源的实时输出情况,所以在这里还需要把实时输出情况绘制出来。同时,我们知道可以通过发送查询得知实时输出。于是,在接收到上一个数据后在坐标轴上标点连线,再次发送查询命令,可画出电源实时输出折线图。
class ChartView(QChartView,QChart):
def __init__(self, *args, **kwargs):
super(ChartView, self).__init__(*args, **kwargs)
self.resize(800, 600)
self.setRenderHint(QPainter.Antialiasing) # 抗锯齿
self.chart_init()
#def timer_init(self):
#使用QTimer,2秒触发一次,更新数据
#self.timer = QTimer(self)
#self.timer.timeout.connect(self.drawLine)
#self.timer.start(2000)
def chart_init(self):
self.chart = QChart()
self.series = QSplineSeries()
#设置曲线名称
self.series.setName("实时数据")
#把曲线添加到QChart的实例中
self.chart.addSeries(self.series)
#声明并初始化X轴,Y轴
self.dtaxisX = QDateTimeAxis()
self.vlaxisY = QValueAxis()
#设置坐标轴显示范围
self.dtaxisX.setMin(QDateTime.currentDateTime().addSecs(-300*1))
self.dtaxisX.setMax(QDateTime.currentDateTime().addSecs(0))
self.vlaxisY.setMin(0)
self.vlaxisY.setMax(10)
#设置X轴时间样式
self.dtaxisX.setFormat("MM月dd hh:mm:ss")
#设置坐标轴上的格点
self.dtaxisX.setTickCount(6)
self.vlaxisY.setTickCount(11)
#设置坐标轴名称
self.dtaxisX.setTitleText("时间")
self.vlaxisY.setTitleText("量程")
#设置网格不显示
self.vlaxisY.setGridLineVisible(False)
#把坐标轴添加到chart中
self.chart.addAxis(self.dtaxisX,Qt.AlignBottom)
self.chart.addAxis(self.vlaxisY,Qt.AlignLeft)
#把曲线关联到坐标轴
self.series.attachAxis(self.dtaxisX)
self.series.attachAxis(self.vlaxisY)
self.setChart(self.chart)
def drawLine(self):
#获取当前时间
bjtime = QDateTime.currentDateTime()
#更新X轴坐标
self.dtaxisX.setMin(QDateTime.currentDateTime().addSecs(-300*1))
self.dtaxisX.setMax(QDateTime.currentDateTime().addSecs(0))
#当曲线上的点超出X轴的范围时,移除最早的点
if(self.series.count()>149):
self.series.removePoints(0,self.series.count()-149)
#产生随即数
yint = random.randint(0,1500)
#添加数据到曲线末端
self.series.append(bjtime.toMSecsSinceEpoch(),yint)
界面图:
三、实际效果演示
程序测试完美运行,功能全实现,搞定!