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.

MSP430F2012: Confusion with Interrupt Handler

Other Parts Discussed in Thread: MSP430F2012

Hi all,

I currently have a program that uses an MSP430F2012 (master) to communicate to a slave device by USI SPI. After getting an init(); function working, I tried to add extra interrupt handlers (the USI SPI communication requires interrupts for the USI count), however it never seemed to go into the interrupt. This is what I have:

void setup_usi_spi_master(void)
{
P1DIR |= CS; // Set P1.2 to output direction
P1OUT |= CS;
USICTL0 |= USIPE5+USIPE6+USIPE7+USIMST + USIOE; // Port, SPI master
USICTL1 |= USIIE; // Counter interrupt, flag remains set
USICKCTL = USIDIV_0 + USISSEL_2;// + USICKPL; // To change clk from present SCLK (was originally USIDIV_4 (/16 SMCLK). 7
USICTL1 |= USICKPH;
USICNT |= USI16B;
USICTL0 &= ~USISWRST;
__enable_interrupt();
}

int main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
setup_usi_spi_master();
__bis_SR_register(LPM0_bits + GIE);
init();
}

After setting up the usi spi master, I attempt to go into my low power mode (infinitely I'm assuming, I didn't set up interrupt handler enablers yet). However for some reason, the program insists on going into the init() function. When i take out the highlighted line above for my setup function, my program does what I expect, never reaching the init(); function do to being in the low power mode waiting for an interrupt which will never happen. Can someone explain to me why this is and if there is a way to fix it? I do need that line in my code for writes and reads to occur properly, but I also need to be able to go into a different low power mode when necessary as well. Any help would be greatly appreciated.

Thanks,

