⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 s3c6400_ldi.c

📁 Samsung公司S3C6400芯片的BSP源码包
💻 C
字号:
#include <windows.h>
#include <bsp_cfg.h>
#include <s3c6400.h>
#include "s3c6400_ldi.h"
#include "LTS222QV_RGB_dataset.h"
#include "LTV350QV_RGB_dataset.h"
#include "LTE480WV_RGB_dataset.h"
#include "LTP700WV_RGB_dataset.h"

#ifdef	REMOVE_BEFORE_RELEASE
#define LDI_MSG(x)
#define LDI_INF(x)
#define LDI_ERR(x)	RETAILMSG(TRUE, x)
#else
//#define LDI_MSG(x)	RETAILMSG(TRUE, x)
#define LDI_MSG(x)
#define LDI_INF(x)	RETAILMSG(TRUE, x)
//#define LDI_INF(x)
#define LDI_ERR(x)	RETAILMSG(TRUE, x)
//#define LDI_ERR(x)
#endif

#define LCD_CLK			(1<<5)  // GPC[5]
#define LCD_MOSI		(1<<6)  // GPC[6]
#define LCD_nSS			(1<<7)  // GPC[7]

#define LCD_CLK_Lo		(g_pGPIOReg->GPCDAT &= ~LCD_CLK)
#define LCD_CLK_Hi		(g_pGPIOReg->GPCDAT |= LCD_CLK)
#define LCD_MOSI_Lo		(g_pGPIOReg->GPCDAT &= ~LCD_MOSI)
#define LCD_MOSI_Hi		(g_pGPIOReg->GPCDAT |= LCD_MOSI)
#define LCD_nSS_Lo		(g_pGPIOReg->GPCDAT &= ~LCD_nSS)
#define LCD_nSS_Hi		(g_pGPIOReg->GPCDAT |= LCD_nSS)

// SYS I/F CSn Main
#define LCD_nCSMain_Lo	(g_pGPIOReg->GPJDAT &= ~(1<<8))
#define LCD_nCSMain_Hi	(g_pGPIOReg->GPJDAT |= (1<<8))
#define LCD_RD_Lo		(g_pGPIOReg->GPJDAT &= ~(1<<7))
#define LCD_RD_Hi		(g_pGPIOReg->GPJDAT |= (1<<7))
#define LCD_RS_Lo		(g_pGPIOReg->GPJDAT &= ~(1<<10))
#define LCD_RS_Hi		(g_pGPIOReg->GPJDAT |= (1<<10))
#define LCD_nWR_Lo		(g_pGPIOReg->GPJDAT &= ~(1<<11))
#define LCD_nWR_Hi		(g_pGPIOReg->GPJDAT |= (1<<11))

#define LCD_DELAY_1MS	18000//30000	// for 667MHz	//18000	//on the basis of 540MHz
#define SPI_DELAY		50//100		// for 667MHz	// 50


static volatile S3C6400_SPI_REG *g_pSPIReg = NULL;
static volatile S3C6400_GPIO_REG *g_pGPIOReg = NULL;
static volatile S3C6400_DISPLAY_REG *g_pDispConReg = NULL;
static LDI_LCD_MODULE_TYPE g_ModuleType;

LDI_ERROR LDI_initialize_register_address(void *pSPIReg, void *pDispConReg, void *pGPIOReg)
{
	LDI_ERROR error = LDI_SUCCESS;

	LDI_MSG((_T("[LDI]++LDI_initialize_register_address(0x%08x, 0x%08x, 0x%08x)\n\r"), pSPIReg, pDispConReg, pGPIOReg));

	if (pSPIReg == NULL || pDispConReg == NULL || pGPIOReg == NULL)
	{
		LDI_ERR((_T("[LDI:ERR] LDI_initialize_register_address() : NULL pointer parameter\n\r")));
		error = LDI_ERROR_NULL_PARAMETER;
	}
	else
	{
		g_pSPIReg = (S3C6400_SPI_REG *)pSPIReg;
		g_pDispConReg = (S3C6400_DISPLAY_REG *)pDispConReg;
		g_pGPIOReg = (S3C6400_GPIO_REG *)pGPIOReg;
		LDI_INF((_T("[LDI:INF] g_pSPIReg = 0x%08x\n\r"), g_pSPIReg));
		LDI_INF((_T("[LDI:INF] g_pDispConReg = 0x%08x\n\r"), g_pDispConReg));
		LDI_INF((_T("[LDI:INF] g_pGPIOReg    = 0x%08x\n\r"), g_pGPIOReg));
	}

	LDI_MSG((_T("[LDI]--LDI_initialize_register_address() : %d\n\r"), error));

	return error;
}

