Request for help with a linear sensor driver. Assembly code challenge?
chris_bfs
Posts: 38
in Propeller 1
I'm thinking this may be easy for folks who know their way around Assembly and Spin; I'm a novice at Spin and have no clue with anything Assembly. Would the experts out there kindly take a whack at this?
Problem: If no magnet is present on the sensor, the Propeller "hangs up" when it tries to use the sensor. how could this driver code be changed such that the Prop doesn't wait forever for a return pulse?
Physical device description: Magnetostrictive sensor, specifically the EP2 start/stop model from MTS Sensors, found here: http://www.mtssensors.com/fileadmin/media/pdfs/Products/Industrial/E_Series/Data_Sheet_E-Series_EP2_Start_Stop_551335_EN.pdf
Driver code (not written by me):
Problem: If no magnet is present on the sensor, the Propeller "hangs up" when it tries to use the sensor. how could this driver code be changed such that the Prop doesn't wait forever for a return pulse?
Physical device description: Magnetostrictive sensor, specifically the EP2 start/stop model from MTS Sensors, found here: http://www.mtssensors.com/fileadmin/media/pdfs/Products/Industrial/E_Series/Data_Sheet_E-Series_EP2_Start_Stop_551335_EN.pdf
Driver code (not written by me):
' ' propeller code to return magnet position as a function of ' MCU clock cycles for the Temposonics EP2D linear position ' sensor. ' ' ' LIMITATIONS: ' ' This function returns the position of the first magnet only. ' (Magnet nearest head of sensor) ' ' This function assumes the reference magnet is affixed to the ' end of the sensor. It will time out (and fail to loop) if ' there is no magnet on the sensor. THE CODE SHOULD BE CHANGED ' TO WAIT A FIXED AMOUNT OF TIME THEN LOOP IF NO VALUE IS ' RETURNED. In practice this has not been a problem except when ' running on a breadboard. ' ' VAR long pulseWidth PUB start '' Start a cog with the measurement routine return cognew(@entry,@pulseWidth) PUB getPosition '' Return the elapsed time (in clock ticks) between start and stop pulses pulseWidth := 0 ' Use zero to indicate no pulse seen yet repeat until pulseWidth ' Wait until this changes to non-zero return pulseWidth ' Return the new value PUB getPositionNoWait return pulseWidth PUB clearPosition pulseWidth := 0 DAT entry org init mov t1, #1 wz ' configure start pin as output shl t1, startPin muxz outa, t1 ' set it LOW muxnz dira, t1 mov t2, #1 wz ' configure stop pin as input shl t2, stopPin muxz dira, t2 mov sampTime, cnt ' Set up delay add sampTime, #9 ' Add 9 clk cycles - time needed to execute this block waitcnt sampTime, sampDelay loop mov t1, #0 wz,nr mov startTime,cnt ' Save the system clock value muxz outa, t1 ' Set the startPin high nop nop nop nop nop nop ' series of NOP's to send 1.5 us pulse nop nop nop nop nop nop nop nop nop nop nop nop nop nop muxnz outa, t1 ' Set startPin low nop nop nop nop nop nop ' series of NOP's to wait for bogus STOP to go low nop nop nop nop nop nop nop nop nop nop nop nop nop nop waitpne zeroes,t2 ' Wait for stopPin to go high (was #%01) mov endTime,cnt ' Get the system clock value sub endTime,startTime ' Calculate elapsed tim wrlong endTime,PAR ' Return the elapsed time 'mov tempvar, t2 'wrlong tempvar,PAR ' Return the elapsed time waitcnt sampTime, sampDelay ' Pause - sensor sampling freq is ~40Hz jmp #loop ' Loop zeroes long 0 stopPin long 24 startPin long 25 sampDelay long 2_500_000 ' delay in clock cycles between sensor samples 'trigDelay long 110 startTime res 1 ' Start pulse system clock endTime res 1 ' Stop pulse system clock (and calculated difference) t1 res 1 t2 res 1 trigTime res 1 sampTime res 1 tempvar res 1
Comments
Your driver is fine(assuming the assembly is fine), it even has a way to do a "no wait get position."
So from your main code, which isn't attached, don't call "getPosition"
Call "getPositionNoWait"
But in a repeat loop. I am guessing if you don't know spin or assembly, then chances are you won't be able to do this even.
And then you must accept that after you call getPositionNoWait you could have a return value of 0, and deal with it in that loop.
It looks like your driver is fine, its your top level spin code that calls the driver.
If you don't know spin, or assembly, you will need someone to help you with the main code, not the driver code you posted here.
This is one of the problems with many people that need help, they don't, or can't, post ALL the code.
You've corroborated my earlier hunch, thanks - I'm going to take another whack at using the NoWait routine... thanks for taking the time to comment.
As long as the START function is still run, then the background code runs identically whether you run the getposition or getpositionnowait.
As long as you still run the start function, and even if you never run getposition or getpositionnowait, your sensor is still being measured and stored into @pulseWidth
If you don't check getpositionnowait fast enough, you might miss something, which is why they probably ran the wait routine.
Really, unless you are running out of cogs (unlikely), you can just run a loop that repeately checks pulseWidth for a change
This code will loop forever only storing values into pulsewidthGood that are not 0
Then pulsewidthGood will always have the last known value that isn't 0.
This is all very rough code stuff...
I can't report success (or failure) at this point, just wanted to thank you for responding.
Will report back whether the no-wait routine works out.
Best,
Chris
That gets tricky...
The highest precision comes from using waitpne, as here, but that does mandate a STOP pulse arriving.
You can do a timeout loop with DJNZ & Pin-poll or similar, but the granularity of that is not as good - perhaps 12x worse.
What precision can you tolerate ?
It says the energise pulse is ~1.5us, is the echo pulse the same width ?
What you might be able to do, is use a Prop CTR in gated mode L=Count, H=Pause
eg CTRMODE = %10101 increments on !A (Pin A = low)
You then Clr CTR, then poll pinA, and immediately after poll exit, read the counter.
The poll loop is coarse, but the counter stops on __/==== with 1 SysCLK precision.
And on your waitpxx you simply wait for a change of any of them two pins.
Enjoy!
Mike
I solved similar situation differently. I needed the precision of waitpne. I used one COG to do this. It would wait indefinitely. Another COG acted as a watchdog and killed it when it timed out.
I am still fighting with that English language.
Ahh, I get it you killed the whole COG?
Enjoy!
Mike
jmg - that's good info; yes, I remember (vaguely), long ago when a developer was taking a look at that code, it seemed we could only get the precision we wanted w/ that assembly routine; but, we may have missed something.
msrobots and rbehm - that sounds exactly like what I'd like to do.. the accuracy of PNE but with a watchdog or some other method to kill the waiting loop... that's great. Would you please send some example code of how you implemented your particular approach to this? rbehm, I had looked into the watchdog approach before and thought that it wasn't possible, for various reasons. Obviously, if you did it, I was wrong and I'd love to know what I missed.
Please consider that I'm a novice coder and mostly just try to understand what others have done and modify it; so, I'm hoping any explanations or code samples you post (I hope you do) will be geared for a neophyte but, hey, I'll take whatever you want to send me.
thanks again, in advance, for any help.
Chris
FYI, I'm working on a project which will help ecologists and biologists decrease the costs of taking the data required for their scientific studies. The studies typically relate to the health of aquatic species, but can be amphibians or other species, as well.
Do you have a spare pin ? The simplest timeout, is as msrobots mentioned above, to use a spare pin, and WAIT masks for either of two pins.
You might be able to dual-purpose the start pin, if you have nothing to spare.
A timer can drive that pin, with timeouts up to many seconds.
How many of these channels do you want to run ?
What timing precision do you need ? (or, what mm / fraction mm LSB must you get ).
So, I think I understand what you're saying, but it seems like this approach is just as accurate (or maybe missing a system clock cycle, so .. half the accuracy?) of the other methods. What am I missing, such that this approach is 12x lower resolution? Please expand on the "poll loop" and the inherent "courseness" you mentioned, if you don't mind.
thanks again - Chris
thanks much,
Chris
The COG counters can output pulses/frequencies to the pins, one example would be to use this
data sheet :
CTRMODE = %00100 NCO single-ended A-Pin = PHSA[31]
PHSA is then cleared on our start pulse, and set to count at some rate that will set bit31 after some time. (longest possible timeout is 2^31/80M = 26.84s)
You need to decide what timeout to allow, and configure the FRQA PHSA values.
WAITPNE then waits on two pins, the Echo pin, and the newly added PHSA[31] pin, and exits pin whichever changes first.
data sheet:
WAITPNE D,S Wait for pins not equal (INA & S) != D
S is the mask, now with 2 bits high, one for Echo Pin, and one for Timer bit31.
D is idle state, if both pins start 0.0 and you want 1.0 or 0.1 to exit, then D = 00 (exits on either pin high)
Once it exits, you might want to test which pin causes exit, so timer bit31 gives some clear 'no probe' return value.
If you replace the WAITPNE with a loop that checks the pins and a timeout counter, the resolution is reduced to 8*12.5ns = 100ns. This gives values around 250'000 at 40 Hz, still over 18bit resolution.
So I would replace: with: and add the timeout value to the definitions at the end of the assembly code (before the res variables): You can use also shorter or longer timeouts: value = ms * 10_000
Andy
You need to check the full-scale delays of the probe, the reading repetition speed does not matter so much.
From memory, these magnet probes do need quite high clocks to resolve sub-mm LSBs
The OP has not given a SysCLks per mm for the probes ?
After browsing the first page of the datasheet it seems somewhat similar to a PING sensor in concept...ultrasonic time of flight.
I think you could benefit from studying the main 'top' code of the PING sensor.
jmg, it's approx 28.8 clock cycles per mm.
typical starting clock cycles (at "zero" mm mark) = ~1200
those are values I'm measuring w/ Propeller and the routine posted, not an external instrument. If the Prop is missing some clock cycles, I wouldn't catch that.
The label on the side of the sensor says:" GRD: 9.1000uS/ln " The sensors come in both inch and mm varieties and they're the same sensors, just labeled differently for convenience. I have some other sensors, same model but different lengths, some in inch and some in mm. One of the "inch" variety gives a GRD of 9.1847uS/ln. All the GRDs are approx 9.1 but vary by ~.1 either way.
Thanks Ariba, that's great; I'll give it a try, report back.
Lardom, I'm sure you're right. I just can't figure out how to interrupt the routine which waits on the pin to toggle (from the top code). I've been working on other things, but will start in on trying to use the no-wait routine. However, when the developer put this together long ago, the "hang up" was a known issue, one we didn't want, so there must be some reason why he didn't use the "no wait" routine (ie, if that one could have worked, seems he would have used it.) I don't know what prevented him, I'm just assuming there's something.
thanks everyone, for weighing in!
success! The 500_000 value for the timeout was too long, for some reason, but changing it to 200_000 or less and it's working! It may work at larger values, I haven't found the exact "breaking point".
fantastic, thanks to everyone who replied. I learned a lot, puzzling through all the stuff. Who knows, maybe I'll be able to pick up some assembly.
28.8 SysCLKs / mm is what you get at 80MHz,(12.5ns) so LSB is 0.03472' mm
The polling loop will be more coarse,(100ns) so some values will skip and LSB is then 0.277' mm
If that more coarse measurement is ok, the polling code you have working is fine to use.
I can not post the code I used, but here is a simple description:
I had 2 COGs running:
- COG 1 was the "foreground" process
It initiated the measurement by starting the second COG and the waited for it to finish and use thed result
- COG 2 did the actual time critical measurement using WAITPNE. When it finished it wrote the result and also a status flag into a variable in HUB RAM
When COG 1 had initiated the measurement and waited for COG 2 to finish it also had a timeout running. If this expired it just did a COGSTOP for COG 2. This also meant the measurement had failed.
jmg (or others), do you think that rbehms' approach would allow me to increase resolution from the .277mm?
jmg, I'm not sure I'm completely following there, would you clarify what LSB is and also why you have the ' after the mm numbers? sounds like you know these sensors better than I do.
thanks again
LSB is "Least Significant Bit", and the resolution of the LSB ( .277mm vs .03472mm) determines the resolution of your measurement.
I think the ~.25mm resolution is okay for now.. good to know there's a method for improving that.
Now it uses a hardware counter to measure the exact pulsewidth. So you get the full resolution, but with the timeout. (it's for sure untested)
Andy
ADC or Counter, the significance of a bit comes into play. For a 12 bit ADC or a 12 bit counter the LSB represents 1/4096 of the maximum possible value, and the MSB 2048/4096 or 1/2 of the maximum possible value.