华大单片机HC32L136笔段式段码LCD显示

关注、星标、置顶公众号


华大单片机LCD简介

华大单片机LCD 控制器是一款适用于单色无源液晶显示器(LCD)的数字控制器/驱动器,以HC32L136K8TA为例,最多具有8 个公用端子(COM)和 40 个区段端子(SEG),用以驱动 160 (4x40)或 288 (8x36)个 LCD 图像元素。其它型号端子的确切数量取决于数据手册中所述的器件引脚。


LCD 主要特性

  • 高度灵活的帧速率控制。
  • 支持静态、1/2、1/3、1/4、1/6 和 1/8 占空比。
  • 支持 1/2、1/3 偏置。
  • 多达 16 个寄存器的 LCD 数据 RAM。
  • 可通过软件配置 LCD 的对比度。
  • 3 种驱动波形生成方式
    – 内部电阻分压、外部电阻分压,外部电容分压方式
    – 可通过软件配置内部电阻分压方式的功耗,从而匹配 LCD 面板所需的电容电荷
  • 支持低功耗模式:LCD 控制器可在 Active、Sleep、DeepSleep 模式下进行显示。
  • 可配置帧中断。
  • 支持 LCD 闪烁功能且可配置多种闪烁频率
  • 未使用的 LCD 区段和公共引脚可配置为数字或模拟功能。

LCD 框图

在这里插入图片描述


LCD 驱动波形

LCD 支持 5 种占空比(Duty)的驱动波形:静态、1/2、1/3、1/4、1/6 和 1/8,由LCD_CR0.Duty 进行设置。LCD 支持 2 种偏置(Bias)的驱动波形:1/2、1/3,由LCD_CR0.Bias 进行设置。建议的组合方式如下表所示:
在这里插入图片描述


LCD Bias 产生电路

LCD 的 Bias 电压具有 3 种来源:内部电阻分压、外部电阻分压、外部电容分压。当选择内部电阻分压时,芯片会自动切换内部的电路以产生符合 Bias 和 Duty 的电压。当选择外部电阻分压或外部电容分压时,需要用户在芯片的外围引脚搭建相关电路。

内部电阻模式

内部电阻模式 VLCDH,VLCD1~VLCD3 可以作为 LCD SEG 输出或者 IO 端口使用。内部电阻模式,LCD 的驱动电压由 CR0.Contrast 控制,如下表所示:
在这里插入图片描述
由于内部电阻驱动方式,无需外部电路支持。当选择内部电阻分压时,芯片会自动切换到内部的电路,这种模式驱动能力较弱。
当选择1/2bias时,大功耗/中功耗/小功耗的电阻分别为 240K/360K/720K;
当选择1/3bias时,大功耗/中功耗/小功耗的电阻分别为 360K/540K/1080K。
通过内部LCD_CR0中的Contrast调整电压使得VLCD电压 = LCD屏电压。

外部电容模式

在这里插入图片描述

外部电阻模式

在这里插入图片描述
注意:
– Rx 为可调电阻,用于调节 LCD 显示对比度。
– 根据使用 LCD 屏幕选择合适的电阻 R。

LCD三种驱动方式对比

内部电阻方式外部电阻方式外部电容方式
功耗(仅LCD驱动)
不包括LCD屏
Type 3.3uA根据外部电阻决定0.5uA
驱动电路无需外接见外部电阻模式图见外部电容模式图
IO管脚COM+SEGCOM+SEG
+4个VLCD1~4
COM+SEG
+4个VLCD1~4
驱动效果一般较好
LCD电压可以根据内部寄存器调整依据外部电阻Rx来调整无法调整,VLCD电压等于VDD电压

LCD显示模式

华大单片机根据把SEG或COM放在寄存器的方式不同分为显示模式0(MODE = 0)和显示模式1(MODE =1)。下面都以1/4Duty为例列出两种显示模式。

LCD 显示模式 0 (MODE = 0)

在这里插入图片描述

LCD 显示模式 1 (MODE = 1)

在这里插入图片描述


LCD驱动讲解

下面以芯缘意码HC32LFx3x-STK-V2.0开发板为例,讲解如何使用华大MCU HC32L136K8TA来驱动显示LCD段码屏。
在这里插入图片描述

LCD屏真值表

在这里插入图片描述

开发板接口电路

在这里插入图片描述
管脚配置代码

/********************************************************************************
 ** \brief  初始化外部GPIO引脚
 **
 ** \return 无
 ******************************************************************************/
