如何设置快速 STM32 F4 FSMC 来控制 STM32F4Discovery 板上的显示?

Posted

技术标签:

【中文标题】如何设置快速 STM32 F4 FSMC 来控制 STM32F4Discovery 板上的显示?【英文标题】:How to setup fast STM32 F4 FSMC to control a display on the STM32F4Discovery board? 【发布时间】:2017-06-07 08:37:41 【问题描述】:

我将 ILI9341 显示控制器连接到 STM32F407vg 微控制器(STM32 探索板)。显示器通过 16 位并行数据总线连接到 STM32。

为了实现高数据速率,我使用了 STM32 的 FSMC。 FSMC 配置为静态 RAM 控制器。我不使用芯片选择或读取。界面正常,我可以向显示器发送数据,但是速度很慢。

我尝试使用 for 循环写入 LCD,但也使用内存到内存模式下的 DMA。我尝试从闪存写入数据,也从 RAM 写入数据。优化各种 DMA 设置。所有这些变化根本不影响速度。所以对我来说,某处似乎存在巨大的瓶颈。

下图显示了一个 16 位字传输的测量结果(仅测量了前 8 行)。如您所见,显示器的 WR 线仅以 558kHz 切换。

下图显示了参考手册中解释的 FSMC 时序。 NWE(写使能)在我的测量中是WRA16D/C

ADDSET 和 DATAST 在 HCLK(AHB 时钟)周期中。 AHB 时钟配置为其最大速度 168MHz。 ADDSET 和 DATAST 设置为 0 和 1。所以我配置了 84MHz 的速度。我不希望达到 84MHz,因为 DMA 控制器速度较慢(见下文)。但我至少希望达到 DMA 速度。

使用 ST 的 HAL v1.6.0.0 库,我将时钟设置为最大速度:

void SystemClock_Config(void)


RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;

__HAL_RCC_PWR_CLK_ENABLE();

__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = 16;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 16;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 7;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)

  Error_Handler();


RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                            |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)

  Error_Handler();


HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);

我初始化 FSMC:

void init_fsmc(void)
SRAM_HandleTypeDef sram_init_struct;
FSMC_NORSRAM_TimingTypeDef fsmc_norsram_timing_struct = 0;

sram_init_struct.Instance = FSMC_NORSRAM_DEVICE;
sram_init_struct.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;

fsmc_norsram_timing_struct.AddressSetupTime       = 0;
fsmc_norsram_timing_struct.AddressHoldTime        = 1; // n/a for SRAM mode A
fsmc_norsram_timing_struct.DataSetupTime          = 1;
fsmc_norsram_timing_struct.BusTurnAroundDuration  = 0; 
fsmc_norsram_timing_struct.CLKDivision            = 2; // n/a for SRAM mode A
fsmc_norsram_timing_struct.DataLatency            = 2; // n/a for SRAM mode A
fsmc_norsram_timing_struct.AccessMode             = FSMC_ACCESS_MODE_A;

sram_init_struct.Init.NSBank             = FSMC_NORSRAM_BANK4;
sram_init_struct.Init.DataAddressMux     = FSMC_DATA_ADDRESS_MUX_DISABLE;
sram_init_struct.Init.MemoryType         = FSMC_MEMORY_TYPE_SRAM;
sram_init_struct.Init.MemoryDataWidth    = FSMC_NORSRAM_MEM_BUS_WIDTH_16;
sram_init_struct.Init.BurstAccessMode    = FSMC_BURST_ACCESS_MODE_DISABLE;
sram_init_struct.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW;
sram_init_struct.Init.WrapMode           = FSMC_WRAP_MODE_DISABLE;
sram_init_struct.Init.WaitSignalActive   = FSMC_WAIT_TIMING_BEFORE_WS;
sram_init_struct.Init.WriteOperation     = FSMC_WRITE_OPERATION_ENABLE;
sram_init_struct.Init.WaitSignal         = FSMC_WAIT_SIGNAL_DISABLE;
sram_init_struct.Init.ExtendedMode       = FSMC_EXTENDED_MODE_DISABLE; // maybe enable?
sram_init_struct.Init.AsynchronousWait   = FSMC_ASYNCHRONOUS_WAIT_DISABLE;
sram_init_struct.Init.WriteBurst         = FSMC_WRITE_BURST_DISABLE;

__HAL_RCC_FSMC_CLK_ENABLE();

HAL_SRAM_Init(&sram_init_struct, &fsmc_norsram_timing_struct, &fsmc_norsram_timing_struct);

我配置了 DMA:

