aboutsummaryrefslogtreecommitdiff
path: root/Marlin/src/libs/W25Qxx.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Marlin/src/libs/W25Qxx.cpp')
-rw-r--r--Marlin/src/libs/W25Qxx.cpp395
1 files changed, 395 insertions, 0 deletions
diff --git a/Marlin/src/libs/W25Qxx.cpp b/Marlin/src/libs/W25Qxx.cpp
new file mode 100644
index 0000000..be5b429
--- /dev/null
+++ b/Marlin/src/libs/W25Qxx.cpp
@@ -0,0 +1,395 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "../inc/MarlinConfig.h"
+
+#if HAS_SPI_FLASH
+
+#include "W25Qxx.h"
+
+W25QXXFlash W25QXX;
+
+#ifndef SPI_FLASH_MISO_PIN
+ #define SPI_FLASH_MISO_PIN W25QXX_MISO_PIN
+#endif
+#ifndef SPI_FLASH_MOSI_PIN
+ #define SPI_FLASH_MOSI_PIN W25QXX_MOSI_PIN
+#endif
+#ifndef SPI_FLASH_SCK_PIN
+ #define SPI_FLASH_SCK_PIN W25QXX_SCK_PIN
+#endif
+#ifndef SPI_FLASH_CS_PIN
+ #define SPI_FLASH_CS_PIN W25QXX_CS_PIN
+#endif
+#ifndef NC
+ #define NC -1
+#endif
+
+MarlinSPI W25QXXFlash::mySPI(SPI_FLASH_MOSI_PIN, SPI_FLASH_MISO_PIN, SPI_FLASH_SCK_PIN, NC);
+
+#define W25QXX_CS_H OUT_WRITE(SPI_FLASH_CS_PIN, HIGH)
+#define W25QXX_CS_L OUT_WRITE(SPI_FLASH_CS_PIN, LOW)
+
+bool flash_dma_mode = true;
+
+void W25QXXFlash::init(uint8_t spiRate) {
+
+ OUT_WRITE(SPI_FLASH_CS_PIN, HIGH);
+
+ /**
+ * STM32F1 APB2 = 72MHz, APB1 = 36MHz, max SPI speed of this MCU if 18Mhz
+ * STM32F1 has 3 SPI ports, SPI1 in APB2, SPI2/SPI3 in APB1
+ * so the minimum prescale of SPI1 is DIV4, SPI2/SPI3 is DIV2
+ */
+ #if SPI_DEVICE == 1
+ #define SPI_CLOCK_MAX SPI_CLOCK_DIV4
+ #else
+ #define SPI_CLOCK_MAX SPI_CLOCK_DIV2
+ #endif
+ uint8_t clock;
+ switch (spiRate) {
+ case SPI_FULL_SPEED: clock = SPI_CLOCK_MAX; break;
+ case SPI_HALF_SPEED: clock = SPI_CLOCK_DIV4; break;
+ case SPI_QUARTER_SPEED: clock = SPI_CLOCK_DIV8; break;
+ case SPI_EIGHTH_SPEED: clock = SPI_CLOCK_DIV16; break;
+ case SPI_SPEED_5: clock = SPI_CLOCK_DIV32; break;
+ case SPI_SPEED_6: clock = SPI_CLOCK_DIV64; break;
+ default: clock = SPI_CLOCK_DIV2;// Default from the SPI library
+ }
+
+ mySPI.setClockDivider(clock);
+ mySPI.setBitOrder(MSBFIRST);
+ mySPI.setDataMode(SPI_MODE0);
+ mySPI.begin();
+}
+
+/**
+ * @brief Receive a single byte from the SPI port.
+ *
+ * @return Byte received
+ */
+uint8_t W25QXXFlash::spi_flash_Rec() {
+ const uint8_t returnByte = mySPI.transfer(0xFF);
+ return returnByte;
+}
+
+uint8_t W25QXXFlash::spi_flash_read_write_byte(uint8_t data) {
+ const uint8_t returnByte = mySPI.transfer(data);
+ return returnByte;
+}
+
+/**
+ * @brief Receive a number of bytes from the SPI port to a buffer
+ *
+ * @param buf Pointer to starting address of buffer to write to.
+ * @param nbyte Number of bytes to receive.
+ * @return Nothing
+ *
+ * @details Uses DMA
+ */
+void W25QXXFlash::spi_flash_Read(uint8_t* buf, uint16_t nbyte) {
+ mySPI.dmaTransfer(0, const_cast<uint8_t*>(buf), nbyte);
+}
+
+/**
+ * @brief Send a single byte on SPI port
+ *
+ * @param b Byte to send
+ *
+ * @details
+ */
+void W25QXXFlash::spi_flash_Send(uint8_t b) { mySPI.transfer(b); }
+
+/**
+ * @brief Write token and then write from 512 byte buffer to SPI (for SD card)
+ *
+ * @param buf Pointer with buffer start address
+ * @return Nothing
+ *
+ * @details Use DMA
+ */
+void W25QXXFlash::spi_flash_SendBlock(uint8_t token, const uint8_t* buf) {
+ mySPI.transfer(token);
+ mySPI.dmaSend(const_cast<uint8_t*>(buf), 512);
+}
+
+uint16_t W25QXXFlash::W25QXX_ReadID(void) {
+ uint16_t Temp = 0;
+ W25QXX_CS_L;
+ spi_flash_Send(0x90);
+ spi_flash_Send(0x00);
+ spi_flash_Send(0x00);
+ spi_flash_Send(0x00);
+ Temp |= spi_flash_Rec() << 8;
+ Temp |= spi_flash_Rec();
+ W25QXX_CS_H;
+ return Temp;
+}
+
+void W25QXXFlash::SPI_FLASH_WriteEnable(void) {
+ // Select the FLASH: Chip Select low
+ W25QXX_CS_L;
+ // Send "Write Enable" instruction
+ spi_flash_Send(W25X_WriteEnable);
+ // Deselect the FLASH: Chip Select high
+ W25QXX_CS_H;
+}
+
+/*******************************************************************************
+* Function Name : SPI_FLASH_WaitForWriteEnd
+* Description : Polls the status of the Write In Progress (WIP) flag in the
+* FLASH's status register and loop until write opertaion
+* has completed.
+* Input : None
+* Output : None
+* Return : None
+*******************************************************************************/
+void W25QXXFlash::SPI_FLASH_WaitForWriteEnd(void) {
+ uint8_t FLASH_Status = 0;
+
+ // Select the FLASH: Chip Select low
+ W25QXX_CS_L;
+ // Send "Read Status Register" instruction
+ spi_flash_Send(W25X_ReadStatusReg);
+
+ // Loop as long as the memory is busy with a write cycle
+ do
+ /* Send a dummy byte to generate the clock needed by the FLASH
+ and put the value of the status register in FLASH_Status variable */
+ FLASH_Status = spi_flash_Rec();
+ while ((FLASH_Status & WIP_Flag) == 0x01); // Write in progress
+
+ // Deselect the FLASH: Chip Select high
+ W25QXX_CS_H;
+}
+
+void W25QXXFlash::SPI_FLASH_SectorErase(uint32_t SectorAddr) {
+ // Send write enable instruction
+ SPI_FLASH_WriteEnable();
+
+ // Sector Erase
+ // Select the FLASH: Chip Select low
+ W25QXX_CS_L;
+ // Send Sector Erase instruction
+ spi_flash_Send(W25X_SectorErase);
+ // Send SectorAddr high nibble address byte
+ spi_flash_Send((SectorAddr & 0xFF0000) >> 16);
+ // Send SectorAddr medium nibble address byte
+ spi_flash_Send((SectorAddr & 0xFF00) >> 8);
+ // Send SectorAddr low nibble address byte
+ spi_flash_Send(SectorAddr & 0xFF);
+ // Deselect the FLASH: Chip Select high
+
+ W25QXX_CS_H;
+ // Wait the end of Flash writing
+ SPI_FLASH_WaitForWriteEnd();
+}
+
+void W25QXXFlash::SPI_FLASH_BlockErase(uint32_t BlockAddr) {
+ SPI_FLASH_WriteEnable();
+ W25QXX_CS_L;
+ // Send Sector Erase instruction
+ spi_flash_Send(W25X_BlockErase);
+ // Send SectorAddr high nibble address byte
+ spi_flash_Send((BlockAddr & 0xFF0000) >> 16);
+ // Send SectorAddr medium nibble address byte
+ spi_flash_Send((BlockAddr & 0xFF00) >> 8);
+ // Send SectorAddr low nibble address byte
+ spi_flash_Send(BlockAddr & 0xFF);
+
+ W25QXX_CS_H;
+
+ SPI_FLASH_WaitForWriteEnd();
+}
+
+/*******************************************************************************
+* Function Name : SPI_FLASH_BulkErase
+* Description : Erases the entire FLASH.
+* Input : None
+* Output : None
+* Return : None
+*******************************************************************************/
+void W25QXXFlash::SPI_FLASH_BulkErase(void) {
+ // Send write enable instruction
+ SPI_FLASH_WriteEnable();
+
+ // Bulk Erase
+ // Select the FLASH: Chip Select low
+ W25QXX_CS_L;
+
+ // Send Bulk Erase instruction
+ spi_flash_Send(W25X_ChipErase);
+ // Deselect the FLASH: Chip Select high
+ W25QXX_CS_H;
+ // Wait the end of Flash writing
+ SPI_FLASH_WaitForWriteEnd();
+}
+
+/*******************************************************************************
+* Function Name : SPI_FLASH_PageWrite
+* Description : Writes more than one byte to the FLASH with a single WRITE
+* cycle(Page WRITE sequence). The number of byte can't exceed
+* the FLASH page size.
+* Input : - pBuffer : pointer to the buffer containing the data to be
+* written to the FLASH.
+* - WriteAddr : FLASH's internal address to write to.
+* - NumByteToWrite : number of bytes to write to the FLASH,
+* must be equal or less than "SPI_FLASH_PageSize" value.
+* Output : None
+* Return : None
+*******************************************************************************/
+void W25QXXFlash::SPI_FLASH_PageWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite) {
+ // Enable the write access to the FLASH
+ SPI_FLASH_WriteEnable();
+
+ // Select the FLASH: Chip Select low
+ W25QXX_CS_L;
+ // Send "Write to Memory " instruction
+ spi_flash_Send(W25X_PageProgram);
+ // Send WriteAddr high nibble address byte to write to
+ spi_flash_Send((WriteAddr & 0xFF0000) >> 16);
+ // Send WriteAddr medium nibble address byte to write to
+ spi_flash_Send((WriteAddr & 0xFF00) >> 8);
+ // Send WriteAddr low nibble address byte to write to
+ spi_flash_Send(WriteAddr & 0xFF);
+
+ NOMORE(NumByteToWrite, SPI_FLASH_PerWritePageSize);
+
+ // While there is data to be written on the FLASH
+ while (NumByteToWrite--) {
+ // Send the current byte
+ spi_flash_Send(*pBuffer);
+ // Point on the next byte to be written
+ pBuffer++;
+ }
+
+ // Deselect the FLASH: Chip Select high
+ W25QXX_CS_H;
+
+ // Wait the end of Flash writing
+ SPI_FLASH_WaitForWriteEnd();
+}
+
+/*******************************************************************************
+* Function Name : SPI_FLASH_BufferWrite
+* Description : Writes block of data to the FLASH. In this function, the
+* number of WRITE cycles are reduced, using Page WRITE sequence.
+* Input : - pBuffer : pointer to the buffer containing the data to be
+* written to the FLASH.
+* - WriteAddr : FLASH's internal address to write to.
+* - NumByteToWrite : number of bytes to write to the FLASH.
+* Output : None
+* Return : None
+*******************************************************************************/
+void W25QXXFlash::SPI_FLASH_BufferWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite) {
+ uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;
+
+ Addr = WriteAddr % SPI_FLASH_PageSize;
+ count = SPI_FLASH_PageSize - Addr;
+ NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
+ NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
+
+ if (Addr == 0) { // WriteAddr is SPI_FLASH_PageSize aligned
+ if (NumOfPage == 0) { // NumByteToWrite < SPI_FLASH_PageSize
+ SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
+ }
+ else { // NumByteToWrite > SPI_FLASH_PageSize
+ while (NumOfPage--) {
+ SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
+ WriteAddr += SPI_FLASH_PageSize;
+ pBuffer += SPI_FLASH_PageSize;
+ }
+ SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
+ }
+ }
+ else { // WriteAddr is not SPI_FLASH_PageSize aligned
+ if (NumOfPage == 0) { // NumByteToWrite < SPI_FLASH_PageSize
+ if (NumOfSingle > count) { // (NumByteToWrite + WriteAddr) > SPI_FLASH_PageSize
+ temp = NumOfSingle - count;
+ SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
+ WriteAddr += count;
+ pBuffer += count;
+ SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
+ }
+ else
+ SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
+ }
+ else { // NumByteToWrite > SPI_FLASH_PageSize
+ NumByteToWrite -= count;
+ NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
+ NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
+
+ SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
+ WriteAddr += count;
+ pBuffer += count;
+
+ while (NumOfPage--) {
+ SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
+ WriteAddr += SPI_FLASH_PageSize;
+ pBuffer += SPI_FLASH_PageSize;
+ }
+
+ if (NumOfSingle != 0)
+ SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
+ }
+ }
+}
+
+/*******************************************************************************
+* Function Name : SPI_FLASH_BufferRead
+* Description : Reads a block of data from the FLASH.
+* Input : - pBuffer : pointer to the buffer that receives the data read
+* from the FLASH.
+* - ReadAddr : FLASH's internal address to read from.
+* - NumByteToRead : number of bytes to read from the FLASH.
+* Output : None
+* Return : None
+*******************************************************************************/
+void W25QXXFlash::SPI_FLASH_BufferRead(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead) {
+ // Select the FLASH: Chip Select low
+ W25QXX_CS_L;
+
+ // Send "Read from Memory " instruction
+ spi_flash_Send(W25X_ReadData);
+
+ // Send ReadAddr high nibble address byte to read from
+ spi_flash_Send((ReadAddr & 0xFF0000) >> 16);
+ // Send ReadAddr medium nibble address byte to read from
+ spi_flash_Send((ReadAddr & 0xFF00) >> 8);
+ // Send ReadAddr low nibble address byte to read from
+ spi_flash_Send(ReadAddr & 0xFF);
+
+ if (NumByteToRead <= 32 || !flash_dma_mode) {
+ while (NumByteToRead--) { // While there is data to be read
+ // Read a byte from the FLASH
+ *pBuffer = spi_flash_Rec();
+ // Point to the next location where the byte read will be saved
+ pBuffer++;
+ }
+ }
+ else
+ spi_flash_Read(pBuffer, NumByteToRead);
+
+ W25QXX_CS_H;
+}
+
+#endif // HAS_SPI_FLASH