void App_PortCfg(void)
{
    Gpio_SetAnalogMode(GpioPortA, GpioPin9);  //COM0
    Gpio_SetAnalogMode(GpioPortA, GpioPin10); //COM1
    Gpio_SetAnalogMode(GpioPortA, GpioPin11); //COM2
    Gpio_SetAnalogMode(GpioPortA, GpioPin12); //COM3   

    Gpio_SetAnalogMode(GpioPortA, GpioPin8);  //SEG0
    Gpio_SetAnalogMode(GpioPortC, GpioPin9);  //SEG1
    Gpio_SetAnalogMode(GpioPortC, GpioPin8);  //SEG2
    Gpio_SetAnalogMode(GpioPortC, GpioPin7);  //SEG3
    Gpio_SetAnalogMode(GpioPortC, GpioPin6);  //SEG4
    Gpio_SetAnalogMode(GpioPortB, GpioPin15); //SEG5
    Gpio_SetAnalogMode(GpioPortB, GpioPin14); //SEG6
    Gpio_SetAnalogMode(GpioPortB, GpioPin13); //SEG7   
}

LCD屏参数

在这里插入图片描述
LCD参数配置

/********************************************************************************
 ** \brief  配置LCD
 **
 ** \return 无
 ******************************************************************************/
void App_LcdCfg(void)
{
    stc_lcd_cfg_t LcdInitStruct;
    stc_lcd_segcom_t LcdSegCom;

    LcdSegCom.u32Seg0_31 = 0xffffff00;                              ///< 配置LCD_POEN0寄存器 开启SEG0~SEG7
    LcdSegCom.stc_seg32_51_com0_8_t.seg32_51_com0_8 = 0xffffffff;   ///< 初始化LCD_POEN1寄存器 全部关闭输出端口
    LcdSegCom.stc_seg32_51_com0_8_t.segcom_bit.Com0_3 = 0;          ///< 使能COM0~COM3
    LcdSegCom.stc_seg32_51_com0_8_t.segcom_bit.Mux = 1;             ///< Mux=0,Seg32_35=0,BSEL=1表示:选择外部电容工作模式,内部电阻断路
    LcdSegCom.stc_seg32_51_com0_8_t.segcom_bit.Seg32_35 = 0xf;
    Lcd_SetSegCom(&LcdSegCom);                                      ///< LCD COMSEG端口配置

    LcdInitStruct.LcdBiasSrc = LcdExtCap;                           ///< 电容分压模式,需要外部电路配合
    LcdInitStruct.LcdDuty = LcdDuty4;                               ///< 1/4duty
    LcdInitStruct.LcdBias = LcdBias3;                               ///< 1/3 BIAS
    LcdInitStruct.LcdCpClk = LcdClk2k;                              ///< 电压泵时钟频率选择2kHz
    LcdInitStruct.LcdScanClk = LcdClk128hz;                         ///< LCD扫描频率选择128Hz
    LcdInitStruct.LcdMode = LcdMode0;                               ///< 选择模式0
    LcdInitStruct.LcdClkSrc = LcdRCL;                               ///< LCD时钟选择RCL
    LcdInitStruct.LcdEn   = LcdEnable;                              ///< 使能LCD模块
    Lcd_Init(&LcdInitStruct);
}

建立LCD映射表

  1. 我们可以根据屏的真值表来完成笔段定义,把表中的PIN5和PIN6定义为一个字节,PIN5为低位。其它PIN定义方法相同。宏定义代码如下:
#define LCDSEG_A 0x01
#define LCDSEG_B 0x10
#define LCDSEG_C 0x40
#define LCDSEG_D 0x80
#define LCDSEG_E 0x04
#define LCDSEG_F 0x02
#define LCDSEG_G 0x20
#define LCDPOINT1P 0x08
#define LCDPOINT2P 0x08
#define LCDPOINT3P 0x08
#define LCDPOINT4P 0x08
  1. 根据笔段定义完成显示数值的定义。如数字0为笔段LCDSEG_A+LCDSEG_B+LCDSEG_C+LCDSEG_D+LCDSEG_E+LCDSEG_F,其它的数值或字母使用同样的方法都可以定义。现在做0~F的定义如下:
