├── .gitignore ├── src ├── prelude.rs ├── lib.rs ├── signature.rs ├── time.rs ├── backup_domain.rs ├── watchdog.rs ├── timer.rs ├── delay.rs ├── afio.rs ├── exti.rs ├── spi.rs ├── exmc.rs ├── rtc.rs ├── eclic.rs ├── eclic │ └── mode.rs ├── pwm.rs ├── rcu.rs ├── serial.rs ├── i2c.rs ├── dma.rs ├── gpio.rs └── adc.rs ├── Cargo.toml ├── .github └── workflows │ └── ci.yml ├── README.md ├── CHANGELOG.md ├── device.x └── CODE_OF_CONDUCT.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | examples/ 5 | memory.x 6 | .cargo/ 7 | -------------------------------------------------------------------------------- /src/prelude.rs: -------------------------------------------------------------------------------- 1 | //! Prelude 2 | 3 | pub use crate::hal::prelude::*; 4 | 5 | pub use crate::afio::AfioExt as _gd32vf103xx_hal_afio_AfioExt; 6 | pub use crate::backup_domain::BkpExt as _gd32vf103xx_hal_backup_domain_BkpExt; 7 | pub use crate::exmc::ExmcExt as _gd32vf103xx_hal_exmc_ExmcExt; 8 | pub use crate::gpio::GpioExt as _gd32vf103xx_hal_gpio_GpioExt; 9 | pub use crate::rcu::RcuExt as _gd32vf103xx_hal_rcu_RcuExt; 10 | pub use crate::time::U32Ext as _gd32vf103xx_hal_time_U32Ext; 11 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! HAL for the GD32VF103xx family 2 | //! 3 | //! This is an implementation of the [`embedded-hal`] traits for the GD32VF103xx family 4 | 5 | //#![deny(missing_docs)] 6 | #![no_std] 7 | 8 | pub use gd32vf103_pac as pac; 9 | 10 | use embedded_hal as hal; 11 | 12 | pub mod adc; 13 | pub mod afio; 14 | pub mod backup_domain; 15 | pub mod delay; 16 | pub mod dma; 17 | pub mod eclic; 18 | pub mod exmc; 19 | pub mod exti; 20 | pub mod gpio; 21 | pub mod i2c; 22 | pub mod prelude; 23 | pub mod pwm; 24 | pub mod rcu; 25 | pub mod rtc; 26 | pub mod serial; 27 | pub mod signature; 28 | pub mod spi; 29 | pub mod time; 30 | pub mod timer; 31 | pub mod watchdog; 32 | -------------------------------------------------------------------------------- /src/signature.rs: -------------------------------------------------------------------------------- 1 | //! Device electronic signature 2 | //! 3 | //! Section 1.5, GD32VF103 User Manual 4 | 5 | const FLASH_SIZE_PTR: *const u16 = 0x1FFF_F7E0 as *const _; 6 | const SRAM_SIZE_PTR: *const u16 = 0x1FFF_F7E2 as *const _; 7 | const DEVICE_ID_PTR: *const [u32; 3] = 0x1FFF_F7E8 as *const _; 8 | 9 | /// Flash memory size in KBytes. 10 | #[inline] 11 | pub fn flash_size_kb() -> u16 { 12 | unsafe { *FLASH_SIZE_PTR } 13 | } 14 | 15 | /// On-chip SRAM size in KBytes. 16 | #[inline] 17 | pub fn sram_size_kb() -> u16 { 18 | unsafe { *SRAM_SIZE_PTR } 19 | } 20 | 21 | /// Factory programed unique device id. 22 | #[inline] 23 | pub fn device_id() -> &'static [u32; 3] { 24 | unsafe { &*DEVICE_ID_PTR } 25 | } 26 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gd32vf103xx-hal" 3 | version = "0.5.0" 4 | authors = ["The RISC-V Team "] 5 | repository = "https://github.com/riscv-rust/gd32vf103xx-hal" 6 | categories = ["embedded", "hardware-support", "no-std"] 7 | description = "HAL for GD32VF103 microcontrollers" 8 | keywords = ["riscv", "gd32", "hal"] 9 | license = "ISC" 10 | edition = "2024" 11 | 12 | [dependencies] 13 | gd32vf103-pac = "0.4.0" 14 | riscv = "0.6.0" 15 | nb = "0.1.2" 16 | void = { version = "1.0.2", default-features = false } 17 | cast = { version = "0.2.3", default-features = false } 18 | vcell = "0.1.2" 19 | embedded-dma = "0.1.2" 20 | 21 | [dependencies.embedded-hal] 22 | version = "0.2.3" 23 | features = ["unproven"] 24 | 25 | [features] 26 | rt = ["gd32vf103-pac/rt"] 27 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ staging, trying, master ] 4 | pull_request: 5 | 6 | name: CI 7 | 8 | jobs: 9 | ci-linux: 10 | runs-on: ubuntu-latest 11 | continue-on-error: ${{ matrix.experimental || false }} 12 | strategy: 13 | matrix: 14 | rust: [stable, nightly] 15 | include: 16 | # Nightly is only for reference and allowed to fail 17 | - rust: nightly 18 | experimental: true 19 | target: [x86_64-unknown-linux-gnu, riscv32imac-unknown-none-elf] 20 | steps: 21 | - uses: actions/checkout@v2 22 | - uses: actions-rs/toolchain@v1 23 | with: 24 | profile: minimal 25 | toolchain: ${{ matrix.rust }} 26 | override: true 27 | - name: Install Rust target 28 | run: rustup target install --toolchain=${{ matrix.rust }} ${{ matrix.target }} 29 | 30 | - name: Check code 31 | run: cargo check --target ${{ matrix.target }} 32 | 33 | - name: Check rt feature 34 | run: cargo check --target ${{ matrix.target }} --features rt 35 | -------------------------------------------------------------------------------- /src/time.rs: -------------------------------------------------------------------------------- 1 | //! Time units 2 | 3 | /// Bits per second 4 | #[derive(PartialEq, PartialOrd, Clone, Copy)] 5 | pub struct Bps(pub u32); 6 | 7 | /// Hertz 8 | #[derive(PartialEq, PartialOrd, Clone, Copy)] 9 | pub struct Hertz(pub u32); 10 | 11 | /// KiloHertz 12 | #[derive(PartialEq, PartialOrd, Clone, Copy)] 13 | pub struct KiloHertz(pub u32); 14 | 15 | /// MegaHertz 16 | #[derive(PartialEq, PartialOrd, Clone, Copy)] 17 | pub struct MegaHertz(pub u32); 18 | 19 | /// Time unit 20 | #[derive(PartialEq, PartialOrd, Clone, Copy)] 21 | pub struct MilliSeconds(pub u32); 22 | 23 | /// Extension trait that adds convenience methods to the `u32` type 24 | pub trait U32Ext { 25 | /// Wrap in `Bps` 26 | fn bps(self) -> Bps; 27 | 28 | /// Wrap in `Hertz` 29 | fn hz(self) -> Hertz; 30 | 31 | /// Wrap in `KiloHertz` 32 | fn khz(self) -> KiloHertz; 33 | 34 | /// Wrap in `MegaHertz` 35 | fn mhz(self) -> MegaHertz; 36 | 37 | /// Wrap in `MilliSeconds` 38 | fn ms(self) -> MilliSeconds; 39 | } 40 | 41 | impl U32Ext for u32 { 42 | fn bps(self) -> Bps { 43 | Bps(self) 44 | } 45 | 46 | fn hz(self) -> Hertz { 47 | Hertz(self) 48 | } 49 | 50 | fn khz(self) -> KiloHertz { 51 | KiloHertz(self) 52 | } 53 | 54 | fn mhz(self) -> MegaHertz { 55 | MegaHertz(self) 56 | } 57 | 58 | fn ms(self) -> MilliSeconds { 59 | MilliSeconds(self) 60 | } 61 | } 62 | 63 | impl Into for KiloHertz { 64 | fn into(self) -> Hertz { 65 | Hertz(self.0 * 1_000) 66 | } 67 | } 68 | 69 | impl Into for MegaHertz { 70 | fn into(self) -> Hertz { 71 | Hertz(self.0 * 1_000_000) 72 | } 73 | } 74 | 75 | impl Into for MegaHertz { 76 | fn into(self) -> KiloHertz { 77 | KiloHertz(self.0 * 1_000) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/d/gd32vf103xx-hal.svg)](https://crates.io/crates/gd32vf103xx-hal) 2 | [![crates.io](https://img.shields.io/crates/v/gd32vf103xx-hal.svg)](https://crates.io/crates/gd32vf103xx-hal) 3 | ![Build Status](https://github.com/riscv-rust/gd32vf103xx-hal/workflows/CI/badge.svg) 4 | 5 | # `gd32vf103xx-hal` 6 | 7 | > HAL for gd32vf103 variants 8 | 9 | This project is developed and maintained by the [RISC-V team][team]. 10 | 11 | Alternative to [gd32vf103-hal](https://github.com/luojia65/gd32vf103-hal) 12 | 13 | ## [Documentation](https://docs.rs/crate/gd32vf103xx-hal) 14 | 15 | ## Usage 16 | 17 | This crate defines a linker script called "device.x". In order to build this crate correctly, you must notify the linker about it. For example, bu ensuring your `.cargo/config.toml` file contains the following: 18 | 19 | ``` 20 | [target.riscv32imac-unknown-none-elf] 21 | rustflags = [ 22 | "-C", "link-arg=-Tdevice.x", 23 | ] 24 | ``` 25 | 26 | ## License 27 | 28 | Copyright 2019 [RISC-V team][team] 29 | 30 | Permission to use, copy, modify, and/or distribute this software for any purpose 31 | with or without fee is hereby granted, provided that the above copyright notice 32 | and this permission notice appear in all copies. 33 | 34 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 35 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 36 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 37 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 38 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 39 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 40 | THIS SOFTWARE. 41 | 42 | ## Code of Conduct 43 | 44 | Contribution to this crate is organized under the terms of the [Rust Code of 45 | Conduct][CoC], the maintainer of this crate, the [RISC-V team][team], promises 46 | to intervene to uphold that code of conduct. 47 | 48 | [CoC]: CODE_OF_CONDUCT.md 49 | [team]: https://github.com/rust-embedded/wg#the-risc-v-team 50 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | ## [Unreleased] 9 | 10 | ### Added 11 | 12 | ### Changed 13 | 14 | ### Removed 15 | 16 | ## [v0.5.0] - 2021-09-04 17 | 18 | ### Added 19 | 20 | - Add support for DMA 21 | - Add GitHub Actions 22 | - Add Spi::change_clock_frequency() 23 | - Add PWM API for remapping pins [#29]. 24 | - Add ADC support 25 | 26 | ### Changed 27 | 28 | - Use cchp.oaen bit to enable automatically all TIMER0 PWM channels 29 | - Replaced unreachable!() with panic!() 30 | - Enabling PWM is now explicit [#30]. 31 | 32 | 33 | ### Removed 34 | 35 | - Remove unused errifx and errifcx fields from the DMA driver code 36 | - Remove Travis CI rules and status badge 37 | 38 | ## [v0.4.0] - 2020-11-01 39 | 40 | ### Added 41 | 42 | - RTC driver 43 | - Add I2C driver 44 | - Add free watchdog 45 | - Add ECLIC mode 46 | - Add Fast Plus mode 47 | - Add UPG to clear timer counter 48 | - Add method to get gpio port and pin information 49 | - Add EXTI support 50 | 51 | ### Changed 52 | 53 | - Update riscv and gd32vf103-pac dependencies 54 | - Fix PWM driver 55 | - Rename des -> signature 56 | 57 | ### Removed 58 | 59 | - Remove defines for unsupported ISA variants 60 | - Remove rebase-hack.S 61 | 62 | ## [v0.3.0] - 2020-04-12 63 | 64 | ### Added 65 | 66 | - Added Afio::disable_jtag() 67 | - Added afio module docs 68 | 69 | ### Changed 70 | 71 | - Binaries Regenerated 72 | - Refactor AFIO and Serial 73 | - Hide closed traits and make them pub(crate) 74 | - Update dependencies, remove unused riscv-rt dependency 75 | - Configure serial pins after enabling usart clock 76 | 77 | ## [v0.2.3] - 2020-02-26 78 | 79 | ### Changed 80 | 81 | - Make GPIO pin activate() public 82 | 83 | [Unreleased]: https://github.com/riscv-rust/gd32vf103xx-hal/compare/v0.5.0...HEAD 84 | [v0.5.0]: https://github.com/riscv-rust/gd32vf103xx-hal/compare/v0.4.0...v0.5.0 85 | [v0.4.0]: https://github.com/riscv-rust/gd32vf103xx-hal/compare/v0.3.0...v0.4.0 86 | [v0.3.0]: https://github.com/rust-embedded/riscv/compare/v0.2.3...v0.3.0 87 | [v0.2.3]: https://github.com/riscv-rust/gd32vf103xx-hal/compare/v0.2.2...v0.2.3 -------------------------------------------------------------------------------- /src/backup_domain.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | Registers that are not reset as long as Vbat or Vdd has power. 3 | 4 | The registers retain their values during wakes from standby mode or system resets. They also 5 | retain their value when Vdd is switched off as long as V_BAT is powered. 6 | The backup domain also contains tamper protection and writes to it must be enabled in order 7 | to use the real time clock (RTC). 8 | Write access to the backup domain is enabled using the `BKP::configure(rcu, &mut pmu)` 9 | function. 10 | */ 11 | 12 | use crate::pac::{rcu, BKP, PMU}; 13 | use crate::rcu::{Rcu, Enable}; 14 | 15 | /// Extension trait that sets up the `BKP` peripheral 16 | pub trait BkpExt { 17 | /// Configure the `BKP` peripheral 18 | fn configure(self, rcu: &mut Rcu, pmu: &mut PMU) -> BackupDomain; 19 | } 20 | 21 | impl BkpExt for BKP { 22 | fn configure(self, rcu: &mut Rcu, pmu: &mut PMU) -> BackupDomain { 23 | // Enable the backup interface 24 | BKP::enable(rcu); 25 | PMU::enable(rcu); 26 | 27 | // Enable access to the backup registers 28 | pmu.ctl.modify(|_r, w| w.bkpwen().set_bit()); 29 | BackupDomain { 30 | _regs: self 31 | } 32 | } 33 | } 34 | 35 | /** 36 | The existence of this struct indicates that writing to the the backup 37 | domain has been enabled. It is acquired by calling `configure` on `BKP` 38 | */ 39 | pub struct BackupDomain { 40 | pub(crate) _regs: BKP, 41 | } 42 | 43 | /// This marks that the LXTAL clock source has been configured and has stabilized. 44 | #[derive(Clone, Copy)] 45 | pub struct Lxtal( 46 | /// Non-public field to stop user from instantiating this type 47 | (), 48 | ); 49 | 50 | impl Lxtal { 51 | /// Enable and don't wait for stabilization. 52 | fn just_enable(rcu: &rcu::RegisterBlock) { 53 | // Enable LXTAL 54 | rcu.bdctl 55 | .modify(|_, w| w.lxtalen().set_bit().lxtalbps().clear_bit()); 56 | } 57 | fn is_stable(rcu: &rcu::RegisterBlock) -> bool { 58 | rcu.bdctl.read().lxtalstb().bit() 59 | } 60 | /// Enable the clock and block until stabilized. 61 | pub fn enable_block(rcu: &Rcu) -> Self { 62 | //let rcu = unsafe { &*RCU::ptr() }; 63 | let rcu = &rcu.regs; 64 | Self::just_enable(rcu); 65 | // Wait for stable LXTAL 66 | while !Self::is_stable(rcu) {} 67 | Self(()) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /device.x: -------------------------------------------------------------------------------- 1 | PROVIDE(INT_SFT = DefaultHandler); 2 | PROVIDE(INT_TMR = DefaultHandler); 3 | PROVIDE(INT_BWEI = DefaultHandler); 4 | PROVIDE(INT_PMOVI = DefaultHandler); 5 | PROVIDE(WWDGT = DefaultHandler); 6 | PROVIDE(EXTI_LVD = DefaultHandler); 7 | PROVIDE(TAMPER = DefaultHandler); 8 | PROVIDE(RTC = DefaultHandler); 9 | PROVIDE(FMC = DefaultHandler); 10 | PROVIDE(RCU = DefaultHandler); 11 | PROVIDE(EXTI_LINE0 = DefaultHandler); 12 | PROVIDE(EXTI_LINE1 = DefaultHandler); 13 | PROVIDE(EXTI_LINE2 = DefaultHandler); 14 | PROVIDE(EXTI_LINE3 = DefaultHandler); 15 | PROVIDE(EXTI_LINE4 = DefaultHandler); 16 | PROVIDE(DMA0_CHANNEL0 = DefaultHandler); 17 | PROVIDE(DMA0_CHANNEL1 = DefaultHandler); 18 | PROVIDE(DMA0_CHANNEL2 = DefaultHandler); 19 | PROVIDE(DMA0_CHANNEL3 = DefaultHandler); 20 | PROVIDE(DMA0_CHANNEL4 = DefaultHandler); 21 | PROVIDE(DMA0_CHANNEL5 = DefaultHandler); 22 | PROVIDE(DMA0_CHANNEL6 = DefaultHandler); 23 | PROVIDE(ADC0_1 = DefaultHandler); 24 | PROVIDE(CAN0_TX = DefaultHandler); 25 | PROVIDE(CAN0_RX0 = DefaultHandler); 26 | PROVIDE(CAN0_RX1 = DefaultHandler); 27 | PROVIDE(CAN0_EWMC = DefaultHandler); 28 | PROVIDE(EXTI_LINE9_5 = DefaultHandler); 29 | PROVIDE(TIMER0_BRK = DefaultHandler); 30 | PROVIDE(TIMER0_UP = DefaultHandler); 31 | PROVIDE(TIMER0_TRG_CMT = DefaultHandler); 32 | PROVIDE(TIMER0_CHANNEL = DefaultHandler); 33 | PROVIDE(TIMER1 = DefaultHandler); 34 | PROVIDE(TIMER2 = DefaultHandler); 35 | PROVIDE(TIMER3 = DefaultHandler); 36 | PROVIDE(I2C0_EV = DefaultHandler); 37 | PROVIDE(I2C0_ER = DefaultHandler); 38 | PROVIDE(I2C1_EV = DefaultHandler); 39 | PROVIDE(I2C1_ER = DefaultHandler); 40 | PROVIDE(SPI0 = DefaultHandler); 41 | PROVIDE(SPI1 = DefaultHandler); 42 | PROVIDE(USART0 = DefaultHandler); 43 | PROVIDE(USART1 = DefaultHandler); 44 | PROVIDE(USART2 = DefaultHandler); 45 | PROVIDE(EXTI_LINE15_10 = DefaultHandler); 46 | PROVIDE(RTC_ALARM = DefaultHandler); 47 | PROVIDE(USBFS_WKUP = DefaultHandler); 48 | PROVIDE(EXMC = DefaultHandler); 49 | PROVIDE(TIMER4 = DefaultHandler); 50 | PROVIDE(SPI2 = DefaultHandler); 51 | PROVIDE(UART3 = DefaultHandler); 52 | PROVIDE(UART4 = DefaultHandler); 53 | PROVIDE(TIMER5 = DefaultHandler); 54 | PROVIDE(TIMER6 = DefaultHandler); 55 | PROVIDE(DMA1_CHANNEL0 = DefaultHandler); 56 | PROVIDE(DMA1_CHANNEL1 = DefaultHandler); 57 | PROVIDE(DMA1_CHANNEL2 = DefaultHandler); 58 | PROVIDE(DMA1_CHANNEL3 = DefaultHandler); 59 | PROVIDE(DMA1_CHANNEL4 = DefaultHandler); 60 | PROVIDE(CAN1_TX = DefaultHandler); 61 | PROVIDE(CAN1_RX0 = DefaultHandler); 62 | PROVIDE(CAN1_RX1 = DefaultHandler); 63 | PROVIDE(CAN1_EWMC = DefaultHandler); 64 | PROVIDE(USBFS = DefaultHandler); 65 | -------------------------------------------------------------------------------- /src/watchdog.rs: -------------------------------------------------------------------------------- 1 | //! Watchdog peripherals 2 | 3 | use crate::{ 4 | hal::watchdog::{Watchdog, WatchdogEnable}, 5 | pac::{DBG, FWDGT}, 6 | time::MilliSeconds, 7 | }; 8 | 9 | /// Wraps the Free Watchdog Timer (FWDGT) peripheral 10 | pub struct FreeWatchdog { 11 | fwdgt: FWDGT, 12 | } 13 | 14 | const IRC40K_KHZ: u32 = 40; 15 | 16 | const MAX_PR: u8 = 8; 17 | const MAX_RL: u16 = 0xFFF; 18 | 19 | const CMD_ACCESS: u16 = 0x5555; 20 | const CMD_RELOAD: u16 = 0xAAAA; 21 | const CMD_START: u16 = 0xCCCC; 22 | 23 | impl FreeWatchdog { 24 | /// Wrap and start the watchdog 25 | pub fn new(fwdgt: FWDGT) -> Self { 26 | FreeWatchdog { fwdgt } 27 | } 28 | 29 | /// Free watchdog stopped when core is halted 30 | pub fn stop_on_debug(&self, dbg: &DBG, stop: bool) { 31 | dbg.ctl.modify(|_, w| w.fwdgt_hold().bit(stop)); 32 | } 33 | 34 | fn setup(&self, timeout_ms: u32) { 35 | let mut pr = 0; 36 | while pr < MAX_PR && Self::timeout_period(pr, MAX_RL) < timeout_ms { 37 | pr += 1; 38 | } 39 | 40 | let max_period = Self::timeout_period(pr, MAX_RL); 41 | let max_rl = u32::from(MAX_RL); 42 | let rl = (timeout_ms * max_rl / max_period).min(max_rl) as u16; 43 | 44 | self.access_registers(|fwdgt| { 45 | fwdgt.psc.modify(|_, w| unsafe { w.psc().bits(pr) }); 46 | fwdgt.rld.modify(|_, w| unsafe { w.rld().bits(rl) }); 47 | }); 48 | } 49 | 50 | fn is_pr_updating(&self) -> bool { 51 | self.fwdgt.stat.read().pud().bit() 52 | } 53 | 54 | /// Returns the interval in ms 55 | pub fn interval(&self) -> MilliSeconds { 56 | while self.is_pr_updating() {} 57 | 58 | let pr = self.fwdgt.psc.read().psc().bits(); 59 | let rl = self.fwdgt.rld.read().rld().bits(); 60 | let ms = Self::timeout_period(pr, rl); 61 | 62 | MilliSeconds(ms) 63 | } 64 | 65 | /// pr: Prescaler divider bits, rl: reload value 66 | /// 67 | /// Returns ms 68 | fn timeout_period(pr: u8, rl: u16) -> u32 { 69 | let divider: u32 = match pr { 70 | 0b000 => 4, 71 | 0b001 => 8, 72 | 0b010 => 16, 73 | 0b011 => 32, 74 | 0b100 => 64, 75 | 0b101 => 128, 76 | 0b110 => 256, 77 | 0b111 => 256, 78 | _ => panic!("Invalid FWDGT prescaler divider"), 79 | }; 80 | (u32::from(rl) + 1) * divider / IRC40K_KHZ 81 | } 82 | 83 | fn access_registers A>(&self, mut f: F) -> A { 84 | // Unprotect write access to registers 85 | self.fwdgt 86 | .ctl 87 | .write(|w| unsafe { w.cmd().bits(CMD_ACCESS) }); 88 | let a = f(&self.fwdgt); 89 | 90 | // Protect again 91 | self.fwdgt 92 | .ctl 93 | .write(|w| unsafe { w.cmd().bits(CMD_RELOAD) }); 94 | a 95 | } 96 | } 97 | 98 | impl WatchdogEnable for FreeWatchdog { 99 | type Time = MilliSeconds; 100 | 101 | fn start>(&mut self, period: T) { 102 | self.setup(period.into().0); 103 | 104 | self.fwdgt.ctl.write(|w| unsafe { w.cmd().bits(CMD_START) }); 105 | } 106 | } 107 | 108 | impl Watchdog for FreeWatchdog { 109 | fn feed(&mut self) { 110 | self.fwdgt 111 | .ctl 112 | .write(|w| unsafe { w.cmd().bits(CMD_RELOAD) }); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # The Rust Code of Conduct 2 | 3 | ## Conduct 4 | 5 | **Contact**: [RISC-V team](https://github.com/rust-embedded/wg#the-riscv-team) 6 | 7 | * We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic. 8 | * On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all. 9 | * Please be kind and courteous. There's no need to be mean or rude. 10 | * Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer. 11 | * Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works. 12 | * We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term "harassment" as including the definition in the [Citizen Code of Conduct](http://citizencodeofconduct.org/); if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups. 13 | * Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the [RISC-V team][team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a safe place for you and we've got your back. 14 | * Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome. 15 | 16 | ## Moderation 17 | 18 | These are the policies for upholding our community's standards of conduct. 19 | 20 | 1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.) 21 | 2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed. 22 | 3. Moderators will first respond to such remarks with a warning. 23 | 4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off. 24 | 5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded. 25 | 6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology. 26 | 7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a different moderator, **in private**. Complaints about bans in-channel are not allowed. 27 | 8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate situation, they should expect less leeway than others. 28 | 29 | In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely. 30 | 31 | And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could've communicated better — remember that it's your responsibility to make your fellow Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust. 32 | 33 | The enforcement policies listed above apply to all official embedded WG venues; including official IRC channels (#rust-embedded); GitHub repositories under rust-embedded; and all forums under rust-embedded.org (forum.rust-embedded.org). 34 | 35 | *Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).* 36 | 37 | [team]: https://github.com/rust-embedded/wg#the-riscv-team 38 | -------------------------------------------------------------------------------- /src/timer.rs: -------------------------------------------------------------------------------- 1 | //! Timers 2 | 3 | use crate::rcu::{BaseFrequency, Enable, Rcu, Reset}; 4 | use crate::time::Hertz; 5 | 6 | use embedded_hal::timer::{CountDown, Periodic}; 7 | use gd32vf103_pac::{TIMER0, TIMER1, TIMER2, TIMER3, TIMER4, TIMER5, TIMER6}; 8 | use void::Void; 9 | 10 | /// Hardware timer 11 | pub struct Timer { 12 | pub(crate) tim: TIM, 13 | pub(crate) timer_clock: Hertz, 14 | pub(crate) timeout: Hertz, 15 | } 16 | 17 | /// Interrupt events 18 | pub enum Event { 19 | /// Update event. It usually happens due to the counter overflow/underflow. 20 | Update, 21 | } 22 | 23 | macro_rules! hal { 24 | ($($TIM:ident: $tim:ident,)+) => { 25 | $( 26 | impl Timer<$TIM> { 27 | pub fn $tim(timer: $TIM, timeout: T, rcu: &mut Rcu) -> Self 28 | where T: Into { 29 | $TIM::enable(rcu); 30 | $TIM::reset(rcu); 31 | let mut t = Timer { 32 | timer_clock: $TIM::base_frequency(rcu), 33 | tim: timer, 34 | timeout: Hertz(0), 35 | }; 36 | t.start(timeout); 37 | 38 | t 39 | } 40 | 41 | /// Starts listening for an `event` 42 | pub fn listen(&mut self, event: Event) { 43 | match event { 44 | Event::Update => self.tim.dmainten.modify(|_, w| w.upie().set_bit()), 45 | } 46 | } 47 | 48 | /// Stops listening for an `event` 49 | pub fn unlisten(&mut self, event: Event) { 50 | match event { 51 | Event::Update => self.tim.dmainten.modify(|_, w| w.upie().clear_bit()), 52 | } 53 | } 54 | 55 | /// Clears Update Interrupt Flag 56 | pub fn clear_update_interrupt_flag(&mut self) { 57 | self.tim.intf.modify(|_, w| w.upif().clear_bit()); 58 | } 59 | 60 | /// Releases the TIMER peripheral 61 | pub fn free(self) -> $TIM { 62 | self.tim.ctl0.modify(|_, w| w.cen().clear_bit()); 63 | self.tim 64 | } 65 | } 66 | 67 | impl Periodic for Timer<$TIM> {} 68 | 69 | impl CountDown for Timer<$TIM> { 70 | type Time = Hertz; 71 | 72 | fn start(&mut self, timeout: T) 73 | where 74 | T: Into, 75 | { 76 | self.timeout = timeout.into(); 77 | 78 | self.tim.ctl0.modify(|_, w| w.cen().clear_bit()); 79 | self.tim.cnt.reset(); 80 | 81 | let ticks = self.timer_clock.0 / self.timeout.0; 82 | let psc = ((ticks - 1) / (1 << 16)) as u16; 83 | let car = (ticks / ((psc + 1) as u32)) as u16; 84 | self.tim.psc.write(|w| unsafe { w.bits(psc) } ); 85 | self.tim.car.write(|w| unsafe { w.bits(car) } ); 86 | 87 | // Set UPS=1 so an UPG will *not* trigger an interrupt 88 | self.tim.ctl0.modify(|_, w| w.ups().set_bit()); 89 | // Trigger an UPG to clear the timer counter register 90 | // see user manual (v1.2) p.261 for details 91 | self.tim.swevg.write(|w| w.upg().set_bit()); 92 | 93 | // clear any outstanding UPIF flag to ensure a clear start 94 | self.tim.intf.modify(|_, w| w.upif().clear_bit()); 95 | 96 | self.tim.ctl0.write(|w| { w 97 | .updis().clear_bit() 98 | .cen().set_bit() 99 | }); 100 | } 101 | 102 | fn wait(&mut self) -> nb::Result<(), Void> { 103 | if self.tim.intf.read().upif().bit_is_clear() { 104 | Err(nb::Error::WouldBlock) 105 | } else { 106 | self.tim.intf.modify(|_r, w| w.upif().clear_bit()); 107 | Ok(()) 108 | } 109 | } 110 | } 111 | )+ 112 | } 113 | } 114 | 115 | hal! { 116 | TIMER0: timer0, 117 | TIMER1: timer1, 118 | TIMER2: timer2, 119 | TIMER3: timer3, 120 | TIMER4: timer4, 121 | TIMER5: timer5, 122 | TIMER6: timer6, 123 | } 124 | -------------------------------------------------------------------------------- /src/delay.rs: -------------------------------------------------------------------------------- 1 | //! Delays 2 | 3 | use crate::timer::Timer; 4 | use crate::time::U32Ext; 5 | 6 | use cast::u32; 7 | use embedded_hal::blocking::delay::{DelayMs, DelayUs}; 8 | use embedded_hal::timer::CountDown; 9 | use gd32vf103_pac::{TIMER0, TIMER1, TIMER2, TIMER3, TIMER4, TIMER5, TIMER6}; 10 | use crate::rcu::Clocks; 11 | 12 | /// Machine mode cycle counter (`mcycle`) as a delay provider 13 | #[derive(Copy, Clone)] 14 | pub struct McycleDelay { 15 | core_frequency: u32 16 | } 17 | 18 | impl McycleDelay { 19 | /// Constructs the delay provider 20 | pub fn new(clocks: &Clocks) -> Self { 21 | Self { 22 | core_frequency: clocks.sysclk().0 23 | } 24 | } 25 | } 26 | 27 | impl DelayUs for McycleDelay { 28 | fn delay_us(&mut self, us: u64) { 29 | let t0 = riscv::register::mcycle::read64(); 30 | let clocks = (us * (self.core_frequency as u64)) / 1_000_000; 31 | while riscv::register::mcycle::read64().wrapping_sub(t0) <= clocks { } 32 | } 33 | } 34 | 35 | impl DelayUs for McycleDelay { 36 | #[inline(always)] 37 | fn delay_us(&mut self, us: u32) { 38 | self.delay_us(us as u64) 39 | } 40 | } 41 | 42 | // Implemented for constructions like `delay.delay_us(50_000);` 43 | impl DelayUs for McycleDelay { 44 | #[inline(always)] 45 | fn delay_us(&mut self, us: i32) { 46 | assert!(us >= 0); 47 | self.delay_us(us as u32); 48 | } 49 | } 50 | 51 | impl DelayUs for McycleDelay { 52 | #[inline(always)] 53 | fn delay_us(&mut self, us: u16) { 54 | self.delay_us(us as u32) 55 | } 56 | } 57 | 58 | impl DelayUs for McycleDelay { 59 | #[inline(always)] 60 | fn delay_us(&mut self, us: u8) { 61 | self.delay_us(us as u32) 62 | } 63 | } 64 | 65 | impl DelayMs for McycleDelay { 66 | fn delay_ms(&mut self, ms: u32) { 67 | self.delay_us((ms as u64) * 1000) 68 | } 69 | } 70 | 71 | // Implemented for constructions like `delay.delay_ms(50_000);` 72 | impl DelayMs for McycleDelay { 73 | #[inline(always)] 74 | fn delay_ms(&mut self, ms: i32) { 75 | assert!(ms >= 0); 76 | self.delay_ms(ms as u32); 77 | } 78 | } 79 | 80 | impl DelayMs for McycleDelay { 81 | #[inline(always)] 82 | fn delay_ms(&mut self, ms: u16) { 83 | self.delay_ms(ms as u32) 84 | } 85 | } 86 | 87 | impl DelayMs for McycleDelay { 88 | #[inline(always)] 89 | fn delay_ms(&mut self, ms: u8) { 90 | self.delay_ms(ms as u32) 91 | } 92 | } 93 | 94 | /// TIMER as a delay provider 95 | pub struct Delay where Timer: CountDown { 96 | timer: Timer, 97 | } 98 | 99 | macro_rules! delay { 100 | ($($TIMER:ident,)+) => { 101 | $( 102 | impl Delay<$TIMER> { 103 | /// Configures the timer as a delay provider 104 | pub fn new(timer: Timer<$TIMER>) -> Self { 105 | 106 | Delay { timer, } 107 | } 108 | 109 | /// Releases the timer resource 110 | pub fn free(self) -> Timer<$TIMER> { 111 | self.timer 112 | } 113 | } 114 | 115 | impl DelayMs for Delay<$TIMER> { 116 | fn delay_ms(&mut self, ms: u32) { 117 | self.delay_us(ms * 1_000); 118 | } 119 | } 120 | 121 | impl DelayMs for Delay<$TIMER> { 122 | fn delay_ms(&mut self, ms: u16) { 123 | self.delay_ms(u32(ms)); 124 | } 125 | } 126 | 127 | impl DelayMs for Delay<$TIMER> { 128 | fn delay_ms(&mut self, ms: u8) { 129 | self.delay_ms(u32(ms)); 130 | } 131 | } 132 | 133 | impl DelayUs for Delay<$TIMER> { 134 | fn delay_us(&mut self, us: u32) { 135 | let freq = 1_000_000 / us; 136 | self.timer.start(freq.hz()); 137 | while let Err(_) = self.timer.wait() { } 138 | self.timer.tim.ctl0.modify(|_, w| w.cen().clear_bit()); 139 | } 140 | } 141 | 142 | impl DelayUs for Delay<$TIMER> { 143 | fn delay_us(&mut self, us: u16) { 144 | self.delay_us(u32(us)) 145 | } 146 | } 147 | 148 | impl DelayUs for Delay<$TIMER> { 149 | fn delay_us(&mut self, us: u8) { 150 | self.delay_us(u32(us)) 151 | } 152 | } 153 | )+ 154 | } 155 | } 156 | 157 | delay! { 158 | TIMER0, 159 | TIMER1, 160 | TIMER2, 161 | TIMER3, 162 | TIMER4, 163 | TIMER5, 164 | TIMER6, 165 | } 166 | -------------------------------------------------------------------------------- /src/afio.rs: -------------------------------------------------------------------------------- 1 | //! Alternate Function I/Os 2 | 3 | use crate::pac::AFIO; 4 | use crate::rcu::{Rcu, Enable, Reset}; 5 | use crate::gpio::{Debugger, Input, Floating, Port}; 6 | use crate::gpio::gpioa::{PA13, PA14, PA15}; 7 | use crate::gpio::gpiob::{PB3, PB4}; 8 | 9 | pub trait AfioExt { 10 | fn constrain(self, rcu: &mut Rcu) -> Afio; 11 | } 12 | 13 | impl AfioExt for AFIO { 14 | fn constrain(self, rcu: &mut Rcu) -> Afio { 15 | AFIO::enable(rcu); 16 | AFIO::reset(rcu); 17 | 18 | Afio { afio: self } 19 | } 20 | } 21 | 22 | pub struct Afio { 23 | afio: AFIO 24 | } 25 | 26 | impl Afio { 27 | /// Disables the JTAG to free up PA13..PA15, PB3 and PB4 for normal use 28 | pub fn disable_jtag( 29 | &mut self, 30 | pa13: PA13, 31 | pa14: PA14, 32 | pa15: PA15, 33 | pb3: PB3, 34 | pb4: PB4 35 | ) -> ( 36 | PA13>, 37 | PA14>, 38 | PA15>, 39 | PB3>, 40 | PB4>, 41 | ) { 42 | // Set remap to "JTAG-DP Disabled" 43 | self.afio.pcf0.modify(|_, w| unsafe { 44 | w.swj_cfg().bits(0b100) 45 | }); 46 | 47 | // NOTE(unsafe) The pins are now in the good state. 48 | unsafe { 49 | ( 50 | pa13.activate(), 51 | pa14.activate(), 52 | pa15.activate(), 53 | pb3.activate(), 54 | pb4.activate() 55 | ) 56 | } 57 | } 58 | 59 | #[inline] 60 | pub fn extiss(&mut self, port: Port, pin: u8) { 61 | match pin { 62 | 0 => self.afio.extiss0.modify(|_, w| unsafe { w.exti0_ss().bits(port as u8)}), 63 | 1 => self.afio.extiss0.modify(|_, w| unsafe { w.exti1_ss().bits(port as u8)}), 64 | 2 => self.afio.extiss0.modify(|_, w| unsafe { w.exti2_ss().bits(port as u8)}), 65 | 3 => self.afio.extiss0.modify(|_, w| unsafe { w.exti3_ss().bits(port as u8)}), 66 | 4 => self.afio.extiss1.modify(|_, w| unsafe { w.exti4_ss().bits(port as u8)}), 67 | 5 => self.afio.extiss1.modify(|_, w| unsafe { w.exti5_ss().bits(port as u8)}), 68 | 6 => self.afio.extiss1.modify(|_, w| unsafe { w.exti6_ss().bits(port as u8)}), 69 | 7 => self.afio.extiss1.modify(|_, w| unsafe { w.exti7_ss().bits(port as u8)}), 70 | 8 => self.afio.extiss2.modify(|_, w| unsafe { w.exti8_ss().bits(port as u8)}), 71 | 9 => self.afio.extiss2.modify(|_, w| unsafe { w.exti9_ss().bits(port as u8)}), 72 | 10 => self.afio.extiss2.modify(|_, w| unsafe { w.exti10_ss().bits(port as u8)}), 73 | 11 => self.afio.extiss2.modify(|_, w| unsafe { w.exti11_ss().bits(port as u8)}), 74 | 12 => self.afio.extiss3.modify(|_, w| unsafe { w.exti12_ss().bits(port as u8)}), 75 | 13 => self.afio.extiss3.modify(|_, w| unsafe { w.exti13_ss().bits(port as u8)}), 76 | 14 => self.afio.extiss3.modify(|_, w| unsafe { w.exti14_ss().bits(port as u8)}), 77 | 15 => self.afio.extiss3.modify(|_, w| unsafe { w.exti15_ss().bits(port as u8)}), 78 | _ => {} 79 | } 80 | } 81 | } 82 | 83 | pub(crate) mod closed_traits { 84 | use crate::afio::Afio; 85 | 86 | pub trait Remap { 87 | type Variant; 88 | 89 | fn remap(afio: &mut Afio, variant: Self::Variant); 90 | } 91 | } 92 | pub(crate) use closed_traits::*; 93 | 94 | macro_rules! remap_set { 95 | ($pcf0:ident, $field:ident, bool, $value:ident) => { 96 | $pcf0.write(|w| w.$field().bit($value)); 97 | }; 98 | ($pcf0:ident, $field:ident, $type:ty, $value:ident) => { 99 | $pcf0.write(|w| unsafe { 100 | w.$field().bits(u8::from($value)) 101 | }); 102 | } 103 | } 104 | 105 | macro_rules! remap { 106 | ($($PER:ident => ($field:ident, $variant:tt),)+) => { 107 | $( 108 | impl Remap for crate::pac::$PER { 109 | type Variant = $variant; 110 | 111 | #[inline(always)] 112 | fn remap(afio: &mut Afio, variant: $variant) { 113 | let pcf0 = &afio.afio.pcf0; 114 | remap_set!(pcf0, $field, $variant, variant); 115 | } 116 | } 117 | )+ 118 | } 119 | } 120 | 121 | remap! { 122 | I2C0 => (i2c0_remap, bool), 123 | SPI0 => (spi0_remap, bool), 124 | SPI2 => (spi2_remap, bool), 125 | USART0 => (usart0_remap, bool), 126 | USART1 => (usart1_remap, bool), 127 | USART2 => (usart2_remap, u8), 128 | TIMER0 => (timer0_remap, u8), 129 | TIMER1 => (timer1_remap, u8), 130 | TIMER2 => (timer2_remap, u8), 131 | TIMER3 => (timer3_remap, bool), 132 | TIMER4 => (timer4ch3_iremap, bool), 133 | } 134 | -------------------------------------------------------------------------------- /src/exti.rs: -------------------------------------------------------------------------------- 1 | //! External interrupt controller (EXTI). 2 | 3 | use crate::pac::EXTI; 4 | 5 | /// An `ExtiLine` that can be `listen()`ed for interrupt 6 | #[derive(Copy, Clone, PartialEq, Eq)] 7 | pub struct ExtiLine(u8); 8 | 9 | /// Internal sources(lines) for ExtiLine 10 | #[repr(u8)] 11 | #[derive(Copy, Clone, PartialEq, Eq)] 12 | pub enum InternalLine { 13 | Lvd = 16, 14 | RtcAlarm = 17, 15 | UsbWakeup = 18, 16 | } 17 | 18 | /// Enable/Disable event genration to wakeup unit for an EXTI line 19 | #[derive(Copy, Clone)] 20 | pub enum ExtiEvent{ 21 | Enable, 22 | Disable, 23 | } 24 | 25 | impl ExtiLine { 26 | /// Generate an `Option` from a GPIO pin configured by Afio 27 | /// 28 | /// A line from gpio can be obtained through its `pin_number()` method 29 | /// ```no_run 30 | /// use gd32vf103xx_hal::exti::{ExtiLine, TriggerEdge}; 31 | /// # fn listen_pin( 32 | /// # some_pin: gd32vf103xx_hal::gpio::gpioa::Generic<()>, 33 | /// # mut exti: gd32vf103xx_hal::exti::Exti, 34 | /// # ) { 35 | /// let extiline = ExtiLine::from_gpio_line(some_pin.pin_number()).unwrap(); 36 | /// exti.listen(extiline, TriggerEdge::Falling); 37 | /// # } 38 | /// ``` 39 | pub fn from_gpio_line(line: u8) -> Option { 40 | match line { 41 | 0..=15 => Some(ExtiLine(line)), 42 | _ => None, 43 | } 44 | } 45 | 46 | /// Generate an `ExtiLine` from internal source specified by `InternalLine` 47 | pub fn from_internal_line(line: InternalLine) -> Self { 48 | ExtiLine(line as u8) 49 | } 50 | } 51 | 52 | /// Edges that can trigger a configurable interrupt line. 53 | pub enum TriggerEdge { 54 | /// Trigger on rising edges only. 55 | Rising, 56 | /// Trigger on falling edges only. 57 | Falling, 58 | /// Trigger on both rising and falling edges. 59 | Both, 60 | } 61 | 62 | /// Higher-lever wrapper around the `EXTI` peripheral. 63 | pub struct Exti { 64 | raw: EXTI, 65 | } 66 | 67 | impl Exti { 68 | /// Creates a new `Exti` wrapper from the raw `EXTI` peripheral. 69 | pub fn new(raw: EXTI) -> Self { 70 | Self { raw } 71 | } 72 | 73 | /// Destroys this `Exti` instance, returning the raw `EXTI` peripheral. 74 | pub fn release(self) -> EXTI { 75 | self.raw 76 | } 77 | 78 | /// Listen on one of the Lines 79 | #[inline] 80 | pub fn listen(&mut self, line: ExtiLine, edge: TriggerEdge) { 81 | let bm: u32 = 1 << line.0; 82 | 83 | unsafe { 84 | match edge { 85 | TriggerEdge::Falling => { 86 | self.raw.ften.modify(|r, w| w.bits(r.bits() | bm)); 87 | self.raw.rten.modify(|r, w| w.bits(r.bits() & !bm)); 88 | }, 89 | TriggerEdge::Rising => { 90 | self.raw.ften.modify(|r, w| w.bits(r.bits() & !bm)); 91 | self.raw.rten.modify(|r, w| w.bits(r.bits() | bm)); 92 | }, 93 | TriggerEdge::Both => { 94 | self.raw.ften.modify(|r, w| w.bits(r.bits() | bm)); 95 | self.raw.rten.modify(|r, w| w.bits(r.bits() | bm)); 96 | } 97 | } 98 | 99 | self.raw.inten.modify(|r, w| w.bits(r.bits() | bm)); 100 | } 101 | } 102 | 103 | /// Unlisten on the specified line 104 | #[inline] 105 | pub fn unlisten(&mut self, line: ExtiLine) { 106 | let bm: u32 = 1 << line.0; 107 | 108 | unsafe { 109 | self.raw.rten.modify(|r, w| w.bits(r.bits() & !bm)); 110 | self.raw.ften.modify(|r, w| w.bits(r.bits() & !bm)); 111 | self.raw.inten.modify(|r, w| w.bits(r.bits() & !bm)); 112 | } 113 | } 114 | 115 | /// Enable/Disable event generation on line to wakeup unit 116 | #[inline] 117 | pub fn gen_event(&mut self, line: ExtiLine, enable: ExtiEvent) { 118 | let bm: u32 = 1 << line.0; 119 | 120 | if let ExtiEvent::Enable = enable { 121 | unsafe { (*EXTI::ptr()).even.modify(|r, w| w.bits(r.bits() | bm)) }; 122 | } else { 123 | unsafe { (*EXTI::ptr()).even.modify(|r, w| w.bits(r.bits() & !bm)) }; 124 | } 125 | } 126 | 127 | /// `true` if this line has a pending interrupt 128 | #[inline] 129 | pub fn is_pending(line: ExtiLine) -> bool { 130 | let bm: u32 = 1 << line.0; 131 | unsafe { (*EXTI::ptr()).pd.read().bits() & bm != 0 } 132 | } 133 | 134 | /// Clear the pending interrupt flag 135 | #[inline] 136 | pub fn clear(line: ExtiLine) { 137 | let bm: u32 = 1 << line.0; 138 | unsafe { (*EXTI::ptr()).pd.write(|w| w.bits(bm)) }; 139 | } 140 | 141 | /// Request a pending interrupt for this line from software 142 | #[inline] 143 | pub fn pend(line: ExtiLine) { 144 | let bm: u32 = 1 << line.0; 145 | unsafe { (*EXTI::ptr()).swiev.modify(|r, w| w.bits(r.bits() | bm)) }; 146 | } 147 | 148 | /// Deactivate a software pending request for this line 149 | #[inline] 150 | pub fn unpend(line: ExtiLine) { 151 | let bm: u32 = 1 << line.0; 152 | unsafe { (*EXTI::ptr()).swiev.modify(|r, w| w.bits(r.bits() & !bm)) }; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/spi.rs: -------------------------------------------------------------------------------- 1 | //! # Serial Peripheral Interface 2 | 3 | use nb; 4 | 5 | pub use crate::hal::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; 6 | use crate::pac::{spi0, SPI0, SPI1}; 7 | use crate::gpio::gpioa::{PA5, PA6, PA7}; 8 | use crate::gpio::gpiob::{PB13, PB14, PB15, PB3, PB4, PB5}; 9 | use crate::gpio::{Alternate, Floating, Input, PushPull}; 10 | use crate::rcu::{Rcu, Enable, Reset, BaseFrequency}; 11 | use crate::time::Hertz; 12 | use crate::afio::{Afio, Remap}; 13 | use core::ops::Deref; 14 | 15 | /// SPI error 16 | #[derive(Debug)] 17 | pub enum Error { 18 | /// Overrun occurred 19 | Overrun, 20 | /// Mode fault occurred 21 | ModeFault, 22 | /// CRC error 23 | Crc, 24 | #[doc(hidden)] 25 | _Extensible, 26 | } 27 | 28 | #[doc(hidden)] 29 | pub trait SpiX: Deref {} 30 | impl SpiX for SPI0 {} 31 | impl SpiX for SPI1 {} 32 | 33 | pub trait Pins { 34 | const REMAP: bool; 35 | } 36 | 37 | impl Pins 38 | for ( 39 | PA5>, 40 | PA6>, 41 | PA7>, 42 | ) 43 | { 44 | const REMAP: bool = false; 45 | } 46 | 47 | impl Pins 48 | for ( 49 | PB3>, 50 | PB4>, 51 | PB5>, 52 | ) 53 | { 54 | const REMAP: bool = true; 55 | } 56 | 57 | impl Pins 58 | for ( 59 | PB13>, 60 | PB14>, 61 | PB15>, 62 | ) 63 | { 64 | const REMAP: bool = false; 65 | } 66 | 67 | pub struct Spi { 68 | spi: SPI, 69 | pins: PINS, 70 | base_freq: Hertz, 71 | } 72 | 73 | impl> Spi { 74 | pub fn spi0( 75 | spi: SPI0, 76 | pins: PINS, 77 | afio: &mut Afio, 78 | mode: Mode, 79 | freq: impl Into, 80 | rcu: &mut Rcu 81 | ) -> Self 82 | { 83 | SPI0::remap(afio, PINS::REMAP); 84 | Spi::new(spi, pins, mode, freq, rcu) 85 | } 86 | } 87 | 88 | impl> Spi { 89 | pub fn spi1( 90 | spi: SPI1, 91 | pins: PINS, 92 | mode: Mode, 93 | freq: impl Into, 94 | rcu: &mut Rcu 95 | ) -> Self 96 | { 97 | Spi::new(spi, pins, mode, freq, rcu) 98 | } 99 | } 100 | 101 | impl Spi where SPI: SpiX 102 | { 103 | fn new( 104 | spi: SPI, 105 | pins: PINS, 106 | mode: Mode, 107 | freq: impl Into, 108 | rcu: &mut Rcu 109 | ) -> Self where SPI: Enable + Reset + BaseFrequency { 110 | SPI::enable(rcu); 111 | SPI::reset(rcu); 112 | 113 | // disable SS output 114 | spi.ctl1.write(|w| w.nssdrv().clear_bit()); 115 | 116 | let base_freq = SPI::base_frequency(rcu); 117 | 118 | let br = match base_freq.0 / freq.into().0 { 119 | 0 => panic!("Requested SPI frequency is too high"), 120 | 1..=2 => 0b000, 121 | 3..=5 => 0b001, 122 | 6..=11 => 0b010, 123 | 12..=23 => 0b011, 124 | 24..=47 => 0b100, 125 | 48..=95 => 0b101, 126 | 96..=191 => 0b110, 127 | _ => 0b111, 128 | }; 129 | 130 | // mstr: master configuration 131 | // lsbfirst: MSB first 132 | // ssm: enable software slave management (NSS pin free for other uses) 133 | // ssi: set nss high = master mode 134 | spi.ctl0.write(|w| unsafe { w 135 | .ckph().bit(mode.phase == Phase::CaptureOnSecondTransition) 136 | .ckpl().bit(mode.polarity == Polarity::IdleHigh) 137 | .mstmod().set_bit() // Master mode 138 | .psc().bits(br) // Master clock prescaler selection 139 | .lf().clear_bit() // Transmit MSB first 140 | .swnss().set_bit() // NSS pin is pulled high 141 | .swnssen().set_bit() // NSS software mode. The NSS level depends on SWNSS bit. 142 | .ro().clear_bit() // Full-duplex mode 143 | .ff16().clear_bit() // 8-bit data frame format 144 | .bden().clear_bit() // 2-line unidirectional mode 145 | .spien().set_bit() // Enable SPI peripheral 146 | }); 147 | 148 | Spi { spi, pins, base_freq } 149 | } 150 | 151 | /// Change the frequency of operation of the SPI bus. 152 | /// The maximum frequency for the SPI bus is the frequency 153 | /// of the APB1 bus which is half the system frequency configured 154 | /// with RCU.configure().sysclk(). Specifying a higher frequency causes panic. 155 | pub fn change_clock_freq(&mut self, freq: impl Into) { 156 | let br = match self.base_freq.0 / freq.into().0 { 157 | 0 => panic!("Requested SPI frequency is too high"), 158 | 1..=2 => 0b000, 159 | 3..=5 => 0b001, 160 | 6..=11 => 0b010, 161 | 12..=23 => 0b011, 162 | 24..=47 => 0b100, 163 | 48..=95 => 0b101, 164 | 96..=191 => 0b110, 165 | _ => 0b111, 166 | }; 167 | 168 | // Disable SPI 169 | self.spi.ctl0.modify(|_, w| { w.spien().clear_bit()}); 170 | 171 | // Restore config, change frequency and re-enable SPI 172 | self.spi.ctl0.modify( |_, w| unsafe { w 173 | .psc().bits(br) 174 | .spien().set_bit() 175 | }); 176 | } 177 | 178 | pub fn free(self) -> (SPI, PINS) { 179 | (self.spi, self.pins) 180 | } 181 | } 182 | 183 | impl crate::hal::spi::FullDuplex for Spi { 184 | type Error = Error; 185 | 186 | fn read(&mut self) -> nb::Result { 187 | let sr = self.spi.stat.read(); 188 | 189 | Err(if sr.rxorerr().bit_is_set() { 190 | nb::Error::Other(Error::Overrun) 191 | } else if sr.conferr().bit_is_set() { 192 | nb::Error::Other(Error::ModeFault) 193 | } else if sr.crcerr().bit_is_set() { 194 | nb::Error::Other(Error::Crc) 195 | } else if sr.rbne().bit_is_set() { 196 | let byte = (self.spi.data.read().bits() & 0xff) as u8; 197 | return Ok(byte); 198 | } else { 199 | nb::Error::WouldBlock 200 | }) 201 | } 202 | 203 | fn send(&mut self, byte: u8) -> nb::Result<(), Error> { 204 | let sr = self.spi.stat.read(); 205 | 206 | Err(if sr.rxorerr().bit_is_set() { 207 | nb::Error::Other(Error::Overrun) 208 | } else if sr.conferr().bit_is_set() { 209 | nb::Error::Other(Error::ModeFault) 210 | } else if sr.crcerr().bit_is_set() { 211 | nb::Error::Other(Error::Crc) 212 | } else if sr.tbe().bit_is_set() { 213 | self.spi.data.write(|w| unsafe { w.bits(byte as u16) }); 214 | return Ok(()); 215 | } else { 216 | nb::Error::WouldBlock 217 | }) 218 | } 219 | 220 | } 221 | 222 | impl crate::hal::blocking::spi::transfer::Default for Spi {} 223 | 224 | impl crate::hal::blocking::spi::write::Default for Spi {} 225 | -------------------------------------------------------------------------------- /src/exmc.rs: -------------------------------------------------------------------------------- 1 | //! External memory controller 2 | 3 | use crate::pac::EXMC; 4 | use crate::rcu::{Rcu, Enable}; 5 | use crate::gpio::gpiod::{PD0, PD1, PD4, PD5, PD6, PD7, PD8, PD9, PD10, PD11, PD12, PD13, PD14, PD15}; 6 | use crate::gpio::gpioe::{PE0, PE1, PE2, PE3, PE4, PE5, PE6, PE7, PE8, PE9, PE10, PE11, PE12, PE13, PE14, PE15}; 7 | use crate::gpio::{Alternate, PushPull}; 8 | use vcell::VolatileCell; 9 | 10 | const REGION_SIZE: usize = 64*1024*1024; 11 | const REGION_PTR: *const () = 0x6000_0000 as *const (); 12 | 13 | /// Extension trait that sets up the `EXMC` peripheral 14 | pub trait ExmcExt { 15 | /// Configures the `EXMC` peripheral 16 | fn configure(self, pins: ExmcPins, conf: ExmcConfiguration, timing_conf: ExmcTimingConfiguration, rcu: &mut Rcu) -> Exmc; 17 | } 18 | 19 | impl ExmcExt for EXMC { 20 | fn configure(self, pins: ExmcPins, conf: ExmcConfiguration, timing_conf: ExmcTimingConfiguration, rcu: &mut Rcu) -> Exmc { 21 | Exmc::new(self, pins, conf, timing_conf, rcu) 22 | } 23 | } 24 | 25 | pub struct Exmc { 26 | regs: EXMC, 27 | pins: ExmcPins, 28 | } 29 | 30 | impl Exmc { 31 | /// Configures the `EXMC` peripheral 32 | pub fn new(regs: EXMC, pins: ExmcPins, conf: ExmcConfiguration, timing_conf: ExmcTimingConfiguration, rcu: &mut Rcu) -> Self { 33 | EXMC::enable(rcu); 34 | 35 | regs.snctl0.write(|w| unsafe { 36 | w.nrmux().bit(conf.address_data_mux_enabled); 37 | w.nrtp().bits(conf.memory_type as u8); 38 | w.nrw().bits(conf.databus_width as u8); 39 | w.nren().bit(conf.memory_type == MemoryType::NORFlash); 40 | w.nrwtpol().bit(conf.nwait_polarity == NwaitPolarity::ActiveHigh); 41 | w.wren().bit(conf.memory_write_enabled); 42 | w.nrwten().bit(conf.nwait_signal_enabled); 43 | w.asyncwait().bit(conf.async_wait_enabled); 44 | w 45 | }); 46 | 47 | regs.sntcfg0.write(|w| unsafe { 48 | w.aset().bits(timing_conf.address_setup_time); 49 | w.ahld().bits(timing_conf.address_hold_time); 50 | w.dset().bits(timing_conf.data_setup_time); 51 | w.buslat().bits(timing_conf.bus_latency) 52 | }); 53 | 54 | // Enable memory bank 55 | regs.snctl0.modify(|_, w| w.nrbken().set_bit()); 56 | 57 | Exmc { 58 | regs, 59 | pins, 60 | } 61 | } 62 | 63 | pub fn release(self) -> (EXMC, ExmcPins) { 64 | // Disable memory bank 65 | self.regs.snctl0.modify(|_, w| w.nrbken().clear_bit()); 66 | 67 | (self.regs, self.pins) 68 | } 69 | 70 | pub fn as_u8_slice(&self) -> &[VolatileCell] { 71 | let ptr = REGION_PTR as *const VolatileCell; 72 | unsafe { core::slice::from_raw_parts(ptr, REGION_SIZE) } 73 | } 74 | 75 | pub fn as_u16_slice(&self) -> &[VolatileCell] { 76 | let ptr = REGION_PTR as *const VolatileCell; 77 | unsafe { core::slice::from_raw_parts(ptr, REGION_SIZE / 2) } 78 | } 79 | 80 | pub fn as_u32_slice(&self) -> &[VolatileCell] { 81 | let ptr = REGION_PTR as *const VolatileCell; 82 | unsafe { core::slice::from_raw_parts(ptr, REGION_SIZE / 4) } 83 | } 84 | } 85 | 86 | /// EXMC configuration 87 | pub struct ExmcConfiguration { 88 | /// Asynchronous wait feature 89 | pub async_wait_enabled: bool, 90 | 91 | /// For Flash memory access in burst mode, this flag 92 | /// enables/disables wait-state insertion via the NWAIT signal 93 | pub nwait_signal_enabled: bool, 94 | 95 | /// Enables/disables write in the bank 96 | pub memory_write_enabled: bool, 97 | 98 | /// NWAIT signal polarity 99 | pub nwait_polarity: NwaitPolarity, 100 | 101 | /// NOR region memory data bus width 102 | pub databus_width: DataBusWidth, 103 | 104 | /// NOR region memory type 105 | pub memory_type: MemoryType, 106 | 107 | /// NOR region memory address/data multiplexing 108 | pub address_data_mux_enabled: bool, 109 | } 110 | 111 | #[repr(u8)] 112 | #[derive(Copy, Clone, PartialEq)] 113 | pub enum NwaitPolarity { 114 | ActiveLow = 0, 115 | ActiveHigh = 1, 116 | } 117 | 118 | #[repr(u8)] 119 | #[derive(Copy, Clone, PartialEq)] 120 | pub enum DataBusWidth { 121 | Width8Bits = 0, 122 | Width16Bits = 1, 123 | } 124 | 125 | #[repr(u8)] 126 | #[derive(Copy, Clone, PartialEq)] 127 | pub enum MemoryType { 128 | SRAM = 0, 129 | PSRAM = 1, 130 | NORFlash = 2, 131 | } 132 | 133 | /// EXMC timing configuration 134 | #[derive(Copy, Clone)] 135 | pub struct ExmcTimingConfiguration { 136 | address_setup_time: u8, 137 | address_hold_time: u8, 138 | data_setup_time: u8, 139 | bus_latency: u8, 140 | } 141 | 142 | impl Default for ExmcTimingConfiguration { 143 | fn default() -> Self { 144 | Self { 145 | address_setup_time: 0xf, 146 | address_hold_time: 0xf, 147 | data_setup_time: 0xff, 148 | bus_latency: 0xf, 149 | } 150 | } 151 | } 152 | 153 | impl ExmcTimingConfiguration { 154 | pub fn address_setup_time(&mut self, hclk_periods: u32) -> &mut Self { 155 | assert!(1 <= hclk_periods && hclk_periods < 16); 156 | self.address_setup_time = (hclk_periods - 1) as u8; 157 | self 158 | } 159 | 160 | pub fn address_hold_time(&mut self, hclk_periods: u32) -> &mut Self { 161 | assert!(2 <= hclk_periods && hclk_periods < 16); 162 | self.address_hold_time = (hclk_periods - 1) as u8; 163 | self 164 | } 165 | 166 | pub fn data_setup_time(&mut self, hclk_periods: u32) -> &mut Self { 167 | assert!(2 <= hclk_periods && hclk_periods < 256); 168 | self.data_setup_time = (hclk_periods - 1) as u8; 169 | self 170 | } 171 | 172 | pub fn bus_latency(&mut self, hclk_periods: u32) -> &mut Self { 173 | assert!(1 <= hclk_periods && hclk_periods < 16); 174 | self.bus_latency = (hclk_periods - 1) as u8; 175 | self 176 | } 177 | } 178 | 179 | #[allow(dead_code)] 180 | pub struct ExmcPins { 181 | pub d0: PD14>, 182 | pub d1: PD15>, 183 | pub d2: PD0>, 184 | pub d3: PD1>, 185 | pub d4: PE7>, 186 | pub d5: PE8>, 187 | pub d6: PE9>, 188 | pub d7: PE10>, 189 | pub d8: Option>>, 190 | pub d9: Option>>, 191 | pub d10: Option>>, 192 | pub d11: Option>>, 193 | pub d12: Option>>, 194 | pub d13: Option>>, 195 | pub d14: Option>>, 196 | pub d15: Option>>, 197 | pub a16: Option>>, 198 | pub a17: Option>>, 199 | pub a18: Option>>, 200 | pub a19: Option>>, 201 | pub a20: Option>>, 202 | pub a21: Option>>, 203 | pub a22: Option>>, 204 | pub a23: Option>>, 205 | pub noe: Option>>, 206 | pub nwe: Option>>, 207 | pub nwait: Option>>, 208 | pub ne0: Option>>, 209 | pub nbl0: Option>>, 210 | pub nbl1: Option>>, 211 | } 212 | -------------------------------------------------------------------------------- /src/rtc.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | Real time clock 3 | 4 | A continuously running clock that counts seconds. It is part of the backup domain which means 5 | that the counter is not affected by system resets or standby mode. If Vbat is connected, it is 6 | not reset even if the rest of the device is powered off. This allows it to be used to wake the 7 | CPU when it is in low power mode. 8 | 9 | Since it is part of the backup domain, write access to it must be enabled before the RTC can be 10 | used. 11 | */ 12 | 13 | use crate::pac::{RCU, RTC}; 14 | 15 | use crate::backup_domain::{BackupDomain, Lxtal}; 16 | use crate::time::Hertz; 17 | 18 | use core::convert::Infallible; 19 | 20 | const LXTAL_HERTZ: u32 = 32_768; 21 | 22 | /** 23 | Interface to the real time clock 24 | */ 25 | pub struct Rtc { 26 | regs: RTC, 27 | /// ensure that LXTAL is configured as long as RTC exists 28 | #[allow(dead_code)] 29 | lxtal: Lxtal, 30 | } 31 | 32 | impl Rtc { 33 | /** 34 | Initialises the RTC. 35 | */ 36 | pub fn rtc(regs: RTC, bkp: &mut BackupDomain, lxtal: Lxtal) -> Self { 37 | let mut result = Rtc { regs, lxtal }; 38 | 39 | Rtc::enable_rtc(bkp, &lxtal); 40 | 41 | // Set the prescaler to make it count up once every second. 42 | let prl = LXTAL_HERTZ - 1; 43 | assert!(prl < 1 << 20); 44 | result.perform_write(|s| { 45 | s.regs.psch.write(|w| unsafe { w.bits(prl >> 16) }); 46 | s.regs.pscl.write(|w| unsafe { w.bits(prl as u16 as u32) }); 47 | }); 48 | 49 | result 50 | } 51 | 52 | /// Enables the RTC device with the LXTAL as the clock. 53 | /// This setting is reset only when the backup domain is reset. 54 | fn enable_rtc(_bkp: &mut BackupDomain, _lxtal: &Lxtal) { 55 | let rcu = unsafe { &*RCU::ptr() }; 56 | rcu.bdctl.modify(|_, w| { 57 | unsafe { 58 | w 59 | // Set the source of the RTC to LXTAL 60 | .rtcsrc() 61 | .bits(0b01) 62 | // Enable the RTC 63 | .rtcen() 64 | .set_bit() 65 | } 66 | }) 67 | } 68 | 69 | /// Selects the frequency at which the RTC counter is updated. 70 | pub fn select_frequency(&mut self, timeout: impl Into) { 71 | let frequency = timeout.into().0; 72 | 73 | assert!(frequency <= LXTAL_HERTZ); 74 | 75 | let prescaler = LXTAL_HERTZ / frequency - 1; 76 | self.perform_write(|s| { 77 | s.regs.psch.write(|w| unsafe { w.bits(prescaler >> 16) }); 78 | s.regs 79 | .pscl 80 | .write(|w| unsafe { w.bits(prescaler as u16 as u32) }); 81 | }); 82 | } 83 | 84 | /// Set the current RTC counter value to the specified amount 85 | pub fn set_time(&mut self, counter_value: u32) { 86 | self.perform_write(|s| { 87 | s.regs 88 | .cnth 89 | .write(|w| unsafe { w.bits(counter_value >> 16) }); 90 | s.regs 91 | .cntl 92 | .write(|w| unsafe { w.bits(counter_value as u16 as u32) }); 93 | }); 94 | } 95 | 96 | /** 97 | Sets the time at which an alarm will be triggered 98 | 99 | This also clears the alarm flag if it is set 100 | */ 101 | pub fn set_alarm(&mut self, counter_value: u32) { 102 | // Set alarm time 103 | // See section 14.3.4 for explanation 104 | let alarm_value = counter_value - 1; 105 | 106 | self.perform_write(|s| { 107 | s.regs 108 | .alrmh 109 | .write(|w| unsafe { w.alrm().bits((alarm_value >> 16) as u16) }); 110 | s.regs 111 | .alrml 112 | .write(|w| unsafe { w.alrm().bits(alarm_value as u16) }); 113 | }); 114 | 115 | self.clear_alarm_flag(); 116 | } 117 | 118 | /// Enables the RTC_ALARM interrupt 119 | pub fn listen_alarm(&mut self) { 120 | // Enable alarm interrupt 121 | self.perform_write(|s| { 122 | s.regs.inten.modify(|_, w| w.alrmie().set_bit()); 123 | }) 124 | } 125 | 126 | /// Disables the RTC_ALARM interrupt 127 | pub fn unlisten_alarm(&mut self) { 128 | // Disable alarm interrupt 129 | self.perform_write(|s| { 130 | s.regs.inten.modify(|_, w| w.alrmie().clear_bit()); 131 | }) 132 | } 133 | 134 | /// Reads the current counter 135 | pub fn current_time(&self) -> u32 { 136 | // Wait for the APB1 interface to be ready 137 | while !self.regs.ctl.read().rsynf().bit() {} 138 | 139 | self.regs.cnth.read().bits() << 16 | self.regs.cntl.read().bits() 140 | } 141 | 142 | /// Enables the RTC second interrupt. 143 | /// This interrupt triggers whenever the RTC counter increases. 144 | pub fn listen_seconds(&mut self) { 145 | self.perform_write(|s| s.regs.inten.modify(|_, w| w.scie().set_bit())) 146 | } 147 | 148 | /// Disables the RTC second interrupt 149 | pub fn unlisten_seconds(&mut self) { 150 | self.perform_write(|s| s.regs.inten.modify(|_, w| w.scie().clear_bit())) 151 | } 152 | 153 | /// Clears the RTC second interrupt flag 154 | pub fn clear_second_flag(&mut self) { 155 | self.perform_write(|s| s.regs.ctl.modify(|_, w| w.scif().clear_bit())) 156 | } 157 | 158 | /// Clears the RTC alarm interrupt flag 159 | pub fn clear_alarm_flag(&mut self) { 160 | self.perform_write(|s| s.regs.ctl.modify(|_, w| w.alrmif().clear_bit())) 161 | } 162 | 163 | /// Return `Ok(())` if the second flag is set, `Err(nb::WouldBlock)` otherwise. 164 | pub fn wait_second(&mut self) -> nb::Result<(), Infallible> { 165 | if self.regs.ctl.read().scif().bit() { 166 | self.regs.ctl.modify(|_, w| w.scif().clear_bit()); 167 | Ok(()) 168 | } else { 169 | Err(nb::Error::WouldBlock) 170 | } 171 | } 172 | 173 | /** 174 | Return `Ok(())` if the alarm flag is set, `Err(nb::WouldBlock)` otherwise. 175 | 176 | ```rust 177 | use nb::block; 178 | use gd32vf103xx_hal::rtc::Rtc; 179 | # fn set_alarm(mut rtc: Rtc) { 180 | rtc.set_alarm(rtc.current_time() + 5); 181 | // NOTE: Safe unwrap because Infallible can't be returned 182 | block!(rtc.wait_alarm()).unwrap(); 183 | # } 184 | ``` 185 | */ 186 | pub fn wait_alarm(&mut self) -> nb::Result<(), Infallible> { 187 | if self.regs.ctl.read().alrmif().bit() { 188 | self.regs.ctl.modify(|_, w| w.alrmif().clear_bit()); 189 | Ok(()) 190 | } else { 191 | Err(nb::Error::WouldBlock) 192 | } 193 | } 194 | 195 | /** 196 | The RTC registers can not be written to at any time as documented in section 197 | 14.3.3 of the manual. Performing writes using this function ensures that 198 | the writes are done correctly. 199 | */ 200 | fn perform_write(&mut self, func: impl Fn(&mut Self)) { 201 | // Wait for the last write operation to be done 202 | while !self.regs.ctl.read().lwoff().bit() {} 203 | // Put the clock into config mode 204 | self.regs.ctl.modify(|_, w| w.cmf().set_bit()); 205 | 206 | // Perform the write operation 207 | func(self); 208 | 209 | // Take the device out of config mode 210 | self.regs.ctl.modify(|_, w| w.cmf().clear_bit()); 211 | // Wait for the write to be done 212 | while !self.regs.ctl.read().lwoff().bit() {} 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /src/eclic.rs: -------------------------------------------------------------------------------- 1 | use crate::pac::ECLIC; 2 | use riscv::interrupt::Nr; 3 | 4 | /// The code is based on vendor provided HAL libraries. 5 | /// Most code come from Firmware\RISCV\env_Eclipse\start.S 6 | pub mod mode; 7 | 8 | const EFFECTIVE_LEVEL_PRIORITY_BITS: u8 = 4; 9 | 10 | #[repr(u8)] 11 | #[derive(Debug)] 12 | pub enum LevelPriorityBits { 13 | L0P4 = 0, 14 | L1P3 = 1, 15 | L2P2 = 2, 16 | L3P1 = 3, 17 | L4P0 = 4, 18 | } 19 | 20 | #[repr(u8)] 21 | #[derive(Debug)] 22 | pub enum TriggerType { 23 | Level = 0, 24 | RisingEdge = 1, 25 | FallingEdge = 3, 26 | } 27 | 28 | #[repr(u8)] 29 | #[derive(Debug)] 30 | pub enum Level { 31 | L0 = 0, 32 | L1 = 1, 33 | L2 = 2, 34 | L3 = 3, 35 | L4 = 4, 36 | L5 = 5, 37 | L6 = 6, 38 | L7 = 7, 39 | L8 = 8, 40 | L9 = 9, 41 | L10 = 10, 42 | L11 = 11, 43 | L12 = 12, 44 | L13 = 13, 45 | L14 = 14, 46 | L15 = 15, 47 | } 48 | 49 | #[repr(u8)] 50 | #[derive(Debug)] 51 | pub enum Priority { 52 | P0 = 0, 53 | P1 = 1, 54 | P2 = 2, 55 | P3 = 3, 56 | P4 = 4, 57 | P5 = 5, 58 | P6 = 6, 59 | P7 = 7, 60 | P8 = 8, 61 | P9 = 9, 62 | P10 = 10, 63 | P11 = 11, 64 | P12 = 12, 65 | P13 = 13, 66 | P14 = 14, 67 | P15 = 15, 68 | } 69 | 70 | pub trait EclicExt { 71 | /// Reset all ECLIC registers to 0 72 | fn reset(); 73 | 74 | /// Set interrupts threshold level 75 | fn set_threshold_level(level: Level); 76 | 77 | /// Get interrupts threshold level 78 | fn get_threshold_level() -> Level; 79 | 80 | fn set_level_priority_bits(lp: LevelPriorityBits); 81 | 82 | fn get_level_priority_bits() -> Option; 83 | 84 | /// Get number of bits designated for interrupt level 85 | fn get_level_bits() -> u8; 86 | 87 | /// Get number of bits designated for interrupt priority 88 | fn get_priority_bits() -> u8; 89 | 90 | /// Setup `interrupt` 91 | fn setup(interrupt: I, tt: TriggerType, level: Level, priority: Priority); 92 | 93 | /// Enables `interrupt` 94 | unsafe fn unmask(interrupt: I); 95 | 96 | /// Disables `interrupt` 97 | fn mask(interrupt: I); 98 | 99 | /// Checks if `interrupt` is enabled 100 | fn is_enabled(interrupt: I) -> bool; 101 | 102 | /// Forces `interrupt` into pending state 103 | fn pend(interrupt: I); 104 | 105 | /// Clears `interrupt`'s pending state 106 | fn unpend(interrupt: I); 107 | 108 | /// Checks if `interrupt` is pending 109 | fn is_pending(interrupt: I) -> bool; 110 | 111 | /// Set `interrupt` trigger type 112 | fn set_trigger_type(interrupt: I, tt: TriggerType); 113 | 114 | /// Get `interrupt` trigger type 115 | fn get_trigger_type(interrupt: I) -> Option; 116 | 117 | // Set `interrupt` level 118 | fn set_level(interrupt: I, level: Level); 119 | 120 | // Get `interrupt` level 121 | fn get_level(interrupt: I) -> Level; 122 | 123 | // Set `interrupt` priority 124 | fn set_priority(interrupt: I, priority: Priority); 125 | 126 | // Get `interrupt` interrupt 127 | fn get_priority(interrupt: I) -> Priority; 128 | } 129 | 130 | impl EclicExt for ECLIC { 131 | fn reset() { 132 | let eclic = unsafe { &*Self::ptr() }; 133 | 134 | eclic.cliccfg.write(|w| unsafe { w.bits(0) }); 135 | eclic.mth.write(|w| unsafe { w.bits(0) }); 136 | 137 | for nr in 0..eclic.clicinfo.read().num_interrupt().bits() as usize { 138 | eclic.clicints[nr].clicintip.write(|w| unsafe { w.bits(0) }); 139 | eclic.clicints[nr].clicintie.write(|w| unsafe { w.bits(0) }); 140 | eclic.clicints[nr] 141 | .clicintattr 142 | .write(|w| unsafe { w.bits(0) }); 143 | eclic.clicints[nr] 144 | .clicintctl 145 | .write(|w| unsafe { w.bits(0) }); 146 | } 147 | } 148 | 149 | #[inline] 150 | fn set_threshold_level(level: Level) { 151 | unsafe { (*Self::ptr()).mth.write(|w| w.mth().bits(level as u8)) } 152 | } 153 | 154 | #[inline] 155 | fn get_threshold_level() -> Level { 156 | unsafe { core::mem::transmute((*Self::ptr()).mth.read().mth().bits() & 0xF) } 157 | } 158 | 159 | #[inline] 160 | fn set_level_priority_bits(lp: LevelPriorityBits) { 161 | unsafe { (*Self::ptr()).cliccfg.write(|w| w.nlbits().bits(lp as u8)) } 162 | } 163 | 164 | #[inline] 165 | fn get_level_priority_bits() -> Option { 166 | match unsafe { (*Self::ptr()).cliccfg.read().nlbits().bits() } { 167 | 0 => Some(LevelPriorityBits::L0P4), 168 | 1 => Some(LevelPriorityBits::L1P3), 169 | 2 => Some(LevelPriorityBits::L2P2), 170 | 3 => Some(LevelPriorityBits::L3P1), 171 | 4 => Some(LevelPriorityBits::L4P0), 172 | _ => None, 173 | } 174 | } 175 | 176 | #[inline] 177 | fn get_level_bits() -> u8 { 178 | let bits = unsafe { (*Self::ptr()).cliccfg.read().nlbits().bits() }; 179 | 180 | core::cmp::min(bits, EFFECTIVE_LEVEL_PRIORITY_BITS) 181 | } 182 | 183 | #[inline] 184 | fn get_priority_bits() -> u8 { 185 | EFFECTIVE_LEVEL_PRIORITY_BITS - Self::get_level_bits() 186 | } 187 | 188 | fn setup(interrupt: I, tt: TriggerType, level: Level, priority: Priority) { 189 | Self::mask(interrupt); 190 | Self::set_trigger_type(interrupt, tt); 191 | Self::set_level(interrupt, level); 192 | Self::set_priority(interrupt, priority); 193 | Self::unpend(interrupt); 194 | } 195 | 196 | #[inline] 197 | unsafe fn unmask(interrupt: I) { 198 | let nr = usize::from(interrupt.nr()); 199 | 200 | unsafe { &*Self::ptr() }.clicints[nr] 201 | .clicintie 202 | .write(|w| w.ie().set_bit()) 203 | } 204 | 205 | #[inline] 206 | fn mask(interrupt: I) { 207 | let nr = usize::from(interrupt.nr()); 208 | 209 | unsafe { 210 | (*Self::ptr()).clicints[nr] 211 | .clicintie 212 | .write(|w| w.ie().clear_bit()) 213 | } 214 | } 215 | 216 | #[inline] 217 | fn is_enabled(interrupt: I) -> bool { 218 | let nr = usize::from(interrupt.nr()); 219 | 220 | unsafe { 221 | (*Self::ptr()).clicints[nr] 222 | .clicintie 223 | .read() 224 | .ie() 225 | .bit_is_set() 226 | } 227 | } 228 | 229 | #[inline] 230 | fn pend(interrupt: I) { 231 | let nr = usize::from(interrupt.nr()); 232 | 233 | unsafe { 234 | (*Self::ptr()).clicints[nr] 235 | .clicintip 236 | .write(|w| w.ip().set_bit()) 237 | } 238 | } 239 | 240 | #[inline] 241 | fn unpend(interrupt: I) { 242 | let nr = usize::from(interrupt.nr()); 243 | 244 | unsafe { 245 | (*Self::ptr()).clicints[nr] 246 | .clicintip 247 | .write(|w| w.ip().clear_bit()) 248 | } 249 | } 250 | 251 | #[inline] 252 | fn is_pending(interrupt: I) -> bool { 253 | let nr = usize::from(interrupt.nr()); 254 | 255 | unsafe { 256 | (*Self::ptr()).clicints[nr] 257 | .clicintip 258 | .read() 259 | .ip() 260 | .bit_is_set() 261 | } 262 | } 263 | 264 | #[inline] 265 | fn set_trigger_type(interrupt: I, tt: TriggerType) { 266 | let nr = usize::from(interrupt.nr()); 267 | 268 | unsafe { 269 | (*Self::ptr()).clicints[nr] 270 | .clicintattr 271 | .write(|w| w.trig().bits(tt as u8).shv().clear_bit()) 272 | } 273 | } 274 | 275 | #[inline] 276 | fn get_trigger_type(interrupt: I) -> Option { 277 | let nr = usize::from(interrupt.nr()); 278 | 279 | match unsafe { (*Self::ptr()).clicints[nr].clicintattr.read().trig().bits() } { 280 | 0 => Some(TriggerType::Level), 281 | 1 => Some(TriggerType::RisingEdge), 282 | 3 => Some(TriggerType::FallingEdge), 283 | _ => None, 284 | } 285 | } 286 | 287 | #[inline] 288 | fn set_level(interrupt: I, level: Level) { 289 | let nr = usize::from(interrupt.nr()); 290 | 291 | let mut intctl = unsafe { 292 | (*Self::ptr()).clicints[nr] 293 | .clicintctl 294 | .read() 295 | .level_priority() 296 | .bits() 297 | }; 298 | let level_bits = Self::get_level_bits(); 299 | 300 | intctl <<= level_bits; 301 | intctl >>= level_bits; 302 | 303 | let level = core::cmp::min(level as u8, (1 << level_bits) - 1); 304 | let level = level << (8 - level_bits); 305 | 306 | unsafe { 307 | (*Self::ptr()).clicints[nr] 308 | .clicintctl 309 | .write(|w| w.level_priority().bits(intctl | level)) 310 | } 311 | } 312 | 313 | #[inline] 314 | fn get_level(interrupt: I) -> Level { 315 | let nr = usize::from(interrupt.nr()); 316 | 317 | let intctl = unsafe { 318 | (*Self::ptr()).clicints[nr] 319 | .clicintctl 320 | .read() 321 | .level_priority() 322 | .bits() 323 | }; 324 | 325 | let level = (u16::from(intctl) >> (8 - Self::get_level_bits())) as u8; 326 | 327 | // Enum contains all values from 0-15 328 | unsafe { core::mem::transmute(level & 0xF) } 329 | } 330 | 331 | #[inline] 332 | fn set_priority(interrupt: I, priority: Priority) { 333 | let nr = usize::from(interrupt.nr()); 334 | 335 | let mut intctl = unsafe { 336 | (*Self::ptr()).clicints[nr] 337 | .clicintctl 338 | .read() 339 | .level_priority() 340 | .bits() 341 | }; 342 | let level_bits = Self::get_level_bits(); 343 | 344 | intctl >>= 8 - level_bits; 345 | intctl <<= 8 - level_bits; 346 | 347 | let priority = core::cmp::min( 348 | priority as u8, 349 | (1 << (EFFECTIVE_LEVEL_PRIORITY_BITS - level_bits)) - 1, 350 | ); 351 | let priority = priority << (8 - EFFECTIVE_LEVEL_PRIORITY_BITS); 352 | 353 | unsafe { 354 | (*Self::ptr()).clicints[nr] 355 | .clicintctl 356 | .write(|w| w.level_priority().bits(intctl | priority)) 357 | } 358 | } 359 | 360 | #[inline] 361 | fn get_priority(interrupt: I) -> Priority { 362 | let nr = usize::from(interrupt.nr()); 363 | 364 | let intctl = unsafe { 365 | (*Self::ptr()).clicints[nr] 366 | .clicintctl 367 | .read() 368 | .level_priority() 369 | .bits() 370 | }; 371 | 372 | let level_bits = Self::get_level_bits(); 373 | let priority = (u16::from(intctl << level_bits) 374 | >> (level_bits + (8 - EFFECTIVE_LEVEL_PRIORITY_BITS))) as u8; 375 | 376 | // Enum contains all values from 0-15 377 | unsafe { core::mem::transmute(priority & 0xF) } 378 | } 379 | } 380 | -------------------------------------------------------------------------------- /src/eclic/mode.rs: -------------------------------------------------------------------------------- 1 | #![allow(named_asm_labels)] 2 | 3 | use core::arch::global_asm; 4 | 5 | global_asm!( 6 | ".equ LOG_REGBYTES, 2", 7 | ".equ REGBYTES, 1 << LOG_REGBYTES", 8 | ".equ MSTATUS_MIE, 0x00000008", 9 | // 10 | ".macro DISABLE_MIE", 11 | "csrc mstatus, MSTATUS_MIE", 12 | ".endm", 13 | // 14 | ".macro SAVE_CONTEXT", 15 | // 16 | "addi sp, sp, -20*REGBYTES", 17 | // 18 | "sw x1, 0*REGBYTES(sp)", 19 | "sw x4, 1*REGBYTES(sp)", 20 | "sw x5, 2*REGBYTES(sp)", 21 | "sw x6, 3*REGBYTES(sp)", 22 | "sw x7, 4*REGBYTES(sp)", 23 | "sw x10, 5*REGBYTES(sp)", 24 | "sw x11, 6*REGBYTES(sp)", 25 | "sw x12, 7*REGBYTES(sp)", 26 | "sw x13, 8*REGBYTES(sp)", 27 | "sw x14, 9*REGBYTES(sp)", 28 | "sw x15, 10*REGBYTES(sp)", 29 | "sw x16, 11*REGBYTES(sp)", 30 | "sw x17, 12*REGBYTES(sp)", 31 | "sw x28, 13*REGBYTES(sp)", 32 | "sw x29, 14*REGBYTES(sp)", 33 | "sw x30, 15*REGBYTES(sp)", 34 | "sw x31, 16*REGBYTES(sp)", 35 | ".endm", 36 | // 37 | ".macro RESTORE_CONTEXT", 38 | "lw x1, 0*REGBYTES(sp)", 39 | "lw x4, 1*REGBYTES(sp)", 40 | "lw x5, 2*REGBYTES(sp)", 41 | "lw x6, 3*REGBYTES(sp)", 42 | "lw x7, 4*REGBYTES(sp)", 43 | "lw x10, 5*REGBYTES(sp)", 44 | "lw x11, 6*REGBYTES(sp)", 45 | "lw x12, 7*REGBYTES(sp)", 46 | "lw x13, 8*REGBYTES(sp)", 47 | "lw x14, 9*REGBYTES(sp)", 48 | "lw x15, 10*REGBYTES(sp)", 49 | "lw x16, 11*REGBYTES(sp)", 50 | "lw x17, 12*REGBYTES(sp)", 51 | "lw x28, 13*REGBYTES(sp)", 52 | "lw x29, 14*REGBYTES(sp)", 53 | "lw x30, 15*REGBYTES(sp)", 54 | "lw x31, 16*REGBYTES(sp)", 55 | // De-allocate the stack space 56 | "addi sp, sp, 20*REGBYTES", 57 | ".endm", 58 | 59 | // 60 | ".section .text.irq", 61 | ".option push", 62 | ".option norelax", 63 | ".align 2", 64 | ".option pop", 65 | ".global _irq_handler", 66 | "_irq_handler:", 67 | "SAVE_CONTEXT", 68 | // 69 | // The special CSR read operation, which actually uses mcause as operand 70 | // to directly store it to memory 71 | "csrrwi x0, 0x7ee, 17", 72 | // The special CSR read operation, which actually uses mepc as operand 73 | // to directly store it to memory 74 | "csrrwi x0, 0x7ef, 18", 75 | // The special CSR read operation, which actually uses msubm as operand 76 | // to directly store it to memory 77 | "csrrwi x0, 0x7eb, 19", 78 | // 79 | // The special CSR read/write operation, which is actually Claim the CLIC to 80 | // find its pending highest ID, if the ID is not 0, then automatically enable 81 | // the mstatus.MIE, and jump to its vector-entry-label, and update the link register. 82 | "csrrw ra, 0x7ed, ra", 83 | // 84 | "DISABLE_MIE", 85 | // 86 | "lw x5, 19*REGBYTES(sp)", 87 | // Load x5 value into MSUBM system status register 88 | "csrw 0x7c4, x5", 89 | "lw x5, 18*REGBYTES(sp)", 90 | "csrw mepc, x5", 91 | "lw x5, 17*REGBYTES(sp)", 92 | "csrw mcause, x5", 93 | // 94 | "RESTORE_CONTEXT", 95 | // 96 | "mret", 97 | // 98 | 99 | ".section .trap, \"ax\"", 100 | ".option push", 101 | ".option norelax", 102 | ".align 6", 103 | ".option pop", 104 | ".global _start_trap", 105 | "_start_trap:", 106 | "addi sp, sp, -16*REGBYTES", 107 | // 108 | "sw ra, 0*REGBYTES(sp)", 109 | "sw t0, 1*REGBYTES(sp)", 110 | "sw t1, 2*REGBYTES(sp)", 111 | "sw t2, 3*REGBYTES(sp)", 112 | "sw t3, 4*REGBYTES(sp)", 113 | "sw t4, 5*REGBYTES(sp)", 114 | "sw t5, 6*REGBYTES(sp)", 115 | "sw t6, 7*REGBYTES(sp)", 116 | "sw a0, 8*REGBYTES(sp)", 117 | "sw a1, 9*REGBYTES(sp)", 118 | "sw a2, 10*REGBYTES(sp)", 119 | "sw a3, 11*REGBYTES(sp)", 120 | "sw a4, 12*REGBYTES(sp)", 121 | "sw a5, 13*REGBYTES(sp)", 122 | "sw a6, 14*REGBYTES(sp)", 123 | "sw a7, 15*REGBYTES(sp)", 124 | // 125 | "add a0, sp, zero", 126 | "jal ra, _start_trap_rust", 127 | // 128 | "lw ra, 0*REGBYTES(sp)", 129 | "lw t0, 1*REGBYTES(sp)", 130 | "lw t1, 2*REGBYTES(sp)", 131 | "lw t2, 3*REGBYTES(sp)", 132 | "lw t3, 4*REGBYTES(sp)", 133 | "lw t4, 5*REGBYTES(sp)", 134 | "lw t5, 6*REGBYTES(sp)", 135 | "lw t6, 7*REGBYTES(sp)", 136 | "lw a0, 8*REGBYTES(sp)", 137 | "lw a1, 9*REGBYTES(sp)", 138 | "lw a2, 10*REGBYTES(sp)", 139 | "lw a3, 11*REGBYTES(sp)", 140 | "lw a4, 12*REGBYTES(sp)", 141 | "lw a5, 13*REGBYTES(sp)", 142 | "lw a6, 14*REGBYTES(sp)", 143 | "lw a7, 15*REGBYTES(sp)", 144 | // 145 | "addi sp, sp, 16*REGBYTES", 146 | "mret", 147 | // 148 | 149 | ".section .text", 150 | ".global _setup_interrupts", 151 | "_setup_interrupts:", 152 | // Set the the NMI base to share with mtvec by setting CSR_MMISC_CTL 153 | "li t0, 0x200", 154 | "csrs 0x7d0, t0", 155 | // 156 | // Set the mtvt 157 | "la t0, vectors", 158 | "csrw 0x307, t0", 159 | // 160 | // Set the mtvt2 and enable it 161 | "la t0, _irq_handler", 162 | "csrw 0x7ec, t0", 163 | "csrs 0x7ec, 0x1", 164 | // 165 | // Enable ECLIC and set trap handler 166 | "la t0, _start_trap", 167 | "andi t0, t0, -64", 168 | "ori t0, t0, 3", 169 | "csrw mtvec, t0", 170 | // 171 | "ret", 172 | options(raw), 173 | ); 174 | 175 | unsafe extern "C" { 176 | fn INT_SFT(); 177 | fn INT_TMR(); 178 | fn INT_BWEI(); 179 | fn INT_PMOVI(); 180 | fn WWDGT(); 181 | fn EXTI_LVD(); 182 | fn TAMPER(); 183 | fn RTC(); 184 | fn FMC(); 185 | fn RCU(); 186 | fn EXTI_LINE0(); 187 | fn EXTI_LINE1(); 188 | fn EXTI_LINE2(); 189 | fn EXTI_LINE3(); 190 | fn EXTI_LINE4(); 191 | fn DMA0_CHANNEL0(); 192 | fn DMA0_CHANNEL1(); 193 | fn DMA0_CHANNEL2(); 194 | fn DMA0_CHANNEL3(); 195 | fn DMA0_CHANNEL4(); 196 | fn DMA0_CHANNEL5(); 197 | fn DMA0_CHANNEL6(); 198 | fn ADC0_1(); 199 | fn CAN0_TX(); 200 | fn CAN0_RX0(); 201 | fn CAN0_RX1(); 202 | fn CAN0_EWMC(); 203 | fn EXTI_LINE9_5(); 204 | fn TIMER0_BRK(); 205 | fn TIMER0_UP(); 206 | fn TIMER0_TRG_CMT(); 207 | fn TIMER0_CHANNEL(); 208 | fn TIMER1(); 209 | fn TIMER2(); 210 | fn TIMER3(); 211 | fn I2C0_EV(); 212 | fn I2C0_ER(); 213 | fn I2C1_EV(); 214 | fn I2C1_ER(); 215 | fn SPI0(); 216 | fn SPI1(); 217 | fn USART0(); 218 | fn USART1(); 219 | fn USART2(); 220 | fn EXTI_LINE15_10(); 221 | fn RTC_ALARM(); 222 | fn USBFS_WKUP(); 223 | fn EXMC(); // not present in Reference Manual but present in vendor HAL 224 | fn TIMER4(); 225 | fn SPI2(); 226 | fn UART3(); 227 | fn UART4(); 228 | fn TIMER5(); 229 | fn TIMER6(); 230 | fn DMA1_CHANNEL0(); 231 | fn DMA1_CHANNEL1(); 232 | fn DMA1_CHANNEL2(); 233 | fn DMA1_CHANNEL3(); 234 | fn DMA1_CHANNEL4(); 235 | fn CAN1_TX(); 236 | fn CAN1_RX0(); 237 | fn CAN1_RX1(); 238 | fn CAN1_EWMC(); 239 | fn USBFS(); 240 | } 241 | #[doc(hidden)] 242 | #[repr(C)] 243 | pub union Vector { 244 | _handler: unsafe extern "C" fn(), 245 | _reserved: u32, 246 | } 247 | 248 | #[doc(hidden)] 249 | #[repr(C)] 250 | #[repr(align(512))] 251 | pub struct Align512(T); 252 | 253 | #[doc(hidden)] 254 | #[unsafe(link_section = ".text.vectors")] 255 | #[unsafe(no_mangle)] 256 | pub static vectors: Align512<[Vector; 87]> = Align512([ 257 | Vector { _reserved: 0 }, 258 | Vector { _reserved: 0 }, 259 | Vector { _reserved: 0 }, 260 | Vector { _handler: INT_SFT }, 261 | Vector { _reserved: 0 }, 262 | Vector { _reserved: 0 }, 263 | Vector { _reserved: 0 }, 264 | Vector { _handler: INT_TMR }, 265 | Vector { _reserved: 0 }, 266 | Vector { _reserved: 0 }, 267 | Vector { _reserved: 0 }, 268 | Vector { _reserved: 0 }, 269 | Vector { _reserved: 0 }, 270 | Vector { _reserved: 0 }, 271 | Vector { _reserved: 0 }, 272 | Vector { _reserved: 0 }, 273 | Vector { _reserved: 0 }, 274 | Vector { _handler: INT_BWEI }, 275 | Vector { _handler: INT_PMOVI }, 276 | Vector { _handler: WWDGT }, 277 | Vector { _handler: EXTI_LVD }, 278 | Vector { _handler: TAMPER }, 279 | Vector { _handler: RTC }, 280 | Vector { _handler: FMC }, 281 | Vector { _handler: RCU }, 282 | Vector { _handler: EXTI_LINE0 }, 283 | Vector { _handler: EXTI_LINE1 }, 284 | Vector { _handler: EXTI_LINE2 }, 285 | Vector { _handler: EXTI_LINE3 }, 286 | Vector { _handler: EXTI_LINE4 }, 287 | Vector { _handler: DMA0_CHANNEL0 }, 288 | Vector { _handler: DMA0_CHANNEL1 }, 289 | Vector { _handler: DMA0_CHANNEL2 }, 290 | Vector { _handler: DMA0_CHANNEL3 }, 291 | Vector { _handler: DMA0_CHANNEL4 }, 292 | Vector { _handler: DMA0_CHANNEL5 }, 293 | Vector { _handler: DMA0_CHANNEL6 }, 294 | Vector { _handler: ADC0_1 }, 295 | Vector { _handler: CAN0_TX }, 296 | Vector { _handler: CAN0_RX0 }, 297 | Vector { _handler: CAN0_RX1 }, 298 | Vector { _handler: CAN0_EWMC }, 299 | Vector { _handler: EXTI_LINE9_5 }, 300 | Vector { _handler: TIMER0_BRK }, 301 | Vector { _handler: TIMER0_UP }, 302 | Vector { _handler: TIMER0_TRG_CMT }, 303 | Vector { _handler: TIMER0_CHANNEL }, 304 | Vector { _handler: TIMER1 }, 305 | Vector { _handler: TIMER2 }, 306 | Vector { _handler: TIMER3 }, 307 | Vector { _handler: I2C0_EV }, 308 | Vector { _handler: I2C0_ER }, 309 | Vector { _handler: I2C1_EV }, 310 | Vector { _handler: I2C1_ER }, 311 | Vector { _handler: SPI0 }, 312 | Vector { _handler: SPI1 }, 313 | Vector { _handler: USART0 }, 314 | Vector { _handler: USART1 }, 315 | Vector { _handler: USART2 }, 316 | Vector { _handler: EXTI_LINE15_10 }, 317 | Vector { _handler: RTC_ALARM }, 318 | Vector { _handler: USBFS_WKUP }, 319 | Vector { _reserved: 0 }, 320 | Vector { _reserved: 0 }, 321 | Vector { _reserved: 0 }, 322 | Vector { _reserved: 0 }, 323 | Vector { _reserved: 0 }, 324 | Vector { _handler: EXMC }, // not present in Reference Manual but present in vendor HAL 325 | Vector { _reserved: 0 }, 326 | Vector { _handler: TIMER4 }, 327 | Vector { _handler: SPI2 }, 328 | Vector { _handler: UART3 }, 329 | Vector { _handler: UART4 }, 330 | Vector { _handler: TIMER5 }, 331 | Vector { _handler: TIMER6 }, 332 | Vector { _handler: DMA1_CHANNEL0 }, 333 | Vector { _handler: DMA1_CHANNEL1 }, 334 | Vector { _handler: DMA1_CHANNEL2 }, 335 | Vector { _handler: DMA1_CHANNEL3 }, 336 | Vector { _handler: DMA1_CHANNEL4 }, 337 | Vector { _reserved: 0 }, 338 | Vector { _reserved: 0 }, 339 | Vector { _handler: CAN1_TX }, 340 | Vector { _handler: CAN1_RX0 }, 341 | Vector { _handler: CAN1_RX1 }, 342 | Vector { _handler: CAN1_EWMC }, 343 | Vector { _handler: USBFS }, 344 | ]); -------------------------------------------------------------------------------- /src/pwm.rs: -------------------------------------------------------------------------------- 1 | //! Pulse width modulation 2 | 3 | use embedded_hal::Pwm; 4 | use gd32vf103_pac::{TIMER0, TIMER1, TIMER2, TIMER3, TIMER4}; 5 | 6 | use crate::afio::{Afio, Remap}; 7 | use crate::gpio::gpioa::*; 8 | use crate::gpio::gpiob::*; 9 | use crate::gpio::gpioc::*; 10 | use crate::gpio::gpiod::*; 11 | use crate::gpio::gpioe::*; 12 | use crate::gpio::{Alternate, PushPull}; 13 | use crate::rcu::{BaseFrequency, Enable, Rcu, Reset}; 14 | use crate::time::{Hertz, U32Ext}; 15 | 16 | use core::marker::PhantomData; 17 | 18 | pub trait Pins { 19 | fn remap(&self) -> Remap; 20 | } 21 | 22 | macro_rules! pwm_pin { 23 | ($timer:ty, $($remap:ident: [ 24 | ch0:$ch0_pin:ident, ch1:$ch1_pin:ident, 25 | ch2:$ch2_pin:ident, ch3:$ch3_pin:ident 26 | ],)+ ) => { 27 | $( 28 | impl Pins<$timer, $remap> for (Option<&$ch0_pin>>, 29 | Option<&$ch1_pin>>, 30 | Option<&$ch2_pin>>, 31 | Option<&$ch3_pin>>) 32 | { 33 | fn remap(&self) -> $remap { $remap {} } 34 | } )+ 35 | } 36 | } 37 | 38 | pwm_pin! { 39 | TIMER0, 40 | NoRemap: [ ch0: PA8, ch1: PA9, ch2: PA10, ch3: PA11 ], 41 | PartialRemap1: [ ch0: PA8, ch1: PA9, ch2: PA10, ch3: PA11 ], 42 | FullRemap: [ ch0: PE9, ch1: PE11, ch2: PE13, ch3: PE14 ], 43 | } 44 | 45 | pwm_pin! { 46 | TIMER1, 47 | NoRemap: [ ch0: PA0, ch1: PA1, ch2: PA2, ch3: PA3 ], 48 | PartialRemap1: [ ch0: PA15, ch1: PB3, ch2: PA2, ch3: PA3 ], 49 | PartialRemap2: [ ch0: PA0, ch1: PA1, ch2: PB10, ch3: PB11 ], 50 | FullRemap: [ ch0: PA15, ch1: PB3, ch2: PB10, ch3: PB11 ], 51 | } 52 | 53 | pwm_pin! { 54 | TIMER2, 55 | NoRemap: [ ch0: PA6, ch1: PA7, ch2: PB0, ch3: PB1 ], 56 | PartialRemap2: [ ch0: PB4, ch1: PB5, ch2: PB0, ch3: PB1 ], 57 | FullRemap: [ ch0: PC6, ch1: PC7, ch2: PC8, ch3: PC9 ], 58 | } 59 | 60 | pwm_pin! { 61 | TIMER3, 62 | NoRemap: [ ch0: PB6, ch1: PB7, ch2: PB8, ch3: PB9 ], 63 | FullRemap: [ ch0: PD12, ch1: PD13, ch2: PD14, ch3: PD15 ], 64 | } 65 | 66 | pwm_pin! { 67 | TIMER4, 68 | NoRemap: [ ch0: PA0, ch1: PA1, ch2: PA2, ch3: PA3 ], 69 | } 70 | 71 | /// Type [`NoRemap`] represent the timers *no remap mode*, which correspond for 72 | /// TIMER0, TIMER1 and TIMER2 to `0b00`; For TIMER3 and TIMER4 correspond 73 | /// to `0b0`. 74 | pub struct NoRemap; 75 | 76 | /// Type [`PartialRemap1`] represent the timers *partial remap* mode, which 77 | /// correspond for TIMER0, TIMER1 to `0b01`. 78 | pub struct PartialRemap1; 79 | 80 | /// Type [`PartialRemap2`] represent the timers *partial remap* mode, which 81 | /// correspond for TIMER1 and TIMER2 to `0b10`, 82 | pub struct PartialRemap2; 83 | 84 | /// Type [`FullRemap`] represent the timers *full remap* mode, which correspond 85 | /// for TIMER0, TIMER1, TIMER2 to `0b11`; For TIMER3 and TIMER4 to `0b1`. 86 | pub struct FullRemap; 87 | 88 | impl From for bool { 89 | fn from(_v: NoRemap) -> Self { 90 | false 91 | } 92 | } 93 | 94 | impl From for u8 { 95 | fn from(_v: NoRemap) -> Self { 96 | 0b00 97 | } 98 | } 99 | 100 | impl From for bool { 101 | fn from(_v: FullRemap) -> Self { 102 | true 103 | } 104 | } 105 | 106 | impl From for u8 { 107 | fn from(_v: FullRemap) -> Self { 108 | 0b11 109 | } 110 | } 111 | 112 | impl From for u8 { 113 | fn from(_v: PartialRemap1) -> Self { 114 | 0b01 115 | } 116 | } 117 | 118 | impl From for u8 { 119 | fn from(_v: PartialRemap2) -> Self { 120 | 0b11 121 | } 122 | } 123 | 124 | /// PWM TIMER configuration. 125 | /// 126 | /// To create a new PWM TIMER you should choose a timer and a REMAP mode from 127 | /// [`NoRemap`], [`PartialRemap1`], [`PartialRemap2`] or [`FullRemap`]. 128 | /// The tuple provided should contains only the pins you will use. 129 | /// 130 | /// ```no_run 131 | /// use gd32vf103xx_hal as hal; 132 | /// use hal::pac::{Peripherals, TIMER0}; 133 | /// use hal::gpio::GpioExt; 134 | /// use hal::rcu::RcuExt; 135 | /// use hal::time::U32Ext; 136 | /// use hal::afio::AfioExt; 137 | /// use hal::pwm::{PwmTimer, Channel, NoRemap}; 138 | /// use embedded_hal::Pwm; 139 | /// 140 | /// // ... 141 | /// let dp = Peripherals::take().unwrap(); 142 | /// let mut rcu = dp.RCU.configure().freeze(); 143 | /// 144 | /// let gpioa = dp.GPIOA.split(&mut rcu); 145 | /// let pa9 = gpioa.pa9.into_alternate_push_pull(); 146 | /// let pa10 = gpioa.pa10.into_alternate_push_pull(); 147 | /// 148 | /// let mut afio = dp.AFIO.constrain(&mut rcu); 149 | /// let timer0 = dp.TIMER0; 150 | 151 | /// let mut pwm_t0 = PwmTimer::::new( 152 | /// timer0, (None, Some(&pa9), Some(&pa10), None), &mut rcu, &mut afio); 153 | /// 154 | /// let max = pwm_t0.get_max_duty(); 155 | /// pwm_t0.set_period(100.hz()); 156 | /// pwm_t0.set_duty(Channel::CH0, max / 4); // 25% duty cycle 157 | /// pwm_t0.enable(Channel::CH0); 158 | /// 159 | /// ``` 160 | pub struct PwmTimer { 161 | timer: TIMER, 162 | timer_clock: Hertz, 163 | max_duty_cycle: u16, 164 | period: Hertz, 165 | duty: [u16; 4], 166 | _remap: PhantomData, 167 | } 168 | 169 | #[derive(Copy, Clone)] 170 | pub enum Channel { 171 | CH0, 172 | CH1, 173 | CH2, 174 | CH3, 175 | } 176 | 177 | macro_rules! advanced_pwm_timer { 178 | ($TIM:ident: $tim:ident) => { 179 | impl> PwmTimer<$TIM, REMAP> { 180 | pub fn new( 181 | timer: $TIM, 182 | pins: impl Pins<$TIM, REMAP>, 183 | rcu: &mut Rcu, 184 | afio: &mut Afio, 185 | ) -> PwmTimer<$TIM, REMAP> { 186 | <$TIM>::remap(afio, pins.remap().into()); 187 | 188 | $TIM::enable(rcu); 189 | $TIM::reset(rcu); 190 | 191 | /* Advanced TIMER implements a BREAK function that deactivates 192 | * the outputs. This bit automatically activates the output when 193 | * no break input is present */ 194 | timer.cchp.modify(|_, w| w.oaen().set_bit()); 195 | 196 | PwmTimer { 197 | timer, 198 | timer_clock: $TIM::base_frequency(rcu), 199 | max_duty_cycle: u16::MAX, 200 | period: 0.hz(), 201 | duty: [0u16; 4], 202 | _remap: PhantomData::, 203 | } 204 | } 205 | } 206 | 207 | pwm_timer!($TIM: $tim); 208 | }; 209 | } 210 | 211 | macro_rules! general_pwm_timer { 212 | ($TIM:ident: $tim:ident) => { 213 | impl + Into> PwmTimer<$TIM, REMAP> { 214 | pub fn new( 215 | timer: $TIM, 216 | pins: impl Pins<$TIM, REMAP>, 217 | rcu: &mut Rcu, 218 | afio: &mut Afio, 219 | ) -> PwmTimer<$TIM, REMAP> { 220 | <$TIM>::remap(afio, pins.remap().into()); 221 | 222 | $TIM::enable(rcu); 223 | $TIM::reset(rcu); 224 | 225 | PwmTimer { 226 | timer, 227 | timer_clock: $TIM::base_frequency(rcu), 228 | max_duty_cycle: u16::MAX, 229 | period: 0.hz(), 230 | duty: [0u16; 4], 231 | _remap: PhantomData::, 232 | } 233 | } 234 | } 235 | 236 | pwm_timer!($TIM: $tim); 237 | }; 238 | } 239 | 240 | macro_rules! pwm_timer { 241 | ($TIM:ident: $tim:ident) => { 242 | impl Pwm for PwmTimer<$TIM, REMAP> { 243 | type Channel = Channel; 244 | type Time = Hertz; 245 | type Duty = u16; 246 | 247 | fn disable(&mut self, channel: Self::Channel) { 248 | match channel { 249 | Channel::CH0 => self.timer.chctl2.modify(|_r, w| w.ch0en().clear_bit()), 250 | Channel::CH1 => self.timer.chctl2.modify(|_r, w| w.ch1en().clear_bit()), 251 | Channel::CH2 => self.timer.chctl2.modify(|_r, w| w.ch2en().clear_bit()), 252 | Channel::CH3 => self.timer.chctl2.modify(|_r, w| w.ch3en().clear_bit()), 253 | } 254 | } 255 | 256 | fn enable(&mut self, channel: Self::Channel) { 257 | match channel { 258 | Channel::CH0 => self.timer.chctl2.modify(|_r, w| w.ch0en().set_bit()), 259 | Channel::CH1 => self.timer.chctl2.modify(|_r, w| w.ch1en().set_bit()), 260 | Channel::CH2 => self.timer.chctl2.modify(|_r, w| w.ch2en().set_bit()), 261 | Channel::CH3 => self.timer.chctl2.modify(|_r, w| w.ch3en().set_bit()), 262 | } 263 | } 264 | 265 | fn get_period(&self) -> Self::Time { 266 | return self.period; 267 | } 268 | 269 | fn get_duty(&self, channel: Self::Channel) -> Self::Duty { 270 | self.duty[channel as usize] 271 | } 272 | 273 | fn get_max_duty(&self) -> Self::Duty { 274 | self.max_duty_cycle 275 | } 276 | 277 | fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { 278 | let mut duty = duty; 279 | if duty > self.max_duty_cycle { 280 | duty = self.max_duty_cycle 281 | } 282 | self.duty[channel as usize] = duty; 283 | match channel { 284 | Channel::CH0 => self.timer.ch0cv.write(|w| unsafe { w.bits(duty) }), 285 | Channel::CH1 => self.timer.ch1cv.write(|w| unsafe { w.bits(duty) }), 286 | Channel::CH2 => self.timer.ch2cv.write(|w| unsafe { w.bits(duty) }), 287 | Channel::CH3 => self.timer.ch3cv.write(|w| unsafe { w.bits(duty) }), 288 | } 289 | } 290 | 291 | fn set_period