LDI_ERROR LDI_set_LCD_module_type(LDI_LCD_MODULE_TYPE ModuleType)
{
	LDI_ERROR error = LDI_SUCCESS;

	LDI_MSG((_T("[LDI]++LDI_set_LCD_module_type(%d)\n\r"), ModuleType));

	g_ModuleType = ModuleType;

	LDI_MSG((_T("[LDI]--LDI_set_LCD_module_type() : %d\n\r"), error));

	return error;
}

LDI_ERROR LDI_initialize_LCD_module(void)
{
	LDI_ERROR error = LDI_SUCCESS;

	LDI_MSG((_T("[LDI]++LDI_initialize_LCD_module()\n\r")));

	switch(g_ModuleType)
	{
	case LDI_LTS222QV_RGB:
		LDI_INF((_T("[LDI:INF] LDI_initialize_LCD_module() : Type [%d] LDI_LTS222QV_RGB\n\r"), g_ModuleType));
		LDI_LTS222QV_port_initialize();
		LDI_LTS222QV_reset();
		LDI_LTS222QV_RGB_initialize();
		break;
	case LDI_LTV350QV_RGB:
		LDI_INF((_T("[LDI:INF] LDI_initialize_LCD_module() : Type [%d] LDI_LTV350QV_RGB\n\r"), g_ModuleType));
		LDI_LTV350QV_port_initialize();
		LDI_LTV350QV_reset();
		LDI_LTV350QV_RGB_initialize();
		break;
	case LDI_LTE480WV_RGB:
		LDI_INF((_T("[LDI:INF] LDI_initialize_LCD_module() : Type [%d] LDI_LTE480WV_RGB\n\r"), g_ModuleType));
		LDI_LTE480WV_RGB_port_initialize();
		LDI_LTE480WV_RGB_reset();
		LDI_LTE480WV_RGB_initialize();
		break;
	case LDI_LTP700WV_RGB:
		LDI_INF((_T("[LDI:INF] LDI_initialize_LCD_module() : Type [%d] LDI_LTP700WV_RGB\n\r"), g_ModuleType));
		LDI_LTP700WV_port_initialize();
		LDI_LTP700WV_reset();
		LDI_LTP700WV_RGB_initialize();
		break;
	default:
		LDI_ERR((_T("[LDI:ERR] LDI_initialize_LCD_module() : Unknown LCD Module Type [%d]\n\r"), g_ModuleType));
		error = LDI_ERROR_ILLEGAL_PARAMETER;
		break;
	}

	LDI_MSG((_T("[LDI]--LDI_initialize_LCD_module() : %d\n\r"), error));

	return error;
}

static void LDI_LTS222QV_port_initialize(void)
{
	LDI_MSG((_T("[LDI]++LDI_LTS222QV_port_initialize()\n\r")));

	// nReset	: GPN[5]

	// set GPIO Initial Value to High
	g_pGPIOReg->GPNDAT |= (1<<5);			// nReset

	// Pull Up/Down Disable
	g_pGPIOReg->GPNPUD &= ~(0x3<<10);				// nReset

	// Set GPIO direction to output
	g_pGPIOReg->GPNCON = (g_pGPIOReg->GPNCON & ~(0x3<<10)) | (1<<10);		// nReset

	LDI_MSG((_T("[LDI]--LDI_LTS222QV_port_initialize()\n\r")));
}

