Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

DMA and LCD TFT ili9341

Alireza770717

Junior Member level 1
Joined
Nov 21, 2023
Messages
19
Helped
0
Reputation
0
Reaction score
0
Trophy points
1
Activity points
99

Hi friends​


I have built a project using an STM32 microcontroller and an ILI9341 LCD. The project uses a separate SD card module to read images and send them to the LCD for display. I am facing a problem with the low refresh rate of the display. Can you suggest some solutions? Is it possible to solve this problem using DMA, and if so, how?
 
Hi,

I have built a project
then show your schematic, code ... and realted informations

and an ILI9341 LCD
Give related informations, like display resolution, color depth, interface...

I am facing a problem with the low refresh rate of the display.
Then tell what refresh rate you expect and what refresh rate you currently achieve.

Can you suggest some solutions? Is it possible to solve this problem using DMA, and if so, how?
What did you do to debug the problem? Did you do timing measurements for the various parts of your code? Did you do calculations about expected data rate .. and so on

Klaus
 

I have put the LCD and SD card libraries in the compressed file. I have also included a picture of the microcontroller pins and a short video of the LCD performance. This is all I have done so far.​

C:
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "fatfs.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "lcd_init.h"
#include "lcd.h"
#include "fatfs_sd.h"
#include "stdio.h"
#include "string.h"

int16_t x, y;
char buff[64];
uint16_t z=25;
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
SPI_HandleTypeDef hspi1;
SPI_HandleTypeDef hspi2;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI1_Init(void);
static void MX_SPI2_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
FATFS fs;  // file system
FIL file; // File
FILINFO fno;
FRESULT fresult;  // result
UINT br, bw;  // File read/write count

/**** capacity related *****/
FATFS *pfs;
DWORD fre_clust;
uint32_t total, free_space;
char buffer[100];
uint16_t farbe;

typedef struct BmpHeader
{
uint8_t B;
uint8_t M;
uint32_t fsize;
uint16_t res1;
uint16_t res2;
uint32_t offset;
uint32_t hsize;
uint32_t w;
uint32_t h;
uint16_t planes;
uint16_t bpp;
uint32_t ctype;
uint32_t dsize;
uint32_t hppm;
uint32_t vppm;
uint32_t colorsused;
uint32_t colorreq;
}BmpHeader;



#define SPIbuffSize 264
#define BUFFER_SIZE 1032
#define BITMAP_HEADER_SIZE sizeof(BmpHeader)
#define MIN(a, b) (((a)<(b))?(a): (b))

int bufsize (char *buf)
{
   int i=0;
   while (*buf++ != '\0') i++;
   return i;
}

int bufclear (void)
{
    for (int i=0; i<1024;i++)
    {
        buffer[i]= '\0';
    }
}

