multiple loops

Discussion about the ZBasic language including the System Library. If you're not sure where to post your message, do it here. However, do not make test posts here; that's the purpose of the Sandbox.
Post Reply
pcleats
Posts: 35
Joined: 12 December 2005, 11:57 AM

multiple loops

Post by pcleats »

Hello all,

I am really struggling trying to convert a program I had written for a different chip to the ZX-24.

I really have no idea how to best get these loops to work correctly with the ZX-24
I have tried a couple of different ways, and I either end up in an endless look or it just doesn't work. So I guess what I am asking is what is the best way to implement these loops so they will function with the ZX-24

Code: Select all

start:
;-------------------------------------------------------------------------------------------------------------------------------
;--------------------------------------------Check solenoid time out-------------------------------------------------
;-------------------------------------------------------------------------------------------------------------------------------
if Sol_Count = $FFFF then
	goto Main			;Wait for solenoid timer to run out before activating system
	else
	pause 50 
		if sol_count = 250 then
			high buzzer
		endif
	goto Checks
endif

;-------------------------------------------------------------------------------------------------------------------------------
;----------------------------------Start of main program------------------------------------------------------------------------
;-------------------------------------------------------------------------------------------------------------------------------
Main:

pause 40				;~40 ms time delay
;-------------------------------------------------------------------------------------------------------------------------------
;---------------------------------This section checks sensor activity--------------------------------------------------
;------------------------------------------------------------------------------------------------------------------------------- 

If (Door_Switch = 0) and (Motion_Trig = 0) Then goto system_hit	;system was hit turn on hit light

If (Door_Switch = 0) Then Goto Door_opened	;Turn on IR leds and recorder

If (motion_Trig = 0) Then goto motion_sensed	;Turn on everything except hit light

;-------------------------------------------------------------------------------------------------------------------------------
;---------------------------these routines decrements counters at ~25 ms loop cycle time----------------------------------------
;-------------------------------------------------------------------------------------------------------------------------------

Checks:
;Wait_time: ffff indicates disabled, 0000 indicates timer completed, other value is current count
if Wait_time=$ffff then Check_sol	;check if disabled
	
	if Wait_time = 0 then		;check if timed out
	
		let Wait_time = $ffff	;disable Wait_time
		
		Low IR_LEDS
		High DVR_Control
		Low Cameras
	endif

Let Wait_time = Wait_time - 1		;decrement real time count


;-------------------------------------------------------------------------------------------------------------------------------
;check to see if 20 seconds has elapsed
;-------------------------------------------------------------------------------------------------------------------------------
check_sol:
if sol_count = $ffff then check_IR_Leds	;check if disabled
		
	if sol_count = 0 then		;check if timed out
		
		let sol_count = $ffff	;disable sol_count
		
		goto system_Startup  ;This gets the system up and running
	
	endif

Let sol_count = sol_count -1		;decrement solenoid time count
	
;-------------------------------------------------------------------------------------------------------------------------------
;check to see if 1 minute has elapsed IR leds are turned on and DVR is recording
;-------------------------------------------------------------------------------------------------------------------------------
check_IR_Leds:
if IR_Leds_Delay = $ffff then check_hit_light			;check if disabled
	
if IR_Leds_Delay = 0 then	;check if timed out
		
	let IR_Leds_Delay = $ffff	;disable Wait_time
		
	low IR_LEDS
	High DVR_Control
	low Cameras
	
endif

Let IR_Leds_Delay = IR_Leds_Delay -1				;decrement real time count


;-------------------------------------------------------------------------------------------------------------------------------
;Delay before the hit light comes on 25 minutes
;-------------------------------------------------------------------------------------------------------------------------------
check_hit_light:
if Hit_light_delay = $ffff then next_check:
	
if (Hit_light_delay = 0) and (Hit_flag = 1) then
		
		High Hit_light
		let Hit_light_delay = $ffff
endif

Let Hit_light_delay = Hit_light_delay -1

next_check:

goto start

The code work well on a Basic Atom Pro, I am just interested in converting it to the ZX-24

Any help on the best way to do this would be appericated.

Thanks
Patrick
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Re: multiple loops

Post by dkinzer »

