fw: start debug i2c
This commit is contained in:
parent
c1c0122684
commit
349fe96b0d
@ -36,7 +36,7 @@ const NUM_TX_SLOTS: u32 = 2;
|
|||||||
const MTU: usize = 1530;
|
const MTU: usize = 1530;
|
||||||
const SLOT_LEN: u32 = 2048;
|
const SLOT_LEN: u32 = 2048;
|
||||||
|
|
||||||
use crate::{busy_wait, read_reg, write_reg};
|
use crate::{read_reg, timer::busy_wait_ms, write_reg};
|
||||||
|
|
||||||
pub struct LiteEthDevice {
|
pub struct LiteEthDevice {
|
||||||
csr_addr: u32,
|
csr_addr: u32,
|
||||||
|
@ -2,146 +2,289 @@
|
|||||||
//!
|
//!
|
||||||
//! See `gateware/i2c.py` for register information
|
//! See `gateware/i2c.py` for register information
|
||||||
|
|
||||||
|
use crate::timer::busy_wait_us;
|
||||||
use crate::{read_reg, write_reg};
|
use crate::{read_reg, write_reg};
|
||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
/// TODO repr(C) a register bank, and add instances
|
use embedded_hal::blocking::i2c::{Operation, Read, SevenBitAddress, Transactional, Write};
|
||||||
// Using the blocking API because the peripheral requires fairly tight timing to operate
|
|
||||||
// correctly, and I don't feel like writing the gateware to resolve that.
|
|
||||||
use embedded_hal::blocking::i2c::{Read, SevenBitAddress, Write};
|
|
||||||
|
|
||||||
const CR: u32 = 0;
|
pub const BASE: u32 = 0xF000_1800;
|
||||||
const SR: u32 = 1;
|
|
||||||
const DWR: u32 = 2;
|
const REG_W: u32 = 0x00;
|
||||||
const DRR: u32 = 3;
|
const FIELD_W_SCL: u32 = 0;
|
||||||
|
const FIELD_W_OE: u32 = 1;
|
||||||
|
const FIELD_W_SDA: u32 = 2;
|
||||||
|
|
||||||
|
const REG_R: u32 = 0x04;
|
||||||
|
const FIELD_R_SDA: u32 = 0;
|
||||||
|
|
||||||
|
const WAIT_PERIOD_US: u64 = (1_000_000 / 100_000) / 2;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Device is busy for some reason
|
|
||||||
Busy,
|
|
||||||
/// I2C bus returned a NACK
|
/// I2C bus returned a NACK
|
||||||
Nack,
|
Nack,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AmlibI2c {
|
pub struct LitexI2c {
|
||||||
base_addr: u32,
|
base_addr: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
use core::fmt::Write as _;
|
use core::fmt::Write as _;
|
||||||
|
|
||||||
impl AmlibI2c {
|
impl LitexI2c {
|
||||||
pub fn new(base_addr: u32) -> Self {
|
pub fn new(base_addr: u32) -> Self {
|
||||||
AmlibI2c { base_addr }
|
LitexI2c { base_addr }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait_while_busy(&mut self) {
|
fn set_scl(&mut self, val: u32) {
|
||||||
unsafe {
|
unsafe {
|
||||||
while read_reg::<u8>(self.base_addr + SR) & 1 != 0 {
|
write_reg(self.base_addr + REG_W, (val & 0x01) << FIELD_W_SCL);
|
||||||
asm!("nop");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_nack(&self) -> bool {
|
fn set_sda(&mut self, val: u32) {
|
||||||
unsafe { read_reg::<u8>(self.base_addr + SR) & 0x02 == 0 }
|
unsafe {
|
||||||
|
write_reg(self.base_addr + REG_W, (val & 0x01) << FIELD_W_SDA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_oe(&mut self, val: u32) {
|
||||||
|
unsafe {
|
||||||
|
write_reg(self.base_addr + REG_W, (val & 0x01) << FIELD_W_OE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_sda(&mut self) -> u8 {
|
||||||
|
unsafe { read_reg::<u8>(self.base_addr + REG_R) & (1 << FIELD_R_SDA) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate a START condition. The next operation should be a clock
|
||||||
|
/// transition to low.
|
||||||
|
///
|
||||||
|
/// Assumes SCL and SDA have been high due to bus inactivity
|
||||||
|
unsafe fn start(&mut self) {
|
||||||
|
self.set_oe(1);
|
||||||
|
self.set_scl(1);
|
||||||
|
self.set_sda(1);
|
||||||
|
busy_wait_us(WAIT_PERIOD_US);
|
||||||
|
|
||||||
|
self.set_sda(0);
|
||||||
|
busy_wait_us(WAIT_PERIOD_US);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate a repeated START condition.
|
||||||
|
///
|
||||||
|
/// Assumes it was called after an ACK bit
|
||||||
|
unsafe fn repeated_start(&mut self) {
|
||||||
|
self.set_scl(0);
|
||||||
|
self.set_sda(1);
|
||||||
|
self.set_oe(1);
|
||||||
|
busy_wait_us(WAIT_PERIOD_US);
|
||||||
|
|
||||||
|
self.set_scl(1);
|
||||||
|
busy_wait_us(WAIT_PERIOD_US);
|
||||||
|
|
||||||
|
// TODO does this third half-clock break timing?
|
||||||
|
self.set_sda(0);
|
||||||
|
busy_wait_us(WAIT_PERIOD_US);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles STOP conditions.
|
||||||
|
///
|
||||||
|
/// Assumes it was called after an ACK bit
|
||||||
|
unsafe fn stop(&mut self) {
|
||||||
|
self.set_scl(0);
|
||||||
|
self.set_sda(0);
|
||||||
|
self.set_oe(1);
|
||||||
|
busy_wait_us(WAIT_PERIOD_US);
|
||||||
|
|
||||||
|
self.set_scl(1);
|
||||||
|
busy_wait_us(WAIT_PERIOD_US);
|
||||||
|
|
||||||
|
// TODO does this third half-clock break timing?
|
||||||
|
//self.set_oe(0); // Disable and let pullup take bus high
|
||||||
|
self.set_sda(1);
|
||||||
|
busy_wait_us(WAIT_PERIOD_US);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn write_byte(&mut self, byte: u8) {
|
||||||
|
self.set_oe(1); // Take SDA to write data out
|
||||||
|
|
||||||
|
for i in 0..8 {
|
||||||
|
// Write data out before we clock high
|
||||||
|
self.set_scl(0);
|
||||||
|
busy_wait_us(WAIT_PERIOD_US);
|
||||||
|
|
||||||
|
// Clock data in
|
||||||
|
self.set_scl(1);
|
||||||
|
self.set_sda(byte as u32 >> (7 - i));
|
||||||
|
busy_wait_us(WAIT_PERIOD_US)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn read_byte(&mut self) -> u8 {
|
||||||
|
self.set_oe(0); // De-assert to read data
|
||||||
|
|
||||||
|
let mut out = 0;
|
||||||
|
for i in 0..8 {
|
||||||
|
self.set_scl(0);
|
||||||
|
busy_wait_us(WAIT_PERIOD_US);
|
||||||
|
|
||||||
|
self.set_scl(1);
|
||||||
|
busy_wait_us(WAIT_PERIOD_US);
|
||||||
|
|
||||||
|
// TODO sampling at the end of the clock may not work 100% of the time
|
||||||
|
// ideally should sample in the middle
|
||||||
|
out |= (self.get_sda() << 7 - i);
|
||||||
|
}
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns if there was an ACK
|
||||||
|
unsafe fn get_ack(&mut self) -> bool {
|
||||||
|
self.set_oe(0); // Release SDA to read ACK
|
||||||
|
self.set_sda(1);
|
||||||
|
self.set_scl(0);
|
||||||
|
busy_wait_us(WAIT_PERIOD_US);
|
||||||
|
|
||||||
|
self.set_scl(1);
|
||||||
|
busy_wait_us(WAIT_PERIOD_US);
|
||||||
|
|
||||||
|
self.get_sda() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn set_ack(&mut self, ack: bool) {
|
||||||
|
self.set_oe(1); // Take SDA
|
||||||
|
self.set_sda(!ack as u32);
|
||||||
|
self.set_scl(0);
|
||||||
|
busy_wait_us(WAIT_PERIOD_US);
|
||||||
|
|
||||||
|
self.set_scl(1);
|
||||||
|
busy_wait_us(WAIT_PERIOD_US);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Write<SevenBitAddress> for AmlibI2c {
|
impl Write<SevenBitAddress> for LitexI2c {
|
||||||
// TODO errors
|
// TODO errors
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn write(&mut self, address: SevenBitAddress, bytes: &[u8]) -> Result<(), Self::Error> {
|
fn write(&mut self, address: SevenBitAddress, bytes: &[u8]) -> Result<(), Self::Error> {
|
||||||
unsafe {
|
unsafe {
|
||||||
if (read_reg::<u8>(self.base_addr + SR) & 1) != 0 {
|
self.start();
|
||||||
return Err(Error::Busy);
|
|
||||||
}
|
|
||||||
|
|
||||||
// START
|
// Write address and write bit
|
||||||
write_reg(self.base_addr + CR, 0x01u8);
|
self.write_byte((address << 1) | 0);
|
||||||
|
if !self.get_ack() {
|
||||||
self.wait_while_busy();
|
self.stop();
|
||||||
|
|
||||||
// Send address byte (R/~W = 0)
|
|
||||||
write_reg(self.base_addr + DWR, address << 1);
|
|
||||||
write_reg(self.base_addr + CR, 0x04u8);
|
|
||||||
self.wait_while_busy();
|
|
||||||
|
|
||||||
// Check NACK
|
|
||||||
if self.is_nack() {
|
|
||||||
return Err(Error::Nack);
|
return Err(Error::Nack);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write data
|
for b in bytes {
|
||||||
for byte in bytes {
|
self.write_byte(*b);
|
||||||
// Write byte
|
if !self.get_ack() {
|
||||||
write_reg(self.base_addr + DWR, *byte);
|
self.stop();
|
||||||
// Send byte
|
|
||||||
write_reg(self.base_addr + CR, 0x04u8);
|
|
||||||
self.wait_while_busy();
|
|
||||||
|
|
||||||
if self.is_nack() {
|
|
||||||
return Err(Error::Nack);
|
return Err(Error::Nack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// STOP
|
self.stop();
|
||||||
write_reg(self.base_addr + CR, 0x02u8);
|
|
||||||
|
|
||||||
self.wait_while_busy();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Read<SevenBitAddress> for AmlibI2c {
|
impl Read<SevenBitAddress> for LitexI2c {
|
||||||
// TODO errors
|
// TODO errors
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn read(&mut self, address: SevenBitAddress, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
fn read(&mut self, address: SevenBitAddress, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
unsafe {
|
unsafe {
|
||||||
if (read_reg::<u8>(self.base_addr + SR) & 1) != 0 {
|
self.start();
|
||||||
return Err(Error::Busy);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set read ACK to 1. Should probably ideally be done in an init() function
|
self.write_byte((address << 1) | 1);
|
||||||
// but it doesn't really matter
|
if !self.get_ack() {
|
||||||
write_reg(self.base_addr + CR, 0x30u8);
|
self.stop();
|
||||||
|
|
||||||
// START
|
|
||||||
write_reg(self.base_addr + CR, 0x01u8);
|
|
||||||
|
|
||||||
self.wait_while_busy();
|
|
||||||
|
|
||||||
// Send address byte (R/~W = 1)
|
|
||||||
write_reg(self.base_addr + DWR, (address << 1) + 1);
|
|
||||||
write_reg(self.base_addr + CR, 0x04u8);
|
|
||||||
self.wait_while_busy();
|
|
||||||
if self.is_nack() {
|
|
||||||
return Err(Error::Nack);
|
return Err(Error::Nack);
|
||||||
}
|
}
|
||||||
|
|
||||||
for byte in buffer {
|
for b in buffer {
|
||||||
// Start reading in a byte
|
*b = self.read_byte();
|
||||||
write_reg(self.base_addr + CR, 0x08u8);
|
self.set_ack(true);
|
||||||
self.wait_while_busy();
|
|
||||||
// Value is available once busy is clear
|
|
||||||
*byte = read_reg::<u8>(self.base_addr + DRR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// STOP
|
self.stop();
|
||||||
write_reg(self.base_addr + CR, 0x02u8);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// I don't need this for MCP4726
|
impl Transactional for LitexI2c {
|
||||||
//impl Transactional for AmlibI2c {
|
type Error = Error;
|
||||||
// type Error = Error;
|
|
||||||
//
|
fn exec<'a>(
|
||||||
// fn exec<'a>(&mut self, address: u8, operations: &mut [Operation<'a>])
|
&mut self,
|
||||||
// -> Result<(), Self::Error> {
|
address: u8,
|
||||||
// Ok(())
|
operations: &mut [Operation<'a>],
|
||||||
// }
|
) -> Result<(), Self::Error> {
|
||||||
//}
|
unsafe {
|
||||||
|
let mut last_operation_was_read: Option<bool> = None;
|
||||||
|
|
||||||
|
for op in operations {
|
||||||
|
let op_is_read = match op {
|
||||||
|
Operation::Write(_) => false,
|
||||||
|
Operation::Read(_) => true,
|
||||||
|
};
|
||||||
|
|
||||||
|
match last_operation_was_read {
|
||||||
|
None => {
|
||||||
|
// First operation; send regular start and address
|
||||||
|
self.start();
|
||||||
|
self.write_byte((address << 1) | op_is_read as u8);
|
||||||
|
if !self.get_ack() {
|
||||||
|
self.stop();
|
||||||
|
return Err(Error::Nack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(last_op_was_read) => {
|
||||||
|
// Not the first, send repeated start
|
||||||
|
self.repeated_start();
|
||||||
|
|
||||||
|
if last_op_was_read != op_is_read {
|
||||||
|
// Operations don't match, so we need to send address byte
|
||||||
|
self.write_byte((address << 1) | op_is_read as u8);
|
||||||
|
if !self.get_ack() {
|
||||||
|
self.stop();
|
||||||
|
return Err(Error::Nack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match op {
|
||||||
|
Operation::Read(buffer) => {
|
||||||
|
for b in buffer.iter_mut() {
|
||||||
|
*b = self.read_byte();
|
||||||
|
self.set_ack(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Operation::Write(buffer) => {
|
||||||
|
for b in buffer.iter() {
|
||||||
|
self.write_byte(*b);
|
||||||
|
if !self.get_ack() {
|
||||||
|
self.stop();
|
||||||
|
return Err(Error::Nack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
last_operation_was_read = Some(op_is_read);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.stop();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -24,7 +24,7 @@ unsafe impl defmt::Logger for DefmtLogger {
|
|||||||
unsafe fn write(bytes: &[u8]) {
|
unsafe fn write(bytes: &[u8]) {
|
||||||
static mut UART: Option<LitexUart> = None;
|
static mut UART: Option<LitexUart> = None;
|
||||||
if UART.is_none() {
|
if UART.is_none() {
|
||||||
UART = Some(LitexUart::new(0xf000_4000));
|
UART = Some(LitexUart::new(0xf000_4800));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut dev = UART.unwrap();
|
let mut dev = UART.unwrap();
|
||||||
|
@ -6,9 +6,7 @@
|
|||||||
extern crate panic_halt;
|
extern crate panic_halt;
|
||||||
|
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
use core::{
|
use core::ptr::{read_volatile, write_volatile};
|
||||||
ptr::{read_volatile, write_volatile},
|
|
||||||
};
|
|
||||||
|
|
||||||
use embedded_hal::prelude::{_embedded_hal_blocking_i2c_Read, _embedded_hal_blocking_i2c_Write};
|
use embedded_hal::prelude::{_embedded_hal_blocking_i2c_Read, _embedded_hal_blocking_i2c_Write};
|
||||||
use mcp4726::Status;
|
use mcp4726::Status;
|
||||||
@ -30,9 +28,9 @@ mod i2c;
|
|||||||
mod logging;
|
mod logging;
|
||||||
mod mcp4726;
|
mod mcp4726;
|
||||||
mod proto;
|
mod proto;
|
||||||
mod uart;
|
|
||||||
mod sampler;
|
mod sampler;
|
||||||
mod timer;
|
mod timer;
|
||||||
|
mod uart;
|
||||||
|
|
||||||
use timer::millis;
|
use timer::millis;
|
||||||
|
|
||||||
@ -115,6 +113,9 @@ fn main() -> ! {
|
|||||||
|
|
||||||
let mut cmd = command_interface::CommandInterface::new();
|
let mut cmd = command_interface::CommandInterface::new();
|
||||||
|
|
||||||
|
let mut i2c = i2c::LitexI2c::new(i2c::BASE);
|
||||||
|
let mut dac = mcp4726::MCP4726::new(3);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let now = millis();
|
let now = millis();
|
||||||
iface.poll(Instant::from_millis(now), &mut device, &mut socket_set);
|
iface.poll(Instant::from_millis(now), &mut device, &mut socket_set);
|
||||||
@ -123,7 +124,7 @@ fn main() -> ! {
|
|||||||
cmd.run(socket_set.get_mut(command_socket));
|
cmd.run(socket_set.get_mut(command_socket));
|
||||||
|
|
||||||
// TODO the need for the second check screams something is unsound somewhere
|
// TODO the need for the second check screams something is unsound somewhere
|
||||||
if now - last_blink > 1000 && now > last_blink {
|
if now - last_blink >= 1000 && now > last_blink {
|
||||||
last_blink = now;
|
last_blink = now;
|
||||||
toggle = !toggle;
|
toggle = !toggle;
|
||||||
write_led(if toggle { 1 } else { 0 });
|
write_led(if toggle { 1 } else { 0 });
|
||||||
@ -133,31 +134,59 @@ fn main() -> ! {
|
|||||||
sock.listen(3000);
|
sock.listen(3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
if toggle {
|
defmt::debug!("heartbeat, time: {}", millis());
|
||||||
sampler::clear_buffers();
|
|
||||||
sampler::start_sampling();
|
|
||||||
|
|
||||||
let buf = unsafe {sampler::get_sample_buffer(0) };
|
match dac.write_config(
|
||||||
let status = sampler::read_status();
|
&mut i2c,
|
||||||
let raw_reg: u32 = unsafe { read_reg(0x8040_0004) };
|
mcp4726::Config {
|
||||||
defmt::debug!("Start: len: {}, complete: {}, running: {}, status: {}", buf.len(), status.capture_complete, status.sampling, raw_reg);
|
vref_source: mcp4726::VRef::UnbufferedVRef,
|
||||||
} else {
|
operation: mcp4726::PowerDown::NormalOperation,
|
||||||
sampler::stop_sampling();
|
use_2x_gain: false,
|
||||||
let buf = unsafe {sampler::get_sample_buffer(0) };
|
},
|
||||||
defmt::debug!("Stopped, len: {}", buf.len());
|
) {
|
||||||
|
Ok(_) => defmt::debug!("Write config success"),
|
||||||
|
Err(_) => defmt::debug!("Write config failed"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//match dac.read_status(&mut i2c) {
|
||||||
|
// Ok(status) => {
|
||||||
|
// defmt::debug!(
|
||||||
|
// "DAC status: ready: {}, powered: {}",
|
||||||
|
// status.ready,
|
||||||
|
// status.device_powered
|
||||||
|
// );
|
||||||
|
|
||||||
|
// if sock.can_send() {
|
||||||
|
// write!(sock, "DAC: ready: {}, powered: {}", status.ready, status.device_powered);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// Err(_) => defmt::debug!("Error reading from DAC"),
|
||||||
|
//};
|
||||||
|
|
||||||
|
//if toggle {
|
||||||
|
// sampler::clear_buffers();
|
||||||
|
// sampler::start_sampling();
|
||||||
|
|
||||||
|
// let buf = unsafe {sampler::get_sample_buffer(0) };
|
||||||
|
// let status = sampler::read_status();
|
||||||
|
// let raw_reg: u32 = unsafe { read_reg(0x8040_0004) };
|
||||||
|
// defmt::debug!("Start: len: {}, complete: {}, running: {}, status: {}", buf.len(), status.capture_complete, status.sampling, raw_reg);
|
||||||
|
//} else {
|
||||||
|
// sampler::stop_sampling();
|
||||||
|
// let buf = unsafe {sampler::get_sample_buffer(0) };
|
||||||
|
// defmt::debug!("Stopped, len: {}", buf.len());
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO I think the timer might actually stop until the event is cleared? this may pose
|
// TODO I think the timer might actually stop until the event is cleared? this may pose
|
||||||
// problems, might explain why moving this above the smoltcp stuff "broke" things
|
// problems, might explain why moving this above the smoltcp stuff "broke" things
|
||||||
timer::poll();
|
timer::poll();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_led(val: u32) {
|
fn write_led(val: u32) {
|
||||||
unsafe {
|
unsafe {
|
||||||
write_reg(0xf000_2000, val);
|
write_reg(0xf000_2800, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Driver for custom sampler peripheral
|
//! Driver for custom sampler peripheral
|
||||||
|
|
||||||
use crate::{write_reg, read_reg};
|
use crate::{read_reg, write_reg};
|
||||||
|
|
||||||
const BASE: u32 = 0x8040_0000;
|
const BASE: u32 = 0x8040_0000;
|
||||||
|
|
||||||
@ -61,4 +61,4 @@ pub unsafe fn get_sample_buffer(sampler: u8) -> &'static [u32] {
|
|||||||
let addr = BASE + SAMPLE_MEM_LEN * (sampler as u32 + 1);
|
let addr = BASE + SAMPLE_MEM_LEN * (sampler as u32 + 1);
|
||||||
|
|
||||||
core::slice::from_raw_parts(addr as *const u32, len as usize)
|
core::slice::from_raw_parts(addr as *const u32, len as usize)
|
||||||
}
|
}
|
||||||
|
@ -3,15 +3,17 @@
|
|||||||
use crate::{read_reg, write_reg};
|
use crate::{read_reg, write_reg};
|
||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
|
|
||||||
|
const BASE: u32 = 0xf000_4000;
|
||||||
|
|
||||||
/// Initializes the timer to use for polling events as a wall clock.
|
/// Initializes the timer to use for polling events as a wall clock.
|
||||||
///
|
///
|
||||||
/// Timer is running at system frequency (60MHz)
|
/// Timer is running at system frequency (60MHz)
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
unsafe {
|
unsafe {
|
||||||
write_reg(0xf000_3808, 0u32); // Disable timer
|
write_reg(BASE + 0x08, 0u32); // Disable timer
|
||||||
write_reg(0xf000_3800, 0u32); // Set LOAD value
|
write_reg(BASE + 0x00, 0u32); // Set LOAD value
|
||||||
write_reg(0xf000_3804, 60_000_000u32); // Set RELOAD value
|
write_reg(BASE + 0x04, 60_000_000u32); // Set RELOAD value
|
||||||
write_reg(0xf000_3808, 1u32); // Enable timer
|
write_reg(BASE + 0x08, 1u32); // Enable timer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,13 +23,13 @@ static mut SECONDS: u32 = 0;
|
|||||||
/// Processes potential timer events. Must be polled regularly
|
/// Processes potential timer events. Must be polled regularly
|
||||||
pub fn poll() {
|
pub fn poll() {
|
||||||
unsafe {
|
unsafe {
|
||||||
if read_reg::<u32>(0xf000_3818) == 0 {
|
if read_reg::<u32>(BASE + 0x18) == 0 {
|
||||||
// No event yet, continue
|
// No event yet, continue
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear TIMER0 event status, and update time
|
// Clear TIMER0 event status, and update time
|
||||||
write_reg(0xf000_3818, 1u32);
|
write_reg(BASE + 0x18, 1u32);
|
||||||
SECONDS += 1;
|
SECONDS += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -37,9 +39,9 @@ pub fn millis() -> u32 {
|
|||||||
riscv::interrupt::free(|| {
|
riscv::interrupt::free(|| {
|
||||||
unsafe {
|
unsafe {
|
||||||
// Latch timer value
|
// Latch timer value
|
||||||
write_reg(0xf000_380c, 1u32);
|
write_reg(BASE + 0x0C, 1u32);
|
||||||
// Read timer value
|
// Read timer value
|
||||||
let val: u32 = read_reg(0xf000_3810);
|
let val: u32 = read_reg(BASE + 0x10);
|
||||||
let val = 60_000_000 - val;
|
let val = 60_000_000 - val;
|
||||||
(SECONDS * 1000) + val / 60_000
|
(SECONDS * 1000) + val / 60_000
|
||||||
}
|
}
|
||||||
@ -51,9 +53,9 @@ pub fn micros() -> u64 {
|
|||||||
riscv::interrupt::free(|| {
|
riscv::interrupt::free(|| {
|
||||||
unsafe {
|
unsafe {
|
||||||
// Latch timer value
|
// Latch timer value
|
||||||
write_reg(0xf000_380c, 1u32);
|
write_reg(BASE + 0x0C, 1u32);
|
||||||
// Read timer value
|
// Read timer value
|
||||||
let val: u32 = read_reg(0xf000_3810);
|
let val: u32 = read_reg(BASE + 0x10);
|
||||||
let val = 60_000_000 - val;
|
let val = 60_000_000 - val;
|
||||||
(SECONDS as u64 * 1_000_000) + (val as u64 / 60)
|
(SECONDS as u64 * 1_000_000) + (val as u64 / 60)
|
||||||
}
|
}
|
||||||
@ -71,7 +73,7 @@ pub fn busy_wait_ms(ms: u32) {
|
|||||||
|
|
||||||
pub fn busy_wait_us(us: u64) {
|
pub fn busy_wait_us(us: u64) {
|
||||||
let start = micros();
|
let start = micros();
|
||||||
while micros() - start < us {
|
while micros() - start < us {
|
||||||
unsafe {
|
unsafe {
|
||||||
asm!("nop");
|
asm!("nop");
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user