This is how is done in msp430gcc:
void Port1Int( void ) __attribute__( ( interrupt( PORT1_VECTOR ) ) ); void Port1Int( void ) { //our isr }
On lauchpad we have one press button that we can use for this task, it's P1.3.
P1DIR &= ~BIT3; // make this pin input P1REN |= BIT3; // turn on internal pull resistor P1OUT |= BIT3; // make pulling up to VCC P1IES |= BIT3; // high to low transition fires interrupt P1IFG &= ~BIT3; // clear interrupt
We have to use internal pull resistor because in newer launchpads there is no resistor for push button. There is a place for one but they removed it probably to lower power consumption.
Let's use timer A0 for some led blinking, here is our setup:
TA0CTL = TASSEL_2 | ID_2 | MC_1; TA0CCR0 = 250000; TA0CCTL0 = CCIE;TASSEL_2 - set clock source for timer to SMCLK which is ~1MHz ID_3 - divide our source clock by /4 MC_1 - counting until we reach value in TACCR0 CCIE - enable compare interrupt
We want to have times around 1s so we divide clock to 1MHz/4 = 250 KHz, so our timer will count 250 000 times per second, it's still pretty fast. So, next we use compare register which will compare our timer value to it and when it's equal it's fire interrupt (CCIE). We compare to 25 000 value so our timer count to this value every 1/10 of second, so we have interrupt 10 times per second.
Let's use all this to make program which starts blinking after pressing push button.
#include#include uint16_t counter = 0; uint8_t second = 0; int main() { WDTCTL = WDTPW + WDTHOLD; BCSCTL1 = CALBC1_1MHZ; DCOCTL = CALDCO_1MHZ; TA0CCR0 = 25000; TA0CCTL0 = CCIE ; P1DIR &= ~BIT3; P1REN |= BIT3; P1OUT |= BIT3; P1IES |= BIT3; P1IFG &= ~BIT3; P1IE |= BIT3; P1DIR |= BIT0 | BIT6; P1OUT &= ~( BIT0 | BIT6 ); __bis_SR_register( GIE ); while(1) { } return 0; } void Port1Int( void ) __attribute__( ( interrupt( PORT1_VECTOR ) ) ); void Port1Int( void ) { switch(P1IFG&BIT3) { case BIT3: P1IFG &= ~BIT3; TA0CTL = TASSEL_2 | ID_2 | MC_1; //run timer break; } } void TimerA0Int( void ) __attribute__( ( interrupt( TIMER0_A0_VECTOR ) ) ); void TimerA0Int( void ) { P1OUT ^= BIT6; if ( counter == 5 ) { counter = 0; second++; P1OUT ^= BIT0; if ( second > 7 ) { TA0CTL = 0; //stop timer second = 0; } } counter++; }
17 - 22 - setting BIT3 PORT1 interrupt
24,25 - setting pins to drive led
26 - this is msp430gcc macro which handle Special Register, here we enable General interrupt enable. When set, enable maskable interrupts ( all except nmi interrupt )
28 - just do nothing and wait for interrupts.
36 - we use switch so we can handle more pins in future
38 - this is important we must clear this bit by software, so we won't renter interrupt again. Normally when there is one interrupt source it's cleared automatically but here we want to check which pin invoke interrupt.
39 - We start our timer so leds will start blinking
44 - It is isr for our timer compare interrupt. One led blinks fast another slower, we blink 7 second and then stopping timer.
Uff, I never thought writing tech blog is so time-consuming and hard. I think my explanation of interrupts and timers is very superficial so don't hesitate and ask question so i can explain more.
Hi, thanks for new post.
ReplyDeleteI have 2 questions. First coming with simple example:
===
#define LED_0 BIT0
#define LED_1 BIT6
#define LED_OUT P1OUT
#define LED_DIR P1DIR
unsigned int timerCount = 0;
int main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
LED_DIR |= (LED_0 + LED_1); // Set P1.0 and P1.6 to output direction
LED_OUT &= ~(LED_0 ); //+ LED_1); // Set the LEDs off
LED_OUT |= LED_1;
CCTL0 = CCIE;
TACTL = TASSEL_2 + MC_2; // Set the timer A to SMCLCK, Continuous
// Clear the timer and enable timer interrupt
__enable_interrupt();
__bis_SR_register(LPM0_bits + GIE); // LPM0 with interrupts enabled
return 0;
}
void timerA0( void ) __attribute__( ( interrupt( TIMER0_A0_VECTOR ) ) );
void timerA0( void )
{
P1OUT ^= (LED_0 + LED_1);
}
===
Question - here I do not have endless loop at the end of main() and it does work. In your example removing endless lop at the end causes program not to work. Why ?
2. question:
I tried to find documentation for tool-chain and C library. What I found is: http://mspgcc.sourceforge.net/manual/ but writing interrupt handler as described there causes compilation errors and is obviously different that in example above. Where can I find documentation for tool-chain that you are using ?
BTW thanks for this blog, I got interested in launchpad thanks to you! :)
ad 1. You put you mcu in low power mode and never wake up, so it doing only what is in interrupt. If you want to wake up mcu you have to use __bic_SR_register( LPMO_bits ) in your interrupt ( bic means bit is cleared ) so after exiting form isr mcu is awaken.
Deletead 2. Look at the date of this document is very old and I'm afraid that there is no actual one. I find solutions in header files and in mspgcc mailing list archive and of course google:D