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.

MSP430F5529 Interrupt Question

Other Parts Discussed in Thread: MSP430F5529

Hello,

I am relatively new to the MSP430 series and am currently using the F5529 chip. I just getting my feet wet with some really basic code, right now I can successfully fire the timerA interrupt and the ADC12 interrupt separately but when I try to combine the code neither of the interrupts work. I was wondering if it is simply impossible to join them (although I can't see why you wouldn't be able to) or if I have some basic flaw in my code. Please note that I am a beginner so there may be a very simplistic and basic explanation for this. I will post my code below, much of which is basically copied more or less directly from the resource manager sample code.

<code>

/*Basic sandbox experimentation */

//List of include files
#include <msp430f5529.h>

//Declare local sub-routines and functions
void init_Pins(void);
void init_WD_timer(void);
void init_Timer(void);
void init_ADC(void);

//Declare global variables
volatile int i; //used in the timer A0 interrupt service routine

//Main program
void main(void) {

//Initialization sub-routines
init_WD_timer();
init_Pins();
init_Timer();
init_ADC();

while(1){

ADC12CTL0 |= ADC12SC; // Start sampling/conversion

// Enter low power mode, enable interrupts
__bis_SR_register(LPM0_bits + GIE);
__no_operation(); // For debugger
}
}

//////////////////////////////////////
//INTERNAL FUNCTIONS
//////////////////////////////////////

void init_ADC(void){
ADC12CTL0 = ADC12SHT02 + ADC12ON; // 64 CLK cycles sampling time, ADC12 on
ADC12CTL1 = ADC12SHP; // SAMPCON is sourced from sampling timer
ADC12IE = 0x01; // Enable interrupt on ADC12IE0
ADC12CTL0 |= ADC12ENC; // Enable a conversion

P6SEL |= 0x01; //Enable P6.0 as ADC input (p93, 5529 datasheet)
}

void init_Timer(void){
TA0CCTL0 = CCIE; //enable interrupt
TA0CCR0 = 50000; //set timer counter to 50000 (16-bit, max is 65536)
TA0CTL = TASSEL_2 + MC_1 + TACLR; //Using SMCLK, use upmode and reset the clock divider
}

void init_WD_timer(void){
//Stop watchdog timer
WDTCTL = WDTPW + WDTHOLD;
}

void init_Pins(void){
P1OUT = 0x00;
P1DIR = 0xFF;
P2OUT = 0x00;
P2DIR = 0xFF;
P3OUT = 0x00;
P3DIR = 0xFF;
P4OUT = 0x00;
P4DIR = 0xFF;
P5OUT = 0x00;
P5DIR = 0xFF;
P6OUT = 0x00;
P6DIR = 0xFE;
P7OUT = 0x00;
P7DIR = 0xFF;
P8OUT = 0x00;
P8DIR = 0xFF;
}

//////////////////////////////////////
//INTERRUPT SERVICE ROUTINES
//////////////////////////////////////

//ADC12 interrupt service routine
#pragma vector = ADC12_VECTOR
__interrupt void ADC12_ISR(void)
{
switch(__even_in_range(ADC12IV,34))
{
case 0: break; // Vector 0: No interrupt
case 2: break; // Vector 2: ADC overflow
case 4: break; // Vector 4: ADC timing overflow
case 6: // Vector 6: ADC12IFG0
if (ADC12MEM0 >= 0x7ff) // ADC12MEM = A0 > 0.5AVcc?
P1OUT |= BIT0; // P1.0 = 1
else
P1OUT &= ~BIT0; // P1.0 = 0

__bic_SR_register_on_exit(LPM0_bits); // Exit active CPU
case 8: break; // Vector 8: ADC12IFG1
case 10: break; // Vector 10: ADC12IFG2
case 12: break; // Vector 12: ADC12IFG3
case 14: break; // Vector 14: ADC12IFG4
case 16: break; // Vector 16: ADC12IFG5
case 18: break; // Vector 18: ADC12IFG6
case 20: break; // Vector 20: ADC12IFG7
case 22: break; // Vector 22: ADC12IFG8
case 24: break; // Vector 24: ADC12IFG9
case 26: break; // Vector 26: ADC12IFG10
case 28: break; // Vector 28: ADC12IFG11
case 30: break; // Vector 30: ADC12IFG12
case 32: break; // Vector 32: ADC12IFG13
case 34: break; // Vector 34: ADC12IFG14
default: break;
}
}


// Timer0 A0 interrupt service routine
#pragma vector=TIMER0_A0_VECTOR
__interrupt void TIMER0_A0_ISR(void)
{
i++;
if(i == 10){
P1OUT ^= BIT0; // Toggle P1.0
i = 0;
}
}

