#i2c #driver #embedded-io #embedded-hal-driver #analog #read-write

no-std mcp23017-tp

Rust Driver for device MCP23017 16-Bit I2C I/O Expander with Serial Interface

2 releases

0.1.1 Jul 20, 2024
0.1.0 Jul 19, 2024

#348 in Embedded development

MIT/Apache

95KB
2K SLoC

mcp23017

16-Bit I/O Expander with Serial Interface

mcp23017 Datasheet

Description

This crate was made for and tested on MCP23017 from Microchip, it is based on I2C from embedded-hal crate. The implementation of this crate is based on #![no_std] but with some minor adjustments it can be used on std environments.

This driver allows you to:

  • choose operating mode: (1x16bit), (2x8bit) or (16x1bit)
  • configure interrupts
  • enable or disable interrupts
  • set internall pull resistor
  • read or write to pin/port/chip dependiong on the mode choosen

NOTE: When operating in 16bit mode, use LittleEndian formatting (0xbbaa).

Version Revision

0.1.0 - First Version

0.1.1 - Fixed doc generation, fixed async support in traits, improved prelude for feature usage, fixed endianess for interrupt functions

Features

features = ["async"] - enables support for async Rust (Currently embedded_hal_bus does not implement async for I2C, therefore if using more than one pin/port, disable the async feature)

features = ["chipmode"] - The driver operates as a 1x 16bit device set entirely as output or input

features = ["portmode"] - The driver operates as a 2x 8bit port device, each port is configured individually

features = ["pinmode"] - The driver operates as a 16x 1bit pins device, each pin is configured individually

ATTENTION: ENABLE ONLY ONE OF THE MODES OR FACE THE CONSEQUENCES.... ASYNC CAN BE USED ON ANY MODE

Example

To use the driver, you must have a concrete implementation of the embedded-hal traits. This example uses stm32f4xx-hal:

When using chipmode, the driver will operate in 16bit, the code below will set all pins to output:

use core::cell::RefCell;
use embedded_hal_bus::i2c;
use mcp23017_tp::prelude::*;

    let mut i2c = dp.I2C1.i2c(
        (scl, sda),
        Mode::Standard {
            frequency: 100.kHz(),
        },
        &clocks,
    );

    let i2c_ref_cell = RefCell::new(i2c);

    let mut mcp = mcp23017_tp::MCP23017::new(i2c::RefCellDevice::new(&i2c_ref_cell), address)
        .set_as_output()
        .unwrap();

    loop {
          mcp.write(0xbbaa).unwrap();
          delay.delay_ms(2000);

          // u16: 0xbbaa - u8[]: [0]aa [1]bb (LittleEndian)
          mcp.write(0x0000).unwrap();
          delay.delay_ms(2000);
        }

When using portmode, the driver will operate in 2x8bit, the code below will set port A as output and port B as input:

use core::cell::RefCell;
use embedded_hal_bus::i2c;
use mcp23017_tp::prelude::*;

    let mut i2c = dp.I2C1.i2c(
        (scl, sda),
        Mode::Standard {
            frequency: 100.kHz(),
        },
        &clocks,
    );

    let i2c_ref_cell = RefCell::new(i2c);

    let mut porta = mcp23017_tp::PortA::new(i2c::RefCellDevice::new(&i2c_ref_cell), address)
         .set_as_output()
         .unwrap();
    
    let mut portb = mcp23017_tp::PortB::new(i2c::RefCellDevice::new(&i2c_ref_cell), address)
        .set_as_input()
        .unwrap()
        .set_pull(PinSet::High)
        .unwrap()
        .ready();

    loop {
          porta.write(0xff).unwrap();
          delay.delay_ms(2000);
          porta.write(0x00).unwrap();
          delay.delay_ms(2000);

          rprintln!("{:#02x}", portb.read().unwrap());
        }

When using pinmode, the driver will operate in 16x1bit, the code below will set pin A1 and pin B3 as input:

use core::cell::RefCell;
use embedded_hal_bus::i2c;
use mcp23017_tp::prelude::*;

    let mut i2c = dp.I2C1.i2c(
        (scl, sda),
        Mode::Standard {
            frequency: 100.kHz(),
        },
        &clocks,
    );

    let i2c_ref_cell = RefCell::new(i2c);

    let mut pina1 = mcp23017_tp::Pina1::new(i2c::RefCellDevice::new(&i2c_ref_cell), address)
        .set_as_input()
        .unwrap()
        .set_pull(PinSet::High)
        .unwrap()
        .ready();

    let mut pinb3 = mcp23017_tp::Pinb3::new(i2c::RefCellDevice::new(&i2c_ref_cell), address)
        .set_as_input()
        .unwrap()
        .set_pull(PinSet::High)
        .unwrap()
        .ready();

    loop {
          delay.delay_ms(2000);
          rprintln!(
              "{:#02x} {:#02x}",
              pina1.read().unwrap(),
              pinb3.read().unwrap()
          );
        }

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Dependencies

~2MB
~40K SLoC