2025-01-09 01:27 AM
Dear Forum Members,
I'm developing a hardware with a small LCD screen which is using a QSPI interface. I succeeded with an STM32H723 with its OCTOSPI interface through DMA, and I got good results, but I encountered memory mapping problems with the complicated split RAM structure of that MCU, so I switched to an STM32H573, which has all of its 640k RAM in one continous block.
Signals used for data transmission: D0~D3, NCS, SCK. For commands and data headers, the protocol uses D0 only in 1-bit SPI mode, than pixel data is being sent in 4-bit QSPI format for each line.
I'm struggling with the XSPI interface setting up correctly for the new microcontroller, it does not send out any data (viewed on a scope), even the NCS signal is not pulled down on HAL_XSPI_Transmit or HAL_XSPI_Command calls. I think I missed some registers which are not generated by the STM32Cube IDE. I've found AN5050, which is mostly about how to attach an external memory chip through these interfaces.
I think I missed setting up or initializing some functions which were not set up completely by the CubeIDE.
Working init code I used for the previous MCU generated by STM32Cube IDE, and added some default headers for commands:
static void MX_OCTOSPI1_Init(void)
{
/* USER CODE BEGIN OCTOSPI1_Init 0 */
/* USER CODE END OCTOSPI1_Init 0 */
OSPIM_CfgTypeDef sOspiManagerCfg = {0};
/* USER CODE BEGIN OCTOSPI1_Init 1 */
/* USER CODE END OCTOSPI1_Init 1 */
/* OCTOSPI1 parameter configuration*/
hospi1.Instance = OCTOSPI1;
hospi1.Init.FifoThreshold = 16;
hospi1.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE;
hospi1.Init.MemoryType = HAL_OSPI_MEMTYPE_MICRON;
hospi1.Init.DeviceSize = 32;
hospi1.Init.ChipSelectHighTime = 5;
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.NCSPort = 1;
sOspiManagerCfg.IOLowPort = HAL_OSPIM_IOPORT_1_LOW;
if (HAL_OSPIM_Config(&hospi1, &sOspiManagerCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN OCTOSPI1_Init 2 */
/* 0xde is the first byte every transmit */
OSPI_Cmdhandler.Instruction = LCD_INS_DATA;
/* qspi 24bit address, lcd command is set in A15~A8 */
OSPI_Cmdhandler.Address = 0x000000;
OSPI_Cmdhandler.DummyCycles = 0;
OSPI_Cmdhandler.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE; // anything except pixel data is 1-bit
OSPI_Cmdhandler.AddressMode = HAL_OSPI_ADDRESS_1_LINE;
OSPI_Cmdhandler.AddressSize = HAL_OSPI_ADDRESS_24_BITS;
OSPI_Cmdhandler.DataMode = HAL_OSPI_DATA_NONE;
OSPI_Cmdhandler.NbData = 0;
OSPI_Cmdhandler.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD;
OSPI_Cmdhandler.DQSMode = HAL_OSPI_DQS_DISABLE;
// OSPI_Cmdhandler.AlternateByteMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
// OSPI_Cmdhandler.DdrMode = HAL_OSPI_DDR_MODE_DISABLE;
// OSPI_Cmdhandler.DdrHoldHalfCycle = HAL_OSPI_DDR_HHC_ANALOG_DELAY;
/* USER CODE END OCTOSPI1_Init 2 */
}
LCD Transmit function: (works perfectly on the STM32H723)
void lcd_transmit(uint32_t cmd, uint32_t len, uint8_t *dat)
{
OSPI_Cmdhandler.Address = 0x00000000;
if (len == 0) { /* write command, no parameter, one line in use */
OSPI_Cmdhandler.Instruction = LCD_INS_CMD;
OSPI_Cmdhandler.Address |= cmd << 8;
OSPI_Cmdhandler.DataMode = HAL_OSPI_DATA_NONE;
OSPI_Cmdhandler.NbData = 0;
/* interrupt mode */
HAL_OSPI_Command(&hospi1, &OSPI_Cmdhandler,
HAL_OSPI_TIMEOUT_DEFAULT_VALUE);
// HAL_OSPI_Command_IT(&hospi1, &OSPI_Cmdhandler);
} else if (len <= INIT_DAT_LEN) { /* write command with parameter, only 1 line in use */
OSPI_Cmdhandler.Instruction = LCD_INS_CMD;
OSPI_Cmdhandler.Address |= cmd << 8;
OSPI_Cmdhandler.DataMode = HAL_OSPI_DATA_1_LINE;
OSPI_Cmdhandler.NbData = len;
/* interrupt mode */
// HAL_OSPI_Command_IT(&hospi1, &OSPI_Cmdhandler);
// HAL_OSPI_Transmit_IT(&hospi1, dat);
HAL_OSPI_Command(&hospi1, &OSPI_Cmdhandler,
HAL_OSPI_TIMEOUT_DEFAULT_VALUE);
HAL_OSPI_Transmit(&hospi1, dat, HAL_OSPI_TIMEOUT_DEFAULT_VALUE);
} else { /* write display data by hbyte length, data must be sent over QSPI */
OSPI_Cmdhandler.Instruction = LCD_INS_DATA;
OSPI_Cmdhandler.Address |= cmd << 8;
OSPI_Cmdhandler.DataMode = HAL_OSPI_DATA_4_LINES;
OSPI_Cmdhandler.NbData = len;
/* mdma mode */
HAL_OSPI_Command(&hospi1, &OSPI_Cmdhandler, HAL_OSPI_TIMEOUT_DEFAULT_VALUE);
HAL_OSPI_Transmit_DMA(&hospi1, dat);
//
// HAL_Delay(1);
// HAL_OSPI_Transmit(&hospi1, dat,HAL_OSPI_TIMEOUT_DEFAULT_VALUE);
}
// HAL_OSPI_AutoPollingMemReady(&hospi, HAL_QPSI_TIMEOUT_DEFAULT_VALUE);
// HAL_Delay(1);
}
Code which is not working for STM32H573
static void MX_OCTOSPI1_Init(void)
{
/* USER CODE BEGIN OCTOSPI1_Init 0 */
/* USER CODE END OCTOSPI1_Init 0 */
/* USER CODE BEGIN OCTOSPI1_Init 1 */
/* USER CODE END OCTOSPI1_Init 1 */
/* OCTOSPI1 parameter configuration*/
hospi1.Instance = OCTOSPI1;
hospi1.Init.FifoThresholdByte = 16;
hospi1.Init.MemoryMode = HAL_XSPI_SINGLE_MEM;
hospi1.Init.MemoryType = HAL_XSPI_MEMTYPE_MICRON;
hospi1.Init.MemorySize = HAL_XSPI_SIZE_16B;
hospi1.Init.ChipSelectHighTimeCycle = 5;
hospi1.Init.FreeRunningClock = HAL_XSPI_FREERUNCLK_DISABLE;
hospi1.Init.ClockMode = HAL_XSPI_CLOCK_MODE_0;
hospi1.Init.WrapSize = HAL_XSPI_WRAP_NOT_SUPPORTED;
hospi1.Init.ClockPrescaler = 50;
hospi1.Init.SampleShifting = HAL_XSPI_SAMPLE_SHIFT_NONE;
hospi1.Init.DelayHoldQuarterCycle = HAL_XSPI_DHQC_DISABLE;
hospi1.Init.ChipSelectBoundary = HAL_XSPI_BONDARYOF_16B;
hospi1.Init.DelayBlockBypass = HAL_XSPI_DELAY_BLOCK_BYPASS;
hospi1.Init.Refresh = 0;
if (HAL_XSPI_Init(&hospi1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN OCTOSPI1_Init 2 */
/* USER CODE END OCTOSPI1_Init 2 */
}
GPIO mappings in the HAL_MSP file:
void HAL_XSPI_MspInit(XSPI_HandleTypeDef* hxspi)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
if(hxspi->Instance==OCTOSPI1)
{
/* USER CODE BEGIN OCTOSPI1_MspInit 0 */
/* USER CODE END OCTOSPI1_MspInit 0 */
/** Initializes the peripherals clock
*/
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_OSPI;
PeriphClkInitStruct.OspiClockSelection = RCC_OSPICLKSOURCE_HCLK;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
/* Peripheral clock enable */
__HAL_RCC_OSPI1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**OCTOSPI1 GPIO Configuration
PA6 ------> OCTOSPI1_IO3
PA7 ------> OCTOSPI1_IO2
PB0 ------> OCTOSPI1_IO1
PB1 ------> OCTOSPI1_IO0
PB2 ------> OCTOSPI1_CLK
PB10 ------> OCTOSPI1_NCS
*/
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF6_OCTOSPI1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_7;
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_AF6_OCTOSPI1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF9_OCTOSPI1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* OCTOSPI1 DMA Init */
/* GPDMA1_REQUEST_OCTOSPI1 Init */
handle_GPDMA1_Channel0.Instance = GPDMA1_Channel0;
handle_GPDMA1_Channel0.Init.Request = GPDMA1_REQUEST_OCTOSPI1;
handle_GPDMA1_Channel0.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
handle_GPDMA1_Channel0.Init.Direction = DMA_MEMORY_TO_PERIPH;
handle_GPDMA1_Channel0.Init.SrcInc = DMA_SINC_FIXED;
handle_GPDMA1_Channel0.Init.DestInc = DMA_DINC_FIXED;
handle_GPDMA1_Channel0.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
handle_GPDMA1_Channel0.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD;
handle_GPDMA1_Channel0.Init.Priority = DMA_LOW_PRIORITY_HIGH_WEIGHT;
handle_GPDMA1_Channel0.Init.SrcBurstLength = 1;
handle_GPDMA1_Channel0.Init.DestBurstLength = 1;
handle_GPDMA1_Channel0.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
handle_GPDMA1_Channel0.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
handle_GPDMA1_Channel0.Init.Mode = DMA_NORMAL;
if (HAL_DMA_Init(&handle_GPDMA1_Channel0) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(hxspi, hdmatx, handle_GPDMA1_Channel0);
if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel0, DMA_CHANNEL_NPRIV) != HAL_OK)
{
Error_Handler();
}
/* OCTOSPI1 interrupt Init */
HAL_NVIC_SetPriority(OCTOSPI1_IRQn, 14, 0);
HAL_NVIC_EnableIRQ(OCTOSPI1_IRQn);
/* USER CODE BEGIN OCTOSPI1_MspInit 1 */
/* USER CODE END OCTOSPI1_MspInit 1 */
}
}
Then, in the main loop I tried to send out something:
(also tried with Transmit_IT or Transmit_DMA but the same result, software runs)
If I set free running clock ON, then the clock can be seen on the scope.
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_GPDMA1_Init();
MX_OCTOSPI1_Init();
MX_DCACHE1_Init();
MX_ICACHE_Init();
/* USER CODE BEGIN 2 */
XSPI_RegularCmdTypeDef sCommand1={0};
/*Initialize the write register command */
sCommand1.OperationType = HAL_XSPI_OPTYPE_COMMON_CFG;
sCommand1.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
sCommand1.InstructionWidth = HAL_XSPI_INSTRUCTION_16_BITS;
sCommand1.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_DISABLE;
sCommand1.Instruction = 0xEA;
sCommand1.AddressMode = HAL_XSPI_ADDRESS_1_LINE;
sCommand1.AddressWidth = HAL_XSPI_ADDRESS_24_BITS;
sCommand1.AddressDTRMode = HAL_XSPI_ADDRESS_DTR_DISABLE;
sCommand1.Address = 0xFF;
sCommand1.AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE;
sCommand1.DataMode = HAL_XSPI_DATA_4_LINES;
sCommand1.DataDTRMode = HAL_XSPI_DATA_DTR_DISABLE;
sCommand1.DataLength = 8;
sCommand1.DummyCycles = 0;
sCommand1.DQSMode = HAL_XSPI_DQS_DISABLE;
sCommand1.IOSelect = HAL_XSPI_SELECT_IO_3_0;
// sCommand1.SIOOMode = HAL_XSPI_SIOO_INST_ONLY_FIRST_CMD;
/* Configure the command*/
HAL_XSPI_Command(&hospi1, &sCommand1, 5000);
uint8_t tmp[16]= { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 };
HAL_XSPI_Transmit(&hospi1, &tmp[0], 100);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_XSPI_Command(&hospi1, &sCommand1, 1000);
HAL_XSPI_Transmit(&hospi1, &tmp[0],1000);
// HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_10);
HAL_Delay(1);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
Solved! Go to Solution.
2025-01-09 04:54 AM
I think the problem was resolved. There's a "memory size" setting in CubeMX which had to be set to "512k / 4mbit" and then the thing begon sending data. Idk why...
For further reference, there was the fix.
2025-01-09 04:54 AM
I think the problem was resolved. There's a "memory size" setting in CubeMX which had to be set to "512k / 4mbit" and then the thing begon sending data. Idk why...
For further reference, there was the fix.