Shop OBEX P1 Docs P2 Docs Learn Events
writing an auto ranging program — Parallax Forums

writing an auto ranging program

ElkinElkin Posts: 58
edited 2012-02-08 03:13 in Propeller 1
I am trying to write an auto ranging program that can tell a little conductivity detector to switch its parameters (frequency, excitation voltage, and sampling range) using the protocol already in place.

Here is a little example of what I have in mind. The issue that I have is that I need to tell it when to change the values on channel 0 while also sending the 3 proceeding strings only one time. The way it is currently written, strings will be sent continuously and it won't work like that. I was thinking that there could perhaps be some type of repeat loop added here or maybe I have to use a different conditional command than IF (maybe using UNTIL). Any ideas?
CON

  _clkmode = XTAL1 + PLL16X                     ' Crystal and PLL settings
  _xinfreq = 5_000_000                          ' 5 MHz crystal




  rx = 30        'tx on Isopod rx on prop (green)
  tx = 1         'rx on Isopod tx on prop (white)


#1, HOME, #8, BKSP, TAB, LF, CLREOL, CLRDN, CR, #16, CLS      ' PST formmatting control
DAT


crlf                    byte    CR, LF, 0
OBJ


 term: "fullduplexserial"


PUB Detect | time


  term.start(Rx, Tx, 00, 115_200)


  DIRA[rx]~
  DIRA[tx]~~


  time := cnt


     term.str(string(CR, CR,))


     waitcnt(time += 400_000_000)


     term.str(string("set range 0.02 0.2 490", CR))


     waitcnt(time += 400_000_000)


     term.str(string("set k 160", CR))


     waitcnt(time += 400_000_000)


     term.str(string("sample ascii 50", CR))                                    'sample ascii at 50 Hz


     IF ((ch0 > 0.01) AND (ch0 =< 0.1))
        term.str(string("!",CR))                                                        'stop current sampling to reset range
        term.str(string("set range 0.2 0.2 990", CR))                         'set parameters to new range values
        term.str(string("sample ascii 50",CR))                                   'sample ascii at 50 Hz



     IF ((ch0 > 0.1) AND (ch0 =< 1))
        term.str(string("!",CR))
        term.str(string("set range 2 0.02 990", CR))
        term.str(string("sample ascii 50",CR))


     IF ((ch0 > 1) AND (ch0 =< 10))
        term.str(string("!",CR))
        term.str(string("set range 20 0.2 9990", CR))
        term.str(string("sample ascii 50",CR))


     IF ((ch0 =< 0.01))
        term.str(string("!",CR))
        term.str(string("set range 0.02 0.2 490", CR))
        term.str(string("sample ascii 50",CR))


