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.

MSP-EXP430F5529LP: How to program the Timer0_B7 CC0 interrupt

Part Number: MSP-EXP430F5529LP
Other Parts Discussed in Thread: MSP430F5529,

OK - and trying to run an interrupt on the Timer B CC0 channel, so when TBxR wraps. I've stripped down my code to a simple test program, that is designed to have the interrupt just flash the LED. So question is what have I missed.

The only slightly unusual thing in my code, is rather than the usual hidden function to write registers, I've just done a direct write to the memory mapped address of the register. Sh ould be able to read my code though because I should have kep standard names

#include <msp430.h>
#include <stdint.h>
#include "timerb.h"

volatile uint32_t count=0;

int main(void)
{
  // Stop WDT
  HWREGW( WDT_A_BASE | WDTCTL ) = WDTPW|WDTHOLD;

  // Now set up Timer B
  // note do this with "+" rather than "|" as TBxEX0 is too far out!
  // first disable before programming
  HWREGW( TIMER_B0_BASE + TBxCTL )&=0xffcf;
  // all Timer B latch updates not tied together (But 
  // 16 bit
  // SMCLK is the clock
  // divide by 1
  // keep disabled
  // Time clear - here we clear just be sure, but will do again when start
  // TBIFG interrupt disabled (we just do CC0)
  // and set TBIDEX to divide by 1 as well
  HWREGW( TIMER_B0_BASE + TBxCTL )=
    TBCLGRP_0 | CNTL__16 | TBSSEL__SMCLK | TBCLR ;
  HWREGW( TIMER_B0_BASE + TBxEX0 )= TAIDEX_0;
  // latch when TB0R counts to zero - and CCR0 sets period
  // and CCR0 is the interupt
  HWREGW( TIMER_B0_BASE + TBxCCTL0 )= CLLD_1 | CCIE;
  HWREGW( TIMER_B0_BASE + TBxCCR0 )= Period-1;
  // Now all set enable and clear
  HWREGW( TIMER_B0_BASE + TBxCTL )= MC__UP | TBCLR ;
  
  // and set the LED so we can write to it
  HWREGB( P1_BASE | PxDIR ) |= BIT0; 

  // now all set for now just loop, with LED flashing
  while(1)                                
  {
    //    HWREGB( P1_BASE | PxOUT ) ^= BIT0;
    for(uint16_t i=65535;i>0;i--);
  }
  

}

// TIMER B CC
__attribute__((interrupt(TIMER0_B0_VECTOR)))
void TIMER0_B0_ISR (void) {
  if ( (count++)%100000 == 0) HWREGB( P1_BASE | PxOUT ) ^= BIT0;
}

and in case you need the header file its:

#include <stdint.h>
#define HWREGB(x) (*((volatile uint8_t *)(x)))
#define HWREGW(x) (*((volatile uint16_t *)(x)))
#define HWREGQ(x) (*((volatile uint32_t *)(x)))

// clocks to PWM cycle, and hence interupt in clocks
#define Period 1000

#define PxIN 0x00
#define PxOUT 0x02
#define PxDIR 0x04
#define PxREN 0x06
#define PxDS 0x08
#define PxSEL 0x0a
#define PxIES 0x18
#define PxIE 0x1a
#define PxIFG 0x1c
#define PxIV 0x0e
#define PxSEL 0x0a

// this is the whole wtdctl address as coun't find the 0xc offset documented 
#define WDTCTL 0x0c
// UCS
#define UCSCTL0 0x00
#define UCSCTL1 0x02
#define UCSCTL2 0x04
#define UCSCTL3 0x06
#define UCSCTL4 0x08
#define UCSCTL5 0x0a
#define UCSCTL6 0x0c
#define UCSCTL7 0x0e
// SFR
#define SFRIFG1 0x02

