Beruflich Dokumente
Kultur Dokumente
For
radio receiver, there are a few output signal formats. The traditional and also most common type
of RX signal is the PWM and basically PWM requires 1 cable per channel. PPM is now getting
more and more popular, because it can handle all 8 channels in 1 signal wire.
You can buy a commercially ready PWM to PPM converter (which also does SBUS output as
well). But for those who enjoy tinkering and DIY, here is a fun project for you.
Al Prettybong on Multicopter International Group shared with me how he made a PWM to PPM
converter using an Arduino Pro Mini, and I thought I should share this with everyone.
I havent tested this code yet, but Al told me there is NO changes to the code, just copy and
upload it to your Arduino and it will work.
// --------------------------------------------------------------------------
-----------------------------------------------------------------------------
-----
// 8 Channel PWM to 1 channel PPM converter for RC receivers, using Arduino
//
//
// ..and has been hacked code to:
// only support Atmel328 chips ( as found on Arduino Duemilanove or Arduino
Uno ) chips
// not support any "error" mode/s, just 8 PWM-IN channels TO one single
PPM OUT
// not support any LED indicators m just PWM-IN, and PPM-OUT
// Integrated the one library that is used to the sketch, for easy user
experience.
// made it Arduino IDE compatible, so it uses standard bootloader and
Serial uploader, like all realy Arduino/s.
// make compile-time option to either "hold last good PPM value" or "hold
default value/s" in case of
// no actual input signal for each channel. see FAILHOLD and FAILCENTRE
in .h file
// .
// --------------------------------------------------------------------------
-----------------------------------------------------------------------------
-----
// PREPROCESSOR DIRECTIVES
// --------------------------------------------------------------------------
-----------------------------------------------------------------------------
-----
#include "Arduino.h"
#include "ppm_encoder.h"
#include <util/delay.h>
#include <avr/io.h>
#define ERROR_THRESHOLD 2
// Number of servo input errors before alerting
#define ERROR_DETECTION_WINDOW 3000 * LOOP_TIMER_10MS //
Detection window for error detection (default to 30s)
#define ERROR_CONDITION_DELAY 500 * LOOP_TIMER_10MS // Servo
error condition LED delay (LED blinking duration)
// Timers
// LED Code
// --------------------------------------------------------------------------
-----------------------------------------------------------------------------
-----
// PPM ENCODER INIT AND AUXILIARY TASKS
// --------------------------------------------------------------------------
-----------------------------------------------------------------------------
-----
// -------------------------------------------------------------------
-----------------------------------------------------------------------------
------------
// LOCAL VARIABLES
// -------------------------------------------------------------------
-----------------------------------------------------------------------------
------------
bool localinit = true; // We are inside init sequence
bool mux_passthrough = false; // Mux passthrough mode status Flag :
passthrough is off
uint16_t led_acceleration; // Led acceleration based on throttle stick
position
bool servo_error_condition = false; // Servo signal error
condition
#ifdef PASSTHROUGH_MODE_ENABLED
static uint8_t mux_timer = 0; // Mux timer
static uint8_t mux_counter = 0; // Mux
counter
static int8_t mux_check = 0;
static uint16_t mux_ppm = 500;
#endif
// -------------------------------------------------------------------
-----------------------------------------------------------------------------
------------
// LOCAL FUNCTIONS
// -------------------------------------------------------------------
-----------------------------------------------------------------------------
------------
// -------------------------------------------------------------------
-----------
// Led blinking (non blocking) function
// -------------------------------------------------------------------
-----------
blink_led_timer++;
// -------------------------------------------------------------------
-----------
// Led code (non blocking) function
// -------------------------------------------------------------------
-----------
// PPM_PASSTROUGH_MODE
{ INTER_CODE, LONG_SYMBOL, LONG_SPACE, SHORT_SYMBOL,
SHORT_SPACE, SHORT_SYMBOL, LOOP },
// JETI_MODE
{ INTER_CODE, LONG_SYMBOL, LONG_SPACE, SHORT_SYMBOL,
SHORT_SPACE, SHORT_SYMBOL, SHORT_SPACE, SHORT_SYMBOL,LOOP }
};
led_code_timer++;
// -------------------------------------------------------------------
-----------
// ppm reading helper - interrupt safe and non blocking function
// -------------------------------------------------------------------
-----------
uint16_t ppm_read( uint8_t channel )
{
uint16_t ppm_tmp = ppm[ channel ];
while( ppm_tmp != ppm[ channel ] ) ppm_tmp = ppm[ channel ];
return ppm_tmp;
}
// -------------------------------------------------------------------
-----------------------------------------------------------------------------
------------
// INITIALISATION CODE
// -------------------------------------------------------------------
-----------------------------------------------------------------------------
------------
void setup() {
// -------------------------------------------------------------------
-----------
// Reset Source checkings
// -------------------------------------------------------------------
-----------
if (MCUSR & 1) // Power-on Reset
{
MCUSR=0; // Clear MCU Status register
// custom code here
}
else if (MCUSR & 2) // External Reset
{
MCUSR=0; // Clear MCU Status register
// custom code here
}
else if (MCUSR & 4) // Brown-Out Reset
{
MCUSR=0; // Clear MCU Status register
brownout_reset=true;
}
else // Watchdog Reset
{
MCUSR=0; // Clear MCU Status register
// custom code here
}
// -------------------------------------------------------------------
-----------
// Servo input and PPM generator init
// -------------------------------------------------------------------
-----------
ppm_encoder_init();
// -------------------------------------------------------------------
-----------
// Outputs init
// -------------------------------------------------------------------
-----------
PPM_DDR |= ( 1 << PB0 ); // Set LED pin (PB0) to output
PPM_DDR |= ( 1 << PB1 ); // Set MUX pin (PB1) to output
PPM_DDR |= ( 1 << PPM_OUTPUT_PIN ); // Set PPM pin (PPM_OUTPUT_PIN,
OC1B) to output
// -------------------------------------------------------------------
-----------
// Timer0 init (normal mode) used for LED control and custom code
// -------------------------------------------------------------------
-----------
TCCR0A = 0x00; // Clock source: System Clock
TCCR0B = 0x05; // Set 1024x prescaler - Clock value: 15.625 kHz - 16
ms max time
TCNT0 = 0x00;
OCR0A = 0x00; // OC0x outputs: Disconnected
OCR0B = 0x00;
TIMSK0 = 0x00; // Timer 1 interrupt disable
// -------------------------------------------------------------------
-----------
// Enable global interrupt
// -------------------------------------------------------------------
-----------
sei(); // Enable Global interrupt flag
// -------------------------------------------------------------------
-----------
// Disable radio passthrough (mux chip A/B control)
// -------------------------------------------------------------------
-----------
PPM_PORT |= ( 1 << PB1 ); // Set PIN B1 to disable Radio
passthrough (mux)
void loop() {
// -------------------------------------------------------------------
-----------------------------------------------------------------------------
------------
// AUXILIARY TASKS
// -------------------------------------------------------------------
-----------------------------------------------------------------------------
------------
PWM_LOOP: // SERVO_PWM_MODE
while( 1 )
{
And then create a new tab in your Arduino IDE and copy this, name it ppm_encoder.h, and
copy the following code in the tab.
// -------------------------------------------------------------
#ifndef _PPM_ENCODER_H_
#define _PPM_ENCODER_H_
#include <avr/io.h>
// -------------------------------------------------------------
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <util/delay.h>
// -------------------------------------------------------------
// SERVO INPUT FILTERS
// -------------------------------------------------------------
// Using both filters is not recommended and may reduce servo input
resolution
#ifndef F_CPU
#define F_CPU 16000000UL
#endif
#ifndef true
#define true 1
#endif
#ifndef false
#define false 0
#endif
//#ifndef bool
//#define bool boolean
//#endif
// 328 does not define PBX but defines an equivalent as PORTBX, comment these
lines out if you already have a PB2 defined.
#define PB2 PORTB2
#define PB1 PORTB1
#define PB0 PORTB0
// -------------------------------------------------------------
// SERVO INPUT MODE - !EXPERIMENTAL!
// -------------------------------------------------------------
// -------------------------------------------------------------
#else
#error NO SUPPORTED DEVICE FOUND! ( ATmega328/p)
#endif
// --------------------------------------------------------------------------
----
// PPM GENERATOR START - TOGGLE ON COMPARE INTERRUPT ENABLE
// --------------------------------------------------------------------------
----
// this starts OUTGOING PPM stream on PPM_PORT (PORTB, Arduino D8-D13) at
PPM_OUTPUT_PIN (PB2, arduino pin D10)
void ppm_start( void )
{
// Prevent reenabling an already active PPM generator
if( ppm_generator_active ) return;
// Stop interrupts
cli();
}
// --------------------------------------------------------------------------
----
// PPM GENERATOR STOP - TOGGLE ON COMPARE INTERRUPT DISABLE
// --------------------------------------------------------------------------
----
void ppm_stop( void )
{
// Store interrupt status and register flags
uint8_t SREG_tmp = SREG;
// Stop interrupts
cli();
// --------------------------------------------------------------------------
----
// Watchdog Interrupt (interrupt only mode, no reset)
// --------------------------------------------------------------------------
----
ISR( WDT_vect ) // If watchdog is triggered then enable missing signal flag
and copy power on or failsafe positions
{
// Use failsafe values if PPM generator is active or if chip has been
reset from a brown-out
if ( ppm_generator_active || brownout_reset )
{
// Copy failsafe values to ppm[..]
for ( uint8_t i = 0; i < PPM_ARRAY_MAX; i++ ) { ppm[ i ] =
failsafe_ppm[ i ]; } } // Set missing receiver signal flag
servo_input_missing = true; // Reset servo input error flag
//servo_input_errors = 0; } // ----------------------------------------------
-------------------------------- // -----------------------------------------
------------------------------------- // SERVO/PPM INPUT - PIN CHANGE
INTERRUPT, for any Arduino pin D0 -> D7
// --------------------------------------------------------------------------
----
ISR( SERVO_INT_VECTOR )
{
// ----------------------------------------------------------------------
--------
// SERVO PWM MODE
// ----------------------------------------------------------------------
--------
CHECK_PINS_START: // Start of servo input check
#ifdef _JITTER_FILTER_
// 0.5us cut filter to remove input jitter
int16_t ppm_tmp = ppm[ _ppm_channel ] - servo_width;
if( ppm_tmp == 1 ) goto CHECK_PINS_NEXT;
if( ppm_tmp == -1 ) goto CHECK_PINS_NEXT;
#endif
// Update ppm[..]
ppm[ _ppm_channel ] = servo_width;
}
}
CHECK_PINS_NEXT:
// --------------------------------------------------------------------------
----
// PPM READ - INTERRUPT SAFE PPM SERVO CHANNEL READ
// --------------------------------------------------------------------------
----
/* uint16_t ppm_read_channel( uint8_t channel )
{
// Limit channel to valid value
uint8_t _channel = channel;
if( _channel == 0 ) _channel = 1;
if( _channel > SERVO_CHANNELS ) _channel = SERVO_CHANNELS;
// --------------------------------------------------------------------------
----
// PPM ENCODER INIT
// --------------------------------------------------------------------------
----
void ppm_encoder_init( void )
{
// ----------------------------------------------------------------------
--------
// Enable watchdog interrupt mode
// ----------------------------------------------------------------------
--------
// Disable watchdog
wdt_disable();
// Reset watchdog timer
wdt_reset();
// Start timed watchdog setup sequence
WDTCSR |= (1<<WDCE) | (1<<WDE );
// Set 250 ms watchdog timeout and enable interrupt
WDTCSR = (1<<WDIE) | (1<<WDP2);
}
//
5V and GND on receiver is connected to RAW and GND pins on the Arduino board. Ch1 to
Ch8 is connected D0 to D7 on the Arduino. (if you are using receiver that has fewer channels,
you dont have to worry about the rest of the pins on the Arduino)
For a more detail connection diagram, check out the top picture in this article.
He removed all the servo pins on this receiver, and soldered direct the arduino pwm to ppm
converter. He said this has been running successfully for about 8 months, and i did the same
conversion on an 8 channel receiver also has failsafe built in to the pwm ppm converter. Failsafe
channel can be set up in the code.
There are 3 wires that will be connected to the FC, 5V, GND and PPM (Green).
And finally he put heatshrink over this unit, and now this cheap PWM RX has turned into a
powerful PPM RX :) He put clear heatshrink in the middle so he could see the status LEDs.
I havent done this project myself yet, but if you have any questions about this project, please
join Multicopter International, and ask Al himself