diff --git a/APSPU_I2C.py b/APSPU_I2C.py index 55b0c95bea3ec5a3aca54d7374779f15367bb129..7d3584dea5e1b107bf10cb281de6694f7b0722f8 100644 --- a/APSPU_I2C.py +++ b/APSPU_I2C.py @@ -1,89 +1,116 @@ -#******************************************# -# I2C address, registers and ports for UNB2c -# Created: 2021-05-11 -#******************************************# - -################################### +""" +Copyright 2022 Stichting Nederlandse Wetenschappelijk Onderzoek Instituten, +ASTRON Netherlands Institute for Radio Astronomy +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + + Created: 2022-12-07 +This file contains the definitions of the APSPU registers etc. + + +""" +# +# I2C address, registers and ports for APSPU +# Created: 2022-12-07 +# + +# # General, Point of load converters -################################### +# -CTR_LBA = 0x3C +CTR_LBA = 0x3C CTR_RCU2_A = 0x3D CTR_RCU2_D = 0x0E -CTR_POLS = {"CTR_LBA" : 0x3C, - "CTR_RCU2_A" : 0x3D, - "CTR_RCU2_D" : 0x3E} - -VOUT_POLS = {"CTR_LBA" : 8.0, - "CTR_RCU2_A" : 5.60009765625, - "CTR_RCU2_D" : 3.2998046875} - -IOUT_POLS = {"CTR_LBA" : 0.45, - "CTR_RCU2_A" : 0.7, - "CTR_RCU2_D" : 0.3} - -LP_VIN = 0x88 # -LP_VOUT_MODE = 0x20 -LP_VOUT = 0x8B # -LP_temp = 0x8D # -LP_IOUT = 0x8C -LP_VOUT_COMMAND = 0x21 -LP_VOUT_TRIM = 0x22 -LP_VOUT_MAX = 0x24 +CTR_POLS = {"CTR_LBA": 0x3C, + "CTR_RCU2_A": 0x3D, + "CTR_RCU2_D": 0x3E} + +VOUT_POLS = {"CTR_LBA": 8.0, + "CTR_RCU2_A": 5.60009765625, + "CTR_RCU2_D": 3.2998046875} + +IOUT_POLS = {"CTR_LBA": 0.45, + "CTR_RCU2_A": 0.7, + "CTR_RCU2_D": 0.3} + +LP_VIN = 0x88 +LP_VOUT_MODE = 0x20 +LP_VOUT = 0x8B +LP_temp = 0x8D +LP_IOUT = 0x8C +LP_VOUT_COMMAND = 0x21 +LP_VOUT_TRIM = 0x22 +LP_VOUT_MAX = 0x24 LP_VOUT_SCALE_LOOP = 0x29 -LP_VOUT_SCALE_MON = 0x2A -LP_VOUT_OV_LIMIT = 0x40 -LP_VOUT_UV_LIMIT = 0x44 -LP_STORE_USER_ALL = 0x15 -LP_ON_OFF_CONFIG = 0x02 -LP_OPERATION = 0x01 - -################################### +LP_VOUT_SCALE_MON = 0x2A +LP_VOUT_OV_LIMIT = 0x40 +LP_VOUT_UV_LIMIT = 0x44 +LP_STORE_USER_ALL = 0x15 +LP_ON_OFF_CONFIG = 0x02 +LP_OPERATION = 0x01 + +# # Central I2C Devices -################################### -EEPROM = 0x50 +# +EEPROM = 0x50 -################################### +# # FAN speed -################################### -MAX6620 = 0x29 +# +MAX6620 = 0x29 REG_GLOBAL = 0x00 -REG_TACH_MSP_REGS = [ 0x10, 0x12, 0x14] -REG_TACH_LSP_REGS = [ 0x11, 0x13, 0x15] +REG_TACH_MSP_REGS = [0x10, 0x12, 0x14] +REG_TACH_LSP_REGS = [0x11, 0x13, 0x15] TACH_PERIODS = 16 TACH_COUNT_FREQ = 8192 FAN_TACHS = 1 RUN_MONITOR = 0x02 - NOF_APS_FANS = 3 -###################### +# # Functions -###################### +# + -# Calculate floating point value according PMBus lineair def calc_lin_2bytes(data): - expo = ((data[1] & 0xf8)>>3) + # + # Calculate floating point value according PMBus lineair + # + # input data (Byte array) + # return value (float) + # + expo = ((data[1] & 0xf8) >> 3) if expo == 1: expo = -7 if expo > (2**4): expo = expo-2**5 mantisse = (data[1] & 0x7)*0x100 + data[0] if mantisse > (2**(8+2)): - mantisse = mantisse -2**(8+3) + mantisse = mantisse - 2**(8 + 3) output = mantisse * 2**expo return output -# Calculate floating point value according PMBus lineair -def calc_lin_3bytes(data,mode): - expo = (mode[0] & 0x1F) + +def calc_lin_3bytes(data, mode): + # + # Calculate floating point value according PMBus lineair + # + # input data (Byte array) + # return value (float) + # + expo = (mode[0] & 0x1F) if expo > 2**4: expo = expo - 2**5 mant = (data[1]*256 + data[0]) output = mant * 2**expo return output - - - diff --git a/apspu_lib.py b/apspu_lib.py index 809a31b9820c37ea1367a72f6633eed1d113017e..39a47f5337a38bcf6461e1abd899774161ac1861 100644 --- a/apspu_lib.py +++ b/apspu_lib.py @@ -12,8 +12,8 @@ See the License for the specific language governing permissions and limitations under the License. - Created: 2021-05-10 -This file contains the UniBoard2 class with all monitoring function. It can be used stand-alone to test it. + Created: 2022-17-12 +This file contains the APSPU class with all monitoring function. It can be used stand-alone to test it. """ @@ -21,20 +21,21 @@ import sys sys.path.insert(0, '.') import os import math - from APSPU_I2C import * + + if os.name == "posix": from I2C_serial_pi2 import * else: from I2C_serial import * +I2CBUSNR = 1 # Bus used on the Pi +DEBUG = False # Set True to print debug information on the screen -I2CBUSNR = 1 -DEBUG = False class ApspuClass: # - # Class that contains all parts on a UniBoard2 + # Toplevel Class that contains all parts on a APSU # def __init__(self): self.status = False @@ -46,7 +47,7 @@ class ApspuClass: def read_all(self): # - # Function to read all monitoring points of the UniBoard + # Function to read all monitoring points of the APSPU # print("--------- \nRead APSPU Status\n---------") for pol in self.pols: @@ -64,6 +65,10 @@ class ApspuClass: return True def set_pols(self): + # + # Function to set output voltage level on the APSPU + # Values are read from the APSPU_I2C + # print("--------- \nProgram Pols\n---------") for pol in self.pols: pol.set_vout_pol(VOUT_POLS[pol.name]) @@ -77,6 +82,11 @@ class ApspuClass: print("Done") def check_apspu(self): + # + # Function to check values of read_all() Used during production + # + # Return True is OK, False if not OK + # print("--------- \nCheck APSPU \n---------") check_ok = True for pol in self.pols: @@ -89,6 +99,12 @@ class ApspuClass: return check_ok def apspu_on_off(self, on): + # + # Function to switch off the POLs on APSPU + # on = True to switch APSU on + # on = False to switch APSU off + # Return: always True + # if on: print(f">> Switch APSPU ON") else: @@ -96,14 +112,15 @@ class ApspuClass: for pol in self.pols: pol.on_off(on) return True - + + class EepromClass: # # Class to handle EEPROM communication # def __init__(self): # - # All monitoring points Point of Load DC/DC converter + # All monitor. read and write functions for the EEPROM # self.dev_i2c_eeprom = I2C(EEPROM) self.dev_i2c_eeprom.bus_nr = I2CBUSNR @@ -112,6 +129,10 @@ class EepromClass: # # Write the EEPROM with the serial number etc. # + # Data = data to write in string formal + # Address = address to write the data to + # Return True if successfully + # ret_ack, ret_value = self.dev_i2c_eeprom.read_bytes(0) if ret_ack < 1: print("EEPROM not found during write") @@ -127,6 +148,10 @@ class EepromClass: # # Read the EEPROM with the serial number etc. # + # Address = address to read from + # nof_bytes = number of bytes to read + # return string with the data from the flash + # ret_ack, ret_value = self.dev_i2c_eeprom.read_last_reg(1) if ret_ack < 1: print("no EEPROM found during read") @@ -136,10 +161,14 @@ class EepromClass: str_return = bytes.fromhex(ret_value[:nof_bytes*2]).decode('UTF-8') return str_return - def wr_rd_eeprom(self, value="APSPU-1", address = 0): + def wr_rd_eeprom(self, value="APSPU-1", address=0): # # Write and Read the EEPROM to check functionality # + # value = string with data to write + # address = address to write the data to + # return True if read back is same as write value + # if self.write_eeprom(value, address=0): ret_value = self.read_eeprom(address=0, nof_bytes=len(value)) stri = "Wrote to EEPROM register 0x{2:x} : {0}, Read from EEPROM: {1}".format(value, ret_value, address) @@ -170,17 +199,17 @@ class PolClass: else: self.status = True - def read_vout_set(self): # # Function to read the output voltage of the Point of Load DC/DC converter # + # Return: output value is of else 999 + # if self.status: ret_ack, raw_value = self.pol_dev.read_bytes(LP_VOUT_COMMAND, 2) - if len(raw_value)<4: + if len(raw_value) < 4: raw_value = '0' + raw_value - ret_value = [] - ret_value= int(raw_value[2:], 16) * 2**8 + ret_value = int(raw_value[2:], 16) * 2**8 ret_value += int(raw_value[:2], 16) output_value = ret_value * 2**-11 if DEBUG: @@ -193,56 +222,80 @@ class PolClass: # # Function to set the output of the DC/DC converter # + # Return always I2C ack + # + ret_ack = False if self.status: - if DEBUG: - ret_ack, raw_value = self.pol_dev.read_bytes(LP_ON_OFF_CONFIG, 1) - print(f"Current setting ON/OFF register 0x{int(raw_value, 16):02x}") - on_off_bit = not 1 << 0 - polarity_pin = 1 << 1 - use_external = 0 << 2 - use_soft = 1 << 3 - default_off = 1 << 4 - wr_data = on_off_bit + polarity_pin + use_external + use_soft + default_off - ret_ack = self.pol_dev.write_bytes(LP_ON_OFF_CONFIG, wr_data) - return True + if DEBUG: + ret_ack, raw_value = self.pol_dev.read_bytes(LP_ON_OFF_CONFIG, 1) + print(f"Current setting ON/OFF register 0x{int(raw_value, 16):02x}") + on_off_bit = not 1 << 0 + polarity_pin = 1 << 1 + use_external = 0 << 2 + use_soft = 1 << 3 + default_off = 1 << 4 + wr_data = on_off_bit + polarity_pin + use_external + use_soft + default_off + ret_ack = self.pol_dev.write_bytes(LP_ON_OFF_CONFIG, wr_data) + return ret_ack def on_off(self, on=True): - wr_data = (on << 7) + 0 - ret_ack = self.pol_dev.write_bytes(LP_OPERATION, wr_data) + # + # Function to switch on or off a POL + # + # if on = True switch on the POL + # if on = Flase swtich off the POL + # Return I2C ack + # + wr_data = (on << 7) + 0 + ret_ack = self.pol_dev.write_bytes(LP_OPERATION, wr_data) + return ret_ack def set_vout_pol(self, value): # # Function to read the output voltage of the Point of Load DC/DC converter # + # value is the output voltage level in V + # return I2C ack + # + ret_ack = False if self.status: set_value = int(value * (2**11)) hex_set_value = hex(set_value) wr_value = (hex_set_value[4:6] + hex_set_value[2:4]) if DEBUG: print(f"Calculated wr_value is {wr_value}") - wr_data =[] + wr_data = [] wr_data.append(int(hex_set_value[4:6], 16)) wr_data.append(int(hex_set_value[2:4], 16)) ret_ack = self.pol_dev.write_bytes(LP_VOUT_COMMAND, wr_data) - return True + return ret_ack def set_vout_ov_limit_pol(self, value): # # Function to read the output voltage of the Point of Load DC/DC converter # + # value is the overvoltage level of the output + # return I2C ack + # + ret_ack = False if self.status: set_value = int(value * (2**11)) hex_set_value = hex(set_value) wr_value = (hex_set_value[4:6] + hex_set_value[2:4]) if DEBUG: print(f"Calculated wr_value is {wr_value}") - wr_data =[] + wr_data = [] wr_data.append(int(hex_set_value[4:6], 16)) wr_data.append(int(hex_set_value[2:4], 16)) ret_ack = self.pol_dev.write_bytes(LP_VOUT_OV_LIMIT, wr_data) - return True + return ret_ack def write_to_nvm(self): + # + # Function to write the POL's registers to NVM memory + # + # return is always True + # ret_ack = self.pol_dev.write_bytes(LP_STORE_USER_ALL, 0) return True @@ -252,12 +305,12 @@ class PolClass: # if self.status: ret_ack, raw_value = self.pol_dev.read_bytes(LP_VIN, 2) - if len(raw_value)<4: + if len(raw_value) < 4: raw_value = '0' + raw_value ret_value = [] ret_value.append(int(raw_value[:2], 16)) - ret_value.append(int(raw_value[2:], 16)) # * 2**8 - output_value = calc_lin_2bytes(ret_value) #ret_value * 2**-11 + ret_value.append(int(raw_value[2:], 16)) + output_value = calc_lin_2bytes(ret_value) self.vin = output_value def read_vout(self): @@ -273,7 +326,7 @@ class PolClass: try: ret_value.append(int(raw_value[2:], 16)) except: - ret_value.append(0) + ret_value.append(0) self.vout = calc_lin_3bytes(ret_value, [vout_mod]) else: self.vout = 999 @@ -281,40 +334,46 @@ class PolClass: def read_ov_limit(self): # # Function to read the output voltage of the Point of Load DC/DC converter + # and print on the screen # + # Return OV limit + # + output_value = 0 if self.status: ret_ack, raw_value = self.pol_dev.read_bytes(LP_VOUT_OV_LIMIT, 2) - if len(raw_value)<4: + if len(raw_value) < 4: raw_value = '0' + raw_value - ret_value = [] - ret_value= int(raw_value[2:], 16) * 2**8 + ret_value = int(raw_value[2:], 16) * 2**8 ret_value += int(raw_value[:2], 16) output_value = ret_value * 2**-11 print(f"Output OV limit is set to: = {output_value:5.2f} V using hex value {raw_value}") + return output_value def read_uv_limit(self): # # Function to read the output voltage of the Point of Load DC/DC converter # + # Return UV limit if OK else False + # if self.status: ret_ack, raw_value = self.pol_dev.read_bytes(LP_VOUT_UV_LIMIT, 2) - if len(raw_value)<4: + if len(raw_value) < 4: raw_value = '0' + raw_value ret_value = [] - ret_value= int(raw_value[2:], 16) * 2**8 + ret_value = int(raw_value[2:], 16) * 2**8 ret_value += int(raw_value[:2], 16) output_value = ret_value * 2**-11 print(f"Output UV limit is set to: = {output_value:5.2f} V using hex value {raw_value}") return output_value else: - return 9999 + return False def read_iout(self): # # Function to read the output current of the Point of Load DC/DC converter # if self.status: - ret_ack, raw_value = self.pol_dev.read_bytes(0x39,2) + ret_ack, raw_value = self.pol_dev.read_bytes(0x39, 2) ret_ack, raw_value = self.pol_dev.read_bytes(LP_IOUT, 2) ret_value = [] ret_value.append(int(raw_value[:2], 16)) @@ -346,6 +405,11 @@ class PolClass: self.read_vin() def check_pol(self): + # + # Function to read all monitoring points of the Point of Load DC/DC converter + # + # Return True if OK, False if not OK + # self.read_all() self.on_off(on=True) check_ok = False @@ -378,9 +442,9 @@ class PolClass: check_ok = True else: check_ok = False - print(f"POL {self.name:10} Iout not OK, expected {i_low:4.2f} A - {i_high:4.2f} A, measured {self.iout:4.2f} A ") + print(f"POL {self.name:10} Iout not OK," + f" expected {i_low:4.2f} A - {i_high:4.2f} A, measured {self.iout:4.2f} A ") return check_ok - def print_status(self): # @@ -395,13 +459,14 @@ class PolClass: print(stri) self.read_vout_set() + class FanmonitorClass: # - # Class to read all monitoring points Point of Load DC/DC converter + # Class to read all monitoring points fan units in the APS # def __init__(self): # - # All monitoring points Point of Load DC/DC converter + # All monitoring points for the fans # self.rpm = [] self.fanmonitor_dev = I2C(MAX6620) @@ -426,7 +491,8 @@ class FanmonitorClass: self.fanmonitor_dev.write_bytes(REG_GLOBAL, RUN_MONITOR) ret_ack, reg_after = self.fanmonitor_dev.read_bytes(REG_GLOBAL, 1) if DEBUG: - stri = "Reg at address 0x{0} before : {1} and after write action {2}".format(REG_GLOBAL, reg_before, reg_after) + stri = "Reg at address 0x{0} before : {1} and after write action {2}"\ + .format(REG_GLOBAL, reg_before, reg_after) print(stri) fan_config_reg = int((math.log(TACH_PERIODS) / math.log(2))) << 5 for fan_cnt in range(NOF_APS_FANS): @@ -436,13 +502,16 @@ class FanmonitorClass: def read_fan(self, fan_nr): # # Function to a single fan + # fan_nr is the fan to read ranging from 0 till 2 + # return the speed of the fan # + if fan_nr > NOF_APS_FANS: + return 0 ret_ack, tach_msb = self.fanmonitor_dev.read_bytes(REG_TACH_MSP_REGS[fan_nr], 1) tach_msb = int(tach_msb, 16) & 0xFF + tach_lsb = 255 + tach = 99999 if tach_msb > 254: - if DEBUG : - tach_lsb = 255 - tach = 99999 rpm = 0 else: ret_ack, tach_lsb = self.fanmonitor_dev.read_bytes(REG_TACH_LSP_REGS[fan_nr], 1) @@ -458,11 +527,14 @@ class FanmonitorClass: # # Function to read all fan's # - self.rpm=[] + self.rpm = [] for fan_counter in range(NOF_APS_FANS): self.rpm.append(self.read_fan(fan_counter)) def check_fans(self): + # + # Function to check fan speeds + # self.read_all() check_ok = True for cnt, speed in enumerate(self.rpm): diff --git a/production_apspu.py b/production_apspu.py index 481bb2fcb4346057914cb190c63e9b4d948adb3f..824cf3d83ba67ff29252b22dab63ce1f274a1452 100644 --- a/production_apspu.py +++ b/production_apspu.py @@ -1,5 +1,5 @@ """ -Copyright 2021 Stichting Nederlandse Wetenschappelijk Onderzoek Instituten, +Copyright 2022 Stichting Nederlandse Wetenschappelijk Onderzoek Instituten, ASTRON Netherlands Institute for Radio Astronomy Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -12,31 +12,29 @@ See the License for the specific language governing permissions and limitations under the License. - Created: 2021-05-10 -This file contains the UniBoard2 class with all monitoring function. It can be used stand-alone to test it. + Created: 2022-12-07 +This file contains the APSPU production script. """ import sys sys.path.insert(0, '.') -import os -import math + from apspu_lib import * - -if len(sys.argv)<2: +if len(sys.argv) < 2: print("Production_apspy.py <ASTRON NR> <Serial number>") print("e.g. python production_apspy.py 2022-01 1234") exit() apspu = ApspuClass() apspu.apspu_on_off(False) -sleep(5) +sleep(5) # Wait for outputs to be stable off apspu.set_pols() -x = input("Change dipswitches and press key to continue..") +input("Change dipswitches and press key to continue..") apspu.apspu_on_off(True) -sleep(10) +sleep(5) # Wait for outputs to be stable on apspu.read_all() apspu.print_status() if apspu.check_apspu(): @@ -45,7 +43,7 @@ if apspu.check_apspu(): apspu.read_all() apspu.print_status() apspu.apspu_on_off(True) - id = "APSPU-" + sys.argv[1] + apspu_id = "APSPU-" + sys.argv[1] serial = sys.argv[2] - apspu.eeprom.wr_rd_eeprom(id, address=0) + apspu.eeprom.wr_rd_eeprom(apspu_id, address=0) apspu.eeprom.wr_rd_eeprom(serial, address=0x20)