文章目录

  • 系列文章目录
  • 前言
  • 一、新建工程
  • 二、LCD了解
    • 1.LCD原理
    • 2.寄存器了解
  • 三、LCD显示实现
    • 1.代码实现
  • 总结

系列文章目录

第一章 HT单片机环境搭建

第二章 点亮第一个LED灯

第三章 按键

第四章 定时器

第五章 PWM

第六章 时基中断

第七章 LCD


前言

前一段时间有小伙伴问我关于LCD的问题,看了一下LCD的原理看完头大。但是再看看MCU的手册,其实发现也没有那么难。下面就跟我一起来看看吧。

一、新建工程

请参照第二章第一节新建工程
——》第二章 点亮第一个LED灯

二、LCD了解

1.LCD原理

在实际的液晶模拟驱动电压中,有几个参数非常关键:
交流电压,液晶分子是需要交流信号来驱动的,长时间的直流电压加在液晶分子两端, 会影响液晶分子的电气化学特性,引起显示模糊,寿命的减少,其破坏性为不可恢复。扫描频率,直接驱动液晶分子的交流电压的频率一般在 60~100Hz 之间,具体是依据 LCD Panel 的面积和设计而定,频率过高,会导致驱动功耗的增加,频率过低,会导致显示闪烁,同时如果扫描频率同光源的频率之间有倍数关系,
则显示也会有闪烁现象出现。

液晶分子是一种电压积分型材料,它的扭曲程度(透光性)仅仅和极板间电压的有效值有关, 和充电波形无关。电压的有效值用 COM/SEG 之间的电压差值的均方根 VRMS 表示:

LCD 类单片机内嵌的 LCD driver(液晶驱动器),正是通过系统的控制,按照用户定义的显示图案,产生点亮 LCD(Liquid Crystal Display,液晶)所需的仿真驱动波形,接到 LCD Panel(液晶显示屏)上点亮对应的像素而达到显示的效果。

占空比(Duty)
该项参数一般也称为 Duty 数或 COM 数。由于 STN/TN 的 LCD 一般是采用时分动态扫描的驱动模式,在此模式下,每个 COM 的有效选通时间与整个扫描周期的比值即占空比(Duty)是固定的,等于 1/COM 数。

偏置(Bias)
LCD 的 SEG/COM 的驱动波形为模拟信号,而各档模拟电压相对于 LCD 输出的最高电压的比例称为偏置,而一般来讲,Bias 是以最低一档与输出最高电压的比值来表示,如下图所示(1/4 Duty,1/3 Bias):

该图对应的是 1/4 duty,1/3 bias 的液晶驱动波形,COM 数为 4,每个COM 的有效选通时间与整个扫描周期的比值(Duty)=1/4,驱动波形的模拟电压共分 3 档,V3 位输出最高电压,V2、V1 为输出中间电压,并且 V1/V3=1/3,所以上述波形图对应的 Duty=1/4,Bias=1/3。
一般而言,Bias 和 Duty 之间是有一定关系的,Duty 数越多,每根 COM 对应的扫描时间变短,而要达到同样的显示亮度和显示对比度,Von 的电压就要提高,选准位和非选准位的差异需要加大,即 Bias 需要加大,Duty 和 Bias 间有一经验公式:
带 SCOM 和 SSEG 功能的 LCD
该 系 列 单 片 机 具 有 驱 动 外 部 LCD 面 板 的 能 力。LCD 驱 动 的 COM 脚 SCOM0~SCOM5 和 SEG 脚 SSEG0~SSEG19 或 SSEG0~SSEG23 与 I/O 口共用。 LCD 信号 COM 和 SEG 由应用程序实现。
LCD 操作
该系列单片机通过设置相关 I/O 引脚为 COM 引脚和 SEG 引脚,以驱动外部 LCD 面板。LCD 驱动功能是由几个 LCD 控制寄存器一起控制的,另外,这些 寄存器还可设置 LCD 的开启和关闭以及 SCOM 和 SSEG 引脚的 R-type 偏压电流,使得 LCD 驱动器 COM 和 SEG 引脚输出 VSS、(1/3)VDD、(2/3)VDD 和 VDD 的电压,从而实现 1/3 bias LCD 的显示。
SLCDC0 寄存器中的 LCDEN 位是 LCD 驱动的主控制位,它与 COMnEN 和 SEGnEN 位搭配共同选择哪些输入 / 输出引脚用于 LCD 驱动。需注意的是,输 入 / 输出端口控制寄存器不需要设置为输出以使能 LCD 驱动操作。