#define CHAR_0   LCDSEG_A+LCDSEG_B+LCDSEG_C+LCDSEG_D+LCDSEG_E+LCDSEG_F
#define CHAR_1   LCDSEG_B+LCDSEG_C
#define CHAR_2   LCDSEG_A+LCDSEG_B+LCDSEG_D+LCDSEG_E+LCDSEG_G
#define CHAR_3   LCDSEG_A+LCDSEG_B+LCDSEG_C+LCDSEG_D+LCDSEG_G
#define CHAR_4   LCDSEG_B+LCDSEG_C+LCDSEG_F+LCDSEG_G
#define CHAR_5   LCDSEG_A+LCDSEG_C+LCDSEG_D+LCDSEG_F+LCDSEG_G
#define CHAR_6   LCDSEG_A+LCDSEG_C+LCDSEG_D+LCDSEG_E+LCDSEG_F+LCDSEG_G
#define CHAR_7   LCDSEG_A+LCDSEG_B+LCDSEG_C
#define CHAR_8   LCDSEG_A+LCDSEG_B+LCDSEG_C+LCDSEG_D+LCDSEG_E+LCDSEG_F+LCDSEG_G
#define CHAR_9   LCDSEG_A+LCDSEG_B+LCDSEG_C+LCDSEG_D+LCDSEG_F+LCDSEG_G
#define CHAR_A   LCDSEG_A+LCDSEG_B+LCDSEG_C+LCDSEG_E+LCDSEG_F+LCDSEG_G
#define CHAR_b   LCDSEG_C+LCDSEG_D+LCDSEG_E+LCDSEG_F+LCDSEG_G
#define CHAR_C   LCDSEG_A+LCDSEG_D+LCDSEG_E+LCDSEG_F
#define CHAR_d   LCDSEG_B+LCDSEG_C+LCDSEG_D+LCDSEG_E+LCDSEG_G
#define CHAR_E   LCDSEG_A+LCDSEG_D+LCDSEG_E+LCDSEG_F+LCDSEG_G
#define CHAR_F   LCDSEG_A+LCDSEG_E+LCDSEG_F+LCDSEG_G
  1. 建立MCU LCD模块映射表。
    在这里插入图片描述
    通过上面的图我们可以知道,我们选择LCD 显示模式 0 (MODE = 0)时,要在LCD屏1的位置显示数字0,需要把装有PIN5和PIN6组成的一个字节数据按高4位和低4位拆成两个字节,分别装入MCU的LCDRAM中才可以显示 ,其它位置的显示道理相同,因此我们可以做出如下的映射数组
const uint16_t u16DisTable[] = {
                                 ((CHAR_0 & 0xf0) <<4 | (CHAR_0 & 0x0f)),  //0 
                                 ((CHAR_1 & 0xf0) <<4 | (CHAR_1 & 0x0f)),  //1 
	                             ((CHAR_2 & 0xf0) <<4 | (CHAR_2 & 0x0f)),  //2
	                             ((CHAR_3 & 0xf0) <<4 | (CHAR_3 & 0x0f)),  //3 
	                             ((CHAR_4 & 0xf0) <<4 | (CHAR_4 & 0x0f)),  //4 
	                             ((CHAR_5 & 0xf0) <<4 | (CHAR_5 & 0x0f)),  //5 
	                             ((CHAR_6 & 0xf0) <<4 | (CHAR_6 & 0x0f)),  //6 
	                             ((CHAR_7 & 0xf0) <<4 | (CHAR_7 & 0x0f)),  //7 
	                             ((CHAR_8 & 0xf0) <<4 | (CHAR_8 & 0x0f)),  //8 
	                             ((CHAR_9 & 0xf0) <<4 | (CHAR_9 & 0x0f)),  //9 
	                             ((CHAR_A & 0xf0) <<4 | (CHAR_A & 0x0f)),  //A 
	                             ((CHAR_b & 0xf0) <<4 | (CHAR_b & 0x0f)),  //b 
	                             ((CHAR_C & 0xf0) <<4 | (CHAR_C & 0x0f)),  //C 
	                             ((CHAR_d & 0xf0) <<4 | (CHAR_d & 0x0f)),  //d 
	                             ((CHAR_E & 0xf0) <<4 | (CHAR_E & 0x0f)),  //E 
	                             ((CHAR_F & 0xf0) <<4 | (CHAR_F & 0x0f)),  //F 
	
                               };
  1. 定义数据类型。由于每个LCDRAM都是32位的的寄存器,里面装的是LCD中两个位置的数据 ,要是直接改变其中的显示数据不是很方便,我们使用共用体来定义成我们方便操作的数据类型,定义如下:
 /******************************************************************************
 ** \brief LCDRAM
 *****************************************************************************/
typedef union{
	    uint8_t  u8_dis[4];
		uint16_t u16_dis[2];
		uint32_t u32_dis;
}un_Ram_Data;

我们之前做的只是数字和字母的映射,特殊点我们并没有映射,现在我们可以做下特殊点的定义如下

 /******************************************************************************
 ** \brief LCD特殊点
 *****************************************************************************/
