From f6340e4d1f6bd9356830e0c73f0bd813912ea2cc Mon Sep 17 00:00:00 2001 From: Determinant Date: Thu, 21 Sep 2017 15:50:48 -0400 Subject: init --- .cargo/config | 10 ++++++ .gdbinit | 6 ++++ .gitignore | 5 +++ Cargo.toml | 22 +++++++++++++ Xargo.toml | 6 ++++ build.rs | 17 ++++++++++ memory.x | 5 +++ src/ds3231.rs | 77 ++++++++++++++++++++++++++++++++++++++++++++ src/i2c.rs | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 345 insertions(+) create mode 100644 .cargo/config create mode 100644 .gdbinit create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 Xargo.toml create mode 100644 build.rs create mode 100644 memory.x create mode 100644 src/ds3231.rs create mode 100644 src/i2c.rs create mode 100644 src/main.rs diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 0000000..6f29034 --- /dev/null +++ b/.cargo/config @@ -0,0 +1,10 @@ +[target.thumbv7m-none-eabi] +runner = 'arm-none-eabi-gdb' +rustflags = [ + "-C", "link-arg=-Tlink.x", + "-C", "linker=arm-none-eabi-ld", + "-Z", "linker-flavor=ld", +] + +[build] +target = "thumbv7m-none-eabi" diff --git a/.gdbinit b/.gdbinit new file mode 100644 index 0000000..7c72d4f --- /dev/null +++ b/.gdbinit @@ -0,0 +1,6 @@ +target remote :3333 + +monitor arm semihosting enable + +load +step diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8bc31d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +**/*.rs.bk +*.org +.gdb_history +Cargo.lock +target/ diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..332dd58 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "bluepill-ds3231" +description = "Test the communication with DS3231" +authors = ["Ted Yin "] +keywords = ["no-std", "arm", "cortex-m", "stm32"] +license = "GPL-3.0" +version = "0.1.0" + +[dependencies.stm32f103xx] +features = ["rt"] +version = "0.7.2" + +[dependencies.cortex-m-rt] +version = "0.3.5" +features = ["abort-on-panic"] + +[dependencies.cortex-m] +version = "0.3.0" + +[profile.release] +debug = true +lto = true diff --git a/Xargo.toml b/Xargo.toml new file mode 100644 index 0000000..89ad4cd --- /dev/null +++ b/Xargo.toml @@ -0,0 +1,6 @@ +[dependencies.core] +stage = 0 + +[dependencies.compiler_builtins] +features = ["mem"] +stage = 1 \ No newline at end of file diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..34363d2 --- /dev/null +++ b/build.rs @@ -0,0 +1,17 @@ +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +pub fn main() { + // Put the linker script somewhere the linker can find it + 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=build.rs"); + println!("cargo:rerun-if-changed=memory.x"); +} diff --git a/memory.x b/memory.x new file mode 100644 index 0000000..59195b8 --- /dev/null +++ b/memory.x @@ -0,0 +1,5 @@ +MEMORY +{ + FLASH : ORIGIN = 0x08000000, LENGTH = 64K + RAM : ORIGIN = 0x20000000, LENGTH = 20K +} diff --git a/src/ds3231.rs b/src/ds3231.rs new file mode 100644 index 0000000..6ad75d8 --- /dev/null +++ b/src/ds3231.rs @@ -0,0 +1,77 @@ +extern crate stm32f103xx; +use ::i2c::{I2C, TransDir}; + +const DS3231_ADDR: u8 = 0b1101000; +const DS3231_REG_SEC: u8 = 0x00; + +pub struct DS3231<'a> { + i2c: I2C<'a> +} + +pub struct Date { + second: u8, + minute: u8, + hour: u8, + day: u8, + date: u8, + month: u8, + year: u8 +} + +impl<'a> DS3231<'a> { + pub fn new(i2c_reg: &'a stm32f103xx::i2c1::RegisterBlock) -> DS3231<'a> { + DS3231{i2c: I2C::new(i2c_reg)} + } + + fn bcd2dec(bcd: u8) -> u8 { + (bcd >> 4) * 10 + (bcd & 0x0f) + } + + fn dec2bcd(dec: u8) -> u8 { + ((dec / 10) << 4) | (dec % 10) + } + + pub fn read_fulldate(&self) -> Date { + let mut buf: [u8; 7] = [0; 7]; + let i2c = &self.i2c; + i2c.conf_ack(true); /* enable ack */ + i2c.start(true, true); /* start condition (for writing reg addr) */ + i2c.send_addr(DS3231_ADDR, TransDir::TRANSMITTER, true); + i2c.send(DS3231_REG_SEC, true); + /* restart condition (for reading val from the reg addr) */ + i2c.start(true, true); + i2c.send_addr(DS3231_ADDR, TransDir::RECEIVER, true); + for i in 0..6 { + buf[i] = i2c.recv(true); + } + i2c.conf_ack(false); /* disable ack (send nack) */ + buf[6] = i2c.recv(true); + i2c.stop(true); + Date{second: DS3231::bcd2dec(buf[0]), + minute: DS3231::bcd2dec(buf[1]), + hour: DS3231::bcd2dec(buf[2]), + day: DS3231::bcd2dec(buf[3]), + date: DS3231::bcd2dec(buf[4]), + month: DS3231::bcd2dec(buf[5]), + year: DS3231::bcd2dec(buf[6])} + } + + pub fn write_fulldate(&self, date: &Date) { + let i2c = &self.i2c; + let buf: [u8; 7] = [DS3231::dec2bcd(date.second), + DS3231::dec2bcd(date.minute), + DS3231::dec2bcd(date.hour), + DS3231::dec2bcd(date.day), + DS3231::dec2bcd(date.date), + DS3231::dec2bcd(date.month), + DS3231::dec2bcd(date.year)]; + i2c.conf_ack(true); + i2c.start(true, true); /* start condition for writing */ + i2c.send_addr(DS3231_ADDR, TransDir::TRANSMITTER, true); + i2c.send(DS3231_REG_SEC, true); + for i in 0..7 { + i2c.send(buf[i], true); + } + i2c.stop(true); + } +} diff --git a/src/i2c.rs b/src/i2c.rs new file mode 100644 index 0000000..8b6d4da --- /dev/null +++ b/src/i2c.rs @@ -0,0 +1,101 @@ +#![allow(dead_code)] +extern crate stm32f103xx; + +pub const EVENT_MASTER_STARTED: u32 = 0x00030001; /* BUSY, MSL and SB flag */ +pub const EVENT_MASTER_TRANSMITTER_MODE_SELECTED: u32 = 0x00070082; /* BUSY, MSL, ADDR, TXE and TRA flags */ +pub const EVENT_MASTER_RECEIVER_MODE_SELECTED: u32 = 0x00030002; /* BUSY, MSL and ADDR flags */ +pub const EVENT_MASTER_BYTE_RECEIVED: u32 = 0x00030040; /* BUSY, MSL and RXNE flags */ +pub const EVENT_MASTER_BYTE_TRANSMITTING: u32 = 0x00070080; /* TRA, BUSY, MSL, TXE flags */ +pub const EVENT_MASTER_BYTE_TRANSMITTED: u32 = 0x00070084; /* TRA, BUSY, MSL, TXE and BTF flags */ + +const CR1_START_MASK: u16 = 0x0100; +const CR1_STOP_MASK: u16 = 0x0200; +const FLAGS_MASK: u32 = 0x00ffffff; + +pub struct I2C<'a> { + i2c: &'a stm32f103xx::i2c1::RegisterBlock, +} + +pub enum TransDir { + TRANSMITTER, + RECEIVER +} + +impl<'a> I2C<'a> { + pub fn new(reg: &'a stm32f103xx::i2c1::RegisterBlock) -> I2C<'a> { + I2C{i2c: reg} + } + + pub fn start(&self, enable: bool, synced: bool) { + let i2c = &self.i2c; + unsafe { + match enable { + true => i2c.cr1.modify(|r, w| w.bits(r.bits()).start().set_bit()), + false => i2c.cr1.modify(|r, w| w.bits(r.bits()).start().clear_bit()) + } + } + if synced { + while !self.check_event(EVENT_MASTER_STARTED) {} + } + } + + pub fn stop(&self, enable: bool) { + let i2c = &self.i2c; + unsafe { + match enable { + true => i2c.cr1.modify(|r, w| w.bits(r.bits()).stop().set_bit()), + false => i2c.cr1.modify(|r, w| w.bits(r.bits()).stop().clear_bit()) + } + } + } + + pub fn conf_ack(&self, enable: bool) { + let i2c = &self.i2c; + unsafe { + match enable { + true => i2c.cr1.modify(|r, w| w.bits(r.bits()).ack().set_bit()), + false => i2c.cr1.modify(|r, w| w.bits(r.bits()).ack().clear_bit()) + } + } + } + + pub fn send_addr(&self, addr: u8, d: TransDir, synced: bool) { + let addr = (addr << 1) | match d { + TransDir::TRANSMITTER => 0, + TransDir::RECEIVER => 1 + }; + unsafe { + self.i2c.dr.write(|w| w.dr().bits(addr)); + } + if synced { + match d { + TransDir::TRANSMITTER => + while !self.check_event(EVENT_MASTER_TRANSMITTER_MODE_SELECTED) {}, + TransDir::RECEIVER => + while !self.check_event(EVENT_MASTER_RECEIVER_MODE_SELECTED) {} + } + } + } + + pub fn send(&self, data: u8, synced: bool) { + unsafe { + self.i2c.dr.write(|w| w.dr().bits(data)); + } + if synced { + while !self.check_event(EVENT_MASTER_BYTE_TRANSMITTED) {} + } + } + + pub fn recv(&self, synced: bool) -> u8 { + if synced { + while !self.check_event(EVENT_MASTER_BYTE_RECEIVED) {} + } + self.i2c.dr.read().dr().bits() + } + + pub fn check_event(&self, ev_mask: u32) -> bool { + let flags = self.i2c.sr1.read().bits() & 0xffff | + ((self.i2c.sr2.read().bits() & 0xffff) << 16) & FLAGS_MASK; + (flags & ev_mask) == ev_mask + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..168e55a --- /dev/null +++ b/src/main.rs @@ -0,0 +1,96 @@ +#![no_std] + +#[macro_use] extern crate stm32f103xx; +extern crate cortex_m; +use stm32f103xx::{GPIOA, RCC, SYST, I2C1}; +use cortex_m::peripheral::SystClkSource; +mod i2c; +mod ds3231; + +struct ShiftRegister<'a> { + gpioa: &'a stm32f103xx::gpioa::RegisterBlock, + width: u8, +} + +static mut SR: Option = None; +static mut RTC: Option = None; +static mut N: u16 = 0; +static mut DIGITS: [u8; 6] = [0; 6]; + +fn systick_handler() { + unsafe { + /* + SR.as_mut().unwrap().output_bits(N as u32); + N += 1; + */ + SR.as_mut().unwrap().output_bits(digits2bcds(&DIGITS[..])); + let mut i = 0; + let mut carry = 1; + while carry > 0 && i < DIGITS.len() { + DIGITS[i] += carry; + carry = if DIGITS[i] > 9 {DIGITS[i] = 0; 1} else {0}; + i += 1; + } + } +} + +//exception!(SYS_TICK, systick_handler); + +impl<'a> ShiftRegister<'a> { + fn new(g: &'a stm32f103xx::gpioa::RegisterBlock, + width: u8) -> ShiftRegister<'a> { + let this = ShiftRegister{gpioa: g, width: width}; + this + } + + fn output_bits(&mut self, bits: u32) { + let bsrr = &self.gpioa.bsrr; + for i in (0..self.width).rev() { + bsrr.write(|w| w.br1().reset()); + /* feed the ser */ + match (bits >> i) & 1 { + 0 => bsrr.write(|w| w.br0().reset()), + 1 => bsrr.write(|w| w.bs0().set()), + _ => panic!() + } + /* shift (trigger the sclk) */ + bsrr.write(|w| w.bs1().set()); + } + /* latch on (trigger the clk) */ + bsrr.write(|w| w.br2().reset()); + bsrr.write(|w| w.bs2().set()); + } +} + +fn digits2bcds(digs: &[u8]) -> u32 { + let mut res: u32 = 0; + for d in digs.iter().rev() { + res = (res << 4) | (*d as u32); + } + res +} + +fn main() { + + let gpioa: &stm32f103xx::gpioa::RegisterBlock = unsafe { &*GPIOA.get() }; + let rcc: &stm32f103xx::rcc::RegisterBlock = unsafe { &*RCC.get() }; + let i2c: &stm32f103xx::i2c1::RegisterBlock = unsafe { &*I2C1.get() }; + let syst: &cortex_m::peripheral::SYST = unsafe { &*SYST.get() }; + + syst.set_clock_source(SystClkSource::Core); + syst.set_reload(100_000); + syst.enable_interrupt(); + syst.enable_counter(); + rcc.apb2enr.modify(|_, w| w.iopaen().enabled()); + gpioa.crl.modify(|_, w| + w.mode0().output().cnf0().push() + .mode1().output().cnf1().push() + .mode2().output().cnf2().push()); + + unsafe { + RTC = Some(ds3231::DS3231::new(i2c)); + SR = Some(ShiftRegister::new(gpioa, 24)); + SR.as_mut().unwrap().output_bits(0); + let t = RTC.as_mut().unwrap().read_fulldate(); + } +} -- cgit v1.2.3