1 /*
2 OrangutanDigital.h - Library for using the digital I/O lines on the
3 Orangutan LV, SV, SVP, X2, Baby Orangutan B, or 3pi robot. The code
4 is all inline, which lets it compile to very small, fast, efficient
5 assembly code if you use constants as your inputs. For example,
6 the line:
7
8 setOutput(3, HIGH);
9
10 compiles to the assembly:
11
12 sbi 0x0b, 3 ;i.e. PORTD |= 1 << 3;
13 sbi 0x0a, 3 ;i.e. DDRD |= 1 << 3;
14
15 In short, if your inputs are constants, you can use this library in
16 place of raw digital I/O register manipulation without worrying
17 about any significantly increased overhead or processing time.
18 Using variables as inputs can increase overhead and processing time,
19 but the functions in this library allow for simpler programmatic
20 approaches to working with digital I/O, since you no longer have to
21 deal with a multitude of pin-specific registers.
22
23 The digital pins on the AVR default to high-impedance inputs after
24 a power-up or reset.
25 */
26
27 /*
28 * Written by Ben Schmidel, August 11, 2009.
29 * Copyright (c) 2009-2012 Pololu Corporation. For more information, see
30 *
31 * http://www.pololu.com
32 * http://forum.pololu.com
33 * http://www.pololu.com/docs/0J18
34 *
35 * You may freely modify and share this code, as long as you keep this
36 * notice intact (including the two links above). Licensed under the
37 * Creative Commons BY-SA 3.0 license:
38 *
39 * http://creativecommons.org/licenses/by-sa/3.0/
40 *
41 * Disclaimer: To the extent permitted by law, Pololu provides this work
42 * without any warranty. It might be defective, in which case you agree
43 * to be responsible for all resulting costs and damages.
44 */
45
46
47 #ifndef OrangutanDigital_h
48 #define OrangutanDigital_h
49
50 #include "../OrangutanResources/include/OrangutanModel.h"
51 #include <avr/io.h>
52
53 #define INPUT 0
54 #define OUTPUT 1
55 #define LOW 0
56 #define HIGH 1
57 #define TOGGLE 0xFF
58 #define HIGH_IMPEDANCE 0
59 #define PULL_UP_ENABLED 1
60
61 // port D pins
62 #define IO_D0 0
63 #define IO_D1 1
64 #define IO_D2 2
65 #define IO_D3 3
66 #define IO_D4 4
67 #define IO_D5 5
68 #define IO_D6 6
69 #define IO_D7 7
70
71 // port B pins
72 #define IO_B0 8
73 #define IO_B1 9
74 #define IO_B2 10
75 #define IO_B3 11
76 #define IO_B4 12
77 #define IO_B5 13
78
79
80 #if defined(_ORANGUTAN_SVP) || defined(_ORANGUTAN_X2)
81
82 #define IO_B6 14
83 #define IO_B7 15
84
85 // port C pins
86 #define IO_C0 16
87 #define IO_C1 17
88 #define IO_C2 18
89 #define IO_C3 19
90 #define IO_C4 20
91 #define IO_C5 21
92 #define IO_C6 22
93 #define IO_C7 23
94
95 // port A pins
96 #define IO_A0 31
97 #define IO_A1 30
98 #define IO_A2 29
99 #define IO_A3 28
100 #define IO_A4 27
101 #define IO_A5 26
102 #define IO_A6 25
103 #define IO_A7 24
104
105 #else
106
107 // port C pins
108 #define IO_C0 14
109 #define IO_C1 15
110 #define IO_C2 16
111 #define IO_C3 17
112 #define IO_C4 18
113 #define IO_C5 19
114 #define IO_C6 20 // only used if RESET pin is changed to be a digital I/O
115
116 #endif
117
118
119 struct IOStruct
120 {
121 // if these aren't volatile, the compiler sometimes incorrectly optimizes away operations involving these registers:
122 volatile unsigned char* pinRegister;
123 volatile unsigned char* portRegister;
124 volatile unsigned char* ddrRegister;
125 unsigned char bitmask;
126 };
127
128 #ifdef __cplusplus
129
130 class OrangutanDigital
131 {
132 public:
133
134 // constructor (doesn't do anything)
135 OrangutanDigital();
136
137
138 // gets a structure with pointers to the three digital I/O registers associated
139 // with the specified pin (DDR, PORT, and PIN) along with a bitmask with a
140 // 1 in the position of the specified pin and 0s everywhere else.
getIORegisters(struct IOStruct * io,unsigned char pin)141 inline static void getIORegisters(struct IOStruct* io, unsigned char pin)
142 {
143 io->pinRegister = 0;
144 io->portRegister = 0;
145 io->ddrRegister = 0;
146 io->bitmask = 0;
147
148 if (pin < 8) // pin 0 = PD0, ..., 7 = PD7
149 {
150 io->pinRegister = (unsigned char*)&PIND;
151 io->portRegister = (unsigned char*)&PORTD;
152 io->ddrRegister = (unsigned char*)&DDRD;
153 io->bitmask = 1 << pin;
154 }
155
156 #if defined(_ORANGUTAN_SVP) || defined(_ORANGUTAN_X2)
157
158 else if (pin < 16) // pin 8 = PB0, ..., 15 = PB7
159 {
160 io->pinRegister = (unsigned char*)&PINB;
161 io->portRegister = (unsigned char*)&PORTB;
162 io->ddrRegister = (unsigned char*)&DDRB;
163 io->bitmask = 1 << (pin - 8);
164 }
165 else if (pin < 24) // pin 16 = PC0, ..., 23 = PC7
166 {
167 io->pinRegister = (unsigned char*)&PINC;
168 io->portRegister = (unsigned char*)&PORTC;
169 io->ddrRegister = (unsigned char*)&DDRC;
170 io->bitmask = 1 << (pin - 16);
171 }
172 else if (pin < 32) // pin 24 = PA7, ..., 31 = PA0
173 {
174 io->pinRegister = (unsigned char*)&PINA;
175 io->portRegister = (unsigned char*)&PORTA;
176 io->ddrRegister = (unsigned char*)&DDRA;
177 io->bitmask = 1 << (31 - pin);
178 }
179
180 #else
181
182 else if (pin < 14) // pin 8 = PB0, ..., 13 = PB5 (PB6 and PB7 reserved for external clock)
183 {
184 io->pinRegister = (unsigned char*)&PINB;
185 io->portRegister = (unsigned char*)&PORTB;
186 io->ddrRegister = (unsigned char*)&DDRB;
187 io->bitmask = 1 << (pin - 8);
188 }
189 else if (pin < 21) // pin 14 = PC0, ..., 19 = PC5 (PC6 is reset, PC7 doesn't exist)
190 {
191 io->pinRegister = (unsigned char*)&PINC;
192 io->portRegister = (unsigned char*)&PORTC;
193 io->ddrRegister = (unsigned char*)&DDRC;
194 io->bitmask = 1 << (pin - 14);
195 }
196 #endif
197 }
198
199
200 // low-level method for setting the data direction (i.e. input or output) of an pin or set of pins
201 // described by an IOStruct pointer.
setDataDirection(struct IOStruct * ioPin,unsigned char val)202 inline static void setDataDirection(struct IOStruct* ioPin, unsigned char val)
203 {
204 if (val)
205 *(ioPin->ddrRegister) |= ioPin->bitmask;
206 else
207 *(ioPin->ddrRegister) &= ~ioPin->bitmask;
208 }
209
210
211 // low-level method for setting the PORT register value of an pin or set of pins
212 // described by an IOStruct pointer. If the pin is an input, this lets you choose between
213 // setting it as high-impedance (val = 0) or enabling the internal pull-up (val = 1). If the pin is an
214 // output, this lets you choose between driving low (val = 0) and driving high (val = 1).
215 // NOTE: if val is 0xFF (255), this method will toggle the PORT register pin(s).
setOutputValue(struct IOStruct * ioPin,unsigned char val)216 inline static void setOutputValue(struct IOStruct* ioPin, unsigned char val)
217 {
218 if (val == 0xFF)
219 *(ioPin->portRegister) ^= ioPin->bitmask;
220 else if (val)
221 *(ioPin->portRegister) |= ioPin->bitmask;
222 else
223 *(ioPin->portRegister) &= ~ioPin->bitmask;
224 }
225
226
227 // low-level method for reading the value of the PIN register for an pin or set of pins
228 // described by an IOStruct pointer.
getInputValue(struct IOStruct * ioPin)229 inline static unsigned char getInputValue(struct IOStruct* ioPin)
230 {
231 return *(ioPin->pinRegister) & ioPin->bitmask;
232 }
233
234
235 // high-level method for setting the specified pin as an output with the specified output state.
236 // An outputState value of 0 will cause the pin to drive low; a value of 1 will cause the pin to
237 // drive high. A value of 0xFF (255) will toggle the output state of the pin (i.e. high -> low and
238 // low -> high).
setOutput(unsigned char pin,unsigned char outputState)239 inline static void setOutput(unsigned char pin, unsigned char outputState)
240 {
241 struct IOStruct registers;
242 getIORegisters(®isters, pin);
243 setOutputValue(®isters, outputState);
244 setDataDirection(®isters, 1);
245 }
246
247
248 // high-level method for setting the specified pin as an input with the specified input state.
249 // An inputState value of 0 will cause the pin to be a high-impedance input; a value of 1 will enable the
250 // pin's internal pull-up resistor, which weakly pulls it to Vcc. A value of 0xFF (255) will toggle the
251 // input state.
setInput(unsigned char pin,unsigned char inputState)252 inline static void setInput(unsigned char pin, unsigned char inputState)
253 {
254 struct IOStruct registers;
255 getIORegisters(®isters, pin);
256 setDataDirection(®isters, 0);
257 setOutputValue(®isters, inputState);
258 }
259
260
261 // high-level method for reading the input value of the specified pin. If the voltage on the pin is low,
262 // this method will return 0. Otherwise, it will return a non-zero result that depends on the value of
263 // the pin.
isInputHigh(unsigned char pin)264 inline static unsigned char isInputHigh(unsigned char pin)
265 {
266 struct IOStruct registers;
267 getIORegisters(®isters, pin);
268 return getInputValue(®isters);
269 }
270
271 };
272
273 extern "C" {
274 #endif // __cplusplus
275
276 // gets a structure with pointers to the three digital I/O registers associated
277 // with the specified pin (DDR, PORT, and PIN) along with a bitmask with a
278 // 1 in the position of the specified pin and 0s everywhere else.
get_io_registers(struct IOStruct * io,unsigned char pin)279 static inline void get_io_registers(struct IOStruct* io, unsigned char pin)
280 {
281 io->pinRegister = 0;
282 io->portRegister = 0;
283 io->ddrRegister = 0;
284 io->bitmask = 0;
285
286 if (pin < 8) // pin 0 = PD0, ..., 7 = PD7
287 {
288 io->pinRegister = (unsigned char*)&PIND;
289 io->portRegister = (unsigned char*)&PORTD;
290 io->ddrRegister = (unsigned char*)&DDRD;
291 io->bitmask = 1 << pin;
292 }
293
294 #if defined(_ORANGUTAN_SVP) || defined(_ORANGUTAN_X2)
295 else if (pin < 16) // pin 8 = PB0, ..., 15 = PB7
296 {
297 io->pinRegister = (unsigned char*)&PINB;
298 io->portRegister = (unsigned char*)&PORTB;
299 io->ddrRegister = (unsigned char*)&DDRB;
300 io->bitmask = 1 << (pin - 8);
301 }
302 else if (pin < 24) // pin 16 = PC0, ..., 23 = PC7
303 {
304 io->pinRegister = (unsigned char*)&PINC;
305 io->portRegister = (unsigned char*)&PORTC;
306 io->ddrRegister = (unsigned char*)&DDRC;
307 io->bitmask = 1 << (pin - 16);
308 }
309 else if (pin < 32) // pin 24 = PA7, ..., 31 = PA0
310 {
311 io->pinRegister = (unsigned char*)&PINA;
312 io->portRegister = (unsigned char*)&PORTA;
313 io->ddrRegister = (unsigned char*)&DDRA;
314 io->bitmask = 1 << (31 - pin);
315 }
316
317 #else
318
319 else if (pin < 14) // pin 8 = PB0, ..., 13 = PB5 (PB6 and PB7 reserved for external clock)
320 {
321 io->pinRegister = (unsigned char*)&PINB;
322 io->portRegister = (unsigned char*)&PORTB;
323 io->ddrRegister = (unsigned char*)&DDRB;
324 io->bitmask = 1 << (pin - 8);
325 }
326 else if (pin < 21) // pin 14 = PC0, ..., 19 = PC5 (PC6 is reset, PC7 doesn't exist)
327 {
328 io->pinRegister = (unsigned char*)&PINC;
329 io->portRegister = (unsigned char*)&PORTC;
330 io->ddrRegister = (unsigned char*)&DDRC;
331 io->bitmask = 1 << (pin - 14);
332 }
333 #endif
334 }
335
336
337 // low-level method for setting the data direction (i.e. input or output) of an pin or set of pins
338 // described by an IOStruct pointer.
set_data_direction(struct IOStruct * ioPin,unsigned char val)339 static inline void set_data_direction(struct IOStruct* ioPin, unsigned char val)
340 {
341 if (val)
342 *(ioPin->ddrRegister) |= ioPin->bitmask;
343 else
344 *(ioPin->ddrRegister) &= ~ioPin->bitmask;
345 }
346
347
348 // low-level method for setting the PORT register value of an pin or set of pins
349 // described by an IOStruct pointer. If the pin is an input, this lets you choose between
350 // setting it as high-impedance (val = 0) or enabling the internal pull-up (val = 1). If the pin is an
351 // output, this lets you choose between driving low (val = 0) and driving high (val = 1).
352 // NOTE: if val is 0xFF (255), this method will toggle the PORT register pin(s).
set_digital_output_value(struct IOStruct * ioPin,unsigned char val)353 static inline void set_digital_output_value(struct IOStruct* ioPin, unsigned char val)
354 {
355 if (val == 0xFF)
356 *(ioPin->portRegister) ^= ioPin->bitmask;
357 else if (val)
358 *(ioPin->portRegister) |= ioPin->bitmask;
359 else
360 *(ioPin->portRegister) &= ~ioPin->bitmask;
361 }
362
363
364 // low-level method for reading the value of the PIN register for an pin or set of pins
365 // described by an IOStruct pointer.
get_digital_input_value(struct IOStruct * ioPin)366 static inline unsigned char get_digital_input_value(struct IOStruct* ioPin)
367 {
368 return *(ioPin->pinRegister) & ioPin->bitmask;
369 }
370
371
372 // high-level method for setting the specified pin as an output with the specified output state.
373 // An outputState value of 0 will cause the pin to drive low; a value of 1 will cause the pin to
374 // drive high. A value of 0xFF (255) will toggle the output state of the pin (i.e. high -> low and
375 // low -> high).
set_digital_output(unsigned char pin,unsigned char outputState)376 static inline void set_digital_output(unsigned char pin, unsigned char outputState)
377 {
378 struct IOStruct registers;
379 get_io_registers(®isters, pin);
380 set_digital_output_value(®isters, outputState);
381 set_data_direction(®isters, 1);
382 }
383
384
385 // high-level method for setting the specified pin as an input with the specified input state.
386 // An inputState value of 0 will cause the pin to be a high-impedance input; a value of 1 will enable the
387 // pin's internal pull-up resistor, which weakly pulls it to Vcc. A value of 0xFF (255) will toggle the
388 // input state.
set_digital_input(unsigned char pin,unsigned char inputState)389 static inline void set_digital_input(unsigned char pin, unsigned char inputState)
390 {
391 struct IOStruct registers;
392 get_io_registers(®isters, pin);
393 set_data_direction(®isters, 0);
394 set_digital_output_value(®isters, inputState);
395 }
396
397
398 // high-level method for reading the input value of the specified pin. If the voltage on the pin is low,
399 // this method will return 0. Otherwise, it will return a non-zero result that depends on the value of
400 // the pin.
is_digital_input_high(unsigned char pin)401 static inline unsigned char is_digital_input_high(unsigned char pin)
402 {
403 struct IOStruct registers;
404 get_io_registers(®isters, pin);
405 return get_digital_input_value(®isters);
406 }
407
408 #ifdef __cplusplus
409 }
410 #endif
411
412 #endif
413
414 // Local Variables: **
415 // mode: C++ **
416 // c-basic-offset: 4 **
417 // tab-width: 4 **
418 // indent-tabs-mode: t **
419 // end: **
420