WatchDog Interrupt CPUSleep

Discussion specific to the 24-pin ZX microcontrollers, e.g. ZX-24r, ZX-24s and ZX-24t.
integrator
Posts: 10
Joined: 05 February 2015, 4:52 AM

WatchDog Interrupt CPUSleep

Post by integrator »

With the following code, the processor resets after WatchDog timeout instead of just waking from CPUSleep. What am I missing?

Code: Select all

Sub Main()
   debug.print "STARTUP"

   'TURN SOME STUFF OFF TO POWER SAVE
   'Register.ADCSRA = Bx0111_1111 'disable ADC
   'Register.ACSR = Bx1000_0000 'disable analog caomparator
   
        'INTERRUPTS
   Register.EICRA = Bx0000_1000 'falling edge on int0 and int1
   Register.EIMSK = Bx0000_0010 'enable int1
   
	Register.SREG = Bx1000_0000 'global interrupt enable 
	'SETUP WATCHDOG
	Register.MCUSR = Bx0000_1000	'WDRF = 1
	Register.WDTCSR = Bx0001_1000 'allow prescaler change ; WDCE = 1 WDE = 1
	Register.WDTCSR = Bx0010_0001 'set prescaler to 1024 = 8s; WDP3=1 WDP0=1
	Register.WDTCSR = Bx0100_0000 'enable watchdog timer interrupt WDIE = 1
	Register.SMCR = &H06
	Call OpenWatchDog(8)
   
	debug.print "Loop"
	do
		Call WatchDog()
	
		debug.print "Sleep"
		Call sleep(0.1)
		'Go to sleep
		Call WatchDog()
		Call CPUSleep()
	
		debug.print "Awake"
		Call WatchDog()
   Loop
End Sub
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Re: WatchDog Interrupt CPUSleep

Post by dkinzer »

integrator wrote:[...] the processor resets after WatchDog timeout instead of just waking from CPUSleep.
You have enabled the WatchDog interrupt but you haven't provided an interrupt handler. See the ZBasic Language Reference for information about defining interrupt handlers (applies to native mode devices only).

For the external interrupt, an interrupt handler is already present for VM mode devices because it is used for WaitForInterrupt(). For native mode devices, the external interrupt handler won't be there unless your code uses WaitForInterrupt() and, of course, the handler that is provided is specific to WaitForInterrupt().

As a side note, it isn't necessary to explicitly enable interrupts. The ZBasic kernel enables interrupts during startup because interrupts are used for the RTC, serial channels, etc.
- Don Kinzer
integrator
Posts: 10
Joined: 05 February 2015, 4:52 AM

Post by integrator »

I am using a non native mode device, ZX-24r. Do I need to somehow provide an interrupt handler?
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

integrator wrote:I am using a non native mode device, ZX-24r. Do I need to somehow provide an interrupt handler?
An interrupt handler is required but it is not possible to provide one on a VM mode device.

Below is some sample code, based in part on the code you posted, that was tested on a ZX-24n. It should work equally well on a ZX-24s. Note, particularly, the use of the Atomic ... End Atomic structure. The code within the Atomic block will be executed without interruption. This allows one to guarantee meeting the requirements described in the datasheet for changing the WatchDog control register.

Code: Select all

Sub Main()
   Debug.Print "startup"

   ' prepare for "Standby" sleep mode
   Register.SMCR = &H06
   
   ' set up the WatchDog for interrupt mode with a 4 second timeout
   Atomic
      ' the first assignment enables change, the second configures
      Register.WDTCSR = Register.WDTCSR Or &H18
      Register.WDTCSR = &He0
   End Atomic
   
   Do
      ' output a message, wait for all chars to be sent
      Debug.Print "sleeping"
      Do While CBool(StatusCom(1) And &H04)
      Loop
     
      ' go to sleep
      Call CPUSleep()
   
      Debug.Print "awakened"
      Call WatchDog()
   Loop
End Sub

' WatchDog timer interrupt service routine
ISR WDT()
  ' nothing to do
End ISR
It is important to note that while the processor is sleeping the RTC won't be running and any characters arriving on the serial inputs (HW or SW) will be missed.
- Don Kinzer
integrator
Posts: 10
Joined: 05 February 2015, 4:52 AM

Post by integrator »

