├── .gitignore ├── bin ├── trap_riscv32i-unknown-none-elf.a └── trap_riscv32if-unknown-none-elf.a ├── hal_defaults.x ├── .cargo └── config.toml ├── assemble.bat ├── .github └── workflows │ ├── clippy.yml │ └── ci.yml ├── memory.x ├── assemble.ps1 ├── assemble.sh ├── Cargo.toml ├── examples ├── blinky.rs ├── serial.rs └── i2c_ssd1306.rs ├── LICENST-MIT ├── README.md ├── src ├── lib.rs ├── delay.rs ├── checksum.rs ├── interrupts.rs ├── spi.rs ├── serial.rs ├── i2c.rs ├── timer.rs ├── gpio.rs └── clock.rs ├── trap.S └── LICENSE-MULAN /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /bin/trap_riscv32i-unknown-none-elf.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjoernQ/bl602-hal/main/bin/trap_riscv32i-unknown-none-elf.a -------------------------------------------------------------------------------- /bin/trap_riscv32if-unknown-none-elf.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjoernQ/bl602-hal/main/bin/trap_riscv32if-unknown-none-elf.a -------------------------------------------------------------------------------- /hal_defaults.x: -------------------------------------------------------------------------------- 1 | PROVIDE(Gpio = DefaultHandler); 2 | PROVIDE(TimerCh0 = DefaultHandler); 3 | PROVIDE(TimerCh1 = DefaultHandler); 4 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.riscv32imac-unknown-none-elf] 2 | rustflags = [ 3 | "-C", "link-arg=-Tmemory.x", 4 | "-C", "link-arg=-Tlink.x", 5 | "-C", "link-arg=-Thal_defaults.x", 6 | ] 7 | runner = "cargo blflash --port=/dev/ttyUSB0" 8 | 9 | [build] 10 | target = "riscv32imac-unknown-none-elf" 11 | -------------------------------------------------------------------------------- /assemble.bat: -------------------------------------------------------------------------------- 1 | del bin\*.a /q 2 | 3 | set crate=bl602-hal 4 | 5 | riscv64-unknown-elf-gcc -ggdb3 -fdebug-prefix-map=%cd%=/%crate% -c -mabi=ilp32 -march=rv32i trap.S -o bin/%crate%.o 6 | riscv64-unknown-elf-ar crs bin/trap_riscv32i-unknown-none-elf.a bin/%crate%.o 7 | 8 | riscv64-unknown-elf-gcc -ggdb3 -fdebug-prefix-map=%cd%=/%crate% -c -mabi=ilp32f -march=rv32if trap.S -o bin/%crate%.o 9 | riscv64-unknown-elf-ar crs bin/trap_riscv32if-unknown-none-elf.a bin/%crate%.o 10 | 11 | del bin\%crate%.o 12 | -------------------------------------------------------------------------------- /.github/workflows/clippy.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: Clippy check 4 | jobs: 5 | clippy_check: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v2 9 | - uses: actions-rs/toolchain@v1 10 | with: 11 | profile: minimal 12 | toolchain: stable 13 | target: riscv32imac-unknown-none-elf 14 | override: true 15 | components: clippy 16 | - uses: actions-rs/clippy-check@v1 17 | with: 18 | token: ${{ secrets.GITHUB_TOKEN }} 19 | -------------------------------------------------------------------------------- /memory.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | ROM (rx) : ORIGIN = 0x21000000, LENGTH = 128K 4 | ITCM (wxa) : ORIGIN = 0x22008000, LENGTH = 48K 5 | DTCM (wxa) : ORIGIN = 0x22014000, LENGTH = 48K 6 | XIP_FLASH (rwx) : ORIGIN = 0x23000000, LENGTH = 16M 7 | WIFI_RAM (wxa) : ORIGIN = 0x42030000, LENGTH = 112K 8 | } 9 | 10 | REGION_ALIAS("REGION_TEXT", XIP_FLASH); 11 | REGION_ALIAS("REGION_RODATA", XIP_FLASH); 12 | REGION_ALIAS("REGION_DATA", ITCM); 13 | REGION_ALIAS("REGION_BSS", ITCM); 14 | REGION_ALIAS("REGION_HEAP", DTCM); 15 | REGION_ALIAS("REGION_STACK", DTCM); 16 | -------------------------------------------------------------------------------- /assemble.ps1: -------------------------------------------------------------------------------- 1 | # remove existing blobs because otherwise this will append object files to the old blobs 2 | Remove-Item -Force bin/*.a 3 | 4 | $crate = "riscv" 5 | 6 | riscv64-unknown-elf-gcc -ggdb3 -fdebug-prefix-map=$pwd=/$crate -c -mabi=ilp32 -march=rv32i trap.S -o bin/$crate.o 7 | riscv64-unknown-elf-ar crs bin/trap_riscv32i-unknown-none-elf.a bin/$crate.o 8 | 9 | riscv64-unknown-elf-gcc -ggdb3 -fdebug-prefix-map=$pwd=/$crate -c -mabi=ilp32f -march=rv32if trap.S -o bin/$crate.o 10 | riscv64-unknown-elf-ar crs bin/trap_riscv32if-unknown-none-elf.a bin/$crate.o 11 | 12 | Remove-Item bin/$crate.o 13 | -------------------------------------------------------------------------------- /assemble.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euxo pipefail 4 | 5 | crate=bl602-hal 6 | 7 | # remove existing blobs because otherwise this will append object files to the old blobs 8 | rm -f bin/*.a 9 | 10 | riscv64-unknown-elf-gcc -ggdb3 -fdebug-prefix-map=$(pwd)=/$crate -c -mabi=ilp32 -march=rv32i trap.S -o bin/$crate.o 11 | riscv64-unknown-elf-ar crs bin/trap_riscv32i-unknown-none-elf.a bin/$crate.o 12 | 13 | riscv64-unknown-elf-gcc -ggdb3 -fdebug-prefix-map=$(pwd)=/$crate -c -mabi=ilp32f -march=rv32if trap.S -o bin/$crate.o 14 | riscv64-unknown-elf-ar crs bin/trap_riscv32if-unknown-none-elf.a bin/$crate.o 15 | 16 | rm bin/$crate.o 17 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: Continuous Integration 4 | 5 | jobs: 6 | rust_check: 7 | name: Rust check 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | # Run checks on stable and nightly Rust 12 | rust: [stable, nightly] 13 | 14 | include: 15 | # Run check with MSRV as well 16 | - rust: 1.42.0 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Install Rust ${{ matrix.rust }} 21 | uses: actions-rs/toolchain@v1 22 | with: 23 | profile: minimal 24 | toolchain: ${{ matrix.rust }} 25 | target: riscv32imac-unknown-none-elf 26 | override: true 27 | - name: Run cargo check 28 | uses: actions-rs/cargo@v1 29 | with: 30 | command: check 31 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bl602-hal" 3 | version = "0.1.0" 4 | authors = ["Sipeed Co.,Ltd. ", "Luo Jia "] 5 | edition = "2018" 6 | license = "MIT OR MulanPSL-2.0" 7 | keywords = ["hal", "bl602", "riscv"] 8 | categories = ["embedded", "no-std", "hardware-support"] 9 | repository = "https://github.com/sipeed/bl602-hal" 10 | description = "HAL for the bl602 microcontroller" 11 | 12 | [dependencies] 13 | bl602-pac = { git = "https://github.com/sipeed/bl602-pac", branch = "main" } 14 | embedded-hal = "1.0.0-alpha.4" 15 | embedded-time = "0.10" 16 | riscv = "0.6.0" 17 | nb = "1.0" 18 | paste = "1.0" 19 | 20 | [dependencies.embedded-hal-zero] 21 | version = "0.2.5" 22 | package = "embedded-hal" 23 | 24 | [dev-dependencies] 25 | riscv-rt = "0.8.0" 26 | panic-halt = "0.2.0" 27 | ssd1306 = "0.6.0" 28 | embedded-graphics = "0.7.1" 29 | 30 | [build-dependencies] 31 | riscv-target = "0.1.2" 32 | -------------------------------------------------------------------------------- /examples/blinky.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use bl602_hal as hal; 5 | use hal::{ 6 | clock::{Strict, SysclkFreq, UART_PLL_FREQ}, 7 | pac, 8 | prelude::*, 9 | }; 10 | use panic_halt as _; 11 | 12 | #[riscv_rt::entry] 13 | fn main() -> ! { 14 | let dp = pac::Peripherals::take().unwrap(); 15 | let mut parts = dp.GLB.split(); 16 | 17 | // Set up all the clocks we need 18 | let clocks = Strict::new() 19 | .use_pll(40_000_000u32.Hz()) 20 | .sys_clk(SysclkFreq::Pll160Mhz) 21 | .uart_clk(UART_PLL_FREQ.Hz()) 22 | .freeze(&mut parts.clk_cfg); 23 | 24 | let mut gpio5 = parts.pin5.into_pull_down_output(); 25 | 26 | // Create a blocking delay function based on the current cpu frequency 27 | let mut d = bl602_hal::delay::McycleDelay::new(clocks.sysclk().0); 28 | 29 | loop { 30 | gpio5.try_set_high().unwrap(); 31 | d.try_delay_ms(1000).unwrap(); 32 | 33 | gpio5.try_set_low().unwrap(); 34 | d.try_delay_ms(1000).unwrap(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LICENST-MIT: -------------------------------------------------------------------------------- 1 | Copyright 2020 Sipeed Inc. 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bl602-hal 2 | 3 | Hardware Abstract Layer for BL602 RISC-V WiFi SoC in embedded Rust. 4 | 5 | Matrix: [#bl602-rust:matrix.org](https://matrix.to/#/#bl602-rust:matrix.org) 6 | 7 | ## Minimum Supported Rust Version 8 | 9 | The minimum supported Rust version (MSRV) for this project is Rust **v1.42.0**. The 10 | project might build on earlier versions, but this is the earliest version that 11 | is expected to work. 12 | 13 | ## Contributing 14 | 15 | We welcome the community to contribute to this project. Please fire an issue or pull request 16 | if you have any idea or contributions. Thank you! 17 | 18 | ## License 19 | 20 | This project is licensed under either of Mulan PSL v2 or MIT. 21 | 22 | ```text 23 | Copyright (c) 2020 Sipeed Co.,Ltd. 24 | bl602-hal is licensed under Mulan PSL v2. 25 | You can use this software according to the terms and conditions of the Mulan PSL v2. 26 | You may obtain a copy of Mulan PSL v2 at: 27 | 28 | http://license.coscl.org.cn/MulanPSL2 29 | 30 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 31 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 32 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 33 | See the Mulan PSL v2 for more details. 34 | ``` 35 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # HAL for the BL602 microcontroller 2 | //! 3 | //! This is an implementation of the [`embedded-hal`] traits for the BL602 microcontroller. 4 | //! 5 | //! [`embedded-hal`]: https://crates.io/crates/embedded-hal 6 | //! 7 | //! # Usage 8 | //! 9 | //! 10 | //! ## Commonly used setup 11 | //! 12 | //! ```rust 13 | //! // Get access to the device specific peripherals from the peripheral access crate 14 | //! let dp = pac::Peripherals::take().unwrap(); 15 | //! let mut parts = dp.GLB.split(); 16 | //! 17 | //! // Freeze the configuration of all the clocks in the system and store the frozen frequencies in 18 | //! // `clocks` 19 | //! let clocks = Strict::new().freeze(&mut parts.clk_cfg); 20 | //! ``` 21 | //! 22 | //! 23 | //! To avoid the linker to complain about missing symbols please add `hal_defaults.x` to `.cargo/config` like this 24 | //! ```toml 25 | //! rustflags = [ 26 | //! "-C", "link-arg=-Tmemory.x", 27 | //! "-C", "link-arg=-Tlink.x", 28 | //! "-C", "link-arg=-Thal_defaults.x", 29 | //! ] 30 | //! ``` 31 | //! 32 | 33 | #![no_std] 34 | 35 | pub use bl602_pac as pac; 36 | 37 | pub mod checksum; 38 | pub mod clock; 39 | pub mod delay; 40 | pub mod gpio; 41 | pub mod i2c; 42 | pub mod interrupts; 43 | pub mod serial; 44 | pub mod spi; 45 | pub mod timer; 46 | 47 | /// HAL crate prelude 48 | pub mod prelude { 49 | pub use crate::gpio::GlbExt as _bl602_hal_gpio_GlbExt; 50 | pub use embedded_hal::prelude::*; 51 | pub use embedded_time::rate::Extensions; 52 | } 53 | -------------------------------------------------------------------------------- /examples/serial.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use bl602_hal as hal; 5 | use core::fmt::Write; 6 | use hal::{ 7 | clock::{Strict, SysclkFreq, UART_PLL_FREQ}, 8 | pac, 9 | prelude::*, 10 | serial::*, 11 | }; 12 | use panic_halt as _; 13 | 14 | #[riscv_rt::entry] 15 | fn main() -> ! { 16 | let dp = pac::Peripherals::take().unwrap(); 17 | let mut parts = dp.GLB.split(); 18 | 19 | // Set up all the clocks we need 20 | let clocks = Strict::new() 21 | .use_pll(40_000_000u32.Hz()) 22 | .sys_clk(SysclkFreq::Pll160Mhz) 23 | .uart_clk(UART_PLL_FREQ.Hz()) 24 | .freeze(&mut parts.clk_cfg); 25 | 26 | // Set up uart output. Since this microcontroller has a pin matrix, 27 | // we need to set up both the pins and the muxs 28 | let pin16 = parts.pin16.into_uart_sig0(); 29 | let pin7 = parts.pin7.into_uart_sig7(); 30 | let mux0 = parts.uart_mux0.into_uart0_tx(); 31 | let mux7 = parts.uart_mux7.into_uart0_rx(); 32 | 33 | // Configure our UART to 115200Baud, and use the pins we configured above 34 | let mut serial = Serial::uart0( 35 | dp.UART, 36 | Config::default().baudrate(115_200.Bd()), 37 | ((pin16, mux0), (pin7, mux7)), 38 | clocks, 39 | ); 40 | 41 | // Create a blocking delay function based on the current cpu frequency 42 | let mut d = bl602_hal::delay::McycleDelay::new(clocks.sysclk().0); 43 | 44 | loop { 45 | serial.write_str("Hello Rust\r\n").ok(); 46 | d.try_delay_ms(1000).unwrap(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /examples/i2c_ssd1306.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use bl602_hal as hal; 5 | use embedded_graphics::{ 6 | mono_font::{ascii::FONT_6X10, MonoTextStyleBuilder}, 7 | pixelcolor::BinaryColor, 8 | prelude::Point, 9 | text::{Baseline, Text}, 10 | Drawable, 11 | }; 12 | use hal::{ 13 | clock::{Strict, SysclkFreq, UART_PLL_FREQ}, 14 | pac, 15 | prelude::*, 16 | }; 17 | use panic_halt as _; 18 | use ssd1306::mode::DisplayConfig; 19 | use ssd1306::{rotation::DisplayRotation, size::DisplaySize128x32, I2CDisplayInterface, Ssd1306}; 20 | 21 | #[riscv_rt::entry] 22 | fn main() -> ! { 23 | let dp = pac::Peripherals::take().unwrap(); 24 | let mut parts = dp.GLB.split(); 25 | 26 | // Set up all the clocks we need 27 | let clocks = Strict::new() 28 | .use_pll(40_000_000u32.Hz()) 29 | .sys_clk(SysclkFreq::Pll160Mhz) 30 | .uart_clk(UART_PLL_FREQ.Hz()) 31 | .i2c_clk(1_000_000u32.Hz()) 32 | .freeze(&mut parts.clk_cfg); 33 | 34 | let scl = parts.pin4.into_i2c_scl(); 35 | let sda = parts.pin5.into_i2c_sda(); 36 | let i2c = hal::i2c::I2c::i2c(dp.I2C, (scl, sda), 100_000u32.Hz(), clocks); 37 | 38 | let interface = I2CDisplayInterface::new(i2c); 39 | let mut display = Ssd1306::new(interface, DisplaySize128x32, DisplayRotation::Rotate0) 40 | .into_buffered_graphics_mode(); 41 | display.init().unwrap(); 42 | display.clear(); 43 | display.flush().unwrap(); 44 | 45 | let text_style = MonoTextStyleBuilder::new() 46 | .font(&FONT_6X10) 47 | .text_color(BinaryColor::On) 48 | .build(); 49 | 50 | Text::with_baseline("Hello BL602", Point::zero(), text_style, Baseline::Top) 51 | .draw(&mut display) 52 | .unwrap(); 53 | 54 | Text::with_baseline("Hello Rust!", Point::new(0, 16), text_style, Baseline::Top) 55 | .draw(&mut display) 56 | .unwrap(); 57 | 58 | display.flush().unwrap(); 59 | 60 | loop {} 61 | } 62 | -------------------------------------------------------------------------------- /src/delay.rs: -------------------------------------------------------------------------------- 1 | //! Delays 2 | 3 | use core::convert::Infallible; 4 | use embedded_hal::blocking::delay::{DelayMs, DelayUs}; 5 | 6 | /// Use RISCV machine-mode cycle counter (`mcycle`) as a delay provider. 7 | /// 8 | /// This can be used for high resolution delays for device initialization, 9 | /// bit-banging protocols, etc 10 | #[derive(Copy, Clone)] 11 | pub struct McycleDelay { 12 | core_frequency: u32, 13 | } 14 | 15 | impl McycleDelay { 16 | /// Constructs the delay provider based on core clock frequency `freq` 17 | pub fn new(freq: u32) -> Self { 18 | Self { 19 | /// System clock frequency, used to convert clock cycles 20 | /// into real-world time values 21 | core_frequency: freq, 22 | } 23 | } 24 | 25 | /// Retrieves the cycle count for the current HART 26 | #[inline] 27 | pub fn get_cycle_count() -> u64 { 28 | riscv::register::mcycle::read64() 29 | } 30 | 31 | /// Returns the number of elapsed cycles since `previous_cycle_count` 32 | #[inline] 33 | pub fn cycles_since(previous_cycle_count: u64) -> u64 { 34 | riscv::register::mcycle::read64().wrapping_sub(previous_cycle_count) 35 | } 36 | 37 | /// Performs a busy-wait loop until the number of cycles `cycle_count` has elapsed 38 | #[inline] 39 | pub fn delay_cycles(cycle_count: u64) { 40 | let start_cycle_count = McycleDelay::get_cycle_count(); 41 | 42 | while McycleDelay::cycles_since(start_cycle_count) <= cycle_count {} 43 | } 44 | } 45 | 46 | impl DelayUs for McycleDelay { 47 | type Error = Infallible; 48 | 49 | /// Performs a busy-wait loop until the number of microseconds `us` has elapsed 50 | #[inline] 51 | fn try_delay_us(&mut self, us: u64) -> Result<(), Infallible> { 52 | McycleDelay::delay_cycles((us * (self.core_frequency as u64)) / 1_000_000); 53 | 54 | Ok(()) 55 | } 56 | } 57 | 58 | impl DelayMs for McycleDelay { 59 | type Error = Infallible; 60 | 61 | /// Performs a busy-wait loop until the number of milliseconds `ms` has elapsed 62 | #[inline] 63 | fn try_delay_ms(&mut self, ms: u64) -> Result<(), Infallible> { 64 | McycleDelay::delay_cycles((ms * (self.core_frequency as u64)) / 1000); 65 | 66 | Ok(()) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /trap.S: -------------------------------------------------------------------------------- 1 | #define STORE sw 2 | #define LOAD lw 3 | #define LOG_REGBYTES 2 4 | #define REGBYTES (1 << LOG_REGBYTES) 5 | 6 | /* 7 | Trap entry point (_start_trap) 8 | 9 | Saves caller saved registers ra, t0..6, a0..7, calls _start_trap_rust, 10 | restores caller saved registers and then returns. 11 | */ 12 | .section .trap, "ax" 13 | .global _start_trap_hal 14 | .option norelax 15 | .align 6 16 | 17 | _start_trap_hal: 18 | addi sp, sp, -32*REGBYTES 19 | 20 | STORE ra, 0*REGBYTES(sp) 21 | STORE t0, 1*REGBYTES(sp) 22 | STORE t1, 2*REGBYTES(sp) 23 | STORE t2, 3*REGBYTES(sp) 24 | STORE t3, 4*REGBYTES(sp) 25 | STORE t4, 5*REGBYTES(sp) 26 | STORE t5, 6*REGBYTES(sp) 27 | STORE t6, 7*REGBYTES(sp) 28 | STORE a0, 8*REGBYTES(sp) 29 | STORE a1, 9*REGBYTES(sp) 30 | STORE a2, 10*REGBYTES(sp) 31 | STORE a3, 11*REGBYTES(sp) 32 | STORE a4, 12*REGBYTES(sp) 33 | STORE a5, 13*REGBYTES(sp) 34 | STORE a6, 14*REGBYTES(sp) 35 | STORE a7, 15*REGBYTES(sp) 36 | STORE s0, 16*REGBYTES(sp) 37 | STORE s1, 17*REGBYTES(sp) 38 | STORE s2, 18*REGBYTES(sp) 39 | STORE s3, 19*REGBYTES(sp) 40 | STORE s4, 20*REGBYTES(sp) 41 | STORE s5, 21*REGBYTES(sp) 42 | STORE s6, 22*REGBYTES(sp) 43 | STORE s7, 23*REGBYTES(sp) 44 | STORE s8, 24*REGBYTES(sp) 45 | STORE s9, 25*REGBYTES(sp) 46 | STORE s10, 26*REGBYTES(sp) 47 | STORE s11, 27*REGBYTES(sp) 48 | STORE gp, 28*REGBYTES(sp) 49 | STORE tp, 29*REGBYTES(sp) 50 | 51 | addi s0, sp, 32*REGBYTES 52 | STORE s0, 30*REGBYTES(sp) 53 | 54 | add a0, sp, zero 55 | jal ra, _start_trap_rust_hal 56 | 57 | LOAD ra, 0*REGBYTES(sp) 58 | LOAD t0, 1*REGBYTES(sp) 59 | LOAD t1, 2*REGBYTES(sp) 60 | LOAD t2, 3*REGBYTES(sp) 61 | LOAD t3, 4*REGBYTES(sp) 62 | LOAD t4, 5*REGBYTES(sp) 63 | LOAD t5, 6*REGBYTES(sp) 64 | LOAD t6, 7*REGBYTES(sp) 65 | LOAD a0, 8*REGBYTES(sp) 66 | LOAD a1, 9*REGBYTES(sp) 67 | LOAD a2, 10*REGBYTES(sp) 68 | LOAD a3, 11*REGBYTES(sp) 69 | LOAD a4, 12*REGBYTES(sp) 70 | LOAD a5, 13*REGBYTES(sp) 71 | LOAD a6, 14*REGBYTES(sp) 72 | LOAD a7, 15*REGBYTES(sp) 73 | LOAD s0, 16*REGBYTES(sp) 74 | LOAD s1, 17*REGBYTES(sp) 75 | LOAD s2, 18*REGBYTES(sp) 76 | LOAD s3, 19*REGBYTES(sp) 77 | LOAD s4, 20*REGBYTES(sp) 78 | LOAD s5, 21*REGBYTES(sp) 79 | LOAD s6, 22*REGBYTES(sp) 80 | LOAD s7, 23*REGBYTES(sp) 81 | LOAD s8, 24*REGBYTES(sp) 82 | LOAD s9, 25*REGBYTES(sp) 83 | LOAD s10, 26*REGBYTES(sp) 84 | LOAD s11, 27*REGBYTES(sp) 85 | LOAD gp, 28*REGBYTES(sp) 86 | LOAD tp, 29*REGBYTES(sp) 87 | LOAD sp, 30*REGBYTES(sp) 88 | 89 | # SP was restored from the original SP 90 | mret 91 | -------------------------------------------------------------------------------- /src/checksum.rs: -------------------------------------------------------------------------------- 1 | //! Hardware checksum engine 2 | 3 | use bl602_pac::CKS; 4 | 5 | /// Checksum engine abstraction 6 | /// 7 | /// # Examples 8 | /// 9 | /// ```no_run 10 | /// use bl602_hal::pac; 11 | /// use bl602_hal::checksum::{Checksum, Endianness}; 12 | /// 13 | /// fn main() -> ! { 14 | /// let dp = pac::Peripherals::take().unwrap(); 15 | /// let checksum = Checksum::new(dp.CKS, Endianness::Little); 16 | /// 17 | /// checksum.write(&[ 18 | /// 0x45, 0x00, 0x00, 0x73, 0x00, 0x00, 0x40, 0x00, 0x40, 0x11, 0x00, 0x00, 0xc0, 0xa8, 0x00, 19 | /// 0x01, 0xc0, 0xa8, 0x00, 0xc7, 20 | /// ]); 21 | /// 22 | /// assert_eq!(checksum.result(), u16::from_be_bytes([0xb8, 0x61])); 23 | /// 24 | /// loop {} 25 | /// } 26 | /// ``` 27 | pub struct Checksum { 28 | cks: CKS, 29 | } 30 | 31 | /// The endianness used when computing checksums. 32 | pub enum Endianness { 33 | /// Big endian 34 | Big, 35 | /// Little endian 36 | Little, 37 | } 38 | 39 | impl Checksum { 40 | /// Resets the CKS state and returns a new `Checksum` instance that is configured to compute 41 | /// checksums with the given `endianness`. 42 | /// 43 | /// This takes ownership of the `CKS` peripheral to ensure that the state won't be modified or 44 | /// reset somewhere else 45 | pub fn new(cks: CKS, endianness: Endianness) -> Self { 46 | let checksum = Self { cks }; 47 | 48 | checksum.reset(endianness); 49 | 50 | checksum 51 | } 52 | 53 | /// Resets the CKS peripheral while setting the `endianness`. 54 | #[inline(always)] 55 | pub fn reset(&self, endianness: Endianness) { 56 | self.cks.cks_config.write(|w| { 57 | // Set `cr_cks_clr` to `1` in order to clear the checksum engine state 58 | w.cr_cks_clr() 59 | .set_bit() 60 | // Set the `cr_cks_byte_swap` bit to 1 when big endian, 0 when little endian. 61 | .cr_cks_byte_swap() 62 | .bit(match endianness { 63 | Endianness::Big => true, 64 | Endianness::Little => false, 65 | }) 66 | }); 67 | } 68 | 69 | /// Sets the `endianness` of the checksum engine. 70 | #[inline(always)] 71 | pub fn set_endianness(&self, endianness: Endianness) { 72 | // Set the `cr_cks_byte_swap` bit to 1 when big endian, 0 when little endian. 73 | self.cks.cks_config.write(|w| { 74 | w.cr_cks_byte_swap().bit(match endianness { 75 | Endianness::Big => true, 76 | Endianness::Little => false, 77 | }) 78 | }); 79 | } 80 | 81 | /// Writes the given slice of `bytes` to the checksum engine, one at a time. 82 | #[inline(always)] 83 | pub fn write(&self, bytes: &[u8]) { 84 | for byte in bytes { 85 | self.cks.data_in.write(|w| unsafe { w.bits(*byte as u32) }); 86 | } 87 | } 88 | 89 | /// Reads the computed 16-bit result from the checksum engine. 90 | #[inline(always)] 91 | pub fn result(&self) -> u16 { 92 | self.cks.cks_out.read().bits() as u16 93 | } 94 | 95 | /// Releases the checksum (`CKS`) peripheral. 96 | pub fn free(self) -> CKS { 97 | self.cks 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/interrupts.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | # Interrupt Management 3 | Interrupts can be enabled, disabled and cleared. 4 | 5 | ## Example 6 | ```rust 7 | enable_interrupt(TimerCh0); 8 | 9 | // ... 10 | 11 | #[no_mangle] 12 | fn TimerCh0() { 13 | // .. 14 | clear_interrupt(TimerCh0); 15 | } 16 | ``` 17 | 18 | ## The following functions can be implemented as interrupt handlers 19 | ```rust 20 | fn Gpio(); 21 | fn TimerCh0(); 22 | fn TimerCh1(); 23 | ``` 24 | */ 25 | 26 | use riscv::register::mcause; 27 | 28 | extern "C" { 29 | fn Gpio(trap_frame: &mut TrapFrame); 30 | fn TimerCh0(trap_frame: &mut TrapFrame); 31 | fn TimerCh1(trap_frame: &mut TrapFrame); 32 | } 33 | 34 | // see components\bl602\bl602_std\bl602_std\RISCV\Core\Include\clic.h 35 | // see components\hal_drv\bl602_hal\bl_irq.c 36 | const IRQ_NUM_BASE: u32 = 16; 37 | const CLIC_HART0_ADDR: u32 = 0x02800000; 38 | const CLIC_INTIE: u32 = 0x400; 39 | const CLIC_INTIP: u32 = 0x000; 40 | 41 | const GPIO_IRQ: u32 = IRQ_NUM_BASE + 44; 42 | const TIMER_CH0_IRQ: u32 = IRQ_NUM_BASE + 36; 43 | const TIMER_CH1_IRQ: u32 = IRQ_NUM_BASE + 37; 44 | 45 | #[doc(hidden)] 46 | #[no_mangle] 47 | pub fn _setup_interrupts() { 48 | extern "C" { 49 | pub fn _start_trap_hal(); 50 | } 51 | 52 | let new_mtvec = _start_trap_hal as usize; 53 | unsafe { 54 | riscv::interrupt::disable(); 55 | riscv::register::mtvec::write(new_mtvec | 2, riscv::register::mtvec::TrapMode::Direct); 56 | } 57 | 58 | // disable all interrupts 59 | let e = unsafe { 60 | core::slice::from_raw_parts_mut((CLIC_HART0_ADDR + CLIC_INTIE) as *mut u32, 16 + 8) 61 | }; 62 | let p = unsafe { 63 | core::slice::from_raw_parts_mut((CLIC_HART0_ADDR + CLIC_INTIP) as *mut u32, 16 + 8) 64 | }; 65 | 66 | e.iter_mut().for_each(|v| *v = 0); 67 | p.iter_mut().for_each(|v| *v = 0); 68 | 69 | unsafe { 70 | riscv::interrupt::enable(); 71 | } 72 | } 73 | 74 | /// Registers saved in trap handler 75 | #[doc(hidden)] 76 | #[allow(missing_docs)] 77 | #[derive(Debug, Default, Clone, Copy)] 78 | #[repr(C)] 79 | pub struct TrapFrame { 80 | pub ra: usize, 81 | pub t0: usize, 82 | pub t1: usize, 83 | pub t2: usize, 84 | pub t3: usize, 85 | pub t4: usize, 86 | pub t5: usize, 87 | pub t6: usize, 88 | pub a0: usize, 89 | pub a1: usize, 90 | pub a2: usize, 91 | pub a3: usize, 92 | pub a4: usize, 93 | pub a5: usize, 94 | pub a6: usize, 95 | pub a7: usize, 96 | pub s0: usize, 97 | pub s1: usize, 98 | pub s2: usize, 99 | pub s3: usize, 100 | pub s4: usize, 101 | pub s5: usize, 102 | pub s6: usize, 103 | pub s7: usize, 104 | pub s8: usize, 105 | pub s9: usize, 106 | pub s10: usize, 107 | pub s11: usize, 108 | pub gp: usize, 109 | pub tp: usize, 110 | pub sp: usize, 111 | } 112 | 113 | /// # Safety 114 | /// 115 | /// This function is called from an assembly trap handler. 116 | #[doc(hidden)] 117 | #[link_section = ".trap.rust"] 118 | #[export_name = "_start_trap_rust_hal"] 119 | pub unsafe extern "C" fn start_trap_rust_hal(trap_frame: *mut TrapFrame) { 120 | extern "C" { 121 | pub fn _start_trap_rust(trap_frame: *const TrapFrame); 122 | } 123 | 124 | let cause = mcause::read(); 125 | if cause.is_exception() { 126 | _start_trap_rust(trap_frame); 127 | } else { 128 | let code = cause.code(); 129 | if code < IRQ_NUM_BASE as usize { 130 | _start_trap_rust(trap_frame); 131 | } else { 132 | let interrupt_number = (code & 0xff) as u32; 133 | let interrupt = Interrupt::from(interrupt_number); 134 | 135 | match interrupt { 136 | Interrupt::Unknown => _start_trap_rust(trap_frame), 137 | Interrupt::Gpio => Gpio(trap_frame.as_mut().unwrap()), 138 | Interrupt::TimerCh0 => TimerCh0(trap_frame.as_mut().unwrap()), 139 | Interrupt::TimerCh1 => TimerCh1(trap_frame.as_mut().unwrap()), 140 | }; 141 | } 142 | } 143 | } 144 | 145 | /// Available interrupts 146 | pub enum Interrupt { 147 | #[doc(hidden)] 148 | Unknown, 149 | /// GPIO Interrupt 150 | Gpio, 151 | /// Timer Channel 0 Interrupt 152 | TimerCh0, 153 | /// Timer Channel 1 Interrupt 154 | TimerCh1, 155 | } 156 | 157 | impl Interrupt { 158 | fn to_irq(&self) -> u32 { 159 | match &self { 160 | Interrupt::Unknown => panic!("Unknown interrupt has no irq number"), 161 | Interrupt::Gpio => GPIO_IRQ, 162 | Interrupt::TimerCh0 => TIMER_CH0_IRQ, 163 | Interrupt::TimerCh1 => TIMER_CH1_IRQ, 164 | } 165 | } 166 | 167 | fn from(irq: u32) -> Interrupt { 168 | match irq { 169 | GPIO_IRQ => Interrupt::Gpio, 170 | TIMER_CH0_IRQ => Interrupt::TimerCh0, 171 | TIMER_CH1_IRQ => Interrupt::TimerCh1, 172 | _ => Interrupt::Unknown, 173 | } 174 | } 175 | } 176 | 177 | /// Enable the given interrupt 178 | pub fn enable_interrupt(interrupt: Interrupt) { 179 | let irq = interrupt.to_irq(); 180 | let ptr = (CLIC_HART0_ADDR + CLIC_INTIE + irq) as *mut u8; 181 | unsafe { 182 | ptr.write_volatile(1); 183 | } 184 | } 185 | 186 | /// Disable the given interrupt 187 | pub fn disable_interrupt(interrupt: Interrupt) { 188 | let irq = interrupt.to_irq(); 189 | let ptr = (CLIC_HART0_ADDR + CLIC_INTIE + irq) as *mut u8; 190 | unsafe { 191 | ptr.write_volatile(0); 192 | } 193 | } 194 | 195 | /// Clear the given interrupt. 196 | /// Usually the interrupt needs to be cleared also on the peripheral level. 197 | pub fn clear_interrupt(interrupt: Interrupt) { 198 | let irq = interrupt.to_irq(); 199 | let ptr = (CLIC_HART0_ADDR + CLIC_INTIP + irq) as *mut u8; 200 | unsafe { 201 | ptr.write_volatile(0); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /LICENSE-MULAN: -------------------------------------------------------------------------------- 1 | 木兰宽松许可证, 第2版 2 | 3 | 2020年1月 http://license.coscl.org.cn/MulanPSL2 4 | 5 | 您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束: 6 | 7 | 0. 定义 8 | 9 | “软件” 是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。 10 | 11 | “贡献” 是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。 12 | 13 | “贡献者” 是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。 14 | 15 | “法人实体” 是指提交贡献的机构及其“关联实体”。 16 | 17 | “关联实体” 是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。 18 | 19 | 1. 授予版权许可 20 | 21 | 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。 22 | 23 | 2. 授予专利许可 24 | 25 | 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。 26 | 27 | 3. 无商标许可 28 | 29 | “本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。 30 | 31 | 4. 分发限制 32 | 33 | 您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。 34 | 35 | 5. 免责声明与责任限制 36 | 37 | “软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。 38 | 39 | 6. 语言 40 | 41 | “本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。 42 | 43 | 条款结束 44 | 45 | 如何将木兰宽松许可证,第2版,应用到您的软件 46 | 47 | 如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步: 48 | 49 | 1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字; 50 | 51 | 2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中; 52 | 53 | 3, 请将如下声明文本放入每个源文件的头部注释中。 54 | 55 | Copyright (c) 2020 Sipeed Co.,Ltd. 56 | bl602-hal is licensed under Mulan PSL v2. 57 | You can use this software according to the terms and conditions of the Mulan PSL v2. 58 | You may obtain a copy of Mulan PSL v2 at: 59 | http://license.coscl.org.cn/MulanPSL2 60 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 61 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 62 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 63 | See the Mulan PSL v2 for more details. 64 | 65 | Mulan Permissive Software License,Version 2 66 | 67 | Mulan Permissive Software License,Version 2 (Mulan PSL v2) 68 | 69 | January 2020 http://license.coscl.org.cn/MulanPSL2 70 | 71 | Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions: 72 | 73 | 0. Definition 74 | 75 | Software means the program and related documents which are licensed under this License and comprise all Contribution(s). 76 | 77 | Contribution means the copyrightable work licensed by a particular Contributor under this License. 78 | 79 | Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License. 80 | 81 | Legal Entity means the entity making a Contribution and all its Affiliates. 82 | 83 | Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity. 84 | 85 | 1. Grant of Copyright License 86 | 87 | Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not. 88 | 89 | 2. Grant of Patent License 90 | 91 | Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken. 92 | 93 | 3. No Trademark License 94 | 95 | No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in section 4. 96 | 97 | 4. Distribution Restriction 98 | 99 | You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software. 100 | 101 | 5. Disclaimer of Warranty and Limitation of Liability 102 | 103 | THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 104 | 105 | 6. Language 106 | 107 | THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL. 108 | 109 | END OF THE TERMS AND CONDITIONS 110 | 111 | How to Apply the Mulan Permissive Software License,Version 2 (Mulan PSL v2) to Your Software 112 | 113 | To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps: 114 | 115 | Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner; 116 | Create a file named "LICENSE" which contains the whole context of this License in the first directory of your software package; 117 | Attach the statement to the appropriate annotated syntax at the beginning of each source file. 118 | Copyright (c) 2020 Sipeed Co.,Ltd. 119 | bl602-hal is licensed under Mulan PSL v2. 120 | You can use this software according to the terms and conditions of the Mulan PSL v2. 121 | You may obtain a copy of Mulan PSL v2 at: 122 | http://license.coscl.org.cn/MulanPSL2 123 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 124 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 125 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 126 | See the Mulan PSL v2 for more details. 127 | -------------------------------------------------------------------------------- /src/spi.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | # Serial Peripheral Interface 3 | To construct the SPI instances, use the `Spi::spi` function. 4 | The pin parameter is a tuple containing `(miso, mosi, cs, sck)` which should be configured via `into_spi_miso, into_spi_mosi, into_spi_ss, into_spi_sclk`. 5 | 6 | CS is optional - so you can also pass a tuple containing `(miso, mosi, sck)` 7 | ## Initialisation example 8 | ```rust 9 | let miso = parts.pin4.into_spi_miso(); 10 | let mosi = parts.pin5.into_spi_mosi(); 11 | let ss = parts.pin2.into_spi_ss(); 12 | let sclk = parts.pin3.into_spi_sclk(); 13 | 14 | let mut spi = hal::spi::Spi::spi( 15 | dp.SPI, 16 | (miso, mosi, ss, sclk), 17 | embedded_hal::spi::MODE_0, 18 | 8_000_000u32.Hz(), 19 | clocks, 20 | ); 21 | ``` 22 | */ 23 | 24 | use bl602_pac::SPI; 25 | pub use embedded_hal::spi::{FullDuplex, Mode}; 26 | use embedded_time::rate::Hertz; 27 | 28 | use crate::pac; 29 | 30 | use crate::clock::Clocks; 31 | 32 | /// SPI error 33 | #[derive(Debug)] 34 | #[non_exhaustive] 35 | pub enum Error { 36 | /// Rx overflow occurred 37 | RxOverflow, 38 | /// Rx underflow occurred 39 | RxUnderflow, 40 | /// Tx overflow occurred 41 | TxOverflow, 42 | /// Tx underflow occurred 43 | TxUnderflow, 44 | } 45 | 46 | /// The bit format to send the data in 47 | #[derive(Debug, Clone, Copy)] 48 | pub enum SpiBitFormat { 49 | /// Least significant bit first 50 | LsbFirst, 51 | /// Most significant bit first 52 | MsbFirst, 53 | } 54 | 55 | /// MISO pins - DO NOT IMPLEMENT THIS TRAIT 56 | pub unsafe trait MisoPin {} 57 | 58 | /// MOSI pins - DO NOT IMPLEMENT THIS TRAIT 59 | pub unsafe trait MosiPin {} 60 | 61 | /// SS pins - DO NOT IMPLEMENT THIS TRAIT 62 | pub unsafe trait SsPin {} 63 | 64 | /// SCLK pins - DO NOT IMPLEMENT THIS TRAIT 65 | pub unsafe trait SclkPin {} 66 | 67 | /// Spi pins - DO NOT IMPLEMENT THIS TRAIT 68 | pub unsafe trait Pins {} 69 | 70 | unsafe impl MisoPin for crate::gpio::Pin0 {} 71 | unsafe impl MosiPin for crate::gpio::Pin1 {} 72 | unsafe impl SsPin for crate::gpio::Pin2 {} 73 | unsafe impl SclkPin for crate::gpio::Pin3 {} 74 | unsafe impl MisoPin for crate::gpio::Pin4 {} 75 | unsafe impl MosiPin for crate::gpio::Pin5 {} 76 | unsafe impl SsPin for crate::gpio::Pin6 {} 77 | unsafe impl SclkPin for crate::gpio::Pin7 {} 78 | unsafe impl MisoPin for crate::gpio::Pin8 {} 79 | unsafe impl MosiPin for crate::gpio::Pin9 {} 80 | unsafe impl SsPin for crate::gpio::Pin10 {} 81 | unsafe impl SclkPin for crate::gpio::Pin11 {} 82 | unsafe impl MisoPin for crate::gpio::Pin12 {} 83 | unsafe impl MosiPin for crate::gpio::Pin13 {} 84 | unsafe impl SsPin for crate::gpio::Pin14 {} 85 | unsafe impl SclkPin for crate::gpio::Pin15 {} 86 | unsafe impl MisoPin for crate::gpio::Pin16 {} 87 | unsafe impl MosiPin for crate::gpio::Pin17 {} 88 | unsafe impl SsPin for crate::gpio::Pin18 {} 89 | unsafe impl SclkPin for crate::gpio::Pin19 {} 90 | unsafe impl MisoPin for crate::gpio::Pin20 {} 91 | unsafe impl MosiPin for crate::gpio::Pin21 {} 92 | unsafe impl SsPin for crate::gpio::Pin22 {} 93 | 94 | unsafe impl Pins for (MISO, MOSI, SS, SCLK) 95 | where 96 | MISO: MisoPin, 97 | MOSI: MosiPin, 98 | SS: SsPin, 99 | SCLK: SclkPin, 100 | { 101 | } 102 | 103 | unsafe impl Pins for (MISO, MOSI, SCLK) 104 | where 105 | MISO: MisoPin, 106 | MOSI: MosiPin, 107 | SCLK: SclkPin, 108 | { 109 | } 110 | 111 | /// A Serial Peripheral Interface 112 | pub struct Spi { 113 | spi: SPI, 114 | pins: PINS, 115 | } 116 | 117 | impl Spi 118 | where 119 | PINS: Pins, 120 | { 121 | /** 122 | Constructs an SPI instance in 8bit dataframe mode. 123 | The pin parameter tuple (miso, mosi, cs, sck) needs to be configured accordingly. 124 | You can also omit `cs` to have manual control over `cs`. 125 | 126 | The frequency cannot be more than half of the spi clock frequency. 127 | */ 128 | pub fn spi(spi: SPI, pins: PINS, mode: Mode, freq: Hertz, clocks: Clocks) -> Self 129 | where 130 | PINS: Pins, 131 | { 132 | let glb = unsafe { &*pac::GLB::ptr() }; 133 | 134 | glb.glb_parm.modify(|_r, w| { 135 | w.reg_spi_0_master_mode() 136 | .set_bit() 137 | .reg_spi_0_swap() 138 | .set_bit() 139 | }); 140 | 141 | // length of phase 0 and 1 (i.e. low / high values of SCLK) 142 | // needs to be divided by two 143 | let len = clocks.spi_clk().0 / freq.0 / 2; 144 | if len > 256 || len == 0 { 145 | panic!("Cannot reach the desired SPI frequency"); 146 | } 147 | 148 | let len = (len - 1) as u8; 149 | spi.spi_prd_0.modify(|_r, w| unsafe { 150 | w.cr_spi_prd_s() 151 | .bits(len) 152 | .cr_spi_prd_p() 153 | .bits(len) 154 | .cr_spi_prd_d_ph_0() 155 | .bits(len) 156 | .cr_spi_prd_d_ph_1() 157 | .bits(len) 158 | }); 159 | 160 | spi.spi_prd_1 161 | .modify(|_r, w| unsafe { w.cr_spi_prd_i().bits(len) }); 162 | 163 | spi.spi_config.modify(|_, w| unsafe { 164 | w.cr_spi_sclk_pol() 165 | .bit(match mode.polarity { 166 | embedded_hal::spi::Polarity::IdleLow => false, 167 | embedded_hal::spi::Polarity::IdleHigh => true, 168 | }) 169 | .cr_spi_sclk_ph() 170 | .bit(match mode.phase { 171 | embedded_hal::spi::Phase::CaptureOnFirstTransition => true, 172 | embedded_hal::spi::Phase::CaptureOnSecondTransition => false, 173 | }) 174 | .cr_spi_m_cont_en() 175 | .clear_bit() // disable cont mode 176 | .cr_spi_frame_size() 177 | .bits(0) // 8 bit frames 178 | .cr_spi_s_en() 179 | .clear_bit() // not slave 180 | .cr_spi_m_en() 181 | .set_bit() // master 182 | }); 183 | 184 | Spi { spi, pins } 185 | } 186 | 187 | pub fn release(self) -> (pac::SPI, PINS) { 188 | (self.spi, self.pins) 189 | } 190 | 191 | /// Select which frame format is used for data transfers 192 | pub fn bit_format(&mut self, format: SpiBitFormat) { 193 | match format { 194 | SpiBitFormat::LsbFirst => self 195 | .spi 196 | .spi_config 197 | .modify(|_, w| w.cr_spi_bit_inv().set_bit()), 198 | SpiBitFormat::MsbFirst => self 199 | .spi 200 | .spi_config 201 | .modify(|_, w| w.cr_spi_bit_inv().clear_bit()), 202 | } 203 | } 204 | 205 | /// Clear FIFOs 206 | pub fn clear_fifo(&mut self) { 207 | self.spi 208 | .spi_fifo_config_0 209 | .write(|w| w.rx_fifo_clr().set_bit().tx_fifo_clr().set_bit()); 210 | } 211 | } 212 | 213 | impl FullDuplex for Spi 214 | where 215 | PINS: Pins, 216 | { 217 | type Error = Error; 218 | 219 | fn try_read(&mut self) -> nb::Result { 220 | let spi_fifo_config_0 = self.spi.spi_fifo_config_0.read(); 221 | 222 | if spi_fifo_config_0.rx_fifo_overflow().bit_is_set() { 223 | Err(nb::Error::Other(Error::RxOverflow)) 224 | } else if spi_fifo_config_0.rx_fifo_underflow().bit_is_set() { 225 | Err(nb::Error::Other(Error::RxUnderflow)) 226 | } else if self.spi.spi_fifo_config_1.read().rx_fifo_cnt().bits() == 0 { 227 | Err(nb::Error::WouldBlock) 228 | } else { 229 | Ok((self.spi.spi_fifo_rdata.read().bits() & 0xff) as u8) 230 | } 231 | } 232 | 233 | fn try_send(&mut self, data: u8) -> nb::Result<(), Self::Error> { 234 | let spi_fifo_config_0 = self.spi.spi_fifo_config_0.read(); 235 | 236 | if spi_fifo_config_0.tx_fifo_overflow().bit_is_set() { 237 | Err(nb::Error::Other(Error::TxOverflow)) 238 | } else if spi_fifo_config_0.tx_fifo_underflow().bit_is_set() { 239 | Err(nb::Error::Other(Error::TxUnderflow)) 240 | } else if self.spi.spi_fifo_config_1.read().tx_fifo_cnt().bits() == 0 { 241 | Err(nb::Error::WouldBlock) 242 | } else { 243 | self.spi 244 | .spi_fifo_wdata 245 | .write(|w| unsafe { w.bits(data as u32) }); 246 | 247 | Ok(()) 248 | } 249 | } 250 | } 251 | 252 | impl embedded_hal::blocking::spi::transfer::Default for Spi where 253 | PINS: Pins 254 | { 255 | } 256 | 257 | impl embedded_hal::blocking::spi::write::Default for Spi where 258 | PINS: Pins 259 | { 260 | } 261 | 262 | impl embedded_hal::blocking::spi::write_iter::Default for Spi where 263 | PINS: Pins 264 | { 265 | } 266 | 267 | impl embedded_hal::blocking::spi::transactional::Default for Spi where 268 | PINS: Pins 269 | { 270 | } 271 | -------------------------------------------------------------------------------- /src/serial.rs: -------------------------------------------------------------------------------- 1 | //! Serial communication 2 | use crate::clock::Clocks; 3 | use crate::pac; 4 | use core::fmt; 5 | use embedded_hal::prelude::_embedded_hal_serial_Write; 6 | use embedded_time::rate::{Baud, Extensions}; 7 | use nb::block; 8 | 9 | /// Serial error 10 | #[derive(Debug)] 11 | #[non_exhaustive] 12 | pub enum Error { 13 | /// Framing error 14 | Framing, 15 | /// Noise error 16 | Noise, 17 | /// RX buffer overrun 18 | Overrun, 19 | /// Parity check error 20 | Parity, 21 | } 22 | 23 | /// Serial configuration 24 | #[derive(Copy, Clone, Debug, PartialEq)] 25 | pub struct Config { 26 | pub baudrate: Baud, 27 | pub order: Order, 28 | pub parity: Parity, 29 | pub stopbits: StopBits, 30 | pub wordlength: WordLength, 31 | } 32 | 33 | impl Config { 34 | /// Sets the target baudrate 35 | pub fn baudrate(mut self, baudrate: impl Into) -> Self { 36 | self.baudrate = baudrate.into(); 37 | 38 | self 39 | } 40 | 41 | /// Sets parity to no parity check 42 | pub fn parity_none(mut self) -> Self { 43 | self.parity = Parity::ParityNone; 44 | 45 | self 46 | } 47 | 48 | /// Sets parity check to even 49 | pub fn parity_even(mut self) -> Self { 50 | self.parity = Parity::ParityEven; 51 | 52 | self 53 | } 54 | 55 | /// Sets parity check to odd 56 | pub fn parity_odd(mut self) -> Self { 57 | self.parity = Parity::ParityOdd; 58 | 59 | self 60 | } 61 | 62 | /// Sets the target stopbits 63 | pub fn stopbits(mut self, stopbits: StopBits) -> Self { 64 | self.stopbits = stopbits; 65 | 66 | self 67 | } 68 | } 69 | 70 | impl Default for Config { 71 | fn default() -> Config { 72 | Config { 73 | baudrate: 115_200_u32.Bd(), 74 | order: Order::LsbFirst, 75 | parity: Parity::ParityNone, 76 | stopbits: StopBits::STOP1, 77 | wordlength: WordLength::Eight, 78 | } 79 | } 80 | } 81 | 82 | /// Order of the bits transmitted and received on the wire 83 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 84 | pub enum Order { 85 | /// Each byte is sent out LSB-first 86 | LsbFirst, 87 | /// Each byte is sent out MSB-first 88 | MsbFirst, 89 | } 90 | 91 | /// Parity check 92 | #[derive(Copy, Clone, Debug, PartialEq)] 93 | pub enum Parity { 94 | /// No parity check 95 | ParityNone, 96 | /// Even parity bit 97 | ParityEven, 98 | /// Odd parity bit 99 | ParityOdd, 100 | } 101 | 102 | /// Stop bits 103 | #[derive(Copy, Clone, Debug, PartialEq)] 104 | pub enum StopBits { 105 | /// 1 stop bit 106 | STOP1, 107 | /// 0.5 stop bits 108 | STOP0P5, 109 | /// 2 stop bits 110 | STOP2, 111 | /// 1.5 stop bits 112 | STOP1P5, 113 | } 114 | 115 | /// Word length 116 | #[derive(Copy, Clone, Debug, PartialEq)] 117 | pub enum WordLength { 118 | Five, 119 | Six, 120 | Seven, 121 | Eight, 122 | } 123 | 124 | /// Interrupt event 125 | pub enum Event { 126 | /// UART RX FIFO error interrupt 127 | RxFifoError, 128 | /// UART TX FIFO error interrupt 129 | TxFifoError, 130 | /// UART RX parity check error interrupt 131 | RxParityError, 132 | /// UART RX Time-out interrupt 133 | RxTimeout, 134 | /// UART RX FIFO ready (rx_fifo_cnt > rx_fifo_th) interrupt 135 | RxFifoReady, 136 | /// UART TX FIFO ready (tx_fifo_cnt > tx_fifo_th) interrupt 137 | TxFifoReady, 138 | /// UART RX transfer end interrupt 139 | RxTransferEnd, 140 | /// UART TX transfer end interrupt 141 | TxTransferEnd, 142 | } 143 | 144 | /// Serial abstraction 145 | pub struct Serial { 146 | uart: UART, 147 | pins: PINS, 148 | } 149 | 150 | impl Serial 151 | where 152 | PINS: Pins, 153 | { 154 | // todo: there is UART0 and UART1 155 | pub fn uart0(uart: pac::UART, config: Config, pins: PINS, clocks: Clocks) -> Self { 156 | // Initialize clocks and baudrate 157 | let uart_clk = clocks.uart_clk(); 158 | let baud = config.baudrate.0; 159 | let divisor = { 160 | // Can't possibly have a baudrate greater than uart_clock 161 | if baud > uart_clk.0 { 162 | panic!("impossible baudrate"); 163 | } 164 | // If we did this calculation using integer math, it always rounds down 165 | // Reduce error by doing calculation using floating point, then 166 | // add half before converting back to integer to round nearest instead 167 | let ans_f = uart_clk.0 as f32 / baud as f32; 168 | let ans = (ans_f + 0.5) as u32; 169 | 170 | if !(1..=65535).contains(&ans) { 171 | panic!("impossible baudrate"); 172 | } 173 | 174 | ans as u16 175 | }; 176 | 177 | uart.uart_bit_prd.write(|w| unsafe { 178 | w.cr_urx_bit_prd() 179 | .bits(divisor - 1) 180 | .cr_utx_bit_prd() 181 | .bits(divisor - 1) 182 | }); 183 | 184 | // Bit inverse configuration; MsbFirst => 1, LsbFirst => 0 185 | let order_cfg = match config.order { 186 | Order::LsbFirst => false, 187 | Order::MsbFirst => true, 188 | }; 189 | 190 | uart.data_config 191 | .write(|w| w.cr_uart_bit_inv().bit(order_cfg)); 192 | 193 | // UART TX config 194 | let data_bits_cfg = match config.wordlength { 195 | WordLength::Five => 4, 196 | WordLength::Six => 5, 197 | WordLength::Seven => 6, 198 | WordLength::Eight => 7, 199 | }; 200 | let stop_bits_cfg = match config.stopbits { 201 | StopBits::STOP0P5 => 0, 202 | StopBits::STOP1 => 1, 203 | StopBits::STOP1P5 => 2, 204 | StopBits::STOP2 => 3, 205 | }; 206 | let (parity_enable, parity_type) = match config.parity { 207 | Parity::ParityNone => (false, false), 208 | Parity::ParityEven => (true, false), // even => 0 209 | Parity::ParityOdd => (true, true), // odd => 1 210 | }; 211 | 212 | uart.utx_config.write(|w| unsafe { 213 | w.cr_utx_prt_en() 214 | .bit(parity_enable) 215 | .cr_utx_prt_sel() 216 | .bit(parity_type) 217 | .cr_utx_bit_cnt_d() 218 | .bits(data_bits_cfg) 219 | .cr_utx_bit_cnt_p() 220 | .bits(stop_bits_cfg) 221 | .cr_utx_frm_en() 222 | .set_bit() // [!] freerun on // todo 223 | .cr_utx_cts_en() 224 | .bit(PINS::HAS_CTS) 225 | .cr_utx_en() 226 | .bit(PINS::HAS_TX) 227 | }); 228 | 229 | // UART RX config 230 | uart.urx_config.write(|w| unsafe { 231 | w.cr_urx_prt_en() 232 | .bit(parity_enable) 233 | .cr_urx_prt_sel() 234 | .bit(parity_type) 235 | .cr_urx_bit_cnt_d() 236 | .bits(data_bits_cfg) 237 | .cr_urx_deg_en() 238 | .clear_bit() // no rx input de-glitch // todo 239 | .cr_urx_rts_sw_mode() 240 | .clear_bit() // no RTS // todo 241 | .cr_urx_en() 242 | .bit(PINS::HAS_RX) 243 | }); 244 | 245 | Serial { uart, pins } 246 | } 247 | 248 | pub fn free(self) -> (pac::UART, PINS) { 249 | // todo! 250 | (self.uart, self.pins) 251 | } 252 | } 253 | 254 | impl embedded_hal::serial::Write for Serial { 255 | type Error = Error; 256 | 257 | fn try_write(&mut self, word: u8) -> nb::Result<(), Self::Error> { 258 | // If there's no room to write a byte or more to the FIFO, return WouldBlock 259 | if self.uart.uart_fifo_config_1.read().tx_fifo_cnt().bits() == 0 { 260 | Err(nb::Error::WouldBlock) 261 | } else { 262 | self.uart 263 | .uart_fifo_wdata 264 | .write(|w| unsafe { w.bits(word as u32) }); 265 | Ok(()) 266 | } 267 | } 268 | 269 | fn try_flush(&mut self) -> nb::Result<(), Self::Error> { 270 | // If we're still transmitting or have data in our 32 byte FIFO, return WouldBlock 271 | if self.uart.uart_fifo_config_1.read().tx_fifo_cnt().bits() != 32 272 | || self.uart.uart_status.read().sts_utx_bus_busy().bit_is_set() 273 | { 274 | Err(nb::Error::WouldBlock) 275 | } else { 276 | Ok(()) 277 | } 278 | } 279 | } 280 | 281 | impl embedded_hal::serial::Read for Serial { 282 | type Error = Error; 283 | 284 | fn try_read(&mut self) -> nb::Result { 285 | let ans = self.uart.uart_fifo_rdata.read().bits(); 286 | 287 | Ok((ans & 0xff) as u8) 288 | } 289 | } 290 | 291 | impl fmt::Write for Serial 292 | where 293 | Serial: embedded_hal::serial::Write, 294 | { 295 | fn write_str(&mut self, s: &str) -> fmt::Result { 296 | s.as_bytes() 297 | .iter() 298 | .try_for_each(|c| block!(self.try_write(*c))) 299 | .map_err(|_| fmt::Error) 300 | } 301 | } 302 | 303 | /// Serial transmit pins - DO NOT IMPLEMENT THIS TRAIT 304 | pub unsafe trait TxPin {} 305 | /// Serial receive pins - DO NOT IMPLEMENT THIS TRAIT 306 | pub unsafe trait RxPin {} 307 | /// Serial rts pins - DO NOT IMPLEMENT THIS TRAIT 308 | pub unsafe trait RtsPin {} 309 | /// Serial cts pins - DO NOT IMPLEMENT THIS TRAIT 310 | pub unsafe trait CtsPin {} 311 | 312 | macro_rules! impl_uart_pin { 313 | ($(($UartSigi: ident, $UartMuxi: ident),)+) => { 314 | use crate::gpio::*; 315 | $( 316 | unsafe impl> TxPin for (PIN, $UartMuxi) {} 317 | unsafe impl> RxPin for (PIN, $UartMuxi) {} 318 | unsafe impl> RtsPin for (PIN, $UartMuxi) {} 319 | unsafe impl> CtsPin for (PIN, $UartMuxi) {} 320 | // unsafe impl> TxPin for (PIN, SIG) {} 321 | // unsafe impl> RxPin for (PIN, SIG) {} 322 | // unsafe impl> RtsPin for (PIN, SIG) {} 323 | // unsafe impl> CtsPin for (PIN, SIG) {} 324 | )+ 325 | }; 326 | } 327 | 328 | impl_uart_pin!( 329 | (UartSig0, UartMux0), 330 | (UartSig1, UartMux1), 331 | (UartSig2, UartMux2), 332 | (UartSig3, UartMux3), 333 | (UartSig4, UartMux4), 334 | (UartSig5, UartMux5), 335 | (UartSig6, UartMux6), 336 | (UartSig7, UartMux7), 337 | ); 338 | 339 | /// Serial pins - DO NOT IMPLEMENT THIS TRAIT 340 | pub unsafe trait Pins { 341 | const HAS_TX: bool; 342 | const HAS_RX: bool; 343 | const HAS_RTS: bool; 344 | const HAS_CTS: bool; 345 | } 346 | 347 | unsafe impl Pins for (TX, RX) 348 | where 349 | TX: TxPin, 350 | RX: RxPin, 351 | { 352 | const HAS_TX: bool = true; 353 | const HAS_RX: bool = true; 354 | const HAS_RTS: bool = false; 355 | const HAS_CTS: bool = false; 356 | } 357 | 358 | unsafe impl Pins for (TX, RX, RTS, CTS) 359 | where 360 | TX: TxPin, 361 | RX: RxPin, 362 | RTS: RxPin, 363 | CTS: RxPin, 364 | { 365 | const HAS_TX: bool = true; 366 | const HAS_RX: bool = true; 367 | const HAS_RTS: bool = true; 368 | const HAS_CTS: bool = true; 369 | } 370 | -------------------------------------------------------------------------------- /src/i2c.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | # Inter-Integrated Circuit (I2C) bus 3 | To construct the I2C instance use the `I2c::i2c` function. 4 | The pin parameter is a tuple containing `(scl, sda)` which should be configured via `into_i2c_scl` and `into_i2c_sda`. 5 | 6 | ## Initialisation example 7 | ```rust 8 | let scl = parts.pin4.into_i2c_scl(); 9 | let sda = parts.pin5.into_i2c_sda(); 10 | 11 | let mut i2c = hal::i2c::I2c::i2c( 12 | dp.I2C, 13 | (scl, sda), 14 | 100_000u32.Hz(), 15 | clocks, 16 | ); 17 | ``` 18 | */ 19 | 20 | use bl602_pac::I2C; 21 | use embedded_hal::blocking; 22 | use embedded_hal::prelude::_embedded_hal_blocking_i2c_Read; 23 | use embedded_hal::prelude::_embedded_hal_blocking_i2c_Write; 24 | use embedded_hal_zero::blocking::i2c::Read as ReadZero; 25 | use embedded_hal_zero::blocking::i2c::Write as WriteZero; 26 | use embedded_time::rate::Hertz; 27 | 28 | use crate::{clock::Clocks, pac}; 29 | 30 | /// I2C error 31 | #[derive(Debug, Eq, PartialEq)] 32 | pub enum Error { 33 | /// Rx overflow occurred 34 | RxOverflow, 35 | /// Rx underflow occurred 36 | RxUnderflow, 37 | /// Tx overflow occurred 38 | TxOverflow, 39 | /// Tx underflow occurred 40 | TxUnderflow, 41 | /// Timeout waiting for fifo occurred 42 | Timeout, 43 | } 44 | 45 | /// SDA pins - DO NOT IMPLEMENT THIS TRAIT 46 | pub unsafe trait SdaPin {} 47 | 48 | /// SCL pins - DO NOT IMPLEMENT THIS TRAIT 49 | pub unsafe trait SclPin {} 50 | 51 | /// I2C pins - DO NOT IMPLEMENT THIS TRAIT 52 | pub unsafe trait Pins {} 53 | 54 | unsafe impl SclPin for crate::gpio::Pin0 {} 55 | unsafe impl SdaPin for crate::gpio::Pin1 {} 56 | unsafe impl SclPin for crate::gpio::Pin2 {} 57 | unsafe impl SdaPin for crate::gpio::Pin3 {} 58 | unsafe impl SclPin for crate::gpio::Pin4 {} 59 | unsafe impl SdaPin for crate::gpio::Pin5 {} 60 | unsafe impl SclPin for crate::gpio::Pin6 {} 61 | unsafe impl SdaPin for crate::gpio::Pin7 {} 62 | unsafe impl SclPin for crate::gpio::Pin8 {} 63 | unsafe impl SdaPin for crate::gpio::Pin9 {} 64 | unsafe impl SclPin for crate::gpio::Pin10 {} 65 | unsafe impl SdaPin for crate::gpio::Pin11 {} 66 | unsafe impl SclPin for crate::gpio::Pin12 {} 67 | unsafe impl SdaPin for crate::gpio::Pin13 {} 68 | unsafe impl SclPin for crate::gpio::Pin14 {} 69 | unsafe impl SdaPin for crate::gpio::Pin15 {} 70 | unsafe impl SclPin for crate::gpio::Pin16 {} 71 | unsafe impl SdaPin for crate::gpio::Pin17 {} 72 | unsafe impl SclPin for crate::gpio::Pin18 {} 73 | unsafe impl SdaPin for crate::gpio::Pin19 {} 74 | unsafe impl SclPin for crate::gpio::Pin20 {} 75 | unsafe impl SdaPin for crate::gpio::Pin21 {} 76 | unsafe impl SclPin for crate::gpio::Pin22 {} 77 | 78 | unsafe impl Pins for (SCL, SDA) 79 | where 80 | SCL: SclPin, 81 | SDA: SdaPin, 82 | { 83 | } 84 | 85 | /// I2C peripheral operating in master mode supporting seven bit addressing 86 | pub struct I2c { 87 | i2c: I2C, 88 | pins: PINS, 89 | timeout: u16, 90 | } 91 | 92 | impl I2c 93 | where 94 | PINS: Pins, 95 | { 96 | /** 97 | Constructs an I2C instance in master mode. 98 | The pin parameter tuple (scl, sda) needs to be configured accordingly. 99 | 100 | The frequency cannot be more than a quarter of the i2c clock frequency. 101 | 102 | The I2C instance supports 7 bit addressing mode. 103 | */ 104 | pub fn i2c(i2c: I2C, pins: PINS, freq: Hertz, clocks: Clocks) -> Self 105 | where 106 | PINS: Pins, 107 | { 108 | // length of phase 0,1,2 and 3 109 | // needs to be divided by four 110 | let len = clocks.i2c_clk().0 / freq.0 / 4; 111 | if len > 256 || len <= 1 { 112 | // from the RM: Note: This value should not be set to 8’d0, adjust source 113 | // clock rate instead if higher I2C clock rate is required 114 | panic!("Cannot reach the desired I2C frequency"); 115 | } 116 | 117 | let len = (len - 1) as u8; 118 | 119 | i2c.i2c_prd_start.modify(|_r, w| unsafe { 120 | w.cr_i2c_prd_s_ph_0() 121 | .bits(len) 122 | .cr_i2c_prd_s_ph_1() 123 | .bits(len) 124 | .cr_i2c_prd_s_ph_2() 125 | .bits(len) 126 | .cr_i2c_prd_s_ph_3() 127 | .bits(len) 128 | }); 129 | 130 | i2c.i2c_prd_stop.modify(|_r, w| unsafe { 131 | w.cr_i2c_prd_p_ph_0() 132 | .bits(len) 133 | .cr_i2c_prd_p_ph_1() 134 | .bits(len) 135 | .cr_i2c_prd_p_ph_2() 136 | .bits(len) 137 | .cr_i2c_prd_p_ph_3() 138 | .bits(len) 139 | }); 140 | 141 | i2c.i2c_prd_data.modify(|_r, w| unsafe { 142 | w.cr_i2c_prd_d_ph_0() 143 | .bits(len) 144 | .cr_i2c_prd_d_ph_1() 145 | .bits(len) 146 | .cr_i2c_prd_d_ph_2() 147 | .bits(len) 148 | .cr_i2c_prd_d_ph_3() 149 | .bits(len) 150 | }); 151 | 152 | I2c { 153 | i2c, 154 | pins, 155 | timeout: 2048, 156 | } 157 | } 158 | 159 | pub fn release(self) -> (pac::I2C, PINS) { 160 | (self.i2c, self.pins) 161 | } 162 | 163 | /// Set the timeout when waiting for fifo (rx and tx). 164 | /// It's not a time unit but the number of cycles to wait. 165 | /// This defaults to 2048 166 | pub fn set_timeout(&mut self, timeout: u16) { 167 | self.timeout = timeout; 168 | } 169 | 170 | /// Clear FIFOs 171 | pub fn clear_fifo(&mut self) { 172 | self.i2c 173 | .i2c_fifo_config_0 174 | .write(|w| w.rx_fifo_clr().set_bit().tx_fifo_clr().set_bit()); 175 | } 176 | } 177 | 178 | impl blocking::i2c::Read for I2c 179 | where 180 | PINS: Pins, 181 | { 182 | type Error = Error; 183 | 184 | fn try_read( 185 | &mut self, 186 | address: blocking::i2c::SevenBitAddress, 187 | buffer: &mut [u8], 188 | ) -> Result<(), Self::Error> { 189 | let fifo_config = self.i2c.i2c_fifo_config_0.read(); 190 | 191 | if fifo_config.rx_fifo_overflow().bit_is_set() { 192 | self.i2c 193 | .i2c_fifo_config_0 194 | .write(|w| w.rx_fifo_clr().set_bit()); 195 | return Err(Error::RxOverflow); 196 | } else if fifo_config.rx_fifo_underflow().bit_is_set() { 197 | self.i2c 198 | .i2c_fifo_config_0 199 | .write(|w| w.rx_fifo_clr().set_bit()); 200 | return Err(Error::RxUnderflow); 201 | } 202 | 203 | let count = buffer.len() / 4 + if buffer.len() % 4 > 0 { 1 } else { 0 }; 204 | let mut word_buffer = [0u32; 255]; 205 | let tmp = &mut word_buffer[..count]; 206 | 207 | self.i2c.i2c_config.modify(|_r, w| unsafe { 208 | w.cr_i2c_pkt_len() 209 | .bits(buffer.len() as u8 - 1u8) 210 | .cr_i2c_slv_addr() 211 | .bits(address) 212 | .cr_i2c_sub_addr_en() 213 | .clear_bit() 214 | .cr_i2c_sub_addr_bc() 215 | .bits(0) 216 | .cr_i2c_scl_sync_en() 217 | .set_bit() 218 | .cr_i2c_pkt_dir() 219 | .set_bit() // = read 220 | .cr_i2c_m_en() 221 | .set_bit() 222 | }); 223 | 224 | for value in tmp.iter_mut() { 225 | let mut timeout_countdown = self.timeout; 226 | while self.i2c.i2c_fifo_config_1.read().rx_fifo_cnt().bits() == 0 { 227 | if timeout_countdown == 0 { 228 | return Err(Error::Timeout); 229 | } 230 | timeout_countdown -= 1; 231 | } 232 | *value = self.i2c.i2c_fifo_rdata.read().i2c_fifo_rdata().bits(); 233 | } 234 | 235 | self.i2c 236 | .i2c_config 237 | .modify(|_r, w| w.cr_i2c_m_en().clear_bit()); 238 | 239 | for (idx, value) in buffer.iter_mut().enumerate() { 240 | let shift_by = (idx % 4 * 8) as u32; 241 | *value = (word_buffer[idx / 4].overflowing_shr(shift_by).0 & 0xff) as u8; 242 | } 243 | 244 | Ok(()) 245 | } 246 | } 247 | 248 | impl blocking::i2c::Write for I2c 249 | where 250 | PINS: Pins, 251 | { 252 | type Error = Error; 253 | 254 | fn try_write( 255 | &mut self, 256 | address: blocking::i2c::SevenBitAddress, 257 | buffer: &[u8], 258 | ) -> Result<(), Self::Error> { 259 | let fifo_config = self.i2c.i2c_fifo_config_0.read(); 260 | 261 | if fifo_config.tx_fifo_overflow().bit_is_set() { 262 | self.i2c 263 | .i2c_fifo_config_0 264 | .write(|w| w.tx_fifo_clr().set_bit()); 265 | return Err(Error::TxOverflow); 266 | } else if fifo_config.tx_fifo_underflow().bit_is_set() { 267 | self.i2c 268 | .i2c_fifo_config_0 269 | .write(|w| w.tx_fifo_clr().set_bit()); 270 | return Err(Error::TxUnderflow); 271 | } 272 | 273 | let mut word_buffer = [0u32; 255]; 274 | let count = buffer.len() / 4 + if buffer.len() % 4 > 0 { 1 } else { 0 }; 275 | for (idx, value) in buffer.iter().enumerate() { 276 | let shift_by = (idx % 4 * 8) as u32; 277 | word_buffer[idx / 4] |= (*value as u32).overflowing_shl(shift_by).0; 278 | } 279 | let tmp = &word_buffer[..count]; 280 | 281 | self.i2c.i2c_config.modify(|_r, w| unsafe { 282 | w.cr_i2c_pkt_len() 283 | .bits(buffer.len() as u8 - 1u8) 284 | .cr_i2c_slv_addr() 285 | .bits(address) 286 | .cr_i2c_sub_addr_en() 287 | .clear_bit() 288 | .cr_i2c_sub_addr_bc() 289 | .bits(0) 290 | .cr_i2c_scl_sync_en() 291 | .set_bit() 292 | .cr_i2c_pkt_dir() 293 | .clear_bit() // = write 294 | .cr_i2c_m_en() 295 | .set_bit() 296 | }); 297 | 298 | for value in tmp.iter() { 299 | let mut timeout_countdown = self.timeout; 300 | while self.i2c.i2c_fifo_config_1.read().tx_fifo_cnt().bits() == 0 { 301 | if timeout_countdown == 0 { 302 | return Err(Error::Timeout); 303 | } 304 | timeout_countdown -= 1; 305 | } 306 | self.i2c 307 | .i2c_fifo_wdata 308 | .write(|w| unsafe { w.i2c_fifo_wdata().bits(*value as u32) }); 309 | } 310 | 311 | while self.i2c.i2c_bus_busy.read().sts_i2c_bus_busy().bit_is_set() { 312 | // wait for transfer to finish 313 | } 314 | 315 | self.i2c 316 | .i2c_config 317 | .modify(|_r, w| w.cr_i2c_m_en().clear_bit()); 318 | 319 | Ok(()) 320 | } 321 | } 322 | 323 | impl ReadZero for I2c 324 | where 325 | PINS: Pins, 326 | { 327 | type Error = Error; 328 | 329 | fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { 330 | self.try_read(address, buffer) 331 | } 332 | } 333 | 334 | impl WriteZero for I2c 335 | where 336 | PINS: Pins, 337 | { 338 | type Error = Error; 339 | 340 | fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { 341 | self.try_write(addr, bytes) 342 | } 343 | } 344 | -------------------------------------------------------------------------------- /src/timer.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | # Timer 3 | The chip has two 32-bit counters, each of which can independently control and configure its parameters and clock frequency 4 | 5 | ## Example 6 | ```rust 7 | let timers = dp.TIMER.split(); 8 | 9 | let ch0 = timers 10 | .channel0 11 | .set_clock_source(ClockSource::Clock1Khz, 1_000u32.Hz()); 12 | 13 | ch0.enable_match0_interrupt(); 14 | ch0.enable_match1_interrupt(); 15 | ch0.disable_match2_interrupt(); 16 | 17 | ch0.set_preload_value(Milliseconds::new(0)); 18 | ch0.set_preload(hal::timer::Preload::PreloadMatchComparator1); 19 | ch0.set_match0(Milliseconds::new(3_000u32)); 20 | ch0.set_match1(Milliseconds::new(7_000u32)); 21 | 22 | ch0.enable(); // start timer 23 | ``` 24 | */ 25 | 26 | use crate::{clock::Clocks, pac}; 27 | use bl602_pac::TIMER; 28 | use core::cell::RefCell; 29 | use embedded_time::{ 30 | duration::Milliseconds, 31 | rate::{Extensions, Hertz}, 32 | }; 33 | use paste::paste; 34 | 35 | /// Error for [CountDown](embedded_hal::timer::CountDown) 36 | #[derive(Debug)] 37 | pub enum CountDownError { 38 | /// Indicates that the clock wrapped during count down 39 | Wrapped, 40 | } 41 | 42 | /// Clock sources for a timer channel. 43 | /// There are four timer clock sources available. 44 | pub enum ClockSource<'a> { 45 | /// System master clock 46 | Fclk(&'a Clocks), 47 | /// 32K clock 48 | Rc32Khz, 49 | /// 1K clock (32K frequency division) 50 | Clock1Khz, 51 | /// 32M clock 52 | Pll32Mhz, 53 | } 54 | 55 | impl<'a> ClockSource<'a> { 56 | fn tccr_value(&self) -> u8 { 57 | match self { 58 | ClockSource::Fclk(_) => 0, 59 | ClockSource::Rc32Khz => 1, 60 | ClockSource::Clock1Khz => 2, 61 | ClockSource::Pll32Mhz => 3, 62 | } 63 | } 64 | 65 | fn hertz(&self) -> Hertz { 66 | match self { 67 | ClockSource::Fclk(clocks) => clocks.sysclk(), 68 | ClockSource::Rc32Khz => 32_000.Hz(), 69 | ClockSource::Clock1Khz => 1_000.Hz(), 70 | ClockSource::Pll32Mhz => 32_000_000.Hz(), 71 | } 72 | } 73 | } 74 | 75 | /// When to preload 76 | pub enum Preload { 77 | /// No preload 78 | NoPreload, 79 | /// Preload when comparator 0 matches 80 | PreloadMatchComparator0, 81 | /// Preload when comparator 1 matches 82 | PreloadMatchComparator1, 83 | /// Preload when comparator 2 matches 84 | PreloadMatchComparator2, 85 | } 86 | 87 | impl Preload { 88 | fn to_prlcr(&self) -> u8 { 89 | match self { 90 | Preload::NoPreload => 0, 91 | Preload::PreloadMatchComparator0 => 1, 92 | Preload::PreloadMatchComparator1 => 2, 93 | Preload::PreloadMatchComparator2 => 3, 94 | } 95 | } 96 | } 97 | 98 | /// Timer Channel 0 99 | pub struct TimerChannel0 {} 100 | 101 | /// Timer Channel 1 102 | pub struct TimerChannel1 {} 103 | 104 | /// Timers obtained from [TIMER.split](bl602_pac::Peripherals::TIMER) 105 | pub struct Timers { 106 | pub channel0: TimerChannel0, 107 | pub channel1: TimerChannel1, 108 | } 109 | 110 | macro_rules! impl_timer_channel { 111 | ($name: ident, $conf_name: ident, $channel: literal, $channel_cs: literal) => { 112 | 113 | /// A configured timer channel ready to use. 114 | pub struct $conf_name { 115 | clock: Hertz, 116 | count_down_target: Option, 117 | last_count_down_value: Option, 118 | is_running: RefCell, 119 | } 120 | 121 | paste! { 122 | impl $conf_name { 123 | /// Enable interrupt for match register 0. 124 | pub fn enable_match0_interrupt(&self) { 125 | let timer = unsafe { &*pac::TIMER::ptr() }; 126 | timer.[].modify(|_r, w| w.tier_0().set_bit()); 127 | } 128 | 129 | /// Enable interrupt for match register 1. 130 | pub fn enable_match1_interrupt(&self) { 131 | let timer = unsafe { &*pac::TIMER::ptr() }; 132 | timer.[].modify(|_r, w| w.tier_1().set_bit()); 133 | } 134 | 135 | /// Enable interrupt for match register 2. 136 | pub fn enable_match2_interrupt(&self) { 137 | let timer = unsafe { &*pac::TIMER::ptr() }; 138 | timer.[].modify(|_r, w| w.tier_2().set_bit()); 139 | } 140 | 141 | /// Disable interrupt for match register 0. 142 | pub fn disable_match0_interrupt(&self) { 143 | let timer = unsafe { &*pac::TIMER::ptr() }; 144 | timer.[].modify(|_r, w| w.tier_0().clear_bit()); 145 | } 146 | 147 | /// Disable interrupt for match register 1. 148 | pub fn disable_match1_interrupt(&self) { 149 | let timer = unsafe { &*pac::TIMER::ptr() }; 150 | timer.[].modify(|_r, w| w.tier_1().clear_bit()); 151 | } 152 | 153 | /// Disable interrupt for match register 2. 154 | pub fn disable_match2_interrupt(&self) { 155 | let timer = unsafe { &*pac::TIMER::ptr() }; 156 | timer.[].modify(|_r, w| w.tier_2().clear_bit()); 157 | } 158 | 159 | /// Enable this counter 160 | pub fn enable(&self) { 161 | let timer = unsafe { &*pac::TIMER::ptr() }; 162 | timer.tcer.modify(|_r, w| w.[]().set_bit()); 163 | self.is_running.replace(true); 164 | } 165 | 166 | /// Disable this counter 167 | pub fn disable(&self) { 168 | let timer = unsafe { &*pac::TIMER::ptr() }; 169 | timer.tcer.modify(|_r, w| w.[]().set_bit()); 170 | self.is_running.replace(false); 171 | } 172 | 173 | /// Check if the timer is enabled / running 174 | pub fn is_enabled(&self) -> bool { 175 | *self.is_running.borrow() 176 | } 177 | 178 | /// Clear interrupt for match register 0. 179 | /// TICR register is write-only, no need to preserve register contents 180 | pub fn clear_match0_interrupt(&self) { 181 | let timer = unsafe { &*pac::TIMER::ptr() }; 182 | timer.[].write(|w| w.tclr_0().set_bit()); 183 | } 184 | 185 | /// Clear interrupt for match register 1. 186 | /// TICR register is write-only, no need to preserve register contents 187 | pub fn clear_match1_interrupt(&self) { 188 | let timer = unsafe { &*pac::TIMER::ptr() }; 189 | timer.[].write(|w| w.tclr_1().set_bit()); 190 | } 191 | 192 | /// Clear interrupt for match register 2. 193 | /// TICR register is write-only, no need to preserve register contents 194 | pub fn clear_match2_interrupt(&self) { 195 | let timer = unsafe { &*pac::TIMER::ptr() }; 196 | timer.[].write(|w| w.tclr_2().set_bit()); 197 | } 198 | 199 | /// Sets when the to preload. 200 | pub fn set_preload(&self, preload: Preload) { 201 | let timer = unsafe { &*pac::TIMER::ptr() }; 202 | timer 203 | .[] 204 | .modify(|_r, w| unsafe { w.tplcr().bits(preload.to_prlcr()) }); 205 | } 206 | 207 | /// Sets match register 0 208 | pub fn set_match0(&self, time: Milliseconds) { 209 | let time = (self.clock.0 as u64 * time.0 as u64 / 1_000u64) as u32; 210 | let timer = unsafe { &*pac::TIMER::ptr() }; 211 | timer.[].modify(|_r, w| unsafe { w.tmr().bits(time) }); 212 | } 213 | 214 | /// Sets match register 1 215 | pub fn set_match1(&self, time: Milliseconds) { 216 | let time = (self.clock.0 as u64 * time.0 as u64 / 1_000u64) as u32; 217 | let timer = unsafe { &*pac::TIMER::ptr() }; 218 | timer.[].modify(|_r, w| unsafe { w.tmr().bits(time) }); 219 | } 220 | 221 | /// Sets match register 2 222 | pub fn set_match2(&self, time: Milliseconds) { 223 | let time = (self.clock.0 as u64 * time.0 as u64 / 1_000u64) as u32; 224 | let timer = unsafe { &*pac::TIMER::ptr() }; 225 | timer.[].modify(|_r, w| unsafe { w.tmr().bits(time) }); 226 | } 227 | 228 | // Current counter value in raw ticks. 229 | pub fn current_ticks(&self) -> u32 { 230 | let timer = unsafe { &*pac::TIMER::ptr() }; 231 | timer.[].read().bits() 232 | } 233 | 234 | // Current counter value in milliseconds. 235 | pub fn current_time(&self) -> Milliseconds { 236 | let timer = unsafe { &*pac::TIMER::ptr() }; 237 | let ticks = timer.[].read().bits() as u64; 238 | Milliseconds::new( (ticks as u64 * 1000u64 / self.clock.0 as u64) as u32) 239 | } 240 | 241 | /// Will only become true if `enable_match0_interrupt` is active 242 | pub fn is_match0(&self) -> bool { 243 | let timer = unsafe { &*pac::TIMER::ptr() }; 244 | timer.[].read().tmsr_0().bit() 245 | } 246 | 247 | /// Will only become true if `enable_match2_interrupt` is active 248 | pub fn is_match1(&self) -> bool { 249 | let timer = unsafe { &*pac::TIMER::ptr() }; 250 | timer.[].read().tmsr_1().bit() 251 | } 252 | 253 | /// Will only become true if `enable_match2_interrupt` is active 254 | pub fn is_match2(&self) -> bool { 255 | let timer = unsafe { &*pac::TIMER::ptr() }; 256 | timer.[].read().tmsr_2().bit() 257 | } 258 | 259 | /// Set pre-load mode. 260 | pub fn pre_load_mode(&self) { 261 | let timer = unsafe { &*pac::TIMER::ptr() }; 262 | timer.tcmr.modify(|_r, w| w.[]().clear_bit()); 263 | } 264 | 265 | /// Set free running mode. 266 | pub fn free_running_mode(&self) { 267 | let timer = unsafe { &*pac::TIMER::ptr() }; 268 | timer.tcmr.modify(|_r, w| w.[]().set_bit()); 269 | } 270 | 271 | /// The value which should be used for preload. 272 | pub fn set_preload_value(&self, time: Milliseconds) { 273 | let time = (self.clock.0 as u64 * time.0 as u64 / 1_000u64) as u32; 274 | let timer = unsafe { &*pac::TIMER::ptr() }; 275 | timer.[].modify(|_r, w| unsafe { w.bits(time) }); 276 | } 277 | } 278 | } 279 | 280 | impl embedded_hal::timer::CountDown for $conf_name { 281 | type Error = CountDownError; 282 | 283 | type Time = Milliseconds; 284 | 285 | fn try_start(&mut self, count: T) -> Result<(), Self::Error> 286 | where 287 | T: Into, 288 | { 289 | self.count_down_target = Some(Milliseconds(self.current_time().0 + count.into().0)); 290 | self.last_count_down_value = None; 291 | Ok(()) 292 | } 293 | 294 | fn try_wait(&mut self) -> nb::Result<(), Self::Error> { 295 | match self.count_down_target { 296 | Some(millis) => { 297 | let current_time = self.current_time(); 298 | 299 | if current_time >= millis { 300 | Ok(()) 301 | } else { 302 | match self.last_count_down_value { 303 | Some(last_count_down_value) => { 304 | if current_time < last_count_down_value { 305 | Err(nb::Error::Other(CountDownError::Wrapped)) 306 | } else { 307 | self.last_count_down_value = Some(current_time); 308 | Err(nb::Error::WouldBlock) 309 | } 310 | } 311 | None => { 312 | self.last_count_down_value = Some(current_time); 313 | Err(nb::Error::WouldBlock) 314 | } 315 | } 316 | } 317 | } 318 | None => Ok(()), 319 | } 320 | } 321 | } 322 | 323 | paste! { 324 | impl $name { 325 | 326 | /// Configures the clock source and creates a configured timer channel 327 | pub fn set_clock_source( 328 | self, 329 | source: ClockSource, 330 | target_clock: Hertz, 331 | ) -> $conf_name { 332 | let timer = unsafe { &*pac::TIMER::ptr() }; 333 | timer 334 | .tccr 335 | .modify(|_r, w| unsafe { w.[]().bits(source.tccr_value()) }); 336 | 337 | let divider = (source.hertz() / target_clock.0).0; 338 | 339 | if !(1..=256).contains(÷r) { 340 | panic!("Unreachable target clock"); 341 | } 342 | 343 | timer 344 | .tcdr 345 | .modify(|_r, w| unsafe { w.[]().bits((divider - 1) as u8) }); 346 | 347 | timer.tcmr.modify(|_r, w| { 348 | w.[]().clear_bit() // pre-load mode 349 | }); 350 | timer.[].modify(|_r, w| unsafe { 351 | w.tplvr().bits(0) // pre-load value 352 | }); 353 | 354 | $conf_name { 355 | clock: target_clock, 356 | count_down_target: None, 357 | last_count_down_value: None, 358 | is_running: RefCell::new(false), 359 | } 360 | } 361 | } 362 | } 363 | } 364 | } 365 | 366 | impl_timer_channel!(TimerChannel0, ConfiguredTimerChannel0, 2, 1); 367 | 368 | impl_timer_channel!(TimerChannel1, ConfiguredTimerChannel1, 3, 2); 369 | 370 | /// Extension trait to split TIMER peripheral into independent channels 371 | pub trait TimerExt { 372 | fn split(self) -> Timers; 373 | } 374 | 375 | impl TimerExt for TIMER { 376 | fn split(self) -> Timers { 377 | Timers { 378 | channel0: TimerChannel0 {}, 379 | channel1: TimerChannel1 {}, 380 | } 381 | } 382 | } 383 | -------------------------------------------------------------------------------- /src/gpio.rs: -------------------------------------------------------------------------------- 1 | //! General Purpose Input/Output 2 | use core::marker::PhantomData; 3 | 4 | use crate::pac; 5 | 6 | /// Extension trait to split GLB peripheral into independent pins, registers and other modules 7 | pub trait GlbExt { 8 | /// Splits the register block into independent pins and modules 9 | fn split(self) -> Parts; 10 | } 11 | 12 | pub use uart_sig::*; 13 | 14 | /// UART signals 15 | pub mod uart_sig { 16 | use core::marker::PhantomData; 17 | 18 | use crate::pac; 19 | 20 | /// UART0 RTS (type state) 21 | pub struct Uart0Rts; 22 | 23 | /// UART0 CTS (type state) 24 | pub struct Uart0Cts; 25 | 26 | /// UART0 TXD (type state) 27 | pub struct Uart0Tx; 28 | 29 | /// UART0 RXD (type state) 30 | pub struct Uart0Rx; 31 | 32 | /// UART1 RXD (type state) 33 | pub struct Uart1Rx; 34 | 35 | /// UART1 RTS (type state) 36 | pub struct Uart1Rts; 37 | 38 | /// UART1 CTS (type state) 39 | pub struct Uart1Cts; 40 | 41 | /// UART1 TXD (type state) 42 | pub struct Uart1Tx; 43 | 44 | macro_rules! impl_uart_sig { 45 | ($UartSigi: ident, $doc1: expr, $sigi:ident, $UartMuxi: ident, $doc2: expr) => { 46 | #[doc = $doc1] 47 | pub struct $UartSigi; 48 | 49 | #[doc = $doc2] 50 | pub struct $UartMuxi { 51 | pub(crate) _mode: PhantomData, 52 | } 53 | 54 | impl $UartMuxi { 55 | /// Configure the internal UART signal to UART0-RTS 56 | pub fn into_uart0_rts(self) -> $UartMuxi { 57 | self.into_uart_mode(0) 58 | } 59 | 60 | /// Configure the internal UART signal to UART0-CTS 61 | pub fn into_uart0_cts(self) -> $UartMuxi { 62 | self.into_uart_mode(1) 63 | } 64 | 65 | /// Configure the internal UART signal to UART0-TX 66 | pub fn into_uart0_tx(self) -> $UartMuxi { 67 | self.into_uart_mode(2) 68 | } 69 | 70 | /// Configure the internal UART signal to UART0-RX 71 | pub fn into_uart0_rx(self) -> $UartMuxi { 72 | self.into_uart_mode(3) 73 | } 74 | 75 | /// Configure the internal UART signal to UART1-RTS 76 | pub fn into_uart1_rts(self) -> $UartMuxi { 77 | self.into_uart_mode(4) 78 | } 79 | 80 | /// Configure the internal UART signal to UART1-CTS 81 | pub fn into_uart1_cts(self) -> $UartMuxi { 82 | self.into_uart_mode(5) 83 | } 84 | 85 | /// Configure the internal UART signal to UART1-TX 86 | pub fn into_uart1_tx(self) -> $UartMuxi { 87 | self.into_uart_mode(6) 88 | } 89 | 90 | /// Configure the internal UART signal to UART1-RX 91 | pub fn into_uart1_rx(self) -> $UartMuxi { 92 | self.into_uart_mode(7) 93 | } 94 | 95 | paste::paste! { 96 | #[inline] 97 | fn into_uart_mode(self, mode: u8) -> $UartMuxi { 98 | let glb = unsafe { &*pac::GLB::ptr() }; 99 | 100 | glb.uart_sig_sel_0.modify(|_r, w| unsafe { w 101 | .[]().bits(mode) 102 | }); 103 | 104 | $UartMuxi { _mode: PhantomData } 105 | } 106 | } 107 | } 108 | }; 109 | } 110 | 111 | impl_uart_sig!( 112 | UartSig0, 113 | "UART signal 0 (type state)", 114 | sig_0, 115 | UartMux0, 116 | "UART multiplexer peripherals for signal 0" 117 | ); 118 | 119 | impl_uart_sig!( 120 | UartSig1, 121 | "UART signal 1 (type state)", 122 | sig_1, 123 | UartMux1, 124 | "UART multiplexer peripherals for signal 1" 125 | ); 126 | 127 | impl_uart_sig!( 128 | UartSig2, 129 | "UART signal 2 (type state)", 130 | sig_2, 131 | UartMux2, 132 | "UART multiplexer peripherals for signal 2" 133 | ); 134 | 135 | impl_uart_sig!( 136 | UartSig3, 137 | "UART signal 3 (type state)", 138 | sig_3, 139 | UartMux3, 140 | "UART multiplexer peripherals for signal 3" 141 | ); 142 | 143 | impl_uart_sig!( 144 | UartSig4, 145 | "UART signal 4 (type state)", 146 | sig_4, 147 | UartMux4, 148 | "UART multiplexer peripherals for signal 4" 149 | ); 150 | 151 | impl_uart_sig!( 152 | UartSig5, 153 | "UART signal 5 (type state)", 154 | sig_5, 155 | UartMux5, 156 | "UART multiplexer peripherals for signal 5" 157 | ); 158 | 159 | impl_uart_sig!( 160 | UartSig6, 161 | "UART signal 6 (type state)", 162 | sig_6, 163 | UartMux6, 164 | "UART multiplexer peripherals for signal 6" 165 | ); 166 | 167 | impl_uart_sig!( 168 | UartSig7, 169 | "UART signal 7 (type state)", 170 | sig_7, 171 | UartMux7, 172 | "UART multiplexer peripherals for signal 7" 173 | ); 174 | } 175 | 176 | /// Clock configurator registers 177 | pub struct ClkCfg { 178 | pub(crate) _ownership: (), 179 | } 180 | 181 | /* 182 | // todo: english 183 | 在GPIO模式下,可以设置内部上下拉,以类型状态机模式设计 184 | SPI、UART、I2C等数字功能下,可以设置内部上下拉,但不会影响返回类型的状态 185 | ADC、DAC下,软件禁止设置内部上下拉。HAL库不会生成此类函数,以免出错。 186 | */ 187 | 188 | /// Hi-Z Floating pin (type state) 189 | pub struct Floating; 190 | /// Pulled down pin (type state) 191 | pub struct PullDown; 192 | /// Pulled up pin (type state) 193 | pub struct PullUp; 194 | 195 | /// Input mode (type state) 196 | pub struct Input { 197 | _mode: PhantomData, 198 | } 199 | 200 | /// Output mode (type state) 201 | pub struct Output { 202 | _mode: PhantomData, 203 | } 204 | 205 | /// UART pin mode (type state) 206 | pub struct Uart; 207 | 208 | /// SPI pin mode (type state) 209 | pub struct Spi; 210 | 211 | /// I2C pin mode (type state) 212 | pub struct I2c; 213 | 214 | #[doc(hidden)] 215 | pub trait UartPin {} 216 | 217 | // There are Pin0 to Pin22, totally 23 pins 218 | 219 | pub use self::pin::*; 220 | 221 | macro_rules! impl_glb { 222 | ($($Pini: ident: ($pini: ident, $gpio_cfgctli: ident, $UartSigi: ident, $sigi: ident, $spi_kind: ident, $i2c_kind: ident, $gpio_i: ident) ,)+) => { 223 | impl GlbExt for pac::GLB { 224 | fn split(self) -> Parts { 225 | Parts { 226 | $( $pini: $Pini { _mode: PhantomData }, )+ 227 | uart_mux0: UartMux0 { _mode: PhantomData }, 228 | uart_mux1: UartMux1 { _mode: PhantomData }, 229 | uart_mux2: UartMux2 { _mode: PhantomData }, 230 | uart_mux3: UartMux3 { _mode: PhantomData }, 231 | uart_mux4: UartMux4 { _mode: PhantomData }, 232 | uart_mux5: UartMux5 { _mode: PhantomData }, 233 | uart_mux6: UartMux6 { _mode: PhantomData }, 234 | uart_mux7: UartMux7 { _mode: PhantomData }, 235 | clk_cfg: ClkCfg { _ownership: () }, 236 | } 237 | } 238 | } 239 | 240 | /// GPIO parts 241 | pub struct Parts { 242 | $( pub $pini: $Pini>, )+ 243 | pub uart_mux0: UartMux0, 244 | pub uart_mux1: UartMux1, 245 | pub uart_mux2: UartMux2, 246 | pub uart_mux3: UartMux3, 247 | pub uart_mux4: UartMux4, 248 | pub uart_mux5: UartMux5, 249 | pub uart_mux6: UartMux6, 250 | pub uart_mux7: UartMux7, 251 | pub clk_cfg: ClkCfg, 252 | } 253 | 254 | /// GPIO pins 255 | pub mod pin { 256 | use core::marker::PhantomData; 257 | use core::convert::Infallible; 258 | use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin, toggleable}; 259 | 260 | use crate::pac; 261 | use super::*; 262 | 263 | $( 264 | /// Pin 265 | pub struct $Pini { 266 | pub(crate) _mode: PhantomData, 267 | } 268 | 269 | impl $Pini { 270 | // 11 -> GPIO_FUN_SWGPIO 271 | /// Configures the pin to operate as a Hi-Z floating output pin. 272 | pub fn into_floating_output(self) -> $Pini> { 273 | self.into_pin_with_mode(11, false, false, false) 274 | } 275 | 276 | /// Configures the pin to operate as a pull-up output pin. 277 | pub fn into_pull_up_output(self) -> $Pini> { 278 | self.into_pin_with_mode(11, true, false, false) 279 | } 280 | 281 | /// Configures the pin to operate as a pull-down output pin. 282 | pub fn into_pull_down_output(self) -> $Pini> { 283 | self.into_pin_with_mode(11, false, true, false) 284 | } 285 | 286 | /// Configures the pin to operate as a Hi-Z floating input pin. 287 | pub fn into_floating_input(self) -> $Pini> { 288 | self.into_pin_with_mode(11, false, false, true) 289 | } 290 | 291 | /// Configures the pin to operate as a pull-up input pin. 292 | pub fn into_pull_up_input(self) -> $Pini> { 293 | self.into_pin_with_mode(11, true, false, true) 294 | } 295 | 296 | /// Configures the pin to operate as a pull-down input pin. 297 | pub fn into_pull_down_input(self) -> $Pini> { 298 | self.into_pin_with_mode(11, false, true, true) 299 | } 300 | 301 | paste::paste! { 302 | #[inline] 303 | fn into_pin_with_mode(self, mode: u8, pu: bool, pd: bool, ie: bool) -> $Pini { 304 | let glb = unsafe { &*pac::GLB::ptr() }; 305 | 306 | glb.$gpio_cfgctli.modify(|_r, w| unsafe { w 307 | .[]().bits(mode) 308 | .[]().bit(ie) // output 309 | .[]().bit(pu) 310 | .[]().bit(pd) 311 | .[]().bits(0) // disabled 312 | .[]().clear_bit() 313 | }); 314 | 315 | // If we're an input clear the Output Enable bit as well, else set it. 316 | glb.gpio_cfgctl34.modify(|_, w| w.[]().bit(!ie)); 317 | 318 | $Pini { _mode: PhantomData } 319 | } 320 | } 321 | } 322 | 323 | impl $Pini> { 324 | paste::paste! { 325 | /// Enable smitter GPIO input filter 326 | pub fn enable_smitter(&mut self) { 327 | let glb = unsafe { &*pac::GLB::ptr() }; 328 | 329 | glb.$gpio_cfgctli.modify(|_, w| w.[]().set_bit()); 330 | } 331 | 332 | /// Enable smitter GPIO output filter 333 | pub fn disable_smitter(&mut self) { 334 | let glb = unsafe { &*pac::GLB::ptr() }; 335 | 336 | glb.$gpio_cfgctli.modify(|_, w| w.[]().clear_bit()); 337 | } 338 | } 339 | } 340 | 341 | impl $Pini { 342 | paste::paste! { 343 | /// Configures the pin to UART alternate mode 344 | pub fn [](self) -> $Pini { 345 | // 7 -> GPIO_FUN_UART 346 | self.into_pin_with_mode(7, true, false, true) 347 | } 348 | 349 | /// Configures the pin to SPI alternate mode 350 | pub fn [](self) -> $Pini { 351 | // 4 -> GPIO0_FUN_SPI_x 352 | self.into_pin_with_mode(4, true, false, true) 353 | } 354 | 355 | /// Configures the pin to I2C alternate mode 356 | pub fn [](self) -> $Pini { 357 | // 6 -> GPIO_FUN_I2C_x 358 | self.into_pin_with_mode(6, true, false, true) 359 | } 360 | } 361 | } 362 | 363 | impl UartPin<$UartSigi> for $Pini {} 364 | 365 | impl InputPin for $Pini> { 366 | type Error = Infallible; 367 | 368 | paste::paste! { 369 | fn try_is_high(&self) -> Result { 370 | let glb = unsafe { &*pac::GLB::ptr() }; 371 | 372 | Ok(glb.gpio_cfgctl30.read().[]().bit_is_set()) 373 | } 374 | 375 | fn try_is_low(&self) -> Result { 376 | let glb = unsafe { &*pac::GLB::ptr() }; 377 | 378 | Ok(glb.gpio_cfgctl30.read().[]().bit_is_clear()) 379 | } 380 | } 381 | } 382 | 383 | impl OutputPin for $Pini> { 384 | type Error = Infallible; 385 | 386 | paste::paste! { 387 | fn try_set_high(&mut self) -> Result<(), Self::Error> { 388 | let glb = unsafe { &*pac::GLB::ptr() }; 389 | 390 | glb.gpio_cfgctl32.modify(|_, w| w.[]().set_bit()); 391 | 392 | Ok(()) 393 | } 394 | 395 | fn try_set_low(&mut self) -> Result<(), Self::Error> { 396 | let glb = unsafe { &*pac::GLB::ptr() }; 397 | 398 | glb.gpio_cfgctl32.modify(|_, w| w.[]().clear_bit()); 399 | 400 | Ok(()) 401 | } 402 | } 403 | } 404 | 405 | impl StatefulOutputPin for $Pini> { 406 | paste::paste! { 407 | fn try_is_set_high(&self) -> Result { 408 | let glb = unsafe { &*pac::GLB::ptr() }; 409 | 410 | Ok(glb.gpio_cfgctl32.read().[]().bit_is_set()) 411 | } 412 | 413 | fn try_is_set_low(&self) -> Result { 414 | let glb = unsafe { &*pac::GLB::ptr() }; 415 | 416 | Ok(glb.gpio_cfgctl32.read().[]().bit_is_clear()) 417 | } 418 | } 419 | } 420 | 421 | impl toggleable::Default for $Pini> {} 422 | )+ 423 | } 424 | }; 425 | } 426 | 427 | // There are Pin0 to Pin22, totally 23 pins 428 | // todo: generate macros 429 | impl_glb! { 430 | Pin0: (pin0, gpio_cfgctl0, UartSig0, sig0, miso, scl, gpio_0), 431 | Pin1: (pin1, gpio_cfgctl0, UartSig1, sig1, mosi, sda, gpio_1), 432 | Pin2: (pin2, gpio_cfgctl1, UartSig2, sig2, ss, scl, gpio_2), 433 | Pin3: (pin3, gpio_cfgctl1, UartSig3, sig3, sclk, sda, gpio_3), 434 | Pin4: (pin4, gpio_cfgctl2, UartSig4, sig4, miso, scl, gpio_4), 435 | Pin5: (pin5, gpio_cfgctl2, UartSig5, sig5, mosi, sda, gpio_5), 436 | Pin6: (pin6, gpio_cfgctl3, UartSig6, sig6, ss, scl, gpio_6), 437 | Pin7: (pin7, gpio_cfgctl3, UartSig7, sig7, sclk, sda, gpio_7), 438 | Pin8: (pin8, gpio_cfgctl4, UartSig0, sig0, miso, scl, gpio_8), 439 | Pin9: (pin9, gpio_cfgctl4, UartSig1, sig1, mosi, sda, gpio_9), 440 | Pin10: (pin10, gpio_cfgctl5, UartSig2, sig2, ss, scl, gpio_10), 441 | Pin11: (pin11, gpio_cfgctl5, UartSig3, sig3, sclk, sda, gpio_11), 442 | Pin12: (pin12, gpio_cfgctl6, UartSig4, sig4, miso, scl, gpio_12), 443 | Pin13: (pin13, gpio_cfgctl6, UartSig5, sig5, mosi, sda, gpio_13), 444 | Pin14: (pin14, gpio_cfgctl7, UartSig6, sig6, ss, scl, gpio_14), 445 | Pin15: (pin15, gpio_cfgctl7, UartSig7, sig7, sclk, sda, gpio_15), 446 | Pin16: (pin16, gpio_cfgctl8, UartSig0, sig0, miso, scl, gpio_16), 447 | Pin17: (pin17, gpio_cfgctl8, UartSig1, sig1, mosi, sda, gpio_17), 448 | Pin18: (pin18, gpio_cfgctl9, UartSig2, sig2, ss, scl, gpio_18), 449 | Pin19: (pin19, gpio_cfgctl9, UartSig3, sig3, sclk, sda, gpio_19), 450 | Pin20: (pin20, gpio_cfgctl10, UartSig4, sig4, miso, scl, gpio_20), 451 | Pin21: (pin21, gpio_cfgctl10, UartSig5, sig5, mosi, sda, gpio_21), 452 | Pin22: (pin22, gpio_cfgctl11, UartSig6, sig6, ss, scl, gpio_22), 453 | } 454 | -------------------------------------------------------------------------------- /src/clock.rs: -------------------------------------------------------------------------------- 1 | //! SoC clock configuration 2 | 3 | // The clocking in this chip is split into several peripheral sections oriented around low power modes 4 | // 5 | // Here is a quick overview of the peripheral sections as relates to those modes 6 | // 7 | // The GLB (global register) portion of the chip controls most clock enable/division circuits 8 | // as well as the GPIO 9 | // The AON (always on) section is parts of the SOC that are active in all but the deepest 10 | // hibernate mode (HBN3). This section controls power to external high frequency crystal 11 | // The PDS (power-down state, sleep) is the smallest level of power saving. 12 | // It always keeps CORE SRAM and timer power enabled. 13 | // Power to CPU, Wireless PHY+MAC, and digital/analog pins is optionally turned off at different pre-set levels 14 | // Peripherals that relate to clocking in this module: PLL 15 | // The HBN (hibernate, deep sleep) section is the largest level of power saving. 16 | // It always turns off CPU, Wireless PHY+MAC, CORE SRAM and timers, and optionally sections or all of AON 17 | // It contains the root clock source selection (sysclk/flck) 18 | // The L1C (level 1 cache) section maps tightly-coupled ram/cache SRAM in front of slower buses 19 | // (ROM, flash). It contains configuration for internal ROM access latency 20 | // 21 | // Currently implemented clock tree configuration options: 22 | // - internal 32Mhz RC oscillator for sysclock 23 | // - XTAL driving PLL, sysclock frequencies of 48/80/120/160/192Mhz 24 | // - UART using PLL if sysclock is using PLL 25 | 26 | use crate::delay::*; 27 | use crate::gpio::ClkCfg; 28 | use crate::pac; 29 | use core::num::NonZeroU32; 30 | use embedded_hal::blocking::delay::DelayUs; 31 | use embedded_time::rate::{Extensions, Hertz}; 32 | 33 | /// Internal high-speed RC oscillator frequency 34 | pub const RC32M: u32 = 32_000_000; 35 | /// UART peripheral clock frequency when PLL selected 36 | pub const UART_PLL_FREQ: u32 = 160_000_000; 37 | 38 | #[derive(PartialEq, Copy, Clone)] 39 | #[repr(u32)] 40 | pub enum SysclkFreq { 41 | Rc32Mhz = 32_000_000, 42 | Pll48Mhz = 48_000_000, 43 | Pll120Mhz = 120_000_000, 44 | Pll160Mhz = 160_000_000, 45 | } 46 | 47 | /// Frozen clock frequencies 48 | /// 49 | /// The existance of this value indicates that the clock configuration can no longer be changed 50 | #[derive(Clone, Copy)] 51 | pub struct Clocks { 52 | sysclk: Hertz, 53 | uart_clk: Hertz, 54 | spi_clk: Hertz, 55 | i2c_clk: Hertz, 56 | _xtal_freq: Option, 57 | pll_enable: bool, 58 | } 59 | 60 | impl Clocks { 61 | pub fn new() -> Self { 62 | Clocks { 63 | sysclk: Hertz(RC32M), 64 | uart_clk: Hertz(RC32M), 65 | spi_clk: Hertz(RC32M), 66 | i2c_clk: Hertz(RC32M), 67 | _xtal_freq: None, 68 | pll_enable: false, 69 | } 70 | } 71 | 72 | pub fn sysclk(&self) -> Hertz { 73 | self.sysclk 74 | } 75 | 76 | pub fn pll_enable(&self) -> bool { 77 | self.pll_enable 78 | } 79 | 80 | pub const fn uart_clk(&self) -> Hertz { 81 | self.uart_clk 82 | } 83 | 84 | pub const fn spi_clk(&self) -> Hertz { 85 | self.spi_clk 86 | } 87 | 88 | pub const fn i2c_clk(&self) -> Hertz { 89 | self.i2c_clk 90 | } 91 | } 92 | 93 | impl Default for Clocks { 94 | fn default() -> Self { 95 | Self::new() 96 | } 97 | } 98 | 99 | /// Strict clock configurator 100 | /// 101 | /// This configurator only accepts strictly accurate value. If all available frequency 102 | /// values after configurated does not strictly equal to the desired value, the `freeze` 103 | /// function panics. Users must be careful to ensure that the output frequency values 104 | /// can be strictly configurated into using input frequency values and internal clock 105 | /// frequencies. 106 | /// 107 | /// If you need to get most precise frequenct possible (other than the stictly accutare 108 | /// value only), use configurator `Precise` instead. 109 | /// 110 | /// For example if 49.60MHz and 50.20MHz are able to be configurated prefectly, input 111 | /// 50MHz into `Strict` would result in a panic when performing `freeze`; however input 112 | /// same 50MHz into `Precise` it would not panic, but would set and freeze into 113 | /// 50.20MHz as the frequency error is smallest. 114 | pub struct Strict { 115 | target_i2c_clk: Option, 116 | target_spi_clk: Option, 117 | target_uart_clk: Option, 118 | pll_xtal_freq: Option, 119 | sysclk: SysclkFreq, 120 | } 121 | 122 | impl Strict { 123 | /// Create a strict configurator 124 | pub fn new() -> Self { 125 | Strict { 126 | target_i2c_clk: None, 127 | target_spi_clk: None, 128 | target_uart_clk: None, 129 | pll_xtal_freq: None, 130 | sysclk: SysclkFreq::Rc32Mhz, 131 | } 132 | } 133 | 134 | /// Sets the desired frequency for the I2C-CLK clock 135 | pub fn i2c_clk(mut self, freq: impl Into) -> Self { 136 | let freq_hz = freq.into().0; 137 | 138 | self.target_i2c_clk = NonZeroU32::new(freq_hz); 139 | 140 | self 141 | } 142 | 143 | /// Sets the desired frequency for the SPI-CLK clock 144 | pub fn spi_clk(mut self, freq: impl Into) -> Self { 145 | let freq_hz = freq.into().0; 146 | 147 | self.target_spi_clk = NonZeroU32::new(freq_hz); 148 | 149 | self 150 | } 151 | 152 | /// Sets the desired frequency for the UART-CLK clock 153 | pub fn uart_clk(mut self, freq: impl Into) -> Self { 154 | let freq_hz = freq.into().0; 155 | 156 | self.target_uart_clk = NonZeroU32::new(freq_hz); 157 | 158 | self 159 | } 160 | 161 | /// Enables PLL clock source, using external XTAL frequency provided 162 | pub fn use_pll(mut self, freq: impl Into) -> Self { 163 | self.pll_xtal_freq = Some(freq.into().0); 164 | 165 | self 166 | } 167 | 168 | /// Set the system clock frequency (fclk/hclk) 169 | /// 170 | /// Supported frequencies: 171 | /// `32_000_000`, `48_000_000`, `80_000_000`, `120_000_000`, `160_000_000` 172 | pub fn sys_clk(mut self, freq: SysclkFreq) -> Self { 173 | self.sysclk = freq; 174 | 175 | self 176 | } 177 | 178 | /// Calculate and balance clock registers to configure into the given clock value. 179 | /// If accurate value is not possible, this function panics. 180 | /// 181 | /// Be aware that Rust's panic is sometimes not obvious on embedded devices; if your 182 | /// program didn't execute as expected, or the `pc` is pointing to somewhere weird 183 | /// (usually `abort: j abort`), it's likely that this function have panicked. 184 | /// Breakpoint on `rust_begin_unwind` may help debugging. 185 | /// 186 | /// # Panics 187 | /// 188 | /// If strictly accurate value of given `ck_sys` etc. is not reachable, this function 189 | /// panics. 190 | pub fn freeze(self, _clk_cfg: &mut ClkCfg) -> Clocks { 191 | // Default to not using the PLL, and selecting the internal RC oscillator if nothing selected 192 | let pll_xtal_freq = self.pll_xtal_freq.unwrap_or(0); 193 | let pll_enabled = pll_xtal_freq != 0; 194 | let sysclk = self.sysclk; 195 | 196 | // If sysclk isn't 32Mhz but PLL isn't enabled, panic 197 | assert!(pll_enabled || sysclk == SysclkFreq::Rc32Mhz); 198 | 199 | // If PLL is available we'll be using the PLL_160Mhz clock, otherwise sysclk 200 | let uart_clk_src = if pll_enabled { 201 | UART_PLL_FREQ 202 | } else { 203 | sysclk as u32 204 | }; 205 | 206 | // UART config 207 | let uart_clk = self 208 | .target_uart_clk 209 | .map(|f| f.get()) 210 | .unwrap_or(uart_clk_src as u32); 211 | 212 | let uart_clk_div = { 213 | let ans = uart_clk_src / uart_clk; 214 | 215 | if !(1..=7).contains(&ans) || ans * uart_clk != uart_clk_src { 216 | panic!("unreachable uart_clk") 217 | } 218 | 219 | ans as u8 220 | }; 221 | 222 | // Enable system clock, PLL + crystal if required 223 | // omit if settings match boot defaults 224 | if sysclk != SysclkFreq::Pll160Mhz || pll_xtal_freq != 40_000_000 { 225 | match sysclk { 226 | SysclkFreq::Rc32Mhz => glb_set_system_clk_rc32(), 227 | _ => glb_set_system_clk_pll(sysclk as u32, pll_xtal_freq), 228 | }; 229 | } 230 | 231 | // If PLL is enabled, use that for the UART base clock 232 | // Otherwise, use sysclk as the UART clock 233 | unsafe { &*pac::HBN::ptr() } 234 | .hbn_glb 235 | .modify(|_, w| w.hbn_uart_clk_sel().bit(pll_enabled)); 236 | 237 | // Write UART clock divider 238 | unsafe { &*pac::GLB::ptr() }.clk_cfg2.modify(|_, w| unsafe { 239 | w.uart_clk_div() 240 | .bits(uart_clk_div - 1_u8) 241 | .uart_clk_en() 242 | .set_bit() 243 | }); 244 | 245 | // SPI config 246 | let spi_clk = self 247 | .target_spi_clk 248 | .map(|f| f.get()) 249 | .unwrap_or(32_000_000u32); 250 | 251 | // SPI Clock Divider (BUS_CLK/(N+1)), default BUS_CLK/4 252 | let bus_clock = calculate_bus_clock(); 253 | let spi_clk_div = bus_clock.0 / spi_clk; 254 | 255 | if spi_clk_div == 0 || spi_clk_div > 0b100000 { 256 | panic!("Unreachable SPI_CLK"); 257 | } 258 | 259 | let spi_clk_div = ((spi_clk_div - 1) & 0b11111) as u8; 260 | // Write SPI clock divider 261 | unsafe { &*pac::GLB::ptr() } 262 | .clk_cfg3 263 | .modify(|_, w| unsafe { w.spi_clk_en().set_bit().spi_clk_div().bits(spi_clk_div) }); 264 | 265 | // I2C config 266 | let i2c_clk = self 267 | .target_i2c_clk 268 | .map(|f| f.get()) 269 | .unwrap_or(32_000_000u32); 270 | 271 | // I2C Clock Divider (BUS_CLK/(N+1)), default BUS_CLK/255 272 | let i2c_clk_div = bus_clock.0 / i2c_clk; 273 | 274 | if i2c_clk_div == 0 || i2c_clk_div > 255 { 275 | panic!("Unreachable I2C_CLK"); 276 | } 277 | 278 | let i2c_clk_div = ((i2c_clk_div - 1) & 0xff) as u8; 279 | // Write I2C clock divider 280 | unsafe { &*pac::GLB::ptr() } 281 | .clk_cfg3 282 | .modify(|_, w| unsafe { w.i2c_clk_en().set_bit().i2c_clk_div().bits(i2c_clk_div) }); 283 | 284 | Clocks { 285 | sysclk: Hertz(sysclk as u32), 286 | uart_clk: Hertz(uart_clk), 287 | spi_clk: Hertz(spi_clk), 288 | i2c_clk: Hertz(i2c_clk), 289 | _xtal_freq: Some(Hertz(pll_xtal_freq)), 290 | pll_enable: pll_enabled, 291 | } 292 | } 293 | } 294 | 295 | impl Default for Strict { 296 | fn default() -> Self { 297 | Self::new() 298 | } 299 | } 300 | 301 | /// Gets the current bus clock rate 302 | fn calculate_bus_clock() -> Hertz { 303 | let root_clk_sel = unsafe { &*pac::GLB::ptr() } 304 | .clk_cfg0 305 | .read() 306 | .hbn_root_clk_sel() 307 | .bits(); 308 | let pll_clk_sel = unsafe { &*pac::GLB::ptr() } 309 | .clk_cfg0 310 | .read() 311 | .reg_pll_sel() 312 | .bits(); 313 | let hclk_div = unsafe { &*pac::GLB::ptr() } 314 | .clk_cfg0 315 | .read() 316 | .reg_hclk_div() 317 | .bits(); 318 | let bclk_div = unsafe { &*pac::GLB::ptr() } 319 | .clk_cfg0 320 | .read() 321 | .reg_bclk_div() 322 | .bits(); 323 | 324 | let root = match root_clk_sel { 325 | 0 => 32_000_000_u32.Hz(), 326 | 1 => 40_000_000_u32.Hz(), 327 | _ => match pll_clk_sel { 328 | 0 => 48_000_000_u32.Hz(), 329 | 1 => 120_000_000_u32.Hz(), 330 | 2 => 160_000_000_u32.Hz(), 331 | _ => 192_000_000_u32.Hz(), 332 | }, 333 | }; 334 | 335 | root / (hclk_div as u32 + 1) / (bclk_div as u32 + 1) 336 | } 337 | 338 | /// Sets the system clock in the (undocumented) system_core_clock register 339 | fn system_core_clock_set(value: u32) { 340 | unsafe { &*pac::HBN::ptr() } 341 | .hbn_rsv2 342 | .write(|w| unsafe { w.bits(value) }) 343 | } 344 | 345 | /// Gets the system clock in the (undocumented) system_core_clock register 346 | fn system_core_clock_get() -> u32 { 347 | unsafe { &*pac::HBN::ptr() }.hbn_rsv2.read().bits() 348 | } 349 | 350 | fn glb_set_system_clk_div(hclkdiv: u8, bclkdiv: u8) { 351 | // recommended: fclk<=160MHz, bclk<=80MHz 352 | // fclk is determined by hclk_div (strange), which then feeds into bclk, hclk and uartclk 353 | // glb_reg_bclk_dis isn't in the SVD file, so it isn't generated through svd2rust 354 | // It's only used by this function so define it as a local variable 355 | let glb_reg_bclk_dis = 0x4000_0FFC as *mut u32; 356 | 357 | unsafe { &*pac::GLB::ptr() } 358 | .clk_cfg0 359 | .modify(|_, w| unsafe { w.reg_hclk_div().bits(hclkdiv).reg_bclk_div().bits(bclkdiv) }); 360 | unsafe { glb_reg_bclk_dis.write_volatile(1) }; 361 | unsafe { glb_reg_bclk_dis.write_volatile(0) }; 362 | 363 | let currclock = system_core_clock_get(); 364 | 365 | system_core_clock_set(currclock / (hclkdiv as u32 + 1)); 366 | 367 | let mut delay = McycleDelay::new(system_core_clock_get()); 368 | 369 | // This delay used to be 8 NOPS (1/4 us). Might need to be replaced again. 370 | delay.try_delay_us(1).unwrap(); 371 | 372 | unsafe { &*pac::GLB::ptr() } 373 | .clk_cfg0 374 | .modify(|_, w| w.reg_hclk_en().set_bit().reg_bclk_en().set_bit()); 375 | 376 | delay.try_delay_us(1).unwrap(); 377 | } 378 | 379 | // This is a reference implementation of `PDS_Select_XTAL_As_PLL_Ref`. 380 | // It is currently unused. 381 | #[allow(dead_code)] 382 | fn pds_select_xtal_as_pll_ref() { 383 | unsafe { &*pac::PDS::ptr() }.clkpll_top_ctrl.modify(|_, w| { 384 | w.clkpll_refclk_sel() 385 | .set_bit() 386 | .clkpll_xtal_rc32m_sel() 387 | .clear_bit() 388 | }); 389 | } 390 | 391 | // This is a reference implementation of `PDS_Power_Off_PLL`. 392 | // It is currently unused. 393 | #[allow(dead_code)] 394 | fn pds_power_off_pll() { 395 | unsafe { &*pac::PDS::ptr() } 396 | .pu_rst_clkpll 397 | .modify(|_, w| w.pu_clkpll_sfreg().clear_bit().pu_clkpll().clear_bit()); 398 | 399 | unsafe { &*pac::PDS::ptr() }.pu_rst_clkpll.modify(|_, w| { 400 | w.clkpll_pu_cp() 401 | .clear_bit() 402 | .clkpll_pu_pfd() 403 | .clear_bit() 404 | .clkpll_pu_fbdv() 405 | .clear_bit() 406 | .clkpll_pu_postdiv() 407 | .clear_bit() 408 | }); 409 | } 410 | 411 | /// Power up PLL using C function built into on-chip ROM 412 | // Powering on the PLL means powering *OFF* the PLL if it's already running. 413 | // If we're running from flash, this causes a lockup. Running from RAM or ROM is 414 | // okay though. So there's probably some steps we need to take to do this safely, 415 | // but the flash peripheral is not documented yet. 416 | // The easiest solution is to use the C function built into the ROM to do the change. 417 | #[inline] 418 | fn pds_power_on_pll_rom(freq: u32) { 419 | // Lookup table for ROM function addresses is at 0x21010800 420 | // index in the table for RomDriver_PDS_Power_On_PLL is 88 421 | // each entry is a pointer, size of each entry is 4 bytes (sizeof usize) 422 | // we can can use core::ptr::offset to calculate the true offset for us. 423 | // In my ROM, the address of rom_pds_power_on_pll is 0x2101_4ACE, 424 | // this line is commented out in case this is not true for others 425 | let rom_function_table_base = (0x2101_0800) as *mut usize; 426 | let power_on_pll_entry = unsafe { rom_function_table_base.offset(88) }; 427 | let power_on_pll_addr = unsafe { power_on_pll_entry.read_volatile() }; 428 | //assert_eq!(power_on_pll_addr, 0x2101_4ACE); 429 | 430 | let romdriver_pds_power_on_pll = unsafe { 431 | core::mem::transmute::<*const (), extern "C" fn(usize) -> usize>( 432 | power_on_pll_addr as *const (), 433 | ) 434 | }; 435 | let xtal_src = match freq { 436 | 24_000_000 => 1, 437 | 32_000_000 => 2, 438 | 38_400_000 => 3, 439 | 40_000_000 => 4, 440 | 26_000_000 => 5, 441 | _ => panic!("Unsupported PLL clock source"), 442 | }; 443 | 444 | // 0 == success, 1 == failure, 2 == timeout 445 | let pll_success = romdriver_pds_power_on_pll(xtal_src); 446 | assert_eq!(pll_success, 0); 447 | } 448 | 449 | /// Minimal implementation of power-on pll. Currently only allows external xtal 450 | /// Fails when running from flash - use the pds_power_on_pll_rom for now 451 | // TODO: work out how to safely power off PLL while running from flash 452 | // 453 | // This is a reference implementation of `PDS_Power_On_PLL`. 454 | // It is currently unused. 455 | #[allow(dead_code)] 456 | fn pds_power_on_pll(freq: u32) { 457 | let pds = unsafe { &*pac::PDS::ptr() }; 458 | let mut delay = McycleDelay::new(system_core_clock_get()); 459 | 460 | pds_select_xtal_as_pll_ref(); 461 | 462 | // power off PLL first - this step is required 463 | pds_power_off_pll(); 464 | 465 | // PLL param config 466 | if freq == 26_000_000 { 467 | pds.clkpll_cp.modify(|_, w| unsafe { 468 | w.clkpll_icp_1u() 469 | .bits(1) 470 | .clkpll_icp_5u() 471 | .bits(0) 472 | .clkpll_int_frac_sw() 473 | .set_bit() 474 | }); 475 | 476 | pds.clkpll_rz.modify(|_, w| unsafe { 477 | w.clkpll_c3() 478 | .bits(2) 479 | .clkpll_cz() 480 | .bits(2) 481 | .clkpll_rz() 482 | .bits(5) 483 | .clkpll_r4_short() 484 | .clear_bit() 485 | }); 486 | } else { 487 | pds.clkpll_cp.modify(|_, w| unsafe { 488 | w.clkpll_icp_1u() 489 | .bits(0) 490 | .clkpll_icp_5u() 491 | .bits(2) 492 | .clkpll_int_frac_sw() 493 | .clear_bit() 494 | }); 495 | 496 | pds.clkpll_rz.modify(|_, w| unsafe { 497 | w.clkpll_c3() 498 | .bits(3) 499 | .clkpll_cz() 500 | .bits(1) 501 | .clkpll_rz() 502 | .bits(1) 503 | .clkpll_r4_short() 504 | .set_bit() 505 | }); 506 | } 507 | 508 | pds.clkpll_top_ctrl 509 | .modify(|_, w| unsafe { w.clkpll_postdiv().bits(0x14).clkpll_refdiv_ratio().bits(2) }); 510 | 511 | pds.clkpll_sdm.modify(|_, w| unsafe { 512 | w.clkpll_sdmin().bits(match freq { 513 | 24_000_000 => 0x50_0000, 514 | 32_000_000 => 0x3C_0000, 515 | 38_400_000 => 0x32_0000, 516 | 40_000_000 => 0x30_0000, 517 | 26_000_000 => 0x49_D39D, 518 | _ => panic!(), 519 | }) 520 | }); 521 | 522 | pds.clkpll_fbdv.modify(|_, w| unsafe { 523 | w.clkpll_sel_fb_clk() 524 | .bits(1) 525 | .clkpll_sel_sample_clk() 526 | .bits(1) 527 | }); 528 | 529 | /*************************/ 530 | /* PLL power up sequence */ 531 | /*************************/ 532 | pds.pu_rst_clkpll 533 | .modify(|_, w| w.pu_clkpll_sfreg().set_bit()); 534 | 535 | delay.try_delay_us(5).unwrap(); 536 | 537 | pds.pu_rst_clkpll.modify(|_, w| w.pu_clkpll().set_bit()); 538 | 539 | pds.pu_rst_clkpll.modify(|_, w| { 540 | w.clkpll_pu_cp() 541 | .set_bit() 542 | .clkpll_pu_pfd() 543 | .set_bit() 544 | .clkpll_pu_fbdv() 545 | .set_bit() 546 | .clkpll_pu_postdiv() 547 | .set_bit() 548 | }); 549 | 550 | delay.try_delay_us(5).unwrap(); 551 | 552 | pds.pu_rst_clkpll 553 | .modify(|_, w| w.clkpll_sdm_reset().set_bit()); 554 | 555 | delay.try_delay_us(1).unwrap(); 556 | 557 | pds.pu_rst_clkpll 558 | .modify(|_, w| w.clkpll_reset_fbdv().set_bit()); 559 | 560 | delay.try_delay_us(2).unwrap(); 561 | 562 | pds.pu_rst_clkpll 563 | .modify(|_, w| w.clkpll_reset_fbdv().clear_bit()); 564 | 565 | delay.try_delay_us(1).unwrap(); 566 | 567 | pds.pu_rst_clkpll 568 | .modify(|_, w| w.clkpll_sdm_reset().clear_bit()); 569 | } 570 | 571 | fn aon_power_on_xtal() -> Result<(), &'static str> { 572 | unsafe { &*pac::AON::ptr() } 573 | .rf_top_aon 574 | .modify(|_, w| w.pu_xtal_aon().set_bit().pu_xtal_buf_aon().set_bit()); 575 | 576 | let mut delaysrc = McycleDelay::new(system_core_clock_get()); 577 | let mut timeout: u32 = 0; 578 | 579 | delaysrc.try_delay_us(10).unwrap(); 580 | 581 | while unsafe { &*pac::AON::ptr() } 582 | .tsen 583 | .read() 584 | .xtal_rdy() 585 | .bit_is_clear() 586 | && timeout < 120 587 | { 588 | delaysrc.try_delay_us(10).unwrap(); 589 | timeout += 1; 590 | } 591 | 592 | if timeout == 120 { 593 | Err("timeout occured") 594 | } else { 595 | Ok(()) 596 | } 597 | } 598 | 599 | fn hbn_set_root_clk_sel_pll() { 600 | unsafe { &*pac::HBN::ptr() }.hbn_glb.modify(|r, w| unsafe { 601 | w.hbn_root_clk_sel() 602 | .bits(r.hbn_root_clk_sel().bits() as u8 | 0b10u8) 603 | }); 604 | } 605 | 606 | fn hbn_set_root_clk_sel_rc32() { 607 | unsafe { &*pac::HBN::ptr() } 608 | .hbn_glb 609 | .modify(|_, w| unsafe { w.hbn_root_clk_sel().bits(0b00u8) }); 610 | } 611 | 612 | fn pds_enable_pll_all_clks() { 613 | unsafe { &*pac::PDS::ptr() } 614 | .clkpll_output_en 615 | .modify(|r, w| unsafe { w.bits(r.bits() | 0x1FF) }); 616 | } 617 | 618 | /// Sets the system clock to use the internal 32Mhz RC oscillator 619 | fn glb_set_system_clk_rc32() { 620 | // reg_bclk_en = reg_hclk_en = reg_fclk_en = 1, cannot be zero 621 | unsafe { &*pac::GLB::ptr() }.clk_cfg0.modify(|_, w| { 622 | w.reg_bclk_en() 623 | .set_bit() 624 | .reg_hclk_en() 625 | .set_bit() 626 | .reg_fclk_en() 627 | .set_bit() 628 | }); 629 | 630 | // Before config XTAL and PLL, make sure root clk is from RC32M 631 | hbn_set_root_clk_sel_rc32(); 632 | 633 | glb_set_system_clk_div(0, 0); 634 | 635 | // Update sysclock 636 | system_core_clock_set(RC32M); 637 | 638 | // Select PKA clock from hclk 639 | unsafe { &*pac::GLB::ptr() } 640 | .swrst_cfg2 641 | .modify(|_, w| w.pka_clk_sel().clear_bit()); 642 | } 643 | 644 | /// Sets the system clock to use the PLL with external crystal 645 | fn glb_set_system_clk_pll(target_core_clk: u32, xtal_freq: u32) { 646 | // Ensure clock is running off internal RC oscillator before changing anything else 647 | glb_set_system_clk_rc32(); 648 | 649 | // Power up the external crystal before we start up the PLL 650 | aon_power_on_xtal().unwrap(); 651 | 652 | // Power up PLL and enable all PLL clock output 653 | pds_power_on_pll_rom(xtal_freq); 654 | 655 | let mut delay = McycleDelay::new(system_core_clock_get()); 656 | delay.try_delay_us(55).unwrap(); 657 | 658 | pds_enable_pll_all_clks(); 659 | 660 | // Enable PLL 661 | unsafe { &*pac::GLB::ptr() } 662 | .clk_cfg0 663 | .modify(|_, w| w.reg_pll_en().set_bit()); 664 | 665 | // select which pll output clock to use before 666 | // selecting root clock via HBN_Set_ROOT_CLK_Sel 667 | // Note that 192Mhz is out of spec 668 | unsafe { &*pac::GLB::ptr() }.clk_cfg0.modify(|_, w| unsafe { 669 | w.reg_pll_sel().bits(match target_core_clk { 670 | 48_000_000 => 0, 671 | 120_000_000 => 1, 672 | 160_000_000 => 2, 673 | 192_000_000 => 3, 674 | _ => { 675 | panic!() 676 | } 677 | }) 678 | }); 679 | 680 | // Keep bclk <= 80MHz 681 | if target_core_clk > 48_000_000 { 682 | glb_set_system_clk_div(0, 1); 683 | } 684 | 685 | // For frequencies above 120Mhz we need 2 clocks to access internal rom 686 | if target_core_clk > 120_000_000 { 687 | unsafe { &*pac::L1C::ptr() } 688 | .l1c_config 689 | .modify(|_, w| w.irom_2t_access().set_bit()); 690 | } 691 | 692 | hbn_set_root_clk_sel_pll(); 693 | system_core_clock_set(target_core_clk); 694 | 695 | let mut delay = McycleDelay::new(system_core_clock_get()); 696 | 697 | // This delay used to be 8 NOPS (1/4 us). (GLB_CLK_SET_DUMMY_WAIT) Might need to be replaced again. 698 | delay.try_delay_us(1).unwrap(); 699 | 700 | // use 120Mhz PLL tap for PKA clock since we're using PLL 701 | // NOTE: This isn't documented in the datasheet! 702 | unsafe { &*pac::GLB::ptr() } 703 | .swrst_cfg2 704 | .modify(|_, w| w.pka_clk_sel().set_bit()); 705 | } 706 | --------------------------------------------------------------------------------