Xan's Phoenix Code

Cool!

Does this count for the GP firmware too?

Ho!

Too many quote boxes. :open_mouth:

I have received the new GP firmware for the corrected Once command. It now starts with the first step and ends with the last step. There was nothing that could be done about the Q command. Sorry… :frowning:

That’s a pitty… :cry:
Well that means that I need to figure out a way to measure the program cycle-time and add a manual delay.

But I’m glad that the GP player is more advanced :slight_smile:

Xan

Hi all,

I moved the offsets to the SSC and got it to work yesterday. I can recommend everybody with a V2 to do the same! Just write the offsets to registers 32 to 63 (output 0 to 31) and change register 0 as well. To get it to work you have to change 2 bits. Bit 15 has to be 0 to enable register 0 and bit 2 has to be 1 to enable the offsets. I wrote 32756 in register 0 (0111 1111 1111 0100).

I want to include the variable speed (to sneak around) in the next version of the code. But to get it perfect I need to figure out the cycle time of the main loop. Zenta, Is it easy for you to measure this if I send you a copy of my code? Thanks!

Xan

Hi Xan,

I’ve not tested the offset yet, but do you know if they work together with the GP sequences? Because the GP sequences are already calculated with the offset values.

About the timing; you could mail the code and let me have a look. I don’t know if I get time for it today, but I’ll try.

I think it’s a very good idea to set the offsets in the SSC-32 before setting up in SEQ. This will result in sequences that will be as close as possible to working the same on any similar sized bot.

Hi Zenta,

I didn’t tested the offsets in combination with the GP player. Even worse, I didn’t tried the GP player at all… :blush: I’ll see if I can get a sequence in there this evening and I’ll let you know what happened. I don’t have a ready to generate PEP but I think I can make a sequence that drives all servos to 1500 to check if it uses the offsets :wink: But I think that it will use the offsets. Is it a lot of work to regenerate your sequences without the offsets?

Thanks! But I don’t think that it will be today. I need to do some changes so maybe by the end of the day. I’ll send you a mail with the code and give it a try when you’ve got some free time. Thanks for the help!

Xan

Hi Jim,

Timing :wink:

Does this mean that the offset registers are also used with the GP player?

Thanks Xan

Oh yes, both firmwares support the offset registers.

Some clarification…

SEQ uses angles for storing sequences, not pulse values. It was written with the assumption that there will be an offset from the centered (1500uS) position. It also allows you to correct symmetry problems by assigning an angle to a pulse value for the extreme CW and CCW positions. So if you do the offset in the SSC-32’s firmware then all you need to do is set up the min and max limits. Hope this helps.

Hi Xan,

I am also willing to help as well. Not sure of my schedule for the next couple of days, but…

If it were me, I would probably start off with building in my own timings. That is I would probably use one of the built-in timers on the PRO. Since you are not using HSERVO, I would probably go to use WTIMER. I would probably make it a general purpose Timer counter. If the overhead of having an interrupt is too high and if the timings are such that it could fit in 17 bits (16 for the timer + 1 for the overflow), you could set it up just for this purpose and reset the counter to zero before each timing test.

I hacked up some code that shows hopefully an approach to do this below. It compiles, but I did not try it out so there may be a few bugs. Also this is the first time I tried a subroutine that returns something but had no variables passed in. This could easily be changed to simply be inline code or the like.

Good Luck,
Kurt

P.S. - I cann’t wait to see your new version

'================================================================================================== 
;Variable declaration 

lTimerWOverflowCnt 	var long     ; used in WTimer overflow...  Will keep a 16 bit overflow so we have a 32 bit timer 
lCurrentTime		var	long
lTimerStart 		var long
lTimerEnd 			var long
;=============================- Interrupt definitions ======================================= 
;Interrupt init 

ONASMINTERRUPT TIMERWINT, Handle_TIMERW 

;============================== Start of CODE ================================================ 