LCD Frames 一个完整的 LCD 波形周期包含两个 Frame, 即 Frame 0 和 Frame 1。下面将做出 详细解释:
Frame 0 :
当要输出 Frame 0 的波形,需将 SLCDC0 寄存器中的 FRAME 位设为 0。 在 Frame 0, COM 信号输出可以是 VDD, 或是 VBIAS = (1/3 )×VDD。SEG 信号输出 可以是 VSS, 或是 VBIAS = (2/3 )×VDD。
Frame 1 :
当要输出 Frame 1 的波形,需将 SLCDC0 寄存器中的 FRAME 位设为 1。 在 Frame 1, COM 信号输出可以是 VSS, 或是 VBIAS =(2/3 )×VDD。SEG 信号输出可 以是 VDD, 或是 VBIAS = (1/3 )×VDD。 COMn 的波形,由应用程序设定 SLCDC0 寄存器中的 FRAME 位控制,及相应 的 I/O 共用引脚数据位决定 COMn 引脚目前输出是 VDD, VSS 或 VBIAS。SEGm 的 波形,由应用程序设定 FRAME 位控制,及相应的 I/O 共用引脚数据位决定 SEGm 引脚目前输出是 VDD, VSS 或 VBIAS。 典型的1/3 bias LCD波形由应用程序以及LCD电压选择电路产生,波形图如下。 请注意,图中“1”代表点亮 LCD 像素。SCOM0~SCOM5 引脚的 COM 信号极 性为“0”或“1”,由相应的 I/O 共用引脚数据位产生。

2.寄存器了解

LCD 控制寄存器
LCD 驱动器 COM 和 SEG 口可以提供多种驱动电流选择以适应不同 LCD 面板 的需求。通过设置 SLCDC0 寄存器中 ISEL0 位和 ISEL1 位可以配置不同的偏压 电阻。所有 COM 和 SEG 引脚和 I/O 引脚共用,可分别通过 SLCDCn 寄存器的 相应引脚功能选择位选择 COM 和 SEG 引脚。

三、LCD显示实现

1.代码实现

以上的内容是不是让你很懵逼?看不懂没关系,跟着我的代码来一起了解吧。
我们实现的功能:点亮LCD屏从0-200滚动显示,我这里用的是TN数码显示屏。

假如我要在一号位点亮数值:1,那我们就需要选中COM1和1B、CO3和1C。在二号位点亮数值:2,那我们就需要选中COM1和2A、COM1和2B、COM3和2C、COM4和2D。
当数值需要滚动时,我们就要依次选中COM1、COM2、COM3、COM4。然后在选中的COM下,使能ABCD等。这个原理类似于点亮LDE数码管或者是矩阵式的LED灯。

man.c

