I need a QSPI Master, with 40 MHz.
FLEXSPI (as QSPI) is not possible: two signals are missing on the extension headers.
So, I have implemented QSPI via "FastGPIO": it works:
41 MHz QSPI SCLK (potentially up to 60 MHz possible).
It is part now of my main project:
tjaekel/MaaXBoard-RT_SPIder: MaaXBoard-RT SPIder framework (github.com)
If you want to know "how I have implemented"...
See the file "pin_mux.c" where the GPIO pins are configured.
And see the file "QSPI_sw.c" (plus *.h) how I toggle the GPIO pins.
/* configure LPSPI2 as FastGPIO pins - for SW QSPI master */ IOMUXC_GPR->GPR43 = 0x00000F84; /* select FastGPIO, CM7_GPIO3, bits 23, 24, 25, 26, 27, 18 */ GPIO_PinInit(CM7_GPIO3, 23U, &USER_GPIO_config); GPIO_PinInit(CM7_GPIO3, 24U, &USER_GPIO_config); GPIO_PinInit(CM7_GPIO3, 25U, &USER_GPIO_config); GPIO_PinInit(CM7_GPIO3, 26U, &USER_GPIO_config); GPIO_PinInit(CM7_GPIO3, 27U, &USER_GPIO_config); GPIO_PinInit(CM7_GPIO3, 18U, &USER_GPIO_config); IOMUXC_SetPinMux( IOMUXC_GPIO_AD_24_GPIO_MUX3_IO23, /* configured as GPIO - SCLK */ 0x0U); IOMUXC_SetPinConfig( IOMUXC_GPIO_AD_24_GPIO_MUX3_IO23, 0x03U); IOMUXC_SetPinMux( IOMUXC_GPIO_AD_25_GPIO_MUX3_IO24, /* configured as GPIO - PCS0*/ 0x0U); IOMUXC_SetPinConfig( IOMUXC_GPIO_AD_25_GPIO_MUX3_IO24, 0x03U); IOMUXC_SetPinMux( IOMUXC_GPIO_AD_26_GPIO_MUX3_IO25, /* configured as GPIO - D0 */ 0x0U); IOMUXC_SetPinConfig( IOMUXC_GPIO_AD_26_GPIO_MUX3_IO25, 0x03U); IOMUXC_SetPinMux( IOMUXC_GPIO_AD_27_GPIO_MUX3_IO26, /* configured as GPIO - D1 */ 0x0U); IOMUXC_SetPinConfig( IOMUXC_GPIO_AD_27_GPIO_MUX3_IO26, 0x03U); IOMUXC_SetPinMux( IOMUXC_GPIO_AD_28_GPIO_MUX3_IO27, /* configured as GPIO - D2 */ 0x0U); IOMUXC_SetPinConfig( IOMUXC_GPIO_AD_28_GPIO_MUX3_IO27, 0x03U); IOMUXC_SetPinMux( IOMUXC_GPIO_AD_19_GPIO_MUX3_IO18, /* configured as GPIO - D3 */ 0x0U); IOMUXC_SetPinConfig( IOMUXC_GPIO_AD_19_GPIO_MUX3_IO18, 0x03U);
/* * QSPI_sw.c * * Created on: Sep 11, 2023 * Author: tjaekel */ /* QSPI in SW GPIO mode (as FastGPIO) */ #include <stdio.h> #include <stdlib.h> #include "fsl_gpio.h" #include "QSPI_sw.h" #include "MEMORY_attributes.h" /* trimmed for 41 MHz, periodic SCLK * our scope has 500 MHz sampling rate, to 2ns error, * MCU runs with 1 GHz, assuming 2 cycles per __NOP() is also 2ns * It looks "perfect", the 'jitter' and inaccuracy comes from the scope! */ /* for SCLK low period */ #define NOP_DELAY __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP() /* for SCLK high period */ #define NOP_DELAY2 __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP() /* last in loop high period */ #define NOP_DELAY2b __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP() /* last (2nd) delay on first byte, before entering loop */ #define NOP_DELAY3 __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP();\ __NOP() uint8_t GPIO_QSPIbuffer[16] = {0xA5, 0x5A, 0x12, 0x21, 0x44, 0x88, 0x77, 0x42, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC}; void QSPI_Init(void) { /* set PCS signal high, all other low */ /* optional: we could have all signals except PCS as inputs (tri-stated) */ CM7_GPIO3->DR_SET = (1 << QSPI_PCSbit); CM7_GPIO3->DR_CLEAR = (1 << QSPI_SCLKbit) | ( 1 << QSPI_D0bit) | (1 << QSPI_D1bit) | (1 << QSPI_D2bit) | (1 << QSPI_D3bit); } #pragma GCC push_options #pragma GCC optimize ("O3") ITCM_CM7 void QSPI_SendWrite(uint8_t *buf, size_t numBytes) { uint8_t b; uint32_t w; __asm volatile ( "cpsid i" ::: "memory" ); __asm volatile ( "dsb" ); __asm volatile ( "isb" ); /* Set Direction of QSPI D0..D3 as output */ /* 2. Set initial D0..D1 - PCS still high, SCLK starting low */ b = *buf++; /* TODO: we could handle 3 bits at once */ w = (b & 0x10) << (QSPI_D0bit - 4); w |= (b & 0x20) << (QSPI_D1bit - 5); w |= (b & 0x40) << (QSPI_D2bit - 6); w |= (b & 0x80) << (QSPI_D3bit - 7); w |= 1 << QSPI_PCSbit; CM7_GPIO3->DR = w; ////__NOP(); /* Set and keep PCS low */ w &= ~(1 << QSPI_PCSbit); CM7_GPIO3->DR = w; NOP_DELAY; w |= 1 << QSPI_SCLKbit; CM7_GPIO3->DR = w; /* keep D0..D1 stable half a clock period on SCLK high */ NOP_DELAY2; /* set SCLK low (falling edge, change to next 4bit Dx) */ w = (b & 0x01) << (QSPI_D0bit - 0); w |= (b & 0x02) << (QSPI_D1bit - 1); w |= (b & 0x04) << (QSPI_D2bit - 2); w |= (b & 0x08) << (QSPI_D3bit - 3); CM7_GPIO3->DR = w; /* wait half a clock period with SCLK low */ NOP_DELAY; /* SCLK high */ w |= 1 << QSPI_SCLKbit; CM7_GPIO3->DR = w; NOP_DELAY3; numBytes--; while (numBytes--) { b = *buf++; w = (b & 0x10) << (QSPI_D0bit - 4); w |= (b & 0x20) << (QSPI_D1bit - 5); w |= (b & 0x40) << (QSPI_D2bit - 6); w |= (b & 0x80) << (QSPI_D3bit - 7); CM7_GPIO3->DR = w; NOP_DELAY; w |= 1 << QSPI_SCLKbit; CM7_GPIO3->DR = w; NOP_DELAY2; w = (b & 0x01) << (QSPI_D0bit - 0); w |= (b & 0x02) << (QSPI_D1bit - 1); w |= (b & 0x04) << (QSPI_D2bit - 2); w |= (b & 0x08) << (QSPI_D3bit - 3); CM7_GPIO3->DR = w; NOP_DELAY; w |= 1 << QSPI_SCLKbit; CM7_GPIO3->DR = w; NOP_DELAY2b; } /* Set PCS high */ w |= 1 << QSPI_PCSbit; CM7_GPIO3->DR = w; /* change D0..D1 direction to input (tri-state) */ __asm volatile ( "cpsie i" ::: "memory" ); } #pragma GCC pop_options void QSPI_Test(void) { size_t i; QSPI_Init(); QSPI_SendWrite(GPIO_QSPIbuffer, 16); }
/* * QSPI_sw.h * * Created on: Sep 11, 2023 * Author: tjaekel */ #ifndef QSPI_SW_H_ #define QSPI_SW_H_ #define QSPI_SCLKbit 23 #define QSPI_PCSbit 24 #define QSPI_D0bit 25 #define QSPI_D1bit 26 #define QSPI_D2bit 27 #define QSPI_D3bit 18 void QSPI_Test(void); #endif /* QSPI_SW_H_ */