typedef union{
	uint32_t LcdSegTotle;
	struct{
		uint8_t LcdSeg_1P:1;
		uint8_t LcdSeg_2P:1;
		uint8_t LcdSeg_3P:1;
		uint8_t LcdSeg_4P:1;
     }stc_Lcd_SP;
}un_Lcd_SP;
  1. LCD显示。上面的工作都做完后只要写个简单的LCD显示函数就可以完成LCD的显示工作了。显示代码如下:
void App_LcdDis(uint8_t *u8LcdDisData , un_Lcd_SP *punLcdSp)
{
    un_Ram_Data unRamData[2];
    un_Lcd_SP unLcdSP;
	
	unLcdSP = *punLcdSp;
	unRamData[0].u16_dis[0] = u16DisTable[*u8LcdDisData++];         //取LCD屏第一个位置显示内容
	unRamData[0].u16_dis[1] = u16DisTable[*u8LcdDisData++];         //取LCD屏第二个位置显示内容
	unRamData[1].u16_dis[0] = u16DisTable[*u8LcdDisData++];         //取LCD屏第三个位置显示内容
	unRamData[1].u16_dis[1] = u16DisTable[*u8LcdDisData];           //取LCD屏第四个位置显示内容
	if ( unLcdSP.LcdSegTotle )                                      //检查是否要显示特殊点
	{
		if(unLcdSP.stc_Lcd_SP.LcdSeg_1P)
		{
			unRamData[0].u8_dis[0] |= LCDPOINT1P ;                  //显示第一个位置的特殊点
		}
		if(unLcdSP.stc_Lcd_SP.LcdSeg_2P)
		{
			unRamData[0].u8_dis[2] |= LCDPOINT2P ;                  //显示第二个位置的特殊点
		}
		if(unLcdSP.stc_Lcd_SP.LcdSeg_3P)
		{
			unRamData[1].u8_dis[0] |= LCDPOINT3P ;                  //显示第三个位置的特殊点
		}
		if(unLcdSP.stc_Lcd_SP.LcdSeg_4P)
		{
			unRamData[1].u8_dis[2] |= LCDPOINT4P ;                  //显示第四个位置的特殊点
		}
	}	
	
	Lcd_WriteRam(0,unRamData[0].u32_dis);  ///< 赋值寄存器LCDRAM0   调用官方的库
    Lcd_WriteRam(1,unRamData[1].u32_dis);  ///< 赋值寄存器LCDRAM1
}
  1. 主程序如下:
int32_t main(void)
{
    uint8_t   u8LcdData[4] = {0x00};
    un_Lcd_SP unLcdSP;
    Sysctrl_ClkSourceEnable(SysctrlClkRCL,TRUE);            ///< 使能RCL时钟
    Sysctrl_SetRCLTrim(SysctrlRclFreq32768);                ///< 配置内部低速时钟频率为32.768kHz

    Sysctrl_SetPeripheralGate(SysctrlPeripheralLcd,TRUE);   ///< 开启LCD时钟
    Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio,TRUE);  ///< 开启GPIO时钟
    
	DDL_ZERO_STRUCT(unLcdSP);
    App_PortCfg();                                           ///< LCD端口配置
    App_LcdCfg();                                            ///< LCD模块配置

    Lcd_ClearDisp();                                         ///< 清屏     调用官方库清屏
	  
	u8LcdData[0] = 9;                                        //显示 9527           
	u8LcdData[1] = 5;
	u8LcdData[2] = 2;
	u8LcdData[3] = 7;
	unLcdSP.stc_Lcd_SP.LcdSeg_1P = 0;
	unLcdSP.stc_Lcd_SP.LcdSeg_2P = 0;
	unLcdSP.stc_Lcd_SP.LcdSeg_3P = 0;
	unLcdSP.stc_Lcd_SP.LcdSeg_4P = 0;	  
    App_LcdDis(u8LcdData , &unLcdSP);
	
    while(1);
}

程序运行结果如下图

在这里插入图片描述

写在结尾

就我们自己来说,使用最多的屏就是1/4duty和1/3bias的LCD屏,自己在做产品的时候不同的产品使用的屏真值表会不同,我们用几分钟更改下笔段的宏定义,LCD屏直接显示成功。当然,现在我们自己家的产品在做笔段宏定义时按屏的类型做条件编译来定义,使用屏的时候只要做下屏的类型宏定义就可以直接使用,这样更方便。


关注微信公众号『芯缘意码』,查看更多内容,回复“加群”加入技术交流群。
淘宝 店铺 搜索 『芯缘意码』,购买开发学习板仿真器。
公众号:芯缘意码https://shop219666036.taobao.com


  • 16
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值