这个圆形LCD屏幕驱动型号是GC9A01芯片,使用SPI接口,屏幕大小1.28寸的,分辨率240*240的。价格在20左右。引脚说明如下:
下面来说一说如何点亮屏幕。
首先我们要根据电路图知道屏幕使用的IO和SPI接口。LPC845有这样一个好处,那就是基本所有外设都可以通过矩阵方式映射到任意管脚。这样就可以在电路设计中基本可以使用任意IO,而不用为搞错外设脚位担心。
如下图电路中就可以找到屏幕使用管脚。
接下来初始化这些管脚以及SPI接口。初始化代码如下
void drv_spi_gpio_init(void)
{
CLOCK_EnableClock(kCLOCK_Iocon);
CLOCK_EnableClock(kCLOCK_Gpio0);
CLOCK_EnableClock(kCLOCK_Gpio1);
CLOCK_EnableClock(kCLOCK_Spi0);
CLOCK_EnableClock(kCLOCK_Spi1);
CLOCK_Select(kSPI0_Clk_From_MainClk);
CLOCK_Select(kSPI1_Clk_From_MainClk);
RESET_PeripheralReset(kSPI0_RST_N_SHIFT_RSTn);
RESET_PeripheralReset(kSPI1_RST_N_SHIFT_RSTn);
gpio_pin_config_t SPILCD_IN_config = {
.pinDirection = kGPIO_DigitalInput,
.outputLogic = 1U,
};
gpio_pin_config_t SPILCD_IOH_config = {
.pinDirection = kGPIO_DigitalOutput,
.outputLogic = 1U,
};
gpio_pin_config_t SPILCD_IOL_config = {
.pinDirection = kGPIO_DigitalOutput,
.outputLogic = 0U,
};
/* Initialize GPIO functionality on pin */
GPIO_PinInit(GPIO, 0, 7, &SPILCD_IOL_config);//LCD_BLK
GPIO_PinInit(GPIO, 1, 6, &SPILCD_IOH_config);//LCD_DC
GPIO_PinInit(GPIO, 1, 7, &SPILCD_IOH_config);//LCD_RST
GPIO_PinInit(GPIO, 1,18, &SPILCD_IOH_config);//LCD_CS
GPIO_PinInit(GPIO, 1,19, &SPILCD_IOH_config);//LCD_CLK
GPIO_PinInit(GPIO, 0, 6, &SPILCD_IOH_config);//LCD_MOSI
GPIO_PinInit(GPIO, 1,13, &SPILCD_IOH_config);//RGB_MOSI
GPIO_PinInit(GPIO, 1,8, &SPILCD_IOH_config); //FLASH_CS
GPIO_PinInit(GPIO, 1,9, &SPILCD_IN_config); //FLASH_MISO
GPIO_PinInit(GPIO, 0,12, &SPILCD_IOH_config);//FLASH_CLK
GPIO_PinInit(GPIO, 0,13, &SPILCD_IOH_config);//FLASH_MOSI
const uint32_t spilcd_ioc = (/* Selects pull-up function */
IOCON_PIO_MODE_PULLUP |
/* Enable hysteresis */
IOCON_PIO_HYS_EN |
/* Input not invert */
IOCON_PIO_INV_DI |
/* Disables Open-drain function */
IOCON_PIO_OD_DI |
/* Bypass input filter */
IOCON_PIO_SMODE_BYPASS |
/* IOCONCLKDIV0 */
IOCON_PIO_CLKDIV0);
IOCON_PinMuxSet(IOCON, IOCON_INDEX_PIO0_7, spilcd_ioc);
IOCON_PinMuxSet(IOCON, IOCON_INDEX_PIO1_6, spilcd_ioc);
IOCON_PinMuxSet(IOCON, IOCON_INDEX_PIO1_7, spilcd_ioc);
IOCON_PinMuxSet(IOCON, IOCON_INDEX_PIO1_18, spilcd_ioc);
/* Enables clock for switch matrix.: enable */
const uint32_t SPI_LCD_CLK = (/* Selects pull-up function */
IOCON_PIO_MODE_PULLUP |
/* Enable hysteresis */
IOCON_PIO_HYS_EN |
/* Input not invert */
IOCON_PIO_INV_DI |
/* Disables Open-drain function */
IOCON_PIO_OD_DI |
/* Bypass input filter */
IOCON_PIO_SMODE_BYPASS |
/* IOCONCLKDIV0 */
IOCON_PIO_CLKDIV0);
IOCON_PinMuxSet(IOCON, IOCON_INDEX_PIO1_19, SPI_LCD_CLK);
const uint32_t SPI_LCD_MOSI = (/* Selects pull-up function */
IOCON_PIO_MODE_PULLUP |
/* Enable hysteresis */
IOCON_PIO_HYS_EN |
/* Input not invert */
IOCON_PIO_INV_DI |
/* Disables Open-drain function */
IOCON_PIO_OD_DI |
/* Bypass input filter */
IOCON_PIO_SMODE_BYPASS |
/* IOCONCLKDIV0 */
IOCON_PIO_CLKDIV0);
IOCON_PinMuxSet(IOCON, IOCON_INDEX_PIO0_6, SPI_LCD_MOSI);
IOCON_PinMuxSet(IOCON, IOCON_INDEX_PIO1_13, SPI_LCD_MOSI); //RGB_IO=MOSI
const uint32_t SPI_MISO = (/* Selects pull-up function */
0 |
/* Enable hysteresis */
IOCON_PIO_HYS_EN |
/* Input not invert */
IOCON_PIO_INV_DI |
/* Disables Open-drain function */
IOCON_PIO_OD_DI |
/* Bypass input filter */
IOCON_PIO_SMODE_BYPASS |
/* IOCONCLKDIV0 */
IOCON_PIO_CLKDIV0);
IOCON_PinMuxSet(IOCON, IOCON_INDEX_PIO1_9, SPI_MISO); //f_miso
IOCON_PinMuxSet(IOCON, IOCON_INDEX_PIO1_8, spilcd_ioc); //f_cs
IOCON_PinMuxSet(IOCON, IOCON_INDEX_PIO0_12, spilcd_ioc); //f_clk
IOCON_PinMuxSet(IOCON, IOCON_INDEX_PIO0_13, spilcd_ioc); //f_mosi
CLOCK_EnableClock(kCLOCK_Swm);
SWM_SetMovablePinSelect(SWM0, kSWM_SPI0_SCK, kSWM_PortPin_P1_19);
SWM_SetMovablePinSelect(SWM0, kSWM_SPI0_MOSI, kSWM_PortPin_P0_6);
SWM_SetMovablePinSelect(SWM0, kSWM_SPI1_MOSI, kSWM_PortPin_P1_13);
/* Disable clock for switch matrix. */
CLOCK_DisableClock(kCLOCK_Swm);
spi_master_config_t userConfig = {0};
uint32_t srcFreq = 0U;
/* Note: The slave board using interrupt way, slave will spend more time to write data
* to TX register, to prevent TX data missing in slave, we will add some delay between
* frames and capture data at the second edge, this operation will make the slave
* has more time to prapare the data.
*/
SPI_MasterGetDefaultConfig(&userConfig);
userConfig.baudRate_Bps = 30000000;
userConfig.sselNumber = kSPI_Ssel0Assert;
userConfig.clockPolarity = kSPI_ClockPolarityActiveHigh;
userConfig.clockPhase = kSPI_ClockPhaseFirstEdge;
userConfig.direction = kSPI_MsbFirst;
userConfig.delayConfig.preDelay = 0x0U;
userConfig.delayConfig.postDelay = 0x0U;
userConfig.delayConfig.frameDelay = 0x0U;
userConfig.delayConfig.transferDelay = 0x0U;
srcFreq = CLOCK_GetFreq(kCLOCK_MainClk);
SPI_MasterInit(SPI0, &userConfig, srcFreq);
userConfig.baudRate_Bps = 6000000;
SPI_MasterInit(SPI1, &userConfig, srcFreq);
}
SPI_Type * spi_table[2]=
{
SPI0,
SPI1,
};
void spi_pre_h(void)
{
CLOCK_EnableClock(kCLOCK_Swm);
SWM_SetMovablePinSelect(SWM0, kSWM_SPI1_MISO, kSWM_PortPin_P1_9); //F_MISO
SWM_SetMovablePinSelect(SWM0, kSWM_SPI1_MOSI, kSWM_PortPin_P0_13); //F_MOSI
SWM_SetMovablePinSelect(SWM0, kSWM_SPI1_SCK , kSWM_PortPin_P0_12); //F_CLK
CLOCK_DisableClock(kCLOCK_Swm);
SPI_MasterSetBaudRate(SPI1,30000000, CLOCK_GetFreq(kCLOCK_MainClk));
}
void spi_pre_l(void)
{
CLOCK_EnableClock(kCLOCK_Swm);
SWM_SetMovablePinSelect(SWM0, kSWM_SPI1_MOSI, kSWM_PortPin_P1_13); //RGB_IO
SWM_SetMovablePinSelect(SWM0, kSWM_SPI1_MISO, 0XFF); //
SWM_SetMovablePinSelect(SWM0, kSWM_SPI1_SCK , 0XFF); //
CLOCK_DisableClock(kCLOCK_Swm);
SPI_MasterSetBaudRate(SPI1,6000000, CLOCK_GetFreq(kCLOCK_MainClk));
}
void spi_writebyte(uint8_t index,uint8_t TxData)
{
spi_table[index]->TXDATCTL = TxData | 0X077E0000;
while ((spi_table[index]->STAT & SPI_STAT_TXRDY_MASK) == 0U){;}
}
uint8_t spi_readbyte(uint8_t index)
{
uint8_t re;
spi_table[index]->TXDATCTL = 0xff | 0X073E0000;
while ((spi_table[index]->STAT & SPI_STAT_RXRDY_MASK) == 0U){;}
re = spi_table[index]->RXDAT & 0XFF;
return re;
}
屏幕也要初始,再来初始化lcd屏幕,初始化代码都是网上找的,找准型号就行。
void spilcd_init(void)
{
spilcd_gpio_init();
LCD_RST_CLR();
delay_ms(10);
LCD_RST_SET();
delay_ms(10);
LCD_BLK_SET();
delay_ms(10);
// LCD_CS_CLR();
LCD_WR_REG(0xFE);
LCD_WR_REG(0xEF);
LCD_WR_REG(0x84);
LCD_WR_DATA8(0x40);
LCD_WR_REG(0xB6);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0x20);
LCD_WR_REG(0x36);
if(USE_HORIZONTAL==0)LCD_WR_DATA8(0x08);
else if(USE_HORIZONTAL==1)LCD_WR_DATA8(0xC8);
else if(USE_HORIZONTAL==2)LCD_WR_DATA8(0x68);
else LCD_WR_DATA8(0xA8);
LCD_WR_REG(0x3A);
LCD_WR_DATA8(0x05);
LCD_WR_REG(0xC3);
LCD_WR_DATA8(0x13);
LCD_WR_REG(0xC4);
LCD_WR_DATA8(0x13);
LCD_WR_REG(0xC9);
LCD_WR_DATA8(0x22);
LCD_WR_REG(0xF0);
LCD_WR_DATA8(0x45);
LCD_WR_DATA8(0x09);
LCD_WR_DATA8(0x08);
LCD_WR_DATA8(0x08);
LCD_WR_DATA8(0x26);
LCD_WR_DATA8(0x2A);
LCD_WR_REG(0xF1);
LCD_WR_DATA8(0x43);
LCD_WR_DATA8(0x70);
LCD_WR_DATA8(0x72);
LCD_WR_DATA8(0x36);
LCD_WR_DATA8(0x37);
LCD_WR_DATA8(0x6F);
LCD_WR_REG(0xF2);
LCD_WR_DATA8(0x45);
LCD_WR_DATA8(0x09);
LCD_WR_DATA8(0x08);
LCD_WR_DATA8(0x08);
LCD_WR_DATA8(0x26);
LCD_WR_DATA8(0x2A);
LCD_WR_REG(0xF3);
LCD_WR_DATA8(0x43);
LCD_WR_DATA8(0x70);
LCD_WR_DATA8(0x72);
LCD_WR_DATA8(0x36);
LCD_WR_DATA8(0x37);
LCD_WR_DATA8(0x6F);
LCD_WR_REG(0xE8);
LCD_WR_DATA8(0x34);
LCD_WR_REG(0x66);
LCD_WR_DATA8(0x3C);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0xCD);
LCD_WR_DATA8(0x67);
LCD_WR_DATA8(0x45);
LCD_WR_DATA8(0x45);
LCD_WR_DATA8(0x10);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0x00);
LCD_WR_REG(0x67);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0x3C);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0x01);
LCD_WR_DATA8(0x54);
LCD_WR_DATA8(0x10);
LCD_WR_DATA8(0x32);
LCD_WR_DATA8(0x98);
LCD_WR_REG(0x35);
LCD_WR_REG(0x21);
LCD_WR_REG(0x11);
LCD_CS_SET();
delay_ms(120);
LCD_WR_REG(0x29);
LCD_CS_SET();
delay_ms(20);
}
屏幕初始化之后,暂时还看不到屏幕效果,下面就是对屏幕填充颜色和绘制图片了,代码:
void spilcd_draw_bitmap(uint16_t x,uint16_t y,uint16_t w,uint16_t h,const uint8_t *pbuff)
{
uint8_t data[4];
uint16_t x1,y1,x2,y2;
uint32_t send_size = w * h * 2;
x1 = x;
y1 = y;
x2 = x+w-1;
y2 = y+h-1;
// LCD_CS_CLR();
/*Column addresses*/
SPILCD_WriteCmd(0x2A);
data[0] = (x1 >> 8) & 0xFF;
data[1] = x1 & 0xFF;
data[2] = (x2 >> 8) & 0xFF;
data[3] = x2 & 0xFF;
SPILCD_WriteData(data[0]);
SPILCD_WriteData(data[1]);
SPILCD_WriteData(data[2]);
SPILCD_WriteData(data[3]);
/*Page addresses*/
SPILCD_WriteCmd(0x2B);
data[0] = (y1 >> 8) & 0xFF;
data[1] = y1 & 0xFF;
data[2] = (y2 >> 8) & 0xFF;
data[3] = y2 & 0xFF;
SPILCD_WriteData(data[0]);
SPILCD_WriteData(data[1]);
SPILCD_WriteData(data[2]);
SPILCD_WriteData(data[3]);
/*Memory write*/
SPILCD_WriteCmd(0x2C);
SPILCD_WriteMultiData(pbuff, send_size);
LCD_CS_SET();
}
void spilcd_fill(uint16_t x,uint16_t y,uint16_t w,uint16_t h,uint16_t color)
{
uint8_t data[4];
uint16_t x1,y1,x2,y2;
uint32_t send_size = w * h;
x1 = x;
y1 = y;
x2 = x+w-1;
y2 = y+h-1;
/*Column addresses*/
SPILCD_WriteCmd(0x2A);
data[0] = (x1 >> 8) & 0xFF;
data[1] = x1 & 0xFF;
data[2] = (x2 >> 8) & 0xFF;
data[3] = x2 & 0xFF;
SPILCD_WriteData(data[0]);
SPILCD_WriteData(data[1]);
SPILCD_WriteData(data[2]);
SPILCD_WriteData(data[3]);
/*Page addresses*/
SPILCD_WriteCmd(0x2B);
data[0] = (y1 >> 8) & 0xFF;
data[1] = y1 & 0xFF;
data[2] = (y2 >> 8) & 0xFF;
data[3] = y2 & 0xFF;
SPILCD_WriteData(data[0]);
SPILCD_WriteData(data[1]);
SPILCD_WriteData(data[2]);
SPILCD_WriteData(data[3]);
/*Memory write*/
SPILCD_WriteCmd(0x2C);
LCD_DC_SET();
while(send_size--)
{
SPI0->TXDATCTL = ((color>>8)&0xff) | 0X077E0000;
while ((SPI0->STAT & SPI_STAT_TXRDY_MASK) == 0U){;}
SPI0->TXDATCTL = (color&0xff) | 0X077E0000;
while ((SPI0->STAT & SPI_STAT_TXRDY_MASK) == 0U){;}
}
LCD_CS_SET();
}
好了,下面就可以调用填充颜色和绘制图片测试了。
main函数:
#include <stdio.h>
#include "main.h"
#include "board.h"
#include "clock_config.h"
#include "pin_mux.h"
#include "drv_gpio.h"
#include "drv_spi.h"
#include "drv_i2c.h"
#include "drv_spilcd.h"
#include "picture.h"
/*******************************************************************************
* Definitions
******************************************************************************/
/*******************************************************************************
* Prototypes
******************************************************************************/
/*******************************************************************************
* Variables
******************************************************************************/
volatile uint32_t g_systickCounter;
volatile uint32_t rgb_number;
fsm_pt_t pt_key_scan;
/*******************************************************************************
* Code
******************************************************************************/
void SysTick_Handler(void)
{
if(pt_key_scan.time) pt_key_scan.time--;
if (g_systickCounter) g_systickCounter--;
}
void SysTick_DelayTicks(uint32_t n)
{
g_systickCounter = n;
while (g_systickCounter != 0U){;}
}
void keyscan_fsm(fsm_pt_t *pt)
{
PT_BEGIN(pt);
//init
//loop
while(1)
{
if(GPIO->B[1][4] == 0) //SW0
{
if(rgb_number < 1000) rgb_number ++;
DbgConsole_Printf("key:%drn",rgb_number);
}
if(GPIO->B[0][15] == 0) //SW1
{
if(rgb_number) rgb_number --;
DbgConsole_Printf("key:%drn",rgb_number);
}
PT_WAIT_TIME(pt,10);
}
PT_END(pt);
}
/*!
* @brief Main function
*/
int main(void)
{
BOARD_InitBootClocks();
SysTick_Config(SystemCoreClock / 1000U);
init_cycle_counter(true);
BOARD_InitBootPins();
drv_gpio_init();
BOARD_InitDebugConsole();
drv_spi_gpio_init();
drv_i2c_gpio_init();
DbgConsole_Printf("LPC845 Mooncake Board Test!rn");
spilcd_init();
__cycleof__("lcd_fresh_time(n/30000=X ms):")
{
spilcd_fill(0,0,240,240,LCD_RED);
}
delay_ms(500);
__cycleof__("lcd_fresh_time(n/30000=X ms):")
{
spilcd_fill(0,0,240,240,LCD_GREEN);
}
delay_ms(500);
__cycleof__("lcd_fresh_time(n/30000=X ms):")
{
spilcd_fill(0,0,240,240,LCD_BLUE);
}
delay_ms(500);
__cycleof__("show 40*40 picture time:")
{
spilcd_draw_bitmap(100,100,40,40,(uint8_t *)gImage_1);
}
delay_ms(500);
while(1)
{
keyscan_fsm(&pt_key_scan);
GPIO_PortToggle(GPIO, 0, 1u << 0); //led灯
}
}
效果如下:
顺便对刷屏时间做了测量,填充整屏花了123ms左右。LPC845使用30MHz。刷屏比较慢。需要多优化一下了。下面测量的数据是时钟周期数,刷屏时间根据周期数除以时钟频率30MHz。由于LPC845的falsh不够,就没有刷整屏图片了。
所有资料都在下面压缩包了。有些需要安装一下pack。
lpc845_mooncake.zip (16.41 MB)