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.

MSP430FR5739: MSP430FR5739, ADC10, DCO, SMCLK, ACLK

Part Number: MSP430FR5739


Tool/software:

Hi TI experts

I am working on MSP430 FR5739, I am trying to record a modulated sine wave audio at 2500 hz using port 1.1. But we are getting close to 2475 not  constant instead of 2500 hz with jitter. Here is the code; 

#include <msp430.h>
#include <stdint.h> // For uint16_t, uint8_t

//-----------------------------------------------------------------------------
// Function Prototypes
//-----------------------------------------------------------------------------
void initClock(void);
void initGPIO(void);
void initADC(void);
void initUART(void);
void initTimerA(void);

//-----------------------------------------------------------------------------
// Global variable to store the ADC result
//-----------------------------------------------------------------------------
volatile uint16_t ADC_Result;

//-----------------------------------------------------------------------------
// Main Program
//-----------------------------------------------------------------------------
void main(void)
{
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer

initClock(); // Configure DCO=8 MHz, SMCLK=8 MHz, ACLK=4 MHz
initGPIO(); // P1.1=ADC input, P2.0/P2.1=UART
initADC(); // Configure ADC10 (software‐trigger inside Timer ISR)
initUART(); // Configure eUSCI_A0 UART @230400 baud (ACLK=4 MHz)
initTimerA(); // Configure Timer_A to fire at 2500 Hz (SMCLK)

__enable_interrupt(); // Enable global interrupts

while (1)
{
__bis_SR_register(LPM0_bits | GIE); // Enter LPM0; will wake on ADC or UART ISR
__no_operation(); // For debugger
}
}

//-----------------------------------------------------------------------------
// initClock()
// – Direct‐register code (no DriverLib).
// – DCOFSEL_3 sets DCO = 8 MHz exactly (DCORSEL=0 implicitly).
// – Route DCOCLK → MCLK/SMCLK/ACLK.
// – Set ACLK = DCO/2 = 4 MHz, SMCLK = DCO/1 = 8 MHz, MCLK = DCO/1 = 8 MHz.
//-----------------------------------------------------------------------------
void initClock(void)
{
CSCTL0_H = CSKEY_H; // Unlock CS registers (key = 0xA5 << 8)

// Select DCOFSEL_3 with DCORSEL=0 (default) → DCO = 8 MHz exactly
CSCTL1 = DCOFSEL_3; // DCOFSEL_3 = 0b011 → step for 8 MHz

// Route DCOCLK to ACLK, SMCLK, and MCLK
CSCTL2 = SELA__DCOCLK // ACLK = DCOCLK
| SELS__DCOCLK // SMCLK = DCOCLK
| SELM__DCOCLK; // MCLK = DCOCLK

// Divide ACLK by 2 → 4 MHz; leave SMCLK=8 MHz, MCLK=8 MHz
CSCTL3 = DIVA__2 // ACLK divider /2
| DIVS__1 // SMCLK divider /1
| DIVM__1; // MCLK divider /1

CSCTL0_H = 0; // Lock CS registers again
}

//-----------------------------------------------------------------------------
// initGPIO()
// – P1.1 as ADC10 A1 (analog input).
// – P2.0 = UCA0TXD, P2.1 = UCA0RXD for UART.
//-----------------------------------------------------------------------------
void initGPIO(void)
{
// P1.1 → ADC input (A1)
P1SEL0 |= BIT1;
P1SEL1 |= BIT1;

// P2.0/P2.1 → UCA0TXD/UCA0RXD
P2SEL0 &= ~(BIT0 | BIT1);
P2SEL1 |= BIT0 | BIT1;

// Exit GPIO power-on default high-impedance mode
PM5CTL0 &= ~LOCKLPM5;
}

