Help or advice please with my Focus Pot to Focus Servo programme (again)

elanman99

Senior Member
With a lot of help from this forum I have got my programme 'nearly' working. I struggle with various aspects of programming, mainly memory handling, word (and byte!) variables and the little bit of maths incorporated produces strange results that I cannot decipher.

My code probably does not follow good programming guidelines, it mostly works however, but the last few hours have resulted in backwards progress and I now realise that there are one or two problems that I do not have the skill to resolve.

The basic idea is that a pot will control the position of a servo, with the added complication that only partial travel of the servo might be used but the full travel of the pot will always will be. (i.e. scaled to suit the output range)

I would be really grateful if someone could look through my code and help me make sense of it, please...

Ian


Code:
; Focus Servo for underwater camera using 08M Picaxe
; Ian Phillips Oct 2010
;
; 1000 to 2000uS PWM. Servo needs 100 to 200 for 9 turns of feedback pot
; Setfreq m8 (or M32?) Might try increasing pulsout resolution later (with 08M2?)
;
; The full range of the input pot (0-255 ADC) is always to be used to cover the
; servo travel between the hi and lo endstop positions which have previously been set
; by the user (to suit different lenses).
;
; The two endstop positions and the scaling value have to be saved to NVR and only
; get changed when the 'Record' and 'Update' functions are used. A 'Centralise'
; routine puts the servo to mid travel and also puts in default scaling and endstop
; values so the user can use the pot to move to the endstop places that need recording.
;




Output 0	; Servo signal	 		(Pin 7)
Input  1	; Focus Pot wiper  		(Pin 6) 5k Pot between 0V and +5V
Input  2	; Update/Save momentary sw	(Pin 5) Pull up resistor, switch takes input low
Input  3	; Save End Posn momentary sw	(Pin 4) Pull up resistor, switch takes input low
Input  4	; Centralise momentary sw	(Pin 3) Pull up resistor, switch takes input low


Symbol ADCVAL	= w0	; ADC reading (0 to 255)
Symbol Posn		= w2	; Servo Position. This value is for pulsout
Symbol Divi		= w4	; Divisor value for formula. To be stored in NVR
Symbol Offset	= w6	; Offset value for formula. To be stored in NVR
Symbol Span       = b7	; Difference between Hi and Lo servo positions
Symbol HiVal	= b10	; POSN value at Hi end of servo travel
Symbol LoVal	= b12	; POSN value at Lo end of servo travel
		
Main:
	pause 20					; Adjust this value to get pulse repetion rate to 20mS
	if Pin3 = 0 then Goto Record		; Write current position/s value to memory
	if Pin2 = 0 then Goto Update		; Write default equation values to memory
	if Pin4 = 0 then Goto Centralise	; Puts servo to mid travel and installs default scaling and stops
	if Pin3 =1 and Pin2 = 1 and Pin4 =1 then goto Normalop ; Normal operation
	Goto Main


Normalop:					
	Read 3,Divi					; Divisor so get correct output scaling
	Read 4,Offset				; Basically same value as one end stop
	Readadc 1,ADCVAL				; Gives 0-255
	Posn = ADCVAL*100/Divi+Offset		; Convert ADC number to value for servo
	Pulsout Output0,Posn			; Send signal to servo
	Goto Main


Centralise:						; Also 'resets' system to allow max travel
	Pulsout Output0,150			; Put servo to mid position for user setup (150 gives 1500uS)
	Write 3,253					; Default for Divisor (See note below)
	Write 4,100					; Default for offset
	goto main


Record:
	Readadc 1, ADCVAL				; I put this line in to 'refresh' value as seen in simulator 
	if ADCVAL <=150 Then WriteLowVal	; Store end (stop) positions for both ends of servo travel
	if ADCVAL  >150 then WriteHighVal	; Numbers are servo values created with default equation data
	goto main


WriteLowVal:					
	Posn = ADCVAL *100/Divi + offset	; Another refresh just to be safe
	Write 6, posn				; Write current servo value to memory
	loval = posn				; Loval is now same as posn
	goto main
	
WriteHighVal:
	Posn = ADCVAL *100/Divi + Offset	; Another refresh to be safe
	write 7, posn				; Write current servo value to memory
	hival = posn				; Hival is now the same as Posn 
	goto main
	
Update:						
	Span = Hival-Loval			; New span range
	Write 5, Span				; Put the span value in location 5
	Divi = 25500 / Span			; Calculates new divisor based on the new end values 
	Write 3,Divi				; Put result in location 3
	write 4,loval				; Save to NVR
	goto main


