MCU: STM32F103VCT6

IDE: STM32CubeIDE

LCD Driver: SSD1963

 

목표: LCD화면을 1초마다 흰색 > 검정 > 파랑 > 초록 > 빨강으로  변경 시키기.

 

 

STM32F103에 있는 FSMC(Flexible static memory controller)를 사용하여 LCD를 제어해보도록 하겠습니다.

 

제가 가지고 있는 보드의 LCD 회로도는 아래와 같습니다.

 

D[0:15] 인걸 봐서 16bit parellal 제어라는 것을 알 수 있고,

LCD RS, LCD_CS, LCD_WR, LCD_RD 핀이 있는걸 봐서는 8080 인터페이스를 쓴다는 것을 알 수 있습니다.(6800은 RD 대신 E)

 

 

CubeMX를 이용하여 아래와 같이 FSMC를 설정합니다.

 

Memory type: LCD Interface

LCD Register Select: A16

Data: 16bits

 

Address setup time i HCLK clock cycles: 0

Data setup time in HCLK clock cycles: 15

Bus turn around time in HCLK clock cycles: 0

 

제가 가지고 있는 보드는 8MHz의 크리스탈이 달려 있어 RCC 설정도 해 주고, PWM핀(PD13)을 GPIO Output으로 설정해 주고, GPIO Output level을 High로 설정했습니다.

클럭 설정도 최대 주파수로 설정해주었구요

 

프로젝트 설정과 (여기서 저는 CubeIDE로 프로젝트를 생성하였습니다.)

 

코드 제너레이터 설정까지 해 주고 GENERATE CODE 버튼을 눌러 프로젝트를 생성하였습니다.

 

 

STMCubeIDE를 켜고 fsmc.c를 켜면 아래와 같이 작성되어있습니다.

void MX_FSMC_Init(void)
{
  /* USER CODE BEGIN FSMC_Init 0 */

  /* USER CODE END FSMC_Init 0 */

  FSMC_NORSRAM_TimingTypeDef Timing = {0};

  /* USER CODE BEGIN FSMC_Init 1 */

  /* USER CODE END FSMC_Init 1 */

  /** Perform the SRAM1 memory initialization sequence
  */
  hsram1.Instance = FSMC_NORSRAM_DEVICE;
  hsram1.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;
  /* hsram1.Init */
  hsram1.Init.NSBank = FSMC_NORSRAM_BANK1;
  hsram1.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE;
  hsram1.Init.MemoryType = FSMC_MEMORY_TYPE_SRAM;
  hsram1.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16;
  hsram1.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE;
  hsram1.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW;
  hsram1.Init.WrapMode = FSMC_WRAP_MODE_DISABLE;
  hsram1.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS;
  hsram1.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE;
  hsram1.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE;
  hsram1.Init.ExtendedMode = FSMC_EXTENDED_MODE_DISABLE;
  hsram1.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE;
  hsram1.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE;
  /* Timing */
  Timing.AddressSetupTime = 0;
  Timing.AddressHoldTime = 15;
  Timing.DataSetupTime = 15;
  Timing.BusTurnAroundDuration = 0;
  Timing.CLKDivision = 16;
  Timing.DataLatency = 17;
  Timing.AccessMode = FSMC_ACCESS_MODE_A;
  /* ExtTiming */

  if (HAL_SRAM_Init(&hsram1, &Timing, NULL) != HAL_OK)
  {
    Error_Handler( );
  }

  /** Disconnect NADV
  */

  __HAL_AFIO_FSMCNADV_DISCONNECTED();

  /* USER CODE BEGIN FSMC_Init 2 */

  /* USER CODE END FSMC_Init 2 */
}

 

이 부분은 SSD1963의 데이터시트를 참조해야 합니다.

 

SSD1963 데이터시트에 있는 타이밍 특성

저희가 프로젝트에서 설정한 HCLK의 주파수는 72MHz였고 이는 13ns 입니다.

Address Setup Time, Address Hold Time 그리고 Write Data Setup Time 의 시간이 13ns보다 짧기 때문에 