//-----------------------------------------------------------------------------
// initADC()
// – Software‐triggered ADC10.
// – ADC10CLK = SMCLK = 8 MHz → conversion time = 32 cycles/8 MHz = 4 µs.
// – Sample & Hold time = 32 ADC10CLK cycles (ADC10SHT_3).
// – Input channel = A1 (P1.1).
// – Interrupt on conversion complete.
//-----------------------------------------------------------------------------
void initADC(void)
{
// 1) Turn on ADC10, set S/H time = 32 ADC10CLK cycles
ADC10CTL0 = ADC10SHT_3 // S/H time = 32 cycles (ADC10CLK)
| ADC10ON; // ADC10 ON

// 2) Use sampling timer, ADC10CLK = SMCLK (ADC10SSEL_2)
// (no hardware trigger)
ADC10CTL1 = ADC10SHP // Sample/Hold using sampling timer
| ADC10SSEL_2; // ADC10CLK source = SMCLK (8 MHz)

// 3) 10-bit resolution
ADC10CTL2 = ADC10RES; // ADC10RES = 10-bit

// 4) Select channel A1 (P1.1)
ADC10MCTL0 = ADC10INCH_1; // ADC10INCH_1 = channel A1

// 5) Enable interrupt when conversion completes
ADC10IE |= ADC10IE0; // Enable ADC10IFG interrupt

// Note: We do NOT set ADC10ENC here; we will set/clear ENC inside the Timer ISR.
}

//-----------------------------------------------------------------------------
// initUART()
// – eUSCI_A0 in UART mode, 8-N-1.
// – Clock = ACLK = 4 MHz.
// – Baud = 230400 → 4 MHz/230400 ≈ 17.36 → UCA0BR0 = 17, UCA0BR1 = 0, UCA0MCTLW = 0x04A0.
//-----------------------------------------------------------------------------
void initUART(void)
{
UCA0CTL1 |= UCSWRST; // Put eUSCI in reset
UCA0CTL1 |= UCSSEL__ACLK; // CLK = ACLK = 4 MHz

UCA0BR0 = 17; // 4 MHz/230400 ≈ 17.36 → INT = 17
UCA0BR1 = 0; // High byte = 0
UCA0MCTLW = 0x04A0; // Modulation settings (as before)

UCA0CTL1 &= ~UCSWRST; // Release eUSCI_A0 for operation

// Note: We will enable UCTXIE later inside the ADC ISR to send the high byte.
}

//-----------------------------------------------------------------------------
// initTimerA()
// – SMCLK = 8 MHz → CCR0 = 3199 → 8 MHz/(3199+1) = 2500 Hz exactly.
// – Enable CCR0 interrupt; in that ISR we will start the ADC10 conversion.
//-----------------------------------------------------------------------------
void initTimerA(void)
{
TA0CCR0 = 3199; // 8 MHz/(3199+1) = 2500 Hz
TA0CCTL0 = CCIE; // Enable CCR0 interrupt
TA0CTL = TASSEL__SMCLK // Use SMCLK = 8 MHz
| MC__UP // Up mode (counts 0 → CCR0)
| TACLR; // Clear TAR to start fresh
}

//-----------------------------------------------------------------------------
// Timer_A0 CCR0 Interrupt Service Routine
// – Fires at exactly 2500 Hz.
// – Clears ADC10ENC if set, then sets ADC10ENC+ADC10SC to start a fresh conversion.
//-----------------------------------------------------------------------------
#pragma vector = TIMER0_A0_VECTOR
__interrupt void Timer_A_CCR0_ISR(void)
{
// Clear ENC so that ADC10SC will actually start a new conversion
ADC10CTL0 &= ~ADC10ENC;
// Arm and start ADC10 conversion now
ADC10CTL0 |= ADC10ENC | ADC10SC;
// ADC will take 4 µs (32 cycles @ 8 MHz) and then fire ADC10_VECTOR.
// We return immediately; LPM0 stays set until ADC ISR wakes the CPU.
}