static void LDI_LTS222QV_spi_port_enable(void)
{
	LDI_MSG((_T("[LDI]++LDI_LTS222QV_spi_port_enable()\n\r")));

	// Clk		: GPC[5]
	// MOSI		: GPC[6]

	g_pGPIOReg->SPCON &= ~(0x3<<0);	// Host I/F
	g_pGPIOReg->GPICON = 0xaaaaaaaa;
	g_pGPIOReg->GPJCON = 0xaaaaaaaa;

	// set GPIO Initial Value
	g_pGPIOReg->GPCDAT |= ((1<<5)|(1<<6));		// Clk, MOSI -> High

	// Pull Up/Down Disable
	g_pGPIOReg->GPCPUD &= ~((0x3<<10)|(0x3<<12));	// Clk, MOSI

	// Set GPIO direction to output
	g_pGPIOReg->GPCCON = (g_pGPIOReg->GPCCON & ~(0xff<<20)) | (0x11<<20);	// Clk, MOSI

	g_pDispConReg->SIFCCON0 = 0x01;	// RS:LO nCS:HI nOE:HI nWE:HI, Manual

	LDI_MSG((_T("[LDI]--LDI_LTS222QV_spi_port_enable()\n\r")));

}


static void LDI_LTS222QV_spi_port_disable(void)
{
	LDI_MSG((_T("[LDI]++LDI_LTS222QV_spi_port_disable()\n\r")));

	g_pGPIOReg->SPCON |= 0x1;		// RGB I/F
	g_pGPIOReg->GPCDAT |= ((1<<5)|(1<<6));		// Clk, MOSI -> High

	g_pGPIOReg->GPICON = 0x0;
	g_pGPIOReg->GPJCON = 0xaaaaaaa0;

	LDI_MSG((_T("[LDI]--LDI_LTS222QV_spi_port_disable()\n\r")));
}

static void LDI_LTS222QV_reset(void)
{
	LDI_MSG((_T("[LDI]++LDI_LTS222QV_reset()\n\r")));

	// nReset	: GPN[5]

	g_pGPIOReg->GPNDAT |= (1<<5);		// nReset High
	DelayLoop_1ms(10);					// 10 ms

	g_pGPIOReg->GPNDAT &= ~(1<<5);	// nReset Low
	DelayLoop_1ms(10);					// 10 ms

	g_pGPIOReg->GPNDAT |= (1<<5);		// nReset High
	DelayLoop_1ms(10);					// 10 ms

	LDI_MSG((_T("[LDI]--LDI_LTS222QV_reset()\n\r")));
}

static LDI_ERROR LDI_LTS222QV_RGB_initialize(void)
{
	LDI_ERROR error = LDI_SUCCESS;
	int i=0;

	LDI_MSG((_T("[LDI]++LDI_LTS222QV_RGB_initialize()\n\r")));

	LDI_LTS222QV_spi_port_enable();

	while(1)
	{
		LDI_LTS222QV_write(LTS222QV_RGB_initialize[i][0], LTS222QV_RGB_initialize[i][1]);
		if (LTS222QV_RGB_initialize[i][2]) DelayLoop_1ms(LTS222QV_RGB_initialize[i][2]);

		i++;

		if (LTS222QV_RGB_initialize[i][0] == 0 && LTS222QV_RGB_initialize[i][1] == 0) break;
	}

	LDI_LTS222QV_spi_port_disable();

	LDI_MSG((_T("[LDI]--LDI_LTS222QV_RGB_initialize() : %d\n\r"), error));

	return error;
}



static void LDI_LTV350QV_port_initialize(void)
{
	LDI_MSG((_T("[LDI]++LDI_LTV350QV_port_initialize()\n\r")));

	// nReset	: GPN[5]
	// Clk		: GPC[5]
	// MOSI		: GPC[6]
	// nSS		: GPC[7]

	// set GPIO Initial Value to High
	g_pGPIOReg->GPNDAT |= (1<<5);	// nReset
	g_pGPIOReg->GPCDAT |= ((1<<5)|(1<<6)|(1<<7));	// Clk, MOSI, nSS

	// Pull Up/Down Disable
	g_pGPIOReg->GPNPUD &= ~(0x3<<10);	// nReset
	g_pGPIOReg->GPCPUD &= ~((0x3<<10)|(0x3<<12)|(0x3<<14));	// Clk, MOSI, nSS

	// Set GPIO direction to output
	g_pGPIOReg->GPNCON = (g_pGPIOReg->GPNCON & ~(0x3<<10)) | (1<<10);	// nReset
	g_pGPIOReg->GPCCON = (g_pGPIOReg->GPCCON & ~(0xfff<<20)) | (0x111<<20);	// Clk, MOSI, nSS

	LDI_MSG((_T("[LDI]--LDI_LTV350QV_port_initialize()\n\r")));
}

