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.
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:
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