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.

LPM not working with PWM

Other Parts Discussed in Thread: MSP430FR5725, MSP430G2452

That title seems to suggest that it is the mcu's fault, but really I can't figure out a certain part.  Right now, I'm using the G2553 on a launchpad, controlling a separate IC.  By default, I'm putting the IC into a power down mode via a PDN pin.  I am controlling this pin from the microcontroller using the timer's PWM functionality, so I can save power.  I have port interrupts set up to listen for a signal from the IC when it is on (when the PWM is high).  If the port interrupt is triggered, the IC will remain on for 5 seconds.  After the 5 seconds, the system will return to its PWM sleep state.  My problem is that my system is not entering LPM.  The pin I'm controlling the IC with seems to always be powering it.  My "wakeup" and timeout works.  I am using two external LEDs to see which state it is in (LPM or timeout).  I've already taken current measurements without them, so they're not causing my unexpected current draw.  My code is linked below.

http://pastebin.com/raw.php?i=b3xWGaMT

  • Hi, I'm new to MSP430 but am also looking at G2553 with PWM so your post caught my eye.  This probably won't help much though...

    As I understand the interrupt and operating mode relationship, when an interrupt occurs the micro automatically goes into 'active' mode (CPU enabled) and copies the status register, containing the operating mode prior to this e.g. LPM3, to the stack.  When exiting the interrupt routine, it pops the previously-saved status register from the stack and puts it back in the status register, thereby switching operating mode automatically to what it was before the interrupt routine (unless you manipulate it manually - but I think you have to manipulate the copy in the stack, i.e. manipulate the stack's copy itself since what's in the stack will be put back on exit).

    In your interrupt routine for Port1, you have the instruction _BIC_SR(LPM1_EXIT); I have no idea what this does (I've put a post myself asking if there's any documentation on the provided functions/macros) but it would seem to me that once the interrupt routine is running, the micro is already in 'active' mode and this change to operating mode should not be necessary.  Presumably you want the interrupt routine to execute quickly with the CPU running, which is handled automatically for you.

    Would it not make more sense for the 'main' routine to enter one of the low power modes (whichever you need) and then simply stay in that low power mode for ever, allowing the interrupt routines to activate/deactivate the CPU whenever they need to?  Assuming the low power mode you use keeps the PWM running.

    As I said, this probably doesn't help much, in which case apologies.

  • Tony Payn said:
    As I understand the interrupt and operating mode relationship,

    totally correct.

    Tony Payn said:
    In your interrupt routine for Port1, you have the instruction _BIC_SR(LPM1_EXIT); I have no idea what this does

    Well, BIC_SR clears bits int he status register. LPM_EXIT, however, is a pseudo-function, acutally an alias for  _bic_SR_on_exit(LPM1_bits). It clears teh LPM1_bits from the copy of the status register on the stack. However, it returns a value whose bits are cleared from the real status register.. Well, I don't think that it does any harm but is surely not what was intended. Best case it serves no purpose to frame the _BIC_SR around the LPM1_EXIT.

    Tony Payn said:
    Would it not make more sense for the 'main' routine to enter one of the low power modes (whichever you need) and then simply stay in that low power mode for ever, allowing the interrupt routines to activate/deactivate the CPU whenever they need to?

    It depends on what is to do. An ISR cannot itnerrupt an ISR. So if you have multiple ISRs, you cannot stay insid ethe ISR for long. Jsut doe what is needed immediately, then wake the main thread, pass it a flag (though a volatile global variable) and let it do the more time-consuming stuff before it enters LPM again. This way, the next event can still be handled by the ISRs immediately while main is still busy.

    Now to the original problem:

    you try to enter LPM with

    _BIS_SR(LPM1);
    However, PM1 is a _BIS_SR(LPM1_bits+GIE) operation by itself. Either use "_BIS_SR(LPM1_bits+GIE);" or use "LPM1;"

    Next problem is that you use awake to signnal between ISR and main. However, the compiler doesn't know about LPMs or interrupts. It sees and compiles the code flow as you wrote it and doesn't know that awake may suddenly change its value without any apparent reason. So it may optimize it by putting a copy of it into a register. Which will ofcourse not change when the ISR changes awake.
    ALL variables that are used in main as well as inside an ISR must be declared volatile. So the compiler will not make any assumptions about them and not optimize them.

    Finally, are you sure that you button doesn't bounce? Bouncing will create multiple successive port interrupts which may confuse your program logic.

  • I've changed my LPM statement to the correct syntax.  Thanks for pointing that out.  I'd like to point out something from another code I have.  After I enter LPM1 with "LPM1;", my variables turn to this: http://i.imgur.com/b2U5536.png

    Once I enter my timer interrupt, however, they restore back to their default values.  Do you know why it would do that?  This is when I'm stepping through my code.  When I run it, then pause it, I receive crazy values like those listed in the picture.  I should only be expecting 1s and 0s and some ADC values.

  • I also meant to say that when I measure the current draw of my system, it doesn't change when stepping through "LPM1;".  It doesn't appear to be entering a low power mode at all.

  • Jens-Michael Gross said:
    An ISR cannot itnerrupt an ISR.

    Disagree. If you enable GIE in the ISR then other IRQ can interrupt ISR you are in. This is advanced approach meaning you shall really understand consequences, but sometimes it makes sense to avoid unnecessary overhead of signalling flag processing thus simplifying and performance-improving code.

  • Have you set all unused pins as outputs, or put pull-ups/downs on them?  If you leave them as inputs and they are unconnected (as are most on Launchpad) then due to their very high impedance they can switch spuriously due to electrostatic/electromagnetic influences in the air around them.  This can push up the current consumption of the IC.  Previously when I used a Microchip part, I was not aware of this but realised how necessary it was and after implementing, the current dropped from around 180 microamps to about 10.  Could it be that this is masking the reduction in power when switching to low power operating mode?

    I've had some weird values out of the debugger too.  I'm not sure what the variables you show represent though.  I found the values in the TAR timer count register were plainly wrong but came to the conclusion that this was due to the debugger not halting the timer counting procedure when halting program execution.  I guess this depends on how the microcontroller clocks when 'linked' to the debugger for a data transfer session - presumably something has to still run inside the microcontroller to be able to put the data out.

    Jens-Michael Gross makes a good point about the switch debounce too. And I certainly learned a lot about _BIS-SR() instructions from the information he provided - thanks for this.

    I think the point about ISRs not interrupting other ISRs means to say that without special measures (like coding specially for nesting) one ISR will not interrupt another.  It seems to be a strength of this microcontroller architecture that there is good flexibility for controlling ISR behaviour.

  • Ilmars said:
    Disagree. If you enable GIE in the ISR

    Then you forfeit its state as ISR. If you don't know what you're doing, the ISR can even interrupt itself. And will be interrupted by lower priority interrupts.

    Yes, nested interrutps are not impossible, but properly handling them is not easy at all.

    However, except for very few cases, the purpose of an ISR is that it handles an event as fast as possible, while being interrupted (especially if you cannot set priorities) durign this tasks renders the implementation as ISR useless. You could be use polling in main instead. Or use the ISR to wake main, and handle the event in main then, if you need the ISR functionality to exit LPM.

    Indeed it sometimes seems mroe performant to spare signalling, but you pay for it by decreased stability and increased chance of racing conditions or even lockups.

    The one and only situation where I really needed interrupt nesting so far was the ISR for my multitasking scheduler. Which goes itself into LPM if no thread is active, and needs to be awakened by the timer. In any other case it made much more trouble than it was worth.

  • Regarding my current draw not changing:  I was using the PWM functionality of the timer.  I had a 1% duty cycle (500us ON, 50ms OFF).  All that I am doing in the ISR is toggling an enable pin for an IC.  When I changed the duty cycle to 50%, I observed no change in the current draw.  I am using the MSP430FR5725.  I do not have the enable pin assigned to a timer pin on the MCU.   I've added my timer init and ISR below.

    void pwm_init(void)
    {
    //PWM sleep mode, 50ms period
    TA1CCR0 = 50000;
    //1% duty cycle, HI 500us
    TA1CCR1 = 500;
    //SMCLK, div 1, UP mode, clear timer
    TA1CTL |= TASSEL_2 + ID_0 + MC_1 + TACLR;
    //out = rst/set
    TA1CCTL0 |= OUTMOD_7;
    }
    
    
    #pragma vector = TIMER1_A0_VECTOR
    __interrupt void TA1_CCR0_ISR(void)
    {
    //Toggle receiver
    PJOUT_PDN ^= RX_PDN;
    }
  • If the ISR is controlling the external device enable pin by toggling it, then why do you need PWM mode?  PWM mode is normally used to control a pin on/off with predictable duty cycle without intervention from CPU.  You could use just timer function - you can still use all three capture/compare units.

    In UP mode, TIMER1_A0_VECTOR is called every time the count reaches TA1CCR0.  If the count goes from TA1CCR0 to zero (= TA1IFG), or if the count reaches TA1CCR1 or TA1CCR2, then it's not TIMER1_A0_VECTOR ISR that gets called, it's TIMER1_A1_VECTOR.ISR (if enabled and set up).  It seems to me that when your count reaches 500, nothing will happen if you don't have a TIMER1_A1_VECTOR ISR to do something.

    Would it not make more sense to use TIMER1_A1_VECTOR ISR?  You could check inside the ISR to see whether the event was TA1IFG (count got to zero again - one count more than TA1CCR0), or TA1CCR1 (500 us) and then do the necessary.  You could dispense with TIMER1_A0_VECTOR altogether.

    Your TIMER1_A0_VECTOR is being called every 50 ms and toggling the pin - that's all that's happening.  Changing the duty cycle in TA1CCR1 probably isn't doing anything.

     

  • That makes sense why my PWM was useless.  Basically, I was using the PWM so I could reduce my current consumption.  I only wanted the IC on for 500us (CCR1) every 50ms (CCR0).  Within that 500us, I call an ADC interrupt.  If an analog value reaches a certain point, I will wake up the entire system; if not, continue cycling.

    If I did this with just the A1 ISR, how would I make sure that I'm only enabling for CCR1 and below?  Once the count reaches CCR1, I would like it to enter a low power mode.  Then when it goes from CCR0 to 0, enable the IC, then repeat.

    I don't mean to explicitly ask for answers, I'm just having a hard time with this part.  I have a substantial program written.  This is the only problem I'm having.

  • I tried using the A0 ISR, with CCR0 = 500 (500us).  In the ISR, I had a count variable increment.  When the count reached 100, I enabled the IC.  Is there a more elegant method to do this?  My thought process was that since I wanted the IC enabled for only 500us every 50ms, on the 100th count the IC would then only be active for 500us (because of CCR0).

    100 counts * 500us = 50ms

  • I found it very hard to get to grips with this range of microcontrollers.  The User Guide and Data Sheets are rather confusing.  I got mine working however, and am sure you will too.  I am a long way short of expert (or even proficient) but my advice would be as follows.

    Firstly, decide the requirements for detecting activity on the other chip when the MSP430 wakes it up,   You say you'll use A to D conversions to see if that other chip is active.  How long is needed after the chip is woken up, before it might show something useful on the ADC?  There's no point in doing an ADC before this point.  How many ADCs do you need to do to determine if it is awake or not?

    If, for example, the other chip will not reveal it's condition until (say) 150 us after wakeup, and the ADC takes (say) 50 us from start-of-conversion to delivery of result (with interrupt if setup), you don't need to trigger the start of ADC until 100 us after wakeup.

    Now you have a number of possible strategies to follow.  It seems low current is important to this application.  What you could do, for example, is:

    Set TA1CCR1 for a count of 500 us.

    Set TA1CCR2 for a count of the delay time between wake up and start of ADC (e.g. in above example, this would be a count of 100).

    Set TA1CCR0 for 500 ms as you have now.

    What you have now defined is: a moment when the output will turn on the other chip (timer count = 0), a moment when the ADC will be started (timer count = 100 us) and a moment when the other chip will be put back to sleep (timer count = 500 us)  - as long as the other chip didn't show activity, in which case you take a special action (whatever that might be).

    You don't really need to use TIMER1_A0_VECTOR at all.

    Setup TIMER1_A1_VECTOR so that, when it triggers, you check the value of the interrupt flag register to find out what caused the interrupt.  See the user guide for Timer A register TAIV, to understand the 'switch' values below.  Something like:

    TIMER1_A1_VECTOR (etc)

    {

      switch (TA1IV)            //this checks the value of TAIV for Timer 1.  (Note there's a '1' between the 'A' and the 'I' as it's Timer 1.)

     {

      case 0x02:        //CCR1 is the cause of the interrupt, i.e. the other chip has been awake for 500 us.   Turn it off - unless it's meant to be on

        if(ADC_result == NoActivityOnOtherChip)   //or any other flag that might indicate that activity is 'dead', set in ADC ISR)

        {

             turn other chip off

        }

       else

       {

         keep it awake and do other things.  Remember to stop the timer interrupts that are no longer needed, and to restart everything from scratch when done.

       }

      break;

    case 0x04:        //CCR2 is the cause of the interrupt, i.e. it's time to start an ADC.

        start_ADC();

       go to LP mode if ADC can run during this

    break;

    case 0x0A:      //Timer overflowed, i.e. the start of the on period for the other chip

       turn on other chip

       go back to LP mode

    break;

    } //end of TIMER1_AI_VECTOR

    When you start the ADC (case 0x04), you can go back to a low power mode if the ADC continues to run.  Configure the ADC so that when the ADC completes, it wakes up (if multiple conversions are needed) and, in the ADC interrupt routine, it sorts out the status - is the other chip active or not?  If only one conversion is needed, and this is completed while in a low-power mode, you could just leave the result in the result register and 'nab' the result in the TIMER1_A1_VECTOR when it's the right moment (case 0x02)..

     As you will see, this doesn't really require PWM mode, it's just using the timer.  The PWM makes use of the timer, which runs in the same low power modes as the PWM is operational, so the current will be minimised just the same.  The power of the PWM comes when you configure the "output units" to automatically switch the output pin while still in low power mode - no CPU intervention.  You can't really take advantage of this since you need to trigger ADC starts and to evaluate the results, which needs CPU intervention.

    You could use the PWM tied to an output pin to wake up the other chip and to turn it off, but if you stayed in a low power mode you would lose the facility to determine if the other chip was active or not.  So you'd need the CPU to run for this purpose.  I doubt if you'd save much (current-wise) doing things this way and it would probably be a lot more complicated in terms of software decision-making.

    I hope it works for you, do send a post to let us all know how you get on.

  • Tony Payn said:
    I found it very hard to get to grips with this range of microcontrollers.  The User Guide and Data Sheets are rather confusing.

    Mainly because the combination of datasheet, users guide and errata sheet is uncommon. However, for migrating between different parts within the MSP family, this combination of device-specific information and family-common information is by far better than individual documents for each single MSP430 device (of which there are >400 now)

    The main problem is that this concept isn't explained in a 'how to use the documentation' document. :)
    In fact, I dedicated a whole chapter of my MSP book (which will hopefully be finished before I get too old to type) to the concept of the documentation and how to use it.

  • Thanks for the suggestion.

    Here is the ISR I tried out. http://pastebin.com/raw.php?i=gSRBU7hs

    I included my initialization of the timer, where I call it in main, the timer ISR, and the ADC ISR.  My ADC ISR is a sequence of channels read.  It works fine.  I'm saving the values where I want and when I want.  For the timer, though, I seem to only be acting on the CCR1 trigger.  Whatever is stated in my CCR1 if-statement stays for the entirety.  I will continue testing.

  • Looks like you're getting closer to a working solution.

    Remember that the interrupt which fires when the timer reaches the TA1CCR0 setting (count 5000 = 50 ms) is TA1IFG and that this needs to be specifically enabled.  It's TAIE in the TA1CCTL register.  Your setting up of TA1CCTL register doesn't specifically set this, as you have:

    TA1CTL |= TASSEL_2 + ID_0 + MC_1 + TACLR;

    You could try enabling the interrupt by changing this line to:

    TA1CTL |= TASSEL_2 + ID_0 + MC_1 + TACLR + TAIE;

    Without this, when the timer reaches the count of 5000, it will not cause the interrupt service routine TIMER1_A1_VECTOR to be called.

    Alternatively you could enable this interrupt as part of the 50 us interrupt routine, but there's not much point in continually changing it - you could enable it once and be done.

    In your TIMER1_A1_VECTOR interrupt service routine, you are manually clearing the interrupt bits (TA1IV &= ~0x02;  and TA1IV &= ~0x0E; ).  This is not necessary as any access, read or write of the TA1IV will clear the highest pending interrupt without you having to do it.  When your statement

    if(TA1IV == 0x02){...

    executes, this is necessitating a read of the TA1IV register which thus resets the highest pending interrupt (there is an order of priority associated with the triggers that can cause this ISR to fire - highest TA1CCR1 CCIFG, then TA1CCR2 CCIFG which you aren't using, then TA1IFG).  If your TA1CCR1 causes the interrupt, on checking TA1IV to see what the cause was, you will automatically reset it.  If TA1IFG caused the interrupt, providing TA1CCR1 CCIFG flag is not set (but how could it be, since this interrupt triggered at 50 us and was reset?), then TA1IFG is the highest pending interrupt and will be reset when you read TA1IV to check the source of the interrupt.  So you can delete the lines that manually reset the interrupt flags.

    You also have the line:

    TA1CCTL1 |= OUTMOD_7;   //CCR1, out = rst/set

    As you are doing everything 'manually' in the ISRs, you perhaps don't need this line.  It is useful if you are making an output pin go high and low at the times you want, but this isn't necessary for getting the operation you want.  It can be useful if you are using an oscilloscope to check on timing activity though, of for some other purpose.

    One other comment - although I don't pretend to understand exactly how your application works - in your TIMER1_A1_VECTOR ISR, you enable the ADC and start a conversion - presumably reading the other chip's activity line.  In the next instruction you turn the other chip off - have you left it on for long enough?  The ADC will need a specified time to sample and hold the voltage on the activity pin - specified in the data sheet. In the instruction after that, you make use of the ADC result by "if((rssi_result >= 500) && ..." (still within the TIMER1_A1_VECTOR ISR) - presumably this is the result of a previous conversion, since there will not have been enough time for the ADC to complete the just-started conversion and to manipulate the result (noting also that this cannot happen unless the ADC10_VECTOR is allowed to run, which must be after the TIMER1_A1_VECTOR has completed and the ADC has completed its conversion).

     

     

     

  • Tony Payn said:
    Remember that the interrupt which fires when the timer reaches the TA1CCR0 setting (count 5000 = 50 ms) is TA1IFG

    Actually not. TA1IFG is triggered when the timer rolls over to 0. THis may happen one timer tick after the tiemr reached CCR0 in up mode, or one timer tick after the timer reached 65535 in cont mode.

    The interrupt triggered when the timer reaches CCR0 is the CCR0.CCIE inetrrupt, which has its own interrupt vector (and auto-cleas its CCIFG bit when the ISR is entered).
    Note that TIMER1_A1_VECTOR ISR is never entered due to CCR0 interrupt. It is entered for TAIE interrupt as well as for CCRx.CCIE interrupt for x != 0.

    Tony Payn said:
    This is not necessary as any access, read or write of the TA1IV will clear the highest pending interrupt without you having to do it. 

    Actually a write will clear ALL pending interrupts. Whiel a read only clears the one that was just reported in this read.

    Tony Payn said:
    The ADC will need a specified time to sample and hold the voltage on the activity pin - specified in the data sheet.

    Not in the datasheet. Specified by either the SSHI signal (from ADC10SC bit or timer) or, when SHP is set, by the SHTx bits and the current ADC10 clock speed. Well, this is the time it takes for sampling and during which the input needs to be stable. The time it needs for sampling the input depends on the sampling capacitor size (see users guide, some pF), teh input switch series resistance (~1K IIRC, also in the users guide) and the input signals output impedance.

    But you're right about the timeline problems and the fact that during timer ISR execution, the ADC10 ISR won't execute.

  • Thanks Jens-Michael for pointing these out.  I am a beginner with MSP430 and am learning - it just so happens I used the TimerA and went through many of the same frustrations that the original poster seems to have experienced, so thought I might be able to help (as there were, at that time, no other responses).

    I'd overlooked the point about the TA1IFG being set on roll-over to zero but had noted this in an earlier post to this question.  I was referring to the interrupt TA1IFG flag being associated with the value in the TA1CCR0 register in up mode (as opposed to the interrupt flags associated with the value in the TA1CCR1 or 2 registers).  Nevertheless I was imprecise.

    Your point about the write to TA1IV clearing all pending interrupts is interesting.  I checked the User Guide and it says "Any access, read or write, of the TA1IV register automatically resets the highest pending interrupt flag.  If another interrupt flag is set, another interrupt is immediately generated after servicing the initial interrupt."  This suggests that a write clears only the highest pending interrupt flag, leaving other interrupt flag(s) (if any) set.  Perhaps the User Guide is not too 'clear' about this point.

    You mentioned in an earlier response that you were planning a book - great idea.  I had previously thought that a more consolidated explanation was needed, and had wondered whether I should write one myself, but it would take me years to acquire the necessary expertise.  And I noted also your suggestion to read the User Guide and Data Sheet from start to finish - both of which I had done (but only for the peripherals I was planning to use as well as the common parts - CPU etc).  No matter how many times I read some sections, it made no sense at all and was really frustrating flipping from one publication to another.  The only way to truly understand it, in the face of fragmentary and non-cohesive documentation, seemed to be to try it out and debug it - at least, this was the only approach I could think of.  I got there in the end and perhaps gained a better understanding of how it works as a result - it did seem like an uphill struggle at times though, and I thought more than a few times of giving up and going to another microcontroller family (PIC or ATmega).  I think the MSP430 documentation combined with the ensuing struggle for beginners could put a lot of people off using this line of microcontrollers.

    I'm just going to find out how to use the comparator now...

  • Tony Payn said:
    Your point about the write to TA1IV clearing all pending interrupts is interesting.  I checked the User Guide and it says "Any access, read or write, of the TA1IV register automatically resets the highest pending interrupt flag.

    You're right.

    I must admit that I used/tested this feature only on the system interrupt vectors (SYSUNIV, SYSSNIV, SYSRSTIV) . There a write indeed clears all 'pending' bits. However, these registers are rather reporting past events tht holding a list of pending itnerrupts (uncleared bits won't cause an NMI or reset). So the difference in the handling is explainable.

    My personal opinion is that the oteh rIV registers should behave the same way. There is no sense in writing to the IV register except if you intentionally want to clear ALL pending interrupts. If you loop unitl it returns 0, you need to do a read anyway. So the write makes no sense if it is indeed implemented to only clear one. However, I never tried myself on th4 timer IV or port pin IV registers.Perhaps it is a msitake in the users guide and propagated by copy/paste. Or wasted silicon.

    Thanks for brining this discrepancy to my attention. I'll check it myself once I find the time.

    Tony Payn said:
    You mentioned in an earlier response that you were planning a book

    I already started it. But after the first three chapters, work stalled. Well, I'm sure I'll continue soon. But I have no idea when it will be finished. If ever.

    Tony Payn said:
    I noted also your suggestion to read the User Guide and Data Sheet from start to finish - both of which I had done (but only for the peripherals I was planning to use as well as the common parts - CPU etc).

    This is the problem: if you leave out peripherals you don't think you will use, you will miss relations between them which are only mentioned in the chapter you skipped reading. You only know that you don'T need something in the guide or datasheet after you read it.

    A big part of my experience comes from the fact that I used almost every feature on out (still) main processor, the 1611, so I had to read everything in the users guide and datasheet. And noticed crossovers and other implications that were not visible when focusing only on one problem, ignoring the rest.
    Would you imagine that the timer PWM functionality could be extremely useful when doing ADC operations? Or that (and how) you can measure digital I/O current with the ADC if you didn't look at the port pin schematics in the datasheet?
    Still, some details (like the IV register anomaly above) slipped my attention.

    Tony Payn said:
    I think the MSP430 documentation combined with the ensuing struggle for beginners could put a lot of people off using this line of microcontrollers.

    Indeed, it is not easy, especially since there is no guide on how to use it. However, this kind of doicumentation makes it simple to switch from one MSP to another. On other microcontroller series, you start from scratch and can't ever figure out any detail differences between the parts, without comparing two independent documentation detail by detail. On most other sytems, you start from scratch if you encounter that your current device won't be enough for the next project. On MSP430, once you materes one, you mastered all.

  • So, I got my timer ISR pretty much working.  I just used CCR0 and did some manipulation.  I changed it to ON for 50ms, OFF for 450ms.

    http://pastebin.com/raw.php?i=GMi8feGs

    I did have a question in regards to doing an ADC read.  After consulting the data sheet of the receiver (the external IC I am enabling/disabling), its response time from waking up from software, as well as outputting a stable signal strength voltage (what I'm measuring at A14 that wakes up my system) should only take ~120us.  However, with the way I have my ADC and timer combination set up, even 50/450 isn't as responsive as I'd expect.  Sometimes, the ADC takes a few tries until it accurately reads the voltage.

    Does this have anything to do with the sample timing?  I saw in the User's Guide (MSP430FR5725) about the different sampling modes (ADC10SHP).  I tried testing with that, as well as the sample hold time (ADC10SHT), but it messed up the ADC ISR (measured analog values were attributed to the incorrect pin).

  • I haven't used the ADC peripheral on the MSP430 yet, although this is my next task for my own application.  So I can't offer any constructive comments until early next week.  Hopefully someone else more knowledgeable can help in the meantime.

    In my experience with other microcontrollers, provided the required times are allowed for, the very first conversion should be accurate.  That's assuming that the signal being converted remains at a steady voltage while it is being sampled.  The way these usually work (although it may be different for MSP430) is that the applied voltage (in your case, from the RSSI on your receiver) is routed to an internal capacitor within the micro.  When you select that pin (channel), the capacitor inside the micro is connected to the pin where it charges up (or down, if the voltage on the capacitor was higher due to a previous conversion) to the same voltage as is presented on the pin by the RSSI signal.  The purpose of this is to 'store' the voltage on the capacitor, so that a snapshot of the voltage is recorded - it may be changing e.g. if it were an audio signal.  Then the capacitor is disconnected from the pin, so that it doesn't change the voltage on the capacitor if the signal changes.  Now the micro's analogue converter can get to work on the stored signal, working out what the digital value of the voltage is.  This is often done by successive approximation, taking a number of clock cycles.  Usually you set the clock speed of the ADC so that it has time to correctly discriminate the digital value.  Because it is essentially an analogue process, if it runs too fast it may not get the measurement right.

    You need to make sure that the channel (pin) is routed to the ADC and that sufficient time is allowed for the voltage on the capacitor to charge to the correct voltage.  The required time is dependent on the size of the capacitor (fixed in the chip) and on the resistance present between the capacitor and the signal source.  Large resistances mean it will charge slower.  Some resistance is present inside the micro, some inside the receiver and you may have some external resistor(s) between the two.  You need to make sure the total resistance is not greater than permissible for this converter, otherwise you may not get correct results initially.  Subsequent conversions (particularly if the sensed voltage remains constant) will allow the capacitor to get nearer with each conversion to the correct voltage.

    If the voltage is essentially constant, you should find that one accurate conversion gives the correct result.  The advantage of doing more than one (several) is that you can average them and this will help to improve the accuracy if there is any noise present, which may cause an individual conversion to be 'out' by a few bits.

    In your timer ISR, you enable the ADC and start the conversion at the same moment.  I would be tempted to check the literature to see if there needs to be a time allowed between enabling the ADC and starting the conversion.  Your timer ISR does this when pwm_count equals 47, 48, 49 and 50.  The first conversion (the one started at pwm_count = 47) causes the ADC ISR to start and trigger a run of conversions (16?) in addition to the ones triggered by the timer ISR.  I'd be inclined to check that the timer ISR isn't starting an ADC while the ADC ISR is part-way through the run of 16.  That depends on ADC clock speed, which I haven't looked at in your code yet.

    That probably doesn't help much.  If I have any realisations when I study/use the ADC I'll post a comment but hopefully someone else will have helped in the meantime.

  • Tony Payn said:
    In my experience with other microcontrollers, provided the required times are allowed for, the very first conversion should be accurate.

    Indeed, it should. However, there are some possible reasons why not.
    In case of a Delta-Sigma converter, there is no samping capacitor. isntead, the input voltage is compared to an internal voltage and the comparaotr throws a '0' or '1' bit into a digital filter. Then the intrnal voltage is raised or lowered based on teh comparison results. After an unlimited number of cycles, teh numbe rof '1' bits compared to the number of all generated bits is a 100% representation of the input voltage (assuming it stable). Now nobody wants to wait eternally. So the digital filter takes the last n incoming bits and generates an output value. The more bits taken, the more precise the output. (called oversampling ratio). After switching the ADC on (ow changing the channel), the digital filter needs to get a certain amount of bits to produce a valid result. In case of the SD16 module, it is four times the oversampling ratio. So the first three conversions after any interruption of a continuous sampling of th esame channel are incorrect.

    In case of the ADC10/12 SAR converters, the first conversion should be correct. But again with some limitations. If you use the internal reference, it willt ake some time to settle before a valid converison can be done.
    Also, the sudden rise of the overall current consumption when waking from LPM may cause a small drop in VCC until the voltage regulator can compensate. If Vcc is used as reference, this may lead to some incorrect (likely too high) results. If the MSP has a separate AVcc pin, it should be disconnected from DVcc by a low-pass filter (a 10 or 100Ohms series resistor and a 10µF tantalum/100nF ceramic combo). On these MSPs, AVcc is taken as reference. And the filter will block any ripple caused by the wakeup.

    Another point is the sampling capacitor. As Tony already pointed out, it needs to charge. However, the time required to charge it depends on its capacitance, the output capacitance of the signal source, any series resitance between (includign voltage dividers) and the series resistance of the channel switcher (~1k IIRC). A sample/hold time of SHT_0 (4 clock cycles?) is quite short. Depending on the signal source's output impedance, it might be too short to fully charge the depleted capacitor.
    To do the actual conversion, the ADC uses a method called charge redistribution. In this case, teh sampling capacitor is not one, but an array of multiple capacitors. During charging, their bottom side is connected to GND, but during conversion, their bottom sides are connected to Vcc as well, so charge flows from one sub-capacitor to the other, and a simple single comparator can be used to determine the bits of the result, rather than an expensive DAC feedback loop.
    As a result, the capacitor is pre-charged after the first conversion. If the sampling time was too short to fully charge the capacitor in the first converison cycle, it gets a 'head start' on the next.
    This could be an explanation for the first conversion(s) being not good.

  • Jens-Michael Gross said:
    My personal opinion is that the oteh rIV registers should behave the same way. There is no sense in writing to the IV register except if you intentionally want to clear ALL pending interrupts. If you loop unitl it returns 0, you need to do a read anyway. So the write makes no sense if it is indeed implemented to only clear one. However, I never tried myself on th4 timer IV or port pin IV registers.Perhaps it is a msitake in the users guide and propagated by copy/paste. Or wasted silicon.

    At least on MSP430G2452, the manual seems to be wrong at this point. I made a test with CCR1 compare, CCR2 compare and TAR overflow interrupts requested simultaneously. Reading TAIV four times in a row gives 2, 4, 10, 0. Inserting a write (I tried writing 0 and -1) after the first read makes the subsequent reads to return 0. The manual is not consistent, TAIV is described as read-only elsewhere. And the IAR header files define TAIV as read-only, so my assembler reported an error on "MOV.W #0, &TAIV".

    Maybe the port interrupt vectors in 5xxx MSPs behave in the same way, 2xxx MSPs do not have PxIV -- a pity, it is difficult to use more than one pin per port for interrupts, although one can quite reliably work around the problem of interrupts being lost due to read-modify-write PxIFG access. Rather off-topic here, but I have seen a related PORT3 erratum for some older chips which is not listed in silicon errata for other chips and I was curious whether it describes the read-modify-write problem and whether it has been fixed somehow in hardware -- I made a test and it showed that it has not been fixed.

**Attention** This is a public forum