def set_frequency(frequency): if frequency not in frequencies: print('Invalid frequency, pick one of: {}'.format(frequencies.keys())) return False # select i2c device #i2c = I2C("/dev/i2c-0") # select i2c channel set_i2c_mux(TCA9548_U165_ADDR, Z_IIC_BUS2) i2c = I2C("/dev/i2c-0") # preamble, postamble, and soft-reset defined in configurations on a per-clock-chip basis # send the manufacturer preamble for initializing the clock chip for being able to accept register modifications print('Handling preamble') do_i2c_write(i2c, Si5345['preamble']) sleep(0.3) # 300 ms delay required by manufacturer # write the frequency configuration defined by manufacturer print('Handling modifications for {0:s}'.format(frequency)) do_i2c_write(i2c, frequencies[frequency]) # required by manufacturer for finishing register modifications print('Handling soft reset') do_i2c_write(i2c, Si5345['soft reset']) # required by manufacturer to lock the clock print('Handling postamble') do_i2c_write(i2c, Si5345['postamble']) print('{0:s} frequency was configured.'.format(frequency)) i2c.close() return True
def __init__(self, address): self.address = address self.i2c = I2C("/dev/i2c-1") wakeup_and_blind = self.generate_command(0x01, 0x01) # wake up the device adc_res = self.generate_command(0x03, 0x0C) # set ADC resolution to 16 bits bias_top = self.generate_command(0x04, 0x05) # bias_bottom = self.generate_command(0x05, 0x05) # clk_speed = self.generate_command(0x06, 0x15) cm_top = self.generate_command(0x07, 0x0C) # BPA cm_bottom = self.generate_command(0x08, 0x0C) # BPA pull_ups = self.generate_command(0x09, 0x88) sleep = self.generate_command(0x01, 0x00) print("Initializing capture settings") self.send_command(wakeup_and_blind) self.send_command(adc_res) self.send_command(bias_top) self.send_command(bias_bottom) self.send_command(clk_speed) self.send_command(cm_top) self.send_command(cm_bottom) self.send_command(pull_ups) print("Grabbing EEPROM data") eeprom = self.get_eeprom() self.extract_eeprom_parameters(eeprom) self.eeprom = eeprom # initialize offset to zero self.offset = np.zeros((32, 32))
def bmr458_mon(dev_addr, reg_addr): i2c = I2C("/dev/i2c-1") write = I2C.Message([reg_addr]) read = I2C.Message([0x0] * 2, read=True) i2c.transfer(dev_addr, [write, read]) i2c.close() return bytes(bytearray(read.data)).encode('hex')
def __init__(self, address=0x1A, i2c_bus="/dev/i2c-1"): self.address = address self.i2c = I2C(i2c_bus) self.blockshift = 4 # data read periodically while imaging self.data_ptats = None self.data_vdd = None self.elec_offset = None # buffer to gather pixel data self.ts = None self.pixel_values = np.zeros(1024) self.header_values = np.zeros(8) self.frame_cnt = 0 print ("Grabbing EEPROM data") eeprom = self.__get_eeprom() self.calib_params = self.__extract_eeprom_parameters(eeprom) self.eeprom = eeprom print("Initializing capture settings with stored calibration data") wakeup_and_blind = self.__generate_command(0x01, 0x01) # wake up the device adc_res = self.__generate_command(0x03, self.calib_params['MBIT_calib']) # set ADC resolution to 16 bits pull_ups = self.__generate_command(0x09, self.calib_params['PU_calib']) self.__send_command(wakeup_and_blind) self.__send_command(adc_res) self.__set_bias_current(self.calib_params['BIAS_calib']) self.__set_clock_speed(self.calib_params['CLK_calib']) self.__set_cm_current(self.calib_params['BPA_calib']) self.__send_command(pull_ups)
def pmbus_read(dev_addr, reg_addr, n_bytes): i2c = I2C("/dev/i2c-0") write = I2C.Message([reg_addr]) read = I2C.Message([0x0] * n_bytes, read=True) i2c.transfer(dev_addr, [write, read]) i2c.close() return bytes(bytearray(read.data)).encode('hex')
def __init__(self, bus, address, big_endian=True): """Initialize I2C Keyword arguments: bus -- The I2C bus: "RPI_1" - RPi hardware I2C "RPI_1SW" - RPi software I2C "GPG3_AD1" - GPG3 AD1 software I2C "GPG3_AD2" - GPG3 AD2 software I2C address -- the slave I2C address. Formatted as bits 0-6, not 1-7. big_endian (default True) -- Big endian? """ if bus == "RPI_1": self.bus_name = bus if RPI_1_Module == "pigpio": self.i2c_bus = pigpio.pi() self.i2c_bus_handle = None elif RPI_1_Module == "smbus": self.i2c_bus = smbus.SMBus(1) elif RPI_1_Module == "periphery": self.bus_name = bus self.i2c_bus = I2C("/dev/i2c-1") elif bus == "RPI_1SW": self.bus_name = bus self.i2c_bus = DI_I2C_RPI_SW() elif bus == "GPG3_AD1" or bus == "GPG3_AD2": self.bus_name = bus self.gopigo3_module = __import__("gopigo3") self.gpg3 = self.gopigo3_module.GoPiGo3() if bus == "GPG3_AD1": self.port = self.gpg3.GROVE_1 elif bus == "GPG3_AD2": self.port = self.gpg3.GROVE_2 self.gpg3.set_grove_type(self.port, self.gpg3.GROVE_TYPE.I2C) time.sleep(0.01) elif bus == "BP3_1" or bus == "BP3_2" or bus == "BP3_3" or bus == "BP3_4": self.bus_name = bus self.brickpi3_module = __import__("brickpi3") self.bp3 = self.brickpi3_module.BrickPi3() if bus == "BP3_1": self.port = self.bp3.PORT_1 elif bus == "BP3_2": self.port = self.bp3.PORT_2 elif bus == "BP3_3": self.port = self.bp3.PORT_3 elif bus == "BP3_4": self.port = self.bp3.PORT_4 self.bp3.set_sensor_type(self.port, self.bp3.SENSOR_TYPE.I2C, [0, 0]) time.sleep(0.01) else: raise IOError("I2C bus not supported") self.mutex = di_mutex.DI_Mutex(name=("I2C_Bus_" + bus)) self.set_address(address) self.big_endian = big_endian
def __init__(self, address): self.address = address self.i2c = I2C("/dev/i2c-1") wakeup_and_blind = self.generate_command(0x01, 0x01) # wake up the device adc_res = self.generate_command(0x03, 0x0C) # set ADC resolution to 16 bits pull_ups = self.generate_command(0x09, 0x88) print("Initializing capture settings") self.send_command(wakeup_and_blind) self.send_command(adc_res) self.send_command(pull_ups) self.set_bias_current(0x05) self.set_clock_speed(0x15) self.set_cm_current(0x0C) print("Grabbing EEPROM data") eeprom = self.get_eeprom() self.extract_eeprom_parameters(eeprom) self.eeprom = eeprom # initialize offset to zero self.offset = np.zeros((32, 32))
def __init__(self, address, revision="2020"): self.address = address self.i2c = I2C("/dev/i2c-1") if revision == "2020": self.blockshift = 4 else: self.blockshift = 2 # nyalakan device wakeup_and_blind = self.generate_command(0x01, 0x01) # atur resolusi ADC ke 16 bits adc_res = self.generate_command(0x03, 0x0C) pull_ups = self.generate_command(0x09, 0x88) print("Inisialisasi cara pengambilan") self.send_command(wakeup_and_blind) self.send_command(adc_res) self.send_command(pull_ups) self.set_bias_current(0x05) self.set_clock_speed(0x15) self.set_cm_current(0x0C) print("Ambil data EEPROM") eeprom = self.get_eeprom() self.extract_eeprom_parameters(eeprom) self.eeprom = eeprom # inisialisasi offset ke nol self.offset = np.zeros((32, 32))
def __init__(self, i2cbus=1): self.devpath = self.I2C_DEV_PATH_PREFIX + str(i2cbus) try: from periphery import I2C except ImportError: exit('[PERIPHERY_PWM] This code requires periphery package') self.i2c = I2C(self.devpath)
def firefly_reg_wr(i2c_bus_addr,firefly_refdes,dev_addr,reg_page,reg_addr,reg_value): firefly_select(firefly_refdes) i2c = I2C("/dev/i2c-0") i2c.transfer(TCA9548_U165_ADDR, [I2C.Message([i2c_bus_addr])]) # select i2c bus i2c.transfer(dev_addr, [I2C.Message([127,reg_page])]) # select the register page i2c.transfer(dev_addr, [I2C.Message([reg_addr,reg_value])]) # write the value to registers i2c.close()
def minipod_reg_wr(i2c_bus_addr, dev_addr, page_addr, reg_addr, reg_value): i2c = I2C("/dev/i2c-1") i2c.transfer(TCA9548_U93_ADDR, [I2C.Message([i2c_bus_addr])]) # select I2C Bus i2c.transfer(dev_addr, [I2C.Message([127, page_addr])]) # set the page i2c.transfer(dev_addr, [I2C.Message([reg_addr, reg_value])]) # write to reg_addr i2c.close()
def __init__(self, address = _VCNL4010_I2CADDR_DEFAULT): self._device = I2C('/dev/i2c-1') # Verify chip ID. revision = self._read_u8(_VCNL4010_PRODUCTID) if (revision & 0xF0) != 0x20: raise RuntimeError('Failed to find VCNL4010, check wiring!') self.led_current = 20 self.frequency = FREQUENCY_390K625 self._write_u8(_VCNL4010_INTCONTROL, 0x08)
def connectNotecard(config): if config.PortType == 'i2c': port = I2C(config.PortName) card = notecard.OpenI2C(port, 0, 0, debug=config.EnableDebug) else: port = serial.Serial(port=config.PortName, baudrate=config.BaudRate) card = notecard.OpenSerial(port, debug=config.EnableDebug) return card
def test_repstart(): i2c = I2C("/dev/i2c-1") write = I2C.Message([0x88]) read = I2C.Message([0x0]*2, read=True) # read 2 bytes msgs = [write, read] i2c.transfer(0x7F, msgs) value = msgs[1].data[1]*256 + msgs[1].data[0] return hex(value)
def tca6424_reg_rd(i2c_bus_addr,dev_addr,reg_addr): i2c = I2C("/dev/i2c-0") i2c.transfer(TCA9548_U165_ADDR, [I2C.Message([i2c_bus_addr])]) # select i2c bus read = I2C.Message([0x0]*1, read=True) i2c.transfer(dev_addr, [I2C.Message([reg_addr])]) # set reg_addr i2c.transfer(dev_addr, [read]) i2c.close() print('read back is 0x{0:x}' .format(read.data[0])) return read.data[0]
def setupPeriphery(): # SPI for the digital potentiometer (digPot) digPot = SPI("/dev/spidev0.0", 0, 50000000,"msb",8,0) # SPI vor the ADC (adc) adc = SPI("/dev/spidev0.1", 0, 50000000,"msb",8,0) # I2C for the Squiggle motor (squiggle) squiggle = I2C("/dev/i2c-0") return
def minipod_reg_rd(i2c_bus_addr, dev_addr, page_addr, reg_addr): i2c = I2C("/dev/i2c-1") i2c.transfer(TCA9548_U93_ADDR, [I2C.Message([i2c_bus_addr])]) # select i2c bus read = I2C.Message([0x0], read=True) i2c.transfer(dev_addr, [I2C.Message([127, page_addr])]) # set the page i2c.transfer(dev_addr, [I2C.Message([reg_addr])]) # set reg_addr i2c.transfer(dev_addr, [read]) i2c.close() return read.data[0]
def connectToNotecard(debugFlag=defaultDebugFlag, useSerial=defaultUseSerialFlag, portName=defaultPortName, baudRate=9600): if useSerial: port = serial.Serial(port=portName, baudrate=baudRate) card = notecard.OpenSerial(port, debug=debugFlag) else: port = I2C(portName) card = notecard.OpenI2C(port, 0, 0, debug=debugFlag) return card
def firefly_reg_rd(i2c_bus_addr,firefly_refdes,dev_addr,reg_page,reg_addr): firefly_select(firefly_refdes) i2c = I2C("/dev/i2c-0") i2c.transfer(TCA9548_U165_ADDR, [I2C.Message([i2c_bus_addr])]) # select i2c bus read = I2C.Message([0x0]*1, read=True) i2c.transfer(dev_addr, [I2C.Message([127,reg_page])]) # select the register page i2c.transfer(dev_addr, [I2C.Message([reg_addr])]) # set reg_addr i2c.transfer(dev_addr, [read]) i2c.close() print('read back is 0x{0:x}' .format(read.data[0])) return read.data[0]
def connect(self): # Instantiate the periphery I2C manager. If it fails it will raise # an I2CError, which we then catch. # If we have an ``__i2c_master`` it means we have a successful # connection. # See http://python-periphery.readthedocs.io/en/latest/i2c.html try: self.__i2c_master = I2C(self.i2c_bus) rospy.loginfo("Connected to AM2315") except I2CError as e: rospy.logwarn("Failed to connect to AM2315: {}".format(e)) self.__i2c_master = None pass
def read_sensor(): address = 0x06 register = 0x01 command = 0x03 unused = 0x00 sensor_id = detect_line_follower() if sensor_id == 2: raise NotImplementedError( 'line follower (black board) is not supported with the line_follower.line_sensor module\n\ please use the di_sensors.easy_line_follower.EasyLineFollower class to interface with both line followers (black + red boards)\n\ check https://di-sensors.readthedocs.io documentation to find out more') try: i2c = I2C('/dev/i2c-' + str(bus_number)) read_bytes = 10 * [0] msg1 = [I2C.Message([register, command] + 3 * [unused])] msg2 = [I2C.Message(read_bytes, read=True)] # we meed to do 2 transfers so we can avoid using repeated starts # repeated starts don't go hand in hand with the line follower i2c.transfer(address, msg1) i2c.transfer(address, msg2) except I2CError as error: return 5 * [-1] # unpack bytes received and process them # bytes_list = struct.unpack('10B',read_results[0]) output_values = [] input_values = msg2[0].data for step in range(5): # calculate the 16-bit number we got sensor_buffer[step].append(input_values[2 * step] * 256 + input_values[2 * step + 1]) # if there're too many elements in the list # then remove one if len(sensor_buffer[step]) > max_buffer_length: sensor_buffer[step].pop(0) # eliminate outlier values and select the most recent one filtered_value = statisticalNoiseReduction(sensor_buffer[step], 2)[-1] # append the value to the corresponding IR sensor output_values.append(filtered_value) return output_values
def ltc2499_current_mon(dev_addr, reg_addr0, reg_addr1): i2c = I2C("/dev/i2c-1") i2c.transfer(dev_addr, [I2C.Message([reg_addr1, reg_addr0])]) sleep(0.5) read = I2C.Message([0x0] * 4, read=True) i2c.transfer(dev_addr, [read]) adc_code = bytes(bytearray(read.data)).encode('hex') i2c.close() resolution = 2500. / 0x80000000 if (int(adc_code, 16) < 0x40000000): amplitude = 0 else: amplitude = (int(adc_code, 16) - 0x40000000) * resolution return amplitude / 40
def __init__(self, bus, address, big_endian = True): """Initialize I2C Keyword arguments: bus -- The I2C bus. "RPI_1", "GPG3_AD1", or "GPG3_AD2". address -- the slave I2C address big_endian (default True) -- Big endian?""" if bus == "RPI_1": self.bus_name = bus if RPI_1_Module == "pigpio": self.i2c_bus = pigpio.pi() self.i2c_bus_handle = None elif RPI_1_Module == "smbus": self.i2c_bus = smbus.SMBus(1) elif RPI_1_Module == "periphery": self.bus_name = bus self.i2c_bus = I2C("/dev/i2c-1") elif bus == "GPG3_AD1" or bus == "GPG3_AD2": self.bus_name = bus self.gopigo3_module = __import__("gopigo3") self.gpg3 = self.gopigo3_module.GoPiGo3() if bus == "GPG3_AD1": self.port = self.gpg3.GROVE_1 elif bus == "GPG3_AD2": self.port = self.gpg3.GROVE_2 self.gpg3.set_grove_type(self.port, self.gpg3.GROVE_TYPE.I2C) time.sleep(0.01) elif bus == "BP3_1" or bus == "BP3_2" or bus == "BP3_3" or bus == "BP3_4": self.bus_name = bus self.brickpi3_module = __import__("brickpi3") self.bp3 = self.brickpi3_module.BrickPi3() if bus == "BP3_1": self.port = self.bp3.PORT_1 elif bus == "BP3_2": self.port = self.bp3.PORT_2 elif bus == "BP3_3": self.port = self.bp3.PORT_3 elif bus == "BP3_4": self.port = self.bp3.PORT_4 self.bp3.set_sensor_type(self.port, self.bp3.SENSOR_TYPE.I2C, [0, 0]) time.sleep(0.01) else: raise IOError("I2C bus not supported") self.set_address(address) self.big_endian = big_endian
def __init__(self, bus, address, big_endian=True): if bus == "RPI_1": self.bus_name = bus if RPI_1_Module == "pigpio": self.i2c_bus = pigpio.pi() self.i2c_bus_handle = None elif RPI_1_Module == "smbus": self.i2c_bus = smbus.SMBus(1) elif RPI_1_Module == "periphery": self.bus_name = bus self.i2c_bus = I2C("/dev/i2c-1") elif bus == "RPI_1SW": self.bus_name = bus self.i2c_bus = net_I2C_RPI_SW() elif bus == "GPG3_AD1" or bus == "GPG3_AD2": self.bus_name = bus self.gopigo3_module = __import__("gopigo3") self.gpg3 = self.gopigo3_module.GoPiGo3() if bus == "GPG3_AD1": self.port = self.gpg3.GROVE_1 elif bus == "GPG3_AD2": self.port = self.gpg3.GROVE_2 self.gpg3.set_grove_type(self.port, self.gpg3.GROVE_TYPE.I2C) time.sleep(0.01) elif bus == "BP3_1" or bus == "BP3_2" or bus == "BP3_3" or bus == "BP3_4": self.bus_name = bus self.brickpi3_module = __import__("brickpi3") self.bp3 = self.brickpi3_module.BrickPi3() if bus == "BP3_1": self.port = self.bp3.PORT_1 elif bus == "BP3_2": self.port = self.bp3.PORT_2 elif bus == "BP3_3": self.port = self.bp3.PORT_3 elif bus == "BP3_4": self.port = self.bp3.PORT_4 self.bp3.set_sensor_type(self.port, self.bp3.SENSOR_TYPE.I2C, [0, 0]) time.sleep(0.01) else: raise IOError("I2C bus not supported") self.mutex = net_mutex.net_Mutex(name=("I2C_Bus_" + bus)) self.set_address(address) self.big_endian = big_endian
def __init__(self, temp_table=None, address=0x1A, i2c_bus="/dev/i2c-5"): self.address = address self.i2c = I2C(i2c_bus) self.ptats = None self.vdd = None self.frame_cnt = 0 self.blockshift = 4 print ("Grabbing EEPROM data") eeprom = self.get_eeprom() self.extract_eeprom_parameters(eeprom) self.eeprom = eeprom print(eeprom) self.temp_table = None if temp_table is not None: for table in temp_table: if table['TABLENUMBER'] == self.TN: self.temp_table = table if self.temp_table is not None: self.temp_interp_f = interpolate.interp2d(self.temp_table['XTATemps'], self.temp_table['YADValues'], self.temp_table['TempTable'], kind='linear') else: self.temp_interp_f = None print('Could not find Table Number %d in temp_table provided' % self.TN) print("Initializing capture settings with stored calibration data") wakeup_and_blind = self.generate_command(0x01, 0x01) # wake up the device adc_res = self.generate_command(0x03, self.MBIT_calib) # set ADC resolution to 16 bits pull_ups = self.generate_command(0x09, self.PU_calib) self.send_command(wakeup_and_blind) self.send_command(adc_res) self.set_bias_current(self.BIAS_calib) self.set_clock_speed(self.CLK_calib) self.set_cm_current(self.BPA_calib) self.send_command(pull_ups) # initialize offset to zero self.elecOffset = np.zeros((32, 32))
def detect_line_follower(): """ returns 0 - for no line follower detected 1 - for detecting the line follower (red board) 2 - for detecting the line follower (black board) """ i2c = I2C('/dev/i2c-' + str(bus_number)) address = 0x06 # see if the device is up and running device_on = False try: read_byte = [0] msg1 = [I2C.Message(read_byte, read=True)] i2c.transfer(address, msg1) device_on = True except: pass if device_on is True: # then it means we have a line follower connected # we still don't know whether it is the black one or the red one board = 1 try: read_bytes = 20 * [0] msg1 = [I2C.Message([0x12])] msg2 = [I2C.Message(read_bytes, read=True)] i2c.transfer(address, msg1) i2c.transfer(address, msg2) name = "" for c in range(20): if msg2[0].data[c] != 0: name += chr(msg2[0].data[c]) else: break if name == 'Line Follower': board = 2 except: pass return board else: return 0
def adm1066_voltage_mon(dev_addr, reg_value_80, reg_value_81, reg_addr_vh, reg_addr_vl, average_on): i2c = I2C("/dev/i2c-1") #read the ADM1066 U52 with addr 0x34 and 0x35 read = I2C.Message([0x0], read=True) i2c.transfer( dev_addr, [I2C.Message([0x82, 0x0 + average_on * 4])]) # reset STOPWRITE BIT i2c.transfer( dev_addr, [I2C.Message([0x80, reg_value_80])]) # reg 0x80 select channel i2c.transfer(dev_addr, [I2C.Message([0x81, reg_value_81])]) # reg 0x81 i2c.transfer( dev_addr, [I2C.Message([0x82, 0x01 + average_on * 4])]) # reg 0x82 select go bit i2c.transfer(dev_addr, [I2C.Message([0x82])]) # reg 0x82 while True: sleep(1.0) i2c.transfer(dev_addr, [read]) status = read.data[ 0] #keep checking the go bit, until it is equal average_on*4 if status == average_on * 4: break i2c.transfer(dev_addr, [I2C.Message([0x82, 0x08 + average_on * 4])]) i2c.transfer( dev_addr, [ I2C.Message([reg_addr_vh]), # reg 0xA8 VH high 8bit voltage value read ]) vh = read.data[0] i2c.transfer( dev_addr, [ I2C.Message([reg_addr_vl]), # reg 0xA9 VH low 8 bits voltag value read ]) vl = read.data[0] i2c.close() voltage = ((vh * 256 + vl) * 2.048 / 4095) / (2**(average_on * 4)) return voltage
def ltc2499_temp_mon(dev_addr, reg_addr0, reg_addr1): # two delays as the chip is slow sleep(0.5) i2c = I2C("/dev/i2c-1") i2c.transfer(dev_addr, [I2C.Message([reg_addr1, reg_addr0])]) # Reg for read sleep(0.5) read = I2C.Message([0x0] * 4, read=True) i2c.transfer(dev_addr, [read]) i2c.close() adc_code = int(bytes(bytearray(read.data)).encode('hex'), 16) resolution = 2500. / 0x80000000 amplitude = (adc_code - 0x40000000) * resolution if (adc_code == 0x3FFFFFFF): amplitude = -1 temperature = 326 - 0.5 * amplitude return temperature
def read_sensor(): address = 0x06 register = 0x01 command = 0x03 unused = 0x00 try: i2c = I2C('/dev/i2c-' + str(bus_number)) read_bytes = 10 * [0] msg1 = [I2C.Message([register, command] + 3 * [unused])] msg2 = [I2C.Message(read_bytes, read=True)] # we meed to do 2 transfers so we can avoid using repeated starts # repeated starts don't go hand in hand with the line follower i2c.transfer(address, msg1) i2c.transfer(address, msg2) except I2CError as error: return 5 * [-1] # unpack bytes received and process them # bytes_list = struct.unpack('10B',read_results[0]) output_values = [] input_values = msg2[0].data for step in range(5): # calculate the 16-bit number we got sensor_buffer[step].append(input_values[2 * step] * 256 + input_values[2 * step + 1]) # if there're too many elements in the list # then remove one if len(sensor_buffer[step]) > max_buffer_length: sensor_buffer[step].pop(0) # eliminate outlier values and select the most recent one filtered_value = statisticalNoiseReduction(sensor_buffer[step], 2)[-1] # append the value to the corresponding IR sensor output_values.append(filtered_value) return output_values
def main(): """Connect to Notcard and run a transaction test.""" print("Opening port...") try: port = I2C("/dev/i2c-1") except Exception as exception: raise Exception("error opening port: " + NotecardExceptionInfo(exception)) print("Opening Notecard...") try: card = notecard.OpenI2C(port, 0, 0) except Exception as exception: raise Exception("error opening notecard: " + NotecardExceptionInfo(exception)) # If success, do a transaction loop print("Performing Transactions...") while True: time.sleep(2) transactionTest(card)