Jump to content
Jake

how would I create a delay in output going high?

Recommended Posts

I am trying to put a piece of my program in with a time delay.  If I get input high for like a full second then the output will go high.

 

I tried with a long switch debounce, but the output still went high right after the input was there. 

{

if (P1IN & BIT3)              \\input high

    then (delay 2 seconds)     \\see if it stays there for 2 seconds
    
    P1OUT |= BIT6;             \\ make output high

    then (delay 60 second)     \\output high for 60 seconds

    P1OUT &= ~BIT6             \\reset P1OUT to low 
 


}

Thanks everybody!

Share this post


Link to post
Share on other sites

I'll try that. I have close to that in my code at the moment but the input still goes high instantly, as soon as the button is pressed.

 

Sent from my XT1254 using Tapatalk

Share this post


Link to post
Share on other sites

I'll try that. I have close to that in my code at the moment but the input still goes high instantly, as soon as the button is pressed.

 

Sent from my XT1254 using Tapatalk

 

Your psudeo code doesn't look like it cares what the value is after the pin is initially pressed.

You need to check the value of the pin again after waiting, here is a quick example.

void delay_for_1_second()
{
// Add some code here to delay for 1 second
}

void check_input()
{
    if (P1IN & BIT3)              \\input high

    \\see if it stays there for 2 seconds
    for( int i = 0 ; i < 2 ; i++)
    {
        delay_for_1_second();
        if(!(P1IN & BIT3)) // if pin low, do not turn output on
             return;
    }

    P1OUT |= BIT6;             \\ make output high

    for( int i = 0 ; i < 60 ; i++)
    {
        delay_for_1_second();
    }

    P1OUT &= ~BIT6             \\reset P1OUT to low 
 
}

Share this post


Link to post
Share on other sites
#include "msp430g2553.h"

#define system_tick
#define button BIT3 //P1.3
#define ledr BIT0   //P1.0
#define ledg BIT6   //P1.6

void main(void)
{
	WDTCTL = WDTPW + WDTHOLD;            // Stop WDT
	TA0CCTL0 = CCIE;                     // CCR0 interrupt enabled
	TA0CTL = TASSEL_2 + MC_1 + ID_3;     // SMCLK/8, upmode
	TA0CCR0 = 50000;                     //  Hz
	P1OUT &= 0x00;                       // Shut down everything
	P1DIR &= 0x00;
	P1DIR |= BIT0 + BIT6;        // P1.0 and P1.6 pins output the rest are input
	P1REN |= BIT3;                     // Enable internal pull-up/down resistors
	P1OUT |= BIT3;                       //Select pull-up mode for P1.3
	P1IE |= BIT3;                        // P1.3 interrupt enabled
	P1IES |= BIT3;                       // P1.3 Hi/lo edge
	P1IFG &= ~BIT3;                      // P1.3 IFG cleared
	_BIS_SR(CPUOFF + GIE);               // Enter LPM0 w/ interrupt

	while (1)                           //Loop forever, we work with interrupts!
	{
	}

}

// Timer A0 interrupt service routine
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A(void)
{

}

// Port 1 interrupt service routine
#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{
	TA0CCR0 += 500000;          // Delay interupt must have P1.3 high for 2 sec

	P1OUT |= ledg;              // Set P1.6 high

	TA0CCR0 += 500000;        // Reset interrupt after 60 seconds

	P1IFG &= ~button;           // P1.3 IFG cleared

	P1OUT &= ~ledg;
}

Well I have kinda gotten closer.  I will try some the approach Greeeg stated as I saw this after I got to this point.  With the above code I can see the LED barely flicker on and back off.  With the timing so fast at the moment that is all it gets.  I need to make the times longer.  If I add to the cycles it just says truncated numbers and sets it back to what it wants. 

 

I wish I could find someone local to get some lessons from.

Share this post


Link to post
Share on other sites

@@Jake your code is looking good. Moving into interrupts is the right idea! However there are a few extra pitfalls.

// Timer A0 interrupt service routine
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A(void)
{

}

// Port 1 interrupt service routine
#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{
	TA0CCR0 += 500000;          // Delay interupt must have P1.3 high for 2 sec

	P1OUT |= ledg;              // Set P1.6 high

	TA0CCR0 += 500000;        // Reset interrupt after 60 seconds

	P1IFG &= ~button;           // P1.3 IFG cleared

	P1OUT &= ~ledg;
}

Firstly, the MSP430 is a 16bit MCU. Since TA0CCR0 is a 16 bit register it can only hold 65,536 (2^16) possible values. Adding 500,000 will actually only be adding 41,248 due to bits outside of 16 bits being ignored.

 

Secondly just adding to the CCR0 register does do alot in hardware, but the CPU will still move on to the next instruction. This is why you are seeing the LED flicker on very quickly. In order to get these interrupts to work you will have to use the timer interrupt.

Share this post


Link to post
Share on other sites

ok so I need to setup this timer arraignment in the timer AO ISR right? 

 

