LX12864P1屏幕使用介绍(ST7567驱动),显示横线、字符、图形

LX12864P1屏幕显示(ST7567驱动)

可编辑12864液晶模组,也就是液晶显示屏是有128*64个点阵组成。12864是一种图形点阵液晶显示器,它主要采用动态驱动原理由行驱动—控制器和列驱动器两部分组成了128(列)×64(行)的全点阵液晶显示此显示器采用了COB的软封装方式,通过导电橡胶和压框连接LCD,使其寿命长,连接可靠。

产品特性编辑工作电压为+5V±10%,可自带驱动LCD所需的负电压,全屏幕点阵,点阵数为128(列)×64(行),可显示8(行)×4(行)个(16×16点阵)汉字,字符16(行)x8(行)个(8 .x 8)也可完成图形字符的显示,与CPU接口采用SPI通讯方式,驱动ST7567

LCD12864DDRAM示意图

LX12864P1屏幕使用介绍(ST7567驱动),显示横线、字符、图形_第1张图片

驱动芯片ST7567 DDRAM示意图

ST7567芯片可写入的页有0 ~ 8 共9页,列有132列,但LCD12864只用到了8页和64列

LX12864P1屏幕使用介绍(ST7567驱动),显示横线、字符、图形_第2张图片

写入数据的点阵显示效果

LX12864P1屏幕使用介绍(ST7567驱动),显示横线、字符、图形_第3张图片

主控芯片与ST7567的通信方式

通信方式有并行的6800和8080,以及串行的4线方式,本次实验使用了4线串行通信,CSB为片选引脚,数据传输前拉低CSB,A0引脚说明传输的是数据还是指令,如果A0为0,说明是指令,如果为1,说明是数据,SCL为时钟线,SDA为数据/指令线,SCL为低电平时放数据或指令,SCL为高电平是ST7567读取SDA上的电平信号
LX12864P1屏幕使用介绍(ST7567驱动),显示横线、字符、图形_第4张图片

LX12864官方手册(带参考代码):https://download.csdn.net/download/weixin_46251230/86731882

参考代码说明

1、LCD初始化函数中行扫描顺序的指令是0xC0,指令说明从上到下扫描,从屏幕上看则最下面是Page0,从下往上直到Page7

2、默认的g_ucComTable数组控制页的访问顺序,默认从Page3开始,若调用 LX12864_ShowChar(0,0,‘A’);函数显示一个字符时,因为字符A高16位,所以占两页,会在Page3开始显示,到Page2结束

3、若想Page0在屏幕的最上边,则初始化时行扫描顺序指令要改为0xC8

4、如果此时g_ucComTable数组不变,则也是从Page3开始显示,再往上显示到Page2,再调用LX12864_ShowChar(0,0,‘A’);则会显示倒着的A

5、如果把g_ucComTable数组改为{0,1,2,3,4,5,6,7},若调用LX12864_ShowChar(3,0,‘A’),则字符A也会倒着显示,并且字符A的上半部分倒着显示在Page3,下半部分倒着显示在Page4,不是想要的效果

6、重新用取模软件取模字符A,则会正常显示

对参考代码进行修改:

1、初始化函数中写入0xC8指令,控制Page0是在屏幕的最上面(根据屏幕实际摆放)

2、将g_ucComTable数组改为{0,1,2,3,4,5,6,7},或者后续可以把函数中的g_ucComTable操作去掉

3、把ASCII码全部重新取模一遍,因为参考代码的字模数组要用特定的数组来显示的,通用性不太好,自己进行修改

代码

GPIO要先初始化

LX12864P1屏幕使用介绍(ST7567驱动),显示横线、字符、图形_第5张图片

/**
 * @name   gpio_Init
 * @brief  GPIO初始化
 * @param  None
 * @retval None
 */	
void gpio_Init(void)
{
    GPIO_InitTypeDef        GPIO_InitStructure;
    /* GPIOB Periph clock enable */
    RCC_AHBPeriphClockCmd(CLK_GPIO, ENABLE);

    /*Configure GPIO_PIN*/
    GPIO_InitStructure.GPIO_Pin = BL|SI|SCL|A0|RS|CS;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(PORT, &GPIO_InitStructure);

    GPIO_SetBits(PORT,CS);
    GPIO_SetBits(PORT,SCL);
    GPIO_SetBits(PORT,SI);
}

BL:背光

