cancel
Showing results for 
Search instead for 
Did you mean: 

How to configure octospi with DMA in indirect write mode?

dh_leslie
Associate II
/* OCTOSPI1 init function */ void MX_OCTOSPI1_Init(void) { /* USER CODE BEGIN OCTOSPI1_Init 0 */ /* USER CODE END OCTOSPI1_Init 0 */ OSPIM_CfgTypeDef sOspiManagerCfg = {0}; HAL_OSPI_DLYB_CfgTypeDef HAL_OSPI_DLYB_Cfg_Struct = {0}; /* USER CODE BEGIN OCTOSPI1_Init 1 */ /* USER CODE END OCTOSPI1_Init 1 */ hospi1.Instance = OCTOSPI1; hospi1.Init.FifoThreshold = 1; hospi1.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE; hospi1.Init.MemoryType = HAL_OSPI_MEMTYPE_MICRON; hospi1.Init.DeviceSize = 32; hospi1.Init.ChipSelectHighTime = 1; hospi1.Init.FreeRunningClock = HAL_OSPI_FREERUNCLK_DISABLE; hospi1.Init.ClockMode = HAL_OSPI_CLOCK_MODE_0; hospi1.Init.WrapSize = HAL_OSPI_WRAP_NOT_SUPPORTED; hospi1.Init.ClockPrescaler = 4; hospi1.Init.SampleShifting = HAL_OSPI_SAMPLE_SHIFTING_NONE; hospi1.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_DISABLE; hospi1.Init.ChipSelectBoundary = 0; hospi1.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_BYPASSED; hospi1.Init.MaxTran = 0; hospi1.Init.Refresh = 0; if (HAL_OSPI_Init(&hospi1) != HAL_OK) { Error_Handler(); } sOspiManagerCfg.ClkPort = 1; sOspiManagerCfg.IOLowPort = HAL_OSPIM_IOPORT_1_LOW; if (HAL_OSPIM_Config(&hospi1, &sOspiManagerCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { Error_Handler(); } HAL_OSPI_DLYB_Cfg_Struct.Units = 0; HAL_OSPI_DLYB_Cfg_Struct.PhaseSel = 0; if (HAL_OSPI_DLYB_SetConfig(&hospi1, &HAL_OSPI_DLYB_Cfg_Struct) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN OCTOSPI1_Init 2 */ /* USER CODE END OCTOSPI1_Init 2 */ } void HAL_OSPI_MspInit(OSPI_HandleTypeDef* ospiHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; DMA_NodeConfTypeDef NodeConfig= {0}; RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; if(ospiHandle->Instance==OCTOSPI1) { /* USER CODE BEGIN OCTOSPI1_MspInit 0 */ /* USER CODE END OCTOSPI1_MspInit 0 */ /** Initializes the peripherals clock */ PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_OSPI; PeriphClkInit.OspiClockSelection = RCC_OSPICLKSOURCE_SYSCLK; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } /* OCTOSPI1 clock enable */ __HAL_RCC_OSPIM_CLK_ENABLE(); __HAL_RCC_OSPI1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOE_CLK_ENABLE(); /**OCTOSPI1 GPIO Configuration PA3 ------> OCTOSPIM_P1_CLK PB0 ------> OCTOSPIM_P1_IO1 PB1 ------> OCTOSPIM_P1_IO0 PE14 ------> OCTOSPIM_P1_IO2 PE15 ------> OCTOSPIM_P1_IO3 */ GPIO_InitStruct.Pin = GPIO_PIN_3; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF10_OCTOSPI1; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF10_OCTOSPI1; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_14|GPIO_PIN_15; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF10_OCTOSPI1; HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); /* OCTOSPI1 DMA Init */ /* GPDMA1_REQUEST_OCTOSPI1 Init */ NodeConfig.NodeType = DMA_GPDMA_LINEAR_NODE; NodeConfig.Init.Request = GPDMA1_REQUEST_OCTOSPI1; NodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST; NodeConfig.Init.Direction = DMA_MEMORY_TO_PERIPH; NodeConfig.Init.SrcInc = DMA_SINC_INCREMENTED; NodeConfig.Init.DestInc = DMA_DINC_FIXED; NodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE; NodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE; NodeConfig.Init.SrcBurstLength = 1; NodeConfig.Init.DestBurstLength = 1; NodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0; NodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER; NodeConfig.Init.Mode = DMA_NORMAL; NodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED; NodeConfig.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE; NodeConfig.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED; if (HAL_DMAEx_List_BuildNode(&NodeConfig, &Node_GPDMA1_Channel3) != HAL_OK) { Error_Handler(); } if (HAL_DMAEx_List_InsertNode(&List_GPDMA1_Channel3, NULL, &Node_GPDMA1_Channel3) != HAL_OK) { Error_Handler(); } if (HAL_DMAEx_List_SetCircularMode(&List_GPDMA1_Channel3) != HAL_OK) { Error_Handler(); } handle_GPDMA1_Channel3.Instance = GPDMA1_Channel3; handle_GPDMA1_Channel3.InitLinkedList.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT; handle_GPDMA1_Channel3.InitLinkedList.LinkStepMode = DMA_LSM_FULL_EXECUTION; handle_GPDMA1_Channel3.InitLinkedList.LinkAllocatedPort = DMA_LINK_ALLOCATED_PORT0; handle_GPDMA1_Channel3.InitLinkedList.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER; handle_GPDMA1_Channel3.InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR; if (HAL_DMAEx_List_Init(&handle_GPDMA1_Channel3) != HAL_OK) { Error_Handler(); } if (HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel3, &List_GPDMA1_Channel3) != HAL_OK) { Error_Handler(); } __HAL_LINKDMA(ospiHandle, hdma, handle_GPDMA1_Channel3); if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel3, DMA_CHANNEL_NPRIV) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN OCTOSPI1_MspInit 1 */ /* USER CODE END OCTOSPI1_MspInit 1 */ } } void OCTOSPI1_Init(void) { OCTOSPI1->CR &= ~OCTOSPI_CR_EN; while ((OCTOSPI1->CR & OCTOSPI_CR_EN) != 0); OCTOSPI1->FCR = OCTOSPI_FCR_CTCF | OCTOSPI_FCR_CTEF | OCTOSPI_FCR_CSMF | OCTOSPI_FCR_CTOF; OCTOSPI1->CR = (1 << OCTOSPI_CR_FTHRES_Pos) | (1 << OCTOSPI_CR_DMAEN_Pos); OCTOSPI1->CCR = (0x3 << OCTOSPI_CCR_DMODE_Pos); OCTOSPI1->WCCR = (0x3 << OCTOSPI_WCCR_DMODE_Pos); OCTOSPI1->WTCR = 0x00000000; OCTOSPI1->DLR = 1; OCTOSPI1->CR |= (0x0 << OCTOSPI_CR_FMODE_Pos); OCTOSPI1->CR |= OCTOSPI_CR_EN; MODIFY_REG(OCTOSPI1->CR, OCTOSPI_CR_FMODE, 0x00000000); } void main(void) { ... MX_OCTOSPI1_Init(); OCTOSPI1_Init(); HAL_DMA_Start(hospi1.hdma, (uint32_t)QSpiTable, (uint32_t)&OCTOSPI1->DR, 2); ... while(1); }
View more

