class StarLord(MIXBoard): ''' StarLord is a high resolution differential input digital audio analyzer module Args: i2c: instance(I2C), the instance of I2C bus. which will be used to used to control eeprom, sensor and io expander. ipcore: instance(MIXAUT5SGR), the instance of MIXAUT5SGR, which include AD717x, FFT Analyzer, Signal Source and gpio function. If device name string is passed to the parameter, the ipcore can be instanced in the module. Examples: i2c = I2C('/dev/i2c-0') starlord = StarLord(i2c, '/dev/MIX_AUT5_SG_R_0') starlord.module_init() # measure left channel input result = starlord.measure('left', 20000, 3) print("vpp={}, freq={}, thd={}, thdn={}, rms={}, noisefloor={}".format(result['vpp'], result['freq'], result['thd'], result['thdn'], result['rms'], result['noisefloor'])) # measure LNA signal result = starlord.measure('left', '50mV', 20000, 3, 1000) print("vpp={}, freq={}, thd={}, thdn={}, rms={}".format(result['vpp'], result['freq'], result['thd'], result['thdn'], result['rms'])) ''' compatible = ['GQQ-AUD003002-000'] rpc_public_api = [ 'module_init', 'enable_upload', 'disable_upload', 'measure', 'enable_output', 'disable_output', 'measure_lna', 'enable_lna_upload', 'disable_lna_upload', 'config_lna_scope' ] + MIXBoard.rpc_public_api def __init__(self, i2c, ipcore=None, range_table=starlord_table): if i2c: self.eeprom = CAT24C32(StarLordDef.EEPROM_I2C_ADDR, i2c) self.nct75 = NCT75(StarLordDef.TEMP_I2C_ADDR, i2c) self.pca9536 = PCA9536(StarLordDef.PCA9536_DEV_ADDR, i2c) else: self.eeprom = EepromEmulator("cat24cxx_emulator") self.nct75 = NCT75Emulator("nct75_emulator") self.pca9536 = PCA9536Emulator("PCA9536_emulator") if ipcore: if isinstance(ipcore, basestring): ipcore = MIXAUT5SGR(ipcore) self.ipcore = ipcore self.analyzer = self.ipcore.analyzer self.signal_source = self.ipcore.signal_source self.ad7175 = self.ipcore.ad717x self.ad7175.config = { 'ch0': { 'P': 'AIN0', 'N': 'AIN1' }, 'ch1': { 'P': 'AIN2', 'N': 'AIN3' } } self.adc_rst_pin = Pin(self.ipcore.gpio, StarLordDef.ADC_RESET_PIN) self.i2s_rx_en_pin = Pin(self.ipcore.gpio, StarLordDef.I2S_RX_EN_PIN) self.dac_rst_pin = Pin(self.ipcore.gpio, StarLordDef.DAC_RESET_PIN) self.i2s_tx_en_pin = Pin(self.ipcore.gpio, StarLordDef.I2S_TX_EN_PIN) self.i2s_ch_select = [ Pin(self.ipcore.gpio, StarLordDef.I2S_CH_SELECT_2), Pin(self.ipcore.gpio, StarLordDef.I2S_CH_SELECT_3) ] self.fft_source_select = Pin(self.ipcore.gpio, StarLordDef.FFT_SOURCE_SELECT) self.ad7175_upload_select = Pin(self.ipcore.gpio, StarLordDef.AD7175_TO_FFT_OR_NOT) else: self.analyzer = MIXFftAnalyzerSGEmulator( "mix_fftanalyzer_sg_emulator") self.signal_source = MIXSignalSourceSGEmulator( "mix_signalsource_sg_emulator") self.adc_rst_pin = Pin(None, StarLordDef.ADC_RESET_PIN) self.i2s_rx_en_pin = Pin(None, StarLordDef.I2S_RX_EN_PIN) self.dac_rst_pin = Pin(None, StarLordDef.DAC_RESET_PIN) self.i2s_tx_en_pin = Pin(None, StarLordDef.I2S_TX_EN_PIN) self.ad7175 = MIXAd7175SGEmulator('mix_ad7175_sg_emulator', StarLordDef.EMULATOR_REG_SIZE) self.i2s_ch_select = [ Pin(None, StarLordDef.I2S_CH_SELECT_2), Pin(None, StarLordDef.I2S_CH_SELECT_3) ] self.fft_source_select = Pin(None, StarLordDef.FFT_SOURCE_SELECT) self.ad7175_upload_select = Pin(None, StarLordDef.AD7175_TO_FFT_OR_NOT) super(StarLord, self).__init__(self.eeprom, self.nct75, cal_table={}, range_table=range_table) self.is_lna_up = False self.is_analyzer_up = False self.is_enable_upload = False def module_init(self): ''' Init module, which will reset dac/adc, i2s module and load calibration Returns: string, "done", execution successful. Examples: starloard.module_init() ''' self.adc_rst_pin.set_dir(StarLordDef.IO_DIR_OUTPUT) self.dac_rst_pin.set_dir(StarLordDef.IO_DIR_OUTPUT) self.i2s_rx_en_pin.set_dir(StarLordDef.IO_DIR_OUTPUT) self.i2s_tx_en_pin.set_dir(StarLordDef.IO_DIR_OUTPUT) self.i2s_ch_select[StarLordDef.AUDIO_CHANNEL_SELECT_BIT0].set_dir( StarLordDef.IO_DIR_OUTPUT) self.i2s_ch_select[StarLordDef.AUDIO_CHANNEL_SELECT_BIT1].set_dir( StarLordDef.IO_DIR_OUTPUT) self.fft_source_select.set_dir(StarLordDef.IO_DIR_OUTPUT) self.ad7175_upload_select.set_dir(StarLordDef.IO_DIR_OUTPUT) # reset ADC self.adc_rst_pin.set_level(0) time.sleep(StarLordDef.RELAY_DELAY_S) self.adc_rst_pin.set_level(1) # reset DAC self.dac_rst_pin.set_level(0) time.sleep(StarLordDef.RELAY_DELAY_S) self.dac_rst_pin.set_level(1) # reset i2s rx self.i2s_rx_en_pin.set_level(0) # reset i2s tx self.i2s_tx_en_pin.set_level(0) # io init self.pca9536.set_pin_dir(StarLordDef.AD7175_CH_LEFT_CTL_BIT, StarLordDef.IO_DIR_OUTPUT) self.pca9536.set_pin_dir(StarLordDef.AD7175_CH_RIGHT_CTL_BIT, StarLordDef.IO_DIR_OUTPUT) self.pca9536.set_pin(StarLordDef.AD7175_CH_LEFT_CTL_BIT, 0) self.pca9536.set_pin(StarLordDef.AD7175_CH_RIGHT_CTL_BIT, 0) # ad7175 init self.ad7175.channel_init() # measure lna config init self.config_lna_scope(StarLordDef.CH_LEFT, StarLordDef.LNA_RANGE_5V) self.load_calibration() return "done" def enable_upload(self): ''' Enable module data upload. Returns: string, "done", execution successful. ''' self.i2s_rx_en_pin.set_level(StarLordDef.I2S_RX_ENABLE) self.analyzer.enable_upload() self.is_enable_upload = True return "done" def disable_upload(self): ''' Disable module data upload. Returns: string, "done", execution successful. ''' self.analyzer.disable_upload() self.i2s_rx_en_pin.set_level(StarLordDef.I2S_RX_DISABLE) self.is_enable_upload = False return "done" def measure(self, channel, bandwidth_hz, harmonic_count, decimation_type=0xFF, sampling_rate=StarLordDef.AUDIO_SAMPLING_RATE): ''' Measure audio input signal, which captures data using CS5361. Args: channel: string, ['left', 'right'], select input signal channel. bandwidth_hz: int/string, [42~48000], unit Hz, the signal bandwidth. harmonic_count: int, [2~10], The harmonic count of signal. decimation_type: int, [1~255], default 0xFF, sample data decimation. sampling_rate: int, [1~192000], default 192000, unit Hz, ADC sampling rate. Returns: dict, {'vpp': value, 'freq': value, 'thd': value, 'thdn': value, 'rms': value, 'noisefloor': value}, measurement result. ''' assert channel in StarLordDef.AUDIO_CHANNEL_LIST if self.is_enable_upload is False: self.i2s_rx_en_pin.set_level(StarLordDef.I2S_RX_ENABLE) pin = self.i2s_ch_select[StarLordDef.AUDIO_CHANNEL_SELECT_BIT0] pin.set_level(StarLordDef.AUDIO_CHANNEL_LIST[channel][ StarLordDef.AUDIO_CHANNEL_SELECT_BIT0]) pin = self.i2s_ch_select[StarLordDef.AUDIO_CHANNEL_SELECT_BIT1] pin.set_level(StarLordDef.AUDIO_CHANNEL_LIST[channel][ StarLordDef.AUDIO_CHANNEL_SELECT_BIT1]) self.fft_source_select.set_level(StarLordDef.FFT_SOURCE_FROM_CS5361) result = self._analyzer(sampling_rate, decimation_type, bandwidth_hz, harmonic_count) range_name = "AUDIO_CS5361_RMS_" + channel result['rms'] = (self.calibrate(range_name, result['rms'][0]), StarLordDef.VOLT_UNIT_RMS) if self.is_enable_upload is False: self.i2s_rx_en_pin.set_level(StarLordDef.I2S_RX_DISABLE) return result def config_lna_scope(self, channel, scope, adc_upload_ch=StarLordDef.AD7175_UPLOAD_TO_FFT): ''' Config LNA measurement scope Args: channel: string, ['left', 'right'], the channel to be upload. scope: string, ['5V', '50mV'], AD7175 measurement range. adc_upload_ch: string, ['dma', 'fft'], default 'fft', AD7175 source data is uploaded to DMA or FFT. Returns: string, "done", execution successful. ''' assert channel in StarLordDef.AD7175_CH_LIST assert scope in StarLordDef.LNA_SCOPE assert adc_upload_ch in StarLordDef.AD7175_UPLOAD_CH self.channel = channel self.scope = scope if scope == StarLordDef.LNA_RANGE_5V: self.pca9536.set_pin(StarLordDef.AD7175_CH_LIST[channel], StarLordDef.SEL_GAIN_1) else: self.pca9536.set_pin(StarLordDef.AD7175_CH_LIST[channel], StarLordDef.SEL_GAIN_100) self.ad7175_upload_select.set_level( StarLordDef.AD7175_UPLOAD_CH[adc_upload_ch]) return "done" def enable_lna_upload(self, channel, sampling_rate=StarLordDef.LNA_SAMPLING_RATE): ''' Enable LNA adc data upload Args: channel: string, ['left', 'right'], the channel to be upload. sampling_rate: float, default 250000, unit Hz, AD7175 sampling rate, please refer to datasheet for more information. Returns: string, "done", execution successful. ''' assert channel in StarLordDef.AD7175_CH_LIST self.is_lna_up = True self.ad7175.disable_continuous_sampling( StarLordDef.AD7175_CH_LIST[channel]) time.sleep(StarLordDef.RELAY_DELAY_S) self.ad7175.enable_continuous_sampling( StarLordDef.AD7175_CH_LIST[channel], sampling_rate) return "done" def disable_lna_upload(self, channel): ''' Disable LNA adc data upload Args: channel: string, ['left', 'right'], the channel to be upload. Returns: string, "done", execution successful. ''' assert channel in StarLordDef.AD7175_CH_LIST self.is_lna_up = False if self.is_analyzer_up is True: self.analyzer.disable_upload() self.is_analyzer_up = False self.ad7175.disable_continuous_sampling( StarLordDef.AD7175_CH_LIST[channel]) return "done" def measure_lna(self, bandwidth_hz, harmonic_count, decimation_type=0xFF, sampling_rate=StarLordDef.LNA_SAMPLING_RATE): ''' Measure audio LNA input signal, which captures data using AD7175. Args: bandwidth_hz: int/string, [42~48000], unit Hz, the signal bandwidth. harmonic_count: int, [2~10], The harmonic count of signal. decimation_type: int, [1~255], default 0xFF, sample data decimation. sampling_rate: int, [5~250000], default 250000, unit Hz, Sample rate of your ADC device. Returns: dict, {'vpp': value, 'freq': value, 'thd': value, 'thdn': value, 'rms': value, 'noisefloor': value}, measurement result. ''' self.fft_source_select.set_level(StarLordDef.FFT_SOURCE_FROM_AD7175) if self.is_lna_up is True and self.is_analyzer_up is False: self.analyzer.enable_upload() self.is_analyzer_up = True self.analyzer.disable() self.analyzer.enable() self.analyzer.analyze_config(sampling_rate, decimation_type, bandwidth_hz, harmonic_count) self.analyzer.analyze() range_name = "LNA_" + self.scope + "_" + self.channel gain = StarLordDef.GAIN_VALUE[self.scope] vpp = self.analyzer.get_vpp() * gain rms = vpp / StarLordDef.RMS_TO_VPP_RATIO rms = self.calibrate(range_name, rms) thdn_value = self.analyzer.get_thdn() result = dict() result["vpp"] = (vpp, StarLordDef.VOLT_UNIT_MV) result["freq"] = (self.analyzer.get_frequency(), StarLordDef.FREQ_UNIT_HZ) result["thd"] = (self.analyzer.get_thd(), StarLordDef.THD_UNIT_DB) result["thdn"] = (thdn_value, StarLordDef.THDN_UNIT_DB) result["rms"] = (rms, StarLordDef.VOLT_UNIT_RMS) result["noisefloor"] = (10**(thdn_value / 20) * rms, StarLordDef.VOLT_UNIT_RMS) return result def enable_output(self, freq, vpp): ''' StarLord CS5361 output audio sine waveform. Args: freq: int, [5~50000], unit Hz, output signal's frequency. vpp: float, [0~6504], unit mV, output signal's vpp. Returns: string, "done", execution successful. ''' assert StarLordDef.OUTPUT_FREQ_MIN <= freq assert freq <= StarLordDef.OUTPUT_FREQ_MAX assert StarLordDef.OUTPUT_VPP_MIN <= vpp assert vpp <= StarLordDef.OUTPUT_VPP_MAX vpp = self.calibrate(StarLordDef.OUTPUT_CAL_ITEM, vpp) vpp = 0 if vpp < 0 else vpp # enable I2S tx module self.i2s_tx_en_pin.set_level(StarLordDef.AUDIO_OUTPUT_ENABLE) self.signal_source.close() self.signal_source.open() # calculate vpp to vpp scale for FPGA vpp_scale = vpp * StarLordDef.VPP_2_SCALE_RATIO self.signal_source.set_swg_paramter(StarLordDef.AUDIO_SAMPLING_RATE, freq, vpp_scale, StarLordDef.OUTPUT_SIGNAL_DUTY) self.signal_source.set_signal_type(StarLordDef.OUTPUT_WAVE) self.signal_source.set_signal_time(StarLordDef.SIGNAL_ALWAYS_OUTPUT) self.signal_source.output_signal() return "done" def disable_output(self): ''' Disable Cs5361 output signal. Returns: string, "done", execution successful. ''' self.signal_source.close() self.i2s_tx_en_pin.set_level(StarLordDef.AUDIO_OUTPUT_DISABLE) return "done" def _analyzer(self, sampling_rate, decimation_type, bandwidth_hz, harmonic_count): ''' Measure audio input signal. Returns: dict, {'vpp': value, 'freq': value, 'thd': value, 'thdn': value, 'rms': value, 'noisefloor': value}, measurement result. ''' self.analyzer.disable() self.analyzer.enable() self.analyzer.analyze_config(sampling_rate, decimation_type, bandwidth_hz, harmonic_count) self.analyzer.analyze() # calculate the actual vpp of HW by VREF vpp = self.analyzer.get_vpp() * StarLordDef.AUDIO_ANALYZER_VREF # vpp = RMS * 2 * sqrt(2) rms = vpp / StarLordDef.RMS_TO_VPP_RATIO vpp = rms * StarLordDef.RMS_TO_VPP_RATIO thdn_value = self.analyzer.get_thdn() result = dict() result["vpp"] = (vpp, StarLordDef.VOLT_UNIT_MV) result["freq"] = (self.analyzer.get_frequency(), StarLordDef.FREQ_UNIT_HZ) result["thd"] = (self.analyzer.get_thd(), StarLordDef.THD_UNIT_DB) result["thdn"] = (thdn_value, StarLordDef.THDN_UNIT_DB) result["rms"] = (rms, StarLordDef.VOLT_UNIT_RMS) result["noisefloor"] = (10**(thdn_value / 20) * rms, StarLordDef.VOLT_UNIT_RMS) return result
class Dazzler(MIXBoard): ''' Dazzler function class. compatible = ['GQQ-AUD001004-000'] The dazzler has a output sine function and one measure channel. It can measure signal's frequency, vpp, THD+N, THD. Args: i2c: instance(I2C), which is used to access eeprom and nct75, if not given, will create eeprom and nct75 emulator. adc_rst_pin: instance(GPIO), used to reset ADC CS5361. Setting low to reset CS5361, seting high to enable CS5361. i2s_rx_en_pin: instance(GPIO), used to enable fft analyzer module. dac_rst_pin: instance(GPIO), used to reset IIS of the CS4398. i2s_tx_en_pin: instance(GPIO), used to enable signal source module. sample_rate: int, Use to config the CS5361 or CS4398, unit is Hz, default 192000. ipcore: instance(MIXAUT1SGR), aggregated MIXAUT1SGR wrapper. Examples: i2c = I2C('/dev/i2c-1') aut1 = MIXAUT1SGR('/dev/MIX_AUT1_0') dazzler = Dazzler(i2c, ipcore=aut1) Example for measure, bandwith is 20000Hz, harmonic_num is 5: result = dazzler.measure(20000, 5) print("vpp={}, freq={}, thd={}, thdn={}, rms={}".format(result["vpp"], result["freq"], result["thd"], result["thdn"], result["rms"])) Example for data upload: dma = Dma("/dev/MIX_DMA") dma.config_channel(0, 1024 * 1024) dma.enable_channel(0) dazzler.enable_upload() time.sleep(1) dazzler.disable_upload() data = dma.read_channel_all_data(0) dma.disable_channel(0) print(data) ''' compatible = ['GQQ-AUD001004-000'] rpc_public_api = [ 'module_init', 'enable_upload', 'disable_upload', 'measure', 'enable_output', 'disable_output' ] + MIXBoard.rpc_public_api def __init__(self, i2c, adc_rst_pin=None, i2s_rx_en_pin=None, dac_rst_pin=None, i2s_tx_en_pin=None, sample_rate=192000, ipcore=None): assert sample_rate > 0 and sample_rate <= DazzlerDef.MAX_SAMPLING_RATE if i2c is None: self.eeprom = EepromEmulator("cat24cxx_emulator") self.nct75 = NCT75Emulator("nct75_emulator") else: self.eeprom = CAT24C32(DazzlerDef.EEPROM_I2C_ADDR, i2c) self.nct75 = NCT75(DazzlerDef.SENSOR_I2C_ADDR, i2c) MIXBoard.__init__(self, self.eeprom, self.nct75, cal_table=dazzler_calibration_info, range_table=dazzler_range_table) if ipcore: if isinstance(ipcore, basestring): axi4 = AXI4LiteBus(ipcore, DazzlerDef.MIX_AUT1_REG_SIZE) self.ip = MIXAUT1SGR(axi4) else: self.ip = ipcore self.analyzer = self.ip.analyzer self.signal_source = self.ip.signal_source self.adc_rst_pin = adc_rst_pin or Pin(self.ip.gpio, DazzlerDef.ADC_RESET_PIN) self.i2s_rx_en_pin = i2s_rx_en_pin or Pin(self.ip.gpio, DazzlerDef.I2S_RX_EN_PIN) self.dac_rst_pin = dac_rst_pin or Pin(self.ip.gpio, DazzlerDef.DAC_RESET_PIN) self.i2s_tx_en_pin = i2s_tx_en_pin or Pin(self.ip.gpio, DazzlerDef.I2S_TX_EN_PIN) elif (not ipcore and not adc_rst_pin and not i2s_rx_en_pin and not dac_rst_pin and not i2s_tx_en_pin): self.analyzer = MIXFftAnalyzerSGEmulator( "mix_fftanalyzer_sg_emulator") self.signal_source = MIXSignalSourceSGEmulator( "mix_signalsource_sg_emulator") self.adc_rst_pin = Pin(None, DazzlerDef.ADC_RESET_PIN) self.i2s_rx_en_pin = Pin(None, DazzlerDef.I2S_RX_EN_PIN) self.dac_rst_pin = Pin(None, DazzlerDef.DAC_RESET_PIN) self.i2s_tx_en_pin = Pin(None, DazzlerDef.I2S_TX_EN_PIN) else: if ipcore: raise DazzlerException( "parameter 'ipcore' can not be given at same time with 'analyzer', " "'signal_source', 'adc_rst_pin', 'i2s_rx_en_pin', " "'dac_rst_pin', 'i2s_tx_en_pin'") else: raise DazzlerException( "parameter 'analyzer', 'signal_source', 'adc_rst_pin', " "'i2s_rx_en_pin', 'dac_rst_pin' and 'i2s_tx_en_pin'" " must be given at the same time") self.sample_rate = sample_rate def __del__(self): self.adc_rst_pin.set_level(0) self.i2s_rx_en_pin.set_level(0) self.dac_rst_pin.set_level(0) self.i2s_tx_en_pin.set_level(0) self.signal_source.close() def module_init(self): ''' Dazzler init module, will reset dac/adc and i2s module. Returns: string, "done", api execution successful. ''' self.adc_rst_pin.set_dir('output') self.dac_rst_pin.set_dir('output') self.i2s_rx_en_pin.set_dir('output') self.i2s_tx_en_pin.set_dir('output') # reset ADC self.adc_rst_pin.set_level(0) time.sleep(DazzlerDef.DELAY_S) self.adc_rst_pin.set_level(1) # reset DAC self.dac_rst_pin.set_level(0) time.sleep(DazzlerDef.DELAY_S) self.dac_rst_pin.set_level(1) self.i2s_rx_en_pin.set_level(1) self.load_calibration() return "done" def enable_upload(self): ''' Dazzler upoad mode open. Control audio upload data of ADC when doing measurement. It's not necessary enable upload when doing measurement. Note that data transfered into DMA is 32bit each data, but high 24bit data is valid. Low 8bit data is invalid. The data format is twos complement. Returns: string, "done", api execution successful. ''' self.analyzer.enable_upload() return "done" def disable_upload(self): ''' Dazzler upoad mode close. Close data upload doesn't influence to measure. Returns: string, "done", api execution successful. ''' self.analyzer.disable_upload() return "done" def measure(self, bandwidth_hz, harmonic_num, decimation_type=0xFF): ''' Dazzler measure signal's Vpp, RMS, THD+N, THD. Args: bandwidth_hz: int, [24~95977], Measure signal's limit bandwidth, unit is Hz. The frequency of the signal should not be greater than half of the bandwidth. harmonic_num: int, [2-10], Use for measuring signal's THD. decimation_type: int, [1~255], Decimation for FPGA to get datas. If decimation is 0xFF, FPGA will choose one suitable number. Returns: dict, {"vpp":value, "freq":value, "thd":value, "thdn":value, "rms":value}, vpp, freq, thd, thdn, rms value. Examples: dazzler.measure(20000, 5, 0xff) ''' assert bandwidth_hz == 'auto' or isinstance(bandwidth_hz, int) assert isinstance(harmonic_num, int) and harmonic_num > 0 assert isinstance(decimation_type, int) and decimation_type > 0 self.analyzer.disable() self.analyzer.enable() self.analyzer.analyze_config(self.sample_rate, decimation_type, bandwidth_hz, harmonic_num) self.analyzer.analyze() # calculate the actual vpp of HW by VREF vpp = self.analyzer.get_vpp() * DazzlerDef.AUDIO_ANALYZER_VREF # vpp = RMS * 2 * sqrt(2) rms = vpp / DazzlerDef.RMS_TO_VPP_RATIO rms = self.calibrate(DazzlerDef.MEASURE_CAL_ITEM, rms) vpp = rms * DazzlerDef.RMS_TO_VPP_RATIO result = dict() result["vpp"] = vpp result["freq"] = self.analyzer.get_frequency() result["thd"] = self.analyzer.get_thd() result["thdn"] = self.analyzer.get_thdn() result["rms"] = rms return result def enable_output(self, freq, rms): ''' Dazzler output sine wave, differencial mode. Args: freq: int, [10~100000], Ouput signal's frequency, unit is Hz. rms: float, [0~2300], Ouput wave's RMS, unit is mV. Returns: string, "done", api execution successful. Examples: dazzler.enable_output(10000, 500) ''' assert freq >= DazzlerDef.OUTPUT_FREQ_MIN assert freq <= DazzlerDef.OUTPUT_FREQ_MAX assert rms >= DazzlerDef.OUTPUT_RMS_MIN assert rms <= DazzlerDef.OUTPUT_RMS_MAX # enable I2S tx module self.i2s_tx_en_pin.set_level(1) self.signal_source.open() rms = self.calibrate(DazzlerDef.OUTPUT_CAL_ITEM, rms) # vpp = RMS * 2 * sqrt(2) vpp = rms * DazzlerDef.RMS_TO_VPP_RATIO # calculate vpp to vpp scale for FPGA vpp_scale = vpp * DazzlerDef.VPP_2_SCALE_RATIO / DazzlerDef.SIGNAL_SOURCE_VREF self.signal_source.set_signal_type(DazzlerDef.OUTPUT_WAVE) self.signal_source.set_signal_time(DazzlerDef.SIGNAL_ALWAYS_OUTPUT) self.signal_source.set_swg_paramter(self.sample_rate, freq, vpp_scale, DazzlerDef.OUTPUT_SIGNAL_DUTY) self.signal_source.output_signal() return "done" def disable_output(self): ''' Dazzler close sine wave output. Returns: string, "done", api execution successful. Examples: dazzler.disable_output() ''' self.signal_source.close() self.i2s_tx_en_pin.set_level(0) return "done"
class AD9628(object): ''' The AD9628 is a monolithic, dual-channel, 1.8V supply, 12-bit, 125MSPS/105MSPS analog-to-digital converter (ADC). It features a high performance sample-and-hold circuit and onchip voltage reference. The digital output data is presented in offset binary, Gray code, or twos complement format. A data output clock (DCO) is provided for each ADC channel to ensure proper latch timing with receiving logic. 1.8V CMOS or LVDS output logic levels are supported. Output data can also be multiplexed onto a single output bus. ClassType = ADC Args: spi_bus: instance(QSPI)/None, MIXQSPISG class instance, if not using, will create emulator. pdwn_ctrl: instance(GPIO)/None, Class instance of GPIO, which is used to control ad9628 pdwn_ctrl signal, default None for not use. oeb_ctrl: instance(GPIO)/None, Class instance of GPIO, which is used to control ad9628 oeb_ctrl signal, default None for not use. cs_ctrl: instance(GPIO)/None, Class instance of GPIO, which is used to control ad9628 cs_ctrl signal, default None for not use. use_cs: Bool('True'/'False'), determine if CS is used internally. Examples: pdwn_ctrl = GPIO(cat9555, 6) oeb_ctrl = GPIO(cat9555, 7) cs_ctrl = GPIO(cat9555, 12) axi4_bus = AXI4LiteBus('/dev/MIX_DUT_SPI_0', 256) spi_bus = MIXQSPISG(axi4_bus) ad9628 = AD9628(spi_bus, pdwn_ctrl, oeb_ctrl, cs_ctrl) ''' def __init__(self, spi_bus=None, pdwn_ctrl=None, oeb_ctrl=None, cs_ctrl=None, use_cs=True): if(spi_bus is None and pdwn_ctrl is None and oeb_ctrl is None): self.spi_bus = MIXQSPISGEmulator("ad9628_emulator", 256) self.pdwn = Pin(None, 6) self.oeb = Pin(None, 7) elif(spi_bus is not None and pdwn_ctrl is not None and oeb_ctrl is not None): self.spi_bus = spi_bus self.pdwn = pdwn_ctrl self.oeb = oeb_ctrl else: raise AD9628RException('__init__ error! Please check the parameters') self.use_cs = use_cs if(self.use_cs is True and cs_ctrl is not None): self.cs = cs_ctrl elif(self.use_cs is False and cs_ctrl is None): self.cs = Pin(None, 12) else: raise AD9628RException('cs use error! Please check the parameters') def read_register(self, reg_addr): ''' AD9628 internal function to read register Args: reg_addr: hexmial, [0~0x102], The register range is 0-0x102 in datasheet, register to be read. Returns: int, value, register read data. Examples: rd_data = ad9628.read_register(0x01) print(rd_data) # rd_data will be like 0x05 ''' assert isinstance(reg_addr, int) assert 0 <= reg_addr <= 0x102 if self.use_cs is True: self.cs.set_level(0) reg_address = (1 << 15) + (reg_addr & 0x3FF) read_data = 0 write_date = [(reg_address >> 8) & 0xFF, reg_address & 0xFF] reg_value = self.spi_bus.transfer(write_date, 1, False) read_data = reg_value[0] if self.use_cs is True: self.cs.set_level(1) return read_data def write_register(self, reg_addr, reg_value): ''' AD9628 internal function to write register Args: reg_addr: hexmial, [0~0x102], register to be write. reg_value: hexmial, [0~0xff], data to be write. Examples: ad9628.write_register(0x01, 0x02) ''' assert isinstance(reg_addr, int) assert isinstance(reg_value, int) assert 0 <= reg_addr <= 0x102 assert 0 <= reg_value <= 0xFF if self.use_cs is True: self.cs.set_level(0) reg_address = reg_addr & 0x3FF write_date = [(reg_address >> 8) & 0xFF, reg_address & 0xFF, reg_value] ret = self.spi_bus.write(write_date) if self.use_cs is True: self.cs.set_level(1) if ret is False: return False return True def get_chip_id(self): ''' AD9628 internal function to get AD9628's chip ID number Returns: int, value, variable which to store the chip ID. Examples: id = ad9628.get_chip_id() print(id) ''' chip_id = self.read_register(AD9628Def.REGISTER_ADDRESS["chip_id"]) return chip_id def get_clock_speed_grade(self): ''' AD9628 internal function to check AD9628's input clock speed supported, 105000000SPS or 125000000SPS. Returns: int, value, Return 105000000SPS or 125000000SPS. Examples: grade = ad9628.clock_speed_grade() print(grade) ''' grade = 0 rd_data = self.read_register( AD9628Def.REGISTER_ADDRESS["chip_grade_id"]) if (((rd_data) >> 4) & 0x07) == 0x04: grade = 105000000 elif (((rd_data) >> 4) & 0x07) == 0x05: grade = 125000000 else: raise AD9628RException('get clock speed grade fail!') return grade def select_channel(self, channel): ''' AD9628 internal function to select which channel to receive SPI command. Args: channel: string, ["A", "B", "BOTH"]. select which channel to receive SPI command. Examples: ad9628.select_channel("A") ''' assert channel in AD9628Def.CHANNEL_SELECT self.write_register( AD9628Def.REGISTER_ADDRESS["device_index"], AD9628Def.CHANNEL_SELECT[channel]) def get_sampling_rate(self): ''' AD9628 internal function to get sample rate. Returns: int, value, Return 80000000SPS, 105000000SPS or 125000000SPS. Examples: rate = ad9628.get_sampling_rate() print(rate) ''' sample_rate = 0 rd_data = self.read_register(AD9628Def.REGISTER_ADDRESS["sample_rate"]) if (rd_data & 0x07) == 0x03: sample_rate = 80000000 elif (rd_data & 0x07) == 0x04: sample_rate = 105000000 elif (rd_data & 0x07) == 0x05: sample_rate = 125000000 else: raise AD9628RException('get sampling rate fail!') return sample_rate def setup(self, type, format, msps, bits): ''' AD9628 internal function to Initial AD9628. Args: type: string, ["CMOS", "LVDS_ANSI", "LVDS_REDUCED"], configure the data output type of the AD9628. format: string, ["OFFSET_BIN", "TWOS_COMPLEMENT", "GRAY_CODE"], configure the data output format of the AD9628. msps: int, [80000000, 105000000, 125000000], configure the sample speed of data output of the AD9628. bits: string, ["WIDTH_10", "WIDTH_12"], configure the output data bits of the AD9628. Examples: ad9628.setup("LVDS_ANSI", "OFFSET_BIN", "125000000", "WIDTH_12") ''' assert type in AD9628Def.OUTPUT_TYPE assert format in AD9628Def.OUTPUT_FORMAT assert msps in AD9628Def.SAMPLE_SPEED assert bits in ["WIDTH_10", "WIDTH_12"] # Digital Output Enable Function self.oeb.set_level(0) # self.pdwn.set_level(0), external power down. self.power_mode("EXTERNAL", "POWER_DOWN") # Internal power-down mode ,register digital reset. self.power_mode("INTERNAL", "DIGITAL_RESET") # Set power mode need to reset it and wait 1 millisecond time.sleep(AD9628Def.SLEEP_TIME) self.power_mode("INTERNAL", "POWER_ON") output_mode = AD9628Def.OUTPUT_TYPE[type] | AD9628Def.OUTPUT_FORMAT[format] self.write_register( AD9628Def.REGISTER_ADDRESS["output_mode"], output_mode) sample_rate_bits = AD9628Def.RESOLUTION_12_BITS if bits == "WIDTH_10": sample_rate_bits = AD9628Def.RESOLUTION_10_BITS elif bits == "WIDTH_12": sample_rate_bits = AD9628Def.RESOLUTION_12_BITS else: raise AD9628RException('width bits set fail!') sample = AD9628Def.SAMPLE_SPEED[msps] | sample_rate_bits self.write_register(AD9628Def.REGISTER_ADDRESS["sample_rate"], sample) self.write_register( AD9628Def.REGISTER_ADDRESS["transfer_addr"], AD9628Def.SYNC_TRANSFER) def clock_phase(self, dco_invert_ctrl, phase_ratio): ''' AD9628 internal function to input clock divider phase adjust relative to the encode clock. Args: dco_invert_ctrl: string, ["NOT_INVERT", "INVERT"], control DCO(dat clock output) invert or not. phase_ratio: string, ["NO_DELAY", "ONE_INPUT", "TWO_INPUT", "THREE_INPUT", "FOUR_INPUT", "FIVE_INPUT", "SIX_INPUT", "SEVEN_INPUT"], selection of clock delays into the input clock divider. Examples: ad9628.clock_phase("INVERT", "ONE_INPUT") ''' assert dco_invert_ctrl in ["NOT_INVERT", "INVERT"] assert phase_ratio in AD9628Def.PHASE_INPUT_CLOCK_CYCLES phase_delay = AD9628Def.PHASE_INPUT_CLOCK_CYCLES[phase_ratio] if dco_invert_ctrl == "INVERT": phase_con = (AD9628Def.INVERT_DCO | phase_delay) else: phase_con = (AD9628Def.NOT_INVERT_DCO | phase_delay) self.write_register( AD9628Def.REGISTER_ADDRESS["clock_phase"], phase_con) def clock_divide(self, divide_ratio, div_next_sync, div_sync_en): ''' AD9628 internal function to config clock divider's divide ratio. Args: divide_ratio: string, ["BY_1", "BY_2", "BY_3", "BY_4", "BY_5", "BY_6", "BY_7", "BY_8"], config clock divider's divide ratio. div_next_sync: string, ["NEX_SYNC", "NEX_ASYNC"], config divider next sync only open or not. div_sync_en: string, ["SYNC_OPEN", "SYNC_CLOSE"], config divider sync open or not. Examples: ad9628.clock_divide("BY_1", "NEX_SYNC", "SYNC_OPEN") ''' assert divide_ratio in AD9628Def.CLOCK_DIVIDE assert div_next_sync in ["NEX_SYNC", "NEX_ASYNC"] assert div_sync_en in ["SYNC_OPEN", "SYNC_CLOSE"] divider_con = AD9628Def.CLOCK_DIVIDE[divide_ratio] self.write_register( AD9628Def.REGISTER_ADDRESS["clock_divide"], divider_con) if div_next_sync == "NEX_SYNC": if div_sync_en == "SYNC_OPEN": sync_con = (AD9628Def.DIV_NEX_SYNC | AD9628Def.DIV_SYNC_OPEN) else: sync_con = (AD9628Def.DIV_NEX_SYNC | AD9628Def.DIV_SYNC_CLOSE) else: if div_sync_en == "SYNC_OPEN": sync_con = (AD9628Def.DIV_NEX_ASYNC | AD9628Def.DIV_SYNC_OPEN) else: sync_con = (AD9628Def.DIV_NEX_ASYNC | AD9628Def.DIV_SYNC_CLOSE) self.write_register(AD9628Def.REGISTER_ADDRESS["sync_ctrl"], sync_con) def clock_stabilizer(self, statu): ''' AD9628 internal function to global clock duty cycle stabilizer control. Args: statu: string, ["DIS_STABILIZE"/"EN_STABILIZE"], control clock duty cycle stabilizer open or not. Examples: ad9628.clock_stabilizer("EN_STABILIZE") ''' assert statu in ["DIS_STABILIZE", "EN_STABILIZE"] if statu == "EN_STABILIZE": stabilizer_con = 0x01 else: stabilizer_con = 0x00 self.write_register( AD9628Def.REGISTER_ADDRESS["global_clock"], stabilizer_con) def cmos_output_adjust(self, dco_strength, data_strength): ''' AD9628 internal function to config CMOS output driver strength properties. Args: dco_strength: string, ["1X", "2X", "3X", "4X"], config DCO strength level when chip config in CMOS mode. data_strength: string, ["1X", "2X", "3X", "4X"], config data strength level when chip config in CMOS mode. Examples: ad9628.cmos_output_adjust("1X", "1X") ''' assert dco_strength in AD9628Def.DCO_STRENGTH assert data_strength in AD9628Def.DATA_STRENGTH cmos_adjust = AD9628Def.DCO_STRENGTH[dco_strength] | AD9628Def.DATA_STRENGTH[data_strength] self.write_register( AD9628Def.REGISTER_ADDRESS["output_adjust"], cmos_adjust) def data_output_delay(self, dco_delay_en, data_delay_en, delay): ''' AD9628 internal function to sets the fines output delay of the output clock, not change the internal timeing. Args: dco_delay_en: string, ["DCO_DELAY_OPEN", "DCO_DELAY_CLOSE"], control DCO delay function open or not. data_delay_en: string, ["DATA_DELAY_OPEN", "DATA_DELAY_CLOSE"], control data delay function open or not. delay: string, ["0_56_NS", "1_12_NS", "1_68_NS", "2_24_NS", "2_80_NS", "3_36_NS", "3_92_NS", "4_48_NS"], select data delay level. Examples: ad9628.data_output_delay("DCO_DELAY_OPEN", "DATA_DELAY_OPEN", "1_12_NS") ''' assert delay in AD9628Def.DATA_OUTPUT_DELAY assert dco_delay_en in ["DCO_DELAY_OPEN", "DCO_DELAY_CLOSE"] assert data_delay_en in ["DATA_DELAY_OPEN", "DATA_DELAY_CLOSE"] output_delay = AD9628Def.DATA_OUTPUT_DELAY[delay] if dco_delay_en == "DCO_DELAY_OPEN": if data_delay_en == "DATA_DELAY_OPEN": output_delay = (AD9628Def.DCO_DELAY_OPEN | AD9628Def.DATA_DELAY_OPEN | (output_delay)) else: output_delay = (AD9628Def.DCO_DELAY_OPEN | AD9628Def.DATA_DELAY_CLOSE | (output_delay)) else: if data_delay_en == "DATA_DELAY_OPEN": output_delay = (AD9628Def.DCO_DELAY_CLOSE | AD9628Def.DATA_DELAY_OPEN | (output_delay)) else: output_delay = (AD9628Def.DCO_DELAY_CLOSE | AD9628Def.DATA_DELAY_CLOSE | (output_delay)) self.write_register( AD9628Def.REGISTER_ADDRESS["output_delay"], output_delay) def vref_set(self, vref, overrange_en): ''' AD9628 internal function to adjuse AD9628 VREF level. Args: vref: string, ["1_00", "1_14", "1_33", "1_60", "2_00"], config the inter VREF level. overrange_en: string, ["OVERRANGE", "NOT_OVERRANGE"], config output overrange enable or not. Examples: ad9628.vref_set("1_00", "OVERRANGE") ''' assert vref in AD9628Def.VREF_LEVEL assert overrange_en in ["OVERRANGE", "NOT_OVERRANGE"] overrange_con = 0x01 if overrange_en == "OVERRANGE": overrange_con = 0x01 else: overrange_con = 0x00 self.write_register( AD9628Def.REGISTER_ADDRESS["overrange_reg"], overrange_con) vref_con = AD9628Def.VREF_LEVEL[vref] self.write_register(AD9628Def.REGISTER_ADDRESS["vref_reg"], vref_con) def _internal_power_mode(self, work_mode): ''' AD9628 internal function to config AD9628 internal power mode. Args: work_mode: string, ["POWER_ON", "POWER_DOWN", "STANDBY", "DIGITAL_RESET"], select AD9628's work mode. Examples: ad9628._internal_power_mode("POWER_ON") ''' assert work_mode in AD9628Def.POWER_MODE power_con = 0 power_con = self.read_register( AD9628Def.REGISTER_ADDRESS["power_mode"]) power_con = ((power_con) & 0xFC) | AD9628Def.POWER_MODE[work_mode] self.write_register( AD9628Def.REGISTER_ADDRESS["power_mode"], power_con) def _external_power_mode(self, work_mode): ''' AD9628 internal function to config AD9628 external power mode. Args: work_mode: string, ["POWER_ON", "POWER_DOWN"], select AD9628's work mode. Examples: ad9628._external_power_mode("POWER_ON") ''' assert work_mode in ["POWER_ON", "POWER_DOWN"] if self.pdwn is None: raise AD9628RException('power mode set fail!') if work_mode == "POWER_ON": self.pdwn.set_level(1) else: self.pdwn.set_level(0) def power_mode(self, refer, work_mode): ''' AD9628 internal function to config AD9628 chip power mode. Args: refer: string, ["INTERNAL", "EXTERNAL"], select internal or external power mode for controling. work_mode: string, ["POWER_ON", "POWER_DOWN", "STANDBY", "DIGITAL_RESET"], select AD9628's work mode. Examples: ad9628.power_mode("EXTERNAL", "POWER_ON") ''' assert refer in ["INTERNAL", "EXTERNAL"] if refer == "INTERNAL": self._internal_power_mode(work_mode) else: self._external_power_mode(work_mode) def soft_reset(self): ''' AD9628 internal function to AD9628 soft reset. Examples: ad9628.soft_reset() ''' port_con = self.read_register( AD9628Def.REGISTER_ADDRESS["port_config"]) port_con = ((port_con) | AD9628Def.SOFT_RESET) self.write_register( AD9628Def.REGISTER_ADDRESS["port_config"], port_con) def chop_mode_open(self): ''' AD9628 internal function to AD9628 enhancement control enable. Examples: ad9628.chop_mode_open() ''' self.write_register( AD9628Def.REGISTER_ADDRESS["enhancement_ctrl"], AD9628Def.CHOP_MODE_OPEN) def chop_mode_close(self): ''' AD9628 internal function to AD9628 enhancement control disable. Examples: ad9628.chop_mode_close() ''' self.write_register( AD9628Def.REGISTER_ADDRESS["enhancement_ctrl"], AD9628Def.CHOP_MODE_CLOSE) def test_mode_config(self, user_mode, rst_pn_l_en, rst_pn_s_en, output_mode): ''' AD9628 internal function to Config test data to AD9628's data output pins. Args: user_mode: string, ["SINGLE", "ALTERNATE_REPEAT", "ONCE", "ALTERNATE_ONCE"], select user test mode. rst_pn_l_en: string, ["EN_RESET_L", "DIS_RESET_L"], control reset PN long gen enable or not. rst_pn_s_en: string, ["EN_RESET_S", "DIS_RESET_S"], control reset PN short gen enable or not. output_mode: string, ["OFF", "MIDSCALE_SHORT", "POSITIVE_FS", "NEGATIVE_FS", "ALTERNATING_CHECKERBOARD", "PN_LONG_SEQUENCE", "PN_SHORT_SEQUENCE", "ONE_WORD_TOGGLE", "USER_TEST_MODE", "RAMP_OUTPUT"], select user output mode. Examples: ad9628.test_mode_config("SINGLE", "EN_RESET_L", "EN_RESET_S", "OFF") ''' assert rst_pn_l_en in ["EN_RESET_L", "DIS_RESET_L"] assert rst_pn_s_en in ["EN_RESET_S", "DIS_RESET_S"] assert user_mode in AD9628Def.TEST_MODE assert output_mode in AD9628Def.OUTPUT_TEST_MODE user_test_mode_con = AD9628Def.TEST_MODE[user_mode] output_test_mode_con = AD9628Def.OUTPUT_TEST_MODE[output_mode] test_mode_con = (user_test_mode_con | AD9628Def.DIS_RESET_PN_LONG_GEN | AD9628Def.DIS_RESET_PN_SHORT_GEN | output_test_mode_con) if rst_pn_l_en == "EN_RESET_L": if rst_pn_s_en == "EN_RESET_S": test_mode_con = (user_test_mode_con | AD9628Def.EN_RESET_PN_LONG_GEN | ( AD9628Def.EN_RESET_PN_SHORT_GEN) | output_test_mode_con) else: test_mode_con = (user_test_mode_con | AD9628Def.EN_RESET_PN_LONG_GEN | ( AD9628Def.DIS_RESET_PN_SHORT_GEN) | output_test_mode_con) else: if rst_pn_s_en == "EN_RESET_S": test_mode_con = ((user_test_mode_con) | AD9628Def.DIS_RESET_PN_LONG_GEN | ( AD9628Def.EN_RESET_PN_SHORT_GEN) | (output_test_mode_con)) else: test_mode_con = ((user_test_mode_con) | AD9628Def.DIS_RESET_PN_LONG_GEN | ( AD9628Def.DIS_RESET_PN_SHORT_GEN) | (output_test_mode_con)) self.write_register( AD9628Def.REGISTER_ADDRESS["test_mode"], test_mode_con) def bist_mode_open(self, init_sequence): ''' AD9628 internal function to BIST(build in self test) mode open. Args: init_sequence: string, ["INIT_SEQ", "NOT_INIT_SEQ"], init sequence or not in BIST(build in self test) mode. Examples: ad9628.bist_mode_open("INIT_SEQ") ''' assert init_sequence in ["INIT_SEQ", "NOT_INIT_SEQ"] bist_mode_con = AD9628Def.INIT_BIST_SEQ if init_sequence == "INIT_SEQ": bist_mode_con = AD9628Def.INIT_BIST_SEQ else: bist_mode_con = AD9628Def.NOT_INIT_BIST_SEQ self.write_register( AD9628Def.REGISTER_ADDRESS["bist_reg"], bist_mode_con) def bist_mode_close(self): ''' AD9628 internal function to BIST(build in self test) mode close. Examples: ad9628.bist_mode_close() ''' self.write_register( AD9628Def.REGISTER_ADDRESS["bist_reg"], 0X00) def customer_offset_adjust(self, data): ''' AD9628 internal function to Customer offset adjust when AD9628 config in twos complement format output type. Args: data: hexmial, [0x00~0xFF], write register value. Examples: ad9628.customer_offset_adjust(0x00) ''' assert isinstance(data, int) assert 0 <= data <= 0xFF self.write_register( AD9628Def.REGISTER_ADDRESS["customer_offset"], data) def user_define_pattern(self, pattern, format_data): ''' AD9628 internal function to AD9628 user pattern definetion config. Args: pattern: string, ["PATTERN_1", "PATTERN_2"], select which pattern to control. format_data: hexmial, [0x00~0xFFFF], write register value. Examples: ad9628.user_define_pattern("PATTERN_1", 0x01) ''' assert isinstance(format_data, int) assert pattern in ["PATTERN_1", "PATTERN_2"] assert 0x00 <= format_data <= 0xFFFF if pattern == "PATTERN_1": reg_address = AD9628Def.REGISTER_ADDRESS["user_pattern_1_L"] & 0x3FF else: reg_address = AD9628Def.REGISTER_ADDRESS["user_pattern_2_L"] & 0x3FF write_date = [] write_date.append((reg_address & 0xFF00) >> 8) write_date.append(reg_address & 0x00FF) write_date.append(format_data >> 8 & 0xFF) write_date.append(format_data & 0xFF) self.spi_bus.write(write_date) def user_io_ctrl(self, io, state): ''' AD9628 internal function to multiplexing pins control. Args: io: string, ["OEB", "PDWN", "VCM", "SDIO"], select which IO to control. state: string, ["EN_PIN", "DIS_PIN"], control pin's function disable(effective when pin is PDWN) or enable. Examples: ad9628.user_io_ctrl("OEB", "EN_PIN") ''' assert io in ["OEB", "PDWN", "VCM", "SDIO"] assert state in ["EN_PIN", "DIS_PIN"] if io == "OEB": rd_data = self.read_register( AD9628Def.REGISTER_ADDRESS["user_ctrl_reg_2"]) if state == "EN_PIN": wr_data = (rd_data & 0x7F | AD9628Def.EN_OEB_PIN) else: wr_data = (rd_data & 0x7F | AD9628Def.DIS_OEB_PIN) return self.write_register( AD9628Def.REGISTER_ADDRESS["user_ctrl_reg_2"], wr_data) elif io == "PDWN": rd_data = self.read_register( AD9628Def.REGISTER_ADDRESS["power_mode"]) if state == "EN_PIN": wr_data = (rd_data & 0xDF | AD9628Def.EN_PDWN_PIN) else: wr_data = (rd_data & 0xDF | AD9628Def.DIS_PDWN_PIN) return self.write_register( AD9628Def.REGISTER_ADDRESS["power_mode"], wr_data) elif io == "VCM": if state == "EN_PIN": wr_data = AD9628Def.VCM_POWER_ON else: wr_data = AD9628Def.VCM_POWER_DOWN return self.write_register( AD9628Def.REGISTER_ADDRESS["user_ctrl_reg_3"], wr_data) elif io == "SDIO": rd_data = self.read_register( AD9628Def.REGISTER_ADDRESS["user_ctrl_reg_2"]) if state == "EN_PIN": wr_data = (rd_data & 0xFE | 1) else: wr_data = (rd_data & 0xFE | 0) return self.write_register( AD9628Def.REGISTER_ADDRESS["user_ctrl_reg_2"], wr_data)
class MIK002004(SGModuleDriver): ''' MIK002004 is a high-pressure differential input digital audio module. compatible = ["GQQ-Q58K-5-040"] Args: i2c: instance(I2C), the instance of I2C bus. which will be used to used to control eeprom, sensor and io expander. ipcore: instance(MIXMIK002SGR)/string, the instance of Ipcore, which has 2 child IP, MIXFftAnalyzerSG, MIXGPIOSG. analyzer: instance(PLFFTAnalyzer)/string, if not given, will create emulator. adc_rst_pin: instance(GPIO), used to reset IIS of the CS5381. adc_ovfl_l_pin: instance(GPIO), used to get state of the ovfl_l pin for CS5381. i2s_left_select_pin: instance(GPIO), used to enable IIS left channel module. i2s_right_select_pin: instance(GPIO), used to enable IIS right channel module. i2s_both_select_pin: instance(GPIO), used to enable IIS both left & right channel module. tone_detect_pin: instance(GPIO), used to get state of the tone detect pin. upload_enable_pin: instance(GPIO), used to enable data upload to DMA directly. measure_enable_pin: instance(GPIO), used to enable FFT measure data. sample_rate: int, unit Hz, default 48000, used to configure the CS5381. Examples: # use non-aggregated IP i2c_bus = I2C('/dev/i2c-1') axi4 = AXI4LiteBus(analyzer_dev, 256) analyzer = MIXFftAnalyzerSG(axi4) adc_rst_pin = GPIO(88, 'output') adc_ovfl_l_pin = GPIO(93, 'output') i2s_left_select_pin = GPIO(88, 'output') i2s_right_select_pin = GPIO(89, 'output') i2s_both_select_pin = GPIO(87, 'output') tone_detect_pin = GPIO(94, 'output') upload_enable_pin = GPIO(97, 'output') measure_enable_pin = GPIO(95, 'output') warlock = MIK002004(i2c=i2c_bus, ipcore=None, analyzer=analyzer, adc_rst_pin=adc_rst_pin, adc_ovfl_l_pin=adc_ovfl_l_pin, i2s_left_select_pin=i2s_left_select_pin, i2s_right_select_pin=i2s_right_select_pin,i2s_both_select_pin=i2s_both_select_pin, tone_detect_pin=tone_detect_pin,upload_enable_pin=upload_enable_pin, measure_enable_pin=measure_enable_pin, sample_rate=48000) # use MIXMIK002SGR aggregated IP i2c_bus = I2C('/dev/i2c-1') ipcore = MIXMIK002SGR('/dev/MIX_MIK002_SG_R') warlock = MIK002004(i2c=i2c_bus, ipcore=ipcore) # measure left channel input in china mode. warlock.headphone_out('1Vrms', 'CH') warlock.measure('left', 20000, 5) # measure xtalk signal in china mode. warlock.hp2mic_xtalk('CH') warlock.xtalk_measure('hp2mic') ''' # launcher will use this to match driver compatible string and load driver if matched. compatible = ["GQQ-Q58K-5-040"] rpc_public_api = [ 'enable_upload', 'disable_upload', 'measure', 'xtalk_measure', 'mikey_tone', 'get_tone_detect_state', 'set_sampling_rate', 'get_sampling_rate', 'adc_hpf_state', 'headset_loopback', 'line_out', 'headphone_out', 'hp2mic_xtalk', 'io_set', 'io_dir_set', 'adc_reset', 'get_overflow_state', 'io_dir_read', 'io_read', 'start_record_data', 'stop_record_data' ] + SGModuleDriver.rpc_public_api def __init__(self, i2c, ipcore=None, analyzer=None, adc_rst_pin=None, adc_ovfl_l_pin=None, i2s_left_select_pin=None, i2s_right_select_pin=None, i2s_both_select_pin=None, tone_detect_pin=None, upload_enable_pin=None, measure_enable_pin=None, sample_rate=48000): assert sample_rate > 0 and sample_rate <= MIK002004Def.MAX_SAMPLING_RATE if (i2c and analyzer and adc_rst_pin and adc_ovfl_l_pin and i2s_left_select_pin and i2s_right_select_pin and i2s_both_select_pin and tone_detect_pin and upload_enable_pin and measure_enable_pin): self.i2c = i2c if isinstance(analyzer, basestring): self.analyzer = MIXXtalkMeasureSG(analyzer) else: self.analyzer = analyzer self.adc_rst_pin = adc_rst_pin self.adc_ovfl_l_pin = adc_ovfl_l_pin self.i2s_left_select_pin = i2s_left_select_pin self.i2s_right_select_pin = i2s_right_select_pin self.i2s_both_select_pin = i2s_both_select_pin self.tone_detect_pin = tone_detect_pin self.upload_enable_pin = upload_enable_pin self.measure_enable_pin = measure_enable_pin elif (ipcore and i2c): self.i2c = i2c if isinstance(ipcore, basestring): axi4_bus = AXI4LiteBus(ipcore, MIK002004Def.MIX_MIK002_REG_SIZE) self.ipcore = MIXMIK002SGR(axi4_bus) else: self.ipcore = ipcore self.analyzer = self.ipcore.analyzer self.adc_rst_pin = Pin(self.ipcore.gpio, MIK002004Def.ADC_RST_PIN) self.adc_ovfl_l_pin = Pin(self.ipcore.gpio, MIK002004Def.ADC_OVFL_L_PIN) self.i2s_left_select_pin = Pin(self.ipcore.gpio, MIK002004Def.I2S_LEFT_SELECT_PIN) self.i2s_right_select_pin = Pin(self.ipcore.gpio, MIK002004Def.I2S_RIGHT_SELECT_PIN) self.i2s_both_select_pin = Pin(self.ipcore.gpio, MIK002004Def.I2S_BOTH_SELECT_PIN) self.tone_detect_pin = Pin(self.ipcore.gpio, MIK002004Def.TONE_DETECT_PIN) self.upload_enable_pin = Pin(self.ipcore.gpio, MIK002004Def.UPLOAD_ENABLE_PIN) self.measure_enable_pin = Pin(self.ipcore.gpio, MIK002004Def.MEASURE_ENABLE_PIN) else: raise MIK002004Exception("Parameter error") self.eeprom = CAT24C32(MIK002004Def.EEPROM_DEV_ADDR, i2c) self.nct75 = NCT75(MIK002004Def.SENSOR_DEV_ADDR, i2c) self.cat9555 = CAT9555(MIK002004Def.CAT9555_DEV_ADDR, i2c) super(MIK002004, self).__init__(self.eeprom, self.nct75, range_table=warlock_range_table) self.sampling_rate = sample_rate self.scope = "" self.mode = "" def post_power_on_init(self, timeout=MIK002004Def.DEFAULT_TIMEOUT): ''' Init MIK002004 module to a know harware state. This function will reset adc and i2s module. Args: timeout: float, (>=0), default 1, unit Second, execute timeout. ''' self.reset(timeout) def reset(self, timeout=MIK002004Def.DEFAULT_TIMEOUT): ''' Reset the instrument module to a know hardware state. Args: timeout: float, (>=0), default 1, unit Second, execute timeout. Returns: string, "done", if execute successfully. ''' start_time = time.time() while True: try: self.adc_rst_pin.set_dir(MIK002004Def.IO_OUTPUT_DIR) self.adc_ovfl_l_pin.set_dir(MIK002004Def.IO_INPUT_DIR) self.i2s_left_select_pin.set_dir(MIK002004Def.IO_OUTPUT_DIR) self.i2s_right_select_pin.set_dir(MIK002004Def.IO_OUTPUT_DIR) self.i2s_both_select_pin.set_dir(MIK002004Def.IO_OUTPUT_DIR) self.tone_detect_pin.set_dir(MIK002004Def.IO_INPUT_DIR) self.upload_enable_pin.set_dir(MIK002004Def.IO_OUTPUT_DIR) self.measure_enable_pin.set_dir(MIK002004Def.IO_OUTPUT_DIR) # reset ADC self.adc_reset() self.i2s_left_select_pin.set_level(0) self.i2s_right_select_pin.set_level(0) self.i2s_both_select_pin.set_level(0) self.measure_enable_pin.set_level(0) self.upload_enable_pin.set_level(1) self.cat9555.set_ports([0x00, 0x1f]) self.cat9555.set_pins_dir([0x00, 0xf0]) self._get_default_sampling_rate() return "done" except Exception as e: if time.time() - start_time > timeout: raise MIK002004Exception("Timeout: {}".format(e.message)) return "done" def pre_power_down(self, timeout=MIK002004Def.DEFAULT_TIMEOUT): ''' Put the hardware in a safe state to be powered down. Args: timeout: float, (>=0), default 1, unit Second, execute timeout. ''' start_time = time.time() while True: try: self.adc_rst_pin.set_level(0) self.i2s_left_select_pin.set_level(0) self.i2s_right_select_pin.set_level(0) self.i2s_both_select_pin.set_level(0) return except Exception as e: if time.time() - start_time > timeout: raise MIK002004Exception("Timeout: {}".format(e.message)) def start_record_data(self, channel): ''' MIK002004 start collect the data and upload to DMA directly. Args: channel: string, ['left', 'right', 'both']. Returns: string, 'done', api execution successful. Examples: warlock.start_record_data('left') ''' assert channel in MIK002004Def.AUDIO_CHANNEL_LIST + ["both"] self._audio_channel_select(channel) time.sleep(MIK002004Def.GPIO_DELAY_MS / 1000.0) return "done" def stop_record_data(self): ''' MIK002004 stop collect the data. Returns: string, 'done', api execution successful. Examples: warlock.stop_record_data() ''' self.i2s_left_select_pin.set_level(0) self.i2s_right_select_pin.set_level(0) self.i2s_both_select_pin.set_level(0) time.sleep(MIK002004Def.GPIO_DELAY_MS / 1000.0) return "done" def _get_default_sampling_rate(self): io_states = self.cat9555.get_ports() target_pin = io_states[1] & MIK002004Def.sample_rate_pin_mask if target_pin in MIK002004Def.sampling_rate_select: self.sampling_rate = MIK002004Def.sampling_rate_select[target_pin] else: self.set_sampling_rate(self.sampling_rate) def _set_function_path(self, config, scope): ''' MIK002004 set function path Args: config: string, ['mode_select', 'range_select','mikey_tone', 'short_circuit_detect', 'extra']. scope: string, ['GB','CH','HP_1Vrms','HP_3.5Vrms','line_out','s1_enable', 's2_enable','s3_enable','all_disable','MIC_S0_enable', 'MIC_S0_disable','enable','disable','hp2mic_xtalk']. Returns: string, 'done', api execution successful. Examples: warlock._set_function_path('mode_select', 'GB') ''' assert config in warlock_function_info assert scope in warlock_function_info[config] bits = warlock_function_info[config][scope]['bits'] for bit in bits: self.cat9555.set_pin(bit[0], bit[1]) time.sleep(MIK002004Def.RELAY_DELAY_MS / 1000.0) def _volt_to_target_unit(self, scope, volt): ''' MIK002004 get target unit value from measured voltage. Args: scope: string, the range of channel measure. volt: float, the measured voltage. Returns: float, value. ''' assert scope in warlock_function_info['range_select'] return volt * warlock_function_info['range_select'][scope][ 'coefficient'] def _audio_channel_select(self, channel): ''' MIK002004 cs5381 chip channel select. Args: channel: string, ['left','right','both'], Use for select which channel to measure. Returns: string, 'done', api execution successful. Examples: warlock._audio_channel_select('left') ''' if channel == 'left': self.i2s_right_select_pin.set_level(0) self.i2s_both_select_pin.set_level(0) self.i2s_left_select_pin.set_level(1) elif channel == 'right': self.i2s_left_select_pin.set_level(0) self.i2s_both_select_pin.set_level(0) self.i2s_right_select_pin.set_level(1) else: self.i2s_both_select_pin.set_level(1) return "done" def enable_upload(self): ''' MIK002004 upoad mode open. Control audio upload data of ADC when doing measurement. It's not necessary enable upload when doing measurement. Note that data transfered into DMA is 32bit each data, but high 24bit data is valid. Low 8bit data is invalid. The data format is twos complement. Returns: string, 'done', api execution successful. ''' self.analyzer.enable_upload() return "done" def disable_upload(self): ''' MIK002004 upoad mode close. Close data upload doesn't influence to measure. Returns: string, 'done', api execution successful. ''' self.analyzer.disable_upload() return "done" def set_sampling_rate(self, sampling_rate): ''' MIK002004 set sampling rate. Args: sampling_rate: int, [192000, 96000, 48000], unit Hz, adc measure sampling rate. Returns: string, 'done', api execution successful. Examples: warlock.set_sampling_rate(96000) ''' assert str( sampling_rate) + "Hz" in warlock_function_info["adc_sample_rate"] io_dir_state = self.cat9555.get_pins_dir() if io_dir_state[1] & MIK002004Def.sample_rate_pin_mask != 0: self.cat9555.set_pins_dir([ io_dir_state[0], io_dir_state[1] & ~MIK002004Def.sample_rate_pin_mask ]) self._set_function_path("adc_sample_rate", str(sampling_rate) + "Hz") self.sampling_rate = sampling_rate if isinstance( sampling_rate, int) else int(sampling_rate) return "done" def get_sampling_rate(self): ''' MIK002004 get sampling rate. Returns: int, unit Hz. Examples: warlock.get_sampling_rate() ''' return self.sampling_rate def headset_loopback(self, mode): ''' MIK002004 set headset_loopback function. Args: mode: string, ['GB', 'CH'], GB mean global mode, CH mean china mode. Returns: string, 'done', api execution successful. Examples: warlock.headset_loopback('CH') ''' assert mode in warlock_function_info["mode_select"] self._set_function_path("extra", "headset_loopback") self._set_function_path("mode_select", mode) return "done" def headphone_out(self, range_name, mode): ''' MIK002004 set headphone_out function. Args: range_name: string, ['1Vrms', '3.5Vrms']. mode: string, ['GB', 'CH'], GB mean global mode, CH mean china mode. Returns: string, 'done', api execution successful. Examples: warlock.headphone_out('1Vrms', 'CH') ''' assert ("HP_" + range_name) in warlock_function_info["range_select"] assert mode in warlock_function_info["mode_select"] self._set_function_path("range_select", "HP_" + range_name) self._set_function_path("mode_select", mode) self.mode = mode self.scope = "HP_" + range_name return "done" def line_out(self): ''' MIK002004 set line_out function. Returns: string, 'done', api execution successful. Examples: warlock.line_out() ''' self._set_function_path("range_select", "line_out") self.scope = "line_out" return "done" def hp2mic_xtalk(self, mode, range_name="32ohm"): ''' MIK002004 set hp2mic_xtalk function. Args: mode: string, ['GB', 'CH'], GB mean global mode, CH mean china mode. range_name: string, ["32ohm", "400ohm"] Returns: string, 'done', api execution successful. Examples: warlock.hp2mic_xtalk('CH') ''' assert mode in warlock_function_info["mode_select"] self._set_function_path("hp2mic_xtalk", range_name) self._set_function_path("mode_select", mode) self.mode = mode self.scope = "HP_1Vrms" return "done" def io_set(self, io_list): ''' MIK002004 set io state, this is a debug function. Args: io_list: list, [[pin,state],[pin,state]], pin [0~15], state [0,1]. Returns: string, 'done', api execution successful. Examples: warlock.io_set([[0,1],[1,1]]) ''' assert isinstance(io_list, list) assert all([isinstance(io, list) and len(io) == 2 for io in io_list]) assert all( [io[0] in xrange(16) and io[1] in xrange(2) for io in io_list]) for io in io_list: self.cat9555.set_pin(io[0], io[1]) return "done" def io_dir_set(self, io_list): ''' MIK002004 set io direction, this is a debug function. Args: io_list: list, [[pin,state],[pin,state]], pin [0~15], state ['input','output']. Returns: string, 'done', api execution successful. Examples: warlock.io_dir_set([[0,'output'],[1,'output']]) ''' assert isinstance(io_list, list) assert all([isinstance(io, list) and len(io) == 2 for io in io_list]) assert all([ io[0] in xrange(16) and io[1] in ['output', 'input'] for io in io_list ]) for io in io_list: self.cat9555.set_pin_dir(io[0], io[1]) return 'done' def io_read(self, io_list): ''' MIK002004 set io state, this is a debug function. Args: io_list: list, [pinx,pinx,… ,pinx], pin x mean [0~15]. Returns: list, [[pin,state],[pin,state]], pin [0~15], state [0,1]. Examples: warlock.read_pin([0,1,2,7]) ''' assert isinstance(io_list, list) assert all([isinstance(io, int) for io in io_list]) assert all([0 <= io <= 15 for io in io_list]) data_list = [] for io in io_list: data_list.append([io, self.cat9555.get_pin(io)]) return data_list def io_dir_read(self, io_list): ''' MIK002004 set io direction, this is a debug function. Args: io_list: list, [pinx,pinx,… ,pinx], pin x mean [0~15]. Returns: list, [[pin,state],[pin,state]], pin [0~15], state ['input','output']. Examples: warlock.read_pin_dir([0,1]) ''' assert isinstance(io_list, list) assert all([isinstance(io, int) for io in io_list]) assert all([0 <= io <= 15 for io in io_list]) data_list = [] for io in io_list: data_list.append([io, self.cat9555.get_pin_dir(io)]) return data_list def adc_reset(self): ''' MIK002004 reset adc. Returns: string, 'done', api execution successful. Examples: warlock.adc_reset() ''' self.adc_rst_pin.set_level(0) time.sleep(MIK002004Def.RST_DELAY_MS / 1000.0) self.adc_rst_pin.set_level(1) return "done" def measure(self, channel, bandwidth_hz=50000, harmonic_num=5, decimation_type=0xFF): ''' MIK002004 measure signal's Vpp, RMS, THD+N, THD. Args: channel: string, ['left', 'right']. bandwidth_hz: int, [50~95977], default 50000, unit Hz, Measure signal's limit bandwidth, The frequency of the signal should not be greater than half of the bandwidth. harmonic_num: int, [2~10], default 5, Use for measuring signal's THD. decimation_type: int, [1~255], default 255, Decimation for FPGA to get datas, If decimation is 0xFF, FPGA will choose one suitable number. Returns: dict, {'vpp': value, 'freq': value, 'thd': value, 'thdn': value, 'rms': value), dict with vpp, freq, thd, thdn, rms. Examples: result = warlock.measure('left', 20000, 5, 255) print result['frequency'], result['vpp'], result['thdn'], result['thd'], result['rms'] ''' assert channel in MIK002004Def.AUDIO_CHANNEL_LIST assert bandwidth_hz == MIK002004Def.BANDWIDTH_AUTO or isinstance( bandwidth_hz, int) assert MIK002004Def.BANDWIDTH_MIN <= bandwidth_hz <= MIK002004Def.BANDWIDTH_MAX assert isinstance(harmonic_num, int) and\ MIK002004Def.HARMONIC_CNT_MIN <= harmonic_num <= MIK002004Def.HARMONIC_CNT_MAX assert isinstance(decimation_type, int) and\ MIK002004Def.DECIMATION_TYPE_MIN <= decimation_type <= MIK002004Def.DECIMATION_TYPE_MAX self.upload_enable_pin.set_level(0) self.stop_record_data() self._audio_channel_select("both") self.measure_enable_pin.set_level(1) self.analyzer.disable() self.analyzer.enable() self.analyzer.measure_select(channel, MIK002004Def.NORMAL_ANALYZE_MODE) self.analyzer.analyze_config(self.sampling_rate, decimation_type, bandwidth_hz, harmonic_num) self.analyzer.analyze() # calculate the actual vpp of HW by VREF vpp = self.analyzer.get_vpp() * MIK002004Def.AUDIO_ANALYZER_VREF vpp = self._volt_to_target_unit(self.scope, vpp) self.upload_enable_pin.set_level(1) # vpp = RMS * 2 * sqrt(2) rms = vpp / MIK002004Def.RMS_TO_VPP_RATIO range_name = (channel + "_" + MIK002004Def.RMS_LIST[self.scope]).upper() rms = self.calibrate(range_name, rms) vpp = rms * MIK002004Def.RMS_TO_VPP_RATIO result = dict() result["vpp"] = vpp result["freq"] = self.analyzer.get_frequency() result["thd"] = self.analyzer.get_thd() result["thdn"] = self.analyzer.get_thdn() result["snr"] = -1 * result["thdn"] result["rms"] = rms return result def _measure(self, channel, bandwidth_hz=50000, harmonic_num=5, decimation_type=1): ''' MIK002004 _measure signal's RMS for Crosstalk measure Args: channel: string, ['left', 'right']. bandwidth_hz: int, [50~95977], default 50000, unit Hz, Measure signal's limit bandwidth, The frequency of the signal should not be greater than half of the bandwidth. harmonic_num: int, [2~10], default 5, Use for measuring signal's THD. decimation_type: int, [1~255], default 1, Decimation for FPGA to get datas, If decimation is 0xFF, FPGA will choose one suitable number. Returns: float. Examples: result = warlock._measure('left', 20000, 5, 1) print result ''' assert channel in MIK002004Def.AUDIO_CHANNEL_LIST assert bandwidth_hz == MIK002004Def.BANDWIDTH_AUTO or isinstance( bandwidth_hz, int) assert MIK002004Def.BANDWIDTH_MIN <= bandwidth_hz <= MIK002004Def.BANDWIDTH_MAX assert isinstance(harmonic_num, int) and\ MIK002004Def.HARMONIC_CNT_MIN <= harmonic_num <= MIK002004Def.HARMONIC_CNT_MAX assert isinstance(decimation_type, int) and\ MIK002004Def.DECIMATION_TYPE_MIN <= decimation_type <= MIK002004Def.DECIMATION_TYPE_MAX self.upload_enable_pin.set_level(0) self.stop_record_data() self._audio_channel_select("both") self.measure_enable_pin.set_level(1) if channel == "left": self.analyzer.disable() self.analyzer.enable() self.analyzer.analyze_config(self.sampling_rate, decimation_type, bandwidth_hz, harmonic_num) self.analyzer.measure_select(channel, MIK002004Def.XTALK_ANALYZE_MODE) self.analyzer.analyze() # calculate the actual vpp of HW by VREF vpp = self.analyzer.get_vpp() * MIK002004Def.AUDIO_ANALYZER_VREF vpp = self._volt_to_target_unit(self.scope, vpp) self.upload_enable_pin.set_level(1) # vpp = RMS * 2 * sqrt(2) rms = vpp / MIK002004Def.RMS_TO_VPP_RATIO range_name = (channel + "_" + MIK002004Def.RMS_LIST[self.scope]).upper() rms = self.calibrate(range_name, rms) result = dict() result["vpp"] = vpp result["freq"] = self.analyzer.get_frequency() result["thd"] = self.analyzer.get_thd() result["thdn"] = self.analyzer.get_thdn() result["snr"] = -1 * result["thdn"] result["rms"] = rms return result def xtalk_measure(self, channel, bandwidth_hz=50000, harmonic_num=5, decimation_type=1): ''' MIK002004 Crosstalk mode measure. Args: channel: string, ['hp2mic', 'hpl2r', 'hpr2l']. bandwidth_hz: int, [50~95977], default 50000, unit Hz, Measure signal's limit bandwidth, The frequency of the signal should not be greater than half of the bandwidth. harmonic_num: int, [2~10], default 5, Use for measuring signal's THD. decimation_type: int, [1~255], default 1, Decimation for FPGA to get data, If decimation is 0xFF, FPGA will choose one suitable number. Returns: list, [value, db]. Examples: warlock.xtalk_measure('hp2mic') ''' assert channel in ["hp2mic", "hpl2r", "hpr2l"] left_result = self._measure("left", bandwidth_hz, harmonic_num, decimation_type) right_result = self._measure("right", bandwidth_hz, harmonic_num, decimation_type) Va = left_result["rms"] Vb = right_result["rms"] if channel == "hpr2l": xtalk = 20 * math.log(Va / Vb, 10) else: xtalk = 20 * math.log(Vb / Va, 10) return {"xtalk": xtalk, "left": left_result, "right": right_result} def mikey_tone(self, freq="default", mode="GB"): ''' MIK002004 set signal output. Args: freq: string, ['S1', 'S2', 'S3', 'S0', 'default'], 'default' mean (S0, S1, S2, S3)=1. mode: string, ['GB', 'CH'], default 'GB', GB mean global mode, CH mean china mode. Returns: string, 'done', api execution successful. Examples: warlock.mikey_tone('S1', 'CH') ''' assert freq in warlock_function_info["mikey_tone"] assert mode in warlock_function_info["mode_select"] self._set_function_path("mode_select", mode) self._set_function_path("mikey_tone", freq) return "done" def get_tone_detect_state(self): ''' MIK002004 get tone detect state. Returns: int, [1,0], 1 mean high level, 0 mean low level. Examples: warlock.get_tone_detect_state() ''' return self.tone_detect_pin.get_level() def get_overflow_state(self): ''' MIK002004 get overflow pin state. Returns: int, [1,0], 1 mean high level, 0 mean low level. Examples: warlock.get_overflow_state() ''' return self.adc_ovfl_l_pin.get_level() def adc_hpf_state(self, state): ''' MIK002004 set adc hpf pin. Args: state: string, ['enable', 'disable']. Returns: string, 'done', api execution successful. Examples: warlock.adc_hpf_state('enable') ''' assert state in ["enable", "disable"] if self.cat9555.get_pin_dir( MIK002004Def.ADC_HPF_PIN) != MIK002004Def.IO_OUTPUT_DIR: self.cat9555.set_pin_dir(MIK002004Def.ADC_HPF_PIN, MIK002004Def.IO_OUTPUT_DIR) self._set_function_path("ADC_HPF", state) return "done" def get_driver_version(self): ''' Get MIK002004 driver version. Returns: string, current driver version. ''' return __version__
class Audio005002(Audio005Base): ''' Audio005002 is a high resolution differential input/output digital audio module. Args: i2c: instance(I2C), the instance of I2C bus. which will be used to used to control eeprom, sensor and io expander. ipcore: instance(MIXAudio005SGR), the instance of MIXAudio005SGR, which include AD717x, FFT Analyzer, Signal Source, RAM Signal, Audio Cache and gpio function. If device name string is passed to the parameter, the ipcore can be instanced in the module. dma: instance(MIXDMASG)/None, the instance of MIXDMASG. which will be used to used left_ch_id: int/None, [0~15], Id of the left channel to be configured. right_ch_id: int/None, [0~15], Id of the right channel to be configured. dma_mem_size: int/None, [0~MIXDMASG_MEM_SIZE], default 1M, unit M, MIX_DMA_SG channel size. 1M=1024*1024 Byte and a point is 32 bit, which is 4 Byte, so 1M=1024*1024/4=262144 points. Examples: i2c = I2C('/dev/i2c-0') audio005002 = Audio005002(i2c, '/dev/MIX_Audio005002_SG_R') audio005002.post_power_on_init() # measure left channel input LEFT_CHANNEL = 1 audio005002.configure_input_channel(LEFT_CHANNEL, True, False, 48000) # start recording # wait for 1 s # collect samples # stop recording audio005002.configure_input_channel(LEFT_CHANNEL, False) data = audio005002.read(LEFT_CHANNEl, 8192) print data ''' rpc_public_api = [ 'configure_input_channel', 'get_input_channel_configuration', 'configure_output_channel', 'get_output_channel_configuration', 'read', 'write', 'get_noisefloor' ] + Audio005Base.rpc_public_api # launcher will use this to match driver compatible string and load driver if matched. compatible = ["GQQ-03C6-5-020", "GQQ-03C6-5-02A"] def __init__(self, i2c, ipcore, dma=None, left_ch_id=None, right_ch_id=None, dma_mem_size=1): super(Audio005002, self).__init__(i2c, ipcore) self.ram_signal = self.ipcore.ram_signal self.audio_cache = self.ipcore.audio_cache self.ram_out_pin = Pin(self.ipcore.gpio, Audio005002Def.RAM_OUT_PIN) self.adc_sample_rate = Audio005002Def.DEF_SAMPLING_RATE self.dac_sample_rate = Audio005002Def.AUDIO_SAMPLING_RATE self.vref = [ Audio005002Def.ANALYZER_2V_VOLT, Audio005002Def.ANALYZER_2V_VOLT ] self.input_enable = [False, False] self.output_enable = False self.model = Audio005002Def.AUDIO005002_CONFIG self.dma = dma if self.dma is not None: self.left_ch_id = left_ch_id self.right_ch_id = right_ch_id self.dma_mem_size = dma_mem_size if self.left_ch_id is None or self.right_ch_id is None or self.dma_mem_size is None: raise Audio005002Exception("dma parameter error") def read_hardware_config(self): ''' returns a string that represents the config ''' return "".join( [chr(c) for c in self.read_nvmem(Audio005002Def.CONFIG_ADDR, 3)]) def load_calibration(self): ''' Load calibration data. If GQQ is defined in eeprom, this function will load calibration defined. ''' self.model = self.read_hardware_config() if self.model == Audio005002Def.AUDIO005002_CONFIG: self._range_table = starlordii_range_table else: # Compatible with 02A model self._range_table = audio005002_range_table super(Audio005002, self).load_calibration() def post_power_on_init(self, timeout=Audio005002Def.TIME_OUT): ''' Init audio005 module to a know harware state. This function will reset reset dac/adc and i2s module. Args: timeout: float, (>=0), default 1, unit Second, execute timeout. ''' self.reset(timeout) if self.dma is not None: self.dma.config_channel(self.left_ch_id, self.dma_mem_size * 0X100000) self.dma.config_channel(self.right_ch_id, self.dma_mem_size * 0X100000) def reset(self, timeout=Audio005002Def.TIME_OUT): ''' Reset the instrument module to a know hardware state. Args: timeout: float, (>=0), default 1, unit Second, execute timeout. ''' start_time = time.time() while True: try: self.adc_rst_pin.set_dir(Audio005002Def.IO_DIR_OUTPUT) self.dac_rst_pin.set_dir(Audio005002Def.IO_DIR_OUTPUT) self.i2s_rx_en_pin.set_dir(Audio005002Def.IO_DIR_OUTPUT) self.i2s_tx_en_pin.set_dir(Audio005002Def.IO_DIR_OUTPUT) self.ram_out_pin.set_dir(Audio005002Def.IO_DIR_OUTPUT) # reset ADC self.adc_rst_pin.set_level(Audio005002Def.PIN_LEVEL_LOW) time.sleep(Audio005002Def.RELAY_DELAY_S) self.adc_rst_pin.set_level(Audio005002Def.PIN_LEVEL_HIGH) # reset DAC self.dac_rst_pin.set_level(Audio005002Def.PIN_LEVEL_LOW) time.sleep(Audio005002Def.RELAY_DELAY_S) self.dac_rst_pin.set_level(Audio005002Def.PIN_LEVEL_HIGH) # reset i2s rx self.i2s_rx_en_pin.set_level(Audio005002Def.PIN_LEVEL_LOW) # reset i2s tx self.i2s_tx_en_pin.set_level(Audio005002Def.PIN_LEVEL_LOW) self.ram_out_pin.set_level(Audio005002Def.PIN_LEVEL_LOW) # io init self.pca9536.set_pin_dir(Audio005002Def.ADC_CH_RIGHT_CTL_BIT, Audio005002Def.IO_DIR_OUTPUT) self.pca9536.set_pin_dir(Audio005002Def.ADC_CH_LEFT_CTL_BIT, Audio005002Def.IO_DIR_OUTPUT) self.pca9536.set_pin_dir(Audio005002Def.ADCM0_CTL_BIT, Audio005002Def.IO_DIR_OUTPUT) self.pca9536.set_pin_dir(Audio005002Def.ADCM1_CTL_BIT, Audio005002Def.IO_DIR_OUTPUT) self.pca9536.set_pin(Audio005002Def.ADC_CH_RIGHT_CTL_BIT, Audio005002Def.PIN_LEVEL_HIGH) self.pca9536.set_pin(Audio005002Def.ADC_CH_LEFT_CTL_BIT, Audio005002Def.PIN_LEVEL_HIGH) self.pca9536.set_pin(Audio005002Def.ADCM0_CTL_BIT, Audio005002Def.PIN_LEVEL_LOW) self.pca9536.set_pin(Audio005002Def.ADCM1_CTL_BIT, Audio005002Def.PIN_LEVEL_LOW) self.model = self.read_hardware_config() if self.model == Audio005002Def.AUDIO005002_CONFIG: self._range_table = starlordii_range_table else: # Compatible with 02A model self._range_table = audio005002_range_table return except Exception as e: if time.time() - start_time > timeout: raise Audio005002Exception("Timeout: {}".format(e.message)) def get_driver_version(self): ''' Get audio005 driver version. Returns: string, current driver version. ''' return __version__ def measure(self, channel, scope, bandwidth_hz, decimation_type=0xFF, sampling_rate=Audio005002Def.DEF_SAMPLING_RATE): ''' Measure audio input signal, which captures data using CS5361. Args: channel: string, ['left', 'right'], select input signal channel. scope: string, ['2V', '20mV'], AD7175 measurement range. bandwidth_hz: int/string, [42~48000], unit Hz, the signal bandwidth. In theory the bandwidth must smaller than half the sampling rate. eg, if sampling_rate = 192000, so bandwidth_hz < 96000. The bandwidth must be greater than the frequency of the input signal. decimation_type: int, [1~255], default 0xFF, sample data decimation. decimation_type is 1 means not to decimate. The smaller the input frequency, the larger the value should be. sampling_rate: int, [0~192000], default 48000, unit Hz, ADC sampling rate. Returns: dict, {'vpp': value, 'freq': value, 'thd': value, 'thdn': value, 'rms': value, 'noisefloor': value}, measurement result. ''' assert channel in Audio005002Def.AUDIO_CHANNEL_LIST assert scope in Audio005002Def.LNA_SCOPE if self.is_enable_upload is False: self.i2s_rx_en_pin.set_level(Audio005002Def.I2S_RX_ENABLE) pin = self.i2s_ch_select[Audio005002Def.SELECT_BIT0] pin.set_level(Audio005002Def.AUDIO_CHANNEL_LIST[channel][ Audio005002Def.SELECT_BIT0]) pin = self.i2s_ch_select[Audio005002Def.SELECT_BIT1] pin.set_level(Audio005002Def.AUDIO_CHANNEL_LIST[channel][ Audio005002Def.SELECT_BIT1]) if scope == Audio005002Def.LNA_RANGE_2V: self.pca9536.set_pin(Audio005002Def.ADC_CH_LIST[channel], Audio005002Def.SEL_GAIN_1) gain = Audio005002Def.AUDIO_ANALYZER_2V_VREF else: self.pca9536.set_pin(Audio005002Def.ADC_CH_LIST[channel], Audio005002Def.SEL_GAIN_100) gain = Audio005002Def.AUDIO_ANALYZER_20mV_VREF index = bisect.bisect(Audio005002Def.SAMPLING_RANGE, sampling_rate) pin_level = Audio005002Def.ADCM_CTL_LIST[index][ Audio005002Def.SELECT_BIT0] self.pca9536.set_pin(Audio005002Def.ADCM0_CTL_BIT, pin_level) pin_level = Audio005002Def.ADCM_CTL_LIST[index][ Audio005002Def.SELECT_BIT1] self.pca9536.set_pin(Audio005002Def.ADCM1_CTL_BIT, pin_level) self.analyzer.disable() self.analyzer.enable() self.analyzer.analyze_config(sampling_rate, decimation_type, bandwidth_hz) self.analyzer.analyze() freq = self.analyzer.get_frequency() harmonic_count = int(Audio005002Def.MAX_HARMONIC_WIDTH / freq) harmonic_count = 10 if harmonic_count > 10 else harmonic_count harmonic_count = 2 if harmonic_count <= 1 else harmonic_count self.analyzer.set_harmonic_count(harmonic_count) vpp = self.analyzer.get_vpp() * gain rms = vpp / Audio005002Def.RMS_TO_VPP_RATIO if scope == Audio005002Def.LNA_RANGE_2V: # Since the calibration range is within 20000Hz, the actual measurement result may be slightly # greater than 20000Hz when the input signal frequency is 20000Hz. # Therefore, the calibration frequency is appropriately increased to 20010Hz. if self.model == Audio005002Def.AUDIO005002_CONFIG: if freq > 20000: index = bisect.bisect(Audio005002Def.CAL_RANGE, freq - 10) else: index = bisect.bisect(Audio005002Def.CAL_RANGE, freq) range_name = "AUDIO_2V_" + str( Audio005002Def.CAL_RANGE[index]) + "Hz_" + channel else: # Compatible with 02A model range_name = "AUDIO_2V_" + channel else: range_name = "AUDIO_20mV_" + channel rms = self.calibrate(range_name, rms) vpp = rms * Audio005002Def.RMS_TO_VPP_RATIO thdn_value = self.analyzer.get_thdn() result = dict() result["vpp"] = (vpp, Audio005002Def.VOLT_UNIT_MV) result["freq"] = (freq, Audio005002Def.FREQ_UNIT_HZ) result["thd"] = (self.analyzer.get_thd(), Audio005002Def.THD_UNIT_DB) result["thdn"] = (thdn_value, Audio005002Def.THDN_UNIT_DB) result["rms"] = (rms, Audio005002Def.VOLT_UNIT_RMS) result["noisefloor"] = (10**(thdn_value / 20) * rms, Audio005002Def.VOLT_UNIT_RMS) if self.is_enable_upload is False: self.i2s_rx_en_pin.set_level(Audio005002Def.I2S_RX_DISABLE) return result def get_noisefloor(self, channel, scope, bandwidth_hz, decimation_type=0xFF, sampling_rate=192000): ''' Measure audio input signal, which captures data using CS5361. Args: channel: string, ['left', 'right'], select input signal channel. scope: string, ['2V', '20mV'], AD7175 measurement range. bandwidth_hz: int/string, [42~48000], unit Hz, the signal bandwidth. In theory the bandwidth must smaller than half the sampling rate. eg, if sampling_rate = 192000, so bandwidth_hz < 96000. The bandwidth must be greater than the frequency of the input signal. decimation_type: int, [1~255], default 0xFF, sample data decimation. decimation_type is 1 means not to decimate. The smaller the input frequency, the larger the value should be. sampling_rate: int, [0~192000], default 192000, unit Hz, ADC sampling rate. Returns: float, value, unit mVrms, Result of signal's noisefloor. ''' if self.is_enable_upload is False: self.i2s_rx_en_pin.set_level(Audio005002Def.I2S_RX_ENABLE) pin = self.i2s_ch_select[Audio005002Def.SELECT_BIT0] pin.set_level(Audio005002Def.AUDIO_CHANNEL_LIST[channel][ Audio005002Def.SELECT_BIT0]) pin = self.i2s_ch_select[Audio005002Def.SELECT_BIT1] pin.set_level(Audio005002Def.AUDIO_CHANNEL_LIST[channel][ Audio005002Def.SELECT_BIT1]) if scope == Audio005002Def.LNA_RANGE_2V: self.pca9536.set_pin(Audio005002Def.ADC_CH_LIST[channel], Audio005002Def.SEL_GAIN_1) gain = Audio005002Def.AUDIO_ANALYZER_2V_VREF else: self.pca9536.set_pin(Audio005002Def.ADC_CH_LIST[channel], Audio005002Def.SEL_GAIN_100) gain = Audio005002Def.AUDIO_ANALYZER_20mV_VREF index = bisect.bisect(Audio005002Def.SAMPLING_RANGE, sampling_rate) pin_level = Audio005002Def.ADCM_CTL_LIST[index][ Audio005002Def.SELECT_BIT0] self.pca9536.set_pin(Audio005002Def.ADCM0_CTL_BIT, pin_level) pin_level = Audio005002Def.ADCM_CTL_LIST[index][ Audio005002Def.SELECT_BIT1] self.pca9536.set_pin(Audio005002Def.ADCM1_CTL_BIT, pin_level) self.analyzer.disable() self.analyzer.enable() self.analyzer.analyze_config(sampling_rate, decimation_type, bandwidth_hz) self.analyzer.analyze() noisefloor = self.analyzer.get_noisefloor() * gain if self.is_enable_upload is False: self.i2s_rx_en_pin.set_level(Audio005002Def.I2S_RX_DISABLE) return noisefloor def enable_output(self, freq, vpp): ''' Audio005 CS5361 output audio sine waveform. Args: freq: int, [5~50000], unit Hz, output signal's frequency. vpp: float, [0~6504], unit mV, output signal's vpp. Returns: string, "done", execution successful. ''' assert Audio005002Def.OUTPUT_FREQ_MIN <= freq assert freq <= Audio005002Def.OUTPUT_FREQ_MAX assert Audio005002Def.OUTPUT_VPP_MIN <= vpp assert vpp <= Audio005002Def.OUTPUT_VPP_MAX vpp = self.calibrate(Audio005002Def.OUTPUT_CAL_ITEM, vpp) vpp = 0 if vpp < 0 else vpp # enable I2S tx module self.i2s_tx_en_pin.set_level(Audio005002Def.AUDIO_OUTPUT_ENABLE) self.ram_out_pin.set_level(Audio005002Def.USE_SIGNAL_SOURCE) self.signal_source.close() self.signal_source.open() # calculate vpp to vpp scale for FPGA vpp_scale = vpp * Audio005002Def.VPP_2_SCALE_RATIO self.signal_source.set_swg_paramter(Audio005002Def.AUDIO_SAMPLING_RATE, freq, vpp_scale, Audio005002Def.OUTPUT_SIGNAL_DUTY) self.signal_source.set_signal_type(Audio005002Def.OUTPUT_WAVE) self.signal_source.set_signal_time(Audio005002Def.SIGNAL_ALWAYS_OUTPUT) self.signal_source.output_signal() return "done" def configure_input_channel(self, channel, enable, enable_lna=False, sample_rate=Audio005002Def.DEF_SAMPLING_RATE): ''' Configure input channel based on given parameters. Args: channel: int, [1, 2], currently defined are 1(left), 2(right). enable: boolean, indicating if given path should be enabled. enable_lna: boolean, true if LNA should be enabled for given input path. sample_rate: int, [48000, 96000, 192000], Sample rate to use for given channel. Exception: InvalidHardwareChannel() if provided channel is not 1 or 2. InvalidSampleRate() if sample_rate is bad. Return: dict, { "channel" : 1, "sample_rate" : 48000, "enable" : True, "enable_lna" : True, "supported_sample_rates" : [48000, 96000, 192000] }, Actual channel configuration. ''' if channel not in [1, 2]: raise InvalidHardwareChannel("channel is not 1 or 2") if sample_rate not in [48000, 96000, 192000]: raise InvalidSampleRate("sample_rate is bad") ADC_CH_LIST = {1: 1, 2: 0} CHANNEL_LIST = {1: self.left_ch_id, 2: self.right_ch_id} code = channel - 1 if enable is True: self.i2s_rx_en_pin.set_level(Audio005002Def.I2S_RX_ENABLE) if enable_lna is True: self.pca9536.set_pin(ADC_CH_LIST[channel], Audio005002Def.SEL_GAIN_100) self.vref[code] = Audio005002Def.ANALYZER_20mV_VOLT else: self.pca9536.set_pin(ADC_CH_LIST[channel], Audio005002Def.SEL_GAIN_1) self.vref[code] = Audio005002Def.ANALYZER_2V_VOLT index = bisect.bisect(Audio005002Def.SAMPLING_RANGE, sample_rate) pin_level = Audio005002Def.ADCM_CTL_LIST[index][ Audio005002Def.SELECT_BIT0] self.pca9536.set_pin(Audio005002Def.ADCM0_CTL_BIT, pin_level) pin_level = Audio005002Def.ADCM_CTL_LIST[index][ Audio005002Def.SELECT_BIT1] self.pca9536.set_pin(Audio005002Def.ADCM1_CTL_BIT, pin_level) self.adc_sample_rate = sample_rate if enable is True: time.sleep(Audio005002Def.PIN_RELAY_DELAY_S) self.dma.enable_channel(CHANNEL_LIST[channel]) self.dma.reset_channel(CHANNEL_LIST[channel]) self.input_enable[code] = True else: self.i2s_rx_en_pin.set_level(Audio005002Def.I2S_RX_DISABLE) self.dma.disable_channel(CHANNEL_LIST[channel]) self.input_enable[code] = False result = dict() result["channel"] = channel result["sample_rate"] = self.adc_sample_rate result["enable"] = enable result["enable_lna"] = enable_lna result["supported_sample_rates"] = [48000, 96000, 192000] return result def get_input_channel_configuration(self, channel): ''' Returns the configuration of the specified channel. Args: channel: int, [1, 2], the desired channel. Exception: InvalidHardwareChannel() if provided channel is not 1 or 2. Returns: dict, { "channel" : 1, "sample_rate" : 48000, "enable" : True, "enable_lna" : True, "supported_sample_rates" : [48000, 96000, 192000] }. ''' if channel not in [1, 2]: raise InvalidHardwareChannel("channel is not 1 or 2") code = channel - 1 if self.input_enable[code] is True: enable = True else: enable = False if self.vref[code] == Audio005002Def.ANALYZER_20mV_VOLT: enable_lna = True else: enable_lna = False result = dict() result["channel"] = channel result["sample_rate"] = self.adc_sample_rate result["enable"] = enable result["enable_lna"] = enable_lna result["supported_sample_rates"] = [48000, 96000, 192000] return result def configure_output_channel( self, channel, enable, sample_rate=Audio005002Def.AUDIO_SAMPLING_RATE): ''' Configure output channel based on given parameters. Args: channel: int, [1, 2], currently defined are 1(left), 2(right). enable: boolean, indicating if given path should be enabled. sample_rate: int, [192000], Sample rate to use for given channel. Exception: InvalidHardwareChannel() if provided channel is not 1 or 2. InvalidSampleRate() if sample_rate is bad. Return: dict, { "channel" : 1, "sample_rate" : 192000, "enable" : True, "supported_sample_rates" : [192000] } ''' if channel not in [1, 2]: raise InvalidHardwareChannel("channel is not 1 or 2") if sample_rate != Audio005002Def.AUDIO_SAMPLING_RATE: raise InvalidSampleRate("sample_rate is bad") if enable is True: self.i2s_tx_en_pin.set_level(Audio005002Def.AUDIO_OUTPUT_ENABLE) self.ram_out_pin.set_level(Audio005002Def.USE_RAM) self.output_enable = True else: self.i2s_tx_en_pin.set_level(Audio005002Def.AUDIO_OUTPUT_DISABLE) self.output_enable = False result = dict() result["channel"] = channel result["sample_rate"] = sample_rate result["enable"] = enable result["supported_sample_rates"] = [192000] return result def get_output_channel_configuration(self, channel): ''' Returns the configuration of the specified channel. Args: channel: int, [1, 2], the desired channel. Exception: InvalidHardwareChannel() if provided channel is not 1 or 2 Returns: dict, { "channel" : 1, "sample_rate" : 192000, "enable" : True, "supported_sample_rates" : [192000] } ''' if channel not in [1, 2]: raise InvalidHardwareChannel("channel is not 1 or 2") if self.output_enable is True: enable = True else: enable = False result = dict() result["channel"] = channel result["sample_rate"] = self.dac_sample_rate result["enable"] = enable result["supported_sample_rates"] = [192000] return result def _conversion_voltage_value(self, vref, data): ''' Converts 32-bit data to voltage values. Args: vref: float, unit mV. data: list. Returns: list, voltage, unit mV. ''' data >>= 8 value = data & 0xffffff if (value >= pow(2, 23)): volt = value - pow(2, 24) else: volt = value volt = float(volt) / pow(2, 23) volt = volt * vref return volt def _read_voltage_points(self, channel, samples_per_channel, timeout=1000): ''' Args: channel: int, [1, 2]. samples_per_channel: int. number of points read. timeout: int, (>=0), default 1000, unit ms, execute timeout. Returns: list, unit mV, voltage. ''' CHANNEL_LIST = {1: self.left_ch_id, 2: self.right_ch_id} code = channel - 1 result, data, num, overflow = self.dma.read_channel_data( CHANNEL_LIST[channel], samples_per_channel * 4, timeout) if result == 0: data_list = data[:num] else: raise Audio005002Exception("get dma data error:{}".format(result)) data_info = [] data_len = len(data_list) for one_payload in range(0, data_len, 4): data = data_list[one_payload + 0] | data_list[one_payload + 1] << 8 | \ data_list[one_payload + 2] << 16 | data_list[one_payload + 3] << 24 if data & 0x05 != 0x00: continue data = self._conversion_voltage_value(self.vref[code], data) data_info.append(data) return data_info def read(self, channels, samples_per_channel, timeout=10.0): ''' Args: channels: int/list, [1, 2], the desired channel(s). samples_per_channel: int. number of points read. timeout: float, (>=0), default 10.0, unit Second, execute timeout. Exception: InvalidHardwareChannel() if provided channel is not 1 or 2 InvalidSampleCount() if samples_per_channel is of bad value InvalidTimeout() if invalid timeout value Returns: list, unit mv, ADC measurement data. ''' if isinstance(channels, int) and channels not in [1, 2]: raise InvalidHardwareChannel("channel is not 1 or 2") if isinstance(channels, list) and (0 == len(channels) or len(channels) > 2): raise InvalidHardwareChannel("channel is not 1 or 2") if isinstance( channels, list) and 1 == len(channels) and channels[0] not in [1, 2]: raise InvalidHardwareChannel("channel is not 1 or 2") if isinstance( channels, list) and 2 == len(channels) and channels[0] not in [1, 2]: raise InvalidHardwareChannel("channel is not 1 or 2") if isinstance( channels, list) and 2 == len(channels) and channels[1] not in [1, 2]: raise InvalidHardwareChannel("channel is not 1 or 2") if samples_per_channel < Audio005002Def.MINI_NUM: raise InvalidSampleCount("samples_per_channel is of bad value") if timeout < 0 or timeout > 10.0: raise InvalidTimeout("invalid timeout value") data_info = [] start_time = time.time() while True: try: if isinstance(channels, list): for i in range(len(channels)): data = self._read_voltage_points( channels[i], samples_per_channel, int(timeout * 1000)) if len(channels) == 2: data_info.append(data) else: data_info = data else: data_info = self._read_voltage_points( channels, samples_per_channel, int(timeout * 1000)) return data_info except Exception as e: if time.time() - start_time > timeout: raise InvalidTimeout("Timeout") if e.__class__.__name__ == 'Audio005002Exception': raise Audio005002Exception("{}".format(str(e))) def write(self, channel, data, repeat): ''' Playback a waveform defined by data array for "repeat" times. Output sample_rate must be setup by means configure_output_channel. Args: channel: int, [1, 2], currently defined are 1(playback). data: list, unit mv, list of data samples in volt. repeat: int, number of repeats. Returns: string, "done", execution successful. ''' assert len(data) >= Audio005002Def.MINI_NUM assert len(data) <= Audio005002Def.OUTPUT_MAX_NUM assert repeat >= Audio005002Def.MINI_NUM w_data = [] for volt in data: volt = int(volt / Audio005002Def.OUTPUT_VOLT_MAX * pow(2, 23)) if volt < 0: volt = pow(2, 24) + volt volt = volt << 8 w_data.append(volt) ram_addr = 0 self.ram_signal.read_disable() self.ram_signal.set_number_of_repeats([repeat]) start_time = time.time() while ram_addr != (len(w_data) - 1): self.ram_signal.disable() # Due to the existence of capacitance, it is necessary to delay # the capacitor discharge for more than 32ms after the loss of energy time.sleep(Audio005002Def.OUTPUT_RELAY_DELAY_S) self.ram_signal.enable() self.ram_signal.set_read_ramend_addr(len(w_data) - 1) self.ram_signal.set_tx_data(w_data) ram_addr = self.ram_signal.get_write_ramend_addr() if time.time() - start_time > Audio005002Def.TIME_OUT: raise Audio005002Exception("write timeout") self.ram_signal.read_enable() return "done"
class BeastBase(SGModuleDriver): ''' Base class of Beast and BeastCompatible. Providing common Beast methods. Args: i2c: instance(I2C), used to control eeprom and nct75 sensor. range_ctrl_pin: instance(GPIO), used to control Beast range. dcac_ctrl_pin: instance(GPIO), used to control DC/AC mode. output_ctrl_pin: instance(GPIO), used to control AD5231 disable/enable state. pl_signal_meter: instance(PLSignalMeter), used to access PLSignalMeter ipcore. ipcore: instance(MIXDAQT2SGR), used to access PLGPIO, PLSignalMeter ipcore. eeprom_dev_addr: int, eeprom device address. sensor_dev_addr: int, NCT75 device address. ''' rpc_public_api = [ 'select_range', 'enable_continuous_measure', 'disable_continuous_measure', 'set_measure_mask', 'frequency_measure', 'vpp_measure', 'rms_measure', 'level', 'adc_enable', 'adc_disable' ] + SGModuleDriver.rpc_public_api def __init__(self, i2c, range_ctrl_pin=None, dcac_ctrl_pin=None, output_ctrl_pin=None, pl_signal_meter=None, ipcore=None, eeprom_dev_addr=BeastDef.EEPROM_DEV_ADDR, sensor_dev_addr=BeastDef.SENSOR_DEV_ADDR): if i2c is None: super(BeastBase, self).__init__(None, None, range_table=beast_range_table) else: self.eeprom = CAT24C32(eeprom_dev_addr, i2c) self.nct75 = NCT75(sensor_dev_addr, i2c) super(BeastBase, self).__init__(self.eeprom, self.nct75, range_table=beast_range_table) if i2c and range_ctrl_pin and dcac_ctrl_pin and output_ctrl_pin and pl_signal_meter and not ipcore: self.range_ctrl_pin = range_ctrl_pin self.dcac_ctrl_pin = dcac_ctrl_pin self.output_ctrl_pin = output_ctrl_pin if isinstance(pl_signal_meter, basestring): axi4_bus = AXI4LiteBus(pl_signal_meter, BeastDef.SIGNAL_METER_REG_SIZE) self.pl_signal_meter_device = MIXSignalMeterSG(axi4_bus) else: self.pl_signal_meter_device = pl_signal_meter elif i2c and not range_ctrl_pin and not dcac_ctrl_pin and not output_ctrl_pin and not pl_signal_meter \ and ipcore: if isinstance(ipcore, basestring): axi4_bus = AXI4LiteBus(ipcore, BeastDef.MIX_DAQT2_REG_SIZE) self.mix_daqt2 = MIXDAQT2SGR(axi4_bus, use_signal_meter1=False, use_spi=False, use_gpio=True) else: self.mix_daqt2 = ipcore self.pl_signal_meter_device = self.mix_daqt2.signal_meter0 self.range_ctrl_pin = Pin(self.mix_daqt2.gpio, BeastDef.BEAST_RANGE_CTRL_PIN) self.dcac_ctrl_pin = Pin(self.mix_daqt2.gpio, BeastDef.BEAST_DCAC_CTRL_PIN) self.output_ctrl_pin = Pin(self.mix_daqt2.gpio, BeastDef.BEAST_OUTPUT_CTRL_PIN) elif i2c and range_ctrl_pin and dcac_ctrl_pin and output_ctrl_pin and not pl_signal_meter and ipcore: if isinstance(ipcore, basestring): axi4_bus = AXI4LiteBus(ipcore, BeastDef.MIX_DAQT2_REG_SIZE) self.mix_daqt2 = MIXDAQT2SGR(axi4_bus, use_signal_meter1=False, use_spi=False, use_gpio=False) else: self.mix_daqt2 = ipcore self.pl_signal_meter_device = self.mix_daqt2.signal_meter0 self.range_ctrl_pin = range_ctrl_pin self.dcac_ctrl_pin = dcac_ctrl_pin self.output_ctrl_pin = output_ctrl_pin else: raise BeastBaseException("Invalid parameter, please check") self._mask = 0 def post_power_on_init(self, timeout=BeastDef.TIME_OUT): ''' Init Beast module to a know harware state. This function will set gpio pin io direction to output and set default range to '20Vpp' Args: timeout: float, unit Second, execute timeout ''' self.reset(timeout) def pre_power_down(self, timeout=BeastDef.TIME_OUT): ''' Put the hardware in a safe state to be powered down. This function will set gpio pin io direction to output. Args: timeout: float, unit Second, execute timeout ''' self.range_ctrl_pin.set_dir(BeastDef.PIN_OUTPUT_DIRECTION) self.dcac_ctrl_pin.set_dir(BeastDef.PIN_OUTPUT_DIRECTION) self.output_ctrl_pin.set_dir(BeastDef.PIN_OUTPUT_DIRECTION) def reset(self, timeout=BeastDef.TIME_OUT): ''' Reset the instrument module to a know hardware state. Args: timeout: float, unit Second, execute timeout. ''' self.range_ctrl_pin.set_dir(BeastDef.PIN_OUTPUT_DIRECTION) self.dcac_ctrl_pin.set_dir(BeastDef.PIN_OUTPUT_DIRECTION) self.output_ctrl_pin.set_dir(BeastDef.PIN_OUTPUT_DIRECTION) # Use max range is 20 Vpp as initial range self.select_range('AC_VOLT', 20) def get_driver_version(self): ''' Get beast driver version. Returns: string, current driver version. ''' return __version__ def select_range(self, signal_type, value): ''' Select Beast measurement range. All support range shown as below. Args: signal_type: string, ['DC_VOLT', 'AC_VOLT'], Range signal_type string. value: int, [2, 20], Range value. Returns: string, "done", api execution successful. ''' assert signal_type in ["DC_VOLT", "AC_VOLT"] assert value in BeastDef.RANGE_VALUE_LIST if signal_type == "DC_VOLT": self.dcac_ctrl_pin.set_level(BeastDef.IO_LOW_LEVEL) else: self.dcac_ctrl_pin.set_level(BeastDef.IO_HIGH_LEVEL) if value == BeastDef.RANGE_VALUE_LIST[0]: self.board_gain = BeastDef.GAIN1 self.range_ctrl_pin.set_level(BeastDef.IO_LOW_LEVEL) else: self.board_gain = BeastDef.GAIN10 self.range_ctrl_pin.set_level(BeastDef.IO_HIGH_LEVEL) self.signal_type = signal_type return 'done' def enable_continuous_measure(self): ''' Beast enable_continuous_measure function. board data will be copyed to dma when this function called. Returns: string, "done", api execution successful. ''' self.adc_enable() self.pl_signal_meter_device.open() self.pl_signal_meter_device.enable_upframe(BeastDef.UPFRAME_MODE) return 'done' def disable_continuous_measure(self): ''' Beast disable_continuous_measure function. Data will disable upload, when this function called. Returns: string, "done", api execution successful. ''' self.adc_disable() self.pl_signal_meter_device.disable_upframe() self.pl_signal_meter_device.close() return 'done' def set_measure_mask(self, mask=0): ''' Beast set signal meter measure mask. Args: mask: int, default 0, mask bit value shown as below. +---------------+-------------------+ | mask | function | +===============+===================+ | bit[0:3] | Reserved | +---------------+-------------------+ | bit[4] | Frequency mask | +---------------+-------------------+ | bit[5] | Duty mask | +---------------+-------------------+ | bit[6] | Vpp mask | +---------------+-------------------+ | bit[7] | rms mask | +---------------+-------------------+ Returns: string, "done", api execution successful. ''' self._mask = mask return "done" def frequency_measure(self, duration, measure_type="LP"): ''' Measure input signal frequncy and duty in a defined duration. Args: duration: int, [1~2000], millisecond time to measure frequncy. measure_type: string, ["HP", "LP"], default "LP", type of measure. Returns: list, [value, value], result value contain freq and duty, units are Hz, %. Examples: # adc_enable() and adc_disable() will operate on Pin # which might belongs to another i2c Mux port from on-board eeprom so put outside. beast.adc_enable() ret = beast.frequency_measure(10) beast.adc_disable() # ret will be list: [freq, duty] ''' assert isinstance(duration, int) and duration > 0 and duration <= 2000 assert measure_type in ["HP", "LP"] self.pl_signal_meter_device.open() self.pl_signal_meter_device.set_vpp_interval( BeastDef.BEAST_VPP_DURATION) self.pl_signal_meter_device.start_measure(duration, BeastDef.BEAST_SAMPLE_RATE, self._mask) freq = self.pl_signal_meter_device.measure_frequency(measure_type) duty = self.pl_signal_meter_device.duty() self.pl_signal_meter_device.close() return [freq, duty] def vpp_measure(self, duration): ''' Measure input signal vpp, max and min voltage in a defined duration. Args: duration: int, [1~2000], millisecond time to measure vpp. Returns: list, [value, value, value], result value contain vpp, max and min voltage, unit is mV. Examples: # adc_enable() and adc_disable() will operate on Pin # which might belongs to another i2c Mux port from on-board eeprom so put outside. beast.adc_enable() ret = beast.vpp_measure(10) beast.adc_disable() # ret will be list: [vpp, max, min] ''' assert isinstance(duration, int) and duration > 0 and duration <= 2000 self.pl_signal_meter_device.open() self.pl_signal_meter_device.set_vpp_interval( BeastDef.BEAST_VPP_DURATION) self.pl_signal_meter_device.start_measure(duration, BeastDef.BEAST_SAMPLE_RATE, self._mask) result = self.pl_signal_meter_device.vpp() vpp = (result[2] * BeastDef.VPP_SCALE_GAIN + BeastDef.VPP_SCALE_OFFSET) * self.board_gain max_data = (result[0] * BeastDef.SCALE_GAIN + BeastDef.SCALE_OFFSET) * self.board_gain min_data = (result[1] * BeastDef.SCALE_GAIN + BeastDef.SCALE_OFFSET) * self.board_gain if self.is_use_cal_data(): if self.board_gain == BeastDef.GAIN1: vpp = self.calibrate('VPP_2V', vpp) else: if self.frequency_measure( BeastDef.DURATION )[0] <= BeastDef.BEAST_HIGH_FREQ_THRESHOLD: vpp = self.calibrate('LOW_FREQ_VPP_20V', vpp) else: vpp = self.calibrate('HIGH_FREQ_VPP_20V', vpp) self.pl_signal_meter_device.close() return [vpp, max_data, min_data] def rms_measure(self, duration): ''' Measure input signal rms and average voltage in a defined duration. Args: duration: int, [1~2000], millisecond time to measure rms. Returns: list, [value, value], result value contain rms and average voltage, unit is mV. Examples: # adc_enable() and adc_disable() will operate on Pin # which might belongs to another i2c Mux port from on-board eeprom so put outside. beast.adc_enable() ret = beast.rms_measure(10) beast.adc_disable() # ret will be list: [rms, average] ''' assert isinstance(duration, int) and duration > 0 and duration <= 2000 self.pl_signal_meter_device.open() self.pl_signal_meter_device.set_vpp_interval( BeastDef.BEAST_VPP_DURATION) self.pl_signal_meter_device.start_measure(duration, BeastDef.BEAST_SAMPLE_RATE, self._mask) result = self.pl_signal_meter_device.rms() rms = (result[0] * BeastDef.RMS_SCALE_GAIN + BeastDef.RMS_SCALE_OFFSET) * self.board_gain average = (result[1] * BeastDef.SCALE_GAIN + BeastDef.SCALE_OFFSET) * self.board_gain if self.is_use_cal_data() and self.signal_type == "AC_VOLT": if self.board_gain == BeastDef.GAIN1: rms = self.calibrate('RMS_2V', rms) else: if self.frequency_measure( BeastDef.DURATION )[0] <= BeastDef.BEAST_HIGH_FREQ_THRESHOLD: rms = self.calibrate('LOW_FREQ_RMS_20V', rms) else: rms = self.calibrate('HIGH_FREQ_RMS_20V', rms) self.pl_signal_meter_device.close() return [rms, average] def level(self): ''' Get current voltage level. Returns: int, [0, 1], 0 for low level, 1 for high level. ''' return self.pl_signal_meter_device.level() def adc_enable(self): ''' This function is used for enable adc output, it is an internal interface function. Returns: string, "done", api execution successful. ''' self.output_ctrl_pin.set_level(BeastDef.IO_LOW_LEVEL) return "done" def adc_disable(self): ''' This function is used for disable adc output, it is an internal interface function. Returns: string, "done", api execution successful. ''' self.output_ctrl_pin.set_level(BeastDef.IO_HIGH_LEVEL) return "done"
class MIXMagneto002SGR(object): ''' MIXMagneto002SGR, support GPIO, FFT. Args: axi4_bus: Instance(AXI4LiteBus)/string, axi4lite instance or dev path fft_data_cnt: int, get fft absolute data count, if not give, with get count from register. Example: magneto002 = MIXMagneto002SGR('/dev/MIX_Magneto002_SG_R_0') ''' def __init__(self, axi4_bus, fft_data_cnt=None): if isinstance(axi4_bus, basestring): # device path; create axi4lite instance self.axi4_bus = AXI4LiteBus(axi4_bus, MIXMagneto002SGRDef.REG_SIZE) else: self.axi4_bus = axi4_bus self.fft_analyzer_axi4_bus = AXI4LiteSubBus( self.axi4_bus, MIXMagneto002SGRDef.MIX_FFT_ANAYLZER_IPCORE_ADDR, MIXMagneto002SGRDef.MIX_FFT_REG_SIZE) self.gpio_axi4_bus = AXI4LiteSubBus( self.axi4_bus, MIXMagneto002SGRDef.MIX_GPIO_IPCORE_ADDR, MIXMagneto002SGRDef.MIX_GPIO_REG_SIZE) self.gpio = MIXGPIOSG(self.gpio_axi4_bus) self.i2s_conf_0 = Pin(self.gpio, MIXMagneto002SGRDef.I2S_CONF0_BIT) self.i2s_conf_1 = Pin(self.gpio, MIXMagneto002SGRDef.I2S_CONF1_BIT) self.cs5361_ovfl = Pin(self.gpio, MIXMagneto002SGRDef.CS5361_OVFL_BIT) self.cs5361_rst = Pin(self.gpio, MIXMagneto002SGRDef.CS5361_RST_BIT) self.i2s_en = Pin(self.gpio, MIXMagneto002SGRDef.I2S_EN_BIT) self.analyzer = MIXFftAnalyzerSG(self.fft_analyzer_axi4_bus, fft_data_cnt) self.reset() def reset(self): self.i2s_conf_0.set_dir('output') self.i2s_conf_1.set_dir('output') self.cs5361_ovfl.set_dir('input') self.cs5361_rst.set_dir('output') self.i2s_en.set_dir('output') # disable ipcore self.i2s_en.set_level(0) self.config('left') # reset ic self.cs5361_rst.set_level(0) time.sleep(MIXMagneto002SGRDef.DELAY) self.cs5361_rst.set_level(1) def config(self, mode): ''' Config. Args: mode: string, ['left', 'right', 'differential'], data mode. Example: magneto002.config('right') ''' assert mode in MIXMagneto002SGRDef.I2S_DATA_MODE self.i2s_conf_0.set_level(MIXMagneto002SGRDef.I2S_DATA_MODE[mode][1]) self.i2s_conf_1.set_level(MIXMagneto002SGRDef.I2S_DATA_MODE[mode][0]) return 'done' def get_driver_version(self): return __version__
class DarkBeastBase(MIXBoard): ''' Base class of DarkBeast and DarkBeastCompatible. Providing common DarkBeast methods Args: firmware_path: string, DarkBeast firmware absolute path pl_uart_drv_ko_file: string, DarkBeast pl_uart_drv.ko drive absolute path if None creating emulator. i2c: instance(I2C)/None, Class instance of PLI2CBus, which is used to control cat9555, eeprom and AD5667 sensor. gpio: instance(GPIO)/None, Class instance of PLGPIO, UART RX connect AID to enable or disable. if None creating emulator. pull_up_pin: instance(GPIO)/None, Class instance of GPIO, data line pull-up control, Provider mode. if None creating emulator. pull_down_pin: instance(GPIO)/None, Class instance of GPIO, data line pull-down control, Consumer mode. if None creating emulator. tx_en_pin: instance(GPIO)/None, Class instance of GPIO, UART_TX buffer enable control. if None creating emulator. connect_det_pin: instance(GPIO)/None, Class instance of GPIO, input pin control, detects whether the DUT is connected. if None creating emulator. removal_det_pin: instance(GPIO)/None, Class instance of GPIO, Input pin control, detect disconnection from DUT. if None creating emulator. a0_pin: instance(GPIO)/None, Class instance of Pin, if None creating emulator. a1_pin: instance(GPIO)/None, Class instance of Pin, if None creating emulator. a2_pin: instance(GPIO)/None, Class instance of Pin, if None creating emulator. elaod_en_pin: instance(GPIO)/None, Class instance of Pin, if None creating emulator. eeprom_devaddr: int, Eeprom device address. ''' rpc_public_api = [ 'module_init', 'close', 'open', 'communicate', 'aid_connect_set', 'io_set', 'io_get' ] + MIXBoard.rpc_public_api def __init__(self, firmware_path, pl_uart_drv_ko_file, i2c, pull_up_pin, pull_down_pin, tx_en_pin, removal_det_pin, connect_det_pin, a0_pin, a1_pin, a2_pin, elaod_en_pin, eeprom_devaddr, gpio): self.path = firmware_path self.process = None self.pl_uart_drv_ko_file = pl_uart_drv_ko_file if (firmware_path == '' and pl_uart_drv_ko_file == '' and i2c is None and pull_up_pin is None and pull_down_pin is None and tx_en_pin is None and removal_det_pin is None and connect_det_pin is None): self.eeprom = EepromEmulator('eeprom_emulator') self.pull_up_pin = GPIOEmulator('pull_up_pin') self.pull_down_pin = GPIOEmulator('pull_down_pin') self.tx_en_pin = GPIOEmulator('tx_en_pin') self.removal_det_pin = GPIOEmulator('removal_det_pin') self.connect_det_pin = GPIOEmulator('connect_det_pin') elif (firmware_path != '' and pl_uart_drv_ko_file != '' and i2c is not None and pull_up_pin is not None and pull_down_pin is not None and tx_en_pin is not None and removal_det_pin is not None and connect_det_pin is not None): self.eeprom = CAT24C02(eeprom_devaddr, i2c) self.pull_up_pin = pull_up_pin self.pull_down_pin = pull_down_pin self.tx_en_pin = tx_en_pin self.removal_det_pin = removal_det_pin self.connect_det_pin = connect_det_pin else: raise DarkBeastException( '__init__ error! Please check the parameters!') self.gpio = gpio if (a0_pin is None and a1_pin is None and a2_pin is None and elaod_en_pin is None): self.a0_pin = Pin(None, DarkBeastDef.A0_PIN_ID) self.a1_pin = Pin(None, DarkBeastDef.A1_PIN_ID) self.a2_pin = Pin(None, DarkBeastDef.A2_PIN_ID) self.elaod_en_pin = Pin(None, DarkBeastDef.ELAOD_EN_PIN_ID) elif (a0_pin is not None and a1_pin is not None and a2_pin is not None and elaod_en_pin is not None): self.a0_pin = a0_pin self.a1_pin = a1_pin self.a2_pin = a2_pin self.elaod_en_pin = elaod_en_pin else: raise DarkBeastException( '__init__ error! Please check the parameters!') super(DarkBeastBase, self).__init__(self.eeprom, None) self.pin_def = { 'PULL_UP': self.pull_up_pin, 'PULL_DOWN': self.pull_down_pin, 'TX_EN': self.tx_en_pin, 'REMOVAL_DETECTION': self.removal_det_pin, 'CONNECT_DETECTION': self.connect_det_pin, 'A0': self.a0_pin, 'A1': self.a1_pin, 'A2': self.a2_pin, 'ELAOD_EN': self.elaod_en_pin } def __del__(self): self.close() def _capture_by_flag(self): ''' capture end flag of result. ''' last_time = time.time() data_str = "" while (time.time() - last_time < DarkBeastDef.DEFAULT_TIMEOUT): read_str = self.process.stdout.read(1) data_str = data_str + read_str if read_str == DarkBeastDef.RESULT_CHECK_FLAG: if data_str.endswith(DarkBeastDef.RESULT_END_FLAG): break if time.time() - last_time >= DarkBeastDef.DEFAULT_TIMEOUT: raise DarkBeastException('process read wait timeout!') return data_str.strip(DarkBeastDef.RESULT_END_FLAG) def _update_linux_driver(self): ''' update pl_uart_drv.ko if exist ''' if os.path.isfile(self.pl_uart_drv_ko_file): os.system('rmmod ' + self.pl_uart_drv_ko_file) os.system('insmod ' + self.pl_uart_drv_ko_file) def module_init(self): ''' DarkBeast module init, initialize ad5592r channel mode and open DarkBeast process. Returns: string, str, return open information. Examples: DarkBeast.module_init() ''' self._update_linux_driver() ret_str = 'done' if os.path.isfile(self.path): ret_str = self.open() # set plgpio init if self.gpio: self.gpio.set_dir('output') self.gpio.set_level(DarkBeastDef.LOWE_LEVEL) # set gpio init self.pull_up_pin.set_dir('output') self.pull_up_pin.set_level(DarkBeastDef.LOWE_LEVEL) self.pull_down_pin.set_dir('output') self.pull_down_pin.set_level(DarkBeastDef.LOWE_LEVEL) self.tx_en_pin.set_dir('output') self.tx_en_pin.set_level(DarkBeastDef.LOWE_LEVEL) self.connect_det_pin.set_dir('input') self.removal_det_pin.set_dir('input') # set pin init self.a0_pin.set_dir('output') self.a0_pin.set_level(DarkBeastDef.LOWE_LEVEL) self.a1_pin.set_dir('output') self.a1_pin.set_level(DarkBeastDef.LOWE_LEVEL) self.a2_pin.set_dir('output') self.a2_pin.set_level(DarkBeastDef.LOWE_LEVEL) self.elaod_en_pin.set_dir('output') self.elaod_en_pin.set_level(DarkBeastDef.LOWE_LEVEL) return ret_str def close(self): ''' close the DarkBeast process. Returns: string, "done", api execution successful. ''' if self.process is not None: string_list = [] pid_list = [] pgid = os.getpgid(self.process.pid) command = 'ps -C {}'.format(os.path.basename(self.path)) progress = Popen(command, shell=True, stdin=PIPE, stdout=PIPE) string = progress.communicate()[0] string_list = string.split("\n") for line in string_list: pid = re.findall("\d+", line) if len(pid) != 0: pid_list.append(pid[0]) for pid in pid_list: if os.getpgid(int(pid)) == pgid: command = "kill " + pid os.system(command) self.process = None return 'done' def open(self): ''' open DarkBeast process by Popen, and return open information or faild raising Exception Returns: string, str, return open information. ''' assert os.access(self.path, os.F_OK | os.X_OK), 'path <{}> not exist or execute'.format( self.path) if self.process is not None: return 'done' command = 'cd {};./{}'.format(os.path.dirname(self.path), os.path.basename(self.path)) # stderr is standard error output, can be file descriptors and STDOUT. None means no output self.process = Popen([command, ""], shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT) return self._capture_by_flag() def communicate(self, command): ''' communicate with DarkBeast process. if process not open, raise DarkBeastException Args: command: string, command string. Returns: string, str, return information. Examples: cmd_string = "SWITCH2HS" print DarkBeast.communicate(cmd_string) Raise: DarkBeastException: process not open, communicate error! ''' if self.process is not None: self.process.stdin.write(command + "\n") self.process.stdin.flush() if DarkBeastCMDDef.EXIT == command: self.process = None return DarkBeastDef.EXIT_STRING else: return self._capture_by_flag() raise DarkBeastException('process not open, communicate error!') def aid_connect_set(self, status): ''' AID connect to dut enable or disable Args: status: string, ['enable', 'disable']. Returns: string, "done", api execution successful. Examples: DarkBeast.aid_connect_set(1) ''' assert status in ('enable', 'disable') if status == 'enable': level = 1 else: level = 0 if self.gpio: self.gpio.set_level(level) return 'done' def io_set(self, io_name, level): ''' DarkBeast io control. Args: io_name: string, ['PULL_UP', 'PULL_DOWN', 'TX_EN', 'CONNECT_DETECTION', 'REMOVAL_DETECTION', 'A0', 'A1', 'A2', 'ELAOD_EN'], io_name can be found in the table below. +---------------------+----------------------------------+ | io name | io meaning | +=====================+==================================+ | PULL_UP | Data line pull-up control, | | | Provider mode. | +---------------------+----------------------------------+ | PULL_DOWN | Data line pull-down control, | | | Consumer mode. | +---------------------+----------------------------------+ | TX_EN | UART_TX buffer enable control | +---------------------+----------------------------------+ | CONNECT_DETECTION | input pin control, detect | | | whether the DUT is connected. | +---------------------+----------------------------------+ | REMOVAL_DETECTION | Input pin control, detect | | | disconnection from DUT. | +--------------------------------------------------------+ | A0 | ORION_COMM_CONN connect | | | PPVBUS_ORION_CONN. | +---------------------+----------------------------------+ | A1 | GND connect ORION_COMM_CONN | +---------------------+----------------------------------+ | A2 | GND connect PPVBUS_ORION_CONN | +---------------------+----------------------------------+ | ELAOD_EN | A0, A1, A2 control1 disabled or | | | enabled (1/disabled 0/enabled)| +---------------------+----------------------------------+ level: int, [0, 1], 0/1 means lower/high Returns: string, "done", api execution successful. Examples: darkbeast.io_set("PULL_UP", 1) ''' assert io_name in [ "PULL_UP", "PULL_DOWN", "TX_EN", "CONNECT_DETECTION", "REMOVAL_DETECTION", "A0", "A1", "A2", "ELAOD_EN" ] device = self.pin_def[io_name] device.set_level(level) return 'done' def io_get(self, io_name): ''' DarkBeast io level get Args: io_name: string, ['PULL_UP', 'PULL_DOWN', 'TX_EN', 'CONNECT_DETECTION', 'REMOVAL_DETECTION', 'A0', 'A1', 'A2', 'ELAOD_EN'], io_name can be found in the table below. +---------------------+----------------------------------+ | io name | io meaning | +=====================+==================================+ | PULL_UP | Data line pull-up control, | | | Provider mode. | +---------------------+----------------------------------+ | PULL_DOWN | Data line pull-down control, | | | Consumer mode. | +---------------------+----------------------------------+ | TX_EN | UART_TX buffer enable control | +---------------------+----------------------------------+ | CONNECT_DETECTION | input pin control, detect | | | whether the DUT is connected. | +---------------------+----------------------------------+ | REMOVAL_DETECTION | Input pin control, detect | | | disconnection from DUT. | +--------------------------------------------------------+ | A0 | ORION_COMM_CONN connect | | | PPVBUS_ORION_CONN. | +---------------------+----------------------------------+ | A1 | GND connect ORION_COMM_CONN | +---------------------+----------------------------------+ | A2 | GND connect PPVBUS_ORION_CONN | +---------------------+----------------------------------+ | ELAOD_EN | A0, A1, A2 control1 disabled or | | | enabled (1/disabled 0/enabled)| +---------------------+----------------------------------+ Returns: int, [0, 1], io level. Examples: level = darkbeast.io_get("PULL_UP") print(level) ''' assert io_name in [ "PULL_UP", "PULL_DOWN", "TX_EN", "CONNECT_DETECTION", "REMOVAL_DETECTION", "A0", "A1", "A2", "ELAOD_EN" ] device = self.pin_def[io_name] return device.get_level()
class NegasonicBase(MIXBoard): ''' Base class of Negasonic and NegasonicCompatible,Providing common Negasonic methods. Args: i2c: instance(I2C), which is used to access eeprom and nct75, if not given, will create eeprom and nct75 emulator. analyzer: instance(FFTAnalyzer)/string, if not given, will create emulator. signalsource: instance(MIXSignalSourceSG)/string, if not given, will create emulator. adc_rst_pin: instance(GPIO), used to reset ADC CS5361. Setting low to reset CS5361, seting high to enable CS5361. i2s_rx_en_pin: instance(GPIO), used to enable fft analyzer module. dac_rst_pin: instance(GPIO), used to reset IIS of the CS4398. i2s_tx_en_pin: instance(GPIO), used to enable signal source module. sample_rate: int, unit Hz, default 192000, used to config the CS5361 or CS4398. ipcore: instance(UART), aggregated MIXAUT1SGR wrapper. eeprom_dev_addr: int, Eeprom device address. sensor_dev_addr: int, NCT75 device address. ''' rpc_public_api = [ 'module_init', 'enable_upload', 'disable_upload', 'measure', 'enable_output', 'disable_output' ] + MIXBoard.rpc_public_api def __init__(self, i2c, analyzer=None, signal_source=None, adc_rst_pin=None, i2s_rx_en_pin=None, dac_rst_pin=None, i2s_tx_en_pin=None, sample_rate=192000, ipcore=None, eeprom_dev_addr=NegasonicDef.EEPROM_I2C_ADDR, sensor_dev_addr=NegasonicDef.SENSOR_I2C_ADDR, cal_info=None, range_table=None): assert sample_rate > 0 and sample_rate <= NegasonicDef.MAX_SAMPLING_RATE self.i2c = i2c if self.i2c is None: self.eeprom = EepromEmulator("cat24cxx_emulator") self.nct75 = NCT75Emulator("nct75_emulator") else: self.eeprom = CAT24C32(eeprom_dev_addr, i2c) self.nct75 = NCT75(sensor_dev_addr, i2c) super(NegasonicBase, self).__init__(self.eeprom, self.nct75, cal_table=cal_info, range_table=range_table) if (ipcore and not analyzer and not signal_source): if isinstance(ipcore, basestring): axi4 = AXI4LiteBus(ipcore, NegasonicDef.MIXAUT1_REG_SIZE) self.ipcore = MIXAUT1SGR(axi4) else: self.ipcore = ipcore self.analyzer = self.ipcore.analyzer self.signal_source = self.ipcore.signal_source self.adc_rst_pin = adc_rst_pin or Pin(self.ipcore.gpio, NegasonicDef.ADC_RESET_PIN) self.i2s_rx_en_pin = i2s_rx_en_pin or Pin( self.ipcore.gpio, NegasonicDef.I2S_RX_EN_PIN) self.dac_rst_pin = dac_rst_pin or Pin(self.ipcore.gpio, NegasonicDef.DAC_RESET_PIN) self.i2s_tx_en_pin = i2s_tx_en_pin or Pin( self.ipcore.gpio, NegasonicDef.I2S_TX_EN_PIN) elif (not ipcore and analyzer and signal_source): if isinstance(analyzer, basestring): axi4 = AXI4LiteBus(analyzer, NegasonicDef.ANALYZER_REG_SIZE) analyzer = MIXFftAnalyzerSG(axi4) else: self.analyzer = analyzer if isinstance(signal_source, basestring): axi4 = AXI4LiteBus(signal_source, NegasonicDef.SIGNALSOURCE_REG_SIZE) signal_source = MIXSignalSourceSG(axi4) else: self.signal_source = signal_source self.adc_rst_pin = adc_rst_pin self.i2s_rx_en_pin = i2s_rx_en_pin self.dac_rst_pin = dac_rst_pin self.i2s_tx_en_pin = i2s_tx_en_pin elif (not ipcore and not analyzer and not signal_source and not adc_rst_pin and not i2s_rx_en_pin and not dac_rst_pin and not i2s_tx_en_pin): self.analyzer = MIXFftAnalyzerSGEmulator( "mix_fftanalyzer_sg_emulator") self.signal_source = MIXSignalSourceSGEmulator( "mix_signalsource_sg_emulator") self.adc_rst_pin = Pin(None, NegasonicDef.ADC_RESET_PIN) self.i2s_rx_en_pin = Pin(None, NegasonicDef.I2S_RX_EN_PIN) self.dac_rst_pin = Pin(None, NegasonicDef.DAC_RESET_PIN) self.i2s_tx_en_pin = Pin(None, NegasonicDef.I2S_TX_EN_PIN) else: if ipcore: raise NegasonicException( "parameter 'ipcore' can not be given at same time with 'analyzer', " "'signal_source', 'adc_rst_pin', 'i2s_rx_en_pin', " "'dac_rst_pin', 'i2s_tx_en_pin'") else: raise NegasonicException( "parameter 'analyzer', 'signal_source', 'adc_rst_pin', " "'i2s_rx_en_pin', 'dac_rst_pin' and 'i2s_tx_en_pin'" " must be given at the same time") self.sample_rate = sample_rate def __del__(self): self.adc_rst_pin.set_level(0) self.i2s_rx_en_pin.set_level(0) self.dac_rst_pin.set_level(0) self.i2s_tx_en_pin.set_level(0) self.signal_source.close() def module_init(self): ''' Negasonic init module, will reset dac/adc and i2s module. Returns: string, "done", api execution successful. Examples: negasonic.module_init() ''' self.adc_rst_pin.set_dir('output') self.dac_rst_pin.set_dir('output') self.i2s_rx_en_pin.set_dir('output') self.i2s_tx_en_pin.set_dir('output') # reset ADC self.adc_rst_pin.set_level(0) time.sleep(NegasonicDef.DELAY_S) self.adc_rst_pin.set_level(1) # reset DAC self.dac_rst_pin.set_level(0) time.sleep(NegasonicDef.DELAY_S) self.dac_rst_pin.set_level(1) self.i2s_rx_en_pin.set_level(1) self.load_calibration() return "done" def enable_upload(self): ''' Negasonic upoad mode open. Control audio upload data of ADC when doing measurement. It's not necessary enable upload when doing measurement. Note that data transfered into DMA is 32bit each data, but high 24bit data is valid. Low 8bit data is invalid. The data format is twos complement. Returns: string, "done", api execution successful. ''' self.analyzer.enable_upload() return "done" def disable_upload(self): ''' Negasonic upoad mode close. Close data upload doesn't influence to measure. Returns: string, "done", api execution successful. ''' self.analyzer.disable_upload() return "done" def measure(self, bandwidth_hz, harmonic_num, decimation_type=0xFF): ''' Negasonic measure signal's Vpp, RMS, THD+N, THD. Args: bandwidth_hz: int, [24~95977], Measure signal's limit bandwidth, unit is Hz. The frequency of the signal should not be greater than half of the bandwidth. harmonic_num: int, [2~10], Use for measuring signal's THD. decimation_type: int, [1~255], Decimation for FPGA to get datas. If decimation is 0xFF, FPGA will choose one suitable number. Returns: dict, {'vpp': value, 'freq': value, 'thd': value, 'thdn': value, 'rms': value), dict with vpp, freq, thd, thdn, rms. Examples: result = negasonic.measure(20000, 5, 0xff) print result.frequency, result.vpp, result.thdn, result.thd, result.rms ''' assert bandwidth_hz == 'auto' or isinstance(bandwidth_hz, int) assert isinstance(harmonic_num, int) and harmonic_num > 0 assert isinstance(decimation_type, int) and decimation_type > 0 self.analyzer.disable() self.analyzer.enable() self.analyzer.analyze_config(self.sample_rate, decimation_type, bandwidth_hz, harmonic_num) self.analyzer.analyze() # calculate the actual vpp of HW by VREF vpp = self.analyzer.get_vpp() * NegasonicDef.AUDIO_ANALYZER_VREF # vpp = RMS * 2 * sqrt(2) rms = vpp / NegasonicDef.RMS_TO_VPP_RATIO rms = self.calibrate(NegasonicDef.MEASURE_CAL_ITEM, rms) vpp = rms * NegasonicDef.RMS_TO_VPP_RATIO result = dict() result["vpp"] = vpp result["freq"] = self.analyzer.get_frequency() result["thd"] = self.analyzer.get_thd() result["thdn"] = self.analyzer.get_thdn() result["rms"] = rms return result def enable_output(self, freq, rms): ''' Negasonic output sine wave, differencial mode. Args: freq: int, [10~50000], unit Hz, Ouput signal's frequency. rms: float, [0~2300], unit mV, Ouput wave's RMS. Returns: string, "done", api execution successful. Examples: negasonic.enable_output(10000, 500) ''' assert freq >= NegasonicDef.OUTPUT_FREQ_MIN assert freq <= NegasonicDef.OUTPUT_FREQ_MAX assert rms >= NegasonicDef.OUTPUT_RMS_MIN assert rms <= NegasonicDef.OUTPUT_RMS_MAX # enable I2S tx module self.i2s_tx_en_pin.set_level(1) self.signal_source.open() rms = self.calibrate(NegasonicDef.OUTPUT_CAL_ITEM, rms) # vpp = RMS * 2 * sqrt(2) vpp = rms * NegasonicDef.RMS_TO_VPP_RATIO # calculate vpp to vpp scale for FPGA vpp_scale = vpp * NegasonicDef.VPP_2_SCALE_RATIO / NegasonicDef.SIGNAL_SOURCE_VREF self.signal_source.set_signal_type(NegasonicDef.OUTPUT_WAVE) self.signal_source.set_signal_time(NegasonicDef.SIGNAL_ALWAYS_OUTPUT) self.signal_source.set_swg_paramter(self.sample_rate, freq, vpp_scale, NegasonicDef.OUTPUT_SIGNAL_DUTY) self.signal_source.output_signal() return "done" def disable_output(self): ''' Negasonic close sine wave output. Returns: string, "done", api execution successful. Examples: negasonic.disable_output() ''' self.signal_source.close() self.i2s_tx_en_pin.set_level(0) return "done" def legacy_write_calibration_cell(self, unit_index, gain, offset, threshold): ''' MIXBoard calibration data write Args: unit_index: int, calibration unit index. gain: float, calibration gain. offset: float, calibration offset. threshold: float, if value < threshold, use this calibration unit data. Returns: string, "done", api execution successful. Examples: board.write_calibration_cel(0, 1.1, 0.1, 100) Raise: BoardArgCheckError: unit_index data type is not int type or unit_index < 0. calibration unit format: Meaning: Gain, Offset, threshold value, Use flag Mem size: 4Bytes, 4Bytes, 4Bytes, Byte Data type: float, float, float, uint8_t Formula: Y = Gain * X + Offset ''' if not isinstance(unit_index, int) or unit_index < 0: raise BoardArgCheckError( "calibration unit memory unit_index data type is not int type or unit_index < 0" ) use_flag = self.calibration_info["use_flag"] data = (gain, offset, use_flag) s = struct.Struct(NegasonicDef.WRITE_CAL_DATA_PACK_FORMAT) pack_data = s.pack(*data) s = struct.Struct(NegasonicDef.WRITE_CAL_DATA_UNPACK_FORMAT) data = s.unpack(pack_data) address = self.calibration_info[ "unit_start_addr"] + NegasonicDef.CAL_DATA_LEN * unit_index self.write_eeprom(address, data) return "done" def legacy_read_calibration_cell(self, unit_index): ''' MIXBoard read calibration data Args: unit_index: int, calibration unit index. Returns: dict, {'gain': value, 'offset': value, 'threshold': value, 'is_use': value}. Examples: data = board.read_calibration_cel(0) print(data) Raise: BoardArgCheckError: unit_index data type is not int type or unit_index < 0. calibration unit format: Meaning: Gain, Offset, threshold value, Use flag Mem size: 4Bytes, 4Bytes, 4Bytes, Byte Data type: float, float, float, uint8_t Formula: Y = Gain * X + Offset ''' if not isinstance(unit_index, int) or unit_index < 0: raise BoardArgCheckError( "calibration unit memory unit_index data type is not int type or unit_index < 0" ) address = self.calibration_info[ "unit_start_addr"] + NegasonicDef.CAL_DATA_LEN * unit_index data = self.read_eeprom(address, NegasonicDef.READ_CAL_BYTE) s = struct.Struct(NegasonicDef.READ_CAL_DATA_PACK_FORMAT) pack_data = s.pack(*data) s = struct.Struct(NegasonicDef.READ_CAL_DATA_UNPACK_FORMAT) result = s.unpack(pack_data) threshold = 0.0 for cal_item in negasonic_calibration_info: for level in negasonic_calibration_info[cal_item]: if unit_index == negasonic_calibration_info[cal_item][level][ "unit_index"]: threshold = negasonic_calibration_info[cal_item][level][ "limit"][0] if self.calibration_info["use_flag"] != result[2]: return {"gain": 1.0, "offset": 0.0, "threshold": 0, "is_use": True} else: return { "gain": result[0], "offset": result[1], "threshold": threshold, "is_use": True } def legacy_erase_calibration_cell(self, unit_index): ''' MIXBoard erase calibration unit Returns: string, "done", api execution successful. Examples: board.erase_calibration_cell(0) ''' if not isinstance(unit_index, int) or unit_index < 0: raise BoardArgCheckError( "calibration unit memory unit_index data type is not int type or unit_index < 0" ) data = [0xff for i in range(9)] address = self.calibration_info["unit_start_addr"] + 9 * unit_index self.write_eeprom(address, data) return "done"
class MIXAUT1SGR(object): ''' MIXAUT1SGR aggregated IPcore has 3 child IP, MIXFftAnalyzerSG, MIXSignalSourceSG, MIXGPIOSG. ClassType = MIXAUT1SGR Args: axi4_bus: instance(AXI4LiteBus)/string, axi4lite instance or dev path. fft_data_cnt: int, get fft absolute data count, if not give, with get count from register. Examples: mix_aut1 = MIXAUT1SGR('/dev/MIX_AUT1_x') ''' rpc_public_api = [ 'reset_adc', 'reset_dac', 'enable_rx', 'disable_rx', 'enable_tx', 'disable_tx', 'enable_upload', 'disable_upload', 'measure', 'enable_output', 'disable_output' ] def __init__(self, axi4_bus, fft_data_cnt=None): if isinstance(axi4_bus, basestring): # device path; create axi4lite instance self.axi4_bus = AXI4LiteBus(axi4_bus, MIXAUT1SGRDef.REG_SIZE) else: self.axi4_bus = axi4_bus if self.axi4_bus is None: self.analyzer = MIXFftAnalyzerSGEmulator( 'mix_fftanalyzer_sg_emulator') self.signal_source = MIXSignalSourceSGEmulator( "mix_signalsource_sg_emulator") self.gpio = MIXGPIOSGEmulator("mix_gpio_sg_emulator", 256) else: self.fft_analyzer_axi4_bus = AXI4LiteSubBus( self.axi4_bus, MIXAUT1SGRDef.MIX_FFT_ANAYLZER_IPCORE_ADDR, MIXAUT1SGRDef.MIX_FFT_REG_SIZE) self.analyzer = MIXFftAnalyzerSG(self.fft_analyzer_axi4_bus, fft_data_cnt) self.signal_source_axi4_bus = AXI4LiteSubBus( self.axi4_bus, MIXAUT1SGRDef.MIX_SIGNAL_SOURCE_IPCORE_ADDR, MIXAUT1SGRDef.MIX_SIGNAL_SOURCE_REG_SIZE) self.signal_source = MIXSignalSourceSG(self.signal_source_axi4_bus) self.gpio_axi4_bus = AXI4LiteSubBus( self.axi4_bus, MIXAUT1SGRDef.MIX_GPIO_IPCORE_ADDR, MIXAUT1SGRDef.MIX_GPIO_REG_SIZE) self.gpio = MIXGPIOSG(self.gpio_axi4_bus) self.adc_rst_pin = Pin(self.gpio, MIXAUT1SGRDef.ADC_RESET_PIN) self.i2s_rx_en_pin = Pin(self.gpio, MIXAUT1SGRDef.I2S_RX_EN_PIN) self.dac_rst_pin = Pin(self.gpio, MIXAUT1SGRDef.DAC_RESET_PIN) self.i2s_tx_en_pin = Pin(self.gpio, MIXAUT1SGRDef.I2S_TX_EN_PIN) def reset_adc(self, delay_ms): ''' Reset ADC. Args: delay_ms: int, unit ms, reset time in ms Returns: "done" ''' self.adc_rst_pin.set_level(0) time.sleep(delay_ms / 1000.0) self.adc_rst_pin.set_level(1) return "done" def reset_dac(self, delay_ms): ''' Reset DAC. Args: delay_ms: int, unit ms, reset time in ms Returns: "done" ''' self.dac_rst_pin.set_level(0) time.sleep(delay_ms / 1000.0) self.dac_rst_pin.set_level(1) return "done" def enable_rx(self): ''' Enable I2S RX ''' self.i2s_rx_en_pin.set_level(1) return "done" def disable_rx(self): ''' Disable I2S RX ''' self.i2s_rx_en_pin.set_level(0) return "done" def enable_tx(self): ''' Enable I2S TX ''' self.i2s_tx_en_pin.set_level(1) return "done" def disable_tx(self): ''' Disable I2S TX ''' self.i2s_tx_en_pin.set_level(0) return "done" def enable_upload(self): ''' Enable FFT data upload Returns: string, "done", api execution successful. ''' self.analyzer.enable_upload() return "done" def disable_upload(self): ''' Disable FFT data upload Returns: string, "done", api execution successful. ''' self.analyzer.disable_upload() return "done" def measure(self, sampling_rate, decimation_type, bandwidth_hz='auto', harmonic_num=None, freq_point=None): ''' Measure signal's freq, vpp, THD+N, THD. Args: sample_rate: int, Sample rate of your ADC device, unit is Hz. Eg. 192000. decimation_type: int, [1~255], Decimation for FPGA to get datas. If decimation is 0xFF, FPGA will choose one suitable number. bandwidth: int/string, FFT calculation bandwidth limit, must smaller than half of sample_rate, unit is Hz. Eg. 20000. If 'auto' given, bandwidth will be automatically adjust based on base frequency. harmonic_count: int/None, [1~10] The harmonic count of signal, default is None will not do calculate. freq_point: int/None, Specified frequency for calculating amplitude at this special frequency, default is None will not do this. Returns: dict, {'vpp': value, 'freq': value, 'thd': value, 'thdn': value), dict with vpp, freq, thd, thdn. Examples: result = aut1.measure(48000, 5, 0xff) print(result['frequency'], result['vpp'], ['thdn'], ['thd']) ''' self.analyzer.disable() self.analyzer.enable() self.analyzer.analyze_config(sampling_rate, decimation_type, bandwidth_hz, harmonic_num, freq_point) self.analyzer.analyze() result = dict() result["vpp"] = self.analyzer.get_vpp() result["freq"] = self.analyzer.get_frequency() result["thd"] = self.analyzer.get_thd() result["thdn"] = self.analyzer.get_thdn() return result def enable_output(self, sampling_rate, freq, vpp): ''' Enable signal source output Args: sampling_rate: int, in SPS, DAC sampling rate. freq: int, unit Hz, Ouput signal's frequency. vpp: float, [0.000~0.999], output signal vpp scale. Returns: string, "done", api execution successful. Examples: aut1.enable_output(10000, 500) ''' self.signal_source.open() self.signal_source.set_signal_type(MIXAUT1SGRDef.OUTPUT_TYPE) self.signal_source.set_signal_time(MIXAUT1SGRDef.ALWAYS_OUTPUT) self.signal_source.set_swg_paramter(sampling_rate, freq, vpp, MIXAUT1SGRDef.OUTPUT_DUTY) self.signal_source.output_signal() return "done" def disable_output(self): ''' Disable signal source output ''' self.signal_source.close() return "done"
class KarmaBase(SGModuleDriver): ''' Base class of Karma and KarmaCompatible. Providing common Karma methods. Args: i2c: instance(I2C), instance of I2CBus, which is used to control cat24c32 and nct75. ipcore: instance(MIXBT001SGR)/string, If given, then use MIXBT001SGR's AD7175, MIXQSPI, MIXGPIO. eeprom_dev_addr: int, eeprom device address. sensor_dev_addr: int, NCT75 device address. range_table: dict, which is ICI calibration range table. ''' rpc_public_api = [ 'dds_reset', 'dds_control', 'discharge_control', 'set_line_path', 'get_line_path', 'get_offset_cal_data', 'sine_output', 'resistance_measure', 'read_adc_voltage', 'set_sampling_rate', 'get_sampling_rate' ] + SGModuleDriver.rpc_public_api def __init__(self, i2c, ipcore, eeprom_dev_addr=KarmaDef.EEPROM_DEV_ADDR, sensor_dev_addr=KarmaDef.SENSOR_DEV_ADDR, range_table=karma_range_table): if (i2c and ipcore): if isinstance(ipcore, basestring): axi4_bus = AXI4LiteBus(ipcore, KarmaDef.REG_SIZE) self.ipcore = MIXBT001SGR(axi4_bus=axi4_bus, ad717x_chip='AD7175', ad717x_mvref=KarmaDef.ADC_VREF, use_spi=True, use_gpio=True) else: self.ipcore = ipcore self.ad7175 = self.ipcore.ad717x self.ad7175.config = { 'ch0': { 'P': 'AIN0', 'N': 'AIN1' }, 'ch1': { 'P': 'AIN2', 'N': 'AIN3' } } self.vx_ix_select_pin = Pin(self.ipcore.gpio, KarmaDef.VX_IX_SELECT_PIN) self.cur_set1_pin = Pin(self.ipcore.gpio, KarmaDef.CUR_SET1_PIN) self.cur_set2_pin = Pin(self.ipcore.gpio, KarmaDef.CUR_SET2_PIN) self.discharge_pin = Pin(self.ipcore.gpio, KarmaDef.DISCHARGE_PIN) self.lpf_select_pin = Pin(self.ipcore.gpio, KarmaDef.LPF_SELECT_PIN) self.reset_l_pin = Pin(self.ipcore.gpio, KarmaDef.RESET_L_PIN) self.trigger_l_pin = Pin(self.ipcore.gpio, KarmaDef.TRIGGER_L_PIN) self.ad9106 = AD9106(self.ipcore.spi, KarmaDef.AD9106_MCLK, KarmaDef.AD9106_DEFAULT_VREF) self.eeprom = CAT24C32(eeprom_dev_addr, i2c) self.sensor = NCT75(sensor_dev_addr, i2c) self.line_path = "" self.sine_data_length = 0 self.cosine_data_length = 0 self.cycle_count = 2 super(KarmaBase, self).__init__(self.eeprom, self.sensor, range_table=range_table) else: raise KarmaException('Parameter error') def post_power_on_init(self, timeout=KarmaDef.DEFAULT_TIMEOUT): ''' Init Karma module to a know harware state. Args: timeout: float, (>=0), default 6, unit Second, execute timeout. ''' self.reset(timeout) def pre_power_down(self, timeout=KarmaDef.DEFAULT_TIMEOUT): ''' Put the hardware in a safe state to be powered down. Args: timeout: float, (>=0), default 6, unit Second, execute timeout. ''' start_time = time.time() while True: try: self.discharge_control('open', KarmaDef.DISCHARGE_DEFAULT_TIME) self.discharge_control('close') self.dds_reset() self.dds_control('close') return except Exception as e: if time.time() - start_time > timeout: raise KarmaException("Timeout: {}".format(e.message)) def reset(self, timeout=KarmaDef.DEFAULT_TIMEOUT): ''' Reset the instrument module to a know hardware state. Args: timeout: float, (>=0), default 6, unit Second, execute timeout. ''' start_time = time.time() while True: try: self.vx_ix_select_pin.set_dir(KarmaDef.IO_OUTPUT_DIR) self.cur_set1_pin.set_dir(KarmaDef.IO_OUTPUT_DIR) self.cur_set2_pin.set_dir(KarmaDef.IO_OUTPUT_DIR) self.discharge_pin.set_dir(KarmaDef.IO_OUTPUT_DIR) self.lpf_select_pin.set_dir(KarmaDef.IO_OUTPUT_DIR) self.reset_l_pin.set_dir(KarmaDef.IO_OUTPUT_DIR) self.trigger_l_pin.set_dir(KarmaDef.IO_OUTPUT_DIR) self.discharge_control('open', KarmaDef.DISCHARGE_DEFAULT_TIME) self.discharge_control('close') self.dds_reset() self.dds_control('close') self.ad9106.set_ref_voltage(KarmaDef.AD9106_DEFAULT_VREF) self.ad7175.channel_init() self.set_sampling_rate(0, KarmaDef.ADC_DEFAULT_SAMPLE_RATE) self.set_sampling_rate(1, KarmaDef.ADC_DEFAULT_SAMPLE_RATE) self.write_sine_pattern(1000, KarmaDef.IOUT_DEFAULT_VPP, self.cycle_count) self.write_cosine_pattern(1000, KarmaDef.IOUT_DEFAULT_VPP, self.cycle_count) return except Exception as e: if time.time() - start_time > timeout: raise KarmaException("Timeout: {}".format(e.message)) def get_driver_version(self): ''' Get driver version. Returns: string, current driver version. ''' return __version__ def dds_reset(self): ''' Karma reset ad9106. This is private function. Returns: string, "done", api execution successful. Examples: karma.dds_reset() ''' self.reset_l_pin.set_level(0) time.sleep(KarmaDef.RST_DELAY_MS / 1000.0) self.reset_l_pin.set_level(1) return "done" def dds_control(self, mode): ''' Pattern trigger input of ad9106. This is private function. Args: mode: string, ['open', 'close'], 'open' mean enable pattern trigger input of ad9106, 'close' mean disable pattern trigger input of ad9106. Returns: string, "done", api execution successful. Examples: karma.dds_control('open') ''' assert mode in ['open', 'close'] if mode == 'open': self.trigger_l_pin.set_level(0) else: self.trigger_l_pin.set_level(1) return "done" def set_sampling_rate(self, channel, sampling_rate): ''' Karma set sampling rate. This is private function. Args: channel: int, [0, 1], channel 0 is for the real part of Vx or Ix, channel 1 is for the imaginary part of Vx or Ix. sampling_rate: float, [5~250000], adc measure sampling rate, which not continuouse, please refer ad7175 datasheet. Returns: string, "done", api execution successful. Examples: karma.set_sampling_rate(1, 10000) ''' assert channel in KarmaDef.ADC_CHANNEL_LIST assert 5 <= sampling_rate <= 250000 if sampling_rate != self.get_sampling_rate(channel): self.ad7175.set_sampling_rate(channel, sampling_rate) return "done" def get_sampling_rate(self, channel): ''' Karma get sampling rate of adc. This is private function. Args: channel: int, [0, 1], channel 0 is for the real part of Vx or Ix, channel 1 is for the imaginary part of Vx or Ix. Returns: string, "done", api execution successful. Examples: sampling_rate = karma.get_sampling_rate(1) print(sampling_rate) ''' assert channel in KarmaDef.ADC_CHANNEL_LIST return self.ad7175.get_sampling_rate(channel) def discharge_control(self, mode, discharge_time=KarmaDef.CLOSE_DEFAULT_TIME): ''' Discharge control. This is private function. Args: mode: string, ['open', 'close'], 'open' mean discharge, 'close' mean charge. discharge_time: float, (>=0), default 0, unit ms, discharge time. Returns: string, "done", api execution successful. Examples: karma.discharge_control('open', 20) ''' assert mode in ['open', 'close'] bits = karma_line_info[mode]['bits'] for bit in bits: if isinstance(bit, list): for io_conf in bit: eval(io_conf[0]).set_level(io_conf[1]) time.sleep(KarmaDef.RST_DELAY_MS / 1000.0) else: eval(bit[0]).set_level(bit[1]) time.sleep(discharge_time / 1000.0) self.line_path = mode return "done" def set_line_path(self, channel, scope, freq, delay_time=KarmaDef.RELAY_DELAY_MS): ''' Karma set channel path. This is debug function. Args: channel: string, ['Vx', 'Ix'], channel 'Vx' mean the vector voltage of the real part or the imaginary part of impedance, channel 'Ix' mean the vector current of the real part or the imaginary part of impedance. scope: string, ['3000mohm', '300mohm'], impedance measurement range. freq: string, ['1kHz', '1Hz'], impedance measurement frequency. delay_time: int, (>0), default 5, unit ms. Returns: string, "done", api execution successful. Examples: karma.set_line_path('Vx', '3000mohm', '1kHz') ''' assert channel in KarmaDef.CHANNEL_LIST assert scope in KarmaDef.RANGE_LIST assert freq in KarmaDef.FREQ_LIST line_path = freq + '_' + scope + '_' + channel if line_path != self.line_path: bits = karma_line_info[channel]['bits'] for bit in bits: eval(bit[0]).set_level(bit[1]) bits = karma_line_info[freq]['bits'] for bit in bits: eval(bit[0]).set_level(bit[1]) bits = karma_line_info[scope]['bits'] for bit in bits: eval(bit[0]).set_level(bit[1]) time.sleep(delay_time / 1000.0) self.line_path = line_path return "done" def get_line_path(self): ''' Karma get channel path. Returns: string, value is channel path. Examples: path = karma.get_line_path() print(path) ''' return self.line_path def sine_output(self, dac_channel, freq, vpp, offset=0, phase_value=0): ''' AD9106 output sine waveform, this is a private function. Args: dac_channel: int, [1, 2, 3], channel for dds output. freq: int, unit Hz, frequency of wavefrom. vpp: float, unit mVpp, vpp of waveform. offset: float, unit mV, default 0, offset of waveform. phase_value: float, (>=0), default 0, phase of waveform. Returns: string, "done", api execution successful. Examples: karma.sine_output(0, 1000, 500) ''' assert dac_channel in [1, 2, 3] assert 0 <= phase_value # IOUTFSx = 32 * VREFIO / xRSET = 32 * VREFIO / 8060, VOUTFS = IOUTFS x Rl = IOUTFS * 249 vp = 32 * KarmaDef.AD9106_DEFAULT_VREF * 249 / 8060.0 gain = vpp / (vp * 2) self.ad9106.set_phase(dac_channel, phase_value) self.ad9106.sine_output(dac_channel, freq, gain, offset) return "done" def write_sine_pattern(self, freq, vpp, cycle_count=1): ''' Write sine wave data to SRAM. Args: freq: int/float, unit Hz, frequency value. vpp: int/float, unit mVpp, vpp value. cycle_count, int/float, (>0), default 1, the number of waveform cycles in SRAM. Returns: string, "done", api execution successful. Examples: karma.write_sine_pattern(1000, 1977, 4) ''' sine_data_list = self.calculate_sine_data(freq, vpp) sine_data_list = sine_data_list * cycle_count if len(sine_data_list) > 4096: raise KarmaException("Out of range of per pattern_period") self.ad9106.write_pattern(KarmaDef.AD9106_SRAM_SINE_ADDR, sine_data_list) self.sine_data_length = len(sine_data_list) return "done" def calculate_sine_data(self, freq, vpp): ''' Calculate sine data. Args: freq: int/float, unit Hz, frequency value. vpp: int/float, unit mVpp, vpp value. Returns: list, sine wave data. Examples: karma.calculate_sine_data(1000, 1977) ''' data_list = list() vp = vpp / 2 freq = freq * 1.0 total_points = int(KarmaDef.AD9106_SAMPLE_RATE / freq) for i in range(total_points): point_vp = math.sin(2 * math.pi * (i / (total_points * 1.0))) * vp if (point_vp < 0): if point_vp < -KarmaDef.AD9106_DEFAULT_VREF: point_vp = -KarmaDef.AD9106_DEFAULT_VREF result = int( KarmaDef.calculation_formula['calculate_negative_data'] (point_vp)) & 0xfff else: if point_vp > KarmaDef.AD9106_DEFAULT_VREF: point_vp = KarmaDef.AD9106_DEFAULT_VREF result = int( KarmaDef.calculation_formula['calculate_positive_data'] (point_vp)) & 0xfff data_list.append(result) return data_list def write_cosine_pattern(self, freq, vpp, cycle_count=1): ''' Write cosine wave data to SRAM. Args: freq: int/float, unit Hz, frequency value. vpp: int/float, unit mVpp, vpp value. cycle_count, int/float, (>0), default 1, the number of waveform cycles in SRAM. Returns: string, "done", api execution successful. Examples: karma.write_cosine_pattern(1000, 1977, 4) ''' cosine_data_list = self.calculate_cosine_data(freq, vpp) cosine_data_list = cosine_data_list * cycle_count if len(cosine_data_list) > 4096: raise KarmaException("Out of range of per pattern_period") self.ad9106.write_pattern(KarmaDef.AD9106_SRAM_COSINE_ADDR, cosine_data_list) self.cosine_data_length = len(cosine_data_list) return "done" def calculate_cosine_data(self, freq, vpp): ''' Calculate cosine data. Args: freq: int/float, unit Hz, frequency value. vpp: int/float, unit mVpp, vpp value. Returns: list, cosine wave data. Examples: karma.calculate_cosine_data(1000, 1977) ''' data_list = list() vp = vpp / 2 freq = freq * 1.0 total_points = int(KarmaDef.AD9106_SAMPLE_RATE / freq) for i in range(total_points): point_vp = math.cos(2 * math.pi * (i / (total_points * 1.0))) * vp if (point_vp < 0): if point_vp < -KarmaDef.AD9106_DEFAULT_VREF: point_vp = -KarmaDef.AD9106_DEFAULT_VREF result = int( KarmaDef.calculation_formula['calculate_negative_data'] (point_vp)) & 0xfff else: if point_vp > KarmaDef.AD9106_DEFAULT_VREF: point_vp = KarmaDef.AD9106_DEFAULT_VREF result = int( KarmaDef.calculation_formula['calculate_positive_data'] (point_vp)) & 0xfff data_list.append(result) return data_list def output_sine_pattern(self, freq, vpp, cycle_count, pat_period_base=1, dac_repeat_cycle=255): ''' Output sine wave in pattern period. Args: freq: int/float, unit Hz, frequency value, it is related to the clock of DAC. vpp: float, unit mVpp, vpp of waveform. cycle_count: int/float, (>0), the number of waveform cycles in per pattern_period. pat_period_base: int, [0-0xf], default 1, the number of dac clock periods per pattern_period lsb. dac_repeat_cycle: int, [0-255], default 1, Number of DAC pattern repeat cycles + 1, (0 means repeat 1 pattern). Returns: string, "done", api execution successful. Examples: karma.write_sine_pattern(1000, 1977, 4) karma.output_sine_pattern(1000, 1977, 4) ''' self.dds_control('close') # IOUTFSx = 32 * VREFIO / xRSET = 32 * VREFIO / 8060, VOUTFS = IOUTFS x Rl = IOUTFS * 249 vp = 32 * KarmaDef.AD9106_DEFAULT_VREF * 249 / 8060.0 gain = vpp / (vp * 2) self.ad9106.play_pattern(1, freq, KarmaDef.AD9106_SRAM_SINE_START_ADDR, self.sine_data_length - 1, pat_period_base=pat_period_base, dac_repeat_cycle=dac_repeat_cycle, cycle_value=cycle_count, gain=gain) self.ad9106.play_pattern(2, freq, KarmaDef.AD9106_SRAM_COSINE_START_ADDR, self.cosine_data_length - 1, pat_period_base=pat_period_base, dac_repeat_cycle=dac_repeat_cycle, cycle_value=cycle_count, gain=gain) self.ad9106.play_pattern(3, freq, KarmaDef.AD9106_SRAM_SINE_START_ADDR, self.sine_data_length - 1, pat_period_base=pat_period_base, dac_repeat_cycle=dac_repeat_cycle, cycle_value=cycle_count, gain=gain) self.ad9106.set_ram_update() self.dds_control('open') return "done" def resistance_measure(self, scope, freq, discharge_time=KarmaDef.DISCHARGE_DEFAULT_TIME, delay_time=KarmaDef.RELAY_DELAY_MS): ''' Karma measure impedance. Please notes that the hardware does not support 1Hz measurement temporarily. Args: scope: string, ['3000mohm', '300mohm'], impedance measurement range. freq: string, ['1kHz', '1Hz'], impedance measurement frequency, the hardware does not support '1Hz' measurement temporarily. discharge_time: float, (>=20), default 20, unit ms, discharge time. delay_time: int, (>0), default 5, unit ms. Returns: dict, {'Rs': (Rs, 'ohm'), 'Xs': (Xs, 'ohm')}, for the real part and imaginary part of impedance. Examples: result = karma.resistance_measure('3000mohm', '1kHz', 20) print(result) ''' assert scope in KarmaDef.RANGE_LIST assert freq in KarmaDef.FREQ_LIST assert 20 <= discharge_time sine_freq_dict = {'1kHz': 1000, '1Hz': 1} sine_freq = sine_freq_dict[freq] r_sense_dict = {'3000mohm': 200, '300mohm': 20} r_sense = r_sense_dict[scope] self.discharge_control('close') if freq == '1kHz': self.output_sine_pattern(sine_freq, KarmaDef.IOUT_DEFAULT_VPP, self.cycle_count) else: raise KarmaException( "Hardware does not support 1Hz measurement temporarily") self.set_line_path('Ix', scope, freq, delay_time) u_re = self.ad7175.read_volt(0) u_im = self.ad7175.read_volt(1) u_re = self.calibrate(scope + '_Ix_ch1', u_re, sine_freq) u_im = self.calibrate(scope + '_Ix_ch2', u_im, sine_freq) self.set_line_path('Vx', scope, freq, delay_time) self.output_sine_pattern(sine_freq, KarmaDef.IOUT_DEFAULT_VPP, self.cycle_count) v_re = self.ad7175.read_volt(0) v_im = self.ad7175.read_volt(1) v_re = self.calibrate(scope + '_Vx_ch1', v_re, sine_freq) v_im = self.calibrate(scope + '_Vx_ch2', v_im, sine_freq) self.discharge_control('open', discharge_time) self.discharge_control('close') result = dict() unit = 'ohm' r_s = -(((v_re * u_re) + (v_im * u_im)) * r_sense / ((u_re**2 + u_im**2) * 65.0)) x_s = -(((v_im * u_re) - (v_re * u_im)) * r_sense / ((u_re**2 + u_im**2) * 65.0)) r_s = self.calibrate(scope, r_s) result['Rs'] = (r_s, unit) result['Xs'] = (x_s, unit) return result def read_adc_voltage(self, channel): ''' Read adc voltage, this is a debug function. Args: channel: int, [0, 1]. adc channel. Returns: float, unit is mV. Examples: result = karma.read_adc_voltage(0) print(result) ''' assert channel in KarmaDef.ADC_CHANNEL_LIST return self.ad7175.read_volt(channel) def get_offset_cal_data(self, frequency): ''' Get offset calibration data, this is a debug function. Args: frequency: string, ['1kHz', '1Hz'], frequency. Returns: dict, e.g. {'1kHz_300mohm_Vx_ch1': 37.65133247681454, '1Hz_300mohm_Ix_ch1': 13.997853636613705, '1kHz_300mohm_Ix_ch2': 43.30072065000062, '1kHz_300mohm_Vx_ch2': 43.32542677673262, '1Hz_3000mohm_Ix_ch2': 12.980402289652961, '1kHz_300mohm_Ix_ch1': 37.70277128832169, '1Hz_300mohm_Ix_ch2': 13.53880843751481, '1Hz_3000mohm_Ix_ch1': 13.895274036841037, '1Hz_300mohm_Vx_ch2': 20.317376870952657, '1Hz_3000mohm_Vx_ch2': 28.178902159863846, '1kHz_3000mohm_Vx_ch1': 37.72822247315779, '1kHz_3000mohm_Vx_ch2': 43.30441613819696, '1Hz_3000mohm_Vx_ch1': 27.222545577439405, '1kHz_3000mohm_Ix_ch2': 43.30870767287659, '1Hz_300mohm_Vx_ch1': 27.315230805589607, '1kHz_3000mohm_Ix_ch1': 37.71659956673381}. Examples: result = karma.get_offset_cal_data() print(result) ''' assert frequency in KarmaDef.FREQ_LIST cal_offset = OrderedDict() adc_channel_dict = {'ch1': 0, 'ch2': 1} sine_freq_dict = {'1kHz': 1000, '1Hz': 1} if frequency == '1Hz': CAL_OFFSET_RANGE = CAL_OFFSET_RANGE_1Hz else: CAL_OFFSET_RANGE = CAL_OFFSET_RANGE_1kHz for offset_range in CAL_OFFSET_RANGE: chan = offset_range.split('_') freq = chan[0] scope = chan[1] vi_chan = chan[2] adc_chan = chan[3] adc_channel = adc_channel_dict[adc_chan] sine_freq = sine_freq_dict[freq] self.discharge_control('close') self.sine_output(1, sine_freq, KarmaDef.IOUT_DEFAULT_VPP) self.sine_output(2, sine_freq, KarmaDef.IOUT_DEFAULT_VPP, phase_value=KarmaDef.COSINE_PHASE) self.dds_control('open') self.set_line_path(vi_chan, scope, freq) data_list = [] for i in range(5): data_list.append(self.read_adc_voltage(adc_channel)) self.dds_control('close') self.discharge_control('open', discharge_time=20) self.discharge_control('close') data_list.sort() data_list = data_list[1:-1] avg_data = sum(data_list) / len(data_list) path = freq + '_' + scope + '_' + vi_chan + '_' + adc_chan cal_offset[path] = avg_data return cal_offset def calibrate(self, range_name, data, frequency=None): ''' This function is used to calibrate data. Args: range_name: string, which range used to do calibration data: float, raw data which need to be calibrated. frequency: int/None, default None. Returns: float: calibrated data. Examples: result = karma.calibrate('300mohm_Ix_ch1', 100, 1000) print(result) ''' if frequency: if self._cal_common_error is not None: raise self._cal_common_error assert range_name in self._calibration_table items = self._calibration_table[range_name] if len(items) == 0: return data if range_name in self._range_err_table: raise self._range_err_table[range_name] level = 0 for i in range(len(items)): if frequency <= items[i]['threshold']: level = i break if not items[i]['is_use']: break level = i return items[level]['gain'] * data + items[level]['offset'] else: return super(KarmaBase, self).calibrate(range_name, data)
class Audio005Base(SGModuleDriver): ''' Audio005Base is a high resolution differential input/output digital audio module. Args: i2c: instance(I2C), the instance of I2C bus. which will be used to used to control eeprom, sensor and io expander. ipcore: instance(MIXAudio005SGR), the instance of MIXAudio005SGR, which include AD717x, FFT Analyzer, Signal Source and gpio function. If device name string is passed to the parameter, the ipcore can be instanced in the module. ''' rpc_public_api = [ 'enable_upload', 'disable_upload', 'measure', 'enable_output', 'disable_output' ] + SGModuleDriver.rpc_public_api def __init__(self, i2c, ipcore, range_table=starlordii_range_table): self.eeprom = CAT24C32(Audio005Def.EEPROM_I2C_ADDR, i2c) self.nct75 = NCT75(Audio005Def.TEMP_I2C_ADDR, i2c) self.pca9536 = PCA9536(Audio005Def.PCA9536_DEV_ADDR, i2c) if isinstance(ipcore, basestring): ipcore = MIXAudio005SGR(ipcore) self.ipcore = ipcore self.analyzer = self.ipcore.analyzer self.signal_source = self.ipcore.signal_source self.adc_rst_pin = Pin(self.ipcore.gpio, Audio005Def.ADC_RESET_PIN) self.i2s_rx_en_pin = Pin(self.ipcore.gpio, Audio005Def.I2S_RX_EN_PIN) self.dac_rst_pin = Pin(self.ipcore.gpio, Audio005Def.DAC_RESET_PIN) self.i2s_tx_en_pin = Pin(self.ipcore.gpio, Audio005Def.I2S_TX_EN_PIN) self.i2s_ch_select = [ Pin(self.ipcore.gpio, Audio005Def.I2S_CH_SELECT_2), Pin(self.ipcore.gpio, Audio005Def.I2S_CH_SELECT_3) ] super(Audio005Base, self).__init__(self.eeprom, self.nct75, range_table=range_table) self.is_enable_upload = False def post_power_on_init(self, timeout=Audio005Def.TIME_OUT): ''' Init audio005 module to a know harware state. This function will reset reset dac/adc and i2s module. Args: timeout: float, (>=0), default 1, unit Second, execute timeout. ''' self.reset(timeout) def reset(self, timeout=Audio005Def.TIME_OUT): ''' Reset the instrument module to a know hardware state. Args: timeout: float, (>=0), default 1, unit Second, execute timeout. ''' start_time = time.time() while True: try: self.adc_rst_pin.set_dir(Audio005Def.IO_DIR_OUTPUT) self.dac_rst_pin.set_dir(Audio005Def.IO_DIR_OUTPUT) self.i2s_rx_en_pin.set_dir(Audio005Def.IO_DIR_OUTPUT) self.i2s_tx_en_pin.set_dir(Audio005Def.IO_DIR_OUTPUT) # reset ADC self.adc_rst_pin.set_level(Audio005Def.PIN_LEVEL_LOW) time.sleep(Audio005Def.RELAY_DELAY_S) self.adc_rst_pin.set_level(Audio005Def.PIN_LEVEL_HIGH) # reset DAC self.dac_rst_pin.set_level(Audio005Def.PIN_LEVEL_LOW) time.sleep(Audio005Def.RELAY_DELAY_S) self.dac_rst_pin.set_level(Audio005Def.PIN_LEVEL_HIGH) # reset i2s rx self.i2s_rx_en_pin.set_level(Audio005Def.PIN_LEVEL_LOW) # reset i2s tx self.i2s_tx_en_pin.set_level(Audio005Def.PIN_LEVEL_LOW) # io init self.pca9536.set_pin_dir(Audio005Def.ADC_CH_RIGHT_CTL_BIT, Audio005Def.IO_DIR_OUTPUT) self.pca9536.set_pin_dir(Audio005Def.ADC_CH_LEFT_CTL_BIT, Audio005Def.IO_DIR_OUTPUT) self.pca9536.set_pin_dir(Audio005Def.ADCM0_CTL_BIT, Audio005Def.IO_DIR_OUTPUT) self.pca9536.set_pin_dir(Audio005Def.ADCM1_CTL_BIT, Audio005Def.IO_DIR_OUTPUT) self.pca9536.set_pin(Audio005Def.ADC_CH_RIGHT_CTL_BIT, Audio005Def.PIN_LEVEL_LOW) self.pca9536.set_pin(Audio005Def.ADC_CH_LEFT_CTL_BIT, Audio005Def.PIN_LEVEL_LOW) self.pca9536.set_pin(Audio005Def.ADCM0_CTL_BIT, Audio005Def.PIN_LEVEL_LOW) self.pca9536.set_pin(Audio005Def.ADCM1_CTL_BIT, Audio005Def.PIN_LEVEL_LOW) return except Exception as e: if time.time() - start_time > timeout: raise Audio005Exception("Timeout: {}".format(e.message)) def pre_power_down(self, timeout=Audio005Def.TIME_OUT): ''' Put the hardware in a safe state to be powered down. This function will set pca9536 io direction to output and set pin level to 0. Args: timeout: float, (>=0), default 1, unit Second, execute timeout. ''' start_time = time.time() while True: try: self.adc_rst_pin.set_level(0) self.i2s_rx_en_pin.set_level(0) self.dac_rst_pin.set_level(0) self.i2s_tx_en_pin.set_level(0) self.signal_source.close() return except Exception as e: if time.time() - start_time > timeout: raise Audio005Exception("Timeout: {}".format(e.message)) def get_driver_version(self): ''' Get audio005 driver version. Returns: string, current driver version. ''' return __version__ def enable_upload(self): ''' Enable module data upload. Returns: string, "done", execution successful. ''' self.i2s_rx_en_pin.set_level(Audio005Def.I2S_RX_ENABLE) self.analyzer.enable_upload() self.is_enable_upload = True return "done" def disable_upload(self): ''' Disable module data upload. Returns: string, "done", execution successful. ''' self.analyzer.disable_upload() self.i2s_rx_en_pin.set_level(Audio005Def.I2S_RX_DISABLE) self.is_enable_upload = False return "done" def measure(self, channel, scope, bandwidth_hz, harmonic_count, decimation_type=0xFF, sampling_rate=Audio005Def.DEF_SAMPLING_RATE): ''' Measure audio input signal, which captures data using CS5361. Args: channel: string, ['left', 'right'], select input signal channel. scope: string, ['2V', '20mV'], AD7175 measurement range. bandwidth_hz: int/string, [42~48000], unit Hz, the signal bandwidth. In theory the bandwidth must smaller than half the sampling rate. eg, if sampling_rate = 192000, so bandwidth_hz < 96000. The bandwidth must be greater than the frequency of the input signal. harmonic_count: int, [2~10], The harmonic count of signal. The harmonic frequency is the frequency of the input signal times the counts of harmonics. eg, The input is 20K, the first harmonic is 20K, the second harmonic is 40K, the third harmonic is 60K, the fourth harmonic is 80K, the fourth harmonic is 100K. The harmonic frequency must be smaller than half the sampling rate. eg, if sampling_rate = 192000, input_signal_frequency = 20000, harmonic_count = 4 so harmonic_frequency = 80000 < 96000. The signal bandwidth must be greater than harmonic frequency. decimation_type: int, [1~255], default 0xFF, sample data decimation. decimation_type is 1 means not to decimate. The smaller the input frequency, the larger the value should be. sampling_rate: int, [0~192000], default 48000, unit Hz, ADC sampling rate. Returns: dict, {'vpp': value, 'freq': value, 'thd': value, 'thdn': value, 'rms': value, 'noisefloor': value}, measurement result. ''' assert channel in Audio005Def.AUDIO_CHANNEL_LIST assert scope in Audio005Def.LNA_SCOPE if self.is_enable_upload is False: self.i2s_rx_en_pin.set_level(Audio005Def.I2S_RX_ENABLE) pin = self.i2s_ch_select[Audio005Def.SELECT_BIT0] pin.set_level( Audio005Def.AUDIO_CHANNEL_LIST[channel][Audio005Def.SELECT_BIT0]) pin = self.i2s_ch_select[Audio005Def.SELECT_BIT1] pin.set_level( Audio005Def.AUDIO_CHANNEL_LIST[channel][Audio005Def.SELECT_BIT1]) if scope == Audio005Def.LNA_RANGE_2V: self.pca9536.set_pin(Audio005Def.ADC_CH_LIST[channel], Audio005Def.SEL_GAIN_1) gain = Audio005Def.AUDIO_ANALYZER_2V_VREF / Audio005Def.AUDIO_HARDWARE_ATTENUATION else: self.pca9536.set_pin(Audio005Def.ADC_CH_LIST[channel], Audio005Def.SEL_GAIN_100) gain = Audio005Def.AUDIO_ANALYZER_20mV_VREF / Audio005Def.AUDIO_HARDWARE_ATTENUATION index = bisect.bisect(Audio005Def.SAMPLING_RANGE, sampling_rate) self.pca9536.set_pin( Audio005Def.ADCM0_CTL_BIT, Audio005Def.ADCM_CTL_LIST[index][Audio005Def.SELECT_BIT0]) self.pca9536.set_pin( Audio005Def.ADCM1_CTL_BIT, Audio005Def.ADCM_CTL_LIST[index][Audio005Def.SELECT_BIT1]) self.analyzer.disable() self.analyzer.enable() self.analyzer.analyze_config(sampling_rate, decimation_type, bandwidth_hz, harmonic_count) self.analyzer.analyze() freq = self.analyzer.get_frequency() vpp = self.analyzer.get_vpp() * gain rms = vpp / Audio005Def.RMS_TO_VPP_RATIO if scope == Audio005Def.LNA_RANGE_2V: index = bisect.bisect(Audio005Def.CAL_RANGE, freq) range_name = "AUDIO_2V_" + str( Audio005Def.CAL_RANGE[index]) + "Hz_" + channel else: range_name = "AUDIO_20mV_" + channel rms = self.calibrate(range_name, rms) vpp = rms * Audio005Def.RMS_TO_VPP_RATIO thdn_value = self.analyzer.get_thdn() result = dict() result["vpp"] = (vpp, Audio005Def.VOLT_UNIT_MV) result["freq"] = (freq, Audio005Def.FREQ_UNIT_HZ) result["thd"] = (self.analyzer.get_thd(), Audio005Def.THD_UNIT_DB) result["thdn"] = (thdn_value, Audio005Def.THDN_UNIT_DB) result["rms"] = (rms, Audio005Def.VOLT_UNIT_RMS) result["noisefloor"] = (10**(thdn_value / 20) * rms, Audio005Def.VOLT_UNIT_RMS) if self.is_enable_upload is False: self.i2s_rx_en_pin.set_level(Audio005Def.I2S_RX_DISABLE) return result def enable_output(self, freq, vpp): ''' Audio005 CS5361 output audio sine waveform. Args: freq: int, [5~50000], unit Hz, output signal's frequency. vpp: float, [0~6504], unit mV, output signal's vpp. Returns: string, "done", execution successful. ''' assert Audio005Def.OUTPUT_FREQ_MIN <= freq assert freq <= Audio005Def.OUTPUT_FREQ_MAX assert Audio005Def.OUTPUT_VPP_MIN <= vpp assert vpp <= Audio005Def.OUTPUT_VPP_MAX vpp = self.calibrate(Audio005Def.OUTPUT_CAL_ITEM, vpp) vpp = 0 if vpp < 0 else vpp # enable I2S tx module self.i2s_tx_en_pin.set_level(Audio005Def.AUDIO_OUTPUT_ENABLE) self.signal_source.close() self.signal_source.open() # calculate vpp to vpp scale for FPGA vpp_scale = vpp * Audio005Def.VPP_2_SCALE_RATIO self.signal_source.set_swg_paramter(Audio005Def.AUDIO_SAMPLING_RATE, freq, vpp_scale, Audio005Def.OUTPUT_SIGNAL_DUTY) self.signal_source.set_signal_type(Audio005Def.OUTPUT_WAVE) self.signal_source.set_signal_time(Audio005Def.SIGNAL_ALWAYS_OUTPUT) self.signal_source.output_signal() return "done" def disable_output(self): ''' Disable Cs5361 output signal. Returns: string, "done", execution successful. ''' self.signal_source.close() self.i2s_tx_en_pin.set_level(Audio005Def.AUDIO_OUTPUT_DISABLE) return "done"