cancel
Showing results for 
Search instead for 
Did you mean: 

About OCTSPI

Myasu.1
Senior

I would like to communicate with FLASH using OCTSPI on nucleo l4r5zi.   

I am creating a driver for OCTSPI for communication, but I have a question.

I plan to use the regular command mode, INDIRECT_READ and INDIRECT_WRITE.

The driver is designed to store the data to be sent in an internal buffer and send the data in the buffer via SPI on interrupt.

The image is as follows.

static uint8_t buf[256];
static uint8_t send_cnt;
static uint8_t send_num;
 
/* interrput */
void interrupt(void){
	
	if(check interrput status)
		DR register = buf[send_idx];
		send_cnt--;
		send_idx++;
		if(send_cnt==0){
			disable interrupt 
		}
	}
}
 
/* send function */
void send(uint8_t *data, uint8_t size){
	send_cnt = size;
	send_idx = 0;
	
	/* IR,AR,DLR等�?�設定 */
	~
	
	/* Set the datas to be sent to internal buffer */
	for(i=0;i<size;i++){
		buf[i] = data[i];
	}
	
	enable interrupt
}

I understand that the number of data to be sent needs to be set in the DLR register when sending/receiving.

In the Data Phase, when sending multiple bytes, the number of times to send (send_cnt) needs to be controlled by the software as described above.

In this case, I do not understand the meaning of the DLR register.

How should DLR be used?

Reference document : dm00310109-stm32l4-series-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdf

1 ACCEPTED SOLUTION
7 REPLIES 7

You'll have to adapt your paradigm to fit the way the hardware works.

The QUADSPI/OCTOSPI Write Page is transactional, there's a command, address and data phase, you know the amount of data at the outset, and you need to break it down so it doesn't span pages.

You might also want to consider your strategies with buffers, ideally one where you don't keep moving stuff around unnecessarily.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Myasu.1
Senior

@Community member​ 

thank you for quick reply.

My understanding is poor and I am not able to imagine the implementation.

For your information, I am trying to control the following flash with OCTOSPI.

w25q20ew_revi 08222017 sfdp.pdf

It states that it can write data from 1 to 256 bytes.

Could you please give me some more advice? Also, do you have any source code that could be used as a sample?

If you have a simple main loop implementation, do the QUADSPI writing in the foreground loop, and fill the next buffer up in the background/interrupts.

The pages are 256-bytes, if the address starts on a 256-byte boundary you can write the full page, otherwise you'll need to write a smaller piece that doesn't span the page boundary..

ie

To write 256 bytes ar 0x90000080, you'll need to write 128 bytes first, and the next 128 bytes at 0x90000100

The BSP code for the QUAD/OCTO parts should be in the CubeL4 driver section, and board level examples.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Myasu.1
Senior

@Community member​ 

thank you for quick reply.

To write 256 bytes ar 0x90000080, you'll need to write 128 bytes first, and the next 128 bytes at 0x90000100

0x90000080 is out of range of OCTOSPI registers, but what kind of area is it?

I could not find out by checking the manual.

0693W00000QODfJQAX.png 

Does it mean that data set in the range 0x90000000 ~ 0xA0000000 is transmitted?

By the way, I am thinking of using INDIRECT WRITE MODE for communication.

I am very sorry for so many questions.

Yes, the QUAD/OCTO SPI memory itself has a zero basis address.

However as I indicate you will need to split writes which span the page size boundaries.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

@Community member​ 

thank you for quick reply.

I am sorry if I am wrong, but I understand that the data written in 0x90000000 ~ 0xA0000000 is sent when using as Memory-mapped mode, is this correct?

In this case, I want to communicate using Indirect mode.

I have checked the CubeL4 driver on my side.

I understand that what I want to do is performed by the following function.

STM32Cube_FW_L4_V1.17.0\Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_ospi.c 

/* Send Function */
 
HAL_StatusTypeDef HAL_OSPI_Transmit_IT(OSPI_HandleTypeDef *hospi, uint8_t *pData)
{
  HAL_StatusTypeDef status = HAL_OK;
 
  /* Check the data pointer allocation */
  if (pData == NULL)
  {
    status = HAL_ERROR;
    hospi->ErrorCode = HAL_OSPI_ERROR_INVALID_PARAM;
  }
  else
  {
    /* Check the state */
    if (hospi->State == HAL_OSPI_STATE_CMD_CFG)
    {
      /* Configure counters and size */
      hospi->XferCount = READ_REG(hospi->Instance->DLR) + 1U;
      hospi->XferSize  = hospi->XferCount;
      hospi->pBuffPtr  = pData;
 
      /* Configure CR register with functional mode as indirect write */
      MODIFY_REG(hospi->Instance->CR, OCTOSPI_CR_FMODE, OSPI_FUNCTIONAL_MODE_INDIRECT_WRITE);
 
      /* Clear flags related to interrupt */
      __HAL_OSPI_CLEAR_FLAG(hospi, HAL_OSPI_FLAG_TE | HAL_OSPI_FLAG_TC);
 
      /* Update the state */
      hospi->State = HAL_OSPI_STATE_BUSY_TX;
 
      /* Enable the transfer complete, fifo threshold and transfer error interrupts */
      __HAL_OSPI_ENABLE_IT(hospi, HAL_OSPI_IT_TC | HAL_OSPI_IT_FT | HAL_OSPI_IT_TE);
    }
    else
    {
      status = HAL_ERROR;
      hospi->ErrorCode = HAL_OSPI_ERROR_INVALID_SEQUENCE;
    }
  }
 
  /* Return function status */
  return status;
}
 