Run the code, dma doesn't transmit anything via octospi.

if I run the code below with the same configuration, I can get signals on oscilloscope.

void main(void) { while(1) { OCTOSPI1_WriteData(QSpiTable, 2); HAL_Delay(1); } } void OCTOSPI1_WriteData(uint8_t* data, uint32_t length) { for (uint32_t i = 0; i < length; i++) { while (!(OCTOSPI1->SR & OCTOSPI_SR_FTF)); *(__IO uint8_t*)&OCTOSPI1->DR = data[i]; } while (!(OCTOSPI1->SR & OCTOSPI_SR_FTF)); }

Does anyone have the experience about octospi inidrect write mode with dma?

Is it possible to make octospi (quad spi) work as same as spi, just with more IOs?

5 REPLIES 5
dh_leslie
Associate II

Another question: Does octospi works under dma circular mode?

/* OCTOSPI1 init function */ void MX_OCTOSPI1_Init(void) { /* USER CODE BEGIN OCTOSPI1_Init 0 */ /* USER CODE END OCTOSPI1_Init 0 */ OSPIM_CfgTypeDef sOspiManagerCfg = {0}; HAL_OSPI_DLYB_CfgTypeDef HAL_OSPI_DLYB_Cfg_Struct = {0}; /* USER CODE BEGIN OCTOSPI1_Init 1 */ /* USER CODE END OCTOSPI1_Init 1 */ hospi1.Instance = OCTOSPI1; hospi1.Init.FifoThreshold = 1; hospi1.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE; hospi1.Init.MemoryType = HAL_OSPI_MEMTYPE_MICRON; hospi1.Init.DeviceSize = 32; hospi1.Init.ChipSelectHighTime = 1; hospi1.Init.FreeRunningClock = HAL_OSPI_FREERUNCLK_DISABLE; hospi1.Init.ClockMode = HAL_OSPI_CLOCK_MODE_0; hospi1.Init.WrapSize = HAL_OSPI_WRAP_NOT_SUPPORTED; hospi1.Init.ClockPrescaler = 2; hospi1.Init.SampleShifting = HAL_OSPI_SAMPLE_SHIFTING_NONE; hospi1.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_DISABLE; hospi1.Init.ChipSelectBoundary = 0; hospi1.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_BYPASSED; hospi1.Init.MaxTran = 0; hospi1.Init.Refresh = 0; if (HAL_OSPI_Init(&hospi1) != HAL_OK) { Error_Handler(); } sOspiManagerCfg.ClkPort = 1; sOspiManagerCfg.IOLowPort = HAL_OSPIM_IOPORT_1_LOW; if (HAL_OSPIM_Config(&hospi1, &sOspiManagerCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { Error_Handler(); } HAL_OSPI_DLYB_Cfg_Struct.Units = 0; HAL_OSPI_DLYB_Cfg_Struct.PhaseSel = 0; if (HAL_OSPI_DLYB_SetConfig(&hospi1, &HAL_OSPI_DLYB_Cfg_Struct) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN OCTOSPI1_Init 2 */ /* USER CODE END OCTOSPI1_Init 2 */ } void HAL_OSPI_MspInit(OSPI_HandleTypeDef* ospiHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; DMA_NodeConfTypeDef NodeConfig= {0}; RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; if(ospiHandle->Instance==OCTOSPI1) { /* USER CODE BEGIN OCTOSPI1_MspInit 0 */ /* USER CODE END OCTOSPI1_MspInit 0 */ /** Initializes the peripherals clock */ PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_OSPI; PeriphClkInit.OspiClockSelection = RCC_OSPICLKSOURCE_PLL1; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } /* OCTOSPI1 clock enable */ __HAL_RCC_OSPIM_CLK_ENABLE(); __HAL_RCC_OSPI1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOE_CLK_ENABLE(); /**OCTOSPI1 GPIO Configuration PA3 ------> OCTOSPIM_P1_CLK PB0 ------> OCTOSPIM_P1_IO1 PB1 ------> OCTOSPIM_P1_IO0 PE14 ------> OCTOSPIM_P1_IO2 PE15 ------> OCTOSPIM_P1_IO3 */ GPIO_InitStruct.Pin = GPIO_PIN_3; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF10_OCTOSPI1; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF10_OCTOSPI1; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_14|GPIO_PIN_15; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF10_OCTOSPI1; HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); /* OCTOSPI1 DMA Init */ /* GPDMA1_REQUEST_OCTOSPI1 Init */ NodeConfig.NodeType = DMA_GPDMA_LINEAR_NODE; NodeConfig.Init.Request = GPDMA1_REQUEST_OCTOSPI1; NodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST; NodeConfig.Init.Direction = DMA_MEMORY_TO_PERIPH; NodeConfig.Init.SrcInc = DMA_SINC_INCREMENTED; NodeConfig.Init.DestInc = DMA_DINC_FIXED; NodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE; NodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE; NodeConfig.Init.SrcBurstLength = 1; NodeConfig.Init.DestBurstLength = 1; NodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0; NodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER; NodeConfig.Init.Mode = DMA_NORMAL; NodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED; NodeConfig.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE; NodeConfig.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED; if (HAL_DMAEx_List_BuildNode(&NodeConfig, &Node_GPDMA1_Channel3) != HAL_OK) { Error_Handler(); } if (HAL_DMAEx_List_InsertNode(&List_GPDMA1_Channel3, NULL, &Node_GPDMA1_Channel3) != HAL_OK) { Error_Handler(); } if (HAL_DMAEx_List_SetCircularMode(&List_GPDMA1_Channel3) != HAL_OK) { Error_Handler(); } handle_GPDMA1_Channel3.Instance = GPDMA1_Channel3; handle_GPDMA1_Channel3.InitLinkedList.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT; handle_GPDMA1_Channel3.InitLinkedList.LinkStepMode = DMA_LSM_FULL_EXECUTION; handle_GPDMA1_Channel3.InitLinkedList.LinkAllocatedPort = DMA_LINK_ALLOCATED_PORT0; handle_GPDMA1_Channel3.InitLinkedList.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER; handle_GPDMA1_Channel3.InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR; if (HAL_DMAEx_List_Init(&handle_GPDMA1_Channel3) != HAL_OK) { Error_Handler(); } if (HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel3, &List_GPDMA1_Channel3) != HAL_OK) { Error_Handler(); } __HAL_LINKDMA(ospiHandle, hdma, handle_GPDMA1_Channel3); if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel3, DMA_CHANNEL_NPRIV) != HAL_OK) { Error_Handler(); } /* OCTOSPI1 interrupt Init */ HAL_NVIC_SetPriority(OCTOSPI1_IRQn, 0, 0); HAL_NVIC_EnableIRQ(OCTOSPI1_IRQn); /* USER CODE BEGIN OCTOSPI1_MspInit 1 */ /* USER CODE END OCTOSPI1_MspInit 1 */ } } HAL_StatusTypeDef QSPI_Write_DMA(uint8_t cmd, uint8_t * pData, uint16_t length) { HAL_StatusTypeDef err = HAL_OK; OSPI_RegularCmdTypeDef sCommand = { 0 }; sCommand.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG; sCommand.Instruction = cmd; sCommand.InstructionMode = HAL_OSPI_INSTRUCTION_4_LINES; sCommand.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS; sCommand.DataMode = HAL_OSPI_DATA_4_LINES; sCommand.NbData = length; sCommand.SIOOMode = HAL_OSPI_SIOO_INST_ONLY_FIRST_CMD; err = HAL_OSPI_Command(&hospi1, &sCommand, 100); if(HAL_OK != err) { return err; } err = HAL_OSPI_Transmit_DMA(&hospi1, pData); if(HAL_OK != err) { return err; } return HAL_OK; } void main(void) { MX_OCTOSPI1_Init(); QSPI_Write_DMA(0xff, QSpiTable, 4); while(1) { } }
View more

