I am using Code Composer Version: 5.2.1.00018 and a MSP430G2553 processor. I have written a program that uses P2.0 as an input and a button is attached to P1.7. When you push the button "GO" is transmitted over the serial port. After 8 interrupts from P2.0, if you push the button "Halt" is transmitted over the serial port. If you transmit an "a" to the launchpad it processes the "Case a" just fine. This resets some memory variables and sets the program up to press the button to get "GO". Everything works fine but I don't know why! I have to put the following in my reset routine or Port 1 Vector ISR goes dead: P2IFG&=~(BIT1 + BIT2 + BUTTON);//enable all interruptsP2IE|=(BIT1 + BIT2 + BUTTON); If I change these to P1IFG&=~(BIT1 + BIT2 + BUTTON);//enable all interrupts
P1IE|=(BIT1 + BIT2 + BUTTON); Port 1 interrupts don't work when I press the button.
Do you have any idea why I have to clear P2 interrupts to get Port 1 Vector ISR interrupts? My program follows:
#include <msp430g2553.h>////////////////Defines////////////////#define LED1 BIT6#define LED0 BIT0#define DAT BIT0 //P2.0 //input signal port#define BUTTON BIT7#define BUTTON_OUT P1OUT#define BUTTON_DIR P1DIR#define BUTTON_IN P1IN#define BUTTON_IE P1IE#define BUTTON_IES P1IES#define BUTTON_IFG P1IFG#define BUTTON_REN P1RENchar charbuffer[8];int i=0;int j=0;int dis=0;int dissave=0;float mycount=0;unsigned int capture_array[30]; // RAM array for capturesint tick=0;int cap=0;int Start2=0;float milesper=1.32/5280;int pre_cap=0;int first_pulse=0;int Start=0;char disbuffer[8];volatile unsigned char butdeb[1];
////////////////Function Protos////////////////void TX(char *tx_message);static char *i2a(unsigned i, char *a, unsigned r);char *itoa(int i, char *a, int r);
static char *i2a(unsigned i, char *a, unsigned r){ if (i/r > 0) a = i2a(i/r,a,r); *a = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i%r]; return a+1;}
char *itoa(int i, char *a, int r){ if ((r < 2) || (r > 36)) r = 10; if (i < 0) { *a = '-'; *i2a(-(unsigned)i,a+1,r) = 0; } else *i2a(i,a,r) = 0; return a;}
void TX(char *tx_message){ unsigned int i=0; //Define end of string loop int char *message; // message variable unsigned int message_num; // define ascii int version variable message = tx_message; // move tx_message into message while(1) { if(message[i]==0) // If end of input string is reached, break loop. {break;} message_num = (int)message[i]; //Cast string char into a int variable UCA0TXBUF = message_num; // write INT to TX buffer i++; // increase string index __delay_cycles(10000); //transmission delay if(i>50) //prevent infinite transmit { P1OUT |= (LED1+LED0); break; } } // End TX Main While Loop} // End TX Functionvoid SendChar( unsigned char c ){ while (!(IFG2&UCA0TXIFG)); // USCI_A0 TX buffer ready? UCA0TXBUF = c; // TX -> RXed character}int main(void){ WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer //setup clock to 1MHZ BCSCTL1 = CALBC1_1MHZ; // Set DCO to 1MHz DCOCTL = CALDCO_1MHZ; ////////////////USCI setup//////////////// butdeb[0] = 0; // Reset button debounce P1SEL = BIT1 + BIT2; // Set P1.1 to RXD and P1.2 to TXD P1SEL2 = BIT1 + BIT2; // UCA0CTL1 |= UCSSEL_2; // Have USCI use SMCLK AKA 1MHz main CLK UCA0BR0 = 104; // Baud: 9600, N= CLK/Baud, N= 10^6 / 9600 UCA0BR1 = 0; // Set upper half of baud select to 0 UCA0MCTL = UCBRS_1; // Modulation UCBRSx = 1 UCA0CTL1 &= ~UCSWRST; // Start USCI IE2 |= UCA0RXIE; // Enable USCI_A0 RX interrupt ////////////////General GPIO Defines////////////////
BUTTON_DIR &= ~BUTTON; BUTTON_OUT |= BUTTON; BUTTON_REN |= BUTTON; BUTTON_IES |= BUTTON; BUTTON_IFG &= ~BUTTON; BUTTON_IE |= BUTTON;
P1DIR |= (LED0 + LED1); //define output ports P1OUT &= ~(LED0 + LED1); //turn ports low P2REN |=(DAT); //enable pullups on respective ports P2IE |= DAT; P2IFG &= ~DAT; P2SEL = DAT; // Set P1.1 to TA0 /////////////////SETUP TIMER TA1CCTL0 = CM_2 + SCS + CCIS_0 + CAP + CCIE; // falling edge + CCI0A (P2.0)// + Capture Mode + Interrupt TA1CTL = TASSEL_2 + MC_2; // SMCLK + Continuous Mode __enable_interrupt(); for(;;) { if (dis!=dissave){ TX("D"); itoa(dis, disbuffer,10); TX(disbuffer); TX("\r\n"); TX("S"); itoa(capture_array[5], charbuffer, 10); TX(charbuffer); TX("\r\n"); dissave=dis; }
}
// Timer1 interrupt service routine#pragma vector=USCIAB0RX_VECTOR__interrupt void USCI0RX_ISR(void){ unsigned char c; c = UCA0RXBUF; //SendChar(c); //SendChar( '>' ); switch( c ) { case 'a': // This resets all variables WDTCTL = WDTPW + WDTHOLD;// Stop watchdog timer //turnoff timer 1 TA1CTL = MC_0; tick=0; cap=0; mycount=0.0; pre_cap=0; first_pulse=0; i=0; j=0; dis=0; TX("FIRST"); TX("\r\n"); TX("FIRST"); TX("\r\n"); TX("FIRST"); TX("\r\n"); Start=0; Start2 = 1; butdeb[0] = 0; // Reset button debounce P2IFG&=~(BIT1 + BIT2 + BUTTON); //enable all interrupts P2IE|=(BIT1 + BIT2 + BUTTON);
BUTTON_IFG=0x00; P1DIR |= 0x01; P1OUT = LED1;// Set P1.0 to output direction break; }}
#pragma vector=TIMER1_A0_VECTOR__interrupt void TIMER1(void){ dis+=1; if (first_pulse==0) { pre_cap=TA1CCR0; first_pulse=1; goto here; //break from interrupt service routine }tick = TA1CCR0;cap = tick- pre_cap;mycount=cap;mycount=mycount/10000000;mycount=mycount/3600;mycount=milesper/mycount;cap=mycount;capture_array[i]=cap;i++;if (i ==10){ P1OUT^=LED0;//toggle led first_pulse=0; i=0; mycount=0; }butdeb[0]?butdeb[0]--:1;pre_cap = tick; // store this capture valuehere:P1OUT^=LED1;}#pragma vector=PORT1_VECTOR__interrupt void P1_ISR(void) {
if (Start2==0){ if ((BUTTON_IFG & BUTTON) && !butdeb[0]) { if(Start==0){ butdeb[0] = 8; //start timer TA1CCTL0 |= CM_2; TA1CTL = TASSEL_2 + MC_2; // SMCLK + Continuous Mode TX("GO"); TX("\r\n"); Start = 1; } else{ butdeb[0] = 8; TX("HALT"); TX("\r\n"); Start = 0; } } } Start2=0; BUTTON_IFG = 0;
I don't really understand what your problem is.
However, when a port interrupt is detected (IFG bit set) and the ISR is called (only if IE bit is set too), this doesn't remove the interrupt condition (except in a few cases, e.g. the TA/TBCCR0 interrupt auto-clears it).Inside your ISR, you'll have to clear the IFG bit. In some cases this is done by handling the interrupt reason (e.g. reading RXBUF clears the RXIFG bit), in most other cases you'll have to manually do it.For soem ISRs there is an interrupt vector register (xxxIV). If read, it will return a number that represents the highest pending interrupt for this module and at the same tiem clear the IFG bit for this interrupt.If you don't use this, you'll have to manually reset the IFG bit, or the interupt is still pending, and leavign the ISR will enter it again right away, unless a higher priority interrupt is pending.So an enabled but not properly reset interrupt may block all other (lower priority) interrupts from ever being handled (and prevents main from running at all).
_____________________________________Before posting bug reports or ask for help, do at least quick scan over this article. It applies to any kind of problem reporting. On any forum. And/or look here.If you cannot discuss your problem in the public, feel free to start a private conversation: click on my name and then 'start conversation'. But please do so only if you really cannot do it in a public thread, as I usually read all threads. And I prefer to answer where others can profit from it (or contribute to it) too.
I think I understand what you are saying. If you look at my program I have to set a flag "Start2" because when I do a reset it triggers the Port 1 ISR and inside the Port 1 ISR it thinks that BUTTON has been pushed. I have been working on this problem for three days and have tried clearing the BUTTON flag by using BUTTON_IFG &= ~BUTTON; to clear the BUTTON interrupt flag and BUTTON_IE |= BUTTON; to reset the interrupt. This didn't work. By what you are saying its the interrupt from a higher priority interrupt, keeping the Port 1 ISR from working and also the reason why I am entering the Port 1 ISR right away. It sounds like I need to reset all P1 and P2 interrupts and restart them. I don't know exactly where to do this but I will try it in the RX ISR. I appreciate your help on this and I will let you know what I come up with.
I fixed the problem. I put
BUTTON_IFG &=~BUTTON; BUTTON_IE&=~BUTTON;
at the end of the Port 1 ISR which stopped the double entry into the ISR.
I reset the BUTTON interrupt in the CASE a routine and also in the Timer ISR using BUTTON_IE |= BUTTON;
I'm still not sure why I have to clear and reset the P2 interrupts when I don't use BIT1, BIT2 or BUTTON ,which is BIT7, in my program for P2.
You enabled P2 interrupt for DAT (P2IE = DAT). As soon as an itnerrupt is detected on P2, it sticks there. You don't have an ISR for P2, do you? (the unformatted code is difficult to read). If no ISR is handling the P2 interrupt...
If you want to have a timer capture interrupt on P2.0, you don't have to set P2IE. These two are totally different things.
BTW: "P2SEL = DAT; // Set P1.1 to TA0" doesn't seem to fit, as you're programming P2.0 where the comment talks about P1.1.
Besides this, you already tried to do some sort of debouncing mechanism with butdeb[0].Pressing the button will likely trigger a series of itnerrupts which will cause the button ISR to be called several times. However, your inital approach to handle the interrupt only if butdeb[0] wa snot preventing further interrupts from happening (stealing CPu time). It only suppressed their handling inside the ISR.
Disabling the button IE bit (blocking all further interrupts) until the timer expired, and then clearing the button IFG bit before re-enabling the IE bit is indeed a good way to have a proper debouncing.
But: calling TX from inside the port ISR (or any ISR) is not a good idea, as its current incarnation uses busy-waiting for transfer completion (so the CPU is locked and won't accept any further interrupt sor do anything useful) until the transfer is done. And if a later version uses interrupts itself for doing the transfer, thsi wouldn't work sicne during ISR execution, all other interrupts are locked.
You should instead set a global flag and let main() do the time-consuming I/O.
A furhte rimprovement: don't do anything inside the por tISR except for settign up the timer. In teh tierm ISR, check the current state of the port pin and initiate any action if the button is still pressed. This will eliminate glitches or button presses below the duration of the timer ISR. it also prevents additional button triggers due to bouncign while the button is actually released. (any bouncing during button release will trigger another button press in your code, but if you delay the check whether the button was really pressed into the tiemr ISR, you'll see that the button is no longer pressed afte rthe debounce time and it was a bounce during button release. (I hope this was not too confusing)
What this program does is measures the pulse width that comes in on P2.0. Sorry about the misplaced comment. It also counts the number of pulses. This is all handled under the vector=TIMER1_A0_VECTOR. This part of the programs work just like it should. Even my reset routine works "Case a". My problem is when I introduced the button. I am using the butdeb[0] to prevent bouncing. This appears to work but even after clearing the BUTTON interrupt flag and do a reset,Case a, I get an interrupt on the Port1 ISR and it thinks the button is the interrupt. And if I don't clear the P2IFG BIT1, BIT2 and Button, which has nothing to do with the P1 BIT1, BIT2 and BUTTON I am using in my program, the Port1 ISR will not fire. I have tried reading the TA1IV, TA0IV and TAIV. This is supposed to clear the interrupts pending, no change. When inside the Port 1 ISR nothing else is happening so the delay that is created by the transfer doesn't really matter. I have two questions:
1. Which XXXIV word goes with the TIMER1_A0_VECTOR? TA1IV TA0IV,etc.
2. Why do I have to clear P2IFG BIT1+BIT2+BUTTON interupts, which I don't use to get the Port 1 ISR to fire?
The statement about setting up the timer in the Port1 ISR, I guess I could use the WD timer as an interval Timer. I found some examples of this that creates an ISR
that contains the following which turns the interrupt off and then resets it in the WD ISR. Is this what you were referring to?
interrupt (PORT1_VECTOR) PORT1_ISR(void) { BUTTON_IFG = 0; BUTTON_IE &= ~BUTTON; /* Debounce */ WDTCTL = WDT_ADLY_250; IFG1 &= ~WDTIFG; /* clear interrupt flag */ IE1 |= WDTIE;
interrupt (WDT_VECTOR) WDT_SIR(void) { IE1 &= ~WDTIE; /* disable interrupt */ IFG1 &= ~WDTIFG; /* clear interrupt flag */ WDTCTL = WDTPW + WDTHOLD; /* put WDT back in hold state */ BUTTON_IE |= BUTTON; /* Debouncing complete */ }
Donald Varela1. Which XXXIV word goes with the TIMER1_A0_VECTOR? TA1IV TA0IV,etc.
For TIMER1_A1_VECTOR, there is the TA1IV register, covering the timer overflow interrupt TAIFG, as well as the TA1CCR1 to CCRx interrupts.Similar for Timer A0 (TA0IV) or Timer B (TBIV) etc.Yes, the naming is a bit confusing. It's so for historical reasons - the first MSPs dodn't have a Timer A1, there was only 'the' Timer A.
Donald Varela2. Why do I have to clear P2IFG BIT1+BIT2+BUTTON interupts, which I don't use to get the Port 1 ISR to fire?
Donald VarelaIs this what you were referring to?
Michael I can"t get " if (BUTTON_IN & BUTTON) {" to be true. It is defined as "#define BUTTON_IN P1IN". Is this correct?
I want to thank everyone for their input. There is something killing my Port 1 interrupt after I do Case a. It has to do with the Timer ISR and the P2 port which I'm only using P2.0. Tried every means that I know of to clear the interrupt to no avail. My intentions on the Case a reset was to reset some memory variables and continue as normal. Well I found out the easiest way to do this, not the most graceful but it works. At the end of Case a I replaced break; with main(); I wish I could have figured out why the Port 1 ISR would go dead if I didn't put the P2IFG statements when I'm not using BIT1, BIT2 or BUTTON on port 2. Maybe someone out there could actually put this on a launchpad and leave off the P2IFG in the Case a to see what I have been up against. The easiest test for this is to put the program on a launchpad, connect to a terminal. Type "a" and then try to get the button to work.
Thanks again for all your inputs. I,m going to mark this as answered but really it isn't.
Donald VarelaMichael I can"t get " if (BUTTON_IN & BUTTON) {" to be true. It is defined as "#define BUTTON_IN P1IN". Is this correct?
Donald VarelaThere is something killing my Port 1 interrupt after I do Case a.
As long as a higher priority interrupt i spending and not handled, the lower priority interrupt swon't be called.
However, I just noticed that in case 'a'. you do "P2IE|=(BIT1 + BIT2 + BUTTON);" while the defines tell that BUTTON is on Port 1. So you're enabling an interrutp on P2.7 that is no input signal at all and may trigger interrupts by catching radio waves.
I got the button to work. Even though I set the IES High to Low the Port fires when I release the button.
What my problem is if I don't put P2IE|=(BIT1 + BIT2 + BUTTON); in the Case a Port 1 will never fire. I agree that some interrupt is keeping the port 1 from firing because of its priority. Take the P2IE and P2ifg commands out of the Case statement and you will see that after doing the reset, "Case a" Port1 will not fire. I have tried clearing the port 2 interrupts that I am using which is P2.0. //Clear P2 IFG " P2IFG &= ~DAT;". I even tried clearing all interrupts for port 1 and port 2
P2IFG &=~0x3F; P1IFG &=~0xFF;
I think this clears all interrupts. Then I initialized just the Botton interrupt.
BUTTON_IE |= BUTTON;
I tried stopping the Timer and stopping the Timer interrupts
//turnoff timer 1 TA1CTL = MC_0;
TA1CCTL0=CM_0;
I can't figure out what is keeping it from firing.
I don't know how to check all pending interrupts. The last interrupt I use is the USCI. I don't know how to check its interrupt status.
I checked (UCA0STAT) and it was zero.
Nothing seems to work.
I would really like a solution for further programs. Currently if I call main() at the end of Case a it works fine, but this is a crappy solution.
This code works fine on my launchpad. Just a guess are P2.7 and P1.7 shorted .
Yes it works but if you take out the P2IFG and P2IE commands, which are really erroneous, it doesn't.
I FOUND THE PROBLEM!!!!
If you remove
P1DIR |= 0x01;P1OUT = LED1;// Set P1.0 to output direction
everything works fine!!!
I don't know what this has to do with what is happening..
I changed
P1OUT = LED1;// Set P1.0 to output direction
to
P1OUT |= LED1;// Set P1.0 to output direction and it works fine