| #include <Arduino.h> |
| #include <SPI.h> |
| #include <bitset> |
| #include <cstddef> |
| #include <stdint.h> |
| |
| #include "gonk/spi_flash.h" |
| |
| #define PW_LOG_LEVEL PW_LOG_LEVEL_DEBUG |
| #define PW_LOG_MODULE_NAME "SpiFlash" |
| |
| #include "pw_log/log.h" |
| #include "pw_result/result.h" |
| #include "pw_span/span.h" |
| #include "pw_status/status.h" |
| |
| namespace gonk::spi_flash { |
| |
| SpiFlash::SpiFlash(uint16_t flash_cs, uint32_t baudrate, SPIClass &flash_spi) |
| : flash_cs_(flash_cs), spi_settings_(baudrate, MSBFIRST, SPI_MODE0), |
| flash_spi_(flash_spi) {} |
| |
| Status SpiFlash::WriteEnable() { |
| flash_spi_.beginTransaction(spi_settings_); |
| digitalWrite(flash_cs_, LOW); |
| flash_spi_.transfer(0x06); |
| digitalWrite(flash_cs_, HIGH); |
| flash_spi_.endTransaction(); |
| |
| return pw::OkStatus(); |
| } |
| |
| Status SpiFlash::WriteDisable() { |
| flash_spi_.beginTransaction(spi_settings_); |
| digitalWrite(flash_cs_, LOW); |
| flash_spi_.transfer(0x04); |
| digitalWrite(flash_cs_, HIGH); |
| flash_spi_.endTransaction(); |
| |
| return pw::OkStatus(); |
| } |
| |
| std::bitset<8> SpiFlash::StatusRegister() { |
| uint8_t status_register[2] = { |
| 0x05, // Read Status Register |
| 0, // Result value |
| }; |
| |
| flash_spi_.beginTransaction(spi_settings_); |
| digitalWrite(flash_cs_, LOW); |
| flash_spi_.transfer(status_register, 2); |
| digitalWrite(flash_cs_, HIGH); |
| flash_spi_.endTransaction(); |
| |
| return (std::bitset<8>)status_register[1]; |
| } |
| |
| bool SpiFlash::IsBusy() { |
| // 1 in the first bit of the status register indicates the device is busy. |
| return StatusRegister()[0]; |
| } |
| |
| bool SpiFlash::WritingIsEnabled() { |
| // 1 in the second bit of the status register indicates the device is busy. |
| return StatusRegister()[1]; |
| } |
| |
| Status SpiFlash::Erase() { |
| if (!WritingIsEnabled()) { |
| return pw::Status::FailedPrecondition(); |
| } |
| |
| PW_LOG_DEBUG("Start Flash Erase"); |
| // Begin chip erase. |
| flash_spi_.beginTransaction(spi_settings_); |
| digitalWrite(flash_cs_, LOW); |
| flash_spi_.transfer(0x60); |
| digitalWrite(flash_cs_, HIGH); |
| flash_spi_.endTransaction(); |
| |
| int wait_time = 100; // Wait at most 1 second. |
| while (IsBusy() && wait_time > 0) { |
| PW_LOG_DEBUG("Device is busy"); |
| delay(10); |
| wait_time--; |
| } |
| |
| if (wait_time < 0) { |
| return pw::Status::DeadlineExceeded(); |
| } |
| |
| return pw::OkStatus(); |
| } |
| |
| pw::Result<SpiFlash::DeviceId> SpiFlash::GetDeviceIds() { |
| uint8_t manufacturer_id[4] = { |
| 0x9f, // Read Manufacturer and Device ID Command |
| 0, 0, 0, // Result values |
| }; |
| |
| // Read the SPI Flash JEDEC ID. |
| flash_spi_.beginTransaction(spi_settings_); |
| digitalWrite(flash_cs_, LOW); |
| flash_spi_.transfer(manufacturer_id, 4); |
| digitalWrite(flash_cs_, HIGH); |
| flash_spi_.endTransaction(); |
| |
| PW_LOG_DEBUG("JEDEC ID: %x %x %x", manufacturer_id[1], manufacturer_id[2], |
| manufacturer_id[3]); |
| |
| // Check for expected values |
| if (manufacturer_id[1] == 0xff && manufacturer_id[2] == 0xff && |
| manufacturer_id[3] == 0xff) { |
| PW_LOG_ERROR("Error: Unexpected device ID values. SPI bus may not be " |
| "released by the FPGA."); |
| return pw::Status::NotFound(); |
| } |
| |
| return SpiFlash::DeviceId{ |
| .manufacturer_id = manufacturer_id[1], |
| .family_code = manufacturer_id[2], |
| .product_version = manufacturer_id[3], |
| }; |
| } |
| |
| void SpiFlash::ResumeFromPowerDown() { |
| // Resume from Deep Power-Down |
| flash_spi_.beginTransaction(spi_settings_); |
| digitalWrite(flash_cs_, LOW); |
| flash_spi_.transfer(0xAB); |
| digitalWrite(flash_cs_, HIGH); |
| flash_spi_.endTransaction(); |
| } |
| |
| void SpiFlash::StartFlashSpi() { |
| flash_spi_.begin(); |
| delay(10); |
| ResumeFromPowerDown(); |
| } |
| |
| void SpiFlash::StopFlashSpi() { flash_spi_.end(); } |
| |
| } // namespace gonk::spi_flash |