// license:BSD-3-Clause // copyright-holders:smf /*************************************************************************** National Semiconductor ADC0831 / ADC0832 / ADC0834 / ADC0838 8-Bit serial I/O A/D Converters with Muliplexer Options ***************************************************************************/ #include "emu.h" #include "adc083x.h" #define VERBOSE_LEVEL ( 0 ) static inline void ATTR_PRINTF( 3, 4 ) verboselog( int n_level, device_t &device, const char *s_fmt, ... ) { if( VERBOSE_LEVEL >= n_level ) { va_list v; char buf[ 32768 ]; va_start( v, s_fmt ); vsprintf( buf, s_fmt, v ); va_end( v ); device.logerror( "%s: %s", device.machine().describe_context( ), buf ); } } /*************************************************************************** PARAMETERS ***************************************************************************/ enum { STATE_IDLE, STATE_WAIT_FOR_START, STATE_SHIFT_MUX, STATE_MUX_SETTLE, STATE_OUTPUT_MSB_FIRST, STATE_WAIT_FOR_SE, STATE_OUTPUT_LSB_FIRST, STATE_FINISHED }; /*************************************************************************** TYPE DEFINITIONS ***************************************************************************/ DEFINE_DEVICE_TYPE(ADC0831, adc0831_device, "adc0831", "ADC0831 A/D Converter") DEFINE_DEVICE_TYPE(ADC0832, adc0832_device, "adc0832", "ADC0832 A/D Converter") DEFINE_DEVICE_TYPE(ADC0834, adc0834_device, "adc0834", "ADC0834 A/D Converter") DEFINE_DEVICE_TYPE(ADC0838, adc0838_device, "adc0838", "ADC0838 A/D Converter") adc083x_device::adc083x_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint32_t mux_bits) : device_t(mconfig, type, tag, owner, clock), m_mux_bits(mux_bits), m_cs(0), m_clk(0), m_di(0), m_se(0), m_do(1), m_sgl(0), m_odd(0), m_sel1(0), m_sel0(0), m_state(STATE_IDLE), m_bit(0), m_output(0), m_input_callback(*this) { } adc0831_device::adc0831_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : adc083x_device(mconfig, ADC0831, tag, owner, clock, 0) { } adc0832_device::adc0832_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : adc083x_device(mconfig, ADC0832, tag, owner, clock, 2) { } adc0834_device::adc0834_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : adc083x_device(mconfig, ADC0834, tag, owner, clock, 3) { } adc0838_device::adc0838_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : adc083x_device(mconfig, ADC0838, tag, owner, clock, 4) { } /*------------------------------------------------- adc083x_device::device_start -------------------------------------------------*/ void adc083x_device::device_start() { clear_sars(); // resolve callbacks m_input_callback.resolve(); // register for state saving save_item( NAME(m_cs) ); save_item( NAME(m_clk) ); save_item( NAME(m_di) ); save_item( NAME(m_se) ); save_item( NAME(m_sars) ); save_item( NAME(m_do) ); save_item( NAME(m_sgl) ); save_item( NAME(m_odd) ); save_item( NAME(m_sel1) ); save_item( NAME(m_sel0) ); save_item( NAME(m_state) ); save_item( NAME(m_bit) ); save_item( NAME(m_output) ); } /*------------------------------------------------- adc083x_device::clear_sars -------------------------------------------------*/ void adc083x_device::clear_sars() { if( type() == ADC0834 || type() == ADC0838 ) { m_sars = 1; } else { m_sars = 0; } } /*------------------------------------------------- adc083x_device::cs_write -------------------------------------------------*/ WRITE_LINE_MEMBER( adc083x_device::cs_write ) { if( m_cs != state ) { verboselog( 2, *this, "adc083x_cs_write( %s, %d )\n", tag(), state ); } if( m_cs == 0 && state != 0 ) { m_state = STATE_IDLE; clear_sars(); m_do = 1; } if( m_cs != 0 && state == 0 ) { if( type() == ADC0831 ) { m_state = STATE_MUX_SETTLE; } else { m_state = STATE_WAIT_FOR_START; } clear_sars(); m_do = 1; } m_cs = state; } /*------------------------------------------------- adc083x_device::conversion -------------------------------------------------*/ uint8_t adc083x_device::conversion() { int result; int positive_channel = ADC083X_AGND; int negative_channel = ADC083X_AGND; double positive = 0; double negative = 0; double gnd = m_input_callback(ADC083X_AGND); double vref = m_input_callback(ADC083X_VREF); if( type() == ADC0831 ) { positive_channel = ADC083X_CH0; negative_channel = ADC083X_CH1; } else if( type() == ADC0832 ) { positive_channel = ADC083X_CH0 + m_odd; if( m_sgl == 0 ) { negative_channel = positive_channel ^ 1; } else { negative_channel = ADC083X_AGND; } } else if( type() == ADC0834 ) { positive_channel = ADC083X_CH0 + m_odd + ( m_sel1 * 2 ); if( m_sgl == 0 ) { negative_channel = positive_channel ^ 1; } else { negative_channel = ADC083X_AGND; } } else if( type() == ADC0838 ) { positive_channel = ADC083X_CH0 + m_odd + ( m_sel0 * 2 ) + ( m_sel1 * 4 ); if( m_sgl == 0 ) { negative_channel = positive_channel ^ 1; } else { negative_channel = ADC083X_COM; } } if( positive_channel != ADC083X_AGND ) { positive = m_input_callback(positive_channel) - gnd; } if( negative_channel != ADC083X_AGND ) { negative = m_input_callback(negative_channel) - gnd; } result = (int) ( ( ( positive - negative ) * 255 ) / vref ); if( result < 0 ) { result = 0; } else if( result > 255 ) { result = 255; } return result; } /*------------------------------------------------- adc083x_device::clk_write -------------------------------------------------*/ WRITE_LINE_MEMBER( adc083x_device::clk_write ) { if( m_clk != state ) { verboselog( 2, *this, "adc083x_clk_write( %s, %d )\n", tag(), state ); } if( m_cs == 0 ) { if( m_clk == 0 && state != 0 ) { switch( m_state ) { case STATE_WAIT_FOR_START: if( m_di != 0 ) { verboselog( 1, *this, "adc083x %s got start bit\n", tag() ); m_state = STATE_SHIFT_MUX; m_sars = 0; m_sgl = 0; m_odd = 0; m_sel1 = 0; m_sel0 = 0; m_bit = 0; } else { verboselog( 1, *this, "adc083x %s not start bit\n", tag() ); } break; case STATE_SHIFT_MUX: switch( m_bit ) { case 0: if( m_di != 0 ) { m_sgl = 1; } verboselog( 1, *this, "adc083x %s sgl <- %d\n", tag(), m_sgl ); break; case 1: if( m_di != 0 ) { m_odd = 1; } verboselog( 1, *this, "adc083x %s odd <- %d\n", tag(), m_odd ); break; case 2: if( m_di != 0 ) { m_sel1 = 1; } verboselog( 1, *this, "adc083x %s sel1 <- %d\n", tag(), m_sel1 ); break; case 3: if( m_di != 0 ) { m_sel0 = 1; } verboselog( 1, *this, "adc083x %s sel0 <- %d\n", tag(), m_sel0 ); break; } m_bit++; if( m_bit == m_mux_bits ) { m_state = STATE_MUX_SETTLE; } break; case STATE_WAIT_FOR_SE: m_sars = 0; if( type() == ADC0838 && m_se != 0 ) { verboselog( 1, *this, "adc083x %s not se\n", tag() ); } else { verboselog( 1, *this, "adc083x %s got se\n", tag() ); m_state = STATE_OUTPUT_LSB_FIRST; m_bit = 1; } break; } } if( m_clk != 0 && state == 0 ) { switch( m_state ) { case STATE_MUX_SETTLE: verboselog( 1, *this, "adc083x %s mux settle\n", tag() ); m_output = conversion(); m_state = STATE_OUTPUT_MSB_FIRST; m_bit = 7; clear_sars(); m_do = 0; break; case STATE_OUTPUT_MSB_FIRST: m_do = ( m_output >> m_bit ) & 1; verboselog( 1, *this, "adc083x %s msb %d -> %d\n", tag(), m_bit, m_do ); m_bit--; if( m_bit < 0 ) { if( type() == ADC0831 ) { m_state = STATE_FINISHED; } else { m_state = STATE_WAIT_FOR_SE; } } break; case STATE_OUTPUT_LSB_FIRST: m_do = ( m_output >> m_bit ) & 1; verboselog( 1, *this, "adc083x %s lsb %d -> %d\n", tag(), m_bit, m_do ); m_bit++; if( m_bit == 8 ) { m_state = STATE_FINISHED; } break; case STATE_FINISHED: m_state = STATE_IDLE; m_do = 0; break; } } } m_clk = state; } /*------------------------------------------------- adc083x_device::di_write -------------------------------------------------*/ WRITE_LINE_MEMBER( adc083x_device::di_write ) { if( m_di != state ) { verboselog( 2, *this, "adc083x_di_write( %s, %d )\n", tag(), state ); } m_di = state; } /*------------------------------------------------- adc083x_device::se_write -------------------------------------------------*/ WRITE_LINE_MEMBER( adc083x_device::se_write ) { if( m_se != state ) { verboselog( 2, *this, "adc083x_se_write( %s, %d )\n", tag(), state ); } m_se = state; } /*------------------------------------------------- adc083x_device::sars_read -------------------------------------------------*/ READ_LINE_MEMBER( adc083x_device::sars_read ) { verboselog( 1, *this, "adc083x_sars_read( %s ) %d\n", tag(), m_sars ); return m_sars; } /*------------------------------------------------- adc083x_device::do_read -------------------------------------------------*/ READ_LINE_MEMBER( adc083x_device::do_read ) { verboselog( 1, *this, "adc083x_do_read( %s ) %d\n", tag(), m_do ); return m_do; }