Matt

  • Also just in general, if someone could explain to me how manipulating the USICTL1 register, more specifically the USIIE bit, can cause me to either successfully or unsuccessfully enter the low power mode, that would be great :)

    Also, could it possibly have to do with how I attempt to go into low power mode -- __bis_SR_register(LPM0_bits + GIE); ??? Not sure that would be the issue, but I'm just leaving all possibilities out there.

    Thanks,

    Matt

  • You enabled USIIE and GIE but didn't provide a valid ISR, so, when IRQ happens, CPU starts executing random code... until reset, and than the story begins again.

    Provide a valid ISR, even a blank one like this:

    #pragma vector=USI_VECTOR

    __interrupt void USI_ISR(void)

    {

    }

    Regards

    Peppe

  • Thanks for the reply Peppe,

    I believe that I do have a valid ISR (except its a port 1 vector):

    #pragma vector=PORT1_VECTOR
    __interrupt void Port_1(void)
    {
    //if(P1IFG & EN_0) write_data = 0x01;
    //else if(P1IFG & EN_1) write_data = 0x02;
    //else if(P1IFG & EN_2) write_data = 0x00;
    //P1IFG &= ~(EN_0 | EN_1 | EN_2);
    __bic_SR_register_on_exit(LPM0_bits+GIE); // Clear LPM bits upon ISR Exit
    }

    This was at the bottom of my code (sorry I forgot to include that). But yeah, it still doesn't look like it works, it still gets to my init(); function -- even when I make it a blank ISR, leaving just the __bic_SR_register_on_exit(LPM0_bits+GIE); inside the ISR. Is there a problem that I have the ISR as a PORT1_VECTOR? I do not really know what this means, but this was the vector that I used in a previous project (with I2C instead of SPI) and it all worked out.

    Also, do you have any idea of the significance of the line USICTL1 |= USIIE; in this specific case?

    Thanks,

    Matt

  • Matthew Wasko said:
    Is there a problem that I have the ISR as a PORT1_VECTOR?

    No, but you have to provide an ISR for each IRQ you have enabled (or CPU starts behaving randomly), and USICTL1 |= USIIE enables USI IRQ.

    Regards,

    Peppe

    EDIT: differently from some small MCUs, like e.g. Microchip PIC16, which supports an unique interrupt request (and then in ISR you have to check which IRQ flag is enabled and act accordingly), MSP430 supports many distinct IRQs and related ISRs.

  • So I can leave that line in my code right? Would I just need to add a line referring to my port 1 vector? If so, can you give me a hint as to how/where to do that? As I do not remember doing it explicitly last time.

    Some general questions about ISRs:

    So when I call __bis_SR_register(LPM0_bits + GIE); is it looking for a USI vector ISR because it is enabled?
    If I also enable the port 1 vector, will it look for both ISRs? Will it simply interrupt in whichever one causes the interrupt first and go to that ISR (if it exists)?
    Finally, once I leave that ISR, can I still get the other interrupt and go into the other ISR in the current state of just finishing the last ISR?

    Thanks,

    Matt

  • Matthew Wasko said:
    So when I call __bis_SR_register(LPM0_bits + GIE); is it looking for a USI vector ISR because it is enabled?

    No. It just enables IRQ processing for CPU, allows IRQs of accordingly configured hardware to happen.

    Hey, you need to understand IRQ concept. Learning such a basics of microcontrollers using forums is.. well.. counterproductive for you and others. Good starting point is reading books or just internet resources.

  • But how is  __bis_SR_register(LPM0_bits + GIE); "accordingly configured hardware" specific to USI or Port 1? How does it determine what it corresponds to? That's what I don't get.

  • This configures hardware to raise interrupt signal:

    USICTL1 |= USIIE; // Counter interrupt, flag remains set

    However all interrupts of any configured for interrupting CPU, hardware are enabled using single flag: GIE.

    So you need to do both: configure hardware for interrupt AND enable General Interrupt Enable (GIE)  flag in the CPU status register. Please refer to user manual, chapter "3.2.3 Status Register (SR)".

  • That still doesn't help me understand why when I call  __bis_SR_register(LPM0_bits + GIE); it just keeps going without even stopping, onto the init(); function, when I do have the ISR made. Am I supposed to set GIE into the function call like I do in that line (__bis...)? Or do I have to explicitly do it somewhere like: SR |= GIE;

    I do not remember doing any of this for my I2C project. Would you happen to know why that is?

  • Matthew Wasko said:
    __bis_SR_register(LPM0_bits + GIE); it just keeps going without even stopping,

    Perhaps IRQ is pending before you enable GIE, ISR is called while you are stepping through LPM0+GIE  - you just don't notice it. Put some LED XOR'ing or breakpoint in your ISR.

  • If you check my code above, youll see that in my setup I call __enable_interrupt(); is that what is making the IRQ pending already? I have already tried setting a breakpoint in my ISR. It never gets there, it just skips over it and into the init(); function.

  • Matthew Wasko said:
    in my setup I call __enable_interrupt(); is that what is making the IRQ pending already?

    No, it makes the IRQ being executed as soon as it happens (xxIFG bit)and is enabled (xxIE bit). Before you enter LPM. This intrinsic is equivalent to _bis_SR_register(GIE).

    Some days ago I posted a quite large post about how interrupts work on the MSP. Unfortunately, I don't have a copy in my inbox anymore, so I cannot provide the link. You can do a forum search on my name and "interrupt".

  • I'm trying to look for the exact post you made about interrupts, but its kind of difficult because the search came up with 363 different posts :) but I'll keep looking.

    I've been reading through the documentation, but I guess what is really stumping me, and is the primary source of my problems is the fact that in my main loop, I try to enter LPM but the code keeps running. Can anyone provide a description as to why that is or explain why my code is doing that? Again, there is that one line in my spi setup that seems to halt the CPU from moving onto init(); when I comment it out.

    Thanks,

    Matt

  • Matthew Wasko said:
    its kind of difficult because the search came up with 363 different posts

    That's why I didn't look it up myself :) Yes, I wrote a lot in the past years.
    The chapter about CPU interrupt in my book is still in the making (along with most of the rest)

    However, in general, teh CPU makes an AND operation with an xxxIFG bit (set by a hardware vent or software), an xxxIE bit (set by software alone, as 'enable' bit) and the global GIE bit. If all three are set, a call to the associated ISR is inserted at the current point of execution.

    Each hardware module has its own set if IFG and IE bits, and usually has one own ISR (some have even two, e.g. the timers).

    Matthew Wasko said:
    in my main loop, I try to enter LPM but the code keeps running.

    Did you put a breakpoint on the init() function call or into the init() function itself? Due to the fact that a breakpoitn is triggered when the CPU fetches the instruction from the breakpoitn address and the other fact that on instrucitons whose destination is a processor register (like the status register SR), the CPU will fetch the next instruction while it is writing to the destination (instruciton prefectch) a breakpoint right after an LPM entry instruction is triggered before the LPM is actually entered. THis is why many demo codes contain a NOP instruction with the comment '// for debugging' after the LPM entry instruction.

    BTW: you should swap the writes to P1DIR and P1OUT. In the current order, you're outputting a short low pulse, which might confuse the slave. Set the port to high output first, before you switch from high-impedance input to output.

  • I wasn't able to figure out what the problem is, and I've been at it for a week now :(

    I believe that everything is working the way it should. I put a breakpoint on the init() function call and inside of the function itself, but it is clear that it is going into that code because I can see the buck voltage toggling on and off, which is what I do in my init(); function. I also swapped P1DIR and P1OUT with no luck :( if someone could look at my code and figure out the issue, that would be really great:

    int main(void)
    {
    WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
    // CODE NEEDED TO SET UP USI SPI MASTER MODE
    DCOCTL = CALDCO_1MHZ;
    P1OUT |= CS;
    P1DIR |= CS; // Set P1.2 to output direction
    P1DIR = 0xFF; // Unused pins as outputs
    P2OUT = 0;
    P2DIR = 0xFF;
    USICTL0 |= USIPE5+USIPE6+USIPE7+USIMST + USIOE; // Port, SPI master
    USICTL1 |= USIIE; // Counter interrupt, flag remains set
    USICKCTL = USIDIV_0 + USISSEL_2;
    USICTL1 |= USICKPH;
    USICNT |= USI16B;
    USICTL0 &= ~USISWRST;
    _enable_interrupts();
    // EXTRA CODE TO ALLOW GPIO PINS TO BE USED
    P1IE |= EN_0; // GPIO pin
    _bis_SR_register(LPM0_bits + GIE);
    one_second_delay(); // one of my functions to act as a NOP in this case.
    init(); // CODE KEEPS GETTING HERE AND STARTS RUNNING THIS FUNCTION
    }

    I put everything into my main function just to make it clear with everything being initialized and called on in one place. If anyone sees any unnecessary lines, let me know and I can eliminate them and first see if my original init(); still works on its own, and then check to see if it fixed the current problem. Any help would be greatly appreciated!

    Thanks,

    Matt

  • oops, forgot to add the two ISRs:

    // USI interrupt service routine
    #pragma vector=USI_VECTOR
    __interrupt void universal_serial_interface(void)
    {
    _bic_SR_register_on_exit(LPM0_bits+GIE);
    }

    #pragma vector=PORT1_VECTOR
    __interrupt void Port_1(void)
    {
    P1IFG &= ~EN_0;
    _bic_SR_register_on_exit(LPM0_bits+GIE);
    }

    Thanks again,

    Matt

  • Matthew Wasko said:
    P1IE |= EN_0; // GPIO pin
    _bis_SR_register(LPM0_bits + GIE);

    When you enable P1IE.0, it is possible that there is already an interrupt pending due to initial port pin conditions and pin setup ant whatever. (you didn't clear P1IFG before enabling the interrupts). In this case, there would be an interrutp immediately following the enable. Since the very next instruction is the LPM entry, it is executed before the interrupt is exedcuted. So the LPM is immediately exited again.

    Before setting P1IE.0, clear P1IFG to remove any port pin edge history.

  • Matthew Wasko said:
    __interrupt void Port_1(void)
    {
    P1IFG &= ~EN_0;

    This disables the interrupt. But you don't clear the IFG bit that caused it. So teh itnerrutp that brough you there is still pending, and once you set P1IE.0 again, you'll immediately get another interrupt.

    Matthew, many of the things you ask are covered in the appropriate sections of the users guide. Did you read any of them? Not just the register list, but also the boring pages of text before?

    Example: 8.2.7.1  [...] Each PxIFG flag must be reset with software. [...]

  • Yes, I read through the users guide. It just seemed very unlikely that some flags were already set right when I start up the program and what not. Anyways, I added the following two lines and it still doesn't work:

    P1IFG = 0; // NEW LINE (IN MAIN)
    P1IE |= EN_0; // GPIO pin
     
    P1IE &= ~EN_0; // NEW LINE (IN ISR)
    P1IFG &= ~EN_0;

    It still insists on going into my init(); function all the time :(

    Again, when I comment out this line: "USICTL1 |= USIIE;" my code will NOT go into my init(); function, it will be waiting for the appropriate interrupt (whatever that may be). Do you have any idea why this is? That line seems somewhat random to me, but it is clearly making a difference in my program.

    Thanks,

    Matt

  • Actually, why would I have to turn off the enable (P1IE &= ~EN_0;) ??? Doesn't that just mean that this specific pin on the MSP430 can lead to the interrupt handler? But once I go into the ISR, turning it off would serve no purpose because afterwards I (should) just come back into main and start running the one second delay and then init(); ???

    Matt

  • Matthew Wasko said:
    I added the following two lines and it still doesn't work:

    Besides adding those lines - did you think too?

  • Yes I did, there is not much code to think about. I don't know what else to do at this point. It really should be working, in my honest opinion.

  • I will just spit out some unsorted thoughts (because this hunt of rogue interrupts is becoming how to say.. whatever).

    Disabled interrupt in the ISR means it shall be enabled again, later. Otherwise current ISR call will be last one for particular peripheral.

    Most of interrupt/sleep implementations requires LPM0+GIE loop. You don't have one. Like:

    while(1) {
    _bis_SR_register(LPM0_bits + GIE);
    _NOP();
    _NOP();
    one_second_delay(); 

    Stop calling init() at the end of your program. It's useless call.

    If you need to find out why USI is firing interrupt when you don't expect it - work on it. Stop just changing code lines but read USI documentation, understand how USI works.

    Ok. Comment out this line:

    USICNT |= USI16B;

    because if you don't load data then you shall not set count register which means what? - yes, starting transfer with resulting interrupt which you are wondering about all the time here. (grin)

    And when you load counter, do it this way:

    USICNT = USI16B;

  • Ilmars said:
    Stop calling init() at the end of your program. It's useless call.

    You know what init() does? Maybe it's a state machine or a loop that never exits? I didn't see its code, so I don't know whether it is useful or not.

    Ilmars said:
    USICNT |= USI16B;

    That's a good catch. Setting the USI16B bit writes to the USICNT register. And if this register doesn't contain a count, this writes 0 to the bit count. From the users guide:

    USIIFG is set when USICNTx becomes zero, either by counting or by directly writing 0 to the USICNTx bits. USIIFG is cleared by writing a value > 0 to the USICNTx bits when USIIFGCC = 0, or directly by software.

    THat measn this line will trigger an interrupt. And the ISR wille xit LPM without clearing the interrupt. But it disables interrupts on exit, so it won't go into ean endless circle of calling the ISR, instead on its first call, it undoes the _enable_interrupts(), and hten, when interrupts are re-enabled when entering LPM, it just exits LPM immediately and disables further interrupts.

    So init() is called because the USI has a permanent interrupt pending and the USI ISR will exit LPM and disable interrutps as soon as interrupts are enabled.

    This leads to some generic rules:

    1) Don't clear the GIE bit on ISR exit unless you really want interrupts to be globally disabled after ISR exit
    2) In an ISR, make sure you handle the interrrupt by clearing the IFG bit that caused the interrupt (either manually, or by performing the appropriate action like writing to TXBUF, reading RXBUF, writing a new count to USICNT, reading the IV register etc.).
    3) when writing to a register, be sure you understood what you are doing.
    4) know the language you are using. A |= B is the same as A = A | B. So |= writes a new value to the register, including any side-effects that might trigger an interrupt. It also reads a register, including any side-effects like perhaps clearing an interrupt or releasing a state machine. (This also applies to the register or memory view of the debugger!)

  • Jens-Michael Gross said:
    You know what init() does?

    I don't care. I don't like even 2 bytes of small uC stack waste, not to mention obscure subroutine names :) It's more or less design style question.

  • Ilmars said:
    I don't care.

    You should, if you say it is a useless call.
    I agree about the naming and style, but even worst style and most cryptic naming is not inherently useless. Just bad style.

    It would be a useless call if the deices enters LPM before with GIE clear, or if it resets the MSP before making the call. So the call cannot be ever reached. But this isn't the case. So it isn't useless. The name might be misleading or it should have been called long before or whatever, but without knwoing what it does, it cannot be considered useless.

  • Jens-Michael Gross said:
    I don't care.

    You should, if you say it is a useless call. [/quote]

    Oh you are insistent :) I admit that mistakenly confused init() with setup_usi_spi_master(void). So it means that actually I did not know what init() does so I have no right to say that this is useless call. It's ok now? :)

  • Ilmars said:
    Oh you are insistent :)

    I hope I'm persistent too :)
    And I too wondered what it might do and what purpose it would serve if you init something at the end right before falling out of main into the void :)

  • Ah that makes sense.

    I have one more question however: Doesn't resetting P1IFG mean that it should stop producing that interrupt automatically? What I mean is that right now, I have an enable_test running in an infinite loop, where if I probe one of three pins, its corresponding buck voltage will go low and then back high... However, whenever I probe lets say the pin corresponding to Buck2, that buck will go low, then high... and then keep doing that over and over, which makes it seem like every time enable_test gets to the LPM, it automatically reads the EN_1 bit of P1IFG (which corresponds to buck 2) to be high, going into the ISR automatically without me probing a pin again.

    I thought that the line "P1IFG &= ~(EN_0 | EN_1 | EN_2); cleared all of those bits on the interrupt flag register so that I can manually turn them back on by probing the pin again. Can you see why what I am expecting is not happening?

    void enable_test()
    {
    P1DIR &= ~(EN_0 | EN_1 | EN_2);
    P1IE |= (EN_0 | EN_1 | EN_2);
    __bis_SR_register(LPM0_bits + GIE);
    write_reg(B_CTRL_reg, write_data);
    one_second_delay();
    write_reg(B_CTRL_reg, 0x83);
    one_second_delay();
    }

    #pragma vector=PORT1_VECTOR
    __interrupt void Port_1(void)
    {
    if(P1IFG & EN_0) write_data = 0x01;
    else if(P1IFG & EN_1) write_data = 0x02;
    else if(P1IFG & EN_2) write_data = 0x00;
    P1IFG &= ~(EN_0 | EN_1 | EN_2); // SHOULDNT THIS RESET THE REGISTER SO I CAN MANUALLY PROBE A PIN AGAIN INSTEAD OF REPEATING ITSELF?
    __bic_SR_register_on_exit(LPM0_bits+GIE); // Clear LPM bits upon ISR Exit
    }

    Thanks,

    Matt

  • Matthew Wasko said:
    probing the pin

    For pin probing you don't need pin change interrupt

    Just read pin state in the loop - that's it.

  • I do not know what you mean by that. Can you please explain? What is pin "change?"

    Thanks,

    Matt

  • Matthew Wasko said:
    What is pin "change?"

    Look, you are going to receive same advice 10th time or so: RTFM (Read The Funny Manual), "Digital I/O chapter", "Port interrupts".

    It says:

    Only transitions, not static levels, cause interrupts. If any PxIFGx flag becomes set during a Px interrupt
    service routine, or is set after the RETI instruction of a Px interrupt service routine is executed, the set
    PxIFGx flag generates another interrupt. This ensures that each transition is acknowledged.

    Look, if you need to read switches, you just periodically check pin input levels. Interrupts are needed in case you need to catch fast signals. Even pushbuttons can be handled by polling, most simple applications/projects usually do so.

    You are literally testing this forum for couple of weeks and yet still show clear signs that you do not read literature, but use most ineffective approach - trial and error.

  • I am planning on using polling (that is kind of what I already have because I'm using pushbuttons). My question is that pushing the button brings the voltage high, which is what I believe what is causing me to go into my ISR. However, I do not understand why I keep going back into the ISR even though I stop pushing the button. There is no more low to high edge because I am not pushing the button so would it have to do with something not being reset? I understand that the ISR is due to transitions, but I do not get why I keep going into my ISR if I am not generating those transitions anymore by pushing the button.

    Thanks,

    Matt

  • The way that I have my code, I believe that I am just waiting for a button to be pushed and then the appropriate action will take place, and then because it is in an infinite loop, I wait again for a button push, and then do some action, and so on. Why does it keep doing the same action that was initiated in the first time I push the button over and over, this time without ever waiting for me to push a button (which could be a different one and cause different actions to take place).

    Matt

  • Sorry, if you look at the most recent code I posted, it doesn't show that enable_test(); is actually in an infinite loop in main, which is how I keep getting these actions to occur over and over when I want to wait each time for a button push.

    Thanks a bunch,

    Matt

  • Matthew Wasko said:
    P1IFG &= ~(EN_0 | EN_1 | EN_2); // SHOULDNT THIS RESET THE REGISTER SO I CAN MANUALLY PROBE A PIN AGAIN INSTEAD OF REPEATING ITSELF?

    You can always manually probe the pin to see its current state. However, manually probing the pin only give that: the current state. it diesn't tell whether it has undergone any tarnsitions since last probing.
    If oyu get an interrupt by the falling edge, and then clear the IFG bit, the next interrupt will tell you that there was a rising edge and another falling edge (at least one). While probing, you cannot say whether the pin is still low or again low.

    Now you stil have the problem with bouncing. If you press a typical pushbutton, this will close and open the contact a few times withing a few ms until it finally settles. So you will get multiple interrupts until bouncing ends.

    The typical debounce procedure is:

    when the port pin detects the endge, trigger a port pin interrupt.
    In the port pin ISR, disable port pin interrupt and start a timer, that triggers a tiemr interrupt after some ms (100 to 250ms).
    When the tiemr expires, in teh tiemr ISR, check whether the pin is low or high and set a global flag for main, then clear any pending por tpin interrupt and re-.enable the port pin interrupt. (or maybe even change the edge direction, so the release will trigger the next interrupt)

    However this starts to get a bit tricky when you have more than one button (multiple button can be pressed simultaneously or in quick succession). But the basic concept remains the same.

    Is it intentional that you clear GIE on ISR exit? In your code, this is the right thing to do, as it prevents anothe rinterrupt while the main funciton is still busy handlinge the last), but I want to ask because sometimes peopple do this without knowing what they do (using just a mirror of their LPM entry instruction).

  • Woah, what is this about bouncing? In my code, if I only call checkForNewMode(); and then I know that it will not be called again until over a second later, do I have to worry about bouncing?

    I am not sure about clearing the GIE on ISR exit. When would I ever want to not clear GIE, just to help me understand the difference?

    Thanks,

    Matt 

  • Matthew Wasko said:
    Woah, what is this about bouncing? In my code, if I only call checkForNewMode(); and then I know that it will not be called again until over a second later, do I have to worry about bouncing?

    Jens-Michael are talking about pushbuttons which are operated in "push-release" style to change state (like keyboard keys you are pushing when write forum posts). Your application most probably is just static switches or even jumpers so you don't need debounce, you don't even need pin interrupt. Just check switch using logic input as you say once per second. Comprehend?

  • well i have temporary push buttons, they come back up to open right when i let go. I actually just realized that since this is the case, I should be checking for GPIO pin1 being high during my LPM, not necessarily going from low to high, because that can happen before i go into LPM. Does that make sense? And then would I have to change the P1EN and what not, because I am not sure how to do that?

    Matt

  • Matthew Wasko said:
    I should be checking for GPIO pin1 being high during my LPM

    You can check static signal during LPM using timer capture. Maybe there's other way but it anyway does not matter for particular application. Otherwise you shall bring CPU out of LPM to do it using CPU i/o instructions reading pin state and checking it.

    There's so many information on internet about debouncing. You shall start with this: http://bit.ly/143iwtV

  • Now im really confused, why would I even want to go to LPM if I have to end up just polling anyways?

    Cant I do something like this:

    while(mode != 0) {
        measure_and_display(); // <-- THIS IS WHY I HAVE TO POLL, BECAUSE I NEED TO KEEP UPDATING MEASUREMENTS AND DISPLAY
        if(P1IN & EN_0) mode = 0; 
    }

    That is a very simplified case with just one push button, but doesn't that kind of do polling for me without going into LPM? If this is the case, is there anything that I could be doing wrong with this strategy, because I tried running this code, and even right when I apply 3.5V to the right pin, it doesn't immediately set mode to 0 and leave the while loop, it takes a couple of tries touching the pin on and off until it works.

    Thanks,

    Matt 

  • I guess here's what it comes down to. If I want to check to see if a button is being pressed down, but continuously do other work (measuring and displaying), that implies that I would have to do polling instead of interrupts correct? Not only because I want to check if a pin is high, not the edge going from low to high, but also because I am doing other things at the same time (my msp430 is not idle while waiting for the button).

    Second, just realized that when I do this command

    while(mode == 0) {
    if(P1IN & (EN_0 | EN_1 | EN_2)) {
    mode = 2;
    }
    }

    mode is changed to 2 ONLY when I have 3.5V connected to the pin which I believe corresponds to EN_0. Is this the proper way to do polling for one of the three pins being high? And if so, can anyone help me understand how P1IN works? I tried checking the documentation, but theres not much information on P1IN, and I could have sworn that each bit in P1IN corresponds to the pins on the MSP430F2012 (so I was thinking 2nd bit for EN_0 (P1.2) which is 0x04, 3rd bit for EN_1 (P1.3) which is 0x08, and 4th bit for EN_2 (P1.4) which is 0x10). Anyways, right when I probe EN_0 (P1.2), mode immediately switches to 2, but for the other two pins, it doesn't work.

    Any help would be greatly appreciated!

    Thanks,

    Matt 


     

  • Matthew Wasko said:
    Now im really confused, why would I even want to go to LPM if I have to end up just polling anyways?

    Because you don't need to poll continuously, every CPU instruction don't need to be pin I/O polling, right? Then do it periodically, let's say 100 times per second. This rate will be OK for pushbuttons. On/off switches can be polled as slow as you want, like 1 time per sec. Pin poll/check will take couple of instructions which will be small part of 10 milliseconds, the rest of time between polling events CPU can sleep.

    Run periodic timer, in it's ISR check pin states, for pushbuttons do debouncing.

  • Matthew Wasko said:
    if(P1IN & (EN_0 | EN_1 | EN_2)) {
    mode = 2;
    mode is changed to 2 ONLY when I have 3.5V connected to the pin which I believe corresponds to EN_0.

    mode shoudl be se tto 2 as soon as any of the three bits is set in P1IN.

    Matthew Wasko said:
    Anyways, right when I probe EN_0 (P1.2), mode immediately switches to 2, but for the other two pins, it doesn't work.

    This may have various reasons, including your PCB layout, crosstalk, whatever.

    Your assumptions about the bits in P1IN are right. However, a floating input pin can oscillate or stay on low or high, and may switch when you come near to it with a probe.
    If you don't have pullup or pulldown resistors, there are too many factors to predict its behaviour.

  • To Ilmars:

    Yes, I see what your saying, but I have to do other things with my MSP430, measuring and displaying things. Where would that fit into my C code? Essentially, instead of going into LPM, I figured I would just use the extra time doing that measuring and displaying that I needed:

    while(1) {
    measure_and_display();
    if(P1IN & 0x1C) change something and then go back into while loop and keep measuring until another button is pushed
    }

    Does that make sense at all? My main question is how LPM would fit in with everything when I have to do intensive things almost continuously (measuring and displaying).

    Thanks,

    Matt 

  • Matthew Wasko said:
    Does that make sense at all?

    Yes. When you additionally implement couple of things:

    1) put CPU into LPM at the end of while() loop

    2) run periodic timer ISR around 100 times per second which do just one thing: wakes CPU from LPM on exit from ISR

    3) in while() loop count global counter (from 0 to 9), limit measure_and_display() calls to 10 times per second, not 100 times/sec.

    [edit] You don't work continuously 24/7 too: you sleep at night - it's LPM. When you do your everyday morning routine - it's keyboard checking. When you check calendar to notice that it's sunday and you can do weekend leisure - it's display routine. But you still do sunday morning routine and and after leisure will go to sleep sunday night.

  • Why would I run a period timer ISR around 100 times per second if I want to measure_and_display only 10 times per second -- also what is the point of having that "periodic timer ISR" ???

    could u show me a pseudocode example? I do not get what everything is doing and what is the purpose for each? Here is how I am interpreting what you are saying:

    // SOMEHOW THIS WILL ONLY MEASURE AND DISPLAY ONLY 10 TIMES PER SECOND AND ELIMINATE POLLING? WHY IS PERIOD ISR TIMER 100x PER SEC?
    counter = 0;
    while(counter < 10) {
    periodic_timer_ISR();
    measure_and_display();
    if(P1IN & 0x1C) change something and then go back into while loop and keep measuring until another button is pushed
    counter++;
    }

    Does this eliminate polling for a button press?

    Thanks,

    Matt

  • Matthew Wasko said:
    Why would I run a period timer ISR around 100 times per second

    To check and debounce keyboard.

    Matthew Wasko said:
    I want to measure_and_display only 10 times per second

    Yes, you can still do it 10 times, not 100 times per second, by calling measure_and_display each 10th cycle.

    Matthew Wasko said:
    while(counter < 10) {
    periodic_timer_ISR();

    OMG.. TimerISR is called by timer hardware IRQ, not by code of your idle loop!! Please read some book about embedded processors or something.

    Matthew Wasko said:
    pseudocode example?

    Assuming that timer ISR will bring CPU out of LPM on exit 100 times per second, pseudocode is as follows:

    volatile int counter = 0; // global counter

    while(1) {
    checkanddebouncebuttons();
    if (++counter >= 9) {
       counter = 0; // initially forgot this ;)
       measureanddispley();
    }
    LPMsleep();
    }

    Matthew Wasko said:
    Does this eliminate polling for a button press?

    No. It still does polling, 100 times per second, in checkanddebouncebuttons();

  • periodic_timer_ISR(); was just a placeholder for the many Timer_A module function setups that I have done before -- it was just pseudocode.

    So it's impossible to do this without polling?

  • Matthew Wasko said:
    periodic_timer_ISR(); was just a placeholder for the many Timer_A module function setups that I have done before -- it was just pseudocode.

    Wrong. You shall initialize timer before timing events processing loop, not inside it.

    Matthew Wasko said:
    So it's impossible to do this without polling?

    Possible. But most applications are fine with periodic polling unless you are building gaming controller or railgun trigger where response time is critical.

**Attention** This is a public forum