I found the TI timers in depth PDF it seems like I need to have the timer in compare mode to cause an event after a certain period of time. 

 

So those time instructions need to be in the timer ISR ?

 

Ill keep working the googlefu to get this figured out. 

 

or is there supposed to be a line in the timer ISR to run the timer,

 

Then in the port ISR you have instructions to wait for x time, then set the pin high, then wait set time again and reset?

 

Thanks again for the help!

Share this post


Link to post
Share on other sites

I am still trying to get through this no luck so far

void delay_for_1_second()
{
// Add some code here to delay for 1 second     <-----I am not sure what I need to add here
}

void check_input()
{
    if (P1IN & BIT3)              \\input high

    \\see if it stays there for 2 seconds
    for( int i = 0 ; i < 2 ; i++)
    {
        delay_for_1_second();
        if(!(P1IN & BIT3)) // if pin low, do not turn output on
             return;
    }

    P1OUT |= BIT6;             \\ make output high

    for( int i = 0 ; i < 60 ; i++)
    {
        delay_for_1_second();
    }

    P1OUT &= ~BIT6             \\reset P1OUT to low 
 
}

Would this work for the interrupt like the button high will trigger the interrupt

 

Then the timer starts making sure the button is high for the duration of the timer (2 seconds)

 

Makes the output high

 

Triggers timer again counts 60 seconds

 

then resets the output back to low. 

 

 

It also says "i" is undefined, I need to add that to the #define i (......dont know what) in the define sections

 

 

Thanks again!! 

Share this post


Link to post
Share on other sites

Here is some code that uses a timer to count to one second, then keeps track of the seconds to toggle an LED every 5 seconds.  So maybe that answers one of your questions.  To be honest, I'm not sure what you are trying to do though...

/*
 * Written for the FR6989 and does the following:
 *    Uses timer to interrupt approximately every second
 *    Toggles red LED approximately every 5 seconds
 */

#include <msp430.h>

#define  RED_LED             BIT7     // P9.7 is the red LED on the FR6989
#define  ACLK                BIT8     // Specify ACLK as the source (0x0100)
#define  UP                  BIT4     // Tell Timer A to count up   (0x0010)
#define  ENABLE_PINS       0xFFFE     // Enable pins
#define  COUNT_SECOND       37000     // approximately one second for timer as set

// Global variables
long numSeconds = 0;                  // used to count how many seconds have passed

// Function prototypes
void Timer0_Init(void);
void IO_Init(void);

//************************************************************************************
// main
//************************************************************************************
main()
{
    WDTCTL = WDTPW | WDTHOLD;          // Stop watchdog timer

    Timer0_Init();                    // Initialize timer

    IO_Init();                        // Initialize the IO

    while(1)                          // Loop over and over
    {
        if (numSeconds == 5)          // 5 seconds have passed
        {
            P9OUT ^= RED_LED;         // Toggle red LED on P9.6
            numSeconds = 0;           // Reset number of seconds
        }
    }
}
//************************************************************************************
// Timer0_Init                          Initialize TimerA0 and start counting seconds
//************************************************************************************
void Timer0_Init()
{
    TA0CCR0 = COUNT_SECOND;           // Timer A0 counts to this number (~1 second)
    TA0CTL |= ACLK;                   // Use ACLK
    TA0CTL |= UP;                     // Count up
    TA0CCTL0 = CCIE;                  // Enable the interrupt for Timer A0

    _BIS_SR(GIE);                     // Global activation of interrupts (done once)
}

//************************************************************************************
// IO_Init                              Initialize the I/O
//************************************************************************************
void IO_Init()
{
    PM5CTL0 = ENABLE_PINS;            // Enable I/O
    P9DIR = RED_LED;                  // Set red LED on P9.6 to output
    P9OUT &= ~RED_LED;                // Turn the red LED off
}
//************************************************************************************
// Timer0 Interrupt Service Routine     Count to one second and increment number of
//                                      seconds since numSeconds was reset
//************************************************************************************
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer0_ISR (void)
{
    numSeconds++;                     // Increment number of seconds since reset
}



Share this post


Link to post
Share on other sites

Awesome THANKS!!  I will see if I can get it to work with that. 

 

What I am trying to do is if there is an continuous input for at least 2 seconds for it to make an output high, after the output goes high reset the output to low after 60 seconds.  I am trying to use one remote button to do multiple tasks, a short push will allow one action to happen, a long (2 sec) push will allow a different action to occur. 



     If (BUTTON HIGH)                    //check for input pin high
         
         START TIMER COUNT 2 SECONDS     //start and run timer as long as input is high

         MAKE OUTPUT PIN HIGH            // if it makes it to the 2 seconds make output pin high
 
         START TIMER COUNT 60 SECONDS    // count down timer 

         RESET OUTPUT TO LOW             // reset output pin to low after 60 seconds elapses


Share this post


Link to post
Share on other sites

I have finally had a bit more time to attempt this.  I did find the real time clock documents, which someone else was using to capture a thermostat reading at specific intervals.

 