;NOTES
; Scaling of the ADC to suit the Servo is by dividing the ADC reading by the difference between the
; two ends of the servo travel (defined here as the Span). The maximum Span is about 100 (200 minus 100)
; for a standard servo. Maximum ADC is 255, so 255/100 gives a divisor of 2.55 so ADC has to be x100 for
; Picaxe maths purposes.
; If only part of the servo travel is being used the span number will be smaller which results in a much
; bigger divisor value so Word variables will be involved.
 

johnkgrannan

New Member
offset &amp; initialisation

Hi,

From a quick scan of your code, two things strike me:
A) even though you're reading things from memory at what point does those items in memory get first initialised? You may need to force run Main when the program is first initialised and nothing has ever been put into the memory locations;
B) your offset has also to be multiplied by 100 before being added as you're getting round the lack of FP maths by multiplying the ADC value by 100.

Hope this helps?
 

westaust55

Moderator
It might help if you gave clear and specific indication as to what you feel/know is wrong and/or what you want to happen.
 

elanman99

Senior Member
Hi,

From a quick scan of your code, two things strike me:
A) even though you're reading things from memory at what point does those items in memory get first initialised? You may need to force run Main when the program is first initialised and nothing has ever been put into the memory locations;
B) your offset has also to be multiplied by 100 before being added as you're getting round the lack of FP maths by multiplying the ADC value by 100.

Hope this helps?
John

Initialisation has to be done only with a new chip by running the Centralise routine. It puts default values into memory. The pot then controls the full range of servo movement which allows the user to move it to the new end positions. Once in a position 'Record' put the chosen high and low positions into memory, they will be the values used until centralise is used again.

I have thought about what you said about multiplying the offset but I don't think its needed. The offset values are the numbers that are used to drive the servo (using pulsout) and are only ever in the range of 100 to 200. The Picaxe deals with mathematical functions in a left to right sequence only so onex the multiplication and division have been done (as on my line 27) the offset is just added as it is.

Ian
 

elanman99

Senior Member
It might help if you gave clear and specific indication as to what you feel/know is wrong and/or what you want to happen.
There are three areas I am unsure about

I need to store three values in NVR so they are present when the kit is powered up. I can see values in the 'Memory' pane of the simulator that do not get cleared when the simulator is reset. I am unclear whether I should use 'Write 4, Value' or Write b4, Value) and I'm not sure whether these two things are the same really.

