// 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 ***************************************************************************/ const device_type ADC0831 = &device_creator; const device_type ADC0832 = &device_creator; const device_type ADC0834 = &device_creator; const device_type ADC0838 = &device_creator; adc083x_device::adc083x_device(const machine_config &mconfig, device_type type, std::string name, std::string tag, device_t *owner, UINT32 clock, std::string shortname, std::string source) : device_t(mconfig, type, name, tag, owner, clock, shortname, source), 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) { } adc0831_device::adc0831_device(const machine_config &mconfig, std::string tag, device_t *owner, UINT32 clock) : adc083x_device(mconfig, ADC0831, "ADC0831", tag, owner, clock, "adc0831", __FILE__) { m_mux_bits = 0; } adc0832_device::adc0832_device(const machine_config &mconfig, std::string tag, device_t *owner, UINT32 clock) : adc083x_device(mconfig, ADC0832, "ADC0832", tag, owner, clock, "adc0832", __FILE__) { m_mux_bits = 2; } adc0834_device::adc0834_device(const machine_config &mconfig, std::string tag, device_t *owner, UINT32 clock) : adc083x_device(mconfig, ADC0834, "ADC0834", tag, owner, clock, "adc0834", __FILE__) { m_mux_bits = 3; } adc0838_device::adc0838_device(const machine_config &mconfig, std::string tag, device_t *owner, UINT32 clock) : adc083x_device(mconfig, ADC0838, "ADC0838", tag, owner, clock, "adc0838", __FILE__) { m_mux_bits = 4; } /*------------------------------------------------- adc083x_device::device_start -------------------------------------------------*/ void adc083x_device::device_start() { clear_sars(); /* resolve callbacks */ m_input_callback.bind_relative_to(*owner()); /* 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) ); save_item( NAME(m_mux_bits) ); } /*------------------------------------------------- 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().c_str(), 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 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().c_str(), 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().c_str() ); 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().c_str() ); } 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().c_str(), m_sgl ); break; case 1: if( m_di != 0 ) { m_odd = 1; } verboselog( 1, *this, "adc083x %s odd <- %d\n", tag().c_str(), m_odd ); break; case 2: if( m_di != 0 ) { m_sel1 = 1; } verboselog( 1, *this, "adc083x %s sel1 <- %d\n", tag().c_str(), m_sel1 ); break; case 3: if( m_di != 0 ) { m_sel0 = 1; } verboselog( 1, *this, "adc083x %s sel0 <- %d\n", tag().c_str(), 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().c_str() ); } else { verboselog( 1, *this, "adc083x %s got se\n", tag().c_str() ); 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().c_str() ); 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().c_str(), 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().c_str(), 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().c_str(), 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().c_str(), 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().c_str(), 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().c_str(), m_do ); return m_do; }