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의 데이터시트를 참조해야 합니다.
저희가 프로젝트에서 설정한 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>© 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' 카테고리의 다른 글
[STM32][DSP] DSP 라이브러리 설정 (2) | 2022.06.17 |
---|---|
[STM32F][HAL] CAN (0) | 2021.05.20 |
[HAL][STM32F]USART와 Queue (2) | 2021.03.24 |
[STM32F]FSMC와 LCD (0) | 2021.02.28 |
[STM32F][HAL] USART Interrupt (0) | 2020.12.23 |
[STM32F][HAL] DAC (Digital to Analog Converter) (0) | 2020.12.22 |
[STM32F][HAL] OUTPUT COMPARE 사용하기 (0) | 2020.06.01 |
[STM32F][HAL] USB CDC (Virtual Port Com) 사용하기 (1) | 2020.05.28 |