Add first TTC/PWM driver
This commit is contained in:
@ -13,9 +13,9 @@ use crate::gpio::{
|
||||
use crate::{
|
||||
enable_amba_peripheral_clock,
|
||||
gpio::{
|
||||
IoPeriph, Mio10, Mio11, Mio12, Mio13, Mio14, Mio15, Mio28, Mio29, Mio30, Mio31, Mio32,
|
||||
Mio33, Mio34, Mio35, Mio36, Mio37, Mio38, Mio39, Mio48, Mio49, Mio52, Mio53, MioPin,
|
||||
MuxConf, PinMode,
|
||||
IoPeriph, IoPeriphPin, Mio10, Mio11, Mio12, Mio13, Mio14, Mio15, Mio28, Mio29, Mio30,
|
||||
Mio31, Mio32, Mio33, Mio34, Mio35, Mio36, Mio37, Mio38, Mio39, Mio48, Mio49, Mio52, Mio53,
|
||||
MioPin, MuxConf, PinMode,
|
||||
},
|
||||
slcr::Slcr,
|
||||
time::Hertz,
|
||||
@ -55,11 +55,11 @@ impl PsI2c for MmioI2c<'static> {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SdaPin {
|
||||
pub trait SdaPin: IoPeriphPin {
|
||||
const ID: I2cId;
|
||||
}
|
||||
|
||||
pub trait SckPin {
|
||||
pub trait SckPin: IoPeriphPin {
|
||||
const ID: I2cId;
|
||||
}
|
||||
|
||||
@ -291,14 +291,16 @@ impl ClockConfig {
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("invalid I2C ID")]
|
||||
pub struct InvalidI2cIdError;
|
||||
pub struct InvalidPsI2cError;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum I2cConstructionError {
|
||||
#[error("invalid I2C ID {0}")]
|
||||
InvalidI2cId(#[from] InvalidI2cIdError),
|
||||
InvalidPsI2c(#[from] InvalidPsI2cError),
|
||||
#[error("pin invalid for I2C ID")]
|
||||
PinInvalidForI2cId,
|
||||
#[error("invalid pin configuration for I2C")]
|
||||
InvalidPinConf,
|
||||
}
|
||||
pub struct I2c {
|
||||
regs: MmioI2c<'static>,
|
||||
@ -308,14 +310,17 @@ impl I2c {
|
||||
pub fn new_with_mio<Sck: SckPin, Sda: SdaPin>(
|
||||
i2c: impl PsI2c,
|
||||
clk_cfg: ClockConfig,
|
||||
_i2c_pins: (Sck, Sda),
|
||||
i2c_pins: (Sck, Sda),
|
||||
) -> Result<Self, I2cConstructionError> {
|
||||
if i2c.id().is_none() {
|
||||
return Err(InvalidI2cIdError.into());
|
||||
return Err(InvalidPsI2cError.into());
|
||||
}
|
||||
if Sck::ID != Sda::ID {
|
||||
return Err(I2cConstructionError::PinInvalidForI2cId);
|
||||
}
|
||||
if i2c_pins.0.mux_conf() != I2C_MUX_CONF || i2c_pins.1.mux_conf() != I2C_MUX_CONF {
|
||||
return Err(I2cConstructionError::InvalidPinConf);
|
||||
}
|
||||
Ok(Self::new_generic(
|
||||
i2c.id().unwrap(),
|
||||
i2c.reg_block(),
|
||||
@ -323,9 +328,9 @@ impl I2c {
|
||||
))
|
||||
}
|
||||
|
||||
pub fn new_with_emio(i2c: impl PsI2c, clk_cfg: ClockConfig) -> Result<Self, InvalidI2cIdError> {
|
||||
pub fn new_with_emio(i2c: impl PsI2c, clk_cfg: ClockConfig) -> Result<Self, InvalidPsI2cError> {
|
||||
if i2c.id().is_none() {
|
||||
return Err(InvalidI2cIdError);
|
||||
return Err(InvalidPsI2cError);
|
||||
}
|
||||
Ok(Self::new_generic(
|
||||
i2c.id().unwrap(),
|
||||
|
@ -22,6 +22,7 @@ pub mod prelude;
|
||||
pub mod slcr;
|
||||
pub mod spi;
|
||||
pub mod time;
|
||||
pub mod ttc;
|
||||
pub mod uart;
|
||||
|
||||
/// This enumeration encodes the various boot sources.
|
||||
|
@ -3,8 +3,8 @@ use core::convert::Infallible;
|
||||
use crate::clocks::Clocks;
|
||||
use crate::enable_amba_peripheral_clock;
|
||||
use crate::gpio::{
|
||||
IoPeriph, Mio10, Mio11, Mio12, Mio13, Mio14, Mio15, Mio28, Mio29, Mio30, Mio31, Mio32, Mio33,
|
||||
Mio34, Mio35, Mio36, Mio37, Mio38, Mio39, MioPin, MuxConf,
|
||||
IoPeriph, IoPeriphPin, Mio10, Mio11, Mio12, Mio13, Mio14, Mio15, Mio28, Mio29, Mio30, Mio31,
|
||||
Mio32, Mio33, Mio34, Mio35, Mio36, Mio37, Mio38, Mio39, MioPin, MuxConf,
|
||||
};
|
||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||
use crate::gpio::{
|
||||
@ -54,22 +54,22 @@ impl PsSpi for MmioSpi<'static> {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SckPin {
|
||||
pub trait SckPin: IoPeriphPin {
|
||||
const SPI: SpiId;
|
||||
const GROUP: usize;
|
||||
}
|
||||
|
||||
pub trait MosiPin {
|
||||
pub trait MosiPin: IoPeriphPin {
|
||||
const SPI: SpiId;
|
||||
const GROUP: usize;
|
||||
}
|
||||
|
||||
pub trait MisoPin {
|
||||
pub trait MisoPin: IoPeriphPin {
|
||||
const SPI: SpiId;
|
||||
const GROUP: usize;
|
||||
}
|
||||
|
||||
pub trait SsPin {
|
||||
pub trait SsPin: IoPeriphPin {
|
||||
const IDX: usize;
|
||||
const SPI: SpiId;
|
||||
const GROUP: usize;
|
||||
@ -413,18 +413,21 @@ pub struct Spi {
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("invalid SPI ID")]
|
||||
pub struct InvalidSpiIdError;
|
||||
pub struct InvalidPsSpiError;
|
||||
|
||||
// TODO: Add and handle MUX config check.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum SpiConstructionError {
|
||||
#[error("invalid SPI ID {0}")]
|
||||
InvalidSpiId(#[from] InvalidSpiIdError),
|
||||
InvalidPsSpi(#[from] InvalidPsSpiError),
|
||||
/// The specified pins are not compatible to the specified SPI peripheral.
|
||||
#[error("pin invalid for SPI ID")]
|
||||
PinInvalidForSpiId,
|
||||
/// The specified pins are not from the same pin group.
|
||||
#[error("pin group missmatch")]
|
||||
GroupMissmatch,
|
||||
#[error("invalid pin configuration for SPI")]
|
||||
InvalidPinConf,
|
||||
}
|
||||
|
||||
impl Spi {
|
||||
@ -432,11 +435,11 @@ impl Spi {
|
||||
spi: impl PsSpi,
|
||||
clocks: &IoClocks,
|
||||
config: Config,
|
||||
_spi_pins: (Sck, Mosi, Miso),
|
||||
spi_pins: (Sck, Mosi, Miso),
|
||||
) -> Result<Self, SpiConstructionError> {
|
||||
let spi_id = spi.id();
|
||||
if spi_id.is_none() {
|
||||
return Err(InvalidSpiIdError.into());
|
||||
return Err(InvalidPsSpiError.into());
|
||||
}
|
||||
let spi_id = spi_id.unwrap();
|
||||
if Sck::GROUP != Mosi::GROUP || Sck::GROUP != Miso::GROUP {
|
||||
@ -445,6 +448,12 @@ impl Spi {
|
||||
if Sck::SPI != spi_id || Mosi::SPI != spi_id || Miso::SPI != spi_id {
|
||||
return Err(SpiConstructionError::PinInvalidForSpiId);
|
||||
}
|
||||
if spi_pins.0.mux_conf() != SPI_MUX_CONF
|
||||
|| spi_pins.0.mux_conf() != spi_pins.2.mux_conf()
|
||||
|| spi_pins.1.mux_conf() != spi_pins.2.mux_conf()
|
||||
{
|
||||
return Err(SpiConstructionError::InvalidPinConf);
|
||||
}
|
||||
Ok(Self::new_generic_unchecked(
|
||||
spi_id,
|
||||
spi.reg_block(),
|
||||
@ -457,12 +466,12 @@ impl Spi {
|
||||
spi: impl PsSpi,
|
||||
clocks: &IoClocks,
|
||||
config: Config,
|
||||
_spi_pins: (Sck, Mosi, Miso),
|
||||
_ss_pin: Ss,
|
||||
spi_pins: (Sck, Mosi, Miso),
|
||||
ss_pin: Ss,
|
||||
) -> Result<Self, SpiConstructionError> {
|
||||
let spi_id = spi.id();
|
||||
if spi_id.is_none() {
|
||||
return Err(InvalidSpiIdError.into());
|
||||
return Err(InvalidPsSpiError.into());
|
||||
}
|
||||
let spi_id = spi_id.unwrap();
|
||||
if Sck::GROUP != Mosi::GROUP || Sck::GROUP != Miso::GROUP || Sck::GROUP != Ss::GROUP {
|
||||
@ -471,6 +480,13 @@ impl Spi {
|
||||
if Sck::SPI != spi_id || Mosi::SPI != spi_id || Miso::SPI != spi_id || Ss::SPI != spi_id {
|
||||
return Err(SpiConstructionError::PinInvalidForSpiId);
|
||||
}
|
||||
if spi_pins.0.mux_conf() != SPI_MUX_CONF
|
||||
|| spi_pins.0.mux_conf() != spi_pins.2.mux_conf()
|
||||
|| spi_pins.1.mux_conf() != spi_pins.2.mux_conf()
|
||||
|| ss_pin.mux_conf() != spi_pins.0.mux_conf()
|
||||
{
|
||||
return Err(SpiConstructionError::InvalidPinConf);
|
||||
}
|
||||
Ok(Self::new_generic_unchecked(
|
||||
spi_id,
|
||||
spi.reg_block(),
|
||||
@ -483,12 +499,12 @@ impl Spi {
|
||||
spi: impl PsSpi,
|
||||
clocks: &IoClocks,
|
||||
config: Config,
|
||||
_spi_pins: (Sck, Mosi, Miso),
|
||||
_ss_pins: (Ss0, Ss1),
|
||||
spi_pins: (Sck, Mosi, Miso),
|
||||
ss_pins: (Ss0, Ss1),
|
||||
) -> Result<Self, SpiConstructionError> {
|
||||
let spi_id = spi.id();
|
||||
if spi_id.is_none() {
|
||||
return Err(InvalidSpiIdError.into());
|
||||
return Err(InvalidPsSpiError.into());
|
||||
}
|
||||
let spi_id = spi_id.unwrap();
|
||||
if Sck::GROUP != Mosi::GROUP
|
||||
@ -506,6 +522,14 @@ impl Spi {
|
||||
{
|
||||
return Err(SpiConstructionError::PinInvalidForSpiId);
|
||||
}
|
||||
if spi_pins.0.mux_conf() != SPI_MUX_CONF
|
||||
|| spi_pins.0.mux_conf() != spi_pins.2.mux_conf()
|
||||
|| spi_pins.1.mux_conf() != spi_pins.2.mux_conf()
|
||||
|| ss_pins.0.mux_conf() != spi_pins.0.mux_conf()
|
||||
|| ss_pins.1.mux_conf() != spi_pins.0.mux_conf()
|
||||
{
|
||||
return Err(SpiConstructionError::InvalidPinConf);
|
||||
}
|
||||
Ok(Self::new_generic_unchecked(
|
||||
spi_id,
|
||||
spi.reg_block(),
|
||||
@ -525,12 +549,12 @@ impl Spi {
|
||||
spi: impl PsSpi,
|
||||
clocks: &IoClocks,
|
||||
config: Config,
|
||||
_spi_pins: (Sck, Mosi, Miso),
|
||||
_ss_pins: (Ss0, Ss1, Ss2),
|
||||
spi_pins: (Sck, Mosi, Miso),
|
||||
ss_pins: (Ss0, Ss1, Ss2),
|
||||
) -> Result<Self, SpiConstructionError> {
|
||||
let spi_id = spi.id();
|
||||
if spi_id.is_none() {
|
||||
return Err(InvalidSpiIdError.into());
|
||||
return Err(InvalidPsSpiError.into());
|
||||
}
|
||||
let spi_id = spi_id.unwrap();
|
||||
if Sck::GROUP != Mosi::GROUP
|
||||
@ -550,6 +574,15 @@ impl Spi {
|
||||
{
|
||||
return Err(SpiConstructionError::PinInvalidForSpiId);
|
||||
}
|
||||
if spi_pins.0.mux_conf() != SPI_MUX_CONF
|
||||
|| spi_pins.0.mux_conf() != spi_pins.2.mux_conf()
|
||||
|| spi_pins.1.mux_conf() != spi_pins.2.mux_conf()
|
||||
|| ss_pins.0.mux_conf() != spi_pins.0.mux_conf()
|
||||
|| ss_pins.1.mux_conf() != spi_pins.0.mux_conf()
|
||||
|| ss_pins.2.mux_conf() != spi_pins.2.mux_conf()
|
||||
{
|
||||
return Err(SpiConstructionError::InvalidPinConf);
|
||||
}
|
||||
Ok(Self::new_generic_unchecked(
|
||||
spi_id,
|
||||
spi.reg_block(),
|
||||
|
341
zynq7000-hal/src/ttc.rs
Normal file
341
zynq7000-hal/src/ttc.rs
Normal file
@ -0,0 +1,341 @@
|
||||
//! Triple-timer counter (TTC) high-level driver.
|
||||
//!
|
||||
//! This module also contains support for PWM and output waveform generation.
|
||||
|
||||
use core::convert::Infallible;
|
||||
|
||||
use arbitrary_int::{Number, u3, u4};
|
||||
use zynq7000::ttc::{MmioTtc, TTC_0_BASE_ADDR, TTC_1_BASE_ADDR};
|
||||
|
||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||
use crate::gpio::{Mio16, Mio17, Mio18, Mio19, Mio40, Mio41, Mio42, Mio43};
|
||||
use crate::{
|
||||
clocks::ArmClocks,
|
||||
gpio::{IoPeriph, IoPeriphPin, Mio28, Mio29, Mio30, Mio31, MioPin, MuxConf, PinMode},
|
||||
time::Hertz,
|
||||
};
|
||||
|
||||
/// Each TTC consists of three independent timers/counters.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum TtcId {
|
||||
Ttc0 = 0,
|
||||
Ttc1 = 1,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum ChannelId {
|
||||
Ch0 = 0,
|
||||
Ch1 = 1,
|
||||
Ch2 = 2,
|
||||
}
|
||||
|
||||
pub trait PsTtc {
|
||||
fn reg_block(&self) -> MmioTtc<'static>;
|
||||
fn id(&self) -> Option<TtcId>;
|
||||
}
|
||||
|
||||
impl PsTtc for MmioTtc<'static> {
|
||||
#[inline]
|
||||
fn reg_block(&self) -> MmioTtc<'static> {
|
||||
unsafe { self.clone() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn id(&self) -> Option<TtcId> {
|
||||
let base_addr = unsafe { self.ptr() } as usize;
|
||||
if base_addr == TTC_0_BASE_ADDR {
|
||||
return Some(TtcId::Ttc0);
|
||||
} else if base_addr == TTC_1_BASE_ADDR {
|
||||
return Some(TtcId::Ttc1);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub const TTC_MUX_CONF: MuxConf = MuxConf::new_with_l3(u3::new(0b110));
|
||||
|
||||
pub trait ClockInPin: IoPeriphPin {
|
||||
const ID: TtcId;
|
||||
}
|
||||
|
||||
pub trait WaveOutPin: IoPeriphPin {
|
||||
const ID: TtcId;
|
||||
}
|
||||
|
||||
macro_rules! into_ttc {
|
||||
($($Mio:ident),+) => {
|
||||
$(
|
||||
impl <M: PinMode> MioPin<$Mio, M> {
|
||||
/// Convert the pin into a TTC pin by configuring the pin routing via the
|
||||
/// MIO multiplexer bits.
|
||||
pub fn into_ttck(self) -> MioPin<$Mio, IoPeriph> {
|
||||
self.into_io_periph(TTC_MUX_CONF, None)
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||
into_ttc!(Mio16, Mio17, Mio18, Mio19, Mio40, Mio41, Mio42, Mio43);
|
||||
into_ttc!(Mio28, Mio29, Mio30, Mio31);
|
||||
|
||||
pub struct Ttc {
|
||||
pub ch0: TtcChannel,
|
||||
pub ch1: TtcChannel,
|
||||
pub ch2: TtcChannel,
|
||||
}
|
||||
|
||||
impl Ttc {
|
||||
/// Create a new TTC instance. The passed TTC peripheral instance MUST point to a valid
|
||||
/// processing system TTC peripheral.
|
||||
///
|
||||
/// Returns [None] if the passed peripheral block does not have a valid PS TTC address.
|
||||
pub fn new(ps_ttc: impl PsTtc) -> Option<Self> {
|
||||
ps_ttc.id()?;
|
||||
let regs = ps_ttc.reg_block();
|
||||
let ch0 = TtcChannel {
|
||||
regs: unsafe { regs.clone() },
|
||||
id: ChannelId::Ch0,
|
||||
};
|
||||
let ch1 = TtcChannel {
|
||||
regs: unsafe { regs.clone() },
|
||||
id: ChannelId::Ch1,
|
||||
};
|
||||
let ch2 = TtcChannel {
|
||||
regs,
|
||||
id: ChannelId::Ch2,
|
||||
};
|
||||
Some(Self { ch0, ch1, ch2 })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TtcChannel {
|
||||
regs: MmioTtc<'static>,
|
||||
id: ChannelId,
|
||||
}
|
||||
|
||||
impl TtcChannel {
|
||||
pub fn regs_mut(&mut self) -> &mut MmioTtc<'static> {
|
||||
&mut self.regs
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_counter(&self) -> u16 {
|
||||
self.regs
|
||||
.read_current_counter(self.id as usize)
|
||||
.unwrap()
|
||||
.count()
|
||||
}
|
||||
|
||||
pub fn id(&self) -> ChannelId {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("invalid TTC pin configuration")]
|
||||
pub struct InvalidTtcPinConfigError(pub MuxConf);
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("frequency is zero")]
|
||||
pub struct FrequencyIsZeroError;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum TtcConstructionError {
|
||||
#[error("invalid TTC pin configuration")]
|
||||
InvalidTtcPinConfig(#[from] InvalidTtcPinConfigError),
|
||||
#[error("frequency is zero")]
|
||||
FrequencyIsZero(#[from] FrequencyIsZeroError),
|
||||
}
|
||||
|
||||
pub fn calculate_prescaler_reg_and_interval_ticks(
|
||||
mut ref_clk: Hertz,
|
||||
freq: Hertz,
|
||||
) -> (Option<u4>, u16) {
|
||||
// TODO: Can this be optimized?
|
||||
let mut prescaler_reg: Option<u4> = None;
|
||||
let mut tick_val = ref_clk / freq;
|
||||
while tick_val > u16::MAX as u32 {
|
||||
ref_clk /= 2;
|
||||
if let Some(prescaler_reg) = prescaler_reg {
|
||||
// TODO: Better error handling for this case? Can this even happen?
|
||||
if prescaler_reg.value() == u4::MAX.value() {
|
||||
break;
|
||||
} else {
|
||||
prescaler_reg.checked_add(u4::new(1));
|
||||
}
|
||||
} else {
|
||||
prescaler_reg = Some(u4::new(0));
|
||||
}
|
||||
tick_val = ref_clk / freq;
|
||||
}
|
||||
(prescaler_reg, tick_val as u16)
|
||||
}
|
||||
|
||||
pub struct Pwm {
|
||||
channel: TtcChannel,
|
||||
ref_clk: Hertz,
|
||||
}
|
||||
|
||||
impl Pwm {
|
||||
/// Create a new PWM instance which uses the CPU 1x clock as the clock source and also uses
|
||||
/// a MIO output pin for the waveform output.
|
||||
pub fn new_with_cpu_clk_and_mio_waveout(
|
||||
channel: TtcChannel,
|
||||
arm_clocks: &ArmClocks,
|
||||
freq: Hertz,
|
||||
wave_out: impl WaveOutPin,
|
||||
) -> Result<Self, TtcConstructionError> {
|
||||
if wave_out.mux_conf() != TTC_MUX_CONF {
|
||||
return Err(InvalidTtcPinConfigError(wave_out.mux_conf()).into());
|
||||
}
|
||||
Ok(Self::new_with_cpu_clk(channel, arm_clocks, freq)?)
|
||||
}
|
||||
|
||||
/// Create a new PWM instance which uses the CPU 1x clock as the clock source.
|
||||
pub fn new_with_cpu_clk(
|
||||
channel: TtcChannel,
|
||||
arm_clocks: &ArmClocks,
|
||||
freq: Hertz,
|
||||
) -> Result<Self, FrequencyIsZeroError> {
|
||||
Self::new_generic(channel, arm_clocks.cpu_1x_clk(), freq)
|
||||
}
|
||||
|
||||
/// Create a new PWM instance based on a reference clock source.
|
||||
pub fn new_generic(
|
||||
channel: TtcChannel,
|
||||
ref_clk: Hertz,
|
||||
freq: Hertz,
|
||||
) -> Result<Self, FrequencyIsZeroError> {
|
||||
if freq.raw() == 0 {
|
||||
return Err(FrequencyIsZeroError);
|
||||
}
|
||||
let (prescaler_reg, tick_val) = calculate_prescaler_reg_and_interval_ticks(ref_clk, freq);
|
||||
let id = channel.id() as usize;
|
||||
let mut pwm = Self { channel, ref_clk };
|
||||
pwm.set_up_and_configure_pwm(id, prescaler_reg, tick_val);
|
||||
Ok(pwm)
|
||||
}
|
||||
|
||||
/// Set a new frequency for the PWM cycle.
|
||||
///
|
||||
/// This resets the duty cycle to 0%.
|
||||
pub fn set_frequency(&mut self, freq: Hertz) -> Result<(), FrequencyIsZeroError> {
|
||||
if freq.raw() == 0 {
|
||||
return Err(FrequencyIsZeroError);
|
||||
}
|
||||
let id = self.channel.id() as usize;
|
||||
let (prescaler_reg, tick_val) =
|
||||
calculate_prescaler_reg_and_interval_ticks(self.ref_clk, freq);
|
||||
self.set_up_and_configure_pwm(id, prescaler_reg, tick_val);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ttc_channel_mut(&mut self) -> &mut TtcChannel {
|
||||
&mut self.channel
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max_duty_cycle(&self) -> u16 {
|
||||
self.channel
|
||||
.regs
|
||||
.read_interval_value(self.channel.id() as usize)
|
||||
.unwrap()
|
||||
.value()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_duty_cycle(&mut self, duty: u16) {
|
||||
let id = self.channel.id() as usize;
|
||||
self.channel
|
||||
.regs
|
||||
.modify_cnt_ctrl(id, |mut val| {
|
||||
val.set_disable(true);
|
||||
val
|
||||
})
|
||||
.unwrap();
|
||||
self.channel
|
||||
.regs
|
||||
.write_match_value_0(
|
||||
self.channel.id() as usize,
|
||||
zynq7000::ttc::RwValue::new_with_raw_value(duty as u32),
|
||||
)
|
||||
.unwrap();
|
||||
self.channel
|
||||
.regs
|
||||
.modify_cnt_ctrl(id, |mut val| {
|
||||
val.set_disable(false);
|
||||
val.set_reset(true);
|
||||
val
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn set_up_and_configure_pwm(&mut self, id: usize, prescaler_reg: Option<u4>, tick_val: u16) {
|
||||
// Disable the counter first.
|
||||
self.channel
|
||||
.regs
|
||||
.write_cnt_ctrl(id, zynq7000::ttc::CounterControl::new_with_raw_value(1))
|
||||
.unwrap();
|
||||
|
||||
// Clock configuration
|
||||
self.channel
|
||||
.regs
|
||||
.write_clk_cntr(
|
||||
id,
|
||||
zynq7000::ttc::ClockControl::builder()
|
||||
.with_ext_clk_edge(false)
|
||||
.with_clk_src(zynq7000::ttc::ClockSource::Pclk)
|
||||
.with_prescaler(prescaler_reg.unwrap_or(u4::new(0)))
|
||||
.with_prescale_enable(prescaler_reg.is_some())
|
||||
.build(),
|
||||
)
|
||||
.unwrap();
|
||||
self.channel
|
||||
.regs
|
||||
.write_interval_value(
|
||||
id,
|
||||
zynq7000::ttc::RwValue::new_with_raw_value(tick_val as u32),
|
||||
)
|
||||
.unwrap();
|
||||
// Corresponds to duty cycle 0.
|
||||
self.channel
|
||||
.regs
|
||||
.write_match_value_0(id, zynq7000::ttc::RwValue::new_with_raw_value(0))
|
||||
.unwrap();
|
||||
self.channel
|
||||
.regs
|
||||
.write_cnt_ctrl(
|
||||
id,
|
||||
zynq7000::ttc::CounterControl::builder()
|
||||
.with_wave_polarity(zynq7000::ttc::WavePolarity::LowToHighOnMatch1)
|
||||
.with_wave_enable_n(zynq7000::ttc::WaveEnable::Enable)
|
||||
.with_reset(true)
|
||||
.with_match_enable(true)
|
||||
.with_decrementing(false)
|
||||
.with_mode(zynq7000::ttc::Mode::Interval)
|
||||
.with_disable(false)
|
||||
.build(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_hal::pwm::ErrorType for Pwm {
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
impl embedded_hal::pwm::SetDutyCycle for Pwm {
|
||||
#[inline]
|
||||
fn max_duty_cycle(&self) -> u16 {
|
||||
self.max_duty_cycle()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
|
||||
self.set_duty_cycle(duty);
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -99,7 +99,6 @@ pub trait UartPins {}
|
||||
#[error("divisor is zero")]
|
||||
pub struct DivisorZero;
|
||||
|
||||
/// TODO: Integrate into macro.
|
||||
macro_rules! pin_pairs {
|
||||
($UartPeriph:path, ($( [$(#[$meta:meta], )? $TxMio:ident, $RxMio:ident] ),+ $(,)? )) => {
|
||||
$(
|
||||
@ -424,12 +423,12 @@ pub struct Uart {
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("invalid UART ID")]
|
||||
pub struct InvalidUartIdError;
|
||||
pub struct InvalidPsUart;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum UartConstructionError {
|
||||
#[error("invalid UART ID: {0}")]
|
||||
InvalidUartId(#[from] InvalidUartIdError),
|
||||
#[error("invalid UART ID")]
|
||||
InvalidPsUart(#[from] InvalidPsUart),
|
||||
#[error("missmatch between pins index and passed index")]
|
||||
IdxMissmatch,
|
||||
#[error("invalid pin mux conf for UART")]
|
||||
@ -442,9 +441,9 @@ impl Uart {
|
||||
///
|
||||
/// A valid PL design which routes the UART pins through into the PL must be used for this to
|
||||
/// work.
|
||||
pub fn new_with_emio(uart: impl PsUart, cfg: UartConfig) -> Result<Uart, InvalidUartIdError> {
|
||||
pub fn new_with_emio(uart: impl PsUart, cfg: UartConfig) -> Result<Uart, InvalidPsUart> {
|
||||
if uart.uart_id().is_none() {
|
||||
return Err(InvalidUartIdError);
|
||||
return Err(InvalidPsUart);
|
||||
}
|
||||
Ok(Self::new_generic_unchecked(
|
||||
uart.reg_block(),
|
||||
@ -464,7 +463,7 @@ impl Uart {
|
||||
{
|
||||
let id = uart.uart_id();
|
||||
if id.is_none() {
|
||||
return Err(InvalidUartIdError.into());
|
||||
return Err(InvalidPsUart.into());
|
||||
}
|
||||
if id.unwrap() != TxPinI::UART_IDX || id.unwrap() != RxPinI::UART_IDX {
|
||||
return Err(UartConstructionError::IdxMissmatch);
|
||||
|
Reference in New Issue
Block a user