diff options
Diffstat (limited to 'Marlin/src/libs/W25Qxx.cpp')
-rw-r--r-- | Marlin/src/libs/W25Qxx.cpp | 395 |
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 |