Thanks Don!
I have your example running on the ZX-24s you just sent. Now I just need to figure out how to get the ZX-24r code to run on the ZX-24s. I changed the target and compiled, but it will not run. I am about to start from scratch and piece it together. Are there any obvious tricks to porting to native mode?

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

Post by dkinzer »

integrator wrote:Are there any obvious tricks to porting to native mode?
Usually, the issues are related to timing. Native mode devices execute code much more quickly than VM devices do. It will probably be most productive if you can start with a simple subset of your app, get it working, and then add other elements one at a time. You can use the conditional code capability of ZBasic to make it simpler to cut out large chunks. For example, in the code below I removed the ISR by putting the #if 0 ... #endif around it. You can also use #if defined identifier and then use #define identifier to enable blocks or comment out the #define to disable.

Code: Select all

Sub Main()
   Debug.Print "startup"

   ' prepare for "Standby" sleep mode
   Register.SMCR = &H06
   
   ' set up the WatchDog for interrupt mode with a 4 second timeout
   Atomic
      ' the first assignment enables change, the second configures
      Register.WDTCSR = Register.WDTCSR Or &H18
      Register.WDTCSR = &He0
   End Atomic
   
   Do
      ' output a message, wait for all chars to be sent
      Debug.Print "sleeping"
      Do While CBool(StatusCom(1) And &H04)
      Loop
     
      ' go to sleep
      Call CPUSleep()
   
      Debug.Print "awakened"
      Call WatchDog()
   Loop
End Sub

#if 0
' WatchDog timer interrupt service routine
ISR WDT()
  ' nothing to do
End ISR
#endif
- Don Kinzer
integrator
Posts: 10
Joined: 05 February 2015, 4:52 AM

Post by integrator »

I think now my problems are with the COM ports. My project uses COM 1 at 9600, COM 3 at 9600, and COM 4 at 19200. I was able to get it to work on the ZX-24r, but not with the native mode device. If I limit it to COM 1 & 3, I can get it to sort of work, but the watchdog times out too fast. When I add COM 4, I get no output.

The test code I am trying for the serial ports is below

Code: Select all

Option SignOn Off

Private Com1OutQ(1 to 30) as Byte
Private Com1InQ(1 to 10) as Byte
Private Com3OutQ(1 to 30) as Byte
Private Com3InQ(1 to 10) as Byte
Private Com4OutQ(1 to 30) as Byte
Private Com4InQ(1 to 10) as Byte

Public Sub Main()
	Call ComChannels(2, 19200)
	
	Call OpenQueue(Com1OutQ, SizeOf(Com1OutQ))
	Call OpenQueue(Com1InQ, SizeOf(Com1OutQ))
	Call OpenQueue(Com3OutQ, SizeOf(Com3OutQ))
	Call OpenQueue(Com3InQ, SizeOf(Com3OutQ))
	Call OpenQueue(Com4OutQ, SizeOf(Com4OutQ))
	Call OpenQueue(Com4InQ, SizeOf(Com4OutQ))
	Call DefineCom(3, 20, 19, bx0000_1000)
	Call DefineCom(4, 18, 13, bx0000_1000)
	Call OpenCom(1, 9600, Com1InQ, Com1OutQ)
	Call OpenCom(3, 9600, Com3InQ, Com3OutQ)
	Call OpenCom(4, 19200, Com1InQ, Com1OutQ)
	
	Call PutPin(Pin.RedLED, 0)
   
	Call PutQueueStr(Com1OutQ, "COM 1 OUT")
	Call PutQueueStr(Com3OutQ, "COM 3 Out")
	Call PutQueueStr(Com4OutQ, "COM 4 Out")
	
	Call PutPin(Pin.RedLED, 1)
	Call PutPin(Pin.GreenLED, 0)
   
	Dim i As Integer
	Dim sMsgOut As String

	'Flash On-Chip LEDs at startup
	For i = 1 To 2
		delay 0.15
		Call putpin(25, 0)
		delay 0.15
		Call putpin(25, 1)
		delay 0.15
		Call putpin(26, 0)
		delay 0.15
		Call putpin(26, 1)
	Next			

	