//-----------------------------------------------------------------------------
// ADC10 Interrupt Service Routine
// – Called when each 10-bit conversion completes (~4 µs after SC).
// – Read ADC10MEM0, send the low byte by UART, then enable UCTXIE for the high byte.
//-----------------------------------------------------------------------------
#pragma vector = ADC10_VECTOR
__interrupt void ADC10_ISR(void)
{
switch (__even_in_range(ADC10IV, ADC10IV_ADC10IFG))
{
case ADC10IV_NONE: break; // No interrupt
case ADC10IV_ADC10OVIFG: break; // Overflow (ignore)
case ADC10IV_ADC10TOVIFG: break; // Time-over (ignore)
case ADC10IV_ADC10HIIFG: break; // Window high (unused)
case ADC10IV_ADC10LOIFG: break; // Window low (unused)
case ADC10IV_ADC10INIFG: break; // Window inside (unused)
case ADC10IV_ADC10IFG: // Conversion complete
ADC_Result = ADC10MEM0; // Read the 10-bit result
UCA0TXBUF = (uint8_t)(ADC_Result & 0xFF); // Send low byte first
UCA0IE |= UCTXIE; // Enable TX interrupt for high byte
break;
default: break;
}
}

//-----------------------------------------------------------------------------
// USCI_A0 (UART) Interrupt Service Routine
// – Fires when UCA0TXBUF is empty (after sending low byte).
// – Send the high byte of ADC_Result, then disable UCTXIE until next conversion.
//-----------------------------------------------------------------------------
#pragma vector = USCI_A0_VECTOR
__interrupt void USCI_A0_ISR(void)
{
switch (__even_in_range(UCA0IV, USCI_UART_UCTXCPTIFG))
{
case USCI_NONE: break;
case USCI_UART_UCRXIFG: break; // RX interrupt (unused)
case USCI_UART_UCTXIFG: // TXBUF is ready for next byte
UCA0TXBUF = (uint8_t)((ADC_Result >> 8) & 0xFF); // Send high byte
UCA0IE &= ~UCTXIE; // Disable UCTXIE until next ADC conversion
break;
case USCI_UART_UCSTTIFG: break;
case USCI_UART_UCTXCPTIFG: break;
default: break;
}
}

Even I have tried alternative clocks ; ACLK still the error of sampling frequency and jitters are same. I am stuck at this stage from 2 months. I tried various approach but the result seems same regarding recorded signal frequency and jitter. I have following questions?

  1. How stable is DCO clock (SMCLK or ACLK)?
  2. What is the reason of jitter?
  3. Do we need Clock calibration? if yes, How to calibrate clock for stable frequency?
  4. Do we need external clock or crystal ?

Please give me pragmatic response We are closed to deadline?

  • How are you measuring the sample rate?

    My calculator says that 2475/2500=0.99, or about 1%. This is within the (trimmed) DCO tolerance. Ideally you'd want an external crystal (usually around 50ppm), but it sounds too late in your development to add that.

    For the jitter, I suggest having TA0 trigger the ADC directly.by setting ADC10SHS=1 (TA0.1). Then set TA0CCR1 to pretty much any value (=1 is good), with OUTMOD=3 so it generates a rising edge once per cycle. Toggling ENC can happen in the ADC10 ISR (with plenty of free time to do it); alternatively, use CONSEQ=2 with MSC=0 and you don't have to toggle ENC at all [Ref UG (SLAU272D) Fig 16-8]. This is illustrated in Example MSP430FR57xx_adc10_11.c:

    https://dev.ti.com/tirex/explore/node?node=A__AFz2OCYY7g-GikErZnK-RA__msp430ware__IOGqZri__LATEST

    If the sample rate error is in fact from the DCO, you can "tune" the TA0CCR0 value; with nominal =3200, your resolution is 1/3200=~.0.03%.

  • Hello k,

    You can try Bruce's mentioned method. I think that will help you.

    Best Regards,

    Janz Bai

**Attention** This is a public forum