diff --git a/APSCT_I2C.py b/APSCT_I2C.py new file mode 100644 index 0000000000000000000000000000000000000000..3ac297b281d38a6aa7f8264db1e8b5d534e3e736 --- /dev/null +++ b/APSCT_I2C.py @@ -0,0 +1,61 @@ +""" +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 diff --git a/apsct_lib.py b/apsct_lib.py index a3778865d00607db5074a92427a2d607dc78eaeb..6eb00df0a25cc44530364096bd17f9ccd52b5eca 100644 --- a/apsct_lib.py +++ b/apsct_lib.py @@ -15,6 +15,7 @@ Set APSCT_CLK """ import sys +import APSCT_I2C import time sys.path.insert(0, '.') import os @@ -26,26 +27,17 @@ else: I2CBUSNR = 5 sleep_time = 0.15 -EEPROM = 0x50 -PLL_200M = 0x20 -PLL_160M = 0x21 - -CS = 6 -SCLK = 4 -SDO = 5 -SDI = 7 - - class ApsctClass: # # Toplevel class that contrains all parts of the APSCT # - def __init__(self): + def __init__(self, frequency="200MHz"): self.status = False + self.frequency = frequency self.eeprom = EepromClass() - self.pll_200 = PllClass(PLL_200M) - self.pll_160 = PllClass(PLL_160M) + self.pll_200 = PllClass("200MHz") + self.pll_160 = PllClass("160MHz") self.sensors = ApsctSensors() def read_IO_expanderis(self): @@ -76,13 +68,13 @@ class ApsctClass: I2C_IO_device_B = I2C(0x21, BUSNR=I2CBUSNR) I2C_IO_device_B.write_bytes(0x06, 0x2C) # '0' is output if state: - bits_to_set_A1 = 0x02 | (1 << CS) | (0 << SCLK) | (0 << SDI) + 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 << CS) | (0 << SCLK) | (0 << SDI) + bits_to_set_B1 = 0x02 | (1 << APSCT_I2C.CS) | (0 << APSCT_I2C.SCLK) | (0 << APSCT_I2C.SDI) else: - bits_to_set_A1 = 0x00 | (1 << CS) | (0 << SCLK) | (0 << SDI) + 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 << CS) | (0 << SCLK) | (0 << SDI) + 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) @@ -90,32 +82,47 @@ class ApsctClass: 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, state): + def set_apsct(self): # # set APSCT to 200MHz, 160MHz or off # - if state == "200MHz": + if self.frequency == "200MHz": self.power(True) self.pll_200.setup_pll() - elif state == "160MHz": + elif self.frequency == "160MHz": self.power(True) self.pll_160.setup_pll() else: self.power(False) + def check_apsct(self): + result = self.sensors.check_values() + 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 contrains all parts of the PLL # - def __init__(self, address=0x20): + def __init__(self, frequency="200MHz"): self.status = False - self.i2c_address = address - self.dev_i2c_pll = I2C(self.i2c_address, BUSNR=I2CBUSNR) - if address == 0x20: - self.frequency = '200MHz' + self.lock = False + self.frequency = frequency + if self.frequency == "160MHz": + self.i2c_address = APSCT_I2C.PLL_160M else: - self.frequency = '160MHz' + 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): # @@ -131,19 +138,19 @@ class PllClass: 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 << CS)) + 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 << CS) | (clk << SCLK) | (int(bit) << SDI) + 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 << CS) | (clk << SCLK) + 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 << CS) | (clk << SCLK) + Write_data = 0x02 | (1 << APSCT_I2C.CS) | (clk << APSCT_I2C.SCLK) self.dev_i2c_pll.write_bytes(0x02, Write_data) - Write_data = 0x02 | (1 << CS) | (0 << SCLK) | (0 << SDI) + 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): @@ -156,24 +163,25 @@ class PllClass: bit_array = "{0:{fill}8b}".format(data, fill='0') for bit in bit_array: for clk in range(2): - Write_data = 0x02 | (0 << CS) | (clk << SCLK) | (int(bit) << SDI) + 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 << SCLK) | (int(bit_array[-1]) << SDI) + 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) >> SDO) & 0x01) + read_bit += str((int(ret_value, 16) >> APSCT_I2C.SDO) & 0x01) else: print("ACK nok") - Write_data = 0x02 | (1 << CS) | (0 << SCLK) | (0 << SDI) + Write_data = 0x02 | (1 << APSCT_I2C.CS) | (0 << APSCT_I2C.SCLK) | (0 << APSCT_I2C.SDI) self.dev_i2c_pll.write_bytes(0x02, Write_data) - 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) + 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): @@ -223,16 +231,22 @@ class PllClass: # # Read lock status # - ret_value = self.Read_byte_PLL(0x00, nof_bytes = 1) - status_pll = int(ret_value,2) + ret_value = self.Read_byte_PLL(0x00, nof_bytes=1) + status_pll = int(ret_value, 2) if status_pll == 0x04: - print("PLL in lock") + self.lock = True + if DEBUG: + print("PLL in lock") elif (status_pll & 0x10) > 0: - print("Not Locked --> No 10 MHz ref") + self.lock = False + if DEBUG: + print("Not Locked --> No 10 MHz ref") else: - print("Not locked --> PLL Error") + self.lock = False + if DEBUG: + print("Not locked --> PLL Error") - def read_lol(self, pll_frequency='200MHz'): + def read_lol(self): # # Read loss of lock status # @@ -245,9 +259,9 @@ class PllClass: ack, ret_value = I2C_IO_device_B.read_bytes(0x01, 1) status_reg = int(ret_value, 16) - if (pll_frequency == '200MHz') & ((status_reg & 0x10) > 0): + if (self.frequency == '200MHz') & ((status_reg & 0x10) > 0): print("200MHz has lost lock") - if ((status_reg & 0x20) > 0) & (pll_frequency == '160MHz'): + if ((status_reg & 0x20) > 0) & (self.frequency == '160MHz'): print("160MHz has last lock") ack, ret_value = I2C_IO_device_A.read_bytes(0x01, 1) old_reg = int(ret_value, 16) @@ -264,7 +278,7 @@ class EepromClass: # # All monitor. read and write functions for the EEPROM # - self.dev_i2c_eeprom = I2C(EEPROM) + self.dev_i2c_eeprom = I2C(APSCT_I2C.EEPROM) self.dev_i2c_eeprom.bus_nr = I2CBUSNR def write_eeprom(self, data="APSPU", address=0): @@ -301,7 +315,6 @@ class EepromClass: else: ret_ack, ret_value = self.dev_i2c_eeprom.read_bytes(address, nof_bytes) ret_value = bytes.fromhex(ret_value[:nof_bytes * 2]) - #print(" ret value = {}".format(ret_value)) str_return = ret_value.decode('UTF-8') return str_return @@ -334,12 +347,39 @@ class ApsctSensors: # 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 def apsct_sensors(self): for sens_line in range(7): self.read_voltage(sens_line) self.read_temp() + def read_all_voltages(self): + for pwr in self.power_supplies: + self.voltages[pwr] = self.read_voltage(APSCT_I2C.PWR_LOCATIONS[pwr]) + return True + + def check_values(self): + # + # Function to check sensor values + # + # return result, True when OK, False in case of error + # + result = True + self.read_all_voltages() + self.read_temp() + for pwr in self.power_supplies: + expected = APSCT_I2C.PWR_VOUT[pwr] + if 0.9*expected > self.voltages[pwr] > 1.1*expected: + result = False + print(f"Error {pwr} expected {expected} V read {self.voltages[pwr]} V") + if 15 > self.temperature > 50: + result = False + print(f"Error temperature read {self.temperature} °C") + return result + def read_voltage(self, input_channel=0): # # Function to read a voltages of APSCT @@ -347,6 +387,7 @@ class ApsctSensors: # 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) @@ -368,6 +409,7 @@ class ApsctSensors: voltage = ((4.7+2.2)/2.2)*2*voltage string = "Voltage sens line {1} is {0:.4f} V".format(voltage, input_channel) print(string) + return voltage def read_temp(self): # @@ -384,9 +426,7 @@ class ApsctSensors: if ret_ack: raw_value = (int(ret_value, 16) & 0x1FFFFF) >> 6 temperature_K = (raw_value/temp_slope) - temperature = temperature_K-273 - stri = "Temperature : {0:.2f} gr. C".format(temperature) - print(stri) + self.temperature = temperature_K-273 else: - print("Error reading tempeature") - + self.temperature = 9999 + return self.temperature diff --git a/production_apsct.py b/production_apsct.py index 10d90a4341227e4d521cfc119ad3b10c694bbe02..97fb0011d2a2dde0a4b86b93db696942ec43843a 100644 --- a/production_apsct.py +++ b/production_apsct.py @@ -11,49 +11,60 @@ 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 +Check APSCT_CLK """ -import time import apsct_lib +import sys -DEBUG = False -SET_PLL = True -READ_LOCK = True READ_ALL = True -CHECK_EEPROM = True -PWR_RST = False # True -READ_SENSOR = True -READ_REGS = False # True CLK_FREQ = '200MHz' -apsct = apsct_lib.ApsctClass() -apsct.sensors.read_temp() +if len(sys.argv) < 2: + print("Production_apsct.py <ASTRON NR> <Serial number>") + print("e.g. python3 production_apsct.py 2023-01 1234") + exit() -if CHECK_EEPROM: - apsct.eeprom.wr_rd_eeprom() +apsct = apsct_lib.ApsctClass(CLK_FREQ) -if PWR_RST: - apsct.power(False) - time.sleep(10) - apsct.power(True) +print*("Check APSCT in 200MHz mode") +apsct.frequency = "200MHz" +apsct.set_apsct() +apsct.pll_200.read_lock() +apsct.pll_200.read_lol() +apsct.pll_160.read_lock() +apsct.pll_160.read_lol() +apsct.sensors.apsct_sensors() -if SET_PLL: - apsct.set_apsct(CLK_FREQ) +print*("Check APSCT in 160MHz mode") +apsct.frequency = "160MHz" +apsct.set_apsct() +apsct.pll_200.read_lock() +apsct.pll_200.read_lol() +apsct.pll_160.read_lock() +apsct.pll_160.read_lol() +apsct.sensors.apsct_sensors() -if READ_LOCK: - apsct.pll_200.read_lock() - -if READ_REGS: - apsct.pll_200.read_all_regs_pll() +print*("Check APSCT in off mode") +apsct.frequency = "OFF" +apsct.set_apsct() +apsct.pll_200.read_lock() +apsct.pll_200.read_lol() +apsct.pll_160.read_lock() +apsct.pll_160.read_lol() +apsct.sensors.apsct_sensors() if READ_ALL: apsct.pll_200.read_all_regs_pll() # apsct.pll_160.read_all_regs_pll() apsct.read_IO_expanderis() -if READ_SENSOR: - apsct.sensors.apsct_sensors() - -apsct.pll_200.read_lol(CLK_FREQ) +if apsct.check_apsct(): + 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") \ No newline at end of file