Skip to content

Commit 96524bc

Browse files
mpbraendliahmedcharles
authored andcommitted
Add MCP23008 support.
1 parent 722428e commit 96524bc

File tree

5 files changed

+172
-4
lines changed

5 files changed

+172
-4
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ display.write_str(msg, delay.as_mut()).await;
9090

9191
### Features
9292
- 4-bit & 8-bit modes are supported
93-
- Support for i2c backpacks
93+
- Support for I2C backpacks based on PCF8574 and MCP23008 port expanders
9494
- Non-blocking API
9595

9696
### Todo

src/bus/i2c.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,21 @@ use embedded_hal::blocking::i2c::Write;
33

44
use crate::{bus::DataBus, error::Result};
55

6+
/// This module supports I2C backpacks with a PCF8574 IC.
7+
/// Connections as follows:
8+
///
9+
/// <table>
10+
/// <tr><th>PCF8574 pin</th><th>name</th><th>LCD pin</th></tr>
11+
/// <tr><td>P0</td><td>RS</td><td>4</td></tr>
12+
/// <tr><td>P1</td><td>RW</td><td>5</td></tr>
13+
/// <tr><td>P2</td><td>E</td><td>6</td></tr>
14+
/// <tr><td>P3</td><td>Backlight</td><td></td></tr>
15+
/// <tr><td>P4</td><td>DB4</td><td>11</td></tr>
16+
/// <tr><td>P5</td><td>DB5</td><td>12</td></tr>
17+
/// <tr><td>P6</td><td>DB6</td><td>13</td></tr>
18+
/// <tr><td>P7</td><td>DB7</td><td>14</td></tr>
19+
/// </table>
20+
621
pub struct I2CBus<I2C: Write> {
722
i2c_bus: I2C,
823
address: u8,

src/bus/i2c_mcp23008.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
use embedded_hal::blocking::delay::{DelayMs, DelayUs};
2+
use embedded_hal::blocking::i2c::Write;
3+
4+
use crate::{bus::DataBus, error::Result};
5+
6+
/// This module supports I2C backpacks with a MCP23008 IC, like
7+
/// the one from adafruit.
8+
/// Connections as follows:
9+
///
10+
/// <table>
11+
/// <tr><th>MCP23008 pin</th><th>name</th><th>LCD pin</th></tr>
12+
/// <tr><td>0</td><td>N/C</td><td></td></tr>
13+
/// <tr><td>1</td><td>RS</td><td>4</td></tr>
14+
/// <tr><td>2</td><td>E</td><td>6</td></tr>
15+
/// <tr><td>3</td><td>DB4</td><td>11</td></tr>
16+
/// <tr><td>4</td><td>DB5</td><td>12</td></tr>
17+
/// <tr><td>5</td><td>DB6</td><td>13</td></tr>
18+
/// <tr><td>6</td><td>DB7</td><td>14</td></tr>
19+
/// <tr><td>7</td><td>Backlight</td><td></td></tr>
20+
/// </table>
21+
22+
pub struct I2CMCP23008Bus<I2C: Write> {
23+
i2c_bus: I2C,
24+
address: u8,
25+
backlight: u8,
26+
}
27+
28+
const REG_IODIR : u8 = 0x00;
29+
const REG_GPIO : u8 = 0x09;
30+
31+
impl<I2C: Write> I2CMCP23008Bus<I2C> {
32+
/// Create a new instance of the MCP23008 I2C driver. The address of those
33+
/// devices is 0b010_0xxx where x is configured by bootstrap pins.
34+
pub fn new(i2c_bus: I2C, address: u8, backlight: bool) -> Result<I2CMCP23008Bus<I2C>> {
35+
let backlight = if backlight { 0b1000_0000 } else { 0 };
36+
let mut mcp23008 = I2CMCP23008Bus { i2c_bus, address, backlight };
37+
// Set to reset values according to datasheet
38+
mcp23008.write_reg(REG_IODIR, 0b1111_1111)?;
39+
for reg in 0x01u8..0x0A {
40+
mcp23008.write_reg(reg, 0)?;
41+
}
42+
// Configure pins 1..=7 as outputs, see pin mapping above
43+
mcp23008.write_reg(REG_IODIR, 0b0000_0001)?;
44+
Ok(mcp23008)
45+
}
46+
47+
/// Turns the backlight on or off based on the value of backlight.
48+
pub fn set_backlight(&mut self, backlight: bool) -> Result<()> {
49+
self.backlight = if backlight { 0b1000_0000 } else { 0 };
50+
self.set_pins(self.backlight)
51+
}
52+
53+
fn write_reg(&mut self, reg: u8, value: u8) -> Result<()> {
54+
let data = [reg, value];
55+
self.i2c_bus.write(self.address, &data)
56+
.map_err(|_| crate::error::Error)
57+
}
58+
59+
fn set_pins(&mut self, pins: u8) -> Result<()> {
60+
self.write_reg(REG_GPIO, pins)
61+
}
62+
}
63+
64+
impl<I2C: Write> DataBus for I2CMCP23008Bus<I2C> {
65+
fn write<D: DelayUs<u16> + DelayMs<u8>>(
66+
&mut self,
67+
byte: u8,
68+
data: bool,
69+
delay: &mut D,
70+
) -> Result<()> {
71+
let rs = if data { 0b10 } else { 0b00 };
72+
let en = 0b0000_0100;
73+
let backlight = self.backlight;
74+
75+
let upper_nibble = (byte & 0xF0) >> 4;
76+
let lower_nibble = byte & 0x0F;
77+
78+
// upper nibble: [d7 d6 d5 d4]
79+
// Pulse EN
80+
// lower nibble: [d3 d2 d1 d0]
81+
// Pulse EN
82+
83+
let pins = rs | backlight | (upper_nibble << 3);
84+
self.set_pins(pins)?;
85+
86+
delay.delay_ms(1);
87+
88+
let pins = rs | en | backlight | (upper_nibble << 3);
89+
self.set_pins(pins)?;
90+
91+
delay.delay_ms(1);
92+
93+
let pins = rs | backlight | (lower_nibble << 3);
94+
self.set_pins(pins)?;
95+
96+
delay.delay_ms(1);
97+
98+
let pins = rs | en | backlight | (lower_nibble << 3);
99+
self.set_pins(pins)?;
100+
101+
delay.delay_ms(1);
102+
103+
let pins = backlight | (lower_nibble << 3);
104+
self.set_pins(pins)?;
105+
106+
Ok(())
107+
}
108+
}

src/bus/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ use embedded_hal::blocking::delay::{DelayMs, DelayUs};
33
mod eightbit;
44
mod fourbit;
55
mod i2c;
6+
mod i2c_mcp23008;
67

78
pub use self::eightbit::EightBitBus;
89
pub use self::fourbit::FourBitBus;
910
pub use self::i2c::I2CBus;
11+
pub use self::i2c_mcp23008::I2CMCP23008Bus;
1012

1113
use crate::error::Result;
1214

src/lib.rs

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use embedded_hal::blocking::i2c;
88
use embedded_hal::digital::v2::OutputPin;
99

1010
pub mod bus;
11-
use bus::{DataBus, EightBitBus, FourBitBus, I2CBus};
11+
use bus::{DataBus, EightBitBus, FourBitBus, I2CBus, I2CMCP23008Bus};
1212

1313
pub mod error;
1414
use error::Result;
@@ -176,13 +176,15 @@ impl<RS: OutputPin, EN: OutputPin, D4: OutputPin, D5: OutputPin, D6: OutputPin,
176176

177177
impl<I2C: i2c::Write> HD44780<I2CBus<I2C>> {
178178
/// Create an instance of a `HD44780` from an i2c write peripheral,
179-
/// the `HD44780` I2C address and a struct implementing the delay trait.
179+
/// I2C address and a struct implementing the delay trait.
180+
/// The `HD44780` is driven through a PCF8574 I2C port expander.
180181
/// - The delay instance is used to sleep between commands to
181182
/// ensure the `HD44780` has enough time to process commands.
182183
/// - The i2c peripheral is used to send data to the `HD44780` and to set
183184
/// its register select and enable pins.
184185
///
185-
/// This mode operates on an I2C bus, using an I2C to parallel port expander
186+
/// This mode operates on an I2C bus, using a PCF8574 I2C to port expander
187+
/// The IC connections are described in `I2CBus`
186188
///
187189
pub fn new_i2c<D: DelayUs<u16> + DelayMs<u8>>(
188190
i2c_bus: I2C,
@@ -202,10 +204,51 @@ impl<I2C: i2c::Write> HD44780<I2CBus<I2C>> {
202204
}
203205
}
204206

207+
impl<I2C: i2c::Write> HD44780<I2CMCP23008Bus<I2C>> {
208+
/// Create an instance of a `HD44780` from an i2c write peripheral,
209+
/// I2C address and a struct implementing the delay trait.
210+
/// The `HD44780` is driven through a MCP23008 I2C port expander.
211+
/// - The delay instance is used to sleep between commands to
212+
/// ensure the `HD44780` has enough time to process commands.
213+
/// - The i2c peripheral is used to send data to the `HD44780` and to set
214+
/// its register select and enable pins.
215+
///
216+
/// This mode operates on an I2C bus, using an I2C to parallel port expander based on MCP23008.
217+
/// The IC connections are described in `I2CMCP23008Bus`
218+
///
219+
pub fn new_i2c_mcp23008<D: DelayUs<u16> + DelayMs<u8>>(
220+
i2c_bus: I2C,
221+
address: u8,
222+
backlight: bool,
223+
delay: &mut D,
224+
) -> Result<HD44780<I2CMCP23008Bus<I2C>>> {
225+
let mut hd = HD44780 {
226+
bus: I2CMCP23008Bus::new(i2c_bus, address, backlight)?,
227+
entry_mode: EntryMode::default(),
228+
display_mode: DisplayMode::default(),
229+
display_size: DisplaySize::default(),
230+
};
231+
232+
hd.init_4bit(delay)?;
233+
234+
return Ok(hd);
235+
}
236+
}
237+
205238
impl<B> HD44780<B>
206239
where
207240
B: DataBus,
208241
{
242+
/// Gets a reference to the underlying bus for this display.
243+
pub fn get_ref(&self) -> &B {
244+
&self.bus
245+
}
246+
247+
/// Gets a mutable reference to the underlying bus for this display.
248+
pub fn get_mut(&mut self) -> &mut B {
249+
&mut self.bus
250+
}
251+
209252
/// Unshifts the display and sets the cursor position to 0
210253
///
211254
/// ```rust,ignore

0 commit comments

Comments
 (0)