if setting dma as circular mode, HAL_OSPI_Transmit_DMA only sends data one time.

Is there anything wrong in my configuration?

KDJEM.1
ST Employee

Hello @dh_leslie 

 

For the configuration of OCTOSPI interface in indirect write mode with DMA, I recommend you to look at OSPI_NOR_ReadWrite_DMA_DTR example. This example describes how to erase part of an OSPI NOR memory, write data in DMA mode, read data in DMA mode then compare the result in an infinite loop. The memory is configured in octal DTR mode.

The OCTOSPI interface is fully configurable and supports two, four, or eight data lines: Single-SPI (traditional SPI), Dual-SPI, Quad-SPI and Octo-SPI. 

To use the OCTOSPI interface with QUAD memory, I advise you to follow the steps shared in AN5050 section "7.2 OCTOSPI configuration with STM32CubeMX" related Quad-SPI PSRAM APS1604M-3SQR memory.

 

Thank you.

Kaouthar

 

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

I have read the documents and soure code before, but I am using a qspi DAC, not a flash memory.

I suspect whether octospi works under dma circular mode with tim15 trgo.

KDJEM.1
ST Employee

Hello @dh_leslie,

 

I don't find any issue to use the OCTOSPI under DMA normal or circular.

You just need to respect the constraints imposed by the OCTOSPI, which aren’t related to a circular mode or not.