아래와 같이 해당 속성을 1로 설정해주었습니다.

 

또, STM LCD 레퍼런스 메뉴얼에 AccessMode는 B로 설정한다고 해서 A도 하고 B도 해봤는데 차이는 없었습니다.

  Timing.AddressSetupTime = 1;
  Timing.AddressHoldTime = 1;
  Timing.DataSetupTime = 1;
  Timing.BusTurnAroundDuration = 0;
  Timing.CLKDivision = 16;
  Timing.DataLatency = 17;
  Timing.AccessMode = FSMC_ACCESS_MODE_B;

기본 설정은 마쳤고, LCD 제어 코드를 아래와 같이 작성했습니다.

 

주소에 대해서는 [MCU/STM32:HAL] - [STM32F]FSMC와 LCD 를 참조해 주세요

/* ssd1963.h */

#ifndef INC_SSD1963_H_
#define INC_SSD1963_H_

#define  SSD1963_LCD_PIXEL_WIDTH    ((uint16_t)480)
#define  SSD1963_LCD_PIXEL_HEIGHT   ((uint16_t)272)


// Bank1 NE1
#define TFT_LCD_REG         (*((volatile unsigned short *) 0x60000000))
// A16
#define TFT_LCD_DATA        (*((volatile unsigned short *) 0x60020000))

void Write_REG(uint16_t Reg);
void Write_Data(uint16_t Data);
void LCD_init(void);
void LCD_Clear(uint16_t Color);

#endif /* INC_SSD1963_H_ */

 

/* ssd1963.c */

#include "main.h"
#include "ssd1963.h"

#define HDP     (479)

#define HT      (531)
#define HPS     (43)
#define LPS     (8)
#define HPW     (10)

#define VDP     (271)
#define VT      (288)
#define VPS     (12)
#define FPS     (4)
#define VPW     (10)

void Write_REG(uint16_t Reg)
{
	TFT_LCD_REG = Reg;
}

void Write_Data(uint16_t Data)
{
	TFT_LCD_DATA = Data;
}


void LCD_init(void)
{

	Write_REG(0x00E2);	              //PLL multiplier, set PLL clock to 120M
	Write_Data(0x0023);               //N=0x36 for 6.5M, 0x23 for 10M crystal
	Write_Data(0x0002);
	Write_Data(0x0004);

	Write_REG(0x00E0);                // PLL enable
	Write_Data(0x0001);

	HAL_Delay(10);

	Write_REG(0x00E0);
	Write_Data(0x0003);

	HAL_Delay(10);

	Write_REG(0x0001);                // software reset

	HAL_Delay(10);

	Write_REG(0x00E6);	              //PLL setting for PCLK, depends on resolution
	Write_Data(0x0001);
	Write_Data(0x0033);
	Write_Data(0x0032);

	Write_REG(0x00B0);	              //LCD SPECIFICATION
	Write_Data(0x0018);
	Write_Data(0x0000);
	Write_Data((HDP >> 8) & 0x00FF);  //Set HDP
	Write_Data(HDP & 0x00FF);
	Write_Data((VDP >> 8) & 0x00FF);  //Set VDP
	Write_Data(VDP & 0x00FF);
	Write_Data(0x0000);

	Write_REG(0x00B4);	              //HSYNC
	Write_Data((HT >> 8) & 0x00FF);   //Set HT
	Write_Data(HT & 0x00FF);
	Write_Data((HPS >> 8) & 0x00FF);  //Set HPS
	Write_Data(HPS & 0x00FF);
	Write_Data(HPW);			      //Set HPW
	Write_Data((LPS >> 8) & 0x00FF);  //Set HPS
	Write_Data(LPS & 0x00FF);
	Write_Data(0x0000);

	Write_REG(0x00B6);	              //VSYNC
	Write_Data((VT >> 8) & 0x00FF);   //Set VT
	Write_Data(VT & 0x00FF);
	Write_Data((VPS >> 8) & 0x00FF);  //Set VPS
	Write_Data(VPS & 0x00FF);
	Write_Data(VPW);			      //Set VPW
	Write_Data((FPS >> 8) & 0x00FF);  //Set FPS
	Write_Data(FPS & 0x00FF);

	// 16bit per pixel
	Write_REG(0x0036);                //rotation
	Write_Data(0x0050);

	// rgb565 format
	Write_REG(0x00F0);                //pixel data interface
	Write_Data(0x0003);

	Write_REG(0x0021);

	Write_REG(0x00BC);
	Write_Data(0x0080);
	Write_Data(0x0080);
	Write_Data(0x0080);
	Write_Data(0x0001);

	HAL_Delay(5);

	Write_REG(0x0029); //display on
}

