rustberry 0.1.0

All-purpose Raspberry Pi library for Rust
Documentation
// Copyright (C) 2018  Adam Gausmann
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

//! Frontend for the PWM register map.

use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};

use error::{Error, ErrorKind};
use peripherals::consts::*;
use system::System;
use util::mem::RegisterMap;

static RESERVED: AtomicBool = ATOMIC_BOOL_INIT; //false

pub use peripherals::consts::PWM_CHANNELS;

/// Low-level (but safe) access to the PWM register segment.
pub struct Pwm {
    registers: RegisterMap,
}

impl Pwm {

    /// Attempts to reserve the PWM register segment, returning it upon
    /// success.
    ///
    /// # Errors
    ///
    /// This struct is a singleton object to enforce single ownership of the
    /// PWM channels. Returns `Error::Reserved` if another object exists.
    pub fn new() -> Result<Pwm, Error> {
        if RESERVED.fetch_or(true, Ordering::SeqCst) {
            return Err(Error::new(ErrorKind::Reserved));
        }

        Ok(Pwm {
            registers: unsafe { RegisterMap::map(
                System::detect()?.peripheral_offset() + PWM_PAGE_OFFSET,
                PAGE_SIZE,
            )}?,
        })
    }

    /// Checks whether the channel number corresponds to a valid channel,
    /// returning `true` if so.
    pub fn is_valid_channel(channel: usize) -> bool {
        channel < PWM_CHANNELS
    }

    /// Convenience wrapper around `is_valid_channel` for use with `try!`,
    /// mapping `true` to `Ok` and `false` to `Err` with the kind
    /// `OutOfRange`.
    pub fn check_channel(channel: usize) -> Result<(), Error> {
        if Pwm::is_valid_channel(channel) {
            Ok(())
        } else {
            Err(Error::new(ErrorKind::OutOfRange))
        }
    }

    /// Enables output on the specified PWM channel by setting `PWENx` in the
    /// `PWM_CTL` register.
    ///
    /// # Panics
    ///
    /// Panics if the given PWM channel number is invalid/out of range.
    pub fn enable(&mut self, channel: usize) {
        let mut reg = self.registers.load(PWM_CTL);
        reg |= PWM_CTL_PWEN[channel];
        self.registers.store(PWM_CTL, reg);
    }

    /// Disables output on the specified PWM channel by clearing `PWENx` in the
    /// `PWM_CTL` register.
    ///
    /// # Panics
    ///
    /// Panics if the given PWM channel number is invalid/out of range.
    pub fn disable(&mut self, channel: usize) {
        let mut reg = self.registers.load(PWM_CTL);
        reg &= !PWM_CTL_PWEN[channel];
        self.registers.store(PWM_CTL, reg);
    }

    /// Enables serial output mode on the specified PWM channel by setting
    /// `MODEx` in the `PWM_CTL` register.
    ///
    /// # Panics
    ///
    /// Panics if the given PWM channel number is invalid/out of range.
    pub fn enable_serial(&mut self, channel: usize) {
        let mut reg = self.registers.load(PWM_CTL);
        reg |= PWM_CTL_MODE[channel];
        self.registers.store(PWM_CTL, reg);
    }

    /// Disables serial output mode on the specified PWM channel by clearing
    /// `MODEx` in the `PWM_CTL` register.
    ///
    /// # Panics
    ///
    /// Panics if the given PWM channel number is invalid/out of range.
    pub fn disable_serial(&mut self, channel: usize) {
        let mut reg = self.registers.load(PWM_CTL);
        reg &= !PWM_CTL_MODE[channel];
        self.registers.store(PWM_CTL, reg);
    }

    /// Sets the channel's silence bit to high by setting `SBITx` in the
    /// `PWM_CTL` register.
    ///
    /// # Panics
    ///
    /// Panics if the given PWM channel number is invalid/out of range.
    pub fn set_sbit_high(&mut self, channel: usize) {
        let mut reg = self.registers.load(PWM_CTL);
        reg |= PWM_CTL_SBIT[channel];
        self.registers.store(PWM_CTL, reg);
    }

