writing an auto ranging program
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?
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
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]repeat
time := cnt
indention are important.
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.
{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.detectI am still working on writing the mux bit program which will be (EDAQ.detect)
{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) }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.
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:
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.
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?
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!
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 ADChere 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 countAnother 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.
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!