'=================	

	sMsgOut = "startup"
   Call PutQueueStr(Com1OutQ, sMsgOut)

   ' prepare for "Standby" sleep mode
   Register.SMCR = &H06
   
   ' set up the WatchDog for interrupt mode with a 4 second timeout
   Atomic
      ' the first assignment enables change, the second configures
      Register.WDTCSR = Register.WDTCSR Or &H18
      Register.WDTCSR = &He0
   End Atomic
   
   Do
      ' output a message, wait for all chars to be sent
	sMsgOut = "sleeping"
   Call PutQueueStr(Com1OutQ, sMsgOut)
      Do While CBool(StatusCom(1) And &H04)
      Loop
     
      ' go to sleep
      Call CPUSleep()
   
	sMsgOut = "awakened"
   Call PutQueueStr(Com1OutQ, sMsgOut)
      Call WatchDog()
   Loop
End Sub

' WatchDog timer interrupt service routine
ISR WDT()
  ' nothing to do
End ISR

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

Post by dkinzer »

integrator wrote:I can get it to sort of work, but the watchdog times out too fast.
Please explain what this means.
integrator wrote:When I add COM 4, I get no output.
A quick glance at the code reveals that Com4 is using the Com1 queues. That has no chance of working - I expect that it is a copy/paste error.

Further, there are typos in the OpenQueue() calls. You can avoid these by omitting the second parameter:

Code: Select all

   Call OpenQueue(Com1OutQ)
   Call OpenQueue(Com1InQ)
   Call OpenQueue(Com3OutQ)
   Call OpenQueue(Com3InQ)
   Call OpenQueue(Com4OutQ)
   Call OpenQueue(Com4InQ)
Also, if you're going to open Com1 explicitly you should probably use the option that suppresses automatically opening the Com1 channel.

Code: Select all

Option Console None
- Don Kinzer
integrator
Posts: 10
Joined: 05 February 2015, 4:52 AM

Post by integrator »

Sorry about the typos. It was a long day and I had tried a bunch of variations. I must have hit the undo button too many times.

However, I am still having the same problem with the COM ports. The following works fine on the ZX-24r, but when I switch to the ZX-24s the COM 3 output is garbled (checked at various baud rates) and the COM 4 output is missing. I even tried adding "delay (0.2)" between each line.

When I switch devices, I uncomment the Console None option and the interrupt handler.

Code: Select all

Option SignOn Off
'Option Console None

Private Com1OutQ(1 to 30) as Byte
Private Com1InQ(1 to 10) as Byte
Private Com3OutQ(1 to 30) as Byte
Private Com3InQ(1 to 10) as Byte
Private Com4OutQ(1 to 30) as Byte
Private Com4InQ(1 to 10) as Byte

Public Sub Main()
	Call ComChannels(2, 19200)
	Call OpenQueue(Com1OutQ)
	Call OpenQueue(Com1InQ)
	Call OpenQueue(Com3OutQ)
	Call OpenQueue(Com3InQ)
	Call OpenQueue(Com4OutQ)
	Call OpenQueue(Com4InQ)
	Call DefineCom(3, 20, 19, bx0000_1000)
	Call DefineCom(4, 18, 13, bx0000_1000)
	Call OpenCom(1, 9600, Com1InQ, Com1OutQ)
	Call OpenCom(3, 9600, Com3InQ, Com3OutQ)
	Call OpenCom(4, 19200, Com4InQ, Com4OutQ)
	'
	Call PutPin(Pin.RedLED, 0)
   
	Call PutQueueStr(Com1OutQ, "COM 1 OUT")
	Call PutQueueStr(Com3OutQ, "COM 3 Out")
	'Enable RS485 Tx
	Call putpin(14, 1)
	delay (0.2)
	Call PutQueueStr(Com4OutQ, "COM 4 Out")
	delay (0.2)
	'Disable RS485 Tx
	Call putpin(14, 0)
	delay (0.002)
	
	Call PutPin(Pin.RedLED, 1)
	Call PutPin(Pin.GreenLED, 0)
   
	Dim i As Integer
	Dim sMsgOut As String

	'Flash On-Chip LEDs at startup
	For i = 1 To 2
		delay 0.15
		Call putpin(25, 0)
		delay 0.15
		Call putpin(25, 1)
		delay 0.15
		Call putpin(26, 0)
		delay 0.15
		Call putpin(26, 1)
	Next			

	

