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.

How __delay_cycles() works

Other Parts Discussed in Thread: MSP430G2231, MSP430G2553

Dear sir,

                I am Using __delay_cycles(); function in my code, I have some questions on it.

1.How __delay_cycles(10000) works internally, And which clock source will select ?

2.The value (10000) is in terms of what ?

3.How to calculate this delay value, means time period ?

Here is the code :

#include <msp430g2231.h>
void main (void)
{
    WDTCTL = WDTPW + WDTHOLD ;


    for(;;)
    {
    P1DIR |= BIT0+BIT6;
    P1OUT ^= BIT0;
    __delay_cycles(10000);
    {
        P1OUT ^=BIT6;
        __delay_cycles(20000);
    }
    }
}

If any mistakes Sorry.

Thank You.

  • delay_cycles(x) is stopping in the code and waiting for x MCLK cycles.

    So for example if you are working at 1 MHz in your code

    delay_cycles(1000) will halt the code for 1000 * 1/1MHz

    its ok for testing to use delay_cycles but in a real programm you shouldnt use it, since it really stops the whole programm for that time, if you are relying on Interrupts and some time-critical code delay_cycles will probably mess your code up.

  • By the way, which Timer does this function use ? 
    Its a good thing to keep track of it, especially when you are using TIMERs in your application.

     

  • seb said:
    delay_cycles(x) is stopping in the code and waiting for x MCLK cycles.

    Not exactly.

    This intrinsic instructs the compiler to generate some code that will take exactly the given number of MCLK cycles to execute.
    AFAIK, it does NOT disable interrupts during execution. It just wastes CPU cycles. So any interrupts will extend the delay by their own execution time.
    This won't happen if the delay is done by the timer: any interrupt happening during (but not at the end of) the delay will not extend a timer-based delay.

  • Dear Sir,

                       Your information was right with calculation(Number of count*1/Selected Frequency), But i made one experiment on __delay_cycle();, I had given number of count value 1, with the calculation it should give 1us delay but it is giving 6.60us delay i checked this with Oscilloscope. for information i have attached code and two images here.

    How to know the Default frequency is 1MHz with MCLK, Can we able to check the MCLK frequency with inserting any code or functions ?

    #include <msp430g2553.h>
    void main (void)
    {
        WDTCTL = WDTPW + WDTHOLD ;
        P1DIR |= BIT0;

       for(;;)
        {
        P1OUT ^= BIT0;
        __delay_cycles(1);

        }
    }

                                                                With __delay_cycles(1);

                                                           With __delay_cycles(10);

    If any mistakes sorry.

    Thank you.

  • how do you set your MCLK to 1 MHz, could you post the clock module code in here?

    Are you using an external Oscillator like a quarz, or is the internal DCO?

    Thanks for your Oscilloscope shots anyway, but if you dont know what MCLK you are working with how can you ever know how long 1 delay cycle is?

    I dont know if your MSP430 has it but some bigger MSP430 ( bigger = more pins like 80 ) have a MCLK pin, on which the actual MCLK clock can be measured, so perhaps yours has one too.

  • Mallappa T said:
    for(;;)
        {
        P1OUT ^= BIT0;
        __delay_cycles(1);
        }

    You can't measure exact delay_cycles time using such approach because for(;;) cycle handling and also port I/O consume CPU cycles so definitely results will be more than 1us high or low time. Also you don't use DCO calibration constants, most probably your clock is not 1MHz but somewhere in 800..1000 kHz range.

    First improvement would be like this:

    for(;;)
        {
        P1OUT |= BIT0;
        __delay_cycles(1000);
       P1OUT &= ~BIT0;
       }

    Even then you shall calculate cycles for port I/O and compensate for it.

  • Mallappa T said:
    I had given number of count value 1, with the calculation it should give 1us delay but it is giving 6.60us delay i checked this with Oscilloscope.

    The scope shows the total execution time for the loop.

    It includes 1 MCLK cycle for the delay, but also 4 MCLK cycles for the pin toggle and 2 cycles for the jump to the loop start. 7 cycles in total.

    Test it without any delay, you'll get ~5.7µs then :)

    You MCLK frequency is apparently  1.05MHz. Which is pretty much the average default speed on 2x family.

  • Mallappa T said:
    How __delay_cycles(10000) works internally ?

    The debugger disassembly window will show you.

    Peter

  • For those interested here's the assembly code for __delay_cycles(100):

     

          ; Begin 100 cycle delay
          PUSHM.A #1, r13
          MOV.W #30, r13
    $1:   SUB.W #1, r13
          JNE $1
          POPM.A #1, r13
          ; End 100 cycle delay ;

  • Hi guys

    Happy holidays

    I am using delay_cycles inside a loop, like this

    // This function will give us 1ms wait time, so for getting 10 ms,
    // then delay_ms(10) will give 10ms and delay_ms(100) will give 100ms
    void delay_ms(unsigned int ms)
    {
        unsigned int i;
        for (i = 0; i<= ms; i++)
           __delay_cycles(6000); // 6000 will give us 1ms
        //(1/6MHz)*X=1ms         MCLK= 6MHz (MCLK is the source for delay cycles)
        //X=6000....this gives 1 ms
    }

    However, I am not getting the right no. for example, if I use

    delay_ms(10)+ delay_ms(10)+ delay_ms(40)

    i.e. I am calling these inside my program, then I will not get 60 ms (I am checking this on the oscilloscope), but I will get 99 ms instead.

    And when using delay_ms(170) (170 ms in total, as I am calling delay_ms multiple times) then I get 190 ms!

    Would you please advice me what is wrong?

    I am setting the MCLK to DCO/ 2. DCO is 12 MHZ calibrated.

    Thank you

  • Murtadha A said:
    for (i = 0; i<= ms; i++) __delay_cycles(6000); // 6000 will give us 1ms

    Won't that for loop perform the __delay_cycles one more iteration than required?

    I.e. should the loop be:

        for (i = 0; i< ms; i++)
           __delay_cycles(6000); // 6000 will give us 1ms

  • Yes you are right and I corrected it, but this will not make a big change as its 1 ms only

  • Murtadha A said:
    Yes you are right and I corrected it, but this will not make a big change as its 1 ms only

    Which device are you using?

    E.g. if you are using a FRAM based device and running code from FRAM their might be extra wait-states, based upon the CPU frequency, which is causing the delay to be larger than expected.

  • I am using MSP430G2553, so FRAM is not applicable is I am not wrong!

  • Murtadha A said:
    I am using MSP430G2553, so FRAM is not applicable is I am not wrong!

    Correct, using a MSP430G2553 and therefore no FRAM and no wait states.

    Some other comments:

    a) Are there any interrupts running during the delay loop, as interrupts will increase the delay?

    b) Have you checked the DCO is set to the expected frequency, e.g. by outputting SMCLK on P1.4?

  • Software delay loops will not be accurate and waste mcu cycles.

    The two timers in a G2553 have CCR1 and CCR2 that you can set to toggle a pin in hardware, with CCR0 value being the freq

    So give up on __delay and learn msp-counters today.

  • Whiel it is true, that FRAM would add waitstates, this only happens for MCLK frequencies above 8MHz. And then, exactly 1 waitstate is added for each FRAM read or write access. It does not apply for access to SRAM or to the peripheral registers. But a __delay_cycles() loop only idles around in program memory and therefore adding a waitstate would exactly double the execution time. As if MCLK had been halved for the execution of this delay.

    However, as Chester already implied, there are other reasons. One of the most overlooked things in this contexts is the fact that a __delay_cacles() delay generates a cod eloop that wastes an exact number of MCLK cycles. But it does nto mean that the resulting delay is exactly that long. This intrinsic generates code that takes a certain 'work' fo rthe MCU to do, it does not directly create a dely. That's why a number of cycles is given as parameter and not a time.
    Anything that interrupts the execution of this 'delay code' enlarges the delay time. Such as any interrupts. The time required to execute any ISRs during the delay is added to the delay.
    You should use __delay_cycles() only if you
    1) do not have any ISRs active or don't care if the delay takes longer than expected and
    2) do not care for the fact that the CPU is wokring hard (and wastign energy) for doing nothing.

    For all other cases, there are the timers (which are, despite their name, basically counters), as Tony suggested.
    You can do it by two different ways:
    1) set a global (volatile!) variable to the number of ms you want to wait. Set a timer to have an interrupt every ms. In the timer ISR you decrement the variable until it reaches zero. In main, you simply wait until the variable reaches zero.
    2) same as 1) but the ISR does not only counts the variable down, it also exits LMP when it reaches zero. And main goes into LPM after setting-up the variable and the timer.

    Both approaches do not care for the time it takes to execute other ISRs (as long as their execution time is below 1ms). The second one also reduces energy consumption, as the CPU doesn't do anything between the 1ms interrupts.
    When using a timer in continuous mode, the ISR would increment the compare value by 1000 (in case of a timer clock of 1MHz) on each interrupt and could detect if another ISR did block it for more than 1ms by checking the timer count. This 1ms "timer tick" can be used for other jobs too, as it only requires the CCR0 register the other CCRs for other jobs. Including serial output bit generation or serial input bit detection/timing. However, they can't be used for PWM when running the timer in continuous timer mode, as PWM requires up- or up/down mode.
  • If you only have one task, using WDT as a timer probably easiest (though you loose out on having the safe guard of a WDT)
    Set a var for how many numbers of WTD IRQ triggers you should sleep, enable WDT IRQ, go to sleep (set-lpmbits)

    WDT_ISR subtracts the counter var, when reached zero, disable its own IRQ, clear-lpmbits-on-exit.

    As to save power the WDT should run from a 32K crystal so you can go in to lpm3.

    It gets a little harder when you have few task wanting to sleep a some random lengths.
    You would need a event-machine that jumps to state-machines.

    If you have 3 task, they each could use CCRx in a free running timmerA, saves you from sorting sleep intervals and making a smart task scheduler.
    TimerA should use a aclk/div8/div4 to save power and will give you 1ms to 64sec delay options, the CCRx ISR should just set as event flag and do a clear-lpmbits-on-exit

    Grabbing current freerunning TA0 value have to done twice and cmp as it comes from a different clock domain if you use aclk.

  • The drawback with the WDT as delay timer is the limited flexibility. It always counts 2^15 steps (or more on newer MSPs). It's difficult to use it for smaller delays. It's perfect, however, for a 1s delay, running on a 32768Hz watch crystal.
    But for 1ms, you'd need a 32MHz crystal :(

**Attention** This is a public forum