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.

Trigger ADC12 from Timer interrupt B0, Just works with Timer A0

Other Parts Discussed in Thread: MSP430FR5969
#include "msp430.h"
#include  <intrinsics.h>
#include <stdint.h>
#include <math.h>

// Define for CE Pin of 8*8 dot
#define CEon()  (P1OUT |= BIT5);
#define CEoff()  (P1OUT &= ~BIT5);


// ISR Flags
volatile int ISRcall = 0;
volatile int debounce2 =0;
volatile uint16_t countS2 =0;

// Variables ADC
volatile uint16_t adcValue =0;
volatile float adcSegFloat =0;
volatile unsigned long adcSeg =0;
volatile int temp =0;

// Variables for 8*8 Dot Matrix
volatile uint16_t  TXdata8dot8 [4] = {0x00, 0x00, 0x00, 0x00};
volatile uint16_t shapeLineVerRed[8] = {~0x00, ~0x00, ~0x00, ~0x00 , ~0x10, ~0x30, ~0x70, ~0xF0};
volatile uint16_t shapeLineVerGreen[8] = { ~0x01, ~0x03, ~0x07, ~0x0F , ~0x1F, ~0x3F, ~0x3F, ~0x3F};
volatile uint16_t coin =0;

void buttonS2Call(void);
void timerCall(void);
void adcCall(void);
void DOTstatic(void);
void SPIwrite(uint16_t write);


///////////////////// Call Functions from ISR-Flag////////////////////////////

void buttonS2Call(void)
{
	// Button Port 4 interrupt call Function
	// Set ISRcall to
	ISRcall=0;
	debounce2=1;
	// Start Timer in Upmode, Div 8, CCR0=1000, gives T=80ms
	TB0CTL = TBSSEL_2 + MC_1 + ID_3;
}

void timerCall(void)
{
	ISRcall=0;
	if(debounce2)
	{
				// Get ADC Value
				adcCall();
				countS2++;
				if(countS2 >= 800)
					{
					// Reset counter
					countS2 = 0;
					debounce2=0;
					// Stop Timer
					TB0CTL |= MC_0;
					// Enable Interrupt on P1.1
					P4IES |= BIT5; // Falling edge
					P4IFG &= ~BIT5; // Interrupt Flag becomes 1 when interrupt is called
					P4IE |= BIT5; // Portpin Interrupt enable
					}
	}
}

void adcCall(void)
{
	  // ADC ON again and Start Comnversion
	  ADC12CTL0 |= ADC12ON;
	  ADC12CTL0|= (ADC12ENC | ADC12SC);

	  // Divide into 8 Segments with 4096/544 and round with math.h
	  adcSeg = (long)roundf(adcValue/544);


	  //Come back from ISR and Call dot
	  DOTstatic();
}


void DOTstatic()
{

	if(coin==0)
		{

		// Following shows bargraph with colour
		//RED LED
		TXdata8dot8[0] = shapeLineVerRed[adcSeg];
		// Blue OFF
		TXdata8dot8[2] = 0xFF;             // color blue off
		// Green LED
		TXdata8dot8[1] = shapeLineVerGreen[adcSeg];
		TXdata8dot8[3] = (0x24);

	    CEoff();     					// when CE is low, display receives data
	    SPIwrite(TXdata8dot8[0]);         //transfer data[0] (red)
	    SPIwrite(TXdata8dot8[2]);         //transfer data[2] (green)
	    SPIwrite(TXdata8dot8[1]);        // transfer data[1] (blue)
	    SPIwrite(TXdata8dot8[3]);      // tansfer data[3] matrix displays
	    CEon();   	 				// when CE is High

		}

	else
	{
	    // Off time of LEDS
	    TXdata8dot8[0] =0xFF;				// red off
		TXdata8dot8[2] = 0xFF;             // color blue off
		TXdata8dot8[1] = 0xFF;			// green off
		TXdata8dot8[3] = (0x18);

	    CEoff();     					// when CE is low, display receives data
	    SPIwrite(TXdata8dot8[0]);         //transfer data[0] to the matrix(red)
	    SPIwrite(TXdata8dot8[2]);         //transfer data[2] to the matrix(green)
	    SPIwrite(TXdata8dot8[1]);        // transfer   data[1] to the matrix(blue)
	    SPIwrite(TXdata8dot8[3]);      // tansfer data[3] to the matrix
	    CEon();   	 				// when CE is High, means that matrix begin to display
	}

}

