diff --git a/APSCT_CLK_I2C.py b/APSCT_CLK_I2C.py index 1465aa7f3f0f03d773174fb7496fdf19b0292ef1..99309c6c99b0140bf4499697e2c9cc513bf45d57 100644 --- a/APSCT_CLK_I2C.py +++ b/APSCT_CLK_I2C.py @@ -26,7 +26,7 @@ else: DEBUG = False I2CBUSNR=5 sleep_time = 0.15 -SET_PLL = False #True +SET_PLL = True READ_LOCK = True READ_ALL = False CHECK_EEPROM = False diff --git a/APSCT_I2C.py b/APSCT_I2C.py new file mode 100644 index 0000000000000000000000000000000000000000..4e10741eadd811dbd97ea5c2d3668a9104c9c918 --- /dev/null +++ b/APSCT_I2C.py @@ -0,0 +1,74 @@ +""" +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 APSCT +# Created: 2023-01-06 +# + +# +# Power supplies +# + +PWR_LOCATIONS = {"INPUT": 0, + "PLL_160M": 1, + "PLL_200M": 2, + "DIST_A": 3, + "DIST_B": 4, + "DIST_C": 5, + "DIST_D": 6, + "CTRL": 7} + +PWR_VOUT = {"INPUT": 3.3, + "PLL_160M": 3.3, + "PLL_200M": 3.3, + "DIST_A": 3.3, + "DIST_B": 3.3, + "DIST_C": 3.3, + "DIST_D": 3.3, + "CTRL": 3.3} + +# +# PLL constants / pin locations +# +CS = 6 +SCLK = 4 +SDO = 5 +SDI = 7 + +PLL_200M = 0x20 +PLL_160M = 0x21 + +# +# Central I2C Devices +# +EEPROM = 0x50 + +# +# I2C switch addresses +# +i2c_switch_addr_rcu = [0x70, 0x71, 0x72, 0x73] +i2c_bus_rcu = 1 +i2c_switch_addr_unb = [0x70] +i2c_bus_unb = 3 + +# +# ID Pins +# +ID_PINS = [8, 7, 12, 16, 20, 21] diff --git a/APSPU_I2C.py b/APSPU_I2C.py index 60a47c53b4cfdd1e5b7e8401e0052ebe5b120ef2..ca6f97e258fae7fc30adf186a1893b04c53ac4b5 100644 --- a/APSPU_I2C.py +++ b/APSPU_I2C.py @@ -1,90 +1,117 @@ -#******************************************# -# I2C address, registers and ports for UNB2c -# Created: 2021-05-11 -# Last update 2023-01-27 for L2TS production series -#******************************************# - -################################### +""" +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.2, + "CTR_RCU2_A": 0.6, + "CTR_RCU2_D": 0.2} + +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 +LP_WRITE_PROTECT = 0x10 +LP_STORE_DEFAULT_ALL = 0x12 +# # 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/I2C_serial_pi.py b/I2C_serial_pi.py index 6aaa3f5e84e50025927e29e38035072ea98e9328..d4cc4cccb0550528e753cc89ef9c46d8d9fa1f1b 100644 --- a/I2C_serial_pi.py +++ b/I2C_serial_pi.py @@ -42,15 +42,20 @@ class I2C: ret_ack = 1 if SLOW: sleep(0.2) - except IOError, err: + except IOError: ret_ack = 0 ret_value = 'ffff' if DEBUG: print("Reading error") + except err: + ret_ack = 0 + ret_value = 'ffff' + if DEBUG: + print("Reading IO-error") return ret_ack, ret_value - def read_last_reg(self, bytes_to_read): + def read_last_reg(self, bytes_to_read, print_on = DEBUG): bus = smbus.SMBus(self.bus_nr) rd_value = [] ret_value = '' @@ -60,10 +65,15 @@ class I2C: ret_ack = 1 if SLOW: sleep(0.2) - except IOError, err: + except IOError: + ret_ack = 0 + rd_value.append(0) + if print_on: + print(f"Reading IOerror {rd_value}") + except err: ret_ack = 0 rd_value.append(0) - if DEBUG: + if print_on: print("Reading error") for cnt in range(bytes_to_read): ret_value += (hex(rd_value[cnt])[2:]) @@ -71,16 +81,23 @@ class I2C: def write_bytes(self, register, data): bus = smbus.SMBus(self.bus_nr) + if type(data) is not list: + data = [data] try: - bus.write_i2c_block_data(self.I2C_Address, register, [data]) + bus.write_i2c_block_data(self.I2C_Address, register, data) ret_ack = 1 if SLOW: sleep(0.3) - except IOError, err: + except IOError: ret_ack = 0 ret_value = 0 if DEBUG: print("Write error") + except err: + ret_ack = 0 + ret_value = 0 + if DEBUG: + print("Write IO-error") return ret_ack def write_register(self, register): @@ -90,11 +107,16 @@ class I2C: ret_ack = 1 if SLOW: sleep(0.3) - except IOError, err: + except IOError: ret_ack = 0 ret_value = 0 if DEBUG: print("Write error") + except err: + ret_ack = 0 + ret_value = 0 + if DEBUG: + print("Write IO-error") return ret_ack def write_pointer(self, register): @@ -104,11 +126,16 @@ class I2C: ret_ack = 1 if SLOW: sleep(0.3) - except IOError, err: + except IOError: ret_ack = 0 ret_value = 0 if DEBUG: print("Write error") + except err: + ret_ack = 0 + ret_value = 0 + if DEBUG: + print("Write IO-error") return ret_ack def ack_check(self): @@ -119,11 +146,16 @@ class I2C: ret_ack = 1 if SLOW: sleep(0.3) - except IOError, err: + except IOError: ret_ack = 0 ret_value = 0 if DEBUG: print("No ACK") + except err: + ret_ack = 0 + ret_value = 0 + if DEBUG: + print("No ACK IO-Error") return ret_ack if __name__ == "__main__": diff --git a/I2C_serial_pi2.py b/I2C_serial_pi2.py index 5ae5af3976e9d28807f0eaa4b0f894a72ff1b8ec..4da3bf406cf0a3e554eeb2dc3232ff5b96b915d3 100644 --- a/I2C_serial_pi2.py +++ b/I2C_serial_pi2.py @@ -97,7 +97,8 @@ class I2C: def write_pointer(self, register): bus = pi.i2c_open(self.bus_nr, self.I2C_Address) try: - pi.i2c_read_device(bus, 1) +# pi.i2c_read_device(bus, 1) + pi.i2c_read_device(bus, register) ret_ack = 1 if SLOW: sleep(0.3) @@ -109,6 +110,20 @@ class I2C: pi.i2c_close(bus) return ret_ack + def write_register(self, register): + bus = pi.i2c_open(self.bus_nr, self.I2C_Address) + try: + ret_value = pi.i2c_write_device(bus, [register]) + ret_ack = 1 + if SLOW: + sleep(0.3) + except IOError: + ret_ack = 0 + ret_value = 0 + if DEBUG: + print("No ACK") + return ret_ack + def ack_check(self): bus = smbus.SMBus(self.bus_nr) try: diff --git a/I2C_serial_pi3.py b/I2C_serial_pi3.py new file mode 100644 index 0000000000000000000000000000000000000000..adfa428918bf52094dd9ea55c851782bc7ad8668 --- /dev/null +++ b/I2C_serial_pi3.py @@ -0,0 +1,146 @@ +''' +Copyright 2021 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. + +I2C_serial_Pi +Started by Gijs + +Class for using the I2C bus of the I2C. This class is used for the +basic I2C scripts to read and write the RCU2, PCC etc. + +''' +import smbus2 +import sys +from time import * + +DEBUG = False #True +SLOW = False + + +class I2C: + + def __init__(self, ADDRESS='040',BUSNR=3): + self.I2C_Address = ADDRESS + self.bus_nr = BUSNR + + def close(self): + bus = smbus2.SMBus(self.bus_nr) + bus.close() + return True + + def open(self): + bus = smbus2.SMBus(self.bus_nr) + bus.open(self.bus_nr) + return True + + def read_bytes(self, register, bytes_to_read=2): + bus = smbus2.SMBus(self.bus_nr) + try: + rd_value = bus.read_i2c_block_data(self.I2C_Address, register, bytes_to_read) + ret_value = '' + for cnt in range(bytes_to_read): + ret_value += (hex(rd_value[cnt])[2:]) + ret_ack = 1 + if SLOW: + sleep(0.2) + except IOError: + ret_ack = 0 + ret_value = 'ffff' + if DEBUG: + print("Reading IO-error") + return ret_ack, ret_value + + + def read_last_reg(self, bytes_to_read): + bus = smbus2.SMBus(self.bus_nr) + rd_value = [] + ret_value = '' + for cnt in range(bytes_to_read): + try: + rd_value.append(bus.read_byte(self.I2C_Address)) + ret_ack = 1 + if SLOW: + sleep(0.2) + except IOError: + ret_ack = 0 + rd_value.append(0) + if DEBUG: + print("IO-Reading error") + for cnt in range(bytes_to_read): + ret_value += (hex(rd_value[cnt])[2:]) + return ret_ack,ret_value + + def write_bytes(self, register, data): + bus = smbus2.SMBus(self.bus_nr) + if type(data) is not list: + data = [data] + try: + bus.write_i2c_block_data(self.I2C_Address, register, data) + ret_ack = 1 + if SLOW: + sleep(0.3) + except IOError: + ret_ack = 0 + ret_value = 0 + if DEBUG: + print("Write IO-error") + return ret_ack + + def write_register(self, register): + bus = smbus2.SMBus(self.bus_nr) + try: + bus.write_byte(self.I2C_Address, register) +# print(f"Wrote {register:x}") + ret_ack = 1 + if SLOW: + sleep(0.3) + except IOError: + ret_ack = 0 + ret_value = 0 +# if DEBUG: + print("Write IO-error") + return ret_ack + + def write_pointer(self, register): + bus = smbus2.SMBus(self.bus_nr) + try: + ret_value = bus.read_i2c_block_data(self.I2C_Address, register, 1) + ret_ack = 1 + if SLOW: + sleep(0.3) + except IOError: + ret_ack = 0 + ret_value = 0 + if DEBUG: + print("Write IO-error") + return ret_ack + + def ack_check(self): + bus = smbus2.SMBus(self.bus_nr) + try: + print("check ACK") + ret_value = bus.write_quick(self.I2C_Address) + ret_ack = 1 + if SLOW: + sleep(0.3) + except IOError: + ret_ack = 0 + ret_value = 0 + if DEBUG: + print("No ACK IO-Error") + return ret_ack + +if __name__ == "__main__": + I2C_Device = I2C(0x40) + I2C_Device.write_bytes(0x00, 0x00) + ret_ack, ret_value = I2C_Device.read_bytes(0x8C, 2) + print(ret_value) diff --git a/apsct_lib.py b/apsct_lib.py new file mode 100644 index 0000000000000000000000000000000000000000..905bd98d3d0f75ac0d57d79da8f1fa5e2c288e6d --- /dev/null +++ b/apsct_lib.py @@ -0,0 +1,605 @@ +""" +Copyright 2021 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. + +Set APSCT_CLK + +""" +import sys +import APSCT_I2C +import time +import RPi.GPIO as gpio +import random +sys.path.insert(0, '.') +import os +if os.name == "posix": + from I2C_serial_pi2 import * +else: + from I2C_serial import * + +I2CBUSNR = 5 +sleep_time = 0.15 +DEBUG = False + + +class ApsctClass: + # + # Toplevel class that contains all controllable parts of the APSCT + # + def __init__(self, frequency="200MHz"): + self.status = False + self.frequency = frequency + self.eeprom = EepromClass() + self.pll_200 = PllClass("200MHz") + self.pll_160 = PllClass("160MHz") + self.sensors = ApsctSensors() + self.pps = PpsClass() + self.i2cswitch = [] + for addr in APSCT_I2C.i2c_switch_addr_rcu: + self.i2cswitch.append(I2cSwitch(address=addr, bus=APSCT_I2C.i2c_bus_rcu)) + for addr in APSCT_I2C.i2c_switch_addr_unb: + self.i2cswitch.append(I2cSwitch(address=addr, bus=APSCT_I2C.i2c_bus_unb)) + self.apsct_id = ApsctId() + + def read_io_expanderis(self): + # + # Read both IO-Expander lines and prints status on the screen + # + i2c_addr = [0x20, 0x21] + for addr in i2c_addr: + i2c_device = I2C(addr, BUSNR=I2CBUSNR) # clock selection + for reg_cnt in range(8): + ack, ret_value = i2c_device.read_bytes(reg_cnt, 2) + stri = "Expander : 0x{:0>2x}, Reg 0x{:0>2x}, value 0x{}{}".format(addr, reg_cnt, ret_value[0], + ret_value[1]) # [start+2:start]) + print(stri) + + def power(self, state): + # + # Set power supply APSCT in the give state + # + # state is True: Power on + # state is False: Power off + # + stri = "Power to {}".format(state) + print(stri) + i2c_io_device_a = I2C(0x20, BUSNR=I2CBUSNR) + i2c_io_device_a.write_bytes(0x06, 0x2C) # '0' is output, '1' is input + i2c_io_device_a.write_bytes(0x07, 0x00) # '0' is output, '1' is input + i2c_io_device_b = I2C(0x21, BUSNR=I2CBUSNR) + i2c_io_device_b.write_bytes(0x06, 0x2C) # '0' is output, '1' is input + if state: + bits_to_set_a1 = 0x02 | (1 << APSCT_I2C.CS) | (0 << APSCT_I2C.SCLK) | (0 << APSCT_I2C.SDI) + bits_to_set_a2 = 0x04 + bits_to_set_b1 = 0x02 | (1 << APSCT_I2C.CS) | (0 << APSCT_I2C.SCLK) | (0 << APSCT_I2C.SDI) + else: + bits_to_set_a1 = 0x00 | (1 << APSCT_I2C.CS) | (0 << APSCT_I2C.SCLK) | (0 << APSCT_I2C.SDI) + bits_to_set_a2 = 0x00 + bits_to_set_b1 = 0x00 | (1 << APSCT_I2C.CS) | (0 << APSCT_I2C.SCLK) | (0 << APSCT_I2C.SDI) + if DEBUG: + stri = "Bits to reg 0 0x{0:x}".format(bits_to_set_a1) + print(stri) + i2c_io_device_a.write_bytes(0x02, bits_to_set_a1) + i2c_io_device_a.write_bytes(0x03, bits_to_set_a2) + i2c_io_device_b.write_bytes(0x02, bits_to_set_b1) + + def set_apsct(self): + # + # set APSCT to 200MHz, 160MHz or off + # + if self.frequency == "200MHz": + self.power(True) + self.pll_200.setup_pll() + elif self.frequency == "160MHz": + self.power(True) + self.pll_160.setup_pll() + else: + self.power(False) + + def check_apsct(self): + # + # Check voltages, temperature and PLL-lock on APSCT + # + # Return Result, True when OK, False in case of an error + result = self.sensors.check_values() + result = result & self.pps.check_timing() + result = result & self.apsct_id.check_id() + for i2c_switch in self.i2cswitch: + result = result & i2c_switch.check_switch(data=random.randint(0, 2**8)) + if self.frequency == "200MHz": + self.pll_200.read_lock() + lock = self.pll_200.lock + result = result & lock + elif self.frequency == "160MHz": + self.pll_160.read_lock() + lock = self.pll_160.lock + result = result & lock + else: + result = result + return result + + +class PllClass: + # + # Toplevel class that contains all function to set the PLL + # + def __init__(self, frequency="200MHz"): + self.status = False + self.lock = False + self.frequency = frequency + if self.frequency == "160MHz": + self.i2c_address = APSCT_I2C.PLL_160M + else: + self.i2c_address = APSCT_I2C.PLL_200M + self.dev_i2c_pll = I2C(self.i2c_address, BUSNR=I2CBUSNR) + + def write_byte_pll(self, reg_address, wr_data): + # + # Write Byte to the PLL + # + pll_rw = 0x00 # 0 for write, 1 for read + if DEBUG: + stri = "Write to address : 0x{1:{fill}2x} value 0x{0:{fill}2x}".format(wr_data, reg_address, fill='0') + print(stri) + self.dev_i2c_pll.write_bytes(0x06, 0x2C) + rd_bytes = self.dev_i2c_pll.read_bytes(0x06, 1) + if DEBUG: + stri = "IO expander wrote 0x{0:x}, read 0x{1}".format(0x2C, rd_bytes[1]) + print(stri) + data = (reg_address << 9) + (pll_rw << 8) + wr_data + bit_array = "{0:{fill}16b}".format(data, fill='0') + self.dev_i2c_pll.write_bytes(0x02, 0x02 | (0x1 << APSCT_I2C.CS)) + for bit in bit_array: + for clk in range(2): + write_data = 0x02 | (0 << APSCT_I2C.CS) | (clk << APSCT_I2C.SCLK) | (int(bit) << APSCT_I2C.SDI) + self.dev_i2c_pll.write_bytes(0x02, write_data) + for clk in range(2): + write_data = 0x02 | (0 << APSCT_I2C.CS) | (clk << APSCT_I2C.SCLK) + self.dev_i2c_pll.write_bytes(0x02, write_data) + for clk in range(2): + write_data = 0x02 | (1 << APSCT_I2C.CS) | (clk << APSCT_I2C.SCLK) + self.dev_i2c_pll.write_bytes(0x02, write_data) + + write_data = 0x02 | (1 << APSCT_I2C.CS) | (0 << APSCT_I2C.SCLK) | (0 << APSCT_I2C.SDI) + self.dev_i2c_pll.write_bytes(0x02, write_data) + + def read_byte_pll(self, reg_address, nof_bytes=1): + # + # Read byte from the PLL + # + pll_rw = 0x01 # 0 for write, 1 for read + self.dev_i2c_pll.write_bytes(0x06, 0x2C) + data = (reg_address << 7) + pll_rw + bit_array = "{0:{fill}8b}".format(data, fill='0') + for bit in bit_array: + for clk in range(2): + write_data = 0x02 | (0 << APSCT_I2C.CS) | (clk << APSCT_I2C.SCLK) | (int(bit) << APSCT_I2C.SDI) + self.dev_i2c_pll.write_bytes(0x02, write_data) + sleep(sleep_time) + read_bit = '' + for cnt in range(8*nof_bytes): + for clk in [0, 1]: # Read after rizing edge + write_data = 0x02 | (clk << APSCT_I2C.SCLK) | (int(bit_array[-1]) << APSCT_I2C.SDI) + self.dev_i2c_pll.write_bytes(0x02, write_data) + ret_ack, ret_value = self.dev_i2c_pll.read_bytes(0x00, 1) + if ret_ack: + read_bit += str((int(ret_value, 16) >> APSCT_I2C.SDO) & 0x01) + else: + print("ACK nok") + write_data = 0x02 | (1 << APSCT_I2C.CS) | (0 << APSCT_I2C.SCLK) | (0 << APSCT_I2C.SDI) + self.dev_i2c_pll.write_bytes(0x02, write_data) + if DEBUG: + stri = "Read back at address 0x{0:{fill}2x} result : 0x{1:{fill}2x} ".format(reg_address, + int(read_bit, 2), fill='0') + print(stri) + return read_bit + + def setup_pll(self): + # + # Set registers on the PLL + # + print(f"Setup PPL {self.frequency}") + self.dev_i2c_pll.write_bytes(0x07, 0x00) + if self.frequency == '160MHz': + i2c_address = APSCT_I2C.PLL_200M + dev_i2c_pll_sel = I2C(i2c_address, BUSNR=I2CBUSNR) + dev_i2c_pll_sel.write_bytes(0x03, 0x08) + else: + self.dev_i2c_pll.write_bytes(0x03, 0x28) + self.write_byte_pll(0x03, 0x0C) + sleep(0.5) + self.write_byte_pll(0x03, 0x08) + self.write_byte_pll(0x03, 0x08) + self.write_byte_pll(0x04, 0xCF) # CF disable not used outputs, 00 enable all + self.write_byte_pll(0x05, 0x97) + self.write_byte_pll(0x06, 0x10) # cp inv = 0xF4 other 0xE4 + self.write_byte_pll(0x07, 0x04) # Divider R = 1 dec + self.write_byte_pll(0x08, 0x01) + self.write_byte_pll(0x07, 0x00) + self.write_byte_pll(0x09, 0x10) # reset + if self.frequency == '160MHz': + self.write_byte_pll(0x0A, 0x10) + else: + self.write_byte_pll(0x0A, 0x14) + self.write_byte_pll(0x09, 0x00) + self.write_byte_pll(0x0C, 0x8F) + self.write_byte_pll(0x0D, 0x88) # Dig CLK = 200/1 = 200 MHz + self.write_byte_pll(0x0F, 0x08) # RCU CLK = 200/1 = 200 MHz + self.write_byte_pll(0x11, 0x08) # PPS ref CLK = 200/1 = 200 MHz + self.write_byte_pll(0x13, 0x88) # T.P. CLK = 200/1 = 200 MHz + + def read_all_regs_pll(self): + # + # Read all registers on the PLL and print on screen + # + self.dev_i2c_pll.write_bytes(0x07, 0x00) + bytes_to_read = 24 + ret_value = self.read_byte_pll(0, nof_bytes=bytes_to_read) + for cnt in range(bytes_to_read): + start = cnt*8 + stri = "Reg nr 0x{:0>2x} value: 0x{:0>2x}".format(cnt, int(ret_value[start:start+8], 2)) + print(stri) + + def read_lock(self, print_on=True): + # + # Read lock status + # + ret_value = self.read_byte_pll(0x00, nof_bytes=1) + status_pll = int(ret_value, 2) + if status_pll == 0x04: + self.lock = True + stri = f"PLL {self.frequency} is in lock" + elif (status_pll & 0x10) > 0: + self.lock = False + stri = f"PLL {self.frequency} Not Locked --> No 10 MHz ref" + else: + self.lock = False + stri = f"PLL {self.frequency} Not locked --> PLL Error" + if print_on: + print(stri) + return self.lock + + def read_lol(self): + # + # Read loss of lock status + # + i2_c_io_device_a = I2C(0x20, BUSNR=I2CBUSNR) + i2_c_io_device_a.write_bytes(0x06, 0x2C) # '0' is output + i2_c_io_device_a.write_bytes(0x07, 0x00) # '0' is output + i2_c_io_device_b = I2C(0x21, BUSNR=I2CBUSNR) + i2_c_io_device_b.write_bytes(0x06, 0x2C) # '0' is output + i2_c_io_device_b.write_bytes(0x07, 0xFF) # '0' is output + + ack, ret_value = i2_c_io_device_b.read_bytes(0x01, 1) + status_reg = int(ret_value, 16) + if (self.frequency == '200MHz') & ((status_reg & 0x10) > 0): + print("200MHz has lost lock") + if ((status_reg & 0x20) > 0) & (self.frequency == '160MHz'): + print("160MHz has last lock") + ack, ret_value = i2_c_io_device_a.read_bytes(0x01, 1) + old_reg = int(ret_value, 16) + i2_c_io_device_a.write_bytes(0x03, (old_reg | 0x10)) # '0' is output + sleep(1) + i2_c_io_device_a.write_bytes(0x03, (old_reg & 0xEF)) # '0' is output + + +class EepromClass: + # + # Class to handle EEPROM communication + # + def __init__(self): + self.dev_i2c_eeprom = I2C(APSCT_I2C.EEPROM) + self.dev_i2c_eeprom.bus_nr = I2CBUSNR + + def write_eeprom(self, data="APSCT", address=0): + # + # 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") + return False + else: + wr_data = bytearray(data.encode("utf-8", errors="ignore")) + for loc, data_byte in enumerate(wr_data): + self.dev_i2c_eeprom.write_bytes(address + loc, data_byte) + sleep(0.1) + return True + + def read_eeprom(self, address=0, nof_bytes=5): + # + # 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") + return False + else: + ret_ack, ret_value = self.dev_i2c_eeprom.read_bytes(address, nof_bytes) + ret_value = bytes.fromhex(ret_value[:nof_bytes * 2]) + str_return = ret_value.decode('UTF-8') + return str_return + + def wr_rd_eeprom(self, value="APSCT-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)) + print(ret_value) + stri = "Wrote to EEPROM register 0x{2:x} : {0}, Read from EEPROM: {1}".format(value, ret_value, address) + print(stri) + if ret_value == value: + return True + else: + return False + + +class ApsctSensors: + # + # All monitor. read and write functions for the voltage sensors on APSCT + # + def __init__(self): + # + # All monitor. read and write functions for the EEPROM + # + self.dev_i2c_sensor = I2C(0x74) + self.dev_i2c_sensor.bus_nr = I2CBUSNR + self.power_supplies = list(APSCT_I2C.PWR_LOCATIONS.keys()) + self.voltages = {} + self.temperature = 9999 + self.dev_i2c_sensor.write_bytes(0xB0, 0xB8) + + def apsct_sensors(self): + # + # read all 7 power sens lines + # + # Return True when done + # + for sens_line in range(7): + self.read_voltage(sens_line) + self.read_temp() + return True + + def read_all_voltages(self): + # + # Function to read and process one sensline + # + # Return True when done + # + # To remove errors, repeat measurement when returned voltage is < 3 V + # + for pwr in self.power_supplies: + self.voltages[pwr] = self.read_voltage(APSCT_I2C.PWR_LOCATIONS[pwr]) + if self.voltages[pwr] < 3: + self.voltages[pwr] = self.read_voltage(APSCT_I2C.PWR_LOCATIONS[pwr]) + return True + + def check_values(self): + # + # Function to check sensor values (voltages and temperature) + # + # return result, True when OK, False in case of error + # + print("Check power sensor values") + result = True + self.read_all_voltages() + self.read_temp() + for pwr in self.power_supplies: + expected = APSCT_I2C.PWR_VOUT[pwr] + if not (0.9*expected < self.voltages[pwr] < 1.1*expected): + result = False + print(f"Error: {pwr: <9} expected: {expected} V read: {self.voltages[pwr]:4.2f} V") + else: + print(f"OK : {pwr: <9} expected: {expected} V read: {self.voltages[pwr]:4.2f} V") + if not (15 < self.temperature < 50): + result = False + print(f"Error temperature read {self.temperature:4.2f} °C") + else: + print(f"OK temperature read {self.temperature:4.2f} °C") + return result + + def read_voltage(self, input_channel=0): + # + # Function to read a voltages of APSCT + # + # input_channel = sens port + # return value + # + voltage = 9999 + vref = 3.0 + one_step = vref/(2**16) + channel_select_word = 0xB0 | ((input_channel % 2) << 3) | ((input_channel >> 1) & 0x7) + if DEBUG: + stri = "Word to select sens input is 0x{0:x}".format(channel_select_word) + print(stri) + self.dev_i2c_sensor.write_bytes(channel_select_word, 0xB8) + sleep(0.3) # Wait for device to take snapshot + ret_ack, ret_value = self.dev_i2c_sensor.read_last_reg(3) + if DEBUG: + stri = "Return value input 0 : 0x{0} ".format(ret_value) + print(stri) + if int(ret_value, 16) >= 0xC00000: + print("over range") + else: + steps = (int(ret_value, 16) & 0x1FFFFF) >> 6 + voltage = one_step * steps + voltage = ((4.7+2.2)/2.2)*2*voltage # Resistor network + half swing + if DEBUG: + string = "Voltage sens line {1} is {0:.4f} V".format(voltage, input_channel) + print(string) + sleep(0.2) # wait for device to go to sleep + return voltage + + def read_temp(self): + # + # Function to read temperature of APSCT + # + # return value + # + vref = 3.0 + temp_slope = 93.5E-6 * 2**(16+1) / vref + ret_ack = self.dev_i2c_sensor.write_bytes(0xA0, 0xC0) + if not ret_ack: + self.temperature = 9999 + return self.temperature + sleep(0.5) + self.temperature = 9999 + loops = 0 + while (self.temperature > 100) & (loops < 2): + loops = loops + 1 + ret_ack, ret_value = self.dev_i2c_sensor.read_last_reg(3) + if ret_ack: + raw_value = (int(ret_value, 16) & 0x1FFFFF) >> 6 + temperature_k = (raw_value/temp_slope) + self.temperature = temperature_k-273 + else: + self.temperature = 9999 + sleep(0.2) + return self.temperature + + +class PpsClass: + # + # Class to check the PPS signal on GPIO24 + # + def __init__(self): + gpio.setmode(gpio.BCM) + gpio.setup(24, gpio.IN) + self.pps_time = 999 + self.timing = False + + def time_pps(self): + # + # measure time between rising edges on (0.5Hz) PPS input + # + gpio.wait_for_edge(24, gpio.RISING) + a = time() + gpio.wait_for_edge(24, gpio.RISING) + b = time() + self.pps_time = (b-a)/2 + return self.pps_time + + def check_pps(self): + # + # Check is signal toggles PPS input within 5 second period + # + gpio.add_event_detect(24, gpio.RISING) + sleep(5) + pps_ok = gpio.event_detected(24) + gpio.remove_event_detect(24) + return pps_ok + + def print_pps(self): + # + # Print PPS period time on screen + # + if self.check_pps(): + print(f"Time between pps is {self.time_pps():4.2f} s") + else: + print("No PPS found") + + def check_timing(self): + # + # Check is time is 1 second +/- 10% + # + print("Check pps timing", end="") + self.timing = False + if not self.check_pps(): + print(" no pps") + return False + timepps = self.time_pps() + if 0.9 < timepps < 1.1: + print(f", timing is OK: {timepps:4.2f} s") + self.timing = True + return self.timing + + +class I2cSwitch: + # + # Class to check a I2C-switch to RCU's and UNB2c's + # + def __init__(self, address=0x70, bus=1): + self.address = address # Address of the switch + self.bus = bus # I2C bus number on the Po + self.dev_i2c_switch = I2C(address) # I2C software device + self.dev_i2c_switch.bus_nr = bus + + def check_switch(self, data=0xa5): + # + # Check if you can write and read from the buffer + # + print(f"Check I2C switch at 0x{self.address:x} bus {self.bus}", end=' ') + ret_ack, ret_value = self.dev_i2c_switch.read_bytes(0) + if ret_ack < 1: + print("I2C-Switch not found") + return False + else: + self.dev_i2c_switch.write_bytes(0, data) + ret_ack, ret_value = self.dev_i2c_switch.read_last_reg(1) + read_value_int = int(ret_value, 16) + if read_value_int == data: + print(f"OK wrote 0x{data:02X} read back 0x{read_value_int:02X}") + return True + else: + print(f"ERROR wrote 0x{data:02X} read back 0x{read_value_int:02X}") + return False + + +class ApsctId: + # + # Class to check ID pins + # + def __init__(self): + # + # APSCT Backplane ID + # + self.id = 9999 # placeholder for APSCT-ID + gpio.setmode(gpio.BCM) # Set IO pins used for the APSCT-ID + for pin in APSCT_I2C.ID_PINS: + gpio.setup(pin, gpio.IN) + + def read_id(self): + # + # Function to read the APSCT-ID from the backplane + # + loc_id = 0 + for pin in APSCT_I2C.ID_PINS: + loc_id = loc_id * 2 + bit = gpio.input(pin) + loc_id = loc_id + bit + self.id = loc_id + return self.id + + def check_id(self): + # + # Function Check the ID. + # + self.read_id() + if self.id == 63: + print(f"OK : Back ID is 0x{self.id:02X}") + return True + else: + print(f"ERROR : Back ID is 0x{self.id:02X} expected 0x{63:2X}") + return False diff --git a/apspu_lib.py b/apspu_lib.py index 809a31b9820c37ea1367a72f6633eed1d113017e..be387ca6acb956d38e17198ba0911ad2e271b97a 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 * + from I2C_serial_pi3 import * else: from I2C_serial import * - -I2CBUSNR = 1 -DEBUG = False +I2CBUSNR = 1 # Bus used on the Pi +DEBUG = False # Set True to print debug information on the screen +I_OK_MARGIN = 0.5 # I_error in Amps 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,24 +65,41 @@ 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]) + vout = pol.read_vout_set() + if not (0.9*VOUT_POLS[pol.name] < vout < 1.1*VOUT_POLS[pol.name]): + print(f"POL {pol.name:10} Error setting Vout, " + f"set to {VOUT_POLS[pol.name]} read back {vout}") + exit() pol.set_vout_ov_limit_pol(1.2*VOUT_POLS[pol.name]) + ov_out = pol.read_ov_limit() + if not (1.1*VOUT_POLS[pol.name] < ov_out < 1.3*VOUT_POLS[pol.name]): + print(f"POL {pol.name:10} Error setting output overvoltage" + f"set {1.2*VOUT_POLS[pol.name]} read back {ov_out}") + exit() pol.set_on_off_config() - if DEBUG: - pol.read_vout_set() - pol.read_ov_limit() - pol.read_uv_limit() + pol.on_off(True) pol.write_to_nvm() 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: check_ok = check_ok & pol.check_pol() check_ok = check_ok & self.fans.check_fans() + check_ok = check_ok & self.eeprom.wr_rd_eeprom(value="PROD_CHECK", address=0x30) if check_ok: print("APSPU OK") else: @@ -89,6 +107,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 +120,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 +137,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 +156,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,15 +169,22 @@ 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) print(stri) - return True + if ret_value == value: + return True + else: + return False class PolClass: @@ -170,17 +210,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,57 +233,91 @@ 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: + ret_ack = self.pol_dev.write_bytes(LP_WRITE_PROTECT, [0]) 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): - ret_ack = self.pol_dev.write_bytes(LP_STORE_USER_ALL, 0) + # + # Function to write the POL's registers to NVM memory + # + # return is always True + # + print(f"Store to NVM for POL {self.name}") + if False: + ret_ack = self.pol_dev.write_register(0x15) + sleep(1) + else: + self.pol_dev.close() + command = f"i2cset -y 1 0x{CTR_POLS[self.name]:02X} 0x15 cp" + os.system(command) + os.system(command) + self.pol_dev.open() return True def read_vin(self): @@ -252,13 +326,17 @@ class PolClass: # if self.status: ret_ack, raw_value = self.pol_dev.read_bytes(LP_VIN, 2) - if len(raw_value)<4: + if not ret_ack: + self.iout=999 + return False + 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 + return True def read_vout(self): # @@ -266,55 +344,71 @@ class PolClass: # if self.status: ret_ack, vout_mod = self.pol_dev.read_bytes(LP_VOUT_MODE, 1) + if not ret_ack: + self.vout=999 + return False ret_ack, raw_value = self.pol_dev.read_bytes(LP_VOUT, 2) + if not ret_ack: + self.vout=999 + return False vout_mod = int(vout_mod, 16) ret_value = [] ret_value.append(int(raw_value[:2], 16)) 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]) + return True else: self.vout = 999 + return False 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}") + if DEBUG: + 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}") + if DEBUG: + 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 +440,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 @@ -372,15 +471,15 @@ class PolClass: check_ok = False print(f"POL {self.name:10} TEMP not OK, expected {temp_low} C - {temp_high} C, measured {self.temp} C ") return check_ok - i_low = 0.75*IOUT_POLS[self.name] - i_high = 1.25*IOUT_POLS[self.name] + i_low = (1-I_OK_MARGIN)*IOUT_POLS[self.name] + i_high = (1+I_OK_MARGIN)*IOUT_POLS[self.name] if i_low < self.iout < i_high: 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 +494,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 +526,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 +537,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 +562,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_apsct.py b/production_apsct.py new file mode 100644 index 0000000000000000000000000000000000000000..a55d7dee8a724aac5c2e082e8064f6bf59873a7f --- /dev/null +++ b/production_apsct.py @@ -0,0 +1,61 @@ +""" +Copyright 2021 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. + +Check APSCT_CLK + +""" +import apsct_lib +import sys + +READ_ALL = False # True + +CLK_FREQ = '200MHz' + +if len(sys.argv) < 2: + print("Production_apsct.py <ASTRON NR> <Serial number>") + print("e.g. python3 production_apsct.py 2023-01 1234") + exit() + +apsct = apsct_lib.ApsctClass(CLK_FREQ) +state = True +modi = ["200MHz", "160MHz", "OFF"] +for mode in modi: + print(f"Check APSCT in {mode} mode") + apsct.frequency = mode + apsct.set_apsct() + if mode == "200MHz": + state = state & apsct.pll_200.read_lock() + if mode == "160MHz": + state = state & apsct.pll_160.read_lock() + +apsct.frequency = "200MHz" +apsct.set_apsct() +apsct.pll_200.read_lock() +apsct.sensors.apsct_sensors() +state = state & apsct.check_apsct() + +if READ_ALL: + apsct.pll_200.read_all_regs_pll() + apsct.pll_160.read_all_regs_pll() + apsct.read_io_expanderis() + +if state: + apsct_id = "APSCT-" + sys.argv[1] + serial = sys.argv[2] + rw_ok = apsct.eeprom.wr_rd_eeprom(apsct_id, address=0) + if rw_ok: + rw_ok = apsct.eeprom.wr_rd_eeprom(serial, address=0x20) + if not rw_ok: + print("EEPROM Error") +else: + print("\n >>> Errors during testing <<<\n") diff --git a/production_apspu.py b/production_apspu.py index 481bb2fcb4346057914cb190c63e9b4d948adb3f..6e4431caa69593e92d394075884a9ca7324746f0 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,40 +12,36 @@ 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(): - apspu.apspu_on_off(False) - sleep(10) - 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(serial, address=0x20) + rw_ok = apspu.eeprom.wr_rd_eeprom(apspu_id, address=0) + if rw_ok: + rw_ok = apspu.eeprom.wr_rd_eeprom(serial, address=0x20) + if not rw_ok: + print("EEPROM Error")