Could you please try with DMA normal? Did you find any issues with DMA normal?

Thank you.

Kaouthar

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Just now I tried again to send 4bytes under 1MHz, 10MHz and 40MHz octospi clock,

octospi under dma normal works well.

If change to dma circular, (see the pics)

1MHz clock: works just like dma normal, data is transmitted just once.

10MHz clock: Command sent once, followed by sending data twice. 

40MHz clock: Command sent once, followed by sending data 6 times. 

Something weird:

  1. there is some obvious delay during dma return to the head.

  2. DMA stops after 2~2.5uS under 10MHz and 40MHz.

Could you share your test code under dma circular?

scope_1.png

scope_0.png

scope_3.png

HAL_StatusTypeDef QSPI_Write_DMA(uint8_t cmd, uint8_t * pData, uint16_t length) { HAL_StatusTypeDef err = HAL_OK; OSPI_RegularCmdTypeDef sCommand = { 0 }; sCommand.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG; sCommand.Instruction = cmd; sCommand.InstructionMode = HAL_OSPI_INSTRUCTION_4_LINES; sCommand.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS; sCommand.DataMode = HAL_OSPI_DATA_4_LINES; sCommand.NbData = length; sCommand.SIOOMode = HAL_OSPI_SIOO_INST_ONLY_FIRST_CMD; err = HAL_OSPI_Command(&hospi1, &sCommand, 100); if(HAL_OK != err) { return err; } err = HAL_OSPI_Transmit_DMA(&hospi1, pData); if(HAL_OK != err) { return err; } return HAL_OK; }