SI(SDA):数据/指令线

SCL:时钟线

A0:说明是数据还是指令

RS:复位

CS:片选

写命令
/**
 * @name   LX12864_WrCmd
 * @brief  写命令
           串口模式下只能写不能读,也不能查忙,因此用户要控制好速度不要太快
 * @param  ucCmd:命令字节
 * @retval None
 */	
void LX12864_WrCmd(uint8_t ucCmd)
{
	uint8_t i;
	GPIO_ResetBits(PORT,CS);                                            /* CS片选引脚输出低电平 */
	GPIO_ResetBits(PORT,A0);                                            /* A0引脚输出低电平,说明是指令 */
	
	for(i=0;i<8;i++)
	{
		GPIO_ResetBits(PORT,SCL);
		if(ucCmd&0x80)
		{
			GPIO_SetBits(PORT,SI);
		}
		else
		{
			GPIO_ResetBits(PORT,SI);
		}		
		GPIO_SetBits(PORT,SCL);
		delay_us(10);
		ucCmd=(ucCmd<<1);
	}
	GPIO_SetBits(PORT,CS);
}
写数据
/**
 * @name   LX12864_WrData
 * @brief  写数据
 * @param  ucData:数据字节
 * @retval None   
 */
void LX12864_WrData(uint8_t ucData)
{   
	uint8_t i;
	GPIO_ResetBits(PORT,CS);                                            /* CS片选引脚输出低电平 */
	GPIO_SetBits(PORT,A0);                                              /* A0引脚输出高电平,说明是数据 */

	for(i=0;i<8;i++)
	{
		GPIO_ResetBits(PORT,SCL);
		if(ucData&0x80)
		{
			GPIO_SetBits(PORT,SI);
		}
		else
		{
			GPIO_ResetBits(PORT,SI);
		}
		GPIO_SetBits(PORT,SCL);
		delay_us(10);
		ucData=(ucData<<1);
	}
	GPIO_SetBits(PORT,CS);
}
初始化
/**
 * @name   LX12864_Init
 * @brief  LX12864初始化
 * @param  None
 * @retval None   
 */
void LX12864_Init(void)
{
	GPIO_ResetBits(PORT,RS);                                            /* 硬复位 */
	delay_us(20);
	GPIO_SetBits(PORT,RS);
	delay_us(20);

	LX12864_WrCmd(0xE2);												/* 软复位 */
	delay_us(50);
	LX12864_WrCmd(0x2C);											    /* 升压步骤1 */
	delay_us(50);
	LX12864_WrCmd(0x2E);												/* 升压步骤2 */
	delay_us(50);
	LX12864_WrCmd(0x2F);												/* 升压步骤3 */
	delay_us(50);
	
	LX12864_WrCmd(0x24);												/* 调整对比度,设置范围0x20~0x27 */
	LX12864_WrCmd(0x81);												/* 微调对比度 */
	LX12864_WrCmd(0x1B);												/* 微调对比度的值 */
	
	LX12864_WrCmd(0xA3);	                                            /* 偏压比(bias),0xA2:1/9   0xA3:1=1/7  */
	
	LX12864_WrCmd(0xA6);	                                            /* 正常显示 */
	LX12864_WrCmd(0xA4);	                                            /* 全部点阵打开	*/
	
	LX12864_WrCmd(0xC8);	                                            /* 行扫描顺序:从上到下 */
	LX12864_WrCmd(0xA0);	                                            /* 列扫描顺序:从左到右 */
	LX12864_WrCmd(0x40);	                                            /* 起始行:第一行开始 */
	
	LX12864_WrCmd(0xAF);	                                            /* 开显示 */
	LX12864_Open_BL();                                                  /* 显示背光 */
}
LCD初始化指令

LX12864P1屏幕使用介绍(ST7567驱动),显示横线、字符、图形_第6张图片

初始化命令中,设置对比度由两条指令组成,一定要先发送指令0x81,告诉ST7567接下来的一条指令是设置对比度的,再发送对比度的值,例如0x1B,由6位来控制;

设置行扫描顺序或者列扫描顺序时需要根据屏幕来设定,因为屏幕摆放的方向不同,则行列的显示也会不同

填充屏幕

/**
 * @name   LX12864_FillScreen
 * @brief  填充屏幕
 * @param  ucFillData:填充的数据
 * @retval None   
 */