; Initiialize Timer 
WTIMERTICSPERMS con 2   ; we have 16 clocks per ms and we are incrementing every 8 so divide again by 2 
TCRW = 0x30 ;clears TCNT and sets the timer to inc clock cycle / 8 
TMRW = 0x80 ;starts the timer counting 

lTimerWOverflowCnt = 0; 

enable TIMERWINT_OVF 


;================================ Main Loop ================================================= 
main_loop 
	gosub GetCurrentTime], lTimerStart
	' Do all your main loop stuff here...
   pause 500 
   gosub GetCurrentTime], lTimerEnd 
   
   ' you can calculate your difference in time here...
   
goto main_loop 


;----------------------------------------------------------------------------------- 
; Handle TimerW interrupt 
   BEGINASMSUB 
HANDLE_TIMERW 
   push.w   r1         ; save away register we will use 
    
   bclr #7,@TSRW:8               ; clear the overflow bit in the Timer status word 
   mov.w @LTIMERWOVERFLOWCNT+1:16,r1   ; We will increment the word that is the highword for a clock timer 
   inc.w #1,r1 
   mov.w r1, @LTIMERWOVERFLOWCNT+1:16 
   pop.w   r1         ; restore our registers 
   rte               ; and return    
    

;-------------------------------------------------------------------------------------
; Simple function to get the current time and verify that no overflow happened
GetCurrentTime
	lCurrentTime = lTimerWoverflowCnt + TCNT			; calculate the timer
	if lCurrentTime.highword <> lTimerWOverflowcnt.highword then
		lCurrentTime = lTimerWoverflowCnt + TCNT			; calculate the timer
	endif
	
	return lCurrentTIme
	

Hi Kurt,

Using a internal timer. I didn’t know that the atom pro had that option :blush: Great advice! When I come home I will start testing right way! If it works it will solve some major issues! Thanks!

I’ll keep you updated!

Xan

  1. Yeah! this is just brilliant! 8)

Kurt, your program is doing exactly what I needed! It is returning the cycles time in ms. I don’t know exactly what all the variables are used for and I can’t find it in the manual. If you could tell me where you’ve got this useful information it would be great! Here is the tested version. I don’t know if I used the WTIMERTICSPERMS value right so some input would be great!

[code] '==================================================================================================
;Variable declaration

lTimerWOverflowCnt var long ; used in WTimer overflow… Will keep a 16 bit overflow so we have a 32 bit timer
lCurrentTime var long
lTimerStart var long
lTimerEnd var long
Time var byte
;=============================- Interrupt definitions =======================================
;Interrupt init

ONASMINTERRUPT TIMERWINT, Handle_TIMERW

;============================== Start of CODE ================================================

; Initiialize Timer
WTIMERTICSPERMS con 2 ; we have 16 clocks per ms and we are incrementing every 8 so divide again by 2
TCRW = 0x30 ;clears TCNT and sets the timer to inc clock cycle / 8
TMRW = 0x80 ;starts the timer counting

lTimerWOverflowCnt = 0;

enable TIMERWINT_OVF

;================================ Main Loop =================================================
main_loop
gosub GetCurrentTime], lTimerStart
’ Do all your main loop stuff here…
pause 200 'mS
gosub GetCurrentTime], lTimerEnd

’ you can calculate your difference in time here…
Time = (lTimerEnd-lTimerStart)/WTIMERTICSPERMS/1000
serout S_OUT, I38400, [dec lTimerStart, " ", dec lTimerEnd, " ", dec Time, 13]

goto main_loop

;-----------------------------------------------------------------------------------
; Handle TimerW interrupt
BEGINASMSUB
HANDLE_TIMERW
push.w r1 ; save away register we will use

bclr #7,@TSRW:8 ; clear the overflow bit in the Timer status word
mov.w @LTIMERWOVERFLOWCNT+1:16,r1 ; We will increment the word that is the highword for a clock timer
inc.w #1,r1
mov.w r1, @LTIMERWOVERFLOWCNT+1:16
pop.w r1 ; restore our registers
rte ; and return

