Ejemplo n.º 1
0
    def __init__(self, name, serialportno=5):
        self.serial_handle = UART(name + " serial port", serialportno)
        self.soft_reset()
        self.comm_checksum = False
        self.detect_comm_csum_mode()
        self.softsoft_reset()
        self.set_checksum_validation()

        self.set_ipga()
        self.set_dc_offset_calibrations(CS5490.CALIB_V_DC_OFFSET, CS5490.CALIB_I_DC_OFFSET)
        self.set_ac_offset_calibrations(CS5490.CALIB_I_AC_OFFSET)
        self.set_gain_calibrations(CS5490.CALIB_V_GAIN, CS5490.CALIB_I_GAIN)
        self.set_phase_compensations()
        self.set_temp_calibrations()
        self.set_settle_time(8000)
        self.set_sample_count(10 * 4000)
        self.start_conversion()

        self.name = name
Ejemplo n.º 2
0
class CS5490(object):
    """
    CS5490 Energy meter
    """

    # Top level commands:
    REGISTER_READ = 0b00000000
    REGISTER_WRITE = 0b01000000
    PAGE_SELECT = 0b10000000
    INSTRUCTION = 0b11000000

    # Instructions:
    SOFT_RESET = 0b000001
    STANDBY = 0b000010
    WAKEUP = 0b000011
    SINGLE_CONV = 0b010100
    CONT_CONV = 0b010101
    HALT_CONV = 0b011000

    # Calibration instructions:
    CALIB_GAIN_IV = 0b111110
    CALIB_DCOFFS_IV = 0b100110
    CALIB_ACOFFS_I = 0b110001

    # Do not write to unlisted registers
    # Page 0 registers
    CONFIG0 = 0
    CONFIG1 = 1
    MASK = 3
    PC = 5
    SERIALCTRL = 7
    PULSEWIDTH = 8
    PULSECTRL = 9
    STATUS0 = 23
    STATUS1 = 24
    STATUS2 = 25
    REGLOCK = 34
    V_PEAK = 36
    I_PEAK = 37
    PSDC = 48
    ZX_NUM = 55

    # Page 16 registers
    CONFIG2 = 0
    REGCHK = 1
    I = 2
    V = 3
    P = 4
    P_AVG = 5
    I_RMS = 6
    V_RMS = 7
    Q_AVG = 14
    Q = 15
    S = 20
    PF = 21
    T = 27
    P_SUM = 29
    S_SUM = 30
    Q_SUM = 31
    I_DCOFF = 32
    I_GAIN = 33
    V_DCOFF = 34
    V_GAIN = 35
    P_OFF = 36
    I_ACOFF = 37
    # Q_OFF = 38  #?????
    EPSILON = 49
    SAMPLECOUNT = 51
    T_GAIN = 54
    T_OFF = 55
    T_SETTLE = 57
    LOAD_MIN = 58
    SYS_GAIN = 60
    TIME = 61

    # Page 17 registers
    VSAG_DUR = 0
    VSAG_LEVEL = 1
    IOVER_DUR = 4
    IOVER_LEVEL = 5

    # Page 18 registers
    IZX_LEVEL = 24
    PULSERATE = 28
    INT_GAIN = 43
    VSWELL_DUR = 46
    VSWELL_LEVEL = 47
    VZX_LEVEL = 58
    CYCLECOUNT = 62
    SCALE = 63

    # Hardware configuration
    SHUNT_R = 0.05  # Ohm
    VDIV_R1 = 1.5e6  # Ohm
    VDIV_R2 = 1.8e3  # Ohm
    MCLK = 4096000  # Hz clock rate

    # Calibration values
    CALIB_I_DC_OFFSET = (14, 218, 255)
    CALIB_V_DC_OFFSET = (31, 44, 254)
    CALIB_I_AC_OFFSET = (129, 153, 22)
    CALIB_I_GAIN = (51, 22, 89)
    CALIB_V_GAIN = (206, 189, 63)

    # Scale
    V_MAX = 0.25 * (VDIV_R1 + VDIV_R2) / VDIV_R2 * 2 ** -0.5  # V_RMS (147.5 volt RMS)
    I_MAX = 4.99 * 2 ** -0.5  # I_RMS
    P_MAX = V_MAX * I_MAX  # P_AVG

    V_SCALE = 120 / 0.6  # V_RMS
    I_SCALE = I_MAX / 0.6
    P_SCALE = V_SCALE * I_SCALE

    def __init__(self, name, serialportno=5):
        self.serial_handle = UART(name + " serial port", serialportno)
        self.soft_reset()
        self.comm_checksum = False
        self.detect_comm_csum_mode()
        self.softsoft_reset()
        self.set_checksum_validation()

        self.set_ipga()
        self.set_dc_offset_calibrations(CS5490.CALIB_V_DC_OFFSET, CS5490.CALIB_I_DC_OFFSET)
        self.set_ac_offset_calibrations(CS5490.CALIB_I_AC_OFFSET)
        self.set_gain_calibrations(CS5490.CALIB_V_GAIN, CS5490.CALIB_I_GAIN)
        self.set_phase_compensations()
        self.set_temp_calibrations()
        self.set_settle_time(8000)
        self.set_sample_count(10 * 4000)
        self.start_conversion()

        self.name = name

    def get_name(self):
        return self.name

    # -~-~-~-~-~-~-~-~-~-~ #
    # Basic functions      #
    # -~-~-~-~-~-~-~-~-~-~ #

    def select_page(self, page_no):
        msg = self.add_comm_checksum((CS5490.PAGE_SELECT + page_no,))
        # print "Select page cmnd: ", msg
        self.serial_handle.send(msg)

    def read_register(self, register_no, page_no):
        """
        @return tuple with 3 bytes of the register
        """
        self.select_page(page_no)
        msg = self.add_comm_checksum((CS5490.REGISTER_READ + register_no,))
        # print "Read register Cmnd: ", msg
        self.serial_handle.send(msg)
        msg = self.serial_handle.read(3 + self.comm_checksum)
        # print "Read register returns: ", msg
        register = self.sub_comm_checksum(msg)
        return register

    def write_register(self, register_no, page_no, data):
        """
        @data = tuple (or list) of 3 integers 0..256 (or 4 if checksum is True)
        """
        if type(data) == list:
            data = tuple(data)
        if not (type(data) == tuple) or len(data) != 3:
            print "Cannot write to CS5490 register, data wrong format"
        self.select_page(page_no)
        msg = self.add_comm_checksum((CS5490.REGISTER_WRITE + register_no,) + data)
        self.serial_handle.send(msg)
        return

    def send_instruction(self, instruction):
        """
        @instruction 6 BIT CS5490 instruction
        """
        msg = self.add_comm_checksum((CS5490.INSTRUCTION + instruction,))
        # print msg
        self.serial_handle.send(msg)

    def start_conversion(self):
        self.send_instruction(CS5490.CONT_CONV)

    def stop_conversion(self):
        self.send_instruction(CS5490.HALT_CONV)

    def single_conversion(self):
        self.send_instruction(CS5490.SINGLE_CONV)

    def soft_reset(self):
        msg = (0xFF,)  # This one works also when in CSUM mode, without adding the csum
        self.serial_handle.send(msg)

    def softsoft_reset(self):
        # The official, documented, reset
        self.send_instruction(CS5490.SOFT_RESET)
        self.comm_checksum = False

    # -~-~-~-~-~-~-~-~-~-~ #
    # Check bit registers  #
    # -~-~-~-~-~-~-~-~-~-~ #

    def temp_updated(self):
        """
        @return True if TUP bit is set in status0
        """
        status0 = self.read_register(CS5490.STATUS0, 0)
        return status0[0] & 0b00100000 != 0

    def rx_timeout(self):
        """
        @return True if RX_TO bit is set in status0
        """
        status0 = self.read_register(CS5490.STATUS0, 0)
        return status0[0] & 0b00000001 != 0

    def rx_checksum_err(self):
        """
        @return True if RX_csum_err bit is set in status0
        """
        status0 = self.read_register(CS5490.STATUS0, 0)
        return status0[0] & 0b00000100 != 0

    def invalid_cmnd(self):
        """
        @return True if IC bit is set in status0
        """
        status0 = self.read_register(CS5490.STATUS0, 0)
        return status0[0] & 0b00001000 != 0

    def data_ready(self):
        """
        @return True if DRDY bit is set in status0
        """
        status0 = self.read_register(CS5490.STATUS0, 0)
        return status0[2] & 0b10000000 != 0

    def pivor(self):
        """
        @return a 3 bit number composed POR, IOR, VOR bits in the status0 register
        any of these bits being one indicates out-of-range of the respective quantities
        """
        status0 = self.read_register(CS5490.STATUS0, 0)
        return status0[1] & 0b01010100

    def ioc(self):
        """
        @return True if I over current
        """
        status0 = self.read_register(CS5490.STATUS0, 0)
        return status0[1] & 0b00000001 == 1

    def tod(self):
        """
        @return True if modulation oscillation has been detected in the temperture ADC (TOD in status1)
        """
        status1 = self.read_register(CS5490.STATUS1, 0)
        return status1[0] & 0b00001000 != 0

    # -~-~-~-~-~-~-~-~-~-~ #
    # Set bit registers    #
    # -~-~-~-~-~-~-~-~-~-~ #

    # Do not write "1" to unpublished bits or bits published as "0"
    # Do notwrite "0" to bits published as "1"

    def set_ipga(self):
        """
        set the IPGA bit for the correct current channel input gain
        using the settings from the class constants
        """
        reg = self.read_register(CS5490.CONFIG0, 0)
        # IPGA = 00 --> 10x gain, max 250 mV_Peak
        # IPGA = 10 --> 50x gain, max 50 mV_Peak
        reg = list(reg)
        if (CS5490.I_MAX * 2 ** 0.5 * CS5490.SHUNT_R) <= 50 / 1000.0:
            reg[0] = (reg[0] | 0b00100000) & 0b00110100
        elif (CS5490.I_MAX * 2 ** 0.5 * CS5490.SHUNT_R) <= 250 / 1000.0:
            reg[0] = reg[0] & 0b00000100
        else:
            print "The max current is more than the current shunt resistor can handle!"
            reg[0] = reg[0] & 0b00000100
        self.write_register(CS5490.CONFIG0, 0, reg)

    def set_uart_baudrate(self, baud):
        reg = self.read_register(CS5490.SERIALCTRL, 0)
        br = baud * CS5490.MCLK / 524288
        brb = self.int_to_bt(br)
        self.write_register(CS5490.SERIALCTRL, 0, (brb[0], brb[1], reg[2] & 0b00000110))

    def set_highpass_filter(self, vhpf=False, ihpf=False):
        """
        Set the high pass filter for voltage (vhpf) and current (ihpf) channels
        """
        reg = self.read_register(CS5490.CONFIG2, 16)
        reg = list(reg)
        if vhpf:
            reg[0] = (reg[0] | 2) & 27
        else:
            reg[0] = reg[0] & 0b00011001
        if ihpf:
            reg[0] = (reg[0] | 8) & 15
        else:
            reg[0] = reg[0] & 0b00000111
        reg[1] = reg[1] & 0b01011110
        reg[2] = reg[2] & 0b01010000
        self.write_register(CS5490.CONFIG2, 16, reg)

    # -~-~-~-~-~-~-~-~-~-~ #
    # Get data functions   #
    # -~-~-~-~-~-~-~-~-~-~ #

    # Instantaneous quantities

    def get_instantaneous_voltage(self):
        """
        @return the instantaneous voltage
        """
        v = self.read_register(CS5490.V, 16)
        return self.twoscompl_to_real(v) * CS5490.V_SCALE

    def get_instantaneous_current(self):
        """
        @return the instantaneous current
        """
        i = self.read_register(CS5490.I, 16)
        return self.twoscompl_to_real(i) * CS5490.I_SCALE

    def get_instantaneous_power(self):
        """
        @return the current power calculated from the voltage and current channels
        """
        p = self.read_register(CS5490.P, 16)
        return self.twoscompl_to_real(p) * CS5490.P_SCALE

    def get_instantaneous_quadrature_power(self):
        """
        @return the current quadrature power (Q)
        """
        q = self.read_register(CS5490.Q, 16)
        return self.twoscompl_to_real(q) * CS5490.P_SCALE

    # Average and RMS quantities

    def get_rms_voltage(self):
        """
        @return rms value of V calculated during each low-rate interval
        """
        reg = self.read_register(CS5490.V_RMS, 16)
        return self.bt_to_int(reg) * 2 ** -24 * CS5490.V_SCALE

    def get_rms_current(self):
        """
        @return rms value of I calculated during each low-rate interval
        """
        reg = self.read_register(CS5490.I_RMS, 16)
        return self.bt_to_int(reg) * 2 ** -24 * CS5490.I_SCALE

    def get_average_power(self):
        """
        @return power averaged over each low-rate interval (samplecount samples)
        """
        pavg = self.read_register(CS5490.P_AVG, 16)
        return self.twoscompl_to_real(pavg) * CS5490.P_SCALE

    def get_average_reactive_power(self):
        """
        @return reactive power (Q) averaged over each low-rate interval (samplecount samples)
        """
        qavg = self.read_register(CS5490.Q_AVG, 16)
        return self.twoscompl_to_real(qavg) * CS5490.P_SCALE

    # Peak quantities

    def get_peak_voltage(self):
        """
        @return The peak voltage
        """
        v = self.read_register(CS5490.V_PEAK, 0)
        return self.twoscompl_to_real(v) * CS5490.V_SCALE

    def get_peak_current(self):
        """
        @return The peak current
        """
        i = self.read_register(CS5490.I_PEAK, 0)
        return self.twoscompl_to_real(i) * CS5490.I_SCALE

    def get_apparent_power(self):
        """
        @return The apparent power (S)
        """
        s = self.read_register(CS5490.S, 16)
        return abs(self.twoscompl_to_real(s)) * CS5490.P_SCALE

    def get_power_factor(self):
        """
        @return The power factor (ratio of P_avg and S) * sign(P_avg)
        """
        pf = self.read_register(CS5490.PF, 16)
        return self.twoscompl_to_real(pf)

    # Total sum quantities

    def get_sum_active_power(self):
        """
        @return Total active power, P_sum
        """
        p_sum = self.read_register(CS5490.P_SUM, 16)
        return self.twoscompl_to_real(p_sum) * CS5490.P_SCALE

    def get_sum_apparent_power(self):
        """
        @return Total apparent power, S_sum
        """
        s_sum = self.read_register(CS5490.S_SUM, 16)
        return abs(self.twoscompl_to_real(s_sum)) * CS5490.P_SCALE

    def get_sum_reactive_power(self):
        """
        @return Total reactive power, Q_sum
        """
        q_sum = self.read_register(CS5490.Q_SUM, 16)
        return self.twoscompl_to_real(q_sum) * CS5490.P_SCALE

    # Special quantities

    def get_system_time(self):
        """
        Read onboard counter that is icreaed with 4.096 MHz
        """
        t = self.read_register(CS5490.TIME, 16)
        return self.bt_to_int(t)

    def get_temperature(self):
        """
        @return the on-chip temperature
        """
        t = self.read_register(CS5490.T, 16)
        return self.twoscompl_to_real(t) * 2 ** 7

    # -~-~-~-~-~-~-~-~-~-~ #
    # Set settings         #
    # -~-~-~-~-~-~-~-~-~-~ #

    def set_settle_time(self, owr_samples=30):
        """
        set the number of Output Word Rate (OWR) (OWR is 4000Hz at 50Hz and 4096MHz)
        samples that will be used to allow filters to settle at the beginning of
        converion and calibration commands
        """
        self.write_register(CS5490.T_SETTLE, 16, self.int_to_bt(owr_samples))

    def set_sample_count(self, N=100):
        """
        Set the number of output word rate (OWR) samples to use in calculating low-rate results
        @N integer between 100 and 2**23-1
        """
        N = int(N)
        if N > 2 ** 23 - 1:
            N = 2 ** 23 - 1
        if N < 100:
            N = 100
        self.write_register(CS5490.SAMPLECOUNT, 16, self.int_to_bt(N))
        return

    def set_calibration_scale(self, scale):
        """
        Set the SCALE register to scale
        @scale a number >=0 and <1
        """
        scale = abs(scale)
        self.write_register(CS5490.SCALE, 18, self.real_to_twoscompl(scale))

    # -~-~-~-~-~-~-~-~-~-~ #
    # Data type helper fie #
    # -~-~-~-~-~-~-~-~-~-~ #

    def twoscompl_to_real(self, twoscompl):
        """
        Transforms three bytes in 2 complement format to a number
        @bt tuple (or list) of three bytes (LSB,..,MSB)
        @return  a number >-1.0 and <1.0
        """
        num = self.bt_to_int(twoscompl)
        if twoscompl[2] & 128 != 0:
            num -= 2 ** 24
        return num * 2 ** -23

    def real_to_twoscompl(self, num):
        """
        Transforms a number to three bytes in 2 complement format
        @twocompl a number >-1.0 and <1.0
        @return tuple of three bytes (LSB,..,MSB) in two complements format
        """
        if abs(num) >= 1:
            print "Too big number to convert in 2-complement format"
        return self.int_to_bt(int(num * 2 ** 23))

    def int_to_bt(self, integer):
        """
        Transforms an integer to 3 bytes
        Negative numbers will correctly be represented in two's complement form
        @return tuple of 3 bytes (LSB,..,MSB)
        """
        return (integer & 255, integer >> 8 & 255, integer >> 16 & 255)

    def bt_to_int(self, bt):
        """
        Transforms 3 bytes to an integer
        @bt tuple (or list) of 3 bytes (LSB,..,MSB)
        @return an unsigned integer
        """
        return bt[0] + bt[1] * 2 ** 8 + bt[2] * 2 ** 16

    # -~-~-~-~-~-~-~-~-~-~ #
    # Calibration          #
    # -~-~-~-~-~-~-~-~-~-~ #

    def do_dc_offset_calibration(self):
        print " It's assumed no line and no voltage/current are applied to the CS5490"
        # set T_Settle to 2000ms and SampleCount to a large number
        self.set_settle_time(8000)  # will add 2 sec to the time
        self.set_sample_count(10 * 4000)  # will take 10 sec
        self.set_highpass_filter(False, False)
        # set I&V Gain to 1 offset to 0
        self.set_gain_calibrations((0, 0, 64), (0, 0, 64))
        self.set_dc_offset_calibrations((0, 0, 0), (0, 0, 0))
        # perform iv-dc calibration
        self.send_instruction(CS5490.CALIB_DCOFFS_IV)
        time.sleep(4)
        while not (self.data_ready()):
            print "Calibration in progress..."
            time.sleep(3)
        if self.pivor() != 0:
            print "PIVOR! P,I or V out of range: ", self.pivor()

        # ToDo put high pass filters back to original state
        self.set_highpass_filter(False, False)

        # get and store I & V offset registers
        i_off = self.read_register(CS5490.I_DCOFF, 16)
        v_off = self.read_register(CS5490.V_DCOFF, 16)
        print "V DC OFFSET is: ", self.twoscompl_to_real(v_off), v_off, "I DC OFFSET is: ", self.twoscompl_to_real(
            i_off
        ), i_off, "remember to store these values"
        self.set_gain_calibrations(CS5490.CALIB_V_GAIN, CS5490.CALIB_I_GAIN)

    def set_dc_offset_calibrations(self, v_off, i_off):
        """
        Set the registers I_DCOFF and V_DCOFF to the values set in the class constants
        """
        self.write_register(CS5490.I_DCOFF, 16, i_off)
        self.write_register(CS5490.V_DCOFF, 16, v_off)

    def do_ac_offset_calibration(self):
        print " It's assumed no line and no voltage/current are applied to the CS5490"
        self.set_settle_time(8000)  # will add 2 sec to the time
        self.set_sample_count(10 * 4000)  # will take 10 sec
        self.set_highpass_filter(True, True)
        # set the iac offset channel to zero
        self.set_ac_offset_calibrations((0, 0, 0))
        # perform AC offset calibration
        # AC offset register will hold the square of the RMS offset
        self.send_instruction(CS5490.CALIB_ACOFFS_I)
        time.sleep(4)
        while not (self.data_ready()):
            print "Calibration in progress..."
            time.sleep(3)
        if self.pivor() != 0:
            print "PIVOR! P,I or V out of range: ", self.pivor()

        # ToDo put high pass filters back to original state
        self.set_highpass_filter(False, False)

        # get and store I AC offset registers
        i_acoff = self.read_register(CS5490.I_ACOFF, 16)
        print "I AC OFFSET is: ", self.bt_to_int(i_acoff) * 2 ** -24, i_acoff, "remember to store this value"

    def set_ac_offset_calibrations(self, i_acoff):
        """
        Set the register I_ACOFF to the value set in the class constants
        """
        self.write_register(CS5490.I_ACOFF, 16, i_acoff)

    def do_gain_calibration(self, calib_vac=120.0, calib_r=5000.0):
        print "A load is assumed to be present: V_RMS = %.2f (V), I_RMS = %.2f (A), R = %d (Ohm), P_RMS = %.2f (W)" % (
            calib_vac,
            1.0 * calib_vac / calib_r,
            calib_r,
            1.0 * calib_vac ** 2 / calib_r,
        )
        # set I&V _Gain to 1
        self.set_gain_calibrations((0, 0, 64), (0, 0, 64))
        # set T_Settle to 2000ms and sample count to a big number
        self.set_settle_time(8000)  # will add 2 sec to the time
        self.set_sample_count(10 * 4000)  # will take 10 sec
        # Set scale register to 0.006
        init_scale_setting = 0.6 * calib_vac / (calib_r * CS5490.I_MAX)
        init_scale_setting = (calib_vac / calib_r) / CS5490.I_SCALE
        print "INIT_SCALE_SETTING: ", init_scale_setting
        self.write_register(CS5490.SCALE, 18, self.real_to_twoscompl(init_scale_setting))
        # perform gain calibration
        self.send_instruction(CS5490.CALIB_GAIN_IV)
        # wait till done, but poll quite often to get an idea of the process
        time.sleep(3)
        while not (self.data_ready()):
            print "Calibration in progress..."
            time.sleep(3)
        # check IOR and VOR status bits
        if self.pivor() != 0:
            print "PIVOR! P, I or V out of range: ", self.pivor()
        # get and store I & V Gain registers
        i_gain = self.read_register(CS5490.I_GAIN, 16)
        v_gain = self.read_register(CS5490.V_GAIN, 16)
        print "V GAIN is: ", self.bt_to_int(v_gain) * 2 ** -22, v_gain, "I GAIN is: ", self.bt_to_int(
            i_gain
        ) * 2 ** -22, i_gain, "remember to store these values"
        # redo AC offset calculation (ToDO)

    def set_gain_calibrations(self, v_gain, i_gain):
        """
        Set the register V_GAIN and I__GAIN with the values from the class constants
        """
        self.write_register(CS5490.V_GAIN, 16, v_gain)
        self.write_register(CS5490.I_GAIN, 16, i_gain)

    def set_phase_compensations(self):
        # set to zero compansation, all errors are accounted for in the gain calibration
        pass

    def set_temp_calibrations(self):
        self.write_register(CS5490.T_GAIN, 16, (0x00, 0x00, 0x01))
        self.write_register(CS5490.T_OFF, 16, (0x00, 0x00, 0x00))
        return

    # ToDo calibration registers checksum check

    # -~-~-~-~-~-~-~-~-~-~ #
    # Checksum             #
    # -~-~-~-~-~-~-~-~-~-~ #

    def set_checksum_validation(self):
        """
        resets bit RX_CSUM_OFF in SerialCtrl to start the checksum validation
        """
        reg = self.read_register(CS5490.SERIALCTRL, 0)
        self.write_register(CS5490.SERIALCTRL, 0, (reg[0], reg[1], reg[2] & 0b00000100))
        self.comm_checksum = True
        return

    def reset_checksum_validation(self):
        """
        sets bit RX_CSUM_OFF in SerialCtrl to stop the checksum validation
        """
        reg = self.read_register(CS5490.SERIALCTRL, 0)
        self.write_register(CS5490.SERIALCTRL, 0, (reg[0], reg[1], (reg[2] | 0b00000010) & 0b00000110))
        self.comm_checksum = False
        return

    def detect_comm_csum_mode(self):
        """
        figures out the current mode the CS5490 is operating in csum or not
        sets or resets the instance comm_checksum bit
        @return True if in csum communication mode
        """
        self.comm_checksum = False  # Try communication without checksum
        reg = self.read_register(CS5490.SERIALCTRL, 0)
        if len(reg) == 3:
            if reg[2] & 0b00000010 != 0:
                self.comm_checksum = False
            else:
                print "Checksum error, register: ", reg
        else:
            self.comm_checksum = True  # Try communication with checksum
            self.soft_reset()  # CS5490 sometimes 'hangs' when errorenous communications were received
            reg = self.read_register(CS5490.SERIALCTRL, 0)  # Check if it works now
            if len(reg) != 3:
                print "Cannot figure the checksum status of the CS5490, value of the register: ", reg
            if reg[2] & 0b00000010 != 0:
                print "Cannot figure the checksum status of the CS5490, value of the register: ", reg
        return self.comm_checksum

    def add_comm_checksum(self, data):
        """
        Adds a checksum byte if comm_checksum is set to True, otherwise leaves data as is
        @return tuple of 3 or 4 bytes (data + 1 checkum byte) depending on comm_checksum
        @data tuple (or list) of data bytes
        """
        csum = ()
        if self.comm_checksum:
            csum = 0xFF
            for byte in data:
                csum = (csum - byte) & 0xFF
            csum = (csum,)
        return tuple(data) + csum

    def sub_comm_checksum(self, data):
        """
        Checks if checksum is correct (if comm_checksum = True)
        and removes the checksum byte if present
        @return tuple with 3 data bytes
        @data tuple (or list) of the data returned from the CS5490
        """
        data = tuple(data)
        if self.comm_checksum:
            csum = self.add_comm_checksum(data[0:3])  # With the anoyingly confusing semantic "0:3" when 0,1,2 is meant!
            if csum[3] != data[3]:
                print "CS5490 read data checksum error, data: ", data
        return data[0:3]