/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-20 23:59:59 UTC
  • Revision ID: tim@ed.am-20131120235959-hh2y1ickvxf2z0lt
added pulse-width reading test program that reads all 8 channels

Show diffs side-by-side

added added

removed removed

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