return
;-------------------------------------------------------------------------------------
; Simple function to get the current time and verify that no overflow happened
GetCurrentTime
lCurrentTime = lTimerWoverflowCnt + TCNT ; calculate the timer
if lCurrentTime.highword <> lTimerWOverflowcnt.highword then
lCurrentTime = lTimerWoverflowCnt + TCNT ; calculate the timer
endif

return lCurrentTIme[/code]

I’m gone wrap it in the main program now. I hope it doesn’t interfere with reading the PS2 inputs and writing the SSC output.

Xan

Lets hope it work and don’t interfere too much. This sound like a much more accurate solution.

Nice work Kurt!

Hi Xan, Most of this code was lifted from a previous post I did about using the underlying I2C capabilites of the Atom Pro. If you run the Atom Pro IDE and go to the help menu, you will find an entery for the H8/3694 hardware manual. This is the underlying chip used on the more recent AP28 chips. Some earlier ones were using the 3664 chip.

Chapter 12 of this manual talks about the Wtimer and in particular section 1.3 describes many of these registers(variables), such as TMRW, TCRW,…

The one constant that I left in the code that I had not used was: WTIMERTICSPERMS .

This was used in the earlier program to convert to us. The chip runs at 16mhz and TCRW is defined as the value 0x30, which amoung other things says TCNT will be incremented by the clockspeed/8 or at 2mhz. So that constant was what I needed to divide by to get to 1mhz. I hope that this makes sense.

The AtomPro manual talks a little bit here and there about using some of the underlying capabilities of the processor, but in fact it allows you to get at most if not all of the underlying capabilities of the processor.

If it does interfer we can probably update the code to not have a general purpose timer with an overflow interrupt, but instead reset the timer each time we want to use it and use the 16 bit value(TCNT) and possibly extend to 17th bit by checking the OVF flag of TSRW. My guess is that it should not harm the other code too much as the only overhead is when the interrupt handler is called every 65536*8 clock cycles and the interrupt handler is pretty small.

If you have any problems with this code I will set-up a simple test to make sure I did not do any simple logic errors… In particular I need to verify my assembly interrupt handler is properly incrementing the high word of my long. Another way to write this code might be like:

   BEGINASMSUB 
HANDLE_TIMERW 
   push.l  er1         ; save away register we will use 
    
   bclr #7,@TSRW:8               ; clear the overflow bit in the Timer status word 
   mov.l @LTIMERWOVERFLOWCNT:16,er1   ; We will increment the word that is the highword for a clock timer 
   inc.w #1,e1 
   mov.l er1, @LTIMERWOVERFLOWCNT:16 
   pop.l  er1         ; restore our registers 
   rte               ; and return    
    
return 
ENDASMSUB

But note, I did this here in the editor and did not verify this version would even compile.

Of course you could also do the interrupt handler in basic as well, by changing the ONASMINTTERUPT to ONINTERRUPT

HANDLE_TIMERW 

    lTimerOverflowCnt = lTimerOverflowCnt + 0x10000   ; add 1 to hiword
    resume

Hi,

Overall I made some good progress yesterday with help of Kurt’s timer. The timer is included in the code and solves the timing problem. I can get the cycles time of the calculations and subtract this from the servo time. The program will use the pause command for the time that it have to wait. This way the servos fully reaches the end of there position before they are overwritten with new positions.

This resolved in VERY good looking gaits! Even the 6 steps ripple gait is very solid looking. The body doesn’t even wobble a bit! The next thing I include was variable timing of the gaits. When moving the analog joystick just a little bit it lets phoenix sneak. The position of the stick doesn’t only adjust the step length anymore but both step length and step speed! This is very cool and natural looking!!

I’ll post a new video this Saturday including the new code! 8)

Hi Kurt,

Ok, this makes sense why I didn’t know about the timer functions. But I had to know because all the uC that I used at school did had timers. But proves again that it is a good thing that we share our knowledge :wink:

