PCF8575配置修改后16通道继电器工作异常求助
问题描述
有一台连接LCR用于测量交流电感的16通道继电器,修改PCF8575配置后未备份,恢复配置后继电器无法正常工作:原本应循环所有16通道且始终保持1路接地激活,但现在仅运行4-5个通道就停止。尝试过将new_state = -16改为new_state = 0xFFFF,但不确定是否正确,需排查修复。
提供的PCF8575配置代码
import smbus class IOPort(list): """ Represents the PCF8575 IO port as a list of boolean values. """ def __init__(self, pcf8575, *args, **kwargs): super(IOPort, self).__init__(*args, **kwargs) self.pcf8575 = pcf8575 def __setitem__(self, key, value): """ Set an individual output pin. """ self.pcf8575.set_output(key, value) def __getitem__(self, key): """ Get an individual pin state. """ return self.pcf8575.get_pin_state(key) def __repr__(self): """ Represent port as a list of booleans. """ state = self.pcf8575.bus.read_word_data(self.pcf8575.address, 0) ret = [] for i in range(16): ret.append(bool(state & 1<<15-i)) return repr(ret) def __len__(self): return 16 def __iter__(self): for i in range(16): yield self[i] def __reversed__(self): for i in range(16): yield self[15-i] class PCF8575(object): """ A software representation of a single PCF8575 IO expander chip. """ def __init__(self, i2c_bus_no, address): self.bus_no = i2c_bus_no self.bus = smbus.SMBus(i2c_bus_no) self.address = address def __repr__(self): return "PCF8575(i2c_bus_no=%r, address=0x%02x)" % (self.bus_no, self.address) @property def port(self): """ Represent IO port as a list of boolean values. """ return IOPort(self) @port.setter def port(self, value): """ Set the whole port using a list. """ assert isinstance(value, list) assert len(value) == 16 new_state = 0xFFFF for i, val in enumerate(value): if val: new_state |= 1 << 15-i self.bus.write_byte_data(self.address, new_state & 0xff, (new_state >> 8) & 0xff) def set_output(self, output_number, value): """ Set a specific output high (True) or low (False). """ assert output_number in range(16), "Output number must be an integer between 0 and 15" current_state = self.bus.read_word_data(self.address, 0) bit = 1 << 15-output_number new_state = current_state | bit if value else current_state & (~bit & 0xFFFF) self.bus.write_byte_data(self.address, new_state & 0xff, (new_state >> 8) & 0xff) def get_pin_state(self, pin_number): """ Get the boolean state of an individual pin. """ assert pin_number in range(16), "Pin number must be an integer between 0 and 15" state = self.bus.read_word_data(self.address, 0) return bool(state & 1<<15-pin_number)
提供的继电器控制代码relay.py
from pcf8575 import PCF8575 import time """ Relay.Py allows to control the relay card, its functions allow to choose the cables to be connected to the LCR meter. """ i2c_port_num = 1 # on terminal do : "i2cdetect -l" ,return : i2c-1, so i2c_port_num = 1 pcf_adress = 0x20 # on terminal do : "i2cdetect -y 1" ,return : 0x20, pcf_adress = 0x20, #you can change it with the i2c slave address switch which is physically on the relayboard pcf=PCF8575(i2c_port_num,pcf_adress) #we assimilate the relay numbers to their chosen designations Relay_1A = 1 Relay_1B = 16 Relay_2A = 2 Relay_2B = 15 Relay_3A = 3 Relay_3B = 14 Relay_4A = 4 Relay_4B = 13 Relay_5A = 5 Relay_5B = 12 Relay_6A = 6 Relay_6B = 11 Relay_7A = 7 Relay_7B = 10 Relay_size = 8 #normally closed on the side A Relay_gnd = 9 #normally closed on the reinforcement A Relay_A=(Relay_1A, Relay_2A, Relay_3A , Relay_4A, Relay_5A, Relay_6A, Relay_7A) Relay_B=(Relay_1B, Relay_2B, Relay_3B , Relay_4B, Relay_5B, Relay_6B, Relay_7B) Relay=(Relay_A, Relay_B) A=0 B=1 def clear_all(): State=[True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True] pcf.port=State return State def switch_on(State,relay): State[abs(relay-16)]=False #the pcf.port list is reversed (pcf.port[0]=relay 16, pcf.port[1]=relay 15, etc...) pcf.port=State #if you do directly pcf.port[0]= false, relay from 9 to 16 will be False (activate), I don't know why, so we use State return State def connect_tendon(num_tendon): State=clear_all() State=switch_on(State, Relay_A[num_tendon-1]) State=switch_on(State, Relay_B[num_tendon-1]) # With this function you can connect you to one side of a cable and choose one ground def connect_with_ground(num_tendon, tendon_side, ground_side): State=clear_all() State=switch_on(State, Relay[tendon_side][num_tendon-1]) if tendon_side==0: State=switch_on(State, Relay_size) else: pass if ground_side==1: State=switch_on(State, Relay_gnd) else: pass # These examples of ground tests are all combinations that can be found with two grounds and two connections per cable. def Connect_gnd_1(num_tendon): connect_with_ground(num_tendon,A,A) def Connect_gnd_2(num_tendon): connect_with_ground(num_tendon,A,B) def Connect_gnd_3(num_tendon): connect_with_ground(num_tendon,B,A) def Connect_gnd_4(num_tendon): connect_with_ground(num_tendon,B,B) try: clear_all() except: pass clear_all()
排查与修复建议
1. 修复PCF8575的字节写入顺序
PCF8575的I2C传输规则是先发送高字节(对应引脚P15-P8),再发送低字节(对应引脚P7-P0),原代码的写入顺序完全颠倒,导致引脚映射混乱。
修改PCF8575类中的两处写入逻辑:
# 修改port.setter中的写入代码 self.bus.write_byte_data(self.address, (new_state >> 8) & 0xff, new_state & 0xff) # 修改set_output中的写入代码 self.bus.write_byte_data(self.address, (new_state >> 8) & 0xff, new_state & 0xff)
2. 修正继电器编号与PCF引脚的映射逻辑
relay.py中switch_on函数的索引计算错误,继电器编号1-16对应PCF引脚15-0,正确的索引计算应为:
def switch_on(State, relay): # 继电器编号1→PCF引脚15,继电器16→PCF引脚0 pin_idx = 15 - (relay - 1) State[pin_idx] = False # 低电平激活继电器(多数继电器模块为低电平触发) pcf.port = State return State
原代码的abs(relay-16)会导致继电器9对应PCF引脚7,而实际应该对应引脚6,这是部分通道失效的核心原因。
3. 添加全通道循环逻辑
原代码未实现循环16通道的功能,补充以下代码到relay.py末尾,确保接地通道始终激活:
def cycle_all_channels(): try: while True: # 初始状态:保持接地激活 State = clear_all() State = switch_on(State, Relay_gnd) # 循环Relay_A组7个通道 for idx in range(len(Relay_A)): State = switch_on(State, Relay_A[idx]) print(f"激活通道Relay_A{idx+1},保持接地") time.sleep(2) # 重置当前通道,保留接地 State = clear_all() State = switch_on(State, Relay_gnd) # 循环Relay_B组7个通道 for idx in range(len(Relay_B)): State = switch_on(State, Relay_B[idx]) print(f"激活通道Relay_B{idx+1},保持接地") time.sleep(2) State = clear_all() State = switch_on(State, Relay_gnd) # 激活Relay_size通道 State = switch_on(State, Relay_size) print(f"激活通道Relay_size,保持接地") time.sleep(2) except KeyboardInterrupt: clear_all() print("循环终止,重置所有继电器") # 启动循环 cycle_all_channels()
4. 硬件与基础验证
- 用
i2cdetect -y 1确认PCF8575的0x20地址能被正常识别,排除I2C通信故障 - 检查继电器模块的供电电压/电流是否满足16路继电器的驱动需求
- 确认故障通道的接线无松动、短路情况
内容的提问来源于stack exchange,提问作者Povilas
相关产品推荐
相关产品推荐

