Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds I2C implementation and basic accelerometer crate. #30

Merged
merged 10 commits into from
Sep 1, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .cargo/config
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
paths = ["./tessel"]

[target.mipsel-unknown-linux-gnu]
linker = "mipsel-openwrt-linux-gcc"
7 changes: 6 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
language: rust
rust:
- 1.10.0
- nightly
script:
- cd tessel
- cargo build
- cargo test
- cargo doc
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ This repo hosts the Tessel library that provides the hardware API (`gpio.high()`
* `mkdir tessel-rust && cd tessel-rust` to create a folder for Rust projects
* Next we'll setup the linker.

For Linux, first download the OpenWRT SDK with:
For Linux, first download the OpenWRT SDK with:
```
wget https://s3.amazonaws.com/builds.tessel.io/t2/OpenWRT+SDK/OpenWrt-SDK-ramips-mt7620_gcc-4.8-linaro_uClibc-0.9.33.2.Linux-x86_64.tar.bz2
tar -xf OpenWrt-SDK-ramips-mt7620_gcc-4.8-linaro_uClibc-0.9.33.2.Linux-x86_64.tar.bz2
```
Then add the SDK linker to your path:
Then add the SDK linker to your path:
```
export PATH=$(readlink -f ./OpenWrt-SDK-ramips-mt7620_gcc-4.8-linaro_uClibc-0.9.33.2.Linux-x86_64/staging_dir/toolchain-mipsel_24kec+dsp_gcc-4.8-linaro_uClibc-0.9.33.2/bin/):$PATH
```
Expand All @@ -38,7 +38,9 @@ wget https://s3.amazonaws.com/builds.tessel.io/t2/OpenWRT+SDK/OpenWrt-SDK-ramips
tar -xf OpenWrt-SDK-ramips-mt7620_gcc-4.8-linaro_uClibc-0.9.33.2.Darwin-x86_64.tar.bz2
```
Then add the linker to your path:
```export PATH=$(pwd)/openwrt/staging_dir/toolchain-mipsel_24kec+dsp_gcc-4.8-linaro_uClibc-0.9.33.2/bin/):$PATH```
```
export PATH=$(pwd)/openwrt/staging_dir/toolchain-mipsel_24kec+dsp_gcc-4.8-linaro_uClibc-0.9.33.2/bin/:$PATH
```

* Create a test project
```
Expand Down Expand Up @@ -69,7 +71,7 @@ extern crate alloc_system;

* Build the project (in release mode to optimize the size of the binary)
```
cargo build --release
cargo build --release --target=mipsel-unknown-linux-gnu
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you add this just for clarity? I don't think I needed this but maybe my setup was just different.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, mine required it. Guess it doesn't hurt to be specific when cross-compiling.

```
* You can now `scp` it over to your Tessel and run it!
```
Expand All @@ -78,6 +80,7 @@ ssh -i ~/.tessel/id_rsa root@YOUR_TESSEL_IP ./tmp/hello-rust
```

## Developing on the Remote Cross Compilation Server (`Docker`, `git` and `Node` 4.x are requirements)

