diff --git a/CMakeLists.txt b/CMakeLists.txt index 3695cb6d85a68baced219cc0d63dc3ad4d888c7f..34e746e3b969f318213433bd683d6b9fc3097b73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -290,6 +290,9 @@ sbs_target(test-i2c-f7 stm32f767zi_nucleo) add_executable(test-wiz5500 src/tests/drivers/test-wiz5500.cpp) sbs_target(test-wiz5500 stm32f767zi_gemini_gs) +add_executable(test-Qflash src/tests/drivers/QuadSpi-Flash/test-Qflash.cpp) +sbs_target(test-Qflash stm32f767zi_compute_unit) + #-----------------------------------------------------------------------------# # Tests - Events # #-----------------------------------------------------------------------------# diff --git a/cmake/boardcore.cmake b/cmake/boardcore.cmake index 309394fcfbd8a65727d0878fc11c102e9f6906cc..0de9353381847617ad5ac8c019146b2c44847c15 100644 --- a/cmake/boardcore.cmake +++ b/cmake/boardcore.cmake @@ -65,6 +65,7 @@ set(BOARDCORE_SRC ${BOARDCORE_PATH}/src/shared/drivers/i2c/I2CDriver-f7.cpp ${BOARDCORE_PATH}/src/shared/drivers/i2c/I2C.cpp ${BOARDCORE_PATH}/src/shared/drivers/WIZ5500/WIZ5500.cpp + ${SBS_BASE}/src/tests/drivers/QuadSpi-flash/qspi-flash.cpp # Events ${BOARDCORE_PATH}/src/shared/events/EventBroker.cpp diff --git a/src/tests/boards/test-qspi-flash.cpp b/src/tests/boards/test-qspi-flash.cpp index 293ed82149aec98b1a017ddaafcad681225191fa..ca2255a98e60929141f016a2aac4d43c7a7e69e6 100644 --- a/src/tests/boards/test-qspi-flash.cpp +++ b/src/tests/boards/test-qspi-flash.cpp @@ -25,6 +25,16 @@ * proper driver for the flash will need to be developed! */ +/** + * A tiny look at the protocol + * QUADSPI is a protocol to manage the communication between MCU and a flash memory. + * The peripheral takes care of all the pins involved, so you don't even need to toggle + * the slave-select pin. In indirect mode every communication is handled by the firmware + * and registers, also every communication is triggered as soon as the peripheral has + * enough information to perform a command. + * Other two modes are available: polling mode and mapped mode. +*/ + #include <miosix.h> #include <utils/ClockUtils.h> @@ -49,10 +59,6 @@ GpioPin flash_io1(GPIOF_BASE, 9); GpioPin flash_io2(GPIOF_BASE, 7); GpioPin flash_io3(GPIOF_BASE, 6); -#include <miosix.h> - -using namespace miosix; - int main() { flash_ncs.mode(Mode::ALTERNATE); @@ -79,7 +85,7 @@ int main() RCC_SYNC(); delayMs(2 * 1000); - printf("Starting\n"); + printf("Starting!\n"); QUADSPI->CR |= QUADSPI_CR_ABORT; // Abort ongoing commands @@ -91,18 +97,16 @@ int main() QUADSPI->CR = 0; QUADSPI->DCR = 0; QUADSPI->CCR = 0; + QUADSPI->DLR = 0; // QSPI peripheral initialization QUADSPI->CR |= QUADSPI_CR_SSHIFT | // Wait a full cycle to read - 3 << QUADSPI_CR_PRESCALER_Pos; // QSPI clock = 216MHz / 3 = 72MHz + 3 << QUADSPI_CR_PRESCALER_Pos; // QSPI clock = 216MHz / 4 = 54MHz // QUADSPI->DCR |= // 21 << QUADSPI_DCR_FSIZE_Pos; // Flash size 32Mb = 4MB = 2^(21+1) // bytes - // Enable the peripheral - QUADSPI->CR |= QUADSPI_CR_EN; - // Send read ID command - 0x9Fcl { QUADSPI->CCR |= 1 << QUADSPI_CCR_FMODE_Pos | // Indirect read mode @@ -111,22 +115,32 @@ int main() 0 << QUADSPI_CCR_ADMODE_Pos | // No address 1 << QUADSPI_CCR_IMODE_Pos; // Instruction on 1-wire - QUADSPI->DLR = 23; // Expect to receive 24 bytes + // Enable the peripheral (IT HAS TO BE DONE AFTER SETTING CCR REGISTER) + QUADSPI->CR |= QUADSPI_CR_EN; - printf("CCR: %lx\n", QUADSPI->CCR); + QUADSPI->DLR = 2; // Expect to receive 3 bytes regarding ID of the flash // Trigger communication start by writing the instruction QUADSPI->CCR |= 0x9F << QUADSPI_CCR_INSTRUCTION_Pos; - // Wait for the transaction to complete, and disable the peripheral. - int count = 0; - while (QUADSPI->SR & QUADSPI_SR_BUSY) - count++; + // wait till the end of the communication + while (!(QUADSPI->SR & (1 << QUADSPI_SR_TCF_Pos))) + ; + + // reset transfer complete flag (TCF) + QUADSPI->FCR &= ~(1 << QUADSPI_FCR_CTCF_Pos); + + // until there are some bytes in the quadspi buffer (FIFO) keep reading them + while (QUADSPI->SR & (63 << QUADSPI_SR_FLEVEL_Pos)) + { + printf("Data: 0x%lx\n", QUADSPI->DR); + } // Disable the peripheral QUADSPI->CR &= ~QUADSPI_CR_EN; - printf("Data: 0x%lx %d\n", QUADSPI->DR, count); + printf("QUADSPI disabled.\n"); + printf("end!\n"); } while (true) diff --git a/src/tests/drivers/QuadSpi-Flash/qspi-flash.cpp b/src/tests/drivers/QuadSpi-Flash/qspi-flash.cpp new file mode 100644 index 0000000000000000000000000000000000000000..da25f93452f4a24e2c4527638bb34bf6db557deb --- /dev/null +++ b/src/tests/drivers/QuadSpi-Flash/qspi-flash.cpp @@ -0,0 +1,217 @@ +#include "qspi-flash.h" + +using namespace miosix; +using namespace Boardcore; +using namespace FlashMemory; + + +/** + * QSPI Flash pins + * + * FLASH_NSS - PB10 - AF9 - QUADSPI_BK1_NCS + * FLASH_CLK - PF10 - AF9 - QUADSPI_CLK + * FLASH_IO0 - PF8 - AF10 - QUADSPI_BK1_IO0 + * FLASH_IO1 - PF9 - AF10 - QUADSPI_BK1_IO1 + * FLASH_IO2 - PF7 - AF9 - QUADSPI_BK1_IO2 + * FLASH_IO3 - PF6 - AF9 - QUADSPI_BK1_IO3 + */ + +GpioPin flash_ncs(GPIOB_BASE, 10); +GpioPin flash_sck(GPIOF_BASE, 10); +GpioPin flash_io0(GPIOF_BASE, 8); +GpioPin flash_io1(GPIOF_BASE, 9); +GpioPin flash_io2(GPIOF_BASE, 7); +GpioPin flash_io3(GPIOF_BASE, 6); + + +void QSPI::enable() { QUADSPI->CR |= QUADSPI_CR_EN; } + + +void QSPI::disable() { QUADSPI->CR &= ~QUADSPI_CR_EN; } + + +void QSPI::init() { + + // init GPIO peripheral pins + flash_ncs.mode(Mode::ALTERNATE); + flash_ncs.alternateFunction(9); + flash_ncs.speed(Speed::_100MHz); + flash_sck.mode(Mode::ALTERNATE); + flash_sck.alternateFunction(9); + flash_sck.speed(Speed::_100MHz); + flash_io0.mode(Mode::ALTERNATE); + flash_io0.alternateFunction(10); + flash_io0.speed(Speed::_100MHz); + flash_io1.mode(Mode::ALTERNATE); + flash_io1.alternateFunction(10); + flash_io1.speed(Speed::_100MHz); + flash_io2.mode(Mode::ALTERNATE); + flash_io2.alternateFunction(9); + flash_io2.speed(Speed::_100MHz); + flash_io3.mode(Mode::ALTERNATE); + flash_io3.alternateFunction(9); + flash_io3.speed(Speed::_100MHz); + + // init peripheral clock + ClockUtils::enablePeripheralClock((QUADSPI_TypeDef*)QSPI_BASE); + + RCC_SYNC(); + + Thread::sleep(200); + + // abort possible ongoing command + QUADSPI->CR |= QUADSPI_CR_ABORT; + + // Wait while aborted + while (QUADSPI->CR & QUADSPI_CR_ABORT) {;} + + // disable peripheral + QSPI::disable(); + + // reset configuration registers + QUADSPI->CR = 0; + QUADSPI->DCR = 0; + QUADSPI->CCR = 0; + QUADSPI->DLR = 0; + + // reset transfer complete flag (TCF) + QUADSPI->FCR &= ~(1 << QUADSPI_FCR_CTCF_Pos); + + // peripheral default initialization + QUADSPI->CR |= QUADSPI_CR_SSHIFT | // Wait a full cycle to read + 3 << QUADSPI_CR_PRESCALER_Pos; // QSPI clock = 216MHz / 4 = 54MHz + // QUADSPI->DCR |= + // 21 << QUADSPI_DCR_FSIZE_Pos; // Flash size 32Mb = 4MB = 2^(21+1) + // bytes + +} + + +void QSPI::waitBusy() { + while(!(QUADSPI->SR & (1 << QUADSPI_SR_BUSY_Pos))) {;} +} + + +void QSPI::waitTransfer() { + + // wait till the end of the communication + while (!(QUADSPI->SR & (1 << QUADSPI_SR_TCF_Pos))) {;} + + // reset transfer complete flag (TCF) + QUADSPI->FCR &= ~(1 << QUADSPI_FCR_CTCF_Pos); + +} + + +// constructor class qspi_flash +qspi_flash::qspi_flash() {;} + + +uint8_t qspi_flash::read_status_reg() { + + // indirect read mode, la comunicazione comincia dopo aver scritto l'istruzione + // nel CCR register. + // 1 invio Read status register command + // ricevo un byte di dati con il valore dello status register. + + QSPI::disable(); + + QSPI::init(); + + QUADSPI->CCR |= 1 << QUADSPI_CCR_FMODE_Pos | // Indirect read mode + 1 << QUADSPI_CCR_DMODE_Pos | // Data on 1-wire + 0 << QUADSPI_CCR_ABMODE_Pos | // No alternate bytes + 0 << QUADSPI_CCR_ADMODE_Pos | // No address + 1 << QUADSPI_CCR_IMODE_Pos; // Instruction on 1-wire + + // Expect to receive 1 byte (flash status register) + QUADSPI->DLR = 0; + + // DEVE ESSERE FATTO PRIMA DI SCRIVERE L'ISTRUZIONE NEL REGISTRO, + // PERCHè IN QUESTO CASO LA COMUNICAZIONE COMINCIA SCIVENDO L'ISTRUZIONE IN CCR + QSPI::enable(); + + // Trigger communication start by writing the instruction + QUADSPI->CCR |= Commands::READ_STATUS_REG << QUADSPI_CCR_INSTRUCTION_Pos; + + // wait till data trasfer is complete + QSPI::waitTransfer(); + + return QUADSPI->DR; +} + + +void qspi_flash::write_enable() { + + // in indirect write mode with no data or address phase (DMODE = 0, ADMODE = 0) the + // communication starts whenever the CCR register is written. + // 1 send wren command + // 2 read status register + // 3 wait for bit WEL = 1 + // poi program/erase commands + + QSPI::disable(); + + QSPI::init(); // reset previous configurations and init clock and gpio + + // indirect write mode, istruction on 1 wire, no data, no address, no alternate bytes + QUADSPI->CCR |= 1 << QUADSPI_CCR_IMODE_Pos; + + QSPI::enable(); + + // start communication writing the instruction to CCR register busy bit = 1 + QUADSPI->CCR |= Commands::WRITE_ENABLE; + + QSPI::waitBusy(); + + QSPI::disable(); + + printf("prima del whileeee\n"); + + // read status register until WEL is set (write enable latch) + uint8_t status = read_status_reg(); + do { + Thread::sleep(1); + status = read_status_reg(); + } + while(!(status & 1 << 1)); // WEL_pos = 1 è il secondo bit +} + + + +uint32_t qspi_flash::readID() { + + QSPI::disable(); + + QSPI::init(); + + QUADSPI->CCR |= 1 << QUADSPI_CCR_FMODE_Pos | // Indirect read mode + 1 << QUADSPI_CCR_DMODE_Pos | // Data on 1-wire + 0 << QUADSPI_CCR_ABMODE_Pos | // No alternate bytes + 0 << QUADSPI_CCR_ADMODE_Pos | // No address + 1 << QUADSPI_CCR_IMODE_Pos; // Instruction on 1-wire + + // Expect to receive 3 bytes regarding ID of the flash + QUADSPI->DLR = 2; + + // DEVE ESSERE FATTO PRIMA DI SCRIVERE L'ISTRUZIONE NEL REGISTRO, + // PERCHè IN QUESTO CASO LA COMUNICAZIONE COMINCIA SCIVENDO L'ISTRUZIONE IN CCR + QSPI::enable(); + + // Trigger communication start by writing the instruction + QUADSPI->CCR |= Commands::READ_ID << QUADSPI_CCR_INSTRUCTION_Pos; + + // wait till communication is ended + QSPI::waitTransfer(); + + // if there are some bytes in the quadspi buffer (FIFO), read them + if (QUADSPI->SR & (63 << QUADSPI_SR_FLEVEL_Pos)) { + uint32_t myID = QUADSPI->DR; + QSPI::disable(); + return myID; + } + else { + QSPI::disable(); + return -1; + } +} \ No newline at end of file diff --git a/src/tests/drivers/QuadSpi-Flash/qspi-flash.h b/src/tests/drivers/QuadSpi-Flash/qspi-flash.h new file mode 100644 index 0000000000000000000000000000000000000000..f710070ac0b9353d08c12e9b72ec6a9a6154f470 --- /dev/null +++ b/src/tests/drivers/QuadSpi-Flash/qspi-flash.h @@ -0,0 +1,128 @@ +#include <miosix.h> +#include <utils/ClockUtils.h> + +#pragma once + +using namespace miosix; +using namespace Boardcore; + + +/* model of flash memory on compute unit: MX25R3235FM1IL0 4MB + * FLASH memory space organisation: + * - number of byte in the memory: 4.194.304 >> about 4 MB + * - "pages" of 256 Byte each - number of pages: 16.384 + * - "sector" of about 4 KByte - number of sector: 1.024 + * - "block32" of about 32 KByte - number of block32: 128 + * - "block64" of about 64 KByte - number of block64: 64 +*/ + +namespace FlashMemory { + +static const uint32_t PAGES_PER_SECTOR = 16; +static const uint32_t SECTORS_PER_BLOCK32 = 8; + +static const uint32_t BLOCK32_NUM = 128; +static const uint32_t BLOCK64_NUM = 64; +static const uint32_t SECTORS_NUM = 1024; +static const uint32_t PAGES_NUM = 16384; +static const uint32_t BYTES_NUM = 4194304; + +// sizes of each area in byte +static const uint32_t PAGE_SIZE = 256; +static const uint32_t SECTOR_SIZE = PAGE_SIZE * PAGES_PER_SECTOR; +static const uint32_t BLOCK32_SIZE = SECTOR_SIZE * SECTORS_PER_BLOCK32; +static const uint32_t BLOCK64_SIZE = BLOCK32_SIZE * 2; + +// memory size in byte +static const uint32_t MEMORY_SIZE = BLOCK64_SIZE * BLOCK64_NUM; // about 4 MB + +}; + + +// QUADSPI peripheral utility-stuff +namespace QSPI { + + // enable/ disbale quadspi + void enable(); + void disable(); + + // init peripheral clock and GPIO + void init(); + + // wait till the current operation is ended + void waitBusy(); + + // wait till all the expected bytes have been transferred + void waitTransfer(); + +} + + +class qspi_flash { + +public: + + // constructor + qspi_flash(); + + // enable writing on memory + void write_enable(); + + // read unique device ID + uint32_t readID(); + +private: + + // disable writing on memory + void write_disable(); + + // read status register of the flash memory + uint8_t read_status_reg(); + + // most important flash memory commands + enum Commands + { + // read unique ID of the memory + READ_ID = 0x9F, + + // write enable, needs to be executed before modify any data + WRITE_ENABLE = 0x06, + + // write disable + WRITE_DISABLE = 0x04, + + // read status register + READ_STATUS_REG = 0x05, + + // write status register + WRITE_STATUS_REG = 0x01, + + // read configuration register + READ_CONFIG_REGISTER = 0x15, + + // read data from memory + READ = 0x03, + + // write a page on memory. + PROGRAM_PAGE = 0x02, + + // erase a specific sector of the memory + SECTOR_ERASE = 0x20, + + // erase all data on the chip - THIS COULD TAKE A LONG TIME ! + ERASE_CHIP = 0xC7, + + // reset enable command + RESET_ENABLE = 0x66, + + // reset memory, reset enable command should be executed first + RESET_MEMORY = 0x99 + }; + + // important bits to be checked in the memory status register + enum bits_status_reg { + WEL_POS = 1, + WIP_POS = 0 + }; + +}; \ No newline at end of file diff --git a/src/tests/drivers/QuadSpi-Flash/test-Qflash.cpp b/src/tests/drivers/QuadSpi-Flash/test-Qflash.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eab9af8248eabfa8194ab91d4409877e3e353625 --- /dev/null +++ b/src/tests/drivers/QuadSpi-Flash/test-Qflash.cpp @@ -0,0 +1,33 @@ +// qspi-flash driver TEST, aggiunto l' eseguibile in cmakelist +// dipendenze: aggiunto qspi-flash.cpp in cmake.boardcore +// il test viene eseguito ma non sembra leggere l'ID + +#include "qspi-flash.h" + +qspi_flash mymemory; + +int main() { + + + // test readID() + printf("starting!\n"); + + uint32_t ID = mymemory.readID(); + + printf("ID: %x\n", ID); + + printf("end ID test\n"); + + + // test write enable command + printf("start write_enable command\n"); + // TODO: COMMITTTTT E POI FARE BENE FUNZIONE INIT QUADSPI E SOPRATTUTTO CHIAMARE INIT + // QUADSPI AL MOMENTO DELLA CREAZIONE DI UN' ISTASNZA DELLA CLASSE QUADSPI-FLASH NEL + // DENTRO IL COSTRUTTORE. MI RACCOMANDO FARE ABORT PRIMA DI FARE ALTRE OPERAZIONI. + mymemory.write_enable(); + + printf("end write_enable command\n"); + + while (true) + Thread::sleep(1000); +} \ No newline at end of file