pcleats wrote:[W]hat is the best way to implement these loops so they will function with the ZX-24?
There are two approaches that I would consider. The first is to attempt a direct translation. The advantage of this approach is that you end up with code that works (nearly) the same way as the existing working code. The disadvantage of this approach is that you end up with the same unstructured code that was (probably) necessary given the predecessor's language limitations.

The second approach is to re-code the logic using the more structured elements of ZBasic. The advantage of using this approach is that you end up with a more modern implementation that is easier to understand and maintain. The disadvantage is that it may take longer to get the system working.

I would suggest the latter approach if you expect this project to have a moderate lifetime. The time invested now in structuring the code will pay off later in simpler maintenance.

If you decide to pursue the former approach, it might be helpful if you were to post your translation. Perhaps we could spot the reason(s) that it is not operating as expected.

Lastly, since parts of the code are missing (e.g. system_hit, door_opened, motion_sensed, etc.) it will be difficult to fully understand what might be happening.
- Don Kinzer
spamiam
Posts: 739
Joined: 13 November 2005, 6:39 AM

Post by spamiam »

Patrick,

You should use subroutines. Those goto's are considered uncool. I must admit that they get the job done, but they make the code look more complex.

You should start the code with the main routine.

In the main routine are a bunch of logic checks and then calls to subroutines. You have this coded mostly like this already. Just replace all your goto's with calls or returns.

After you main routine's code you can place your subroutines. The other method is have another module with all the subroutines (or multiple modules with only logically related subroutines in each). This way it is eaasier to find the subroutines you are looking for. In a small program, it is easy enough to keep all the code in one module.

The most understandable way to write your main routine is to have it ONLY call subroutines. Little or no logic involving whether or not to make the call. The called subroutine does the logic on how to proceed.

So your main code might look like:

Code: Select all

do
     call system_hit()
     call door_opened()
     call motion_sensed()
     sleep(40)
loop
Then for instance, the routine hit might have the following code:

Code: Select all

If (Door_Switch > 0) or (Motion_Trig > 0) return
.... code here to react to a hit.....
Od course I have not dealt with defining any of the variables nor dealt with their scope.

For the time-based routines, you can use the really neat aspects of this operating system to use tasks with a "wait for interval", but you have to use the same interval for all tasks using the "wait for interval". So some will have to wait for multiple intervals.

But the simplest way to do things is what you have right now. In the main code you can decrement multiple counters and when they hit zero, then a subroutine can get called, then the counter is reset.

If you needed to keep things running synchronized to the clock, then some of the time-based routines could be run when the time of day reaches a certain point.

Overall, this project is very do-able, and when you are done you will have mastered all the basic and some not-so-basic aspects if ZBasic.

Go for it! It will be fun once you get over the first hurdle!

-Tony
pcleats
Posts: 35
Joined: 12 December 2005, 11:57 AM

Re: multiple loops

Post by pcleats »

My problem is with the timing. Knowing that it takes ~50 miliseconds to go thru all the loops I can get pretty close to the times I need. 50ms * 1200 = ~60 seconds.

Because I don't know enough about the ZX-24 I don't know what I will have to do to get the same timing. I know the ZX-24 runs much faster than the basic atom.

Patrick
Last edited by pcleats on 09 October 2007, 6:46 AM, edited 1 time in total.
spamiam
Posts: 739
Joined: 13 November 2005, 6:39 AM

Post by spamiam »

Well, it would not be all that useful for us to actually WRITE your code for you, but we can definitely give some pointers.

First, just test the waters by writing a part of what you need. So, define your variables, and write the initial main program. Just get the starting code written. From main call several subroutines. Use the names you will use for the actual subroutines in the future. However, these subroutines are actually just an empty shell.

This way you can see that the code is comiling and the initial logic works.

From there add new functions one at a time until it is done.

When you have questions, post the ZBasic code and the issue and we can help out very easily.

Without you getting the ball rolling, our help is not going to be very educational for you.

-Tony
spamiam
Posts: 739
Joined: 13 November 2005, 6:39 AM

Post by spamiam »

Here is a start to your code:

Code: Select all