// Timer B
#define TBxCTL 0x00
#define TBxCCTL0 0x02
#define TBxCCTL1 0x04
#define TBxCCTL2 0x06
#define TBxCCTL3 0x08
#define TBxCCTL4 0x0a
#define TBxCCTL5 0x0c
#define TBxCCTL6 0x0e
#define TBxR 0x10
#define TBxCCR0 0x12
#define TBxCCR1 0x14
#define TBxCCR2 0x16
#define TBxCCR3 0x18
#define TBxCCR4 0x1a
#define TBxCCR5 0x1c
#define TBxCCR6 0x1e
#define TBxIV 0x2e
#define TBxEX0 0x20

  • >  HWREGW( TIMER_B0_BASE + TBxCTL )= MC__UP | TBCLR ;

    You need to supply a clock; a good first guess is SMCLK (1MHz). Try instead:

    >  HWREGW( TIMER_B0_BASE + TBxCTL )= TBSSEL_2 |  MC__UP | TBCLR ; // TBSSEL__SMCLK

    ----------------

    >  if ( (count++)%100000 == 0) HWREGB( P1_BASE | PxOUT ) ^= BIT0;

    It looks like this will interrupt at 1kHz, so you'll only see an effect after 100 seconds. 

    Unsolicited: The "|" here is a bit jarring. Maybe it will (accidentally) work but I would suggest "+" instead.

  • OOps - yes original code I brought up XT2 and switched SMCLK to XT2:

      HWREGW( UCS_BASE | UCSCTL4 ) =
        SELM__XT2CLK | SELS__XT2CLK | SELA__REFOCLK;
    

    backed that out to simplify what was going on.

    My header file (msp430f5529.h) says that TBSSEL_2 is SMCLK which is also what TBSSEL__SMCLK, e.g. 0x0200. So should be the same. Yes decreased the count, originally with CPU at 4MHz was doing Period=400 cycles, equals 100kHz - and hence the number to see the flashing light.

    Anyway your changes made no difference, I'll keep digging.

  • Oh, right I missed this: You need to enable interrupts (GIE):

    >  __enable_interrupt();  // GIE=1

  • Well, there are competing statements that do "TBxCTL =" but the second only sets MC_UP and TBCLR. Wiping out the settings from the first.

  • No - that didn't help. So I now backed out my direct writing to memory registers - and makes no difference. Code still doesn't work. Currently:

    #include <msp430.h>
    #include <stdint.h>
    //#include "timerb.h"
    
    volatile uint32_t count=0;
    
    int main(void)
    {
      // Stop WDT
      WDTCTL = WDTPW + WDTHOLD;
    
      __disable_interrupt();
    
      // Now set up Timer B
      // first disable before programming
      TB0CTL &=0xffcf;
      // and set TBIDEX to divide by 1 as well
      TB0EX0 = TAIDEX_0;
      // latch when TB0R counts to zero - and CCR0 sets period
      // and CCR0 is the interupt
      TB0CCTL0 = CLLD_1 | CCIE;
      TB0CCR0 = 999;
      // Now all set enable and clear
      // all Timer B latch updates not tied together (But 
      // 16 bit
      // SMCLK is the clock
      // divide by 1
      // enable
      // Time clear - here we clear just be sure, but will do again when start
      // TBIFG interrupt disabled (we just do CC0)
      TB0CTL = TBSSEL__SMCLK | MC__UP | TBCLR ;
      
      // and set the LED so we can write to it
      P1DIR = BIT0;
    
      __enable_interrupt();
    
    
      // now all set for now just loop, with LED flashing
      while(1)                                
      {
        // HWREGB( P1_BASE | PxOUT ) ^= BIT0;
        for(uint16_t i=65535;i>0;i--) { __nop(); }
      }
      
    
    }
    
    // TIMER B CC
    void __attribute__((interrupt(TIMER0_B0_VECTOR))) TIMER0_B0_ISR (void) {
      if ( (count++)%1000 == 0) P1OUT ^= BIT0;
    }
    

    Oh yes I used "|" rather than "+" as it forces me to align everything. So Registers for devices are aligned so "|" usually works, and the same with the bit masks inside the registers - its bits that need setting and resetting, so if I use bit operations to my mind it should be safer ...

  • OK when I remove CLLD_1 from TBCCTL0 it works. Now my problem is I want to Latch CC1-4 only when The counter wraps, which is what I understand CLLD_1 does ...

    So any ideas how I do both?

    OK - looks like I have to set CLLD_1 on CC1-4 but not set it on CC0 ...

  • Hm. I had never tried using CLLD>0 for CCR0 (in Up mode). My first guess is that TB0CL0 is (initially) 0, so the counter can't count (wrap) to 0 in order to load it with the new value (999) you gave it.

    Do you need to set CLLD>0 for CCR0? I suppose there could be some application, but I've never encountered one. One possibility might be to reverse the order: set CCR0 (with CLLD=0), then set CCTL0 (with CLLD=1).

  • No I think I don't need CLLD for CCR0 - CCR1-4 are PWM and go out to pins, and want all synchronised, and loading values set in the previous cycle. CCR0 though is just for timing.

    Wasn't sure though when coding, if all CCR needed the CCLD. ALas not easy for me to test, on the MSP-EXP430F5529LP only one of the four Timer B Pins I using actually go out to headers ,,,

  • It looks like TB0.5/.6 (P3.5/.6) are pinned out. Could you use those, at least until you get to your final board?

  • You could also use the port-mapping mechanism: Assign TB0CCR1-4 to P4.0-3 (all pinned out). [Ref User Guide (SLAU208Q) Chap 13, datasheet (SLAS590P) Table 9-7.

    There's a relevant example in MSP430F55xx_PortMap_01.c here:

    https://dev.ti.com/tirex/explore/node?node=AIUr0Ozgnda2pccZ0g-U1w__IOGqZri__LATEST

  • How did I miss P3.5/6! Yes I just looked at the run of CPU pins from 55 to 59 giving TB0.0 to TB0.4 - and never went on to find TB0.5 and 6! I may well use that, I've 100Hz sine waves coming out on all of these pins, but with subtle phase relations between some, and it would be good to be test!

    This said, more worried about timing right now; the target for operation is the pwm period, and interrupt to 100kHz, now the development board is an order of magnitude slower - so only aiming for a 10kHz interrupt. But even doing that needs *very* careful timing - and I think I'm just missing right now ...

    I'll have to look into port mapping, how its implemented (e.g. is it in hardware), and any effects on timing.

    Thanks for the help and suggestions though.

**Attention** This is a public forum