</code>

  • We found the solution and thought we'd share. Basically we took the low power mode option out of _bis_SR_register (so now it only turns on global interrupts) and everything works fine. We're still trying to get a better handle on what that line of code really does as there doesn't seem to be a great explanation out there. If anybody could either point us to a decent piece of literature (we haven't found a good one that describes what that does yet) or explain it to us yourself that would be tremendously appreciated.

    Thanks!

  • brendon sauer said:
    Basically we took the low power mode option out of _bis_SR_register (so now it only turns on global interrupts) and everything works fine.

    Rather not (see below)

    brendon sauer said:
    We're still trying to get a better handle on what that line of code really does as there doesn't seem to be a great explanation out there

    Oh, it's there, but you'll have to collect the data from slightly different locatiosn of the users guide.

    Basically, setting GIE bit in the processor status register allows interrupts to happen globally. If this bit is clear (and it is clear after reset), no ISR will be called even though the proper IE bits are set.

    Setting the LPM0_Bits simply sets the CPUOFF bit in the status register. (you search for th edefines in the header files). This stops MCLK and therefore the CPU freezes instantly.
    When an interrupt happens (and GIE is set!), the current status register (with GIE and CPUOFF set) is saved on stack and both bits are cleared, so the CPU can continue and handle the interrupt, and no other interrupt can interrupt the execution of this ISR. When the ISR exits, the saved status register content is restored and this instantly switches the CPU off again and enables interrupts.

    The meaning of the status register bits is explaine dint eh chapter of teh users guide abotu the status register and abotu low power modes.

    Using the _bic_SR_register_on_exit intrinsic (intrinsics are compiler-specific pseudo-functions which do things that are outside the scope of the C language, such as accessign the status register) generates code in the ISR that alters the saved copy of the status register on the stack, so on ISR exit, the CPUOFF bit is not restored and the CPU continues executing the main code.

    Now this isn't your problem. Removing the _bis_SR line causes the main loop to continue while no conversion has been finished yet. But wit will start the next conversion and the next while the ADC hasn't yet finished one.

    But there are other problems with your code.

    You don't set the ADC12MCTL0 register that contains information abotu what to do. This register is undefined at power-on (even though it usually is 0x00) and may convert a totally different input channel.

    Youre case 6 in the ADC ISR has no break, so it falls through to case 8.

    Your timer ISR toggles the same port pin as your ADC ISR. o teh ADC ISR, which is triggered much more often than the relatively slow timer ISR (ADC interrutp frequency ~ 10kHz or more, timer 20Hz) so it will override the blnking of the timer ISR by its voltage-dependent static display. Only a scope will reveal the short blips of the output (which I assume is connected to an LED)

  • Thanks a lot for the reply; it helped explain quite a few things.

    There were a few things we were aware of already but others were fixed accordingly. We are still getting our heads wrapped around the best way to handle multiple interrupts and how to prioritize them. We were thinking of switching to an RTOS but obviously there will be a steeper learning curve there (or at least I am assuming so). Incidentally the code shown above is more or less copied from the compiler examples provided by TI. We did want the code blinking the same port (it is indeed an LED) so that was on purpose, and not initializing the ADC12MCTL0 register was on purpose too; the default setting is exactly what we want and we checked the register during debugging and it is good to go.

    The part that definitely helped and something that we are working at was "Now this isn't your problem. Removing the _bis_SR line causes the main loop to continue while no conversion has been finished yet. But wit will start the next conversion and the next while the ADC hasn't yet finished one". This was an issue we faced last time around with a different line of MCUs so we will definitely be looking more closely at that.

    Thanks again, we really appreciate it :)

     

  • brendon sauer said:
    not initializing the ADC12MCTL0 register was on purpose too; the default setting is exactly what we want

    But there is no default setting., The content of this register after power-up is undefined (see users guide) and only coincidentally matches your requirements for this specific MSP silicon revision, or just for this one MSP.

    Interrupts are prioritizing interrupts is hardwired by the MSp hardware. The order is listed in teh device datasheet. However, once an ISR has been entered, it won't be interrupted until it exits. Unless you manually re-enable interrupts. Then it can be interrupted by any otehr, even lower priority interrupt, including itself (whcih can lead to massive problems, so nesting interrupt sis strongly discouraged).

    No RTOS will help you there. An RTOS will only add complexity, latency and code overhead. But it might be convenient to use if you have more processing power and memory than you need.

    brendon sauer said:
    Incidentally the code shown above is more or less copied from the compiler examples provided by TI.

    The code in the examples only works as-is for the exact processor and purpose it was written for. You cannot simply copy two demo codes together and expect them still working. These examples ar eexamples for one specific situation and not meant as a code library to build your application with.

    You need to understand how the examples work (with the help of the users guide and the datasheet) in every detail, and then write your own code based on this understanding. As long as you're unsure of the meaning of one signle line in the example, it is dangerous to just copy it.

    brendon sauer said:
    We did want the code blinking the same port (it is indeed an LED) so that was on purpose

    This is usually fine - if you thought about all consequences.
    In your situation, assuming 1MHz CPU speed, a conversion takes 77 MODOSC cycles = ~13 µs. Then the ISR is started. It takes about 30µs to execute, another 8 for the loop, it sums up to ~50µs per conversion (20kHz). So every 50µs the ADC ISR will put the LED to its output value. If the timer ISR toggles the pin every 50ms, this will be undone by the ADC ISR 50µs or less later. Not noticeable with the naked eye. Only a scope could tell.

  • Michael,

    I don't know when you eat or sleep but thank you from the rest of us.

     

    This may be off topic but I am sure that I am not the only one with this question. I have also posted it on the Forum.

    I am looking for any example code using the MSP430F5529 Real Time Clock Calendar in the Calendar mode. The link to an example that Katie provided failed.

    I will figure it out but I only have a couple of days to finish a project and any help would be greatly appreciated.

    Mark

  • Mark MacNab said:
    I don't know when you eat or sleep but thank you from the rest of us.

    I don't know either, but thank you :)

    About the demo code, teh 5x family driver lib should contain functions for the RTC, you can take a look at the source code.

**Attention** This is a public forum