void init_dma(void)
  __HAL_RCC_DMA2_CLK_ENABLE();

  /*##-2- Select the DMA functional Parameters ###############################*/
  dma_handle.Init.Channel = DMA_CHANNEL_0;
  dma_handle.Init.Direction = DMA_MEMORY_TO_MEMORY;
  dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;               /* Peripheral increment mode */
  dma_handle.Init.MemInc = DMA_MINC_DISABLE;                  /* Memory increment mode */
  dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; /* Peripheral data alignment */
  dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;    /* memory data alignment */
  dma_handle.Init.Mode = DMA_NORMAL;                         /* Normal DMA mode */
  dma_handle.Init.Priority = DMA_PRIORITY_HIGH;              /* priority level */
  dma_handle.Init.FIFOMode = DMA_FIFOMODE_DISABLE;           /* FIFO mode disabled */
  dma_handle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
  dma_handle.Init.MemBurst = DMA_MBURST_SINGLE;              /* Memory burst */
  dma_handle.Init.PeriphBurst = DMA_PBURST_SINGLE;           /* Peripheral burst */

  dma_handle.Instance = DMA2_Stream0;

  if(HAL_DMA_Init(&dma_handle) != HAL_OK)
  
    // @todo proper error handling.
    return;
  

  HAL_DMA_RegisterCallback(&dma_handle, HAL_DMA_XFER_CPLT_CB_ID, dma_transfer_complete);
  // @todo proper error handling
  HAL_DMA_RegisterCallback(&dma_handle, HAL_DMA_XFER_ERROR_CB_ID, dma_transfer_error);

  /*##-6- Configure NVIC for DMA transfer complete/error interrupts ##########*/
  /* Set Interrupt Group Priority */
  HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 1, 0);

  /* Enable the DMA STREAM global Interrupt */
  HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);

这就是我开始交易的方式:

HAL_DMA_Start_IT(&dma_handle, (uint32_t)&data_buffer, (uint32_t)&LCD_RAM, pixelCount);

当我使用这种 DMA 配置执行从 SRAM1 到 SRAM2 的 DMA 传输时,我实现了 ~38MHz 的传输速度。所以这是我对 FSMC 的期望速度。

是什么阻碍了 FSMC?

【问题讨论】:

你说它“慢”,给我们看一张有几个数字的图片。那些 558kHz 应该表示什么? RAM 写入速率? RAM读取率?显示刷新率?还有什么?你期望的数字是多少?当你在屏幕上写东西时,它会不会刷新很慢?您可以向我们提供的详细信息越多,就越容易为您提供帮助。 您能否详细说明限制 FSMC 时序设置的因素以及您希望它们产生的频率?我对外围设备一无所知,但它们比较突出。 你应该使用带有TFT控制器的微控制器,例如STM32F437,它会简化你的生活。 @Someprogrammerdude 谢谢,我已经更新了我的问题。 你不应该使用dma_handle.Init.MemInc = DMA_MINC_ENABLE;吗? 【参考方案1】:
"sram_init_struct.Init.NSBank             = FSMC_NORSRAM_BANK4"

银行号可能有误,你可以试试用 FSMC_NORSRAM_BANK1 代替 FSMC_NORSRAM_BANK4。

【讨论】:

【参考方案2】:

也许你应该使用 HSE 而不是 HSI:

  /* Enable HSE Oscillator and activate PLL with HSE as source */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 8; // 8MHz crystal frequency
  RCC_OscInitStruct.PLL.PLLN = 336; // 2*168, 168MHz PLL_Freq
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  HAL_RCC_OscConfig(&RCC_OscInitStruct);

问候

【讨论】:

您能否详细说明 HSE 如何比 HSI 更适合? 哎哟!你是对的,我只是计算错误,因为我使用了错误的 PLL 参数!所以,我发现总线上的低频会影响 FSMC 的性能。对不起。 :)【参考方案3】:

我发现了一段很短的代码 here,它显示了如何仅使用 CMSIS 配置 FSMC。

用一行HAL代码激活时钟,这段代码如下:

__HAL_RCC_FSMC_CLK_ENABLE();

int FSMC_Bank = 0;
FSMC_Bank1->BTCR[FSMC_Bank+1] = FSMC_BTR1_ADDSET_1 | FSMC_BTR1_DATAST_1;

// Bank1 NOR/SRAM control register configuration
FSMC_Bank1->BTCR[FSMC_Bank] = FSMC_BCR1_MWID_0 | FSMC_BCR1_WREN | FSMC_BCR1_MBKEN;

我尝试了这段代码,现在我达到了 ~27MHz 的速度。这在我预期的范围内。

现在我会继续使用这段代码,根本原因分析是以后的事情。

顺便说一句,如果您在 STM32F4 Discovery 板上使用 FSMC,请拆焊电阻 R50,因为它连接到 FSMC 的 NWE 引脚,并且使用该 0 欧姆电阻连接到 STM32 的电路有一个上拉电阻,这会扭曲传输.

【讨论】:

以上是关于如何设置快速 STM32 F4 FSMC 来控制 STM32F4Discovery 板上的显示?的主要内容,如果未能解决你的问题,请参考以下文章

STM32 FSMC/FMC原理保姆级讲解

STM32 FSMC/FMC原理保姆级讲解

STM32 FSMC/FMC原理保姆级讲解

STM32 FSMC/FMC原理保姆级讲解

STM32F429第二十四篇之SRAM原理

STM32F429第二十四篇之SRAM原理