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(&registers, pin);
243 		setOutputValue(&registers, outputState);
244 		setDataDirection(&registers, 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(&registers, pin);
256 		setDataDirection(&registers, 0);
257 		setOutputValue(&registers, 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(&registers, pin);
268 		return getInputValue(&registers);
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(&registers, pin);
380 	set_digital_output_value(&registers, outputState);
381 	set_data_direction(&registers, 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(&registers, pin);
393 	set_data_direction(&registers, 0);
394 	set_digital_output_value(&registers, 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(&registers, pin);
405 	return get_digital_input_value(&registers);
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