#include "HT66F0185.h"
#include "LCD.h"/******************************************************************************** @fn        delayMs * @brief   延时函数* @param   延时时间 单位为ms* @return    无*******************************************************************************/
void delayMs(unsigned long int ms){while(ms--)GCC_DELAY(2000);//主频8Mhz,执行一条指令为0.5us。一条指令周期等于四条机器周期——》 1/8Mhz * 4 = 0.5us
}/******************************************************************************** @fn     timeBaseInit    * @brief   timebase 初始化* @param   无* @return 无*******************************************************************************/
void timeBaseInit(void){_tbc=0xc4;//时基信号  tbc = fsys / 4 = 8MHz / 2 =  4MHz 溢出周期: t = 2^12 / tbc = 2.048ms_tb0e=1;//允许时基中断_emi=1;//打开总中断}/******************************************************************************** @fn     tb0 * @brief   tb0 为函数名(自己设定) 0x1c为TimeBase0中断向量 2.048ms中断一次* @param    无* @return 无*******************************************************************************/
DEFINE_ISR(tb0,0x1c){lcdScan();
}/******************************************************************************** @fn     main    * @brief   主函数* @param    无* @return 无*******************************************************************************/
void main(){unsigned char i;_wdtc = 0b10101000;//关闭看门狗。直接配置看门狗寄存器,0b代表二进制。/*关闭复用功能*/_csel=0;//比较器共用脚PB5(C+)和PB6(C-)用作I/O_cos=1;//设置PA3管脚为IO,而不是比较器输出_acerl=0;//引脚(PB3、PA7、PA6、PA5、PA4、PB2、PB1、PB0)不用做A/D输入/*LCD初始化*/lcdInit();/*时基初始化*/timeBaseInit();while(1){setLcd(0xff);//lcd全显示delayMs(500);//延迟500ms 为了让人眼看见变化setLcd(0x00);//清空lcddelayMs(500);//延迟500ms 为了让人眼看见变化   /*依次显示小数点等符号*/for(i = 0; i < 4; i++){     dispMark(i,1);delayMs(200);}/*依次消除小数点等符号*/for(i = 0; i < 4; i++){dispMark(i,0);delayMs(200);}/*从0显示到200*/for(i = 0; i < 200; i++){      dispNum(i);delayMs(50); }}
}

lcd.h

#ifndef __LCD__H__
#define __LCD__H__/*
*   此结构采用位域,目的是节省空间。总共使用1个字节
*   假如没有用位域,一共使用8个字节
*/
typedef struct {volatile unsigned char b0 : 1;  //对应B段volatile unsigned char b1 : 1;    //对应G段  volatile unsigned char b2 : 1;  //对应C段volatile unsigned char b3 : 1;    //对应D段volatile unsigned char b4 : 1;    //对应A段volatile unsigned char b5 : 1;    //对应F段volatile unsigned char b6 : 1;    //对应E段volatile unsigned char b7 : 1;    //对应特殊段(P1或2P或3P或4P)
}bits;/*
*   使用共用体,目的是省略赋值操作
*/
typedef union{bits b;unsigned char data;
}LCD;//定义lcd的com口对应的io
#define COM1 _pc2
#define COM2 _pa1
#define COM3 _pa3
#define COM4 _pb6//定义lcd的seg口对应的io
#define SEG1 _pb5
#define SEG2 _pb4
#define SEG3 _pb3
#define SEG4 _pa6
#define SEG5 _pa5
#define SEG6 _pd0
#define SEG7 _pd3
#define SEG8 _pa4//定义特殊符号是否显示
#define SECPOINT 0  void dispIndex(unsigned char index,unsigned char num);void dispNum(unsigned int data);void dispMark(unsigned char obj,unsigned char on);void setLcd(unsigned char data);void lcdInit();void lcdScan();#endif

lcd.c

#include "HT66F0185.h"
#include "lcd.h"/*
*================================================================================
* HEX:0x7D      * HEX:0x05      * HEX:0x5B      * HEX:0x1F      * HEX:0x27      =
*                                                                               =
* BIN:0111 1101 * BIN:0000 0101 * BIN:0101 1011 * BIN:0001 1111 * BIN:0010 0111 =
*================================================================================
* HEX:0x3E      * HEX:0x7E      * HEX:0x15      * HEX:0x7F      * HEX:0x3F      =
*                                                                               =
* BIN:0011 1110 * BIN:0111 1110 * BIN:0001 0101 * BIN:0111 1111 * BIN:0011 1111 =
*================================================================================
*///LCD显示 0   1    2    3    4    5   6     7   8    9  依次与下面对应
const unsigned char dispcode[10] = {0x7D,0x5,0x5B,0x1F,0x27,0x3E,0x7E,0x15,0x7F,0x3F};LCD LCDBUFF[4];//定义显示缓存,运用共用体省略赋值操作。/******************************************************************************** @fn        dispIndex   * @brief   根据数字查找数组的元素* @param    index:显示第几位num:显示的数字* @return  无*******************************************************************************/
void dispIndex(unsigned char index,unsigned char num){if(LCDBUFF[index].b.b7)LCDBUFF[index].data = dispcode[num] | 0x80;elseLCDBUFF[index].data = dispcode[num];
}/******************************************************************************** @fn     dispNum * @brief   显示数字* @param   data:0-9999* @return   无*******************************************************************************/
void dispNum(unsigned int data){unsigned char time = 3;/*输出个位*/dispIndex(time,data % 10);/*判断输入数字是第几位数,然后输出*/while((data / 10) > 0){time -= 1;data /= 10;dispIndex(time,data % 10);}}/******************************************************************************** @fn        dispMark    * @brief   显示标点* @param   obj:显示的对象(开头定义)on: 0-不显示  1-显示* @return  无*******************************************************************************/
void dispMark(unsigned char obj,unsigned char on){LCDBUFF[obj].b.b7 = on;
}/******************************************************************************** @fn     setLcd  * @brief   设置LCD显示段* @param   data: 0-不显示  0xff-全显示* @return 无*******************************************************************************/
void setLcd(unsigned char data){unsigned char i;for(i = 0; i < 4; i++)LCDBUFF[i].data = data;    }/******************************************************************************** @fn     lcdInit * @brief   LCD初始化* @param 无* @return 无*******************************************************************************/
void lcdInit(void){/*SLCDC0和SLCDC1 寄存器操作 使能SCOM和SEEG功能*/_com0en=1;_com2en=1;_com3en=1;_com4en=1;_com5en=1;/**以上可以简写为:*      _slcdc0 &= 0b00001111*     _slcdc1 &= 0b11000000*//*SLCDC1 寄存器操作 SCOM或SSEEG引脚功能选择 */_comsegs0=0;//选择为com_comsegs2=0;//选择为com_comsegs3=0;//选择为com_comsegs4=0;//选择为com_comsegs5=1;//选择为SSEG    /**以上可以简写为:*     _slcdc1 &= 0b00100000*//*SLCDC2和SLCDC3 寄存器操作 SSEG引脚功能选择*/_seg6en=1;_seg7en=1;_seg9en=1;_seg10en=1;_seg11en=1;_seg14en=1;_seg15en=1;/**以上可以简写为:*      _slcdc2 &= 0b00111011*     _slcdc3 &= 0b00000011*/    /*LCD对应引脚设置为输出 *以下注释COM和SEG是硬件的名称*实际使用的COM和SEG需要根据数据手册查看*/_pcc2=0;//COM1_pac1=0;//COM2_pac3=0;//COM3_pbc6=0;//COM4_pbc5=0;//SEG1_pbc4=0;//SEG2_pbc3=0;//SEG3_pac6=0;//SEG4_pac5=0;//SEG5_pdc0=0;//SEG6_pdc3=0;//SEG7_pac4=0;//SEG8_lcden = 1;//LCD使能_isel0 = 1;//电流选择最小驱动_isel1 = 1;_frame = 0;//默认frame = 0   }/******************************************************************************** @fn     lcdScan * @brief   LCD扫描* @param  无* @return 无*******************************************************************************/
void lcdScan(void){static unsigned char scanstep=0;if(_frame){//正反帧输出/*扫描阶段*/switch(scanstep){case 0:          /*选中第一行*/COM1 = 1;COM2 = 0;COM3 = 0;COM4 = 0;/*选中1的A、B段*/if(LCDBUFF[0].b.b4==0)SEG1 = 0;else SEG1 = 1;if(LCDBUFF[0].b.b0==0)SEG2 = 0;else SEG2 = 1;/*选中2的A、B段*/if(LCDBUFF[1].b.b4==0)SEG3 = 0;else SEG3 = 1;if(LCDBUFF[1].b.b0==0)SEG4 = 0;else SEG4 = 1;/*选中3的A、B段*/if(LCDBUFF[2].b.b4==0)SEG5 = 0;else SEG5 = 1;if(LCDBUFF[2].b.b0==0)SEG6 = 0;else SEG6 = 1;/*选中4的A、B段*/if(LCDBUFF[3].b.b4==0)SEG7 = 0;else SEG7 = 1;if(LCDBUFF[3].b.b0==0)SEG8 = 0;else SEG8 = 1;/*进行下一行的扫描*/scanstep = 1; break;case 1:               /*选中第二行*/COM1 = 0;COM2 = 1;COM3 = 0;COM4 = 0;/*选中1的F、G段*/if(LCDBUFF[0].b.b5 == 0)SEG1 = 0;else SEG1 = 1;if(LCDBUFF[0].b.b1 == 0)SEG2 = 0;else SEG2 = 1;/*选中2的F、G段*/if(LCDBUFF[1].b.b5 == 0)SEG3 = 0;else SEG3 = 1;if(LCDBUFF[1].b.b1 == 0)SEG4 = 0;else SEG4 = 1;/*选中3的F、G段*/if(LCDBUFF[2].b.b5 == 0)SEG5 = 0;else SEG5 = 1;if(LCDBUFF[2].b.b1 == 0)SEG6 = 0;else SEG6 = 1;/*选中4的F、G段*/if(LCDBUFF[3].b.b5 == 0)SEG7 = 0;else SEG7 = 1;if(LCDBUFF[3].b.b1 == 0)SEG8 = 0;else SEG8 = 1;/*进行下一行的扫描*/scanstep = 2;break;case 2:/*选中第三行*/COM1 = 0;COM2 = 0;COM3 = 1;COM4 = 0;/*选中1的E、C段*/if(LCDBUFF[0].b.b6 == 0)SEG1 = 0;else SEG1 = 1;if(LCDBUFF[0].b.b2 == 0)SEG2 = 0;else SEG2 = 1;/*选中2的E、C段*/if(LCDBUFF[1].b.b6 == 0)SEG3 = 0;else SEG3 = 1;if(LCDBUFF[1].b.b2 == 0)SEG4 = 0;else SEG4 = 1;/*选中3的E、C段*/if(LCDBUFF[2].b.b6 == 0)SEG5 = 0;else SEG5 = 1;if(LCDBUFF[2].b.b2 == 0)SEG6 = 0;else SEG6 = 1;/*选中4的E、C段*/if(LCDBUFF[3].b.b6 == 0)SEG7 = 0;else SEG7 = 1;if(LCDBUFF[3].b.b2 == 0)SEG8 = 0;else SEG8 = 1;/*进行下一行的扫描*/scanstep = 3;break;case 3:/*选中第四行*/COM1 = 0;COM2 = 0;COM3 = 0;COM4 = 1;/*选中1的D、P1段*/if(LCDBUFF[0].b.b7 == 0)SEG1 = 0;else SEG1 = 1;if(LCDBUFF[0].b.b3 == 0)SEG2 = 0;else SEG2 = 1;/*选中2的D、2P段*/if(LCDBUFF[1].b.b7 == 0)SEG3 = 0;else SEG3 = 1;if(LCDBUFF[1].b.b3 == 0)SEG4 = 0;else SEG4 = 1;/*选中3的D、3P段*/if(LCDBUFF[2].b.b7 == 0)SEG5 = 0;else SEG5 = 1;if(LCDBUFF[2].b.b3 == 0)SEG6 = 0;else SEG6 = 1;/*选中4的D、4P段*/if(LCDBUFF[3].b.b7 == 0)SEG7 = 0;else SEG7 = 1;if(LCDBUFF[3].b.b3 == 0)SEG8 = 0;else SEG8 = 1;/*回到第一行扫描*/                scanstep = 0;break;default:    ;}_frame = 0;//下一次反向输出}else{_frame = 1;}  }

工程链接—》LCD


总结

其中需要注意的是: 1.LCD的扫描时间(60HZ~100HZ) 2.LCD是交流电驱动,所以有使用_frame进行反向。 3.代码中共用体和位寻址的使用 4.原理其实和矩阵LED灯是差不多的

HT合泰单片机入门教程(第七章 LCD)相关推荐

  1. HT合泰单片机入门教程(第一章 HT单片机环境搭建)

    目录 系列文章目录 前言 一.合泰单片机的优势 二.IDE安装 1.HT-IDE3000下载 2.HT-IDE3000安装 三.烧录软件安装 1.HOPE3000下载 2.HOPE3000安装 总结 ...

  2. HT合泰单片机入门教程(第二章 点亮第一个LED灯)

    文章目录 系列文章目录 前言 一.新建工程 1.启动HT-IDE3000 2.建立工程 二.点亮LED灯 1.了解I/O 2.LED代码 总结 系列文章目录 第一章 HT单片机环境搭建 第二章 点亮第 ...

  3. HT合泰单片机入门教程(第六章 时基中断)

    文章目录 系列文章目录 前言 一.新建工程 二.时基中断了解 1.时基中断原理 2.寄存器了解 三.时基中断实现 1.代码实现 总结 系列文章目录 第一章 HT单片机环境搭建 第二章 点亮第一个LED ...

  4. HT合泰单片机入门教程(第五章 PWM)

    文章目录 系列文章目录 前言 一.新建工程 二.PWM相关了解 1.PWM了解 2.硬件电路了解 三.PWM实现 1.代码实现 总结 系列文章目录 第一章 HT单片机环境搭建 第二章 点亮第一个LED ...

  5. HT合泰单片机入门教程(第四章 定时器)

    文章目录 系列文章目录 前言 一.新建工程 二.定时器计数功能实现 1.定时器了解 2.中断了解 三.定时器实现 1.代码实现 总结 系列文章目录 第一章 HT单片机环境搭建 第二章 点亮第一个LED ...

  6. matlab 四叶草,Matlab入门教程 第七章 Simulink 基础

    第七章 Simulink 基础 Chapter 7: Introduction to Simulink 一.Simulink 初步(Primer of Simulink) MATLAB Simulin ...

  7. 全国计算机等级考试二级教程第七章,2012年全国计算机等级考试二级VB入门教程第七章(1)...

    上一章里,我们讨论的VB的输人机制,这次我们来集中讨论VB中信息的输出. 输入和输出是不可分割的,人们输入的最终目的是希望计算机作出反应,任何一个程序都不可能避开这个问题,所以就算要完成一个最基本的程 ...

  8. ArcGIS for Desktop入门教程_第二章_Desktop简介 - ArcGIS知乎-新一代ArcGIS问答社区

    原文:ArcGIS for Desktop入门教程_第二章_Desktop简介 - ArcGIS知乎-新一代ArcGIS问答社区 1 Desktop简介 1.1 ArcGIS for Desktop ...

  9. WPF入门教程(七)---依赖属性(3)(转)

    WPF入门教程(七)---依赖属性(3) 2018年08月24日 08:33:43 weixin_38029882 阅读数:50 四. 只读依赖属性 在以前在对于非WPF的功能来说,对于类的属性的封装 ...

最新文章

  1. 坚持使用GNU/Linux
  2. Deep learning:一(基础知识_1)
  3. 数据结构——顺序表(二)
  4. osgi框架和spring区别_最新100道大厂高频spring面试题附答案
  5. RHEL 6.5 rpm包安装mplyer
  6. 单例设计模式八种写法
  7. 前端技术学习之选择器(十三)
  8. NeHe OpenGL教程 第四十五课:顶点缓存
  9. 无人驾驶之硬件平台详解
  10. 名帖77 刘弘珪 楷书《金刚般若波罗蜜经》
  11. java web atm机_Java项目实现模拟ATM机
  12. [jzoj5791]【NOIP2008模拟】阶乘 (数学)
  13. R-2R梯形网络 DAC简易的电路
  14. 大数据求签,人工智能算命,技术革新下传统行业还有灵魂吗
  15. 【Python自动化测试16】测试用例数据分离
  16. 全光谱防蓝光护眼灯有用吗?怎么分辨是全光谱灯
  17. 打脸谢尔顿的电池,了解一下
  18. MATLAB中的一些方法
  19. 你的电脑是如何识别色图的?
  20. 元宇宙创造大势所趋,一切皆有可能

热门文章

  1. 百度出来的 KAP架构师
  2. 个人支付宝h5支付源码,即时到个人账户
  3. 几次安全实习面试简单记录
  4. 如何成为一名合格的学者---从写好一篇学术论文谈起 ---孟德宇
  5. java ctr_java – CTR模式使用初始向量(IV)
  6. Python版-LeetCode 学习:322 零钱兑换问题
  7. linux系统数据库服务器配置,Linux安装配置MariaDB数据库全程详解
  8. python将图片插入word文件的指定位置,并转为pdf文件
  9. (九)打印机驱动设置—USB接口的设置
  10. 亚马逊云科技的十年创新之路