This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
Hi all,
I've been looking around and I haven't been able to find any good help on exiting low power mode, in particular in use with Timer_A. The following is my C code to turn on the LED for a split second, by implementing a delay in between turning the LED on and off:
int main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
P1DIR |= 0x01; // P1.0 output
P1OUT |= 0x01; // LED ON
delay_us(65535); // DELAY
P1OUT ^= 0x01; // LED OFF
}
void delay_us(unsigned int us) {
TACCTL0 = CCIE;
TACCR0 = us;
TACTL = TASSEL_2 + MC_1; // UP MODE
_bis_SR_register(LPM0_bits);
}
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A (void)
{
_bis_SR_register_on_exit(LPM0_bits);
}
Note that LMP0_bits is defined as CPUOFF. Most of this code came from TI's example projects page (msp430x20x3_ta_02, Timer_A, Toggle P1.0, CCR0 Up Mode ISR, DCO SMCLK).
With this code above, the LED simply stays turned on forever. Could anyone tell me if I am entering and exiting low-power mode correctly?
Thanks,
Matt
A couple of things spring to mind
1. you don't enable interrupts so TImer_A ISR will never fire _bis_SR_register(LPM0_bits + GIE);
2. Even if it did fire you don't loop anything within main() so if Timer_A did work it would toggle the LED off and main would terminate.
In the example, TI uses Timer_A ISR to do all the work as that is the only thing that will execute without a loop-forever in main().
hi matt, i found few mistakes in ur code,i have modified those, i hope it will work now.
void delay_us(unsigned int );
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
P1DIR= 0xFF; // Configure Port pin
TACCTL0 = CCIE; // Capture compare interrupt enable
TACTL = TASSEL_2;
TACTL |= MC_1; // Up mode, TA start
P1OUT |= 0x01; //LED ON
delay_us(65535); // DELAY
P1OUT &= ~(BIT0); // LED OFF
}
void delay_us(unsigned int us) {
TACCR0 = us;
__bis_SR_register(LPM0_bits + GIE);
}
#pragma vector=TIMERA0_VECTOR
__interrupt void TIMER_ISR(void)
{
TACTL &=~(MC_1); // Stop Timer_A
__bic_SR_register_on_exit(LPM0_bits+GIE); // Clear LPM bits upon ISR Exit
}
Thanks for the reply bharathi,
It does work as you have shown above. However, my intent is to create a timer function that can be used multiple times at any length I choose (I am just running it using a blinking LED to make sure that it works). Do you know which variables I would have to reset, and where to do that, in order for my delay_us function can be used multiple times, for example:
LED_ON
delay_us(65535);
LED_OFF
delay_us(65535);
LED_ON
delay_us(65535);
delay_us(65535);
LED_OFF
Right now, if I call delay_us() a second time, it seems to get stuck in an infinite loop.
Thanks,
Matt
UPDATE:
Below is the code for essentially what I want to accomplish:
void delay_us(unsigned int full, unsigned int remainder);
int main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
P1DIR= 0xFF; // Configure Port pin
//TACCTL0 = CCIE; // Capture compare interrupt enable
//TACTL = TASSEL_2;
//TACTL |= MC_1; // Up mode, TA start
P1OUT |= 0x01; //LED ON
delay_us(15, 16975); // ONE SECOND
P1OUT ^= 0x01; // LED OFF
delay_us(15, 16975); // ONE SECOND
P1OUT ^= 0x01; // LED ON
delay_us(30, 33950); // TWO SECONDS
P1OUT ^= 0x01; // LED OFF
delay_us(30, 33950); // TWO SECONDS
P1OUT ^= 0x01; // LED ON
delay_us(45, 50925); // THREE SECONDS
P1OUT ^= 0x01; // LED OFF
delay_us(45, 50925); // THREE SECONDS
}
void delay_us(unsigned int full, unsigned int remainder) {
//unsigned long numCycles = remainder*TA_CLK_FREQ_IN_MHZ;
unsigned int i;
for(i = 0; i < full; i++) {
TACCTL0 = CCIE; // Capture compare interrupt enable
TACTL = TASSEL_2; // SMCLK
TACTL |= MC_1; // Up mode, TA start
TACCR0 = MAX_TA - 5; // ELIMINATE CLOCK CYCLES ALREADY ACCOUNTED FOR BY C CODE INSTRUCTIONS.
__bis_SR_register(LPM0_bits + GIE);
}
TACCTL0 = CCIE; // Capture compare interrupt enable
TACTL = TASSEL_2;
TACTL |= MC_1; // Up mode, TA start
TACCR0 = remainder; // TACCR0 CAN ONLY HOLD 16 BITS WHICH IS WHY MAX_TA IS 2^16 - 1 = 65535.
__bis_SR_register(LPM0_bits + GIE);
}
#pragma vector=TIMERA0_VECTOR
__interrupt void TIMER_ISR(void)
{
TACTL &= ~(MC_1); // Stop Timer_A
__bic_SR_register_on_exit(LPM0_bits+GIE); // Clear LPM bits upon ISR Exit
}
1. Does anyone know what SMCLK is set to so that I can use that to accurately determine how many cycles to wait (I think it's like 1 MHz or 1.1 Mhz)?
2. Are there any unnecessary repeated lines in my delay_us function (in particular in the for loop), that I can take out to keep my program from slowing down?
3. How can I determine how many cycles each of the instructions in my delay function take up (both the for loop and the code after the loop), so that I can remove them from the timer in order to get an even more accurate delay? I just used the number 5 in the for loop at random as an example of what I'm trying to do.
Thanks,
Matt
Hi Matt,
Some tips:
What MSP430 are you using? I was wondering if you might want to look into something like Energia http://energia.nu/?
There are some different options to set up a delay, depending on your goal.
Regards,
Katie
Hi Katie,
Thanks for the reply. I am not entirely sure how I will be implementing this timer delay function, but I was told that it needs to be able to take in a variable, which I do not think the __delay_cycles(x); function allows. Otherwise, I would definitely use it.
I will also definitely consider passing in clk cycles rather than us, once I start getting more into the project and what it actually entails.
As for dividing the clock frequency down, first off, I only used 1 second delays because it was easy to see the led blink and recognize the time that was delayed. I was told that I would be waiting around magnitudes of 50 us, 100 us, or even 500 us, maybe up to 1 ms, but thats about the max. Furthermore, I was asked to get accurate delays of around a few us. If that is the case, I would need a high frequency (1 MHz) to keep everything accurate, but at the same time, that frequency shouldn't exceed the kinds of delays that I would expect. Is this a reasonable approach to my task?
Matt
Matthew Wasko said:3. How can I determine how many cycles each of the instructions in my delay function take up (both the for loop and the code after the loop), so that I can remove them from the timer in order to get an even more accurate delay? I just used the number 5 in the for loop at random as an example of what I'm trying to do.
'View > Disassembly '
That will display a window with the assembly code of your 'C' program.
Joseph: Thanks for the tip on reading the assembly.
bharathi: I am using an MSP430F2013.
One last thing before I close this post:
When I enter LPM, will the next line ever be executed before I exit LPM? For example:
void enable_test()
{
P1DIR = 0x00;
P1IE = EN_0 | EN_1 | EN_2;
__bis_SR_register(LPM0_bits + GIE);
unsigned int i = 1;
}
Will 'i' ever be assigned before the interrupt handler is called on and it is subsequently exited? I think that is the case, but when I put a breakpoint on the last line in the function call, it hits that breakpoint before the interrupt handler actually goes off (it goes off on a GPIO pin low-to-high transition). If my assumption above is true, why does the breakpoint hit and point to the last line already?
Thanks,
Matt
No. When you enter any LPM, MCLK is stopped.Matthew Wasko said:When I enter LPM, will the next line ever be executed before I exit LPM? For example:
However, the same MCLK cycle the LPM bits are set in the status register, causing MCLK to stop, the CPU will fetch the next instruction. So if you set a breakpoint on the instruction directly after an LPM entry, the breakpoint will be triggered before (or rather exactly at the moment) the LPM is entered. As Breakpoints are triggered at the instruction fetch from the memory location, not at command execution.
This is why usually an LPM entry is followed by a NOP. The comment 'for debugging' in many demo codes doesn't mean this is where you should place the breakpoint (you could put it on any instruction, not just the NOP, so this would be nonsense) but rather that you should NOT put it there and the additional NOP allows you to put it on the next maningful instruction fetched after LPM exit.
matt, just try this code , and let me know whether its working or not..
#include "msp430 x20x3.h"
#include <intrinsics.h>
void delay(unsigned int);
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
P1DIR= 0xFF; // Configure Port pin
BCSCTL1= CALBC1_12MHZ; // Configure Clock system
DCOCTL = CALDCO_12MHZ; // Set DCO = 12MHz
BCSCTL3 = LFXT1S_2; // ACLK = VLO
TACTL= TASSEL0; // TACLK = ACLK=VLO
TACCTL0 = CCIE; // Capture compare interrupt enable
TACTL |= MC_1; // Up mode, TA start
while(1)
{
P1OUT |= BIT0; //LED ON
delay(11999); //delay for 1sec
P1OUT &= ~(BIT0); // LED OFF
delay(11999); //delay for 1sec
P1OUT |= BIT0; // LED ON
delay(23999); //delay for 2sec
P1OUT &= ~(BIT0); // LED OFF
delay(23999); //delay for 2sec
}
}
void delay(unsigned int count)
{
TACCR0 = count;
__bis_SR_register(LPM3_bits + GIE); // LPM3, Global Interrupts Enable
}
// Timer_A0 Interrupt Service Routine
#pragma vector=TIMERA0_VECTOR
__interrupt void TIMER_ISR(void)
{
TACTL &=~(MC_1); // Stop Timer_A
__bic_SR_register_on_exit(LPM3_bits+GIE); // Clear LPM bits upon ISR Exit
}
here i used AUXILORY CLOCK for TIMERA ,
as per my knowledge the following statements should be with in the main
(TACTL= TASSEL0; // TACLK = ACLK=VLO
TACCTL0 = CCIE; // Capture compare interrupt enable
TACTL |= MC_1; // Up mode, TA start)
and delay(11999) gives 1 second delay i.e
for 1 tick----->1/12000HZ (since ACLK connected to VLO which works at 12000HZ by default)
? <------- 1second
then we will get 12000 as count, but TIMERA starts from '0' we need to upload (12000-1=11999) value in TACCR0 register
Thanks Jens-Michael.
bharathi: I already have an implementation of my timer working. Just curious though, why/how did you decide to use LPM3 instead of LPM0. All of the code I use or have read just use LMP0. What would be a good reason for picking one LPM over the other (I know that different clocks are turned off, but not much else)?
Matt
actually in LPM3
– CPU is disabled
– MCLK and SMCLK are disabled
– DCO's dc-generator is disabled
– ACLK remains active
and in LPM0
– CPU is disabled
– ACLK and SMCLK remain active
– MCLK is disabled
so if you want to give a delay,LPM3 is enough, no need to go for LPM0.
anyway both will work but i chose simple one.
Ok, so LPM3 goes into a "deeper" LPM correct? What do you mean by LPM3 is enough, and I do not need to go for LPM0? Are you saying its better to go as deep into LPM as possible as long as everything still functions, to save more power I guess?
Matt
yeah,in order to save power LPM3 is one of the best choices.
actually we require different LOW POWER MODES in different situations.so we have to choose corresponding LOW POWER MODE according to our requirement in order to save the power
as i said before in LPM3 everything is disabled except ACLK,
here TIMERA is connected to ACLK which is connected to VLO (Internal low frequency oscillator with 12-kHz nominal frequency) i.e
TIMERA <--ACLK <--VLO
obviously it is enough to give a simple delay as well as to save the power......
That's not entirely true. Depending on MSP family, some modules may make an unconditional clock request that maybe keeps SMCLK active even in LPM3.bharathi kala said:as i said before in LPM3 everything is disabled except ACLK,
with quite a large variation across MSPs and a large temperature drift and drift with VCC. So timings by VLO may be off by +-66%bharathi kala said:VLO (Internal low frequency oscillator with 12-kHz nominal frequency)
**Attention** This is a public forum