static void LDI_LTV350QV_reset(void)
{
	LDI_MSG((_T("[LDI]++LDI_LTV350QV_reset()\n\r")));

	// nReset	: GPN[5]

	g_pGPIOReg->GPNDAT |= (1<<5);		// nReset High
	DelayLoop_1ms(10);					// 10 ms

	g_pGPIOReg->GPNDAT &= ~(1<<5);	// nReset Low
	DelayLoop_1ms(5);					// 5 ms

	g_pGPIOReg->GPNDAT |= (1<<5);		// nReset High
	DelayLoop_1ms(5);					// 5 ms

	LDI_MSG((_T("[LDI]--LDI_LTV350QV_reset()\n\r")));
}

static LDI_ERROR LDI_LTV350QV_RGB_initialize(void)
{
	LDI_ERROR error = LDI_SUCCESS;
	int i=0;

	LDI_MSG((_T("[LDI]++LDI_LTV350QV_RGB_initialize()\n\r")));

	while(1)
	{
		LDI_LTV350QV_write(LTV350QV_RGB_initialize[i][0], LTV350QV_RGB_initialize[i][1]);
		if (LTV350QV_RGB_initialize[i][2]) DelayLoop_1ms(LTV350QV_RGB_initialize[i][2]);

		i++;

		if (LTV350QV_RGB_initialize[i][0] == 0 && LTV350QV_RGB_initialize[i][1] == 0) break;
	}

	LDI_MSG((_T("[LDI]--LDI_LTV350QV_RGB_initialize() : %d\n\r"), error));

	return error;
}

static void LDI_LTE480WV_RGB_port_initialize(void)
{
	LDI_MSG((_T("[LDI]++LDI_LTE480WV_RGB_port_initialize()\n\r")));

	// set GPIO Initial Value to High
	g_pGPIOReg->GPNDAT |= (1<<5);	// nReset

	// Pull Up/Down Disable
	g_pGPIOReg->GPNPUD &= ~(0x3<<10);	// nReset

	// Set GPIO direction to output
	g_pGPIOReg->GPNCON = (g_pGPIOReg->GPNCON & ~(0x3<<10)) | (1<<10);	// nReset

	LDI_MSG((_T("[LDI]--LDI_LTE480WV_RGB_port_initialize()\n\r")));
}

static void LDI_LTE480WV_RGB_reset(void)
{
	LDI_MSG((_T("[LDI]++LDI_LTE480WV_RGB_reset()\n\r")));

	// nReset	: GPN[5]

	g_pGPIOReg->GPNDAT &= ~(1<<5);	// nReset Low
	DelayLoop_1ms(20);					// 20 ms (more than 1 frame)

	g_pGPIOReg->GPNDAT |= (1<<5);		// nReset High

	LDI_MSG((_T("[LDI]--LDI_LTE480WV_RGB_reset()\n\r")));
}

static LDI_ERROR LDI_LTE480WV_RGB_initialize(void)
{
	LDI_ERROR error = LDI_SUCCESS;

	LDI_MSG((_T("[LDI]++LDI_LTE480WV_RGB_initialize()\n\r")));

	// There is No Power Sequence for LTE480WV

	LDI_MSG((_T("[LDI]--LDI_LTE480WV_RGB_initialize() : %d\n\r"), error));

	return error;
}

static void LDI_LTP700WV_port_initialize(void)
{
	LDI_MSG((_T("[LDI]++LDI_LTP700WV_port_initialize()\n\r")));

	// nReset	: GPN[5]

	// set GPIO Initial Value to High
	g_pGPIOReg->GPNDAT |= (1<<5);	// nReset

	// Pull Up/Down Disable
	g_pGPIOReg->GPNPUD &= ~(0x3<<10);	// nReset

	// Set GPIO direction to output
	g_pGPIOReg->GPNCON = (g_pGPIOReg->GPNCON & ~(0x3<<10)) | (1<<10);	// nReset

	LDI_MSG((_T("[LDI]--LDI_LTP700WV_port_initialize()\n\r")));
}

