Skip to content

Commit

Permalink
Add I2C Support (#13)
Browse files Browse the repository at this point in the history
* Add an I2CBus to the available buses

It handles I2C backpacks as described in #12

* Add example for I2CBus
  • Loading branch information
nils-van-zuijlen authored and JohnDoneth committed Nov 17, 2019
1 parent 5bc2e44 commit 7d89926
Show file tree
Hide file tree
Showing 12 changed files with 250 additions and 3 deletions.
9 changes: 9 additions & 0 deletions examples/stm32f30x-i2c/.cargo/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
runner = "gdb-multiarch -q -x openocd.gdb"

rustflags = [
"-C", "link-arg=-Tlink.x",
]

[build]
target = "thumbv7em-none-eabihf"
5 changes: 5 additions & 0 deletions examples/stm32f30x-i2c/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
**/*.rs.bk
.#*
.gdb_history
Cargo.lock
target/
23 changes: 23 additions & 0 deletions examples/stm32f30x-i2c/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
authors = ["Robin Krahl <robin.krahl@ireas.org>", "Nils Van Zuijlen <nils.van-zuijlen@mailo.com>"]
edition = "2018"
name = "stm32f30x-hd44780-i2c-example"
version = "0.1.0"
license = "MIT"
publish = false

[dependencies]
cortex-m = "0.5.8"
cortex-m-rt = "0.6.5"
embedded-hal = "0.2.2"
panic-halt = "0.2.0"
hd44780-driver = { path = "../.." }

[dependencies.hal]
version = "0.2.0"
package = "stm32f30x-hal"

[profile.release]
codegen-units = 1
debug = true
lto = true
3 changes: 3 additions & 0 deletions examples/stm32f30x-i2c/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This example is based on the cortex-m-quickstart project avaiable at
https://github.com/rust-embedded/cortex-m-quickstart under the MIT License or
the Apache License, Version 2.0.
15 changes: 15 additions & 0 deletions examples/stm32f30x-i2c/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;

fn main() {
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("memory.x"))
.unwrap()
.write_all(include_bytes!("memory.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());

println!("cargo:rerun-if-changed=memory.x");
}
5 changes: 5 additions & 0 deletions examples/stm32f30x-i2c/memory.x
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
MEMORY
{
FLASH : ORIGIN = 0x08000000, LENGTH = 512K
RAM : ORIGIN = 0x20000000, LENGTH = 64K
}
12 changes: 12 additions & 0 deletions examples/stm32f30x-i2c/openocd.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Sample OpenOCD configuration for the STM32F3DISCOVERY development board

# Depending on the hardware revision you got you'll have to pick ONE of these
# interfaces. At any time only one interface should be commented out.

# Revision C (newer revision)
source [find interface/stlink-v2-1.cfg]

# Revision A and B (older revisions)
# source [find interface/stlink-v2.cfg]

source [find target/stm32f3x.cfg]
32 changes: 32 additions & 0 deletions examples/stm32f30x-i2c/openocd.gdb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
target extended-remote :3333

# print demangled symbols
set print asm-demangle on

# detect unhandled exceptions, hard faults and panics
break DefaultHandler
break UserHardFault
break rust_begin_unwind

# *try* to stop at the user entry point (it might be gone due to inlining)
break main

monitor arm semihosting enable

# # send captured ITM to the file itm.fifo
# # (the microcontroller SWO pin must be connected to the programmer SWO pin)
# # 8000000 must match the core clock frequency
# monitor tpiu config internal itm.txt uart off 8000000

# # OR: make the microcontroller SWO pin output compatible with UART (8N1)
# # 8000000 must match the core clock frequency
# # 2000000 is the frequency of the SWO pin
# monitor tpiu config external uart off 8000000 2000000

# # enable ITM port 0
# monitor itm port 0 on

load

# start the process but immediately halt the processor
stepi
52 changes: 52 additions & 0 deletions examples/stm32f30x-i2c/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#![no_std]
#![no_main]

extern crate panic_halt;

use core::fmt::Write;
use cortex_m_rt::entry;
use hal::prelude::*;
use hal::flash::FlashExt;
use hal::i2c::I2c;
use hd44780_driver::{Cursor, CursorBlink, Display, DisplayMode, HD44780};

// Connections:
// VSS: GND
// VDD: 5V
// SCL: PB6
// SDA: PB9
// I2C address : 0x3F

const I2C_ADDRESS: u8 = 0x3F;

#[entry]
fn main() -> ! {
let cp = cortex_m::Peripherals::take().unwrap();
let dp = hal::stm32f30x::Peripherals::take().unwrap();

let mut flash = dp.FLASH.constrain();
let mut rcc = dp.RCC.constrain();
let mut gpiob = dp.GPIOB.split(&mut rcc.ahb);

let clocks = rcc.cfgr.freeze(&mut flash.acr);
let delay = hal::delay::Delay::new(cp.SYST, clocks);

let scl = gpiob.pb6.into_af4(&mut gpiob.moder, &mut gpiob.afrl);
let sda = gpiob.pb9.into_af4(&mut gpiob.moder, &mut gpiob.afrh);

let i2c = I2c::i2c1(dp.I2C1, (scl, sda), 400.khz(), clocks, &mut rcc.apb1);

let mut lcd = HD44780::new_i2c(i2c, I2C_ADDRESS, delay);
lcd.reset();
lcd.clear();
lcd.set_display_mode(
DisplayMode {
display: Display::On,
cursor_visibility: Cursor::Visible,
cursor_blink: CursorBlink::On,
}
);
let _ = lcd.write_str("Hello, world!");

loop {}
}
57 changes: 57 additions & 0 deletions src/bus/i2c.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use embedded_hal::blocking::delay::{DelayMs, DelayUs};
use embedded_hal::blocking::i2c::Write;

use bus::DataBus;

pub struct I2CBus<
I2C: Write,
> {
i2c_bus: I2C,
address: u8,
}

const BACKLIGHT: u8 = 0b0000_1000;
const ENABLE: u8 = 0b0000_0100;
// const READ_WRITE: u8 = 0b0000_0010; // Not used as no reading of the `HD44780` is done
const REGISTER_SELECT: u8 = 0b0000_0001;

impl<I2C: Write>
I2CBus<I2C>
{
pub fn new(
i2c_bus: I2C,
address: u8
) -> I2CBus<I2C> {
I2CBus {
i2c_bus,
address,
}
}

/// Write a nibble to the lcd
/// The nibble should be in the upper part of the byte
fn write_nibble<D: DelayUs<u16> + DelayMs<u8>>(&mut self, nibble: u8, data: bool, delay: &mut D) {
let rs = match data {
false => 0u8,
true => REGISTER_SELECT,
};
let byte = nibble | rs | BACKLIGHT;

let _ = self.i2c_bus.write(self.address, &[byte, byte|ENABLE]);
delay.delay_ms(2u8);
let _ = self.i2c_bus.write(self.address, &[byte]);
}
}

impl<I2C: Write>
DataBus for I2CBus<I2C>
{
fn write<D: DelayUs<u16> + DelayMs<u8>>(&mut self, byte: u8, data: bool, delay: &mut D) {

let upper_nibble = byte & 0xF0;
self.write_nibble(upper_nibble, data, delay);

let lower_nibble = (byte & 0x0F) << 4;
self.write_nibble(lower_nibble, data, delay);
}
}
2 changes: 2 additions & 0 deletions src/bus/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ use embedded_hal::blocking::delay::{DelayMs, DelayUs};

mod eightbit;
mod fourbit;
mod i2c;

pub use self::eightbit::EightBitBus;
pub use self::fourbit::FourBitBus;
pub use self::i2c::I2CBus;

pub trait DataBus {
fn write<D: DelayUs<u16> + DelayMs<u8>>(&mut self, byte: u8, data: bool, delay: &mut D);
Expand Down
38 changes: 35 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ use core::fmt::Write;

extern crate embedded_hal;

use embedded_hal::blocking::delay::DelayMs;
use embedded_hal::blocking::delay::DelayUs;
use embedded_hal::blocking::delay::{DelayMs, DelayUs};
use embedded_hal::blocking::i2c;
use embedded_hal::digital::OutputPin;

pub mod bus;

use bus::{DataBus, EightBitBus, FourBitBus};
use bus::{DataBus, EightBitBus, FourBitBus, I2CBus};

pub mod entry_mode;

Expand Down Expand Up @@ -152,6 +152,38 @@ impl<
}
}

impl<
D: DelayUs<u16> + DelayMs<u8>,
I2C: i2c::Write,
> HD44780<D, I2CBus<I2C>>
{
/// Create an instance of a `HD44780` from an i2c write peripheral,
/// the `HD44780` I2C address and a struct implementing the delay trait.
/// - The delay instance is used to sleep between commands to
/// ensure the `HD44780` has enough time to process commands.
/// - The i2c peripheral is used to send data to the `HD44780` and to set
/// its register select and enable pins.
///
/// This mode operates on an I2C bus, using an I2C to parallel port expander
///
pub fn new_i2c(
i2c_bus: I2C,
address: u8,
delay: D,
) -> HD44780<D, I2CBus<I2C>> {
let mut hd = HD44780 {
bus: I2CBus::new(i2c_bus, address),
delay,
entry_mode: EntryMode::default(),
display_mode: DisplayMode::default(),
};

hd.init_4bit();

return hd;
}
}

impl<D, B> HD44780<D, B>
where
D: DelayUs<u16> + DelayMs<u8>,
Expand Down

0 comments on commit 7d89926

Please sign in to comment.