/elec/quadcopter

To get this branch, use:
bzr branch http://bzr.ed.am/elec/quadcopter
11 by Tim Marston
added pulse-width reading test program that reads all 8 channels
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
}