For the scaling I need to create a divisor by dividing the ADC count by the span range of the servo. If only a small output range of the servo is used the divisor will end up as a largish number (biggest span is 100, smallest might be 30, (dividing 255 by 30 needs an equation of '255x100/850') and I do not know how to handle that. I will have to put in Max and min so it does not give invalid numbers and end up dividing by zero.

Ultimately I would like to get the best resolution possible using READADC10 and using the highest clock frequency to get the smallest pulsout increments. I presume it will rely heavily on word variables but I don't know how this conflicts with memory locations I might have saved values to.

Lastly. When I single step through the simulation to see the effect of each line it would help if I could see each result before the next line acts upon it. Can I put in, say, extra Writes to temporary memory places so that I can see in them in the memory pane

Hope I have explained it better than before.

Regards

Ian
 

hippy

Ex-Staff (retired)
The easiest way to start is by not using non-volatile storage. Get the code working / simulating with variables only; don't worry that you have to reset your minimum and maximum values each time.

Something like below but this is untested and the 'servoval' calculation can be improved -

Code:
symbol pot = b0
Symbol minpot = b1
symbol maxpot = b2
Symbol servoval = b3

minpot = 0
maxpot = 255

Do
  ReadAdc 1, pot
  If setmin_pin = 0 Then : minpot = pot : End If
  If setmax_pin = 0 Then : maxpot = pot : End If
  servoval =   maxpot - minpot * pot / 255 + minpot
  SerTxd( "Pot=", #pot, 9, "Servoval=",#servoval, CR, LF ) 
  Pause 1000
Loop
 

elanman99

Senior Member
The easiest way to start is by not using non-volatile storage. Get the code working / simulating with variables only; don't worry that you have to reset your minimum and maximum values each time.

Something like below but this is untested and the 'servoval' calculation can be improved -

Code:
symbol pot = b0
Symbol minpot = b1
symbol maxpot = b2
Symbol servoval = b3

minpot = 0
maxpot = 255

Do
  ReadAdc 1, pot
  If setmin_pin = 0 Then : minpot = pot : End If
  If setmax_pin = 0 Then : maxpot = pot : End If
  servoval =   maxpot - minpot * pot / 255 + minpot
  SerTxd( "Pot=", #pot, 9, "Servoval=",#servoval, CR, LF ) 
  Pause 1000
Loop
Hippy

I put your example into the simulator but got some syntax errors, 'pot' seems to be a reserved word, changed to 'pots' and then got missing symbols. I know its untested but as I did not fully understand what it was trying to do did not persevere with editing it. Sorry if this is a daft question, I can see it should display ADC and Servo values but does it scale the full pot travel to full servo travel?

My programme was actually working in most respects. If I chose new end positions that were very close to full travel it worked perfectly. It wrote and read the saved positions OK too. I think that once the divisor is over a certain value something wraps round with the result that at two or more places, part way through the pot travel the servo changes direction.

Since then though I have changed few of the variable to words in the vain hope that it would do the maths better! I originally had all the variables visible as they were b0, b2, b3, etc.

Thank you for your help.

Ian
 

Rick100

Senior Member
Ian

In your program a byte variable and a word variable are overlapping . Word variable W6 is made up of byte variables b13 and b12 so they will interfere with one another . See manual 1 section: General Purpose Variables page 52 for an explanation of how variables are laid out .

When you write or read a word value you have to use the keyword 'WORD' before the variable . Each word value takes 2 bytes so you have to make your address allow for this . If you write a word value to address 0 , and another word value to address 1 , they will overlap . See manual 2: write and read commands .

The overlapping variables and overlapping writes and reads will cause havoc in your program . I changed your program to lay out the variables so they don't overlap and added constants for the 'eeprom' addresses . I put 'word' in each read and write command . I changed Hival , Loval , and Span to word variables . It won't hurt anything and if you go for higher resolution on your pulsout command , by running at a higher clock speed , they will have to be words . You can try it and see if it behaves better .

You said "I originally had all the variables visible as they were b0, b2, b3, etc." . You can see the word variables with their values if you select the 'word' checkbox in the simulation pane .


Code:
; Focus Servo for underwater camera using 08M Picaxe
; Ian Phillips Oct 2010
;
; 1000 to 2000uS PWM. Servo needs 100 to 200 for 9 turns of feedback pot
; Setfreq m8 (or M32?) Might try increasing pulsout resolution later (with 08M2?)
;
; The full range of the input pot (0-255 ADC) is always to be used to cover the
; servo travel between the hi and lo endstop positions which have previously been set
; by the user (to suit different lenses).
;
; The two endstop positions and the scaling value have to be saved to NVR and only
; get changed when the 'Record' and 'Update' functions are used. A 'Centralise'
; routine puts the servo to mid travel and also puts in default scaling and endstop
; values so the user can use the pot to move to the endstop places that need recording.
;


'******************* added constants for storage address *********************
'Constants
Symbol DiviAdd = 0	'locations for saved values 2 bytes for each word
Symbol OffsetAdd =2
Symbol HiValAdd = 4
Symbol LoValAdd = 6
Symbol SpanAdd = 8


Output 0	; Servo signal	 		(Pin 7)
Input  1	; Focus Pot wiper  		(Pin 6) 5k Pot between 0V and +5V
Input  2	; Update/Save momentary sw	(Pin 5) Pull up resistor, switch takes input low
Input  3	; Save End Posn momentary sw	(Pin 4) Pull up resistor, switch takes input low
Input  4	; Centralise momentary sw	(Pin 3) Pull up resistor, switch takes input low


'*************** all variables words ********************************
'variables
Symbol ADCVAL	= w1	; ADC reading (0 to 255)
Symbol Posn	= w2	; Servo Position. This value is for pulsout
Symbol Divi		= w3	; Divisor value for formula. To be stored in NVR
Symbol Offset	= w4	; Offset value for formula. To be stored in NVR
Symbol HiVal	= w5	; POSN value at Hi end of servo travel
Symbol LoVal	= w6	; POSN value at Lo end of servo travel
Symbol Span       = w7; Difference between Hi and Lo servo position

		
Main:
	pause 20					; Adjust this value to get pulse repetion rate to 20mS
	if Pin3 = 0 then Goto Record		; Write current position/s value to memory
	if Pin2 = 0 then Goto Update		; Write default equation values to memory
	if Pin4 = 0 then Goto Centralise	; Puts servo to mid travel and installs default scaling and stops
	if Pin3 =1 and Pin2 = 1 and Pin4 =1 then goto Normalop ; Normal operation
	Goto Main


Normalop:					
	Read DiviAdd,word Divi					; Divisor so get correct output scaling
	Read OffsetAdd,word Offset				; Basically same value as one end stop
	Readadc 1,ADCVAL				; Gives 0-255
	Posn = ADCVAL*100/Divi+Offset		; Convert ADC number to value for servo
	Pulsout Output0,Posn			; Send signal to servo
	Goto Main


Centralise:						; Also 'resets' system to allow max travel
	Divi = 253
	offset = 100
	Pulsout Output0,150			; Put servo to mid position for user setup (150 gives 1500uS)
	Write DiviAdd,word Divi				; Default for Divisor (See note below)
	Write OffsetAdd,word offset			; Default for offset
	goto main


Record:
	Readadc 1, ADCVAL				; I put this line in to 'refresh' value as seen in simulator 
	if ADCVAL <=150 Then WriteLowVal	; Store end (stop) positions for both ends of servo travel
	if ADCVAL  >150 then WriteHighVal	; Numbers are servo values created with default equation data
	goto main


WriteLowVal:					
	Posn = ADCVAL *100/Divi + offset	; Another refresh just to be safe
	Write LoValAdd, word posn				; Write current servo value to memory
	loval = posn				; Loval is now same as posn
	goto main
	
WriteHighVal:
	Posn = ADCVAL *100/Divi + Offset	; Another refresh to be safe
	write HiValAdd, word posn				; Write current servo value to memory
	hival = posn				; Hival is now the same as Posn 
	goto main
	
Update:						
	Span = Hival-Loval			; New span range
	Write SpanAdd, word Span		; Put the span value in location 
	Divi = 25500 / Span			; Calculates new divisor based on the new end values 
	Write DiviAdd,word Divi				; Put result in location 3
	write LoValAdd,loval				; Save to NVR
	goto main


;NOTES
; Scaling of the ADC to suit the Servo is by dividing the ADC reading by the difference between the
; two ends of the servo travel (defined here as the Span). The maximum Span is about 100 (200 minus 100)
; for a standard servo. Maximum ADC is 255, so 255/100 gives a divisor of 2.55 so ADC has to be x100 for
; Picaxe maths purposes.
; If only part of the servo travel is being used the span number will be smaller which results in a much
; bigger divisor value so Word variables will be involved.

Good Luck,
Rick
 

elanman99

Senior Member
Ian

In your program a byte variable and a word variable are overlapping . Word variable W6 is made up of byte variables b13 and b12 so they will interfere with one another . See manual 1 section: General Purpose Variables page 52 for an explanation of how variables are laid out .

When you write or read a word value you have to use the keyword 'WORD' before the variable . Each word value takes 2 bytes so you have to make your address allow for this . If you write a word value to address 0 , and another word value to address 1 , they will overlap . See manual 2: write and read commands .

The overlapping variables and overlapping writes and reads will cause havoc in your program . I changed your program to lay out the variables so they don't overlap and added constants for the 'eeprom' addresses . I put 'word' in each read and write command . I changed Hival , Loval , and Span to word variables . It won't hurt anything and if you go for higher resolution on your pulsout command , by running at a higher clock speed , they will have to be words . You can try it and see if it behaves better .

You said "I originally had all the variables visible as they were b0, b2, b3, etc." . You can see the word variables with their values if you select the 'word' checkbox in the simulation pane .

Good Luck,
Rick

Rick

Your'e brilliant!

It works, finally after all the guidance from the forum members in conjunction with my trial and error techniques it works exactly as I wanted it too.

Initially I got a syntax error with your code as it did not like w7 (because its on an 08m) and I had to put in another line in the Update (write loval to the eeprom Offset address) but other than that your code was perfect.

I have to go out today but will post a fuller reply later

Many thanks

Ian
 

westaust55

Moderator
Rick

Your'e brilliant! It works, finally
@elanman/Ian

Great to read you have your project working.

If I can give you another tip (just a tip - no grumble intended) on posting in reply to others.
When replying using the "Reply With Quote" button at the bottom right of the screen/post try to extract out just the sentence(s) that you are replying to rather than leave an entire copy of the post.
Alternatively you can just put a "commercial at" symbol ( @ ) in front of the forum members name to address them.
That way, those who read the thread and in particular your posts do not have to read/see everything twice.
 

Rick100

Senior Member
Ian

Thanks . Good to hear your making progress . I thought you were using an 08m2 and had plenty of variable space .

Good luck
Rick
 

elanman99

Senior Member
@elanman/Ian

If I can give you another tip (just a tip - no grumble intended) on posting in reply to others.
When replying using the "Reply With Quote" button at the bottom right of the screen/post try to extract out just the sentence(s) that you are replying to rather than leave an entire copy of the post.
Totally concur. I find numerous repeats and re-quotes of parts of the thread that are no longer relevant, spoil the flow of the discussion. My only excuse on this occasion is that I posted my reply when I was in a rush, I did take out a massive chunk of the quoted text by deleting the shaded code area though.

Ian
 
Top