void SPIwrite(uint16_t write)
{
  	CEoff();
	// loop here till Buffer is ready
	// the fill TXbuffer
  	while(!(UCB0IFG & UCTXIFG))
  	{
  		;
  	}

	// Load Buffer
	UCB0TXBUF = write;

  	// Delay 100us necessary!!!
	// without last Byte can't be decoded
  	__delay_cycles(100);
}

int main(void)
{
	WDTCTL = WDTPW | WDTHOLD;                 // Stop Watchdog

  // Disable the GPIO power-on default high-impedance mode to activate
  // previously configured port settings, This is important
  PM5CTL0 &= ~LOCKLPM5;


//////////////////////////////Clock to SMCLK=1MHz///////////////////////////////
// Set clock to min DCO 1Mhz, User guide p. 104
  CSCTL0_H = CSKEY >> 8;                    // Unlock clock registers
  CSCTL1 = DCOFSEL_0 | DCORSEL;             // Set DCO to 1MHz with DCOFSEL_0
  CSCTL2 = SELA__VLOCLK | SELS__DCOCLK | SELM__DCOCLK;
  CSCTL3 = DIVA__1 | DIVS__1 | DIVM__1;     // Set all dividers
  CSCTL0_H = 0;                             // Lock CS registers


  //////////////////////////////GPIOS/////////////////////////////////////////////////
   // Configure Switches on Ports P4.5 , direct to GND without PUllup////////

	 P4DIR &= ~BIT5; // Define Direction P4.5 = Input
	 P4REN |= BIT5; // Define Pullup enable P1.1
	 P4OUT |= BIT5; // Define Pullup not Pulldown

	 P4IES |= BIT5; // Falling edge
	 P4IFG &= ~BIT5; // Interrupt Flag becomes 1 when interrupt is called
	 P4IE |= BIT5; // Portpin Interrupt enable

 // Configure LEDS on Ports P4.6 and P1.0, direct to GND without PUllup////////
	 P4DIR |= BIT6; // Define Direction P4.6 = Output
	 P1DIR |= BIT0; // Define Direction P1.0 = Output

	 P4OUT |= BIT6; // Output to 1
	 P1OUT |= BIT0; // Output to 1

  ///////Timer B0///////////////////////////////////////////
  ///////TIMER A0////////////////////////////////////////////
  	  TB0CCTL0 = CCIE;                           // TBCCR0 interrupt enabled
  	  TB0CCR0 = 10000;                           // PWM Period
  	  TB0CCTL1 = OUTMOD_2;                       // TBCCR1 reset/set
  	  TB0CCR1 = 5000;                             // TBCCR1 =1
  	  // SMCLK, 1MHz, MC1=Upmode, Divider 8, Timer disabled with MC_0
  	  TB0CTL = TBSSEL_2 + MC_0 + ID_3; //
  	 //TB0CTL = TBSSEL__SMCLK | MC__UP;

  ///////SPI B0////////////////////////////////////////////
  	  // 1 step is Set UCSWRST before configure SPI, GPIOS
  	  UCB0CTLW0 |= UCSWRST;
  	  // P2SEL1.2 =1; P2SEL0.2 = 0 to set P2.2 to SPI CLK USCIB0, Page 84
  	  P2SEL1 |= (BIT2);                       //
  	  P1SEL0 &= ~(BIT2);
  	  // P1SEL1 = 1, P1SEL0 = 0 to set Periph to SPI or I2C -> See datasheet on page 83  	  //P1.7 = UCB0SOMI
  	  //P1.6 = UCB0SIMO
  	  P1SEL0 &= ~(BIT6|BIT7);                       //
  	  P1SEL1 |= (BIT6|BIT7);						// Pin Direction is set by eUSCI_B0
  	  // GPIO as Software nSS
  	  P1DIR |= BIT5; // Define Direction P1.5 = Output
  	  P1OUT |= BIT5; // Output to 1

  	  // Initialize SPI MOde 3
  	  UCB0CTLW0 = UCCKPL| UCMST | UCMODE_0 | UCSYNC | UCMSB |UCSSEL__SMCLK |UCSWRST; // SPI Mode 3 needs UCCKPL = 1 -> Clock High in idle, UCCKPH=0 -> write on first failing edge;
  	  //Master,UCMODE_0-> 3 - wire default mode, Synchron, keep UCSWRST ==1 or nothing happens!

  	  // Set Prescaler for 100kHz SPI clockPrescale=1MHz/100kHz=10
   	  UCB0BRW=10;
  	  // This Releases USCIB0 from reset
  	  UCB0CTLW0 &= ~UCSWRST;
  	  // RX Enable Interrupts on receive
  	 // UCB0IE |= UCRXIE;

 ///////ADC A10 Pin P4.2////////////////////////////////////////////
  	  // Configure GPIOS, POt 10kOhm is on VCC, (P4.2->A10), (P2.6->GND)
  	  // P2.6 Output and set to Low
	  P2DIR |= BIT6;
	  P2OUT &= ~BIT6;
  	  // P4SEL1 =1; P4SEL0.2 = 1 Page 94
  	  P4SEL1 |= (BIT2);                       //
  	  P4SEL0 |= (BIT2);

  	  // CTL Register 0: Set ADC12ON Bit, before any changes in CTL Regiser
  	  // ADC12=N Bit, ADC12SHT1 for ADC10 and 16 cycles
  	  //Sample for 16 cycles from 10*TAU=10*27pF*5k and clock 6.3MHz and rounded [cyles]=10Tau*fclk
  	  ADC12CTL0 = ADC12ON | ADC12SHT1_2;
  	  // CTL register 1: Single Conversion CONSEQ0, Clock Select ADC internal ADC120SC is mode 0, 3 is smclk, NO Clock divider
  	  // Sample and Hold source is trigger from ADC12SC
  	  ADC12CTL1 = ADC12CONSEQ0 | ADC12SSEL_0 | ADC12DIV0 | ADC12SHS0;
  	  // CTL Register 2: ADC12DF = 0, for Binary unsigned format 0x0000...0xFFFF, ADCREsolution 12Bit
  	  ADC12CTL2 = ADC12RES_2;
  	  // Very Important to choose the mem register, without doesn't enter the ISR
  	  ADC12CTL3 = ADC12CSTARTADD_10;
  	  // Conversion Memory control Register for ADC10, Reference is set to VCC, GND with ADC12VRSEL=0
  	  ADC12MCTL10 = ADC12INCH_10;

  	  // Interrups ADC10 in Register 0
  	  ADC12IER0 = ADC12IE10;


  	  _bis_SR_register(GIE);       //  interrupts enabled

while(1)
	{

	switch(ISRcall)
		{
		case 1: timerCall(); break; // Timer wakes up CPU every 50ms and sets ISRcall=1;
		case 2: __no_operation(); break; // Do nothing
		case 3: buttonS2Call(); break; // Switch on P4.5

		default: //_bis_SR_register(CPUOFF);
		;break;
		}
	}


}