void LX12864_FillScreen(uint8_t ucFillData)
{
	uint16_t i,j;
	for(i=0;i<8;i++)
	{
		LX12864_WrCmd(0xB0|i);	                                        /* 设置页地址 */
		LX12864_WrCmd(0x10);	                                        /* 设置列地址的高位 */
		LX12864_WrCmd(0x00);	                                        /* 设置列地址的低位 */
		for(j=0;j<128;j++)
		{
			LX12864_WrData(ucFillData);
		}
	}
}
填充一页
/**
 * @name   LX12864_FillPage
 * @brief  填充一页
 * @param  ucFillData:填充的数据
 * @retval None   
 */
void LX12864_FillPage(uint8_t ucPage,uint8_t ucFillData)
{
	uint16_t i;

	LX12864_WrCmd(0xB0|ucPage);	                                        /* 设置页地址 */
	LX12864_WrCmd(0x10);	                                            /* 设置列地址的高位 */
	LX12864_WrCmd(0x00);	                                            /* 设置列地址的低位 */
	for(i=0;i<128;i++)
	{
		LX12864_WrData(ucFillData);
	}
}
显示一条横线
/**
 * @name   LX12864_ShowHorLine
 * @brief  显示一条横线
 * @param  ucPage:页地址 范围:0 ~ 7
 * @param  ucCol:列地址 范围:0 ~ 127
 * @param  ucLen:长度 范围:0 ~ 127
 * @param  ucData:数据
 * @retval None   
 */
void LX12864_ShowHorLine(uint8_t  ucPage,
	                     uint8_t  ucCol,
					     uint8_t  ucLen,
					     uint8_t  ucData)
{
	uint8_t i;
	LX12864_WrCmd((ucPage&0x07)|0xB0);	                                /* 设置页地址 */
	LX12864_WrCmd((ucCol>>4)|0x10);	                                    /* 设置列地址高位 */
	LX12864_WrCmd(ucCol&0x0F);                                          /* 设置列地址低位 */
	for(i=0;i<ucLen;i++)
	{
		LX12864_WrData(ucData);
	}
}
显示一条竖线
/**
 * @name   LX12864_ShowVerLine
 * @brief  显示一条竖线
 * @param  ucStartPage:起始页地址 范围:0 ~ 7
 * @param  ucCol:列地址
 * @param  ucEndPage:结束页地址 范围:0 ~ 7 注意:结束页地址要大于或等于起始页地址
 * @retval None   
 */
void LX12864_ShowVerLine(uint8_t  ucStartPage,
	                     uint8_t  ucCol,
					     uint8_t  ucEndPage)
{
	uint8_t i,ucTemp;
	ucTemp = (ucEndPage - ucStartPage)+1;
	for(i=0;i<ucTemp;i++)
	{
		LX12864_WrCmd((ucStartPage&0x07)|0xB0);	                        /* 设置页地址 */
		LX12864_WrCmd((ucCol>>4)|0x10);	                                /* 设置列地址高位 */
		LX12864_WrCmd(ucCol&0x0F);                                      /* 设置列地址低位 */
		LX12864_WrData(0xFF);
		ucStartPage++;
	}
}
显示字符
typedef enum
{
  g_enBigNumber,
  g_enSmallNumber,
}__ENCHARARRAY_T;

/**
 * @name   LX12864_ShowChar
 * @brief  显示字符
 * @param  ucPage:页地址 范围:0 ~ 7
 * @param  ucCol:列地址 范围:0 ~ 127
 * @param  ucWidth:字体宽度
 * @param  ucHight:字体高度
 * @param  ucChar:字符
 * @param  ucCharArray:字模数组
 * @retval None   
 */
void LX12864_ShowChar(uint8_t ucPage, 
                      uint8_t ucCol, 
					  uint8_t ucWidth, 
					  uint8_t ucHight,
					  uint8_t ucChar,
					  __ENCHARARRAY_T ucCharArray)
{
	uint8_t i,j,ucNeedPage;
	uint16_t ucIndex = 0;
	ucNeedPage = ucHight / 8;
	if((ucHight%8) != 0)
	{
		ucNeedPage = ucNeedPage+1;                                      /* 加上不够一页的部分 */
	}
	for(i=0;i<ucNeedPage;i++)
	{
		LX12864_WrCmd((ucPage&0x07)|0xB0);	                            /* 设置页地址 */
		LX12864_WrCmd((ucCol>>4)|0x10);	                                /* 设置列地址高位 */
		LX12864_WrCmd(ucCol&0x0F);                                      /* 设置列地址低位 */
		for(j=0;j<ucWidth;j++)
		{
			if(ucCharArray == g_enBigNumber)
			{
				LX12864_WrData(g_ucBigNumber[ucChar][ucIndex]);         /* 大字体字模显示字符 */
				ucIndex++;
			}
			else if(ucCharArray == g_enSmallNumber)
			{
				LX12864_WrData(g_ucSmallNumber[ucChar][ucIndex]);       /* 小字体字模显示字符 */
				ucIndex++;
			}
		}
		ucPage++;
	}
}