    /// Sets the channel's silence bit to low by clearing `SBITx` in the
    /// `PWM_CTL` register.
    ///
    /// # Panics
    ///
    /// Panics if the given PWM channel number is invalid/out of range.
    pub fn set_sbit_low(&mut self, channel: usize) {
        let mut reg = self.registers.load(PWM_CTL);
        reg &= !PWM_CTL_SBIT[channel];
        self.registers.store(PWM_CTL, reg);
    }

    /// Inverts the channel's output polarity by setting `POLAx` in the
    /// `PWM_CTL` register.
    ///
    /// # Panics
    ///
    /// Panics if the given PWM channel number is invalid/out of range.
    pub fn set_polarity_inverse(&mut self, channel: usize) {
        let mut reg = self.registers.load(PWM_CTL);
        reg |= PWM_CTL_POLA[channel];
        self.registers.store(PWM_CTL, reg);
    }

    /// Sets the channel's output polarity to normal by clearing `POLAx` in the
    /// `PWM_CTL` register.
    ///
    /// # Panics
    ///
    /// Panics if the given PWM channel number is invalid/out of range.
    pub fn set_polarity_normal(&mut self, channel: usize) {
        let mut reg = self.registers.load(PWM_CTL);
        reg &= !PWM_CTL_POLA[channel];
        self.registers.store(PWM_CTL, reg);
    }

    /// Enables the channel's use of the `PWM_FIFO1` register by setting
    /// `USEFx` in the `PWM_CTL` register.
    ///
    /// # Panics
    ///
    /// Panics if the given PWM channel number is invalid/out of range.
    pub fn enable_fifo(&mut self, channel: usize) {
        let mut reg = self.registers.load(PWM_CTL);
        reg |= PWM_CTL_USEF[channel];
        self.registers.store(PWM_CTL, reg);
    }

    /// Disables the channel's use of the `PWM_FIFO1` register by clearing
    /// `USEFx` in the `PWM_CTL` register.
    ///
    /// # Panics
    ///
    /// Panics if the given PWM channel number is invalid/out of range.
    pub fn disable_fifo(&mut self, channel: usize) {
        let mut reg = self.registers.load(PWM_CTL);
        reg &= !PWM_CTL_USEF[channel];
        self.registers.store(PWM_CTL, reg);
    }

    /// Clears the FIFO for all channels by setting `CLRF1` in the `PWM_CTL`
    /// register (a one-shot operation).
    pub fn clear_fifo(&mut self) {
        let mut reg = self.registers.load(PWM_CTL);
        reg |= PWM_CTL_CLRF1;
        self.registers.store(PWM_CTL, reg);
    }

    /// Enables the use of the traditional mark-space PWM algorithm instead of
    /// the default balanced algorithm by setting `MSENx` in the `PWM_CTL`
    /// register.
    ///
    /// # Panics
    ///
    /// Panics if the given PWM channel number is invalid/out of range.
    pub fn enable_mark_space(&mut self, channel: usize) {
        let mut reg = self.registers.load(PWM_CTL);
        reg |= PWM_CTL_MSEN[channel];
        self.registers.store(PWM_CTL, reg);
    }

    /// Disables the use of the traditional mark-space algorithm, enabling the
    /// default balanced algorithm, by clearing `MSENx` in the `PWM_CTL`
    ///
    /// # Panics
    ///
    /// Panics if the given PWM channel number is invalid/out of range.
    /// register.
    pub fn disable_mark_space(&mut self, channel: usize) {
        let mut reg = self.registers.load(PWM_CTL);
        reg &= !PWM_CTL_MSEN[channel];
        self.registers.store(PWM_CTL, reg);
    }

    /// Sets the range for the given channel by assigning its value to the
    /// `PWM_RNGx` register.
    ///
    /// # Panics
    ///
    /// Panics if the given PWM channel number is invalid/out of range.
    pub fn set_range(&mut self, channel: usize, range: u32) {
        self.registers.store(PWM_RNG[channel], range);
    }

    /// Sets the current value of the given channel by assigning it to the
    /// `PWM_DATx` register.
    ///
    /// # Panics
    ///
    /// Panics if the given PWM channel number is invalid/out of range.
    pub fn set_data(&mut self, channel: usize, data: u32) {
        self.registers.store(PWM_DAT[channel], data);
    }

    /// Writes the given value to the output FIFO.
    pub fn write_fifo(&mut self, data: u32) {
        self.registers.store(PWM_FIF1, data);
    }
}