就拿某著名搜索引擎来说,输入段码液晶一搜,首先得欣赏洋洋洒洒的广告,然后一个个链接点进去,一会儿是只言片语,一会儿提醒打开另一个APP,一会儿要注册后继续浏览,有的更直接,要从此处过,留下买路财!唉,都人工智能时代了,怎么感觉有点儿人工智障。
吐槽完毕,跟大家做一些分享。如果用到时希望能节省点儿时间。段码屏是一种广泛使用的低成本,低功耗显示设备,我们留心看一下就会发现不少。比如遥控器,电表,电子时钟,电动汽车仪表盘。特别是在电池供电,而又需要显示的场合,段码液晶屏可以说是不二之选。我们简要介绍一下段码液晶屏的显示原理,并以STM8L052给出一个上手即用的参考实例。
01
段码液晶分类
断码液晶的基本显示原理很简单,就是利用液晶这种物质在电场作用下会扭曲的特性,让光线通过或不通过,从而实现显示。它的技术也在不断地进步,性能越来越好。主要分为下面几大类:
TN(Twisted Nematic)
- 特点:TN扭转式向列场效应是将入射光旋转90度。
这是最为常见的一种液晶显示器件,制造成本相对较低,液晶分子偏转速度快,响应时间较快;但视角表现相对较弱,一般视角范围在50度左右。
- 应用场景:广泛应用于计算器、遥控器、电子闹钟等对视角要求不是特别高、显示内容较为简单的小型电子设备中。
HTN(High Twisted Nematic)
- 特点:在TN液晶屏的基础上进行了改进,液晶分子的取向扭转角度更大,通常在110至130度。它的对比度有所提高,视角范围也比TN更宽,可达到70度左右,并且具有功耗低、驱动电压低等特点,但动态驱动性能较差。
- 应用场景:适用于对视角范围有一定要求,但对显示效果要求不是特别高的设备,比如一些简单的仪器仪表等。
STN(Super Twisted Nematic)
- 特点:显示原理与TN相类似,但扭转角度更大,可以实现更高的对比度和更丰富的色彩显示。普通的STN液晶屏,液晶分子可以旋转180-270度。不过,其响应时间相对较长,在快速变化的画面显示上可能会有一定的滞后。
- 应用场景:能够满足需要显示较为复杂图形或文字的需求,常用于一些中低端的电子设备,如电子词典、早期的手机显示屏等。
FSTN(Film Compensated Super Twisted Nematic)
- 特点:在STN的基础上进一步优化,通过在偏光片上增加补偿膜来消除色散,实现更清晰的黑白显示。其视角更宽,显示效果更佳,对比度也较高。
- 应用场景:适用于对显示效果有较高要求的设备,如多功能电表、电力检测仪器等。
VATN(Vertical Alignment Twisted Nematic)
- 特点:也称为VA(BTN)液晶屏,具有更高的对比度和更宽广的可视角度,显示效果较为出色。
- 应用场景:常用于一些高端显示设备,不过由于其成本相对较高,应用范围相对较窄。
02
段码液晶重要参数
1、视角
何为视角呢?以一挂时钟来作为参考说明视角的方向,6 点为仰视,3点为左侧视,9点为右侧视,12点为俯视。行业内的名词参数:6H,9H,3H,12H.也就是我们所说的6点钟方向、9点钟方向、3点 钟方向、12点钟方向的意思了。下图为一个6点钟视角的液晶示意图。
2、视角范围
视角范围是指在某个特定视角内,图像保持清晰的最大范围。例如,TN材质的段码液晶屏视角范围是50度,意味着在上下50度以内图像清晰。
3、Segment
是组成液晶显示图案的基本单元。比如要显示一个数字“8”,它是由多个段组成的,这些段通过组合可以形成不同的数字、字母或者简单的图形符号。可以把它想象成拼图的小碎片,通过点亮不同的片段组合出想要的形状。
4、Common
公共电极。液晶显示器的工作原理是通过在段电极SEG和公共电极COM之间施加电压,来控制液晶分子的排列,从而实现显示。多个segment会共享一个common电极,通过控制每个“segment”与“common”之间的电压,就能控制各个“segment”是否显示,以此来构成完整的显示内容。
5、Duty
每个COM端扫描时间占一次完整扫描的比例,比如有4个COM,Duty就是1/4。
6、Bias
表明电极的驱动电压有几个电平。如1/3bias表明有VSS,1/3Vlcd,2/3Vlcd,Vlcd这4个电平。两个相邻电平之间的差是1/3Vlcd。这里Vlcd是段码屏的工作电压。
此外,还要注意屏的工作电压,和工作温度范围。
03
段码液晶如何驱动
有的段码屏自身集成驱动芯片,对外接口常用IIC,SPI等串行总线,这种屏成本稍高一些。有的屏不带驱动芯片,对外接口为COM,和SEG引脚。驱动这种屏,需要用驱动芯片,如HT1621,CNV1792S等。段码屏的每个段,点亮的条件是在COM和SEG引脚之间施加一个压差,压差超过一定门限就会点亮。
但是要注意给液晶不能施加直流信号,比如COM加0V,SEG加3.3V,虽然这样能显示,但时间长了后会由于电泳现象导致液晶损坏,显示模糊。正确的做法是,隔一小段时间就把电压翻转一次,COM加3.3V,SEG变为0V,这样交替进行。
如果想更深入的了解原理和驱动方法,可以参考ST的一篇官方文档:
AN3114 How to use the STM8AL3Lxx, STM8L152xx and STM8L162xx LCD controllers
写的非常好。
断码液晶实例
STM8L052C6是一款带段码液晶驱动模块的单片机,提供了驱动库,可以通过调用库函数,设置DUTY和BIAS,段码显示与否,调节对比度,闪烁频率等。
我们用的这个段码屏,是大连奇耘的QYT13264S(T)P10V3T,TN型,1/4Duty,1/3Bias,工作电压3V。淘宝上可以买到,5块钱左右。管脚图,1-4管脚是COM0-COM3,5-13管脚是SEG0-SEG8。
下图是屏和单片机的接线图,stm8L052和stm8L152管脚兼容。注意VLCD这个引脚,如果配置段码屏使用内部电压时,断码屏的工作电压VLCD由内部的升压电路从MCU的电源VDD产生,VLCD外接一个0.1-2uF的小电容,可以使电压更稳定。此引脚的电压可以在一定范围内通过程序调节,从而改变屏的对比度。
下面是初始化的子程序:
void LCD_GLASS_Init(void)
{
/\* Enable LCD/RTC clock \*/
CLK_PeripheralClockConfig(CLK_Peripheral_RTC, ENABLE);
CLK_PeripheralClockConfig(CLK_Peripheral_LCD, ENABLE);
#ifdef USE_LSE
CLK_RTCClockConfig(CLK_RTCCLKSource_LSE, CLK_RTCCLKDiv_1);
#else
CLK_RTCClockConfig(CLK_RTCCLKSource_LSI, CLK_RTCCLKDiv_1);
#endif
/\* Initialize the LCD \*/
LCD_Init(LCD_Prescaler_1, LCD_Divider_31, LCD_Duty_1\_4,
LCD_Bias_1\_3, LCD_VoltageSource_Internal);
/\* Mask register
For declare the segements used.
in the Discovery we use 0 to 8 segments. \*/
// 使用0-8 segments
LCD_PortMaskConfig(LCD_PortMaskRegister_0, 0xFF);
LCD_PortMaskConfig(LCD_PortMaskRegister_1, 0x01);
LCD_PortMaskConfig(LCD_PortMaskRegister_2, 0x00);
/\* To set contrast to mean value \*/
LCD_ContrastConfig(LCD_Contrast_3V0);
LCD_DeadTimeConfig(LCD_DeadTime_0);
LCD_PulseOnDurationConfig(LCD_PulseOnDuration_1);
/\* Enable LCD peripheral \*/
LCD_Cmd(ENABLE);
}
//设置实例使用LCD_Duty_1_4, LCD_Bias_1_3代表1/4DUTY,1/3BIAS
//LCD_PortMaskRegister_0 设置LCD_PM0寄存器,启用的SEG对应位置1。
如何点亮一个液晶的段?比如小数点P。下面是实现的子程序:
void LCD_GLASS_SetP(bool P ) // P "." 小数点
{
if(P)
LCD->RAM\[LCD_RAMRegister_0\] \|= 0x10; // 亮 (7:0) LCD_RAM0第4位
else
LCD->RAM\[LCD_RAMRegister_0\] &= 0xEF; // 灭 (7:0) LCD_RAM0第4位
}
下图LCD_RAM0寄存器 S0[7:0] (COM0 or COM4) 内容的每一位的值对应着图5 COM0和SEG0-SEG7交叉位置的元素被点亮还是熄灭。
小数点P是COM0和PIN9交叉位置。PIN9 对应的SEG为SEG4
LCD_RAM0=0x10;代表P(小数点) 元素被点亮。
其它元素的点亮原理与此相同。
下面是电池电量显示部分的子程序:
void LCD_GLASS_SetT(uint8_t T) //电池电量显示
{
switch (T)
{
case 0: //PD2_LCDSEG8
LCD->RAM\[LCD_RAMRegister_1\] &= 0xFE; //S9清零 S0(15:8) 外框
LCD->RAM\[LCD_RAMRegister_4\] &= 0xEF; //S10清零 S1(11:4)
LCD->RAM\[LCD_RAMRegister_8\] &= 0xFE; //S12清零 S2(15:8)
LCD->RAM\[LCD_RAMRegister_11\] &= 0xEF; //S11清零 S3(11:4)
break;
case 1: //PD2_LCDSEG8
LCD->RAM\[LCD_RAMRegister_1\] \|= 0x01; //S9亮 S0(15:8) 外框
LCD->RAM\[LCD_RAMRegister_4\] \|= 0x10; //S10亮 S1(11:4)
LCD->RAM\[LCD_RAMRegister_8\] &= 0xFE; //S12清零 S2(15:8)
LCD->RAM\[LCD_RAMRegister_11\] &= 0xEF; //S11清零 S3(11:4)
break;
case 2: //PD2_LCDSEG8
LCD->RAM\[LCD_RAMRegister_1\] \|= 0x01; //S9亮 S0(15:8) 外框
LCD->RAM\[LCD_RAMRegister_4\] \|= 0x10; //S10亮 S1(11:4)
LCD->RAM\[LCD_RAMRegister_8\] &= 0xFE; //S12清零 S2(15:8)
LCD->RAM\[LCD_RAMRegister_11\] \|= 0x10; //S11亮 S3(11:4)
break;
case 3: //PD2_LCDSEG8
LCD->RAM\[LCD_RAMRegister_1\] \|= 0x01; //S9亮 S0(15:8) 外框
LCD->RAM\[LCD_RAMRegister_4\] \|= 0x10; //S10亮 S1(11:4)
LCD->RAM\[LCD_RAMRegister_8\] \|= 0x01; //S12亮 S2(15:8)
LCD->RAM\[LCD_RAMRegister_11\] \|= 0x10; //S11亮 S3(11:4)
break;
}
}
下面是字段S1 S2 S3 S4 显示的子程序
void LCD_GLASS_SetS(bool S1,bool S2,bool S3,bool S4) // S1 S2 S3 "%"S4 " ℃"
{
if(S1)
LCD->RAM\[LCD_RAMRegister_0\] \|= 0x04; // 亮 (7:0) 第2位
else
LCD->RAM\[LCD_RAMRegister_0\] &= 0xfB; // 灭 (7:0) 第2位
if(S2)
LCD->RAM\[LCD_RAMRegister_0\] \|= 0x01; // 亮 S0(7:0) 第1位
else
LCD->RAM\[LCD_RAMRegister_0\] &= 0xfE; // 灭 S0(7:0) 第1位
if(S3)
LCD->RAM\[LCD_RAMRegister_11\] \|= 0x04; // 亮 S3(11:4) 第1位
else
LCD->RAM\[LCD_RAMRegister_11\] &= 0xFB; // 灭 S3(11:4) 第1位
if(S4)
LCD->RAM\[LCD_RAMRegister_7\] \|= 0x40; // 亮 S2(7:0) 第6位
else
LCD->RAM\[LCD_RAMRegister_7\] &= 0xBF; // 灭 S2(7:0) 第6位
}
3个数字的显示需要一些技巧,下面是数字部分显示的子程序
/\* =========================================================================
LCD MAPPING
A
D
An LCD character coding is based on the following matrix:
{ S , G }
{ D , B }
{ E , F }
{ C , A }
The character '9' for example is:
LSB { 0 , 1 }
{ 1 , 1 }
{ 0 , 1 }
MSB { 1 , 1 }
The character '8' for example is:
LSB { 0 , 1 }
{ 1 , 1 }
{ 1 , 1 }
MSB { 1 , 1 }
The character '7' for example is:
LSB { 0 , 0 }
{ 0 , 1 }
{ 0 , 0 }
MSB { 1 , 1 }
The character '6' for example is:
LSB { 1 , 1 }
{ 0 , 0 }
{ 1 , 1 }
MSB { 1 , 1 }
The character '5' for example is:
LSB { 0 , 1 }
{ 1 , 0 }
{ 0 , 1 }
MSB { 1 , 1 }
The character '4' for example is:
LSB { 0 , 1 }
{ 0 , 1 }
{ 0 , 1 }
MSB { 1 , 0 }
The character '3' for example is:
LSB { 0 , 1 }
{ 1 , 1 }
{ 0 , 0 }
MSB { 1 , 1 }
The character '2' for example is:
LSB { 0 , 1 }
{ 1 , 1 }
{ 1 , 0 }
MSB { 0 , 1 }
The character '1' for example is:
LSB { 0 , 0 }
{ 0 , 0}
{ 1, 1 }
MSB { 0, 0 }
The character '0' for example is:
LSB { 0 , 0 }
{ 1 , 1}
{ 1 , 1 }
MSB { 1 , 1 }
\*/
const uint8_t NumberMap\[10\]=
{
0xEE ,0x44 ,0xB6, 0xBA, 0x78, 0xDA, 0xDD, 0xA8, 0xFE, 0xFA
};
static void LCD_Conv_Char_Seg(uint8_t\* c, uint8_t\* digit)
{
uint16_t ch = 0 ;
uint8_t i,j;
switch (\*c)
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
ch = NumberMap\[\*c\];
break;
default:
break;
}
//下面是大连奇耘段码屏的顺序
digit\[0\] = (ch ) & 0x03;//D S 每个digit\[ \]用最低两位
digit\[1\] = (ch \>\> 2) & 0x03;//C E
digit\[2\] = (ch \>\> 4) & 0x03;//B G
digit\[3\] = (ch \>\> 6 ) & 0x03;//A F
}
//在第position位置 显示1个\* ch 字符
void LCD_GLASS_WriteChar(uint8_t\* ch, uint8_t position) {
uint8_t digit\[4\]; /\* Digit frame buffer \*/
LCD_Conv_Char_Seg(ch, digit);
switch (position)
{
case 1: //十位数字
LCD->RAM\[LCD_RAMRegister_0\] &= 0x0fd;
LCD->RAM\[LCD_RAMRegister_0\] \|= (uint8_t)(digit\[0\]& 0x02); // 1D
LCD->RAM\[LCD_RAMRegister_3\] &= 0x0cf;
LCD->RAM\[LCD_RAMRegister_3\] \|= (uint8_t)(digit\[1\]\<\<4 & 0x30); // 1C 1E
LCD->RAM\[LCD_RAMRegister_7\] &= 0x0fc;
LCD->RAM\[LCD_RAMRegister_7\] \|= (uint8_t)(digit\[2\]&0x03); // 1B 1G
LCD->RAM\[LCD_RAMRegister_10\] &= 0xcf;
LCD->RAM\[LCD_RAMRegister_10\] \|= (uint8_t)((digit\[3\]\<\<4)& 0x30); // 1A 1F
break;
/\* Position 2 on LCD (Digit2)\*/
case 2: //个位数字
LCD->RAM\[LCD_RAMRegister_0\] &= 0x0f7;
LCD->RAM\[LCD_RAMRegister_0\] \|= (uint8_t)((digit\[0\]\<\<2)&0x08); // 2D
LCD->RAM\[LCD_RAMRegister_3\] &= 0x3f;
LCD->RAM\[LCD_RAMRegister_3\] \|= (uint8_t)((digit\[1\]\<\<6) & 0xc0); // 2C 2E
LCD->RAM\[LCD_RAMRegister_7\] &= 0xf3;
LCD->RAM\[LCD_RAMRegister_7\] \|= (uint8_t)((digit\[2\]\<\<2)& 0x0c); // 2B 2G
LCD->RAM\[LCD_RAMRegister_10\] &= 0x3f;
LCD->RAM\[LCD_RAMRegister_10\] \|= (uint8_t)((digit\[3\]\<\<6)& 0xC0); // 2A 2F
break;
/\* Position 3 on LCD (Digit3)\*/
case 3: //小数点后1位数字
LCD->RAM\[LCD_RAMRegister_0\] &= 0xdf;
LCD->RAM\[LCD_RAMRegister_0\] \|= (uint8_t)(digit\[0\]\<\<4) & 0x20; // 3D
LCD->RAM\[LCD_RAMRegister_4\] &= 0xfc;
LCD->RAM\[LCD_RAMRegister_4\] \|= (uint8_t)(digit\[1\]) & 0x03; // 3C 3E
LCD->RAM\[LCD_RAMRegister_7\] &= 0xcf;
LCD->RAM\[LCD_RAMRegister_7\] \|= (uint8_t)(digit\[2\]\<\<4)&0x30; // 3B 3G
LCD->RAM\[LCD_RAMRegister_11\] &= 0xfc;
LCD->RAM\[LCD_RAMRegister_11\] \|= (uint8_t)(digit\[3\]) & 0x03 ; // 3A 3F
break;
default:
break;
}
}
//在主函数里调用上面子函数测试一下效果
void main(void)
{
uint8_tchar_tmp;
char_tmp = 1;
LCD_GLASS_WriteChar(&char_tmp, 1);
char_tmp = 2;
LCD_GLASS_WriteChar(&char_tmp, 2);
char_tmp = 3;
LCD_GLASS_WriteChar(&char_tmp, 3);
LCD_GLASS_SetT(2);
LCD_GLASS_SetS(1,1,1,1);
//LCD_GLASS_SetS(0,0,1,1);
LCD_GLASS_SetP(1) ;
}
下面是实际效果:
参考文献:
AN3114 How to use the STM8AL3Lxx, STM8L152xx and STM8L162xx LCD controllers(ST官方资料)
END
来源:TopSemic嵌入式
版权归原作者所有,如有侵权,请联系删除。
▍推荐阅读
→点关注,不迷路←