diff options
Diffstat (limited to 'src/devices/machine/adc0804.cpp')
-rw-r--r-- | src/devices/machine/adc0804.cpp | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/src/devices/machine/adc0804.cpp b/src/devices/machine/adc0804.cpp new file mode 100644 index 00000000000..aad5b88ffb3 --- /dev/null +++ b/src/devices/machine/adc0804.cpp @@ -0,0 +1,263 @@ +// license:BSD-3-Clause +// copyright-holders:AJR +/********************************************************************** + + National Semiconductor ADC0804 8-bit µP Compatible A/D Converter + + The ADC0804 is the most widely used member of a family of five + CMOS 8-bit ADCs with microprocessor-compatible interfaces, which + differ from each other only in error specifications: + + ADC0801 ±¼ LSB Full-scale adjusted + ADC0802 ±½ LSB Vref/2 = 2.500 Vdc + ADC0803 ±½ LSB Full-scale adjusted + ADC0804 ±1 LSB Vref/2 = 2.500 Vdc + ADC0805 ±1 LSB Vref/2 = NC + + Though these devices are designed to convert differential analog + inputs, single-phase conversion can be achieved by tying pin 7 + (negative Vin) to pin 8 (analog GND). A CD4051B or other analog + switch IC is often used to multiplex several input sources. + + Pin 4 should be connected to either an externally generated clock + signal (640 kHz typical, 1460 kHz maximum) or a timing capacitor + plus a resistor bridged to pin 19. The resistor is typically 10 kΩ + but higher values may be needed due to loading conditions when + multiple other devices are also clocked through pin 19, which + yields the output of the internal Schmitt trigger clock circuit. + + The ADC0804's INTR semaphore output reflects not the actual busy + state of the converter (as EOC does on the ADC0808) but an + internal flip-flop attached to it. This flip-flop is reset by + pulling either RD or WR active low; therefore, INTR will never + toggle if RD is tied to GND to keep the data outputs continuously + active. + +**********************************************************************/ + +#include "emu.h" +#include "adc0804.h" + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +DEFINE_DEVICE_TYPE(ADC0804, adc0804_device, "adc0804", "ADC0804 A/D Converter") + +ALLOW_SAVE_TYPE(adc0804_device::read_mode); + + +//************************************************************************** +// DEVICE CONSTRUCTION AND INITIALIZATION +//************************************************************************** + +//------------------------------------------------- +// adc0804_device - constructor +//------------------------------------------------- + +adc0804_device::adc0804_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : device_t(mconfig, ADC0804, tag, owner, clock) + , m_vin_callback(*this) + , m_intr_callback(*this) + , m_res(0.0) + , m_cap(0.0) + , m_fclk_rc(attotime::zero) + , m_timer(nullptr) + , m_rd_mode(RD_STROBED) + , m_rd_active(false) + , m_wr_active(false) + , m_result(0) + , m_intr_active(false) +{ +} + + +//------------------------------------------------- +// device_resolve_objects - resolve objects that +// may be needed for other devices to set +// initial conditions at start time +//------------------------------------------------- + +void adc0804_device::device_resolve_objects() +{ + m_vin_callback.resolve_safe(0); + m_intr_callback.resolve_safe(); + + if (m_rd_mode == RD_GROUNDED) + m_rd_active = true; +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void adc0804_device::device_start() +{ + // calculate RC timing + if (m_res == 0.0 || m_cap == 0.0) + m_fclk_rc = attotime::zero; + else + m_fclk_rc = attotime::from_double(m_res * m_cap / 1.1); + + // create timer + m_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(adc0804_device::conversion_done), this)); + + // save state + if (m_rd_mode == RD_BITBANGED) + save_item(NAME(m_rd_active)); + save_item(NAME(m_wr_active)); + save_item(NAME(m_result)); + save_item(NAME(m_intr_active)); +} + + +//************************************************************************** +// CONVERSION SEQUENCE +//************************************************************************** + +// Conversion occurs over a busy period of 66–73 cycles, after 1–8 cycles of latency +const int adc0804_device::s_conversion_cycles = 74; + + +//------------------------------------------------- +// set_interrupt - set or clear INTR output +//------------------------------------------------- + +void adc0804_device::set_interrupt(bool state) +{ + if (m_intr_active != state) + { + m_intr_active = state; + m_intr_callback(state ? ASSERT_LINE : CLEAR_LINE); + } +} + + +//------------------------------------------------- +// conversion_start - begin the busy period +//------------------------------------------------- + +void adc0804_device::conversion_start() +{ + if (!m_timer->enabled()) + m_timer->adjust(m_fclk_rc != attotime::zero ? m_fclk_rc * s_conversion_cycles : clocks_to_attotime(s_conversion_cycles)); + else + logerror("%s: Tried to start conversion when already in progress\n", machine().describe_context()); +} + + +//------------------------------------------------- +// conversion_done - load result and signal +// interrupt +//------------------------------------------------- + +TIMER_CALLBACK_MEMBER(adc0804_device::conversion_done) +{ + m_result = m_vin_callback(); + + if (!m_rd_active && !m_wr_active) + set_interrupt(true); +} + + +//************************************************************************** +// BUS INTERFACE +//************************************************************************** + +//------------------------------------------------- +// read - read the 8-bit conversion result +//------------------------------------------------- + +u8 adc0804_device::read() +{ + switch (m_rd_mode) + { + case RD_STROBED: + if (!machine().side_effects_disabled()) + set_interrupt(false); + break; + + case RD_BITBANGED: + if (!m_rd_active) + return 0xff; // open bus + break; + + case RD_GROUNDED: + break; + } + + return m_result; +} + + +//------------------------------------------------- +// write - begin a new conversion +//------------------------------------------------- + +void adc0804_device::write(u8 data) +{ + // data is ignored + set_interrupt(false); + conversion_start(); +} + + +//------------------------------------------------- +// read_and_write - read the result of the last +// conversion and begin a new one +//------------------------------------------------- + +u8 adc0804_device::read_and_write() +{ + if (!machine().side_effects_disabled()) + { + set_interrupt(false); + conversion_start(); + } + + if (m_rd_mode == RD_BITBANGED && !m_rd_active) + return 0xff; // open bus + else + return m_result; +} + + +//------------------------------------------------- +// rd_w - enable data bus by line write +//------------------------------------------------- + +WRITE_LINE_MEMBER(adc0804_device::rd_w) +{ + assert(m_rd_mode == RD_BITBANGED); + + // RD input is active low + if (!state && !m_rd_active) + { + m_rd_active = true; + set_interrupt(false); + } + else if (state && m_rd_active) + m_rd_active = false; +} + + +//------------------------------------------------- +// wr_w - begin conversion by line write +//------------------------------------------------- + +WRITE_LINE_MEMBER(adc0804_device::wr_w) +{ + // WR input is active low + if (!state && !m_wr_active) + { + m_wr_active = true; + set_interrupt(false); + } + else if (state && m_wr_active) + { + m_wr_active = false; + conversion_start(); + } +} |