Shop OBEX P1 Docs P2 Docs Learn Events
ADC-to-Millivolts running in background of Spin2 — Parallax Forums

ADC-to-Millivolts running in background of Spin2

In the new PNut_v34p I just posted, there's a new program called ADC_to_VGA_millivolts.spin2.

It has a PASM program that runs in the background of Spin2 to perform calibrated ADC measurements, where it resolves to millivolt values. I changed the way REG[] works in Spin2, so that you can easily reference any registers declared under ORG blocks, and address them within your own cog. You can see in the 4th line below "PUB go() | i" how REG can be used:
' Program reads 1..8 consecutive ADC pins and displays millivolts for each.
' A terminate-stay-resident PASM program performs conversions in background.
' Samples are always available via REG[samples][{0..7}].

CON
  _clkfreq = 297_000_000		'set clock frequency
  _pins    = 16 addpins 7		'do conversions on these pins

OBJ
  vga : "VGA_640x480_text_80x40"	'_clkfreq >= 50MHz
' vga : "VGA_1280x1024_text_160x85"	'_clkfreq >= 216MHz
' vga : "HDTV_1920x1080_text_240x90"	'_clkfreq >= 297MHz
' vga : "HDTV_1920x1080_text_135x160"	'_clkfreq >= 297MHz

PUB go() | i

  vga.start(8)			'start vga

  reg[pins] := _pins		'set 'pins' variable
  regexec(@adc_pasm)		'launch PASM program (uses 'pins')

  send := @vga.print		'set send pointer

  repeat			'print samples set every 100ms
    repeat i from 0 to _pins >> 6
      send(dec4(reg[samples][i]), "mV ")
    send(13)
    waitms(100)

