1 // license:BSD-3-Clause
2 // copyright-holders:AJR
3 /**********************************************************************
4
5 National Semiconductor ADC0804 8-bit µP Compatible A/D Converter
6
7 The ADC0804 is the most widely used member of a family of five
8 CMOS 8-bit ADCs with microprocessor-compatible interfaces, which
9 differ from each other only in error specifications:
10
11 ADC0801 ±¼ LSB Full-scale adjusted
12 ADC0802 ±½ LSB Vref/2 = 2.500 Vdc
13 ADC0803 ±½ LSB Full-scale adjusted
14 ADC0804 ±1 LSB Vref/2 = 2.500 Vdc
15 ADC0805 ±1 LSB Vref/2 = NC
16
17 Though these devices are designed to convert differential analog
18 inputs, single-phase conversion can be achieved by tying pin 7
19 (negative Vin) to pin 8 (analog GND). A CD4051B or other analog
20 switch IC is often used to multiplex several input sources.
21
22 Pin 4 should be connected to either an externally generated clock
23 signal (640 kHz typical, 1460 kHz maximum) or a timing capacitor
24 plus a resistor bridged to pin 19. The resistor is typically 10 kΩ
25 but higher values may be needed due to loading conditions when
26 multiple other devices are also clocked through pin 19, which
27 yields the output of the internal Schmitt trigger clock circuit.
28
29 The ADC0804's INTR semaphore output reflects not the actual busy
30 state of the converter (as EOC does on the ADC0808) but an
31 internal flip-flop attached to it. This flip-flop is reset by
32 pulling either RD or WR active low; therefore, INTR will never
33 toggle if RD is tied to GND to keep the data outputs continuously
34 active.
35
36 **********************************************************************/
37
38 #include "emu.h"
39 #include "adc0804.h"
40
41
42 //**************************************************************************
43 // TYPE DEFINITIONS
44 //**************************************************************************
45
46 DEFINE_DEVICE_TYPE(ADC0803, adc0803_device, "adc0803", "ADC0803 A/D Converter")
47 DEFINE_DEVICE_TYPE(ADC0804, adc0804_device, "adc0804", "ADC0804 A/D Converter")
48
49 ALLOW_SAVE_TYPE(adc0804_device::read_mode);
50
51
52 //**************************************************************************
53 // DEVICE CONSTRUCTION AND INITIALIZATION
54 //**************************************************************************
55
56 //-------------------------------------------------
57 // adc0804_device - constructor
58 //-------------------------------------------------
59
adc0804_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,u32 clock)60 adc0804_device::adc0804_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock)
61 : device_t(mconfig, type, tag, owner, clock)
62 , m_vin_callback(*this)
63 , m_intr_callback(*this)
64 , m_res(0.0)
65 , m_cap(0.0)
66 , m_fclk_rc(attotime::zero)
67 , m_timer(nullptr)
68 , m_rd_mode(RD_STROBED)
69 , m_rd_active(false)
70 , m_wr_active(false)
71 , m_result(0)
72 , m_intr_active(false)
73 {
74 }
75
adc0804_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)76 adc0804_device::adc0804_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
77 : adc0804_device(mconfig, ADC0804, tag, owner, clock)
78 {
79 }
80
81
82 //-------------------------------------------------
83 // adc0803_device - constructor
84 //-------------------------------------------------
85
adc0803_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)86 adc0803_device::adc0803_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
87 : adc0804_device(mconfig, ADC0803, tag, owner, clock)
88 {
89 }
90
91
92 //-------------------------------------------------
93 // device_resolve_objects - resolve objects that
94 // may be needed for other devices to set
95 // initial conditions at start time
96 //-------------------------------------------------
97
device_resolve_objects()98 void adc0804_device::device_resolve_objects()
99 {
100 m_vin_callback.resolve_safe(0);
101 m_intr_callback.resolve_safe();
102
103 if (m_rd_mode == RD_GROUNDED)
104 m_rd_active = true;
105 }
106
107
108 //-------------------------------------------------
109 // device_start - device-specific startup
110 //-------------------------------------------------
111
device_start()112 void adc0804_device::device_start()
113 {
114 // calculate RC timing
115 if (m_res == 0.0 || m_cap == 0.0)
116 m_fclk_rc = attotime::zero;
117 else
118 m_fclk_rc = attotime::from_double(m_res * m_cap / 1.1);
119
120 // create timer
121 m_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(adc0804_device::conversion_done), this));
122
123 // save state
124 if (m_rd_mode == RD_BITBANGED)
125 save_item(NAME(m_rd_active));
126 save_item(NAME(m_wr_active));
127 save_item(NAME(m_result));
128 save_item(NAME(m_intr_active));
129 }
130
131
132 //**************************************************************************
133 // CONVERSION SEQUENCE
134 //**************************************************************************
135
136 // Conversion occurs over a busy period of 66–73 cycles, after 1–8 cycles of latency
137 const int adc0804_device::s_conversion_cycles = 74;
138
139
140 //-------------------------------------------------
141 // set_interrupt - set or clear INTR output
142 //-------------------------------------------------
143
set_interrupt(bool state)144 void adc0804_device::set_interrupt(bool state)
145 {
146 if (m_intr_active != state)
147 {
148 m_intr_active = state;
149 m_intr_callback(state ? ASSERT_LINE : CLEAR_LINE);
150 }
151 }
152
153
154 //-------------------------------------------------
155 // conversion_start - begin the busy period
156 //-------------------------------------------------
157
conversion_start()158 void adc0804_device::conversion_start()
159 {
160 if (!m_timer->enabled())
161 m_timer->adjust(m_fclk_rc != attotime::zero ? m_fclk_rc * s_conversion_cycles : clocks_to_attotime(s_conversion_cycles));
162 else
163 logerror("%s: Tried to start conversion when already in progress\n", machine().describe_context());
164 }
165
166
167 //-------------------------------------------------
168 // conversion_done - load result and signal
169 // interrupt
170 //-------------------------------------------------
171
TIMER_CALLBACK_MEMBER(adc0804_device::conversion_done)172 TIMER_CALLBACK_MEMBER(adc0804_device::conversion_done)
173 {
174 m_result = m_vin_callback();
175
176 if (!m_rd_active && !m_wr_active)
177 set_interrupt(true);
178 }
179
180
181 //**************************************************************************
182 // BUS INTERFACE
183 //**************************************************************************
184
185 //-------------------------------------------------
186 // read - read the 8-bit conversion result
187 //-------------------------------------------------
188
read()189 u8 adc0804_device::read()
190 {
191 switch (m_rd_mode)
192 {
193 case RD_STROBED:
194 if (!machine().side_effects_disabled())
195 set_interrupt(false);
196 break;
197
198 case RD_BITBANGED:
199 if (!m_rd_active)
200 return 0xff; // open bus
201 break;
202
203 case RD_GROUNDED:
204 break;
205 }
206
207 return m_result;
208 }
209
210
211 //-------------------------------------------------
212 // write - begin a new conversion
213 //-------------------------------------------------
214
write(u8 data)215 void adc0804_device::write(u8 data)
216 {
217 // data is ignored
218 set_interrupt(false);
219 conversion_start();
220 }
221
222
223 //-------------------------------------------------
224 // read_and_write - read the result of the last
225 // conversion and begin a new one
226 //-------------------------------------------------
227
read_and_write()228 u8 adc0804_device::read_and_write()
229 {
230 if (!machine().side_effects_disabled())
231 {
232 set_interrupt(false);
233 conversion_start();
234 }
235
236 if (m_rd_mode == RD_BITBANGED && !m_rd_active)
237 return 0xff; // open bus
238 else
239 return m_result;
240 }
241
242
243 //-------------------------------------------------
244 // rd_w - enable data bus by line write
245 //-------------------------------------------------
246
WRITE_LINE_MEMBER(adc0804_device::rd_w)247 WRITE_LINE_MEMBER(adc0804_device::rd_w)
248 {
249 assert(m_rd_mode == RD_BITBANGED);
250
251 // RD input is active low
252 if (!state && !m_rd_active)
253 {
254 m_rd_active = true;
255 set_interrupt(false);
256 }
257 else if (state && m_rd_active)
258 m_rd_active = false;
259 }
260
261
262 //-------------------------------------------------
263 // wr_w - begin conversion by line write
264 //-------------------------------------------------
265
WRITE_LINE_MEMBER(adc0804_device::wr_w)266 WRITE_LINE_MEMBER(adc0804_device::wr_w)
267 {
268 // WR input is active low
269 if (!state && !m_wr_active)
270 {
271 m_wr_active = true;
272 set_interrupt(false);
273 }
274 else if (state && m_wr_active)
275 {
276 m_wr_active = false;
277 conversion_start();
278 }
279 }
280