CPUSleep?
CPUSleep?
Is there a good example anyware of how to use CPUSleep()?
Thanks,
MikeD
Thanks,
MikeD
Re: CPUSleep?
It can be as simple as:mdown wrote:Is there a good example anyware of how to use CPUSleep()?
Code: Select all
Call CPUSleep()
Which of the available modes to use depends on what you're trying to accomplish. You'll have to read the Power Management and Sleep Modes section of the applicable ATmega datasheet to understand the benefits and ramifications of each mode.
For the sake of example, let's say that you want to use Power-down mode on a ZX-44a. The following code would suffice:
Code: Select all
Register.SMCR = &H04
Call CPUSleep()
Code: Select all
Register.MCUCR = (Register.MCUCR And &H8f) Or &H20
Call CPUSleep()
- Don Kinzer
In order to use Timer2 to wake up the CPU you must enable one of the Timer2 interrupts. Of course, there must be an interrupt handler for any interrupt that is enabled and the VM only provides an interrupt handler for the compare interrupt since that is used for the software UART (channels 3-6).mdown wrote:I have the power mode I want &H03 and the cpu goes to sleep, I can't get it to wake up from the timer though.
The sample code below shows how to use Timer2 to awaken the processor. Although it is written for a mega644-based ZX, the same ideas can be used for other ZX devices. Of course, this is useful only if you aren't using any of the serial channels Com3 through Com6.
The sample code configures the timer for the maximum delay available - about 18mS. You could effectively create longer delays by counting wakeups and then taking action after a certain number. For example, 3375 wakeups would be about a minute. Longer delays could also be achieved by using an external clock for Timer2 - either a 32768Hz crystal on TOSC1/TOSC2 or an external signal fed to TOSC1.
The sample code uses the A.7 pin to generate an output signal indicating when the processor is sleeping. An oscilloscope or logic analyzer on this pin can be used to confirm the sleep time.
It is important to note that the RTC will not keep accurate time when any sleep mode is used since RTC interrupts will not be serviced when the processor is sleeping. If accurate timekeeping is required an external RTC will need to be used.
Code: Select all
'
' N.B.: this code is for mega644-based ZX devices. Changes will
' be required for other ZX devices.
'
Sub Main()
Call Sleep(0.5) ' allow sign-on message to display
Call PutPin(A.7, 1) ' configure a debug output
Do
' select the desired sleep mode
Register.SMCR = &H06 ' power-save mode
' reset all Timer2 interrupt flags
Call SetBits(Register.TIFR2, &H07, &H07)
' enable the Timer2 compare A match interrupt
Call SetBits(Register.TIMSK2, &H02, &H02)
' prepare Timer2 for a wake-up delay (about 18mS)
Register.TCNT2 = 0 ' divide by 256
Register.TCCR2A = &H02 ' WGM21
Register.TCCR2B = 7 ' 1024 pre-scaler
' sleep for a while
Call PutPin(A.7, 0)
Call CPUSleep()
Call PutPin(A.7, 1)
' stop the timer, disable Timer2 interrupt
Register.TCCR2B = 0
Call SetBits(Register.TIMSK2, &H02, &H00)
' ' report re-awakening
' Debug.Print "I'm awake at "; CStr(Register.RTCTick)
' Call Sleep(0.5) ' to allow message to display
Loop
End Sub
- Don Kinzer
Another thing to look for in using CPUSleep() is to make sure to enable the sleep mode as well as set the particular sleep mode that you desire.
In the mega32 chip, this is the most-significant-bit of the MCUCR register (bit 7, called SE [sleep enable] in the Atmel documentation). By default it is 0 (no sleep, even if CPUSleep() command is issued), and must be set to 1 to enable sleep mode.
By my reckoning, then, I would suggest trying this modification to Don's code above:
The part in parenthesis will zero the 4 most significant bits in the MCUCR, while preserving the state of the 4 lower bits, then the &HA0 command will set bit 7 to 1 and the next bits (6,5, and 4) to 010, respectively, i.e., to power mode 2 [power-down mode].
Since the MCUCR defaults to 0 on power-up (at least on my chips), it's important to set bit 7 to 1 to enable sleep mode. See page 30 of the Atmel mega32 documentation (in the downloads section) for details.
I haven't yet confirmed this, but will be playing with the CPUSleep() feature over the next few days. I'll try to follow up with what I find.
Thanks for a great forum, all.
-Jon (gandalf1)
In the mega32 chip, this is the most-significant-bit of the MCUCR register (bit 7, called SE [sleep enable] in the Atmel documentation). By default it is 0 (no sleep, even if CPUSleep() command is issued), and must be set to 1 to enable sleep mode.
By my reckoning, then, I would suggest trying this modification to Don's code above:
Code: Select all
Register.MCUCR = (Register.MCUCR And &H0f) Or &HA0
Call CPUSleep()
Since the MCUCR defaults to 0 on power-up (at least on my chips), it's important to set bit 7 to 1 to enable sleep mode. See page 30 of the Atmel mega32 documentation (in the downloads section) for details.
I haven't yet confirmed this, but will be playing with the CPUSleep() feature over the next few days. I'll try to follow up with what I find.
Thanks for a great forum, all.
-Jon (gandalf1)
Actually, setting the sleep enable bit is not necessary - that is done as part of the CPUSleep() call. There is no drawback to doing so; it's just that it isn't necessary. When the processor awakens from the sleep, the sleep enable bit is turned off before the VM executes the next instruction.gandalf1 wrote:Another thing to look for in using CPUSleep() is to make sure to enable the sleep mode as well as set the particular sleep mode that you desire.
It is important to note that in many cases it is desirable (and sometimes necessary) to use the SetBits() subroutine to perform a read-modify-write on an I/O port. Using that call is guaranteed to perform the operation atomically. In comparison, using a sequence like
Code: Select all
Register.MCUCR = (Register.MCUCR And &H0f) Or &HA0
The operation intended by the code above can be performed using SetBits() thusly:
Code: Select all
Call SetBits(Register.MCUCR, &Hf0, &Ha0)
- Don Kinzer
Thanks for the clarification, Don.
I suppose these are some of the idiosyncracies of having two systems at work: the Atmel at the hardware level and the ZBASIC firmware layer on top of it. Thanks for clarifying this issue.
Moving forward, I think I will take your suggestion and simply use the SetBits() command for any and all register modifications--this seems to be the safest bet.
Thanks again for the clarification.
-Jon
I suppose these are some of the idiosyncracies of having two systems at work: the Atmel at the hardware level and the ZBASIC firmware layer on top of it. Thanks for clarifying this issue.
Moving forward, I think I will take your suggestion and simply use the SetBits() command for any and all register modifications--this seems to be the safest bet.
Thanks again for the clarification.
-Jon
Since the register names and bit definitions are sometimes different between various AVR CPUs, it is convenient to use conditional compilation to write code that will run on different ZX models. One such example is setting the sleep mode for mega32-based and mega644-based ZX devices. The code below illustrates how this can be done using conditional compilation.
Note, particularly, the #else case. If you compile this code for a ZX device that is not based on either the mega32 or the mega644 you'll get a compile error reminding you that some code changes are required.
Code: Select all
' select Power-Down sleep mode
#if Option.CPUType = "mega32"
Call SetBits(Register.MCUCR, &H70, &H20)
#elseif Option.CPUType = "mega644"
Call SetBits(Register.SMCR, &H0e, &H04)
#else
#error Target CPU not supported
#endif
- Don Kinzer
Sure. Any signal that has the right characteristics can be used irrespective of the source.mdown wrote:Can a pin be used as an the interrupt say from a 555?
The mega32-based ZX devices have three possible interrupts inputs: INT0, INT1 and INT2. The mega644-based devices have these plus the ability to interrupt on a state change of any I/O pin. The ZX-1281 has some pin change interrupts plus additional INT3 through INT7 inputs.
- Don Kinzer
Yes. The mega644 supports a "pin change" interrupt on all I/O pins. WaitForInterrupt() can be instructed to wait for a pin change interrupt. The System Library Manual describes how to do this.mdown wrote:So on a 644 we can use any pin?
All that is necessary is to arrange for the pin change interrupt to occur. This means, ultimately, that the particular pin change interrupt needs to be enabled before calling CPUSleep(). Invoking WaitForInterrupt() will do this but I believe that you may also manipulate the CPU registers directly to accomplish the same goal without the need to have the extra task stack.mdown wrote:is there a comand similar to WaitForInterrupt() that we would call before calling CPUSleep()?
I haven't tried this code but the general idea should work. The code is written to awaken via a pin change interrupt on A.0. Note, particularly, that either logic transition (zero to one or one to zero) will trigger the interrupt.
Code: Select all
'
' N.B.: this code is for mega644-based ZX devices.
'
Sub Main()
Call Sleep(0.5) ' allow sign-on message to display
Call PutPin(A.7, 1)
Do
' select the desired sleep mode
Register.SMCR = &H06 ' power-save mode
' clear the pin change interrupt flag for Port A
Register.PCIFR = &H01
' enable the pin change interrupt on A.0
Call SetBits(Register.PCMSK0, &H01, &H01)
Call SetBits(Register.PCICR, &H01, &H01)
' sleep for a while
Call PutPin(A.7, 0)
Call CPUSleep()
Call PutPin(A.7, 1)
' disable the pin change interrupt on Port A
Call SetBits(Register.PCICR, &H01, &H00)
' ' report re-awakening
' Debug.Print "I'm awake at "; CStr(Register.RTCTick)
' Call Sleep(0.5) ' to allow message to display
Loop
End Sub
- Don Kinzer