1// ---------------------------------------------------------------------------
2// Created by Francisco Malpartida on 20/08/11.
3// Copyright 2011 - Under creative commons license 3.0:
4// Attribution-ShareAlike CC BY-SA
5//
6// This software is furnished "as is", without technical support, and with no
7// warranty, express or implied, as to its usefulness for any purpose.
8//
9// Thread Safe: No
10// Extendable: Yes
11//
12// @file I2CIO.h
13// This file implements a basic IO library using the PCF8574 I2C IO Expander
14// chip.
15//
16// @brief
17// Implement a basic IO library to drive the PCF8574* I2C IO Expander ASIC.
18// The library implements basic IO general methods to configure IO pin direction
19// read and write uint8_t operations and basic pin level routines to set or read
20// a particular IO port.
21//
22//
23// @version API 1.0.0
24//
25// @author F. Malpartida - fmalpartida@gmail.com
26// ---------------------------------------------------------------------------
27#if (ARDUINO < 100)
28#include <WProgram.h>
29#else
30#include <Arduino.h>
31#endif
32
33#include <inttypes.h>
34
35#include <Wire.h>
36#include "Visuino_I2CIO.h"
37
38// CLASS VARIABLES
39// ---------------------------------------------------------------------------
40
41
42// CONSTRUCTOR
43// ---------------------------------------------------------------------------
44I2CIO::I2CIO ( )
45{
46 _i2cAddr = 0x0;
47 _dirMask = 0xFF; // mark all as INPUTs
48 _shadow = 0x0; // no values set
49 _initialised = false;
50}
51
52// PUBLIC METHODS
53// ---------------------------------------------------------------------------
54
55//
56// begin
57int I2CIO::begin ( uint8_t i2cAddr )
58{
59 _i2cAddr = i2cAddr;
60
61 Wire.begin ( );
62
63 _initialised = Wire.requestFrom ( _i2cAddr, (uint8_t)1 );
64
65#if (ARDUINO < 100)
66 _shadow = Wire.receive ();
67#else
68 _shadow = Wire.read (); // Remove the byte read don't need it.
69#endif
70
71 return ( _initialised );
72}
73
74//
75// pinMode
76void I2CIO::pinMode ( uint8_t pin, uint8_t dir )
77{
78 if ( _initialised )
79 {
80 if ( OUTPUT == dir )
81 {
82 _dirMask &= ~( 1 << pin );
83 }
84 else
85 {
86 _dirMask |= ( 1 << pin );
87 }
88 }
89}
90
91//
92// portMode
93void I2CIO::portMode ( uint8_t dir )
94{
95
96 if ( _initialised )
97 {
98 if ( dir == INPUT )
99 {
100 _dirMask = 0xFF;
101 }
102 else
103 {
104 _dirMask = 0x00;
105 }
106 }
107}
108
109//
110// read
111uint8_t I2CIO::read ( void )
112{
113 uint8_t retVal = 0;
114
115 if ( _initialised )
116 {
117 Wire.requestFrom ( _i2cAddr, (uint8_t)1 );
118#if (ARDUINO < 100)
119 retVal = ( _dirMask & Wire.receive ( ) );
120#else
121 retVal = ( _dirMask & Wire.read ( ) );
122#endif
123
124 }
125 return ( retVal );
126}
127
128//
129// write
130int I2CIO::write ( uint8_t value )
131{
132 int status = 0;
133
134 if ( _initialised )
135 {
136 // Only write HIGH the values of the ports that have been initialised as
137 // outputs updating the output shadow of the device
138 _shadow = ( value & ~(_dirMask) );
139
140 Wire.beginTransmission ( _i2cAddr );
141#if (ARDUINO < 100)
142 Wire.send ( _shadow );
143#else
144 Wire.write ( _shadow );
145#endif
146 status = Wire.endTransmission ();
147 }
148 return ( (status == 0) );
149}
150
151//
152// digitalRead
153uint8_t I2CIO::digitalRead ( uint8_t pin )
154{
155 uint8_t pinVal = 0;
156
157 // Check if initialised and that the pin is within range of the device
158 // -------------------------------------------------------------------
159 if ( ( _initialised ) && ( pin <= 7 ) )
160 {
161 // Remove the values which are not inputs and get the value of the pin
162 pinVal = this->read() & _dirMask;
163 pinVal = ( pinVal >> pin ) & 0x01; // Get the pin value
164 }
165 return (pinVal);
166}
167
168//
169// digitalWrite
170int I2CIO::digitalWrite ( uint8_t pin, uint8_t level )
171{
172 uint8_t writeVal;
173 int status = 0;
174
175 // Check if initialised and that the pin is within range of the device
176 // -------------------------------------------------------------------
177 if ( ( _initialised ) && ( pin <= 7 ) )
178 {
179 // Only write to HIGH the port if the port has been configured as
180 // an OUTPUT pin. Add the new state of the pin to the shadow
181 writeVal = ( 1 << pin ) & ~_dirMask;
182 if ( level == HIGH )
183 {
184 _shadow |= writeVal;
185
186 }
187 else
188 {
189 _shadow &= ~writeVal;
190 }
191 status = this->write ( _shadow );
192 }
193 return ( status );
194}
195
196//
197// PRIVATE METHODS
198// ---------------------------------------------------------------------------