Comments

  • PliersPliers Posts: 280
    edited 2012-02-04 17:49
    How well is your project going?
    I enjoy reading the forums, but I don't have a lot of real programming time. I looked at your code , did not try running it, it will only operate once because there is no LOOP instruction.
    I added a REPEAT command that should put your machine into an infinite loop.
    I planned to show your code with the added REPEAT command, but I cannot get the code to display correctly.
    CON  _clkmode = XTAL1 + PLL16X                     ' Crystal and PLL settings  _xinfreq = 5_000_000                          ' 5 MHz crystal  rx = 30        'tx on Isopod rx on prop (green)  tx = 1         'rx on Isopod tx on prop (white)#1, HOME, #8, BKSP, TAB, LF, CLREOL, CLRDN, CR, #16, CLS      ' PST formmatting controlDATcrlf                    byte    CR, LF, 0OBJ term: "fullduplexserial"PUB Detect | time  term.start(Rx, Tx, 00, 115_200)  DIRA[rx]~  DIRA[tx]~~  time := cnt     term.str(string(CR, CR,))     waitcnt(time += 400_000_000)     term.str(string("set range 0.02 0.2 490", CR))     waitcnt(time += 400_000_000)     term.str(string("set k 160", CR))     waitcnt(time += 400_000_000)     term.str(string("sample ascii 50", CR))                                    'sample ascii at 50 Hz     IF ((ch0 > 0.01) AND (ch0 =< 0.1))        term.str(string("!",CR))                                                        'stop current sampling to reset range        term.str(string("set range 0.2 0.2 990", CR))                         'set parameters to new range values        term.str(string("sample ascii 50",CR))                                   'sample ascii at 50 Hz     IF ((ch0 > 0.1) AND (ch0 =< 1))        term.str(string("!",CR))        term.str(string("set range 2 0.02 990", CR))        term.str(string("sample ascii 50",CR))     IF ((ch0 > 1) AND (ch0 =< 10))        term.str(string("!",CR))        term.str(string("set range 20 0.2 9990", CR))        term.str(string("sample ascii 50",CR))     IF ((ch0 =< 0.01))        term.str(string("!",CR))        term.str(string("set range 0.02 0.2 490", CR)) [LEFT][COLOR=#333333][FONT=Parallax]        
    [/FONT][/COLOR][/LEFT]
    
    
    
    Put the REPEAT before Time := CNT


    repeat
    time := cnt

    indention are important.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-02-04 18:41
    Elkin,

    Can you descibe in more detail what you want your code to do. It would help if you walked through it step by step and stated what you want it to do.
  • ElkinElkin Posts: 58
    edited 2012-02-06 01:10
    {This code is supposed to control a isopod conductivity detector from EDAQ.  the ispod protocol operates on protocol already loaded into thefirmware and the goal here is to mimic the PC terminal commands with commands on the propeller. Because the isopod is not auto ranging and
    because conductivity is not linear, I have to adjust the range so that the measurements are reproducable. What I am trying to do here is to
    write a code that will change the range based on the current measurement while still sampling at 50 Hz. Every time a new command is sent to
    the isopod a signal needs to be resent to start sampling at 50 Hz again.
    
    
    ISOPOD Protocol:
    
    
    > set range <r> <V> <f>      Set range 200, 20, 2, 0.2, 0.02, 0.002 mS, Set excitation voltage between 0- 0.2 V, Set freq between 10- 10000 Hz.
    > set k <value>              Set the cell constant of the conductivity probe. Values are reported in (mS/cm).
    > zero now                   Take a reading and apply an offset to adjust the reported value to zero.  Offset applied to subsequent readings.
    > r                          Return a single reading.
    > sample ascii <f>           Return a single value at a frequency between 1-100 Hz.
    > sample ascii <#>           Return a single value every time a # is sent.
    > !                          Exit current sampling mode.
    > interval ascii <time>      Return one sample every <time> in seconds.
    > version                    Returns the firmware version.
    
    
    Because this data will be stored on flash memory, it may be easiest to sample a single reading while syncing the measurements with the SD_card
    clock.  This should allow the bits to be properly written and stored.
    

    The flash memory program that I want to use for this is not written yet but I am most likely going to use something like this:
    sync := cnt  count := 1
      
      repeat
        read_EDAQ(@ch0)                                              ' get data
    
    
        ' write to log file
    
    
        \sdcard.pputs(dec2str(count, @sbuf))                        ' write count
        \sdcard.pputc(",")                                          ' separate
        \sdcard.pputs(dec2str(ch0, @sbuf))                          ' write ch0 value
        '\sdcard.pputc(",")                                          ' separate
        '\sdcard.pputs(dec2str(ch1, @sbuf))                          ' write ch1 value
        \sdcard.pputs(@crlf)                                        ' terminate line
    
    
        if (term.rxcheck <> -1)                                     ' if key pressed
          quit                                                      '  abort
    
    
        waitcnt(sync += clkfreq/10)                                 ' update every 100 milliseconds
        count += 1                                                  ' update readings count
                      
      \sdcard.pclose
      term.str(string("-- log file closed", CR))
      term.str(string("-- records logged: "))
      term.dec(count)
         
      term.str(string(CR, CR, "Done.", CR))   
      \sdcard.unmount
    
    
      repeat
        waitcnt(0)                                                  ' stop here    
        
    pub read_EDAQ (pntr0) | ch0
    
    
        long[pntr0] := EDAQ.detect
    

    I am still working on writing the mux bit program which will be (EDAQ.detect)
  • ElkinElkin Posts: 58
    edited 2012-02-06 01:23
    Here is a bit more explaining of what I would like the code to do...
    {IF ((ch0 > 0.01) AND (ch0 =< 0.1))                                         'if ch0 reading is between 0.01 and 0.1        term.str(string("!",CR))                                                'stop current sampling to reset range
            term.str(string("set range 0.2 0.2 990", CR))                           'set parameters to new range values
            term.str(string("sample ascii 50",CR))                                  'sample ascii at 50 Hz
    
    
         IF ((ch0 > 0.1) AND (ch0 =< 1))                                             'if ch0 reading is between 0.1 and 1
            term.str(string("!",CR))                                                'stop current sampling to reset range
            term.str(string("set range 2 0.02 990", CR))
            term.str(string("sample ascii 50",CR))
    
    
         IF ((ch0 > 1) AND (ch0 =< 10))                                              'if ch0 reading is between 1 and 10
            term.str(string("!",CR))                                                'stop current sampling to reset range
            term.str(string("set range 20 0.2 9990", CR))
            term.str(string("sample ascii 50",CR))
    
    
         IF ((ch0 =< 0.01))                                                          'if ch0 reading is less than 0.01
            term.str(string("!",CR))                                                'stop current sampling to reset range
            term.str(string("set range 0.02 0.2 490", CR))
            term.str(string("sample ascii 50",CR))
    
    
            }
    
    
    { the goal here is to have to correct settings for the specified range.  A ! has to be sent to exit the sampling before anything else can
    be done.  the range and parameters need to be sent. Lastly, the sampling needs to begin again.  It may be another option to write
    the sampling code such as:
    
    
    Repeat
      term.str(string("r", CR))                             'take a single reading
      waitcnt(time += 8_000_000 +cnt)                       'sync counter with system (approx. 0.1 sec)
      Quit if: ch0 <> range specified                       'quit if range is invalid (if conductivity changes enough, the parameters must be adjusted)
    
    
    }
    
    
    
  • Toby SeckshundToby Seckshund Posts: 2,027
    edited 2012-02-06 02:16
    It is good to put a small amount of hysterisis on the boundy changes, because everything you measure will be smack on top of them and keep flitting back and forth between ranges.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-02-06 06:45
    I think I'm starting to understand what you're after.

    You want to send a command to change the range but only once.

    To do this, your program needs to remember what the previous range was. This is a very common need in computer/uC programming.

    Setup some constants to define ranges.
    CON
      ' range enumeration
      #0, _Thousandths, _Hundredths, _Tenths, _Units, _Tens
    

    I don't know if you've seen constants set up like this before or not. The above is just an easier way of writing:
    CON
      _Thousandths = 0
      _Hundredths = 1
      _Tenths = 2
      _Units = 3
      _Tens = 4
    

    It's important that each constant have a different value.

    Setup two variables to keep track of the range. Since these varaiables are only going to have the values of 0 through 4, I'll use byte variables.
    VAR
      byte previousRange, currentRange
    

    Early in the program you would set "previousRange" to a value outside of the possible values of ranges to make sure the first range read is detected as a new range.
    PUB Main
      previousRange := 5
    
      repeat
        if ch0 =< 0.01
          currentRange := _Thousandths     
        elseif ch0 =< 0.1    ' you don't need to check for the lower end of value
                            'since only values larger than 0.01 make it to this part of code 
          currentRange := _Hundredths 
        elseif ch0 =< 1
          currentRange := _Teths
        elseif ch0 =< 10
          currentRange := _Units
        else ' since this is the largest possible range, you just use "else" instead of "else if"
          currentRange := _Tens
        if currentRange <> previousRange  ' is this a new range?
          SetRange(currentRange)
        previousRange := currentRange 
    
    PUB SetRange(rangeToSet)
      ' send range setting command 
      case rangeToSet
        _Thousands:
          ' send command to change range to thousandths
        _Hundredths:
          ' send command to change range to hundreths.
    

    As usual with programming, there are more than one way of doing this.

    Is this what you're trying to do?
  • ElkinElkin Posts: 58
    edited 2012-02-06 06:55
    Duane,

    This seems like it is exactly what I am trying to do. I have used constants like this before as well so this really isn't anything new, I just couldn't visualize it before so thanks for that! Now I just have to get it to save on to the memory card!
  • ElkinElkin Posts: 58
    edited 2012-02-07 05:51
    Here is my code with everything into it as I would like it to run, EDAQ.spin. I am still having a problem with the auto ranging. The problem seems to be that the ranges will not switch from the "_Thousandths" setting. The prop doesn't seem to be reading the values coming in from the meter. My guess is that my ch0 variable is causing the problem here. Do I need another pub here form the prop to read ch0 for example:
    pub read(ch, mode) | mux
    
    '' Read MCP3202 channel
    '' -- ch is channel, 0 to 1
    '' -- mode is 0 for single-ended, 1 for differential
    
    
      mux := %1100 | ((mode & 1) << 2) | ((ch & %1) << 1)           ' create mux bits
      return readx(mux) 
    
    
      
    pub readx(mux) | level
    
    
    '' Read MCP3202 channel
    '' -- mux is encoded mux bits for channel/mode
    
    
      outa[cs] := 0                                                 ' activate adc
      dira[dio] := 1                                                ' dio is output
    
    
      ' output mux bits, MSBFIRST
      
      mux <<= constant(32-4)                                        ' prep for msb output
      repeat 4                                                      ' send mux bits
        outa[dio] := (mux <-= 1) & 1                                ' output a bit
        outa[clk] := 1                                              ' clock the bit
        outa[clk] := 0
    
    
      ' Note: Null bit is output on falling edge of last clock of mux bits (MSBF bit)
      ' -- this behavior is specific to MCP3202 only
        
      ' input data bits, MSBPOST
      
      dira[dio] := 0                                                ' dio is input
      level := 0                                                    ' clear work var  
      repeat 12                                                     ' 12 data bits
      'repeat 24                                                     ' 24 data bits
        outa[clk] := 1                                              ' clock a bit
        outa[clk] := 0
        level := (level << 1) | ina[dio]                            ' input data bit
    
    
      outa[cs] := 1                                                 ' de-activate adc
      
      return (level & $FFF)                                         ' 12-bit ADC
      'return (level & $FFFFFF)                                      ' 24-bit ADC
    
    
    

    here below: I know this is not going to work directly but I know it is something like this.
    repeat    read_adc(@ch0)                                              ' get data
    
    
        ' write to log file
    
    
        \sdcard.pputs(dec2str(count, @sbuf))                        ' write count
        \sdcard.pputc(",")                                          ' separate
        \sdcard.pputs(dec2str(ch0, @sbuf))                          ' write ch0 value
        '\sdcard.pputc(",")                                          ' separate
        '\sdcard.pputs(dec2str(ch1, @sbuf))                          ' write ch1 value
        \sdcard.pputs(@crlf)                                        ' terminate line
    
    
        if (term.rxcheck <> -1)                                     ' if key pressed
          quit                                                      '  abort
    
    
        waitcnt(sync += clkfreq/10)                                 ' update every 100 milliseconds
        count += 1                                                  ' update readings count
    
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-02-07 08:10
    You do need a way of changing "ch0" for the code to work. You would either include the if, ifelse tests within the main loop which also reads the "ch0" value, or the main loop could call a method with all the comparisons (you'd want to remove the top repeat).

    Another alternative is to have a separate cog updating the ch0 value.

    I don't understand your second section of code in post #9. Does "read_adc(@ch0)" return the number of times you want the loop repeated? Even if this is the case, I don't think it's a good idea to use a method call as a parameter of repeat since (as Phil pointed out) Spin calls the method each time it loops. There are times calling methods as a repeat parameter would be okay, but I think they all have a while or until added to the repeat statement to indicate when to exit the loop based on the return value of the method call.
  • ElkinElkin Posts: 58
    edited 2012-02-08 03:13
    The second bit of code in post #9 above is from the SD card reader object from obex. In the bit of code, the repeat loop writes the ADC values from ch0 into an open SD card file. More specifically, read_adc(@ch0) calls the first bit of code which reads the data or gets the data from the ADC. from there, the SD_card object writes this into the open SD file along with a count of how many times this loop has been repeated. this is just so you can keep track of how many values you have recorded. One of the alternatives that you listed above, "the main loop could call a method with if/else tests" would be an example of that as far as I understand it.

    Here is the EDAQ.spin code that I have updated. I have changed the code from post #9 to fit into this code better and tried to make it easier to understand. It would probably be just as easy to put the code that reads ch0 directly into the main loop that includes the if/ if else tests but here it is with the main loop calling the read_ch0 code. I am not really sure I understand the difference between having the repeat loop call a method with the if/ if else tests from the read_ch0 code. if it does make a difference as you mentioned then that really wouldn't be that difficult to change.

    Thanks for the help!
Sign In or Register to comment.