不得不吐槽一下,現(xiàn)在找點(diǎn)兒技術(shù)資料真是挺難的!就拿段碼液晶屏來(lái)說(shuō),這是一種很成熟而古老的技術(shù)。但是當(dāng)你項(xiàng)目里要用,想找點(diǎn)而資料,就會(huì)發(fā)現(xiàn),似乎唾手可得,可偏偏又會(huì)浪費(fèi)掉很多時(shí)間。
就拿某著名搜索引擎來(lái)說(shuō),輸入段碼液晶一搜,首先得欣賞洋洋灑灑的廣告,然后一個(gè)個(gè)鏈接點(diǎn)進(jìn)去,一會(huì)兒是只言片語(yǔ),一會(huì)兒提醒打開(kāi)另一個(gè)APP,一會(huì)兒要注冊(cè)后繼續(xù)瀏覽,有的更直接,要從此處過(guò),留下買(mǎi)路財(cái)!唉,都人工智能時(shí)代了,怎么感覺(jué)有點(diǎn)兒人工智障。
吐槽完畢,跟大家做一些分享。如果用到時(shí)希望能節(jié)省點(diǎn)兒時(shí)間。段碼屏是一種廣泛使用的低成本,低功耗顯示設(shè)備,我們留心看一下就會(huì)發(fā)現(xiàn)不少。比如遙控器,電表,電子時(shí)鐘,電動(dòng)汽車(chē)儀表盤(pán)。特別是在電池供電,而又需要顯示的場(chǎng)合,段碼液晶屏可以說(shuō)是不二之選。我們簡(jiǎn)要介紹一下段碼液晶屏的顯示原理,并以STM8L052給出一個(gè)上手即用的參考實(shí)例。
段碼液晶分類(lèi)
斷碼液晶的基本顯示原理很簡(jiǎn)單,就是利用液晶這種物質(zhì)在電場(chǎng)作用下會(huì)扭曲的特性,讓光線(xiàn)通過(guò)或不通過(guò),從而實(shí)現(xiàn)顯示。它的技術(shù)也在不斷地進(jìn)步,性能越來(lái)越好。主要分為下面幾大類(lèi):
TN(Twisted Nematic):
- 特點(diǎn):TN扭轉(zhuǎn)式向列場(chǎng)效應(yīng)是將入射光旋轉(zhuǎn)90度。
這是最為常見(jiàn)的一種液晶顯示器件,制造成本相對(duì)較低,液晶分子偏轉(zhuǎn)速度快,響應(yīng)時(shí)間較快;但視角表現(xiàn)相對(duì)較弱,一般視角范圍在50度左右。
- 應(yīng)用場(chǎng)景:廣泛應(yīng)用于計(jì)算器、遙控器、電子鬧鐘等對(duì)視角要求不是特別高、顯示內(nèi)容較為簡(jiǎn)單的小型電子設(shè)備中。
HTN(High Twisted Nematic):
- 特點(diǎn):在TN液晶屏的基礎(chǔ)上進(jìn)行了改進(jìn),液晶分子的取向扭轉(zhuǎn)角度更大,通常在110至130度。它的對(duì)比度有所提高,視角范圍也比TN更寬,可達(dá)到70度左右,并且具有功耗低、驅(qū)動(dòng)電壓低等特點(diǎn),但動(dòng)態(tài)驅(qū)動(dòng)性能較差。
- 應(yīng)用場(chǎng)景:適用于對(duì)視角范圍有一定要求,但對(duì)顯示效果要求不是特別高的設(shè)備,比如一些簡(jiǎn)單的儀器儀表等。
STN(Super Twisted Nematic):
- 特點(diǎn):顯示原理與TN相類(lèi)似,但扭轉(zhuǎn)角度更大,可以實(shí)現(xiàn)更高的對(duì)比度和更豐富的色彩顯示。普通的STN液晶屏,液晶分子可以旋轉(zhuǎn)180-270度。不過(guò),其響應(yīng)時(shí)間相對(duì)較長(zhǎng),在快速變化的畫(huà)面顯示上可能會(huì)有一定的滯后。
- 應(yīng)用場(chǎng)景:能夠滿(mǎn)足需要顯示較為復(fù)雜圖形或文字的需求,常用于一些中低端的電子設(shè)備,如電子詞典、早期的手機(jī)顯示屏等。
FSTN(Film Compensated Super Twisted Nematic):
- 特點(diǎn):在STN的基礎(chǔ)上進(jìn)一步優(yōu)化,通過(guò)在偏光片上增加補(bǔ)償膜來(lái)消除色散,實(shí)現(xiàn)更清晰的黑白顯示。其視角更寬,顯示效果更佳,對(duì)比度也較高。
- 應(yīng)用場(chǎng)景:適用于對(duì)顯示效果有較高要求的設(shè)備,如多功能電表、電力檢測(cè)儀器等。
VATN(Vertical Alignment Twisted Nematic):
- 特點(diǎn):也稱(chēng)為VA(BTN)液晶屏,具有更高的對(duì)比度和更寬廣的可視角度,顯示效果較為出色。
- 應(yīng)用場(chǎng)景:常用于一些高端顯示設(shè)備,不過(guò)由于其成本相對(duì)較高,應(yīng)用范圍相對(duì)較窄。
段碼液晶重要參數(shù)
視角
何為視角呢?以一掛時(shí)鐘來(lái)作為參考說(shuō)明視角的方向,6 點(diǎn)為仰視,3點(diǎn)為左側(cè)視,9點(diǎn)為右側(cè)視,12點(diǎn)為俯視。行業(yè)內(nèi)的名詞參數(shù):6H,9H,3H,12H.也就是我們所說(shuō)的6點(diǎn)鐘方向、9點(diǎn)鐘方向、3點(diǎn) 鐘方向、12點(diǎn)鐘方向的意思了。下圖為一個(gè)6點(diǎn)鐘視角的液晶示意圖。
視角范圍
視角范圍是指在某個(gè)特定視角內(nèi),圖像保持清晰的最大范圍。例如,TN材質(zhì)的段碼液晶屏視角范圍是50度,意味著在上下50度以?xún)?nèi)圖像清晰?。
Segment
是組成液晶顯示圖案的基本單元。比如要顯示一個(gè)數(shù)字“8”,它是由多個(gè)段組成的,這些段通過(guò)組合可以形成不同的數(shù)字、字母或者簡(jiǎn)單的圖形符號(hào)??梢园阉胂蟪善磮D的小碎片,通過(guò)點(diǎn)亮不同的片段組合出想要的形狀。
Common
公共電極。液晶顯示器的工作原理是通過(guò)在段電極SEG和公共電極COM之間施加電壓,來(lái)控制液晶分子的排列,從而實(shí)現(xiàn)顯示。多個(gè)segment會(huì)共享一個(gè)common電極,通過(guò)控制每個(gè)“segment”與“common”之間的電壓,就能控制各個(gè)“segment”是否顯示,以此來(lái)構(gòu)成完整的顯示內(nèi)容。
Duty
每個(gè)COM端掃描時(shí)間占一次完整掃描的比例,比如有4個(gè)COM,Duty就是1/4。
Bias
表明電極的驅(qū)動(dòng)電壓有幾個(gè)電平。如1/3bias表明有VSS,1/3Vlcd,2/3Vlcd,Vlcd這4個(gè)電平。兩個(gè)相鄰電平之間的差是1/3Vlcd。這里Vlcd是段碼屏的工作電壓。
還要注意屏的工作電壓,和工作溫度范圍。
段碼液晶如何驅(qū)動(dòng)
有的段碼屏自身集成驅(qū)動(dòng)芯片,對(duì)外接口常用IIC,SPI等串行總線(xiàn),這種屏成本稍高一些。有的屏不帶驅(qū)動(dòng)芯片,對(duì)外接口為COM,和SEG引腳。驅(qū)動(dòng)這種屏,需要用驅(qū)動(dòng)芯片,如HT1621,CNV1792S等。段碼屏的每個(gè)段,點(diǎn)亮的條件是在COM和SEG引腳之間施加一個(gè)壓差,壓差超過(guò)一定門(mén)限就會(huì)點(diǎn)亮。但是要注意給液晶不能施加直流信號(hào),比如COM加0V,SEG加3.3V,雖然這樣能顯示,但時(shí)間長(zhǎng)了后會(huì)由于電泳現(xiàn)象導(dǎo)致液晶損壞,顯示模糊。正確的做法是,隔一小段時(shí)間就把電壓翻轉(zhuǎn)一次,COM加3.3V,SEG變?yōu)?V,這樣交替進(jìn)行。
如果想更深入的了解原理和驅(qū)動(dòng)方法,可以參考ST的一篇官方文檔:
AN3114 How to use the STM8AL3Lxx, STM8L152xx and STM8L162xx LCD controllers
寫(xiě)的非常好。
- 斷碼液晶實(shí)例
STM8L052C6是一款帶段碼液晶驅(qū)動(dòng)模塊的單片機(jī),提供了驅(qū)動(dòng)庫(kù),可以通過(guò)調(diào)用庫(kù)函數(shù),設(shè)置DUTY和BIAS,段碼顯示與否,調(diào)節(jié)對(duì)比度,閃爍頻率等。
我們用的這個(gè)段碼屏,是大連奇耘的QYT13264S(T)P10V3T,TN型,1/4Duty,1/3Bias,工作電壓3V。淘寶上可以買(mǎi)到,5塊錢(qián)左右。管腳圖,1-4管腳是COM0-COM3,5-13管腳是SEG0-SEG8。
下圖是屏和單片機(jī)的接線(xiàn)圖,stm8L052和stm8L152管腳兼容。注意VLCD這個(gè)引腳,如果配置段碼屏使用內(nèi)部電壓時(shí),斷碼屏的工作電壓VLCD由內(nèi)部的升壓電路從MCU的電源VDD產(chǎn)生,VLCD外接一個(gè)0.1-2uF的小電容,可以使電壓更穩(wěn)定。此引腳的電壓可以在一定范圍內(nèi)通過(guò)程序調(diào)節(jié),從而改變屏的對(duì)比度。
下面是初始化的子程序
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);
}
//設(shè)置實(shí)例使用LCD_Duty_1_4, LCD_Bias_1_3代表1/4DUTY,1/3BIAS
//LCD_PortMaskRegister_0 設(shè)置LCD_PM0寄存器,啟用的SEG對(duì)應(yīng)位置1。
如何點(diǎn)亮一個(gè)液晶的段?比如小數(shù)點(diǎn)P。下面是實(shí)現(xiàn)的子程序
void LCD_GLASS_SetP(bool P ) // P "." 小數(shù)點(diǎn)
{
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) 內(nèi)容的每一位的值對(duì)應(yīng)著圖5 COM0和SEG0-SEG7交叉位置的元素被點(diǎn)亮還是熄滅。
小數(shù)點(diǎn)P是COM0和PIN9交叉位置。PIN9 對(duì)應(yīng)的SEG為SEG4
LCD_RAM0=0x10;代表P(小數(shù)點(diǎn)) 元素被點(diǎn)亮。
其它元素的點(diǎn)亮原理與此相同
下面是電池電量顯示部分的子程序
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個(gè)數(shù)字的顯示需要一些技巧,下面是數(shù)字部分顯示的子程序
/* =========================================================================
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 每個(gè)digit[ ]用最低兩位
digit[1] = (ch >> 2) & 0x03;//C E
digit[2] = (ch >> 4) & 0x03;//B G
digit[3] = (ch >> 6 ) & 0x03;//A F
}
//在第position位置 顯示1個(gè)* 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: //十位數(shù)字
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: //個(gè)位數(shù)字
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: //小數(shù)點(diǎn)后1位數(shù)字
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;
}
}
//在主函數(shù)里調(diào)用上面子函數(shù)測(cè)試一下效果
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) ;
}
下面是實(shí)際效果
參考:
記一次段碼屏調(diào)試總結(jié)
AN3114 How to use the STM8AL3Lxx, STM8L152xx and STM8L162xx LCD controllers(ST官方資料)
AN1447 SOFTWARE DRIVER FOR 4-MULTIPLEXED LCD WITH A STANDARD ST62(ST官方資料)