diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 96e928a6..4a372fcb 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -3,42 +3,18 @@ name: All Checks on: [push, pull_request] jobs: - build-hals: + build-hal: runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Build HAL(s) - run: cargo build --verbose - - name: Run HAL(s) tests - run: cargo test --verbose - - name: Check format for HAL(s) - run: cargo fmt --all -- --check - - name: Run clippy for HAL(s) - run: cargo clippy -- -D warnings - - # imxrt1060-hal feature checks - imxrt1060-features: strategy: matrix: - features: ["rand_core", "rtic", "rt", "rand_core,rtic,rt"] - env: - RUSTFLAGS: -D warnings - runs-on: ubuntu-latest + feature: ["imxrt1061", "imxrt1062", "imxrt1064"] steps: - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: thumbv7em-none-eabihf - override: true - profile: minimal - - name: Check imxrt-hal with features '${{ matrix.features }}' - uses: actions-rs/cargo@v1 - with: - command: check - args: > - --features=${{ matrix.features }} - --manifest-path=imxrt-hal/Cargo.toml - --target=thumbv7em-none-eabihf - --verbose - + - name: Build imxrt-hal for (${{ matrix.feature }}) HAL + run: cd imxrt-hal && cargo build --verbose --features ${{ matrix.feature }} + - name: Run tests (${{ matrix.feature }}) for HAL + run: cd imxrt-hal && cargo test --verbose --features ${{ matrix.feature }} + - name: Check format (${{ matrix.feature }}) for HAL + run: cd imxrt-hal && cargo fmt --all -- --check + - name: Run clippy (${{ matrix.feature }}) for HAL + run: cd imxrt-hal && cargo clippy --features ${{ matrix.feature }} -- -D warnings diff --git a/README.md b/README.md index 404de44c..3b9287d4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# imxrt-hal +# imxrt-rs A Rust hardware abstraction layer (HAL) for NXP i.MX RT processors. @@ -9,19 +9,6 @@ A Rust hardware abstraction layer (HAL) for NXP i.MX RT processors. [imxrt-hal-badge]: https://img.shields.io/crates/v/imxrt-hal [imxrt-hal-url]: https://crates.io/crates/imxrt-hal -[matrix-chat](https://matrix.to/#/#imxrt-rs:matrix.org) - -## Goals - -- Create *the* collaborative group to support using Rust on NXP's i.MX RT series. -- Simple but useful register level access. It compiles quickly, and it's intuitive for existing embedded developers. Every hal republishes the RAL. -- Embedded HAL support. -- RTIC support. -- NXP EVK board support -- Supporting popular boards such as the Teensy 4. - -## Getting Started - ### HAL If you want to develop Rust libraries and applications for an i.MX RT-based system, use the `imxrt-hal` crate. The `imxrt-hal` crate provides implementations of the [`embedded-hal` traits](https://crates.io/crates/embedded-hal) specific to i.MX RT processors. Use the HAL if you want to @@ -33,7 +20,7 @@ If you want to develop Rust libraries and applications for an i.MX RT-based syst - read and write serial data (UART) - send and receive data over SPI -The publicly-supported HAL is on [crates.io](https://crates.io/crates/imxrt-hal). Include the HAL in your Rust library or binary: +The HAL is on [crates.io](https://crates.io/crates/imxrt-hal). Include the HAL in your Rust library or binary: ```toml [dependencies.imxrt-hal] @@ -52,11 +39,6 @@ The `"rt"` feature flag is recommended for users who are Enabling the `"rt"` feature-flag will link in the i.MX RT interrupt table. If you're familiar with crates that are generated from `svd2rust`, [the `"rt"` feature](https://docs.rs/svd2rust/0.17.0/svd2rust/#the-rt-feature) has the same behaviors in the `imxrt-hal` as it does in `svd2rust`-generated crates. -#### Future of the HAL - -Follow #56 to understand how we're breaking `imxrt-hal` into separate crates. If you'd like to try the new HAL crate(s), depend on `imxrt1060-hal`, and skip the feature flag that describes your i.MX RT variant. - - ## Q/A #### *Are there any board support packages (BSP) that use the `imxrt-hal` crate?* @@ -94,4 +76,4 @@ Licensed under either of http://www.apache.org/licenses/LICENSE-2.0) - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) -at your option. +at your option. \ No newline at end of file diff --git a/docs/RELEASE.md b/docs/RELEASE.md index a79f5fb9..ef20b3af 100644 --- a/docs/RELEASE.md +++ b/docs/RELEASE.md @@ -1,22 +1,17 @@ ## Publish a release -From a clean repository, at the root: +Ensure that all other imxrt-rs dependencies, like `imxrt-ral` and +`imxrt-iomuxc`, are already published. Then, publish the HAL: -1. If needed, publish a new imxrt-ral following instructions there for release. -2. In each HAL such as imxrt1060-hal/Cargo.toml`, update both - - the version of the HAL - - the HAL's dependency of the RAL -3. Commit the changes, and create a tag. -4. Publish the HAL(s): - ``` - cargo publish --manifest-path imxrt1060-hal/Cargo.toml - ``` +``` +cargo publish --manifest-path imxrt-hal/Cargo.toml --features imxrt1062 +``` ## Maintaining older releases This section describes how imxrt-rs project maintainers support older releases. If there is a bug fix that you would like to apply to an older version of the -RAL or HAL crates, follow the process below to create a new patch +RAL, HAL, or IOMUXC crates, follow the process below to create a new patch release. - Integrate bug fixes on the main branch. diff --git a/imxrt-hal/CHANGELOG.md b/imxrt-hal/CHANGELOG.md index 41c8b070..bf2d150b 100644 --- a/imxrt-hal/CHANGELOG.md +++ b/imxrt-hal/CHANGELOG.md @@ -2,6 +2,11 @@ ## [Unreleased] +### Added + +Abstraction layer for `tempmon` module. Incl. auto measurement and +alarm interrupts (only available for `"imxrt106x"`) + ## [0.4.5] 2021-12-02 ### Added diff --git a/imxrt-hal/Cargo.toml b/imxrt-hal/Cargo.toml index 2f3b3342..1397ee22 100644 --- a/imxrt-hal/Cargo.toml +++ b/imxrt-hal/Cargo.toml @@ -23,8 +23,8 @@ rand_core = { version = "0.5", default-features = false, optional = true } [dependencies.imxrt-iomuxc] version = "0.1.2" -git = "https://github.com/dstric-aqueduct/imxrt-iomuxc.git" -branch = "v0.1.2/can" +git = "https://github.com/imxrt-rs/imxrt-iomuxc.git" +branch = "v0.1" [dev-dependencies.cortex-m-rt] version = "0.6" diff --git a/imxrt-hal/README.md b/imxrt-hal/README.md index 802a47b8..7eb42111 100644 --- a/imxrt-hal/README.md +++ b/imxrt-hal/README.md @@ -1,22 +1,18 @@ -# imxrt-hal +# imxrt1060-hal -This project provides a Rust HAL (hardware abstraction layer) for all NXP i.MX RT -microcontrollers based on the imxrt-ral crate. +`imxrt1060-hal` is a Rust hardware abstraction layer that's specific to i.MX +RT 1060 processors. This includes some of the following processor parts: -A feature flag needs to be set for any of the supported i.MX RT SoC. +- i.MX RT 1061 +- i.MX RT 1062 -## What is it? +It is the successor to `imxrt-hal`, version 0.4, with `feature = "imxrt1062"`. -imxrt-hal is an experiment into a lightweight hardware abstraction layer. It -provides access to some of the peripherals of the i.MX RT series processors -from NXP using embedded-hal and other community driven hardware APIs. +## Features -The main aims are fast compilation, compactness, and simplicity. +The table below describes the optional features supported by `imxrt1060-hal`. -Please consider trying it out and contributing or leaving feedback! - -## Goals - -* Simple to use and hard to use incorrectly -* All peripherals and busses supported -* Support the entire i.MX RT Series with a single crate to maximize code reuse +| Feature | Description | +| -------- | ---------------------------------- | +| `"rt"` | Runtime support with `cortex-m-rt` | +| `"rtic"` | Support for RTIC | diff --git a/imxrt-hal/src/adc.rs b/imxrt-hal/src/adc.rs index 75d23fc3..98bf9072 100644 --- a/imxrt-hal/src/adc.rs +++ b/imxrt-hal/src/adc.rs @@ -4,10 +4,10 @@ //! //! # Example //! ```no_run -//! use imxrt1060_hal::{self, adc}; +//! use imxrt_hal::{self, adc}; //! use embedded_hal::adc::OneShot; //! -//! let mut peripherals = imxrt1060_hal::Peripherals::take().unwrap(); +//! let mut peripherals = imxrt_hal::Peripherals::take().unwrap(); //! let (adc1_builder, _) = peripherals.adc.clock(&mut peripherals.ccm.handle); //! //! let mut adc1 = adc1_builder.build(adc::ClockSelect::default(), adc::ClockDivision::default()); @@ -258,26 +258,27 @@ where } } -unsafe impl crate::dma::peripheral::Source for AdcSource +impl crate::dma::peripheral::Source for AdcSource where ADCx: adc::ADC + AdcDmaSource, P: Pin, { - fn source_signal(&self) -> u32 { - ADCx::SOURCE_REQUEST_SIGNAL - } + type Error = (); + + const SOURCE_REQUEST_SIGNAL: u32 = ADCx::SOURCE_REQUEST_SIGNAL; fn source(&self) -> *const u16 { &self.adc.reg.R0 as *const _ as *const u16 } - fn enable_source(&self) { + fn enable_source(&mut self) -> Result<(), Self::Error> { let channel =

