├── .gitignore ├── Cargo.toml ├── LICENSE-MIT ├── LICENSE-MULAN ├── README.md ├── asm.S ├── assemble.ps1 ├── assemble.sh ├── bin └── riscv32imac-unknown-none-elf.a ├── build.rs ├── gd32vf103-hal └── build.rs └── src ├── adc.rs ├── afio.rs ├── backup.rs ├── crc.rs ├── ctimer.rs ├── debug.rs ├── delay.rs ├── esig.rs ├── fmc.rs ├── gpio.rs ├── lib.rs ├── rcu.rs ├── serial.rs ├── spi.rs ├── timer.rs ├── unit.rs └── wdog.rs /.gitignore: -------------------------------------------------------------------------------- 1 | **/target 2 | **/*.rs.bk 3 | 4 | /Cargo.lock 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gd32vf103-hal" 3 | version = "0.0.6" 4 | categories = ["embedded", "hardware-support", "no-std"] 5 | repository = "https://github.com/luojia65/gd32vf103-hal" 6 | description = "Hardware abstract layer (HAL) for RISC-V microcontroller GD32VF103" 7 | documentation = "https://docs.rs/gd32vf103-hal" 8 | authors = ["luojia65 "] 9 | edition = "2018" 10 | license = "MulanPSL-2.0" 11 | readme = "README.md" 12 | keywords = ["riscv", "gd32v", "hal", "embedded-hal"] 13 | build = "build.rs" 14 | 15 | [package.metadata.docs.rs] 16 | targets = ["riscv32imac-unknown-none-elf"] 17 | 18 | [features] 19 | inline-asm = ["riscv/inline-asm"] 20 | 21 | [dependencies] 22 | gd32vf103-pac = "0.4" 23 | embedded-hal = "1.0.0-alpha.1" 24 | nb = "1" # todo: remove when `embedded-hal` updated 25 | riscv = "0.6" 26 | 27 | [lib] 28 | name = "gd32vf103_hal" 29 | test = false 30 | bench = false 31 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright 2019 Luo Jia 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. -------------------------------------------------------------------------------- /LICENSE-MULAN: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Luo Jia 2 | gd32vf103-hal is licensed under the Mulan PSL v1. 3 | You can use this software according to the terms and conditions of the Mulan PSL v1. 4 | You may obtain a copy of Mulan PSL v1 at: 5 | http://license.coscl.org.cn/MulanPSL 6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR 7 | IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR 8 | PURPOSE. 9 | See the Mulan PSL v1 for more details. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `gd32vf103-hal` 2 | 3 | > Hardware abstract layer ([HAL]) for the GD32VF103 RISC-V microcontroller 4 | 5 | [HAL]: https://crates.io/crates/embedded-hal 6 | 7 | [![crates.io](https://img.shields.io/crates/v/gd32vf103-hal.svg)](https://crates.io/crates/gd32vf103-hal) 8 | [![Released API docs](https://docs.rs/gd32vf103-hal/badge.svg)](https://docs.rs/gd32vf103-hal) 9 | 10 | This project is under early stage development; you may find example project and images [here](https://github.com/luojia65/example-gd32vf103). 11 | 12 | Matrix: [#gd32v-rust:matrix.org](https://matrix.to/#/#gd32v-rust:matrix.org) 13 | 14 | See also: [gd32vf103xx-hal](https://github.com/riscv-rust/gd32vf103xx-hal) 15 | 16 | ## Use this project 17 | 18 | To use this HAL project, you may need Rust installed. Checkout `rustup.rs` if you don't have one. 19 | You do not need to install GNU toolchain if you are an application developer. 20 | 21 | Examples may be found at [`gd32vf103-example`](https://github.com/gd32v-rust/gd32vf103-example). 22 | 23 | ## Helps on assembling 24 | 25 | ALERT: this section is only for HAL project itself. If you are an application developer, you do not 26 | need to build this binary by yourself. 27 | 28 | The assemble script requires you have `riscv32imac-unknown-elf-gcc` installed. 29 | 30 | Configure and compile GNU toolchain using: 31 | 32 | ```shell 33 | ../configure --prefix=/opt/riscv32 --with-arch=rv32imac --with-abi=ilp32 34 | ``` 35 | 36 | ```shell 37 | make && make install 38 | ``` 39 | 40 | Run assemble script: (run on any path is okay) 41 | 42 | ```shell 43 | ./assemble.sh 44 | ``` 45 | 46 | ## License 47 | 48 | This project is licensed under either of 49 | 50 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT)) 51 | - Mulan PSL v1 ([LICENSE-MULAN](LICENSE-MULAN) or [http://license.coscl.org.cn/MulanPSL](http://license.coscl.org.cn/MulanPSL)) 52 | -------------------------------------------------------------------------------- /asm.S: -------------------------------------------------------------------------------- 1 | /* author: Luo Jia 2019-11-29 */ 2 | .section .vectors, "ax" 3 | .globl _gd32vf103_vectors 4 | _gd32vf103_vectors: 5 | /* source: Firmware\RISCV\env_Eclipse\start.S */ 6 | .weak eclic_msip_handler 7 | .weak eclic_mtip_handler 8 | .weak eclic_bwei_handler 9 | .weak eclic_pmovi_handler 10 | .weak WWDGT_IRQHandler 11 | .weak LVD_IRQHandler 12 | .weak TAMPER_IRQHandler 13 | .weak RTC_IRQHandler 14 | .weak FMC_IRQHandler 15 | .weak RCU_IRQHandler 16 | .weak EXTI0_IRQHandler 17 | .weak EXTI1_IRQHandler 18 | .weak EXTI2_IRQHandler 19 | .weak EXTI3_IRQHandler 20 | .weak EXTI4_IRQHandler 21 | .weak DMA0_Channel0_IRQHandler 22 | .weak DMA0_Channel1_IRQHandler 23 | .weak DMA0_Channel2_IRQHandler 24 | .weak DMA0_Channel3_IRQHandler 25 | .weak DMA0_Channel4_IRQHandler 26 | .weak DMA0_Channel5_IRQHandler 27 | .weak DMA0_Channel6_IRQHandler 28 | .weak ADC0_1_IRQHandler 29 | .weak CAN0_TX_IRQHandler 30 | .weak CAN0_RX0_IRQHandler 31 | .weak CAN0_RX1_IRQHandler 32 | .weak CAN0_EWMC_IRQHandler 33 | .weak EXTI5_9_IRQHandler 34 | .weak TIMER0_BRK_IRQHandler 35 | .weak TIMER0_UP_IRQHandler 36 | .weak TIMER0_TRG_CMT_IRQHandler 37 | .weak TIMER0_Channel_IRQHandler 38 | .weak TIMER1_IRQHandler 39 | .weak TIMER2_IRQHandler 40 | .weak TIMER3_IRQHandler 41 | .weak I2C0_EV_IRQHandler 42 | .weak I2C0_ER_IRQHandler 43 | .weak I2C1_EV_IRQHandler 44 | .weak I2C1_ER_IRQHandler 45 | .weak SPI0_IRQHandler 46 | .weak SPI1_IRQHandler 47 | .weak USART0_IRQHandler 48 | .weak USART1_IRQHandler 49 | .weak USART2_IRQHandler 50 | .weak EXTI10_15_IRQHandler 51 | .weak RTC_Alarm_IRQHandler 52 | .weak USBFS_WKUP_IRQHandler 53 | .weak EXMC_IRQHandler 54 | .weak TIMER4_IRQHandler 55 | .weak SPI2_IRQHandler 56 | .weak UART3_IRQHandler 57 | .weak UART4_IRQHandler 58 | .weak TIMER5_IRQHandler 59 | .weak TIMER6_IRQHandler 60 | .weak DMA1_Channel0_IRQHandler 61 | .weak DMA1_Channel1_IRQHandler 62 | .weak DMA1_Channel2_IRQHandler 63 | .weak DMA1_Channel3_IRQHandler 64 | .weak DMA1_Channel4_IRQHandler 65 | .weak CAN1_TX_IRQHandler 66 | .weak CAN1_RX0_IRQHandler 67 | .weak CAN1_RX1_IRQHandler 68 | .weak CAN1_EWMC_IRQHandler 69 | .weak USBFS_IRQHandler 70 | jal x0, _start_logical_addr 71 | .word 0 72 | .word 0 73 | .word eclic_msip_handler 74 | .word 0 75 | .word 0 76 | .word 0 77 | .word eclic_mtip_handler 78 | .word 0 79 | .word 0 80 | .word 0 81 | .word 0 82 | .word 0 83 | .word 0 84 | .word 0 85 | .word 0 86 | .word 0 87 | .word eclic_bwei_handler 88 | .word eclic_pmovi_handler 89 | .word WWDGT_IRQHandler 90 | .word LVD_IRQHandler 91 | .word TAMPER_IRQHandler 92 | .word RTC_IRQHandler 93 | .word FMC_IRQHandler 94 | .word RCU_IRQHandler 95 | .word EXTI0_IRQHandler 96 | .word EXTI1_IRQHandler 97 | .word EXTI2_IRQHandler 98 | .word EXTI3_IRQHandler 99 | .word EXTI4_IRQHandler 100 | .word DMA0_Channel0_IRQHandler 101 | .word DMA0_Channel1_IRQHandler 102 | .word DMA0_Channel2_IRQHandler 103 | .word DMA0_Channel3_IRQHandler 104 | .word DMA0_Channel4_IRQHandler 105 | .word DMA0_Channel5_IRQHandler 106 | .word DMA0_Channel6_IRQHandler 107 | .word ADC0_1_IRQHandler 108 | .word CAN0_TX_IRQHandler 109 | .word CAN0_RX0_IRQHandler 110 | .word CAN0_RX1_IRQHandler 111 | .word CAN0_EWMC_IRQHandler 112 | .word EXTI5_9_IRQHandler 113 | .word TIMER0_BRK_IRQHandler 114 | .word TIMER0_UP_IRQHandler 115 | .word TIMER0_TRG_CMT_IRQHandler 116 | .word TIMER0_Channel_IRQHandler 117 | .word TIMER1_IRQHandler 118 | .word TIMER2_IRQHandler 119 | .word TIMER3_IRQHandler 120 | .word I2C0_EV_IRQHandler 121 | .word I2C0_ER_IRQHandler 122 | .word I2C1_EV_IRQHandler 123 | .word I2C1_ER_IRQHandler 124 | .word SPI0_IRQHandler 125 | .word SPI1_IRQHandler 126 | .word USART0_IRQHandler 127 | .word USART1_IRQHandler 128 | .word USART2_IRQHandler 129 | .word EXTI10_15_IRQHandler 130 | .word RTC_Alarm_IRQHandler 131 | .word USBFS_WKUP_IRQHandler 132 | .word 0 133 | .word 0 134 | .word 0 135 | .word 0 136 | .word 0 137 | .word EXMC_IRQHandler 138 | .word 0 139 | .word TIMER4_IRQHandler 140 | .word SPI2_IRQHandler 141 | .word UART3_IRQHandler 142 | .word UART4_IRQHandler 143 | .word TIMER5_IRQHandler 144 | .word TIMER6_IRQHandler 145 | .word DMA1_Channel0_IRQHandler 146 | .word DMA1_Channel1_IRQHandler 147 | .word DMA1_Channel2_IRQHandler 148 | .word DMA1_Channel3_IRQHandler 149 | .word DMA1_Channel4_IRQHandler 150 | .word 0 151 | .word 0 152 | .word CAN1_TX_IRQHandler 153 | .word CAN1_RX0_IRQHandler 154 | .word CAN1_RX1_IRQHandler 155 | .word CAN1_EWMC_IRQHandler 156 | .word USBFS_IRQHandler 157 | 158 | # RISC-V defined CSR registers 159 | .equ mstatus, 0x300 160 | .equ mie, 0x304 161 | .equ mtvec, 0x305 162 | # Bumblebee core defined CSR registers 163 | .equ mtvt, 0x307 164 | .equ msubm, 0x7c4 165 | .equ mtvt2, 0x7ec 166 | .equ jalmnxti, 0x7ed 167 | .equ pushmcause, 0x7ee 168 | .equ pushmepc, 0x7ef 169 | .equ pushmsubm, 0x7eb 170 | # Constants 171 | .equ REGBYTES,4 172 | .equ MSTATUS_MIE, 0x00000008 173 | 174 | .section .init, "ax" 175 | .globl _start_logical_addr 176 | 177 | _start_logical_addr: 178 | la a0, _start_logical_addr 179 | li a1, 1 180 | slli a1, a1, 29 181 | bleu a1, a0, _start_bootstrap 182 | srli a1, a1, 2 183 | bleu a1, a0, _start_bootstrap 184 | la a0, _start_bootstrap 185 | add a0, a0, a1 186 | jr a0 187 | 188 | _start_bootstrap: 189 | # Initialize mtvt 190 | la t0, _gd32vf103_vectors 191 | csrw mtvt, t0 192 | # Initialize and enable mtvt2 193 | la t0, _gd32vf103_irq_entry 194 | csrw mtvt2, t0 195 | csrs mtvt2, 0x1 196 | # Initialize mtvec for trap & NMI base addr 197 | la t0, _gd32vf103_trap_entry 198 | csrw mtvec, t0 # todo: conflict with riscv-rt? 199 | # Start riscv-rt Rust entry which would check hart id, 200 | # allocate stack, clear bss section 201 | j _start 202 | 203 | .macro SAVE_CONTEXT 204 | sw ra, 0*REGBYTES(sp) 205 | sw tp, 1*REGBYTES(sp) 206 | sw t0, 2*REGBYTES(sp) 207 | sw t1, 3*REGBYTES(sp) 208 | sw t2, 4*REGBYTES(sp) 209 | sw t3, 5*REGBYTES(sp) 210 | sw t4, 6*REGBYTES(sp) 211 | sw t5, 7*REGBYTES(sp) 212 | sw t6, 8*REGBYTES(sp) 213 | sw a0, 9*REGBYTES(sp) 214 | sw a1, 10*REGBYTES(sp) 215 | sw a2, 11*REGBYTES(sp) 216 | sw a3, 12*REGBYTES(sp) 217 | sw a4, 13*REGBYTES(sp) 218 | sw a5, 14*REGBYTES(sp) 219 | sw a6, 15*REGBYTES(sp) 220 | sw a7, 16*REGBYTES(sp) 221 | .endm 222 | 223 | .macro RESTORE_CONTEXT 224 | lw ra, 0*REGBYTES(sp) 225 | lw tp, 1*REGBYTES(sp) 226 | lw t0, 2*REGBYTES(sp) 227 | lw t1, 3*REGBYTES(sp) 228 | lw t2, 4*REGBYTES(sp) 229 | lw t3, 5*REGBYTES(sp) 230 | lw t4, 6*REGBYTES(sp) 231 | lw t5, 7*REGBYTES(sp) 232 | lw t6, 8*REGBYTES(sp) 233 | lw a0, 9*REGBYTES(sp) 234 | lw a1, 10*REGBYTES(sp) 235 | lw a2, 11*REGBYTES(sp) 236 | lw a3, 12*REGBYTES(sp) 237 | lw a4, 13*REGBYTES(sp) 238 | lw a5, 14*REGBYTES(sp) 239 | lw a6, 15*REGBYTES(sp) 240 | lw a7, 16*REGBYTES(sp) 241 | .endm 242 | 243 | .section .trap, "ax" 244 | .p2align 6 # 4-byte aligned 245 | .globl _gd32vf103_trap_entry 246 | .weak _gd32vf103_trap_entry 247 | _gd32vf103_trap_entry: 248 | ; .globl _start_trap 249 | ; _start_trap: # todo: verify 250 | # Allocate stack space 251 | addi sp, sp, -19*REGBYTES 252 | # Save ra, tp, t*, a* registers 253 | SAVE_CONTEXT # save 17 registers 254 | # Store mcause, mepc and msubm into memory 255 | csrrwi zero, pushmcause, 17 256 | csrrwi zero, pushmepc, 18 257 | csrrwi zero, pushmsubm, 19 258 | # Set the function argument of trap handler 259 | mv a0, sp 260 | # Calls the trap handler (defined in riscv-rt) 261 | # extern "C" fn start_trap_rust(trap_frame: *const TrapFrame) 262 | jal ra, _start_trap_rust 263 | # Restore mcause, mepc and msubm 264 | lw a0, 19*REGBYTES(sp) 265 | csrw msubm, a0 266 | lw a0, 18*REGBYTES(sp) 267 | csrw mepc, a0 268 | lw a0, 17*REGBYTES(sp) 269 | csrw mcause, a0 270 | # Restore ra, tp, t*, a* registers 271 | RESTORE_CONTEXT # restore 17 registers 272 | # Free stack space 273 | addi sp, sp, 20*REGBYTES 274 | mret 275 | 276 | .section .trap, "ax" 277 | .align 2 278 | .globl _gd32vf103_irq_entry 279 | .weak _gd32vf103_irq_entry 280 | _gd32vf103_irq_entry: 281 | # Adjust stack pointer to allocate stack space 282 | addi sp, sp, -20*REGBYTES 283 | # Save ra, tp, t*, a* registers 284 | SAVE_CONTEXT # save 17 registers 285 | # These are special CSR read operations 286 | # which actually uses mcause, mepc and msubm as operand 287 | # to directly store it to memory (stack) 288 | csrrwi zero, pushmcause, 17 289 | csrrwi zero, pushmepc, 18 290 | csrrwi zero, pushmsubm, 19 291 | # This special CSR read/write operation 292 | # It claims the CLIC to find its pending highest ID. 293 | # If the ID is not 0, then _automatically enable 294 | # the mstatus.MIE_, and jump to its vector-entry-label, 295 | # and update the link register. 296 | # This design targets to speed up biting tail interrupts. 297 | csrrw ra, jalmnxti, ra 298 | # Disable interrupts 299 | csrc mstatus, MSTATUS_MIE 300 | # Restore mcause, mepc and msubm 301 | lw a0, 19*REGBYTES(sp) 302 | csrw msubm, a0 303 | lw a0, 18*REGBYTES(sp) 304 | csrw mepc, a0 305 | lw a0, 17*REGBYTES(sp) 306 | csrw mcause, a0 307 | # Restore ra, tp, t*, a* registers 308 | RESTORE_CONTEXT # restore 17 registers 309 | # Free stack space 310 | addi sp, sp, 20*REGBYTES 311 | mret 312 | -------------------------------------------------------------------------------- /assemble.ps1: -------------------------------------------------------------------------------- 1 | $source = "$PSScriptRoot/asm.S" 2 | $o_file = "$PSScriptRoot/bin/riscv32imac-unknown-none-elf.o" 3 | $a_file = "$PSScriptRoot/bin/riscv32imac-unknown-none-elf.a" 4 | 5 | riscv64-unknown-elf-gcc $source -o $o_file -march=rv32imac -mabi=ilp32 -mno-relax -Wa,-mno-relax -c -g 6 | riscv64-unknown-elf-ar rcs $a_file $o_file 7 | Remove-Item $o_file 8 | -------------------------------------------------------------------------------- /assemble.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cur_path=$(cd `dirname $0`; pwd) 4 | o_file=$cur_path/bin/riscv32imac-unknown-none-elf.o 5 | a_file=$cur_path/bin/riscv32imac-unknown-none-elf.a 6 | src_file=$cur_path/asm.S 7 | compiler="" 8 | 9 | which riscv64-unknown-elf-gcc >/dev/null 2>&1 10 | if [ "$?" == 0 ]; then 11 | compiler=riscv64-unknown-elf-gcc 12 | fi 13 | which riscv32-unknown-elf-gcc >/dev/null 2>&1 14 | if [ "$?" == 0 ]; then 15 | compiler=riscv32-unknown-elf-gcc 16 | fi 17 | 18 | if [ "$compiler" == "" ]; then 19 | echo "error: Cannot detect any assembly compiler! 20 | You may install riscv32-unknown-elf-gcc with: ./configure --prefix=/opt/riscv32 --with-arch=rv32imac --with-abi=ilp32" 21 | exit 22 | fi 23 | 24 | $compiler $src_file -o $o_file -march=rv32imac -mabi=ilp32 -mno-relax -Wa,-mno-relax -c -g 25 | if [ "$?" != 0 ]; then 26 | exit 27 | fi 28 | ar rcs $a_file $o_file 29 | if [ "$?" != 0 ]; then 30 | exit 31 | fi 32 | rm $o_file 33 | 34 | echo "ASSEMBLY SUCCESS" 35 | -------------------------------------------------------------------------------- /bin/riscv32imac-unknown-none-elf.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gd32v-rust/gd32vf103-hal/f985b549f1f2ef550128bb77a0d5d781d39a1ab9/bin/riscv32imac-unknown-none-elf.a -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | // Ref: Sha Miao, rv32m1_ri5cy-example/build.rs 2 | 3 | use std::env; 4 | use std::fs; 5 | use std::path::PathBuf; 6 | 7 | const MISSING_CARGO_ENV: &str = "Missing environment variables provided by Cargo."; 8 | 9 | /// Include `.a` files generated by assembly codes 10 | fn include_a_files(out_dir: &str) { 11 | let target = env::var("TARGET").expect(MISSING_CARGO_ENV); 12 | let out_dir = PathBuf::from(out_dir); 13 | let name = env::var("CARGO_PKG_NAME").expect(MISSING_CARGO_ENV); 14 | 15 | if &target == "riscv32imac-unknown-none-elf" { 16 | fs::copy( 17 | format!("bin/{}.a", target), 18 | out_dir.join(format!("lib{}.a", name)), 19 | ) 20 | .unwrap(); 21 | 22 | println!("cargo:rustc-link-lib=static={}", name); 23 | println!("cargo:rustc-link-search={}", out_dir.display()); 24 | println!("cargo:rerun-if-changed=bin/{}.a", target); 25 | } 26 | } 27 | 28 | /// Build script for the crate. 29 | fn main() { 30 | let out_dir = env::var("OUT_DIR").expect(MISSING_CARGO_ENV); 31 | include_a_files(&out_dir); 32 | println!("cargo:rerun-if-changed=build.rs"); 33 | } 34 | -------------------------------------------------------------------------------- /gd32vf103-hal/build.rs: -------------------------------------------------------------------------------- 1 | // Ref: Sha Miao, rv32m1_ri5cy-example/build.rs 2 | 3 | use std::env; 4 | use std::fs; 5 | use std::path::PathBuf; 6 | 7 | const MISSING_CARGO_ENV: &str = "Missing environment variables provided by Cargo."; 8 | 9 | /// Include `.a` files generated by assembly codes 10 | fn include_a_files(out_dir: &str) { 11 | let target = env::var("TARGET").expect(MISSING_CARGO_ENV); 12 | let out_dir = PathBuf::from(out_dir); 13 | let name = env::var("CARGO_PKG_NAME").expect(MISSING_CARGO_ENV); 14 | 15 | if &target == "riscv32imac-unknown-none-elf" { 16 | fs::copy( 17 | format!("bin/{}.a", target), 18 | out_dir.join(format!("lib{}.a", name)), 19 | ) 20 | .unwrap(); 21 | 22 | println!("cargo:rustc-link-lib=static={}", name); 23 | println!("cargo:rustc-link-search={}", out_dir.display()); 24 | println!("cargo:rerun-if-changed=bin/{}.a", target); 25 | } 26 | } 27 | 28 | fn linker_script() { 29 | println!("cargo:rustc-link-arg-bins=-Tmemory.x"); 30 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); 31 | } 32 | 33 | /// Build script for the crate. 34 | fn main() { 35 | let out_dir = env::var("OUT_DIR").expect(MISSING_CARGO_ENV); 36 | include_a_files(&out_dir); 37 | linker_script(); 38 | println!("cargo:rerun-if-changed=build.rs"); 39 | } 40 | -------------------------------------------------------------------------------- /src/adc.rs: -------------------------------------------------------------------------------- 1 | //! (TODO) Analog-to-Digital Converter 2 | -------------------------------------------------------------------------------- /src/afio.rs: -------------------------------------------------------------------------------- 1 | //! (TODO) Alternate Function I/O 2 | 3 | use crate::pac::{afio, AFIO}; 4 | use crate::rcu::APB2; 5 | 6 | pub trait AfioExt { 7 | fn split(self, apb2: &mut APB2) -> Parts; 8 | } 9 | 10 | impl AfioExt for AFIO { 11 | fn split(self, apb2: &mut APB2) -> Parts { 12 | riscv::interrupt::free(|_| { 13 | apb2.en().modify(|_, w| w.afen().set_bit()); 14 | apb2.rst().modify(|_, w| w.afrst().set_bit()); 15 | apb2.rst().modify(|_, w| w.afrst().clear_bit()); 16 | }); 17 | Parts { 18 | ec: EC { _ownership: () }, 19 | pcf0: PCF0 { _ownership: () }, 20 | pcf1: PCF1 { _ownership: () }, 21 | _todo: (), 22 | } 23 | } 24 | } 25 | 26 | pub struct Parts { 27 | pub ec: EC, // pub ec: EventOutCtrl 28 | // pub extiss: ExtiSelect 29 | pub pcf0: PCF0, 30 | pub pcf1: PCF1, 31 | _todo: (), 32 | } 33 | 34 | pub struct EC { 35 | _ownership: (), 36 | } 37 | 38 | // todo: impl EC 39 | 40 | /// Opaque PCF0 register 41 | pub struct PCF0 { 42 | _ownership: (), 43 | } 44 | 45 | impl PCF0 { 46 | #[inline] 47 | pub(crate) fn pcf0(&mut self) -> &afio::PCF0 { 48 | unsafe { &(*AFIO::ptr()).pcf0 } 49 | } 50 | } 51 | 52 | /// Opaque PCF1 register 53 | pub struct PCF1 { 54 | _ownership: (), 55 | } 56 | -------------------------------------------------------------------------------- /src/backup.rs: -------------------------------------------------------------------------------- 1 | //! (TODO) Backup register domain 2 | 3 | use crate::pac::{bkp, BKP, PMU}; 4 | use crate::rcu::APB1; 5 | use core::marker::PhantomData; 6 | 7 | // todo: constrain an alternate PC13 pin? 8 | 9 | /// Extension trait that constrains the `BKP` peripheral 10 | pub trait BkpExt { 11 | /// Split the `BKP` peripheral into stand alone backup domain modules 12 | fn split(self, apb1: &mut APB1, pmu: &mut PMU) -> Parts; 13 | } 14 | 15 | impl BkpExt for BKP { 16 | fn split(self, apb1: &mut APB1, pmu: &mut PMU) -> Parts { 17 | // After chip reset, all write operation to backup domain (e.g. 18 | // registers and RTC) are forbidden. To enable write access to all 19 | // backup domain, first enable APB1EN's PMUEN for PMU clock, BKPIEN 20 | // for BKP clock; then enable PMU_CTL's BKPWEN bit for write access 21 | // to registers and RTC. 22 | riscv::interrupt::free(|_| { 23 | // 1. use apb1 to enable backup domain clock 24 | apb1.en() 25 | .modify(|_, w| w.pmuen().set_bit().bkpien().set_bit()); 26 | // 2. use pmuctl to enbale write access 27 | // todo: should PMU be designed as a separate module? 28 | pmu.ctl.write(|w| w.bkpwen().set_bit()); 29 | }); 30 | Parts { 31 | data: Data { 32 | _owned_incontinuous_storage: PhantomData, 33 | }, 34 | tamper: Tamper { _ownership: () }, 35 | octl: OCTL { _ownership: () }, 36 | } 37 | } 38 | } 39 | 40 | /// `BKP` Parts 41 | pub struct Parts { 42 | /// Backup data register 43 | /// 44 | /// Constrains all `BKP_DATAx` (x in 0..=41). 45 | pub data: Data, 46 | /// Tamper event monitor 47 | /// 48 | /// Constrains `BKP_TPCTL` and `BKP_TPCS`. 49 | pub tamper: Tamper, 50 | /// RTC signal output control register 51 | /// 52 | /// Constrains `BKP_OCTL`. 53 | pub octl: OCTL, 54 | } 55 | 56 | // verified on GD32VF103C-START board; 2020-03-16 57 | /// Backup data register 58 | /// 59 | /// Constrains all `BKP_DATAx` registers, totally 42 * `u16` _incontinuous_ 60 | /// storages which adds up to 84 bytes. These storages may be used to save 61 | /// user defined application data, and will not be reset after wake from 62 | /// standby mode or power reset. 63 | /// 64 | /// This struct is considered as an owned incontinuous storage, thus could be 65 | /// shared with and sent between different contexts. 66 | /// 67 | /// Ref: Section 4.1 & 4.4.1, the User Manual 68 | pub struct Data { 69 | _owned_incontinuous_storage: PhantomData<[u16; 42]>, 70 | } 71 | 72 | impl Data { 73 | /// Read a 16-bit value from `BKP_DATA` backup data register. 74 | /// Parameter `idx` must be a valid register index (in `[0, 41]`) 75 | /// for there are 42 registers in total; otherwise this function panics. 76 | #[inline] 77 | pub fn read(&self, idx: usize) -> u16 { 78 | unsafe { *Self::get_ptr(idx) } 79 | } 80 | 81 | /// Write a 16-bit value into `BKP_DATA` backup data register. 82 | /// Parameter `idx` must be a valid register index (in `[0, 41]`) 83 | /// for there are 42 registers in total; otherwise this function panics. 84 | #[inline] 85 | pub fn write(&mut self, idx: usize, data: u16) { 86 | unsafe { *Self::get_ptr(idx) = data } 87 | } 88 | 89 | // address verified 90 | #[inline] 91 | fn get_ptr(idx: usize) -> *mut u16 { 92 | if idx <= 9 { 93 | // data0 ..= data9 94 | unsafe { (BKP::ptr() as *mut u8).add(idx * 0x04 + 0x04) as *mut u16 } 95 | } else if idx <= 41 { 96 | // data10 ..= data41 97 | unsafe { (BKP::ptr() as *mut u8).add((idx - 10) * 0x04 + 0x40) as *mut u16 } 98 | } else { 99 | panic!("invalid index") 100 | } 101 | } 102 | } 103 | 104 | /// Tamper event monitor 105 | /// 106 | /// todo: detailed doc & module verify 107 | pub struct Tamper { 108 | _ownership: (), 109 | } 110 | 111 | impl Tamper { 112 | /// Enable temper detection. 113 | /// 114 | /// After enabled the TAMPER pin is dedicated for Backup Reset function. 115 | /// The active level on the TAMPER pin resets all data of the BKP_DATAx 116 | /// registers. 117 | /// 118 | /// Ref: Section 4.4.3, the User Manual 119 | pub fn enable(&mut self) { 120 | unsafe { &*BKP::ptr() } 121 | .tpctl 122 | .modify(|_, w| w.tpen().set_bit()); 123 | } 124 | 125 | /// Disable temper detection. 126 | /// 127 | /// After disabled, the TAMPER pin is free for GPIO functions. 128 | pub fn disable(&mut self) { 129 | unsafe { &*BKP::ptr() } 130 | .tpctl 131 | .modify(|_, w| w.tpen().clear_bit()); 132 | } 133 | 134 | /// Set the TAMPER pin to active high. The TAMPER pin defaults to active 135 | /// high after reset. 136 | /// 137 | /// Ref: Section 4.4.3, the User Manual 138 | pub fn set_pin_active_high(&mut self) { 139 | unsafe { &*BKP::ptr() } 140 | .tpctl 141 | .modify(|_, w| w.tpal().clear_bit()); 142 | } 143 | 144 | /// Set the TAMPER pin to active low. The TAMPER pin defaults to active 145 | /// high after reset. 146 | /// 147 | /// Ref: Section 4.4.3, the User Manual 148 | pub fn set_pin_active_low(&mut self) { 149 | unsafe { &*BKP::ptr() } 150 | .tpctl 151 | .modify(|_, w| w.tpal().set_bit()); 152 | } 153 | 154 | /// Check the tamper event flag by reading from `TEF` register bit. 155 | pub fn check_event(&self) -> bool { 156 | unsafe { &*BKP::ptr() }.tpcs.read().tef().bit() 157 | } 158 | 159 | /// Clear the tamper interrupt flag bit by writing 1 to `TER` register bit. 160 | pub fn clear_event_bit(&mut self) { 161 | unsafe { &*BKP::ptr() } 162 | .tpcs 163 | .modify(|_, w| w.ter().set_bit()); 164 | } 165 | 166 | /// Enable the tamper interrupt by setting the _Tamper interrupt enable 167 | /// (TPIE)_ register bit. 168 | pub fn enable_interrupt(&mut self) { 169 | unsafe { &*BKP::ptr() } 170 | .tpcs 171 | .modify(|_, w| w.tpie().set_bit()); 172 | } 173 | 174 | /// Disable the tamper interrupt by clearing the _Tamper interrupt enable 175 | /// (TPIE)_ register bit. 176 | pub fn disable_interrupt(&mut self) { 177 | unsafe { &*BKP::ptr() } 178 | .tpcs 179 | .modify(|_, w| w.tpie().clear_bit()); 180 | } 181 | 182 | /// Check the tamper interrupt flag by reading from `TIF` register bit. 183 | pub fn check_interrupt(&self) -> bool { 184 | unsafe { &*BKP::ptr() }.tpcs.read().tif().bit() 185 | } 186 | 187 | /// Clear the tamper interrupt flag bit by writing 1 to `TIR` register bit. 188 | pub fn clear_interrupt_bit(&mut self) { 189 | unsafe { &*BKP::ptr() } 190 | .tpcs 191 | .modify(|_, w| w.tir().set_bit()); 192 | } 193 | } 194 | 195 | /// RTC signal output control register (BKP_OCTL) 196 | pub struct OCTL { 197 | _ownership: (), 198 | } 199 | 200 | impl OCTL { 201 | // todo: use this register 202 | // pub(crate) fn octl(&mut self) -> &bkp::OCTL { 203 | // unsafe { &(*BKP::ptr()).octl } 204 | // } 205 | } 206 | -------------------------------------------------------------------------------- /src/crc.rs: -------------------------------------------------------------------------------- 1 | //! CRC calculation unit 2 | //! 3 | //! The hardware cyclic redundancy check (CRC) unit on GD32VF103 has 32-bit data 4 | //! input and 32-bit data output. Calculation period is 4 AHB clock cycles 5 | //! for 32-bit input data size, from data entered to the calculation result 6 | //! available. 7 | //! 8 | //! This unit uses fixed polynomial 0x4C11DB7, which is a common polynomial 9 | //! used in Ethernet. 10 | //! 11 | //! Ref: Section 8, the User Manual; Firmware/Source/gd32vf103_crc.c 12 | //! 13 | //! # Usage 14 | //! 15 | //! ## CRC calculation 16 | //! 17 | //! To use this module, create a [`Crc`] wrapper using [`Crc::crc`] function. This 18 | //! function requires peripheral CRC and ownership of AHB peripheral bus; it turns 19 | //! on the CRC clock and creates an owned `Crc` wrapper. 20 | //! After the wrapper is created, you need to create a [`Digest`] struct. 21 | //! The function [`crc.new_digest()`] will clear the underlying CRC buffer and return 22 | //! an owned Digest struct to get ready for calculation. 23 | //! With the Digest struct, you may keep writing `u32` values with function 24 | //! [`digest.write_u32(value)`]. To read the digest value out, use [`digest.finish()`]. 25 | //! Further values can still be written after the digest value is read out. 26 | //! After all the processes, you may stop writing to digests and get the `Crc` wrapper 27 | //! back with function [`digest.free()`]. 28 | //! To release and turn off the clock to get CRC peripheral back, use [`crc.release()`]. 29 | //! 30 | //! [`Crc`]: struct.Crc.html 31 | //! [`Crc::crc`]: struct.Crc.html#method.crc 32 | //! [`crc.new_digest()`]: struct.Crc.html#method.new_digest 33 | //! [`Digest`]: struct.Digest.html 34 | //! [`digest.write_u32(value)`]: struct.Digest.html#method.write_u32 35 | //! [`digest.finish()`]: struct.Digest.html#method.finish 36 | //! [`digest.free()`]: struct.Digest.html#method.free 37 | //! [`crc.release()`]: struct.Crc.html#method.release 38 | //! 39 | //! ## Free data register `fdata` 40 | //! 41 | //! The `fdata` register provides you with additional 8-bit global storage. You may read 42 | //! from or write to this register using function [`fdata_read`] and [`fdata_write`]. 43 | //! 44 | //! [`fdata_read`]: fn.fdata_read.html 45 | //! [`fdata_write`]: fn.fdata_write.html 46 | //! 47 | //! # Example 48 | //! 49 | //! This example is tested on GD32VF103C-START board. It calculated the CRC value of 50 | //! `0xABCD1234`. The desired result is `0xF7018A40`; if the underlying hardware had 51 | //! calculated correctly, PA7 is set high to turn on the LED with anode connected to 52 | //! the MCU. 53 | //! 54 | //! ``` 55 | //! #![no_std] 56 | //! #![no_main] 57 | //! 58 | //! use gd32vf103_hal as hal; 59 | //! use hal::{crc::Crc, pac, prelude::*}; 60 | //! use panic_halt as _; 61 | //! 62 | //! #[riscv_rt::entry] 63 | //! fn main() -> ! { 64 | //! let dp = pac::Peripherals::take().unwrap(); 65 | //! let mut rcu = dp.RCU.constrain(); 66 | //! let mut gpioa = dp.GPIOA.split(&mut rcu.apb2); 67 | //! let mut pa7 = gpioa.pa7.into_push_pull_output(&mut gpioa.ctl0); 68 | //! 69 | //! let src: u32 = 0xABCD1234; 70 | //! let crc = Crc::crc(dp.CRC, &mut rcu.ahb); 71 | //! let mut digest = crc.new_digest(); 72 | //! digest.write_u32(src); 73 | //! 74 | //! if digest.finish() == 0xF7018A40 { 75 | //! pa7.set_high().unwrap(); 76 | //! } 77 | //! 78 | //! loop {} 79 | //! } 80 | //! ``` 81 | 82 | use crate::pac::CRC; 83 | use crate::rcu::AHB; 84 | 85 | /// Read the value of the free data register `fdata`. 86 | #[inline] 87 | pub fn fdata_read() -> u8 { 88 | // note(unsafe): separate ownership, volatile read 89 | unsafe { &*CRC::ptr() }.fdata.read().fdata().bits() 90 | } 91 | 92 | /// Write data to the free data register `fdata`. 93 | #[inline] 94 | pub fn fdata_write(byte: u8) { 95 | // note(unsafe): separate ownership, volatile write 96 | // for high 24 bits we may keep reset value 97 | unsafe { &*CRC::ptr() } 98 | .fdata 99 | .modify(|_, w| unsafe { w.fdata().bits(byte) }); 100 | } 101 | 102 | /// CRC module abstraction 103 | /// 104 | /// Owns `CRC_DATA` and `CRC_CTL`. 105 | pub struct Crc { 106 | crc: CRC, 107 | } 108 | 109 | impl Crc { 110 | /// Take ownership of CRC and enable CRC clock. 111 | /// 112 | /// To create struct `Crc`, it's need to pass the peripheral `CRC` and a mutable 113 | /// reference of `AHB` peripheral bus. Get the ownership of `CRC` from the device 114 | /// peripheral struct `pac::Peripherals` (may already exists in your code) as `dp` 115 | /// then use `dp.CRC`. To get AHB you need to constrain `RCU` module using 116 | /// `dp.RCU.constrain()`, then use `&mut rcu.ahb` to get its mutable reference. 117 | /// 118 | /// # Examples 119 | /// 120 | /// Basic usage: 121 | /// 122 | /// ```no_run 123 | /// // Prepare CRC peripheral for calculation 124 | /// let crc = Crc::crc(dp.CRC, &mut rcu.ahb); 125 | /// ``` 126 | #[inline] 127 | pub fn crc(crc: CRC, ahb: &mut AHB) -> Self { 128 | ahb.en().modify(|_, w| w.crcen().set_bit()); 129 | Crc { crc } 130 | } 131 | 132 | /// Create new Digest struct for CRC calculation. 133 | /// 134 | /// The underlying CRC buffer is cleaned to prepare for incoming values. Write 135 | /// operations could be later performed using functions in `Digest`. You may 136 | /// refer to [`digest.write_u32(value)`] for how to write the value for CRC 137 | /// calculation. 138 | /// 139 | /// [`digest.write_u32(value)`]: struct.Digest.html#method.write_u32 140 | /// 141 | /// # Examples 142 | /// 143 | /// Basic usage: 144 | /// 145 | /// ```no_run 146 | /// // Prepare CRC peripheral for calculation 147 | /// let crc = Crc::crc(dp.CRC, &mut rcu.ahb); 148 | /// // Create a Digest instance to write values for calculation 149 | /// let mut digest = crc.new_digest(); 150 | /// ``` 151 | #[inline] 152 | pub fn new_digest(self) -> Digest { 153 | self.crc.ctl.modify(|_, w| w.rst().set_bit()); 154 | // after initialization finished, hardware would set `rst` bit to `false`. 155 | while self.crc.ctl.read().rst() == true {} 156 | Digest { crc: self.crc } 157 | } 158 | 159 | /// Disable the CRC clock and release the peripheral. 160 | /// 161 | /// The clock is switched off using AHB; you must provide a mutable referrence 162 | /// of AHB to release the CRC peripheral. After release, the peripheral is freed 163 | /// for further use. 164 | /// 165 | /// # Examples 166 | /// 167 | /// Basic usage: 168 | /// 169 | /// ```no_run 170 | /// // Prepare CRC peripheral for calculation 171 | /// let crc = Crc::crc(dp.CRC, &mut rcu.ahb); 172 | /// // ... actual calculations with `crc` 173 | /// // Release the wrapper and get CRC peripheral back 174 | /// let crc = crc.release(&mut rcu.ahb); 175 | /// ``` 176 | #[inline] 177 | pub fn release(self, ahb: &mut AHB) -> CRC { 178 | ahb.en().modify(|_, w| w.crcen().clear_bit()); 179 | self.crc 180 | } 181 | } 182 | 183 | /// Digest struct for CRC calculation. 184 | /// 185 | /// This struct is created using [`Crc::new_digest`] function. Use [`write_u32`] 186 | /// function to write data for calculation; use [`finish`] to read the result. 187 | /// After calculation, use [`free`] to get the Crc wrapper back. 188 | /// 189 | /// [`Crc::new_digest`]: ./struct.Crc.html#method.new_digest 190 | /// [`write_u32`]: #method.write_u32 191 | /// [`finish`]: #method.finish 192 | /// [`free`]: #method.free 193 | /// 194 | /// # Examples 195 | /// 196 | /// Calculate CRC result of single value: 197 | /// 198 | /// ```no_run 199 | /// // Write a single value 200 | /// digest.write_u32(0x12345678); 201 | /// // Read its CRC calculation result 202 | /// let ans = digest.finish(); 203 | /// ``` 204 | /// 205 | /// Calculate CRC reuslt of an array of values: 206 | /// 207 | /// ```no_run 208 | /// // Write all values of an array 209 | /// for value in array { 210 | /// digest.write_u32(value); 211 | /// } 212 | /// // Read the CRC calculation result of this array 213 | /// let ans = digest.finish(); 214 | /// ``` 215 | pub struct Digest { 216 | crc: CRC, 217 | } 218 | 219 | impl Digest { 220 | /// Writes a single u32 into this hasher. 221 | /// 222 | /// Multiple u32 values may be written one by one using this function. 223 | /// After all values written for calculation, you may read the CRC result 224 | /// using function [`finish`]. 225 | /// 226 | /// [`finish`]: #method.finish 227 | /// 228 | /// # Examples 229 | /// 230 | /// Write a single value: 231 | /// 232 | /// ```no_run 233 | /// // Write a single value 234 | /// digest.write_u32(0x12345678); 235 | /// ``` 236 | /// 237 | /// Write an array of values: 238 | /// 239 | /// ```no_run 240 | /// // Write all values of an array 241 | /// for value in array { 242 | /// digest.write_u32(value); 243 | /// } 244 | /// ``` 245 | #[inline] 246 | pub fn write_u32(&mut self, i: u32) { 247 | self.crc.data.write(|w| unsafe { w.data().bits(i) }); 248 | } 249 | 250 | /// Returns the hash value for the values written so far. 251 | /// 252 | /// # Examples 253 | /// 254 | /// Get CRC reuslt of a single value: 255 | /// 256 | /// ```no_run 257 | /// // Write a single value 258 | /// digest.write_u32(0x12345678); 259 | /// // Read its CRC calculation result 260 | /// let ans = digest.finish(); 261 | /// ``` 262 | /// 263 | /// Get CRC reuslt of an array of values: 264 | /// 265 | /// ```no_run 266 | /// // Write all values of an array 267 | /// for value in array { 268 | /// digest.write_u32(value); 269 | /// } 270 | /// // Read the CRC calculation result of this array 271 | /// let ans = digest.finish(); 272 | /// ``` 273 | #[inline] 274 | pub fn finish(&self) -> u32 { 275 | self.crc.data.read().data().bits() 276 | } 277 | 278 | /// Frees the Digest struct to return the underlying Crc peripheral. 279 | /// 280 | /// # Examples 281 | /// 282 | /// Basic usage: 283 | /// 284 | /// ```no_run 285 | /// // Calculate CRC of a single value 286 | /// digest.write_u32(0x12345678); 287 | /// let ans = digest.finish(); 288 | /// // Free the Digest to get the CRC wrapper back 289 | /// let crc = digest.free(); 290 | /// ``` 291 | #[inline] 292 | pub fn free(self) -> Crc { 293 | Crc { crc: self.crc } 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /src/ctimer.rs: -------------------------------------------------------------------------------- 1 | //! (TODO) Core Timer 2 | 3 | use gd32vf103_pac::CTIMER; 4 | 5 | // This right now just gets the systick register, system timer, or mtime. 6 | // I believe system timer is the correct name, as the documentation seems to 7 | // imply mtimer is instruction count, while the system timer increments on 8 | // clock pulses. 9 | // todo: A more proper name? I may prefer core timer 10 | // this module may cause software interrupt, thus may be controlled by multiple 11 | // contexts (e.g. modification in interrupts), so I prefer design as ownership 12 | // acquire model (CoreTimer is a new-type struct of CTIMER register block). 13 | /// CTIMER 14 | pub struct CoreTimer { 15 | ctimer: CTIMER, 16 | } 17 | 18 | impl CoreTimer { 19 | pub fn new(ctimer: CTIMER) -> Self { 20 | CoreTimer { ctimer } 21 | } 22 | 23 | /// Release the core timer resource 24 | pub fn free(self) -> CTIMER { 25 | self.ctimer 26 | } 27 | 28 | // todo: is this function necessary? check. 29 | #[doc(hidden)] 30 | pub fn get_value(&self) -> u64 { 31 | // Hi is systick1 32 | let hi: u32 = self.ctimer.mtime_hi.read().bits(); 33 | let lo: u32 = self.ctimer.mtime_lo.read().bits(); 34 | if hi == self.ctimer.mtime_hi.read().bits() { 35 | (hi as u64) << 32 | (lo as u64) 36 | } else { 37 | (self.ctimer.mtime_hi.read().bits() as u64) << 32 38 | | (self.ctimer.mtime_lo.read().bits() as u64) 39 | } 40 | } 41 | 42 | // This chip is a 32-bit MCU. Leave u32 functions here for convenience. 43 | #[inline] 44 | pub fn mtime_hi(&self) -> u32 { 45 | self.ctimer.mtime_hi.read().bits() 46 | } 47 | 48 | #[inline] 49 | pub fn mtime_lo(&self) -> u32 { 50 | self.ctimer.mtime_lo.read().bits() 51 | } 52 | 53 | // todo: more functions for mtimercmp, mstop and msip. 54 | } 55 | -------------------------------------------------------------------------------- /src/debug.rs: -------------------------------------------------------------------------------- 1 | //! Debug features 2 | 3 | // todo: design mode hold register (instead of `pub use`) 4 | use crate::pac::DBG; 5 | 6 | /// Read the DBG ID code register 7 | pub fn debug_id() -> u32 { 8 | // note(unsafe): read read-only register 9 | unsafe { &*DBG::ptr() }.id.read().bits() 10 | } 11 | -------------------------------------------------------------------------------- /src/delay.rs: -------------------------------------------------------------------------------- 1 | //! Delays 2 | use crate::ctimer::CoreTimer; 3 | use crate::rcu::Clocks; 4 | use crate::unit::*; 5 | use embedded_hal::blocking::delay::DelayMs; 6 | use core::convert::Infallible; 7 | 8 | /// CoreTimer as delay provider 9 | pub struct Delay { 10 | ctimer: CoreTimer, 11 | clock_frequency: Hertz, 12 | } 13 | 14 | impl Delay { 15 | // note: Clocks : Copy 16 | /// Configures the core timer as a delay provider 17 | pub fn new(clocks: Clocks, ctimer: CoreTimer) -> Self { 18 | Delay { 19 | ctimer, 20 | clock_frequency: clocks.ck_sys(), // SystemCoreClock 21 | } 22 | } 23 | 24 | /// Release and return the ownership of the core timer resource 25 | pub fn free(self) -> CoreTimer { 26 | self.ctimer 27 | } 28 | } 29 | 30 | impl DelayMs for Delay { 31 | type Error = Infallible; 32 | // This doesn't wait for a systick tick, so may be off by a few ns. Sorry 33 | // The divide by two may be incorrect for other dividors. It should be 8 34 | // according to the clock diagram, but 2 is accurate. I suspect 35 | // this will need to change with further documentation updates. 36 | // ----- 37 | // @luojia65: Ref: Examples/ADC/ADC0_TIMER1_trigger_inserted_channel/systick.c 38 | // the divide factor is 4000.0 39 | fn try_delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> { 40 | // factor 4000 is verified from official example files 41 | // leave u64 here 42 | let count: u64 = ms as u64 * (self.clock_frequency.0 / 4000) as u64; 43 | let tmp: u64 = self.ctimer.get_value(); 44 | let mut start: u64 = self.ctimer.get_value(); 45 | while start == tmp { 46 | start = self.ctimer.get_value(); 47 | } 48 | // prevent u64 overflow 49 | while u64::wrapping_sub(self.ctimer.get_value(), start) < count {} 50 | Ok(()) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/esig.rs: -------------------------------------------------------------------------------- 1 | //! Device electronic signature 2 | //! 3 | //! Ref: Section 1.5, GD32VF103 User Manual 4 | //! 5 | //! TODO: verify unique_id in this module 6 | // should this module be named `signature`? this name may be too long 7 | 8 | const UNIQUE_ID: *const [u32; 3] = 0x1FFF_F7E8 as *const _; 9 | const MEMORY_DENSITY: *const u16 = 0x1FFF_F7E0 as *const _; 10 | 11 | /// Factory programed unique device id. 12 | /// 13 | /// This field if 96 bits wide. It may be only read using 32-bit load 14 | /// procedures. 15 | /// 16 | /// According to section 1.5.2 of the Manual, this value 17 | /// can never be altered by user. 18 | #[inline] 19 | pub fn unique_id() -> &'static [u32; 3] { 20 | // note(unsafe): static read-only value 21 | unsafe { &*UNIQUE_ID } 22 | } 23 | 24 | /// Flash memory density in KBytes. 25 | /// 26 | /// This value indicates the flash memory density of the device in KBytes. 27 | /// For example, `0x0020` means 32 KBytes. 28 | /// 29 | /// Ref: Section 1.5.1, the Manual 30 | #[inline] 31 | pub fn flash_density() -> u16 { 32 | // note(unsafe): static read-only value 33 | unsafe { *MEMORY_DENSITY } // read bits [15:0] 34 | } 35 | 36 | /// On-chip SRAM density in KBytes. 37 | /// 38 | /// This value indicates the on-chip SRAM density of the device in KBytes. 39 | /// For example, `0x0008` means 8 KBytes. 40 | /// 41 | /// Ref: Section 1.5.1, the Manual 42 | #[inline] 43 | pub fn sram_density() -> u16 { 44 | // note(unsafe): static read-only value 45 | unsafe { *(MEMORY_DENSITY.add(1)) } // read bits [31:16] 46 | } 47 | -------------------------------------------------------------------------------- /src/fmc.rs: -------------------------------------------------------------------------------- 1 | //! (TODO) Flash Memory Controller (FMC) 2 | 3 | // this module maybe used in on-air firmware update programs (OTA) 4 | // or store user defined important data 5 | use crate::pac::FMC; 6 | 7 | #[doc(hidden)] // not finished 8 | pub struct Fmc { 9 | fmc: FMC, 10 | } 11 | 12 | impl Fmc { 13 | pub fn new(fmc: FMC) -> Self { 14 | Fmc { fmc } 15 | } 16 | 17 | pub fn free(self) -> FMC { 18 | self.fmc 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/gpio.rs: -------------------------------------------------------------------------------- 1 | //! General Purpose Input / Output 2 | 3 | use crate::rcu::APB2; 4 | use core::marker::PhantomData; 5 | 6 | /// Extension trait to split a GPIO peripheral into independent pins and registers 7 | pub trait GpioExt { 8 | /// The type to split the GPIO into 9 | type Parts; 10 | 11 | /// Splits the GPIO block into independent pins and registers 12 | fn split(self, apb2: &mut APB2) -> Self::Parts; 13 | } 14 | 15 | /// Analog input mode (type state) 16 | pub struct Analog; 17 | 18 | /// Input mode (type state) 19 | pub struct Input { 20 | _typestate_mode: PhantomData, 21 | } 22 | 23 | /// Floating input mode (type state) 24 | pub struct Floating; 25 | 26 | /// Pulled down input mode (type state) 27 | pub struct PullDown; 28 | 29 | /// Pulled up input mode (type state) 30 | pub struct PullUp; 31 | 32 | /// Output mode (type state) 33 | pub struct Output { 34 | _typestate_mode: PhantomData, 35 | } 36 | 37 | /// Alternate mode (type state) 38 | pub struct Alternate { 39 | _typestate_mode: PhantomData, 40 | } 41 | 42 | /// Push-pull output or alternate (type state) 43 | pub struct PushPull; 44 | 45 | /// Open drain output or alternate (type state) 46 | pub struct OpenDrain; 47 | 48 | /// Marker trait for active states 49 | pub trait Active {} 50 | 51 | impl Active for Analog {} 52 | 53 | impl Active for Input {} 54 | 55 | impl Active for Output {} 56 | 57 | impl Active for Alternate {} 58 | 59 | /// Output speed up to 10 MHz (type param) 60 | pub struct UpTo10MHz; 61 | 62 | /// Output speed up to 2 MHz (type param) 63 | pub struct UpTo2MHz; 64 | 65 | /// Output speed up to 50 MHz (type param) 66 | pub struct UpTo50MHz; 67 | 68 | /// Marker trait for valid output speed 69 | pub trait Speed { 70 | // The MD\[1:0\] bits this speed is represented into 71 | #[doc(hidden)] 72 | const MD_BITS: u32; 73 | } 74 | 75 | impl Speed for UpTo50MHz { 76 | const MD_BITS: u32 = 0b11; 77 | } 78 | 79 | impl Speed for UpTo10MHz { 80 | const MD_BITS: u32 = 0b01; 81 | } 82 | 83 | impl Speed for UpTo2MHz { 84 | const MD_BITS: u32 = 0b10; 85 | } 86 | 87 | /// Wraps a pin if this pin is locked 88 | pub struct Locked(T); 89 | 90 | // implement all digital input/output traits for locked pins 91 | mod impl_for_locked { 92 | use super::Locked; 93 | use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin}; 94 | 95 | impl OutputPin for Locked 96 | where 97 | T: OutputPin, 98 | { 99 | type Error = T::Error; 100 | 101 | fn try_set_low(&mut self) -> Result<(), Self::Error> { 102 | self.0.try_set_low() 103 | } 104 | 105 | fn try_set_high(&mut self) -> Result<(), Self::Error> { 106 | self.0.try_set_high() 107 | } 108 | } 109 | 110 | impl StatefulOutputPin for Locked 111 | where 112 | T: StatefulOutputPin, 113 | { 114 | fn try_is_set_high(&self) -> Result { 115 | self.0.try_is_set_high() 116 | } 117 | 118 | fn try_is_set_low(&self) -> Result { 119 | self.0.try_is_set_low() 120 | } 121 | } 122 | 123 | impl ToggleableOutputPin for Locked 124 | where 125 | T: ToggleableOutputPin, 126 | { 127 | type Error = T::Error; 128 | 129 | fn try_toggle(&mut self) -> Result<(), Self::Error> { 130 | self.0.try_toggle() 131 | } 132 | } 133 | 134 | impl InputPin for Locked 135 | where 136 | T: InputPin, 137 | { 138 | type Error = T::Error; 139 | 140 | fn try_is_high(&self) -> Result { 141 | self.0.try_is_high() 142 | } 143 | 144 | fn try_is_low(&self) -> Result { 145 | self.0.try_is_low() 146 | } 147 | } 148 | } 149 | 150 | /// Useful unlock methods for lock marked pins 151 | /// 152 | /// _Note: We design this trait other than giving all pins an `unlock` method 153 | /// because if we do so, the rust doc of struct `Lock` could be full of `unlock` 154 | /// methods (dozens of them) with full documents for each `unlock` functions, which 155 | /// could be confusing for users and costs much time to read and build. If any 156 | /// questions, please fire an issue to let us know._ 157 | pub trait Unlock { 158 | /// The lock controller register block, typically a `LOCK` struct with temporary 159 | /// variant bits for each pins. 160 | type Lock; 161 | 162 | /// Unlock output, typically a `PXi` struct with mode typestate. 163 | type Output; 164 | 165 | /// Mark the locked pin as unlocked to allow configurations of pin mode. 166 | /// 167 | /// Typically this method uses a mutable borrow of `LOCK` struct of the gpio port. 168 | /// This function is not an actually unlock; it only clears the corresponding 169 | /// bit in a temporary variant in `LOCK`. To actually perform and freeze the lock, 170 | /// use `freeze`; see function `lock` for details. 171 | /// 172 | /// The caller of this method must obtain a mutable reference of `LOCK` struct; 173 | /// if you have called the `freeze` method of that `LOCK` struct, the actually lock 174 | /// operation would perform and lock state of all pins would be no longer possible to 175 | /// change - see its documentation for details. 176 | fn unlock(self, lock: &mut Self::Lock) -> Self::Output; 177 | } 178 | 179 | trait PinIndex { 180 | const OP_LK_INDEX: usize; 181 | 182 | const CTL_MD_INDEX: usize; 183 | } 184 | 185 | macro_rules! impl_gpio { 186 | ($GPIOX:ident,$gpiox:ident,$gpioy:ident,$en:ident,$rst: ident,$PXx:ident, [ 187 | $($PXi:ident:($pxi:ident,$i:expr,$MODE:ty,$CTL:ident,$ctl:ident),)+ 188 | ]) => { 189 | /// GPIO port 190 | pub mod $gpiox { 191 | use super::{ 192 | Active, Alternate, Analog, Floating, GpioExt, Input, OpenDrain, Output, 193 | PinIndex, PullDown, PullUp, PushPull, Speed, UpTo50MHz, Locked, Unlock 194 | }; 195 | use crate::pac::{$gpioy, $GPIOX}; 196 | use crate::rcu::APB2; 197 | use crate::atomic::{atomic_set_bit, atomic_toggle_bit}; 198 | use core::convert::Infallible; 199 | use core::marker::PhantomData; 200 | use core::sync::atomic::AtomicU32; 201 | use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin}; 202 | 203 | /// GPIO parts 204 | pub struct Parts { 205 | /// Opaque CTL0 register 206 | pub ctl0: CTL0, 207 | /// Opaque CTL1 register 208 | pub ctl1: CTL1, 209 | /// Opaque OCTL register 210 | pub octl: OCTL, 211 | /// Opaque LOCK register 212 | pub lock: LOCK, 213 | $( 214 | /// Pin 215 | pub $pxi: $PXi<$MODE>, 216 | )+ 217 | #[doc(hidden)] 218 | _extensible: (), 219 | } 220 | 221 | impl GpioExt for $GPIOX { 222 | type Parts = Parts; 223 | 224 | fn split(self, apb2: &mut APB2) -> Self::Parts { 225 | riscv::interrupt::free(|_| { 226 | apb2.en().modify(|_,w| w.$en().set_bit()); 227 | apb2.rst().write(|w| w.$rst().set_bit()); 228 | apb2.rst().write(|w| w.$rst().clear_bit()); 229 | }); 230 | Parts { 231 | ctl0: CTL0 { _ownership: () }, 232 | ctl1: CTL1 { _ownership: () }, 233 | octl: OCTL { _ownership: () }, 234 | lock: LOCK { 235 | tmp_bits: unsafe { &(*$GPIOX::ptr()).lock }.read().bits(), 236 | _ownership: () 237 | }, 238 | $( 239 | $pxi: $PXi { 240 | _typestate_mode: PhantomData, 241 | }, 242 | )+ 243 | _extensible: (), 244 | } 245 | } 246 | } 247 | 248 | /// Opaque CTL0 register 249 | pub struct CTL0 { 250 | _ownership: (), 251 | } 252 | 253 | impl CTL0 { 254 | pub(crate) fn ctl0(&mut self) -> &$gpioy::CTL0 { 255 | unsafe { &(*$GPIOX::ptr()).ctl0 } 256 | } 257 | } 258 | 259 | /// Opaque CTL1 register 260 | pub struct CTL1 { 261 | _ownership: (), 262 | } 263 | 264 | impl CTL1 { 265 | pub(crate) fn ctl1(&mut self) -> &$gpioy::CTL1 { 266 | unsafe { &(*$GPIOX::ptr()).ctl1 } 267 | } 268 | } 269 | 270 | /// Opaque OCTL register 271 | pub struct OCTL { 272 | _ownership: (), 273 | } 274 | 275 | impl OCTL { 276 | pub(crate) fn octl(&mut self) -> &$gpioy::OCTL { 277 | unsafe { &(*$GPIOX::ptr()).octl } 278 | } 279 | } 280 | 281 | /// Opaque LOCK register 282 | pub struct LOCK { 283 | tmp_bits: u32, 284 | _ownership: (), 285 | } 286 | 287 | impl LOCK { 288 | pub(crate) fn lock(&mut self) -> &$gpioy::LOCK { 289 | unsafe { &(*$GPIOX::ptr()).lock } 290 | } 291 | 292 | /// Freeze pin modes of this GPIO port to forbid furtuer modifications 293 | /// on pin modes. 294 | /// 295 | /// By the time this function succeeds to execute, the program cannot 296 | /// change CTL0 and CTL1 registers of this port anymore before chip reset. 297 | /// To perform the real lock process, this operation writes LKy and LKK 298 | /// registers in a special way, and this configuration cannot be undone 299 | /// so it consumes the LOCK register struct `self`. 300 | /// 301 | /// Instead of returning the LOCK back, this function panics on lock failure. 302 | /// That's because we consider all lock failures comes from mistakes in 303 | /// underlying libraries or chip design which may be not proper for users 304 | /// to handle by themselves. If this design results in mistake, please 305 | /// fire an issue to let us know. 306 | pub fn freeze(mut self) { 307 | let tmp = self.tmp_bits; 308 | let a = tmp | 0x00010000; 309 | // write in special ways to lock the register 310 | let success = riscv::interrupt::free(|_| { 311 | self.lock().write(|w| unsafe { w.bits(a) }); 312 | self.lock().write(|w| unsafe { w.bits(tmp) }); 313 | self.lock().write(|w| unsafe { w.bits(a) }); 314 | let ans1 = self.lock().read().bits(); 315 | let ans2 = self.lock().read().bits(); 316 | ans1 == 0 && ans2 & 0x00010000 != 0 317 | }); 318 | // if success, this function returns 319 | if !success { 320 | panic!("the LOCK freeze process won't succeed") 321 | } 322 | } 323 | } 324 | 325 | /// Partially erased pin 326 | pub struct $PXx { 327 | i: u8, 328 | _typestate_mode: PhantomData, 329 | } 330 | 331 | impl InputPin for $PXx> { 332 | type Error = Infallible; 333 | 334 | fn try_is_high(&self) -> Result { 335 | let ans = 336 | (unsafe { &(*$GPIOX::ptr()).istat }.read().bits() & (1 << self.i)) != 0; 337 | Ok(ans) 338 | } 339 | 340 | fn try_is_low(&self) -> Result { 341 | Ok(!self.try_is_high()?) 342 | } 343 | } 344 | 345 | impl OutputPin for $PXx> { 346 | type Error = Infallible; 347 | 348 | fn try_set_high(&mut self) -> Result<(), Self::Error> { 349 | unsafe { &(*$GPIOX::ptr()).bop }.write(|w| unsafe { w.bits(1 << self.i) }); 350 | Ok(()) 351 | } 352 | 353 | fn try_set_low(&mut self) -> Result<(), Self::Error> { 354 | unsafe { &(*$GPIOX::ptr()).bc }.write(|w| unsafe { w.bits(1 << self.i) }); 355 | Ok(()) 356 | } 357 | } 358 | 359 | impl StatefulOutputPin for $PXx> { 360 | fn try_is_set_high(&self) -> Result { 361 | let ans = 362 | (unsafe { &(*$GPIOX::ptr()).octl }.read().bits() & (1 << self.i)) != 0; 363 | Ok(ans) 364 | } 365 | 366 | fn try_is_set_low(&self) -> Result { 367 | Ok(!self.try_is_set_high()?) 368 | } 369 | } 370 | 371 | impl ToggleableOutputPin for $PXx> { 372 | type Error = Infallible; 373 | 374 | fn try_toggle(&mut self) -> Result<(), Self::Error> { 375 | let r: &AtomicU32 = unsafe { &*(&(*$GPIOX::ptr()).octl as *const _ as *const _) }; 376 | atomic_toggle_bit(r, self.i as usize); 377 | Ok(()) 378 | } 379 | } 380 | 381 | impl InputPin for $PXx> { 382 | type Error = Infallible; 383 | 384 | fn try_is_high(&self) -> Result { 385 | let ans = 386 | (unsafe { &(*$GPIOX::ptr()).istat }.read().bits() & (1 << self.i)) != 0; 387 | Ok(ans) 388 | } 389 | 390 | fn try_is_low(&self) -> Result { 391 | Ok(!self.try_is_high()?) 392 | } 393 | } 394 | $( 395 | /// Pin 396 | pub struct $PXi { 397 | _typestate_mode: PhantomData, 398 | } 399 | 400 | impl PinIndex for $PXi { 401 | const OP_LK_INDEX: usize = $i; 402 | 403 | const CTL_MD_INDEX: usize = (4 * $i) % 32; 404 | } 405 | 406 | impl $PXi 407 | where 408 | MODE: Active, 409 | { 410 | /// Configures the pin to serve as an analog input pin. 411 | pub fn into_analog(self, $ctl: &mut $CTL) -> $PXi { 412 | self.into_with_ctrl_md($ctl, 0b00_00) 413 | } 414 | 415 | /// Configures the pin to serve as a floating input pin. 416 | pub fn into_floating_input(self, $ctl: &mut $CTL) -> $PXi> { 417 | self.into_with_ctrl_md($ctl, 0b01_00) 418 | } 419 | 420 | /// Configures the pin to serve as a pull down input pin. 421 | pub fn into_pull_down_input( 422 | self, 423 | $ctl: &mut $CTL, 424 | octl: &mut OCTL, 425 | ) -> $PXi> { 426 | let r: &AtomicU32 = unsafe { &*(&octl.octl() as *const _ as *const _) }; 427 | atomic_set_bit(r, false, Self::OP_LK_INDEX); 428 | self.into_with_ctrl_md($ctl, 0b10_00) 429 | } 430 | 431 | /// Configures the pin to serve as a pull up input pin. 432 | pub fn into_pull_up_input( 433 | self, 434 | $ctl: &mut $CTL, 435 | octl: &mut OCTL, 436 | ) -> $PXi> { 437 | let r: &AtomicU32 = unsafe { &*(&octl.octl() as *const _ as *const _) }; 438 | atomic_set_bit(r, true, Self::OP_LK_INDEX); 439 | self.into_with_ctrl_md($ctl, 0b10_00) 440 | } 441 | 442 | /// Configures the pin to serve as a push pull output pin; 443 | /// the maximum speed is set to the default value 50MHz. 444 | pub fn into_push_pull_output( 445 | self, 446 | $ctl: &mut $CTL, 447 | ) -> $PXi> { 448 | let ctrl_md = 0b00_00 | UpTo50MHz::MD_BITS; 449 | self.into_with_ctrl_md($ctl, ctrl_md) 450 | } 451 | 452 | /// Configures the pin to serve as an open drain output pin; 453 | /// the maximum speed is set to the default value 50MHz. 454 | pub fn into_open_drain_output( 455 | self, 456 | $ctl: &mut $CTL, 457 | ) -> $PXi> { 458 | let ctrl_md = 0b01_00 | UpTo50MHz::MD_BITS; 459 | self.into_with_ctrl_md($ctl, ctrl_md) 460 | } 461 | 462 | /// Configures the pin to serve as a push pull alternate pin; 463 | /// the maximum speed is set to the default value 50MHz. 464 | pub fn into_alternate_push_pull( 465 | self, 466 | $ctl: &mut $CTL, 467 | ) -> $PXi> { 468 | let ctrl_md = 0b10_00 | UpTo50MHz::MD_BITS; 469 | self.into_with_ctrl_md($ctl, ctrl_md) 470 | } 471 | 472 | /// Configures the pin to serve as an open drain alternate pin; 473 | /// the maximum speed is set to the default value 50MHz. 474 | pub fn into_alternate_open_drain( 475 | self, 476 | $ctl: &mut $CTL, 477 | ) -> $PXi> { 478 | let ctrl_md = 0b11_00 | UpTo50MHz::MD_BITS; 479 | self.into_with_ctrl_md($ctl, ctrl_md) 480 | } 481 | 482 | /// Configures the pin to serve as a push pull output pin with maximum speed given. 483 | pub fn into_push_pull_output_speed( 484 | self, 485 | $ctl: &mut $CTL, 486 | ) -> $PXi> { 487 | let ctrl_md = 0b00_00 | SPEED::MD_BITS; 488 | self.into_with_ctrl_md($ctl, ctrl_md) 489 | } 490 | 491 | /// Configures the pin to serve as an open drain output pin with maximum speed given. 492 | pub fn into_open_drain_output_speed( 493 | self, 494 | $ctl: &mut $CTL, 495 | ) -> $PXi> { 496 | let ctrl_md = 0b01_00 | SPEED::MD_BITS; 497 | self.into_with_ctrl_md($ctl, ctrl_md) 498 | } 499 | 500 | /// Configures the pin to serve as a push pull alternate pin with maximum speed given 501 | pub fn into_alternate_push_pull_speed( 502 | self, 503 | $ctl: &mut $CTL, 504 | ) -> $PXi> { 505 | let ctrl_md = 0b10_00 | SPEED::MD_BITS; 506 | self.into_with_ctrl_md($ctl, ctrl_md) 507 | } 508 | 509 | /// Configures the pin to serve as an open drain alternate pin with maximum speed given. 510 | pub fn into_alternate_open_drain_speed( 511 | self, 512 | $ctl: &mut $CTL, 513 | ) -> $PXi> { 514 | let ctrl_md = 0b11_00 | SPEED::MD_BITS; 515 | self.into_with_ctrl_md($ctl, ctrl_md) 516 | } 517 | 518 | #[inline] 519 | fn into_with_ctrl_md(self, $ctl: &mut $CTL, ctl_and_md: u32) -> $PXi { 520 | $ctl.$ctl().modify(|r, w| unsafe { 521 | w.bits( 522 | (r.bits() & !(0b1111 << Self::CTL_MD_INDEX)) 523 | | (ctl_and_md << Self::CTL_MD_INDEX), 524 | ) 525 | }); 526 | $PXi { 527 | _typestate_mode: PhantomData, 528 | } 529 | } 530 | 531 | /// Lock the pin to prevent further configurations on pin mode. 532 | /// 533 | /// After this function is called, the pin is not actually locked; it only 534 | /// sets a marker temporary variant to prepare for the real lock freezing 535 | /// procedure `freeze`. To actually perform the lock, users are encouraged 536 | /// to call `freeze` after all pins configured and marked properly for lock. 537 | /// 538 | /// The output state of this pin can still be changed. You may unlock locked 539 | /// pins by using `unlock` method with a mutable reference of `LOCK` struct, 540 | /// but it will not be possible if `freeze` method of LOCK struct was 541 | /// called; see its documentation for details. 542 | #[inline] 543 | pub fn lock(self, lock: &mut LOCK) -> Locked<$PXi> { 544 | let r: &AtomicU32 = unsafe { &*(&lock.tmp_bits as *const _ as *const _) }; 545 | atomic_set_bit(r, true, Self::OP_LK_INDEX); 546 | Locked($PXi { 547 | _typestate_mode: PhantomData, 548 | }) 549 | } 550 | } 551 | 552 | impl Unlock for Locked<$PXi> 553 | where 554 | MODE: Active, 555 | { 556 | type Lock = LOCK; 557 | 558 | type Output = $PXi; 559 | 560 | #[inline] 561 | fn unlock(self, lock: &mut Self::Lock) -> Self::Output { 562 | // set temporary bit for this pin in LOCK struct 563 | let r: &AtomicU32 = unsafe { &*(&lock.tmp_bits as *const _ as *const _) }; 564 | atomic_set_bit(r, false, $i); // PXi::OP_LK_INDEX 565 | $PXi { 566 | _typestate_mode: PhantomData, 567 | } 568 | } 569 | } 570 | 571 | impl $PXi 572 | where 573 | MODE: Active 574 | { 575 | /// Erases the pin number from the type. 576 | /// 577 | /// This is useful when you want to collect the pins into an array 578 | /// where you need all the elements to have the same type. 579 | pub fn downgrade(self) -> $PXx { 580 | $PXx { 581 | i: $i, 582 | _typestate_mode: PhantomData 583 | } 584 | } 585 | } 586 | 587 | impl InputPin for $PXi> { 588 | type Error = Infallible; 589 | 590 | fn try_is_high(&self) -> Result { 591 | let ans = 592 | (unsafe { &(*$GPIOX::ptr()).istat }.read().bits() & (1 << Self::OP_LK_INDEX)) != 0; 593 | Ok(ans) 594 | } 595 | 596 | fn try_is_low(&self) -> Result { 597 | Ok(!self.try_is_high()?) 598 | } 599 | } 600 | 601 | impl OutputPin for $PXi> { 602 | type Error = Infallible; 603 | 604 | fn try_set_high(&mut self) -> Result<(), Self::Error> { 605 | unsafe { &(*$GPIOX::ptr()).bop }.write(|w| unsafe { w.bits(1 << Self::OP_LK_INDEX) }); 606 | Ok(()) 607 | } 608 | 609 | fn try_set_low(&mut self) -> Result<(), Self::Error> { 610 | unsafe { &(*$GPIOX::ptr()).bc }.write(|w| unsafe { w.bits(1 << Self::OP_LK_INDEX) }); 611 | Ok(()) 612 | } 613 | } 614 | 615 | impl OutputPin for $PXi> { 616 | type Error = Infallible; 617 | 618 | fn try_set_high(&mut self) -> Result<(), Self::Error> { 619 | unsafe { &(*$GPIOX::ptr()).bop }.write(|w| unsafe { w.bits(1 << Self::OP_LK_INDEX) }); 620 | Ok(()) 621 | } 622 | 623 | fn try_set_low(&mut self) -> Result<(), Self::Error> { 624 | unsafe { &(*$GPIOX::ptr()).bc }.write(|w| unsafe { w.bits(1 << Self::OP_LK_INDEX) }); 625 | Ok(()) 626 | } 627 | } 628 | 629 | impl StatefulOutputPin for $PXi> { 630 | fn try_is_set_high(&self) -> Result { 631 | let ans = 632 | (unsafe { &(*$GPIOX::ptr()).octl }.read().bits() & (1 << Self::OP_LK_INDEX)) != 0; 633 | Ok(ans) 634 | } 635 | 636 | fn try_is_set_low(&self) -> Result { 637 | Ok(!self.try_is_set_high()?) 638 | } 639 | } 640 | 641 | impl StatefulOutputPin for $PXi> { 642 | fn try_is_set_high(&self) -> Result { 643 | let ans = 644 | (unsafe { &(*$GPIOX::ptr()).octl }.read().bits() & (1 << Self::OP_LK_INDEX)) != 0; 645 | Ok(ans) 646 | } 647 | 648 | fn try_is_set_low(&self) -> Result { 649 | Ok(!self.try_is_set_high()?) 650 | } 651 | } 652 | 653 | impl ToggleableOutputPin for $PXi> { 654 | type Error = Infallible; 655 | 656 | fn try_toggle(&mut self) -> Result<(), Self::Error> { 657 | let r: &AtomicU32 = unsafe { &*(&(*$GPIOX::ptr()).octl as *const _ as *const _) }; 658 | atomic_toggle_bit(r, Self::OP_LK_INDEX); 659 | Ok(()) 660 | } 661 | } 662 | 663 | impl ToggleableOutputPin for $PXi> { 664 | type Error = Infallible; 665 | 666 | fn try_toggle(&mut self) -> Result<(), Self::Error> { 667 | let r: &AtomicU32 = unsafe { &*(&(*$GPIOX::ptr()).octl as *const _ as *const _) }; 668 | atomic_toggle_bit(r, Self::OP_LK_INDEX); 669 | Ok(()) 670 | } 671 | } 672 | 673 | impl InputPin for $PXi> { 674 | type Error = Infallible; 675 | 676 | fn try_is_high(&self) -> Result { 677 | let ans = 678 | (unsafe { &(*$GPIOX::ptr()).istat }.read().bits() & (1 << Self::OP_LK_INDEX)) != 0; 679 | Ok(ans) 680 | } 681 | 682 | fn try_is_low(&self) -> Result { 683 | Ok(!self.try_is_high()?) 684 | } 685 | } 686 | )+ 687 | } 688 | }; 689 | } 690 | 691 | impl_gpio! { GPIOA, gpioa, gpioa, paen, parst, PAx, [ 692 | PA0: (pa0, 0, Input, CTL0, ctl0), 693 | PA1: (pa1, 1, Input, CTL0, ctl0), 694 | PA2: (pa2, 2, Input, CTL0, ctl0), 695 | PA3: (pa3, 3, Input, CTL0, ctl0), 696 | PA4: (pa4, 4, Input, CTL0, ctl0), 697 | PA5: (pa5, 5, Input, CTL0, ctl0), 698 | PA6: (pa6, 6, Input, CTL0, ctl0), 699 | PA7: (pa7, 7, Input, CTL0, ctl0), 700 | PA8: (pa8, 8, Input, CTL1, ctl1), 701 | PA9: (pa9, 9, Input, CTL1, ctl1), 702 | PA10: (pa10, 10, Input, CTL1, ctl1), 703 | PA11: (pa11, 11, Input, CTL1, ctl1), 704 | PA12: (pa12, 12, Input, CTL1, ctl1), 705 | PA13: (pa13, 13, Input, CTL1, ctl1), 706 | PA14: (pa14, 14, Input, CTL1, ctl1), 707 | PA15: (pa15, 15, Input, CTL1, ctl1), 708 | ] } 709 | 710 | impl_gpio! { GPIOB, gpiob, gpioa, pben, pbrst, PBx, [ 711 | PB0: (pb0, 0, Input, CTL0, ctl0), 712 | PB1: (pb1, 1, Input, CTL0, ctl0), 713 | PB2: (pb2, 2, Input, CTL0, ctl0), 714 | PB3: (pb3, 3, Input, CTL0, ctl0), 715 | PB4: (pb4, 4, Input, CTL0, ctl0), 716 | PB5: (pb5, 5, Input, CTL0, ctl0), 717 | PB6: (pb6, 6, Input, CTL0, ctl0), 718 | PB7: (pb7, 7, Input, CTL0, ctl0), 719 | PB8: (pb8, 8, Input, CTL1, ctl1), 720 | PB9: (pb9, 9, Input, CTL1, ctl1), 721 | PB10: (pb10, 10, Input, CTL1, ctl1), 722 | PB11: (pb11, 11, Input, CTL1, ctl1), 723 | PB12: (pb12, 12, Input, CTL1, ctl1), 724 | PB13: (pb13, 13, Input, CTL1, ctl1), 725 | PB14: (pb14, 14, Input, CTL1, ctl1), 726 | PB15: (pb15, 15, Input, CTL1, ctl1), 727 | ] } 728 | 729 | impl_gpio! { GPIOC, gpioc, gpioa, pcen, pcrst, PCx, [ 730 | PC0: (pc0, 0, Input, CTL0, ctl0), 731 | PC1: (pc1, 1, Input, CTL0, ctl0), 732 | PC2: (pc2, 2, Input, CTL0, ctl0), 733 | PC3: (pc3, 3, Input, CTL0, ctl0), 734 | PC4: (pc4, 4, Input, CTL0, ctl0), 735 | PC5: (pc5, 5, Input, CTL0, ctl0), 736 | PC6: (pc6, 6, Input, CTL0, ctl0), 737 | PC7: (pc7, 7, Input, CTL0, ctl0), 738 | PC8: (pc8, 8, Input, CTL1, ctl1), 739 | PC9: (pc9, 9, Input, CTL1, ctl1), 740 | PC10: (pc10, 10, Input, CTL1, ctl1), 741 | PC11: (pc11, 11, Input, CTL1, ctl1), 742 | PC12: (pc12, 12, Input, CTL1, ctl1), 743 | PC13: (pc13, 13, Input, CTL1, ctl1), 744 | PC14: (pc14, 14, Input, CTL1, ctl1), 745 | PC15: (pc15, 15, Input, CTL1, ctl1), 746 | ] } 747 | 748 | impl_gpio! { GPIOD, gpiod, gpioa, pden, pdrst, PDx, [ 749 | PD0: (pd0, 0, Input, CTL0, ctl0), 750 | PD1: (pd1, 1, Input, CTL0, ctl0), 751 | PD2: (pd2, 2, Input, CTL0, ctl0), 752 | PD3: (pd3, 3, Input, CTL0, ctl0), 753 | PD4: (pd4, 4, Input, CTL0, ctl0), 754 | PD5: (pd5, 5, Input, CTL0, ctl0), 755 | PD6: (pd6, 6, Input, CTL0, ctl0), 756 | PD7: (pd7, 7, Input, CTL0, ctl0), 757 | PD8: (pd8, 8, Input, CTL1, ctl1), 758 | PD9: (pd9, 9, Input, CTL1, ctl1), 759 | PD10: (pd10, 10, Input, CTL1, ctl1), 760 | PD11: (pd11, 11, Input, CTL1, ctl1), 761 | PD12: (pd12, 12, Input, CTL1, ctl1), 762 | PD13: (pd13, 13, Input, CTL1, ctl1), 763 | PD14: (pd14, 14, Input, CTL1, ctl1), 764 | PD15: (pd15, 15, Input, CTL1, ctl1), 765 | ] } 766 | 767 | impl_gpio! { GPIOE, gpioe, gpioa, peen, perst, PEx, [ 768 | PE0: (pe0, 0, Input, CTL0, ctl0), 769 | PE1: (pe1, 1, Input, CTL0, ctl0), 770 | PE2: (pe2, 2, Input, CTL0, ctl0), 771 | PE3: (pe3, 3, Input, CTL0, ctl0), 772 | PE4: (pe4, 4, Input, CTL0, ctl0), 773 | PE5: (pe5, 5, Input, CTL0, ctl0), 774 | PE6: (pe6, 6, Input, CTL0, ctl0), 775 | PE7: (pe7, 7, Input, CTL0, ctl0), 776 | PE8: (pe8, 8, Input, CTL1, ctl1), 777 | PE9: (pe9, 9, Input, CTL1, ctl1), 778 | PE10: (pe10, 10, Input, CTL1, ctl1), 779 | PE11: (pe11, 11, Input, CTL1, ctl1), 780 | PE12: (pe12, 12, Input, CTL1, ctl1), 781 | PE13: (pe13, 13, Input, CTL1, ctl1), 782 | PE14: (pe14, 14, Input, CTL1, ctl1), 783 | PE15: (pe15, 15, Input, CTL1, ctl1), 784 | ] } 785 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Hardware abstract layer (HAL) for the GD32VF103 microcontroller chip. 2 | //! 3 | //! This is an implementation of `embedded-hal` traits for the GD32VF103, MCU with 4 | //! one RISC-V's RV32IMAC core as well as up to 128 KiB of Flash and 32 KiB of SRAM, 5 | //! produced by GigaDevice Semiconductor Inc. 6 | //! 7 | //! # Usage 8 | //! Add this crate to your dependencies: 9 | //! ``` 10 | //! [dependencies] 11 | //! gd32vf103-hal = "0.0" 12 | //! ``` 13 | //! 14 | //! # Example 15 | //! ``` 16 | //! #![no_std] 17 | //! #![no_main] 18 | //! // choose a panic handler crate 19 | //! extern crate panic_halt; 20 | //! // include this library 21 | //! use gd32vf103_hal::{pac, prelude::*}; 22 | //! // use the `riscv_rt` runtime to define entry 23 | //! #[riscv_rt::entry] 24 | //! fn main() -> ! { 25 | //! // Get ownership of device peripherals 26 | //! let dp = pac::Peripherals::take().unwrap(); 27 | //! // Constrain RCU register for further use 28 | //! let mut rcu = dp.RCU.constrain(); 29 | //! // Split GPIOA into separate pins. 30 | //! // You need a mutable reference of APB2 struct to initialize GPIOA, 31 | //! // so we offer `&mut rcu.apb2` as a parameter here. 32 | //! let mut gpioa = dp.GPIOA.split(&mut rcu.apb2); 33 | //! // Change the state of `pa1` into push-pull output with default speed. 34 | //! let mut pa1 = gpioa.pa1.into_push_pull_output(&mut gpioa.ctl0); 35 | //! // Use API offered by `embedded-hal` to set `pa1` low, 36 | //! // An LED light with cathode connected to PA1 should be lit now. 37 | //! pa1.set_low().unwrap(); 38 | //! // We just end this program with infinite loop. 39 | //! // A `wfi` instruction should be also acceptable here. 40 | //! loop {} 41 | //! } 42 | //! ``` 43 | 44 | #![no_std] 45 | // #![deny(missing_docs)] 46 | 47 | pub use gd32vf103_pac as pac; 48 | 49 | pub mod adc; 50 | pub mod afio; 51 | pub mod backup; 52 | pub mod crc; 53 | pub mod ctimer; 54 | pub mod debug; 55 | pub mod delay; 56 | pub mod esig; 57 | pub mod fmc; 58 | pub mod gpio; 59 | pub mod rcu; 60 | pub mod serial; 61 | pub mod spi; 62 | pub mod timer; 63 | pub mod unit; 64 | pub mod wdog; 65 | 66 | /// Prelude 67 | pub mod prelude { 68 | pub use embedded_hal::prelude::*; 69 | pub use crate::afio::AfioExt as _gd32vf103_hal_afio_AfioExt; 70 | pub use crate::gpio::GpioExt as _gd32vf103_hal_gpio_GpioExt; 71 | pub use crate::gpio::{Unlock as _gd32vf103_hal_gpio_Unlock, UpTo10MHz, UpTo2MHz, UpTo50MHz}; 72 | pub use crate::rcu::RcuExt as _gd32vf103_hal_rcu_RcuExt; 73 | pub use crate::unit::U32Ext as _gd32vf103_hal_unit_U32Ext; 74 | } 75 | 76 | mod atomic { 77 | use core::sync::atomic::{AtomicU32, Ordering}; 78 | // This function uses AtomicU32, compiles into atomic instructions to prevent data race 79 | // and optimize for speed. 80 | // 81 | // If we don't do like this, we would need to go into critical section, where additional 82 | // interrupt disabling and enabling operations are required, which needs lots of CSR 83 | // read/write instructions and costs lot of time. 84 | // 85 | // For all `is_one: true` params, the core feature of this function compiles into 86 | // only one atomic instruction `amoor.w` to set the target register. 87 | // (For `is_one: false` params, it compiles into ont `amoand.w`). 88 | // Additional instructions to set the mask may differ between actual applications, 89 | // this part may cost additional one to two instructions (mainly `lui` and `addi`). 90 | // 91 | // Note: we uses `fetch_and(!mask, ...)` instead of `fetch_nand(mask, ...)`; that's 92 | // because RISC-V's RV32A does not provide an atomic nand instruction, thus `rustc` 93 | // may compile code into very long binary output. 94 | #[inline(always)] 95 | pub(crate) fn atomic_set_bit(r: &AtomicU32, is_one: bool, index: usize) { 96 | let mask = 1 << index; 97 | if is_one { 98 | r.fetch_or(mask, Ordering::Relaxed); 99 | } else { 100 | r.fetch_and(!mask, Ordering::Relaxed); 101 | } 102 | } 103 | 104 | // This function compiles into RV32A's `amoxor.w` instruction to prevent data 105 | // race as well as optimize for speed. 106 | #[inline(always)] 107 | pub(crate) fn atomic_toggle_bit(r: &AtomicU32, index: usize) { 108 | let mask = 1 << index; 109 | r.fetch_xor(mask, Ordering::Relaxed); 110 | } 111 | } 112 | 113 | // == Notes on prelude trait function naming: 114 | // 115 | // If we wrap some register modules into one Rust `mod`, we infer that 116 | // all the modules share common switches, clocks or unlock process. 117 | // 118 | // To take apart whole module into functional module register groups, 119 | // we use traits with one function. Function name can be arbitrary in 120 | // theory but we prefer following frequent function names: 121 | // - split 122 | // - constrain 123 | // - configure 124 | // 125 | // The function name should depends on how the modules logically effect 126 | // each other: 127 | // 128 | // If logical state of module registers do not depend on each other, 129 | // the trait function name could be `split`. 130 | // 131 | // If logical states of module registers is under inherit or hierarchy 132 | // relation thus may depend on each other, name could be `constain` 133 | // or `configure`. If all combination of register states are valid, 134 | // use `configure`; otherwise if some combinations are invalid, use 135 | // `constrain`. 136 | -------------------------------------------------------------------------------- /src/rcu.rs: -------------------------------------------------------------------------------- 1 | //! Reset and Control Unit 2 | 3 | use crate::pac::{rcu, RCU}; 4 | use crate::unit::*; 5 | use core::num::NonZeroU32; 6 | 7 | /// Extension trait that constrains the `RCU` peripheral 8 | pub trait RcuExt { 9 | /// Constrains the `RCU` peripheral so it plays nicely with the other abstractions 10 | fn constrain(self) -> Rcu; 11 | } 12 | 13 | impl RcuExt for RCU { 14 | fn constrain(self) -> Rcu { 15 | Rcu { 16 | apb1: APB1 { _ownership: () }, 17 | apb2: APB2 { _ownership: () }, 18 | ahb: AHB { _ownership: () }, 19 | cfg: CFG { _ownership: () }, 20 | bdctl: BDCTL { _ownership: () }, 21 | // rstsck: RSTSCK 22 | // dsv: DSV 23 | _todo: (), 24 | } 25 | } 26 | } 27 | 28 | /// Constrained RCU peripheral 29 | pub struct Rcu { 30 | // pub ahb: AHB, 31 | /// Advanced Pheripheral Bus 1 (APB1) registers 32 | /// 33 | /// Constrains `APB1EN` and `ABR1RST`. 34 | pub apb1: APB1, 35 | /// Advanced Pheripheral Bus 2 (APB2) registers 36 | /// 37 | /// Constrains `APB2EN` and `ABR2RST`. 38 | pub apb2: APB2, 39 | /// AHB registers 40 | /// 41 | /// Constrains `AHBEN` and `AHBRST`. 42 | /// 43 | /// Note: only `USBFS` AHB peripheral is able to be reset. (Section 5.3.11) 44 | pub ahb: AHB, 45 | /// Clock configuration registers 46 | /// 47 | /// Constrains `CFG0` and `CFG1` and `CTL0` 48 | pub cfg: CFG, 49 | // // todo: remove 50 | // pub clocks: Clocks, 51 | /// Backup domain control register 52 | /// 53 | /// Constrains `BDCTL`. 54 | pub bdctl: BDCTL, 55 | // ... 56 | _todo: (), 57 | } 58 | 59 | /// AMBA High-performance Bus (AHB) registers 60 | /// 61 | /// Constrains `AHBEN` and `AHBRST`. 62 | /// 63 | /// Note: only `USBFS` AHB peripheral is able to be reset. (Section 5.3.11) 64 | pub struct AHB { 65 | _ownership: (), 66 | } 67 | 68 | impl AHB { 69 | #[inline] 70 | pub(crate) fn en(&mut self) -> &rcu::AHBEN { 71 | unsafe { &(*RCU::ptr()).ahben } 72 | } 73 | // pub(crate) fn rst 74 | } 75 | 76 | /// Advanced Pheripheral Bus 1 (APB1) registers 77 | /// 78 | /// Constrains `APB1EN` and `ABR1RST`. 79 | pub struct APB1 { 80 | _ownership: (), 81 | } 82 | 83 | impl APB1 { 84 | #[inline] 85 | pub(crate) fn en(&mut self) -> &rcu::APB1EN { 86 | unsafe { &(*RCU::ptr()).apb1en } 87 | } 88 | 89 | #[inline] 90 | pub(crate) fn rst(&mut self) -> &rcu::APB1RST { 91 | unsafe { &(*RCU::ptr()).apb1rst } 92 | } 93 | } 94 | 95 | /// Advanced Pheripheral Bus 2 (APB2) registers 96 | /// 97 | /// Constrains `APB2EN` and `ABR2RST`. 98 | pub struct APB2 { 99 | _ownership: (), 100 | } 101 | 102 | impl APB2 { 103 | #[inline] 104 | pub(crate) fn en(&mut self) -> &rcu::APB2EN { 105 | unsafe { &(*RCU::ptr()).apb2en } 106 | } 107 | 108 | #[inline] 109 | pub(crate) fn rst(&mut self) -> &rcu::APB2RST { 110 | unsafe { &(*RCU::ptr()).apb2rst } 111 | } 112 | } 113 | 114 | /// Clock configuration registers 115 | /// 116 | /// Constrains `CFG0` and `CFG1` and `CTL0` 117 | pub struct CFG { 118 | _ownership: (), 119 | } 120 | 121 | impl CFG { 122 | #[inline] 123 | pub(crate) fn cfg0(&mut self) -> &rcu::CFG0 { 124 | unsafe { &(*RCU::ptr()).cfg0 } 125 | } 126 | #[inline] 127 | pub(crate) fn cfg1(&mut self) -> &rcu::CFG1 { 128 | unsafe { &(*RCU::ptr()).cfg1 } 129 | } 130 | #[inline] 131 | pub(crate) fn ctl(&mut self) -> &rcu::CTL { 132 | unsafe { &(*RCU::ptr()).ctl } 133 | } 134 | } 135 | 136 | // read the registers and store in struct, rather than hardcode defaults 137 | // actually freeze these somehow... 138 | // done(luojia65 2020-2-29) // TODO: Verify the result 139 | /// Frozen clock freqencies 140 | /// 141 | /// The existence of this value indicates that the core clock 142 | /// configuration can no longer be changed 143 | #[derive(Clone, Copy)] 144 | pub struct Clocks { 145 | ck_sys: Hertz, 146 | ahb_shr: u8, // [0, 9] -> [1, 512] 147 | apb1_shr: u8, // [0, 4] -> [2, 16] 148 | apb2_shr: u8, // [0, 4] -> [2, 16] 149 | adc_div: u8, // {2, 4, 6, 8, 12, 16} 150 | usb_valid: bool, 151 | } 152 | 153 | impl Clocks { 154 | /// Returns the frequency of the system clock 155 | pub const fn ck_sys(&self) -> Hertz { 156 | self.ck_sys 157 | } 158 | 159 | /// Returns the frequency of the AHB clock 160 | pub const fn ck_ahb(&self) -> Hertz { 161 | Hertz(self.ck_sys.0 >> self.ahb_shr) 162 | } 163 | 164 | /// Returns the freqency of the Advanced Peripheral Bus 1 clock 165 | pub const fn ck_apb1(&self) -> Hertz { 166 | Hertz(self.ck_sys.0 >> (self.ahb_shr + self.apb1_shr)) 167 | } 168 | 169 | /// Returns the freqency of the Advanced Peripheral Bus 2 clock 170 | pub const fn ck_apb2(&self) -> Hertz { 171 | Hertz(self.ck_sys.0 >> (self.ahb_shr + self.apb2_shr)) 172 | } 173 | 174 | /// Returns the freqency of the CK_TIMERx clock 175 | pub const fn ck_timerx(&self) -> Hertz { 176 | // Hertz(self.ck_sys.0 >> (self.ahb_shr + self.apb2_shr 177 | // - if self.apb2_shr == 0 { 0 } else { 1 })) 178 | Hertz( 179 | self.ck_sys.0 180 | >> (self.ahb_shr + self.apb2_shr - [0, 1, 1, 1, 1][self.apb2_shr as usize]), 181 | ) 182 | } 183 | 184 | /// Returns the freqency of the CK_ADCx clock 185 | pub const fn ck_adc(&self) -> Hertz { 186 | Hertz((self.ck_sys.0 >> (self.ahb_shr + self.apb2_shr)) / self.adc_div as u32) 187 | } 188 | 189 | /// Returns whether the CK_USBFS clock frequency is valid for the USB peripheral 190 | pub const fn ck_usbfs_valid(&self) -> bool { 191 | self.usb_valid 192 | } 193 | } 194 | 195 | /// Strict clock configurator 196 | /// 197 | /// This configurator only accepts strictly accurate value. If all available frequency 198 | /// values after configurated does not strictly equal to the desired value, the `freeze` 199 | /// function panics. Users must be careful to ensure that the output frequency values 200 | /// can be strictly configurated into using input frequency values and internal clock 201 | /// frequencies. 202 | /// 203 | /// If you need to get most precise frequenct possible (other than the stictly accutare 204 | /// value only), use configurator `Precise` instead. 205 | /// 206 | /// For example if 49.60MHz and 50.20MHz are able to be configurated prefectly, input 207 | /// 50MHz into `Strict` would result in a panic when performing `freeze`; however input 208 | /// same 50MHz into `Precise` it would not panic, but would set and freeze into 209 | /// 50.20MHz as the frequency error is smallest. 210 | #[derive(Default)] 211 | pub struct Strict { 212 | hxtal: Option, 213 | target_ck_sys: Option, 214 | target_ck_i2s: Option, 215 | target_ck_ahb: Option, 216 | target_ck_apb1: Option, 217 | target_ck_apb2: Option, 218 | target_ck_adc: Option, 219 | } 220 | 221 | impl Strict { 222 | /// Create a configurator 223 | pub fn new() -> Self { 224 | Strict { 225 | hxtal: None, 226 | target_ck_sys: None, 227 | target_ck_i2s: None, 228 | target_ck_ahb: None, 229 | target_ck_apb1: None, 230 | target_ck_apb2: None, 231 | target_ck_adc: None, 232 | } 233 | } 234 | 235 | /// Prefer use HXTAL (external oscillator) as the clock source. 236 | pub fn use_hxtal(mut self, freq: impl Into) -> Self { 237 | let freq_hz = freq.into().0; 238 | assert!(freq_hz >= 4_000_000 && freq_hz <= 32_000_000); // Figure 5.2, the Manual 239 | self.hxtal = NonZeroU32::new(freq_hz); 240 | self 241 | } 242 | 243 | /// Sets the desired frequency for the CK_SYS clock 244 | pub fn ck_sys(mut self, freq: impl Into) -> Self { 245 | let freq_hz = freq.into().0; 246 | assert!(freq_hz <= 108_000_000); // Figure 5.2, the Manual 247 | self.target_ck_sys = NonZeroU32::new(freq_hz); 248 | self 249 | } 250 | 251 | #[doc(hidden)] // todo 252 | /// Sets the desired frequency for the CK_I2S clock 253 | pub fn ck_i2s(mut self, freq: impl Into) -> Self { 254 | let freq_hz = freq.into().0; 255 | self.target_ck_i2s = NonZeroU32::new(freq_hz); 256 | self 257 | } 258 | 259 | /// Sets the desired frequency for the CK_AHB clock 260 | pub fn ck_ahb(mut self, freq: impl Into) -> Self { 261 | let freq_hz = freq.into().0; 262 | assert!(freq_hz <= 108_000_000); // Figure 5.2, the Manual 263 | self.target_ck_ahb = NonZeroU32::new(freq_hz); 264 | self 265 | } 266 | 267 | /// Sets the desired frequency for the CK_APB1 clock 268 | pub fn ck_apb1(mut self, freq: impl Into) -> Self { 269 | let freq_hz = freq.into().0; 270 | assert!(freq_hz <= 54_000_000); // Figure 5.2, the Manual 271 | self.target_ck_apb1 = NonZeroU32::new(freq_hz); 272 | self 273 | } 274 | 275 | /// Sets the desired frequency for the CK_APB2 clock 276 | pub fn ck_apb2(mut self, freq: impl Into) -> Self { 277 | let freq_hz = freq.into().0; 278 | assert!(freq_hz <= 108_000_000); // Figure 5.2, the Manual 279 | self.target_ck_apb2 = NonZeroU32::new(freq_hz); 280 | self 281 | } 282 | 283 | /// Sets the desired frequency for the CK_ADCx clock 284 | pub fn ck_adc(mut self, freq: impl Into) -> Self { 285 | let freq_hz = freq.into().0; 286 | assert!(freq_hz <= 14_000_000); // Figure 5.2, the Manual 287 | self.target_ck_adc = NonZeroU32::new(freq_hz); 288 | self 289 | } 290 | 291 | /// Calculate and balance clock registers to configure into the given clock value. 292 | /// If accurate value is not possible, this function panics. 293 | /// 294 | /// Be aware that Rust's panic is sometimes not obvious on embedded devices; if your 295 | /// program didn't execute as expected, or the `pc` is pointing to somewhere weird 296 | /// (usually `abort: j abort`), it's likely that this function have panicked. 297 | /// Breakpoint on `rust_begin_unwind` may help debugging. 298 | /// 299 | /// # Panics 300 | /// 301 | /// If strictly accurate value of given `ck_sys` etc. is not reachable, this function 302 | /// panics. 303 | pub fn freeze(self, cfg: &mut CFG) -> Clocks { 304 | // todo: this function is much too complex; consider split into independent parts 305 | const IRC8M: u32 = 8_000_000; 306 | let mut usb_valid = false; 307 | let target_ck_sys = self.target_ck_sys.map(|f| f.get()).unwrap_or(IRC8M); 308 | let target_ck_ahb = self.target_ck_ahb.map(|f| f.get()).unwrap_or(target_ck_sys); 309 | let (scs, use_pll) = match (self.hxtal, target_ck_sys) { 310 | (Some(hxtal), sys) if hxtal.get() == sys => (0b01, false), 311 | (None, sys) if IRC8M == sys => (0b00, false), 312 | _ => (0b10, true), 313 | }; 314 | let pllmf = if use_pll { 315 | if let Some(hxtal) = self.hxtal { 316 | let hxtal = hxtal.get(); 317 | let calc_pllmf = || { 318 | for div in 1..=16 { 319 | if target_ck_sys == hxtal * 13 / 2 { 320 | return 0b01101; // 6.5 321 | } 322 | let mul = target_ck_sys / (div * hxtal); 323 | if mul < 2 || mul > 32 || mul == 15 { 324 | continue; 325 | } 326 | let out_ck_sys = hxtal * mul / div; 327 | if out_ck_sys == target_ck_sys { 328 | return if mul <= 14 { mul - 2 } else { mul - 1 }; 329 | } 330 | } 331 | panic!("invalid frequency") 332 | }; 333 | calc_pllmf() as u8 334 | } else { 335 | // does not use HXTAL 336 | let pllsel0_src = IRC8M / 2; 337 | let mul_pllmf = target_ck_sys / pllsel0_src; 338 | // pllmf: 00000 => 2, 00001 => 3, ..., 01100 => 14; 01101 => 6.5; 339 | // 01111 => 16, 10000 => 17, ..., 11111 => 32; 340 | // may use 6.5 here 341 | let mul_pllmf = u32::max(2, u32::min(mul_pllmf, 32)); 342 | if target_ck_sys == mul_pllmf * pllsel0_src { 343 | // use 2..=14 | 16..=32 344 | (if mul_pllmf <= 14 { 345 | mul_pllmf - 2 346 | } else { 347 | mul_pllmf - 1 348 | }) as u8 349 | } else if target_ck_sys == pllsel0_src * 13 / 2 { 350 | 0b01101 as u8 // use special 6.5 multiplier 351 | } else { 352 | panic!("invalid frequency") 353 | } 354 | } 355 | } else { 356 | 0 // placeholder, not use_pll 357 | }; 358 | let (ahbpsc, ahb_shr) = { 359 | // 0xxx: /1; 1000: /2; 1001: /4; ... 1111: /512. (skip /32) 360 | let mut ahb_shr = 0; // log2(1) 361 | let mut ans = 0b0111u8; 362 | let mut target_freq = target_ck_ahb; 363 | while ahb_shr <= 9 { 364 | // log2(512) 365 | if ahb_shr != 5 && target_freq == target_ck_sys { 366 | break; 367 | } 368 | target_freq *= 2; 369 | ahb_shr += 1; 370 | if ahb_shr != 5 { 371 | // log2(32) 372 | ans += 1; 373 | } 374 | } 375 | if ans > 0b1111 { 376 | panic!("invalid frequency") 377 | } 378 | (ans, ahb_shr) 379 | }; 380 | let calc_psc_apbx = |target_ck_apbx: u32| { 381 | let mut ans = 0b011u8; 382 | let mut target_freq = target_ck_apbx; 383 | while ans <= 0b111 { 384 | if target_freq == target_ck_ahb { 385 | break; 386 | } 387 | target_freq *= 2; 388 | ans += 1; 389 | } 390 | if ans > 0b111 { 391 | panic!("invalid frequency") 392 | }; 393 | ans 394 | }; 395 | let target_ck_apb1 = self 396 | .target_ck_apb1 397 | .map(|f| f.get()) 398 | .unwrap_or(target_ck_ahb / 2); 399 | let apb1psc = calc_psc_apbx(target_ck_apb1); 400 | let target_ck_apb2 = self 401 | .target_ck_apb2 402 | .map(|f| f.get()) 403 | .unwrap_or(target_ck_ahb); 404 | let apb2psc = calc_psc_apbx(target_ck_apb2); 405 | let target_ck_adc = self 406 | .target_ck_adc 407 | .map(|f| f.get()) 408 | .unwrap_or(target_ck_apb2 / 8); 409 | let adcpsc = if target_ck_adc * 2 == target_ck_apb2 { 410 | 0b000 /* alias: 0b100 */ 411 | } else if target_ck_adc * 4 == target_ck_apb2 { 412 | 0b001 413 | } else if target_ck_adc * 6 == target_ck_apb2 { 414 | 0b010 415 | } else if target_ck_adc * 8 == target_ck_apb2 { 416 | 0b011 /* alias: 0b110 */ 417 | } else if target_ck_adc * 12 == target_ck_apb2 { 418 | 0b101 419 | } else if target_ck_adc * 16 == target_ck_apb2 { 420 | 0b111 421 | } else { 422 | panic!("invalid freqency") 423 | }; 424 | // 1. enable IRC8M 425 | if self.hxtal.is_none() { 426 | // enable IRC8M 427 | cfg.ctl().modify(|_, w| w.irc8men().set_bit()); 428 | // Wait for oscillator to stabilize 429 | while cfg.ctl().read().irc8mstb().bit_is_clear() {} 430 | } 431 | // 2. enable hxtal 432 | if self.hxtal.is_some() { 433 | // enable hxtal 434 | cfg.ctl().modify(|_, w| w.hxtalen().set_bit()); 435 | // wait before stable 436 | while cfg.ctl().read().hxtalstb().bit_is_clear() {} 437 | } 438 | // 3. enable pll 439 | if use_pll { 440 | cfg.cfg0().modify(|_, w| unsafe { 441 | // Configure PLL input selector 442 | w.pllsel().bit(use_pll); 443 | // Configure PLL multiplier 444 | w.pllmf_4().bit(pllmf & 0x10 != 0); 445 | w.pllmf_3_0().bits(pllmf & 0xf) 446 | }); 447 | // Enable PLL 448 | cfg.ctl().modify(|_, w| w.pllen().set_bit()); 449 | // Wait for PLL to stabilize 450 | while cfg.ctl().read().pllstb().bit_is_clear() {} 451 | } else { 452 | // or we disable PLL 453 | cfg.ctl().modify(|_, w| w.pllen().clear_bit()); 454 | } 455 | // 4. check SCS selector 456 | cfg.cfg0().modify(|_, w| unsafe { w.scs().bits(scs) }); 457 | // 5. check and enable usb 458 | if self.hxtal.is_some() { 459 | let ck_pll = target_ck_sys; 460 | let (usb_freq_okay, usbfspsc) = match ck_pll { 461 | 48_000_000 => (true, 0b01), // ck_pll / 1 462 | 72_000_000 => (true, 0b00), // ck_pll / 1.5 463 | 96_000_000 => (true, 0b11), // ck_pll / 2 464 | // 0b10 (ck_pll / 2.5) is impossible in this algorithm 465 | _ => (false, 0), 466 | }; 467 | usb_valid = usb_freq_okay; 468 | // adjust USB prescaler 469 | cfg.cfg0() 470 | .modify(|_, w| unsafe { w.usbfspsc().bits(usbfspsc) }); 471 | } 472 | // todo: verify if three switches in one modify is okay 473 | cfg.cfg0().modify(|_, w| unsafe { 474 | // 6. adjust AHB and APB clocks 475 | w.ahbpsc().bits(ahbpsc); 476 | w.apb1psc().bits(apb1psc); 477 | w.apb2psc().bits(apb2psc); 478 | // 7. adjust ADC clocks 479 | w.adcpsc_2().bit(adcpsc & 0b100 != 0); 480 | w.adcpsc_1_0().bits(adcpsc & 0b11) 481 | }); 482 | Clocks { 483 | ck_sys: Hertz(target_ck_sys), 484 | ahb_shr, 485 | apb1_shr: apb1psc - 0b011, 486 | apb2_shr: apb2psc - 0b011, 487 | adc_div: (target_ck_apb2 / target_ck_adc) as u8, 488 | usb_valid, 489 | } 490 | } 491 | } 492 | 493 | /// (TODO) Precise clock configurator 494 | /// 495 | /// This configurator would offer config to get the most precise output value possible 496 | /// using input values. Errors between desired and actual output would be acceptible; 497 | /// it would be minimized by the algorithm, thus the output would be as precise as 498 | /// possible. 499 | pub struct Precise { 500 | _todo: (), 501 | } 502 | 503 | /// Opaque `BDCTL` register 504 | pub struct BDCTL { 505 | _ownership: (), 506 | } 507 | 508 | impl BDCTL { 509 | #[inline] 510 | pub(crate) fn bdctl(&mut self) -> &rcu::BDCTL { 511 | unsafe { &(*RCU::ptr()).bdctl } 512 | } 513 | } 514 | -------------------------------------------------------------------------------- /src/serial.rs: -------------------------------------------------------------------------------- 1 | //! (TODO) Serial Communication (USART) 2 | 3 | #![macro_use] 4 | 5 | use crate::pac; 6 | use core::fmt::Write; 7 | use pac::{GPIOA, RCU, USART0}; 8 | 9 | //TODO - use the APB/RCU/GPIO primitives in this crate, rather than unsafe memory poking! 10 | 11 | // yay, math functions arn't implemented in core! 12 | fn round(n: f32) -> f32 { 13 | let int_part: i32 = n as i32; //truncate 14 | let fraction_part: f32 = n - int_part as f32; 15 | if fraction_part >= 0.5 { 16 | (int_part + 1) as f32 17 | } else { 18 | int_part as f32 19 | } 20 | } 21 | 22 | fn init_usart() { 23 | // enable the peripheral clock 24 | unsafe { 25 | (*USART0::ptr()).ctl0.modify(|r, w| { 26 | w.bits(r.bits()).uen().clear_bit() //disable while being configured TODO could wait for TC=1? 27 | }); 28 | 29 | (*RCU::ptr()).apb2en.modify(|r, w| { 30 | w.bits(r.bits()) 31 | .usart0en() 32 | .set_bit() 33 | .afen() 34 | .set_bit() 35 | .paen() 36 | .set_bit() 37 | }); 38 | 39 | (*GPIOA::ptr()).ctl1.modify(|r, w| { 40 | w.bits(r.bits()) 41 | .md9() 42 | .bits(0b11) //output, 50mhz 43 | .ctl9() 44 | .bits(0b10) //alternate push pull 45 | .md10() 46 | .bits(0b00) //input 47 | .ctl10() 48 | .bits(0b01) //floating 49 | }); 50 | 51 | // for 9600 baud rate @ 8mhz clock, USARTDIV = CLK/(16*baud) 52 | // USARTDIV = 8000000/(16*9600) = 52.0833333 53 | // integer part = 52, fractional ~= 1/16 -> intdiv=53, fradiv=1 54 | // can calculate automatically given clk and baud, but note that 55 | // if fradiv=16, then intdiv++; fradiv=0; 56 | 57 | let _baud = 9600f32; 58 | let clk_freq = 8_000_000f32; 59 | let usart_div = clk_freq / (16f32 * 9600f32); 60 | let mut int_div = usart_div as i32; //note that trunc(), fract(), rount() are not implemented in core... 61 | let mut fra_div = round(16.0 * (usart_div - int_div as f32)) as i32; 62 | if fra_div == 16 { 63 | int_div += 1; 64 | fra_div = 0; 65 | } 66 | 67 | (*USART0::ptr()).baud.modify(|r, w| { 68 | w.bits(r.bits()) 69 | .intdiv() 70 | .bits(int_div as u16) 71 | .fradiv() 72 | .bits(fra_div as u8) 73 | }); 74 | 75 | (*USART0::ptr()).ctl2.modify(|r, w| { 76 | w.bits(r.bits()) 77 | .ctsen() 78 | .clear_bit() //enable CTS hardware flow control 79 | .rtsen() 80 | .clear_bit() //enable RTS hardware flow control 81 | }); 82 | 83 | (*USART0::ptr()).ctl1.modify(|r, w| { 84 | w.bits(r.bits()) 85 | .stb() 86 | .bits(0b00) //set # of stop bits = 1 87 | .cken() 88 | .clear_bit() 89 | }); 90 | 91 | (*USART0::ptr()).ctl0.modify(|r, w| { 92 | w.bits(r.bits()) 93 | .wl() 94 | .clear_bit() //set word size to 8 95 | .ten() 96 | .set_bit() //enable tx 97 | .ren() 98 | .set_bit() //enable rx 99 | .pcen() 100 | .clear_bit() //no parity check function plz 101 | .pm() 102 | .clear_bit() //0=even parity 1=odd parity 103 | .uen() 104 | .set_bit() //enable the uart, yay! 105 | }); 106 | } 107 | } 108 | 109 | /// todo: more proper name 110 | #[doc(hidden)] // experimental, not for practical use 111 | pub struct SerialWrapper; 112 | 113 | impl core::fmt::Write for SerialWrapper { 114 | fn write_str(&mut self, s: &str) -> core::fmt::Result { 115 | for &byte in s.as_bytes() { 116 | unsafe { 117 | (*USART0::ptr()).data.write(|w| w.data().bits(byte.into())); 118 | while (*USART0::ptr()).stat.read().tbe().bit_is_clear() {} 119 | } 120 | } 121 | Ok(()) 122 | } 123 | } 124 | 125 | // hold things in a static place 126 | static mut STDOUT: Option = None; 127 | 128 | #[allow(unused_variables)] 129 | #[doc(hidden)] // experimental, not for practical use 130 | pub fn init_stdout(uart: USART0) { 131 | init_usart(); 132 | unsafe { 133 | STDOUT.replace(SerialWrapper {}); 134 | } 135 | } 136 | 137 | /// Writes string to stdout 138 | #[doc(hidden)] // experimental, not for practical use 139 | pub fn write_str(s: &str) { 140 | unsafe { 141 | if let Some(stdout) = STDOUT.as_mut() { 142 | let _ = stdout.write_str(s); 143 | } else { 144 | panic!("couldn't get stdout!"); 145 | } 146 | } 147 | } 148 | 149 | /// Writes formatted string to stdout 150 | #[doc(hidden)] // experimental, not for practical use 151 | pub fn write_fmt(args: core::fmt::Arguments) { 152 | unsafe { 153 | if let Some(stdout) = STDOUT.as_mut() { 154 | let _ = stdout.write_fmt(args); 155 | } else { 156 | panic!("couldn't get stdout!"); 157 | } 158 | } 159 | } 160 | 161 | /// Macro for printing to the serial standard output 162 | #[doc(hidden)] // experimental 163 | #[macro_export] 164 | macro_rules! sprint { 165 | ($s:expr) => { 166 | crate::serial::write_str($s) 167 | }; 168 | ($($tt:tt)*) => { 169 | crate::serial::write_fmt(format_args!($($tt)*)) 170 | }; 171 | 172 | } 173 | 174 | // --- // 175 | 176 | // use crate::pac::USART0; 177 | use crate::afio::PCF0; 178 | use crate::gpio::gpioa::{PA10, PA11, PA12, PA8, PA9}; 179 | use crate::gpio::{Alternate, Floating, Input, PushPull}; 180 | use crate::rcu::{Clocks, APB2}; 181 | use crate::unit::{Bps, U32Ext}; 182 | 183 | /// Serial config 184 | pub struct Config { 185 | pub baudrate: Bps, 186 | pub parity: Parity, 187 | pub stop_bits: StopBits, 188 | // pub flow_control 189 | } 190 | 191 | impl Default for Config { 192 | fn default() -> Self { 193 | Config { 194 | baudrate: 115200u32.bps(), 195 | parity: Parity::ParityNone, 196 | stop_bits: StopBits::STOP1, 197 | } 198 | } 199 | } 200 | 201 | impl Config { 202 | pub fn baudrate(mut self, baudrate: Bps) -> Config { 203 | self.baudrate = baudrate; 204 | self 205 | } 206 | 207 | pub fn parity(mut self, parity: Parity) -> Config { 208 | self.parity = parity; 209 | self 210 | } 211 | 212 | pub fn stop_bits(mut self, stop_bits: StopBits) -> Config { 213 | self.stop_bits = stop_bits; 214 | self 215 | } 216 | } 217 | 218 | /// Serial parity 219 | pub enum Parity { 220 | /// Disable parity check 221 | ParityNone, 222 | /// Enable even parity check 223 | ParityEven, 224 | /// Enable odd parity check 225 | ParityOdd, 226 | } 227 | 228 | impl Parity { 229 | // (word_length, parity_enable, parity_config) 230 | // word_length: 0 => 8 bits; 1 => 9 bits 231 | // parity_enable: 0 => disable; 1 => enable 232 | // parity_config: 0 => odd; 1 => even 233 | #[inline] 234 | fn config(&self) -> (bool, bool, bool) { 235 | match *self { 236 | Parity::ParityNone => (false, false, false), 237 | Parity::ParityEven => (true, true, false), 238 | Parity::ParityOdd => (true, true, true), 239 | } 240 | } 241 | } 242 | 243 | /// Serial stop bits 244 | pub enum StopBits { 245 | /// 1 stop bit 246 | STOP1, 247 | /// 0.5 stop bit 248 | STOP0P5, 249 | /// 2 stop bits 250 | STOP2, 251 | /// 1.5 stop bit 252 | STOP1P5, 253 | } 254 | 255 | impl StopBits { 256 | #[inline] 257 | fn config(&self) -> u8 { 258 | match *self { 259 | StopBits::STOP1 => 0b00, 260 | StopBits::STOP0P5 => 0b01, 261 | StopBits::STOP2 => 0b10, 262 | StopBits::STOP1P5 => 0b11, 263 | } 264 | } 265 | } 266 | 267 | /// Serial abstraction 268 | pub struct Serial { 269 | usart: USART, 270 | pins: PINS, 271 | } 272 | 273 | impl Serial { 274 | /// Power on and create serial instance 275 | pub fn usart0( 276 | usart0: USART0, 277 | pins: PINS, 278 | pcf0: &mut PCF0, 279 | config: Config, 280 | clocks: Clocks, 281 | apb2: &mut APB2, 282 | ) -> Self 283 | where 284 | PINS: Bundle, 285 | { 286 | // calculate baudrate divisor fractor 287 | let baud_div = { 288 | // use apb2 or apb1 may vary 289 | // round the value to get most accurate one (without float point) 290 | let baud_div = (clocks.ck_apb2().0 + config.baudrate.0 / 2) / config.baudrate.0; 291 | assert!(baud_div >= 0x0010 && baud_div <= 0xFFFF, "impossible baudrate"); 292 | baud_div 293 | }; 294 | // get parity config 295 | let (wl, pcen, pm) = config.parity.config(); 296 | // get stop bit config 297 | let stb = config.stop_bits.config(); 298 | riscv::interrupt::free(|_| { 299 | // enable and reset usart peripheral 300 | apb2.en().modify(|_, w| w.usart0en().set_bit()); 301 | apb2.rst().modify(|_, w| w.usart0rst().set_bit()); 302 | apb2.rst().modify(|_, w| w.usart0rst().clear_bit()); 303 | // set serial remap 304 | pcf0.pcf0() 305 | .modify(|_, w| w.usart0_remap().bit(PINS::REMAP == 1)); 306 | // does not enable DMA in this section; DMA is enabled separately 307 | // set baudrate 308 | usart0 309 | .baud 310 | .write(|w| unsafe { w.bits(baud_div) }); 311 | // configure stop bits 312 | usart0.ctl1.modify(|_, w| unsafe { w.stb().bits(stb) }); 313 | usart0.ctl0.modify(|_, w| { 314 | // set parity check settings 315 | w.wl().bit(wl).pcen().bit(pcen).pm().bit(pm); 316 | // enable the peripheral 317 | // todo: split receive and transmit 318 | w.uen().set_bit().ren().set_bit().ten().set_bit() 319 | }); 320 | }); 321 | Serial { 322 | usart: usart0, 323 | pins, 324 | } 325 | } 326 | 327 | /// Power down and return ownership of owned registers 328 | pub fn release(self, apb2: &mut APB2) -> (USART0, PINS) { 329 | // disable the peripheral 330 | self.usart 331 | .ctl0 332 | .modify(|_, w| w.uen().clear_bit().ren().clear_bit().ten().clear_bit()); 333 | // disable the clock 334 | apb2.en().modify(|_, w| w.usart0en().clear_bit()); 335 | 336 | // return the ownership 337 | (self.usart, self.pins) 338 | } 339 | } 340 | 341 | /// Serial error 342 | #[derive(Debug)] 343 | pub enum Error { 344 | /// New data frame received while read buffer is not empty. (ORERR) 345 | Overrun, 346 | /// Noise detected on the RX pin when receiving a frame. (NERR) 347 | Noise, 348 | /// RX pin is detected low during the stop bits of a receive frame. (FERR) 349 | Framing, 350 | /// Parity bit of the receive frame does not match the expected parity value. (PERR) 351 | Parity, 352 | } 353 | 354 | impl embedded_hal::serial::Read for Serial { 355 | type Error = Error; 356 | 357 | fn try_read(&mut self) -> nb::Result { 358 | let stat = self.usart.stat.read(); 359 | // the chip has already filled data buffer with input data 360 | // check for errors present 361 | let err = if stat.orerr().bit_is_set() { 362 | Some(Error::Overrun) 363 | } else if stat.nerr().bit_is_set() { 364 | Some(Error::Noise) 365 | } else if stat.ferr().bit_is_set() { 366 | Some(Error::Framing) 367 | } else if stat.perr().bit_is_set() { 368 | Some(Error::Parity) 369 | } else { 370 | None 371 | }; 372 | 373 | if let Some(err) = err { 374 | // error occurred, no data is read. clean the data buffer and error flags 375 | // note(unsafe): stateless register read 376 | unsafe { 377 | core::ptr::read_volatile(&self.usart.stat as *const _ as *const _); 378 | core::ptr::read_volatile(&self.usart.data as *const _ as *const _); 379 | } 380 | // returns error; no data is returned 381 | Err(nb::Error::Other(err)) 382 | } else { 383 | // if a byte is available, return the byte; or the upstream should wait 384 | // until a byte is ready 385 | if stat.rbne().bit_is_set() { 386 | // read buffer non empty, return this byte 387 | Ok(unsafe { core::ptr::read_volatile(&self.usart.data as *const _ as *const _) }) 388 | } else { 389 | // byte is not ready 390 | Err(nb::Error::WouldBlock) 391 | } 392 | } 393 | } 394 | } 395 | 396 | impl embedded_hal::serial::Write for Serial { 397 | type Error = core::convert::Infallible; // ! 398 | 399 | fn try_write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { 400 | let stat = self.usart.stat.read(); 401 | 402 | if stat.tbe().bit_is_set() { 403 | // NOTE(unsafe) atomic write to stateless register 404 | // impossible using PAC only to write u8 value 405 | unsafe { 406 | // compiles into `lui a?, %hi(USART_DATA); sb a??, %lo(USART_DATA)(a?)` 407 | core::ptr::write_volatile(&self.usart.data as *const _ as *mut _, byte) 408 | } 409 | Ok(()) 410 | } else { 411 | // upstream should wait until end of transmit 412 | Err(nb::Error::WouldBlock) 413 | } 414 | } 415 | 416 | fn try_flush(&mut self) -> nb::Result<(), Self::Error> { 417 | // if translate completed, do not wait 418 | if self.usart.stat.read().tc().bit_is_set() { 419 | Ok(()) 420 | } else { 421 | // otherwise upstream should wait 422 | Err(nb::Error::WouldBlock) 423 | } 424 | } 425 | } 426 | 427 | impl core::fmt::Write for Serial { 428 | fn write_str(&mut self, s: &str) -> core::fmt::Result { 429 | use embedded_hal::serial::Write; 430 | s.as_bytes() 431 | .iter() 432 | .try_for_each(|c| nb::block!(self.try_write(*c))) 433 | .map_err(|_| core::fmt::Error) // no write error is possible 434 | } 435 | } 436 | 437 | // /// IrDA Config 438 | // pub struct IrConfig { 439 | // /// If IrDA low power mode should be enabled 440 | // pub low_power: bool, 441 | // /// Serial baudrate 442 | // pub baudrate: Bps, 443 | // /// Serial parity 444 | // pub parity: Parity, 445 | // } 446 | 447 | // /// Infrared Data Association (IrDA) communication abstraction 448 | // pub struct IrDA { 449 | // usart: USART, 450 | // pins: PINS, 451 | // } 452 | 453 | // impl IrDA { 454 | // /// Power on and create IrDA instance 455 | // #[doc(hidden)] 456 | // pub fn usart0( 457 | // usart0: USART0, 458 | // pins: PINS, 459 | // pcf0: &mut PCF0, 460 | // clocks: Clocks, 461 | // apb2: &mut APB2, 462 | // ) -> Self 463 | // where 464 | // PINS: Bundle, 465 | // { 466 | // todo!("actual power up process"); 467 | // Self { 468 | // usart: usart0, 469 | // pins, 470 | // } 471 | // } 472 | 473 | // /// Power down and return ownership of owned registers 474 | // #[doc(hidden)] 475 | // pub fn release(self) -> (USART0, PINS) { 476 | // todo!("actual power down"); 477 | // (self.usart, self.pins) 478 | // } 479 | // } 480 | 481 | pub trait Pins { 482 | // private::Sealed; internal use only 483 | #[doc(hidden)] 484 | const REMAP: u8; 485 | 486 | type TX; 487 | 488 | type RX; 489 | 490 | type RTS; 491 | 492 | type CTS; 493 | 494 | type CK; 495 | } 496 | 497 | impl Pins for USART0 { 498 | const REMAP: u8 = 0; 499 | 500 | type TX = PA9>; 501 | type RX = PA10>; 502 | // todo: mode of PA12, PA11 and P8 503 | type RTS = PA12>; 504 | type CTS = PA11>; 505 | type CK = PA8>; 506 | } 507 | 508 | // TX, RX, RTS, CTS 509 | 510 | pub trait Bundle { 511 | #[doc(hidden)] 512 | const REMAP: u8 = USART::REMAP; 513 | #[doc(hidden)] 514 | fn enable_ctl0(); 515 | #[doc(hidden)] 516 | fn enable_ctl2(); 517 | } 518 | 519 | impl Bundle for (USART::TX, USART::RX) { 520 | #[inline] 521 | fn enable_ctl0() { 522 | // w.ren().set_bit().ten().set_bit() 523 | } 524 | #[inline] 525 | fn enable_ctl2() { 526 | // w.rtsen().clear_bit().ctsen().clear_bit() 527 | } 528 | } 529 | 530 | impl Bundle for (USART::TX, USART::RX, USART::RTS, USART::CTS) { 531 | #[inline] 532 | fn enable_ctl0() { 533 | // w.ren().set_bit().ten().set_bit() 534 | } 535 | #[inline] 536 | fn enable_ctl2() { 537 | // w.rtsen().set_bit().ctsen().set_bit() 538 | } 539 | } 540 | 541 | //todo 542 | -------------------------------------------------------------------------------- /src/spi.rs: -------------------------------------------------------------------------------- 1 | //! Serial Peripheral Interface (SPI) bus 2 | use crate::gpio::gpioa::*; 3 | use crate::gpio::gpiob::*; 4 | use crate::gpio::{Alternate, Floating, Input, Output, PushPull}; 5 | use crate::pac::{SPI0, SPI1, SPI2}; 6 | use crate::rcu::{Clocks, APB1, APB2}; 7 | use crate::unit::Hertz; 8 | use embedded_hal::blocking::spi::*; 9 | use embedded_hal::spi::{FullDuplex, Mode, Phase, Polarity}; 10 | 11 | /// SPI error 12 | #[derive(Debug)] 13 | pub enum Error { 14 | /// Configuration fault error 15 | ConfigFault, 16 | /// Rx overrun error 17 | ReceiveOverrun, 18 | /// TI mdode format error 19 | Format, 20 | /// CRC error 21 | Crc, 22 | } 23 | 24 | /// SPI object that can be used to make FullDuplex SPI peripherals 25 | pub struct Spi { 26 | spi: SPI, 27 | pins: PINS, 28 | } 29 | 30 | #[doc(hidden)] 31 | mod private { 32 | pub trait Sealed {} 33 | } 34 | 35 | pub trait SckPin: private::Sealed {} 36 | pub trait MisoPin: private::Sealed {} 37 | pub trait MosiPin: private::Sealed {} 38 | pub trait NssPin: private::Sealed {} 39 | 40 | macro_rules! pins { 41 | ($spi:ident, SCK: [$($sck:ident),*], MISO: [$($miso:ident),*], MOSI: [$($mosi:ident),*], NSS: [$($nss:ident),*]) => { 42 | $( 43 | impl private::Sealed for $sck> {} 44 | impl SckPin<$spi> for $sck> {} 45 | )* 46 | $( 47 | impl private::Sealed for $miso> {} 48 | impl MisoPin<$spi> for $miso> {} 49 | )* 50 | $( 51 | impl private::Sealed for $mosi> {} 52 | impl MosiPin<$spi> for $mosi> {} 53 | )* 54 | $( 55 | impl private::Sealed for $nss> {} 56 | impl NssPin<$spi> for $nss> {} 57 | impl private::Sealed for $nss> {} 58 | impl NssPin<$spi> for $nss> {} 59 | )* 60 | } 61 | 62 | } 63 | 64 | macro_rules! spi { 65 | ($($SPIX:ident: ($spiX:ident, $APBX:ident, $spiXen:ident, $spiXrst:ident, $pclkX:ident),)+) => { 66 | $( 67 | impl Spi<$SPIX, (SCK, MISO, MOSI, NSS)> { 68 | /// Configures the SPI peripheral to operate in full duplex master mode 69 | pub fn $spiX( 70 | spi: $SPIX, 71 | pins: (SCK, MISO, MOSI, NSS), 72 | mode: Mode, 73 | freq: F, 74 | clocks: Clocks, 75 | apb: &mut $APBX, 76 | ) -> Self 77 | where 78 | F: Into, 79 | SCK: SckPin<$SPIX>, 80 | MISO: MisoPin<$SPIX>, 81 | MOSI: MosiPin<$SPIX>, 82 | NSS: NssPin<$SPIX> 83 | { 84 | 85 | let prescaler_bits = match clocks.$pclkX().0 / freq.into().0 { 86 | 0 => unreachable!(), 87 | 2..=2 => 0b000, 88 | 4..=5 => 0b001, 89 | 8..=11 => 0b010, 90 | 16..=23 => 0b011, 91 | 32..=39 => 0b100, 92 | 64..=95 => 0b101, 93 | 128..=191 => 0b110, 94 | _ => 0b111, 95 | }; 96 | 97 | apb.en().modify(|_,w| w.$spiXen().set_bit()); 98 | //apb.rst().write(|w| w.$spiXrst().set_bit()); 99 | //apb.rst().write(|w| w.$spiXrst().clear_bit()); 100 | 101 | spi.ctl0.write(|w| w.spien().clear_bit()); //disable while configuring... 102 | spi.ctl1.modify(|_,w| w.nssdrv().clear_bit()); //let application drive the nss pin. 103 | unsafe { //unsafe because of call to psc().bits(...) 104 | spi.ctl0.modify(|_,w| { 105 | w 106 | .bden().clear_bit() //bidirectional 107 | .ff16().clear_bit() // 8 bit word size 108 | .ro().clear_bit() //not read-only 109 | .psc().bits(prescaler_bits) 110 | .swnssen().clear_bit() // use hardware nss mode. ?? 111 | .swnss().clear_bit() 112 | .lf().clear_bit() //MSB first 113 | .mstmod().set_bit() //master mode 114 | .ckpl().bit(mode.polarity == Polarity::IdleHigh) 115 | .ckph().bit(mode.phase == Phase::CaptureOnSecondTransition) 116 | .spien().set_bit() 117 | }); 118 | } 119 | 120 | 121 | Spi { spi, pins } 122 | } 123 | 124 | /// Releases the SPI peripheral and associated pins 125 | pub fn free(self) -> ($SPIX, (SCK, MISO, MOSI, NSS)) { 126 | (self.spi, self.pins) 127 | } 128 | } 129 | 130 | impl FullDuplex for Spi<$SPIX, PINS> { 131 | type Error = Error; 132 | 133 | fn try_read(&mut self) -> nb::Result { 134 | if self.spi.stat.read().rbne().bit_is_clear() { 135 | Err(nb::Error::WouldBlock) 136 | } else { 137 | let rx_byte = self.spi.data.read().spi_data().bits(); 138 | Ok(rx_byte as u8) 139 | } 140 | } 141 | 142 | fn try_send(&mut self, byte: u8) -> nb::Result<(), Error> { 143 | if self.spi.stat.read().tbe().bit_is_clear() { 144 | Err(nb::Error::WouldBlock) 145 | } else { 146 | self.spi.data.write(|w|{ 147 | unsafe{ 148 | w.spi_data().bits(byte.into()) 149 | } 150 | }); 151 | Ok(()) 152 | } 153 | } 154 | } 155 | 156 | impl transfer::Default for Spi<$SPIX, PINS> {} 157 | impl write::Default for Spi<$SPIX, PINS> {} 158 | )+ 159 | } 160 | } 161 | 162 | pins! {SPI0, 163 | SCK: [PA5], //TODO leaving off alternate AFIO pins, due to conflicting Sealed trait impls 164 | MISO: [PA6], 165 | MOSI: [PA7], 166 | NSS: [PA4] 167 | } 168 | 169 | pins! {SPI1, 170 | SCK: [PB13], 171 | MISO: [PB14], 172 | MOSI: [PB15], 173 | NSS: [PB12] 174 | } 175 | 176 | pins! {SPI2, 177 | SCK: [PB3], 178 | MISO: [PB4], 179 | MOSI: [PB5], 180 | NSS: [PA15] 181 | } 182 | 183 | spi! { 184 | SPI0: (spi0, APB2, spi0en, spi0rst, ck_apb2), 185 | SPI1: (spi1, APB1, spi1en, spi1rst, ck_apb1), 186 | SPI2: (spi2, APB1, spi2en, spi2rst, ck_apb1), 187 | } 188 | -------------------------------------------------------------------------------- /src/timer.rs: -------------------------------------------------------------------------------- 1 | //! Timers 2 | use crate::pac::TIMER6; 3 | use crate::rcu::{Clocks, APB1}; 4 | use crate::unit::Hertz; 5 | use embedded_hal::blocking::delay::DelayMs; 6 | use embedded_hal::timer::CountDown; 7 | use core::convert::Infallible; 8 | 9 | // I'd prefer using Timer for convenience 10 | /// Timer object 11 | pub struct Timer { 12 | timer: TIMER, 13 | clock_scaler: u16, 14 | clock_frequency: Hertz, 15 | } 16 | 17 | impl Timer { 18 | /// Initialize the timer. 19 | /// 20 | /// An enable and reset procedure is procceed to peripheral to clean its state. 21 | pub fn timer6(timer: TIMER6, clock: Clocks, apb1: &mut APB1) -> Self { 22 | riscv::interrupt::free(|_| { 23 | apb1.en().modify(|_, w| w.timer6en().set_bit()); 24 | apb1.rst().write(|w| w.timer6rst().set_bit()); 25 | apb1.rst().write(|w| w.timer6rst().clear_bit()); 26 | }); 27 | Timer { 28 | timer, 29 | clock_scaler: 1000, 30 | clock_frequency: clock.ck_apb1(), 31 | } 32 | } 33 | } 34 | 35 | impl Timer { 36 | // in future designs we do not stop timer in this function 37 | // but prefer using Timer::start(self, ...) -> SomeTimer 38 | // when SomeTimer should be stopped, it has function returns timer back 39 | // as SomeTimer::stop(self) -> Timer. 40 | /// Release the timer, return its ownership. 41 | pub fn release(self) -> TIMER { 42 | self.timer 43 | } 44 | } 45 | 46 | impl> DelayMs for Timer { 47 | type Error = Infallible; 48 | fn try_delay_ms(&mut self, ms: T) -> Result<(), Self::Error> { 49 | let count = (ms.into() * self.clock_frequency.0) / (self.clock_scaler as u32 * 1000); 50 | if count > u16::max_value() as u32 { 51 | panic!("can not delay that long"); 52 | } 53 | self.try_start(count as u16).ok(); 54 | nb::block!(self.try_wait()).ok(); 55 | Ok(()) 56 | } 57 | } 58 | 59 | impl CountDown for Timer { 60 | type Error = Infallible; 61 | type Time = u16; 62 | 63 | fn try_start(&mut self, count: T) -> Result<(), Self::Error> 64 | where 65 | T: Into, 66 | { 67 | let c = count.into(); 68 | riscv::interrupt::free(|_| { 69 | self.timer 70 | .psc 71 | .write(|w| unsafe { w.psc().bits(self.clock_scaler) }); 72 | self.timer.intf.write(|w| w.upif().clear_bit()); 73 | self.timer.swevg.write(|w| w.upg().set_bit()); 74 | self.timer.intf.write(|w| w.upif().clear_bit()); 75 | self.timer.car.modify(|_, w| unsafe { w.carl().bits(c) }); 76 | self.timer.ctl0.modify(|_, w| w.cen().set_bit()); 77 | }); 78 | Ok(()) 79 | } 80 | 81 | // this signature changes in a future version, so we don'ot need the void crate. 82 | // --- 83 | // changed to Self::Error, removing void (luojia65) 84 | fn try_wait(&mut self) -> nb::Result<(), Self::Error> { 85 | let flag = self.timer.intf.read().upif().bit_is_set(); 86 | if flag { 87 | Ok(()) 88 | } else { 89 | Err(nb::Error::WouldBlock) 90 | } 91 | } 92 | } 93 | 94 | // impl Periodic for Timer {} 95 | -------------------------------------------------------------------------------- /src/unit.rs: -------------------------------------------------------------------------------- 1 | //! Measurement units 2 | 3 | /// Hertz 4 | #[derive(Clone, Copy)] 5 | pub struct Hertz(pub u32); 6 | 7 | /// Kilo hertz 8 | #[derive(Clone, Copy)] 9 | pub struct KiloHertz(pub u32); 10 | 11 | /// Mega hertz 12 | #[derive(Clone, Copy)] 13 | pub struct MegaHertz(pub u32); 14 | 15 | /// Extension trait that add convenient methods to the `u32` type 16 | pub trait U32Ext { 17 | /// Wrap in `Hertz` 18 | fn hz(self) -> Hertz; 19 | /// Wrap in `KiloHertz` 20 | fn khz(self) -> KiloHertz; 21 | /// Wrap in `MegaHertz` 22 | fn mhz(self) -> MegaHertz; 23 | /// Wrap in `MilliSeconds` 24 | fn ms(self) -> MilliSeconds; 25 | /// Wrap in `MicroSeconds` 26 | fn us(self) -> MicroSeconds; 27 | /// Wrap in `Bps` 28 | fn bps(self) -> Bps; 29 | } 30 | 31 | impl U32Ext for u32 { 32 | fn hz(self) -> Hertz { 33 | Hertz(self) 34 | } 35 | 36 | fn khz(self) -> KiloHertz { 37 | KiloHertz(self) 38 | } 39 | 40 | fn mhz(self) -> MegaHertz { 41 | MegaHertz(self) 42 | } 43 | 44 | fn ms(self) -> MilliSeconds { 45 | MilliSeconds(self) 46 | } 47 | 48 | fn us(self) -> MicroSeconds { 49 | MicroSeconds(self) 50 | } 51 | 52 | fn bps(self) -> Bps { 53 | Bps(self) 54 | } 55 | } 56 | 57 | impl Into for KiloHertz { 58 | fn into(self) -> Hertz { 59 | Hertz(self.0 * 1_000) 60 | } 61 | } 62 | 63 | impl Into for MegaHertz { 64 | fn into(self) -> Hertz { 65 | Hertz(self.0 * 1_000_000) 66 | } 67 | } 68 | 69 | impl Into for MegaHertz { 70 | fn into(self) -> KiloHertz { 71 | KiloHertz(self.0 * 1_000) 72 | } 73 | } 74 | 75 | /// Milliseconds 76 | pub struct MilliSeconds(pub u32); 77 | 78 | // todo: there's no need for accurate time units by now 79 | /// Microseconds 80 | pub struct MicroSeconds(pub u32); 81 | 82 | impl Into for MilliSeconds { 83 | fn into(self) -> MicroSeconds { 84 | MicroSeconds(self.0 * 1_000) 85 | } 86 | } 87 | 88 | /// Bits per second 89 | pub struct Bps(pub u32); 90 | -------------------------------------------------------------------------------- /src/wdog.rs: -------------------------------------------------------------------------------- 1 | //! (TODO) Watchdog Timer (WDGT) 2 | //! 3 | //! Ref: Section 13, the User Manual 4 | 5 | use crate::pac::{FWDGT, WWDGT}; 6 | use crate::unit::MicroSeconds; 7 | use embedded_hal::watchdog::{Watchdog, Enable}; 8 | use core::convert::Infallible; 9 | 10 | // Ref: Section 13.1.4 11 | const FWDGT_CMD_ENABLE: u16 = 0xCCCC; 12 | const FWDGT_CMD_RELOAD: u16 = 0xAAAA; 13 | const FWDGT_CMD_WRITE_ACCESS_ENABLE: u16 = 0x5555; 14 | const FWDGT_CMD_WRITE_ACCESS_DISABLE: u16 = 0x0000; 15 | 16 | // in this library we do not use 0b111 as psc(0b110) equals psc(0b111) 17 | const FWDGT_MAX_PSC: u8 = 0b110; 18 | // RLD == 0 => multiplier = 1, RLD == FFF => multiplier = 4096 19 | const FWDGT_MAX_RLD: u16 = 0xFFF; 20 | 21 | /* 22 | 23 | We declare this struct Free and Free and 24 | implement different traits for them. 25 | However if existing designs still need old style API, they may 26 | set two Target associated types to Self. Then these designs could 27 | implement all trait for (e.g.) a Free struct alone. 28 | 29 | */ 30 | 31 | /// Type state struct for disabled watchdog timers. 32 | pub struct Disabled; 33 | 34 | /// Type state struct for enabled watchdog timers. 35 | pub struct Enabled; 36 | 37 | /// Free Watchdog Timer (FWDGT) peripheral 38 | /// 39 | /// This watchdog timer cannot be disabled. 40 | /// 41 | /// TODO: debug 42 | pub struct Free { 43 | fwdgt: FWDGT, 44 | state: STATE, 45 | } 46 | 47 | impl Free { 48 | /// Wrap the watchdog 49 | pub fn new(fwdgt: FWDGT) -> Free { 50 | Free { fwdgt, state: Disabled } 51 | } 52 | 53 | /// Returns the interval in microseconds 54 | pub fn interval(&self) -> MicroSeconds { 55 | while self.rud_or_pud_updating() {} 56 | let psc: u32 = self.fwdgt.psc.read().psc().bits().into(); 57 | let rld: u32 = self.fwdgt.rld.read().rld().bits().into(); 58 | let time_us = (rld + 1) * (1 << psc) * 100; 59 | MicroSeconds(time_us) 60 | } 61 | 62 | // todo: stop_on_debug function (DBG peripheral) 63 | } 64 | 65 | impl Free { 66 | #[inline] 67 | fn rud_or_pud_updating(&self) -> bool { 68 | let stat = self.fwdgt.stat.read(); 69 | stat.rud().bits() || stat.pud().bits() 70 | } 71 | #[inline] 72 | fn calc_psc_rld(time_us: u32) -> (u8, u16) { 73 | let mut psc = 0; 74 | let mut max_time = 409_600; // 4096 * 1/(40KHz * 1/4) = 409.6ms = 409600us 75 | while psc < FWDGT_MAX_PSC && max_time < time_us { 76 | psc += 1; 77 | max_time *= 2; 78 | } 79 | let mut rld = u32::saturating_sub(time_us, 1) / (100 * (1 << psc)); 80 | if rld > FWDGT_MAX_RLD as u32 { 81 | rld = FWDGT_MAX_RLD as u32; 82 | } 83 | (psc, rld as u16) 84 | } 85 | // ref: stm32f4xx_hal 86 | fn access_protected A>(&self, mut f: F) -> A { 87 | // Unprotect write access to registers 88 | self.fwdgt 89 | .ctl 90 | .write(|w| unsafe { w.cmd().bits(FWDGT_CMD_WRITE_ACCESS_ENABLE) }); 91 | let ans = f(&self.fwdgt); 92 | 93 | // Protect again 94 | self.fwdgt 95 | .ctl 96 | .write(|w| unsafe { w.cmd().bits(FWDGT_CMD_WRITE_ACCESS_DISABLE) }); 97 | ans 98 | } 99 | } 100 | 101 | impl Free { 102 | // We may set another period when watchdog is enabled 103 | pub fn set_period(&mut self, period: impl Into) { 104 | let (psc, rld) = Self::calc_psc_rld(period.into().0); 105 | while self.rud_or_pud_updating() {} 106 | riscv::interrupt::free(|_| { 107 | self.access_protected(|fwdgt| { 108 | // note(unsafe): valid values ensured by calc_psc_rld 109 | fwdgt.psc.modify(|_, w| unsafe { w.psc().bits(psc) }); 110 | fwdgt.rld.modify(|_, w| unsafe { w.rld().bits(rld) }); 111 | }); 112 | // note(unsafe): write valid command constant defined in the Manual 113 | self.fwdgt 114 | .ctl 115 | .write(|w| unsafe { w.cmd().bits(FWDGT_CMD_RELOAD) }); 116 | }); 117 | } 118 | } 119 | 120 | impl Enable for Free { 121 | type Error = Infallible; 122 | type Time = MicroSeconds; 123 | type Target = Free; 124 | 125 | fn try_start(self, period: T) -> Result, Self::Error> 126 | where 127 | T: Into, 128 | { 129 | // prepare configurations 130 | let (psc, rld) = Self::calc_psc_rld(period.into().0); 131 | // According to the Manual, if applications need to modify the PSC and 132 | // RLD registers, they should wait until PUD and RUD bit is cleared to 133 | // zero by hardware. (Section 13.1.4, FWDGT_RLD) 134 | while self.rud_or_pud_updating() {} 135 | // perform config and start process 136 | riscv::interrupt::free(|_| { 137 | self.access_protected(|fwdgt| { 138 | // note(unsafe): valid values ensured by calc_psc_rld 139 | fwdgt.psc.modify(|_, w| unsafe { w.psc().bits(psc) }); 140 | fwdgt.rld.modify(|_, w| unsafe { w.rld().bits(rld) }); 141 | }); 142 | // note(unsafe): write valid command constant defined in the Manual 143 | self.fwdgt 144 | .ctl 145 | .write(|w| unsafe { w.cmd().bits(FWDGT_CMD_RELOAD) }); 146 | self.fwdgt 147 | .ctl 148 | .write(|w| unsafe { w.cmd().bits(FWDGT_CMD_ENABLE) }); 149 | }); 150 | // Change typestate to `Enabled` 151 | Ok(Free { fwdgt: self.fwdgt, state: Enabled }) 152 | } 153 | } 154 | 155 | // We only implement `Watchdog` for a watchdog that is enabled. 156 | // Application developers may not being able to `feed` an `Free`. 157 | // In this case, developers would not forget to `enable` before feed, 158 | // or we would not allow developers to feed a disabled dog. 159 | impl Watchdog for Free { 160 | type Error = Infallible; 161 | 162 | fn try_feed(&mut self) -> Result<(), Self::Error> { 163 | // note(unsafe): write valid command constant defined in the Manual 164 | self.fwdgt 165 | .ctl 166 | .write(|w| unsafe { w.cmd().bits(FWDGT_CMD_RELOAD) }); 167 | Ok(()) 168 | } 169 | } 170 | 171 | /* 172 | GD32VF103's Free Watchdog Timer does not support disable operation. 173 | We keep code here for example to show that, if there is a watchdog timer 174 | that can be disabled, how could we write code for it. 175 | */ 176 | 177 | // impl Disable for Free { 178 | // type Error = Infallible; 179 | // type Target = Free; 180 | 181 | // fn try_disable(self) -> Result { 182 | // // There should be probable DISABLE command 183 | // // Change typestate to `Disabled` 184 | // Ok(Free { fwdgt: self.fwdgt, state: Disabled }) 185 | // } 186 | // } 187 | 188 | /// Window Watchdog Timer (WWDGT) peripheral 189 | pub struct Window { 190 | wwdgt: WWDGT, 191 | } 192 | --------------------------------------------------------------------------------