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.

Modify Interrupts priorities?

Other Parts Discussed in Thread: MSP430F2132

Hi all,

This is my first post on this helpful forum. I'm currently carrying out my graduating placement as embedded system engineer. Therefore  I'm not very experienced with micro-programming and totally new to MSP430. Also I am french so excuse me in advance if sometimes my English sounds weird!

I'm using MSP430F2132 with IAR EW 4.20.1 and I am facing some issues with interrupts.

What I'm trying to do is to drive an IR LED. My output signal is modulated at about 10KHz with a PWM ratio of approximately  10% (high level for 10us and low for 90us). To do this I'm using Timer A in up/down mode. I have no problem for this.

The second point is that I want to emit like this: 20 pulses and then nothing, this periodically every 100ms. I do  this with  Timer B in up mode and again it works fine.

Last point is the critical point, I want to control this emitting with a knob. When the knob is pushed on: start emitting, when it's released: stop emitting. My main() doesn't do anything except turning the MSP into LPM0. For the time being I only use interrupts. What I do is that I detect first a falling edge on my knob (internal pull-up resistor on Port1 is used) then turn on Timers and toggle the interrupt edge. Then I detect rising edge and stop all timers.

The prob is that sometimes I miss the releasing event. I assume it happens because the MSP is processing Timers ISR and Timers interrupts priorities are higher than I/O Ports ones, so my "knob interrupt" is missed. I would like to know if it's possible to modify interrupts priorities or bypass by any way this problem?

Here is part of my code:

 

 

void main( void )
{

  WDTCTL = WDTPW + WDTHOLD;                 //stop watchdog
  BCSCTL1 = CALBC1_1MHZ;                    //Set DCO to 1MHz
  DCOCTL = CALDCO_1MHZ;                     //Set DCO to 1MHz
 
  __bis_SR_register(GIE);                   //Enable interrupts
 
  P1DIR |= (BIT0);                          //P1.0 output -> IR emitting diode
  P1OUT &= ~(BIT0);                         //P1.0 = 0
  P1DIR &= ~(BIT1);                         //P1.1 input -> child locker knob
  P1REN |= (BIT1);                          //P1.1 pull-up enabled -> SW to GND
  P1IE |= (BIT1);                           //P1.1 interrupt enabled
  P1IES |= (BIT1);                          //P1.1 interrupt edge: high-to-low
  P1IFG = 0;                                //Clear port 1 interrupt flag


  ADC10CTL0 = ADC10SHT_2 + ADC10ON + ADC10IE;
                                   //Sample time 16CLK, ADC ON, Interrupt Enable
  ADC10AE0 = 0x01;                          //P2.0 ADC10 option select 


  while(1)
  {
    __bis_SR_register(LPM0_bits);           //LP mode 0 = turns CPU OFF       
  }
}

 

////////////////////////////////////////////////////////////////////////////////
// Interrupts
////////////////////////////////////////////////////////////////////////////////