>::Input::U32; ral::modify_reg!(ral::adc, self.adc.reg, GC, ADCO: 1, DMAEN: 1); ral::modify_reg!(ral::adc, self.adc.reg, HC0, |_| channel); + Ok(()) } - fn disable_source(&self) { + fn disable_source(&mut self) { ral::modify_reg!(ral::adc, self.adc.reg, GC, ADCO: 0, DMAEN: 0); } } diff --git a/imxrt-hal/src/can/mod.rs b/imxrt-hal/src/can/mod.rs index e5bdcb88..51cdb5ce 100644 --- a/imxrt-hal/src/can/mod.rs +++ b/imxrt-hal/src/can/mod.rs @@ -9,7 +9,6 @@ use ral::{modify_reg, read_reg, write_reg}; use crate::ccm; use crate::iomuxc::consts::{Unsigned, U1, U2}; -use crate::iomuxc::can; use crate::ral; use core::convert::Infallible; @@ -104,14 +103,7 @@ where /// Builds a Can peripheral. The return /// is a configured FlexCan peripheral running at 24MHz. - pub fn build(self, mut tx: TX, mut rx: RX) -> CAN - where - TX: can::Pin, - RX: can::Pin, - { - crate::iomuxc::can::prepare(&mut tx); - crate::iomuxc::can::prepare(&mut rx); - + pub fn build(self) -> CAN { CAN::new(self.source_clock, self.reg) } } @@ -193,6 +185,8 @@ where pub fn begin(&mut self) { self.set_ccm_ccg(); + self.set_tx(); + self.set_rx(); ral::modify_reg!(ral::can, self.reg, MCR, MDIS: MDIS_0); @@ -294,6 +288,114 @@ where } } + fn set_tx(&mut self) { + match self.instance_number() { + 1 => { + unsafe { + modify_reg!( + ral::iomuxc, + IOMUXC, + SW_MUX_CTL_PAD_GPIO_AD_B1_08, + MUX_MODE: ALT2, + SION: ENABLED + ) + }; + unsafe { + write_reg!( + ral::iomuxc, + IOMUXC, + SW_PAD_CTL_PAD_GPIO_AD_B1_08, + 0x10B0_u32 + ) + }; + } + 2 => { + unsafe { + modify_reg!( + ral::iomuxc, + IOMUXC, + SW_MUX_CTL_PAD_GPIO_AD_B0_02, + MUX_MODE: ALT0, + SION: ENABLED + ) + }; + unsafe { + write_reg!( + ral::iomuxc, + IOMUXC, + SW_PAD_CTL_PAD_GPIO_AD_B0_02, + 0x10B0_u32 + ) + }; + } + u => { + log::error!("Invalid Can instance (set_tx): {:?}", u); + } + } + } + + fn set_rx(&mut self) { + match self.instance_number() { + 1 => { + unsafe { + modify_reg!( + ral::iomuxc, + IOMUXC, + FLEXCAN1_RX_SELECT_INPUT, + DAISY: GPIO_AD_B1_09_ALT2 + ) + }; + unsafe { + modify_reg!( + ral::iomuxc, + IOMUXC, + SW_MUX_CTL_PAD_GPIO_AD_B1_09, + MUX_MODE: ALT2, + SION: ENABLED + ) + }; + unsafe { + write_reg!( + ral::iomuxc, + IOMUXC, + SW_PAD_CTL_PAD_GPIO_AD_B1_09, + 0x10B0_u32 + ) + }; + } + 2 => { + unsafe { + modify_reg!( + ral::iomuxc, + IOMUXC, + FLEXCAN2_RX_SELECT_INPUT, + DAISY: GPIO_AD_B0_03_ALT0 + ) + }; + unsafe { + modify_reg!( + ral::iomuxc, + IOMUXC, + SW_MUX_CTL_PAD_GPIO_AD_B0_03, + MUX_MODE: ALT0, + SION: ENABLED + ) + }; + unsafe { + write_reg!( + ral::iomuxc, + IOMUXC, + SW_PAD_CTL_PAD_GPIO_AD_B0_03, + 0x10B0_u32 + ) + }; + } + u => { + log::error!("Invalid Can instance (set_rx): {:?}", u); + } + } + } + pub fn get_clock(&self) -> u32 { self.source_clock.0 } diff --git a/imxrt-hal/src/ccm.rs b/imxrt-hal/src/ccm.rs index 593b3d89..dfc2be3d 100644 --- a/imxrt-hal/src/ccm.rs +++ b/imxrt-hal/src/ccm.rs @@ -57,10 +57,6 @@ impl PLL1 { PLL1(()) } - #[cfg(any(feature = "imxrt1011", feature = "imxrt1015"))] - pub const ARM_HZ: u32 = 500_000_000; - - #[cfg(any(feature = "imxrt1064", feature = "imxrt1062", feature = "imxrt1061"))] pub const ARM_HZ: u32 = 600_000_000; /// Set the clock speed for the ARM core. This represents the base processor frequency. @@ -925,30 +921,6 @@ pub mod spi { LPSPI_PODF_6 = ccm::CBCMR::LPSPI_PODF::RW::LPSPI_PODF_6, /// 0b0111: divide by 8 LPSPI_PODF_7 = ccm::CBCMR::LPSPI_PODF::RW::LPSPI_PODF_7, - /// 0b1000: divide by 9 - #[cfg(features = "imxrt1011")] - LPSPI_PODF_8 = ccm::CBCMR::LPSPI_PODF::RW::LPSPI_PODF_8, - /// 0b1001: divide by 10 - #[cfg(features = "imxrt1011")] - LPSPI_PODF_9 = ccm::CBCMR::LPSPI_PODF::RW::LPSPI_PODF_9, - /// 0b1010: divide by 11 - #[cfg(features = "imxrt1011")] - LPSPI_PODF_10 = ccm::CBCMR::LPSPI_PODF::RW::LPSPI_PODF_10, - /// 0b1011: divide by 12 - #[cfg(features = "imxrt1011")] - LPSPI_PODF_11 = ccm::CBCMR::LPSPI_PODF::RW::LPSPI_PODF_11, - /// 0b1100: divide by 13 - #[cfg(features = "imxrt1011")] - LPSPI_PODF_12 = ccm::CBCMR::LPSPI_PODF::RW::LPSPI_PODF_12, - /// 0b1101: divide by 14 - #[cfg(features = "imxrt1011")] - LPSPI_PODF_13 = ccm::CBCMR::LPSPI_PODF::RW::LPSPI_PODF_13, - /// 0b1110: divide by 15 - #[cfg(features = "imxrt1011")] - LPSPI_PODF_14 = ccm::CBCMR::LPSPI_PODF::RW::LPSPI_PODF_14, - /// 0b1111: divide by 16 - #[cfg(features = "imxrt1011")] - LPSPI_PODF_15 = ccm::CBCMR::LPSPI_PODF::RW::LPSPI_PODF_15, } impl From for Frequency { @@ -1123,4 +1095,4 @@ pub mod can { Divider((prescalar_select as u32) + 1) } } -} \ No newline at end of file +} diff --git a/imxrt-hal/src/dma.rs b/imxrt-hal/src/dma.rs index 25c63321..4ad641ff 100644 --- a/imxrt-hal/src/dma.rs +++ b/imxrt-hal/src/dma.rs @@ -34,7 +34,7 @@ //! handler, since we're enabling an interrupt when the receive completes. //! //! ```no_run -//! use imxrt_hal::dma::{Circular, Buffer, Linear, Peripheral, bidirectional_u16}; +//! use imxrt1060_hal::dma::{Circular, Buffer, Linear, Peripheral, bidirectional_u16}; //! //! // Circular buffers have alignment requirements //! #[repr(align(512))] @@ -44,7 +44,7 @@ //! static RX_BUFFER: Buffer<[u16; 256]> = Buffer::new([0; 256]); //! static TX_BUFFER: Align = Align(Buffer::new([0; 256])); //! -//! let mut peripherals = imxrt_hal::Peripherals::take().unwrap(); +//! let mut peripherals = imxrt1060_hal::Peripherals::take().unwrap(); //! //! // //! // SPI setup... @@ -52,8 +52,8 @@ //! //! let (_, _, _, spi4_builder) = peripherals.spi.clock( //! &mut peripherals.ccm.handle, -//! imxrt_hal::ccm::spi::ClockSelect::Pll2, -//! imxrt_hal::ccm::spi::PrescalarSelect::LPSPI_PODF_5, +//! imxrt1060_hal::ccm::spi::ClockSelect::Pll2, +//! imxrt1060_hal::ccm::spi::PrescalarSelect::LPSPI_PODF_5, //! ); //! //! let mut spi4 = spi4_builder.build( @@ -150,314 +150,55 @@ //! - Channel priority, and channel priority swapping //! - Channel chaining -#![allow(non_snake_case)] // Compatibility with RAL - mod buffer; -mod element; mod memcpy; pub(crate) mod peripheral; -mod register; + +use imxrt_dma::Transfer; +pub use imxrt_dma::{Channel, Element, ErrorStatus}; pub use buffer::{Buffer, Circular, CircularError, Drain, Linear, ReadHalf, WriteHalf}; -pub use element::Element; pub use memcpy::Memcpy; pub use peripheral::{helpers::*, Peripheral}; use crate::{ccm, ral}; -use core::{ - fmt::{self, Debug, Display}, - mem, -}; -pub use register::tcd::BandwidthControl; -use register::{DMARegisters, MultiplexerRegisters, Static, DMA, MULTIPLEXER}; - -/// A DMA channel -/// -/// DMA channels provide one-way transfers of data. They accept a source of data, -/// and a destination of data. They copy data from the source to the destination. -/// When the transfer is complete, a DMA channel signals completion by changing a -/// value in a register, or triggering an interrupt. -/// -/// DMA channels have very little public interface. They're best used when paired with a -/// [`Peripheral`](struct.Peripheral.html) or a [`Memcpy`](struct.Memcpy.html). -pub struct Channel { - /// Our channel number, expected to be between 0 to 31 - index: usize, - /// Reference to the DMA registers - registers: Static, - /// Reference to the DMA multiplexer - multiplexer: Static, -} - -impl Channel { - /// Set the channel's bandwidth control - /// - /// The bandwidth control will be used in any [`Memcpy`](struct.Memcpy.html) or - /// [`Peripheral`](struct.Peripheral.html) DMA transfers. - /// - /// - `None` disables bandwidth control (default setting) - /// - `Some(bwc)` sets the bandwidth control to `bwc` - pub fn set_bandwidth_control(&mut self, bandwidth: Option) { - let raw = BandwidthControl::raw(bandwidth); - let tcd = self.tcd(); - ral::modify_reg!(register::tcd, tcd, CSR, BWC: raw); - } - - /// Returns the DMA channel number - /// - /// Channels are unique and numbered within the half-open range `[0, 32)`. - pub fn channel(&self) -> usize { - self.index - } - - /// Allocates a DMA channel, and sets the initial state for - /// the channel. - fn new(index: usize) -> Self { - let channel = Channel { - index, - registers: DMA, - multiplexer: MULTIPLEXER, - }; - channel.registers.TCD[channel.index].reset(); - channel - } - - /// Returns a handle to this channel's transfer control descriptor - fn tcd(&self) -> ®ister::TransferControlDescriptor { - &self.registers.TCD[self.index] - } - - /// Prepare the source of a transfer; see [`Transfer`](struct.Transfer.html) for details. - /// - /// # Safety - /// - /// Address pointer must be valid for lifetime of the transfer. - unsafe fn set_source_transfer>, E: Element>(&mut self, transfer: T) { - let tcd = self.tcd(); - let transfer = transfer.into(); - ral::write_reg!(register::tcd, tcd, SADDR, transfer.address as u32); - ral::write_reg!(register::tcd, tcd, SOFF, transfer.offset); - ral::modify_reg!(register::tcd, tcd, ATTR, SSIZE: E::DATA_TRANSFER_ID, SMOD: transfer.modulo); - ral::write_reg!(register::tcd, tcd, SLAST, transfer.last_address_adjustment); - } - - /// Prepare the destination for a transfer; see [`Transfer`](struct.Transfer.html) for details. - /// - /// # Safety - /// - /// Address pointer must be valid for lifetime of the transfer. - unsafe fn set_destination_transfer>, E: Element>(&mut self, transfer: T) { - let tcd = self.tcd(); - let transfer = transfer.into(); - ral::write_reg!(register::tcd, tcd, DADDR, transfer.address as u32); - ral::write_reg!(register::tcd, tcd, DOFF, transfer.offset); - ral::modify_reg!(register::tcd, tcd, ATTR, DSIZE: E::DATA_TRANSFER_ID, DMOD: transfer.modulo); - ral::write_reg!( - register::tcd, - tcd, - DLAST_SGA, - transfer.last_address_adjustment - ); - } - - /// Set the number of *bytes* to transfer per minor loop - /// - /// Describes how many bytes we should transfer for each DMA service request. - fn set_minor_loop_bytes(&mut self, nbytes: u32) { - let tcd = self.tcd(); - ral::write_reg!(register::tcd, tcd, NBYTES, nbytes); - } - - /// Se the number of elements to move in each minor loop - /// - /// Describes how many elements we should transfer for each DMA service request. - fn set_minor_loop_elements(&mut self, len: usize) { - self.set_minor_loop_bytes((mem::size_of::() * len) as u32); - } - - /// Tells the DMA channel how many transfer iterations to perform - /// - /// A 'transfer iteration' is a read from a source, and a write to a destination, with - /// read and write sizes described by a minor loop. Each iteration requires a DMA - /// service request, either from hardware or from software. - fn set_transfer_iterations(&mut self, iterations: u16) { - let tcd = self.tcd(); - ral::write_reg!(register::tcd, tcd, CITER, iterations); - ral::write_reg!(register::tcd, tcd, BITER, iterations); - } - /// Enable or disabling triggering from hardware - /// - /// If source is `Some(value)`, we trigger from hardware identified by the source identifier. - /// If `source` is `None`, we disable hardware triggering. - fn set_trigger_from_hardware(&mut self, source: Option) { - let chcfg = &self.multiplexer.chcfg[self.index]; - chcfg.write(0); - if let Some(source) = source { - chcfg.write(MultiplexerRegisters::ENBL | source); - } - } - - /// Set this DMA channel as always on - /// - /// Use `set_always_on()` so that the DMA multiplexer drives the transfer with no - /// throttling. Specifically, an "always-on" transfer will not need explicit re-activiation - /// between major loops. - /// - /// Use an always-on channel for memory-to-memory transfers, so that you don't need explicit - /// software re-activation to maintain the transfer. On the other hand, most peripheral transfers - /// should not use an always-on channel, since the peripheral should control the data flow through - /// explicit activation. - fn set_always_on(&mut self) { - let chcfg = &self.multiplexer.chcfg[self.index]; - chcfg.write(0); - chcfg.write(MultiplexerRegisters::ENBL | MultiplexerRegisters::A_ON); - } - - /// Returns `true` if the DMA channel is receiving a service signal from hardware - fn is_hardware_signaling(&self) -> bool { - self.registers.HRS.read() & (1 << self.index) != 0 - } - - /// Enable or disable the DMA's multiplexer request - /// - /// In this DMA implementation, all peripheral transfers and memcpy requests - /// go through the DMA multiplexer. So, this needs to be set for the multiplexer - /// to service the channel. - fn set_enable(&mut self, enable: bool) { - if enable { - self.registers.SERQ.write(self.index as u8); - } else { - self.registers.CERQ.write(self.index as u8); - } - } - - /// Returns `true` if this DMA channel generated an interrupt - fn is_interrupt(&self) -> bool { - self.registers.INT.read() & (1 << self.index) != 0 - } - - /// Clear the interrupt flag from this DMA channel - fn clear_interrupt(&mut self) { - self.registers.CINT.write(self.index as u8); - } - - /// Enable or disable 'disable on completion' - /// - /// 'Disable on completion' lets the DMA channel automatically clear the request signal - /// when it completes a transfer. - fn set_disable_on_completion(&mut self, dreq: bool) { - let tcd = self.tcd(); - ral::modify_reg!(register::tcd, tcd, CSR, DREQ: dreq as u16); - } - - /// Enable or disable interrupt generation when the transfer completes - /// - /// You're responsible for registering your interrupt handler. - pub fn set_interrupt_on_completion(&mut self, intr: bool) { - let tcd = self.tcd(); - ral::modify_reg!(register::tcd, tcd, CSR, INTMAJOR: intr as u16); - } - - /// Returns `true` if this channel's completion will generate an interrupt - pub fn is_interrupt_on_completion(&self) -> bool { - let tcd = self.tcd(); - ral::read_reg!(register::tcd, tcd, CSR, INTMAJOR == 1) - } - - /// Enable or disable interrupt generation when the transfer is half complete - /// - /// You're responsible for registering your interrupt handler. - pub fn set_interrupt_on_half(&mut self, intr: bool) { - let tcd = self.tcd(); - ral::modify_reg!(register::tcd, tcd, CSR, INTHALF: intr as u16); - } - - /// Returns `true` if this channel will generate an interrupt halfway through transfer - pub fn is_interrupt_on_half(&self) -> bool { - let tcd = self.tcd(); - ral::read_reg!(register::tcd, tcd, CSR, INTHALF == 1) - } - - /// Indicates if the DMA transfer has completed - fn is_complete(&self) -> bool { - let tcd = self.tcd(); - ral::read_reg!(register::tcd, tcd, CSR, DONE == 1) - } - - /// Clears completion indication - fn clear_complete(&mut self) { - self.registers.CDNE.write(self.index as u8); - } - - /// Indicates if the DMA channel is in an error state - fn is_error(&self) -> bool { - self.registers.ERR.read() & (1 << self.index) != 0 - } - - /// Clears the error flag - fn clear_error(&mut self) { - self.registers.CERR.write(self.index as u8); - } - - /// Indicates if this DMA channel is actively transferring data - fn is_active(&self) -> bool { - let tcd = self.tcd(); - ral::read_reg!(register::tcd, tcd, CSR, ACTIVE == 1) - } - - /// Indicates if this DMA channel is enabled - fn is_enabled(&self) -> bool { - self.registers.ERQ.read() & (1 << self.index) != 0 - } - - /// Returns the value from the **global** error status register - /// - /// It may reflect the last channel that produced an error, and that - /// may not be related to this channel. - fn error_status(&self) -> u32 { - self.registers.ES.read() - } - - /// Start a DMA transfer - /// - /// `start()` should be used to request service from the DMA controller. It's - /// necessary for in-memory DMA transfers. Do not use it for hardware-initiated - /// DMA transfers. DMA transfers that involve hardware will rely on the hardware - /// to request DMA service. - /// - /// Flag is automatically cleared by hardware after it's asserted. - fn start(&mut self) { - self.registers.SSRT.write(self.index as u8); - } -} +/// The number of DMA channels +pub const CHANNEL_COUNT: usize = 32; /// An error when preparing a transfer #[derive(Debug)] #[non_exhaustive] -pub enum Error

