/elec/quadcopter

To get this branch, use:
bzr branch http://bzr.ed.am/elec/quadcopter

« back to all changes in this revision

Viewing changes to test/receiver/pulse-width-x8/main.ino

  • Committer: Tim Marston
  • Date: 2013-11-21 00:05:58 UTC
  • Revision ID: tim@ed.am-20131121000558-06xtcb4fue72td7q
updated 8-channel pulse width reading test program's comments

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//
 
2
// main.ino
 
3
//
 
4
// Testing to see if we can read all 8 channels, multiplexed together on to two
 
5
// interrupts (odd channels on one, and even on the other).  We read the
 
6
// channels by measuring the pulse width.
 
7
//
 
8
// Ideally, it would be nice if we could read the raw PPM stream from the
 
9
// receiver.  Sometimes you can open up your receiver and get to this stream,
 
10
// but we have been unable.  And since this isn't guaranteed to ever be
 
11
// available, reading the analogue outputs is certainly more portable.  So, we
 
12
// are multiplexing the separate channels to two streams of pulses, like this:
 
13
//
 
14
//                  ch1  ch3  ch5  ch7  ch2  ch4  ch6  ch8
 
15
//                   |    |    |    |    |    |    |    |
 
16
//                   ▼    ▼    ▼    ▼    ▼    ▼    ▼    ▼
 
17
//                   ¯    ¯    ¯    ¯    ¯    ¯    ¯    ¯
 
18
//            ___    |    |    |    |    |    |    |    |     ___
 
19
//    GND ---|___|---+----+----+----+    +----+----+----+----|___|--- GND
 
20
//             R                    |    |                     R
 
21
//             ____________________ | __ | _____________________
 
22
//                                  |    |
 
23
//                  (int. 0) pin 2  o    o  pin 3 (int. 1)
 
24
//
 
25
// The two resistors in the circuit are pull-down resistors.  Without them, the
 
26
// Arduino is unable to read the pulse wave (I guess because of the diodes --
 
27
// it's fine without them!).
 
28
//
 
29
// (Note that on our receiver, and contrary to the above diagram, the channels
 
30
// are not actually sent in order.  Channels 2 and 3 are reversed.  To this end,
 
31
// the order that channels are received can be adjusted below.)
 
32
 
 
33
 
 
34
#include <limits.h>
 
35
 
 
36
 
 
37
// minimum pulse width (in ms), used to weed out crappy signals
 
38
#define MIN_PULSE_WIDTH 1000UL
 
39
 
 
40
// maximum pulse width (in ms), used to weed out crappy signals
 
41
#define MAX_PULSE_WIDTH 2000UL
 
42
 
 
43
// number of channels
 
44
#define NUM_CHANNELS 8
 
45
 
 
46
// minimum frame gap time (in ms), used to check that the frame gap is where we
 
47
// expect it to be and that we have read the channels properly
 
48
#define MIN_FRAME_GAP_WIDTH ( 4000UL + MIN_PULSE_WIDTH )
 
49
 
 
50
// the width of the display of a single channel (in chars)
 
51
#define GRAPH_SIZE 7
 
52
 
 
53
// the channel's expected range for use in drawing (should be similar to
 
54
// {MAX,MIN}_PULSE_WIDTH values)
 
55
#define GRAPH_MIN 1000
 
56
#define GRAPH_MAX 2000
 
57
 
 
58
 
 
59
// channel sequence order
 
60
const int channel_order_[] = { 1, 3, 2, 4, 5, 6, 7, 8 };
 
61
 
 
62
// set to the time of the last pulse edges
 
63
static unsigned long new_pulse_up_[2] = { 0, 0 };
 
64
static unsigned long new_pulse_down_ = 0;
 
65
static char new_pulse_interrupt_;
 
66
 
 
67
 
 
68
// ISR to handle the PPM signals
 
69
inline void signal_handler( int interrupt, int pin )
 
70
{
 
71
        // record rising/falling edge
 
72
        if( digitalRead( pin ) )
 
73
                new_pulse_up_[ interrupt ] = micros();
 
74
        else {
 
75
                new_pulse_down_ = micros();
 
76
 
 
77
                // record which interrupt just had a falling edge
 
78
                new_pulse_interrupt_ = interrupt;
 
79
        }
 
80
}
 
81
void signal_handler_0()
 
82
{
 
83
        signal_handler( 0, 2 );
 
84
}
 
85
void signal_handler_1()
 
86
{
 
87
        signal_handler( 1, 3 );
 
88
}
 
89
 
 
90
 
 
91
void setup()
 