* Clone the [cross-compilation server repo](https://github.com/tessel/rust-compilation-server) and checkout the `jon-1.0.0` branch.
* Build the Docker image with `docker build -t rustcc .`, then run it with `docker run -p 49160:8080 rustcc`.
* [Clone](https://github.com/tessel/t2-cli) or install the command line interface: `npm install t2-cli -g`
Expand Down Expand Up @@ -106,7 +109,7 @@ In order to integrate with the CLI, we need to write a deployment plugin. This p
The CLI will detect a Rust project, bundle it into a tarball, send it to the cross compilation server, and then deploy the resulting binary to an available Tessel.

## Tessel Standard Library
The Tessel Standard Library (this repo) is the library that gets 'loaded' into a user program (runs on T2) and presents an API for configuring the hardware (LEDs, module ports, network interfaces, etc.).
The Tessel Standard Library (this repo) is the library that gets 'loaded' into a user program (runs on T2) and presents an API for configuring the hardware (LEDs, module ports, network interfaces, etc.).
You can see the JavaScript version of the Tessel Standard Library [here](https://github.com/tessel/t2-firmware/blob/master/node/tessel-export.js). The most important function is communication with module ports which takes places by writing to a Unix Domain Socket always running on OpenWRT. See [the technical overview](https://github.com/tessel/onboarding/blob/master/T2-TECHNICAL-OVERVIEW.md) or previously linked JS Standard Library for more detailed information on how that works. Everything sent to the domain socket gets sent to the microcontroller. There is a simple protocol between the MediaTek (running OpenWRT) and the coprocessor to coordinate hardware operations.

## Module Libraries
Expand Down
11 changes: 11 additions & 0 deletions accel-mma84/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "accel-mma84"
version = "0.1.0"
authors = ["Tim Ryan <[email protected]>"]

[dependencies]
tessel = "0.2.0"

[[bin]]
name = "tessel-accel"
doc = false
173 changes: 173 additions & 0 deletions accel-mma84/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
#![feature(alloc_system)]

extern crate alloc_system;
extern crate tessel;

use tessel::Tessel;
use std::thread::sleep;
use std::time::Duration;
use std::io::prelude::*;

pub mod mma84 {
use tessel;
use std::io;

#[repr(u8)]
pub enum ScaleRange {
Scale2G = 0x00,
Scale4G = 0x01,
Scale8G = 0x10,
}

#[repr(u8)]
#[allow(dead_code)]
enum Command {
OutXMsb = 0x01,
XyzDataCfg = 0x0E,
WhoAmI = 0x0D,
CtrlReg1 = 0x2A,
CtrlReg4 = 0x2D,
}

pub struct Accelerometer<'a> {
i2c: tessel::I2C<'a>,
}

#[repr(u8)]
#[allow(non_camel_case_types)]
pub enum OutputRate {
Rate800 = 0,
Rate400 = 1,
Rate200 = 2,
Rate100 = 3,
Rate50 = 4,
Rate12_5 = 5,
Rate6_25 = 6,
Rate1_56 = 7,
}

const I2C_ID: u8 = 0x1d;

impl<'a> Accelerometer<'a> {
pub fn new<'b>(i2c: tessel::I2C<'b>) -> Accelerometer<'b> {
Accelerometer {
i2c: i2c,
}
}

fn read_register(&mut self, cmd: Command) -> io::Result<u8> {
let mut xr: [u8; 1] = [0; 1];
try!(self.read_registers(cmd, &mut xr));
Ok(xr[0])
}

/// Reads sequential buffers.
fn read_registers(&mut self, cmd: Command, buf: &mut [u8]) -> io::Result<()> {
try!(self.i2c.transfer(I2C_ID, &[cmd as u8], buf));
Ok(())
}

fn write_register(&mut self, cmd: Command, value: u8) -> io::Result<()> {
self.i2c.send(I2C_ID, &[cmd as u8, value]);
Ok(())
}

pub fn connect(&mut self) -> io::Result<()> {
if try!(self.read_register(Command::WhoAmI)) != 0x2A {
return Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid connection code."))
}

try!(self.set_scale_range(ScaleRange::Scale2G));
try!(self.set_output_rate(OutputRate::Rate1_56));

Ok(())
}

fn standby_enable(&mut self) -> io::Result<()> {
// Sets the MMA8452 to standby mode.
let value = try!(self.read_register(Command::CtrlReg1));
self.write_register(Command::CtrlReg1, value & !(0x01u8))
}

fn standby_disable(&mut self) -> io::Result<()> {
// Sets the MMA8452 to active mode.
let value = try!(self.read_register(Command::CtrlReg1));
self.write_register(Command::CtrlReg1, value | (0x01u8))
}

pub fn set_scale_range(&mut self, range: ScaleRange) -> io::Result<()> {
try!(self.standby_enable());
try!(self.write_register(Command::XyzDataCfg, range as u8));
try!(self.standby_disable());

Ok(())
}

pub fn set_output_rate(&mut self, rate: OutputRate) -> io::Result<()> {
try!(self.standby_enable());

// Clear the three bits of output rate control (0b11000111 = 199)
let mut value = try!(self.read_register(Command::CtrlReg1));
value &= 0b11000111;
try!(self.write_register(Command::CtrlReg1, value | ((rate as u8) << 3)));

try!(self.standby_disable());

Ok(())
}

pub fn read_acceleration(&mut self) -> io::Result<(f64, f64, f64)> {
let mut buf = [0; 6];
try!(self.read_registers(Command::OutXMsb, &mut buf));

let mut out = vec![0.0, 0.0, 0.0];

// Loop to calculate 12-bit ADC and g value for each axis
for (i, win) in buf.chunks(2).enumerate() {
// Combine the two 8 bit registers into one 12-bit number.
// The registers are left aligned, so right align the 12-bit integer.
let g = (((win[0] as u16) << 8) | (win[1] as u16)) >> 4;

// If the number is negative, we have to make it so manually.
// Transform into negative 2's complement.
let dim = if win[0] > 0x7F {
-(1 + 0xFFF - (g as i16))
} else {
g as i16
};

let scale_range = 2.0;
out[i] = (dim as f64) / ((1 << 11) as f64) * scale_range;
}

Ok((out[0], out[1], out[2]))
}
}
}

fn main() {
// Create a new Tessel
let mut tessel = Tessel::new();

let mut acc = mma84::Accelerometer::new(tessel.port.a.i2c(100000).unwrap());
acc.connect().expect("Could not connect to accelerometer.");
println!("Connected!");

// Turn on one of the LEDs
tessel.led[2].on().unwrap();

println!("I'm blinking! (Press CTRL + C to stop)");

// Loop forever
loop {
// Toggle each LED
tessel.led[2].toggle().unwrap();
tessel.led[3].toggle().unwrap();
// Re-execute the loop after sleeping for 100ms
sleep(Duration::from_millis(100));

println!("acceleration: {:?}", acc.read_acceleration());

let _ = std::io::stdout().flush();
}
}
Loading