|
| 1 | +#![no_std] |
| 2 | +#![no_main] |
| 3 | +#![allow(dead_code)] |
| 4 | +/// This example demonstrates how to use the QSPI peripheral in both indirect-mode and memory-mapped mode. |
| 5 | +/// If you want to test this example, please pay attention to flash pins and check flash device datasheet |
| 6 | +/// to make sure operations in this example are compatible with your device, especially registers I/O operations. |
| 7 | +use defmt::info; |
| 8 | +use embassy_stm32::mode; |
| 9 | +use embassy_stm32::qspi::enums::{ |
| 10 | + AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, MemorySize, QspiWidth, |
| 11 | +}; |
| 12 | +use embassy_stm32::qspi::{self, Instance, TransferConfig}; |
| 13 | +pub struct FlashMemory<I: Instance> { |
| 14 | + qspi: qspi::Qspi<'static, I, mode::Async>, |
| 15 | +} |
| 16 | +use embassy_executor::Spawner; |
| 17 | +use embassy_time::Timer; |
| 18 | +use {defmt_rtt as _, panic_probe as _}; |
| 19 | + |
| 20 | +const MEMORY_PAGE_SIZE: usize = 256; |
| 21 | +const CMD_READ_SR: u8 = 0x05; |
| 22 | +const CMD_READ_CR: u8 = 0x35; |
| 23 | +const CMD_QUAD_READ: u8 = 0x6B; |
| 24 | +const CMD_QUAD_WRITE_PG: u8 = 0x32; |
| 25 | +const CMD_READ_ID: u8 = 0x9F; |
| 26 | +const CMD_READ_MID: u8 = 0x90; |
| 27 | +const CMD_READ_UUID: u8 = 0x4B; |
| 28 | +const CMD_ENABLE_RESET: u8 = 0x66; |
| 29 | +const CMD_RESET: u8 = 0x99; |
| 30 | +const CMD_WRITE_ENABLE: u8 = 0x06; |
| 31 | +const CMD_SECTOR_ERASE: u8 = 0x20; |
| 32 | + |
| 33 | +const CMD_WRITE_SR: u8 = 0x01; |
| 34 | + |
| 35 | +impl<I: Instance> FlashMemory<I> { |
| 36 | + pub fn new(qspi: qspi::Qspi<'static, I, mode::Async>) -> Self { |
| 37 | + let mut memory = Self { qspi }; |
| 38 | + |
| 39 | + memory.reset_memory(); |
| 40 | + memory.enable_quad(); |
| 41 | + |
| 42 | + memory |
| 43 | + } |
| 44 | + fn enable_quad(&mut self) { |
| 45 | + let sr = self.read_sr_lsb(); |
| 46 | + let cr = self.read_sr_msb(); |
| 47 | + |
| 48 | + self.write_sr(sr, cr | 0x02); |
| 49 | + } |
| 50 | + fn read_register(&mut self, cmd: u8) -> u8 { |
| 51 | + let mut buffer = [0; 1]; |
| 52 | + let transaction = TransferConfig { |
| 53 | + iwidth: QspiWidth::SING, |
| 54 | + awidth: QspiWidth::NONE, |
| 55 | + dwidth: QspiWidth::SING, |
| 56 | + instruction: cmd, |
| 57 | + address: None, |
| 58 | + dummy: DummyCycles::_0, |
| 59 | + }; |
| 60 | + self.qspi.blocking_read(&mut buffer, transaction); |
| 61 | + buffer[0] |
| 62 | + } |
| 63 | + |
| 64 | + fn write_register(&mut self, cmd: u8, value: u8) { |
| 65 | + let buffer = [value; 1]; |
| 66 | + let transaction: TransferConfig = TransferConfig { |
| 67 | + iwidth: QspiWidth::SING, |
| 68 | + awidth: QspiWidth::NONE, |
| 69 | + dwidth: QspiWidth::SING, |
| 70 | + instruction: cmd, |
| 71 | + address: None, |
| 72 | + dummy: DummyCycles::_0, |
| 73 | + }; |
| 74 | + self.qspi.blocking_write(&buffer, transaction); |
| 75 | + } |
| 76 | + pub fn write_sr(&mut self, lsb: u8, msb: u8) { |
| 77 | + let buffer = [lsb, msb]; |
| 78 | + let transaction: TransferConfig = TransferConfig { |
| 79 | + iwidth: QspiWidth::SING, |
| 80 | + awidth: QspiWidth::NONE, |
| 81 | + dwidth: QspiWidth::SING, |
| 82 | + instruction: CMD_WRITE_SR, |
| 83 | + address: None, |
| 84 | + dummy: DummyCycles::_0, |
| 85 | + }; |
| 86 | + self.qspi.blocking_write(&buffer, transaction); |
| 87 | + } |
| 88 | + |
| 89 | + pub fn read_sr_lsb(&mut self) -> u8 { |
| 90 | + self.read_register(CMD_READ_SR) |
| 91 | + } |
| 92 | + pub fn read_sr_msb(&mut self) -> u8 { |
| 93 | + self.read_register(CMD_READ_CR) |
| 94 | + } |
| 95 | + |
| 96 | + pub fn reset_memory(&mut self) { |
| 97 | + self.exec_command(CMD_ENABLE_RESET); |
| 98 | + self.exec_command(CMD_RESET); |
| 99 | + self.wait_write_finish(); |
| 100 | + } |
| 101 | + fn exec_command(&mut self, cmd: u8) { |
| 102 | + let transaction = TransferConfig { |
| 103 | + iwidth: QspiWidth::SING, |
| 104 | + awidth: QspiWidth::NONE, |
| 105 | + dwidth: QspiWidth::NONE, |
| 106 | + instruction: cmd, |
| 107 | + address: None, |
| 108 | + dummy: DummyCycles::_0, |
| 109 | + }; |
| 110 | + self.qspi.blocking_command(transaction); |
| 111 | + } |
| 112 | + fn wait_write_finish(&mut self) { |
| 113 | + while (self.read_sr_lsb() & 0x01) != 0 {} |
| 114 | + } |
| 115 | + |
| 116 | + pub fn read_mid(&mut self) -> [u8; 2] { |
| 117 | + let mut buffer = [0; 2]; |
| 118 | + let transaction: TransferConfig = TransferConfig { |
| 119 | + iwidth: QspiWidth::SING, |
| 120 | + awidth: QspiWidth::SING, |
| 121 | + dwidth: QspiWidth::SING, |
| 122 | + instruction: CMD_READ_MID, |
| 123 | + address: Some(0), |
| 124 | + dummy: DummyCycles::_0, |
| 125 | + }; |
| 126 | + self.qspi.blocking_read(&mut buffer, transaction); |
| 127 | + buffer |
| 128 | + } |
| 129 | + pub fn read_uuid(&mut self) -> [u8; 16] { |
| 130 | + let mut buffer = [0; 16]; |
| 131 | + let transaction: TransferConfig = TransferConfig { |
| 132 | + iwidth: QspiWidth::SING, |
| 133 | + awidth: QspiWidth::SING, |
| 134 | + dwidth: QspiWidth::SING, |
| 135 | + instruction: CMD_READ_UUID, |
| 136 | + address: Some(0), |
| 137 | + dummy: DummyCycles::_8, |
| 138 | + }; |
| 139 | + self.qspi.blocking_read(&mut buffer, transaction); |
| 140 | + buffer |
| 141 | + } |
| 142 | + pub fn read_id(&mut self) -> [u8; 3] { |
| 143 | + let mut buffer = [0; 3]; |
| 144 | + let transaction: TransferConfig = TransferConfig { |
| 145 | + iwidth: QspiWidth::SING, |
| 146 | + awidth: QspiWidth::NONE, |
| 147 | + dwidth: QspiWidth::SING, |
| 148 | + instruction: CMD_READ_ID, |
| 149 | + address: None, |
| 150 | + dummy: DummyCycles::_0, |
| 151 | + }; |
| 152 | + self.qspi.blocking_read(&mut buffer, transaction); |
| 153 | + buffer |
| 154 | + } |
| 155 | + |
| 156 | + pub fn enable_mmap(&mut self) { |
| 157 | + let transaction: TransferConfig = TransferConfig { |
| 158 | + iwidth: QspiWidth::SING, |
| 159 | + awidth: QspiWidth::SING, |
| 160 | + dwidth: QspiWidth::QUAD, |
| 161 | + instruction: CMD_QUAD_READ, |
| 162 | + address: Some(0), |
| 163 | + dummy: DummyCycles::_8, |
| 164 | + }; |
| 165 | + self.qspi.enable_memory_map(&transaction); |
| 166 | + } |
| 167 | + fn perform_erase(&mut self, addr: u32, cmd: u8) { |
| 168 | + let transaction = TransferConfig { |
| 169 | + iwidth: QspiWidth::SING, |
| 170 | + awidth: QspiWidth::SING, |
| 171 | + dwidth: QspiWidth::NONE, |
| 172 | + instruction: cmd, |
| 173 | + address: Some(addr), |
| 174 | + dummy: DummyCycles::_0, |
| 175 | + }; |
| 176 | + self.enable_write(); |
| 177 | + self.qspi.blocking_command(transaction); |
| 178 | + self.wait_write_finish(); |
| 179 | + } |
| 180 | + pub fn enable_write(&mut self) { |
| 181 | + self.exec_command(CMD_WRITE_ENABLE); |
| 182 | + } |
| 183 | + pub fn erase_sector(&mut self, addr: u32) { |
| 184 | + self.perform_erase(addr, CMD_SECTOR_ERASE); |
| 185 | + } |
| 186 | + fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize, use_dma: bool) { |
| 187 | + assert!( |
| 188 | + (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32, |
| 189 | + "write_page(): page write length exceeds page boundary (len = {}, addr = {:X}", |
| 190 | + len, |
| 191 | + addr |
| 192 | + ); |
| 193 | + |
| 194 | + let transaction = TransferConfig { |
| 195 | + iwidth: QspiWidth::SING, |
| 196 | + awidth: QspiWidth::SING, |
| 197 | + dwidth: QspiWidth::QUAD, |
| 198 | + instruction: CMD_QUAD_WRITE_PG, |
| 199 | + address: Some(addr), |
| 200 | + dummy: DummyCycles::_0, |
| 201 | + }; |
| 202 | + self.enable_write(); |
| 203 | + if use_dma { |
| 204 | + self.qspi.blocking_write_dma(buffer, transaction); |
| 205 | + } else { |
| 206 | + self.qspi.blocking_write(buffer, transaction); |
| 207 | + } |
| 208 | + self.wait_write_finish(); |
| 209 | + } |
| 210 | + pub fn write_memory(&mut self, addr: u32, buffer: &[u8], use_dma: bool) { |
| 211 | + let mut left = buffer.len(); |
| 212 | + let mut place = addr; |
| 213 | + let mut chunk_start = 0; |
| 214 | + |
| 215 | + while left > 0 { |
| 216 | + let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize; |
| 217 | + let chunk_size = if left >= max_chunk_size { max_chunk_size } else { left }; |
| 218 | + let chunk = &buffer[chunk_start..(chunk_start + chunk_size)]; |
| 219 | + self.write_page(place, chunk, chunk_size, use_dma); |
| 220 | + place += chunk_size as u32; |
| 221 | + left -= chunk_size; |
| 222 | + chunk_start += chunk_size; |
| 223 | + } |
| 224 | + } |
| 225 | + |
| 226 | + pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8], use_dma: bool) { |
| 227 | + let transaction = TransferConfig { |
| 228 | + iwidth: QspiWidth::SING, |
| 229 | + awidth: QspiWidth::SING, |
| 230 | + dwidth: QspiWidth::QUAD, |
| 231 | + instruction: CMD_QUAD_READ, |
| 232 | + address: Some(addr), |
| 233 | + dummy: DummyCycles::_8, |
| 234 | + }; |
| 235 | + if use_dma { |
| 236 | + self.qspi.blocking_read_dma(buffer, transaction); |
| 237 | + } else { |
| 238 | + self.qspi.blocking_read(buffer, transaction); |
| 239 | + } |
| 240 | + } |
| 241 | +} |
| 242 | + |
| 243 | +const MEMORY_ADDR: u32 = 0x00000000 as u32; |
| 244 | + |
| 245 | +#[embassy_executor::main] |
| 246 | +async fn main(_spawner: Spawner) { |
| 247 | + let p = embassy_stm32::init(Default::default()); |
| 248 | + |
| 249 | + let config = qspi::Config { |
| 250 | + memory_size: MemorySize::_16MiB, |
| 251 | + address_size: AddressSize::_24bit, |
| 252 | + prescaler: 200, |
| 253 | + cs_high_time: ChipSelectHighTime::_1Cycle, |
| 254 | + fifo_threshold: FIFOThresholdLevel::_16Bytes, |
| 255 | + }; |
| 256 | + let driver = qspi::Qspi::new_bank1(p.QUADSPI, p.PB1, p.PB0, p.PA7, p.PA6, p.PA3, p.PA2, p.DMA2_CH7, config); |
| 257 | + let mut flash = FlashMemory::new(driver); |
| 258 | + let mut wr_buf = [0u8; 256]; |
| 259 | + for i in 0..32 { |
| 260 | + wr_buf[i] = i as u8; |
| 261 | + } |
| 262 | + let mut rd_buf = [0u8; 32]; |
| 263 | + flash.erase_sector(MEMORY_ADDR); |
| 264 | + flash.write_memory(MEMORY_ADDR, &wr_buf, false); |
| 265 | + flash.read_memory(MEMORY_ADDR, &mut rd_buf, false); |
| 266 | + |
| 267 | + info!("data read from indirect mode: {}", rd_buf); |
| 268 | + flash.enable_mmap(); |
| 269 | + let qspi_base = unsafe { core::slice::from_raw_parts(0x9000_0000 as *const u8, 32) }; |
| 270 | + info!("data read from mmap: {}", qspi_base); |
| 271 | + loop { |
| 272 | + Timer::after_millis(1000).await; |
| 273 | + } |
| 274 | +} |
0 commit comments