Did I use the constant the way that it should or is it reserved (and used) in the lower levels of the code?

It makes perfect sense to me :smiley:

I want to get this right so correct me if I’m wrong. The first code that you’ve posted resolved it an us timer. I divided this by 1000 to get a ms timer. This timer will use the interrupt every…1us?

The second version that you’ve posted will just use a overflow interrupt so that’s 65526*8 us?
I did include the timer in the main program and it is working without any problems. Should I leave it or replace it with the second version?

Thanks Xan

Sounds great that even the 6 step ripple worked, I didn’t test that. Looking forward to see (the video) your version of the sneak mode. :wink: Another factor I also took in consideration when calculating the gait speed is the LegLiftHeight. In one walking mode I made the LegLiftHeight variable, the gaitspeed should be a bit lower when using large leg lift values.

Hi Zenta,

Did you made the LegLiftHeight adjustable by walking faster/slower? Or was it a separate setting? The only reason of changing the height would be because you’re walking on a different surface. If the surface is smooth you don’t need much height. Walking through grass would need more leg lift. Or does it look better when you change it?

Something else, I’ve noticed that you move the legs like a square while you use the 12 steps ripple, quad, and 2nd tripod. I lowered the outer positions to get a more fluent movement of the legs. The servos will reach the exact position because of the included timer. Before the use of the timer, one movement melted over in the other which made it look more fluent (but not correct controllable/stable). You will see what I mean when you try it :wink:

Xan

Hi Xan,

It was a separate setting, useful when doing outdoor walking or to demonstrate the balance mode while doing slow walking and high lifting legs (very cool 8) ).

Ah! I think I know what you mean, looking forward to see the video!

BTW:

I’m thinking of making a more advanced walking gait, a copy of the real life ant gait. The ant gait are similar to the tripod gait but all legs (front, middle and rear) have different travelengths at the swing and stance position. i’ve not figured it all out yet… I’m also working on the Felix again, doing some servo replacing (the Towerpro’s are really bad :imp: …). The greatest challenge is to find time for everything :unamused:

Hi Zenta,

Hehe I bet it does! Are you planning to share the balance calculations? I would love to try it out!

sry for my bad English… :blush:

It sounds like a cool looking gait. But if all legs have a different travel length, how do you make them travel the same distance? Do the short middle legs make more steps? Did you find some information or a video of the gait in motion?

That’s a good idea as well. Felix defiantly deserves some time! It would be great if you’ll get it to walk!

About the Towerpro’s, In Holland we use to say “Goedkoop is duurkoopâ€

Hi Xan,

It sounds great that it is working for you and the gaites are working better.

The scale of the timer by itself is 1 tick per every 8 CPU clock ticks. Note, the hardware simply increments TCNT at this point, the exception to this is when TCNT overflows (from 65535 -> 0). In this case the overflow bit will be set in TSRW. Since we have enabled an interrupt handler for this condition, that is when our interrupt handler will be called. That is every 65536*8 cpu cycles.

How your code wants to scale this is up to what works for you. If you divide by 2 than a servo at mid point would be a value 1500…

All three timer interrupt functions should in theory do the same thing. If the first one works right it should be slightly faster than the second one. In the first one I simply try to load the highword of a long into a word register and increment it. In the second version I loaded the whole long into a 32 bit register set and then I again just incremented the high word and saved away the whole 32 bit register set. This is slightly slower as I had to save and restore a long register instead of a word register and loaded and saved a long instead of a word. Each of these instructions went from 6 cyles to 8 (Appendix A in the 3694 document). The third one was simply doing the same in basic, which is easier to understand, but with lots more overhead. It saves away all of the registers, not just the one I used, plus runs the steps through the basic interpreter… So if the first one is working I would stick with it!

Yep I do appreciate all of the information that is shared up here in the forums and the ability for many of us to make use of the code of others! I try to help in any small way I can.

I also can not wait too see your video and the new version of the code!

Kurt