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.

MSP430FR4133: help with servo motor code

Part Number: MSP430FR4133

Hi,

I am trying to implement a servo motor(sg90) into the msp430's potentiometer. I want the servo motor to be controlled by the potentiometer i.e turning the potentiometer would turn the motor. I have the ADC code but I am struggling with the other code needed to implement this. Any help would be greatly appreciated. Thanks a lot!

  • I assume you mean A/D, not "potentiometer".

    For a servo, you need to set a PWM to a particular frequency and adjust the duty cycle. Can you show us what you have? We don't want to just do your homework for you.

    try the timer_a_ex4_pwmMultipleUpDown example.

    Here s a servo tutorial:

    https://learn.sparkfun.com/tutorials/basic-servo-control-for-beginners/servo-motor-basics

    For your servo, you want a 20 millisecond period and a duty cycle that varies from 5% (1mS pulse) to 10% (2ms pulse)

  • Hi Bilal,

    Not sure what you want to output from MSP430.

    If it is a digital signal, you can take the suggestion from Keith.

    If it is a analog signal, this device can not derectly ouput a analog voltage (DAC). Maybe you can output a PWM signal to simulate a analog signal.

    B.R.

    Sal

  • A servo is a motor that takes a certain pwm signal to set a motor to a particular position. In this case  a 7.5ms pulse with a 20ms repetition rate is used to set the servo to its zero position. Adjustments in duty cycle set different positions. They are used extensively in small radio controlled vehicles.

    I suggest you go to SparkFun, get one, and play with it it is pretty fun.

  • Thanks Keith for introduction, I got what servo is. Just potentiometer seems like a voltage output which makes me little confused.

    B.R.

    Sal

  • He did not say it clearly. I believe this is a beginner's homework assignment that takes the position of a potentiometer and sets a servo accordingly via a reading from the ADC, and subsequent setting of the PWM dutycycle.

    The servomotor equivalent of a blinky.

  • Keith, yes the scenario meakes sense to me.

  • Hi Keith, thank you for your response. This is what my code is looking like right now and im not sure how to adjust it. 

    #include <msp430.h>
    #include <driverlib.h>
    #include <Board.h>

    void configureADC()
    {
    //ADC setup
    SYSCFG2 |= ADCPCTL9; // turn on analogue pin 9 so it is not GPIO

    ADCCTL0 |= ADCON; // TURN ON ADC
    ADCCTL1 |= ADCSHP; // choose bit 9
    ADCCTL1 |= ADCSSEL_2; // choose SMCLK

    ADCCTL2 |= ADCRES_2; // clock resolution


    ADCMCTL0 |= ADCSREF_7; //+ve and -ve reference pins
    ADCMCTL0 |= ADCINCH_9; // A9 as input input

    ADCIFG &= ~0x01; //Clear interrupt flag
    ADCIE |= ADCIE0; //Enable ADC conversion complete interrupt
    }

    void initPWM() {
    TA0CCTL1 = OUTMOD_7; // CCR1 reset/set
    TA0CTL = TASSEL_2 + MC_1 + TACLR; // SMCLK, up mode, clear TAR
    TA0CCR0 = 20000-1; // PWM Period for 20ms (50Hz)
    TA0CCR1 = 1000; // Initial duty cycle for 1ms pulse

    P1DIR |= BIT2; // P1.2 as output for PWM
    P1SEL0 |= BIT2; // P1.2 option select
    }

    void main(void) {
    WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
    configureADC(); // Setup ADC
    initPWM(); // Setup PWM for servo

    __bis_SR_register(GIE); // Enable global interrupts to start ADC

    while(1) {
    ADCCTL0 |= ADCENC | ADCSC; // Start sampling/conversion
    __bis_SR_register(LPM0_bits + GIE); // Enter LPM0, ADC_ISR will force exit
    }
    }

    // ADC interrupt service routine
    #pragma vector=ADC_VECTOR
    __interrupt void ADC_ISR(void) {
    if(ADCIFG & ADCIFG0) { // Check if conversion is complete
    unsigned int adcValue = ADCMEM0; // Fetch ADC conversion result
    unsigned int dutyCycle = 1000 + (adcValue * 1000 / 4095); // Map ADC value to duty cycle range
    TA0CCR1 = dutyCycle; // Update PWM duty cycle to adjust servo position
    ADCIFG &= ~ADCIFG0; // Clear interrupt flag
    __bic_SR_register_on_exit(LPM0_bits); // Exit LPM0
    }
    }

  • What is happening or not happening?

  • I want the motor to rotate, but nothing is happening when I rotate the A/D on the msp430. I have also connected it to a 5V supply so I’m unsure why it’s not working.

  • Do you have a scope?

  • One problem is that 1.2 does not connect to the Timer, it is used by the USCI.

    You need to use 1.6 or 1.7 as the example shows:

    //
    //           MSP430FR4133
    //         ---------------
    //     /|\|               |
    //      | |               |
    //      --|RST            |
    //        |               |
    //        |     P1.7/TA0.1|--> CCR1 - 75% PWM
    //        |     P1.6/TA0.2|--> CCR2 - 25% PWM
    //

  • Thanks for the replies, I have made those changes and yes, I have a scope

  • this is what my code looks like now : 

    #include <msp430.h>
    #include <driverlib.h>
    #include <Board.h>

    void configureADC() {
    // ADC setup
    SYSCFG2 |= ADCPCTL9; // Enable A9 for ADC input

    ADCCTL0 |= ADCON; // Turn on ADC
    ADCCTL1 |= ADCSHP; // Sample-and-hold pulse-mode select
    ADCCTL1 |= ADCSSEL_2; // SMCLK as ADC clock source
    ADCCTL2 |= ADCRES_2; // 12-bit conversion results

    ADCMCTL0 |= ADCSREF_7; // Reference voltages are V(R+) = VREF+ and V(R-) = VREF-
    ADCMCTL0 |= ADCINCH_9; // A9 as input

    ADCIFG &= ~0x01; // Clear interrupt flag
    ADCIE |= ADCIE0; // Enable ADC conversion complete interrupt
    }

    void initPWM() {
    // Configure GPIO
    P1DIR |= BIT7; // Set P1.7 as output
    P1SEL0 |= BIT7; // Select TA0.1 option for P1.7

    // Timer A0 configuration for PWM
    TA0CCTL1 = OUTMOD_7; // CCR1 reset/set
    TA0CTL = TASSEL_2 + MC_1 + TACLR; // SMCLK, up mode, clear TAR
    TA0CCR0 = 20000-1; // PWM Period: 20ms (for 50Hz)
    TA0CCR1 = 1500; // Start with a neutral duty cycle (e.g., ~7.5% for 1.5ms pulse width)

    // Note: Adjust the TA0CCR1 value based on ADC reading in ADC_ISR
    }

    void main(void) {
    WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
    configureADC(); // Setup ADC
    initPWM(); // Setup PWM for servo

    __bis_SR_register(GIE); // Enable global interrupts to start ADC

    while(1) {
    ADCCTL0 |= ADCENC | ADCSC; // Start sampling/conversion
    __bis_SR_register(LPM0_bits + GIE); // Enter LPM0, ADC_ISR will force exit
    }
    }

    // ADC interrupt service routine
    #pragma vector=ADC_VECTOR
    __interrupt void ADC_ISR(void) {
    if(ADCIFG & ADCIFG0) { // Check if conversion is complete
    unsigned int adcValue = ADCMEM0; // Fetch ADC conversion result
    // Map ADC value to PWM duty cycle range (1000 for 5% to 2000 for 10%)
    unsigned int dutyCycle = 1000 + (adcValue * 1000 / 4095);
    TA0CCR1 = dutyCycle; // Update PWM duty cycle to adjust servo position
    ADCIFG &= ~ADCIFG0; // Clear interrupt flag
    __bic_SR_register_on_exit(LPM0_bits); // Exit LPM0
    }
    }

  • unsigned int dutyCycle = 1000 + (adcValue * 1000 / 4095);

    The FR4133 has a 10-bit ADC. Also it is highly probable that the multiplication will overflow. The overall result is that your offset from 1000 (full-right?) will vary in a very small range. You might do better with something like:

    > unsigned int dutyCycle = 1000 + (adcValue * 1000UL / 1024);

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

    > ADCMCTL0 |= ADCSREF_7; // Reference voltages are V(R+) = VREF+ and V(R-) = VREF-

    I expect you don't have the external VREF pins connected, so I'm not sure what the result will be. SREF=0 (VCC) or =1 (internal VREF) will probably get better results.

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

    You're setting (by omission) ADCSHT=0, which is a very short time, particularly coming from a resistor-divider circuit -- those generally have very high impedance. You're not in a big hurry, so trying setting ADCSHT to something fairly large, e.g.

    > ADCCTL0 |= ADCSHT_6;   // 128 ADC clocks for sample/hold

  • I only have a 2311 available, but this code works - I changed the numbers to get your 20ms period.

    This is the MSP430fr231X_tb0_16 example.

    I suggest you start with a simple pwm example to get it working before you hook up the ADC.

    (Oh, and please use the code insert facility to make your code readable)

    int main(void)
    {
        WDTCTL = WDTPW | WDTHOLD;                 // Stop WDT
    
        P1DIR |= BIT6 | BIT7;                     // P1.6 and P1.7 output
        P1SEL1 |= BIT6 | BIT7;                    // P1.6 and P1.7 options select
        
        // Disable the GPIO power-on default high-impedance mode to activate
        // previously configured port settings
        PM5CTL0 &= ~LOCKLPM5;
    
        TB0CCR0 = 20000;                         // PWM Period
        TB0CCTL1 = OUTMOD_7;                      // CCR1 reset/set
        TB0CCR1 = 15000;                            // CCR1 PWM duty cycle
        TB0CCTL2 = OUTMOD_7;                      // CCR2 reset/set
        TB0CCR2 = 5000;                            // CCR2 PWM duty cycle
        TB0CTL = TBSSEL__SMCLK | MC__UP | TBCLR;  // SMCLK, up mode, clear TBR
    
        __bis_SR_register(LPM0_bits);             // Enter LPM0
        __no_operation();                         // For debugger
    }

  • Thanks a lot guys for your time and patience - I'll give that a go and hopefully it works. Cheers

**Attention** This is a public forum