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.

MSP430: Building a PPM signal

I am having problems designing a project to collect information from potenciometers and then building a PPM string signal. Does anyone has code I can use or maybe a complete application that I can check. I am certainly new in MSP but I want to migrate all my applications from ATMEL to MSP hence my lack of experience in these kind of MCU´s. I want to collect 8 different signals from 6 different potenciometers and 2 switches then arrange the signals in a desired order for the receiver to decode it... each signal has to be contained in a 2ms frame and after the 8th signal is added to the string I need to add a 20ms space to let know the decoder that another set is starting. Thanks comunity.
  • Generating a chain of pulses is not an easy task. The built-in PWM hardware can produce a chain of identical pulses with a given frequency (well, that's what PWM is), but to keep the pulses in a defined timing but with individuallength, it can prove difficult.

    Here's one approach:

    • Set up a clock to fit your smalles tiem window (e.g. if all pulses and pauses are a multiple of 1ms, set up a clock source that is 1kHz or can be divided down to 1kHz - that's a trivial task)
    • now program an SPI to put out data with this bitrate.
    • Assemble as tring of bytes where every bit corresponds to 1ms of your final signal.
    • now feed the SPI with this byte-.chain. The MOSI line of the SPI (you can ignore the clock) will reproduce your pulse pattern.

    The only thing is that you MUST ensure that the SPI is properly stuffed without delay (it has a 1 byte = 8 bit buffer, so that's your time window for providing the next byte), or you'll add a gap (which isn't important during real SPI function but would disrupt your signal)

     

  • Hi Daniel,

    so u r going to make 8 ch rc module...

    Dont worry....

    As msp 430 has 8 adc channels your task is very easy...

    follow this step

    connect six pot to six adc ch and connect two switch to adc ch 7 and 8

    configure any remaining io as out pin which is going to be generate PPM

    Now read carefully

    start

    initialize adc and output=0

    main: if ch1(throttle) voltage <= 5% of its max then //it will disable tx until throttle is at min pos 

    repeat : output=1

    wait 0.2 ms

    output =0

    wait for time which is mapped with voltage of ch1 up to 8 in each repeat cycle

    goto repeat 8 times

    wait for 10 ms // for reset the receiver decoder

    again go to repeat

    enjoy 

  • Hi Denial,
    The following is code for 1 to 7 Digital channel PPM Encoder...
    for Analog channels i will post soon.....
    Enjoy coding with LaunchPad...
    
    
    //***************************************************************************************
    // MSP430 PPM_ENCODER_REV_1 for 1 to 7 digital channel - Software
    //
    // Description;
    // Generate PPM at P1.7.
    // In put Digital Channel at P1.0 ..... P1.6
    // ACLK = n/a, MCLK = SMCLK = default DCO
    //
    // ====IDEAL PPM FRAME====
    //
    // |<--------------------------~20ms------------------------------->|
    // -->| |<--MAX_WIDTH -->| |<--MIN_WIDTH
    // ___ ___ ___ ___ ___ ___ ___ ___
    // | | | | | | | | | | | | | | | |
    //_____| |________| |________| |__ _ ___| |___ _ _ ______________________| |___| |________| |__ _ _ __| |____________
    //
    // ch1 ch2 ch3..7 |<---------RST_WIDTH---------->| -->| |<--CH_GAP
    //
    //
    //
    // MSP430x2xx
    // -----------------
    // /|\| XIN|-
    // | | |
    // --|RST XOUT|-
    // | |
    // | P1.7|-->PPM_OUT
    // | |
    // | P1.6|<-- CH7
    // | P1.5|<-- CH6
    // | P1.4|<-- CH5
    // | P1.3|<-- CH4
    // | P1.2|<-- CH3
    // | P1.1|<-- CH2
    // | P1.0|<-- CH1
    // | |
    // R.Butani
    // MEFGI Rajkot, India
    // ravi_butani@yahoo.com
    // Jan 2012
    // Built with Code Composer Studio v5
    //***************************************************************************************


    #include <msp430.h>

    #define RST_WIDTH 1000 //Reset pulse width for reset RX Decoder after each PPM frame
    #define MIN_WIDTH 20 //Min width of ch pulse for servo at min pos. or cut throttle
    #define MAX_WIDTH 100 //Max width of ch pulse for servo at max pos. or full throttle
    #define CH_GAP 20 //Gap betn two ch

    #define CH 6 //No. of Channels to be used

    #define PPM_OUT 0x80 //P1.7 is used for PPM Output


    void delayMS(unsigned int us_10 ) // delay function us*10 when mclk = 1MHz
    {
    unsigned int i;
    for (i = 0; i<= us_10; i++)
    __delay_cycles(10);
    }

    void main(void)
    {
    unsigned int i;
    unsigned char ch_serve;
    WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
    P1DIR |= PPM_OUT; // Set P1.7 to output direction

    while (1) // Test P1.6
    {
    ch_serve=0x01;
    P1OUT &= ~PPM_OUT;
    delayMS(RST_WIDTH);
    for(i=1;i<=CH;i++)
    {
    P1OUT |= PPM_OUT;
    delayMS(CH_GAP);
    P1OUT &= ~PPM_OUT;
    if ((ch_serve & P1IN)) delayMS(MAX_WIDTH); // if P1.3 set, set P6.0
    else delayMS(MIN_WIDTH); // else reset
    ch_serve = ch_serve +ch_serve; // (ch_no >> i);
    }
    P1OUT |= PPM_OUT;
    delayMS(CH_GAP);
    }
    }
  • Such a Nice Piece of Code.

    Thank you so much for sending me this. Is definitely of great help. I will test it and I will let you know as soon as possible how it goes.

    Regards,

    Daniel Hercules

  • Daniel this code is very helpful to you...
    ANALOG 6-Channel PPM Encoder......Build this code with ccs AND ENJOYYYYYYYYYY......................
    When tx is switched on if the throttle is not at less then 5% of its Max value then PPM Should not Be generated because it will sudden start motor of plane and it can harm the person who hold the plane
    ...... try to implement this logic (Assignment)
    
    
    
    
    
    
    
    
    //********************************************************************************************************************
    //----------------------MSP430 PPM_ENCODER_REV_2 for 1 to 7 Analog channel - Software---------------------------------
    // -------------------------------------------------------------
    //
    // Description:
    // Generate PPM at P1.7.
    // In put Analog Channel at P1.0 ..... P1.6
    // ACLK = n/a, MCLK = SMCLK = default DCO
    //
    // ====IDEAL PPM FRAME====
    //
    // |<--------------------------~20ms------------------------------->|
    // -->| |<--MAX_WIDTH -->| |<--MIN_WIDTH
    // ___ ___ ___ ___ ___ ___ ___ ___
    // | | | | | | | | | | | | | | | |
    //_____| |________| |________| |__ _ ___| |___ _ _ ______________________| |___| |________| |__ _ _ __| |____________
    //
    // ch1 ch2 ch3..6 |<---------RST_WIDTH---------->| -->| |<--CH_GAP
    //
    //___________________________________________________________________
    // Ch | Function | Type |Control |
    //------|--------------------------------|----------|---------------|
    // ch1 | Throttle =| Analog |Pot |
    // ch2 | Standard Servo (ELEVATOR) =| Analog |Pot(Center) |
    // ch3 | Standard Servo (RUDDER) =| Analog |Pot(Center) |
    // ch4 | Standard Servo (ALERON) =| Analog |Pot(Center) |
    // ch5 | Landing Gear =| Analog |Switch |
    // ch6 | Optional =| Analog |Switch |
    //------|--------------------------------|----------|---------------|
    //
    // MSP430x2xx (LaunchPad)
    // -----------------
    // /|\| XIN|-
    // | | |
    // --|RST XOUT|-
    // | |
    // | P1.7|-->PPM_OUT o Vcc
    // | | |
    // | | /
    // | P1.5|<-- CH6(Analog)-------------->\ 10K Pot
    // | P1.4|<-- CH5(Analog) /
    // | P1.3|<-- CH4(Analog) \
    // | P1.2|<-- CH3(Analog) _|_
    // | P1.1|<-- CH2(Analog-Switch) = Gnd
    // | P1.0|<-- CH1(Analog-Switch)
    // | |
    //
    // R.Butani
    // MEFGI Rajkot, India
    // ravi_butani@yahoo.com
    // Jan 2012
    // Built with Code Composer Studio v5
    //********************************************************************************************************************


    #include <msp430.h>
    #include "stdbool.h"
    #define RST_WIDTH 1000 //Reset pulse width for reset RX Decoder after each PPM frame
    #define MIN_WIDTH 20 //Min width of ch pulse for servo at min pos. or cut throttle
    #define MAX_WIDTH 100 //Max width of ch pulse for servo at max pos. or full throttle
    #define CH_GAP 20 //Gap betn two ch

    #define CH 6 //No. of Channels to be used

    #define PPM_OUT 0x80 //P1.7 is used for PPM Output

    bool ADCDone; // ADC Done flag
    unsigned int ADCValue; // Measured ADC Value

    void delayMS(unsigned int us_10 ) // delay function us*10 when mclk = 1MHz
    {
    unsigned int i;
    for (i = 0; i<= us_10; i++)
    __delay_cycles(10);
    }


    void Single_Measure(unsigned int chan) //Reads ADC 'chan' once using AVCC as the reference.
    {
    ADC10CTL1 = chan;
    ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start
    __bis_SR_register(CPUOFF + GIE); // Wait in LPM0 to reduce power consumption
    }

    void main(void) //main code start here
    {
    unsigned char i;
    WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
    ADC10CTL0 = ADC10SHT_2 + ADC10ON + ADC10IE; // ADC10ON, interrupt enabled
    ADC10AE0 |= 0x7f; // PA.1....to....PA.6 ADC option select
    P1DIR |= PPM_OUT; // Set P1.7 to output direction

    while (1) // Generate PPM on P1.7
    {
    P1OUT &= ~PPM_OUT;
    delayMS(RST_WIDTH);
    for(i=1;i<=CH;i++)
    {
    P1OUT |= PPM_OUT;
    delayMS(CH_GAP);
    P1OUT &= ~PPM_OUT;
    switch(i) // Switch depending on command no of channel
    {
    case 0x01:
    Single_Measure(INCH_0); // Read A0 (CH-1 Throttle Pot) once
    break;

    case 0x02:
    Single_Measure(INCH_1); // Read A1 (CH-2 Elevator Pot) once
    break;

    case 0x03:
    Single_Measure(INCH_2); // Read A2 (CH-3 Rudder Pot) once
    break;

    case 0x04:
    Single_Measure(INCH_3); // Read A3 (CH-4 Aleron Pot) once
    break;

    case 0x05:
    Single_Measure(INCH_4); // Read A4 (CH-5 Landing_gear Switch) once
    break;

    case 0x06:
    Single_Measure(INCH_5); // Read A6 (CH-6 Optional Switch) once
    break;

    default:;
    }
    while(!ADCDone); // Wait until ADC Flag set

    ADCDone = false;
    delayMS(MIN_WIDTH); // Add Min_Width Delay for seperate two channel even if ADCValue is 0
    ADCValue=ADCValue/10; // as Max_width = 1ms Delay value shuld be less then 100
    delayMS(ADCValue); // Add delay proportional to CH voltage

    }
    P1OUT |= PPM_OUT;
    delayMS(CH_GAP); // Add Ch_Gap Delay of 0.2 ms
    }
    }

    #pragma vector=ADC10_VECTOR // ADC Interrupt Service routine
    __interrupt void ADC10_ISR (void)
    {
    ADC10CTL0 &= ~ENC; // Disable ADC
    ADCValue = ADC10MEM; // Saves measured value.
    ADCDone = true; // Sets flag for main loop.
    __bic_SR_register_on_exit(CPUOFF); // Enable CPU so the main while loop continues
    }
  • Here is code to generate 8 channel PPM using timer A. Enjoy.

    #include "msp430g2231.h"

    //PPM output pin
    #define PPM_out BIT2
    //PPM period
    unsigned int period = 2500;
    //channel positions (125-150)
    unsigned int ch1_pos = 190;
    unsigned int ch2_pos = 190;
    unsigned int ch3_pos = 190;
    unsigned int ch4_pos = 190;
    unsigned int ch5_pos = 190;
    unsigned int ch6_pos = 190;
    unsigned int ch7_pos = 190;
    unsigned int ch8_pos = 190;

    unsigned int pulse_length = 44;
    unsigned int channel_counter = 0;

    void main(void) {
    WDTCTL = WDTPW + WDTHOLD; // Stop WDT
    BCSCTL1 = CALBC1_1MHZ; //calibration data
    DCOCTL = CALDCO_1MHZ;


    P1DIR |= PPM_out; // Set output direction
    P1OUT &= ~PPM_out; //turn output off

    //setup clock
    CCTL0 |= CCIE; //CCR0 interrupt enabled
    CCTL1 |= CCIE; // CCR1 interrupt enabled
    CCR0 = period; // set period 20ms cycle(2500)
    CCR1 = pulse_length; // CCR1 first pulse
    TACTL = TASSEL_2 | MC_1 | ID_3; // SMCLK/8(125kHz), upmode

    __bis_SR_register(LPM0_bits + GIE); // switch to LPM0 with interrupts

    }


    #pragma vector = TIMERA1_VECTOR
    #pragma vector = TIMERA0_VECTOR
    __interrupt void Timer_A (void) {

    switch( TAIV ) //check interrupt
    {
    case 0: //setup period
    channel_counter = 1; //reset channel counter
    CCR1 = 0;
    CCTL1 |= CCIFG; //generate CCR1 interrupt
    break;

    case 2: // CCR1
    switch(channel_counter){ //start channel count

    case 1: //channel 1**********************************
    if(P1OUT & PPM_out){
    P1OUT &= ~PPM_out; //turn off output
    CCR1 += (ch1_pos-pulse_length); // Add Offset to CCR1
    channel_counter++; //increment counter
    }else{
    P1OUT |= PPM_out; //turn on output
    CCR1 += pulse_length; // Add Offset to CCR1
    }
    break; //***************************************

    case 2: //channel 2**********************************
    if(P1OUT & PPM_out){
    P1OUT &= ~PPM_out; //turn off output
    CCR1 += (ch2_pos-pulse_length); // Add Offset to CCR1
    channel_counter++; //increment counter
    }else{
    P1OUT |= PPM_out; //turn on output
    CCR1 += pulse_length; // Add Offset to CCR1
    }
    break; //***************************************

    case 3: //channel 3**********************************
    if(P1OUT & PPM_out){
    P1OUT &= ~PPM_out; //turn off output
    CCR1 += (ch3_pos-pulse_length); // Add Offset to CCR1
    channel_counter++; //increment counter
    }else{
    P1OUT |= PPM_out; //turn on output
    CCR1 += pulse_length; // Add Offset to CCR1
    }
    break; //***************************************

    case 4: //channel 4**********************************
    if(P1OUT & PPM_out){
    P1OUT &= ~PPM_out; //turn off output
    CCR1 += (ch4_pos-pulse_length); // Add Offset to CCR1
    channel_counter++; //increment counter
    }else{
    P1OUT |= PPM_out; //turn on output
    CCR1 += pulse_length; // Add Offset to CCR1
    }
    break; //***************************************

    case 5: //channel 5**********************************
    if(P1OUT & PPM_out){
    P1OUT &= ~PPM_out; //turn off output
    CCR1 += (ch5_pos-pulse_length); // Add Offset to CCR1
    channel_counter++; //increment counter
    }else{
    P1OUT |= PPM_out; //turn on output
    CCR1 += pulse_length; // Add Offset to CCR1
    }
    break; //***************************************

    case 6: //channel 6**********************************
    if(P1OUT & PPM_out){
    P1OUT &= ~PPM_out; //turn off output
    CCR1 += (ch6_pos-pulse_length); // Add Offset to CCR1
    channel_counter++; //increment counter
    }else{
    P1OUT |= PPM_out; //turn on output
    CCR1 += pulse_length; // Add Offset to CCR1
    }
    break; //***************************************

    case 7: //channel 7**********************************
    if(P1OUT & PPM_out){
    P1OUT &= ~PPM_out; //turn off output
    CCR1 += (ch7_pos-pulse_length); // Add Offset to CCR1
    channel_counter++; //increment counter
    }else{
    P1OUT |= PPM_out; //turn on output
    CCR1 += pulse_length; // Add Offset to CCR1
    }
    break; //***************************************

    case 8: //channel 8**********************************
    if(P1OUT & PPM_out){
    P1OUT &= ~PPM_out; //turn off output
    CCR1 += (ch8_pos-pulse_length); // Add Offset to CCR1
    channel_counter++; //increment counter
    }else{
    P1OUT |= PPM_out; //turn on output
    CCR1 += pulse_length; // Add Offset to CCR1
    }
    break; //***************************************

    case 9: //last channel end pulse********************************************
    if(P1OUT & PPM_out){
    P1OUT &= ~PPM_out; //turn off output
    channel_counter++; //increment counter
    }else{
    P1OUT |= PPM_out; //turn on output
    CCR1 += pulse_length; // Add Offset to CCR1
    }
    break; //***************************************
    }//end channel count switch


    break;

    case 4:break; // CCR2 not used
    case 10:break; // overflow not used
    }

    }

**Attention** This is a public forum