{ +pub enum Error { /// There is already a scheduled transfer /// /// Cancel the transfer and try again. ScheduledTransfer, - /// The peripheral returned an error - Peripheral(P), /// Error setting up the DMA transfer Setup(ErrorStatus), } +/// Helper symbol to support DMA channel initialization +/// +/// We always provide users with an array of 32 channels. But, only the first `CHANNEL_COUNT` +/// channels are initialized. +const DMA_CHANNEL_INIT: [Option; 32] = [ + None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, +]; + /// Unclocked, uninitialized DMA channels /// /// Use [`clock()`](struct.Unclocked.html#method.clock) to initialize and acquire all DMA channels /// /// ```no_run -/// let mut peripherals = imxrt_hal::Peripherals::take().unwrap(); +/// let mut peripherals = imxrt1060_hal::Peripherals::take().unwrap(); /// /// let mut dma_channels = peripherals.dma.clock(&mut peripherals.ccm.handle); /// let channel_27 = dma_channels[27].take().unwrap(); /// let channel_0 = dma_channels[0].take().unwrap(); /// ``` -pub struct Unclocked([Option; 32]); +pub struct Unclocked([Option; CHANNEL_COUNT]); impl Unclocked { pub(crate) fn new(dma: ral::dma0::Instance, mux: ral::dmamux::Instance) -> Self { // Explicitly dropping instances @@ -467,134 +208,24 @@ impl Unclocked { drop(dma); drop(mux); - Unclocked([ - None, None, None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, None, - ]) + Unclocked(DMA_CHANNEL_INIT) } /// Enable the clocks for the DMA peripheral /// - /// The return is 32 channels, each being initialized as `Some(Channel)`. Users may take channels as needed. - /// The index in the array maps to the DMA channel number. + /// The return is an array of 32 channels. However, **only the first [`CHANNEL_COUNT`](constant.CHANNEL_COUNT.html) channels + /// are initialized to `Some(channel)`. The rest are `None`.** + /// + /// Users may take channels as needed. The index in the array maps to the DMA channel number. pub fn clock(mut self, ccm: &mut ccm::Handle) -> [Option; 32] { let (ccm, _) = ccm.raw(); ral::modify_reg!(ral::ccm, ccm, CCGR5, CG3: 0x03); - for (idx, channel) in self.0.iter_mut().enumerate() { - *channel = Some(Channel::new(idx)); + for (idx, channel) in self.0.iter_mut().take(CHANNEL_COUNT).enumerate() { + // Safety: because we have the DMA instance, we assume that we own the DMA + // peripheral. That means we own all the DMA channels. + let mut chan = unsafe { Channel::new(idx) }; + chan.reset(); + *channel = Some(chan); } self.0 } } - -/// A wrapper around a DMA error status value -/// -/// The wrapper contains a copy of the DMA controller's -/// error status register at the point of an error. The -/// wrapper implements both `Debug` and `Display`. The -/// type may be printed to understand why there was a -/// DMA error. -#[derive(Clone, Copy)] -pub struct ErrorStatus { - /// The raw error status - es: u32, -} - -impl ErrorStatus { - const fn new(es: u32) -> Self { - ErrorStatus { es } - } -} - -impl Debug for ErrorStatus { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "DMA_ES({:#010X})", self.es) - } -} - -impl Display for ErrorStatus { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, - "DMA_ES: VLD {vld} ECX {ecx} GPE {gpe} CPE {cpe} ERRCHN {errchn} SAE {sae} SOE {soe} DAE {dae} DOE {doe} NCE {nce} SGE {sge} SBE {sbe} DBE {dbe}", - vld = (self.es >> 31) & 0x1, - ecx = (self.es >> 16) & 0x1, - gpe = (self.es >> 15) & 0x1, - cpe = (self.es >> 14) & 0x1, - errchn = (self.es >> 8) & 0x1F, - sae = (self.es >> 7) & 0x1, - soe = (self.es >> 6) & 0x1, - dae = (self.es >> 5) & 0x1, - doe = (self.es >> 4) & 0x1, - nce = (self.es >> 3) & 0x1, - sge = (self.es >> 2) & 0x1, - sbe = (self.es >> 1) & 0x1, - dbe = self.es & 0x1 - ) - } -} - -/// Describes a DMA transfer -/// -/// `Transfer` describes a source or a destination of a DMA transfer. See the member -/// documentation for details. -#[derive(Clone, Copy, Debug)] -struct Transfer { - /// The starting address for the DMA transfer - /// - /// If this describes a source, `address` will be the first - /// address read. If this describes a destination, `address` - /// will be the first address written. - address: *const E, - - /// Offsets to perform for each read / write of a memory address. - /// - /// When defining a transfer for a peripheral source or destination, - /// `offset` should be zero. Otherwise, `offset` should represent the - /// size of the data element, `E`. - /// - /// Negative (backwards) adjustments are permitted, if you'd like to read - /// a buffer backwards or something. - offset: i16, - - /* size: u16, // Not needed; captured in E: Element type */ - /// Defines the strategy for reading / writing linear or circular buffers - /// - /// `modulo` should be zero if this definition defines a transfer from linear - /// memory or a peripheral. `modulo` will be non-zero when defining a transfer - /// from a circular buffer. The non-zero value is the number of high bits to freeze - /// when performing address offsets (see `offset`). Given that we're only supporting - /// power-of-two buffer sizes, `modulo` will be `31 - clz(cap * sizeof(E))`, where `cap` is the - /// total size of the circular buffer, `clz` is "count leading zeros," and `sizeof(E)` is - /// the size of the element, in bytes. - modulo: u16, - - /// Perform any last-address adjustments when we complete the transfer - /// - /// Once we complete moving data from a linear buffer, we should set our pointer back to the - /// initial address. For this case, `last_address_adjustment` should be a negative number that - /// describes how may *bytes* to move backwards from our current address to reach our starting - /// address. Alternatively, it could describe how to move to a completely new address, like - /// a nearby buffer that we're using for a double-buffer. Or, set it to zero, which means "keep - /// your current position." "Keep your current position" is important when working with a - /// peripheral address! - last_address_adjustment: i32, -} - -impl Transfer { - fn hardware(address: *const E) -> Self { - Transfer { - address, - // Don't move the address pointer - offset: 0, - // We're not a circular buffer - modulo: 0, - // Don't move the address pointer - last_address_adjustment: 0, - } - } -} - -// It's OK to send a channel across an execution context. -// They can't be cloned or copied, so there's no chance of -// them being (mutably) shared. -unsafe impl Send for Channel {} diff --git a/imxrt-hal/src/dma/peripheral.rs b/imxrt-hal/src/dma/peripheral.rs index 9066275e..f2c33182 100644 --- a/imxrt-hal/src/dma/peripheral.rs +++ b/imxrt-hal/src/dma/peripheral.rs @@ -15,9 +15,100 @@ //! See the [Rust API guidelines on future-proofing](https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed) //! to learn about the 'Sealed' pattern. Use the UART peripheral as an example. -use super::{buffer, Channel, Circular, Element, Error, ReadHalf, Transfer, WriteHalf}; +use super::{ + buffer, Channel, Circular, Element, Error, ErrorStatus, ReadHalf, Transfer, WriteHalf, +}; use core::sync::atomic::{compiler_fence, Ordering}; -pub use imxrt_dma::{Destination, Source}; + +/// Describes a peripheral that can be the source of DMA data +/// +/// By 'source,' we mean that it provides data for a DMA transfer. +/// A 'source,' would be a hardware device sending data into our +/// memory. +/// +/// This trait may only be implemented by HAL authors. Users will find +/// that [HAL peripherals already implement `Source`](trait.Source.html#implementors). +pub trait Source: private::Sealed { + type Error; + /// Peripheral source request signal + /// + /// See Table 4-3 of the reference manual. A source probably + /// has something like 'receive' in the name. + const SOURCE_REQUEST_SIGNAL: u32; + /// Returns a pointer to the register from which the DMA channel + /// reads data + /// + /// This is the register that software reads to acquire data from + /// a device. The type of the pointer describes the type of reads + /// the DMA channel performs when transferring data. + /// + /// This memory is assumed to be static. + fn source(&self) -> *const E; + /// Perform any actions necessary to enable DMA transfers + /// + /// Callers use this method to put the peripheral in a state where + /// it can supply the DMA channel with data. + fn enable_source(&mut self) -> Result<(), Self::Error>; + /// Perform any actions necessary to disable or cancel DMA transfers + /// + /// This may include undoing the actions in `enable_source()`. + fn disable_source(&mut self); +} + +/// Describes a peripheral that can be the destination for DMA data +/// +/// By 'destination,' we mean that it receives data from a DMA transfer. +/// Software is sending data from memory to a device using DMA. +/// +/// The trait may only be implemented by HAL authors. Users will find +/// that [HAL peripherals already implement `Destination`](trait.Destination.html#implementors). +pub trait Destination: private::Sealed { + type Error; + /// Peripheral destination request signal + /// + /// See Table 4-3 of the reference manual. A destination probably + /// has something like 'transfer' in the name. + const DESTINATION_REQUEST_SIGNAL: u32; + /// Returns a pointer to the register into which the DMA channel + /// writes data + /// + /// This is the register that software writes to when sending data to a + /// device. The type of the pointer describes the type of reads the + /// DMA channel performs when transferring data. + fn destination(&self) -> *const E; + /// Perform any actions necessary to enable DMA transfers + /// + /// Callers use this method to put the peripheral into a state where + /// it can accept transfers from a DMA channel. + fn enable_destination(&mut self) -> Result<(), Self::Error>; + /// Perform any actions necessary to disable or cancel DMA transfers + /// + /// This may include undoing the actions in `enable_destination()`. + fn disable_destination(&mut self); +} + +/// Preventing crate users from implementing `Source` and `Destination` +/// for arbitrary types. +mod private { + pub trait Sealed {} + + use crate::uart; + impl Sealed for uart::UART where M: crate::iomuxc::consts::Unsigned {} + impl Sealed for uart::Rx where M: crate::iomuxc::consts::Unsigned {} + impl Sealed for uart::Tx where M: crate::iomuxc::consts::Unsigned {} + + use crate::adc; + impl Sealed for adc::AdcSource {} + + use crate::spi; + impl Sealed for spi::SPI where M: crate::iomuxc::consts::Unsigned {} +} + +impl

From

for Error

{ + fn from(error: P) -> Self { + Error::Peripheral(error) + } +} /// A DMA-capable peripheral /// @@ -74,12 +165,12 @@ where } fn init_receive(&mut self, mut channel: Channel) { - channel.set_trigger_from_hardware(Some(self.peripheral.source_signal())); + channel.set_trigger_from_hardware(Some(P::SOURCE_REQUEST_SIGNAL)); // Safety: Source trait is only implemented on peripherals within // this crate. We may study those implementations to show that the // pointers point to valid memory. unsafe { - channel.set_source_transfer(&Transfer::hardware(self.peripheral.source())); + channel.set_source_transfer(Transfer::hardware(self.peripheral.source())); } channel.set_disable_on_completion(true); self.rx_channel = Some(channel); @@ -88,28 +179,28 @@ where /// Start a DMA transfer that transfers data from the peripheral into the supplied buffer /// /// A complete transfer is signaled by `is_receive_complete()`, and possibly an interrupt. - pub fn start_receive(&mut self, mut buffer: D) -> Result<(), (D, Error)> { + pub fn start_receive(&mut self, mut buffer: D) -> Result<(), (D, Error)> { let rx_channel = self.rx_channel.as_mut().unwrap(); if rx_channel.is_enabled() { return Err((buffer, Error::ScheduledTransfer)); + } else if let Err(error) = self.peripheral.enable_source() { + return Err((buffer, Error::Peripheral(error))); } - self.peripheral.enable_source(); + let dst = buffer.destination(); unsafe { - rx_channel.set_destination_transfer(&dst); + rx_channel.set_destination_transfer(dst); } rx_channel.set_minor_loop_elements::(1); - rx_channel.set_transfer_iterations(buffer.destination_len() as u16); + rx_channel.set_transfer_iterations(dst.len() as u16); buffer.prepare_destination(); compiler_fence(Ordering::Release); - unsafe { - rx_channel.enable(); - } + rx_channel.set_enable(true); if rx_channel.is_error() { - let es = rx_channel.error_status(); + let es = ErrorStatus::new(rx_channel.error_status()); rx_channel.clear_error(); Err((buffer, Error::Setup(es))) } else { @@ -159,7 +250,7 @@ where #[allow(deprecated)] core::sync::atomic::spin_loop_hint(); } - rx_channel.disable(); + rx_channel.set_enable(false); compiler_fence(Ordering::Acquire); self.destination_buffer.take() } @@ -214,12 +305,12 @@ where } fn init_transfer(&mut self, mut channel: Channel) { - channel.set_trigger_from_hardware(Some(self.peripheral.destination_signal())); + channel.set_trigger_from_hardware(Some(P::DESTINATION_REQUEST_SIGNAL)); // Safety: Destination trait is only implemented on peripherals within // this crate. We may study those implementations to show that the pointers // point to valid memory. unsafe { - channel.set_destination_transfer(&Transfer::hardware(self.peripheral.destination())); + channel.set_destination_transfer(Transfer::hardware(self.peripheral.destination())); } channel.set_disable_on_completion(true); self.tx_channel = Some(channel); @@ -228,28 +319,28 @@ where /// Start a DMA transfer that transfers data from the supplied buffer to the peripheral /// /// A complete transfer is signaled by `is_transfer_complete()`, and possibly an interrupt. - pub fn start_transfer(&mut self, mut buffer: S) -> Result<(), (S, Error)> { + pub fn start_transfer(&mut self, mut buffer: S) -> Result<(), (S, Error)> { let tx_channel = self.tx_channel.as_mut().unwrap(); if tx_channel.is_enabled() { return Err((buffer, Error::ScheduledTransfer)); + } else if let Err(error) = self.peripheral.enable_destination() { + return Err((buffer, Error::Peripheral(error))); } - self.peripheral.enable_destination(); + let src = buffer.source(); unsafe { - tx_channel.set_source_transfer(&src); + tx_channel.set_source_transfer(src); } tx_channel.set_minor_loop_elements::(1); - tx_channel.set_transfer_iterations(buffer.source_len() as u16); + tx_channel.set_transfer_iterations(src.len() as u16); buffer.prepare_source(); compiler_fence(Ordering::Release); - unsafe { - tx_channel.enable(); - } + tx_channel.set_enable(true); if tx_channel.is_error() { - let es = tx_channel.error_status(); + let es = ErrorStatus::new(tx_channel.error_status()); tx_channel.clear_error(); Err((buffer, Error::Setup(es))) } else { @@ -299,7 +390,7 @@ where #[allow(deprecated)] core::sync::atomic::spin_loop_hint(); } - tx_channel.disable(); + tx_channel.set_enable(false); compiler_fence(Ordering::Acquire); self.source_buffer.take() } @@ -421,7 +512,7 @@ pub mod helpers { rx: Channel, ) -> Peripheral where - P: Source + Destination, + P: Source>::Error> + Destination, S: buffer::Source, D: buffer::Destination, { @@ -436,7 +527,7 @@ pub mod helpers { rx: Channel, ) -> Peripheral where - P: Source + Destination, + P: Source>::Error> + Destination, S: buffer::Source, D: buffer::Destination, { diff --git a/imxrt-hal/src/gpio.rs b/imxrt-hal/src/gpio.rs index d0f2baef..be77d487 100644 --- a/imxrt-hal/src/gpio.rs +++ b/imxrt-hal/src/gpio.rs @@ -10,9 +10,9 @@ //! # Example //! //! ```no_run -//! use imxrt1060_hal::{self, gpio::GPIO}; +//! use imxrt_hal::{self, gpio::GPIO}; //! -//! let mut peripherals = imxrt1060_hal::Peripherals::take().unwrap(); +//! let mut peripherals = imxrt_hal::Peripherals::take().unwrap(); //! let input = GPIO::new(peripherals.iomuxc.ad_b0.p11); //! //! assert!(!input.is_set()); @@ -60,10 +60,10 @@ //! invokes a user's callback when input pin detects a rising edge. //! //! ```no_run -//! use imxrt1060_hal as hal; +//! use imxrt_hal as hal; //! use hal::{ //! gpio::{GPIO, Input, InterruptConfiguration}, -//! iomuxc::imxrt1060::b0::B0_10, +//! iomuxc::imxrt106x::b0::B0_10, //! }; //! use cortex_m::interrupt::Mutex; //! use core::cell::RefCell; diff --git a/imxrt-hal/src/lib.rs b/imxrt-hal/src/lib.rs index 7445956f..ad8ceed5 100644 --- a/imxrt-hal/src/lib.rs +++ b/imxrt-hal/src/lib.rs @@ -1,7 +1,7 @@ //! NXP iMXRT hardware abstraction layer //! //! An [`embedded-hal`](https://crates.io/crates/embedded-hal) implementation -//! targeting processors in NXP's IMXRT1060 family. +//! targeting processors in NXP's IMXRT106x family. //! //! See the module-level documentation for more information and examples. @@ -15,9 +15,11 @@ pub use imxrt_ral as ral; /// See the `imxrt_iomuxc` crate documentation for more information on this module. /// This module re-exports that crate, along with a chip-specific IOMUXC crate. pub mod iomuxc { - pub use imxrt_iomuxc::imxrt106x::*; pub use imxrt_iomuxc::*; + #[cfg(any(feature = "imxrt1061", feature = "imxrt1062", feature = "imxrt1064",))] + pub use imxrt_iomuxc::imxrt106x::*; + /// Use this function to acquire the IOMUXC pads. It requires that you have an /// instance to the RAL's IOMUXC instance. pub(super) fn pads(_: crate::ral::iomuxc::Instance) -> Pads { @@ -36,6 +38,8 @@ pub mod pit; pub mod pwm; pub mod spi; pub mod srtc; +#[cfg(any(feature = "imxrt1061", feature = "imxrt1062", feature = "imxrt1064"))] +pub mod tempmon; pub mod trng; pub mod uart; @@ -67,6 +71,8 @@ pub struct Peripherals { pub gpt2: gpt::Unclocked, pub dma: dma::Unclocked, pub srtc: srtc::Unclocked, + #[cfg(any(feature = "imxrt1061", feature = "imxrt1062", feature = "imxrt1064"))] + pub tempmon: tempmon::Uninitialized, pub trng: trng::Unclocked, } @@ -122,6 +128,8 @@ impl Peripherals { gpt2: gpt::Unclocked::two(ral::gpt::GPT2::steal()), dma: dma::Unclocked::new(ral::dma0::DMA0::steal(), ral::dmamux::DMAMUX::steal()), srtc: srtc::Unclocked::new(ral::snvs::SNVS::steal()), + #[cfg(any(feature = "imxrt1061", feature = "imxrt1062", feature = "imxrt1064"))] + tempmon: tempmon::Uninitialized::new(ral::tempmon::TEMPMON::steal()), trng: trng::Unclocked::new(ral::trng::TRNG::steal()), } } @@ -174,6 +182,8 @@ impl Peripherals { gpt2: gpt::Unclocked::two(ral::gpt::GPT2::take()?), dma: dma::Unclocked::new(ral::dma0::DMA0::take()?, ral::dmamux::DMAMUX::take()?), srtc: srtc::Unclocked::new(ral::snvs::SNVS::take()?), + #[cfg(any(feature = "imxrt1061", feature = "imxrt1062", feature = "imxrt1064"))] + tempmon: tempmon::Uninitialized::new(ral::tempmon::TEMPMON::take()?), trng: trng::Unclocked::new(ral::trng::TRNG::take()?), }; Some(p) diff --git a/imxrt-hal/src/srtc.rs b/imxrt-hal/src/srtc.rs index cdd6364e..00b4dc93 100644 --- a/imxrt-hal/src/srtc.rs +++ b/imxrt-hal/src/srtc.rs @@ -10,9 +10,9 @@ //! # Example //! //! ```no_run -//! use imxrt1060_hal; +//! use imxrt_hal; //! -//! let mut peripherals = imxrt1060_hal::Peripherals::take().unwrap(); +//! let mut peripherals = imxrt_hal::Peripherals::take().unwrap(); //! //! let mut srtc = peripherals.srtc.enable_and_set(&mut peripherals.ccm.handle, 1600000000, 0); //! // Interpreted as Unix time: Sep 13 2020 12:26:40.000 diff --git a/imxrt-hal/src/tempmon.rs b/imxrt-hal/src/tempmon.rs new file mode 100644 index 00000000..300b5dbb --- /dev/null +++ b/imxrt-hal/src/tempmon.rs @@ -0,0 +1,395 @@ +//! # Temperature Monitor (TEMPMON) +//! +//! ## IMPORTANT NOTE: +//! The temperature sensor uses and assumes that the bandgap +//! reference, 480MHz PLL and 32KHz RTC modules are properly programmed and fully +//! settled for correct operation. +//! +//! +//! ## Example 1 +//! +//! Manually triggered read +//! +//! ```no_run +//! use imxrt_hal; +//! +//! let mut peripherals = imxrt_hal::Peripherals::take().unwrap(); +//! +//! let (_, ipg_hz) = peripherals.ccm.pll1.set_arm_clock( +//! imxrt_hal::ccm::PLL1::ARM_HZ, +//! &mut peripherals.ccm.handle, +//! &mut peripherals.dcdc, +//! ); +//! +//! let mut cfg = peripherals.ccm.perclk.configure( +//! &mut peripherals.ccm.handle, +//! imxrt_hal::ccm::perclk::PODF::DIVIDE_3, +//! imxrt_hal::ccm::perclk::CLKSEL::IPG(ipg_hz), +//! ); +//! +//! // Init temperature monitor +//! let mut temp_mon = peripherals.tempmon.init(); +//! loop { +//! if let Ok(temperature) = nb::block!(temp_mon.measure_temp()) { +//! // Temperature in mC (1°C = 1000°mC) +//! } +//! } +//! ``` +//! +//! ## Example 2 +//! +//! Non-blocking reading +//! +//! ```no_run +//! use imxrt_hal::{self, tempmon::TempMon}; +//! +//! # let mut peripherals = imxrt_hal::Peripherals::take().unwrap(); +//! # let (_, ipg_hz) = peripherals.ccm.pll1.set_arm_clock( +//! # imxrt_hal::ccm::PLL1::ARM_HZ, +//! # &mut peripherals.ccm.handle, +//! # &mut peripherals.dcdc, +//! # ); +//! # let mut cfg = peripherals.ccm.perclk.configure( +//! # &mut peripherals.ccm.handle, +//! # imxrt_hal::ccm::perclk::PODF::DIVIDE_3, +//! # imxrt_hal::ccm::perclk::CLKSEL::IPG(ipg_hz), +//! # ); +//! +//! // Init temperature monitor with 8Hz measure freq +//! // 0xffff = 2 Sec. Read more at `measure_freq()` +//! let mut temp_mon = peripherals.tempmon.init_with_measure_freq(0x1000); +//! temp_mon.start(); +//! +//! let mut last_temp = 0_i32; +//! loop { +//! // Get the last temperature read by the measure_freq +//! if let Ok(temp) = temp_mon.get_temp() { +//! if last_temp != temp { +//! // Temperature changed +//! last_temp = temp; +//! } +//! // Do something else +//! } +//! } +//! ``` +//! +//! ## Example 3 +//! +//! Low and high temperature Interrupt +//! +//! *NOTE*: TEMP_LOW_HIGH is triggered for `TempSensor low` and `TempSensor high` +//! +//! ```no_run +//! use imxrt_hal::{self, tempmon::TempMon}; +//! use imxrt_hal::ral::interrupt; +//! +//! # let mut peripherals = imxrt_hal::Peripherals::take().unwrap(); +//! # let (_, ipg_hz) = peripherals.ccm.pll1.set_arm_clock( +//! # imxrt_hal::ccm::PLL1::ARM_HZ, +//! # &mut peripherals.ccm.handle, +//! # &mut peripherals.dcdc, +//! # ); +//! # let mut cfg = peripherals.ccm.perclk.configure( +//! # &mut peripherals.ccm.handle, +//! # imxrt_hal::ccm::perclk::PODF::DIVIDE_3, +//! # imxrt_hal::ccm::perclk::CLKSEL::IPG(ipg_hz), +//! # ); +//! +//! // Init temperature monitor with 8Hz measure freq +//! // 0xffff = 2 Sec. Read more at `measure_freq()` +//! let mut temp_mon = peripherals.tempmon.init_with_measure_freq(0x1000); +//! +//! // Set low_alarm, high_alarm, and panic_alarm temperature +//! temp_mon.set_alarm_values(-5_000, 65_000, 95_000); +//! +//! // Use values from registers if you like to compare it somewhere +//! let (low_alarm, high_alarm, panic_alarm) = temp_mon.alarm_values(); +//! +//! // Enables interrupts for low_high_alarm +//! unsafe { +//! cortex_m::peripheral::NVIC::unmask(interrupt::TEMP_LOW_HIGH); +//! } +//! +//! // Start could fail if the module is not powered up +//! if temp_mon.start().is_err() { +//! temp_mon.power_up(); +//! temp_mon.start(); +//! } +//! +//! #[cortex_m_rt::interrupt] +//! fn TEMP_LOW_HIGH() { +//! // disable the interrupt to avoid endless triggers +//! cortex_m::peripheral::NVIC::mask(interrupt::TEMP_LOW_HIGH); +//! +//! // don't forget to enable it after the temperature is back to normal +//! } +//! ``` + +use crate::ral; + +/// Indicates that the temperature monitor is powered down. +/// +/// If you receive this error, `power_up()` the temperature monitor first, +/// and try again. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct PowerDownError(()); + +/// An Uninitialized temperature monitor module +/// +/// # Important note: +/// +/// The temperature sensor uses and assumes that the bandgap +/// reference, 480MHz PLL and 32KHz RTC modules are properly +/// programmed and fully settled for correct operation. +pub struct Uninitialized(ral::tempmon::Instance); + +impl Uninitialized { + /// assign the tempmon Instance to this temperature monitor wrapper. + pub fn new(base: ral::tempmon::Instance) -> Self { + Self(base) + } + + /// Initialize the temperature monitor. + pub fn init(self) -> TempMon { + // this operation is safe. This value is read-only and set by the manufacturer. + let calibration = unsafe { ral::read_reg!(ral::ocotp, OCOTP, ANA1) }; + + // The ral doesn't provide direct access to the values. + let n1_room_count = (calibration >> 20) as i32; + let t1_room_temp = 25_000_i32; + let n2_hot_count = ((calibration >> 8) & 0xFFF) as i32; + let t2_hot_temp = (calibration & 0xFF) as i32 * 1_000; + + // Tmeas = HOT_TEMP - (Nmeas - HOT_COUNT) * ((HOT_TEMP - 25.0) / (ROOM_COUNT – HOT_COUNT)) + let scaler = (t2_hot_temp - t1_room_temp) / (n1_room_count - n2_hot_count); + // Tmeas = HOT_TEMP - (Nmeas - HOT_COUNT) * scaler + + let mut t = TempMon { + base: self.0, + scaler, + hot_count: n2_hot_count, + hot_temp: t2_hot_temp, + }; + t.power_up(); + t + } + + /// Initialize the temperature monitor. + /// + /// The `measure_freq` determines how many RTC clocks to wait before automatically repeating a temperature + /// measurement. The pause time before remeasuring is the field value multiplied by the RTC period. + /// + /// Find more details `TempMon.set_measure_frequency` + pub fn init_with_measure_freq(self, measure_freq: u16) -> TempMon { + let mut t = self.init(); + t.set_measure_frequency(measure_freq); + t + } +} + +/// A Temperature Monitor (TEMPMON) +/// +/// # Example +/// +/// ```no_run +/// use imxrt_hal; +/// +/// let mut peripherals = imxrt_hal::Peripherals::take().unwrap(); +/// let (_, ipg_hz) = peripherals.ccm.pll1.set_arm_clock( +/// imxrt_hal::ccm::PLL1::ARM_HZ, +/// &mut peripherals.ccm.handle, +/// &mut peripherals.dcdc, +/// ); +/// let mut cfg = peripherals.ccm.perclk.configure( +/// &mut peripherals.ccm.handle, +/// imxrt_hal::ccm::perclk::PODF::DIVIDE_3, +/// imxrt_hal::ccm::perclk::CLKSEL::IPG(ipg_hz), +/// ); +/// +/// // init temperature monitor +/// // consider using init_with_measure_freq +/// let mut temp_mon = peripherals.tempmon.init(); +/// loop { +/// if let Ok(_temperature) = nb::block!(temp_mon.measure_temp()) { +/// // _temperature in mC (1°C = 1000°mC) +/// } +/// } +/// ``` +pub struct TempMon { + base: ral::tempmon::Instance, + /// Scaler + scaler: i32, + /// Hot_count + hot_count: i32, + /// Hot_temp * 1000 + hot_temp: i32, +} + +impl TempMon { + /// Converts the temp_cnt into a human readable temperature [°mC] (1/1000 °C) + /// + /// param **temp_cnt**: measurement value from the tempmon module + /// + /// return: Temperature in °mC (1/1000°C) + fn convert(&self, temp_cnt: i32) -> i32 { + let n_meas = temp_cnt - self.hot_count; + self.hot_temp - n_meas * self.scaler + } + + /// Decode the temp_value into measurable bytes + /// + /// param **temp_value_mc**: temperature value in °mC (1/1000°C) + /// + /// return: decoded temperature, compatible to the module internal measurements + fn decode(&self, temp_value_mc: i32) -> u32 { + let v = (temp_value_mc - self.hot_temp) / self.scaler; + (self.hot_count - v) as u32 + } + + /// Triggers a new measurement + /// + /// If you configured automatically repeating, this will trigger additional measurement. + /// Use get_temp instate to get the last read value + /// + /// The returning temperature in 1/1000 Celsius (°mC) + /// + /// Example: 25500°mC -> 25.5°C + pub fn measure_temp(&mut self) -> nb::Result { + if !self.is_powered_up() { + Err(nb::Error::from(PowerDownError(()))) + } else { + // If no measurement is active, trigger new measurement + let active = ral::read_reg!(ral::tempmon, self.base, TEMPSENSE0, MEASURE_TEMP == START); + if !active { + ral::write_reg!(ral::tempmon, self.base, TEMPSENSE0_SET, MEASURE_TEMP: START); + } + + // If the measurement is not finished or not started + // i.MX Docs: This bit should be cleared by the sensor after the start of each measurement + if ral::read_reg!(ral::tempmon, self.base, TEMPSENSE0, FINISHED == INVALID) { + // measure_temp could be triggered again without any effect + Err(nb::Error::WouldBlock) + } else { + // Clear MEASURE_TEMP to trigger a new measurement at the next call + ral::write_reg!(ral::tempmon, self.base, TEMPSENSE0_CLR, MEASURE_TEMP: START); + + let temp_cnt = ral::read_reg!(ral::tempmon, self.base, TEMPSENSE0, TEMP_CNT) as i32; + Ok(self.convert(temp_cnt)) + } + } + } + + /// Returns the last read value from the temperature sensor + /// + /// The returning temperature in 1/1000 Celsius (°mC) + /// + /// Example: 25500°mC -> 25.5°C + pub fn get_temp(&self) -> nb::Result { + if self.is_powered_up() { + let temp_cnt = ral::read_reg!(ral::tempmon, self.base, TEMPSENSE0, TEMP_CNT) as i32; + Ok(self.convert(temp_cnt)) + } else { + Err(nb::Error::from(PowerDownError(()))) + } + } + + /// Starts the measurement process. If the measurement frequency is zero, this + /// results in a single conversion. + pub fn start(&mut self) -> nb::Result<(), PowerDownError> { + if self.is_powered_up() { + ral::write_reg!(ral::tempmon, self.base, TEMPSENSE0_SET, MEASURE_TEMP: START); + Ok(()) + } else { + Err(nb::Error::from(PowerDownError(()))) + } + } + + /// Stops the measurement process. This only has an effect If the measurement + /// frequency is not zero. + pub fn stop(&mut self) { + ral::write_reg!(ral::tempmon, self.base, TEMPSENSE0_CLR, MEASURE_TEMP: START); + } + + /// Returns the true if the tempmon module is powered up. + pub fn is_powered_up(&self) -> bool { + ral::read_reg!(ral::tempmon, self.base, TEMPSENSE0, POWER_DOWN == POWER_UP) + } + + /// This powers down the temperature sensor. + pub fn power_down(&mut self) { + ral::write_reg!( + ral::tempmon, + self.base, + TEMPSENSE0_SET, + POWER_DOWN: POWER_DOWN + ); + } + + /// This powers up the temperature sensor. + pub fn power_up(&mut self) { + ral::write_reg!( + ral::tempmon, + self.base, + TEMPSENSE0_CLR, + POWER_DOWN: POWER_DOWN + ); + } + + /// Set the temperature that will generate a low alarm, high alarm, and panic alarm interrupt + /// when the temperature exceeded this values. + /// + /// ## Note: + /// low_alarm_mc, high_alarm_mc, and panic_alarm_mc are in milli Celsius (1/1000 °C) + pub fn set_alarm_values(&mut self, low_alarm_mc: i32, high_alarm_mc: i32, panic_alarm_mc: i32) { + let low_alarm = self.decode(low_alarm_mc); + let high_alarm = self.decode(high_alarm_mc); + let panic_alarm = self.decode(panic_alarm_mc); + ral::modify_reg!(ral::tempmon, self.base, TEMPSENSE0, ALARM_VALUE: high_alarm); + ral::write_reg!( + ral::tempmon, + self.base, + TEMPSENSE2, + LOW_ALARM_VALUE: low_alarm, + PANIC_ALARM_VALUE: panic_alarm + ); + } + + /// Queries the temperature that will generate a low alarm, high alarm, and panic alarm interrupt. + /// + /// Returns (low_alarm_temp, high_alarm_temp, panic_alarm_temp) + pub fn alarm_values(&self) -> (i32, i32, i32) { + let high_alarm = ral::read_reg!(ral::tempmon, self.base, TEMPSENSE0, ALARM_VALUE); + let (low_alarm, panic_alarm) = ral::read_reg!( + ral::tempmon, + self.base, + TEMPSENSE2, + LOW_ALARM_VALUE, + PANIC_ALARM_VALUE + ); + ( + self.convert(low_alarm as i32), + self.convert(high_alarm as i32), + self.convert(panic_alarm as i32), + ) + } + + /// This bits determines how many RTC clocks to wait before automatically repeating a temperature + /// measurement. The pause time before remeasuring is the field value multiplied by the RTC period. + /// + /// | value | note | + /// | ------ | ----------------------------------------------------- | + /// | 0x0000 | Defines a single measurement with no repeat. | + /// | 0x0001 | Updates the temperature value at a RTC clock rate. | + /// | 0x0002 | Updates the temperature value at a RTC/2 clock rate. | + /// | ... | ... | + /// | 0xFFFF | Determines a two second sample period with a 32.768KHz RTC clock. Exact timings depend on the accuracy of the RTC clock.| + /// + pub fn set_measure_frequency(&mut self, measure_freq: u16) { + ral::modify_reg!( + ral::tempmon, + self.base, + TEMPSENSE1, + MEASURE_FREQ: measure_freq as u32 + ); + } +} diff --git a/imxrt-hal/src/trng.rs b/imxrt-hal/src/trng.rs index 7cbbe2af..65c44001 100644 --- a/imxrt-hal/src/trng.rs +++ b/imxrt-hal/src/trng.rs @@ -30,19 +30,6 @@ //! [rand_core]: https://crates.io/crates/rand_core //! [rand]: https://crates.io/crates/rand //! [nfr]: https://github.com/rust-lang/cargo/issues/7916 -//! -//! # Example -//! -//! ```no_run -//! use imxrt1060_hal as hal; -//! -//! let mut peripherals = hal::Peripherals::take().unwrap(); -//! peripherals.trng.set_sample_mode(hal::trng::SampleMode::VonNeumannRaw); -//! peripherals.trng.set_retry_count(10).unwrap(); -//! -//! let mut trng = peripherals.trng.clock(&mut peripherals.ccm.handle); -//! let random_data = trng.next_u32().unwrap(); -//! ``` use core::fmt;