/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: dan
  • Date: 2013-10-16 19:34:55 UTC
  • Revision ID: dan-20131016193455-dpdna7f6tzdxr2re
Added notes

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
 
}