// Button S1 left
#pragma vector=PORT4_VECTOR
__interrupt void Port4_ISR(void)
{
	// Set flag for button Call
	  ISRcall=3;

	  // toggle green LED
	   P1OUT ^=BIT0;
	   // Switch off red Toggle LED
	   P4OUT &=~BIT6;

	  // Clear Flags or being stuck in switch ISR
	  P4IFG &= ~BIT5; // Flag register
	  P4IE &= ~BIT5; // Portpin Interrupt Disable
}

// ISR for Timer A0///////////////////////////////////////////////////////
#pragma vector=TIMER0_B0_VECTOR
__interrupt void Timer_B0 (void)
{

	// Timer interrupt every 1000 * 8 /1MHz = 8ms
	// Toggle variable coin
	coin^=coin;
	// Set flag for timer Call
	ISRcall=1;
}

// ADC Interrupt
#pragma vector = ADC12_VECTOR
__interrupt void ADC12_ISR (void)
{

	  adcValue = ADC12MEM10;
	// Disable Conversion
	  ADC12CTL0 &= ~ADC12ENC;
	  ADC12CTL0 &= ~ADC12ON;

	  // Toggle red LED
		P4OUT ^=BIT6;
}


// SPI Inerrupt
#pragma vector = USCI_B0_VECTOR
__interrupt void USCIB0_Rx_ISR (void)
{
	//RXdata=UCB0RXBUF;
}


  • Hello there,

    here is a post of my level meter Code supposed to be running on a MSP430FR5969. Unfortunately it doesn't do at the moment.

    The ADC12 is used to sense a DC-voltage and is triggered by the Timer B0 every 8ms. The DC voltage is divided into 8 segements and is send via SPI to be shown on 8 RGB LEDS. The procedure is started by a switch and is running for some seconds.

    When using the timer A0 the program works fine and I see the ADC values changing when putting a breakpoint in the program and the LEDS on the level meter.
    With the Timer B0 the ADC never reaches the ADC ISR. At least I don't see the program going there, when adding a breakpoint in the ADC ISR. When putting the breakpoint to the ADC call function, the ADC value is always 0. This is the code above. When changing everything to timer A0 it seems to work.

    I also tried the timer A1, but this doesn't work as well with the Register settings above.

    When using the timer in a pure software mode, I am also a bit confused about the output mode and how to choos TA0CCR1, can you please give some hints on the ideas behind? When using a hardware PWM, then I understand the reason for CCR0 and CCR1 setting for example the duty cycle. Here I just want to go into the ISR everey time TAR goes to CCR0 and get the ADC value. Do I also enter the ISR with TACCR1? Does all the timer A0,A1,B0 have their own CCR0,CCR1,CCR2 register and is it correct for all that, when using CCR0 the Interrupt flag is cleared by its own and when using CCR1 and CCR2, the Timer vektor e.g. for TA0 TA0IV needs to be read over the switch case to clear the flag?

    Sorry for all of my beginner questions and I hope somebody of you finds the time to help to look through the code.

    One last general question. Does it make sense to set flags in the ISR functions, which call normal functions (I called the Call functions) after the ISR is finished and the program is back in the while(1)-loop?

    Kind regards
  • Hello Audio Designer,

    Have you tried changing the ADC12SHSx bits of the ADC12CTL1 register? Your code indicates a value of 001b which triggers the ADC from the TA0 CCR1 output. Looking at Table 6-18 of the Datasheet, if you were to change this value to 010b or 011b then the ADC would instead trigger from TB0 CCR0 or CCR1 outputs, respectively.

    TIMER0_A0_VECTOR handles CCR0 interrupts and TIMER0_A1_VECTOR handles CCR1, CCR2, and CTL interrupts for TA0. The same is true for TA1 and TB0. Your ISRcall flags are logical, you should exit the ISRs as soon as possible.

    Regards,
    Ryan
  • Dear Ryan!

    Thank you very much for your answer. The ADC12SHSx bits of ADC12 were the right point to set the timer correctly.

    For using the Timer B0, I had to set the bits to 011b -> TB0 CCR1 output.

    Initially I had ADC12SHS0 =000b in the code for software trigger. Why does this just work with Timer A0? Doesn't the software trigger mean, I can use any Timer and start the ADC conversion by writing the following code in a Timer ISR?

    // ADC ON again and Start Comnversion
    ADC12CTL0 |= ADC12ON;
    ADC12CTL0|= (ADC12ENC | ADC12SC);

    Afterwards clearing this bits, when conversion is done.

    My aim is to have one conversion on CCR0 and another on CCR1, than wait till the Timer starts from 0 again. Can I do this with one timer or do I have to use 2 timers and change the ADCSHS-Bit?


    Kind regards
  • Hi AD,

    The ADC can only trigger conversions from one source, ADC12SC or a TAxCCRn output, so you will need to adjust your timer frequency or ISRs accordingly to compensate. ADC12SC will work with any timer ISR but your code has ADC12SHS0 set for an ADC12SHSx value of 001b (TA0 CCR1 output trigger).

    Regards,
    Ryan

**Attention** This is a public forum