本帖最后由 yang377156216 于 2022-12-16 15:17 编辑
#申请原创# @21小跑堂
前言
相信大家都对段码液晶屏都不陌生了,在电子大爆炸的日常生活中到处可以见到它的身影,比如在通信工具、家用电器、仪器仪表、交通工具、文教体娱、医疗保健、光阀产品等等应用领域。段码屏一般外形尺寸都在 285×345mm 以下,一般带的导电玻璃厚度会有 0.4、0.55、0.7、1.1mm 几种规格,像素最小的间隙为 10um,显示方式有正显和负显2种方式,颜色模式可以分为黄绿、灰、黑、白、蓝等几种常见的。段码式液晶屏也可以叫做笔段式液晶屏,它主要是用于替代 LED 数码管的显示场景,一般显示内容除了数字外还可以定制为任意用户想要的图案或者文字。既然段码屏已经被广泛使用了,那我们就需要掌握驱动屏的方法,传统的驱动方式就是使用专用驱动IC来配合MCU的I2C或者SPI等串行通讯接口,这种方式会更加简单但是会增加成本和PCB的面积;随着MCU 的功能越来越强大,有很多厂家已经将专用驱动控制器这个模块放到MCU 内部了,比如灵动微最新出来的 L0136 系列产品,这也会让用户使用起来更加便捷,但是同样会带来平台的限定性问题;还有一种就是今天要探讨的方式——使用通用I/O口直接扫描驱动,这种驱动方式可以方便移植到其它MCU 上,同时也能降低成本,因此也被广泛使用在不需要太多段显示的屏上,下面来介绍如何使用灵动微的MM32F031C6T6 这颗芯片的 GPIO 来驱动一款段码屏。
段码屏显示原理
笔段式液晶屏是一种特殊的液态晶体,在电场的作用下晶体的排列方向会发生扭转,因而改变其透光性来显示我们想要的内容。将所有的公共电极(COM)各施加一次扫描电压的时间称之为一帧,单位时间扫描多少帧的频率我们称之为帧频,将扫描公共电极(COM)选通的时间与帧周期之比为占空比。通常占空比等于公共电极数N的倒数,即1/N。在显示时,要使LCD的某个像素实现显示,就必须循环地在该像素用公共级扫描脉冲(COM)与段扫描脉冲(SEG)合成一个超过液晶阈值电压的工作电压才能完成。LCD段码屏有三个参数:工作电压、Duty(对应COM数的倒数)和BIAS(偏压,对应阈值),比如,4.5V、1/4 Duty、1/3 BIAS表示LCD的工作电压为4.5V,有4个COM,阈值大约是1V(4.5/3=1.5V),当加在某段LCD两端的电压大于1.5V时(一般加4.5v)显示,当加在LCD两端的电压小于等于1.5V时不显示。实际使用中,为保证显示效果良好,通常给电极两端加的电压差接近LCD的工作电压,因为LCD对驱动电压的反应不是很大,比如LCD两端的压差为2.5V的时候,可能会显示很微弱,也就是我们所说的“鬼影”。若要不显示,通常给电极两端加的电压差接近0V。 注意液晶分子是需要用交流信号来驱动的,万万不可将直流电压长时间的加在电极两端,否则,会影响液晶分子的电气化学特性,引起显示效果模糊,使用寿命减少的后果,其破坏性不可恢复。所以我们在软件设计的时候使用分割扫描法,在同一时间只有一个COM有效,其余的COM处于无效的状态。
2种段码屏驱动方案
驱动 1/4DUTY 1/2BIAS 3V VLCD 段码屏的扫描原理如下图:
IO 驱动方案电路如下图:
MCU最小系统如下图:
MCU为3.3V工作,使用GPIO的高阻态模式作为com口,使用GPIO的推挽输出模式连接SEG,并且每个COM都接一个47K电阻到一个电容,RC滤波后得到一个中点电压。在轮到某个COM扫描时,设置成推挽输出,如果与本COM连接的SEG不显示,则SEG输出与COM同相,如果显示,则反相。扫描完后,这个COM的IO就设置成高阻态,这样这个COM就通过47K电阻得到1/2VDD电压,而SEG继续输出方波,这样加在LCD上的电压,显示时是+-VDD,不显示时是+-1/2VDD,保证了LCD两端平均直流电压为0。 驱动 1/4DUTY 1/3BIAS 3V VLCD 段码屏的扫描原理如下图: MCU的工作电压为5V,LCD 驱动电路中需要在R17-R15选用150k电阻,R13-R16选用10K电阻,R33-R44选用100K电阻,R29-R32选用47K的电阻。COM口的引脚通过电阻在不同的模式下分压分别输出0.5V,2.5V,4.5V,其中在高阻态的时候输出2.5V的电压。当COM进行扫描时设置COM为推挽输出,如果此时与此相连接的SEG不显示,则说明COM与SEG为同相,如果显示为反相,当此COM扫描完成以后,这个COM的IO就设置为高阻态,这样COM就通过4.7K的电阻得到2.5V的电压,此时SEG继续输出方波,这样加在LCD上的显示电压,显示时为+-3.0V,不显示时为+-1.0V,满足LCD的扫描的要求。 实际测试时选用一个 1/3 bias 的屏,屏幕资料如下:
具体的硬件设计和屏幕详细资料可以参见附件内容。
驱动软件接口
在进行显示的时候我们只需要两个函数就能实现对段码LCD的显示操作 1. void LCD_scan(void)LCD段码扫描函数 程序每隔一段时间就会调用此函数,就会将LCD显示缓存区的内容显示到LCD上面,由于每个字符都有8段,所以全部扫描一次需要8个调用周期,调用周期为2ms的时候显示效果最好,所以刷新频率为62.5HZ。 <blockquote>void Lcd_Scan(void) //2ms定时调用此函数,显示效果较佳
2. void Lcd_DisplaySingle(u8 n,u8 dat)预装载函数 本函数用来将显示的数字或字符放在LCD显示缓冲中,比如Lcd_DisplaySingle(1,6),就是要在第一个数字位置显示数字6,支持显示0~9,A~F,其它字符用户可以自己添加。 //标准字库,显示字符断码表
static const uint8_t s_chDisplay[] = {
0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,
0x77,0x7C,0x58,0x5E,0x79,0x71,0x73,0x76,0x38,0x3E
};
// 0 1 2 3 4 5 6 7 8 9
// A B C D E F P H L U
//字符段码装载函数
void Lcd_DisplaySingle(uint8_t chPos,uint8_t chData) //n为第几个数字,为1~6,dat为要显示的数字
{
uint8_t chTab,k = 0;
if ((chPos == 0) || (chPos > 5)) {
return; //4*12位断码屏
}
chTab = s_chDisplay[chData]; //查表找到显示数字对应断码
if (chPos < 3) { //1、2 //SEG0-SEG11反向,需要判断显示数字在9之前还是9之后
k = (chPos + 1) * 2; //PB(k)
g_hwLcdBuff[3] = (g_hwLcdBuff[3] & ~(0x01<<k )) | ( chTab>>3 & 0x01 )<<k; //.D
g_hwLcdBuff[2] = (g_hwLcdBuff[2] & ~(0x03<<k )) | ((chTab>>1 & 0x02) | (chTab>>4 & 0x01))<<k; //CE
g_hwLcdBuff[1] = (g_hwLcdBuff[1] & ~(0x03<<k )) | ((chTab & 0x02) | (chTab>>6 & 0x01))<<k; //BG
g_hwLcdBuff[0] = (g_hwLcdBuff[0] & ~(0x03<<k )) | ((chTab<<1 & 0x02) | (chTab>>5 & 0x01))<<k; //AF
} else { //3、4、5
k = (10 - chPos ) * 2; //PB(k)
g_hwLcdBuff[3] = (g_hwLcdBuff[3] & ~(0x02<<k )) | ( chTab>>2 & 0x02 )<<k; //D.
g_hwLcdBuff[2] = (g_hwLcdBuff[2] & ~(0x03<<k )) | ((chTab>>2 & 0x01) | (chTab>>3 & 0x02))<<k; //EC
g_hwLcdBuff[1] = (g_hwLcdBuff[1] & ~(0x03<<k )) | ((chTab>>1 & 0x01) | (chTab>>5 & 0x02))<<k; //GB
g_hwLcdBuff[0] = (g_hwLcdBuff[0] & ~(0x03<<k )) | ((chTab>>0 & 0x01) | (chTab>>4 & 0x02))<<k; //FA
}
}
3. void Low_Power(void)进入低功耗模式 当我们不需要进行显示的时候我们可以进入低功耗模式来降低能量的消耗(STANDBY或者STOP模式)。void Low_Power(void)
{
do {
do {
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB |
RCC_AHBPeriph_GPIOC | RCC_AHBPeriph_GPIOD, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; //PA.0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; //PA.0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //上拉输入
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; //PA.0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //上拉输入
GPIO_Init(GPIOC, &GPIO_InitStructure); //初始化IO
} while (0);
Wkup_Init();
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
//PWR_EnterSTANDBYMode();
PWR_EnterSTOPMode(PWR_Regulator_ON,PWR_STOPEntry_WFE);
} while (0);
}
实物照片及演示
以下是测试的展示视频,效果还不错并且没有鬼影:
Schematic_MM32F031q_IO_SLCD_MB.zip
(237.85 KB)
MM32F031xx_q_Lib_SLCD.rar
(375.41 KB)
|
|
|
IO直接驱动段码屏,相应的也提高了实现难度,且较为浪费单片机的IO资源,但不可否认确实会省下成本,但是二姨觉得48pin的单片机为了实现该功能确实是大材小用。但是技术至上,整体实现过程很好,关键点介绍详细,咱们的大佬还是厉害哦!继续加油!