92
{
 
93
        // set up an interrupts
 
94
        attachInterrupt( 0, signal_handler_0, CHANGE );
 
95
        attachInterrupt( 1, signal_handler_1, CHANGE );
 
96
        digitalWrite( 2, LOW );
 
97
        digitalWrite( 3, LOW );
 
98
 
 
99
        Serial.begin( 9600 );
 
100
}
 
101
 
 
102
 
 
103
unsigned long calculate_duration( unsigned long then, unsigned long now )
 
104
{
 
105
        return now - then;
 
106
        
 
107
        if( now < then )
 
108
                return now + ( ULONG_MAX - then );
 
109
        else
 
110
                return now - then;
 
111
}
 
112
 
 
113
 
 
114
static int count_ = 0, good_ = 0;
 
115
 
 
116
 
 
117
bool read_channels( unsigned long channel_values[] )
 
118
{
 
119
        static unsigned long last_pulse_down = 0;
 
120
        static int next_channel = 0;
 
121
 
 
122
        // capture pulse values atomically
 
123
        noInterrupts();
 
124
        unsigned long pulse_up = new_pulse_up_[ new_pulse_interrupt_ ];
 
125
        unsigned long pulse_down = new_pulse_down_;
 
126
        char new_pulse_interrupt = new_pulse_interrupt_;
 
127
        interrupts();
 
128
 
 
129
        // if the amount of time that has passed since the last falling edge is
 
130
        // greater than the frame gap, reset the next channel so that we can start
 
131
        // reading them again
 
132
        if( next_channel &&
 
133
                new_pulse_interrupt == 1 &&
 
134
                calculate_duration( pulse_down, micros() ) > MIN_FRAME_GAP_WIDTH )
 
135
        {
 
136
                // reset the next channel (which restarts reading them)
 
137
                next_channel = 0;
 
138
        }
 
139
 
 
140
        // check for a new complete pulse
 
141
        if( pulse_down != last_pulse_down )
 
142
        {
 
143
                // are there still pulses to read?
 
144
                if( next_channel < NUM_CHANNELS )
 
145
                {
 
146
                        unsigned long duration =
 
147
                                calculate_duration( pulse_up, pulse_down );
 
148
 
 
149
                        // does this pulse look ok?
 
150
                        if( duration >= MIN_PULSE_WIDTH &&
 
151
                                duration <= MAX_PULSE_WIDTH )
 
152
                        {
 
153
                                // store channel value
 
154
                                int channel = channel_order_[ next_channel ] - 1;
 
155
                                channel_values[ channel ] = duration;
 
156
 
 
157
                                // we got a channel
 
158
                                next_channel++;
 
159
                        }
 
160
                        else {
 
161
                                // set invalid channel number (to indicate error)
 
162
                                next_channel = NUM_CHANNELS + 1;
 
163
                        }
 
164
                }
 
165
 
 
166
                last_pulse_down = pulse_down;
 
167
        }
 
168
 
 
169
        // if we've read a frame, invalidate the frame (so we don't report it a
 
170
        // second time) and return true
 
171
        if( next_channel == NUM_CHANNELS ) {
 
172
                next_channel++;
 
173
                return true;
 
174
        }
 
175
        
 
176
        return false;
 
177
}
 
178
 
 
179
 
 
180
void draw_graph( unsigned long channel_values[] )
 
181
{
 
182
        // init graph
 
183
        static char graph[ GRAPH_SIZE + 2 ];
 
184
        static char inited_graph = false;
 
185
        if( !inited_graph ) {
 
186
                for( int a = 1; a < GRAPH_SIZE + 1; a++ )
 
187
                        graph[ a ] = '_';
 
188
                graph[ 0 ] = '|';
 
189
                graph[ GRAPH_SIZE + 1 ] = 0;
 
190
                inited_graph = true;
 
191
        }
 
192
 
 
193
        // draw channels
 
194
        for( int a = 0; a < NUM_CHANNELS; a++ ) {
 
195
                unsigned long value = max( 0,
 
196
                        min( channel_values[ a ], GRAPH_MAX ) - GRAPH_MIN );
 
197
                int pos = ( GRAPH_SIZE ) * value / ( GRAPH_MAX - GRAPH_MIN );
 
198
                graph[ pos + 1 ] = '^';
 
199
                Serial.print( graph );
 
200
                graph[ pos + 1 ] = '_';
 
201
        }
 
202
        Serial.println( "|" );
 
203
}
 
204
 
 
205
 
 
206
void loop()
 
207
{
 
208
        unsigned long channel_values[ NUM_CHANNELS ];
 
209
 
 
210
        while( true )
 
211
        {
 
212
                if( read_channels( channel_values ) )
 
213
                        draw_graph( channel_values );
 
214
        }
 
215
}