Marlin 2.0 for Flying Bear 4S/5
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

395 lines
13 KiB

/**
* 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)
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 < 33) {
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