/* OCTOSPI INTERRUPT */
 
void HAL_OSPI_IRQHandler(OSPI_HandleTypeDef *hospi)
{
  __IO uint32_t *data_reg = &hospi->Instance->DR;
  uint32_t flag           = hospi->Instance->SR;
  uint32_t itsource       = hospi->Instance->CR;
  uint32_t currentstate   = hospi->State;
 
  /* OctoSPI fifo threshold interrupt occurred -------------------------------*/
  if (((flag & HAL_OSPI_FLAG_FT) != 0U) && ((itsource & HAL_OSPI_IT_FT) != 0U))
  {
    if (currentstate == HAL_OSPI_STATE_BUSY_TX)
    {
      /* Write a data in the fifo */
      *((__IO uint8_t *)data_reg) = *hospi->pBuffPtr;
      hospi->pBuffPtr++;
      hospi->XferCount--;
    }
    else if (currentstate == HAL_OSPI_STATE_BUSY_RX)
    {
      /* Read a data from the fifo */
      *hospi->pBuffPtr = *((__IO uint8_t *)data_reg);
      hospi->pBuffPtr++;
      hospi->XferCount--;
    }
    else
    {
      /* Nothing to do */
    }
 
    if (hospi->XferCount == 0U)
    {
      /* All data have been received or transmitted for the transfer */
      /* Disable fifo threshold interrupt */
      __HAL_OSPI_DISABLE_IT(hospi, HAL_OSPI_IT_FT);
    }
 
    /* Fifo threshold callback */
#if defined (USE_HAL_OSPI_REGISTER_CALLBACKS) && (USE_HAL_OSPI_REGISTER_CALLBACKS == 1U)
    hospi->FifoThresholdCallback(hospi);
#else
    HAL_OSPI_FifoThresholdCallback(hospi);
#endif
  }
  /* OctoSPI transfer complete interrupt occurred ----------------------------*/
  else if (((flag & HAL_OSPI_FLAG_TC) != 0U) && ((itsource & HAL_OSPI_IT_TC) != 0U))
  {
    if (currentstate == HAL_OSPI_STATE_BUSY_RX)
    {
      if ((hospi->XferCount > 0U) && ((flag & OCTOSPI_SR_FLEVEL) != 0U))
      {
        /* Read the last data received in the fifo */
        *hospi->pBuffPtr = *((__IO uint8_t *)data_reg);
        hospi->pBuffPtr++;
        hospi->XferCount--;
      }
      else if(hospi->XferCount == 0U)
      {
        /* Clear flag */
        hospi->Instance->FCR = HAL_OSPI_FLAG_TC;
 
        /* Disable the interrupts */
        __HAL_OSPI_DISABLE_IT(hospi, HAL_OSPI_IT_TC | HAL_OSPI_IT_FT | HAL_OSPI_IT_TE);
 
        /* Update state */
        hospi->State = HAL_OSPI_STATE_READY;
 
        /* RX complete callback */
#if defined (USE_HAL_OSPI_REGISTER_CALLBACKS) && (USE_HAL_OSPI_REGISTER_CALLBACKS == 1U)
        hospi->RxCpltCallback(hospi);
#else
        HAL_OSPI_RxCpltCallback(hospi);
#endif
      }
      else
      {
        /* Nothing to do */
      }
    }
    else
    {
      /* Clear flag */
      hospi->Instance->FCR = HAL_OSPI_FLAG_TC;
 
      /* Disable the interrupts */
      __HAL_OSPI_DISABLE_IT(hospi, HAL_OSPI_IT_TC | HAL_OSPI_IT_FT | HAL_OSPI_IT_TE);
 
      /* Update state */
      hospi->State = HAL_OSPI_STATE_READY;
 
      if (currentstate == HAL_OSPI_STATE_BUSY_TX)
      {
        /* TX complete callback */
#if defined (USE_HAL_OSPI_REGISTER_CALLBACKS) && (USE_HAL_OSPI_REGISTER_CALLBACKS == 1U)
        hospi->TxCpltCallback(hospi);
#else
        HAL_OSPI_TxCpltCallback(hospi);
#endif
      }
      else if (currentstate == HAL_OSPI_STATE_BUSY_CMD)
      {
        /* Command complete callback */
#if defined (USE_HAL_OSPI_REGISTER_CALLBACKS) && (USE_HAL_OSPI_REGISTER_CALLBACKS == 1U)
        hospi->CmdCpltCallback(hospi);
#else
        HAL_OSPI_CmdCpltCallback(hospi);
#endif
      }
      else if (currentstate == HAL_OSPI_STATE_ABORT)
      {
        if (hospi->ErrorCode == HAL_OSPI_ERROR_NONE)
        {
          /* Abort called by the user */
          /* Abort complete callback */
#if defined (USE_HAL_OSPI_REGISTER_CALLBACKS) && (USE_HAL_OSPI_REGISTER_CALLBACKS == 1U)
          hospi->AbortCpltCallback(hospi);
#else
          HAL_OSPI_AbortCpltCallback(hospi);
#endif
        }
        else
        {
          /* Abort due to an error (eg : DMA error) */
          /* Error callback */
#if defined (USE_HAL_OSPI_REGISTER_CALLBACKS) && (USE_HAL_OSPI_REGISTER_CALLBACKS == 1U)
          hospi->ErrorCallback(hospi);
#else
          HAL_OSPI_ErrorCallback(hospi);
#endif
        }
      }
      else
      {
        /* Nothing to do */
      }
    }
  }
  /* OctoSPI status match interrupt occurred ---------------------------------*/
  else if (((flag & HAL_OSPI_FLAG_SM) != 0U) && ((itsource & HAL_OSPI_IT_SM) != 0U))
  {
    /* Clear flag */
    hospi->Instance->FCR = HAL_OSPI_FLAG_SM;
 
    /* Check if automatic poll mode stop is activated */
    if ((hospi->Instance->CR & OCTOSPI_CR_APMS) != 0U)
    {
      /* Disable the interrupts */
      __HAL_OSPI_DISABLE_IT(hospi, HAL_OSPI_IT_SM | HAL_OSPI_IT_TE);
 
      /* Update state */
      hospi->State = HAL_OSPI_STATE_READY;
    }
 
    /* Status match callback */
#if defined (USE_HAL_OSPI_REGISTER_CALLBACKS) && (USE_HAL_OSPI_REGISTER_CALLBACKS == 1U)
    hospi->StatusMatchCallback(hospi);
#else
    HAL_OSPI_StatusMatchCallback(hospi);
#endif
  }
  /* OctoSPI transfer error interrupt occurred -------------------------------*/
  else if (((flag & HAL_OSPI_FLAG_TE) != 0U) && ((itsource & HAL_OSPI_IT_TE) != 0U))
  {
    /* Clear flag */
    hospi->Instance->FCR = HAL_OSPI_FLAG_TE;
 
    /* Disable all interrupts */
    __HAL_OSPI_DISABLE_IT(hospi, (HAL_OSPI_IT_TO | HAL_OSPI_IT_SM | HAL_OSPI_IT_FT | HAL_OSPI_IT_TC | HAL_OSPI_IT_TE));
 
    /* Set error code */
    hospi->ErrorCode = HAL_OSPI_ERROR_TRANSFER;
 
    /* Check if the DMA is enabled */
    if ((hospi->Instance->CR & OCTOSPI_CR_DMAEN) != 0U)
    {
      /* Disable the DMA transfer on the OctoSPI side */
      CLEAR_BIT(hospi->Instance->CR, OCTOSPI_CR_DMAEN);
 
      /* Disable the DMA transfer on the DMA side */
      hospi->hdma->XferAbortCallback = OSPI_DMAAbortCplt;
      if (HAL_DMA_Abort_IT(hospi->hdma) != HAL_OK)
      {
        /* Update state */
        hospi->State = HAL_OSPI_STATE_READY;
 
        /* Error callback */
#if defined (USE_HAL_OSPI_REGISTER_CALLBACKS) && (USE_HAL_OSPI_REGISTER_CALLBACKS == 1U)
        hospi->ErrorCallback(hospi);
#else
        HAL_OSPI_ErrorCallback(hospi);
#endif
      }
    }
    else
    {
      /* Update state */
      hospi->State = HAL_OSPI_STATE_READY;
 
      /* Error callback */
#if defined (USE_HAL_OSPI_REGISTER_CALLBACKS) && (USE_HAL_OSPI_REGISTER_CALLBACKS == 1U)
      hospi->ErrorCallback(hospi);
#else
      HAL_OSPI_ErrorCallback(hospi);
#endif
    }
  }
  /* OctoSPI timeout interrupt occurred --------------------------------------*/
  else if (((flag & HAL_OSPI_FLAG_TO) != 0U) && ((itsource & HAL_OSPI_IT_TO) != 0U))
  {
    /* Clear flag */
    hospi->Instance->FCR = HAL_OSPI_FLAG_TO;
 
    /* Timeout callback */
#if defined (USE_HAL_OSPI_REGISTER_CALLBACKS) && (USE_HAL_OSPI_REGISTER_CALLBACKS == 1U)
    hospi->TimeOutCallback(hospi);
#else
    HAL_OSPI_TimeOutCallback(hospi);
#endif
  }
  else
  {
    /* Nothing to do */
  }
}

As shown above, the internal buffer data is set to OCTOSPI_DR in the interrupt.

The number of transmissions seems to be controlled by HOSPI->XferCount.

In HAL_OSPI_Transmit_IT, it does not seem that registers such as OCTOSPI_IR, OCTOSPI_AR, and OCTOSPI_DLR are set, but is it in the order of calling HAL_OSPI_Command -> HAL_OSPI_Transmit_IT?