//Timer A0 interrupt service routine (TACCR0)
#pragma vector = TIMER0_A0_VECTOR
__interrupt void Timer_A_TACCR0 (void)
{
  if (IR_pulse++ < 20)                      //IR emitted for 20periods
  {

    P1OUT |= (BIT0);                        //set P1.0
    ADC10CTL0 |= ENC + ADC10SC;             //Sampling and conversion start

  }
  else                                      //20pulses have been done
  {
    IR_pulse = 0;
    IR_Process_TimerA_clear();              //Stops TimerA at 8Mhz
    IR_Process_TimerB_init();               //Runs TimerB at 1MHz
}


//Timer A1 interrupt service routine (TAIV)
#pragma vector = TIMER0_A1_VECTOR
__interrupt void Timer_A_TAIV (void)
{   
  switch (__even_in_range(TAIV, 10))        //Efficient switch-implementation
  {
    case  2: break;                         //TACCR1 not used
    case  4:                                //TACCR2
      P1OUT &= ~(BIT0);                     //Clear P1.0
    case 10: break;                         //Overflow not used
  default: break;                           //Others cases ignored
  }
}


//Timer B0 interrupt service routine (TACCR0)
#pragma vector = TIMER1_A0_VECTOR
__interrupt void Timer_B_TACCR0 (void)
{
  IR_Process_TimerB_clear();
  IR_Process_TimerA_init();
}


//Port 1 interrupt service routine
#pragma vector = PORT1_VECTOR
__interrupt void Child_locker_ISR (void)
{
    __bic_SR_register(GIE);
  if (P1IFG & BIT1)
  {
    if (P1IN & BIT1)                        //Interrupt triggered on low-to-high
    {
      IR_Process_TimerA_clear();
      IR_Process_TimerB_clear(); 
    }
    else                                    //Interrupt triggered on high-to-low
    {
      IR_Process_TimerA_init();
      __bic_SR_register_on_exit(LPM0_bits); //Disable LP mode 0 = CPU is active
    }
  }
  P1IES ^= (BIT1);                          //Toggle P1.1 interrupt edge
  P1IFG = 0;                                //Clear Port 1 interrupt flag
  __bis_SR_register(GIE);
}

 

//ADC10 interrupt service routine
#pragma vector = ADC10_VECTOR
__interrupt void ADC10_ISR(void)
{
  __bic_SR_register_on_exit(LPM0_bits);     //Disable LP mode 0 = CPU is active
}

 

////////////////////////////////////////////////////////////////////////////////
// Timers Registers settings functions
////////////////////////////////////////////////////////////////////////////////

void IR_Process_TimerA_init (void)
{
  BCSCTL1 = CALBC1_8MHZ;                    //Switch DCO to 8MHz
  DCOCTL = CALDCO_8MHZ;                     ////////////////////
  TACCTL0 = CCIE;                           //TACCR0 Interrupt enabled
  TACCTL2 = CCIE;                           //TACCR2 Interrupt enabled
  TACCR0 = TACCR_0;                         //Count value of TACCR0
  TACCR2 = TACCR_2;                         //Count value of TACCR2
  TACTL = TASSEL_2 + MC_3;                  //SMCLK + Up-down mode
}


void IR_Process_TimerB_init (void)
{
  TA1CCTL0 = CCIE;                          //TA1CCR0 Interrupt enabled
  TA1CCR0 = TA1CCR_0;                       //Count value of TA1CCR0
  TA1CTL = TASSEL_2 + ID_3 + MC_1;          //SMCLK + CLK/8 + Up mode
}


void IR_Process_TimerA_clear (void)
{
  BCSCTL1 = CALBC1_1MHZ;                    //Switch Back DCO to 1MHz
  DCOCTL = CALDCO_1MHZ;                     /////////////////////////
  TACCTL0 &= ~(CCIE);                       //TACCR0 Interrupt disabled
  TACCTL2 &= ~(CCIE);                       //TACCR2 Interrupt disabled
  TACTL &= ~(TASSEL_2 + MC_3);              //Clear SMCLK + Up-down mode
  TACCR0 = 0x0000;                          //Clear count value
  TACCR2 = 0x0000;                          //Clear count value
}


void IR_Process_TimerB_clear (void)
{
  TA1CCTL0 &= ~(CCIE);                       //TACCR0 Interrupt disabled
  TA1CTL &= ~(TASSEL_2 + MC_1);              //Clear SMCLK + Up mode
  TA1CCR0 = 0x0000;                          //Clear count value
}

 

 

I hope everything is understandable. Any help or advice is deeply welcomed!

Thank you in advance.

Kind Regards,

Antoine

  • There is no way to alter interrupt priorities. Thay are hard-wired into the processor. The higher the vector address, the higher the priority.
    But priority does not mean that interrupts are lost. Only if two interrupts happen from the same source before the first one has been handled.
    If you miss an interrupt, this has to do with your code and not the interrupt priority.

    Has your user button a debounce circuitry? Else you'll flooding the MSP by port interrupts when the user presses or releases the button. In your ISR, you're checking the current state of P1IN but you do not check whether this actually fits the current interrupt edge setting. I bouncing has caused the port pin to change again before you finished the reprogramming of the interrupt edge, you're hosed. A good deal of non-periodical occurring bugs are caused by racing conditions. Changing priorities would only reduce but not eliminate the problem.

    I'd use a different setup for your task (the LED part only):

    Use TimerA and Timer B for your two timings: TimerA manages the pulses by generating a PWM signal to the diodes anode and TimerB outputs a PWM signal with your 100ms frequency and an appropriate duty cycle so teh PWM output can be used to drive teh cathode of the LED. This way, you'll get your 20 pulses every 100ms without a single line of interrupt code.

    Then set up the TimeB interrupt so it will be executed each time when a 20 pulses sequence has ended. The diode is off adn you'll have 90ms to react. Test the current state of the port pin and if it is high (button released) change the outmode of the timerb PWM CCR to output the OUT bit (which is set to hold the diode disabled) and if the button is low (pressed), switch back to toggle mode, so after the current 100ms cycle has ended, the LED will start pulsing again.

    Only one ISR (TBCCR1) dealing with all this, plenty of time to react and no button press/release will be missed. Even if there is bouncing, all that can happen is that the button state change will be detected 1 TimerB cycle = 100ms later. (way below the users reaction time). The only difficult thing (not really) is to synchronize TA and TB so their cycles start synchronously (else the first or last pulse of the LED might be a few MCLK cycles shorter).

    Once all is initialized, all you'll ever have to do is switching the OUTMODE of TBCCR1 depending on the button state of each TB cycle. This could also be done by an external flipflop. Also, currently your por tinterrupt will interrupt the timerB 100ms cycle somewhere. By detecting the button state in the TimerB interrupt, it will always complete the current cycle.

    Of course you can also add ISRs to do additional work like starting the conversions. Driving the LED obviously isn't the only purpose of your firmware.

     

  • Thank you very much Jean-Michael for your inputs!

    Actually I solved my problem by simply clearing the Port1 interrupt flag at the beginning of my ISR, just after checking P1.1 flag. Now it works fine.

    Your method for the modulation is definitely smarter than mine I will try it and post as soon as possible the working code. Unfortunately (it depends for who :D) I'll be in holydays next week so I'll post it the week after.

    Thank you again, wish you a nice week end.

    Kind Regards,

    Antoine.

  • Antoine Mattera said:
    I solved my problem by simply clearing the Port1 interrupt flag at the beginning of my ISR, just after checking P1.1 flag.


    Sort of solved, I'd say. If it worked, then it's pretty sure that your problem is related to bouncing.
    If human intervention is part of the job, then it makes no difference whether you cleared the flag at the start or the end of the function. Your ISR would run hundreds of times before the user can willingly change the state of the button.
    SO if this small timespan matters, it is high-frequency bouncing of the button that caused the problem, calling your ISR so often in a row until one of the many interrupts was lost and the reception system locked-up. By changing the timing, you'll now getting all the superfluous interrupts, switching tiemrs on and off a dozen times per button press, but at the end you'll leave the code in the correct state.
    It's well possible that your LED will emit some high-frequency pulses at the start of the first 'normal' pulse.

    Antoine Mattera said:
    wish you a nice week end.

    And nice holidays to you.

  • Hi,

    Thank you for feedbacks Jean-Michael. I took a better look on your solution and I think I'll have a prob in the future. Indeed, I plan to switch my MSP into LPmode 4 to save as much energy as I can and switch to activ or other LP modes when external interrupts happened.  As far as I understood it, your solution needs Timers to be constantly runned (at least to check knob state every 100ms).

    About bounces, I tried to check it with a Logic analyzer (the only means I currently have) and with 24MHz sampling I don't see any bounces on my knob signal. To my mind bounces are supposed to happens at a lower frequency than 24MHz but maybe I'm wrong.

    Antoine

  • Normally, you already have some tasks that need to be done every now and then, so you'll need a timer. You can, however, attach the knob to port 1/2 and let it trigger an interrupt when pressed, then let the timers run and detect when it is released. After release stop the timers and put the device to LPM4 again until the next press.

    Bounces above MCLK frequency won't have any influence, even if they were that fast. Usually, they are in a low kHz range or even slower - maybe the logic analyzer is too fast and does not sample long enough. Also, the trigger level of the analyzer might not fit the MSP, so the MSP is detecting a level change that is not detected by the analyzer.

  • Ok for bounces, few kHz were also what I thought. I lowered the frequency of my analyzer (2002kHz) and indeed I see few bounces sometime but It doesn't affect my system at all.I'll keep in this way and If I met some issue I'll modify my code. By the way, aren't bounces supposed to bounce between GND and Vcc?

    About the timer my aim is to integrate a RTC module in my design (found one working with I2C protocol as all of my external modules). I did it to save flash space on my MSP. Later on I'll maybe integrate it in software stack but in a first stage and to save beta dev time I prefered to do it like this.

    Now my code runs most of the time in LPM4, switchs to LPM3 when the button is pushed and switch to LPM0 during pulsing stage.I reduced a lot my power consumption.

    Anyway, thank you again for your time it helped me a lot!

    Antoine

  • Antoine Mattera said:
    About the timer my aim is to integrate a RTC module in my design (found one working with I2C protocol as all of my external modules)

    I use the PCF8583 with great success. With a gold cap, it survives some days in power-down (a battery lasts for years) and can be easily trimmed to great precision (a simple adjustable capacitor on the watch crystal). It also offers 240 bytes of buffered ram, if you don't want to mess with the info flash.

    The first devices I built with it were for the C64 and I sold several hundreds of them in the early 90's. I got several requests about Y2K compatibility in '99, some devices still running with the original battery, a zinc-air button battery. (well, I did extend the battery life with a trick, using a germanium diode for very low dropout in discharge direction and a low charge current when the device was externally powered)

    Antoine Mattera said:
    By the way, aren't bounces supposed to bounce between GND and Vcc

    Yes. At the point they are happening. But depending on the momentary contact resistance and the line capacitances etc, the resulting signal at the MSP pin might be something completely different.
    That's the fun with hardware - it's never digial 1 or 0, it's somewhere in between and sometimes beyond. Only users and HF are funnier.

**Attention** This is a public forum