Public Sub Main()

     Dim Var1 as Byte  'insert your actual variables here
     Dim Var2 as Byte
     Dim Var3 as Boolean
     Dim NextTick as Long

     'delay startup for 20 seconds
     sleep(20.0)     'single == seconds
     NextTick = Register.RTCTick    'synch to the clock

   
     Do     'infinite loop

          NextTick = NextTick + 26     '26 ticks == 50.8ms
          'check here for the magnitude of NextTick and wrap across zero if necessary

          'insert the subroutine calls here
          'hopefully they take less than 50.8mS to complete
          'otherwise they need to be done in a round-robin fashion

          'decrement 1 or more counters: this happens every 50.8mS
          'service the counters.   Here too, if they take more than 50.8Ms, then they may need to be serviced in a round-robin manner.

          ' for comparing the time vars, you must consider crossing zero between Tick and NextTick

          while &#40;Register.RTCTick <> NextTick&#41;
               'do nothing, just wait for time to pass
          wend

     Loop

End Sub
One thing to consider is how necessary it is to do some of the tasks PRECISELY on a 50.8mS (or whatever) schedule. Most of the time, you poll all the statuses as rapidly as possible, and only worry if it takes TOO LONG between pollings. Is there any reaason not to check as soon as possible? The code above will run synched to the realtime clock, but most of the time this is NOT necessary.

-Tony
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

Here is an alternate method to implement the loop timing. Using WaitForInterval() eliminated the complexity of detecting RTC rollover.

Code: Select all

Sub Main&#40;&#41;
     Dim Var1 as Byte  'insert your actual variables here 
     Dim Var2 as Byte 
     Dim Var3 as Boolean 

     'delay startup for 20 seconds 
     sleep&#40;20.0&#41;     'single == seconds 

     ' set the length of the interval for controlling loop iteration
     Call SetInterval&#40;26&#41;     '26 ticks == 50.8ms
    
     Do     'infinite loop 

          'insert the subroutine calls here 
          'hopefully they take less than 50.8mS to complete 
          'otherwise they need to be done in a round-robin fashion 

          'decrement 1 or more counters&#58; this happens every 50.8mS 
          'service the counters.   Here too, if they take more than 50.8Ms, then they may need to be serviced in a round-robin manner. 

          ' await the expiration of the interval
          Call WaitForInterval&#40;0&#41;
     Loop 
End Sub
- Don Kinzer
spamiam
Posts: 739
Joined: 13 November 2005, 6:39 AM

Post by spamiam »

Oh, Yes...

WaitForInterval(). For some reason, I had not understood the details of that function. I had never needed to synch to real time that way. But this is a perfect application of the function!

For a program using this method of pacing execution, you need to be sure that you have finished all your subroutines BEFORE the end of the interval, or else you will not end up executing the loop at the top of each and every interval.

Don, for debugging purposes, is there a way to tell if the interval has expired already? Is there is system register that can be checked to see if it has already decremented to zero? If the interval counter has already hit zero before you reach the WaitForInterval(0) statement, then you are already past the end of the interval. You may need to know this so you can trim down the amount of code within the loop, or do some subroutines in a round-robin pattern.

-Tony
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

spamiam wrote:[Y]ou need to be sure that you have finished all your subroutines BEFORE the end of the interval, or else you will not end up executing the loop at the top of each and every interval.
The WaitForInterval() subroutine has a few different modes of operation. With a parameter value of zero, if the interval has already expired the subroutine will return immediately. This means that execution won't be delayed until the next expiration. If the execution of the code within the loop occasionally takes longer than the interval period (but significantly less than two periods) the timing of the loop, on average, will still be accurate. On the other hand, if the loop consistently takes longer than the interval period, then the loop timing will not be as desired.

If the zero bit of the parameter is on (&H01), the WaitForInterval() call will await then next interval expiration. Adding the &H02 bit causes the interval timer to be reset resulting in a full-length interval wait.
spamiam wrote:s there a way to tell if the interval has expired already?
No, there isn't. However, you could determine this indirectly in a couple of different ways. Firstly, using an output pin you could output a zero just before the WaitForInterval() call and then output a 1 immediately after the return. Observing the output pin with a logic analyzer or oscilloscope would give you a rough idea of the timing. Alternately, you could simply toggle an output each time through the loop. The period of the resulting signal should be roughly twice the loop time.

A software-only solution would be to use one of the timers to determine how long loop execution takes or, alternately, how long the WaitForInterval() call takes.
- Don Kinzer
Post Reply