(&mut self, period: P) 292 | where 293 | P: Into, 294 | { 295 | self.timer.ctl0.modify(|_, w| w.cen().clear_bit()); 296 | self.timer.cnt.reset(); 297 | 298 | let freq = period.into(); 299 | 300 | let ticks = self.timer_clock.0 / freq.0; 301 | let psc = ((ticks - 1) / (1 << 16)) as u16; 302 | let car = (ticks / ((psc + 1) as u32)) as u16; 303 | 304 | self.max_duty_cycle = car; 305 | self.period = freq; 306 | 307 | self.timer.psc.write(|w| unsafe { w.bits(psc) }); 308 | self.timer.car.write(|w| unsafe { w.bits(car) }); 309 | 310 | self.timer.chctl0_output().modify(|_r, w| unsafe { 311 | w 312 | // Enable PWM Mode 0 for channel 0 and 1 313 | .ch0comctl().bits(0b110) 314 | .ch1comctl().bits(0b110) 315 | 316 | // Output mode for channel 0 and 1 317 | .ch0ms().bits(0b00) 318 | .ch1ms().bits(0b00) 319 | }); 320 | self.timer.chctl1_output().modify(|_r, w| unsafe { 321 | w 322 | // Enable PWM Mode 0 for channel 2 and 3 323 | .ch2comctl().bits(0b110) 324 | .ch3comctl().bits(0b110) 325 | 326 | // Output mode for channel 2 and 3 327 | .ch2ms().bits(0b00) 328 | .ch3ms().bits(0b00) 329 | }); 330 | 331 | // Enable the timer 332 | self.timer.ctl0.write(|w| { 333 | w 334 | .updis().clear_bit() 335 | .cen().set_bit() 336 | }); 337 | } 338 | } 339 | }; 340 | } 341 | 342 | advanced_pwm_timer! {TIMER0: timer0} 343 | general_pwm_timer! {TIMER1: timer1} 344 | general_pwm_timer! {TIMER2: timer2} 345 | general_pwm_timer! {TIMER3: timer3} 346 | general_pwm_timer! {TIMER4: timer4} 347 | -------------------------------------------------------------------------------- /src/rcu.rs: -------------------------------------------------------------------------------- 1 | //! Reset and clock unit 2 | 3 | use crate::pac::RCU; 4 | use riscv::interrupt; 5 | use crate::time::Hertz; 6 | use core::cmp; 7 | 8 | 9 | /// Extension trait that sets up the `RCU` peripheral 10 | pub trait RcuExt { 11 | /// Configure the clocks of the `RCU` peripheral 12 | fn configure(self) -> UnconfiguredRcu; 13 | } 14 | 15 | impl RcuExt for RCU { 16 | fn configure(self) -> UnconfiguredRcu { 17 | UnconfiguredRcu::new(self) 18 | } 19 | } 20 | 21 | /// Configured RCU peripheral 22 | pub struct Rcu { 23 | /// Frozen clock frequencies 24 | pub clocks: Clocks, 25 | pub(crate) regs: RCU, 26 | } 27 | 28 | pub struct UnconfiguredRcu { 29 | hxtal: Option, 30 | sysclk: Option, 31 | regs: RCU, 32 | } 33 | 34 | impl UnconfiguredRcu { 35 | fn new(rcu: RCU) -> Self { 36 | Self { 37 | hxtal: None, 38 | sysclk: None, 39 | regs: rcu, 40 | } 41 | } 42 | 43 | /// Uses an external oscillator instead of IRC8M (internal RC oscillator) as the high-speed 44 | /// clock source. Will result in a hang if an external oscillator is not connected or it fails 45 | /// to start. 46 | pub fn ext_hf_clock(mut self, freq: impl Into) -> Self { 47 | let freq = freq.into().0; 48 | assert!(4_000_000 <= freq && freq <= 32_000_000); 49 | 50 | self.hxtal = Some(freq); 51 | self 52 | } 53 | 54 | /// Sets the desired frequency for the SYSCLK clock 55 | pub fn sysclk(mut self, freq: impl Into) -> Self { 56 | let freq = freq.into().0; 57 | assert!(freq <= 108_000_000); 58 | 59 | self.sysclk = Some(freq); 60 | self 61 | } 62 | 63 | /// Freezes clock configuration, making it effective 64 | pub fn freeze(self) -> Rcu { 65 | const IRC8M: u32 = 8_000_000; 66 | 67 | let target_sysclk = self.sysclk.unwrap_or(IRC8M); 68 | 69 | let (scs_bits, use_pll) = match (self.hxtal, target_sysclk) { 70 | (Some(freq), sysclk) if freq == sysclk => (0b01, false), 71 | (None, sysclk) if IRC8M == sysclk => (0b00, false), 72 | _ => (0b10, true), 73 | }; 74 | 75 | let pllsel_bit; 76 | let predv0_bits; 77 | let pllmf_bits; 78 | if use_pll { 79 | let pllmf; 80 | 81 | if let Some(hxtal_freq) = self.hxtal { 82 | // Use external clock + divider 83 | pllsel_bit = true; 84 | 85 | let calculate_pll = |source: u32, target: u32| -> Option<(u8, u8)> { 86 | const PLL_IN_MIN: u32 = 600_000; 87 | let div_max = cmp::min(16, source / PLL_IN_MIN); 88 | 89 | for d in 1..=div_max { 90 | let pllsource = source / d; 91 | let pllm = target / pllsource; 92 | if pllm < 2 || pllm == 15 || pllm > 32{ 93 | continue; 94 | } 95 | let actual_freq = pllsource * pllm; 96 | if actual_freq == target { 97 | return Some((d as u8, pllm as u8)); 98 | } 99 | } 100 | None 101 | }; 102 | 103 | let (d, m) = calculate_pll(hxtal_freq, target_sysclk).expect("invalid sysclk value"); 104 | predv0_bits = d - 1; 105 | pllmf = m; 106 | } else { 107 | // IRC8M/2 is used as an input clock 108 | pllsel_bit = false; 109 | 110 | let pllsource = IRC8M / 2; 111 | let m = target_sysclk / pllsource; 112 | let m = cmp::max(2, cmp::min(m, 32)); 113 | assert_ne!(m, 15, "invalid sysclk value"); 114 | let actual_sysclk = pllsource * m; 115 | assert_eq!(target_sysclk, actual_sysclk, "invalid sysclk value"); 116 | 117 | predv0_bits = 0; 118 | pllmf = m as u8; 119 | } 120 | 121 | pllmf_bits = match pllmf { 122 | 2..=14 => pllmf - 2, 123 | 16..=32 => pllmf - 1, 124 | _ => unreachable!("invalid pll multiplier"), 125 | }; 126 | } else { 127 | pllsel_bit = false; 128 | predv0_bits = 0; 129 | pllmf_bits = 0; 130 | } 131 | 132 | // Switch to the internal clock 133 | let rcu = unsafe { &*crate::pac::RCU::ptr() }; 134 | rcu.ctl.modify(|_, w| w.irc8men().set_bit()); // Enable IRC8M oscillator 135 | while rcu.ctl.read().irc8mstb().bit_is_clear() {} // Wait for oscillator to stabilize 136 | rcu.cfg0.modify(|_, w| unsafe { w.scs().bits(0b00) }); // Switch to the internal oscillator 137 | rcu.ctl.modify(|_, w| w.pllen().clear_bit()); // Disable PLL 138 | 139 | // Set bus prescalers 140 | rcu.cfg0.modify(|_, w| unsafe { w.ahbpsc().bits(0b0000) }); // CK_SYS 141 | rcu.cfg0.modify(|_, w| unsafe { w.apb1psc().bits(0b100) }); // CK_AHB / 2 142 | rcu.cfg0.modify(|_, w| unsafe { w.apb2psc().bits(0b000) }); // CK_AHB 143 | let apb1_psc = 2; 144 | let apb2_psc = 1; 145 | 146 | if self.hxtal.is_some() { 147 | // Enable external oscillator 148 | rcu.ctl.modify(|_, w| w.hxtalen().set_bit()); 149 | // Wait for oscillator to stabilize 150 | while rcu.ctl.read().hxtalstb().bit_is_clear() {} 151 | 152 | // Select HXTAL as prescaler input source clock 153 | rcu.cfg1.modify(|_, w| w.predv0sel().clear_bit()); 154 | // Configure the prescaler 155 | rcu.cfg1.modify(|_, w| unsafe { w.predv0().bits(predv0_bits) }); 156 | } 157 | 158 | if use_pll { 159 | // Configure PLL input selector 160 | rcu.cfg0.modify(|_, w| w.pllsel().bit(pllsel_bit)); 161 | // Configure PLL multiplier 162 | rcu.cfg0.modify(|_, w| unsafe { w 163 | .pllmf_4().bit(pllmf_bits & 0x10 != 0) 164 | .pllmf_3_0().bits(pllmf_bits & 0xf) 165 | }); 166 | // Enable PLL 167 | rcu.ctl.modify(|_, w| w.pllen().set_bit()); 168 | // Wait for PLL to stabilize 169 | while rcu.ctl.read().pllstb().bit_is_clear() {} 170 | } else { 171 | // Disable PLL 172 | rcu.ctl.modify(|_, w| w.pllen().clear_bit()); 173 | } 174 | 175 | // Switch to the configured clock source 176 | rcu.cfg0.modify(|_, w| unsafe { w.scs().bits(scs_bits) }); 177 | 178 | let usbclk_valid; 179 | if use_pll { 180 | let pllclk = target_sysclk; 181 | let (valid, pr) = match pllclk { 182 | 48_000_000 => (true, 0b01), // pllclk / 1 183 | 72_000_000 => (true, 0b00), // pllclk / 1.5 184 | 96_000_000 => (true, 0b11), // pllclk / 2 185 | _ => (false, 0), 186 | }; 187 | usbclk_valid = valid; 188 | 189 | // Configure USB prescaler 190 | rcu.cfg0.modify(|_, w| unsafe { w.usbfspsc().bits(pr) }); 191 | } else { 192 | usbclk_valid = false; 193 | } 194 | 195 | let clocks = Clocks { 196 | sysclk: Hertz(target_sysclk), 197 | apb1_psc, 198 | apb2_psc, 199 | usbclk_valid 200 | }; 201 | 202 | Rcu { 203 | clocks, 204 | regs: self.regs 205 | } 206 | } 207 | } 208 | 209 | #[derive(Copy, Clone)] 210 | pub struct Clocks { 211 | sysclk: Hertz, 212 | apb1_psc: u8, 213 | apb2_psc: u8, 214 | usbclk_valid: bool, 215 | } 216 | 217 | impl Clocks { 218 | /// Returns the system (core) frequency 219 | pub const fn sysclk(&self) -> Hertz { 220 | self.sysclk 221 | } 222 | 223 | /// Returns the frequency of the AHB 224 | pub const fn hclk(&self) -> Hertz { 225 | self.sysclk 226 | } 227 | 228 | /// Returns the frequency of the APB1 229 | pub const fn pclk1(&self) -> Hertz { 230 | Hertz(self.sysclk.0 / self.apb1_psc as u32) 231 | } 232 | 233 | /// Returns the frequency of the APB2 234 | pub const fn pclk2(&self) -> Hertz { 235 | Hertz(self.sysclk.0 / self.apb2_psc as u32) 236 | } 237 | 238 | /// Returns the frequency of the SysTick timer 239 | pub const fn systick(&self) -> Hertz { 240 | Hertz(self.sysclk.0 / 4) 241 | } 242 | 243 | /// Returns the frequency of the TIMER0 base clock 244 | pub fn timer0(&self) -> Hertz { 245 | let pclk2 = self.pclk2(); 246 | if self.apb2_psc == 1 { 247 | pclk2 248 | } else { 249 | Hertz(pclk2.0 * 2) 250 | } 251 | } 252 | 253 | /// Returns the frequency of the TIMER1..6 base clock 254 | pub fn timerx(&self) -> Hertz { 255 | let pclk1 = self.pclk1(); 256 | if self.apb1_psc == 1 { 257 | pclk1 258 | } else { 259 | Hertz(pclk1.0 * 2) 260 | } 261 | } 262 | 263 | /// Returns whether the USBCLK clock frequency is valid for the USB peripheral 264 | pub const fn usbclk_valid(&self) -> bool { 265 | self.usbclk_valid 266 | } 267 | } 268 | 269 | macro_rules! base_freq { 270 | ($($PER:ident => $func:ident,)+) => { 271 | $( 272 | impl BaseFrequency for crate::pac::$PER { 273 | #[inline(always)] 274 | fn base_frequency(rcu: &Rcu) -> Hertz { 275 | rcu.clocks.$func() 276 | } 277 | } 278 | )+ 279 | } 280 | } 281 | 282 | base_freq! { 283 | ADC0 => pclk2, 284 | ADC1 => pclk2, 285 | I2C0 => pclk1, 286 | I2C1 => pclk1, 287 | SPI0 => pclk2, 288 | SPI1 => pclk1, 289 | SPI2 => pclk1, 290 | TIMER0 => timer0, 291 | TIMER1 => timerx, 292 | TIMER2 => timerx, 293 | TIMER3 => timerx, 294 | TIMER4 => timerx, 295 | TIMER5 => timerx, 296 | TIMER6 => timerx, 297 | UART3 => pclk1, 298 | UART4 => pclk1, 299 | USART0 => pclk2, 300 | USART1 => pclk1, 301 | USART2 => pclk1, 302 | } 303 | 304 | pub(crate) mod closed_traits { 305 | use super::Rcu; 306 | use crate::time::Hertz; 307 | 308 | /// Enable/disable peripheral 309 | pub trait Enable { 310 | fn enable(rcu: &mut Rcu); 311 | fn disable(rcu: &mut Rcu); 312 | } 313 | 314 | /// Reset peripheral 315 | pub trait Reset { 316 | fn reset(rcu: &mut Rcu); 317 | } 318 | 319 | pub trait BaseFrequency { 320 | fn base_frequency(rcu: &Rcu) -> Hertz; 321 | } 322 | } 323 | pub(crate) use closed_traits::*; 324 | 325 | macro_rules! bus_enable { 326 | ($PER:ident => ($apben:ident, $peren:ident)) => { 327 | impl Enable for crate::pac::$PER { 328 | #[inline(always)] 329 | fn enable(rcu: &mut Rcu) { 330 | interrupt::free(|_| { 331 | rcu.regs.$apben.modify(|_, w| w.$peren().set_bit()); 332 | }); 333 | } 334 | 335 | #[inline(always)] 336 | fn disable(rcu: &mut Rcu) { 337 | interrupt::free(|_| { 338 | rcu.regs.$apben.modify(|_, w| w.$peren().clear_bit()); 339 | }); 340 | } 341 | } 342 | } 343 | } 344 | 345 | macro_rules! bus { 346 | ($($PER:ident => ($apben:ident, $apbrst:ident, $peren:ident, $perrst:ident),)+) => { 347 | $( 348 | bus_enable!($PER => ($apben, $peren)); 349 | 350 | impl Reset for crate::pac::$PER { 351 | #[inline(always)] 352 | fn reset(rcu: &mut Rcu) { 353 | interrupt::free(|_| { 354 | rcu.regs.$apbrst.modify(|_, w| w.$perrst().set_bit()); 355 | rcu.regs.$apbrst.modify(|_, w| w.$perrst().clear_bit()); 356 | }); 357 | } 358 | } 359 | )+ 360 | } 361 | } 362 | 363 | bus! { 364 | ADC0 => (apb2en, apb2rst, adc0en, adc0rst), 365 | ADC1 => (apb2en, apb2rst, adc1en, adc1rst), 366 | AFIO => (apb2en, apb2rst, afen, afrst), 367 | BKP => (apb1en, apb1rst, bkpien, bkpirst), 368 | CAN0 => (apb1en, apb1rst, can0en, can0rst), 369 | CAN1 => (apb1en, apb1rst, can1en, can1rst), 370 | DAC => (apb1en, apb1rst, dacen, dacrst), 371 | GPIOA => (apb2en, apb2rst, paen, parst), 372 | GPIOB => (apb2en, apb2rst, pben, pbrst), 373 | GPIOC => (apb2en, apb2rst, pcen, pcrst), 374 | GPIOD => (apb2en, apb2rst, pden, pdrst), 375 | GPIOE => (apb2en, apb2rst, peen, perst), 376 | I2C0 => (apb1en, apb1rst, i2c0en, i2c0rst), 377 | I2C1 => (apb1en, apb1rst, i2c1en, i2c1rst), 378 | PMU => (apb1en, apb1rst, pmuen, pmurst), 379 | SPI0 => (apb2en, apb2rst, spi0en, spi0rst), 380 | SPI1 => (apb1en, apb1rst, spi1en, spi1rst), 381 | SPI2 => (apb1en, apb1rst, spi2en, spi2rst), 382 | TIMER0 => (apb2en, apb2rst, timer0en, timer0rst), 383 | TIMER1 => (apb1en, apb1rst, timer1en, timer1rst), 384 | TIMER2 => (apb1en, apb1rst, timer2en, timer2rst), 385 | TIMER3 => (apb1en, apb1rst, timer3en, timer3rst), 386 | TIMER4 => (apb1en, apb1rst, timer4en, timer4rst), 387 | TIMER5 => (apb1en, apb1rst, timer5en, timer5rst), 388 | TIMER6 => (apb1en, apb1rst, timer6en, timer6rst), 389 | UART3 => (apb1en, apb1rst, uart3en, uart3rst), 390 | UART4 => (apb1en, apb1rst, uart4en, uart4rst), 391 | USART0 => (apb2en, apb2rst, usart0en, usart0rst), 392 | USART1 => (apb1en, apb1rst, usart1en, usart1rst), 393 | USART2 => (apb1en, apb1rst, usart2en, usart2rst), 394 | USBFS_GLOBAL => (ahben, ahbrst, usbfsen, usbfsrst), 395 | WWDGT => (apb1en, apb1rst, wwdgten, wwdgtrst), 396 | } 397 | bus_enable!(CRC => (ahben, crcen)); 398 | bus_enable!(DMA0 => (ahben, dma0en)); 399 | bus_enable!(DMA1 => (ahben, dma1en)); 400 | bus_enable!(EXMC => (ahben, exmcen)); 401 | -------------------------------------------------------------------------------- /src/serial.rs: -------------------------------------------------------------------------------- 1 | //! # Serial Communication (USART) 2 | //! This module contains the functions to utilize the USART (Universal 3 | //! synchronous asynchronous receiver transmitter) 4 | //! 5 | //! 6 | //! ## Example usage: 7 | //! ```rust 8 | //! use embedded_hal::serial::{Read, Write}; 9 | //! use nb::block; 10 | //! use gd32vf103_pac as pac; 11 | //! use gd32vf103xx_hal::afio::AfioExt; 12 | //! use gd32vf103xx_hal::gpio::GpioExt; 13 | //! use gd32vf103xx_hal::serial::{Config, Serial}; 14 | //! use gd32vf103xx_hal::time::U32Ext; 15 | //! # fn create_serial(mut rcu: gd32vf103xx_hal::rcu::Rcu) { 16 | //! // prelude: create handles to the peripherals and registers 17 | //! let p = pac::Peripherals::take().unwrap(); 18 | //! let mut afio = p.AFIO.constrain(&mut rcu); 19 | //! let mut gpioa = p.GPIOA.split(&mut rcu); 20 | //! 21 | //! // USART0 on Pins A9 and A10 22 | //! let pin_tx = gpioa.pa9; 23 | //! let pin_rx = gpioa.pa10; 24 | //! // Create an interface struct for USART0 with 9600 Baud 25 | //! let serial = Serial::new( 26 | //! p.USART0, 27 | //! (pin_tx, pin_rx), 28 | //! Config::default().baudrate(9_600.bps()), 29 | //! &mut afio, 30 | //! &mut rcu, 31 | //! ); 32 | //! 33 | //! // separate into tx and rx channels 34 | //! let (mut tx, mut rx) = serial.split(); 35 | //! 36 | //! // Write 'R' to the USART 37 | //! block!(tx.write(b'R')).ok(); 38 | //! // Receive a byte from the USART and store it in "received" 39 | //! let received = block!(rx.read()).unwrap(); 40 | //! # } 41 | //! ``` 42 | 43 | use core::marker::PhantomData; 44 | use core::ptr; 45 | 46 | use nb; 47 | use core::convert::Infallible; 48 | use embedded_hal::serial::Write; 49 | 50 | use crate::rcu::Rcu; 51 | use crate::time::{U32Ext, Bps}; 52 | use crate::afio::Afio; 53 | 54 | /// Interrupt event 55 | pub enum Event { 56 | /// New data has been received 57 | Rxne, 58 | /// New data can be sent 59 | Txe, 60 | } 61 | 62 | /// Serial error 63 | #[derive(Debug)] 64 | pub enum Error { 65 | /// Framing error 66 | Framing, 67 | /// Noise error 68 | Noise, 69 | /// RX buffer overrun 70 | Overrun, 71 | /// Parity check error 72 | Parity, 73 | #[doc(hidden)] 74 | _Extensible, 75 | } 76 | 77 | mod closed_traits { 78 | use gd32vf103_pac::{USART0, USART1, USART2, usart0::RegisterBlock}; 79 | use core::ops::Deref; 80 | use crate::rcu::{Enable, Reset, BaseFrequency}; 81 | use crate::afio::Remap; 82 | use crate::gpio::{Alternate, Floating, Input, PushPull}; 83 | use crate::gpio::gpioa::{PA10, PA2, PA3, PA9}; 84 | use crate::gpio::gpiob::{PB6, PB7, PB10, PB11}; 85 | use crate::gpio::gpioc::{PC10, PC11}; 86 | use crate::gpio::gpiod::{PD5, PD6, PD8, PD9}; 87 | 88 | pub trait UsartX : Deref + Enable + Reset + BaseFrequency + Remap { 89 | fn ptr() -> *const RegisterBlock; 90 | } 91 | 92 | pub trait Pins { 93 | const REMAP: USART::Variant; 94 | type Tx; 95 | type Rx; 96 | fn configure(self) -> (Self::Tx, Self::Rx); 97 | } 98 | 99 | macro_rules! pins { 100 | ($usart:ty, $remap_type:ty, $remap_value:expr, $tx:ident, $rx:ident) => { 101 | impl crate::serial::Pins<$usart> for ($tx, $rx) 102 | where 103 | TM: crate::gpio::Active, 104 | RM: crate::gpio::Active 105 | { 106 | const REMAP: $remap_type = $remap_value; 107 | type Tx = $tx>; 108 | type Rx = $rx>; 109 | 110 | #[inline(always)] 111 | fn configure(self) -> (Self::Tx, Self::Rx) { 112 | let tx = self.0.into_alternate_push_pull(); 113 | let rx = self.1.into_floating_input(); 114 | (tx, rx) 115 | } 116 | } 117 | } 118 | } 119 | 120 | impl UsartX for USART0 { 121 | #[inline(always)] 122 | fn ptr() -> *const RegisterBlock { 123 | USART0::ptr() 124 | } 125 | } 126 | 127 | pins!(USART0, bool, false, PA9, PA10); 128 | pins!(USART0, bool, true, PB6, PB7); 129 | 130 | impl UsartX for USART1 { 131 | #[inline(always)] 132 | fn ptr() -> *const RegisterBlock { 133 | USART1::ptr() 134 | } 135 | } 136 | 137 | pins!(USART1, bool, false, PA2, PA3); 138 | pins!(USART1, bool, true, PD5, PD6); 139 | 140 | impl UsartX for USART2 { 141 | #[inline(always)] 142 | fn ptr() -> *const RegisterBlock { 143 | USART2::ptr() 144 | } 145 | } 146 | 147 | pins!(USART2, u8, 0, PB10, PB11); 148 | pins!(USART2, u8, 1, PC10, PC11); 149 | pins!(USART2, u8, 0b11, PD8, PD9); 150 | } 151 | use closed_traits::*; 152 | 153 | 154 | pub enum Parity { 155 | ParityNone, 156 | ParityEven, 157 | ParityOdd, 158 | } 159 | 160 | pub enum StopBits { 161 | #[doc = "1 stop bit"] 162 | STOP1, 163 | #[doc = "0.5 stop bits"] 164 | STOP0P5, 165 | #[doc = "2 stop bits"] 166 | STOP2, 167 | #[doc = "1.5 stop bits"] 168 | STOP1P5, 169 | } 170 | 171 | pub struct Config { 172 | pub baudrate: Bps, 173 | pub parity: Parity, 174 | pub stopbits: StopBits, 175 | } 176 | 177 | impl Config { 178 | pub fn baudrate(mut self, baudrate: Bps) -> Self { 179 | self.baudrate = baudrate; 180 | self 181 | } 182 | 183 | pub fn parity_none(mut self) -> Self { 184 | self.parity = Parity::ParityNone; 185 | self 186 | } 187 | 188 | pub fn parity_even(mut self) -> Self { 189 | self.parity = Parity::ParityEven; 190 | self 191 | } 192 | 193 | pub fn parity_odd(mut self) -> Self { 194 | self.parity = Parity::ParityOdd; 195 | self 196 | } 197 | 198 | pub fn stopbits(mut self, stopbits: StopBits) -> Self { 199 | self.stopbits = stopbits; 200 | self 201 | } 202 | } 203 | 204 | impl Default for Config { 205 | fn default() -> Config { 206 | let baudrate = 115_200_u32.bps(); 207 | Config { 208 | baudrate, 209 | parity: Parity::ParityNone, 210 | stopbits: StopBits::STOP1, 211 | } 212 | } 213 | } 214 | 215 | /// Serial abstraction 216 | pub struct Serial { 217 | usart: USART, 218 | tx: TX, 219 | rx: RX, 220 | } 221 | 222 | /// Serial receiver 223 | pub struct Rx { 224 | _usart: PhantomData, 225 | } 226 | 227 | /// Serial transmitter 228 | pub struct Tx { 229 | _usart: PhantomData, 230 | } 231 | 232 | impl Serial 233 | { 234 | /// Configures the serial interface and creates the interface 235 | /// struct. 236 | /// 237 | /// `pins` is a tuple specifying transmit and receive pins. Current mode of 238 | /// these pins does not matter, as they are reconfigured during USART 239 | /// initialization. 240 | /// 241 | /// `config` holds UART configuration such as the baud rate of the interface. 242 | /// 243 | /// The `Serial` struct takes ownership over the `USARTx` device 244 | /// registers and the specified `PINS` 245 | /// 246 | /// `afio` and `rcu` are register handles which are passed for 247 | /// configuration. (`afio` is used to map the USART to the 248 | /// corresponding pins. `rcu` is used to reset the USART.) 249 | pub fn new( 250 | usart: USART, 251 | pins: PINS, 252 | config: Config, 253 | afio: &mut Afio, 254 | rcu: &mut Rcu 255 | ) -> Self 256 | where PINS: Pins 257 | { 258 | // enable and reset USART 259 | USART::enable(rcu); 260 | USART::reset(rcu); 261 | 262 | // Pin configuration must happen after configuring USART clock in order 263 | // to avoid junk being transmitted during initialization 264 | let (tx, rx) = pins.configure(); 265 | 266 | // Remap pins 267 | USART::remap(afio, PINS::REMAP); 268 | 269 | // enable DMA transfers 270 | usart.ctl2.write(|w| w.dent().set_bit().denr().set_bit()); 271 | 272 | // Configure baud rate 273 | let brr = USART::base_frequency(rcu).0 / config.baudrate.0; 274 | assert!(brr >= 16, "impossible baud rate"); 275 | usart.baud.write(|w| unsafe { w.bits(brr) }); 276 | 277 | // Configure parity and word length 278 | // Unlike most uart devices, the "word length" of this usart device refers to 279 | // the size of the data plus the parity bit. I.e. "word length"=8, parity=even 280 | // results in 7 bits of data. Therefore, in order to get 8 bits and one parity 281 | // bit, we need to set the "word" length to 9 when using parity bits. 282 | let (word_length, parity_control_enable, parity) = match config.parity { 283 | Parity::ParityNone => (false, false, false), 284 | Parity::ParityEven => (true, true, false), 285 | Parity::ParityOdd => (true, true, true), 286 | }; 287 | usart.ctl0.modify(|_r, w| { 288 | w.wl().bit(word_length); 289 | w.pm().bit(parity); 290 | w.pcen().bit(parity_control_enable) 291 | }); 292 | 293 | // Configure stop bits 294 | let stop_bits = match config.stopbits { 295 | StopBits::STOP1 => 0b00, 296 | StopBits::STOP0P5 => 0b01, 297 | StopBits::STOP2 => 0b10, 298 | StopBits::STOP1P5 => 0b11, 299 | }; 300 | usart.ctl1.modify(|_r, w| unsafe { 301 | w.stb().bits(stop_bits) 302 | }); 303 | 304 | // UE: enable USART 305 | // RE: enable receiver 306 | // TE: enable transceiver 307 | usart.ctl0.modify(|_r, w| { 308 | w.uen().set_bit(); 309 | w.ren().set_bit(); 310 | w.ten().set_bit() 311 | }); 312 | 313 | Serial { usart, tx, rx } 314 | } 315 | 316 | /// Starts listening to the USART by enabling the _Received data 317 | /// ready to be read (RXNE)_ interrupt and _Transmit data 318 | /// register empty (TXE)_ interrupt 319 | pub fn listen(&mut self, event: Event) { 320 | match event { 321 | Event::Rxne => self.usart.ctl0.modify(|_, w| w.rbneie().set_bit()), 322 | Event::Txe => self.usart.ctl0.modify(|_, w| w.tbeie().set_bit()), 323 | } 324 | } 325 | 326 | /// Stops listening to the USART by disabling the _Received data 327 | /// ready to be read (RXNE)_ interrupt and _Transmit data 328 | /// register empty (TXE)_ interrupt 329 | pub fn unlisten(&mut self, event: Event) { 330 | match event { 331 | Event::Rxne => self.usart.ctl0.modify(|_, w| w.rbneie().clear_bit()), 332 | Event::Txe => self.usart.ctl0.modify(|_, w| w.tbeie().clear_bit()), 333 | } 334 | } 335 | 336 | /// Returns ownership of the borrowed register handles 337 | pub fn release(self) -> (USART, TX, RX) { 338 | (self.usart, self.tx, self.rx) 339 | } 340 | 341 | /// Separates the serial struct into separate channel objects for sending (Tx) and 342 | /// receiving (Rx) 343 | pub fn split(self) -> (Tx, Rx) { 344 | ( 345 | Tx { 346 | _usart: PhantomData, 347 | }, 348 | Rx { 349 | _usart: PhantomData, 350 | }, 351 | ) 352 | } 353 | } 354 | 355 | impl Tx { 356 | pub fn listen(&mut self) { 357 | unsafe { (*USART::ptr()).ctl0.modify(|_, w| w.tbeie().set_bit()) }; 358 | } 359 | 360 | pub fn unlisten(&mut self) { 361 | unsafe { (*USART::ptr()).ctl0.modify(|_, w| w.tbeie().clear_bit()) }; 362 | } 363 | } 364 | 365 | impl Rx { 366 | pub fn listen(&mut self) { 367 | unsafe { (*USART::ptr()).ctl0.modify(|_, w| w.rbneie().set_bit()) }; 368 | } 369 | 370 | pub fn unlisten(&mut self) { 371 | unsafe { (*USART::ptr()).ctl0.modify(|_, w| w.rbneie().clear_bit()) }; 372 | } 373 | } 374 | 375 | impl crate::hal::serial::Read for Rx { 376 | type Error = Error; 377 | 378 | fn read(&mut self) -> nb::Result { 379 | // NOTE(unsafe) atomic read with no side effects 380 | let sr = unsafe { (*USART::ptr()).stat.read() }; 381 | 382 | // Check for any errors 383 | let err = if sr.perr().bit_is_set() { 384 | Some(Error::Parity) 385 | } else if sr.ferr().bit_is_set() { 386 | Some(Error::Framing) 387 | } else if sr.nerr().bit_is_set() { 388 | Some(Error::Noise) 389 | } else if sr.orerr().bit_is_set() { 390 | Some(Error::Overrun) 391 | } else { 392 | None 393 | }; 394 | 395 | if let Some(err) = err { 396 | // Some error occurred. In order to clear that error flag, you have to 397 | // do a read from the sr register followed by a read from the dr 398 | // register 399 | // NOTE(read_volatile) see `write_volatile` below 400 | unsafe { 401 | ptr::read_volatile(&(*USART::ptr()).stat as *const _ as *const _); 402 | ptr::read_volatile(&(*USART::ptr()).data as *const _ as *const _); 403 | } 404 | Err(nb::Error::Other(err)) 405 | } else { 406 | // Check if a byte is available 407 | if sr.rbne().bit_is_set() { 408 | // Read the received byte 409 | // NOTE(read_volatile) see `write_volatile` below 410 | Ok(unsafe { 411 | ptr::read_volatile(&(*USART::ptr()).data as *const _ as *const _) 412 | }) 413 | } else { 414 | Err(nb::Error::WouldBlock) 415 | } 416 | } 417 | } 418 | } 419 | 420 | impl crate::hal::serial::Write for Tx { 421 | type Error = Infallible; 422 | 423 | fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { 424 | // NOTE(unsafe) atomic read with no side effects 425 | let sr = unsafe { (*USART::ptr()).stat.read() }; 426 | 427 | if sr.tbe().bit_is_set() { 428 | // NOTE(unsafe) atomic write to stateless register 429 | // NOTE(write_volatile) 8-bit write that's not possible through the svd2rust API 430 | unsafe { 431 | let usart_mut = USART::ptr() as *mut USART::Target; 432 | ptr::write_volatile(ptr::addr_of_mut!((*usart_mut).data) as *mut u8, byte); 433 | } 434 | Ok(()) 435 | } else { 436 | Err(nb::Error::WouldBlock) 437 | } 438 | } 439 | 440 | fn flush(&mut self) -> nb::Result<(), Self::Error> { 441 | // NOTE(unsafe) atomic read with no side effects 442 | let sr = unsafe { (*USART::ptr()).stat.read() }; 443 | 444 | if sr.tc().bit_is_set() { 445 | Ok(()) 446 | } else { 447 | Err(nb::Error::WouldBlock) 448 | } 449 | } 450 | } 451 | 452 | impl core::fmt::Write for Tx 453 | where 454 | Tx: embedded_hal::serial::Write, 455 | { 456 | fn write_str(&mut self, s: &str) -> core::fmt::Result { 457 | s.as_bytes() 458 | .iter() 459 | .try_for_each(|c| nb::block!(self.write(*c))) 460 | .map_err(|_| core::fmt::Error) 461 | } 462 | } 463 | -------------------------------------------------------------------------------- /src/i2c.rs: -------------------------------------------------------------------------------- 1 | //! Inter-Integrated Circuit (I2C) bus 2 | 3 | use crate::gpio::gpiob::{PB10, PB11, PB6, PB7, PB8, PB9}; 4 | use crate::gpio::{Alternate, OpenDrain}; 5 | use crate::hal::blocking::i2c::{Read, Write, WriteRead}; 6 | use crate::pac::{I2C0, I2C1}; 7 | use crate::rcu::{Rcu, Clocks, Enable, Reset, BaseFrequency}; 8 | use crate::time::Hertz; 9 | use crate::afio::{Afio, Remap}; 10 | use riscv::register::mcycle; 11 | use nb::Error::{Other, WouldBlock}; 12 | use nb::{Error as NbError, Result as NbResult}; 13 | 14 | /// I2C error 15 | #[derive(Debug, Eq, PartialEq)] 16 | pub enum Error { 17 | /// Bus error 18 | Bus, 19 | /// Arbitration loss 20 | Arbitration, 21 | /// No ack received 22 | Acknowledge, 23 | /// Overrun/underrun 24 | Overrun, 25 | // Pec, // SMBUS mode only 26 | // Timeout, // SMBUS mode only 27 | // Alert, // SMBUS mode only 28 | #[doc(hidden)] 29 | _Extensible, 30 | } 31 | 32 | #[derive(Eq, PartialEq)] 33 | pub enum DutyCycle { 34 | Ratio2to1, 35 | Ratio16to9, 36 | } 37 | 38 | #[derive(PartialEq)] 39 | pub enum Mode { 40 | Standard { 41 | frequency: Hertz, 42 | }, 43 | Fast { 44 | frequency: Hertz, 45 | duty_cycle: DutyCycle, 46 | }, 47 | FastPlus { 48 | frequency: Hertz, 49 | duty_cycle: DutyCycle, 50 | } 51 | } 52 | 53 | impl Mode { 54 | pub fn standard>(frequency: F) -> Self { 55 | Mode::Standard { 56 | frequency: frequency.into(), 57 | } 58 | } 59 | 60 | pub fn fast>(frequency: F, duty_cycle: DutyCycle) -> Self { 61 | Mode::Fast { 62 | frequency: frequency.into(), 63 | duty_cycle, 64 | } 65 | } 66 | 67 | pub fn fast_plus>(frequency: F, duty_cycle: DutyCycle) -> Self { 68 | Mode::Fast { 69 | frequency: frequency.into(), 70 | duty_cycle, 71 | } 72 | } 73 | 74 | pub fn get_frequency(&self) -> Hertz { 75 | match *self { 76 | Mode::Standard { frequency } => frequency, 77 | Mode::Fast { frequency, .. } => frequency, 78 | Mode::FastPlus { frequency, .. } => frequency, 79 | } 80 | } 81 | } 82 | 83 | /// Helper trait to ensure that the correct I2C pins are used for the corresponding interface 84 | pub trait Pins { 85 | const REMAP: bool; 86 | } 87 | 88 | impl Pins for (PB6>, PB7>) { 89 | const REMAP: bool = false; 90 | } 91 | 92 | impl Pins for (PB8>, PB9>) { 93 | const REMAP: bool = true; 94 | } 95 | 96 | impl Pins for (PB10>, PB11>) { 97 | const REMAP: bool = false; 98 | } 99 | 100 | /// I2C peripheral operating in master mode 101 | pub struct I2c { 102 | i2c: I2C, 103 | pins: PINS, 104 | mode: Mode, 105 | pclk1: u32, 106 | } 107 | 108 | /// embedded-hal compatible blocking I2C implementation 109 | pub struct BlockingI2c { 110 | nb: I2c, 111 | start_timeout: u32, 112 | start_retries: u8, 113 | addr_timeout: u32, 114 | data_timeout: u32, 115 | } 116 | 117 | impl I2c { 118 | /// Creates a generic I2C0 object on pins PB6 and PB7 or PB8 and PB9 (if remapped) 119 | pub fn i2c0( 120 | i2c: I2C0, 121 | pins: PINS, 122 | afio: &mut Afio, 123 | mode: Mode, 124 | rcu: &mut Rcu, 125 | ) -> Self 126 | where 127 | PINS: Pins, 128 | { 129 | I2C0::remap(afio, PINS::REMAP); 130 | I2c::_i2c0(i2c, pins, mode, rcu) 131 | } 132 | } 133 | 134 | impl BlockingI2c { 135 | /// Creates a blocking I2C0 object on pins PB6 and PB7 or PB8 and PB9 using the embedded-hal `BlockingI2c` trait. 136 | pub fn i2c0( 137 | i2c: I2C0, 138 | pins: PINS, 139 | afio: &mut Afio, 140 | mode: Mode, 141 | rcu: &mut Rcu, 142 | start_timeout_us: u32, 143 | start_retries: u8, 144 | addr_timeout_us: u32, 145 | data_timeout_us: u32, 146 | ) -> Self 147 | where 148 | PINS: Pins, 149 | { 150 | I2C0::remap(afio, PINS::REMAP); 151 | BlockingI2c::_i2c0( 152 | i2c, 153 | pins, 154 | mode, 155 | rcu, 156 | start_timeout_us, 157 | start_retries, 158 | addr_timeout_us, 159 | data_timeout_us, 160 | ) 161 | } 162 | } 163 | 164 | impl I2c { 165 | /// Creates a generic I2C1 object on pins PB10 and PB11 using the embedded-hal `BlockingI2c` trait. 166 | pub fn i2c1( 167 | i2c: I2C1, 168 | pins: PINS, 169 | mode: Mode, 170 | rcu: &mut Rcu, 171 | ) -> Self 172 | where 173 | PINS: Pins, 174 | { 175 | I2c::_i2c1(i2c, pins, mode, rcu) 176 | } 177 | } 178 | 179 | impl BlockingI2c { 180 | /// Creates a blocking I2C1 object on pins PB10 and PB11 181 | pub fn i2c1( 182 | i2c: I2C1, 183 | pins: PINS, 184 | mode: Mode, 185 | rcu: &mut Rcu, 186 | start_timeout_us: u32, 187 | start_retries: u8, 188 | addr_timeout_us: u32, 189 | data_timeout_us: u32, 190 | ) -> Self 191 | where 192 | PINS: Pins, 193 | { 194 | BlockingI2c::_i2c1( 195 | i2c, 196 | pins, 197 | mode, 198 | rcu, 199 | start_timeout_us, 200 | start_retries, 201 | addr_timeout_us, 202 | data_timeout_us, 203 | ) 204 | } 205 | } 206 | 207 | /// Generates a blocking I2C instance from a universal I2C object 208 | fn blocking_i2c( 209 | i2c: I2c, 210 | clocks: Clocks, 211 | start_timeout_us: u32, 212 | start_retries: u8, 213 | addr_timeout_us: u32, 214 | data_timeout_us: u32, 215 | ) -> BlockingI2c { 216 | let sysclk_mhz = clocks.sysclk().0 / 1_000_000; 217 | BlockingI2c { 218 | nb: i2c, 219 | start_timeout: start_timeout_us * sysclk_mhz, 220 | start_retries, 221 | addr_timeout: addr_timeout_us * sysclk_mhz, 222 | data_timeout: data_timeout_us * sysclk_mhz, 223 | } 224 | } 225 | 226 | macro_rules! wait_for_flag { 227 | ($i2c:expr, $flag:ident) => {{ 228 | let stat0 = $i2c.stat0.read(); 229 | 230 | if stat0.berr().bit_is_set() { 231 | $i2c.stat0.modify(|_, w| w.berr().clear_bit()); 232 | Err(Other(Error::Bus)) 233 | } else if stat0.lostarb().bit_is_set() { 234 | $i2c.stat0.modify(|_, w| w.lostarb().clear_bit()); 235 | Err(Other(Error::Arbitration)) 236 | } else if stat0.aerr().bit_is_set() { 237 | $i2c.stat0.modify(|_, w| w.aerr().clear_bit()); 238 | Err(Other(Error::Acknowledge)) 239 | } else if stat0.ouerr().bit_is_set() { 240 | $i2c.stat0.modify(|_, w| w.ouerr().clear_bit()); 241 | Err(Other(Error::Overrun)) 242 | } else if stat0.$flag().bit_is_set() { 243 | Ok(()) 244 | } else { 245 | Err(WouldBlock) 246 | } 247 | }}; 248 | } 249 | 250 | macro_rules! busy_wait { 251 | ($nb_expr:expr, $exit_cond:expr) => {{ 252 | loop { 253 | let res = $nb_expr; 254 | if res != Err(WouldBlock) { 255 | break res; 256 | } 257 | if $exit_cond { 258 | break res; 259 | } 260 | } 261 | }}; 262 | } 263 | 264 | macro_rules! busy_wait_cycles { 265 | ($nb_expr:expr, $cycles:expr) => {{ 266 | let started = mcycle::read(); 267 | let cycles = $cycles as usize; 268 | busy_wait!( 269 | $nb_expr, 270 | mcycle::read().wrapping_sub(started) >= cycles 271 | ) 272 | }}; 273 | } 274 | 275 | // Generate the same code for both I2Cs 276 | macro_rules! hal { 277 | ($($I2CX:ident: ($i2cX:ident),)+) => { 278 | $( 279 | impl I2c<$I2CX, PINS> { 280 | /// Configures the I2C peripheral to work in master mode 281 | fn $i2cX( 282 | i2c: $I2CX, 283 | pins: PINS, 284 | mode: Mode, 285 | rcu: &mut Rcu, 286 | ) -> Self { 287 | $I2CX::enable(rcu); 288 | $I2CX::reset(rcu); 289 | 290 | let pclk1 = $I2CX::base_frequency(rcu).0; 291 | 292 | assert!(mode.get_frequency().0 <= 1_000_000); 293 | 294 | let mut i2c = I2c { i2c, pins, mode, pclk1 }; 295 | i2c.init(); 296 | i2c 297 | } 298 | 299 | /// Initializes I2C. 300 | fn init(&mut self) { 301 | let freq = self.mode.get_frequency(); 302 | let pclk1_mhz = (self.pclk1 / 1000000) as u16; 303 | 304 | self.i2c.ctl1.write(|w| unsafe { 305 | w.i2cclk().bits(pclk1_mhz as u8) 306 | }); 307 | self.i2c.ctl0.write(|w| w.i2cen().clear_bit()); 308 | 309 | match self.mode { 310 | Mode::Standard { .. } => { 311 | self.i2c.rt.write(|w| unsafe { 312 | w.risetime().bits((pclk1_mhz + 1) as u8) 313 | }); 314 | self.i2c.ckcfg.write(|w| unsafe { 315 | w.clkc().bits(((self.pclk1 / (freq.0 * 2)) as u16).max(4)) 316 | }); 317 | }, 318 | Mode::Fast { ref duty_cycle, .. } => { 319 | self.configure_fast_mode(pclk1_mhz, freq, duty_cycle) 320 | } 321 | Mode::FastPlus { ref duty_cycle, .. } => { 322 | self.configure_fast_mode(pclk1_mhz, freq, duty_cycle); 323 | 324 | self.i2c.fmpcfg.write(|w| w.fmpen().set_bit()) 325 | } 326 | }; 327 | 328 | self.i2c.ctl0.modify(|_, w| w.i2cen().set_bit()); 329 | } 330 | 331 | fn configure_fast_mode(&self, pclk1_mhz: u16, freq: Hertz, duty_cycle: &DutyCycle) { 332 | self.i2c.rt.write(|w| unsafe { 333 | w.risetime().bits((pclk1_mhz * 300 / 1000 + 1) as u8) 334 | }); 335 | 336 | self.i2c.ckcfg.write(|w| { 337 | let (freq, duty) = match duty_cycle { 338 | DutyCycle::Ratio2to1 => (((self.pclk1 / (freq.0 * 3)) as u16).max(1), false), 339 | DutyCycle::Ratio16to9 => (((self.pclk1 / (freq.0 * 25)) as u16).max(1), true) 340 | }; 341 | 342 | unsafe { 343 | w.clkc().bits(freq).dtcy().bit(duty).fast().set_bit() 344 | } 345 | }); 346 | } 347 | 348 | /// Perform an I2C software reset 349 | fn reset(&mut self) { 350 | self.i2c.ctl0.write(|w| w.i2cen().set_bit().sreset().set_bit()); 351 | self.i2c.ctl0.reset(); 352 | self.init(); 353 | } 354 | 355 | /// Generate START condition 356 | fn send_start(&mut self) { 357 | self.i2c.ctl0.modify(|_, w| w.start().set_bit()); 358 | } 359 | 360 | /// Check if START condition is generated. If the condition is not generated, this 361 | /// method returns `WouldBlock` so the program can act accordingly 362 | /// (busy wait, async, ...) 363 | fn wait_after_sent_start(&mut self) -> NbResult<(), Error> { 364 | wait_for_flag!(self.i2c, sbsend) 365 | } 366 | 367 | /// Check if STOP condition is generated. If the condition is not generated, this 368 | /// method returns `WouldBlock` so the program can act accordingly 369 | /// (busy wait, async, ...) 370 | fn wait_for_stop(&mut self) -> NbResult<(), Error> { 371 | if self.i2c.ctl0.read().stop().bit_is_set() { 372 | Ok(()) 373 | } else { 374 | Err(WouldBlock) 375 | } 376 | } 377 | 378 | /// Sends the (7-Bit) address on the I2C bus. The 8th bit on the bus is set 379 | /// depending on wether it is a read or write transfer. 380 | fn send_addr(&self, addr: u8, read: bool) { 381 | self.i2c.data.write(|w| unsafe { w.trb().bits(addr << 1 | (if read {1} else {0})) }); 382 | } 383 | 384 | /// Generate STOP condition 385 | fn send_stop(&self) { 386 | self.i2c.ctl0.modify(|_, w| w.stop().set_bit()); 387 | } 388 | 389 | /// Releases the I2C peripheral and associated pins 390 | pub fn free(self) -> ($I2CX, PINS) { 391 | (self.i2c, self.pins) 392 | } 393 | } 394 | 395 | impl BlockingI2c<$I2CX, PINS> { 396 | fn $i2cX( 397 | i2c: $I2CX, 398 | pins: PINS, 399 | mode: Mode, 400 | rcu: &mut Rcu, 401 | start_timeout_us: u32, 402 | start_retries: u8, 403 | addr_timeout_us: u32, 404 | data_timeout_us: u32 405 | ) -> Self { 406 | blocking_i2c(I2c::$i2cX(i2c, pins, mode, rcu), 407 | rcu.clocks, start_timeout_us, start_retries, 408 | addr_timeout_us, data_timeout_us) 409 | } 410 | 411 | fn send_start_and_wait(&mut self) -> NbResult<(), Error> { 412 | let mut retries_left = self.start_retries; 413 | let mut last_ret: NbResult<(), Error> = Err(WouldBlock); 414 | while retries_left > 0 { 415 | self.nb.send_start(); 416 | last_ret = busy_wait_cycles!(self.nb.wait_after_sent_start(), self.start_timeout); 417 | if last_ret.is_err() { 418 | self.nb.reset(); 419 | } else { 420 | break; 421 | } 422 | retries_left -= 1; 423 | } 424 | last_ret 425 | } 426 | 427 | fn send_addr_and_wait(&mut self, addr: u8, read: bool) -> NbResult<(), Error> { 428 | self.nb.i2c.stat0.read(); 429 | self.nb.send_addr(addr, read); 430 | let ret = busy_wait_cycles!(wait_for_flag!(self.nb.i2c, addsend), self.addr_timeout); 431 | 432 | if ret == Err(Other(Error::Acknowledge)) { 433 | self.nb.send_stop(); 434 | } 435 | ret 436 | } 437 | 438 | fn write_bytes_and_wait(&mut self, bytes: &[u8]) -> NbResult<(), Error> { 439 | self.nb.i2c.stat0.read(); 440 | self.nb.i2c.stat1.read(); 441 | 442 | self.nb.i2c.data.write(|w| unsafe { w.trb().bits(bytes[0]) }); 443 | 444 | for byte in &bytes[1..] { 445 | busy_wait_cycles!(wait_for_flag!(self.nb.i2c, tbe), self.data_timeout)?; 446 | self.nb.i2c.data.write(|w| unsafe { w.trb().bits(*byte) }); 447 | } 448 | busy_wait_cycles!(wait_for_flag!(self.nb.i2c, btc), self.data_timeout)?; 449 | 450 | Ok(()) 451 | } 452 | 453 | fn write_without_stop(&mut self, addr: u8, bytes: &[u8]) -> NbResult<(), Error> { 454 | self.send_start_and_wait()?; 455 | self.send_addr_and_wait(addr, false)?; 456 | 457 | let ret = self.write_bytes_and_wait(bytes); 458 | if ret == Err(Other(Error::Acknowledge)) { 459 | self.nb.send_stop(); 460 | } 461 | ret 462 | } 463 | } 464 | 465 | impl Write for BlockingI2c<$I2CX, PINS> { 466 | type Error = NbError; 467 | 468 | fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { 469 | self.write_without_stop(addr, bytes)?; 470 | self.nb.send_stop(); 471 | busy_wait_cycles!(self.nb.wait_for_stop(), self.data_timeout)?; 472 | 473 | Ok(()) 474 | } 475 | } 476 | 477 | impl Read for BlockingI2c<$I2CX, PINS> { 478 | type Error = NbError; 479 | 480 | fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { 481 | self.send_start_and_wait()?; 482 | self.send_addr_and_wait(addr, true)?; 483 | 484 | match buffer.len() { 485 | 1 => { 486 | self.nb.i2c.ctl0.modify(|_, w| w.acken().clear_bit()); 487 | self.nb.i2c.stat0.read(); 488 | self.nb.i2c.stat1.read(); 489 | self.nb.send_stop(); 490 | 491 | busy_wait_cycles!(wait_for_flag!(self.nb.i2c, rbne), self.data_timeout)?; 492 | buffer[0] = self.nb.i2c.data.read().trb().bits(); 493 | 494 | busy_wait_cycles!(self.nb.wait_for_stop(), self.data_timeout)?; 495 | self.nb.i2c.ctl0.modify(|_, w| w.acken().set_bit()); 496 | } 497 | 2 => { 498 | self.nb.i2c.ctl0.modify(|_, w| w.pecen().set_bit().acken().set_bit()); 499 | self.nb.i2c.stat0.read(); 500 | self.nb.i2c.stat1.read(); 501 | self.nb.i2c.ctl0.modify(|_, w| w.acken().clear_bit()); 502 | 503 | busy_wait_cycles!(wait_for_flag!(self.nb.i2c, btc), self.data_timeout)?; 504 | self.nb.send_stop(); 505 | buffer[0] = self.nb.i2c.data.read().trb().bits(); 506 | buffer[1] = self.nb.i2c.data.read().trb().bits(); 507 | 508 | busy_wait_cycles!(self.nb.wait_for_stop(), self.data_timeout)?; 509 | self.nb.i2c.ctl0.modify(|_, w| w.pecen().clear_bit().acken().clear_bit()); 510 | self.nb.i2c.ctl0.modify(|_, w| w.acken().set_bit()); 511 | } 512 | buffer_len => { 513 | self.nb.i2c.ctl0.modify(|_, w| w.acken().set_bit()); 514 | self.nb.i2c.stat0.read(); 515 | self.nb.i2c.stat1.read(); 516 | 517 | let (first_bytes, last_two_bytes) = buffer.split_at_mut(buffer_len - 3); 518 | for byte in first_bytes { 519 | busy_wait_cycles!(wait_for_flag!(self.nb.i2c, rbne), self.data_timeout)?; 520 | *byte = self.nb.i2c.data.read().trb().bits(); 521 | } 522 | 523 | busy_wait_cycles!(wait_for_flag!(self.nb.i2c, btc), self.data_timeout)?; 524 | self.nb.i2c.ctl0.modify(|_, w| w.acken().clear_bit()); 525 | last_two_bytes[0] = self.nb.i2c.data.read().trb().bits(); 526 | self.nb.send_stop(); 527 | last_two_bytes[1] = self.nb.i2c.data.read().trb().bits(); 528 | busy_wait_cycles!(wait_for_flag!(self.nb.i2c, rbne), self.data_timeout)?; 529 | last_two_bytes[2] = self.nb.i2c.data.read().trb().bits(); 530 | 531 | busy_wait_cycles!(self.nb.wait_for_stop(), self.data_timeout)?; 532 | self.nb.i2c.ctl0.modify(|_, w| w.acken().set_bit()); 533 | } 534 | } 535 | 536 | Ok(()) 537 | } 538 | } 539 | 540 | impl WriteRead for BlockingI2c<$I2CX, PINS> { 541 | type Error = NbError; 542 | 543 | fn write_read( 544 | &mut self, 545 | addr: u8, 546 | bytes: &[u8], 547 | buffer: &mut [u8], 548 | ) -> Result<(), Self::Error> { 549 | if !bytes.is_empty() { 550 | self.write_without_stop(addr, bytes)?; 551 | } 552 | 553 | if !buffer.is_empty() { 554 | self.read(addr, buffer)?; 555 | } else if !bytes.is_empty() { 556 | self.nb.send_stop(); 557 | busy_wait_cycles!(self.nb.wait_for_stop(), self.data_timeout)?; 558 | } 559 | 560 | Ok(()) 561 | } 562 | } 563 | )+ 564 | } 565 | } 566 | 567 | hal! { 568 | I2C0: (_i2c0), 569 | I2C1: (_i2c1), 570 | } 571 | -------------------------------------------------------------------------------- /src/dma.rs: -------------------------------------------------------------------------------- 1 | //! # Direct Memory Access 2 | #![allow(dead_code)] 3 | 4 | use core::{ 5 | marker::PhantomData, 6 | sync::atomic::{compiler_fence, Ordering}, 7 | }; 8 | use embedded_dma::{StaticReadBuffer, StaticWriteBuffer}; 9 | 10 | use crate::rcu::Rcu; 11 | 12 | #[derive(Debug)] 13 | #[non_exhaustive] 14 | pub enum Error { 15 | Overrun, 16 | } 17 | 18 | pub enum Event { 19 | HalfTransfer, 20 | TransferComplete, 21 | } 22 | 23 | #[derive(Clone, Copy, PartialEq)] 24 | pub enum Half { 25 | First, 26 | Second, 27 | } 28 | 29 | pub enum Direction { 30 | PeripherialToMemory, 31 | MemoryToPeripherial, 32 | MemoryToMemory, 33 | } 34 | 35 | pub struct CircBuffer 36 | where 37 | BUFFER: 'static, 38 | { 39 | buffer: &'static mut [BUFFER; 2], 40 | payload: PAYLOAD, 41 | readable_half: Half, 42 | } 43 | 44 | impl CircBuffer 45 | where 46 | &'static mut [BUFFER; 2]: StaticWriteBuffer, 47 | BUFFER: 'static, 48 | { 49 | pub(crate) fn new(buf: &'static mut [BUFFER; 2], payload: PAYLOAD) -> Self { 50 | CircBuffer { 51 | buffer: buf, 52 | payload, 53 | readable_half: Half::Second, 54 | } 55 | } 56 | } 57 | 58 | pub trait DmaExt { 59 | type Channels; 60 | 61 | fn split(self, rcu: &mut Rcu) -> Self::Channels; 62 | } 63 | 64 | pub trait TransferPayload { 65 | fn start(&mut self); 66 | fn stop(&mut self); 67 | } 68 | 69 | pub struct Transfer 70 | where 71 | PAYLOAD: TransferPayload, 72 | { 73 | _mode: PhantomData, 74 | buffer: BUFFER, 75 | payload: PAYLOAD, 76 | } 77 | 78 | impl Transfer 79 | where 80 | PAYLOAD: TransferPayload, 81 | { 82 | pub(crate) fn r(buffer: BUFFER, payload: PAYLOAD) -> Self { 83 | Transfer { 84 | _mode: PhantomData, 85 | buffer, 86 | payload, 87 | } 88 | } 89 | } 90 | 91 | impl Transfer 92 | where 93 | PAYLOAD: TransferPayload, 94 | { 95 | pub(crate) fn w(buffer: BUFFER, payload: PAYLOAD) -> Self { 96 | Transfer { 97 | _mode: PhantomData, 98 | buffer, 99 | payload, 100 | } 101 | } 102 | } 103 | 104 | impl Drop for Transfer 105 | where 106 | PAYLOAD: TransferPayload, 107 | { 108 | fn drop(&mut self) { 109 | self.payload.stop(); 110 | compiler_fence(Ordering::SeqCst); 111 | } 112 | } 113 | 114 | /// Read transfer 115 | pub struct R; 116 | 117 | /// Write transfer 118 | pub struct W; 119 | 120 | macro_rules! dma { 121 | ($($DMAX:ident: ($dmaX:ident, { 122 | $($CX:ident: ( 123 | $CTLX:ident, 124 | $CNTX:ident, 125 | $PADDRX:ident, 126 | $MADDRX:ident, 127 | $ctlX:ident, 128 | $cntX:ident, 129 | $paddrX:ident, 130 | $maddrX:ident, 131 | $htfifX:ident, 132 | $ftfifX:ident, 133 | $htfifcX:ident, 134 | $ftfifcX:ident, 135 | $gifcX:ident 136 | ),)+ 137 | }),)+) => { 138 | $( 139 | pub mod $dmaX { 140 | use core::{sync::atomic::{self, Ordering}, ptr, mem}; 141 | 142 | use crate::pac::{$DMAX, $dmaX}; 143 | use crate::rcu::{Rcu, Enable}; 144 | 145 | use crate::dma::{CircBuffer, Direction, DmaExt, Error, Event, Half, Transfer, W, RxDma, TxDma, TransferPayload}; 146 | 147 | #[allow(clippy::manual_non_exhaustive)] 148 | pub struct Channels((), $(pub $CX),+); 149 | 150 | $( 151 | /// A singleton that represents a single DMAx channel (channel X in this case) 152 | /// 153 | /// This singleton has exclusive access to the registers of the DMAx channel X 154 | pub struct $CX { _0: () } 155 | 156 | impl $CX { 157 | /// Associated peripheral `address` 158 | /// 159 | /// `inc` indicates whether the address will be incremented after every byte transfer 160 | pub unsafe fn set_peripheral_address(&mut self, address: u32, inc: bool) { 161 | self.paddr().write(|w| unsafe { w.bits(address) } ); 162 | self.ctl().modify(|_, w| w.pnaga().bit(inc) ); 163 | } 164 | 165 | /// `address` where from/to data will be read/write 166 | /// 167 | /// `inc` indicates whether the address will be incremented after every byte transfer 168 | pub unsafe fn set_memory_address(&mut self, address: u32, inc: bool) { 169 | self.maddr().write(|w| unsafe { w.bits(address) } ); 170 | self.ctl().modify(|_, w| w.mnaga().bit(inc) ); 171 | } 172 | 173 | /// Number of bytes to transfer 174 | pub fn set_transfer_length(&mut self, len: usize) { 175 | self.cnt().write(|w| unsafe { w.cnt().bits(cast::u16(len).unwrap()) }); 176 | } 177 | 178 | pub fn set_direction(&mut self, dir: Direction) { 179 | match dir { 180 | Direction::PeripherialToMemory => { 181 | self.ctl().modify(|_, w| w.m2m().clear_bit()); 182 | self.ctl().modify(|_, w| w.dir().clear_bit()) 183 | } 184 | Direction::MemoryToPeripherial => { 185 | self.ctl().modify(|_, w| w.m2m().clear_bit()); 186 | self.ctl().modify(|_, w| w.dir().set_bit()) 187 | } 188 | Direction::MemoryToMemory => { 189 | self.ctl().modify(|_, w| w.m2m().set_bit()) 190 | } 191 | }; 192 | } 193 | 194 | /// Starts the DMA transfer 195 | pub fn start(&mut self) { 196 | self.ctl().modify(|_, w| w.chen().set_bit() ); 197 | } 198 | 199 | /// Stops the DMA transfer 200 | pub fn stop(&mut self) { 201 | self.intc().write(|w| w.$gifcX().set_bit()); 202 | self.ctl().modify(|_, w| w.chen().clear_bit() ); 203 | } 204 | 205 | /// Returns `true` if there's a transfer in progress 206 | pub fn in_progress(&self) -> bool { 207 | self.intf().$ftfifX().bit_is_clear() 208 | } 209 | } 210 | 211 | impl $CX { 212 | pub fn listen(&mut self, event: Event) { 213 | match event { 214 | Event::HalfTransfer => self.ctl().modify(|_, w| w.htfie().set_bit()), 215 | Event::TransferComplete => { 216 | self.ctl().modify(|_, w| w.ftfie().set_bit()) 217 | } 218 | } 219 | } 220 | 221 | pub fn unlisten(&mut self, event: Event) { 222 | match event { 223 | Event::HalfTransfer => { 224 | self.ctl().modify(|_, w| w.htfie().clear_bit()) 225 | }, 226 | Event::TransferComplete => { 227 | self.ctl().modify(|_, w| w.ftfie().clear_bit()) 228 | } 229 | } 230 | } 231 | 232 | pub fn intf(&self) -> $dmaX::intf::R { 233 | // NOTE(unsafe) atomic read with no side effects 234 | unsafe { (*$DMAX::ptr()).intf.read() } 235 | } 236 | 237 | pub fn intc(&self) -> &$dmaX::INTC { 238 | unsafe { &(*$DMAX::ptr()).intc } 239 | } 240 | 241 | pub fn ctl(&self) -> &$dmaX::$CTLX { 242 | unsafe { &(*$DMAX::ptr()).$ctlX } 243 | } 244 | 245 | pub fn cnt(&self) -> &$dmaX::$CNTX { 246 | unsafe { &(*$DMAX::ptr()).$cntX } 247 | } 248 | 249 | pub fn paddr(&self) -> &$dmaX::$PADDRX { 250 | unsafe { &(*$DMAX::ptr()).$paddrX } 251 | } 252 | 253 | pub fn maddr(&self) -> &$dmaX::$MADDRX { 254 | unsafe { &(*$DMAX::ptr()).$maddrX } 255 | } 256 | } 257 | 258 | impl CircBuffer> 259 | where 260 | RxDma: TransferPayload, 261 | { 262 | /// Peeks into the readable half of the buffer 263 | pub fn peek(&mut self, f: F) -> Result 264 | where 265 | F: FnOnce(&B, Half) -> R, 266 | { 267 | let half_being_read = self.readable_half()?; 268 | 269 | let buf = match half_being_read { 270 | Half::First => &self.buffer[0], 271 | Half::Second => &self.buffer[1], 272 | }; 273 | 274 | // XXX does this need a compiler barrier? 275 | let ret = f(buf, half_being_read); 276 | 277 | 278 | let intf = self.payload.channel.intf(); 279 | let first_half_is_done = intf.$htfifX().bit_is_set(); 280 | let second_half_is_done = intf.$ftfifX().bit_is_set(); 281 | 282 | if (half_being_read == Half::First && second_half_is_done) || 283 | (half_being_read == Half::Second && first_half_is_done) { 284 | Err(Error::Overrun) 285 | } else { 286 | Ok(ret) 287 | } 288 | } 289 | 290 | /// Returns the `Half` of the buffer that can be read 291 | pub fn readable_half(&mut self) -> Result { 292 | let intf = self.payload.channel.intf(); 293 | let first_half_is_done = intf.$htfifX().bit_is_set(); 294 | let second_half_is_done = intf.$ftfifX().bit_is_set(); 295 | 296 | if first_half_is_done && second_half_is_done { 297 | return Err(Error::Overrun); 298 | } 299 | 300 | let last_read_half = self.readable_half; 301 | 302 | Ok(match last_read_half { 303 | Half::First => { 304 | if second_half_is_done { 305 | self.payload.channel.intc().write(|w| w.$ftfifcX().set_bit()); 306 | 307 | self.readable_half = Half::Second; 308 | Half::Second 309 | } else { 310 | last_read_half 311 | } 312 | } 313 | Half::Second => { 314 | if first_half_is_done { 315 | self.payload.channel.intc().write(|w| w.$htfifcX().set_bit()); 316 | 317 | self.readable_half = Half::First; 318 | Half::First 319 | } else { 320 | last_read_half 321 | } 322 | } 323 | }) 324 | } 325 | 326 | /// Stops the transfer and returns the underlying buffer and RxDma 327 | pub fn stop(mut self) -> (&'static mut [B; 2], RxDma) { 328 | self.payload.stop(); 329 | 330 | (self.buffer, self.payload) 331 | } 332 | } 333 | 334 | impl Transfer> 335 | where 336 | RxDma: TransferPayload, 337 | { 338 | pub fn is_done(&self) -> bool { 339 | !self.payload.channel.in_progress() 340 | } 341 | 342 | pub fn wait(mut self) -> (BUFFER, RxDma) { 343 | while !self.is_done() {} 344 | 345 | atomic::compiler_fence(Ordering::Acquire); 346 | 347 | self.payload.stop(); 348 | 349 | // we need a read here to make the Acquire fence effective 350 | // we do *not* need this if `dma.stop` does a RMW operation 351 | unsafe { ptr::read_volatile(&0); } 352 | 353 | // we need a fence here for the same reason we need one in `Transfer.wait` 354 | atomic::compiler_fence(Ordering::Acquire); 355 | 356 | // `Transfer` needs to have a `Drop` implementation, because we accept 357 | // managed buffers that can free their memory on drop. Because of that 358 | // we can't move out of the `Transfer`'s fields, so we use `ptr::read` 359 | // and `mem::forget`. 360 | // 361 | // NOTE(unsafe) There is no panic branch between getting the resources 362 | // and forgetting `self`. 363 | unsafe { 364 | let buffer = ptr::read(&self.buffer); 365 | let payload = ptr::read(&self.payload); 366 | mem::forget(self); 367 | (buffer, payload) 368 | } 369 | } 370 | } 371 | 372 | impl Transfer> 373 | where 374 | TxDma: TransferPayload, 375 | { 376 | pub fn is_done(&self) -> bool { 377 | !self.payload.channel.in_progress() 378 | } 379 | 380 | pub fn wait(mut self) -> (BUFFER, TxDma) { 381 | while !self.is_done() {} 382 | 383 | atomic::compiler_fence(Ordering::Acquire); 384 | 385 | self.payload.stop(); 386 | 387 | // we need a read here to make the Acquire fence effective 388 | // we do *not* need this if `dma.stop` does a RMW operation 389 | unsafe { ptr::read_volatile(&0); } 390 | 391 | // we need a fence here for the same reason we need one in `Transfer.wait` 392 | atomic::compiler_fence(Ordering::Acquire); 393 | 394 | // `Transfer` needs to have a `Drop` implementation, because we accept 395 | // managed buffers that can free their memory on drop. Because of that 396 | // we can't move out of the `Transfer`'s fields, so we use `ptr::read` 397 | // and `mem::forget`. 398 | // 399 | // NOTE(unsafe) There is no panic branch between getting the resources 400 | // and forgetting `self`. 401 | unsafe { 402 | let buffer = ptr::read(&self.buffer); 403 | let payload = ptr::read(&self.payload); 404 | mem::forget(self); 405 | (buffer, payload) 406 | } 407 | } 408 | } 409 | 410 | impl Transfer> 411 | where 412 | RxDma: TransferPayload, 413 | { 414 | pub fn peek(&self) -> &[T] 415 | where 416 | BUFFER: AsRef<[T]>, 417 | { 418 | let pending = self.payload.channel.cnt().read().bits() as usize; 419 | 420 | let slice = self.buffer.as_ref(); 421 | let capacity = slice.len(); 422 | 423 | &slice[..(capacity - pending)] 424 | } 425 | } 426 | )+ 427 | 428 | impl DmaExt for $DMAX { 429 | type Channels = Channels; 430 | 431 | fn split(self, rcu: &mut Rcu) -> Channels { 432 | $DMAX::enable(rcu); 433 | 434 | // reset the DMA control registers (stops all on-going transfers) 435 | $( 436 | self.$ctlX.reset(); 437 | )+ 438 | 439 | Channels((), $($CX { _0: () }),+) 440 | } 441 | } 442 | } 443 | )+ 444 | } 445 | } 446 | 447 | dma! { 448 | DMA0: (dma0, { 449 | C0: ( 450 | CH0CTL, CH0CNT, CH0PADDR, CH0MADDR, 451 | ch0ctl, ch0cnt, ch0paddr, ch0maddr, 452 | htfif0, ftfif0, 453 | htfifc0, ftfifc0, gifc0 454 | ), 455 | C1: ( 456 | CH1CTL, CH1CNT, CH1PADDR, CH1MADDR, 457 | ch1ctl, ch1cnt, ch1paddr, ch1maddr, 458 | htfif1, ftfif1, 459 | htfifc1, ftfifc1, gifc1 460 | ), 461 | C2: ( 462 | CH2CTL, CH2CNT, CH2PADDR, CH2MADDR, 463 | ch2ctl, ch2cnt, ch2paddr, ch2maddr, 464 | htfif2, ftfif2, 465 | htfifc2, ftfifc2, gifc2 466 | ), 467 | C3: ( 468 | CH3CTL, CH3CNT, CH3PADDR, CH3MADDR, 469 | ch3ctl, ch3cnt, ch3paddr, ch3maddr, 470 | htfif3, ftfif3, 471 | htfifc3, ftfifc3, gifc3 472 | ), 473 | C4: ( 474 | CH4CTL, CH4CNT, CH4PADDR, CH4MADDR, 475 | ch4ctl, ch4cnt, ch4paddr, ch4maddr, 476 | htfif4, ftfif4, 477 | htfifc4, ftfifc4, gifc4 478 | ), 479 | C5: ( 480 | CH5CTL, CH5CNT, CH5PADDR, CH5MADDR, 481 | ch5ctl, ch5cnt, ch5paddr, ch5maddr, 482 | htfif5, ftfif5, 483 | htfifc5, ftfifc5, gifc5 484 | ), 485 | C6: ( 486 | CH6CTL, CH6CNT, CH6PADDR, CH6MADDR, 487 | ch6ctl, ch6cnt, ch6paddr, ch6maddr, 488 | htfif6, ftfif6, 489 | htfifc6, ftfifc6, gifc6 490 | ), 491 | }), 492 | 493 | DMA1: (dma1, { 494 | C0: ( 495 | CH0CTL, CH0CNT, CH0PADDR, CH0MADDR, 496 | ch0ctl, ch0cnt, ch0paddr, ch0maddr, 497 | htfif0, ftfif0, 498 | htfifc0, ftfifc0, gifc0 499 | ), 500 | C1: ( 501 | CH1CTL, CH1CNT, CH1PADDR, CH1MADDR, 502 | ch1ctl, ch1cnt, ch1paddr, ch1maddr, 503 | htfif1, ftfif1, 504 | htfifc1, ftfifc1, gifc1 505 | ), 506 | C2: ( 507 | CH2CTL, CH2CNT, CH2PADDR, CH2MADDR, 508 | ch2ctl, ch2cnt, ch2paddr, ch2maddr, 509 | htfif2, ftfif2, 510 | htfifc2, ftfifc2, gifc2 511 | ), 512 | C3: ( 513 | CH3CTL, CH3CNT, CH3PADDR, CH3MADDR, 514 | ch3ctl, ch3cnt, ch3paddr, ch3maddr, 515 | htfif3, ftfif3, 516 | htfifc3, ftfifc3, gifc3 517 | ), 518 | C4: ( 519 | CH4CTL, CH4CNT, CH4PADDR, CH4MADDR, 520 | ch4ctl, ch4cnt, ch4paddr, ch4maddr, 521 | htfif4, ftfif4, 522 | htfifc4, ftfifc4, gifc4 523 | ), 524 | }), 525 | } 526 | 527 | /// DMA Receiver 528 | pub struct RxDma { 529 | pub(crate) payload: PAYLOAD, 530 | pub channel: RXCH, 531 | } 532 | 533 | /// DMA Transmitter 534 | pub struct TxDma { 535 | pub(crate) payload: PAYLOAD, 536 | pub channel: TXCH, 537 | } 538 | 539 | /// DMA Receiver/Transmitter 540 | pub struct RxTxDma { 541 | pub(crate) payload: PAYLOAD, 542 | pub rxchannel: RXCH, 543 | pub txchannel: TXCH, 544 | } 545 | 546 | pub trait Receive { 547 | type RxChannel; 548 | type TransmittedWord; 549 | } 550 | 551 | pub trait Transmit { 552 | type TxChannel; 553 | type ReceivedWord; 554 | } 555 | 556 | /// Trait for circular DMA readings from peripheral to memory. 557 | pub trait CircReadDma: Receive 558 | where 559 | &'static mut [B; 2]: StaticWriteBuffer, 560 | B: 'static, 561 | Self: core::marker::Sized, 562 | { 563 | fn circ_read(self, buffer: &'static mut [B; 2]) -> CircBuffer; 564 | } 565 | 566 | /// Trait for DMA readings from peripheral to memory. 567 | pub trait ReadDma: Receive 568 | where 569 | B: StaticWriteBuffer, 570 | Self: core::marker::Sized + TransferPayload, 571 | { 572 | fn read(self, buffer: B) -> Transfer; 573 | } 574 | 575 | /// Trait for DMA writing from memory to peripheral. 576 | pub trait WriteDma: Transmit 577 | where 578 | B: StaticReadBuffer, 579 | Self: core::marker::Sized + TransferPayload, 580 | { 581 | fn write(self, buffer: B) -> Transfer; 582 | } 583 | -------------------------------------------------------------------------------- /src/gpio.rs: -------------------------------------------------------------------------------- 1 | //! General Purpose Input / Output 2 | 3 | use core::marker::PhantomData; 4 | use riscv::interrupt; 5 | use crate::rcu::Rcu; 6 | 7 | /// Extension trait to split a GPIO peripheral in independent pins and registers 8 | pub trait GpioExt { 9 | /// The to split the GPIO into 10 | type Parts; 11 | 12 | /// Splits the GPIO block into independent pins and registers 13 | fn split(self, rcu: &mut Rcu) -> Self::Parts; 14 | } 15 | 16 | #[repr(u8)] 17 | pub enum Port { 18 | PAx = 0, 19 | PBx = 1, 20 | PCx = 2, 21 | PDx = 3, 22 | PEx = 4, 23 | } 24 | 25 | /// Marker trait for active states. 26 | pub trait Active {} 27 | 28 | /// Input mode (type state) 29 | pub struct Input { 30 | _mode: PhantomData, 31 | } 32 | impl Active for Input {} 33 | 34 | /// Used by the debugger 35 | /// 36 | /// To convert this pin into a normal one, call [Afio::disable_jtag()](crate::afio::Afio::disable_jtag) 37 | pub struct Debugger; 38 | /// Floating Input 39 | pub struct Floating; 40 | /// Pulled down Input 41 | pub struct PullDown; 42 | /// Pulled up Input 43 | pub struct PullUp; 44 | 45 | /// Output mode (type state) 46 | pub struct Output { 47 | _mode: PhantomData, 48 | } 49 | impl Active for Output {} 50 | 51 | /// Totem Pole aka Push-Pull 52 | pub struct PushPull; 53 | /// Open drain output 54 | pub struct OpenDrain; 55 | 56 | /// Analog mode 57 | pub struct Analog; 58 | 59 | /// Alternate function 60 | pub struct Alternate { 61 | _mode: PhantomData, 62 | } 63 | impl Active for Alternate {} 64 | 65 | pub enum State { 66 | High, 67 | Low, 68 | } 69 | 70 | #[derive(Copy, Clone)] 71 | #[repr(u8)] 72 | enum InputPortConfiguration { 73 | Analog = 0b0000, 74 | Floating = 0b0100, 75 | Pulled = 0b1000, 76 | } 77 | 78 | #[derive(Copy, Clone)] 79 | #[repr(u8)] 80 | enum OutputPortConfiguration { 81 | GpioPushPull = 0b0000, 82 | GpioOpenDrain = 0b0100, 83 | AfioPushPull = 0b1000, 84 | AfioOpenDrain = 0b1100, 85 | } 86 | 87 | #[allow(dead_code)] 88 | #[derive(Copy, Clone)] 89 | enum PortMode { 90 | Input(InputPortConfiguration), 91 | Output10Mhz(OutputPortConfiguration), 92 | Output2Mhz(OutputPortConfiguration), 93 | Output50Mhz(OutputPortConfiguration), 94 | } 95 | 96 | impl PortMode { 97 | #[inline(always)] 98 | pub fn into_bits(self) -> u8 { 99 | match self { 100 | PortMode::Input(conf) => (conf as u8) | 0b00, 101 | PortMode::Output10Mhz(conf) => (conf as u8) | 0b01, 102 | PortMode::Output2Mhz(conf) => (conf as u8) | 0b10, 103 | PortMode::Output50Mhz(conf) => (conf as u8) | 0b11, 104 | } 105 | } 106 | } 107 | 108 | trait PeripheralAccess { 109 | fn peripheral() -> &'static crate::pac::gpioa::RegisterBlock; 110 | 111 | fn set_mode(index: u8, mode: PortMode) { 112 | assert!(index < 16); 113 | 114 | let bits = mode.into_bits(); 115 | let offset = (index * 4) % 32; 116 | let mask = !(0b1111u32 << offset); 117 | let value = (bits as u32) << offset; 118 | let regs = Self::peripheral(); 119 | 120 | interrupt::free(|_| { 121 | if index < 8 { 122 | regs.ctl0.modify(|r, w| unsafe { 123 | w.bits((r.bits() & mask) | value) 124 | }); 125 | } else { 126 | regs.ctl1.modify(|r, w| unsafe { 127 | w.bits((r.bits() & mask) | value) 128 | }); 129 | } 130 | }); 131 | } 132 | 133 | #[inline(always)] 134 | fn set_bit(index: u8) { 135 | assert!(index < 16); 136 | 137 | let regs = Self::peripheral(); 138 | 139 | // NOTE(unsafe) atomic write to a stateless register 140 | regs.bop.write(|w| unsafe { w.bits(1u32 << index) }); 141 | } 142 | 143 | #[inline(always)] 144 | fn clear_bit(index: u8) { 145 | assert!(index < 16); 146 | 147 | let regs = Self::peripheral(); 148 | 149 | // NOTE(unsafe) atomic write to a stateless register 150 | regs.bop.write(|w| unsafe { w.bits(1u32 << (16 + index)) }); 151 | } 152 | 153 | #[inline(always)] 154 | fn is_high(index: u8) -> bool { 155 | assert!(index < 16); 156 | 157 | let regs = Self::peripheral(); 158 | 159 | let mask = 1u32 << index; 160 | regs.istat.read().bits() & mask != 0 161 | } 162 | 163 | #[inline(always)] 164 | fn is_set_high(index: u8) -> bool { 165 | assert!(index < 16); 166 | 167 | let regs = Self::peripheral(); 168 | 169 | let mask = 1u32 << index; 170 | regs.octl.read().bits() & mask != 0 171 | } 172 | } 173 | 174 | macro_rules! gpio { 175 | ($GPIOX:ident, $gpiox:ident, $gpioy:ident, $PXx:ident, [ 176 | $($PXi:ident: ($pxi:ident, $i:expr, $MODE:ty),)+ 177 | ]) => { 178 | /// GPIO 179 | pub mod $gpiox { 180 | use core::convert::Infallible; 181 | use core::marker::PhantomData; 182 | use crate::hal::digital::v2::{OutputPin, InputPin, StatefulOutputPin, toggleable}; 183 | use crate::pac::$GPIOX; 184 | use crate::rcu::{Rcu, Enable, Reset}; 185 | use super::{ 186 | PeripheralAccess, 187 | PortMode, 188 | InputPortConfiguration, 189 | OutputPortConfiguration, 190 | Alternate, Floating, GpioExt, Input, 191 | OpenDrain, 192 | Output, 193 | PullDown, 194 | PullUp, 195 | PushPull, 196 | Analog, 197 | State, 198 | Active, 199 | Debugger, 200 | Pxx, 201 | Port 202 | }; 203 | 204 | /// GPIO parts 205 | pub struct Parts { 206 | $( 207 | /// Pin 208 | pub $pxi: $PXi<$MODE>, 209 | )+ 210 | } 211 | 212 | impl PeripheralAccess for $GPIOX { 213 | #[inline(always)] 214 | fn peripheral() -> &'static crate::pac::gpioa::RegisterBlock { 215 | unsafe { &*$GPIOX::ptr() } 216 | } 217 | } 218 | 219 | impl GpioExt for $GPIOX { 220 | type Parts = Parts; 221 | 222 | fn split(self, rcu: &mut Rcu) -> Parts { 223 | $GPIOX::enable(rcu); 224 | $GPIOX::reset(rcu); 225 | 226 | Parts { 227 | $( 228 | $pxi: $PXi { _mode: PhantomData }, 229 | )+ 230 | } 231 | } 232 | } 233 | 234 | /// Partially erased pin. Only used in the Pxx enum 235 | pub struct Generic { 236 | i: u8, 237 | _mode: PhantomData, 238 | } 239 | 240 | impl Generic { 241 | pub fn downgrade(self) -> Pxx { 242 | Pxx::$PXx(self) 243 | } 244 | 245 | pub fn pin_number(&self) -> u8 { 246 | self.i 247 | } 248 | } 249 | 250 | impl OutputPin for Generic> { 251 | type Error = Infallible; 252 | fn set_high(&mut self) -> Result<(), Self::Error> { 253 | $GPIOX::set_bit(self.i); 254 | Ok(()) 255 | } 256 | 257 | fn set_low(&mut self) -> Result<(), Self::Error> { 258 | $GPIOX::clear_bit(self.i); 259 | Ok(()) 260 | } 261 | } 262 | 263 | impl InputPin for Generic> { 264 | type Error = Infallible; 265 | fn is_high(&self) -> Result { 266 | Ok($GPIOX::is_high(self.i)) 267 | } 268 | 269 | fn is_low(&self) -> Result { 270 | Ok(!$GPIOX::is_high(self.i)) 271 | } 272 | } 273 | 274 | 275 | impl StatefulOutputPin for Generic> { 276 | fn is_set_high(&self) -> Result { 277 | Ok($GPIOX::is_set_high(self.i)) 278 | } 279 | 280 | fn is_set_low(&self) -> Result { 281 | Ok(!$GPIOX::is_set_high(self.i)) 282 | } 283 | } 284 | 285 | impl toggleable::Default for Generic> {} 286 | 287 | impl InputPin for Generic> { 288 | type Error = Infallible; 289 | fn is_high(&self) -> Result { 290 | Ok($GPIOX::is_high(self.i)) 291 | } 292 | 293 | fn is_low(&self) -> Result { 294 | Ok(!$GPIOX::is_high(self.i)) 295 | } 296 | } 297 | 298 | pub type $PXx = Pxx; 299 | 300 | 301 | 302 | $( 303 | /// Pin 304 | pub struct $PXi { 305 | _mode: PhantomData, 306 | } 307 | 308 | impl $PXi { 309 | pub const fn port(&self) -> Port { 310 | Port::$PXx 311 | } 312 | 313 | pub const fn pin_number(&self) -> u8 { 314 | $i 315 | } 316 | } 317 | 318 | impl $PXi { 319 | /// Put the pin in an active state. The caller 320 | /// must enforce that the pin is really in this 321 | /// state in the hardware. 322 | #[allow(dead_code)] 323 | pub(crate) unsafe fn activate(self) -> $PXi> { 324 | // JTAG/Serial-Wired Debug pins are in input PU/PD mode after reset. 325 | // Explicitly convert into floating inputs. 326 | let mode = PortMode::Input(InputPortConfiguration::Floating); 327 | $GPIOX::set_mode($i, mode); 328 | 329 | $PXi { _mode: PhantomData } 330 | } 331 | } 332 | 333 | impl $PXi where MODE: Active { 334 | /// Configures the pin to operate as an alternate function push-pull output 335 | /// pin. 336 | pub fn into_alternate_push_pull(self) -> $PXi> { 337 | let mode = PortMode::Output50Mhz(OutputPortConfiguration::AfioPushPull); 338 | $GPIOX::set_mode($i, mode); 339 | 340 | $PXi { _mode: PhantomData } 341 | } 342 | 343 | /// Configures the pin to operate as an alternate function open-drain output 344 | /// pin. 345 | pub fn into_alternate_open_drain(self) -> $PXi> { 346 | let mode = PortMode::Output50Mhz(OutputPortConfiguration::AfioOpenDrain); 347 | $GPIOX::set_mode($i, mode); 348 | 349 | $PXi { _mode: PhantomData } 350 | } 351 | 352 | /// Configures the pin to operate as a floating input pin 353 | pub fn into_floating_input(self) -> $PXi> { 354 | let mode = PortMode::Input(InputPortConfiguration::Floating); 355 | $GPIOX::set_mode($i, mode); 356 | 357 | $PXi { _mode: PhantomData } 358 | } 359 | 360 | /// Configures the pin to operate as a pulled down input pin 361 | pub fn into_pull_down_input(self) -> $PXi> { 362 | $GPIOX::clear_bit($i); // pull down 363 | 364 | let mode = PortMode::Input(InputPortConfiguration::Pulled); 365 | $GPIOX::set_mode($i, mode); 366 | 367 | $PXi { _mode: PhantomData } 368 | } 369 | 370 | /// Configures the pin to operate as a pulled up input pin 371 | pub fn into_pull_up_input(self) -> $PXi> { 372 | $GPIOX::set_bit($i); // pull up 373 | 374 | let mode = PortMode::Input(InputPortConfiguration::Pulled); 375 | $GPIOX::set_mode($i, mode); 376 | 377 | $PXi { _mode: PhantomData } 378 | } 379 | 380 | /// Configures the pin to operate as an open-drain output pin. 381 | /// Initial state will be low. 382 | pub fn into_open_drain_output(self) -> $PXi> { 383 | self.into_open_drain_output_with_state(State::Low) 384 | } 385 | 386 | /// Configures the pin to operate as an open-drain output pin. 387 | /// `initial_state` specifies whether the pin should be initially high or low. 388 | pub fn into_open_drain_output_with_state( 389 | self, 390 | initial_state: State, 391 | ) -> $PXi> { 392 | match initial_state { 393 | State::High => $GPIOX::set_bit($i), 394 | State::Low => $GPIOX::clear_bit($i), 395 | } 396 | 397 | let mode = PortMode::Output50Mhz(OutputPortConfiguration::GpioOpenDrain); 398 | $GPIOX::set_mode($i, mode); 399 | 400 | $PXi { _mode: PhantomData } 401 | } 402 | 403 | /// Configures the pin to operate as an push-pull output pin. 404 | /// Initial state will be low. 405 | pub fn into_push_pull_output( 406 | self 407 | ) -> $PXi> { 408 | self.into_push_pull_output_with_state(State::Low) 409 | } 410 | 411 | /// Configures the pin to operate as an push-pull output pin. 412 | /// `initial_state` specifies whether the pin should be initially high or low. 413 | pub fn into_push_pull_output_with_state( 414 | self, 415 | initial_state: State, 416 | ) -> $PXi> { 417 | match initial_state { 418 | State::High => $GPIOX::set_bit($i), 419 | State::Low => $GPIOX::clear_bit($i), 420 | } 421 | 422 | let mode = PortMode::Output50Mhz(OutputPortConfiguration::GpioPushPull); 423 | $GPIOX::set_mode($i, mode); 424 | 425 | $PXi { _mode: PhantomData } 426 | } 427 | 428 | 429 | /// Configures the pin to operate as an analog input pin 430 | pub fn into_analog(self) -> $PXi { 431 | let mode = PortMode::Input(InputPortConfiguration::Analog); 432 | $GPIOX::set_mode($i, mode); 433 | 434 | $PXi { _mode: PhantomData } 435 | } 436 | } 437 | 438 | impl $PXi where MODE: Active { 439 | /// Erases the pin number from the type 440 | fn into_generic(self) -> Generic { 441 | Generic { 442 | i: $i, 443 | _mode: self._mode, 444 | } 445 | } 446 | 447 | /// Erases the pin number and port from the type 448 | /// 449 | /// This is useful when you want to collect the pins into an array where you 450 | /// need all the elements to have the same type 451 | pub fn downgrade(self) -> Pxx { 452 | self.into_generic().downgrade() 453 | } 454 | } 455 | 456 | impl OutputPin for $PXi> { 457 | type Error = Infallible; 458 | fn set_high(&mut self) -> Result<(), Self::Error> { 459 | $GPIOX::set_bit($i); 460 | Ok(()) 461 | } 462 | 463 | fn set_low(&mut self) -> Result<(), Self::Error> { 464 | $GPIOX::clear_bit($i); 465 | Ok(()) 466 | } 467 | } 468 | 469 | impl StatefulOutputPin for $PXi> { 470 | fn is_set_high(&self) -> Result { 471 | Ok($GPIOX::is_set_high($i)) 472 | } 473 | 474 | fn is_set_low(&self) -> Result { 475 | Ok(!$GPIOX::is_set_high($i)) 476 | } 477 | } 478 | 479 | impl toggleable::Default for $PXi> {} 480 | 481 | impl InputPin for $PXi> { 482 | type Error = Infallible; 483 | fn is_high(&self) -> Result { 484 | Ok($GPIOX::is_high($i)) 485 | } 486 | 487 | fn is_low(&self) -> Result { 488 | Ok(!$GPIOX::is_high($i)) 489 | } 490 | } 491 | 492 | impl InputPin for $PXi> { 493 | type Error = Infallible; 494 | fn is_high(&self) -> Result { 495 | Ok($GPIOX::is_high($i)) 496 | } 497 | 498 | fn is_low(&self) -> Result { 499 | Ok(!$GPIOX::is_high($i)) 500 | } 501 | } 502 | )+ 503 | } 504 | } 505 | } 506 | 507 | macro_rules! impl_pxx { 508 | ($(($port:ident :: $pin:ident)),*) => { 509 | use core::convert::Infallible; 510 | use embedded_hal::digital::v2::{InputPin, StatefulOutputPin, OutputPin}; 511 | 512 | pub enum Pxx { 513 | $( 514 | $pin($port::Generic) 515 | ),* 516 | } 517 | 518 | impl Pxx { 519 | pub fn pin_number(&self) -> u8 { 520 | match self { 521 | $(Pxx::$pin(pin) => pin.pin_number()),* 522 | } 523 | } 524 | } 525 | 526 | impl OutputPin for Pxx> { 527 | type Error = Infallible; 528 | fn set_high(&mut self) -> Result<(), Infallible> { 529 | match self { 530 | $(Pxx::$pin(pin) => pin.set_high()),* 531 | } 532 | } 533 | 534 | fn set_low(&mut self) -> Result<(), Infallible> { 535 | match self { 536 | $(Pxx::$pin(pin) => pin.set_low()),* 537 | } 538 | } 539 | } 540 | 541 | impl StatefulOutputPin for Pxx> { 542 | fn is_set_high(&self) -> Result { 543 | match self { 544 | $(Pxx::$pin(pin) => pin.is_set_high()),* 545 | } 546 | } 547 | 548 | fn is_set_low(&self) -> Result { 549 | match self { 550 | $(Pxx::$pin(pin) => pin.is_set_low()),* 551 | } 552 | } 553 | } 554 | 555 | impl InputPin for Pxx> { 556 | type Error = Infallible; 557 | fn is_high(&self) -> Result { 558 | match self { 559 | $(Pxx::$pin(pin) => pin.is_high()),* 560 | } 561 | } 562 | 563 | fn is_low(&self) -> Result { 564 | match self { 565 | $(Pxx::$pin(pin) => pin.is_low()),* 566 | } 567 | } 568 | } 569 | } 570 | } 571 | 572 | impl_pxx! { 573 | (gpioa::PAx), 574 | (gpiob::PBx), 575 | (gpioc::PCx), 576 | (gpiod::PDx), 577 | (gpioe::PEx) 578 | } 579 | 580 | gpio!(GPIOA, gpioa, gpioa, PAx, [ 581 | PA0: (pa0, 0, Input), 582 | PA1: (pa1, 1, Input), 583 | PA2: (pa2, 2, Input), 584 | PA3: (pa3, 3, Input), 585 | PA4: (pa4, 4, Input), 586 | PA5: (pa5, 5, Input), 587 | PA6: (pa6, 6, Input), 588 | PA7: (pa7, 7, Input), 589 | PA8: (pa8, 8, Input), 590 | PA9: (pa9, 9, Input), 591 | PA10: (pa10, 10, Input), 592 | PA11: (pa11, 11, Input), 593 | PA12: (pa12, 12, Input), 594 | PA13: (pa13, 13, Debugger), 595 | PA14: (pa14, 14, Debugger), 596 | PA15: (pa15, 15, Debugger), 597 | ]); 598 | 599 | gpio!(GPIOB, gpiob, gpioa, PBx, [ 600 | PB0: (pb0, 0, Input), 601 | PB1: (pb1, 1, Input), 602 | PB2: (pb2, 2, Input), 603 | PB3: (pb3, 3, Debugger), 604 | PB4: (pb4, 4, Debugger), 605 | PB5: (pb5, 5, Input), 606 | PB6: (pb6, 6, Input), 607 | PB7: (pb7, 7, Input), 608 | PB8: (pb8, 8, Input), 609 | PB9: (pb9, 9, Input), 610 | PB10: (pb10, 10, Input), 611 | PB11: (pb11, 11, Input), 612 | PB12: (pb12, 12, Input), 613 | PB13: (pb13, 13, Input), 614 | PB14: (pb14, 14, Input), 615 | PB15: (pb15, 15, Input), 616 | ]); 617 | 618 | gpio!(GPIOC, gpioc, gpioa, PCx, [ 619 | PC0: (pc0, 0, Input), 620 | PC1: (pc1, 1, Input), 621 | PC2: (pc2, 2, Input), 622 | PC3: (pc3, 3, Input), 623 | PC4: (pc4, 4, Input), 624 | PC5: (pc5, 5, Input), 625 | PC6: (pc6, 6, Input), 626 | PC7: (pc7, 7, Input), 627 | PC8: (pc8, 8, Input), 628 | PC9: (pc9, 9, Input), 629 | PC10: (pc10, 10, Input), 630 | PC11: (pc11, 11, Input), 631 | PC12: (pc12, 12, Input), 632 | PC13: (pc13, 13, Input), 633 | PC14: (pc14, 14, Input), 634 | PC15: (pc15, 15, Input), 635 | ]); 636 | 637 | gpio!(GPIOD, gpiod, gpioa, PDx, [ 638 | PD0: (pd0, 0, Input), 639 | PD1: (pd1, 1, Input), 640 | PD2: (pd2, 2, Input), 641 | PD3: (pd3, 3, Input), 642 | PD4: (pd4, 4, Input), 643 | PD5: (pd5, 5, Input), 644 | PD6: (pd6, 6, Input), 645 | PD7: (pd7, 7, Input), 646 | PD8: (pd8, 8, Input), 647 | PD9: (pd9, 9, Input), 648 | PD10: (pd10, 10, Input), 649 | PD11: (pd11, 11, Input), 650 | PD12: (pd12, 12, Input), 651 | PD13: (pd13, 13, Input), 652 | PD14: (pd14, 14, Input), 653 | PD15: (pd15, 15, Input), 654 | ]); 655 | 656 | gpio!(GPIOE, gpioe, gpioa, PEx, [ 657 | PE0: (pe0, 0, Input), 658 | PE1: (pe1, 1, Input), 659 | PE2: (pe2, 2, Input), 660 | PE3: (pe3, 3, Input), 661 | PE4: (pe4, 4, Input), 662 | PE5: (pe5, 5, Input), 663 | PE6: (pe6, 6, Input), 664 | PE7: (pe7, 7, Input), 665 | PE8: (pe8, 8, Input), 666 | PE9: (pe9, 9, Input), 667 | PE10: (pe10, 10, Input), 668 | PE11: (pe11, 11, Input), 669 | PE12: (pe12, 12, Input), 670 | PE13: (pe13, 13, Input), 671 | PE14: (pe14, 14, Input), 672 | PE15: (pe15, 15, Input), 673 | ]); 674 | -------------------------------------------------------------------------------- /src/adc.rs: -------------------------------------------------------------------------------- 1 | //! # Analog to Digital Converter 2 | 3 | use core::marker::PhantomData; 4 | use core::sync::atomic::{self, Ordering}; 5 | 6 | use crate::dma::{dma0::C0, CircBuffer, CircReadDma, Receive, RxDma, Transfer, TransferPayload, W}; 7 | use crate::gpio::{gpioa, gpiob, gpioc, Analog}; 8 | use crate::pac::{ADC0, ADC1}; 9 | use crate::rcu::{BaseFrequency, Clocks, Enable, Rcu, Reset}; 10 | use embedded_dma::StaticWriteBuffer; 11 | use embedded_hal::adc::{Channel, OneShot}; 12 | 13 | #[doc(hidden)] 14 | pub trait AdcX {} 15 | impl AdcX for ADC0 {} 16 | impl AdcX for ADC1 {} 17 | 18 | /// Continuous mode 19 | pub struct Continuous; 20 | /// Scan mode 21 | pub struct Scan; 22 | 23 | /// ADC configuration 24 | pub struct Adc { 25 | rb: ADC, 26 | sample_time: SampleTime, 27 | align: Align, 28 | clocks: Clocks, 29 | } 30 | 31 | #[derive(Clone, Copy, Debug, PartialEq)] 32 | #[allow(non_camel_case_types)] 33 | /// ADC sampling time 34 | /// 35 | /// Options for the sampling time, each is T + 0.5 ADC clock cycles. 36 | pub enum SampleTime { 37 | /// 1.5 cycles sampling time 38 | T_1, 39 | /// 7.5 cycles sampling time 40 | T_7, 41 | /// 13.5 cycles sampling time 42 | T_13, 43 | /// 28.5 cycles sampling time 44 | T_28, 45 | /// 41.5 cycles sampling time 46 | T_41, 47 | /// 55.5 cycles sampling time 48 | T_55, 49 | /// 71.5 cycles sampling time 50 | T_71, 51 | /// 239.5 cycles sampling time 52 | T_239, 53 | } 54 | 55 | impl Default for SampleTime { 56 | /// Get the default sample time (currently 28.5 cycles) 57 | fn default() -> Self { 58 | SampleTime::T_28 59 | } 60 | } 61 | 62 | impl From for u8 { 63 | fn from(val: SampleTime) -> Self { 64 | use SampleTime::*; 65 | match val { 66 | T_1 => 0, 67 | T_7 => 1, 68 | T_13 => 2, 69 | T_28 => 3, 70 | T_41 => 4, 71 | T_55 => 5, 72 | T_71 => 6, 73 | T_239 => 7, 74 | } 75 | } 76 | } 77 | 78 | #[derive(Clone, Copy, Debug, PartialEq)] 79 | /// ADC data register alignment 80 | pub enum Align { 81 | /// Right alignment of output data 82 | Right, 83 | /// Left alignment of output data 84 | Left, 85 | } 86 | 87 | impl Default for Align { 88 | /// Default: right alignment 89 | fn default() -> Self { 90 | Align::Right 91 | } 92 | } 93 | 94 | impl From for bool { 95 | fn from(val: Align) -> Self { 96 | match val { 97 | Align::Right => false, 98 | Align::Left => true, 99 | } 100 | } 101 | } 102 | 103 | macro_rules! adc_pins { 104 | ($($pin:ty => $chan:expr),+ $(,)*) => { 105 | $( 106 | impl Channel for $pin { 107 | type ID = u8; 108 | fn channel() -> u8 { $chan } 109 | } 110 | )+ 111 | }; 112 | } 113 | 114 | adc_pins!( 115 | gpioa::PA0 => 0_u8, 116 | gpioa::PA1 => 1_u8, 117 | gpioa::PA2 => 2_u8, 118 | gpioa::PA3 => 3_u8, 119 | gpioa::PA4 => 4_u8, 120 | gpioa::PA5 => 5_u8, 121 | gpioa::PA6 => 6_u8, 122 | gpioa::PA7 => 7_u8, 123 | gpiob::PB0 => 8_u8, 124 | gpiob::PB1 => 9_u8, 125 | gpioc::PC0 => 10_u8, 126 | gpioc::PC1 => 11_u8, 127 | gpioc::PC2 => 12_u8, 128 | gpioc::PC3 => 13_u8, 129 | gpioc::PC4 => 14_u8, 130 | gpioc::PC5 => 15_u8, 131 | ); 132 | 133 | /// Stored ADC config can be restored using the `Adc::restore_cfg` method 134 | #[derive(Copy, Clone, Debug, PartialEq, Default)] 135 | pub struct StoredConfig(SampleTime, Align); 136 | 137 | fn delay(clocks: u64) { 138 | let c0 = riscv::register::mcycle::read64(); 139 | while riscv::register::mcycle::read64().wrapping_sub(c0) <= clocks {} 140 | } 141 | 142 | #[allow(non_camel_case_types)] 143 | #[derive(Copy, Clone, Debug, PartialEq)] 144 | pub enum ETSRC_A { 145 | /// Timer 0 Channel 0 146 | TIM0CH0, 147 | /// Timer 0 Channel 1 148 | TIM0CH1, 149 | /// Timer 0 Channel 2 150 | TIM0CH2, 151 | /// Timer 1 Channel 1 152 | TIM1CH1, 153 | /// Timer 1 TRGO 154 | TIM2TRGO, 155 | /// Timer 3 Channel 3 156 | TIM3CH3, 157 | /// EXTI Line 11 158 | EXTI11, 159 | /// SWRCST 160 | SWRCST, 161 | } 162 | 163 | impl From for u8 { 164 | fn from(src: ETSRC_A) -> u8 { 165 | match src { 166 | ETSRC_A::TIM0CH0 => 0b000, 167 | ETSRC_A::TIM0CH1 => 0b001, 168 | ETSRC_A::TIM0CH2 => 0b010, 169 | ETSRC_A::TIM1CH1 => 0b011, 170 | ETSRC_A::TIM2TRGO => 0b100, 171 | ETSRC_A::TIM3CH3 => 0b101, 172 | ETSRC_A::EXTI11 => 0b110, 173 | ETSRC_A::SWRCST => 0b111, 174 | } 175 | } 176 | } 177 | 178 | macro_rules! adc_hal { 179 | ($($ADC:ident: $adc:ident),+ $(,)*) => { $( 180 | impl Adc<$ADC> { 181 | /// Init a new Adc 182 | /// 183 | /// Sets all configurable parameters to one-shot defaults, performs a boot-time calibration. 184 | pub fn $adc(adc: $ADC, rcu: &mut Rcu) -> Self { 185 | let mut s = Self { 186 | rb: adc, 187 | sample_time: SampleTime::default(), 188 | align: Align::default(), 189 | clocks: rcu.clocks, 190 | }; 191 | s.enable_clock(rcu); 192 | s.power_down(); 193 | s.reset(rcu); 194 | s.setup_oneshot(); 195 | s.power_up(); 196 | 197 | // The manual states that we need to wait 14 ADC clock cycles after power-up before 198 | // starting calibration, we already delayed in the power-up process, but if the adc 199 | // clock is too low that was not enough 200 | if $ADC::base_frequency(rcu).0 < 2_500_000 { 201 | let cycles = rcu.clocks.sysclk().0 / $ADC::base_frequency(rcu).0 * 14; 202 | let delayed = rcu.clocks.sysclk().0 / 800_000; 203 | if cycles > delayed { 204 | delay((cycles - delayed) as u64); 205 | } 206 | } 207 | s.calibrate(); 208 | s 209 | } 210 | 211 | /// Save current ADC config 212 | pub fn save_cfg(&mut self) -> StoredConfig { 213 | StoredConfig(self.sample_time, self.align) 214 | } 215 | 216 | /// Restore saved ADC config 217 | pub fn restore_cfg(&mut self, cfg: StoredConfig) { 218 | self.sample_time = cfg.0; 219 | self.align = cfg.1; 220 | } 221 | 222 | /// Reset the ADC config to default, return existing config 223 | pub fn default_cfg(&mut self) -> StoredConfig { 224 | let cfg = self.save_cfg(); 225 | self.sample_time = SampleTime::default(); 226 | self.align = Align::default(); 227 | cfg 228 | } 229 | 230 | /// Set ADC sampling time 231 | /// 232 | /// Options can be found in [SampleTime]. 233 | pub fn set_sample_time(&mut self, t_samp: SampleTime) { 234 | self.sample_time = t_samp; 235 | } 236 | 237 | /// Set the Adc result alignment 238 | /// 239 | /// Options can be found in [Align]. 240 | pub fn set_align(&mut self, align: Align) { 241 | self.align = align; 242 | } 243 | 244 | /// Returns the largest possible sample value for the current settings 245 | pub fn max_sample(&self) -> u16 { 246 | match self.align { 247 | Align::Left => u16::max_value(), 248 | Align::Right => (1 << 12) - 1, 249 | } 250 | } 251 | 252 | #[inline(always)] 253 | pub fn set_external_trigger(&mut self, trigger: ETSRC_A) { 254 | // Actually safe since the resulting u8 comes from our From<> impl 255 | self.rb.ctl1.modify(|_, w| unsafe { w.etsrc().bits(trigger.into()) }) 256 | } 257 | 258 | fn power_up(&mut self) { 259 | self.rb.ctl1.modify(|_, w| w.adcon().set_bit()); 260 | 261 | // The reference manual says that a stabilization time is needed after power_up, 262 | // this time can be found in the datasheets. 263 | // Here we are delaying for approximately 1us, considering 1.25 instructions per 264 | // cycle. Is there a chip which needs more than 1us? 265 | delay((self.clocks.sysclk().0 / 800_000) as u64); 266 | } 267 | 268 | fn power_down(&mut self) { 269 | self.rb.ctl1.modify(|_, w| w.adcon().clear_bit()); 270 | } 271 | 272 | fn reset(&mut self, rcu: &mut Rcu) { 273 | $ADC::reset(rcu); 274 | } 275 | 276 | fn enable_clock(&mut self, rcu: &mut Rcu) { 277 | $ADC::enable(rcu) 278 | } 279 | 280 | fn disable_clock(&mut self, rcu: &mut Rcu) { 281 | $ADC::disable(rcu) 282 | } 283 | 284 | fn calibrate(&mut self) { 285 | // Reset calibration 286 | self.rb.ctl1.modify(|_, w| w.rstclb().set_bit()); 287 | while self.rb.ctl1.read().rstclb().bit_is_set() {} 288 | 289 | // Calibrate 290 | self.rb.ctl1.modify(|_, w| w.clb().set_bit()); 291 | while self.rb.ctl1.read().clb().bit_is_set() {} 292 | } 293 | 294 | fn setup_oneshot(&mut self) { 295 | self.rb.ctl1.modify(|_, w| unsafe { w 296 | .ctn().clear_bit() 297 | .eterc().set_bit() 298 | .etsrc().bits(0b111) 299 | }); 300 | self.rb.ctl0.modify(|_, w| w 301 | .sm().clear_bit() 302 | .disrc().set_bit() 303 | ); 304 | self.rb.rsq0.modify(|_, w| unsafe { w.rl().bits(0b0) }); 305 | } 306 | 307 | fn set_channel_sample_time(&mut self, chan: u8, sample_time: SampleTime) { 308 | let sample_time = sample_time.into(); 309 | 310 | // Safe because the value is controlled by our From for u8 311 | unsafe { 312 | match chan { 313 | 0 => self.rb.sampt1.modify(|_, w| w.spt0().bits(sample_time)), 314 | 1 => self.rb.sampt1.modify(|_, w| w.spt1().bits(sample_time)), 315 | 2 => self.rb.sampt1.modify(|_, w| w.spt2().bits(sample_time)), 316 | 3 => self.rb.sampt1.modify(|_, w| w.spt3().bits(sample_time)), 317 | 4 => self.rb.sampt1.modify(|_, w| w.spt4().bits(sample_time)), 318 | 5 => self.rb.sampt1.modify(|_, w| w.spt5().bits(sample_time)), 319 | 6 => self.rb.sampt1.modify(|_, w| w.spt6().bits(sample_time)), 320 | 7 => self.rb.sampt1.modify(|_, w| w.spt7().bits(sample_time)), 321 | 8 => self.rb.sampt1.modify(|_, w| w.spt8().bits(sample_time)), 322 | 9 => self.rb.sampt1.modify(|_, w| w.spt9().bits(sample_time)), 323 | 10 => self.rb.sampt0.modify(|_, w| w.spt10().bits(sample_time)), 324 | 11 => self.rb.sampt0.modify(|_, w| w.spt11().bits(sample_time)), 325 | 12 => self.rb.sampt0.modify(|_, w| w.spt12().bits(sample_time)), 326 | 13 => self.rb.sampt0.modify(|_, w| w.spt13().bits(sample_time)), 327 | 14 => self.rb.sampt0.modify(|_, w| w.spt14().bits(sample_time)), 328 | 15 => self.rb.sampt0.modify(|_, w| w.spt15().bits(sample_time)), 329 | 16 => self.rb.sampt0.modify(|_, w| w.spt16().bits(sample_time)), 330 | 17 => self.rb.sampt0.modify(|_, w| w.spt17().bits(sample_time)), 331 | _ => unreachable!(), 332 | } 333 | } 334 | } 335 | 336 | fn set_regular_sequence(&mut self, channels: &[u8]) { 337 | let len = channels.len(); 338 | let bits = channels.iter().take(6).enumerate().fold(0u32, |s, (i, c)| 339 | s | ((*c as u32) << (i * 5)) 340 | ); 341 | self.rb.rsq2.write(|w| unsafe { w.bits(bits) }); 342 | if len > 6 { 343 | let bits = channels.iter().skip(6).take(6).enumerate().fold(0u32, |s, (i, c)| 344 | s | ((*c as u32) << (i * 5)) 345 | ); 346 | self.rb.rsq1.write(|w| unsafe { w.bits(bits) }); 347 | } 348 | if len > 12 { 349 | let bits = channels.iter().skip(12).take(4).enumerate().fold(0u32, |s, (i, c)| 350 | s | ((*c as u32) << (i * 5)) 351 | ); 352 | self.rb.rsq0.write(|w| unsafe { w.bits(bits) }); 353 | } 354 | self.rb.rsq0.modify(|_, w| unsafe { w.rl().bits((len-1) as u8) }); 355 | } 356 | 357 | fn set_continuous_mode(&mut self, continuous: bool) { 358 | self.rb.ctl1.modify(|_, w| w.ctn().bit(continuous)); 359 | } 360 | 361 | fn set_discontinuous_mode(&mut self, channels_count: Option) { 362 | self.rb.ctl0.modify(|_, w| match channels_count { 363 | Some(count) => unsafe { w.disrc().set_bit().disnum().bits(count) }, 364 | None => w.disrc().clear_bit(), 365 | }) 366 | } 367 | 368 | /// Performs an ADC conversion 369 | 370 | /// NOTE: Conversions can be started by writing a 1 to the ADCON 371 | /// bit in the `CTL1` while it is already 1, and no other bits 372 | /// are being written in the same operation. This means that 373 | /// the EOC bit *might* be set already when entering this function 374 | /// which can cause a read of stale values 375 | /// 376 | /// The check for `ctl1.swrcst.bit_is_set` *should* fix it, but 377 | /// does not. Therefore, ensure you do not do any no-op modifications 378 | /// to `ctl1` just before calling this function 379 | fn convert(&mut self, chan: u8) -> u16 { 380 | // Dummy read in case something accidentally triggered 381 | // a conversion by writing to CTL1 without changing any 382 | // of the bits 383 | self.rb.rdata.read().rdata().bits(); 384 | 385 | self.set_channel_sample_time(chan, self.sample_time); 386 | self.rb.rsq2.modify(|_, w| unsafe { w.rsq0().bits(chan) }); 387 | 388 | // ADC start conversion of regular sequence 389 | self.rb.ctl1.modify(|_, w| 390 | w 391 | .swrcst().set_bit() 392 | .dal().bit(self.align.into()) 393 | ); 394 | while self.rb.ctl1.read().swrcst().bit_is_set() {} 395 | // ADC wait for conversion results 396 | while self.rb.stat.read().eoc().bit_is_clear() {} 397 | 398 | let res = self.rb.rdata.read().rdata().bits(); 399 | res 400 | } 401 | 402 | /// Powers down the ADC, disables the ADC clock and releases the ADC Peripheral 403 | pub fn release(mut self, rcu: &mut Rcu) -> $ADC { 404 | self.power_down(); 405 | self.disable_clock(rcu); 406 | self.rb 407 | } 408 | } 409 | 410 | impl ChannelTimeSequence for Adc<$ADC> { 411 | #[inline(always)] 412 | fn set_channel_sample_time(&mut self, chan: u8, sample_time: SampleTime) { 413 | self.set_channel_sample_time(chan, sample_time); 414 | } 415 | #[inline(always)] 416 | fn set_regular_sequence (&mut self, channels: &[u8]) { 417 | self.set_regular_sequence(channels); 418 | } 419 | #[inline(always)] 420 | fn set_continuous_mode(&mut self, continuous: bool) { 421 | self.set_continuous_mode(continuous); 422 | } 423 | #[inline(always)] 424 | fn set_discontinuous_mode(&mut self, channels: Option) { 425 | self.set_discontinuous_mode(channels); 426 | } 427 | } 428 | 429 | impl OneShot<$ADC, WORD, PIN> for Adc<$ADC> 430 | where 431 | WORD: From, 432 | PIN: Channel<$ADC, ID = u8>, 433 | { 434 | type Error = (); 435 | 436 | fn read(&mut self, _pin: &mut PIN) -> nb::Result { 437 | let res = self.convert(PIN::channel()); 438 | Ok(res.into()) 439 | } 440 | } 441 | )+}; 442 | } 443 | 444 | adc_hal!(ADC0: adc0, ADC1: adc1); 445 | 446 | impl Adc { 447 | fn read_aux(&mut self, chan: u8) -> u16 { 448 | let tsv_off = if self.rb.ctl1.read().tsvren().bit_is_clear() { 449 | self.rb.ctl1.modify(|_, w| w.tsvren().set_bit()); 450 | 451 | // The reference manual says that a stabilization time is needed after the powering the 452 | // sensor, this time can be found in the datasheets. 453 | // Here we are delaying for approximately 10us, considering 1.25 instructions per 454 | // cycle. Do we support a chip which needs more than 10us ? 455 | delay((self.clocks.sysclk().0 / 80_000) as u64); 456 | true 457 | } else { 458 | false 459 | }; 460 | 461 | let val = self.convert(chan); 462 | 463 | if tsv_off { 464 | self.rb.ctl1.modify(|_, w| w.tsvren().clear_bit()); 465 | } 466 | 467 | val 468 | } 469 | 470 | /// Temperature sensor is connected to channel 16 on ADC0. This sensor can be used 471 | /// to measure ambient temperature of the device. However note that the returned 472 | /// value is not an absolute temperature value. 473 | /// 474 | /// In particular, according to section 11.4.11 from Reference Manual: 475 | /// "The output voltage of the temperature sensor changes linearly with temperature. Because 476 | /// there is an offset, which is up to 45 °C and varies from chip to chip due to process 477 | /// variation, the internal temperature sensor is more suited for applications that detect 478 | /// temperature variations instead of absolute temperature. When it is used to detect accurate 479 | /// temperature, an external temperature sensor part should be used to calibrate the offset 480 | /// error." 481 | /// 482 | /// Formula to calculate temperature value is also taken from the section 11.4.11. 483 | pub fn read_temp(&mut self) -> i32 { 484 | /// According to section 4.14 "Temperature sensor characteristics" 485 | /// from GD32VF1xx datasheets, TS constants values are as follows: 486 | /// AVG_SLOPE - average slope 487 | /// V_25 - temperature sensor ADC voltage at 25°C 488 | const AVG_SLOPE: i32 = 41; 489 | const V_25: i32 = 1450; 490 | 491 | let prev_cfg = self.save_cfg(); 492 | 493 | // recommended ADC sampling for temperature sensor is 17.1 usec, 494 | // so use the following approximate settings 495 | // to support all ADC frequencies 496 | let sample_time = match self.clocks.pclk2().0 { 497 | 0..=1_200_000 => SampleTime::T_1, 498 | 1_200_001..=1_500_000 => SampleTime::T_7, 499 | 1_500_001..=2_400_000 => SampleTime::T_13, 500 | 2_400_001..=3_100_000 => SampleTime::T_28, 501 | 3_100_001..=4_000_000 => SampleTime::T_41, 502 | 4_000_001..=5_000_000 => SampleTime::T_55, 503 | 5_000_001..=14_000_000 => SampleTime::T_71, 504 | _ => SampleTime::T_239, 505 | }; 506 | 507 | self.set_sample_time(sample_time); 508 | let val_temp: i32 = self.read_aux(16u8).into(); 509 | let val_vref: i32 = self.read_aux(17u8).into(); 510 | let v_sense = val_temp * 1200 / val_vref; 511 | 512 | self.restore_cfg(prev_cfg); 513 | 514 | (V_25 - v_sense) * 10 / AVG_SLOPE + 25 515 | } 516 | 517 | /// Internal reference voltage Vrefint is connected to channel 17 on ADC0. 518 | /// The reference manual nor datasheet indicate what the typical value for this reference 519 | /// voltage is, so it will be assumed to be the same as the STM32F1xx this chip is a clone of. 520 | /// According to section 5.3.4 "Embedded reference voltage" from STM32F1xx 521 | /// datasheets, typical value of this reference voltage is 1200 mV. 522 | /// 523 | /// This value is useful when ADC readings need to be converted into voltages. 524 | /// For instance, reading from any ADC channel can be converted into voltage (mV) 525 | /// using the following formula: 526 | /// v_chan = adc.read(chan) * 1200 / adc.read_vref() 527 | pub fn read_vref(&mut self) -> u16 { 528 | self.read_aux(17u8) 529 | } 530 | } 531 | 532 | pub struct AdcPayload { 533 | adc: Adc, 534 | pins: PINS, 535 | _mode: PhantomData, 536 | } 537 | 538 | pub trait ChannelTimeSequence { 539 | /// Set ADC sampling time for particular channel 540 | fn set_channel_sample_time(&mut self, chan: u8, sample_time: SampleTime); 541 | /// ADC Set a Regular Channel Conversion Sequence 542 | /// 543 | /// Define a sequence of channels to be converted as a regular group. 544 | fn set_regular_sequence(&mut self, channels: &[u8]); 545 | /// Set ADC continuous conversion 546 | /// 547 | /// When continuous conversion is enabled conversion does not stop at the last selected group 548 | /// channel but continues again from the first selected group channel. 549 | fn set_continuous_mode(&mut self, continuous: bool); 550 | /// Set ADC discontinuous mode 551 | /// 552 | /// It can be used to convert a short sequence of conversions (up to 8) which is a part of the 553 | /// regular sequence of conversions. 554 | fn set_discontinuous_mode(&mut self, channels_count: Option); 555 | } 556 | 557 | /// Set channel sequence and sample times for custom pins 558 | /// 559 | /// Example: 560 | /// ```rust 561 | /// use gd32vf103xx_hal::adc::{self, Adc, ChannelTimeSequence, SetChannels}; 562 | /// use gd32vf103xx_hal::gpio::Analog; 563 | /// use gd32vf103xx_hal::gpio::gpioa::{PA0, PA2}; 564 | /// use gd32vf103_pac::ADC0; 565 | /// pub struct AdcPins(PA0, PA2); 566 | /// impl SetChannels for Adc { 567 | /// fn set_samples(&mut self) { 568 | /// self.set_channel_sample_time(0, adc::SampleTime::T_28); 569 | /// self.set_channel_sample_time(2, adc::SampleTime::T_28); 570 | /// } 571 | /// fn set_sequence(&mut self) { 572 | /// self.set_regular_sequence(&[0, 2, 0, 2]); 573 | /// // Optionally we can set continuous scan mode 574 | /// self.set_continuous_mode(true); 575 | /// // Also we can use discontinuous conversion (3 channels per conversion) 576 | /// self.set_discontinuous_mode(Some(3)); 577 | /// } 578 | /// } 579 | /// ``` 580 | pub trait SetChannels: ChannelTimeSequence { 581 | fn set_samples(&mut self); 582 | fn set_sequence(&mut self); 583 | } 584 | 585 | pub type AdcDma = RxDma, C0>; 586 | 587 | impl Receive for AdcDma { 588 | type RxChannel = C0; 589 | type TransmittedWord = u16; 590 | } 591 | 592 | impl TransferPayload for AdcDma { 593 | fn start(&mut self) { 594 | self.channel.start(); 595 | self.payload.adc.rb.ctl1.modify(|_, w| w.ctn().set_bit()); 596 | self.payload.adc.rb.ctl1.modify(|_, w| w.adcon().set_bit()); 597 | } 598 | fn stop(&mut self) { 599 | self.channel.stop(); 600 | self.payload.adc.rb.ctl1.modify(|_, w| w.ctn().clear_bit()); 601 | } 602 | } 603 | 604 | impl TransferPayload for AdcDma { 605 | fn start(&mut self) { 606 | self.channel.start(); 607 | self.payload.adc.rb.ctl1.modify(|_, w| w.adcon().set_bit()); 608 | } 609 | fn stop(&mut self) { 610 | self.channel.stop(); 611 | } 612 | } 613 | 614 | impl Adc { 615 | pub fn with_dma(mut self, pins: PIN, dma_ch: C0) -> AdcDma 616 | where 617 | PIN: Channel, 618 | { 619 | self.rb.ctl0.modify(|_, w| w.disrc().clear_bit()); 620 | self.rb.ctl1.modify(|_, w| w.dal().bit(self.align.into())); 621 | self.set_channel_sample_time(PIN::channel(), self.sample_time); 622 | self.rb.rsq2.modify(|_, w| unsafe { w.rsq0().bits(PIN::channel()) }); 623 | self.rb.ctl1.modify(|_, w| w.dma().set_bit()); 624 | 625 | RxDma { payload: AdcPayload { adc: self, pins, _mode: PhantomData }, channel: dma_ch } 626 | } 627 | 628 | pub fn with_scan_dma(mut self, pins: PINS, dma_ch: C0) -> AdcDma 629 | where 630 | Self: SetChannels, 631 | { 632 | self.rb.ctl1.modify(|_, w| w 633 | .adcon().clear_bit() 634 | .dma().clear_bit() 635 | .ctn().clear_bit() 636 | .dal().bit(self.align.into()) 637 | ); 638 | self.rb.ctl0.modify(|_, w| w.sm().set_bit().disrc().clear_bit()); 639 | self.set_samples(); 640 | self.set_sequence(); 641 | self.rb.ctl1.modify(|_, w| w.dma().set_bit().adcon().set_bit()); 642 | 643 | RxDma { payload: AdcPayload { adc: self, pins, _mode: PhantomData }, channel: dma_ch } 644 | } 645 | } 646 | 647 | impl AdcDma 648 | where 649 | Self: TransferPayload, 650 | { 651 | pub fn split(mut self) -> (Adc, PINS, C0) { 652 | self.stop(); 653 | 654 | let AdcDma { payload, channel } = self; 655 | payload.adc.rb.ctl1.modify(|_, w| w.dma().clear_bit()); 656 | payload.adc.rb.ctl0.modify(|_, w| w.disrc().set_bit()); 657 | 658 | (payload.adc, payload.pins, channel) 659 | } 660 | } 661 | 662 | impl AdcDma 663 | where 664 | Self: TransferPayload 665 | { 666 | pub fn split(mut self) -> (Adc, PINS, C0) { 667 | self.stop(); 668 | 669 | let AdcDma { payload, channel } = self; 670 | payload.adc.rb.ctl1.modify(|_, w| w.dma().clear_bit()); 671 | payload.adc.rb.ctl0.modify(|_, w| w.disrc().set_bit()); 672 | payload.adc.rb.ctl0.modify(|_, w| w.sm().clear_bit()); 673 | 674 | (payload.adc, payload.pins, channel) 675 | } 676 | } 677 | 678 | impl CircReadDma for AdcDma 679 | where 680 | Self: TransferPayload, 681 | &'static mut [B; 2]: StaticWriteBuffer, 682 | B: 'static, 683 | { 684 | fn circ_read(mut self, mut buffer: &'static mut [B; 2]) -> CircBuffer { 685 | // NOTE(unsafe) We own the buffer now and we won't call other `&mut` on it until the end of 686 | // the transfer 687 | let (ptr, len) = unsafe { buffer.static_write_buffer() }; 688 | unsafe { 689 | self.channel.set_peripheral_address(&(*ADC0::ptr()).rdata as *const _ as u32, false); 690 | self.channel.set_memory_address(ptr as u32, true); 691 | } 692 | self.channel.set_transfer_length(len); 693 | 694 | atomic::compiler_fence(Ordering::Release); 695 | self.channel.ctl().modify(|_, w| unsafe { w 696 | .m2m().clear_bit() 697 | .prio().bits(0b01) // Medium 698 | .mwidth().bits(0b01) // 16 bits 699 | .pwidth().bits(0b01) // 16 bits 700 | .cmen().set_bit() 701 | .dir().clear_bit() 702 | }); 703 | self.start(); 704 | 705 | CircBuffer::new(buffer, self) 706 | } 707 | } 708 | 709 | impl crate::dma::ReadDma for AdcDma 710 | where 711 | Self: TransferPayload, 712 | B: StaticWriteBuffer, 713 | { 714 | fn read(mut self, mut buffer: B) -> Transfer { 715 | // NOTE(unsafe) We own the buffer now and we won't call other `&mut` on it 716 | // until the end of the transfer. 717 | let (ptr, len) = unsafe { buffer.static_write_buffer() }; 718 | unsafe { 719 | self.channel.set_peripheral_address(&(*ADC0::ptr()).rdata as *const _ as u32, false); 720 | self.channel.set_memory_address(ptr as u32, true); 721 | } 722 | self.channel.set_transfer_length(len); 723 | 724 | atomic::compiler_fence(Ordering::Release); 725 | self.channel.ctl().modify(|_, w| unsafe { w 726 | .m2m().clear_bit() 727 | .prio().bits(0b01) // Medium 728 | .mwidth().bits(0b01) // 16 bits 729 | .pwidth().bits(0b01) // 16 bits 730 | .cmen().clear_bit() 731 | .dir().clear_bit() 732 | }); 733 | self.start(); 734 | 735 | Transfer::w(buffer, self) 736 | } 737 | } 738 | --------------------------------------------------------------------------------