This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Entering IDLE mode within Timer0

To keep very accurate timing during ACTIVE and IDLE modes I would like to push the core into IDLE mode and pull it out within Timer0. When I try to do this the system hangs. If I simply set a flag and have the main loop set PCON.IDLE it works fine. It is written in assembler and I am checking the push/pop stuff. I think the problem is in RETI. What happens to the hardware with RETI? Any help or pointers would be great.

  • Hi Michael,

    Are you also trying to set the program into idle mode within the timer0 interrupt routine? if so this may explain why your program hangs as you are relying upon an interrupt to wake up the system and if a reti has not been executed then the interrupt system remains 'blocked' from responding to the next (timer0/any) interrupt.

    Also, I am not quite sure why you feel the need to put the processor into idle mode to preserve timer accuracy - isn't that what interrupts are for? to vector to your timer routine the moment it overflows? and this should be no different to normal operation unless you have sections of code where interrupts are blocked (CLR EA)

    Hope this helps
    Mark :-)

  • Thanks for the quick reply. I am suffering through conflicting requirements. My device must maintain very low power consumption and support FHSS on a single timer. The other timers are allocated for other uses. My plan is to have the synchronous FHSS run completely via timer0. This means I must enter and exit IDLE mode in this timer or have a very tight main loop to deal with the entry. This not a good plan because the main loop can not really do anything now or in the furture. If I can find out what RETI does maybe I can tweak the write stuff before I enter IDLE.

  • I found a way. Simply push a new address onto the stack and run RETI. The new address is popped and at that point you can enter idle.

    Thanks.

    TIMER_0_ISR:
    	PUSH 	ACC
    	PUSH 	PSW
    	MOV  	PSW,#00H
    	PUSH 	AR6
    	PUSH 	AR7
    	USING	0
    	ANL		RFMAIN,#0F8H			; power up the main osc
    
    ;wait at least 5 mS for 14.7456MHz clock to start
    	MOV  	R7,#09H
    loop:
    	MOV  	R6,AR7
    	DEC  	R7
    	MOV  	A,R6
    	JNZ  	loop
    
    	ANL  	X32CON,#0FEH			; switch clock to main osc
    	NOP								; wait 3 clock periods (3/4 of a instruction cycle)
    									; for glitch free operation, Datasheet p33
    
    	MOV  	TH0,#TIMER0_ACTIVE_H	; set Timer0 counters
    	MOV  	TL0,#TIMER0_ACTIVE_L
    
            PUSH 	B						; push remaining registers now that we are running at 14MHz
    	PUSH 	DPH
    	PUSH 	DPL
    	PUSH 	AR0
    	PUSH 	AR1
    	PUSH 	AR2
    	PUSH 	AR3
    	PUSH 	AR4
    	PUSH 	AR5
    	USING	0
    	LCALL	TIMER_0_c_ISR
    	POP  	AR5
    	POP  	AR4
    	POP  	AR3
    	POP  	AR2
    	POP  	AR1
    	POP  	AR0
    	POP  	DPL
    	POP  	DPH
    	POP  	B
    	POP  	AR7
    	POP  	AR6
    ; Put return address to code on stack
    	MOV     A, #LOW(?enter_idle)
    	PUSH    ACC
    	MOV     A, #HIGH(?enter_idle)
    	PUSH    ACC
    	RETI
    
    ?enter_idle:
    	ORL  	X32CON,#01H		; switch to 32kHz clock
    
    ; wait 3 clock periods (3/4 of a instruction cycle) for glitch
    ; free operation, Datasheet p33
    	NOP
    	MOV  	RFMAIN,#03EH	; power down the RF and main osc
    	ORL     PCON, #0x01                     ; Enter idle mode
    	NOP                                     ; For safety
    	POP  	PSW
    	POP  	ACC
    	RETI
    	END
    
    

  • Hi Michael,

    There are some problems with the code you have posted, your routine ?enter_idle does things in the wrong order, the POP PSW and POP ACC will NEVER be executed because the processor is in idle mode so obviously? the stack will eventually overflow and may cause problems in other parts of your program. You also need to remove the Timer0 ISR return address off the stack so if you modify your code from where timer is reloaded to be as follows it should work better for you.

    	MOV  	TH0,#TIMER0_ACTIVE_H	; set Timer0 counters
    	MOV  	TL0,#TIMER0_ACTIVE_L
    
    	POP		AR7
    	POP		AR6
    ; Put return address to code on stack
    	MOV     A, #LOW(?enter_idle)
    	PUSH    ACC
    	MOV     A, #HIGH(?enter_idle)
    	PUSH    ACC
    	RETI
    
    ?enter_idle:
    	POP  	PSW
    	POP  	ACC
    	DEC		SP				;remove original 'return from interrupt' address
    	DEC		SP
    
    
    	ORL  	X32CON,#01H		; switch to 32kHz clock
    
    ; wait 3 clock periods (3/4 of a instruction cycle) for glitch
    ; free operation, Datasheet p33
    	NOP
    	MOV  	RFMAIN,#03EH	; power down the RF and main osc
    	ORL     PCON, #0x01                     ; Enter idle mode
    	NOP                                     ; For safety
    ;	RET										; Can never be executed!
    	END
    

    Mark :-)