static void LDI_LTP700WV_reset(void)
{
	LDI_MSG((_T("[LDI]++LDI_LTP700WV_reset()\n\r")));

	// nReset	: GPN[5]

	g_pGPIOReg->GPNDAT |= (1<<5);		// nReset High
	DelayLoop_1ms(10);					// 10 ms

	g_pGPIOReg->GPNDAT &= ~(1<<5);	// nReset Low
	DelayLoop_1ms(10);					// 10 ms

	g_pDispConReg->VIDCON0 |= 0x3;		// VCLK Output enable
	DelayLoop_1ms(100);					// More than 4 frames..

	g_pGPIOReg->GPNDAT |= (1<<5);		// nReset High
	DelayLoop_1ms(10);					// 10 ms

	g_pDispConReg->VIDCON0 &= ~0x3;	// VCLK Output disable

	LDI_MSG((_T("[LDI]--LDI_LTP700WV_reset()\n\r")));
}

static LDI_ERROR LDI_LTP700WV_RGB_initialize(void)
{
	LDI_ERROR error = LDI_SUCCESS;

	LDI_MSG((_T("[LDI]++LDI_LTP700WV_RGB_initialize()\n\r")));

	// There is No Power Sequence for LTP700WV

	LDI_MSG((_T("[LDI]--LDI_LTP700WV_RGB_initialize() : %d\n\r"), error));

	return error;
}

static void LDI_LTS222QV_write(unsigned int address, unsigned int data)
{
	int j;

	//LDI_MSG((_T("[LDI]++LDI_LTS222QV_write(0x%08x, 0x%08x)\n\r"), address, data));

	LCD_CLK_Hi;
	LCD_MOSI_Lo;
	DelayLoop(SPI_DELAY);

	g_pDispConReg->SIFCCON0 = 0x11;	// RS:LO nCS:LO nOE:HI nWE:HI, Manual
	g_pDispConReg->SIFCCON0 = 0x13;	// RS:LO nCS:LO nOE:HI nWE:LO, Manual
	DelayLoop(SPI_DELAY);

	for (j = 7; j >= 0; j--)
	{
		LCD_CLK_Lo;

		if ((address >> j) & 0x0001)	// DATA HIGH or LOW
		{
			LCD_MOSI_Hi;
		}
		else
		{
			LCD_MOSI_Lo;
		}

		DelayLoop(SPI_DELAY);

		LCD_CLK_Hi;			// CLOCK = High
		DelayLoop(SPI_DELAY);
	}

	LCD_MOSI_Lo;
	DelayLoop(SPI_DELAY);

	g_pDispConReg->SIFCCON0 = 0x11;	// RS:LO nCS:LO nOE:HI nWE:HI, Manual
	g_pDispConReg->SIFCCON0 = 0x01;	// RS:LO nCS:HI nOE:HI nWE:HI, Manual
	DelayLoop(SPI_DELAY);

	g_pDispConReg->SIFCCON0 = 0x11;	// RS:LO nCS:LO nOE:HI nWE:HI, Manual
	g_pDispConReg->SIFCCON0 = 0x13;	// RS:LO nCS:LO nOE:HI nWE:LO, Manual
	DelayLoop(SPI_DELAY);

	for (j = 7; j >= 0; j--)
	{
		LCD_CLK_Lo;							//	SCL Low

		if ((data >> j) & 0x0001)	// DATA HIGH or LOW
		{
			LCD_MOSI_Hi;
		}
		else
		{
			LCD_MOSI_Lo;
		}

		DelayLoop(SPI_DELAY);

		LCD_CLK_Hi;			// CLOCK = High
		DelayLoop(SPI_DELAY);
	}

	g_pDispConReg->SIFCCON0 = 0x11;	// RS:LO nCS:LO nOE:HI nWE:HI, Manual
	g_pDispConReg->SIFCCON0 = 0x01;	// RS:LO nCS:HI nOE:HI nWE:HI, Manual
	DelayLoop(SPI_DELAY);

	LCD_MOSI_Lo;
	DelayLoop(SPI_DELAY);

	//LDI_MSG((_T("[LDI]--LDI_LTS222QV_write()\n\r")));
}