'=================	

	sMsgOut = "startup"
   Call PutQueueStr(Com1OutQ, sMsgOut)

   ' prepare for "Standby" sleep mode
   Register.SMCR = &H06
   
   ' set up the WatchDog for interrupt mode with a 4 second timeout
   Atomic
      ' the first assignment enables change, the second configures
      Register.WDTCSR = Register.WDTCSR Or &H18
      Register.WDTCSR = &He0
   End Atomic
   
   Do
      ' output a message, wait for all chars to be sent
	sMsgOut = "sleeping"
   Call PutQueueStr(Com1OutQ, sMsgOut)
      Do While CBool(StatusCom(1) And &H04)
      Loop
     
      ' go to sleep
      Call CPUSleep()
   
	sMsgOut = "awakened"
   Call PutQueueStr(Com1OutQ, sMsgOut)
      Call WatchDog()
   Loop
End Sub

' WatchDog timer interrupt service routine
'ISR WDT()
  ' nothing to do
'End ISR

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

Post by dkinzer »

integrator wrote:[...] the COM 3 output is garbled (checked at various baud rates) and the COM 4 output is missing.
This is caused by a previously undetected issue in the native mode ZBasic libraries that arises when configured for multiple software UART channels and the first channel to be opened is slower than the maximum speed specified in the ComChannels() call. The cause of the problem has been found and corrected. You can work around the problem by opening Com4 at 19200 before opening Com3 at 9600 or you can download an updated ZBasic Library from the link below. It contains corrected ZBasic Libraries only for the mega644P and the mega1284P, the contents should be extract to the zlib subdirectory of the ZBasic installation directory (preserving path names).

http://www.zbasic.net/download/zlib/4.2 ... 6_part.zip

From an earlier post:
integrator wrote:[...] the watchdog times out too fast.
I also discovered the cause of this. Setting Register.SMCR to &H06 doesn't set the sleep mode to Standby as I had indicated in the comment. Rather, it is set to Power-Save mode and in that mode, as well as some of the others, an interrupt from Timer2 will awaken the device. Since Timer2 is used to govern the timing of the software UARTs and that timer is running if at least one software serial channel is open, it is the Timer2 interrupt that is awakening the device prematurely. This issue can be corrected by setting Register.SMCR to &H0c which selects Standby sleep mode.

There is a table in the mega1284P datasheet that indicates the Wake-up Sources in various sleep modes. Power-down and Standby are the only two modes that don't allow a Timer2 interrupt to awaken the device. If one of the other modes must be used, you could disable Timer2 just before going to sleep and then re-enabling it after awakening.

Note, too, that it may be necessary to ensure that Com3 and Com4 output have been completed before going to sleep. This can be done by modifying the wait loop thus:

Code: Select all

      Do While CBool(StatusCom(1) And &H04) Or _
	      CBool(StatusCom(3) And &H04) Or CBool(StatusCom(4) And &H04)
      Loop
- Don Kinzer
integrator
Posts: 10
Joined: 05 February 2015, 4:52 AM

Post by integrator »

Thanks Don! That did the trick. Now on to the next challenges.
integrator
Posts: 10
Joined: 05 February 2015, 4:52 AM

Post by integrator »

Is there a way to maintain a timed loop while using CPUSleep with the WatchDog interrupt? I am trying to do one task every four minutes and another task every hour, but I can't figure out a way to do this. The Watchdog is set to wake from CPUSleep every four seconds, but often it wakes immediately. I understand that the functions relying on the RTC would not work in this case, so I was thinking that if I could get it to reliably sleep for four seconds, I would be fine. I don't need an accurate time, +/- 20% would work. Right now, my four minute interval is anywhere from one minute to eight minutes.
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

integrator wrote:The Watchdog is set to wake from CPUSleep every four seconds, but often it wakes immediately.
That suggests that there is an issue somewhere. Either the watchdog timer isn't being reset (as it should be) immediately before the sleep commences or something is waking it up early.

I'd be willing to take a look at the current version of your code to see if I can spot something.
- Don Kinzer
integrator
Posts: 10
Joined: 05 February 2015, 4:52 AM

Post by integrator »

I emailed you my code. Let me know if you don't get it.

Thanks!
integrator
Posts: 10
Joined: 05 February 2015, 4:52 AM

Post by integrator »

I may have it working now. I had to move the following code so that it only checks the COM status once, right after my task instead of every time I call CPUSleep.

Code: Select all

		'Do While CBool(StatusCom(1) And &H04) _
		'	Or CBool(StatusCom(3) And &H04) Or CBool(StatusCom(4) And &H04)
		'Loop
Post Reply