mirror of
https://github.com/laanwj/k210-sdk-stuff.git
synced 2024-11-22 09:26:21 +04:00
rust: Generalize I2C peripheral
This commit is contained in:
parent
13b91fe993
commit
b14679e35a
@ -10,9 +10,9 @@ use k210_hal::stdout::Stdout;
|
||||
use k210_shared::board::def::{io,DISP_WIDTH,DISP_HEIGHT,MSA300_SLV_ADDR,MSA300_ADDR_BITS,MSA300_CLK};
|
||||
use k210_shared::board::lcd::{LCD,self};
|
||||
use k210_shared::board::lcd_colors;
|
||||
use k210_shared::board::msa300;
|
||||
use k210_shared::board::msa300::Accelerometer;
|
||||
use k210_shared::soc::fpioa;
|
||||
use k210_shared::soc::i2c;
|
||||
use k210_shared::soc::i2c::{I2C,I2CExt};
|
||||
use k210_shared::soc::sleep::usleep;
|
||||
use k210_shared::soc::spi::SPIExt;
|
||||
use k210_shared::soc::sysctl;
|
||||
@ -85,11 +85,12 @@ fn main() -> ! {
|
||||
let mut image: ScreenImage = [0; DISP_WIDTH * DISP_HEIGHT / 2];
|
||||
|
||||
writeln!(stdout, "MSA300 init").unwrap();
|
||||
i2c::init(MSA300_SLV_ADDR, MSA300_ADDR_BITS, MSA300_CLK);
|
||||
msa300::init().unwrap();
|
||||
let i2c = p.I2C0.constrain();
|
||||
i2c.init(MSA300_SLV_ADDR, MSA300_ADDR_BITS, MSA300_CLK);
|
||||
let acc = Accelerometer::init(i2c).unwrap();
|
||||
|
||||
loop {
|
||||
let (x, y, z) = msa300::measure().unwrap();
|
||||
let (x, y, z) = acc.measure().unwrap();
|
||||
let mag = (x*x+y*y+z*z).sqrt();
|
||||
// writeln!(stdout, "m/s^2 x={} y={} z={} (size={})", x, y, z, mag).unwrap();
|
||||
|
||||
|
@ -12,7 +12,7 @@ use k210_shared::board::lcd::{LCD,self};
|
||||
use k210_shared::board::lcd_colors;
|
||||
use k210_shared::board::ns2009::TouchScreen;
|
||||
use k210_shared::soc::fpioa;
|
||||
use k210_shared::soc::i2c;
|
||||
use k210_shared::soc::i2c::{I2C,I2CExt};
|
||||
use k210_shared::soc::sleep::usleep;
|
||||
use k210_shared::soc::spi::SPIExt;
|
||||
use k210_shared::soc::sysctl;
|
||||
@ -163,9 +163,10 @@ fn main() -> ! {
|
||||
let mut image: ScreenImage = [0; DISP_WIDTH * DISP_HEIGHT / 2];
|
||||
|
||||
writeln!(stdout, "NS2009 init").unwrap();
|
||||
i2c::init(NS2009_SLV_ADDR, NS2009_ADDR_BITS, NS2009_CLK);
|
||||
let i2c = p.I2C0.constrain();
|
||||
i2c.init(NS2009_SLV_ADDR, NS2009_ADDR_BITS, NS2009_CLK);
|
||||
|
||||
let mut filter = if let Some(filter) = TouchScreen::init(NS2009_CAL) {
|
||||
let mut filter = if let Some(filter) = TouchScreen::init(i2c, NS2009_CAL) {
|
||||
filter
|
||||
} else {
|
||||
writeln!(stdout, "NS2009 init failure").unwrap();
|
||||
|
@ -1,6 +1,6 @@
|
||||
/** Support for MSA300 accelerometer */
|
||||
/* MSA300 code based on 'accelerometer' demo by j.m.voogd@gmail.com */
|
||||
use crate::soc::i2c;
|
||||
use crate::soc::i2c::I2C;
|
||||
|
||||
/** MSA300 Registers */
|
||||
enum reg {
|
||||
@ -41,10 +41,14 @@ const DATARATE_1000_HZ: u8 = 0x0F;
|
||||
/** Gravity constant (Earth surface) */
|
||||
const GRAVITY: f32 = 9.80665;
|
||||
|
||||
pub struct Accelerometer<IF> {
|
||||
i2c: IF,
|
||||
}
|
||||
|
||||
/** Read a register of the MSA300 via I2C */
|
||||
fn read_register(reg: reg) -> Result<u8, ()> {
|
||||
fn read_register<IF: I2C>(i2c: &IF, reg: reg) -> Result<u8, ()> {
|
||||
let mut reg_val = [0u8; 2];
|
||||
if i2c::recv_data(&[reg as u8], &mut reg_val).is_ok() {
|
||||
if i2c.recv_data(&[reg as u8], &mut reg_val).is_ok() {
|
||||
Ok(reg_val[0])
|
||||
} else {
|
||||
Err(())
|
||||
@ -52,14 +56,15 @@ fn read_register(reg: reg) -> Result<u8, ()> {
|
||||
}
|
||||
|
||||
/** Set a register of the MSA300 via I2C */
|
||||
fn set_register(reg: reg, val: u8) -> Result<(), ()> {
|
||||
i2c::send_data(&[reg as u8, val])
|
||||
fn set_register<IF: I2C>(i2c: &IF, reg: reg, val: u8) -> Result<(), ()> {
|
||||
i2c.send_data(&[reg as u8, val])
|
||||
}
|
||||
|
||||
impl<IF: I2C> Accelerometer<IF> {
|
||||
/** Initialize chip */
|
||||
pub fn init() -> Result<(), ()> {
|
||||
pub fn init(i2c: IF) -> Result<Self, ()> {
|
||||
let correct_id = 0x13;
|
||||
if let Ok(part_id) = read_register(reg::PARTID) {
|
||||
if let Ok(part_id) = read_register(&i2c, reg::PARTID) {
|
||||
if part_id != correct_id {
|
||||
//writeln!(stdout, "MSA device not found (ID should be {:02x} but is {:02x})", correct_id, part_id).unwrap();
|
||||
return Err(());
|
||||
@ -71,44 +76,47 @@ pub fn init() -> Result<(), ()> {
|
||||
|
||||
// set (and check) the power mode to 0x1A: normal power mode + 500Hz bandwidth
|
||||
let desired_mode = 0x1A;
|
||||
if set_register(reg::PWR_MODE_BW, desired_mode).is_err() {
|
||||
if set_register(&i2c, reg::PWR_MODE_BW, desired_mode).is_err() {
|
||||
//writeln!(stdout, "Problem: setting power mode went wrong").unwrap();
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let pwr_mode = read_register(reg::PWR_MODE_BW).unwrap();
|
||||
let pwr_mode = read_register(&i2c, reg::PWR_MODE_BW).unwrap();
|
||||
if pwr_mode != desired_mode {
|
||||
//writeln!(stdout, "Power mode should be {:02x} but is {:02x}", desired_mode, pwr_mode).unwrap();
|
||||
return Err(());
|
||||
}
|
||||
|
||||
// Enable x, y and z + set output data rate to 500 Hz
|
||||
set_register(reg::ODR, 0x09)?;
|
||||
set_register(&i2c, reg::ODR, 0x09)?;
|
||||
// resolution 14 bits (MSB=8 bits, LSB=6 bits + 2 zero bits), range +- 4G
|
||||
set_register(reg::RES_RANGE, 0x01)?;
|
||||
set_register(&i2c, reg::RES_RANGE, 0x01)?;
|
||||
// no interrupts
|
||||
set_register(reg::INT_SET_0, 0x00)?;
|
||||
set_register(reg::INT_SET_1, 0x00)?;
|
||||
set_register(&i2c, reg::INT_SET_0, 0x00)?;
|
||||
set_register(&i2c, reg::INT_SET_1, 0x00)?;
|
||||
// reset all latched interrupts, temporary latched 250ms
|
||||
set_register(reg::INT_LATCH, 0x81)?;
|
||||
set_register(&i2c, reg::INT_LATCH, 0x81)?;
|
||||
|
||||
Ok(())
|
||||
Ok(Self {
|
||||
i2c: i2c,
|
||||
})
|
||||
}
|
||||
|
||||
/** Return measurement in m/s^2 for x, y, z */
|
||||
pub fn measure() -> Result<(f32, f32, f32), ()> {
|
||||
pub fn measure(&self) -> Result<(f32, f32, f32), ()> {
|
||||
// for x, y, and z: read the LSB (6 bits + 2 zero bits) and the MSB (8 bits)
|
||||
// shift the MSB left 8 bits, add the LSB, and multiply this with a calibration constant
|
||||
let tx = (read_register(reg::ACC_X_LSB)? as i32) +
|
||||
((read_register(reg::ACC_X_MSB)? as i8) as i32)*256;
|
||||
let tx = (read_register(&self.i2c, reg::ACC_X_LSB)? as i32) +
|
||||
((read_register(&self.i2c, reg::ACC_X_MSB)? as i8) as i32)*256;
|
||||
let x = 0.25f32 * MG2G_MULTIPLIER_4_G * GRAVITY * (tx as f32);
|
||||
let ty = (read_register(reg::ACC_Y_LSB)? as i32) +
|
||||
((read_register(reg::ACC_Y_MSB)? as i8) as i32)*256;
|
||||
let ty = (read_register(&self.i2c, reg::ACC_Y_LSB)? as i32) +
|
||||
((read_register(&self.i2c, reg::ACC_Y_MSB)? as i8) as i32)*256;
|
||||
let y = 0.25f32 * MG2G_MULTIPLIER_4_G * GRAVITY * (ty as f32);
|
||||
// looks like Z has a large bias - don't know if this is general or just on my board
|
||||
let tz = (read_register(reg::ACC_Z_LSB)? as i32) +
|
||||
((read_register(reg::ACC_Z_MSB)? as i8) as i32)*256 + 3386;
|
||||
let tz = (read_register(&self.i2c, reg::ACC_Z_LSB)? as i32) +
|
||||
((read_register(&self.i2c, reg::ACC_Z_MSB)? as i8) as i32)*256 + 3386;
|
||||
let z = 0.25f32 * MG2G_MULTIPLIER_4_G * GRAVITY * (tz as f32);
|
||||
|
||||
Ok((x, y, z))
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use core::result::Result;
|
||||
|
||||
use crate::soc::i2c;
|
||||
use crate::soc::i2c::I2C;
|
||||
use crate::util::filters;
|
||||
|
||||
/** Touch event will trigger at a z1 value higher than this (lower values regarded as noise)
|
||||
@ -24,10 +24,10 @@ pub enum command {
|
||||
}
|
||||
|
||||
/** Read a 12-bit value. */
|
||||
pub fn read(cmd: command) -> Result<u16, ()>
|
||||
pub fn read<IF: I2C>(i2c: &IF, cmd: command) -> Result<u16, ()>
|
||||
{
|
||||
let mut buf = [0u8; 2];
|
||||
if i2c::recv_data(&[cmd as u8], &mut buf).is_ok() {
|
||||
if i2c.recv_data(&[cmd as u8], &mut buf).is_ok() {
|
||||
Ok(((buf[0] as u16) << 4) | ((buf[1] as u16) >> 4))
|
||||
} else {
|
||||
Err(())
|
||||
@ -92,19 +92,21 @@ pub struct Event {
|
||||
}
|
||||
|
||||
/** High-level touch screen abstraction */
|
||||
pub struct TouchScreen {
|
||||
pub struct TouchScreen<IF> {
|
||||
i2c: IF,
|
||||
filter: TSFilter,
|
||||
press: bool,
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
|
||||
impl TouchScreen {
|
||||
impl<IF: I2C> TouchScreen<IF> {
|
||||
/* input: calibration matrix */
|
||||
pub fn init(cal: [i32; 7]) -> Option<Self> {
|
||||
pub fn init(i2c: IF, cal: [i32; 7]) -> Option<Self> {
|
||||
// Do a test read
|
||||
if let Ok(_) = read(command::LOW_POWER_READ_Z1) {
|
||||
if let Ok(_) = read(&i2c, command::LOW_POWER_READ_Z1) {
|
||||
Some(Self {
|
||||
i2c: i2c,
|
||||
filter: TSFilter::new(cal),
|
||||
press: false,
|
||||
x: 0,
|
||||
@ -118,9 +120,9 @@ impl TouchScreen {
|
||||
/** Poll for touch screen event, return the current event (Begin, Move, End) or None */
|
||||
pub fn poll(&mut self) -> Option<Event> {
|
||||
let mut ev: Option<Event> = None;
|
||||
if let Ok(z1) = read(command::LOW_POWER_READ_Z1) {
|
||||
if let Ok(z1) = read(&self.i2c, command::LOW_POWER_READ_Z1) {
|
||||
if z1 > TOUCH_THR_MIN && z1 < TOUCH_THR_MAX {
|
||||
if let (Ok(x), Ok(y)) = (read(command::LOW_POWER_READ_X), read(command::LOW_POWER_READ_Y)) {
|
||||
if let (Ok(x), Ok(y)) = (read(&self.i2c, command::LOW_POWER_READ_X), read(&self.i2c, command::LOW_POWER_READ_Y)) {
|
||||
let (x, y) = self.filter.update(x, y);
|
||||
if !self.press
|
||||
{
|
||||
|
@ -1,22 +1,66 @@
|
||||
use core::cmp;
|
||||
use core::convert::TryInto;
|
||||
use core::ops::Deref;
|
||||
use core::result::Result;
|
||||
use k210_hal::pac;
|
||||
use k210_hal::pac::{I2C0,I2C1,I2C2,i2c0};
|
||||
|
||||
use crate::soc::sysctl;
|
||||
|
||||
// TODO parametrize type, other I2C than I2C0
|
||||
fn clk_init()
|
||||
{
|
||||
sysctl::clock_enable(sysctl::clock::I2C0);
|
||||
sysctl::clock_set_threshold(sysctl::threshold::I2C0, 3);
|
||||
/// Extension trait that constrains I2C peripherals
|
||||
pub trait I2CExt: Sized {
|
||||
/// Constrains I2C peripheral so it plays nicely with the other abstractions
|
||||
fn constrain(self) -> I2CImpl<Self>;
|
||||
}
|
||||
|
||||
pub fn init(slave_address: u16, address_width: u32, i2c_clk: u32)
|
||||
{
|
||||
/// Trait for generalizing over I2C0 and I2C1 (I2C2 is slave-only and I2C3 is !!!special!!!)
|
||||
pub trait I2C012: Deref<Target = i2c0::RegisterBlock> {
|
||||
#[doc(hidden)]
|
||||
const CLK: sysctl::clock;
|
||||
#[doc(hidden)]
|
||||
const DIV: sysctl::threshold;
|
||||
}
|
||||
|
||||
impl I2C012 for I2C0 {
|
||||
const CLK: sysctl::clock = sysctl::clock::I2C0;
|
||||
const DIV: sysctl::threshold = sysctl::threshold::I2C0;
|
||||
}
|
||||
impl I2C012 for I2C1 {
|
||||
const CLK: sysctl::clock = sysctl::clock::I2C1;
|
||||
const DIV: sysctl::threshold = sysctl::threshold::I2C1;
|
||||
}
|
||||
impl I2C012 for I2C2 {
|
||||
const CLK: sysctl::clock = sysctl::clock::I2C2;
|
||||
const DIV: sysctl::threshold = sysctl::threshold::I2C2;
|
||||
}
|
||||
|
||||
impl<I2C: I2C012> I2CExt for I2C {
|
||||
fn constrain(self) -> I2CImpl<I2C> {
|
||||
I2CImpl::<I2C>::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct I2CImpl<IF> {
|
||||
i2c: IF,
|
||||
}
|
||||
|
||||
pub trait I2C {
|
||||
fn init(&self, slave_address: u16, address_width: u32, i2c_clk: u32);
|
||||
fn recv_data(&self, send_buf: &[u8], receive_buf: &mut [u8]) -> Result<(), ()>;
|
||||
fn send_data(&self, send_buf: &[u8]) -> Result<(), ()>;
|
||||
}
|
||||
|
||||
impl<IF: I2C012> I2CImpl<IF> {
|
||||
pub fn new(i2c: IF) -> Self {
|
||||
Self { i2c }
|
||||
}
|
||||
}
|
||||
|
||||
impl<IF: I2C012> I2C for I2CImpl<IF> {
|
||||
fn init(&self, slave_address: u16, address_width: u32, i2c_clk: u32) {
|
||||
assert!(address_width == 7 || address_width == 10);
|
||||
|
||||
clk_init();
|
||||
sysctl::clock_enable(IF::CLK);
|
||||
sysctl::clock_set_threshold(IF::DIV, 3);
|
||||
|
||||
let v_i2c_freq = sysctl::clock_get_freq(sysctl::clock::I2C0);
|
||||
let v_period_clk_cnt = v_i2c_freq / i2c_clk / 2;
|
||||
@ -24,38 +68,35 @@ pub fn init(slave_address: u16, address_width: u32, i2c_clk: u32)
|
||||
let v_period_clk_cnt = cmp::max(v_period_clk_cnt, 1);
|
||||
|
||||
unsafe {
|
||||
let ptr = pac::I2C0::ptr();
|
||||
(*ptr).enable.write(|w| w.bits(0));
|
||||
(*ptr).con.write(|w| w.master_mode().bit(true)
|
||||
self.i2c.enable.write(|w| w.bits(0));
|
||||
self.i2c.con.write(|w| w.master_mode().bit(true)
|
||||
.slave_disable().bit(true)
|
||||
.restart_en().bit(true)
|
||||
.addr_slave_width().bit(address_width == 10) // TODO variant
|
||||
.speed().bits(1)); // TODO variant
|
||||
(*ptr).ss_scl_hcnt.write(|w| w.count().bits(v_period_clk_cnt));
|
||||
(*ptr).ss_scl_lcnt.write(|w| w.count().bits(v_period_clk_cnt));
|
||||
(*ptr).tar.write(|w| w.address().bits(slave_address));
|
||||
(*ptr).intr_mask.write(|w| w.bits(0));
|
||||
(*ptr).dma_cr.write(|w| w.bits(0x3));
|
||||
(*ptr).dma_rdlr.write(|w| w.bits(0));
|
||||
(*ptr).dma_tdlr.write(|w| w.bits(4));
|
||||
(*ptr).enable.write(|w| w.enable().bit(true));
|
||||
self.i2c.ss_scl_hcnt.write(|w| w.count().bits(v_period_clk_cnt));
|
||||
self.i2c.ss_scl_lcnt.write(|w| w.count().bits(v_period_clk_cnt));
|
||||
self.i2c.tar.write(|w| w.address().bits(slave_address));
|
||||
self.i2c.intr_mask.write(|w| w.bits(0));
|
||||
self.i2c.dma_cr.write(|w| w.bits(0x3));
|
||||
self.i2c.dma_rdlr.write(|w| w.bits(0));
|
||||
self.i2c.dma_tdlr.write(|w| w.bits(4));
|
||||
self.i2c.enable.write(|w| w.enable().bit(true));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recv_data(send_buf: &[u8], receive_buf: &mut [u8]) -> Result<(), ()>
|
||||
{
|
||||
fn recv_data(&self, send_buf: &[u8], receive_buf: &mut [u8]) -> Result<(), ()> {
|
||||
unsafe {
|
||||
let ptr = pac::I2C0::ptr();
|
||||
let mut txi = 0;
|
||||
let mut tx_left = send_buf.len();
|
||||
while tx_left != 0 {
|
||||
let fifo_len = 8 - ((*ptr).txflr.read().bits() as usize);
|
||||
let fifo_len = 8 - (self.i2c.txflr.read().bits() as usize);
|
||||
let fifo_len = cmp::min(tx_left, fifo_len);
|
||||
for _ in 0..fifo_len {
|
||||
(*ptr).data_cmd.write(|w| w.data().bits(send_buf[txi]));
|
||||
self.i2c.data_cmd.write(|w| w.data().bits(send_buf[txi]));
|
||||
txi += 1;
|
||||
}
|
||||
if (*ptr).tx_abrt_source.read().bits() != 0 {
|
||||
if self.i2c.tx_abrt_source.read().bits() != 0 {
|
||||
return Err(());
|
||||
}
|
||||
tx_left -= fifo_len;
|
||||
@ -66,21 +107,21 @@ pub fn recv_data(send_buf: &[u8], receive_buf: &mut [u8]) -> Result<(), ()>
|
||||
let mut rxi = 0;
|
||||
while cmd_count != 0 || rx_left != 0 {
|
||||
/* XXX this is a kind of strange construction, sanity check */
|
||||
let fifo_len = (*ptr).rxflr.read().bits() as usize;
|
||||
let fifo_len = self.i2c.rxflr.read().bits() as usize;
|
||||
let fifo_len = cmp::min(rx_left, fifo_len);
|
||||
for _ in 0..fifo_len {
|
||||
receive_buf[rxi] = (*ptr).data_cmd.read().data().bits();
|
||||
receive_buf[rxi] = self.i2c.data_cmd.read().data().bits();
|
||||
rxi += 1;
|
||||
}
|
||||
rx_left -= fifo_len;
|
||||
|
||||
/* send 0x100 for every byte that we want to receive */
|
||||
let fifo_len = 8 - (*ptr).txflr.read().bits() as usize;
|
||||
let fifo_len = 8 - self.i2c.txflr.read().bits() as usize;
|
||||
let fifo_len = cmp::min(cmd_count, fifo_len);
|
||||
for _ in 0..fifo_len {
|
||||
(*ptr).data_cmd.write(|w| w.cmd().bit(true));
|
||||
self.i2c.data_cmd.write(|w| w.cmd().bit(true));
|
||||
}
|
||||
if (*ptr).tx_abrt_source.read().bits() != 0 {
|
||||
if self.i2c.tx_abrt_source.read().bits() != 0 {
|
||||
return Err(());
|
||||
}
|
||||
cmd_count -= fifo_len;
|
||||
@ -89,40 +130,39 @@ pub fn recv_data(send_buf: &[u8], receive_buf: &mut [u8]) -> Result<(), ()>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn send_data(send_buf: &[u8]) -> Result<(), ()>
|
||||
{
|
||||
fn send_data(&self, send_buf: &[u8]) -> Result<(), ()> {
|
||||
unsafe {
|
||||
let ptr = pac::I2C0::ptr();
|
||||
let mut txi = 0;
|
||||
let mut tx_left = send_buf.len();
|
||||
|
||||
// Clear TX abort by reading from clear register
|
||||
// Hopefully this is not optimized out
|
||||
(*ptr).clr_tx_abrt.read().bits();
|
||||
self.i2c.clr_tx_abrt.read().bits();
|
||||
|
||||
// Send all data that is left, handle errors that occur
|
||||
while tx_left != 0 {
|
||||
let fifo_len = 8 - ((*ptr).txflr.read().bits() as usize);
|
||||
let fifo_len = 8 - (self.i2c.txflr.read().bits() as usize);
|
||||
let fifo_len = cmp::min(tx_left, fifo_len);
|
||||
for _ in 0..fifo_len {
|
||||
(*ptr).data_cmd.write(|w| w.data().bits(send_buf[txi]));
|
||||
self.i2c.data_cmd.write(|w| w.data().bits(send_buf[txi]));
|
||||
txi += 1;
|
||||
}
|
||||
if (*ptr).tx_abrt_source.read().bits() != 0 {
|
||||
if self.i2c.tx_abrt_source.read().bits() != 0 {
|
||||
return Err(());
|
||||
}
|
||||
tx_left -= fifo_len;
|
||||
}
|
||||
|
||||
// Wait for TX succeed
|
||||
while (*ptr).status.read().activity().bit() || !(*ptr).status.read().tfe().bit() {
|
||||
while self.i2c.status.read().activity().bit() || !self.i2c.status.read().tfe().bit() {
|
||||
// NOP
|
||||
}
|
||||
|
||||
// Check for errors one last time
|
||||
if (*ptr).tx_abrt_source.read().bits() != 0 {
|
||||
if self.i2c.tx_abrt_source.read().bits() != 0 {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user