int displayImage(const char* fname) {
//    UART_Printf("Openning %s...\r\n", fname);
    FIL file;
    FRESULT res = f_open(&file, fname, FA_READ);
    if(res != FR_OK) {

      // LCD_ShowString(0,10,"f_open() failed, res = %d\r\n",RED,WHITE,16,0);
        return -1;
    }

   // LCD_ShowString(0,30,"File opened, reading...\r\n",RED,WHITE,16,0);
    unsigned int bytesRead;
    uint8_t header[34];
    res = f_read(&file, header, sizeof(header), &bytesRead);
    if(res != FR_OK) {

       // LCD_ShowString(0,50,"f_read() failed, res = %d\r\n",RED,WHITE,16,0);
        f_close(&file);
        return -2;
    }

    if((header[0] != 0x42) || (header[1] != 0x4D)) {

      //  LCD_ShowString(0,90,"Wrong BMP signature: 0x%02X 0x%02X\r\n",RED,WHITE,16,0);
        f_close(&file);
        return -3;
    }

    uint32_t imageOffset = header[10] | (header[11] << 8) | (header[12] << 16) | (header[13] << 24);
    uint32_t imageWidth = header[18] | (header[19] << 8) | (header[20] << 16) | (header[21] << 24);
    uint32_t imageHeight = header[22] | (header[23] << 8) | (header[24] << 16) | (header[25] << 24);
    uint16_t imagePlanes = header[26] | (header[27] << 8);
    uint16_t imageBitsPerPixel = header[28] | (header[29] << 8);
    uint32_t imageCompression = header[30] | (header[31] << 8) | (header[32] << 16) | (header[33] << 24);

    snprintf(buff, sizeof(buff), "Pixels offset: %lu\r\n ", imageOffset);
    // LCD_ShowString(0,110,buff,RED,WHITE,16,0);
    snprintf(buff, sizeof(buff), "WxH: %lux%lu\r\n ", imageWidth, imageHeight);
    // LCD_ShowString(0,130,buff,RED,WHITE,16,0);
    snprintf(buff, sizeof(buff), "Planes: %d\r\n ", imagePlanes);
    // LCD_ShowString(0,150,buff,RED,WHITE,16,0);
    snprintf(buff, sizeof(buff), "Bits per pixel: %d\r\n ", imageBitsPerPixel);
    // LCD_ShowString(0,170,buff,RED,WHITE,16,0);
    snprintf(buff, sizeof(buff), "Compression: %d\r\n ", imageCompression);
    // LCD_ShowString(0,190,buff,RED,WHITE,16,0);

    if((imageWidth != LCD_H) || (imageHeight != LCD_W)) {
//        UART_Printf("Wrong BMP size, %dx%d expected\r\n", ST7735_WIDTH, ST7735_HEIGHT);
        //LCD_ShowString(0,210,"Wrong BMP size, %dx%d expected\r\n",RED,WHITE,16,0);
        f_close(&file);
        return -4;
    }

    if((imagePlanes != 1) || (imageBitsPerPixel != 24) || (imageCompression != 0)) {

        //LCD_ShowString(0,230,"Unsupported image format\r\n",RED,WHITE,16,0);
        f_close(&file);
        return -5;
    }

    res = f_lseek(&file, imageOffset);
    if(res != FR_OK) {

        snprintf(buff, sizeof(buff), "f_lseek() failed, res = %d\r\n ", res);

         //LCD_ShowString(0,250,buff,RED,WHITE,16,0);
        f_close(&file);
        return -6;
    }

    // row size is aligned to 4 bytes
    uint8_t imageRow[(LCD_H * 3 + 3) & ~3];
    for(uint32_t y = 0; y < imageHeight; y++) {
        uint32_t rowIdx = 0;
        res = f_read(&file, imageRow, sizeof(imageRow), &bytesRead);
        if(res != FR_OK) {
            snprintf(buff, sizeof(buff), "f_read() failed, res = %d\r\n", res);

             //LCD_ShowString(0,270,buff,RED,WHITE,16,0);
            f_close(&file);
            return -7;
        }

        for(uint32_t x = 0; x < imageWidth; x++) {
            uint8_t b = imageRow[rowIdx++];
            uint8_t g = imageRow[rowIdx++];
            uint8_t r = imageRow[rowIdx++];
            uint16_t color565 = ILI9341_COLOR565(r, g, b);
            LCD_DrawPoint(x,imageHeight - y - 1,color565);
        }
    }

    res = f_close(&file);
    if(res != FR_OK) {
//        UART_Printf("f_close() failed, res = %d\r\n", res);
        snprintf(buff, sizeof(buff), "f_close() failed, res = %d\r\n", res);

        // LCD_ShowString(0,290,buff,RED,WHITE,16,0);
        return -8;
    }

    return 0;
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_SPI1_Init();
  MX_SPI2_Init();
  MX_FATFS_Init();
  /* USER CODE BEGIN 2 */
  LCD_Init();
  FRESULT res = f_mount(&fs, "", 0);
    if(res != FR_OK) {
  //      UART_Printf("f_mount() failed, res = %d\r\n", res);
        snprintf(buff, sizeof(buff), "f_mount() failed, res = %d\r\n", res);
        //LCD_ShowString(0,310,buff,RED,WHITE,16,0);
        return -2;
    }

    //LCD_ShowString(0,330,"f_mount() done!\r\n",RED,WHITE,16,0);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
         // displayImage("ismail.bmp");
          // HAL_Delay(1000);
          // displayImage("tiger.bmp");
           HAL_Delay(1000);
           displayImage("tiger.bmp");
           HAL_Delay(1000);
           displayImage("tiger2.bmp");
           HAL_Delay(1000);
           displayImage("mi.bmp");
          HAL_Delay(1000);
          displayImage("parrot.bmp");
           HAL_Delay(1000);






    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  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_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief SPI1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_SPI1_Init(void)
{

  /* USER CODE BEGIN SPI1_Init 0 */

  /* USER CODE END SPI1_Init 0 */

  /* USER CODE BEGIN SPI1_Init 1 */

  /* USER CODE END SPI1_Init 1 */
  /* SPI1 parameter configuration*/
  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SPI1_Init 2 */

  /* USER CODE END SPI1_Init 2 */

}

/**
  * @brief SPI2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_SPI2_Init(void)
{

  /* USER CODE BEGIN SPI2_Init 0 */

  /* USER CODE END SPI2_Init 0 */

  /* USER CODE BEGIN SPI2_Init 1 */

  /* USER CODE END SPI2_Init 1 */
  /* SPI2 parameter configuration*/
  hspi2.Instance = SPI2;
  hspi2.Init.Mode = SPI_MODE_MASTER;
  hspi2.Init.Direction = SPI_DIRECTION_2LINES;
  hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi2.Init.NSS = SPI_NSS_SOFT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SPI2_Init 2 */

  /* USER CODE END SPI2_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOA, LCD_BLK_Pin|LCD_DC_Pin|LCD_RES_Pin|LCD_CS_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(SD_CS_GPIO_Port, SD_CS_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pins : LCD_BLK_Pin LCD_DC_Pin LCD_RES_Pin LCD_CS_Pin */
  GPIO_InitStruct.Pin = LCD_BLK_Pin|LCD_DC_Pin|LCD_RES_Pin|LCD_CS_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pin : SD_CS_Pin */
  GPIO_InitStruct.Pin = SD_CS_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(SD_CS_GPIO_Port, &GPIO_InitStruct);

/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
--- Updated ---

--- Updated ---



--- Updated ---

--- Updated ---

 

Attachments

  • main.zip
    49.1 KB · Views: 50
  • 1.mp4
    2.1 MB
Last edited:
Hello again!

I have put the LCD and SD card libraries in the compressed file. I have also
included a picture of the microcontroller pins and a short video of the LCD
performance. This is all I have done so far.

I suppose it's related to your other post. Ok, you put some soft together.
You need a few seconds to draw an image. And indeed you should consider DMA,
but we are not going to write it for you. At least I won't.
But before you have to make sure that with DMA you will have the proper
throughput. Be aware that it's a SPI device. At least it looks so.

What is your SPI setting?
Your file shows that it takes 15 seconds to not even write a whole screen.
Measuring on my own screen on the left border, you start 50mm from the top,
you write 29 mm from bottom, which is 89mm. The left border of the screen
is 133 mm long, so you are writing 89 / 133 of one screen, therrfore about
67% or 2/3, therefore 213 lines. Supposing you write in RGB565, this means
213 x 240 x 16 bits. And 213 x 240 x 16 bits in 15 seconds means 213 x 256
bits per second. About 55 kbps. It's indeed very slow. Are you writing pixel
by pixel? Something like WritePixAt(line, col, val)?

Depending on which STM32 you are using, 320 x 240 x2 is 153kbytes and
ff your device allows to keep a full image in memory, you can go pretty
fast using 2 buffers and a DMA. Write the buffer, send by DMA and switch.
On a small LCD like this one, you can reach video-like frame rate.

Now if you really want a high frame rate, you should choose another LCD,
for example one that supports LVDS. And use an LVDS enabled µcontroller.

Dora.
 
I hope that the following information maybe of some help to you. The following is for a display of 800x600 with 24 bit colour giving a file size of 1.2MB.

BMP file of 1.2MB read time 2 seconds and render time of 1 second for a total of 3 seconds with DMA this saves 1 second for a total time of 2 seconds.
PCX file of 600K read time 1 second and render time of 1 second for a total of 2 seconds with DMA this saves half a second for a total time of 1.5 seconds.
JPG file of 30K read time 50 mS and render time of 5 seconds for a total time of 5 seconds with DMA you would save 25mS.

The DMA is using ping pong buffers so that you can read one sector while you are rendering the previous sector, the code is written in highly optimized assembler to achieve these times. You can divide these times by 5 to get the times that you would expect with your display.

Edit I just realised that your display is a SPI interface type where as mine is a parallel type, this will significantly slow down the display, so you can divide my times by 5 and add about half a second to get your display times.
 
Last edited:

LaTeX Commands Quick-Menu:

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top