Shop OBEX P1 Docs P2 Docs Learn Events
Experimenting with A/D modes in TAQOZ - help me! — Parallax Forums

Experimenting with A/D modes in TAQOZ - help me!

Peter JakackiPeter Jakacki Posts: 10,193
edited 2019-01-25 09:26 in Propeller 2
As much as I wanted to experiment with A/D and D/A I just haven't had time. But I decided to do a one liner in TAQOZ and although it is a bit basic, it seems to do the job:
pub ADC ( cycles -- )	F %100_011_0000000_00_01111 WRFNC WXPIN 0 WYPIN L ;
So if I select a pin such as 40 PIN I can then say 40000 ADC to setup pin 40. I haven't done any auto calibration yet since I have to delve through the documentation but I just setup an array for all the pins to store a min reading for 0V and a reference reading for 1V, but mind you this is only just for mucking about.
64 words amin	64 words aref
Then I create a couple of words to reference those arrays using the current pin and create a VOLTS@ word which will read 1,000 for 1V in, and 3,000 for 3V in etc.
: AREF	@PIN 2* aref + ;
: AMIN	@PIN 2* amin + ;
pub SETADC ( min ref -- )	AREF W! AMIN W! ;
pub VOLTS@ ( -- volts*1000 )	RDPIN AMIN W@ - 1000 AREF W@ */ ;
Now I grab a pin at random and feed 0V into it from my programmable supply, plus I'm using a 10k series resistor just in case.
TAQOZ# 24 PIN 40,000 ADC ---  ok
TAQOZ#  ---  ok
TAQOZ# RDPIN . --- 6903 ok
So I manually set the AMIN or zero ref with this reading.
TAQOZ# 6903 AMIN W! ---  ok

Now I set the pin to 1V, read, adjust and save that value (the long way)
TAQOZ# RDPIN . --- 14667 ok
TAQOZ# 14667 AMIN W@ - . --- 7764 ok
TAQOZ# 7764 AREF W! ---  ok

With this manual calibration I take readings for 1V 2.75V 3.3V and 0.25V
TAQOZ# VOLTS@ . --- 999 ok
TAQOZ# VOLTS@ . --- 2753 ok
TAQOZ# VOLTS@ . --- 3303 ok
TAQOZ# VOLTS@ . --- 251 ok
That's not too bad I think even if it is rough and ready. But I have only just started so I would welcome any tips for sure.