static void LDI_LTV350QV_write(unsigned int address, unsigned int data)
{
	unsigned char dev_id_code = 0x1D;
	int j;

	//LDI_MSG((_T("[LDI]++LDI_LTV350QV_write(0x%08x, 0x%08x)\n\r"), address, data));

	LCD_nSS_Hi; 		//	EN = High					CS high
	LCD_CLK_Hi;							//	SCL High
	LCD_MOSI_Hi;							//	Data Low

	DelayLoop(SPI_DELAY);

	LCD_nSS_Lo; 		//	EN = Low				CS Low
	DelayLoop(SPI_DELAY);

	for (j = 5; j >= 0; j--)
	{
		LCD_CLK_Lo;							//	SCL Low

		if ((dev_id_code >> j) & 0x0001)	// DATA HIGH or LOW
		{
			LCD_MOSI_Hi;
		}
		else
		{
			LCD_MOSI_Lo;
		}

		DelayLoop(SPI_DELAY);

		LCD_CLK_Hi;			// CLOCK = High
		DelayLoop(SPI_DELAY);

	}

	// RS = "0" : index data
	LCD_CLK_Lo;			// CLOCK = Low
	LCD_MOSI_Lo;
	DelayLoop(SPI_DELAY);
	LCD_CLK_Hi;			// CLOCK = High
	DelayLoop(SPI_DELAY);

	// Write
	LCD_CLK_Lo;			// CLOCK = Low
	LCD_MOSI_Lo;
	DelayLoop(SPI_DELAY);
	LCD_CLK_Hi;			// CLOCK = High
	DelayLoop(SPI_DELAY);

	for (j = 15; j >= 0; j--)
	{
		LCD_CLK_Lo;							//	SCL Low

		if ((address >> j) & 0x0001)	// DATA HIGH or LOW
		{
			LCD_MOSI_Hi;
		}
		else
		{
			LCD_MOSI_Lo;
		}

		DelayLoop(SPI_DELAY);

		LCD_CLK_Hi;			// CLOCK = High
		DelayLoop(SPI_DELAY);

	}

	LCD_MOSI_Hi;
	DelayLoop(SPI_DELAY);

	LCD_nSS_Hi; 				// EN = High
	DelayLoop(SPI_DELAY*10);

	LCD_nSS_Lo; 		//	EN = Low				CS Low
	DelayLoop(SPI_DELAY);

	for (j = 5; j >= 0; j--)
	{
		LCD_CLK_Lo;							//	SCL Low

		if ((dev_id_code >> j) & 0x0001)	// DATA HIGH or LOW
		{
			LCD_MOSI_Hi;
		}
		else
		{
			LCD_MOSI_Lo;
		}

		DelayLoop(SPI_DELAY);

		LCD_CLK_Hi;			// CLOCK = High
		DelayLoop(SPI_DELAY);

	}

	// RS = "1" instruction data
	LCD_CLK_Lo;			// CLOCK = Low
	LCD_MOSI_Hi;
	DelayLoop(SPI_DELAY);
	LCD_CLK_Hi;			// CLOCK = High
	DelayLoop(SPI_DELAY);

	// Write
	LCD_CLK_Lo;			// CLOCK = Low
	LCD_MOSI_Lo;
	DelayLoop(SPI_DELAY);
	LCD_CLK_Hi;			// CLOCK = High
	DelayLoop(SPI_DELAY);

	for (j = 15; j >= 0; j--)
	{
		LCD_CLK_Lo;							//	SCL Low

		if ((data >> j) & 0x0001)	// DATA HIGH or LOW
		{
			LCD_MOSI_Hi;
		}
		else
		{
			LCD_MOSI_Lo;
		}

		DelayLoop(SPI_DELAY);

		LCD_CLK_Hi;			// CLOCK = High
		DelayLoop(SPI_DELAY);

	}

	LCD_nSS_Hi; 				// EN = High
	DelayLoop(SPI_DELAY);

	//LDI_MSG((_T("[LDI]--LDI_LTV350QV_write()\n\r")));
}

static void DelayLoop_1ms(int msec)
{
	volatile int j;
	for(j = 0; j < LCD_DELAY_1MS*msec; j++)  ;
}

static void DelayLoop(int delay)
{
	volatile int j;
	for(j = 0; j < delay; j++)  ;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -