Hi:
I am working on a custom board equipped with K66 MCU(120MHz, and 60MHz bus clock), serial Flash, and other peripherals. The purpose of serial Flash is just for data storage. I have successfully read the device JEDEC ID through DSPI bus 1 (20MHz baud). However, based on the scope observation, the delay between 8-bit frames are too long (approximately 3.2us). Please refer to the following scope screenshot,
Yellow signal: PCS0 on DSPI1,
Green signal: CLK,
Blue signal: SOUT, and
Pink signal: SIN.
I would like to have minimum delay between transfer by setting delay with DSPI_HAL_SetDelay() function. Based on the calculation,
PCSSCK=0, CSSCk=0 --> tcsc=(1/60M)*1*2=33.3ns
PASS=0, ASC=0, tasc=(1/60M)*1*2=33.3ns
PDT=0, DT=0, tdt=(1/60M)*1*2=33.3ns
I have verified the register has been correct set to zero before calling transfer. Based on the reference manual, the delay between transfer should be tasc+tcsc or maybe even none. However, I have 3.2us between transfer. This is too much away from specification. I wonder if any thing wrong in my code. Please let me know if you have idea or recommendation. Thank you very much.
The following are source code for my test:
int main(void) { uint32_t int32utmp; /* Write your code here */ // Init hardware hardware_init(); dbg_uart_init(); PRINTF("\r\n\nRunning SPI_Test example."); // Configure SPI pins configure_spi_pins(BOARD_ETHSW_SERFLASH_SPI_INSTANCE); //configure SPI1 gpio_Init(); int32utmp = W25Q128FV_ReadID(); if (int32utmp == 0xEF4018) printf("\nW25Q128FV is detected."); /* This for loop should be replaced. By default this loop allows a single stepping. */ for (;;) { } /* Never leave main */ return 0; }
uint32_t W25Q128FV_ReadID(void) { uint8_t send_buf[5]; uint8_t recv_buf[5]; uint32_t result; GPIOE_PSOR |= 0x00000001; result = dspicomm_w25qxf_init(DSPI_W25QxFV_INSTANCE, TRANSFER_HIGH_BAUDRATE); GPIOE_PCOR |= 0x00000001; if (result != kStatus_DSPI_Success) return (-1); send_buf[0] = JEDEC_ID; GPIOE_PSOR |= 0x00000001; result = dspicomm_read(DSPI_W25QxFV_INSTANCE, send_buf, 1, recv_buf, 3); GPIOE_PCOR |= 0x00000001; dspicomm_close(DSPI_W25QxFV_INSTANCE); result = (uint32_t)(recv_buf[1] << 16); // Manuf ID : Winbond = 0xEF result |= recv_buf[2] << 8; // Memory Type : SPI Serial Flash = 0x40 result |= recv_buf[3]; // Memory Size : W25Q128FV = 0x18 return result; }
int8_t dspicomm_w25qxf_init(uint32_t instance, uint32_t speed) { dspi_status_t dspiResult; uint32_t calculatedBaudRate; w25Q128FV_masterUserConfig.isChipSelectContinuous = true; w25Q128FV_masterUserConfig.isSckContinuous = false; w25Q128FV_masterUserConfig.pcsPolarity = kDspiPcs_ActiveLow; w25Q128FV_masterUserConfig.whichCtar = kDspiCtar0; w25Q128FV_masterUserConfig.whichPcs = kDspiPcs0; // Initialize master driver. dspiResult = DSPI_DRV_MasterInit(instance, &w25Q128FV_masterState, &w25Q128FV_masterUserConfig); if (dspiResult != kStatus_DSPI_Success) { PRINTF("\r\nERROR: Can not initialize SPI master driver!"); return -1; } // Setup the configuration. w25Q128FV_masterDevice.dataBusConfig.bitsPerFrame = 8; w25Q128FV_masterDevice.dataBusConfig.clkPhase = kDspiClockPhase_FirstEdge; w25Q128FV_masterDevice.dataBusConfig.clkPolarity = kDspiClockPolarity_ActiveHigh; w25Q128FV_masterDevice.dataBusConfig.direction = kDspiMsbFirst; w25Q128FV_masterDevice.bitsPerSec = speed; // dspiResult = DSPI_DRV_MasterConfigureBus(instance, // &w25Q128FV_masterDevice, // &calculatedBaudRate); // if (dspiResult != kStatus_DSPI_Success) // { // printf("\r\nERROR: failure in configuration SPI bus!"); // return -1; // } // The original DSPI_DRV_MasterConfigureBus() routine takes about 30us to complete. // The following initialization takes only about 2.5us dspi_master_state_t * dspiState = (dspi_master_state_t *)g_dspiStatePtr[instance]; SPI_Type *base = g_dspiBase[instance]; SPI_BWR_CTAR_DBR(base, dspiState->whichCtar, 0x01); //DBR = 1 SPI_BWR_CTAR_PBR(base, dspiState->whichCtar, 0x01); //PBR = 1 SPI_BWR_CTAR_BR(base, dspiState->whichCtar, 0x00); //BR = 0, Baud = (fp/PBR)*[(1+DBR)/BR] = (60M/3)*[(1+1)/2] = 20MHz SPI_BWR_CTAR_FMSZ(base, dspiState->whichCtar, (w25Q128FV_masterDevice.dataBusConfig.bitsPerFrame - 1)); SPI_BWR_CTAR_CPOL(base, dspiState->whichCtar, w25Q128FV_masterDevice.dataBusConfig.clkPolarity); SPI_BWR_CTAR_CPHA(base, dspiState->whichCtar, w25Q128FV_masterDevice.dataBusConfig.clkPhase); SPI_BWR_CTAR_LSBFE(base, dspiState->whichCtar, w25Q128FV_masterDevice.dataBusConfig.direction); DSPI_HAL_SetDelay(base, dspiState->whichCtar, 0x00, 0x00, kDspiPcsToSck); //PCSSCK=0, CSSCK=0, Tcsc=(1/60M)*1*2=32ns DSPI_HAL_SetDelay(base, dspiState->whichCtar, 0x00, 0x00, kDspiLastSckToPcs); //PASC=0, ASC=0, Tasc=(1/60M)*1*2=32ns DSPI_HAL_SetDelay(base, dspiState->whichCtar, 0x00, 0x00, kDspiAfterTransfer); //PDT=0, DT=0, Tdt=(1/60M)*1*2=32ns return (kStatus_DSPI_Success); } int8_t dspicomm_close(uint32_t instance) { dspi_status_t dspiResult; dspiResult = DSPI_DRV_MasterDeinit(instance); if (dspiResult != kStatus_DSPI_Success) { PRINTF("\r\nERROR: failure in configuration SPI bus!"); return -1; } return (kStatus_DSPI_Success); } int32_t dspicomm_write(uint32_t instance, uint8_t *data, uint32_t size) { dspi_status_t dspiResult; uint32_t wordsTransfer = 0; // Send the data. dspiResult = DSPI_DRV_MasterTransfer(instance, NULL, data, NULL, size); if (dspiResult != kStatus_DSPI_Success) { PRINTF("\r\nERROR: Write data transfer error!"); return (-1); } // Wait until the transfer is complete. while (DSPI_DRV_MasterGetTransferStatus(instance, &wordsTransfer) == kStatus_DSPI_Busy) {} return size; } int32_t dspicomm_read(uint32_t instance, uint8_t *txData, uint32_t txSize, uint8_t *rxData, uint32_t rxSize) { dspi_status_t dspiResult; uint32_t wordsTransfer = 0; // Send the data. dspiResult = DSPI_DRV_MasterTransfer(instance, NULL, txData, rxData, txSize+rxSize); if (dspiResult != kStatus_DSPI_Success) { PRINTF("\r\nERROR: Read data transfer error!"); return (-1); } // Wait until the transfer is complete. while (DSPI_DRV_MasterGetTransferStatus(instance, &wordsTransfer) == kStatus_DSPI_Busy) {} return rxSize; }
已解决! 转到解答。
Hi, Peter,
Regarding your question, for the K66, the PTE0 is multiplexed with the other peripherals as following:
Pin assignment:PTE0, ADC1_SE4a,SPI1_PCS1, UART1_TX, SDHC0_D1, TRACE_CLKOUT,I2C1_SDA RTC_CLKOUT.
From your code, it seems that you set the PTE0 pin as GPIO output and toggle it in software. If you use the pin PTE0 as SPI1_PCS1, you should not set the PTE0 as GPIO by setting the MUX bits of PORTE_PCR0 as ALT1, you should set the MUX bits of PORTE_PCR0 as ALT2 so that the SPI1 module can drive the PTE2 automatically.
Of course, you are right that the PDT/DT bits in SPIx_CTARn determine the interval between two SPI transfer. If you do not call the SDK function and write the SPIx_PUSHR register directly, I think the interval between two transfer will be very short which is based on the DT/PTD mechanism. I have checked your code, I think the code is okay, but the SDK is a high level function, the DSPI_DRV_MasterTransfer() function just put the data to an internal buffer, the code efficiency is low, which leads to the interval is long. If you require the speed of SPI, I suggest you write register directly without calling SDK function.
Hope it can help you.
BR
Xiangjun Rong
Hi, Peter,
Regarding your question, for the K66, the PTE0 is multiplexed with the other peripherals as following:
Pin assignment:PTE0, ADC1_SE4a,SPI1_PCS1, UART1_TX, SDHC0_D1, TRACE_CLKOUT,I2C1_SDA RTC_CLKOUT.
From your code, it seems that you set the PTE0 pin as GPIO output and toggle it in software. If you use the pin PTE0 as SPI1_PCS1, you should not set the PTE0 as GPIO by setting the MUX bits of PORTE_PCR0 as ALT1, you should set the MUX bits of PORTE_PCR0 as ALT2 so that the SPI1 module can drive the PTE2 automatically.
Of course, you are right that the PDT/DT bits in SPIx_CTARn determine the interval between two SPI transfer. If you do not call the SDK function and write the SPIx_PUSHR register directly, I think the interval between two transfer will be very short which is based on the DT/PTD mechanism. I have checked your code, I think the code is okay, but the SDK is a high level function, the DSPI_DRV_MasterTransfer() function just put the data to an internal buffer, the code efficiency is low, which leads to the interval is long. If you require the speed of SPI, I suggest you write register directly without calling SDK function.
Hope it can help you.
BR
Xiangjun Rong
Hi Xiangjun:
Thanks your feedback!
The PTE0 is used as GPIO for output to scope for debug purpose, not for SPI1. I have no problem with SPI1 signals.
Based on your feedback, I have tried to send data by writing to PUSHR register without calling KSDK, it gives me the best situation 760ns between transfer, which is much better than before. I think it will be OK for my system.
I also tried to implement based on eDMA in KSDK, it gives me pretty good 67ns between transfer. However, the initialization takes 45us to complete. I will assume it will be good for large quantity of data transfer, not for 4 bytes data transfer.
Thanks your help.
Peter Shih