I am not getting any errors in the code, but it will not build.  I am not sure where I have gone wrong. 

 

Do yall see anything that I could do to get this rolling ?

#include "RTC.h"
#include "msp430g2553.h"
void main(void)
{
	WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
        P1DIR |= 0x01; // Set P1.0 to output direction
	CCR0 = 32768 - 1;
	TACTL = TASSEL_1 + MC_1; // ACLK, upmode
	CCTL0 |= CCIE; // enable CCRO interrupt
	_EINT();
	P1OUT &= 0x00;               // Shut down everything
	P1DIR &= 0x00;
	P1DIR |= BIT0 + BIT6;        // P1.0 and P1.6 pins output the rest are input
	P1REN |= BIT3;                   // Enable internal pull-up/down resistors
	P1OUT |= BIT3;                   //Select pull-up mode for P1.3
	P1IE |= BIT3;                       // P1.3 interrupt enabled
	P1IES |= BIT3;                     // P1.3 Hi/lo edge
	P1IFG &= ~BIT3;                  // P1.3 IFG cleared
	while (1)
	{
		LPM3; // enter LPM3, clock will be updated
		P1OUT ^= 0x01; // do any other needed items in loop
		_NOP(); // set breakpoint here to see 1 second int.
	}
}
// Timer A0 interrupt service routine
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A(void)
{
	incrementSeconds();
	LPM3_EXIT;
}
// Port 1 interrupt service routine
#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{
	if(P1IN & BUTTON);		// Start timer when button is high
	incrementSeconds(2);    // count 2 seconds as long as button is high
	P1OUT |= BIT6;		    // Set output high
	incrementSeconds(60);	// count 60 seconds after output is high
	P1IFG &= ~BIT6;         // Clear IFG, reset P1.6 low
}

Share this post


Link to post
Share on other sites

Hi @@Jake,

 

I've answered your PM here, so that others can benefit if they have similar problems.

 

From what you described in the PM, you're still working on this problem. I have tidied up the code you sent me and added a state machine into the interrupt to handle the logic.

 

These are two states

  • Waiting for a button held continuously for over 2 seconds
  • Waiting for 60 seconds with an output held high.

This code has been tested on my Launchpad G2331. let me know if there are areas you don't understand.

#include <msp430.h> 

#define LEDR BIT0
#define LEDG BIT6
#define BUTTON BIT3

const int TICKS_PER_SECOND = 32;
enum {state_waiting, state_hold_high};

unsigned int current_state = state_waiting;
unsigned int hold_timer = 0;

void init(void)
{

  // Stop watchdog timer to prevent time out reset
  WDTCTL = WDTPW + WDTHOLD;

  // Enable LED outputs and Button pullup
  P1OUT = BUTTON;
  P1DIR = LEDR + LEDG;
  P1REN = BUTTON;



  // Set up 32768Hz crystal
  BCSCTL3 |= XCAP_3;                              // select 12pF caps

  // initialize Timer0_A
  TA0CCR0 = ( 32768 / TICKS_PER_SECOND ) - 1;     // set up timer for 32Hz
  TA0CTL = TASSEL_1 + ID_0 + MC_1;                // configure and start timer

  // enable interrupts
  TA0CCTL0 = CCIE;                                // enable timer CCR0 interrupts
  __enable_interrupt();                           // set GIE in SR
  LPM3;                                           // select low power mode 3
  while(1);
}


/**
* Timer interrupt called at 32Hz (TICKS_PER_SECOND)
*/
#pragma vector = TIMER0_A0_VECTOR
__interrupt void myTimerISR(void)
{

  switch( current_state )
  {
    /* state waiting */
    case state_waiting:
      if( ( P1IN & BUTTON ) == 0 )
      {
        if( hold_timer >= TICKS_PER_SECOND * 2 ) 
        {
          /* If button held for 2 seconds change state */
          current_state = state_hold_high;
          hold_timer = 0;
          break;
        }
        else
        {
          /* If button pressed, but not for 2 seconds yet inc timer */
          hold_timer++;
        }
      }
      else
      {
        /* Button not pressed, reset timer */
        hold_timer = 0;
      }
      break;

    /* state hold high */
    case state_hold_high:
      /* Set output high */
      P1OUT |= LEDR;

      if( hold_timer >= TICKS_PER_SECOND * 60 )
      {
        /* If timer has elapsed, set output LOW, switch state */
        P1OUT &= ~LEDR;
        hold_timer = 0;
        current_state = state_waiting;
        break;
      }
      else
      {
        hold_timer++;
      }
      break;

    /* return to a idle state */
    default:
      current_state = state_waiting;
      break;
  }
}

void main( void )
{
  init();
}

Share this post


Link to post
Share on other sites

@@greeeg

 

Thanks for the clear example. I have a general question - we are often admonished to make interrupts as short as possible. myTimerISR above is somewhat long but obviously works. Do you have any rules of thumb about how long is too long?

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...