PRI dec4(value) | digflag, negflag, place, digit

  digflag~
  negflag := value < 0
  abs= value
  place := 1_000
  repeat
    if digflag || (digit := value / place // 10) or place == 1
      if not digflag~~
        send(negflag ? "-" : " ")
      send("0" + digit)
    else
      send(" ")
    if place == 1_000
      send(digflag ? "," : " ")
  while place /= 10

DAT

'********************************************************
'* PASM Program which launches via REGEXEC		*
'* Sets up an interrupt and then runs in the background *
'* Performs calibrated ADC measurements of up to 8 pins *
'* Reports samples in millivolts			*
'********************************************************
'
'
' REGEXEC header words
'
adc_pasm	word	adc_start		'initial register to load
		word	adc_end-adc_start-1	'additional registers to load

		org	$B0			'put PASM code up high
'
'
' ADC program setup
'
adc_start	fltl	pins			'set pins to ADC mode
		wrpin	adc_modes,pins
		wxpin	#9,pins			'#9 is 512-clock, 10-bit conversion (8 per sample)
		drvl	pins			'start pins on same clock

		mov	pin_base,pins		'get pin base
		and	pin_base,#$3F

		mov	pin_count,pins		'get pin count
		shr	pin_count,#6
		add	pin_count,#1

		mov	level_base,#adc_levels	'prepare level_base altd pointer
		sub	level_base,pin_base
		bith	level_base,#9

		mov	ijmp1,#adc_isr		'set interrupt jump

		mov	x,pin_base		'set base pin IN-rise event
		or	x,#%001<<6
		setse1	x

	_ret_	setint1	#int_se1		'enable interrupt on event, return to Spin2
'
'
' ADC interrupt service routine - runs in background of Spin2 interpreter
'
adc_isr		akpin	pins			'ADC samples done, acknowledge pins

		alts	cycle,#vio_levels	'lookup vio and gio levels for sample computation
		mov	x,0
		alts	cycle,#gio_levels
		mov	y,0

		sub	x,y			'(3300 << 12) / (vio_level - gio_level)
		qdiv	##3300<<12,x		'cordic runs during REP

		rep	#3,pin_count		'read ADC samples and sum into adc_levels
		rdpin	x,pin_base
		altd	pin_base,level_base
		add	0,x

		sub	pin_base,pin_count	'restore pin_base

		getqx	x			'get QDIV quotient

		alts	cycle,#pin_levels	'(quotient * (pin_level - gio_level)) >> 12
		subr	y,0
		muls	y,x
		sar	y,#12

		altd	cycle,#samples		'write finished sample
		mov	0,y

		incmod	cycle,#7	wc	'repeat for 8 cycles
	if_nc	reti1				'return to Spin2


		altd	state,#adc_modes	'end of 8th cycle, switch to next gio/vio/pin
		wrpin	0,pins

		resi1				'return to Spin2, resume on next interrupt


		akpin	pins			'9th cycle, acknowledge pins

		alts	state,#moves		'move adc_levels to gio/vio/pin_levels
		mov	x,0
		rep	#2,pin_count
		alti	x,#%111_111
		mov	0,0

		resi1				'return to Spin2, resume on next interrupt


		akpin	pins			'10th cycle, acknowledge pins

		setd	x,#adc_levels		'clear adc_levels
		rep	#2,pin_count
		alti	x,#%111_000
		mov	0,#0

		incmod	state,#3		'increment state

		mov	ijmp1,#adc_isr		'return to Spin2, start over on next interrupt
		reti1
'
'
' Defined data
'
cycle		long	0	'cycles {0..7, 0, 0} for each state
state		long	0	'states {0..3}

adc_modes	long	p_adc_gio | p_adc		'adc modes, by state
		long	p_adc_1x  | p_adc
		long	p_adc_vio | p_adc
		long	p_adc_1x  | p_adc

moves		long	pin_levels<<9 | adc_levels	'moves, by state
		long	gio_levels<<9 | adc_levels
		long	pin_levels<<9 | adc_levels
		long	vio_levels<<9 | adc_levels

adc_end				'end of PASM code to load into registers
'
'
' Undefined data
'
pins		res	1	'initially set by Spin2 code to select the pins

x		res	1
y		res	1
pin_base	res	1
pin_count	res	1
level_base	res	1

adc_levels	res	8	'conversions are accumulated into this buffer
gio_levels	res	8	'..and then copied to one of these three buffers
vio_levels	res	8
pin_levels	res	8

samples		res	8	'final samples, available via REG[samples][{0..7}]
'
'
'	cycle		operations
'	------------------------------------------------------------------------------------
'	0 (1st)		add conversions to levels, compute sample[0]
'	1		add conversions to levels, compute sample[1]
'	2		add conversions to levels, compute sample[2]
'	3		add conversions to levels, compute sample[3]
'	4		add conversions to levels, compute sample[4]
'	5		add conversions to levels, compute sample[5]
'	6		add conversions to levels, compute sample[6]
'	7 (8th)		add conversions to levels, compute sample[7], switch to gio/vio/pin
'	0 (9th)		move levels to gio/vio/pin_levels
'	0 (10th)	clear levels, advance state
'
'	state		operations
'	------------------------------------------------------------------------------------
'	0		sample gio for calibration
'	1		sample pin for signal
'	2		sample vio for calibration
'	3		sample pin for signal
'

The program assumes VIO is 3.300V. Because of mismatch in the calibration resistor paths, readings are offset by a few mV on top and bottom. If I get a chance to redesign the ADC, I will put the calibration switches BEFORE the big resistor. That would reduce the error by quite a bit. Note that the first pin is tied to GND, while the second pin is tied to VIO. Some pins are quieter than others and I'm not sure why. I believe it has to do with process variances between components within the ADC circuits.

Here is a picture of it running:

Comments

  • jmgjmg Posts: 15,140
    What is the noise floor of an ADC channel running highest gain, and Pin decoupled to GND ?
    There is another thread talking about chopper-handling a thermocouple signal, but I suspect the P2 noise floor may be too high for that direct use ?
  • cgraceycgracey Posts: 14,133
    edited 2020-03-29 00:57
    jmg wrote: »
    What is the noise floor of an ADC channel running highest gain, and Pin decoupled to GND ?
    There is another thread talking about chopper-handling a thermocouple signal, but I suspect the P2 noise floor may be too high for that direct use ?

    I'm not sure, but it can digitize complex waveforms with 30 millivolts of amplitude at full scale.
  • pilot0315pilot0315 Posts: 834
    edited 2020-04-06 01:39
    @cgracey

    In the new PNut_v34p I just posted, there's a new program called ADC_to_VGA_millivolts.spin2.


    I have been out of the loop for three weeks. I see version Q. What is the latest version. Where is this posting about PNut_34 p and the es to ADC_to_VGA_millivolts.spin2.

    I am confused.
    Had to convert in class curriculum at my Aircraft Mechanic school to online in less than two weeks. Now finally coming up for air and getting back to the P2

    Thanks
    Martin
Sign In or Register to comment.