├── flash.sh ├── .cargo └── config ├── openocd.gdb ├── memory-c8.x ├── memory-cb.x ├── .gitignore ├── Cargo.toml ├── openocd.cfg ├── scripts ├── convert.py ├── font.py └── esp8266.py ├── examples ├── blinky.rs ├── irq.rs ├── uart.rs ├── lcd-font.rs ├── cpuinfo.rs ├── rtc.rs ├── adc.rs ├── button.rs ├── bad_apple.rs └── usbhid.rs ├── FT2232HL.cfg ├── src ├── lib.rs ├── stdout.rs ├── lcd.rs └── adc.rs ├── README.md └── LICENSE /flash.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | riscv64-unknown-elf-objcopy -O binary \ 6 | target/riscv32imac-unknown-none-elf/release/examples/bad_apple \ 7 | firmware.bin 8 | 9 | 10 | dfu-util -a 0 -s 0x08000000:leave -D firmware.bin 11 | -------------------------------------------------------------------------------- /.cargo/config: -------------------------------------------------------------------------------- 1 | [target.riscv32imac-unknown-none-elf] 2 | runner = 'riscv64-unknown-elf-gdb -x openocd.gdb' 3 | rustflags = [ 4 | "-C", "link-arg=-Tmemory-cb.x", 5 | "-C", "link-arg=-Tlink.x", 6 | ] 7 | 8 | [build] 9 | target = "riscv32imac-unknown-none-elf" 10 | -------------------------------------------------------------------------------- /openocd.gdb: -------------------------------------------------------------------------------- 1 | 2 | target extended-remote :3333 3 | 4 | # print demangled symbols 5 | set print asm-demangle on 6 | 7 | set confirm off 8 | 9 | # set backtrace limit to not have infinite backtrace loops 10 | set backtrace limit 32 11 | 12 | load 13 | continue 14 | -------------------------------------------------------------------------------- /memory-c8.x: -------------------------------------------------------------------------------- 1 | /* GD32VF103C8 */ 2 | MEMORY 3 | { 4 | FLASH : ORIGIN = 0x08000000, LENGTH = 64k 5 | RAM : ORIGIN = 0x20000000, LENGTH = 20k 6 | } 7 | 8 | REGION_ALIAS("REGION_TEXT", FLASH); 9 | REGION_ALIAS("REGION_RODATA", FLASH); 10 | REGION_ALIAS("REGION_DATA", RAM); 11 | REGION_ALIAS("REGION_BSS", RAM); 12 | REGION_ALIAS("REGION_HEAP", RAM); 13 | REGION_ALIAS("REGION_STACK", RAM); 14 | -------------------------------------------------------------------------------- /memory-cb.x: -------------------------------------------------------------------------------- 1 | /* GD32VF103CB */ 2 | MEMORY 3 | { 4 | FLASH : ORIGIN = 0x08000000, LENGTH = 128k 5 | RAM : ORIGIN = 0x20000000, LENGTH = 32k 6 | } 7 | 8 | REGION_ALIAS("REGION_TEXT", FLASH); 9 | REGION_ALIAS("REGION_RODATA", FLASH); 10 | REGION_ALIAS("REGION_DATA", RAM); 11 | REGION_ALIAS("REGION_BSS", RAM); 12 | REGION_ALIAS("REGION_HEAP", RAM); 13 | REGION_ALIAS("REGION_STACK", RAM); 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | 13 | # Added by cargo 14 | 15 | /target 16 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "longan-nano-playground" 3 | version = "0.1.0" 4 | authors = ["Andelf "] 5 | edition = "2018" 6 | 7 | 8 | [dependencies] 9 | embedded-hal = "0.2.4" 10 | nb = "0.1.2" 11 | riscv = "0.6.0" 12 | gd32vf103xx-hal = { path = "../gd32vf103xx-hal" } 13 | st7735-lcd = "0.7" 14 | 15 | # deps for examples 16 | [dev-dependencies] 17 | # outdated in crates.io, use git 18 | # longan-nano = { git = "https://github.com/riscv-rust/longan-nano", features = ["lcd"] } 19 | riscv-rt = "0.8.0" 20 | panic-halt = "0.2.0" 21 | embedded-graphics = "0.6" 22 | embedded-sdmmc = "0.3" 23 | ssd1306 = "0.4" 24 | embedded-drivers = { path = "../embedded-drivers" } 25 | # embedded-picofont = "0.2.1" 26 | # profont = "0.4.0" 27 | -------------------------------------------------------------------------------- /openocd.cfg: -------------------------------------------------------------------------------- 1 | # autoexit true 2 | 3 | set _CHIPNAME riscv 4 | jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x1e200a6d 5 | 6 | set _TARGETNAME $_CHIPNAME.cpu 7 | target create $_TARGETNAME riscv -chain-position $_TARGETNAME 8 | $_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size 20480 -work-area-backup 0 9 | 10 | 11 | # Work-area is a space in RAM used for flash programming 12 | if { [info exists WORKAREASIZE] } { 13 | set _WORKAREASIZE $WORKAREASIZE 14 | } else { 15 | set _WORKAREASIZE 0x5000 16 | } 17 | 18 | # Allow overriding the Flash bank size 19 | if { [info exists FLASH_SIZE] } { 20 | set _FLASH_SIZE $FLASH_SIZE 21 | } else { 22 | # autodetect size 23 | set _FLASH_SIZE 0 24 | } 25 | 26 | # flash size will be probed 27 | set _FLASHNAME $_CHIPNAME.flash 28 | 29 | flash bank $_FLASHNAME gd32vf103 0x08000000 0 0 0 $_TARGETNAME 30 | riscv set_reset_timeout_sec 1 31 | init 32 | 33 | halt 34 | -------------------------------------------------------------------------------- /scripts/convert.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from PIL import Image 4 | 5 | 6 | # im = Image.open("./out/0879.png") 7 | # with open("./sample.raw", 'wb') as fp: 8 | # fp.write(im_to_raw(im)) 9 | 10 | # RGB, 106x80 11 | 12 | 13 | def to_rgb565(r, g, b): 14 | r = (r & 0b11111000) << 8 15 | g = (g & 0b11111100) << 3 16 | b = b >> 3 17 | val = r + g + b 18 | return val.to_bytes(2, 'big') 19 | 20 | 21 | def im_to_raw(im): 22 | raw = [] 23 | # im_gray = im.convert('L') 24 | for y in range(80): 25 | for x in range(106): 26 | r, g, b = im.getpixel((x, y)) 27 | raw.append(to_rgb565(r, g, b)) 28 | 29 | return b''.join(raw) 30 | 31 | 32 | # Must use DOS 8.3 file name. 33 | with open("./badapple.raw", "wb") as fp: 34 | for i in range(1, 5258 + 1): 35 | fname = "./out/%04d.png" % i 36 | print(fname) 37 | im = Image.open(fname) 38 | raw = im_to_raw(im) 39 | fp.write(raw) 40 | -------------------------------------------------------------------------------- /examples/blinky.rs: -------------------------------------------------------------------------------- 1 | //! Blinky for Wio Lite RISC-V 2 | 3 | #![no_std] 4 | #![no_main] 5 | #![feature(asm)] 6 | 7 | use panic_halt as _; 8 | 9 | use gd32vf103xx_hal::delay; 10 | use gd32vf103xx_hal::pac; 11 | use gd32vf103xx_hal::prelude::*; 12 | use riscv_rt::entry; 13 | 14 | use embedded_hal::digital::v2::ToggleableOutputPin; 15 | 16 | #[entry] 17 | fn main() -> ! { 18 | let dp = pac::Peripherals::take().unwrap(); 19 | 20 | // Configure clocks 21 | let mut rcu = dp 22 | .RCU 23 | .configure() 24 | .ext_hf_clock(8.mhz()) 25 | .sysclk(108.mhz()) 26 | .freeze(); 27 | 28 | // let mut afio = dp.AFIO.constrain(&mut rcu); 29 | 30 | let gpioa = dp.GPIOA.split(&mut rcu); 31 | // let gpiob = dp.GPIOB.split(&mut rcu); 32 | // let gpioc = dp.GPIOC.split(&mut rcu); 33 | 34 | let mut delay = delay::McycleDelay::new(&rcu.clocks); 35 | 36 | // Wio Lite RISC-V: PA8 37 | // Longan Nano: PA2 (with RGB PC13, PA1, PA2) 38 | let mut blue_led = gpioa.pa8.into_push_pull_output(); 39 | 40 | loop { 41 | blue_led.toggle().unwrap(); 42 | delay.delay_ms(500); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /FT2232HL.cfg: -------------------------------------------------------------------------------- 1 | # OpenOCD configuration file for the FTDI FT2232HL 2 | # evaluation board used as JTAG adapter 3 | 4 | # Include the configuration for the JTAG adapter. 5 | # If you have a different interface, please edit this to include the 6 | # configuration file of yours. 7 | adapter driver ftdi 8 | # interface ftdi # old style 9 | 10 | # USB vendor ID and product ID 11 | ftdi_vid_pid 0x0403 0x6010 12 | 13 | # iProduct string 14 | # ftdi_device_desc "Dual RS232-HS" 15 | 16 | # channel of the FTDI device to use for MPSSE operations. 17 | ftdi_channel 0 18 | 19 | # Initial values of the FTDI GPIO data and direction registers. 20 | # ref: FTDI AN-129 21 | 22 | # [data] [direction] 23 | # [ACBUS7~ACBUS0, ADBUS7, ADBUS0] 24 | # direction: 1 for output, 0 for input, use 0 for default 25 | # state: 1 for high, 0 for low 26 | 27 | # PIN JTAG DIR DATA 28 | # AD0 TCK output 0 29 | # AD1 TDI output 0 30 | # AD2 TDO input 0 31 | # AD3 TMS output 1 32 | 33 | # so data cfg = 0b00001000 = 0x08 34 | # direction = 0b00001011 = 0x0b 35 | 36 | ftdi_layout_init 0x0008 0x000b 37 | 38 | # Value from other boards (with GPIO as LED) 39 | # ftdi_layout_init 0x0038 0x003b 40 | # ftdi_layout_init 0x00e8 0x00eb 41 | 42 | # Connection 43 | # ft232r_jtag_nums tck tms tdi tdo 44 | # default = 0 3 1 2 45 | # AD0 - TCLK/SK 46 | # AD1 - TDI/DO 47 | # AD2 - TDO/DI 48 | # AD3 - TMS/CS 49 | 50 | transport select jtag 51 | 52 | # The speed of the JTAG interface, in KHz. If you get DSR/DIR errors (and they 53 | # do not relate to OpenOCD trying to read from a memory range without physical 54 | # memory being present there), you can try lowering this. 55 | adapter speed 8000 56 | # adapter_khz 8000 # old style 57 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | pub use gd32vf103xx_hal as hal; 4 | pub use hal::pac; 5 | 6 | pub mod adc; 7 | pub mod lcd; 8 | pub mod stdout; 9 | pub mod esp_at; 10 | 11 | use core::fmt; 12 | use core::str; 13 | 14 | /// A fmt::Write for bytes. 15 | pub struct ByteMutWriter<'a> { 16 | buf: &'a mut [u8], 17 | cursor: usize, 18 | } 19 | 20 | impl<'a> ByteMutWriter<'a> { 21 | pub fn new(buf: &'a mut [u8]) -> Self { 22 | ByteMutWriter { buf, cursor: 0 } 23 | } 24 | 25 | pub fn as_str(&self) -> &str { 26 | unsafe { str::from_utf8_unchecked(&self.buf[0..self.cursor]) } 27 | } 28 | 29 | #[inline] 30 | pub fn capacity(&self) -> usize { 31 | self.buf.len() 32 | } 33 | 34 | pub fn clear(&mut self) { 35 | self.cursor = 0; 36 | } 37 | 38 | pub fn len(&self) -> usize { 39 | self.cursor 40 | } 41 | 42 | pub fn empty(&self) -> bool { 43 | self.cursor == 0 44 | } 45 | 46 | pub fn full(&self) -> bool { 47 | self.capacity() == self.cursor 48 | } 49 | 50 | pub fn write_byte(&mut self, b: u8) { 51 | if !self.full() { 52 | self.buf[self.cursor] = b; 53 | self.cursor += 1; 54 | } 55 | } 56 | 57 | pub fn write_char(&mut self, c: char) { 58 | self.write_byte(c as u8); 59 | } 60 | } 61 | 62 | impl fmt::Write for ByteMutWriter<'_> { 63 | fn write_str(&mut self, s: &str) -> fmt::Result { 64 | let cap = self.capacity(); 65 | for (i, &b) in self.buf[self.cursor..cap] 66 | .iter_mut() 67 | .zip(s.as_bytes().iter()) 68 | { 69 | *i = b; 70 | } 71 | self.cursor = usize::min(cap, self.cursor + s.as_bytes().len()); 72 | Ok(()) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/stdout.rs: -------------------------------------------------------------------------------- 1 | //! Stdout based on the UART hooked up to the debug connector 2 | 3 | use core::fmt::{self, Write}; 4 | use riscv::interrupt; 5 | use gd32vf103xx_hal::{ 6 | serial::{Serial, Tx, self}, 7 | gpio::{Active, gpioa::{PA10, PA9}}, 8 | time::Bps, 9 | rcu::Rcu, 10 | afio::Afio, 11 | pac::USART0, 12 | }; 13 | 14 | 15 | static mut STDOUT: Option> = None; 16 | 17 | 18 | /// Configures stdout 19 | pub fn configure( 20 | uart: USART0, tx: PA9, rx: PA10, 21 | baud_rate: Bps, afio: &mut Afio, rcu: &mut Rcu 22 | ) where X: Active, Y: Active 23 | { 24 | let tx = tx.into_alternate_push_pull(); 25 | let rx = rx.into_floating_input(); 26 | let config = serial::Config::default().baudrate(baud_rate); 27 | let serial = Serial::new(uart, (tx, rx), config, afio, rcu); 28 | let (tx, _) = serial.split(); 29 | 30 | interrupt::free(|_| { 31 | unsafe { 32 | STDOUT.replace(tx); 33 | } 34 | }) 35 | } 36 | 37 | 38 | /// Writes formatted string to stdout 39 | pub fn write_fmt(args: fmt::Arguments) { 40 | interrupt::free(|_| unsafe { 41 | if let Some(stdout) = STDOUT.as_mut() { 42 | let _ = stdout.write_fmt(args); 43 | } 44 | }) 45 | } 46 | 47 | 48 | /// Macro for printing to the serial standard output 49 | #[macro_export] 50 | macro_rules! sprint { 51 | ($s:expr) => { 52 | $crate::stdout::write_fmt(format_args!($s)) 53 | }; 54 | ($($tt:tt)*) => { 55 | $crate::stdout::write_fmt(format_args!($($tt)*)) 56 | }; 57 | } 58 | 59 | /// Macro for printing to the serial standard output, with a newline. 60 | #[macro_export] 61 | macro_rules! sprintln { 62 | ($s:expr) => { 63 | $crate::stdout::write_fmt(format_args!(concat!($s, "\n"))) 64 | }; 65 | ($s:expr, $($tt:tt)*) => { 66 | $crate::stdout::write_fmt(format_args!(concat!($s, "\n"), $($tt)*)) 67 | }; 68 | } -------------------------------------------------------------------------------- /scripts/font.py: -------------------------------------------------------------------------------- 1 | from PIL import Image, ImageDraw, ImageFont 2 | import PIL.features 3 | 4 | print('libraqm:', PIL.features.check('raqm')) 5 | 6 | 7 | size = (320, 16) 8 | 9 | """ 10 | FORMAT = "RGB" 11 | BG = (255, 255, 255) 12 | FG = (0, 0, 0) 13 | """ 14 | 15 | FORMAT = '1' 16 | BG = 0 17 | FG = 1 18 | 19 | YOFF = 0 # or -1 20 | 21 | 22 | CHARS = "气温湿度照压卧槽艹牛逼数据" 23 | 24 | 25 | im = Image.new(FORMAT, size, BG) 26 | 27 | # font = ImageFont.truetype("sarasa-mono-sc-nerd-light.ttf", size=16, index=0) 28 | # font = ImageFont.truetype("sarasa-mono-sc-nerd-regular.ttf", size=16, index=0) 29 | font = ImageFont.truetype("Unibit.ttf", size=16, index=0) 30 | # font = ImageFont.truetype("zpix.ttf", size=12, index=0) 31 | 32 | 33 | draw = ImageDraw.Draw(im) 34 | 35 | draw.text((0, YOFF), CHARS, font=font, fill=FG, language='zh-CN') 36 | im.save('font.png') 37 | im.show() 38 | 39 | 40 | draw.rectangle([(0, 0), size], fill=BG) 41 | 42 | 43 | # NOTE, char 127 is replaced with '°' 44 | ASCII = '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~°' 45 | 46 | charmap = [] 47 | 48 | for i, c in enumerate(ASCII): 49 | if c.isprintable(): 50 | draw.text((0, YOFF), c, font=font, fill=FG) 51 | else: 52 | draw.text((0, YOFF), " ", font=font, fill=FG) 53 | 54 | for y in range(16): 55 | v = 0 56 | for x in range(0, 8): 57 | b = im.getpixel((x, y)) 58 | v = (v << 1) + b 59 | 60 | charmap.append(v) 61 | 62 | draw.rectangle([(0, 0), size], fill=BG) 63 | 64 | # ascii done 65 | 66 | # print(len(charmap)) 67 | 68 | for i, c in enumerate(CHARS): 69 | draw.text((0, YOFF), c, font=font, fill=FG) 70 | 71 | for y in range(16): 72 | v = 0 73 | for x in range(0, 16): 74 | b = im.getpixel((x, y)) 75 | v = (v << 1) + b 76 | 77 | charmap.append(v >> 8) 78 | charmap.append(v & 0xFF) 79 | 80 | draw.rectangle([(0, 0), size], fill=BG) 81 | 82 | 83 | with open('font.raw', 'wb') as fp: 84 | fp.write(bytes(charmap)) 85 | -------------------------------------------------------------------------------- /src/lcd.rs: -------------------------------------------------------------------------------- 1 | //! On-board LCD 2 | 3 | use embedded_hal::digital::v2::OutputPin; 4 | use gd32vf103xx_hal::afio::Afio; 5 | use gd32vf103xx_hal::delay::McycleDelay; 6 | use gd32vf103xx_hal::gpio::gpioa::{PA5, PA6, PA7}; 7 | use gd32vf103xx_hal::gpio::gpiob::{PB0, PB1, PB2}; 8 | use gd32vf103xx_hal::gpio::{Alternate, Floating, Input, Output, PushPull}; 9 | use gd32vf103xx_hal::pac::SPI0; 10 | use gd32vf103xx_hal::rcu::Rcu; 11 | use gd32vf103xx_hal::spi::{Spi, MODE_0}; 12 | use gd32vf103xx_hal::time::U32Ext; 13 | use st7735_lcd::{Orientation, ST7735}; 14 | 15 | /// Sets up all the needed GPIO pins for the LCD 16 | /// 17 | /// ``` 18 | /// let gpioa = dp.GPIOA.split(&mut rcu); 19 | /// let gpiob = dp.GPIOB.split(&mut rcu); 20 | /// let lcd_pins = lcd_pins!(gpioa, gpiob); 21 | /// ``` 22 | #[macro_export] 23 | macro_rules! lcd_pins { 24 | ($gpioa:ident, $gpiob:ident) => {{ 25 | $crate::lcd::LcdPins { 26 | miso: $gpioa.pa6.into_floating_input(), 27 | mosi: $gpioa.pa7.into_alternate_push_pull(), 28 | sck: $gpioa.pa5.into_alternate_push_pull(), 29 | cs: $gpiob.pb2.into_push_pull_output(), 30 | dc: $gpiob.pb0.into_push_pull_output(), 31 | rst: $gpiob.pb1.into_push_pull_output(), 32 | } 33 | }}; 34 | } 35 | 36 | type MisoPin = PA6>; 37 | type MosiPin = PA7>; 38 | type SckPin = PA5>; 39 | type CsPin = PB2>; 40 | type DcPin = PB0>; 41 | type RstPin = PB1>; 42 | type SpiType = Spi; 43 | 44 | /// On board LCD 160x80 45 | pub type Lcd = ST7735; 46 | 47 | /// Pins consumed by LCD driver 48 | pub struct LcdPins { 49 | /// SPI0 miso, unused 50 | pub miso: MisoPin, 51 | /// SPI0 mosi 52 | pub mosi: MosiPin, 53 | /// SPI0 sck 54 | pub sck: SckPin, 55 | /// CS for ST7735 56 | pub cs: CsPin, 57 | /// DC for ST7735 58 | pub dc: DcPin, 59 | /// RST for ST7735 60 | pub rst: RstPin, 61 | } 62 | 63 | /// Constructs LCD driver from the required components 64 | pub fn configure(spi: SPI0, pins: LcdPins, afio: &mut Afio, rcu: &mut Rcu) -> Lcd { 65 | let (width, height) = (160, 80); 66 | let spi0 = Spi::spi0( 67 | spi, 68 | (pins.sck, pins.miso, pins.mosi), 69 | afio, 70 | MODE_0, 71 | 16.mhz(), 72 | rcu, 73 | ); 74 | 75 | let mut cs = pins.cs; 76 | cs.set_low().unwrap(); 77 | 78 | let mut lcd = ST7735::new(spi0, pins.dc, pins.rst, false, true, width, height); 79 | let mut delay = McycleDelay::new(&rcu.clocks); 80 | lcd.init(&mut delay).unwrap(); 81 | lcd.set_orientation(&Orientation::Landscape).unwrap(); 82 | lcd.set_offset(1, 26); 83 | 84 | lcd 85 | } 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # longan-nano-playground.rs 2 | 3 | Longan Nano board examples, in Rust, under macOS. Bad Apple included. 4 | 5 | A playground with [riscv-rust/longan-nano](https://github.com/riscv-rust/longan-nano). 6 | 7 | ## Environment setup 8 | 9 | ```console 10 | $ # Download RISC-V gcc toolchains to PATH (both rv32 and rv64 will do) 11 | $ riscv64-unknown-elf-objcopy --version 12 | GNU objcopy (SiFive Binutils 2.32.0-2020.04.0) 2.32 13 | ... 14 | 15 | $ # Install dfu-tool from brew (git HEAD required, including bugfix for GD32VF) 16 | $ brew install dfu-util --HEAD 17 | 18 | $ # List device (Hold BOOT0 and Press RESET, device reboot to DFU mode) 19 | $ dfu-util -l 20 | Found DFU: [28e9:0189] ver=0100, devnum=6, cfg=1, intf=0, path="20-1", alt=0, name="@Internal Flash /0x08000000/128*001Kg", serial="3CBJ" 21 | 22 | $ # Add rust toolchain for riscv32imac 23 | $ rustup target add riscv32imac-unknown-none-elf 24 | ``` 25 | 26 | Run on Longan Nano board: 27 | 28 | ```sh 29 | # cargo build.. 30 | ./build.sh 31 | 32 | # Hold BOOT0 and Press RESET. reboot to DFU mode... 33 | ./flash.sh 34 | 35 | # Press RESET on the board 36 | ``` 37 | 38 | ## A writter for byte array 39 | 40 | You cannot use `println!()` and `writeln!()` in a bare embedded device, since no alloc lib defined. 41 | And `String`, `Vec` requires dynamic allocation on heap. 42 | 43 | So a `ByteMutWriter` is defined for `core::fmt::Write`. 44 | 45 | Usage: 46 | 47 | ```rust 48 | let mut buf = [0u8; 20 * 5]; 49 | let mut buf = ByteMutWriter::new(&mut buf[..]); 50 | 51 | buf.clear(); 52 | 53 | writeln!(buf, "Hello {}", "Rust"); 54 | write!(buf, "Val: 0x{:08x}", debug_val); 55 | 56 | // buf.as_str(); 57 | ``` 58 | 59 | ## Steps for Bad Apple 60 | 61 | - Prepare an SD card, format it to FAT32(FAT16) 62 | - I use an outdated SD card, 4GB, Class4 63 | - Download a Bad Apple video 64 | - `youtube-dl` might help 65 | - Convert video file to image sequences (scale to LCD screen resolution) 66 | - Use `ffmpeg` 67 | - Convert images to a `Rgb565` ImageRaw file, save it to the root directory of SD card 68 | - I wrote a small python script using PIL(Pillow) 69 | - WARN: must use a DOS style 8.3 filename 70 | - Upload `bad_apple.rs` firmware to the board 71 | - enjoy! 72 | 73 | ```sh 74 | # Download video 75 | youtube-dl https://.... 76 | 77 | # Video info 78 | ffmpeg -i BadApple.mp4 79 | # .... 80 | # Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709), 960x720 [SAR 1:1 DAR 4:3], 567 kb/s, 30 fps, 30 tbr, 15360 tbn, 60 tbc (default) 81 | # .... 82 | 83 | # Now you know it's a 960x720 resolution, 30 fps. 84 | # To fit a Longan Nano's 160x80 screen, you will need to scale it to 106x80. 85 | 86 | # NOTE: Lower fps might help. 87 | 88 | # Now, convert to image sequences. 89 | ffmpeg -i BadApple.mp4 -vf scale=106:80,fps=24 'out/%04d.png' 90 | 91 | # Convert the image sequence to a `Rgb565` ImageRaw file. (requires python3-pillow) 92 | python3 scripts/convert.py 93 | ``` 94 | 95 | Then copy the `badapple.raw` to your SD card. Reboot with new firmware. :) 96 | -------------------------------------------------------------------------------- /scripts/esp8266.py: -------------------------------------------------------------------------------- 1 | import serial 2 | import time 3 | 4 | # initialization and open the port 5 | 6 | # possible timeout values: 7 | # 1. None: wait forever, block call 8 | # 2. 0: non-blocking mode, return immediately 9 | 10 | 11 | ser = serial.Serial() 12 | # ser.port = "/dev/ttyUSB0" 13 | ser.port = "/dev/cu.usbserial-1410" 14 | # ser.port = "/dev/ttyS2" 15 | ser.baudrate = 115200 16 | ser.bytesize = serial.EIGHTBITS # number of bits per bytes 17 | ser.parity = serial.PARITY_NONE # set parity check: no parity 18 | ser.stopbits = serial.STOPBITS_ONE # number of stop bits 19 | # ser.timeout = None #block read 20 | ser.timeout = 1 # non-block read 21 | # ser.timeout = 2 #timeout block read 22 | ser.xonxoff = False # disable software flow control 23 | ser.rtscts = False # disable hardware (RTS/CTS) flow control 24 | ser.dsrdtr = False # disable hardware (DSR/DTR) flow control 25 | ser.writeTimeout = 2 # timeout for write 26 | 27 | try: 28 | ser.open() 29 | except Exception as e: 30 | print("error open serial port: " + str(e)) 31 | exit() 32 | 33 | ser.flushInput() # flush input buffer, discarding all its contents 34 | ser.flushOutput() # flush output buffer, aborting current output 35 | # and discard all that is in buffer 36 | 37 | 38 | """ 39 | ATE0 40 | 41 | AT+UART_CUR? 42 | > +UART_CUR:115273,8,1,0,1 43 | 44 | AT+UART_CUR=115200,8,1,0,0 45 | 46 | 47 | AT+RFPOWER? 48 | < +RFPOWER:78 49 | 50 | AT+CWQAP 51 | > 断开 52 | 53 | AT+CWMODE=1 54 | 1-3 55 | 1=station 56 | 2=ap 57 | 3=ap+station 58 | 59 | 60 | AT+CWLAP 61 | < 62 | < +CWLAP:(3,"cjyy",-39,"90:12:34:d4:e4:aa",6) 63 | < +CWLAP:(3,"",-40,"90:12:34:d4:e4:ab",6) 64 | < +CWLAP:(3,"Galaxy Note10+ 5G574e",-58,"4a:eb:62:15:3a:e2",11) 65 | < +CWLAP:(3,"feather",-65,"04:d9:f5:c4:93:98",11) 66 | 67 | AT+CWJAP? 68 | < No AP 69 | or 70 | < +CWJAP:"feather","04:d9:f5:c4:93:98",11,-69,0,0,0 71 | 72 | 73 | AT+CWJAP="feather","-------" 74 | < 75 | < I (2053728) wifi: state: 0 -> 2 (b0) 76 | < I (2053733) wifi: state: 2 -> 3 (0) 77 | < I (2053740) wifi: state: 3 -> 5 (10) 78 | < WIFI CONNECTED 79 | < 80 | < WIFI GOT IP 81 | 82 | AT+CWDHCP? 83 | < +CWDHCP:3 84 | 85 | AT+CIPSTA? 86 | < +CIPSTA:ip:"192.168.1.9" 87 | < +CIPSTA:gateway:"192.168.1.1" 88 | < +CIPSTA:netmask:"255.255.0.0" 89 | 90 | AT+CWHOSTNAME? 91 | < +CWHOSTNAME:LWIP 92 | 主机名 93 | 94 | AT+PING="baidu.com" 95 | < +PING:203 96 | 97 | AT+CIPDOMAIN="baidu.com" 98 | < +CIPDOMAIN:220.181.38.148 99 | 100 | AT+CIPSTATUS 101 | < STATUS:2 102 | 连接状态 103 | """ 104 | 105 | # nc -vv -u -l -p 2000 106 | cmd = 'AT+CIPSTART="UDP","192.168.1.198",2000,1002,2' 107 | 108 | cmd = 'AT+CIPSTATUS' # 获得连接id=0 109 | 110 | # cmd = 'AT+CIPSEND=10\r\n0123456789' 111 | # cmd = 'AT+CIPSENDBUF=10\r\n01234567891' 112 | 113 | #cmd = 'AT+GMR' 114 | 115 | # cmd = 'AT+RESTORE' # 出厂设置 116 | 117 | # cmd = 'AT+SYSLOG' 118 | 119 | # cmd = 'AT+GMR' 120 | 121 | # use station mode 122 | #cmd = 'AT+CWMODE=1' 123 | 124 | # scan ap 125 | # cmd = 'AT+CWLAP' 126 | 127 | # Http get 128 | # cmd = 'AT+HTTPCLIENT=2,0,"http://httpbin.org/ip","httpbin.org","/ip",1' 129 | 130 | ser.write(cmd.encode() + b"\r\n") 131 | print("=> {}".format(cmd)) 132 | 133 | time.sleep(0.5) # give the serial port sometime to receive the data 134 | 135 | while True: 136 | response = ser.readline() 137 | print("< ", response.replace(b'\r\n', b'').decode()) 138 | 139 | if response.strip().decode('ascii') in ['OK', 'ERROR']: 140 | break 141 | 142 | 143 | ser.close() -------------------------------------------------------------------------------- /examples/irq.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(asm)] 4 | 5 | use panic_halt as _; 6 | 7 | use core::fmt::Write; 8 | use longan_nano_playground::ByteMutWriter; 9 | 10 | use embedded_graphics::fonts::{Font8x16, Text}; 11 | use embedded_graphics::pixelcolor::Rgb565; 12 | use embedded_graphics::prelude::*; 13 | use embedded_graphics::primitives::Rectangle; 14 | use embedded_graphics::{primitive_style, text_style}; 15 | // gd32vf103_pac 16 | use gd32vf103xx_hal::pac; 17 | use gd32vf103xx_hal::prelude::*; 18 | use gd32vf103xx_hal::timer; 19 | use longan_nano_playground::{lcd, lcd_pins}; 20 | use riscv_rt::entry; 21 | #[macro_use(block)] 22 | extern crate nb; 23 | 24 | use embedded_hal::digital::v2::ToggleableOutputPin; 25 | 26 | use gd32vf103xx_hal::delay::McycleDelay; 27 | 28 | #[entry] 29 | fn main() -> ! { 30 | let dp = pac::Peripherals::take().unwrap(); 31 | 32 | // Configure clocks 33 | let mut rcu = dp 34 | .RCU 35 | .configure() 36 | .ext_hf_clock(8.mhz()) 37 | .sysclk(108.mhz()) 38 | .freeze(); 39 | let mut afio = dp.AFIO.constrain(&mut rcu); 40 | 41 | let gpioa = dp.GPIOA.split(&mut rcu); 42 | let gpiob = dp.GPIOB.split(&mut rcu); 43 | 44 | let lcd_pins = lcd_pins!(gpioa, gpiob); 45 | let mut lcd = lcd::configure(dp.SPI0, lcd_pins, &mut afio, &mut rcu); 46 | let (width, height) = (lcd.size().width as i32, lcd.size().height as i32); 47 | 48 | let mut blue = gpioa.pa2.into_push_pull_output(); 49 | 50 | macro_rules! cls { 51 | () => { 52 | Rectangle::new(Point::new(0, 0), Point::new(width - 1, height - 1)) 53 | .into_styled(primitive_style!(fill_color = Rgb565::BLACK)) 54 | .draw(&mut lcd) 55 | .unwrap() 56 | }; 57 | } 58 | // Clear screen 59 | cls!(); 60 | 61 | let style = text_style!( 62 | font = Font8x16, // Font6x8, 63 | text_color = Rgb565::WHITE, 64 | background_color = Rgb565::BLACK 65 | ); 66 | 67 | let mut buf = [0u8; 20 * 5]; 68 | let mut buf = ByteMutWriter::new(&mut buf[..]); 69 | 70 | let mut delay = McycleDelay::new(&rcu.clocks); 71 | 72 | // trigger timer at 0.1s interval 73 | let mut timer = timer::Timer::timer0(dp.TIMER0, 10.hz(), &mut rcu); 74 | // timer.listen(Event::Update); 75 | for _ in 0..10 { 76 | write!(&mut buf, ".").unwrap(); 77 | Text::new(buf.as_str(), Point::new(0, 0)) 78 | .into_styled(style) 79 | .draw(&mut lcd) 80 | .unwrap(); 81 | let _ = block!(timer.wait()); 82 | } 83 | 84 | cls!(); 85 | buf.clear(); 86 | let device_id = gd32vf103xx_hal::signature::device_id(); 87 | write!( 88 | &mut buf, 89 | "flash size: {}kb\nsram size: {}kb\ndev id[0]: {:x}\ndev id[1]: {:x}\ndev id[2]: {:x}", 90 | gd32vf103xx_hal::signature::flash_size_kb(), 91 | gd32vf103xx_hal::signature::sram_size_kb(), 92 | device_id[0], 93 | device_id[1], 94 | device_id[2] 95 | ) 96 | .unwrap(); 97 | Text::new(buf.as_str(), Point::new(0, 0)) 98 | .into_styled(style) 99 | .draw(&mut lcd) 100 | .unwrap(); 101 | 102 | delay.delay_ms(1000); 103 | 104 | // Clear screen 105 | cls!(); 106 | buf.clear(); 107 | write!(&mut buf, "led blinky").unwrap(); 108 | Text::new(buf.as_str(), Point::new(0, 0)) 109 | .into_styled(style) 110 | .draw(&mut lcd) 111 | .unwrap(); 112 | 113 | loop { 114 | blue.toggle().unwrap(); 115 | delay.delay_ms(200); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /examples/uart.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(asm)] 4 | 5 | use panic_halt as _; 6 | 7 | use core::fmt::Write; 8 | 9 | use embedded_graphics::fonts::{Font8x16, Text}; 10 | use embedded_graphics::pixelcolor::Rgb565; 11 | use embedded_graphics::prelude::*; 12 | use embedded_graphics::primitives::Rectangle; 13 | use embedded_graphics::{primitive_style, text_style}; 14 | // gd32vf103_pac 15 | use gd32vf103xx_hal::pac; 16 | use gd32vf103xx_hal::prelude::*; 17 | use gd32vf103xx_hal::timer; 18 | // use longan_nano::sprintln; 19 | // use longan_nano::{lcd, lcd_pins}; 20 | use longan_nano_playground::ByteMutWriter; 21 | use longan_nano_playground::{lcd, lcd_pins, sprintln}; 22 | use longan_nano_playground::stdout; 23 | 24 | use riscv_rt::entry; 25 | #[macro_use(block)] 26 | extern crate nb; 27 | 28 | use embedded_hal::digital::v2::ToggleableOutputPin; 29 | 30 | use gd32vf103xx_hal::delay::McycleDelay; 31 | 32 | #[entry] 33 | fn main() -> ! { 34 | let dp = pac::Peripherals::take().unwrap(); 35 | 36 | // Configure clocks 37 | let mut rcu = dp 38 | .RCU 39 | .configure() 40 | .ext_hf_clock(8.mhz()) 41 | .sysclk(108.mhz()) 42 | .freeze(); 43 | let mut afio = dp.AFIO.constrain(&mut rcu); 44 | 45 | let gpioa = dp.GPIOA.split(&mut rcu); 46 | let gpiob = dp.GPIOB.split(&mut rcu); 47 | 48 | // stdout via uart0. 115200 8N1 49 | stdout::configure( 50 | dp.USART0, 51 | gpioa.pa9, 52 | gpioa.pa10, 53 | 115200.bps(), 54 | &mut afio, 55 | &mut rcu, 56 | ); 57 | 58 | let lcd_pins = lcd_pins!(gpioa, gpiob); 59 | let mut lcd = lcd::configure(dp.SPI0, lcd_pins, &mut afio, &mut rcu); 60 | let (width, height) = (lcd.size().width as i32, lcd.size().height as i32); 61 | 62 | let mut blue = gpioa.pa2.into_push_pull_output(); 63 | 64 | macro_rules! cls { 65 | () => { 66 | Rectangle::new(Point::new(0, 0), Point::new(width - 1, height - 1)) 67 | .into_styled(primitive_style!(fill_color = Rgb565::BLACK)) 68 | .draw(&mut lcd) 69 | .unwrap() 70 | }; 71 | } 72 | // Clear screen 73 | cls!(); 74 | 75 | let style = text_style!( 76 | font = Font8x16, // Font6x8, 77 | text_color = Rgb565::WHITE, 78 | background_color = Rgb565::BLACK 79 | ); 80 | 81 | let mut buf = [0u8; 20 * 5]; 82 | let mut buf = ByteMutWriter::new(&mut buf[..]); 83 | 84 | let mut delay = McycleDelay::new(&rcu.clocks); 85 | 86 | // trigger timer at 0.1s interval 87 | let mut timer = timer::Timer::timer0(dp.TIMER0, 10.hz(), &mut rcu); 88 | // timer.listen(Event::Update); 89 | for _ in 0..10 { 90 | write!(&mut buf, ".").unwrap(); 91 | Text::new(buf.as_str(), Point::new(0, 0)) 92 | .into_styled(style) 93 | .draw(&mut lcd) 94 | .unwrap(); 95 | let _ = block!(timer.wait()); 96 | } 97 | 98 | cls!(); 99 | buf.clear(); 100 | let device_id = gd32vf103xx_hal::signature::device_id(); 101 | write!( 102 | &mut buf, 103 | "flash size: {}kb\nsram size: {}kb\ndev id[0]: {:x}\ndev id[1]: {:x}\ndev id[2]: {:x}", 104 | gd32vf103xx_hal::signature::flash_size_kb(), 105 | gd32vf103xx_hal::signature::sram_size_kb(), 106 | device_id[0], 107 | device_id[1], 108 | device_id[2] 109 | ) 110 | .unwrap(); 111 | Text::new(buf.as_str(), Point::new(0, 0)) 112 | .into_styled(style) 113 | .draw(&mut lcd) 114 | .unwrap(); 115 | 116 | delay.delay_ms(1000); 117 | 118 | // Clear screen 119 | cls!(); 120 | buf.clear(); 121 | write!(&mut buf, "led blinky").unwrap(); 122 | Text::new(buf.as_str(), Point::new(0, 0)) 123 | .into_styled(style) 124 | .draw(&mut lcd) 125 | .unwrap(); 126 | 127 | sprintln!("Hello World from UART!"); 128 | 129 | loop { 130 | sprintln!("Hello World from UART!"); 131 | 132 | blue.toggle().unwrap(); 133 | delay.delay_ms(200); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /examples/lcd-font.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(asm)] 4 | 5 | use panic_halt as _; 6 | 7 | use core::fmt::Write; 8 | use longan_nano_playground::ByteMutWriter; 9 | 10 | use embedded_graphics::fonts::{Font8x16, Text}; 11 | use embedded_graphics::pixelcolor::Rgb565; 12 | use embedded_graphics::prelude::*; 13 | use embedded_graphics::primitives::Rectangle; 14 | use embedded_graphics::{primitive_style, text_style}; 15 | // gd32vf103_pac 16 | use gd32vf103xx_hal::pac; 17 | use gd32vf103xx_hal::prelude::*; 18 | use gd32vf103xx_hal::timer; 19 | use longan_nano_playground::{lcd, lcd_pins}; 20 | use riscv_rt::entry; 21 | #[macro_use(block)] 22 | extern crate nb; 23 | 24 | /* 25 | ProFont7Point The 7 point size with a character size of 5x9 pixels. 26 | ProFont9Point The 9 point size with a character size of 6x11 pixels. 27 | ProFont10Point The 10 point size with a character size of 7x13 pixels. 28 | ProFont12Point The 12 point size with a character size of 8x15 pixels. 29 | ProFont14Point The 14 point size with a character size of 10x18 pixels. 30 | ProFont18Point The 18 point size with a character size of 12x22 pixels. 31 | ProFont24Point The 24 point size with a character size of 16x30 pixels. 32 | */ 33 | // use profont::ProFont10Point; 34 | 35 | use embedded_graphics::fonts::Font; 36 | use embedded_graphics::geometry::Size; 37 | 38 | #[derive(Clone, Copy)] 39 | pub struct ChnFont; 40 | 41 | impl Font for ChnFont { 42 | const FONT_IMAGE: &'static [u8] = include_bytes!("../font.raw"); 43 | const FONT_IMAGE_WIDTH: u32 = 16; 44 | const CHARACTER_SIZE: Size = Size::new(16, 16); 45 | const VARIABLE_WIDTH: bool = true; 46 | 47 | fn char_offset(c: char) -> u32 { 48 | unreachable!() 49 | } 50 | 51 | fn char_width(c: char) -> u32 { 52 | if (c as u16) < 128 { 53 | 8 54 | } else { 55 | 16 56 | } 57 | } 58 | 59 | fn character_pixel(c: char, x: u32, y: u32) -> bool { 60 | if (c as u16) < 128 { 61 | let map = &Self::FONT_IMAGE[16 * (c as usize)..16 + (c as usize) * 16]; 62 | return (map[y as usize] & (1 << (7 - x))) != 0; 63 | } 64 | 65 | const start: usize = 128 * 16; 66 | const step: usize = 2 * 16; 67 | let map = match c { 68 | '卧' => &Self::FONT_IMAGE[start..start + 1 * step], 69 | '槽' => &Self::FONT_IMAGE[start + 1 * step..start + 2 * step], 70 | '艹' => &Self::FONT_IMAGE[start + 2 * step..start + 3 * step], 71 | _ => { 72 | return false; 73 | } 74 | }; 75 | 76 | if x >= 8 { 77 | (map[y as usize * 2 + 1] & (1 << (15 - x))) != 0 78 | } else { 79 | (map[y as usize * 2] & (1 << (7 - x))) != 0 80 | } 81 | } 82 | } 83 | 84 | // rtc 85 | use gd32vf103xx_hal::rtc::Rtc; 86 | 87 | #[entry] 88 | fn main() -> ! { 89 | let dp = pac::Peripherals::take().unwrap(); 90 | 91 | // Configure clocks 92 | let mut rcu = dp 93 | .RCU 94 | .configure() 95 | .ext_hf_clock(8.mhz()) 96 | .sysclk(108.mhz()) 97 | .freeze(); 98 | let mut afio = dp.AFIO.constrain(&mut rcu); 99 | 100 | let gpioa = dp.GPIOA.split(&mut rcu); 101 | let gpiob = dp.GPIOB.split(&mut rcu); 102 | 103 | let lcd_pins = lcd_pins!(gpioa, gpiob); 104 | let mut lcd = lcd::configure(dp.SPI0, lcd_pins, &mut afio, &mut rcu); 105 | let (width, height) = (lcd.size().width as i32, lcd.size().height as i32); 106 | 107 | // Clear screen 108 | Rectangle::new(Point::new(0, 0), Point::new(width - 1, height - 1)) 109 | .into_styled(primitive_style!(fill_color = Rgb565::BLACK)) 110 | .draw(&mut lcd) 111 | .unwrap(); 112 | 113 | let style = text_style!( 114 | font = ChnFont, // Font6x8, 115 | text_color = Rgb565::WHITE, 116 | background_color = Rgb565::BLACK 117 | ); 118 | 119 | // let max_duty = pwm.try_get_max_duty().unwrap(); 120 | 121 | // 160 / 8 = 20 chars, per line 122 | let mut buf = [0u8; 20]; 123 | let mut buf = ByteMutWriter::new(&mut buf[..]); 124 | 125 | // Create a text at position (20, 30) and draw it using style defined above 126 | Text::new("卧槽艹~ABCD卧卧槽", Point::new(0, 0)) 127 | .into_styled(style) 128 | .draw(&mut lcd) 129 | .unwrap(); 130 | 131 | loop {} 132 | } 133 | -------------------------------------------------------------------------------- /examples/cpuinfo.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(asm)] 4 | 5 | use panic_halt as _; 6 | 7 | use core::fmt::Write; 8 | 9 | // for LCD 10 | use embedded_graphics::fonts::{Font8x16, Text}; 11 | use embedded_graphics::pixelcolor::Rgb565; 12 | use embedded_graphics::prelude::*; 13 | use embedded_graphics::primitives::Rectangle; 14 | use embedded_graphics::{primitive_style, text_style}; 15 | // gd32vf103_pac 16 | use gd32vf103xx_hal::delay; 17 | use gd32vf103xx_hal::pac; 18 | use gd32vf103xx_hal::prelude::*; 19 | use longan_nano_playground::ByteMutWriter; 20 | use longan_nano_playground::{lcd, lcd_pins}; 21 | use riscv_rt::entry; 22 | 23 | #[entry] 24 | fn main() -> ! { 25 | let dp = pac::Peripherals::take().unwrap(); 26 | 27 | // Configure clocks 28 | let mut rcu = dp 29 | .RCU 30 | .configure() 31 | .ext_hf_clock(8.mhz()) 32 | .sysclk(108.mhz()) 33 | .freeze(); 34 | let mut afio = dp.AFIO.constrain(&mut rcu); 35 | 36 | let gpioa = dp.GPIOA.split(&mut rcu); 37 | let gpiob = dp.GPIOB.split(&mut rcu); 38 | 39 | let lcd_pins = lcd_pins!(gpioa, gpiob); 40 | let mut lcd = lcd::configure(dp.SPI0, lcd_pins, &mut afio, &mut rcu); 41 | let (width, height) = (lcd.size().width as i32, lcd.size().height as i32); 42 | 43 | macro_rules! cls { 44 | () => { 45 | Rectangle::new(Point::new(0, 0), Point::new(width - 1, height - 1)) 46 | .into_styled(primitive_style!(fill_color = Rgb565::BLACK)) 47 | .draw(&mut lcd) 48 | .unwrap() 49 | }; 50 | } 51 | // Clear screen 52 | cls!(); 53 | 54 | let style = text_style!( 55 | font = Font8x16, // Font6x8, 56 | text_color = Rgb565::WHITE, 57 | background_color = Rgb565::BLACK 58 | ); 59 | 60 | // 160 / 8 = 20 chars, per line 61 | let mut buf = [0u8; 20 * 5]; 62 | let mut buf = ByteMutWriter::new(&mut buf[..]); 63 | 64 | // delay using mcycle 65 | let mut delay = delay::McycleDelay::new(&rcu.clocks); 66 | 67 | // Create a text at position (20, 30) and draw it using style defined above 68 | Text::new(" Hello from Rust! ", Point::new(0, 0)) 69 | .into_styled(style) 70 | .draw(&mut lcd) 71 | .unwrap(); 72 | 73 | delay.delay_ms(2_000_u16); 74 | 75 | buf.clear(); 76 | let _ = writeln!(buf, "misa: {:08x}", misa()); 77 | let _ = writeln!(buf, "mvendorid: {:08x}", mvendorid()); 78 | let _ = writeln!(buf, "marchid: {:08x}", marchid()); 79 | let _ = writeln!(buf, "mimpid: {:08x}", mimpid()); 80 | let _ = writeln!(buf, "mhartid: {:08x}", mhartid()); 81 | 82 | Text::new(buf.as_str(), Point::new(0, 0)) 83 | .into_styled(style) 84 | .draw(&mut lcd) 85 | .unwrap(); 86 | 87 | loop {} 88 | } 89 | 90 | fn misa() -> u32 { 91 | let mut x: u32 = 0; 92 | unsafe { 93 | asm!( 94 | "csrr a0, {mcsr}", 95 | "mv {x}, a0", 96 | x = inout(reg) x, 97 | mcsr = const 0x301, 98 | options(nostack) 99 | ); 100 | } 101 | x 102 | } 103 | 104 | fn mvendorid() -> u32 { 105 | let mut x: u32 = 0; 106 | unsafe { 107 | asm!( 108 | "csrr a0, {mcsr}", 109 | "mv {x}, a0", 110 | x = inout(reg) x, 111 | mcsr = const 0xf11, 112 | options(nostack) 113 | ); 114 | } 115 | x 116 | } 117 | 118 | fn marchid() -> u32 { 119 | let mut x: u32 = 0; 120 | unsafe { 121 | asm!( 122 | "csrr a0, {mcsr}", 123 | "mv {x}, a0", 124 | x = inout(reg) x, 125 | mcsr = const 0xf12, 126 | options(nostack) 127 | ); 128 | } 129 | x 130 | } 131 | 132 | fn mimpid() -> u32 { 133 | let mut x: u32 = 0; 134 | unsafe { 135 | asm!( 136 | "csrr a0, {mcsr}", 137 | "mv {x}, a0", 138 | x = inout(reg) x, 139 | mcsr = const 0xf13, 140 | options(nostack) 141 | ); 142 | } 143 | x 144 | } 145 | fn mhartid() -> u32 { 146 | let mut x: u32 = 0; 147 | unsafe { 148 | asm!( 149 | "csrr a0, {mcsr}", 150 | "mv {x}, a0", 151 | x = inout(reg) x, 152 | mcsr = const 0xf14, 153 | options(nostack) 154 | ); 155 | } 156 | x 157 | } 158 | -------------------------------------------------------------------------------- /examples/rtc.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(asm)] 4 | 5 | use panic_halt as _; 6 | 7 | use core::fmt::Write; 8 | use longan_nano_playground::ByteMutWriter; 9 | 10 | use embedded_graphics::fonts::{Font8x16, Text}; 11 | use embedded_graphics::pixelcolor::Rgb565; 12 | use embedded_graphics::prelude::*; 13 | use embedded_graphics::primitives::Rectangle; 14 | use embedded_graphics::{primitive_style, text_style}; 15 | // gd32vf103_pac 16 | use gd32vf103xx_hal::pac; 17 | use gd32vf103xx_hal::prelude::*; 18 | use gd32vf103xx_hal::timer; 19 | use longan_nano_playground::{lcd, lcd_pins}; 20 | use riscv_rt::entry; 21 | #[macro_use(block)] 22 | extern crate nb; 23 | 24 | // rtc 25 | use gd32vf103xx_hal::rtc::Rtc; 26 | 27 | #[entry] 28 | fn main() -> ! { 29 | let dp = pac::Peripherals::take().unwrap(); 30 | 31 | // Configure clocks 32 | let mut rcu = dp 33 | .RCU 34 | .configure() 35 | .ext_hf_clock(8.mhz()) 36 | .sysclk(108.mhz()) 37 | .freeze(); 38 | let mut afio = dp.AFIO.constrain(&mut rcu); 39 | 40 | let gpioa = dp.GPIOA.split(&mut rcu); 41 | let gpiob = dp.GPIOB.split(&mut rcu); 42 | 43 | let lcd_pins = lcd_pins!(gpioa, gpiob); 44 | let mut lcd = lcd::configure(dp.SPI0, lcd_pins, &mut afio, &mut rcu); 45 | let (width, height) = (lcd.size().width as i32, lcd.size().height as i32); 46 | 47 | // Clear screen 48 | Rectangle::new(Point::new(0, 0), Point::new(width - 1, height - 1)) 49 | .into_styled(primitive_style!(fill_color = Rgb565::BLACK)) 50 | .draw(&mut lcd) 51 | .unwrap(); 52 | 53 | let style = text_style!( 54 | font = Font8x16, // Font6x8, 55 | text_color = Rgb565::WHITE, 56 | background_color = Rgb565::BLACK 57 | ); 58 | 59 | // let max_duty = pwm.try_get_max_duty().unwrap(); 60 | 61 | // 160 / 8 = 20 chars, per line 62 | let mut buf = [0u8; 20]; 63 | let mut buf = ByteMutWriter::new(&mut buf[..]); 64 | 65 | // Create a text at position (20, 30) and draw it using style defined above 66 | /*Text::new(" Hello from Rust! ", Point::new(0, 0)) 67 | .into_styled(style) 68 | .draw(&mut lcd) 69 | .unwrap(); 70 | */ 71 | 72 | { 73 | buf.clear(); 74 | write!( 75 | &mut buf, 76 | "flash size: {}kb", 77 | gd32vf103xx_hal::signature::flash_size_kb() 78 | ) 79 | .unwrap(); 80 | } 81 | Text::new(buf.as_str(), Point::new(0, 16 * 0)) 82 | .into_styled(style) 83 | .draw(&mut lcd) 84 | .unwrap(); 85 | 86 | { 87 | buf.clear(); 88 | write!( 89 | &mut buf, 90 | "sram size: {}kb", 91 | gd32vf103xx_hal::signature::sram_size_kb() 92 | ) 93 | .unwrap(); 94 | } 95 | Text::new(buf.as_str(), Point::new(0, 16 * 1)) 96 | .into_styled(style) 97 | .draw(&mut lcd) 98 | .unwrap(); 99 | 100 | { 101 | let device_id = gd32vf103xx_hal::signature::device_id(); 102 | for i in 0..3 { 103 | buf.clear(); 104 | write!(&mut buf, "dev id[{}]: {:08x}", i, device_id[i]).unwrap(); 105 | Text::new(buf.as_str(), Point::new(0, 16 * (2 + i as i32))) 106 | .into_styled(style) 107 | .draw(&mut lcd) 108 | .unwrap(); 109 | } 110 | } 111 | 112 | /* 113 | Text::new("booting...", Point::new(0, 12 * 5)) 114 | .into_styled(style) 115 | .draw(&mut lcd) 116 | .unwrap(); 117 | */ 118 | 119 | // rcu.clocks.timer0() 120 | // 以多高频率触发 121 | // 0.1s 122 | let mut timer = timer::Timer::timer0(dp.TIMER0, 20.hz(), &mut rcu); 123 | // timer.listen(Event::Update); 124 | for _ in 0..50 { 125 | let _ = block!(timer.wait()); 126 | } 127 | 128 | // Clear screen 129 | Rectangle::new(Point::new(0, 0), Point::new(width - 1, height - 1)) 130 | .into_styled(primitive_style!(fill_color = Rgb565::BLACK)) 131 | .draw(&mut lcd) 132 | .unwrap(); 133 | 134 | let mut pmu = dp.PMU; 135 | let mut bak_dom = dp.BKP.configure(&mut rcu, &mut pmu); 136 | let rtc = Rtc::rtc(dp.RTC, &mut bak_dom); 137 | 138 | loop { 139 | let _ = block!(timer.wait()); 140 | 141 | buf.clear(); 142 | let ctime = rtc.current_time(); 143 | if ctime > 60 * 60 { 144 | write!( 145 | &mut buf, 146 | " uptime: {}:{:02}:{:02}", 147 | ctime / 3600, 148 | ctime % 3600 / 60, 149 | ctime % 60 150 | ) 151 | .unwrap(); 152 | } else if ctime > 60 { 153 | write!(&mut buf, " uptime: {}:{:02}", ctime / 60, ctime % 60).unwrap(); 154 | } else { 155 | write!(&mut buf, " uptime: {}", ctime).unwrap(); 156 | } 157 | Text::new(buf.as_str(), Point::new(0, height / 2 - 8)) 158 | .into_styled(style) 159 | .draw(&mut lcd) 160 | .unwrap(); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /examples/adc.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(asm)] 4 | 5 | use panic_halt as _; 6 | 7 | use core::fmt::Write; 8 | 9 | use embedded_hal::digital::v2::ToggleableOutputPin; 10 | use nb::block; 11 | use riscv_rt::entry; 12 | 13 | use embedded_graphics::fonts::{Font6x12, Font8x16, Text}; 14 | use embedded_graphics::pixelcolor::Rgb565; 15 | use embedded_graphics::prelude::*; 16 | use embedded_graphics::primitives::Rectangle; 17 | use embedded_graphics::{primitive_style, text_style}; 18 | 19 | // gd32vf103_pac 20 | use gd32vf103xx_hal::delay::McycleDelay; 21 | use gd32vf103xx_hal::pac; 22 | use gd32vf103xx_hal::prelude::*; 23 | use gd32vf103xx_hal::timer; 24 | // board support 25 | use longan_nano_playground::adc::{self, Adc, Temperature, Vrefint}; 26 | use longan_nano_playground::{lcd, lcd_pins, sprintln}; 27 | use longan_nano_playground::{stdout, ByteMutWriter}; 28 | 29 | #[entry] 30 | fn main() -> ! { 31 | let dp = pac::Peripherals::take().unwrap(); 32 | 33 | // Configure clocks 34 | let mut rcu = dp 35 | .RCU 36 | .configure() 37 | .ext_hf_clock(8.mhz()) 38 | .sysclk(108.mhz()) 39 | .freeze(); 40 | let mut afio = dp.AFIO.constrain(&mut rcu); 41 | 42 | let gpioa = dp.GPIOA.split(&mut rcu); 43 | let gpiob = dp.GPIOB.split(&mut rcu); 44 | 45 | let mut delay = McycleDelay::new(&rcu.clocks); 46 | 47 | // stdout via uart0. 115200 8N1 48 | stdout::configure( 49 | dp.USART0, 50 | gpioa.pa9, 51 | gpioa.pa10, 52 | 115200.bps(), 53 | &mut afio, 54 | &mut rcu, 55 | ); 56 | // debug requires stdout configuration. 57 | 58 | let a0 = adc::AnalogPin(gpioa.pa0.into_analog()); 59 | let b0 = adc::AnalogPin(gpioa.pa1.into_analog()); 60 | // ADC 61 | 62 | let mut adc = Adc::adc0(dp.ADC0, &mut rcu); 63 | let config = adc::config::AdcConfig::default() 64 | .scan(adc::config::Scan::Enabled) 65 | .enable_inserted_channel(Default::default()); 66 | 67 | adc.apply_config(config); 68 | 69 | let vrefint = Vrefint::new().enable(&mut adc); 70 | let temp = Temperature::new().enable(&mut adc); 71 | 72 | adc.configure_inserted_channel(0, &temp, adc::config::SampleTime::Point_239_5); 73 | adc.configure_inserted_channel(1, &vrefint, adc::config::SampleTime::Point_239_5); 74 | adc.configure_inserted_channel(2, &a0, adc::config::SampleTime::Point_239_5); 75 | //adc.configure_inserted_channel(3, &b0, adc::config::SampleTime::Point_239_5); 76 | 77 | let mut adc = adc.enable(); 78 | adc.calibrate(); 79 | 80 | //adc.enable_software_trigger(); 81 | //adc.wait_for_conversion(); 82 | 83 | //sprintln!("ADC READ => 0x{:08x}", adc.read0()); 84 | //sprintln!("ADC READ => 0x{:08x}", adc.read1()); 85 | /* 86 | 87 | for _ in 0..10 { 88 | delay.delay_ms(100); 89 | let temperature = (1.45 - adc.read0() as f32 * 3.3 / 4096.0) * 1000.0 / 4.1 + 25.0; 90 | let vref_value = adc.read1() as f32 * 3.3 / 4096.0; 91 | 92 | sprintln!("temp: {}C", temperature); 93 | sprintln!("vref: {}V", vref_value); 94 | } 95 | */ 96 | 97 | // LCD 98 | let lcd_pins = lcd_pins!(gpioa, gpiob); 99 | let mut lcd = lcd::configure(dp.SPI0, lcd_pins, &mut afio, &mut rcu); 100 | let (width, height) = (lcd.size().width as i32, lcd.size().height as i32); 101 | 102 | // LED 103 | let mut blue = gpioa.pa2.into_push_pull_output(); 104 | 105 | macro_rules! cls { 106 | () => { 107 | Rectangle::new(Point::new(0, 0), Point::new(width - 1, height - 1)) 108 | .into_styled(primitive_style!(fill_color = Rgb565::BLACK)) 109 | .draw(&mut lcd) 110 | .unwrap() 111 | }; 112 | } 113 | // Clear screen 114 | cls!(); 115 | 116 | let style = text_style!( 117 | font = Font6x12, // Font8x16, // 118 | text_color = Rgb565::GREEN, 119 | background_color = Rgb565::BLACK 120 | ); 121 | 122 | let mut buf = [0u8; 20 * 5]; 123 | let mut buf = ByteMutWriter::new(&mut buf[..]); 124 | 125 | // TIMER: 0.1s 126 | let mut timer = timer::Timer::timer0(dp.TIMER0, 10.hz(), &mut rcu); 127 | // timer.listen(Event::Update); 128 | for _ in 0..10 { 129 | write!(&mut buf, ".").unwrap(); 130 | Text::new(buf.as_str(), Point::new(0, 0)) 131 | .into_styled(style) 132 | .draw(&mut lcd) 133 | .unwrap(); 134 | let _ = block!(timer.wait()); 135 | } 136 | 137 | loop { 138 | sprintln!("Hello World from UART!"); 139 | 140 | blue.toggle().unwrap(); 141 | 142 | adc.enable_software_trigger(); 143 | adc.wait_for_conversion(); 144 | adc.clear_end_of_conversion_flag(); 145 | 146 | // delay.delay_ms(5); 147 | 148 | // {(V25 – Vtemperature) / Avg_Slope} + 25 149 | let raw_temp = adc.read_idata2(); 150 | let temperature = (1.45 - (raw_temp as f32 * 3.3 / 4096.0)) * 1000.0 / 4.1 + 25.0; 151 | let vref_value = adc.read_idata1() as f32 * 3.3 / 4096.0; 152 | //let a0_val = adc.read2(); 153 | 154 | /* 155 | // 单次 156 | adc.enable_software_trigger(); 157 | */ 158 | 159 | buf.clear(); 160 | 161 | writeln!(buf, "temp: {:.2}C", temperature).unwrap(); 162 | writeln!(buf, "Vref: {:.4}V", vref_value).unwrap(); 163 | writeln!(buf, "data0: {}", adc.read_idata0()).unwrap(); 164 | writeln!(buf, "data1: {}", adc.read_idata1()).unwrap(); 165 | writeln!(buf, "data2: {}", adc.read_idata2()).unwrap(); 166 | writeln!(buf, "data3: {}", adc.read_idata3()).unwrap(); 167 | 168 | //write!(buf, "a0: 0x{:04x}", a0_val).unwrap(); 169 | Text::new(buf.as_str(), Point::new(0, 0)) 170 | .into_styled(style) 171 | .draw(&mut lcd) 172 | .unwrap(); 173 | 174 | delay.delay_ms(500); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /examples/button.rs: -------------------------------------------------------------------------------- 1 | //! PA8 button. 2 | //! red: PC13, green: PA1, blue: PA2 3 | 4 | #![no_std] 5 | #![no_main] 6 | #![feature(asm)] 7 | 8 | use panic_halt as _; 9 | 10 | use core::fmt::Write; 11 | use longan_nano_playground::ByteMutWriter; 12 | 13 | use embedded_graphics::fonts::{Font8x16, Text}; 14 | use embedded_graphics::pixelcolor::Rgb565; 15 | use embedded_graphics::prelude::*; 16 | use embedded_graphics::primitives::Rectangle; 17 | use embedded_graphics::{primitive_style, text_style}; 18 | use longan_nano_playground::{lcd, lcd_pins}; 19 | // gd32vf103_pac 20 | use gd32vf103xx_hal::pac; 21 | use gd32vf103xx_hal::prelude::*; 22 | use gd32vf103xx_hal::timer; 23 | 24 | use riscv_rt::entry; 25 | #[macro_use(block)] 26 | extern crate nb; 27 | 28 | use embedded_hal::digital::v2::{InputPin, OutputPin, ToggleableOutputPin}; 29 | 30 | use gd32vf103xx_hal::delay::McycleDelay; 31 | 32 | use gd32vf103xx_hal::eclic::{EclicExt, Level, LevelPriorityBits, Priority, TriggerType}; 33 | use gd32vf103xx_hal::exti::{Exti, ExtiLine, TriggerEdge}; 34 | use gd32vf103xx_hal::pac::{Interrupt, ECLIC}; 35 | 36 | use gd32vf103xx_hal::gpio::{gpioa::PA8, gpioc::PC13}; 37 | use gd32vf103xx_hal::gpio::{Floating, Input, Output, PushPull}; 38 | 39 | static mut COUNT: i32 = 0; 40 | 41 | static mut BTN: Option>> = None; 42 | static mut RED: Option>> = None; 43 | 44 | #[entry] 45 | fn main() -> ! { 46 | let dp = pac::Peripherals::take().unwrap(); 47 | 48 | // Configure clocks 49 | let mut rcu = dp 50 | .RCU 51 | .configure() 52 | .ext_hf_clock(8.mhz()) 53 | .sysclk(108.mhz()) 54 | .freeze(); 55 | let mut afio = dp.AFIO.constrain(&mut rcu); 56 | 57 | let gpioa = dp.GPIOA.split(&mut rcu); 58 | let gpiob = dp.GPIOB.split(&mut rcu); 59 | let gpioc = dp.GPIOC.split(&mut rcu); 60 | 61 | // # LCD 62 | let lcd_pins = lcd_pins!(gpioa, gpiob); 63 | let mut lcd = lcd::configure(dp.SPI0, lcd_pins, &mut afio, &mut rcu); 64 | let (width, height) = (lcd.size().width as i32, lcd.size().height as i32); 65 | macro_rules! cls { 66 | () => { 67 | Rectangle::new(Point::new(0, 0), Point::new(width - 1, height - 1)) 68 | .into_styled(primitive_style!(fill_color = Rgb565::BLACK)) 69 | .draw(&mut lcd) 70 | .unwrap() 71 | }; 72 | } 73 | // Clear screen 74 | cls!(); 75 | 76 | let style = text_style!( 77 | font = Font8x16, // Font6x8, 78 | text_color = Rgb565::WHITE, 79 | background_color = Rgb565::BLACK 80 | ); 81 | 82 | let mut buf = [0u8; 20 * 5]; 83 | let mut buf = ByteMutWriter::new(&mut buf[..]); 84 | 85 | // # LED / BUTTON 86 | let mut blue = gpioa.pa1.into_push_pull_output(); 87 | let mut red = gpioc.pc13.into_push_pull_output(); 88 | let boot0_btn = gpioa.pa8.into_floating_input(); 89 | // off 90 | let _ = blue.set_high(); 91 | let _ = red.set_high(); 92 | 93 | // # Delay / Timer 94 | let mut delay = McycleDelay::new(&rcu.clocks); 95 | // trigger timer at 0.1s interval 96 | let mut timer = timer::Timer::timer0(dp.TIMER0, 10.hz(), &mut rcu); 97 | // timer.listen(Event::Update); 98 | for _ in 0..10 { 99 | write!(&mut buf, ".").unwrap(); 100 | Text::new(buf.as_str(), Point::new(0, 0)) 101 | .into_styled(style) 102 | .draw(&mut lcd) 103 | .unwrap(); 104 | let _ = block!(timer.wait()); 105 | } 106 | 107 | // IRQ 108 | ECLIC::reset(); 109 | ECLIC::set_threshold_level(Level::L0); 110 | // Use 3 bits for level, 1 for priority 111 | ECLIC::set_level_priority_bits(LevelPriorityBits::L3P1); 112 | 113 | // eclic_irq_enable(EXTI5_9_IRQn, 1, 1); 114 | ECLIC::setup( 115 | Interrupt::EXTI_LINE9_5, 116 | TriggerType::Level, 117 | Level::L1, 118 | Priority::P1, 119 | ); 120 | 121 | // gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOA, GPIO_PIN_SOURCE_8); 122 | afio.extiss(boot0_btn.port(), boot0_btn.pin_number()); 123 | 124 | // ECLIC::setup(Interrupt::TIMER0_UP, TriggerType::Level, Level::L0, Priority::P0); 125 | unsafe { ECLIC::unmask(Interrupt::EXTI_LINE9_5) }; 126 | // unsafe { ECLIC::unmask(Interrupt::TIMER0_UP) }; 127 | 128 | let mut exti = Exti::new(dp.EXTI); 129 | 130 | let extiline = ExtiLine::from_gpio_line(boot0_btn.pin_number()).unwrap(); 131 | exti.listen(extiline, TriggerEdge::Both); 132 | Exti::clear(extiline); 133 | 134 | unsafe { 135 | RED = Some(red); 136 | BTN = Some(boot0_btn); 137 | } 138 | 139 | unsafe { riscv::interrupt::enable() }; 140 | 141 | loop { 142 | // Clear screen 143 | // cls!(); 144 | buf.clear(); 145 | write!(&mut buf, "led blinky {}", unsafe { COUNT }).unwrap(); 146 | Text::new(buf.as_str(), Point::new(0, 0)) 147 | .into_styled(style) 148 | .draw(&mut lcd) 149 | .unwrap(); 150 | // blue.toggle().unwrap(); 151 | delay.delay_ms(200); 152 | } 153 | } 154 | 155 | #[allow(non_snake_case)] 156 | #[no_mangle] 157 | fn EXTI_LINE9_5() { 158 | let extiline = ExtiLine::from_gpio_line(8).unwrap(); 159 | // exti.listen(extiline, TriggerEdge::Both); 160 | if Exti::is_pending(extiline) { 161 | Exti::unpend(extiline); 162 | Exti::clear(extiline); 163 | unsafe { 164 | // press 165 | if BTN.as_ref().unwrap().is_high().unwrap() { 166 | COUNT += 1; 167 | RED.as_mut().unwrap().set_low().unwrap(); // ON 168 | } else { 169 | // release 170 | COUNT += 10; 171 | RED.as_mut().unwrap().set_high().unwrap(); 172 | } 173 | } 174 | } 175 | } 176 | 177 | #[allow(non_snake_case)] 178 | #[no_mangle] 179 | fn DefaultHandler() { 180 | let code = riscv::register::mcause::read().code() & 0xFFF; 181 | let cause = riscv::register::mcause::Exception::from(code); 182 | 183 | // sprintln!("DefaultHandler [code={}, cause={:?}]", code, cause); 184 | unsafe { 185 | COUNT += 1; 186 | } 187 | 188 | loop {} 189 | 190 | // loop {} 191 | } 192 | -------------------------------------------------------------------------------- /examples/bad_apple.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(asm)] 4 | 5 | use panic_halt as _; 6 | 7 | use core::fmt::Write; 8 | use longan_nano_playground::ByteMutWriter; 9 | 10 | use embedded_graphics::fonts::{Font8x16, Text}; 11 | use embedded_graphics::pixelcolor::Rgb565; 12 | use embedded_graphics::prelude::*; 13 | use embedded_graphics::primitives::Rectangle; 14 | use embedded_graphics::{primitive_style, text_style}; 15 | use embedded_hal::digital::v2::OutputPin; 16 | // gd32vf103_pac 17 | use gd32vf103xx_hal::pac; 18 | use gd32vf103xx_hal::prelude::*; 19 | // use gd32vf103xx_hal::timer; 20 | use longan_nano_playground::{lcd, lcd_pins}; 21 | use riscv_rt::entry; 22 | 23 | use embedded_graphics::image::{Image, ImageRaw}; 24 | 25 | use gd32vf103xx_hal::delay; 26 | 27 | // spi 28 | use gd32vf103xx_hal::spi::{Spi, MODE_0}; 29 | 30 | // sdcard 31 | use embedded_sdmmc as sdmmc; 32 | 33 | #[entry] 34 | fn main() -> ! { 35 | let dp = pac::Peripherals::take().unwrap(); 36 | 37 | // Configure clocks 38 | let mut rcu = dp 39 | .RCU 40 | .configure() 41 | .ext_hf_clock(8.mhz()) 42 | .sysclk(108.mhz()) 43 | .freeze(); 44 | let mut afio = dp.AFIO.constrain(&mut rcu); 45 | 46 | let gpioa = dp.GPIOA.split(&mut rcu); 47 | let gpiob = dp.GPIOB.split(&mut rcu); 48 | 49 | let lcd_pins = lcd_pins!(gpioa, gpiob); 50 | let mut lcd = lcd::configure(dp.SPI0, lcd_pins, &mut afio, &mut rcu); 51 | let (width, height) = (lcd.size().width as i32, lcd.size().height as i32); 52 | 53 | macro_rules! cls { 54 | () => { 55 | cls!(Rgb565::BLACK) 56 | }; 57 | ($color:path) => { 58 | Rectangle::new(Point::new(0, 0), Point::new(width - 1, height - 1)) 59 | .into_styled(primitive_style!(fill_color = $color)) 60 | .draw(&mut lcd) 61 | .unwrap() 62 | }; 63 | } 64 | 65 | cls!(); 66 | 67 | let style = text_style!( 68 | font = Font8x16, // Font6x8, 69 | text_color = Rgb565::WHITE, 70 | background_color = Rgb565::BLACK 71 | ); 72 | 73 | // 160 / 8 = 20 chars, per line 74 | let mut buf = [0u8; 20 * 5]; 75 | let mut buf = ByteMutWriter::new(&mut buf[..]); 76 | 77 | Text::new(" Hello from Rust! ", Point::new(0, 0)) 78 | .into_styled(style) 79 | .draw(&mut lcd) 80 | .unwrap(); 81 | 82 | // rcu.clocks.timer0() 83 | // triggers timer at 10Hz = 0.1s 84 | // let timer = timer::Timer::timer0(dp.TIMER0, 10.hz(), &mut rcu); 85 | // let mut delay = delay::Delay::::new(timer); 86 | let mut delay = delay::McycleDelay::new(&rcu.clocks); 87 | 88 | delay.delay_ms(2500_u16); 89 | cls!(); 90 | 91 | // SPI1_SCK(PB13), SPI1_MISO(PB14) and SPI1_MOSI(PB15) GPIO pin configuration 92 | let spi = Spi::spi1( 93 | dp.SPI1, 94 | ( 95 | gpiob.pb13.into_alternate_push_pull(), 96 | gpiob.pb14.into_floating_input(), 97 | gpiob.pb15.into_alternate_push_pull(), 98 | ), 99 | MODE_0, 100 | 20.mhz(), // 16.mzh() 101 | &mut rcu, 102 | ); 103 | 104 | let mut cs = gpiob.pb12.into_push_pull_output(); 105 | cs.set_low().unwrap(); 106 | 107 | let mut cntlr = sdmmc::Controller::new(sdmmc::SdMmcSpi::new(spi, cs), DummyTimeSource); 108 | 109 | buf.clear(); 110 | match cntlr.device().init() { 111 | Ok(_) => { 112 | match cntlr.device().card_size_bytes() { 113 | Ok(size) => { 114 | writeln!(buf, "Device OK!\nCard size: {}mb", size / 1024 / 1024).unwrap() 115 | } 116 | Err(e) => writeln!(buf, "Err: {:?}", e).unwrap(), 117 | } 118 | for i in 0..3 { 119 | write!(buf, "Volume {}: ", i).unwrap(); 120 | match cntlr.get_volume(sdmmc::VolumeIdx(i)) { 121 | Ok(_) => writeln!(buf, "found").unwrap(), 122 | Err(_e) => { 123 | writeln!(buf, "none").unwrap(); 124 | break; 125 | } 126 | } 127 | } 128 | } 129 | Err(e) => writeln!(buf, "{:?}!", e).unwrap(), 130 | } 131 | Text::new(buf.as_str(), Point::new(0, 0)) 132 | .into_styled(style) 133 | .draw(&mut lcd) 134 | .unwrap(); 135 | delay.delay_ms(2_000_u16); 136 | 137 | cls!(); 138 | buf.clear(); 139 | 140 | let mut img_buf = [0u8; 106 * 80 * 2]; 141 | // first volume 142 | 'outer: loop { 143 | let mut vol = cntlr.get_volume(sdmmc::VolumeIdx(0)).unwrap(); 144 | let dir = match cntlr.open_root_dir(&vol) { 145 | Ok(dir) => dir, 146 | Err(e) => { 147 | let _ = writeln!(buf, "E: {:?}", e); 148 | break; 149 | } 150 | }; 151 | // read sample 152 | /* 153 | let mut fp = 154 | match cntlr.open_file_in_dir(&mut vol, &dir, "sample.raw", sdmmc::Mode::ReadOnly) { 155 | Ok(fp) => fp, 156 | Err(e) => { 157 | let _ = writeln!(buf, "E: {:?}", e); 158 | break; 159 | } 160 | }; 161 | let nread = match cntlr.read(&mut vol, &mut fp, &mut img_buf[..]) { 162 | Ok(n) => n, 163 | Err(e) => { 164 | let _ = writeln!(buf, "E: {:?}", e); 165 | break; 166 | } 167 | }; 168 | let _ = writeln!(buf, "Read: {}", nread); 169 | let _ = writeln!(buf, "bytes: {:02x}{:02x}", img_buf[0], img_buf[1]); 170 | 171 | drop(fp); 172 | */ 173 | // Must use DOS 8.3 name 174 | let mut fp = 175 | match cntlr.open_file_in_dir(&mut vol, &dir, "badapple.raw", sdmmc::Mode::ReadOnly) { 176 | Ok(fp) => fp, 177 | Err(e) => { 178 | let _ = writeln!(buf, "E: {:?}", e); 179 | break; 180 | } 181 | }; 182 | 183 | loop { 184 | let nread = match cntlr.read(&mut vol, &mut fp, &mut img_buf[..]) { 185 | Ok(n) => n, 186 | Err(e) => { 187 | let _ = writeln!(buf, "E: {:?}", e); 188 | break 'outer; 189 | } 190 | }; 191 | if nread == 0 { 192 | let _ = writeln!(buf, "Done!"); 193 | break; 194 | } 195 | let _ = fp.seek_from_current(106 * 80 * 2); // skip 1 frame 196 | 197 | let raw_image: ImageRaw = ImageRaw::new(&img_buf[..], 106, 80); 198 | let image: Image<_, Rgb565> = Image::new(&raw_image, Point::new(26, 0)); 199 | image.draw(&mut lcd).unwrap(); 200 | delay.delay_ms(18_u16); // 1_000 / 24 201 | } 202 | 203 | break; 204 | } 205 | 206 | cls!(); 207 | loop { 208 | Text::new(buf.as_str(), Point::new(0, 0)) 209 | .into_styled(style) 210 | .draw(&mut lcd) 211 | .unwrap(); 212 | delay.delay_ms(2_000_u16); 213 | cls!(); 214 | let raw_image: ImageRaw = ImageRaw::new(&img_buf[..], 106, 80); 215 | let image: Image<_, Rgb565> = Image::new(&raw_image, Point::new(26, 0)); 216 | image.draw(&mut lcd).unwrap(); 217 | delay.delay_ms(2_000_u16); 218 | } 219 | } 220 | 221 | /// Zero time as fake time source. 222 | pub struct DummyTimeSource; 223 | 224 | impl sdmmc::TimeSource for DummyTimeSource { 225 | fn get_timestamp(&self) -> sdmmc::Timestamp { 226 | sdmmc::Timestamp::from_fat(0, 0) 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /examples/usbhid.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(asm)] 4 | 5 | use panic_halt as _; 6 | 7 | use riscv_rt::entry; 8 | 9 | use core::fmt::Write; 10 | 11 | use gd32vf103xx_hal::delay::McycleDelay; 12 | use gd32vf103xx_hal::pac; 13 | use gd32vf103xx_hal::prelude::*; 14 | 15 | use gd32vf103xx_hal::eclic::{EclicExt, Level, LevelPriorityBits, Priority, TriggerType}; 16 | use gd32vf103xx_hal::exti::{Exti, ExtiLine, InternalLine, TriggerEdge}; 17 | use gd32vf103xx_hal::pac::{Interrupt, ECLIC}; 18 | use gd32vf103xx_hal::timer; 19 | 20 | use embedded_hal::digital::v2::{InputPin, OutputPin, ToggleableOutputPin}; 21 | 22 | // LCD 23 | use embedded_graphics::fonts::{Font8x16, Text}; 24 | use embedded_graphics::pixelcolor::Rgb565; 25 | use embedded_graphics::prelude::*; 26 | use embedded_graphics::primitives::Rectangle; 27 | use embedded_graphics::{primitive_style, text_style}; 28 | use longan_nano_playground::{lcd, lcd_pins, ByteMutWriter}; 29 | 30 | use gd32vf103xx_hal::gpio::{ 31 | gpioa::PA2, gpioa::PA8, gpioc::PC13, Floating, Input, Output, PushPull, 32 | }; 33 | 34 | static mut COUNT: i32 = 0; 35 | 36 | static mut BTN: Option>> = None; 37 | static mut RED: Option>> = None; 38 | static mut BLUE: Option>> = None; 39 | static mut TIMER: Option> = None; 40 | 41 | #[entry] 42 | fn main() -> ! { 43 | let dp = pac::Peripherals::take().unwrap(); 44 | 45 | // Configure clocks 46 | // 48MHz, 72MHz, 96MHz for USB 47 | let mut rcu = dp 48 | .RCU 49 | .configure() 50 | .ext_hf_clock(8.mhz()) 51 | .sysclk(96.mhz()) 52 | .freeze(); 53 | assert!(rcu.clocks.usbclk_valid()); 54 | let mut afio = dp.AFIO.constrain(&mut rcu); 55 | 56 | let gpioa = dp.GPIOA.split(&mut rcu); 57 | let gpiob = dp.GPIOB.split(&mut rcu); 58 | let gpioc = dp.GPIOC.split(&mut rcu); 59 | 60 | let mut exti = Exti::new(dp.EXTI); 61 | 62 | let boot0_btn = gpioa.pa8.into_floating_input(); 63 | 64 | let usb_dp = gpioa.pa12; 65 | let usb_dm = gpioa.pa11; 66 | 67 | let mut delay = McycleDelay::new(&rcu.clocks); 68 | 69 | // # LCD 70 | let lcd_pins = lcd_pins!(gpioa, gpiob); 71 | let mut lcd = lcd::configure(dp.SPI0, lcd_pins, &mut afio, &mut rcu); 72 | let (width, height) = (lcd.size().width as i32, lcd.size().height as i32); 73 | macro_rules! cls { 74 | () => { 75 | Rectangle::new(Point::new(0, 0), Point::new(width - 1, height - 1)) 76 | .into_styled(primitive_style!(fill_color = Rgb565::BLACK)) 77 | .draw(&mut lcd) 78 | .unwrap() 79 | }; 80 | } 81 | // Clear screen 82 | cls!(); 83 | 84 | let style = text_style!( 85 | font = Font8x16, // Font6x8, 86 | text_color = Rgb565::WHITE, 87 | background_color = Rgb565::BLACK 88 | ); 89 | 90 | Text::new("USB-HID example", Point::new(0, 0)) 91 | .into_styled(style) 92 | .draw(&mut lcd) 93 | .unwrap(); 94 | delay.delay_ms(1_000); 95 | 96 | let mut buf = [0u8; 20 * 5]; 97 | let mut buf = ByteMutWriter::new(&mut buf[..]); 98 | // LEDs 99 | let mut green = gpioa.pa1.into_push_pull_output(); 100 | let mut red = gpioc.pc13.into_push_pull_output(); 101 | let mut blue = gpioa.pa2.into_push_pull_output(); 102 | green.set_high().unwrap(); 103 | red.set_high().unwrap(); 104 | blue.set_high().unwrap(); 105 | unsafe { 106 | RED = Some(red); 107 | BLUE = Some(blue); 108 | }; 109 | 110 | // interrupt 111 | ECLIC::reset(); 112 | ECLIC::set_threshold_level(Level::L0); 113 | unsafe { riscv::interrupt::enable() }; 114 | ECLIC::set_level_priority_bits(LevelPriorityBits::L2P2); 115 | 116 | unsafe { 117 | (*pac::RCU::ptr()) 118 | .ahben 119 | .modify(|_, w| w.usbfsen().set_bit()); 120 | } 121 | 122 | // usb_timer_init 123 | let mut timer2 = timer::Timer::timer2(dp.TIMER2, 1.hz(), &mut rcu); 124 | ECLIC::setup( 125 | Interrupt::TIMER2, 126 | TriggerType::Level, 127 | Level::L3, 128 | Priority::P0, 129 | ); 130 | unsafe { ECLIC::unmask(Interrupt::TIMER2) }; 131 | timer2.listen(timer::Event::Update); 132 | unsafe { 133 | TIMER = Some(timer2); 134 | } 135 | 136 | Text::new("DEBUG 1", Point::new(0, 20)) 137 | .into_styled(style) 138 | .draw(&mut lcd) 139 | .unwrap(); 140 | 141 | //systick_config 142 | unsafe { 143 | // mtimecmp 144 | let mtimecmp = (rcu.clocks.sysclk().0 / 4 / 100); 145 | (*pac::CTIMER::ptr()) 146 | .mtimecmp_lo 147 | .write(|w| w.bits(mtimecmp)); 148 | (*pac::CTIMER::ptr()).mtimecmp_hi.write(|w| w.bits(0)); 149 | } 150 | ECLIC::set_level_priority_bits(LevelPriorityBits::L2P2); 151 | ECLIC::setup( 152 | Interrupt::INT_TMR, 153 | TriggerType::Level, 154 | Level::L3, 155 | Priority::P3, 156 | ); 157 | unsafe { ECLIC::unmask(Interrupt::INT_TMR) }; 158 | // clear mtime 159 | unsafe { 160 | // mtime 161 | (*pac::CTIMER::ptr()).mtime_lo.write(|w| w.bits(0)); 162 | (*pac::CTIMER::ptr()).mtime_hi.write(|w| w.bits(0)); 163 | } 164 | 165 | // usb_intr_config 166 | ECLIC::setup( 167 | Interrupt::USBFS, 168 | TriggerType::Level, 169 | Level::L1, 170 | Priority::P0, 171 | ); 172 | unsafe { ECLIC::unmask(Interrupt::USBFS) }; 173 | 174 | // enable PMU 175 | buf.clear(); 176 | writeln!(buf, "pmuen={}", unsafe { 177 | (*pac::RCU::ptr()).apb1en.read().pmuen().bits() 178 | }); 179 | Text::new(buf.as_str(), Point::new(0, 40)) 180 | .into_styled(style) 181 | .draw(&mut lcd) 182 | .unwrap(); 183 | unsafe { 184 | (*pac::RCU::ptr()).apb1en.modify(|_, w| w.pmuen().set_bit()); 185 | } 186 | /// EXTI_18 187 | let extiline = ExtiLine::from_internal_line(InternalLine::UsbWakeup); 188 | Exti::clear(extiline); 189 | exti.listen(extiline, TriggerEdge::Rising); 190 | 191 | ECLIC::setup( 192 | Interrupt::USBFS_WKUP, 193 | TriggerType::Level, 194 | Level::L3, 195 | Priority::P0, 196 | ); 197 | unsafe { ECLIC::unmask(Interrupt::USBFS_WKUP) }; 198 | 199 | // button BOOT0 200 | afio.extiss(boot0_btn.port(), boot0_btn.pin_number()); 201 | ECLIC::setup( 202 | Interrupt::EXTI_LINE9_5, 203 | TriggerType::Level, 204 | Level::L1, 205 | Priority::P1, 206 | ); 207 | let extiline = ExtiLine::from_gpio_line(boot0_btn.pin_number()).unwrap(); 208 | unsafe { ECLIC::unmask(Interrupt::EXTI_LINE9_5) }; 209 | exti.listen(extiline, TriggerEdge::Both); 210 | Exti::clear(extiline); 211 | 212 | unsafe { 213 | BTN = Some(boot0_btn); 214 | } 215 | 216 | // unsafe { riscv::interrupt::enable() }; 217 | 218 | // usbd_init 219 | 220 | // - usb basic init 221 | // /* set the host channel numbers */ 222 | // usb_basic->num_pipe = USBFS_MAX_CHANNEL_COUNT; = 8 223 | // /* set the device endpoint numbers */ 224 | // usb_basic->num_ep = USBFS_MAX_EP_COUNT; = 4 225 | // /* USBFS core use embedded physical layer */ 226 | // usb_basic->phy_itf = USB_EMBEDDED_PHY; = 2 227 | 228 | // usb_basic->sof_enable = USB_SOF_OUTPUT; = 1 229 | // usb_basic->low_power = USB_LOW_POWER; == 1 230 | unsafe { 231 | // usb_regs.gr = USBFS_GLOBAL 232 | // usb_regs.hr = USBFS_HOST 0x5000_0400 233 | // usb_regs.dr = USBFS_DEVICE 0x5000_0800 234 | // usb_regs.HPCS = USBFS_HOST.hpcs 235 | // usb_regs.PWRCLKCTL = USBFS_PWRCLK.pwrclkctl 0x5000_0e00 236 | // (*pac::USBFS_GLOBAL::ptr()); 237 | } 238 | 239 | // assign device endpoint registers address 240 | // num_ep 设置? = 4 241 | // usb_regs->er_in in endpoint reg 242 | // USBFS_DEVICE.diep0ctl,diep1ctl,diep2ctl,diep3ctl 243 | 244 | // usb_regs->er_out 245 | // USBFS_DEVICE.doep0ctl, doep1ctl, doep2ctl, doep3ctl, 246 | 247 | // assign host pipe registers address 248 | // USB Host channel-x control register 249 | //usb_regs->pr num_pipe = 8 250 | // USBFS_HOST.hch0ctl, hch1ctl, .... hch7ctl 251 | // usb_regs->DFIFO 8 个, 无文档 252 | // 0x5000_0000 + 0x1000 + (i* 0x1000) 253 | 254 | // # usb basic init ends. 255 | 256 | // # usb_core_init 257 | // initailizes the USB core 258 | // initializes the USB controller registers and 259 | // prepares the core device mode or host mode operation 260 | 261 | unsafe { 262 | // disable USB global interrupt 263 | (*pac::USBFS_GLOBAL::ptr()) 264 | .gahbcs 265 | .modify(|_, w| w.ginten().clear_bit()); 266 | 267 | // phy layer (embeded) usb_basic.phy_itf 268 | // GUSBCS_EMBPHY bit6, undocumented 269 | (*pac::USBFS_GLOBAL::ptr()) 270 | .gusbcs 271 | .modify(|r, w| w.bits(r.bits() | (1 << 6))); 272 | 273 | // soft reset the core 274 | // enable core soft reset 275 | (*pac::USBFS_GLOBAL::ptr()) 276 | .grstctl 277 | .modify(|_, w| w.csrst().set_bit()); 278 | // wait for the core to be soft reset 279 | while (*pac::USBFS_GLOBAL::ptr()) 280 | .grstctl 281 | .read() 282 | .csrst() 283 | .bit_is_set() 284 | {} 285 | // wait for addtional 3 PHY clocks 286 | delay.delay_us(3); 287 | 288 | // sof en 289 | (*pac::USBFS_GLOBAL::ptr()).gccfg.write(|w| { 290 | w.pwron() 291 | .set_bit() 292 | .vbusacen() 293 | .set_bit() 294 | .vbusbcen() 295 | .set_bit() 296 | .vbusig() 297 | .set_bit() 298 | .sofoen() 299 | .set_bit() 300 | }); 301 | delay.delay_ms(20) 302 | // 303 | } 304 | // # end of usb_core_init 305 | 306 | // set device disconnect 307 | // usbd_disconnect only in non-otg mode 308 | // disconnect device for 3ms 309 | // usb_dev_disconnect 310 | unsafe { 311 | // soft disconnect 312 | (*pac::USBFS_DEVICE::ptr()) 313 | .dctl 314 | .modify(|r, w| w.sd().set_bit()); 315 | } 316 | delay.delay_ms(3); 317 | 318 | // initailizes device mode 319 | // usb_devcore_init 320 | // initialize USB core registers for device mode 321 | unsafe { 322 | // force to peripheral mode 323 | (*pac::USBFS_GLOBAL::ptr()) 324 | .gusbcs 325 | .modify(|_, w| w.fdm().clear_bit().fhm().clear_bit()); 326 | (*pac::USBFS_GLOBAL::ptr()) 327 | .gusbcs 328 | .modify(|_, w| w.fdm().set_bit()); 329 | 330 | // restart the Phy Clock (maybe don't need to...) 331 | (*pac::USBFS_PWRCLK::ptr()).pwrclkctl.write(|w| w.bits(0)); 332 | 333 | // config periodic frame interval to default value 334 | const FRAME_INTERVAL_80: u8 = 0; 335 | (*pac::USBFS_DEVICE::ptr()) 336 | .dcfg 337 | .modify(|_, w| w.eopft().bits(FRAME_INTERVAL_80).ds().bits(0)); 338 | 339 | //make sure all FIFOs are flushed 340 | // usb_txfifo_flush (&udev->regs, 0x10); 341 | // usb_rxfifo_flush (&udev->regs); 342 | // TODO 343 | 344 | // clear all pending device interrupts 345 | (*pac::USBFS_DEVICE::ptr()).diepinten.write(|w| w.bits(0)); 346 | (*pac::USBFS_DEVICE::ptr()).doepinten.write(|w| w.bits(0)); 347 | // (*pac::USBFS_DEVICE::ptr()).daepint.write(|w| w.bits(0)); 348 | (*pac::USBFS_DEVICE::ptr()).daepinten.write(|w| w.bits(0)); 349 | } 350 | 351 | // configure all IN/OUT endpoints 352 | // USBFS_DEVICE.diep0ctl,diep1ctl,diep2ctl,diep3ctl 353 | // USBFS_DEVICE.doep0ctl, doep1ctl, doep2ctl, doep3ctl, 354 | 355 | unsafe { 356 | let USBFS_DEVICE = &(*pac::USBFS_DEVICE::ptr()); 357 | 358 | if USBFS_DEVICE.diep0ctl.read().epen().bit_is_set() { 359 | USBFS_DEVICE 360 | .diep0ctl 361 | .modify(|_, w| w.epd().set_bit().snak().set_bit()); 362 | } else { 363 | USBFS_DEVICE.diep0ctl.write(|w| w.bits(0)); 364 | } 365 | 366 | // set IN endpoint transfer length to 0 367 | USBFS_DEVICE.diep0len.write(|w| w.bits(0)); 368 | // clear all pending IN endpoint interrupts 369 | USBFS_DEVICE.diep0intf.write(|w| w.bits(0xff)); 370 | 371 | if USBFS_DEVICE.doep0ctl.read().epen().bit_is_set() { 372 | USBFS_DEVICE.doep0ctl.modify(|r, w| { 373 | // w.epd().set_bit().snak().set_bit(); epd not doced 374 | w.bits(r.bits() | !((1 << 30) | (1 << 27))) 375 | }) 376 | } else { 377 | USBFS_DEVICE.doep0ctl.write(|w| w.bits(0)); 378 | } 379 | 380 | // set OUT endpoint transfer length to 0 381 | USBFS_DEVICE.doep0len.write(|w| w.bits(0)); 382 | // clear all pending OUT endpoint interrupts 383 | USBFS_DEVICE.doep0intf.write(|w| w.bits(0)); 384 | } 385 | 386 | // # usb_devint_enable 387 | unsafe { 388 | let USBFS_GLOBAL = &(*pac::USBFS_GLOBAL::ptr()); 389 | 390 | //clear any pending USB OTG interrupts 391 | USBFS_GLOBAL.gotgintf.write(|w| w.bits(0xFFFFFFFF)); 392 | 393 | // clear any pending interrupts 394 | USBFS_GLOBAL.gintf.write(|w| w.bits(0xBFFFFFFF)); 395 | 396 | // enable the USB wakeup and suspend interrupts 397 | USBFS_GLOBAL 398 | .ginten 399 | .write(|w| w.wkupie().set_bit().spie().set_bit()); 400 | // enable device_mode-related interrupts 401 | // FIFO mode / or DMA mode 402 | // NOTE: assume FIFO mode 403 | USBFS_GLOBAL.ginten.modify(|_, w| { 404 | w.rstie() 405 | .set_bit() 406 | .enumfie() 407 | .set_bit() 408 | .iepie() 409 | .set_bit() 410 | .oepie() 411 | .set_bit() 412 | .sofie() 413 | .set_bit() 414 | .mfie() 415 | .set_bit() 416 | }); 417 | 418 | // enable USB global interrupt 419 | USBFS_GLOBAL.gahbcs.modify(|_, w| w.ginten().set_bit()); 420 | } 421 | // end of usb_devcore_init 422 | 423 | // set device connect 424 | // usb_dev_connect - connect device 425 | unsafe { 426 | (*pac::USBFS_DEVICE::ptr()) 427 | .dctl 428 | .modify(|r, w| w.sd().set_bit()); 429 | } 430 | delay.delay_ms(3); 431 | 432 | loop { 433 | buf.clear(); 434 | write!(&mut buf, "INT {:08x} ", unsafe { COUNT }).unwrap(); 435 | Text::new(buf.as_str(), Point::new(0, 0)) 436 | .into_styled(style) 437 | .draw(&mut lcd) 438 | .unwrap(); 439 | // green.toggle().unwrap(); 440 | delay.delay_ms(200); 441 | } 442 | } 443 | 444 | #[allow(non_snake_case)] 445 | #[no_mangle] 446 | fn EXTI_LINE9_5() { 447 | let extiline = ExtiLine::from_gpio_line(8).unwrap(); 448 | // exti.listen(extiline, TriggerEdge::Both); 449 | if Exti::is_pending(extiline) { 450 | Exti::unpend(extiline); 451 | Exti::clear(extiline); 452 | 453 | unsafe { 454 | BLUE.as_mut().unwrap().toggle().unwrap(); 455 | 456 | if BTN.as_ref().unwrap().is_high().unwrap() { 457 | COUNT += 1; 458 | } else { 459 | // release 460 | COUNT -= 1; 461 | } 462 | } 463 | } 464 | } 465 | 466 | #[allow(non_snake_case)] 467 | #[no_mangle] 468 | fn INT_TMR() { 469 | // get usb data here? 470 | unsafe { 471 | // mtime 472 | (*pac::CTIMER::ptr()).mtime_lo.write(|w| w.bits(0)); 473 | (*pac::CTIMER::ptr()).mtime_hi.write(|w| w.bits(0)); 474 | } 475 | 476 | unsafe { 477 | // BLUE.as_mut().unwrap().toggle().unwrap(); 478 | // COUNT += 1; 479 | } 480 | } 481 | 482 | #[allow(non_snake_case)] 483 | #[no_mangle] 484 | fn USBFS() { 485 | unsafe { COUNT += 100 } 486 | 487 | unsafe { 488 | let USBFS_GLOBAL = &(*pac::USBFS_GLOBAL::ptr()); 489 | let intr = USBFS_GLOBAL.gintf.read().bits() | USBFS_GLOBAL.ginten.read().bits(); 490 | } 491 | 492 | // usbd_isr 493 | } 494 | 495 | #[allow(non_snake_case)] 496 | #[no_mangle] 497 | fn EXTI4() { 498 | unsafe { COUNT += 0x100 } 499 | } 500 | 501 | #[allow(non_snake_case)] 502 | #[no_mangle] 503 | fn USBFS_WKUP() { 504 | unsafe { COUNT += 0x10000 } 505 | 506 | let extiline = ExtiLine::from_internal_line(InternalLine::UsbWakeup); 507 | Exti::clear(extiline); 508 | } 509 | 510 | #[allow(non_snake_case)] 511 | #[no_mangle] 512 | fn TIMER2() { 513 | unsafe { 514 | if let Some(timer2) = &mut TIMER { 515 | timer2.clear_update_interrupt_flag(); 516 | } 517 | } 518 | unsafe { 519 | if let Some(led) = &mut RED { 520 | led.toggle().unwrap(); 521 | } 522 | } 523 | unsafe { COUNT += 0x1000000 } 524 | } 525 | 526 | #[allow(non_snake_case)] 527 | #[no_mangle] 528 | fn DefaultHandler() { 529 | let code = riscv::register::mcause::read().code() & 0xFFF; 530 | let cause = riscv::register::mcause::Exception::from(code); 531 | 532 | // sprintln!("DefaultHandler [code={}, cause={:?}]", code, cause); 533 | unsafe { 534 | COUNT += 0xffffff; 535 | } 536 | 537 | // loop {} 538 | 539 | loop {} 540 | } 541 | -------------------------------------------------------------------------------- /src/adc.rs: -------------------------------------------------------------------------------- 1 | //! Analog-to-digital converter 2 | //! 3 | use core::marker::PhantomData; 4 | 5 | // NOTE: embedded_hal's Channel is not suitable for ths 6 | use embedded_hal::adc::Channel; 7 | use gd32vf103xx_hal::gpio::gpioa::{PA0, PA1, PA2, PA3, PA4, PA5, PA6, PA7}; 8 | use gd32vf103xx_hal::gpio::gpiob::{PB0, PB1}; 9 | use gd32vf103xx_hal::gpio::gpioc::{PC0, PC1, PC2, PC3, PC4, PC5}; 10 | 11 | use gd32vf103xx_hal::gpio::Analog; 12 | 13 | use crate::hal::delay::McycleDelay; 14 | use crate::hal::rcu::Rcu; 15 | use crate::pac::{ADC0, ADC1, RCU}; 16 | 17 | use crate::sprintln; 18 | 19 | macro_rules! adc_pins { 20 | ($ADC:ident, $($input:ty => $chan:expr),+ $(,)*) => { 21 | $( 22 | impl Channel<$ADC> for AnalogPin<$input> { 23 | type ID = u8; 24 | 25 | fn channel() -> u8 { 26 | $chan 27 | } 28 | } 29 | )+ 30 | }; 31 | } 32 | 33 | /// Contains types related to ADC configuration 34 | #[allow(non_camel_case_types)] 35 | pub mod config { 36 | 37 | /// The number of cycles to sample a given channel for 38 | #[derive(Clone, Copy, Debug, PartialEq)] 39 | #[allow(non_camel_case_types)] 40 | #[repr(u8)] 41 | pub enum SampleTime { 42 | /// 1.5 cycles 43 | Point_1_5 = 0, 44 | /// 7.5 cycles 45 | Point_7_5, 46 | /// 13.5 cycles 47 | Point_13_5, 48 | /// 28.5 cycles 49 | Point_28_5, 50 | /// 41.5 cycles 51 | Point_41_5, 52 | /// 55.5 cycles 53 | Point_55_5, 54 | /// 71.5 cycles 55 | Point_71_5, 56 | /// 239.5 cycles 57 | Point_239_5, 58 | } 59 | 60 | impl Default for SampleTime { 61 | fn default() -> Self { 62 | SampleTime::Point_55_5 63 | } 64 | } 65 | 66 | /// Clock config for the ADC 67 | /// Check the datasheet for the maximum speed the ADC supports 68 | #[derive(Debug, Clone, Copy)] 69 | pub enum Clock { 70 | /// ADC prescaler select CK_APB2/2 71 | Apb2_div_2 = 0, 72 | /// ADC prescaler select CK_APB2/4 73 | Apb2_div_4 = 1, 74 | /// ADC prescaler select CK_APB2/6 75 | Apb2_div_6 = 2, 76 | /// ADC prescaler select CK_APB2/8 77 | Apb2_div_8 = 3, 78 | /// ADC prescaler select CK_APB2/12 79 | Apb2_div_12 = 5, 80 | /// ADC prescaler select CK_APB2/16 81 | Apb2_div_16 = 7, 82 | } 83 | 84 | /// Resolution to sample at 85 | #[derive(Debug, Clone, Copy)] 86 | #[repr(u8)] 87 | pub enum Resolution { 88 | /// 12-bit ADC resolution 89 | Twelve = 0, 90 | /// 10-bit ADC resolution 91 | Ten = 1, 92 | /// 8-bit ADC resolution 93 | Eight = 2, 94 | /// 6-bit ADC resolution 95 | Six = 3, 96 | } 97 | 98 | /// Regular group trigger source. 99 | #[derive(Debug, Clone, Copy)] 100 | pub enum RegularExternalTrigger { 101 | /// TIMER0 CH0 event select 102 | Timer0_Ch0 = 0b000, 103 | /// TIMER0 CH1 event select 104 | Timer0_Ch1 = 0b001, 105 | /// TIMER0 CH2 event select 106 | Timer0_Ch2 = 0b010, 107 | /// TIMER1 CH1 event select 108 | Timer1_Ch1 = 0b011, 109 | /// TIMER2 TRGO event select 110 | Timer2_Trgo = 0b100, 111 | /// TIMER3 CH3 event select 112 | Timer3_Ch3 = 0b101, 113 | /// external interrupt line 11 114 | Exti11 = 0b110, 115 | /// software trigger 116 | None = 0b111, 117 | } 118 | 119 | /// Inserted group trigger source. 120 | #[derive(Debug, Clone, Copy)] 121 | pub enum InsertedExternalTrigger { 122 | /// TIMER0 TRGO event select 123 | Timer2_Trgo = 0b000, 124 | /// TIMER0 CH3 event select 125 | Timer0_Ch3 = 0b001, 126 | /// TIMER1 TRGO event select 127 | Timer1_Trgo = 0b010, 128 | /// TIMER1 CH0 event select 129 | Timer1_Ch0 = 0b011, 130 | /// TIMER2 CH3 event select 131 | Timer2_Ch3 = 0b100, 132 | /// TIMER3 TRGO event select 133 | Timer3_Trgo = 0b101, 134 | /// external interrupt line 15 135 | Exti15 = 0b110, 136 | /// software trigger 137 | None = 0b111, 138 | } 139 | 140 | /// ADC data alignment 141 | #[derive(Debug, Clone, Copy)] 142 | pub enum Align { 143 | /// LSB alignment 144 | Right, 145 | /// MSB alignment 146 | Left, 147 | } 148 | 149 | impl From for bool { 150 | fn from(a: Align) -> bool { 151 | match a { 152 | Align::Right => false, 153 | Align::Left => true, 154 | } 155 | } 156 | } 157 | 158 | /// Scan enable/disable 159 | #[derive(Debug, Clone, Copy)] 160 | pub enum Scan { 161 | /// Scan mode disabled 162 | Disabled, 163 | /// Scan mode enabled 164 | Enabled, 165 | } 166 | impl From for bool { 167 | fn from(s: Scan) -> bool { 168 | match s { 169 | Scan::Disabled => false, 170 | Scan::Enabled => true, 171 | } 172 | } 173 | } 174 | 175 | /// Continuous mode enable/disable 176 | #[derive(Debug, Clone, Copy)] 177 | pub enum Continuous { 178 | /// Single mode, continuous disabled 179 | Single, 180 | /// Continuous mode enabled 181 | Continuous, 182 | } 183 | impl From for bool { 184 | fn from(c: Continuous) -> bool { 185 | match c { 186 | Continuous::Single => false, 187 | Continuous::Continuous => true, 188 | } 189 | } 190 | } 191 | 192 | /// Config for regular channel group 193 | #[derive(Debug, Clone, Copy)] 194 | pub struct RegularChannelGroupConfig { 195 | pub(crate) external_trigger: RegularExternalTrigger, 196 | } 197 | 198 | impl RegularChannelGroupConfig { 199 | /// change the external_trigger field 200 | pub fn external_trigger(mut self, external_trigger: RegularExternalTrigger) -> Self { 201 | self.external_trigger = external_trigger; 202 | self 203 | } 204 | } 205 | 206 | impl Default for RegularChannelGroupConfig { 207 | fn default() -> Self { 208 | Self { 209 | external_trigger: RegularExternalTrigger::None, 210 | } 211 | } 212 | } 213 | 214 | /// Inserted channel management 215 | #[derive(Debug, Clone, Copy)] 216 | pub enum Insertion { 217 | /// Disabled 218 | Triggered, 219 | /// Inserted channel group convert automatically 220 | Auto, 221 | } 222 | impl From for bool { 223 | fn from(c: Insertion) -> bool { 224 | match c { 225 | Insertion::Triggered => false, 226 | Insertion::Auto => true, 227 | } 228 | } 229 | } 230 | 231 | /// Config for inserted channel group 232 | #[derive(Debug, Clone, Copy)] 233 | pub struct InsertedChannelGroupConfig { 234 | pub(crate) external_trigger: InsertedExternalTrigger, 235 | pub(crate) insertion: Insertion, 236 | } 237 | 238 | impl InsertedChannelGroupConfig { 239 | /// change the external_trigger field 240 | pub fn external_trigger(mut self, external_trigger: InsertedExternalTrigger) -> Self { 241 | self.external_trigger = external_trigger; 242 | self 243 | } 244 | /// change the insertion field 245 | pub fn insertion(mut self, insertion: Insertion) -> Self { 246 | self.insertion = insertion; 247 | self 248 | } 249 | } 250 | 251 | impl Default for InsertedChannelGroupConfig { 252 | fn default() -> Self { 253 | Self { 254 | external_trigger: InsertedExternalTrigger::None, 255 | insertion: Insertion::Triggered, 256 | } 257 | } 258 | } 259 | 260 | // TODO: DMA for regular channel 261 | // TODO: 262 | 263 | /// Configuration for the adc. 264 | /// There are some additional parameters on the adc peripheral that can be 265 | /// added here when needed but this covers several basic usecases. 266 | #[derive(Debug, Clone, Copy)] 267 | pub struct AdcConfig { 268 | pub(crate) clock: Clock, 269 | pub(crate) resolution: Resolution, 270 | pub(crate) align: Align, 271 | pub(crate) scan: Scan, 272 | pub(crate) continuous: Continuous, 273 | pub(crate) regular_channel: Option, 274 | pub(crate) inserted_channel: Option, 275 | pub(crate) default_sample_time: SampleTime, 276 | } 277 | 278 | impl AdcConfig { 279 | /// change the clock field 280 | pub fn clock(mut self, clock: Clock) -> Self { 281 | self.clock = clock; 282 | self 283 | } 284 | /// change the resolution field 285 | pub fn resolution(mut self, resolution: Resolution) -> Self { 286 | self.resolution = resolution; 287 | self 288 | } 289 | /// change the align field 290 | pub fn align(mut self, align: Align) -> Self { 291 | self.align = align; 292 | self 293 | } 294 | /// change the scan field 295 | pub fn scan(mut self, scan: Scan) -> Self { 296 | self.scan = scan; 297 | self 298 | } 299 | /// change the continuous field 300 | pub fn continuous(mut self, continuous: Continuous) -> Self { 301 | self.continuous = continuous; 302 | self 303 | } 304 | /// change the external_trigger field 305 | pub fn enable_regular_channel(mut self, cfg: RegularChannelGroupConfig) -> Self { 306 | self.regular_channel = Some(cfg); 307 | self 308 | } 309 | /// change the external_trigger field 310 | pub fn enable_inserted_channel(mut self, cfg: InsertedChannelGroupConfig) -> Self { 311 | self.inserted_channel = Some(cfg); 312 | self 313 | } 314 | /// change the default_sample_time field 315 | pub fn default_sample_time(mut self, default_sample_time: SampleTime) -> Self { 316 | self.default_sample_time = default_sample_time; 317 | self 318 | } 319 | } 320 | 321 | impl Default for AdcConfig { 322 | fn default() -> Self { 323 | Self { 324 | clock: Clock::Apb2_div_16, 325 | resolution: Resolution::Twelve, 326 | align: Align::Right, 327 | scan: Scan::Disabled, 328 | continuous: Continuous::Single, 329 | regular_channel: None, 330 | inserted_channel: None, 331 | default_sample_time: SampleTime::Point_55_5, 332 | } 333 | } 334 | } 335 | } 336 | 337 | /// Enabled ADC (type state) 338 | pub struct Enabled; 339 | /// Disabled ADC (type state) 340 | pub struct Disabled; 341 | 342 | /// Type state trait for a enabled ADC channel 343 | pub trait ED {} 344 | /// Enabled ADC (type state) 345 | impl ED for Enabled {} 346 | /// Disabled ADC (type state) 347 | impl ED for Disabled {} 348 | 349 | /// Wrapper for an analog pin 350 | pub struct AnalogPin(pub PIN); 351 | 352 | adc_pins!(ADC0, 353 | PA0 => 0, 354 | PA1 => 1, 355 | PA2 => 2, 356 | PA3 => 3, 357 | PA4 => 4, 358 | PA5 => 5, 359 | PA6 => 6, 360 | PA7 => 7, 361 | PB0 => 8, 362 | PB1 => 9, 363 | PC0 => 10, 364 | PC1 => 11, 365 | PC2 => 12, 366 | PC3 => 13, 367 | PC4 => 14, 368 | PC5 => 15, 369 | ); 370 | 371 | /// Analog to Digital Converter 372 | pub struct Adc { 373 | rb: ADC, 374 | config: config::AdcConfig, 375 | _enabled: PhantomData, 376 | } 377 | 378 | impl Adc { 379 | /// Enables the ADC clock, resets the peripheral 380 | pub fn adc0(adc: ADC0, _rcu: &mut Rcu) -> Self { 381 | let mut adc = Self::default_from_rb(adc); 382 | 383 | let rcu = unsafe { core::mem::MaybeUninit::::uninit().assume_init() }; 384 | // TODO, use rcu.regs 385 | // rcu_config adc0en 386 | // enable ADC clock 387 | rcu.apb2en.modify(|_, w| w.adc0en().set_bit()); 388 | 389 | // config ADC clock 390 | adc.set_clock(adc.config.clock, _rcu); 391 | 392 | // adc_deinit 393 | adc.reset(_rcu); 394 | 395 | unsafe { 396 | // adc_mode_config, for single channel, use free mode 397 | adc.rb 398 | .ctl0 399 | .modify(|_, w| w.syncm().bits(SyncMode::Free as u8)); 400 | 401 | // reset inserted sequence 402 | adc.rb.isq.modify(|_, w| w.il().bits(0x00)); 403 | } 404 | adc 405 | } 406 | 407 | /// Creates ADC with default settings 408 | fn default_from_rb(rb: ADC0) -> Self { 409 | Self { 410 | rb, 411 | config: config::AdcConfig::default(), 412 | _enabled: PhantomData, 413 | } 414 | } 415 | 416 | fn set_clock(&mut self, clock: config::Clock, _rcu: &mut Rcu) { 417 | use self::config::Clock::*; 418 | 419 | let rcu = unsafe { core::mem::MaybeUninit::::uninit().assume_init() }; 420 | match clock { 421 | Apb2_div_2 | Apb2_div_4 | Apb2_div_6 | Apb2_div_8 => unsafe { 422 | rcu.cfg0 423 | .modify(|_, w| w.adcpsc_1_0().bits(clock as u8).adcpsc_2().clear_bit()); 424 | }, 425 | Apb2_div_12 | Apb2_div_16 => unsafe { 426 | rcu.cfg0.modify(|_, w| { 427 | w.adcpsc_1_0() 428 | .bits(clock as u8 >> 2) 429 | .adcpsc_2() 430 | .bit(clock as u8 & 0x1 == 0x1) 431 | }); 432 | }, 433 | } 434 | } 435 | 436 | /// Sets the sampling resolution 437 | pub fn set_resolution(&mut self, resolution: config::Resolution) { 438 | self.config.resolution = resolution; 439 | } 440 | 441 | /// Sets the DR register alignment to left or right 442 | pub fn set_align(&mut self, align: config::Align) { 443 | self.config.align = align; 444 | } 445 | 446 | /// Enables and disables scan mode 447 | pub fn set_scan(&mut self, scan: config::Scan) { 448 | self.config.scan = scan; 449 | } 450 | 451 | /// Enables and disables continuous mode 452 | pub fn set_continuous(&mut self, continuous: config::Continuous) { 453 | self.config.continuous = continuous; 454 | } 455 | 456 | fn configure(&mut self) { 457 | let config = &self.config; 458 | // ADC scan function enable 459 | unsafe { 460 | // resolution 461 | self.rb 462 | .ovsampctl 463 | .modify(|_, w| w.dres().bits(config.resolution as u8)); 464 | 465 | // data align 466 | self.rb.ctl1.modify(|_, w| w.dal().bit(config.align.into())); 467 | 468 | // scan mode 469 | self.rb.ctl0.modify(|_, w| w.sm().bit(config.scan.into())); 470 | 471 | // continuous mode 472 | self.rb 473 | .ctl1 474 | .modify(|_, w| w.ctn().bit(config.continuous.into())); 475 | 476 | // external trigger source 477 | if let Some(trigger_config) = config.regular_channel { 478 | self.rb 479 | .ctl1 480 | .modify(|_, w| w.etsrc().bits(trigger_config.external_trigger as u8)); 481 | self.rb.ctl1.modify(|_, w| w.eterc().set_bit()); 482 | } 483 | 484 | if let Some(trigger_config) = config.inserted_channel { 485 | self.rb 486 | .ctl0 487 | .modify(|_, w| w.ica().bit(trigger_config.insertion.into())); 488 | self.rb 489 | .ctl1 490 | .modify(|_, w| w.etsic().bits(trigger_config.external_trigger as u8)); 491 | self.rb.ctl1.modify(|_, w| w.eteic().set_bit()); 492 | } 493 | 494 | // TODO: discontinuous 495 | // TODO: constraints 496 | // - 单次转换模式只能有一个通道 RSQ0[4:0], ISQ3[4:0] 497 | // - 连续转换模式只能有一个通道 RSQ0[4:0], 只能regular 498 | // - 规则组扫描时必须 DMA 499 | // - 规则组和注入组不能同时工作在间断模式,同一时刻只能有一组被设置成间断模式。 500 | } 501 | } 502 | 503 | /// Applies all fields in AdcConfig 504 | pub fn apply_config(&mut self, config: config::AdcConfig) { 505 | self.config = config; 506 | } 507 | 508 | /// ADC sampling time config 509 | fn set_channel_sample_time(&mut self, channel: u8, sample_time: config::SampleTime) { 510 | match channel { 511 | 0..=9 => unsafe { 512 | let mask = !(0b111 << (3 * channel)); 513 | self.rb.sampt1.modify(|r, w| { 514 | let cleared = r.bits() & mask; 515 | let masked = (sample_time as u8 as u32) << (3 * channel); 516 | w.bits(cleared | masked) 517 | }); 518 | }, 519 | 10..=17 => unsafe { 520 | let mask = !(0b111 << (3 * (channel - 10))); 521 | self.rb.sampt0.modify(|r, w| { 522 | let cleared = r.bits() & mask; 523 | let masked = (sample_time as u8 as u32) << (3 * (channel - 10)); 524 | w.bits(cleared | masked) 525 | }); 526 | }, 527 | _ => unreachable!("invalid channel"), 528 | } 529 | } 530 | 531 | /// configure ADC regular channel 532 | /// 533 | /// 12 - Used in single mode 534 | pub fn configure_regular_channel( 535 | &mut self, 536 | rank: u8, 537 | _channel: &CHANNEL, 538 | sample_time: config::SampleTime, 539 | ) where 540 | CHANNEL: Channel, 541 | { 542 | let channel = CHANNEL::channel(); 543 | 544 | // ADC regular sequence config 545 | match rank { 546 | 0..=5 => unsafe { 547 | let mask = !(0b11111 << (5 * rank)); 548 | self.rb.rsq2.modify(|r, w| { 549 | let cleared = r.bits() & mask; 550 | w.bits(cleared | ((rank as u32) << (5 * rank))) 551 | }) 552 | }, 553 | 6..=11 => unsafe { 554 | let mask = !(0b11111 << (5 * (rank - 6))); 555 | self.rb.rsq1.modify(|r, w| { 556 | let cleared = r.bits() & mask; 557 | w.bits(cleared | ((rank as u32) << (5 * (rank - 6)))) 558 | }) 559 | }, 560 | 12..=15 => unsafe { 561 | let mask = !(0b11111 << (5 * (rank - 12))); 562 | self.rb.rsq0.modify(|r, w| { 563 | let cleared = r.bits() & mask; 564 | w.bits(cleared | ((rank as u32) << (5 * (rank - 12)))) 565 | }) 566 | }, 567 | _ => panic!("invalid rank"), 568 | } 569 | // ADC sampling time config 570 | self.set_channel_sample_time(channel, sample_time); 571 | } 572 | 573 | /// configure ADC inserted channel 574 | /// 575 | /// - 0 -> ISQ3 576 | /// - 1 -> ISQ2 577 | /// - 2 -> ISQ1 578 | /// - 3 -> ISQ0 579 | pub fn configure_inserted_channel( 580 | &mut self, 581 | rank: u8, 582 | _channel: &CHANNEL, 583 | sample_time: config::SampleTime, 584 | ) where 585 | CHANNEL: Channel, 586 | { 587 | // Check the sequence is long enough 588 | self.rb.isq.modify(|r, w| { 589 | let prev = r.il().bits(); 590 | if prev < rank { 591 | unsafe { w.il().bits(rank) } 592 | } else { 593 | w 594 | } 595 | }); 596 | 597 | let channel = CHANNEL::channel(); 598 | 599 | unsafe { 600 | // the channel number is written to these bits to select a channel 601 | // as the nth conversion in the inserted channel group 602 | // 603 | // Inserted channels are converted starting from (4 - IL[1:0] - 1), 604 | // if IL[1:0] length is less than 4. 605 | match rank { 606 | 0 => self.rb.isq.modify(|_, w| w.isq3().bits(channel)), 607 | 1 => self.rb.isq.modify(|_, w| w.isq2().bits(channel)), 608 | 2 => self.rb.isq.modify(|_, w| w.isq1().bits(channel)), 609 | 3 => self.rb.isq.modify(|_, w| w.isq0().bits(channel)), 610 | _ => panic!("invalid rank"), 611 | } 612 | } 613 | // ADC sampling time config 614 | self.set_channel_sample_time(channel, sample_time); 615 | } 616 | 617 | fn reset(&mut self, _rcu: &mut Rcu) { 618 | let rcu = unsafe { core::mem::MaybeUninit::::uninit().assume_init() }; 619 | rcu.apb2rst.modify(|_, w| w.adc0rst().set_bit()); 620 | rcu.apb2rst.modify(|_, w| w.adc0rst().clear_bit()); 621 | } 622 | 623 | /// Enables the adc 624 | pub fn enable(mut self) -> Adc { 625 | self.configure(); 626 | 627 | self.rb.ctl1.modify(|_, w| w.adcon().set_bit()); 628 | sprintln!("adc0 enabled"); 629 | Adc { 630 | rb: self.rb, 631 | config: self.config, 632 | _enabled: PhantomData, 633 | } 634 | } 635 | } 636 | 637 | impl Adc { 638 | /// Disable the ADC 639 | pub fn power_down(self) -> Adc { 640 | self.rb.ctl1.modify(|_, w| w.adcon().clear_bit()); 641 | Adc { 642 | rb: self.rb, 643 | config: self.config, 644 | _enabled: PhantomData, 645 | } 646 | } 647 | 648 | /// Enable software trigger for regular channel and inserted channel (if any) 649 | pub fn enable_software_trigger(&mut self) { 650 | if self.config.regular_channel.is_some() { 651 | self.rb.ctl1.modify(|_, w| w.swrcst().set_bit()); 652 | } 653 | if self.config.inserted_channel.is_some() { 654 | self.rb.ctl1.modify(|_, w| w.swicst().set_bit()); 655 | } 656 | } 657 | 658 | /// Wait for the conversion sequence to finished 659 | pub fn wait_for_conversion(&self) { 660 | while self.rb.stat.read().eoc().bit_is_clear() {} 661 | 662 | if self.config.inserted_channel.is_some() { 663 | while self.rb.stat.read().eoic().bit_is_clear() {} 664 | } 665 | } 666 | 667 | /// Resets the end-of-conversion flag, and optionally end-of-inserted-conversion flag 668 | pub fn clear_end_of_conversion_flag(&self) { 669 | self.rb.stat.modify(|_, w| { 670 | if self.config.inserted_channel.is_some() { 671 | w.eoc().clear_bit().eoic().clear_bit() 672 | } else { 673 | w.eoc().clear_bit() 674 | } 675 | }); 676 | } 677 | 678 | fn reset_calibrate(&mut self) { 679 | // reset the selected ADC1 calibration registers 680 | self.rb.ctl1.modify(|_, w| w.rstclb().set_bit()); 681 | while self.rb.ctl1.read().rstclb().bit_is_set() {} 682 | } 683 | 684 | /// Calibrates the ADC in single channel mode 685 | pub fn calibrate(&mut self) { 686 | self.reset_calibrate(); 687 | self.rb.ctl1.modify(|_, w| w.clb().set_bit()); 688 | while self.rb.ctl1.read().clb().bit_is_set() {} 689 | } 690 | 691 | /// Read data from regular channel 692 | pub fn read_rdata(&self) -> u16 { 693 | self.rb.rdata.read().rdata().bits() 694 | } 695 | 696 | /// Read data from inserted channel 0 697 | pub fn read_idata0(&self) -> u16 { 698 | self.rb.idata0.read().idatan().bits() 699 | } 700 | /// Read data from inserted channel 1 701 | pub fn read_idata1(&self) -> u16 { 702 | self.rb.idata1.read().idatan().bits() 703 | } 704 | /// Read data from inserted channel 2 705 | pub fn read_idata2(&self) -> u16 { 706 | self.rb.idata2.read().idatan().bits() 707 | } 708 | /// Read data from inserted channel 3 709 | pub fn read_idata3(&self) -> u16 { 710 | self.rb.idata3.read().idatan().bits() 711 | } 712 | } 713 | 714 | /// Internal temperature sensor 715 | pub struct Temperature { 716 | _marker: PhantomData, 717 | } 718 | 719 | impl Temperature { 720 | /// Internal temperature sensor 721 | // TODO: avoid creating multiple instences 722 | pub fn new() -> Self { 723 | Self { 724 | _marker: PhantomData, 725 | } 726 | } 727 | } 728 | 729 | impl Temperature { 730 | /// Enable temperature sensor channel 731 | pub fn enable(&mut self, adc: &mut Adc) -> Temperature { 732 | adc.rb.ctl1.modify(|_, w| w.tsvren().set_bit()); 733 | Temperature { 734 | _marker: PhantomData, 735 | } 736 | } 737 | } 738 | 739 | impl Channel for Temperature { 740 | type ID = u8; 741 | 742 | fn channel() -> u8 { 743 | 16 744 | } 745 | } 746 | 747 | /// Vref internal signal 748 | // internally connected to the ADC0_CH17 input channel 749 | pub struct Vrefint { 750 | _marker: PhantomData, 751 | } 752 | 753 | impl Vrefint { 754 | /// New Vref internal signal 755 | pub fn new() -> Self { 756 | Self { 757 | _marker: PhantomData, 758 | } 759 | } 760 | } 761 | 762 | impl Vrefint { 763 | /// Enable Vrefint sensor channel 764 | pub fn enable(self, adc: &mut Adc) -> Vrefint { 765 | adc.rb.ctl1.modify(|_, w| w.tsvren().set_bit()); 766 | Vrefint { 767 | _marker: PhantomData, 768 | } 769 | } 770 | } 771 | 772 | impl Channel for Vrefint { 773 | type ID = u8; 774 | 775 | fn channel() -> u8 { 776 | 17 777 | } 778 | } 779 | 780 | /// ADC sync mode 781 | #[repr(u8)] 782 | pub enum SyncMode { 783 | /// all the ADCs work independently 784 | Free = 0, 785 | /// ADC0 and ADC1 work in combined regular parallel + inserted parallel mode 786 | DualRegulalParallelInsertedParallel, 787 | /// ADC0 and ADC1 work in combined regular parallel + trigger rotation mode 788 | DualRegulalParallelInsertedRotation, 789 | /// ADC0 and ADC1 work in combined inserted parallel + follow-up fast mode 790 | DualInsertedParallelRegulalFollowupFast, 791 | /// ADC0 and ADC1 work in combined inserted parallel + follow-up slow mode 792 | DualInsertedParallelRegulalFollowupSlow, 793 | /// ADC0 and ADC1 work in inserted parallel mode only 794 | DualInsertedParallel, 795 | /// ADC0 and ADC1 work in regular parallel mode only 796 | DualRegulalParallel, 797 | /// ADC0 and ADC1 work in follow-up fast mode only 798 | DualRegulalFollowupFast, 799 | /// ADC0 and ADC1 work in follow-up slow mode only 800 | DualRegulalFollowupSlow, 801 | /// ADC0 and ADC1 work in trigger rotation mode only 802 | DualInsertedTriggerRotation, 803 | } 804 | 805 | /// Init ADC0 and ADC1 at once, enabling sync mode. 806 | pub fn adc01( 807 | _adc0: ADC0, 808 | _adc1: ADC1, 809 | _sync_mode: SyncMode, 810 | _delay: &mut McycleDelay, 811 | //prec: rec::Adc12, 812 | //clocks: &CoreClocks, 813 | ) -> (Adc, Adc) { 814 | unimplemented!("DMA required to use sync mode") 815 | } 816 | --------------------------------------------------------------------------------