如何设置快速 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
(写使能)在我的测量中是WR
。 A16
是 D/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 板上的显示?的主要内容,如果未能解决你的问题,请参考以下文章