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/I2C_serial_pi.py b/I2C_serial_pi.py index 010041ab1d7d95894213dc9cf797329b2bf97eeb..d4cc4cccb0550528e753cc89ef9c46d8d9fa1f1b 100644 --- a/I2C_serial_pi.py +++ b/I2C_serial_pi.py @@ -43,6 +43,11 @@ class I2C: if SLOW: sleep(0.2) except IOError: + ret_ack = 0 + ret_value = 'ffff' + if DEBUG: + print("Reading error") + except err: ret_ack = 0 ret_value = 'ffff' if DEBUG: @@ -50,7 +55,7 @@ class I2C: 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 = '' @@ -63,8 +68,13 @@ class I2C: except IOError: ret_ack = 0 rd_value.append(0) - if DEBUG: - print("IO-Reading error") + if print_on: + print(f"Reading IOerror {rd_value}") + except err: + ret_ack = 0 + rd_value.append(0) + if print_on: + print("Reading error") for cnt in range(bytes_to_read): ret_value += (hex(rd_value[cnt])[2:]) return ret_ack,ret_value @@ -79,6 +89,11 @@ class I2C: if SLOW: sleep(0.3) except IOError: + ret_ack = 0 + ret_value = 0 + if DEBUG: + print("Write error") + except err: ret_ack = 0 ret_value = 0 if DEBUG: @@ -93,6 +108,11 @@ class I2C: if SLOW: sleep(0.3) except IOError: + ret_ack = 0 + ret_value = 0 + if DEBUG: + print("Write error") + except err: ret_ack = 0 ret_value = 0 if DEBUG: @@ -107,6 +127,11 @@ class I2C: if SLOW: sleep(0.3) except IOError: + ret_ack = 0 + ret_value = 0 + if DEBUG: + print("Write error") + except err: ret_ack = 0 ret_value = 0 if DEBUG: @@ -122,6 +147,11 @@ class I2C: if SLOW: sleep(0.3) except IOError: + ret_ack = 0 + ret_value = 0 + if DEBUG: + print("No ACK") + except err: ret_ack = 0 ret_value = 0 if DEBUG: diff --git a/apsct_lib.py b/apsct_lib.py index 99309c6c99b0140bf4499697e2c9cc513bf45d57..905bd98d3d0f75ac0d57d79da8f1fa5e2c288e6d 100644 --- a/apsct_lib.py +++ b/apsct_lib.py @@ -1,4 +1,4 @@ -''' +""" Copyright 2021 Stichting Nederlandse Wetenschappelijk Onderzoek Instituten, ASTRON Netherlands Institute for Radio Astronomy Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,249 +13,430 @@ limitations under the License. Set APSCT_CLK -''' +""" import sys +import APSCT_I2C import time -sys.path.insert(0,'.') +import RPi.GPIO as gpio +import random +sys.path.insert(0, '.') import os -if os.name =="posix": +if os.name == "posix": from I2C_serial_pi2 import * else: from I2C_serial import * -DEBUG = False -I2CBUSNR=5 +I2CBUSNR = 5 sleep_time = 0.15 -SET_PLL = True -READ_LOCK = True -READ_ALL = False -CHECK_EEPROM = False -PWR_RST = False #True #False -READ_SENSOR = False #True -READ_REGS = False #True - -CLK_FREQ = '200MHz' -dev_i2c_eeprom = I2C(0x50) -dev_i2c_eeprom.bus_nr = I2CBUSNR - -CS = 6 -SCLK = 4 -SDO = 5 -SDI = 7 - -PLL_200M = 0x20 -PLL_160M = 0x21 - -def Write_byte_PLL(reg_address, wr_data, ADDRESS=0x20): +DEBUG = False + + +class ApsctClass: # - # Write Byte to the ADC + # Toplevel class that contains all controllable parts of the APSCT # - I2C_device = I2C(ADDRESS, BUSNR=I2CBUSNR) - PLL_rw = 0x00 # 0 for write, 1 for read - stri = "Write to address : 0x{1:{fill}2x} value 0x{0:{fill}2x}".format(wr_data, reg_address, fill='0') - print(stri) - I2C_device.write_bytes(0x06, 0x2C) - if DEBUG: - rd_bytes = I2C_device.read_bytes(0x06, 1) - stri = "IO expander wrote 0x{0:x}, read 0x{1}".format(0x2C, rd_bytes[1]) + 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) - data = (reg_address << 9) + (PLL_rw << 8) + wr_data - bit_array = "{0:{fill}16b}".format(data, fill='0') - I2C_device.write_bytes(0x02, 0x02 | (0x1 << CS)) - for bit in bit_array: - for clk in range(2): - Write_data = 0x02 | (0 << CS) | (clk << SCLK) | (int(bit) << SDI) - I2C_device.write_bytes(0x02, Write_data) - for clk in range(2): - Write_data = 0x02 | (0 << CS) | (clk << SCLK) - I2C_device.write_bytes(0x02, Write_data) - for clk in range(2): - Write_data = 0x02 | (1 << CS) | (clk << SCLK) - I2C_device.write_bytes(0x02, Write_data) - - Write_data = 0x02 | (1 << CS) | (0 << SCLK) | (0 << SDI) - I2C_device.write_bytes(0x02, Write_data) - if DEBUG: - read_bits = Read_byte_PLL(reg_address, nof_bytes=1, ADDRESS=ADDRESS) -# stri = "Bits written 0x{0:x} to register 0x{1:x} read from PLL are {2}".format(wr_data, reg_address, read_bits) -# print(stri) - - -def Read_byte_PLL(reg_address, nof_bytes=1, ADDRESS=0x20 ): - # - # Read Byte from the ADC - # - I2C_device = I2C(ADDRESS, BUSNR=I2CBUSNR) - PLL_rw = 0x01 # 0 for write, 1 for read + 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 - I2C_device.write_bytes(0x06, 0x2C) - data = ( reg_address << 7 ) + PLL_rw - bit_array = "{0:{fill}8b}".format(data, fill='0') - for bit in bit_array: +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 | (0 << CS) | (clk << SCLK) | ( int(bit) << SDI) - I2C_device.write_bytes(0x02, Write_data) - sleep(sleep_time) - -# print("read byte") - 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) << SDI ) - I2C_device.write_bytes(0x02, Write_data) - ret_ack, ret_value = I2C_device.read_bytes(0x00, 1) -# stri= "ret_value = {}".format(int(ret_value,16)) -# print(stri) - if ret_ack: - read_bit += str((int(ret_value, 16) >> SDO) & 0x01) + 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: - print("ACK nok") - Write_data = 0x02 | (1 << CS) | (0 << SCLK) | (0 << SDI) - I2C_device.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) - return read_bit; - -def read_all_regs_pll(pll_frequency='200MHz') : - I2C_device = I2C(0x20, BUSNR=I2CBUSNR) #clock selection - I2C_device.write_bytes(0x07, 0x00) - if pll_frequency == '160MHz': - print("Read PLL 160 MHz") - pll_address = PLL_160M -# I2C_device.write_bytes(0x03, 0x0F) - else: - print("Read PLL 200 MHz") - pll_address=PLL_200M -# I2C_device.write_bytes(0x03, 0xF8) -# for reg_cnt in range(0x15): - bytes_to_read = 24 - ret_value = Read_byte_PLL(0, nof_bytes = bytes_to_read, ADDRESS=pll_address) - 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) + 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_IO_expanderis(): - 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]) + 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 -def setup_pll(pll_frequency='200MHz') : - I2C_device = I2C(0x20, BUSNR=I2CBUSNR) #clock selection - I2C_device.write_bytes(0x07, 0x00) - if pll_frequency == '160MHz': - print("Set PLL to 160 MHz mode") - pll_address = PLL_160M - I2C_device.write_bytes(0x03, 0x08) - else: - print("Set PLL to 200 MHz mode") - pll_address=PLL_200M - I2C_device.write_bytes(0x03, 0x28) - Write_byte_PLL(0x03, 0x0C, pll_address) - sleep(0.5) - Write_byte_PLL(0x03, 0x08, pll_address) - Write_byte_PLL(0x03, 0x08, pll_address) - Write_byte_PLL(0x04, 0xCF, pll_address) # CF disable not used outputs, 00 enable all - Write_byte_PLL(0x05, 0x97, pll_address) - Write_byte_PLL(0x06, 0x10, pll_address) # cp inv = 0xF4 other 0xE4 - Write_byte_PLL(0x07, 0x04, pll_address) # Divider R = 1 dec - Write_byte_PLL(0x08, 0x01, pll_address) - Write_byte_PLL(0x07, 0x00, pll_address) - Write_byte_PLL(0x09, 0x10, pll_address) # reset - if pll_frequency == '160MHz' : - Write_byte_PLL(0x0A, 0x10, pll_address) - else: - Write_byte_PLL(0x0A, 0x14, pll_address) - Write_byte_PLL(0x09, 0x00, pll_address) - Write_byte_PLL(0x0C, 0x8F, pll_address) - Write_byte_PLL(0x0D, 0x88, pll_address) # Dig CLK = 200/1 = 200 MHz - Write_byte_PLL(0x0F, 0x08, pll_address) # RCU CLK = 200/1 = 200 MHz - Write_byte_PLL(0x11, 0x08, pll_address) # PPS ref CLK = 200/1 = 200 MHz - Write_byte_PLL(0x13, 0x88, pll_address) # T.P. CLK = 200/1 = 200 MHz - - -def power(state): - 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 - I2C_IO_device_A.write_bytes(0x07, 0x00) # '0' is output - 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_A2 = 0x04 - bits_to_set_B1 = 0x02 | (1 << CS) | (0 << SCLK) | (0 << SDI) - else: - bits_to_set_A1 = 0x00 | (1 << CS) | (0 << SCLK) | (0 << SDI) - bits_to_set_A2 = 0x00 - bits_to_set_B1 = 0x00 | (1 << CS) | (0 << SCLK) | (0 << 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 write_eeprom( data=0x01): +class ApsctSensors: # - # Write the EEPROM with the serial number etc. + # All monitor. read and write functions for the voltage sensors on APSCT # - ret_ack, ret_value = dev_i2c_eeprom.read_bytes(0) - if ret_ack < 1: - print("EEPROM not found during write") - return False - else: - dev_i2c_eeprom.write_bytes(0x00, data) - sleep(0.1) + 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_eeprom(): - # - # Read the EEPROM with the serial number etc. - # - ret_ack, ret_value = dev_i2c_eeprom.read_last_reg(1) - if ret_ack < 1: - print("no EEPROM found during read") - return False - else: - ret_ack, ret_value = dev_i2c_eeprom.read_bytes(0x00, 1) - return ret_value - -def wr_rd_eeprom(value=0x34): - # - # Write and Read the EEPROM to check functionality - # - if write_eeprom(value): - ret_value = read_eeprom() - stri = "Wrote to EEPROM: 0x{0:X}, Read from EEPROM: 0x{1} ".format(value, ret_value) - print(stri) - return True - -def apsct_sensors(): - for sens_line in range(7): - read_voltage(sens_line) - read_temp() - -def read_voltage(input_channel=0): - addr = 0x74 - Vref = 3.0 - one_step = Vref/(2**(16)) - I2C_device = I2C(addr, BUSNR=I2CBUSNR) - 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) - sleep(0.1) - I2C_device.write_bytes(channel_select_word, 0xB8) - sleep(0.5) - ret_ack, ret_value = I2C_device.read_last_reg(3) - if 1: #ret_ack: + 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) @@ -264,94 +445,161 @@ def read_voltage(input_channel=0): else: steps = (int(ret_value, 16) & 0x1FFFFF) >> 6 voltage = one_step * steps - voltage = ((4.7+2.2)/2.2)*2*voltage - string = "Voltage sens line {1} is {0:.4f} V".format(voltage, input_channel) - print(string) - else: - stri = " No ACK on device 0x{0:x} ".format(addr) - print(stri) - -def read_temp(): - Vref = 3.0 - addr = 0x74 - one_step = Vref/(2**(16)) - I2C_device = I2C(addr, BUSNR=I2CBUSNR) - temp_slope = 93.5E-6 * 2**(16+1) / Vref - sleep(1.0) - I2C_device.write_bytes(0xA0, 0xC0) - sleep(1.0) - ret_ack, ret_value = I2C_device.read_last_reg(3) - 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) - else: - print("Error reading tempeature") - - -def read_lol(pll_frequency='200MHz'): - I2C_IO_device_A = I2C(0x20, BUSNR=I2CBUSNR) - I2C_IO_device_A.write_bytes(0x06, 0x2C) # '0' is output - I2C_IO_device_A.write_bytes(0x07, 0x00) # '0' is output - I2C_IO_device_B = I2C(0x21, BUSNR=I2CBUSNR) - I2C_IO_device_B.write_bytes(0x06, 0x2C) # '0' is output - I2C_IO_device_B.write_bytes(0x07, 0xFF) # '0' is output - - 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): - print("lost lock 200MHz") - if ((status_reg & 0x20) > 0) & (pll_frequency=='160MHz'): - print("lost lock 160MHz") - ack, ret_value = I2C_IO_device_A.read_bytes(0x01, 1) - old_reg = int(ret_value,16) - I2C_IO_device_A.write_bytes(0x03, (old_reg | 0x10)) # '0' is output - sleep(1) - I2C_IO_device_A.write_bytes(0x03, (old_reg & 0xEF)) # '0' is output - - - - - -#if READ_REGS: -# read_all_regs_pll(CLK_FREQ) - -read_temp() - -if CHECK_EEPROM : - wr_rd_eeprom() - -if PWR_RST : - power(False) - sleep(10) - power(True) - -if SET_PLL : - setup_pll(CLK_FREQ) - -if READ_LOCK: - if CLK_FREQ == '160MHz' : - pll_addr = PLL_160M - else: - pll_addr = PLL_200M - ret_value = Read_byte_PLL(0x00, nof_bytes = 1, ADDRESS=pll_addr) - status_pll = int(ret_value,2) - if status_pll == 0x04: - print("PLL in lock") - elif (status_pll & 0x10) > 0: - print("Not Locked --> No 10 MHz ref") - else: - print("Not locked --> PLL Error") - -if READ_REGS: - read_all_regs_pll(CLK_FREQ) - -if READ_ALL: - read_all_regs_pll(CLK_FREQ) - read_IO_expanderis() - -if READ_SENSOR: - apsct_sensors() -read_lol(CLK_FREQ) + 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/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")