Main Page

adc.c
Go to the documentation of this file.
1 /*
2 * $Id: adc.c,v 1.4 2004/03/13 19:55:34 troth Exp $
3 *
4 ****************************************************************************
5 *
6 * simulavr - A simulator for the Atmel AVR family of microcontrollers.
7 * Copyright (C) 2003, 2004 Keith Gudger
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 ****************************************************************************
24 */
25
26 /**
27 * \file adc.c
28 * \brief Module to simulate the AVR's ADC module.
29 *
30 */
31
32 #include <config.h>
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37
38 #include "avrerror.h"
39 #include "avrmalloc.h"
40 #include "avrclass.h"
41 #include "utils.h"
42 #include "callback.h"
43 #include "op_names.h"
44
45 #include "storage.h"
46 #include "flash.h"
47
48 #include "vdevs.h"
49 #include "memory.h"
50 #include "stack.h"
51 #include "register.h"
52 #include "sram.h"
53 #include "eeprom.h"
54 #include "timers.h"
55 #include "ports.h"
56 #include "adc.h"
57
58 #include "avrcore.h"
59
60 #include "intvects.h"
61
62 /****************************************************************************\
63 *
64 * ADC Interrupts
65 *
66 \****************************************************************************/
67
68 static void adc_iadd_addr (VDevice *vdev, int addr, char *name, int rel_addr,
69 void *data);
70 static uint8_t adc_intr_read (VDevice *dev, int addr);
71 static void adc_intr_write (VDevice *dev, int addr, uint8_t val);
72 static void adc_intr_reset (VDevice *dev);
73 static int adc_intr_cb (uint64_t time, AvrClass *data);
74 static int adc_clk_incr_cb (uint64_t ck, AvrClass *data);
75
76 /** \brief Allocate a new ADC interrupt */
77
78 VDevice *
79 adc_int_create (int addr, char *name, int rel_addr, void *data)
80 {
81 return (VDevice *)adc_intr_new (addr, name, rel_addr);
82 }
83
84 ADCIntr_T *
85 adc_intr_new (int addr, char *name, int rel_addr)
86 {
87 ADCIntr_T *adc;
88
89 adc = avr_new (ADCIntr_T, 1);
90 adc_intr_construct (adc, addr, name, rel_addr);
91 class_overload_destroy ((AvrClass *)adc, adc_intr_destroy);
92
93 return adc;
94 }
95
96 /** \brief Constructor for adc interrupt object. */
97
98 void
99 adc_intr_construct (ADCIntr_T *adc, int addr, char *name, int rel_addr)
100 {
101 if (adc == NULL)
102 avr_error ("passed null ptr");
103
104 vdev_construct ((VDevice *)adc, adc_intr_read, adc_intr_write,
105 adc_intr_reset, adc_iadd_addr);
106
107 if (rel_addr)
108 adc->rel_addr = rel_addr;
109 adc_iadd_addr ((VDevice *)adc, addr, name, 0, NULL);
110
111 adc_intr_reset ((VDevice *)adc);
112 }
113
114 static void
115 adc_iadd_addr (VDevice *vdev, int addr, char *name, int rel_addr, void *data)
116 {
117 ADCIntr_T *adc = (ADCIntr_T *)vdev;
118
119 if (strncmp ("ADCSR", name, 5) == 0)
120 {
121 adc->adcsr_addr = addr;
122 }
123
124 else if (strncmp ("ADMUX", name, 5) == 0)
125 {
126 adc->admux_addr = addr;
127 }
128
129 else
130 {
131 avr_error ("invalid ADC register name: '%s' @ 0x%04x", name, addr);
132 }
133 }
134
135 /** \brief Destructor for adc interrupt object. */
136
137 void
138 adc_intr_destroy (void *adc)
139 {
140 if (adc == NULL)
141 return;
142
143 vdev_destroy (adc);
144 }
145
146 static uint8_t
147 adc_intr_read (VDevice *dev, int addr)
148 {
149 ADCIntr_T *adc = (ADCIntr_T *)dev;
150
151 if (addr == adc->adcsr_addr)
152 return (adc->adcsr);
153
154 else if (addr == adc->admux_addr)
155 return (adc->admux);
156
157 else
158 avr_error ("Bad address: 0x%04x", addr);
159
160 return 0; /* will never get here */
161 }
162
163 static void
164 adc_intr_write (VDevice *dev, int addr, uint8_t val)
165 {
166 ADCIntr_T *adc = (ADCIntr_T *)dev;
167 CallBack *cb;
168 ADC_T *adc_d;
169
170 adc_d =
171 (ADC_T *)avr_core_get_vdev_by_addr ((AvrCore *)
172 vdev_get_core ((VDevice *)adc),
173 adc->rel_addr);
174
175 if (addr == adc->adcsr_addr)
176 {
177 if (val & mask_ADIF) /* clears interrupt flag */
178 adc->adcsr = val & ~mask_ADIF;
179 else
180 adc->adcsr = val;
181
182 if ((val & mask_ADSC) && (val & mask_ADEN))
183 {
184 if ((adc->intr_cb == NULL))
185 {
186 /* we need to install the intr_cb function */
187 cb = callback_new (adc_intr_cb, (AvrClass *)adc);
188 adc->intr_cb = cb;
189 avr_core_async_cb_add ((AvrCore *)vdev_get_core (dev), cb);
190 }
191 if ((adc_d->clk_cb == NULL))
192 {
193 /* we need to install the clk_cb function */
194 cb = callback_new (adc_clk_incr_cb, (AvrClass *)adc_d);
195 adc_d->clk_cb = cb;
196 avr_core_clk_cb_add ((AvrCore *)
197 vdev_get_core ((VDevice *)adc_d), cb);
198 }
199 adc_d->adc_count = 13;
200 switch ((adc->adcsr) & (mask_ADPS0 | mask_ADPS1 | mask_ADPS2))
201 {
202 case ADC_CK_0:
203 case ADC_CK_2:
204 adc_d->divisor = 2;
205 break;
206 case ADC_CK_4:
207 adc_d->divisor = 4;
208 break;
209 case ADC_CK_8:
210 adc_d->divisor = 8;
211 break;
212 case ADC_CK_16:
213 adc_d->divisor = 16;
214 break;
215 case ADC_CK_32:
216 adc_d->divisor = 32;
217 break;
218 case ADC_CK_64:
219 adc_d->divisor = 64;
220 break;
221 case ADC_CK_128:
222 adc_d->divisor = 128;
223 break;
224 default:
225 avr_error ("The impossible happened!");
226 }
227 }
228 else
229 {
230 adc->intr_cb = NULL; /* no interrupt are enabled, remove
231 the callback */
232 }
233 }
234
235 else if (addr == adc->admux_addr)
236 {
237 adc->admux = val;
238 }
239
240 else
241 {
242 avr_error ("Bad address: 0x%04x", addr);
243 }
244 }
245
246 static void
247 adc_intr_reset (VDevice *dev)
248 {
249 ADCIntr_T *adc = (ADCIntr_T *)dev;
250
251 adc->intr_cb = NULL;
252
253 adc->adcsr = 0;
254 adc->admux = 0;
255 }
256
257 static int
258 adc_intr_cb (uint64_t time, AvrClass *data)
259 {
260 ADCIntr_T *adc = (ADCIntr_T *)data;
261
262 if (adc->intr_cb == NULL)
263 return CB_RET_REMOVE;
264
265 if ((adc->adcsr & mask_ADEN) && (adc->adcsr & mask_ADIE)
266 && (adc->adcsr & mask_ADIF))
267 {
268 /* an enabled interrupt occured */
269 AvrCore *core = (AvrCore *)vdev_get_core ((VDevice *)adc);
270 avr_core_irq_raise (core, irq_vect_table_index (ADC));
271 adc->adcsr &= ~mask_ADIF;
272 }
273
274 return CB_RET_RETAIN;
275 }
276
277 /****************************************************************************\
278 *
279 * ADC
280 *
281 \****************************************************************************/
282
283 static void adc_add_addr (VDevice *vdev, int addr, char *name, int rel_addr,
284 void *data);
285 static uint8_t adc_read (VDevice *dev, int addr);
286 static void adc_write (VDevice *dev, int addr, uint8_t val);
287 static void adc_reset (VDevice *dev);
288
289 /** \brief Allocate a new ADC structure. */
290
291 VDevice *
292 adc_create (int addr, char *name, int rel_addr, void *data)
293 {
294 uint8_t *data_ptr = (uint8_t *) data;
295 if (data)
296 return (VDevice *)adc_new (addr, name, (uint8_t) * data_ptr,
297 rel_addr);
298 else
299 avr_error ("Attempted A/D create with NULL data pointer");
300 return 0;
301 }
302
303 ADC_T *
304 adc_new (int addr, char *name, uint8_t uier, int rel_addr)
305 {
306 ADC_T *adc;
307
308 adc = avr_new (ADC_T, 1);
309 adc_construct (adc, addr, name, uier, rel_addr);
310 class_overload_destroy ((AvrClass *)adc, adc_destroy);
311
312 return adc;
313 }
314
315 /** \brief Constructor for ADC object. */
316
317 void
318 adc_construct (ADC_T *adc, int addr, char *name, uint8_t uier, int rel_addr)
319 {
320 if (adc == NULL)
321 avr_error ("passed null ptr");
322
323 vdev_construct ((VDevice *)adc, adc_read, adc_write, adc_reset,
324 adc_add_addr);
325
326 if (rel_addr)
327 adc->rel_addr = rel_addr;
328 adc_add_addr ((VDevice *)adc, addr, name, 0, NULL);
329
330 adc_reset ((VDevice *)adc);
331 adc->u_divisor = uier ? 12 : 1;
332 }
333 static void
334 adc_add_addr (VDevice *vdev, int addr, char *name, int rel_addr, void *data)
335 {
336 ADC_T *adc = (ADC_T *)vdev;
337
338 if (strncmp ("ADCL", name, 4) == 0)
339 {
340 adc->adcl_addr = addr;
341 }
342
343 else if (strncmp ("ADCH", name, 4) == 0)
344 {
345 adc->adch_addr = addr;
346 }
347
348 else
349 {
350 avr_error ("invalid ADC register name: '%s' @ 0x%04x", name, addr);
351 }
352 }
353
354 /** \brief Destructor for ADC object. */
355
356 void
357 adc_destroy (void *adc)
358 {
359 if (adc == NULL)
360 return;
361
362 vdev_destroy (adc);
363 }
364
365 static uint8_t
366 adc_read (VDevice *dev, int addr)
367 {
368 ADC_T *adc = (ADC_T *)dev;
369
370 if (addr == adc->adcl_addr)
371 return adc->adcl;
372
373 else if (addr == adc->adch_addr)
374 return adc->adch;
375
376 else
377 avr_error ("Bad address: 0x%04x", addr);
378
379 return 0; /* will never get here */
380 }
381
382 static void
383 adc_write (VDevice *dev, int addr, uint8_t val)
384 {
385 avr_error ("Bad ADC write address: 0x%04x", addr);
386 }
387
388 static void
389 adc_reset (VDevice *dev)
390 {
391 ADC_T *adc = (ADC_T *)dev;
392
393 adc->clk_cb = NULL;
394
395 adc->adcl = 0;
396 adc->adch = 0;
397
398 adc->adc_count = 0;
399 adc->adc_in = 0;
400 adc->divisor = 0;
401 }
402
403 static int
404 adc_clk_incr_cb (uint64_t ck, AvrClass *data)
405 {
406 ADC_T *adc = (ADC_T *)data;
407 uint8_t last = adc->adc_count;
408 ADCIntr_T *adc_ti;
409
410 adc_ti =
411 (ADCIntr_T *)avr_core_get_vdev_by_addr ((AvrCore *)
412 vdev_get_core ((VDevice *)
413 adc),
414 adc->rel_addr);
415
416 if (adc->clk_cb == NULL)
417 return CB_RET_REMOVE;
418
419 if (adc->divisor <= 0)
420 avr_error ("Bad divisor value: %d", adc->divisor);
421
422 /* decrement clock if ck is a multiple of divisor */
423 adc->adc_count -= ((ck % (adc->divisor * adc->u_divisor)) == 0);
424
425 if (adc->adc_count != last) /* we've changed the counter */
426 {
427 if (adc->adc_count == 0)
428 {
429 adc_ti->adcsr |= mask_ADIF;
430 adc_ti->adcsr &= ~mask_ADSC;
431 adc->adc_in = adc_port_rd (adc_ti->admux);
432 adc->adcl = (adc->adc_in) & 0xff; /* update adcl to what we
433 read */
434 adc->adch = ((adc->adc_in) >> 8) & 0x03; /* update adch */
435 if (adc_ti->adcsr & mask_ADFR) /* free running mode */
436 adc->adc_count = 13;
437 else
438 {
439 adc->clk_cb = NULL;
440 return CB_RET_REMOVE;
441 }
442 }
443 }
444 return CB_RET_RETAIN;
445 }
446
447 /* FIXME: TRoth/2003-11-29: These will eventually need to be plugged into an
448 external connection interface. */
449
450 uint16_t
451 adc_port_rd (uint8_t mux)
452 {
453 int data;
454 char line[80];
455
456 while (1)
457 {
458 fprintf (stderr, "\nEnter data to read into the ADC for channel %d: ",
459 mux);
460
461 /* try to read in a line of input */
462 if (fgets (line, sizeof (line), stdin) == NULL)
463 continue;
464
465 /* try to parse the line for a byte of data */
466 if (sscanf (line, "%d\n", &data) != 1)
467 continue;
468
469 break;
470 }
471 return (uint16_t) (data & 0x3ff);
472 }
473
474 void
475 adc_port_wr (uint8_t val)
476 {
477 fprintf (stderr, "wrote 0x%02x to ADC\n", val);
478 }
avr_new
#define avr_new(type, count)
Macro for allocating memory.
Definition: avrmalloc.c:57
adc_int_create
VDevice * adc_int_create(int addr, char *name, int rel_addr, void *data)
Allocate a new ADC interrupt.
Definition: adc.c:79
avr_core_async_cb_add
void avr_core_async_cb_add(AvrCore *core, CallBack *cb)
Add a new asynchronous callback to list.
avr_core_get_vdev_by_addr
VDevice * avr_core_get_vdev_by_addr(AvrCore *core, int addr)
Returns the VDevice which handles the address addr.
vdev_destroy
void vdev_destroy(void *dev)
Destructor for a VDevice.
Definition: device.c:105
adc_destroy
void adc_destroy(void *adc)
Destructor for ADC object.
Definition: adc.c:357
avr_core_irq_raise
void avr_core_irq_raise(AvrCore *core, unsigned int irq)
Raises an irq by adding it&#39;s data to the irq_pending list.
Definition: avrcore.c:854
vdev_get_core
AvrClass * vdev_get_core(VDevice *dev)
Get the core field.
vdev_construct
void vdev_construct(VDevice *dev, VDevFP_Read rd, VDevFP_Write wr, VDevFP_Reset reset, VDevFP_AddAddr add_addr)
Constructor for a VDevice.
Definition: device.c:89
adc_construct
void adc_construct(ADC_T *adc, int addr, char *name, uint8_t uier, int rel_addr)
Constructor for ADC object.
Definition: adc.c:318
avr_error
#define avr_error(fmt, args...)
Print an error message to stderr and terminate program.
Definition: avrerror.c:50
adc_intr_destroy
void adc_intr_destroy(void *adc)
Destructor for adc interrupt object.
Definition: adc.c:138
avr_core_clk_cb_add
void avr_core_clk_cb_add(AvrCore *core, CallBack *cb)
Add a new clock callback to list.
adc_intr_construct
void adc_intr_construct(ADCIntr_T *adc, int addr, char *name, int rel_addr)
Constructor for adc interrupt object.
Definition: adc.c:99
adc_create
VDevice * adc_create(int addr, char *name, int rel_addr, void *data)
Allocate a new ADC structure.
Definition: adc.c:292
class_overload_destroy
void class_overload_destroy(AvrClass *klass, AvrClassFP_Destroy destroy)
Overload the default destroy method.
Definition: avrclass.c:92

Automatically generated by Doxygen 1.8.12 on Tue Apr 18 2017.