diff --git a/Cargo.toml b/Cargo.toml index c5ac9559607aa925450126baf836d5eaa54effc0..ed263b39bef5dedc5d7e1d7a07fe2687fac724b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "bl-soc" version = "0.0.0" authors = [ "Mingrui Ma ", - "Zeqing Qin " + "Zeqing Qin ", ] edition = "2021" @@ -17,7 +17,7 @@ embedded-time = "0.12.1" embedded-io = "0.6.0" cfg-if = "1.0.0" # For backward compatibility only. -embedded-hal-027 ={ package = "embedded-hal" , version = "0.2.7" } +embedded-hal-027 = { package = "embedded-hal", version = "0.2.7" } as-slice = "0.2.1" [dev-dependencies] @@ -34,6 +34,8 @@ members = [ "examples/peripherals/pwm-demo", "examples/peripherals/spi-demo", "examples/peripherals/uart-demo", + "examples/peripherals/sdcard-demo", + "examples/peripherals/sdcard-gpt-demo", ] [features] diff --git a/examples/peripherals/sdcard-demo/Cargo.toml b/examples/peripherals/sdcard-demo/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..73939634296921bdbb3f5f9ee736f6e063849b2f --- /dev/null +++ b/examples/peripherals/sdcard-demo/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "sdcard-demo" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bl-soc = { path = "../../..", features = ["bl808"] } +base-address = "0.0.0" +panic-halt = "0.2.0" +embedded-time = "0.12.1" +embedded-hal = "1.0.0-rc.1" +riscv = "0.10.1" +display-interface-spi = "0.4.1" +mipidsi = "0.7.1" +embedded-graphics = "0.8.1" +embedded-sdmmc = "0.6.0" + +[dependencies.bl-rom-rt] +git = "https://gitee.com/rustsbi/bl-rom-rt" +branch = "main" +default-features = false +features = ["bl808-dsp"] diff --git a/examples/peripherals/sdcard-demo/build.rs b/examples/peripherals/sdcard-demo/build.rs new file mode 100644 index 0000000000000000000000000000000000000000..3509edb78b16f69c40351e7e21ee482e492753bc --- /dev/null +++ b/examples/peripherals/sdcard-demo/build.rs @@ -0,0 +1,3 @@ +fn main() { + println!("cargo:rustc-link-arg=-Tbl-rom-rt.ld"); +} diff --git a/examples/peripherals/sdcard-demo/src/main.rs b/examples/peripherals/sdcard-demo/src/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..fb2ec1edc7c1db286f42c72ce0bf4224321455f4 --- /dev/null +++ b/examples/peripherals/sdcard-demo/src/main.rs @@ -0,0 +1,82 @@ +// This is a example of how to read a SD card with a MBR partition table and a FAT32 partition. +// Build this example with: +// rustup target install riscv64imac-unknown-none-elf +// cargo build --target riscv64imac-unknown-none-elf --release -p sdcard-demo + +#![no_std] +#![no_main] + +use base_address::Static; +use bl_rom_rt::entry; +use bl_soc::{clocks::Clocks, gpio::Pads, prelude::*, spi::Spi, uart::UartMuxes, GLB, SPI, UART}; +use embedded_hal::spi::MODE_3; +use embedded_sdmmc::{SdCard, VolumeManager}; +use embedded_time::rate::*; +use panic_halt as _; + +struct MyTimeSourse {} + +impl embedded_sdmmc::TimeSource for MyTimeSourse { + fn get_timestamp(&self) -> embedded_sdmmc::Timestamp { + // TODO + embedded_sdmmc::Timestamp::from_calendar(2023, 1, 1, 0, 0, 0).unwrap() + } +} + +#[entry] +fn main() -> ! { + // values initialized by ROM runtime + let uart0: UART, 0> = unsafe { core::mem::transmute(()) }; + let uart_muxes: UartMuxes> = unsafe { core::mem::transmute(()) }; + let clocks = Clocks { + xtal: Hertz(40_000_000), + }; + let gpio: Pads> = unsafe { core::mem::transmute(()) }; + let glb: GLB> = unsafe { core::mem::transmute(()) }; + let spi: SPI> = unsafe { core::mem::transmute(()) }; + + let tx = gpio.io14.into_uart(); + let rx = gpio.io15.into_uart(); + let sig2 = uart_muxes.sig2.into_transmit::<0>(); + let sig3 = uart_muxes.sig3.into_receive::<0>(); + + let config = Default::default(); + let mut serial = uart0.freerun(config, 2000000.Bd(), ((tx, sig2), (rx, sig3)), &clocks); + + let mut led = gpio.io8.into_floating_output(); + let mut led_state = PinState::High; + + let spi_clk = gpio.io3.into_spi::<1>(); + let spi_mosi = gpio.io1.into_spi::<1>(); + let spi_miso = gpio.io2.into_spi::<1>(); + let spi_cs = gpio.io0.into_spi::<1>(); + let spi_sd = Spi::new(spi, (spi_clk, spi_mosi, spi_miso, spi_cs), MODE_3, &glb); + + let delay = riscv::delay::McycleDelay::new(40_000_000); + // TODO: let embedded_sdmmc::SdCard control cs pin + let fake_cs = gpio.io12.into_floating_output(); + let sdcard = SdCard::new(spi_sd, fake_cs, delay); + writeln!(serial, "Card size: {}", sdcard.num_bytes().unwrap()).ok(); + + let time_source = MyTimeSourse {}; + let mut volume_mgr = VolumeManager::new(sdcard, time_source); + + let volume0 = volume_mgr + .open_volume(embedded_sdmmc::VolumeIdx(0)) + .unwrap(); + let root_dir = volume_mgr.open_root_dir(volume0).unwrap(); + + volume_mgr + .iterate_dir(root_dir, |entry| { + writeln!(serial, "Entry: {:?}", entry).ok(); + }) + .unwrap(); + + volume_mgr.close_dir(root_dir).unwrap(); + + loop { + led.set_state(led_state).ok(); + led_state = !led_state; + unsafe { riscv::asm::delay(100_000) } + } +} diff --git a/examples/peripherals/sdcard-gpt-demo/Cargo.toml b/examples/peripherals/sdcard-gpt-demo/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..e7ecdcc86a6a1fe36cdbcb8cd7eca9cb49c8de8a --- /dev/null +++ b/examples/peripherals/sdcard-gpt-demo/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "sdcard-gpt-demo" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bl-soc = { path = "../../..", features = ["bl808"] } +base-address = "0.0.0" +panic-halt = "0.2.0" +embedded-time = "0.12.1" +embedded-hal = "1.0.0-rc.1" +riscv = "0.10.1" +display-interface-spi = "0.4.1" +mipidsi = "0.7.1" +embedded-graphics = "0.8.1" +embedded-sdmmc = "0.6.0" +gpt_disk_io = "0.15.0" +gpt_disk_types = "0.15.0" +fatfs = { default-features = false, git = "https://github.com/rafalh/rust-fatfs" } + +[dependencies.bl-rom-rt] +git = "https://gitee.com/rustsbi/bl-rom-rt" +branch = "main" +default-features = false +features = ["bl808-dsp"] diff --git a/examples/peripherals/sdcard-gpt-demo/build.rs b/examples/peripherals/sdcard-gpt-demo/build.rs new file mode 100644 index 0000000000000000000000000000000000000000..3509edb78b16f69c40351e7e21ee482e492753bc --- /dev/null +++ b/examples/peripherals/sdcard-gpt-demo/build.rs @@ -0,0 +1,3 @@ +fn main() { + println!("cargo:rustc-link-arg=-Tbl-rom-rt.ld"); +} diff --git a/examples/peripherals/sdcard-gpt-demo/src/main.rs b/examples/peripherals/sdcard-gpt-demo/src/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..72a42fe1b40c4fed301f946f5b94a42c8a096c2b --- /dev/null +++ b/examples/peripherals/sdcard-gpt-demo/src/main.rs @@ -0,0 +1,304 @@ +// This is a example of how to read a SD card with a GPT partition table and a EFI partition. +// Build this example with: +// rustup target install riscv64imac-unknown-none-elf +// cargo build --target riscv64imac-unknown-none-elf --release -p sdcard-gpt-demo + +#![no_std] +#![no_main] + +use base_address::Static; +use bl_rom_rt::entry; +use bl_soc::{clocks::Clocks, gpio::Pads, prelude::*, spi::Spi, uart::UartMuxes, GLB, SPI, UART}; +use embedded_hal::spi::MODE_3; +use embedded_sdmmc::*; +use embedded_time::rate::*; +use fatfs::Read; +use gpt_disk_io::Disk; +use gpt_disk_types::{Lba, LbaLe}; +use panic_halt as _; + +struct MySdCard { + sdcard: T, + cache: [u8; 512], + cache_lba: Lba, +} + +impl MySdCard { + #[inline] + pub fn new(sdcard: T) -> Self { + MySdCard { + cache: [0; 512], + cache_lba: Lba(sdcard.num_blocks().unwrap().0.into()), + sdcard, + } + } + #[inline] + fn read_block(&mut self, lba: Lba, buf: &mut Block) -> Result<(), Error> { + if self.cache_lba != lba { + let mut block = [Block::new()]; + let block_idx = BlockIdx(lba.0.try_into().unwrap()); + self.sdcard.read(&mut block, block_idx, "").unwrap(); + self.cache.copy_from_slice(block[0].as_slice()); + self.cache_lba = lba; + } + buf.copy_from_slice(&self.cache); + Ok(()) + } +} + +#[derive(Debug)] +#[non_exhaustive] +pub enum Error { + Other, +} + +impl core::fmt::Display for Error { + #[inline] + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + match self { + Error::Other => write!(f, "Error"), + } + } +} + +impl gpt_disk_io::BlockIo for MySdCard { + type Error = Error; + #[inline] + fn block_size(&self) -> gpt_disk_io::gpt_disk_types::BlockSize { + gpt_disk_io::gpt_disk_types::BlockSize::new(512).unwrap() + } + #[inline] + fn read_blocks(&mut self, lba: Lba, buf: &mut [u8]) -> Result<(), Self::Error> { + let mut block = Block::new(); + let mut lba = lba; + for b in buf.chunks_mut(512) { + self.read_block(lba, &mut block).unwrap(); + b.copy_from_slice(block.as_slice()); + lba.0 += 1; + } + Ok(()) + } + #[inline] + fn write_blocks(&mut self, _lba: Lba, _buf: &[u8]) -> Result<(), Self::Error> { + todo!() + } + #[inline] + fn num_blocks(&mut self) -> Result { + Ok(self.sdcard.num_blocks().unwrap().0.into()) + } + #[inline] + fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } +} + +struct MyVolume { + my_sdcard: MySdCard, + begin: u64, + size: u64, + offset: u64, +} + +impl MyVolume { + #[inline] + pub fn new(my_sdcard: MySdCard, begin: Lba, end: Lba) -> Self { + MyVolume { + my_sdcard, + begin: begin.0 * 512, + size: end.0 * 512 - begin.0 * 512, + offset: 0, + } + } + #[inline] + fn to_lba(&self, offset: u64) -> (Lba, u64) { + ( + Lba((self.begin + offset) / 512), + (self.begin + offset) % 512, + ) + } +} + +impl fatfs::IoError for Error { + #[inline] + fn is_interrupted(&self) -> bool { + false + } + #[inline] + fn new_unexpected_eof_error() -> Self { + Self::Other + } + #[inline] + fn new_write_zero_error() -> Self { + Self::Other + } +} + +impl fatfs::IoBase for MyVolume { + type Error = Error; +} + +impl fatfs::Read for MyVolume { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> Result { + let mut bytes_read = 0; + let mut block = Block::new(); + for b in buf { + let (lba, offset_in_block) = self.to_lba(self.offset); + self.my_sdcard.read_block(lba, &mut block).unwrap(); + *b = block[offset_in_block as usize]; + bytes_read += 1; + self.offset += 1; + } + Ok(bytes_read) + } +} + +impl fatfs::Write for MyVolume { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + // TODO + Ok(buf.len()) + } + #[inline] + fn flush(&mut self) -> Result<(), Self::Error> { + // TODO + Ok(()) + } +} + +impl fatfs::Seek for MyVolume { + #[inline] + fn seek(&mut self, pos: fatfs::SeekFrom) -> Result { + let new_offset_opt: Option = match pos { + fatfs::SeekFrom::Current(x) => i64::try_from(self.offset) + .ok() + .and_then(|n| n.checked_add(x)) + .and_then(|n| u64::try_from(n).ok()), + fatfs::SeekFrom::Start(x) => Some(x), + fatfs::SeekFrom::End(o) => i64::try_from(self.size) + .ok() + .and_then(|size| size.checked_add(o)) + .and_then(|n| u64::try_from(n).ok()), + }; + if let Some(new_offset) = new_offset_opt { + if new_offset > self.size { + Err(Error::Other) + } else { + self.offset = new_offset; + Ok(self.offset) + } + } else { + Err(Error::Other) + } + } +} + +#[entry] +fn main() -> ! { + // values initialized by ROM runtime + let uart0: UART, 0> = unsafe { core::mem::transmute(()) }; + let uart_muxes: UartMuxes> = unsafe { core::mem::transmute(()) }; + let clocks = Clocks { + xtal: Hertz(40_000_000), + }; + let gpio: Pads> = unsafe { core::mem::transmute(()) }; + let glb: GLB> = unsafe { core::mem::transmute(()) }; + let spi: SPI> = unsafe { core::mem::transmute(()) }; + + let tx = gpio.io14.into_uart(); + let rx = gpio.io15.into_uart(); + let sig2 = uart_muxes.sig2.into_transmit::<0>(); + let sig3 = uart_muxes.sig3.into_receive::<0>(); + + let config = Default::default(); + let mut serial = uart0.freerun(config, 2000000.Bd(), ((tx, sig2), (rx, sig3)), &clocks); + + let mut led = gpio.io8.into_floating_output(); + let mut led_state = PinState::High; + + let spi_clk = gpio.io3.into_spi::<1>(); + let spi_mosi = gpio.io1.into_spi::<1>(); + let spi_miso = gpio.io2.into_spi::<1>(); + let spi_cs = gpio.io0.into_spi::<1>(); + let fake_cs = gpio.io12.into_floating_output(); + + let spi_sd = Spi::new(spi, (spi_clk, spi_mosi, spi_miso, spi_cs), MODE_3, &glb); + + let delay = riscv::delay::McycleDelay::new(40_000_000); + let sdcard = SdCard::new(spi_sd, fake_cs, delay); + writeln!(serial, "Card size: {}", sdcard.num_bytes().unwrap()).ok(); + writeln!(serial, "").ok(); + + let my_sdcard = MySdCard::new(sdcard); + let mut disk = Disk::new(my_sdcard).unwrap(); + let mut buffer = [0u8; 512]; + let header = disk.read_primary_gpt_header(&mut buffer).unwrap(); + writeln!(serial, "Primary header: {:?}", header).ok(); + + let layout = header.get_partition_entry_array_layout().unwrap(); + writeln!(serial, "Layout: {:?}", layout).ok(); + let mut iter = disk + .gpt_partition_entry_array_iter(layout, &mut buffer) + .unwrap(); + while let Some(res) = iter.next() { + let res = res.unwrap(); + if res.starting_lba == LbaLe(gpt_disk_types::U64Le([0; 8])) { + break; + } + writeln!(serial, "Partition: {:?}", res).ok(); + } + drop(iter); + + // TODO: switch to disk.into_inner() once Disk struct have this function + let my_sdcard: MySdCard< + SdCard< + Spi< + Static<805339136>, + ( + bl_soc::gpio::Pad, 3, bl_soc::gpio::Spi<1>>, + bl_soc::gpio::Pad, 1, bl_soc::gpio::Spi<1>>, + bl_soc::gpio::Pad, 2, bl_soc::gpio::Spi<1>>, + bl_soc::gpio::Pad, 0, bl_soc::gpio::Spi<1>>, + ), + 1, + >, + bl_soc::gpio::Pad, 12, bl_soc::gpio::Output>, + riscv::delay::McycleDelay, + >, + > = unsafe { core::mem::transmute(disk) }; + let my_volume = MyVolume::new(my_sdcard, Lba(2048), Lba(616447)); + let fs = fatfs::FileSystem::new(my_volume, fatfs::FsOptions::new()).unwrap(); + let root_dir = fs.root_dir(); + writeln!(serial, "List files:").ok(); + for r in root_dir.iter() { + let entry = r.unwrap(); + write!(serial, " File: ").ok(); + for b in entry.short_file_name_as_bytes() { + write!(serial, "{}", *b as char).ok(); + } + writeln!(serial, "").ok(); + } + writeln!(serial, "").ok(); + + writeln!(serial, "Read a file: ").ok(); + let mut file = root_dir.open_file("TEST").unwrap(); + let mut size = 0u64; + let mut buffer = [0u8]; + while let Ok(s) = file.read(&mut buffer) { + if s != 1 { + break; + } + write!(serial, "{}", buffer[0] as char).ok(); + size += 1; + } + writeln!(serial, "").ok(); + writeln!(serial, "File size: {}", size).ok(); + + writeln!(serial, "OK").ok(); + + loop { + led.set_state(led_state).ok(); + led_state = !led_state; + unsafe { riscv::asm::delay(100_000) } + } +} diff --git a/src/spi.rs b/src/spi.rs index ec00af5f04dcec785a0e8e1fc4e8bff64839eb55..122f377f513726492f1ff4a4ae6a98e197173dd4 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -670,13 +670,13 @@ impl Spi { ); spi.period_signal.write( PeriodSignal(0) - .set_data_phase_0(1) - .set_data_phase_1(1) - .set_start_condition(1) - .set_stop_condition(1), + .set_data_phase_0(4) + .set_data_phase_1(4) + .set_start_condition(4) + .set_stop_condition(4), ); spi.period_interval - .write(PeriodInterval(0).set_frame_interval(1)); + .write(PeriodInterval(0).set_frame_interval(4)); } Spi { spi, pads } } @@ -733,6 +733,7 @@ impl embedded_hal::spi::SpiBus for Spi embedded_hal::spi::SpiBus for Spi Result<(), Self::Error> { - todo!() + fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { + unsafe { self.spi.config.modify(|config| config.enable_master()) }; + + for word in words.iter_mut() { + while self.spi.fifo_config_1.read().transmit_available_bytes() == 0 { + core::hint::spin_loop(); + } + unsafe { self.spi.data_write.write(*word) } + *word = self.spi.data_read.read(); + } + + unsafe { self.spi.config.modify(|config| config.disable_master()) }; + Ok(()) } #[inline] fn flush(&mut self) -> Result<(), Self::Error> { @@ -814,7 +826,19 @@ impl embedded_hal_027::blocking::spi::Writ type Error = Error; #[inline] fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { - ::write(self, words) + ::write(self, words)?; + Ok(()) + } +} + +impl embedded_hal_027::blocking::spi::Transfer + for Spi +{ + type Error = Error; + #[inline] + fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { + ::transfer_in_place(self, words)?; + Ok(words) } } @@ -837,6 +861,25 @@ where { } +impl Pads<1> + for ( + Pad>, + Pad>, + Pad>, + Pad>, + ) +where + A1: BaseAddress, + A2: BaseAddress, + A3: BaseAddress, + A4: BaseAddress, + Pad>: HasClkSignal, + Pad>: HasMosiSignal, + Pad>: HasMisoSignal, + Pad>: HasCsSignal, +{ +} + /// Check if target gpio `Pin` is internally connected to SPI clock signal. pub trait HasClkSignal {}