设置页地址时,参数ucPage为想要显示字符的页数,因为页被分为了0 ~ 7页, 一共8页,只用3位二进制位即可遍历8页,28 = 3,所以&0x07是为了取出ucPage的低3位,再或上0xB0,就设置了想要显示的页

/* 小字体的字模数组 */
unsigned char g_ucSmallNumber[][24]={
{0xC0,0xE0,0x70,0x30,0x30,0x70,0xE0,0xC0,0x3F,0x7F,0xE0,0xC0,0xC0,0xE0,0x7F,0x3F,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"0",0*/
/* (8 X 17 , Arial, 加粗 )*/
{0x00,0x80,0xC0,0x60,0xF0,0xF0,0x00,0x00,0x00,0x01,0x00,0x00,0xFF,0xFF,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"1",1*/
/* (8 X 17 , Arial, 加粗 )*/
{0xC0,0xE0,0x70,0x30,0x30,0x30,0xE0,0xC0,0xC0,0xE0,0xF0,0xD8,0xDC,0xCE,0xC7,0xC1,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"2",2*/
…………………………
};

用PCtoLCD2002取模软件取出想要显示的字符,本次实验用二维数组保存字模数据,根据参数ucChar作为下标去找到要显示的字符的一位数组,再遍历数组写入数据

显示有符号整型数
 /**
  * @name 	Pow
  * @brief 	求sBase的sPower次方
  * @param 	sBase:底数
  * @param 	sPower:次方数
  * @retval	返回次方后的结果
  */
int16_t Pow(int16_t sBase,int16_t sPower)
{
	uint8_t i;
	int16_t sResult = 1;
	for(i = 0; i < sPower; i++)
	{
		sResult *= sBase;
	}
	return sResult;
}

/**
 * @name   LX12864_ShowVariableNum
 * @brief  显示有符号的整型数
 * @param  ucPage:页地址,范围:0 ~ 7
 * @param  ucCol:列地址,范围:0 ~ 127
 * @param  ucWidth:字符宽度
 * @param  ucHight:字符高度
 * @param  ucNumLen:待显示数字的位数
 * @param  sNum:待显示的数字
 * @param  ucCharArray:字模数组
 * @retval None   
 */
void LX12864_ShowSignedNum(uint8_t  ucPage,
					 	   uint8_t  ucCol,
						   uint8_t  ucWidth,
						   uint8_t  ucHight,
						   uint8_t  ucNumLen,
					 	   int16_t  sNum,
						   __ENCHARARRAY_T ucCharArray)
{
	uint8_t i;
	int16_t sNumTemp,sResult;
	if(sNum >= 0)
	{
		LX12864_ShowChar(ucPage,ucCol-(ucWidth+2),ucWidth,ucHight,11,ucCharArray);           /* 11对应空白,如果是正数则去掉负号 */
		sNumTemp = sNum;
	}
	else
	{
		LX12864_ShowChar(ucPage,ucCol-(ucWidth+2),ucWidth,ucHight,10,ucCharArray);           /* 10对应字符'-',负数则在前面加上负号 */
		sNumTemp = -sNum;
	}

	for(i=ucNumLen;i>0;i--)
	{
		sResult = sNumTemp/Pow(10,i-1)%10;                                        /* 从高位开始,取出数字的每一位 */
		LX12864_ShowChar(ucPage,ucCol+((ucWidth+2)*(ucNumLen-i)),ucWidth,ucHight,sResult,ucCharArray);/*显示数字 */
	}
}

其中Pow函数的用法参考了之前写的LCD1602代码,可以参考
http://t.csdn.cn/mMBIA

显示图形

图形需要用取模软件生成

/**
 * @name   LX12864_ShowBmp
 * @brief  显示图形
 * @param  ucPage:页地址 范围:0 ~ 7
 * @param  ucCol:列地址 范围:0 ~ 127
 * @param  ucWidth:图形宽度
 * @param  ucHight:图形高度
 * @param  pucBmp:图形字模数组指针
 * @retval None   
 */
void LX12864_ShowBmp(uint8_t ucPage, 
                     uint8_t ucCol, 
				     uint8_t ucWidth, 
					 uint8_t ucHight,
					 uint8_t* pucBmp)
{
	uint8_t i,j,ucNeedPage;
	uint16_t ucIndex = 0;
	ucNeedPage = ucHight / 8;                                           /* 所需页数 */
	if((ucHight%8) != 0)
	{
		ucNeedPage = ucNeedPage+1;                                      /* 加上不够一页的部分 */
	}
	for(i=0;i<ucNeedPage;i++)
	{
		LX12864_WrCmd((ucPage&0x07)|0xB0);	                            /* 设置页地址 */
		LX12864_WrCmd((ucCol>>4)|0x10);	                                /* 设置列地址高位 */
		LX12864_WrCmd(ucCol&0x0F);                                      /* 设置列地址低位 */
		for(j=0;j<ucWidth;j++)
		{
			LX12864_WrData(pucBmp[ucIndex]);                            /* 显示图形 */
			ucIndex++;
		}
		ucPage++;
	}
}

LCD实现显示亮一会,暗一会

实现亮一会,暗一会主要通过修改对比度来实现

在LCD_Init初始化函数中,有对比度的设置

LX12864_WrCmd(0x81);												/* 微调对比度 */
LX12864_WrCmd(0x1B);												/* 微调对比度的值 */

在这里插入图片描述

要先写入指令0x81,说明是后续的一个字节是修改对比度的

后续这个字节的是6位的,EV5和EV4的值会影响屏幕显示,有下面三种情况

0x1B:

正常显示(屏幕有点坏)

LX12864P1屏幕使用介绍(ST7567驱动),显示横线、字符、图形_第7张图片

0x2B:

会显示填充整个128x64屏幕的黑点,“腾讯QQ”显示会比较暗,正面看看不清

LX12864P1屏幕使用介绍(ST7567驱动),显示横线、字符、图形_第8张图片

如果从侧面看则可以看清

LX12864P1屏幕使用介绍(ST7567驱动),显示横线、字符、图形_第9张图片

0x3B:

正面看看不到显示内容,屏幕全填充

LX12864P1屏幕使用介绍(ST7567驱动),显示横线、字符、图形_第10张图片

侧面看也有黑点
LX12864P1屏幕使用介绍(ST7567驱动),显示横线、字符、图形_第11张图片

所以调整对比度就只能设置EV3 ~ EV0这4位,高两位的EV4设置为1,EV5设置为0,才能正常显示

经过测试,0x1F对比度最高,显示内容很清晰,值越小则对比度越低,显示内容逐渐变暗,0x10时几乎看不见显示的内容

在主函数循环中,通过改变对比度的值,再加上适当的延时,就可以让显示内容亮一会,暗一会

/*
* @name   Run
* @brief  系统运行
* @param  None
* @retval None      
*/
void Run(void)
{
   LX12864_WrCmd(0x81);												/* 微调对比度 */
   LX12864_WrCmd(0x1B);						                		/* 亮 */
   LCD_ShowBmp(g_ucBmp1);
   delay_ms(2000);
   delay_ms(2000);
   LX12864_WrCmd(0x81);												/* 微调对比度 */
   LX12864_WrCmd(0x15);												/* 微亮 */
   LCD_ShowBmp(g_ucBmp1);
   delay_ms(2000);
   delay_ms(2000);
}

LCD12864最终显示界面

LX12864P1屏幕使用介绍(ST7567驱动),显示横线、字符、图形_第12张图片

右上角的WiFi显示一会亮,一会灭

实现思路:因为改变对比度是对一整个屏幕而言的,没发现有对某一部分屏幕改变对比度的方法,所以对WiFi显示的字模数组全部改为0x00,则WiFi图形这一部分就显示空白,WiFi图形显示1秒后再写入全是0x00的字模数组,则可以对WiFi图形擦除,擦除1秒后再显示,则可以达到闪烁的目标

你可能感兴趣的:(STM32,单片机,嵌入式硬件,LCD12864,学习)