Comments

  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2019-01-25 09:56
    Here's a quick n dirty DAC mode to set a pin to a voltage:
    pub DAC ( bits -- )	%101000000000001000110 WRPIN |< WXPIN L ;
    pub V ( volts*1000 --- )	$1.0000 3300 */ WYPIN ;
    
    Configure a pin:
    38 PIN 12 DAC
    

    To set the pin to 1.85V
    1.850 V
    

    It's pretty close, my meter reads 1.88V. Any tips?
  • Peter,
    In the definition of ADC you use the word WRFNC. What does that do?

    Do these definitions require the version 2 TAQOZ or just the v 1.0 rom?

    Your efforts are doing a lot to help me understand the smart pins.
    Thanks
    Tom
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2019-01-25 21:27
    Tom, you inspired me to sit down and play with the analog modes. I initially had a look at your routines and then made them more general purpose after which I looked at using the ADC as simply as possible so that it could be setup on the fly to read any and many pins. While I have seen a great deal of effort going into "improving" the A/D modes, I haven't however really seen any practical examples such as the old "microphone to VGA" demo that was done for the P1.

    With the smartpins there is the basic 5-bit M field to set the mode along with a 2-bit T field for direction control plus all the other bit-fields as we see here: %AAAA_BBBB_FFF_PPPPPPPPPPPPP_TT_MMMMM_0

    WRFNC floats the pin (to reset the smartpin), takes the 5+2-bit field and shifts it left one bit then merges it with any other bit-fields that have been set such as filters and low-level pin control, then does a WRPIN after which it will enable it by setting the DIR bit with a L. There are a lot of optional bit-fields for each smartpin so I figured doing it this way could help although I probably choose a better name for the function.
    : WRFNC ( MMMMM -- )		F 2* _pinmode @ OR WRPIN L ;
    

    Once I get a handle on these modes I may write a "microphone to VGA" as a one-liner.

    All these tests I am doing at present are mainly designed for V2 since this is the version that will need to be ready for the ROM very soon. Of course it may get cut down a lot even after compression, but I'd like to have these extra smartpin modes operating.
  • Peter thanks for the info.

    Off topic --- One thing I noticed was how you define & use arrays.
    When I wrote "Starting Forth" forth I used the following for defining & using arrays:
    \ Create an integer array <name> 
    
    : ARRAY              \ name ( #cells)
    CREATE cells ALLOT
    DOES> ( i -- 'cell) SWAP cells + ;
    
    To store to or fetch from an element: ele# variable_name ! or @ (ele# required for element 0)
    That way I didn't have to do the multiply and add to the base variable.

    I recall that Tachyon did not have CREATE DOES. Is there a way to do that in TAQOZ?

    Thanks
    Tom
  • Tom, Tachyon eventually did get a version of CREATE DOES and maybe I might do the same for TAQOZ too.

    Standard Forth inlines the dictionary header, link field etc and then a code field pointer followed by the "parameters". TAQOZ of course is very different as it keeps the dictionary header separate (then there is no need for a link field), but there is no code field and parameter field, it just assume by the address that it is assembly/threaded/special.

    The code here is very compact:
    64 words amin	64 words aref
    : AREF	@PIN 2* aref + ;
    : AMIN	@PIN 2* amin + ;
    
    AREF takes 10 bytes but even so I could combine it in such a way with AMIN so the lot takes 14 bytes instead.

    BTW, you didn't have to do a multiply but CELLS is one of those ANSI definitions which in our case would be:
    : CELLS  4* ;
    
    But 4* in TAQOZ is simply shift left 2 bits anyway. Also, ARRAY assumes you want CELLS, not bytes or words or whatever, so that definition is not very flexible but fairly typical of many PC Forths. It is easy enough to create just the structure we need or not need without any fancy standards.

  • Peter,
    I've modified my adc program to auto calibrate. It is not a very efficient program (lots of lines & variables) but it works and I think it is pretty clear.
    I want to modify it to sample a number of calibrations as well as re-calibrating every few K readings in case there is an effect of temperature rise.
    With the pot set to 3.3v I am getting steady reading to 3 digits with variations in digits 4 and 5.
    Here's the code.
    ---  ------------------------------------------------------
    ---	**** adcspnewcogcal.fth
    ---	Use Pin 22 as smart pin in ADC mode
    ---	The program calibrates at ground and VIO and then reads the potentiometer voltage
    --- 	adcsp runs in cog 1
    --- 	raw data is printed to terminal from cog 0
    --- Mode %100_000_0000000_00_01111_0 is the ground calibration setting, 
    --- Mode %100_011_0000000_00_01111_0 will read the A input (ie the pin normally) 
    --- Mode %100_001_0000000_00_01111_0 is Vio calibration
    
    ------------------------------------------------------
    ---	Define Constants
    
       %100_000_0000000_00_01111_0 := ADC_MODE_VSS  \ ground calibration
       %100_001_0000000_00_01111_0 := ADC_MODE_VIO   \ VIO calibration
       %100_011_0000000_00_01111_0 := ADC_MODE   \ ADC A input
       22 := ADC_PIN 
       49152 := ADC_CYCLES
       3.3000 := VREF 	\ ref voltage * 10000
    
    ---  Variables
       long vadcspraw
       long calmin
       long calmax
       long delcal
       long v_delay
    
    : adcsp  ( -- )
    --- cal zero
    ADC_PIN PIN F 	  \ set pin dir = 0
    ADC_MODE_VSS WRPIN  \ ADC mode ground cal
    ADC_CYCLES WXPIN 	\ ADC_CYCLES
    0 WYPIN
    L 1 ms		  \ set pin dir = 1 and delay
    RDPIN calmin !
    1 ms
    
    --- cal vio
    ADC_PIN PIN F 	  \ set pin dir = 0
    ADC_MODE_VIO WRPIN  \ ADC_MODE VIO Calibration
    ADC_CYCLES WXPIN 	\ ADC_CYCLES
    0 WYPIN
    L 1 ms		  \ set pin dir = 1 and delay
    RDPIN calmax !
    1 ms
    
    --- read potentiometer
    ADC_PIN PIN F 	  \ set pin dir = 0
    ADC_MODE WRPIN  \ ADC_MODE
    ADC_CYCLES WXPIN 	\ ADC_CYCLES
    0 WYPIN
    L 1 ms		  \ set pin dir = 1 and delay
    Begin		  \ measure continuously
      RDPIN vadcspraw !
      10 ms
    AGAIN
    ;
    
    : adcdata  ( -- )
    clkhz 150 80_000_000 */ v_delay !
    calmax @ calmin @ - delcal !
    BEGIN
      vadcspraw @ dup CRLF .
      calmin @ - VREF * delcal @ / 
      CRLF .AS" #.#### volts "
      v_delay @ ms
    KEY 1 and UNTIL	   		\ press 1 to stop
    1 COGSTOP
    ;
    
    \ enter next 2 lines and then adcdata <ret>
    1 NEWCOG
    ' adcsp 1 TASK W! 
    \ --------------
    \ --- **** Note the 2 lines above must be entered each time
    \        adcdata is stopped by pressing key 1 and restarted.
    \ --------------
    
    

  • @twm47099 - Here's a simplified version of your code showing how you use a common function WRADC to do all the repetitive housework in setting up a mode. But once your adcdata stops due to a key press (KEY returns only one value, the code as a "flag") you have to manually restart it. So this version selects the pin and starts it for you type 22 ADC but if you stop it, you can type 22 ADC again without manually restarting the cog, or use any other pin.

    Of course this code of yours doesn't need the A/D running in another cog since there is no advantage to simply performing a RDPIN whenever you want to read the value in the main printing loop, but I'm guessing you just wanted to play with using another cog.

    I stripped out any superfluous comments and moved the delcal code from the printing loop to where it belongs in the task where it obtains and calculates these values. Finally I placed the raw reading and the voltage reading on the same line as it should look cleaner and easier to follow and the printing delay is fixed independent of clkhz if you use V1.1 or V2 (recommended). BTW, putting the 150ms delay at the start of the loop takes care of any task startup and settling delay too so there is no need to have an extra and redundant delays before this.

    Hope you don't mind a few tips and you may find that by factoring and simplifying you can reduce unnecessary comments and clutter and make your code more readable, not only for others, but for yourself. Once it is more readable for yourself you will see where you can simplify and improve even more.

    BTW - I made it stop when you hit '1' but I'm not sure if that is what you meant since your code did a 1 AND on the returned code so that means that any key that and the lsb set would exit but 0 or 2 or B etc would not.

    Also, I will be using the calibration modes myself too, I just like to check the absolute bare basics first to establish a reference. Thanks for trying out the A/D.
    {
    	**** adcspnewcogcal.fth
    	Use Pin 22 as smart pin in ADC mode
    	The program calibrates at ground and VIO and then reads the potentiometer voltage
    	adcsp runs in cog 1
    	raw data is printed to terminal from cog 0
    }
    
    --- Constants
    
       %100_000_0000000_00_01111_0 := ADC_MODE_VSS	--- ground calibration
       %100_001_0000000_00_01111_0 := ADC_MODE_VIO	--- VIO calibration
       %100_011_0000000_00_01111_0 := ADC_MODE	--- ADC A input
    
       49152 := ADC_CYCLES
       3.3000 := VREF 				--- ref voltage * 10000
    
    ---  Variables
    
       long vadc		--- current reading
       long calmin
       long calmax
       long delcal
    
    
    : WRADC ( mode -- )		F WRPIN ADC_CYCLES WXPIN 0 WYPIN L 1 ms ;
    
    : ADC.TASK  ( -- )
    --- cal zero
    	0 PIN 			--- default value of 0 gets overwritten 
    	ADC_MODE_VSS WRADC	--- ADC mode ground cal
    	RDPIN calmin !
    	ADC_MODE_VIO WRADC	--- ADC_MODE VIO Calibration
    	RDPIN DUP calmax !
    	calmin @ - delcal !
    --- read potentiometer
    	ADC_MODE WRADC		--- ADC_MODE
    	BEGIN
    	  RDPIN vadc !
    	  10 ms
    	AGAIN
    	;
    
    : ADC  ( pin -- )
    	' ADC.TASK C!		--- write pin directly to ADC.TASK constant (first word)
    	1 NEWCOG ' ADC.TASK 1 TASK W!
    	BEGIN
    	  150 ms
    	  CRLF vadc @ DUP 5 U.R
    	  SPACE calmin @ - 0 MAX VREF delcal @ */ 
    	  .AS" #.#### volts "
    	  KEY '1' =
    	UNTIL
    	1 COGSTOP
    	;
    
  • Peter,
    Thanks for the critique and explanations. I've made some assumptions about how some word work that were wrong, and I appreciate your help and the time it takes you.

    In my versions, I had tried to put the words to start a new cog and run "adcsp" in cog 1 into "adcdata", but I was not able to get it working in a definition. I forgot about cog start up time.

    Thanks particularly for your explanations re "why" as well as how, that really helps.

    Tom
  • I find that when I put
    1 NEWCOG ' ADC.TASK 1 TASK W!
    
    into a definition that I have to add a pause after NEWCOG such as
    1 NEWCOG 10 ms ' ADC.TASK 1 TASK W!
    
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2019-01-26 23:31
    twm47099 wrote: »
    I find that when I put
    1 NEWCOG ' ADC.TASK 1 TASK W!
    
    into a definition that I have to add a pause after NEWCOG such as
    1 NEWCOG 10 ms ' ADC.TASK 1 TASK W!
    

    Yes, that's right, I remember that it does but it was a long day and well after midnight, so I'm glad you picked this one up. After a new TAQOZ cog is loaded and started up it initializes its variables, including the entry in the TASK array which is why we need a delay. But how much of a delay? While 10ms is not a problem for this little demo, it is an awfully long time in other more demanding applications. So I tested it:
    TAQOZ# ' CONSOLE 1 TASK W! ---  ok
    TAQOZ# LAP 1 NEWCOG BEGIN 1 TASK W@ 0= UNTIL LAP .LAP --- 1,547 cycles= 6,445ns @240MHz ok
    
    Turns out that even 10 us would suffice but you could also 2000 WAITX to make it independent of the clock frequency.

    So it got me thinking since it is so easy to run NEWCOG that perhaps I could also pass the address of the TASK to it so that extra TAQOZ cogs can be run on-demand. Then I have high level interrupts to look at too.

    How about:
    : RUN ( task cog -- )	DUP NEWCOG 2000 WAITX TASK W! ;
    
    ( to use: ' <mytask> 1 RUN )
    

Sign In or Register to comment.