// LCD Window 범위 설정
void ssd1963_SetDisplayWindow(uint16_t Xpos, uint16_t Ypos, uint16_t Width, uint16_t Height)
{
	uint8_t data = 0;
	/* Column addr set, 4 args, no delay: XSTART = Xpos, XEND = (Xpos + Width - 1) */
	Write_REG(0x002A);
	data = ((Xpos) >> 8) & 0xFF;
	Write_Data(data);
	data = (Xpos) & 0xFF;
	Write_Data(data);
	data = ((Xpos + Width - 1) >> 8) & 0xFF;
	Write_Data(data);
	data = (Xpos + Width - 1) & 0xFF;
	Write_Data(data);
	/* Row addr set, 4 args, no delay: YSTART = Ypos, YEND = (Ypos + Height - 1) */
	Write_REG(0x002b);
	data = ((Ypos) >> 8) & 0xFF;
	Write_Data(data);
	data = (Ypos) & 0xFF;
	Write_Data(data);
	data = ((Ypos + Height - 1) >> 8) & 0xFF;
	Write_Data(data);
	data = (Ypos + Height - 1) & 0xFF;
	Write_Data(data);
}

// Horizontal Line 그리기 함수
void ssd1963_DrawHLine(uint16_t RGBCode, uint16_t Xpos, uint16_t Ypos, uint16_t Length)
{
	uint16_t counter = 0;

	if(Xpos + Length > SSD1963_LCD_PIXEL_WIDTH) return;

	/* Set Cursor */
	ssd1963_SetDisplayWindow(Xpos, Ypos, Length, 1);

	Write_REG(0x002c);

	for(counter = 0; counter < Length; counter++)
	{
		Write_Data(RGBCode);
	}
}

// 전체 화면을 Color로 덮어쓰기
void LCD_Clear(uint16_t Color)
{
  uint32_t counter = 0;

  for(counter = 0; counter < SSD1963_LCD_PIXEL_HEIGHT; counter++)
  {
	  ssd1963_DrawHLine(Color, 0, counter, SSD1963_LCD_PIXEL_WIDTH);
  }
}

/* main.c */
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"
#include "fsmc.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "ssd1963.h" // ssd1963 헤더 추가
/* 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 ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 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_FSMC_Init();
  /* USER CODE BEGIN 2 */

  LCD_init(); // LCD 초기화

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  //White
	  LCD_Clear(0xFFFF);
	  HAL_Delay(1000);
	  //Black
	  LCD_Clear(0x0000);
	  HAL_Delay(1000);
	  //Blue
	  LCD_Clear(0x001F);
	  HAL_Delay(1000);
	  //Green
	  LCD_Clear(0x07E0);
	  HAL_Delay(1000);
	  //Red
	  LCD_Clear(0xF800);
	  HAL_Delay(1000);

  }
  /* 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();
  }
}

/* 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 */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

 

이제 프로그램을 MCU에 다운로딩하면 1초마다 흰색 > 검정색 > 파랑색 > 초록색 > 빨간색으로 화면이 바뀌는것을 확인 할 수 있습니다.

 

Reference

 

TFT LCD interfacing with the high-density STM32F10xxx FSMC

Microsoft Word - SSD1963_1.3.doc (displaytech-us.com)

[MCU/STM32:HAL] - [STM32F]FSMC와 LCD

LCD Init 참조 코드

반응형

+ Recent posts