LCD Glass段码屏的驱动

01 什么是段码屏

段码液晶屏(段码屏)是非点阵类的液晶屏,主要是用来替代LED数码管的,比如温度计、计算器、钟表等等,显示的内容基本都是数字,也有些是符号。
段码屏

02 段码屏驱动原理

段码液晶由若干段区(单像素或完整符号)组成,每个区段都包含一层在两根电极之间对齐的液晶分子。液晶在电场的影响下,晶体的排序方向会产生扭曲,因而改变其透光性,从而能够看到显示的內容。要使晶体产生扭曲现象,必须使电极两端的压差大于一定的阈值,才能显示內容。

一般段码屏有两个极,一个为段电极,一个为公共极,如下图所示。SEG1 ~ SEGn为段电极,COM为公共极。上述电场的电压就是施加在段电极和公共极两端的。
Snipaste_2022-02-14_22-03-35
但是段码液晶显示屏不像数码管那样,施以一定大小的直流正向电压就能显示,如果在段码屏SEG和COM两端加直流电压,将会导致液态晶体不可逆的损坏。

需要某段液晶显示的,那么就在某段液晶的SEG和COM两端施加一定压差的交流电,如果不需要液晶显示的话,那么也需要在SEG和COM两端施加电压,不过所施加电压的压差很小或者为零即可。
Snipaste_2022-02-14_22-22-00

03 段码屏驱动实现

  1. LCD段码屏三个主要参数
  • 工作电压
    段码液晶屏的操作电压。

  • 占空比(Duty)
    该参数也称作COM数,定义为1 / (给定LCD上的公共端子数)。一般LCD的驱动方式是采用时分动态扫描的方式。所以每个 COM 的有效选通时间与整个扫描周期的比值,即占空比(Duty)是固定的,等于 1/COM 。

  • 偏置(BIAS)
    驱动LCD时使用的电压等级。定义为1 / (用于驱动LCD显示器的电压等级数 - 1)

  1. 基本驱动方法
    如液晶屏的工作电压约为3.3V,只要给液晶两个电机间施加约3.3V的电压差(如COM端为3.3V,SEG端为0V),并间隔适当的时间,将两个电极的电压反转(如COM端为0V,SEG端为3.3V)即可让液晶显示。
    Snipaste_2022-02-14_22-22-56
    而液晶不显示时,保证两个电极间的电压差为0V(如COM为3.3V,SEG为3.3V),并且间隔适当的时间反转两极的电压(如COM为0V,SEG为0V)。
    Snipaste_2022-02-14_22-22-09
  2. 驱动方案
  • MCU没有LCD驱动外设
    使用IO口直接驱动的方式,偏置比只能选择1/2,这种方式需要在COM口加上拉、下拉各1个电阻,阻值约为100KΩ - 200KΩ。
  • MCU+专用的LCD显示驱动芯片
    常用的驱动芯片有HT1621、HT1622,对应的偏压比为1/3、1/4。
  • MCU带有LCD外设
    如STM32L073系列芯片。

硬件设计

以带有LCD外设的MCU驱动YR1433段码屏为例。

STM32L0外设LCD

  1. 主要特性
  • 帧速率可调节。
  • 占空比支持静态、1/2、1/3、1/4和1/8。
  • 偏置支持静态、1/2、1/3和1/4。
  • 双缓冲存储器,允许应用程序随时更新LCD_RAM寄存器的数据
  • 对比度可调节
  • 支持闪烁功能
  1. LCD控制器框图
    Snipaste_2022-02-14_22-39-10
  2. LCD外设的时钟源
  • 32.768KHz低速外部时钟(LSE)
  • 32.768KHz低速内部时钟(LSI)
  • 高速外部时钟(HSE)的分频,最大支持1MHz

YR1433段码液晶屏

Snipaste_2022-02-15_20-46-51

  1. YR1433的参数
工作电压 DUTY BIAS
3.2V 1/4 1/3
  1. 段码屏MAP图
PIN 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
COM1 COM1 1F 1A 2F 2A 3F 3A 4F 4A 5F 5A 6F 6A 7F 7A 8F 8A K2 S S1 T2
COM2 COM2 1G 1B 2G 2B 3G 3B 4G 4B 5G 5B 6G 6B 7G 7B 8G 8B K3 T8 S2 T1
COM3 COM3 1E 1C 2E 2C 3E 3C 4E 4C 5C 5C 6E 6C 7E 7C 8E 8C K4 T7 S6 T0
COM4 COM4 1D T3 2D T4 3D T5 4D T6 5D S7 6D S8 7D S4 8D S5 K1 K5 S3 T

固件设计

  1. 固件配置流程
  • 定义端口
    根据MAP图,我们将STM32L073RBT6的引脚功能分配如下。
#define LCD_SEG0_PIN                    GPIO_PIN_1
#define LCD_SEG1_PIN                    GPIO_PIN_2
#define LCD_SEG2_PIN                    GPIO_PIN_3
#define LCD_SEG3_PIN                    GPIO_PIN_6
#define LCD_SEG4_PIN                    GPIO_PIN_7
#define LCD_SEG5_PIN                    GPIO_PIN_0
#define LCD_SEG6_PIN                    GPIO_PIN_1
#define LCD_SEG7_PIN                    GPIO_PIN_3
#define LCD_SEG8_PIN                    GPIO_PIN_4
#define LCD_SEG9_PIN                    GPIO_PIN_5
#define LCD_SEG10_PIN                   GPIO_PIN_10
#define LCD_SEG11_PIN                   GPIO_PIN_11
#define LCD_SEG12_PIN                   GPIO_PIN_12
#define LCD_SEG13_PIN                   GPIO_PIN_13
#define LCD_SEG14_PIN                   GPIO_PIN_14
#define LCD_SEG15_PIN                   GPIO_PIN_15
#define LCD_SEG16_PIN                   GPIO_PIN_8
#define LCD_SEG17_PIN                   GPIO_PIN_15
#define LCD_SEG18_PIN                   GPIO_PIN_0
#define LCD_SEG19_PIN                   GPIO_PIN_1
#define LCD_SEG20_PIN                   GPIO_PIN_2
#define LCD_SEG_GPIOA_PORT              GPIOA
#define LCD_SEG_GPIOB_PORT              GPIOB
#define LCD_SEG_GPIOC_PORT              GPIOC
#define LCD_SEG_AF                      GPIO_AF1_LCD

#define LCD_COM0_PIN                    GPIO_PIN_8
#define LCD_COM1_PIN                    GPIO_PIN_9
#define LCD_COM2_PIN                    GPIO_PIN_10
#define LCD_COM3_PIN                    GPIO_PIN_9
#define LCD_COM0_2_GPIO_PORT            GPIOA
#define LCD_COM3_GPIO_PORT              GPIOB
#define LCD_COM_AF                      GPIO_AF1_LCD
  • 初始化寄存器
void LCD_GlassInit(void)
{
    LCDHandle.Instance                  = LCD;
    LCDHandle.Init.Prescaler            = LCD_PRESCALER_4;
    LCDHandle.Init.Divider              = LCD_DIVIDER_16;
    LCDHandle.Init.Duty                 = LCD_DUTY_1_4;
    LCDHandle.Init.Bias                 = LCD_BIAS_1_3;
    LCDHandle.Init.VoltageSource        = LCD_VOLTAGESOURCE_INTERNAL;
    LCDHandle.Init.Contrast             = LCD_CONTRASTLEVEL_5;
    LCDHandle.Init.DeadTime             = LCD_DEADTIME_0;
    LCDHandle.Init.PulseOnDuration      = LCD_PULSEONDURATION_7;
    LCDHandle.Init.BlinkMode            = LCD_BLINKMODE_OFF;
    LCDHandle.Init.BlinkFrequency       = LCD_BLINKFREQUENCY_DIV8;
    LCDHandle.Init.MuxSegment           = LCD_MUXSEGMENT_DISABLE;
    /* YR1433 LCD glass need open high drive*/
    LCDHandle.Init.HighDrive            = LCD_HIGHDRIVE_1;
    
    __HAL_LCD_RESET_HANDLE_STATE(&LCDHandle);
    
    HAL_LCD_MspInit(&LCDHandle);
    
    HAL_LCD_Init(&LCDHandle);
    
    HAL_LCD_Clear(&LCDHandle);
}
  • 用LSE作为LCD时钟源并初始化端口
void HAL_LCD_MspInit(LCD_HandleTypeDef *hlcd)
{
    GPIO_InitTypeDef  GPIO_InitStruct;
    RCC_OscInitTypeDef  RCC_OscInitStruct;
    
    if(hlcd->Instance == LCD)
    {
        __HAL_RCC_PWR_CLK_ENABLE();
        
        HAL_PWR_EnableBkUpAccess();
        
        __HAL_RCC_BACKUPRESET_FORCE();
        __HAL_RCC_BACKUPRESET_RELEASE();
        
        /** Enable LCD LSE Clock*/
        RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;
        RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
        RCC_OscInitStruct.LSEState = RCC_LSE_ON;
        if(HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
        {
            Error_Handler();
        }
        __HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_LSE);
        
        LCD_SEG_GPIOA_CLK_ENABLE();
        LCD_SEG_GPIOB_CLK_ENABLE();
        LCD_SEG_GPIOC_CLK_ENABLE();
        
        LCD_COM0_2_GPIO_CLK_ENABLE();
        LCD_COM3_GPIO_CLK_ENABLE();
        
        /** Configure peripheral GPIO*/
        GPIO_InitStruct.Pin         = LCD_SEG0_PIN | LCD_SEG1_PIN | LCD_SEG2_PIN \
                                    | LCD_SEG3_PIN | LCD_SEG4_PIN | LCD_SEG17_PIN;
        GPIO_InitStruct.Mode        = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull        = GPIO_NOPULL;
        GPIO_InitStruct.Speed       = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate   = LCD_SEG_AF;
        HAL_GPIO_Init(LCD_SEG_GPIOA_PORT, &GPIO_InitStruct);
        
        GPIO_InitStruct.Pin         = LCD_SEG5_PIN | LCD_SEG6_PIN | LCD_SEG7_PIN \
                                    | LCD_SEG8_PIN | LCD_SEG9_PIN | LCD_SEG10_PIN \
                                    | LCD_SEG11_PIN | LCD_SEG12_PIN | LCD_SEG13_PIN \
                                    | LCD_SEG14_PIN | LCD_SEG15_PIN | LCD_SEG16_PIN;
        HAL_GPIO_Init(LCD_SEG_GPIOB_PORT, &GPIO_InitStruct);
        
        GPIO_InitStruct.Pin         = LCD_SEG18_PIN | LCD_SEG19_PIN;
        HAL_GPIO_Init(LCD_SEG_GPIOC_PORT, &GPIO_InitStruct);
        
        GPIO_InitStruct.Pin         = LCD_COM0_PIN | LCD_COM1_PIN | LCD_COM2_PIN;
        GPIO_InitStruct.Alternate   = LCD_COM_AF;
        HAL_GPIO_Init(LCD_COM0_2_GPIO_PORT, &GPIO_InitStruct);
        
        GPIO_InitStruct.Pin         = LCD_COM3_PIN;
        GPIO_InitStruct.Alternate   = LCD_COM_AF;
        HAL_GPIO_Init(LCD_COM3_GPIO_PORT, &GPIO_InitStruct);
        
        LCD_CLK_ENABLE();
    }
}
  1. 固件应用实例
    限于篇幅,以下只列举简单的数字显示应用,其他部分的应用,同学可以自行类推研究下。
    在上述配置完成后,接下来编写应用层的函数。
  • 根据段码屏的定义我们可以得到以下MAP信息。
/*!
 * @brief   GLASS LCD MAPPING(YR1433)
 *          LCD allows to display informations on GAS Meter 8-segment digits:
 *
 * 单价 阀开 阀关 透支 异常 *{[][][][]}请换电池 余额不足 WIFI 灯
         1     2     3     4     5    6      7     8
   剩余 ---   ---   ---   ---   ---   ---   ---   ---    日期
       |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
   累计 ---   ---   ---   ---   ---   ---   ---   ---    时间
       |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
   环境 ---   ---   ---   ---   --- 。--- 。---   ---    元/m3
 */
  • 其中数字字符段(暂时排除符号段)的信号关系如下(参考前述的段码屏MAP图)。
/*!  
 * @brief An LCD character coding is based on the following matrix:
 * COM              0   1   2   3
 * SEG(n)         { F , G , E , D }
 * SEG(n+1)       { A , B , C , T/S}
 * n <= 14
 *
 * @brief The character '8' for example is:
    ------------------------------------
 * LSB            { 1 , 1 , 1 , 1 }
 * MSB            { 1 , 1 , 1 , 0 }
    ------------------------------------
 * '8' = 0xEF   hex
 */
  • 定义数字列表
const uint16_t numberMap[16] =
{
    /** 0    1    2    3    4    5    6    7    8    9    a    b    c    d    e    f*/
    0xEB,  0x60,  0xC7,  0xE5,  0x6C,  0xAD,  0xAF,  0xE0,  0xEF,  0xED,  0xEE,  0x2F,  0x8B,  0x67,  0x8F,  0x8E
};
  • 定义寄存器和偏移量
#define fastAbs(n)                      n >= 0 ? n : -n
#define ASCII_CHAR_0                    0x30

#define LCD_COM0                        LCD_RAM_REGISTER0
#define LCD_COM1                        LCD_RAM_REGISTER2
#define LCD_COM2                        LCD_RAM_REGISTER4
#define LCD_COM3                        LCD_RAM_REGISTER6
#define LCD_COM4                        LCD_RAM_REGISTER8

#define LCD_SEG0_SHIFT                  0
#define LCD_SEG1_SHIFT                  1
#define LCD_SEG2_SHIFT                  2
#define LCD_SEG3_SHIFT                  3
#define LCD_SEG4_SHIFT                  4
#define LCD_SEG5_SHIFT                  5
#define LCD_SEG6_SHIFT                  6
#define LCD_SEG7_SHIFT                  7
#define LCD_SEG8_SHIFT                  8
#define LCD_SEG9_SHIFT                  9
#define LCD_SEG10_SHIFT                 10
#define LCD_SEG11_SHIFT                 11
#define LCD_SEG12_SHIFT                 12
#define LCD_SEG13_SHIFT                 13
#define LCD_SEG14_SHIFT                 14
#define LCD_SEG15_SHIFT                 15
#define LCD_SEG16_SHIFT                 16
#define LCD_SEG17_SHIFT                 17
#define LCD_SEG18_SHIFT                 18
#define LCD_SEG19_SHIFT                 19

#define LCD_COM_NUM                     4
#define LCD_SEG_NUM                     20

typedef struct
{
    uint32_t lcdRegData[LCD_COM_NUM];
} LCD_GLASS_T;
  • 浮点数字应用程序
/** public variables */
LCD_GLASS_T lcdGlassInfo;

/*!
 * @brief       LCD data convert
 *
 * @param       data
 *
 * @retval      None
 *
 */
static void LCD_GlassConvert(uint8_t data)
{
    uint16_t character = 0;
    uint8_t i = 0;
    uint8_t j = 0;
    
    switch(data)
    {
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            character = numberMap[data - ASCII_CHAR_0];
        break;
        
        default:
            character = 0x00;
            break;
    }
    
    /** 分离出单个字符的MSB 4bit和LSB 4bit*/
    for(i = 4, j = 0; j < 2; i -= 4, j++)
    {
        digit[j] = (character >> i) & 0x0F;
    }
}

/*!
 * @brief       LCD glass write float number
 *
 * @param       number
 *
 * @param       dotType
 *
 * @retval      None
 *
 */
void LCD_GlassWriteFloat(float data, LCD_DOT_T dotType)
{
    char character[20];
    int i, j = 0;
    
    sprintf(character, "%09.3f", fastAbs(data));
    
    for(i = 0; i < strlen(character); i++)
    {
        if(data < 0)
        {
            if(i == 0)
            {
                LCD_GlassConvert((uint8_t)character[i]);
            }
            else if(character[i] == '.')
            {
                LCD_GlassConvert((uint8_t)character[i + 1]);
                i += 1;
            }
            else
            {
                LCD_GlassConvert((uint8_t)character[i]);
            }
        }
        else if(character[i] == '.')
        {
            LCD_GlassConvert((uint8_t)character[i + 1]);
            i += 1;
        }
        else
        {
            LCD_GlassConvert((uint8_t)character[i]);
        }
        digitData[j] = digit[0];
        digitData[j + 1] = digit[1];
        j += 2;
    }
    /** 转换COM0对应的数据, 每个LSB和MSB的最高位*/
    lcdGlassInfo.lcdRegData[LCD_COM0_TYPE] |= (((digitData[1] & 0x8) >> 3) << LCD_SEG0_SHIFT) | (((digitData[0] & 0x8) >> 3) << LCD_SEG1_SHIFT) |\
        (((digitData[3] & 0x8) >> 3) << LCD_SEG2_SHIFT) | (((digitData[2] & 0x8) >> 3) << LCD_SEG3_SHIFT) |\
        (((digitData[5] & 0x8) >> 3) << LCD_SEG4_SHIFT) | (((digitData[4] & 0x8) >> 3) << LCD_SEG5_SHIFT) |\
        (((digitData[7] & 0x8) >> 3) << LCD_SEG6_SHIFT) | (((digitData[6] & 0x8) >> 3) << LCD_SEG7_SHIFT) |\
        (((digitData[9] & 0x8) >> 3) << LCD_SEG8_SHIFT) | (((digitData[8] & 0x8) >> 3) << LCD_SEG9_SHIFT) |\
        (((digitData[11] & 0x8) >> 3) << LCD_SEG10_SHIFT) | (((digitData[10] & 0x8) >> 3) << LCD_SEG11_SHIFT) |\
        (((digitData[13] & 0x8) >> 3) << LCD_SEG12_SHIFT) | (((digitData[12] & 0x8) >> 3) << LCD_SEG13_SHIFT) |\
        (((digitData[15] & 0x8) >> 3) << LCD_SEG14_SHIFT) | (((digitData[14] & 0x8) >> 3) << LCD_SEG15_SHIFT);
    
    /** 转换COM1对应的数据*/
    lcdGlassInfo.lcdRegData[LCD_COM1_TYPE] |= (((digitData[1] & 0x4) >> 2) << LCD_SEG0_SHIFT) | (((digitData[0] & 0x4) >> 2) << LCD_SEG1_SHIFT) |\
        (((digitData[3] & 0x4) >> 2) << LCD_SEG2_SHIFT) | (((digitData[2] & 0x4) >> 2) << LCD_SEG3_SHIFT) |\
        (((digitData[5] & 0x4) >> 2) << LCD_SEG4_SHIFT) | (((digitData[4] & 0x4) >> 2) << LCD_SEG5_SHIFT) |\
        (((digitData[7] & 0x4) >> 2) << LCD_SEG6_SHIFT) | (((digitData[6] & 0x4) >> 2) << LCD_SEG7_SHIFT) |\
        (((digitData[9] & 0x4) >> 2) << LCD_SEG8_SHIFT) | (((digitData[8] & 0x4) >> 2) << LCD_SEG9_SHIFT) |\
        (((digitData[11] & 0x4) >> 2) << LCD_SEG10_SHIFT) | (((digitData[10] & 0x4) >> 2) << LCD_SEG11_SHIFT) |\
        (((digitData[13] & 0x4) >> 2) << LCD_SEG12_SHIFT) | (((digitData[12] & 0x4) >> 2) << LCD_SEG13_SHIFT) |\
        (((digitData[15] & 0x4) >> 2) << LCD_SEG14_SHIFT) | (((digitData[14] & 0x4) >> 2) << LCD_SEG15_SHIFT);
    
    /** 转换COM2对应的数据*/
    lcdGlassInfo.lcdRegData[LCD_COM2_TYPE] |= (((digitData[1] & 0x2) >> 1) << LCD_SEG0_SHIFT) | (((digitData[0] & 0x2) >> 1) << LCD_SEG1_SHIFT) |\
        (((digitData[3] & 0x2) >> 1) << LCD_SEG2_SHIFT) | (((digitData[2] & 0x2) >> 1) << LCD_SEG3_SHIFT) |\
        (((digitData[5] & 0x2) >> 1) << LCD_SEG4_SHIFT) | (((digitData[4] & 0x2) >> 1) << LCD_SEG5_SHIFT) |\
        (((digitData[7] & 0x2) >> 1) << LCD_SEG6_SHIFT) | (((digitData[6] & 0x2) >> 1) << LCD_SEG7_SHIFT) |\
        (((digitData[9] & 0x2) >> 1) << LCD_SEG8_SHIFT) | (((digitData[8] & 0x2) >> 1) << LCD_SEG9_SHIFT) |\
        (((digitData[11] & 0x2) >> 1) << LCD_SEG10_SHIFT) | (((digitData[10] & 0x2) >> 1) << LCD_SEG11_SHIFT) |\
        (((digitData[13] & 0x2) >> 1) << LCD_SEG12_SHIFT) | (((digitData[12] & 0x2) >> 1) << LCD_SEG13_SHIFT) |\
        (((digitData[15] & 0x2) >> 1) << LCD_SEG14_SHIFT) | (((digitData[14] & 0x2) >> 1) << LCD_SEG15_SHIFT);
        
    /** 转换COM3对应的数据*/
    lcdGlassInfo.lcdRegData[LCD_COM3_TYPE] |= ((digitData[1] & 0x1) << LCD_SEG0_SHIFT) | ((digitData[0] & 0x1) << LCD_SEG1_SHIFT) |\
        ((digitData[3] & 0x1) << LCD_SEG2_SHIFT) | ((digitData[2] & 0x1) << LCD_SEG3_SHIFT) |\
        ((digitData[5] & 0x1) << LCD_SEG4_SHIFT) | ((digitData[4] & 0x1) << LCD_SEG5_SHIFT) |\
        ((digitData[7] & 0x1) << LCD_SEG6_SHIFT) | ((digitData[6] & 0x1)  << LCD_SEG7_SHIFT) |\
        ((digitData[9] & 0x1) << LCD_SEG8_SHIFT) | ((digitData[8] & 0x1) << LCD_SEG9_SHIFT) |\
        ((digitData[11] & 0x1) << LCD_SEG10_SHIFT) | ((digitData[10] & 0x1) << LCD_SEG11_SHIFT) |\
        ((digitData[13] & 0x1) << LCD_SEG12_SHIFT) | ((digitData[12] & 0x1) << LCD_SEG13_SHIFT) |\
        ((digitData[15] & 0x1) << LCD_SEG14_SHIFT) | ((digitData[14] & 0x1) << LCD_SEG15_SHIFT);
        
    /** dot type*/
    if(dotType == LCD_DOT_TYPE1)
    {
        LCD_DisSignal(LCD_DOT1_SG);
    }
    else if(dotType == LCD_DOT_TYPE2)
    {
        LCD_DisSignal(LCD_DOT2_SG);
    }
}
  • 实时刷新LCD显示寄存器
/*!
 * @brief       LCD display
 *
 * @param       data
 *
 * @retval      None
 *
 */
void LCD_GlassDisplay(void)
{
    HAL_LCD_Write(&LCDHandle, LCD_DIGIT_COM0, LCD_DIGIT_COM0_SEG_MASK, lcdGlassInfo.lcdRegData[LCD_COM0_TYPE]);
    HAL_LCD_Write(&LCDHandle, LCD_DIGIT_COM1, LCD_DIGIT_COM1_SEG_MASK, lcdGlassInfo.lcdRegData[LCD_COM1_TYPE]);
    HAL_LCD_Write(&LCDHandle, LCD_DIGIT_COM2, LCD_DIGIT_COM2_SEG_MASK, lcdGlassInfo.lcdRegData[LCD_COM2_TYPE]);
    HAL_LCD_Write(&LCDHandle, LCD_DIGIT_COM3, LCD_DIGIT_COM3_SEG_MASK, lcdGlassInfo.lcdRegData[LCD_COM3_TYPE]);
    
    /** Update the LCD display*/
    HAL_LCD_UpdateDisplayRequest(&LCDHandle);
}
  1. 注意事项
  • 每个COM有两个RAM寄存器(2 x 32bit),分别对应每个像素点
  • LCD接口的时钟使用LSI或者LSE,使用时记得要开启其中一个
  • 有些内阻较高的显示器,需要更长时间驱动才能达到令人满意的对比度,显示不正常时,可开启high drive模式

版权声明:本文为CSDN博主「weiDev101」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_40749320/article/details/122869925

01 什么是段码屏

段码液晶屏(段码屏)是非点阵类的液晶屏,主要是用来替代LED数码管的,比如温度计、计算器、钟表等等,显示的内容基本都是数字,也有些是符号。
段码屏

02 段码屏驱动原理

段码液晶由若干段区(单像素或完整符号)组成,每个区段都包含一层在两根电极之间对齐的液晶分子。液晶在电场的影响下,晶体的排序方向会产生扭曲,因而改变其透光性,从而能够看到显示的內容。要使晶体产生扭曲现象,必须使电极两端的压差大于一定的阈值,才能显示內容。

一般段码屏有两个极,一个为段电极,一个为公共极,如下图所示。SEG1 ~ SEGn为段电极,COM为公共极。上述电场的电压就是施加在段电极和公共极两端的。
Snipaste_2022-02-14_22-03-35
但是段码液晶显示屏不像数码管那样,施以一定大小的直流正向电压就能显示,如果在段码屏SEG和COM两端加直流电压,将会导致液态晶体不可逆的损坏。

需要某段液晶显示的,那么就在某段液晶的SEG和COM两端施加一定压差的交流电,如果不需要液晶显示的话,那么也需要在SEG和COM两端施加电压,不过所施加电压的压差很小或者为零即可。
Snipaste_2022-02-14_22-22-00

03 段码屏驱动实现

  1. LCD段码屏三个主要参数
  • 工作电压
    段码液晶屏的操作电压。

  • 占空比(Duty)
    该参数也称作COM数,定义为1 / (给定LCD上的公共端子数)。一般LCD的驱动方式是采用时分动态扫描的方式。所以每个 COM 的有效选通时间与整个扫描周期的比值,即占空比(Duty)是固定的,等于 1/COM 。

  • 偏置(BIAS)
    驱动LCD时使用的电压等级。定义为1 / (用于驱动LCD显示器的电压等级数 - 1)

  1. 基本驱动方法
    如液晶屏的工作电压约为3.3V,只要给液晶两个电机间施加约3.3V的电压差(如COM端为3.3V,SEG端为0V),并间隔适当的时间,将两个电极的电压反转(如COM端为0V,SEG端为3.3V)即可让液晶显示。
    Snipaste_2022-02-14_22-22-56
    而液晶不显示时,保证两个电极间的电压差为0V(如COM为3.3V,SEG为3.3V),并且间隔适当的时间反转两极的电压(如COM为0V,SEG为0V)。
    Snipaste_2022-02-14_22-22-09
  2. 驱动方案
  • MCU没有LCD驱动外设
    使用IO口直接驱动的方式,偏置比只能选择1/2,这种方式需要在COM口加上拉、下拉各1个电阻,阻值约为100KΩ - 200KΩ。
  • MCU+专用的LCD显示驱动芯片
    常用的驱动芯片有HT1621、HT1622,对应的偏压比为1/3、1/4。
  • MCU带有LCD外设
    如STM32L073系列芯片。

硬件设计

以带有LCD外设的MCU驱动YR1433段码屏为例。

STM32L0外设LCD

  1. 主要特性
  • 帧速率可调节。
  • 占空比支持静态、1/2、1/3、1/4和1/8。
  • 偏置支持静态、1/2、1/3和1/4。
  • 双缓冲存储器,允许应用程序随时更新LCD_RAM寄存器的数据
  • 对比度可调节
  • 支持闪烁功能
  1. LCD控制器框图
    Snipaste_2022-02-14_22-39-10
  2. LCD外设的时钟源
  • 32.768KHz低速外部时钟(LSE)
  • 32.768KHz低速内部时钟(LSI)
  • 高速外部时钟(HSE)的分频,最大支持1MHz

YR1433段码液晶屏

Snipaste_2022-02-15_20-46-51

  1. YR1433的参数
工作电压 DUTY BIAS
3.2V 1/4 1/3
  1. 段码屏MAP图
PIN 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
COM1 COM1 1F 1A 2F 2A 3F 3A 4F 4A 5F 5A 6F 6A 7F 7A 8F 8A K2 S S1 T2
COM2 COM2 1G 1B 2G 2B 3G 3B 4G 4B 5G 5B 6G 6B 7G 7B 8G 8B K3 T8 S2 T1
COM3 COM3 1E 1C 2E 2C 3E 3C 4E 4C 5C 5C 6E 6C 7E 7C 8E 8C K4 T7 S6 T0
COM4 COM4 1D T3 2D T4 3D T5 4D T6 5D S7 6D S8 7D S4 8D S5 K1 K5 S3 T

固件设计

  1. 固件配置流程
  • 定义端口
    根据MAP图,我们将STM32L073RBT6的引脚功能分配如下。
#define LCD_SEG0_PIN                    GPIO_PIN_1
#define LCD_SEG1_PIN                    GPIO_PIN_2
#define LCD_SEG2_PIN                    GPIO_PIN_3
#define LCD_SEG3_PIN                    GPIO_PIN_6
#define LCD_SEG4_PIN                    GPIO_PIN_7
#define LCD_SEG5_PIN                    GPIO_PIN_0
#define LCD_SEG6_PIN                    GPIO_PIN_1
#define LCD_SEG7_PIN                    GPIO_PIN_3
#define LCD_SEG8_PIN                    GPIO_PIN_4
#define LCD_SEG9_PIN                    GPIO_PIN_5
#define LCD_SEG10_PIN                   GPIO_PIN_10
#define LCD_SEG11_PIN                   GPIO_PIN_11
#define LCD_SEG12_PIN                   GPIO_PIN_12
#define LCD_SEG13_PIN                   GPIO_PIN_13
#define LCD_SEG14_PIN                   GPIO_PIN_14
#define LCD_SEG15_PIN                   GPIO_PIN_15
#define LCD_SEG16_PIN                   GPIO_PIN_8
#define LCD_SEG17_PIN                   GPIO_PIN_15
#define LCD_SEG18_PIN                   GPIO_PIN_0
#define LCD_SEG19_PIN                   GPIO_PIN_1
#define LCD_SEG20_PIN                   GPIO_PIN_2
#define LCD_SEG_GPIOA_PORT              GPIOA
#define LCD_SEG_GPIOB_PORT              GPIOB
#define LCD_SEG_GPIOC_PORT              GPIOC
#define LCD_SEG_AF                      GPIO_AF1_LCD

#define LCD_COM0_PIN                    GPIO_PIN_8
#define LCD_COM1_PIN                    GPIO_PIN_9
#define LCD_COM2_PIN                    GPIO_PIN_10
#define LCD_COM3_PIN                    GPIO_PIN_9
#define LCD_COM0_2_GPIO_PORT            GPIOA
#define LCD_COM3_GPIO_PORT              GPIOB
#define LCD_COM_AF                      GPIO_AF1_LCD
  • 初始化寄存器
void LCD_GlassInit(void)
{
    LCDHandle.Instance                  = LCD;
    LCDHandle.Init.Prescaler            = LCD_PRESCALER_4;
    LCDHandle.Init.Divider              = LCD_DIVIDER_16;
    LCDHandle.Init.Duty                 = LCD_DUTY_1_4;
    LCDHandle.Init.Bias                 = LCD_BIAS_1_3;
    LCDHandle.Init.VoltageSource        = LCD_VOLTAGESOURCE_INTERNAL;
    LCDHandle.Init.Contrast             = LCD_CONTRASTLEVEL_5;
    LCDHandle.Init.DeadTime             = LCD_DEADTIME_0;
    LCDHandle.Init.PulseOnDuration      = LCD_PULSEONDURATION_7;
    LCDHandle.Init.BlinkMode            = LCD_BLINKMODE_OFF;
    LCDHandle.Init.BlinkFrequency       = LCD_BLINKFREQUENCY_DIV8;
    LCDHandle.Init.MuxSegment           = LCD_MUXSEGMENT_DISABLE;
    /* YR1433 LCD glass need open high drive*/
    LCDHandle.Init.HighDrive            = LCD_HIGHDRIVE_1;
    
    __HAL_LCD_RESET_HANDLE_STATE(&LCDHandle);
    
    HAL_LCD_MspInit(&LCDHandle);
    
    HAL_LCD_Init(&LCDHandle);
    
    HAL_LCD_Clear(&LCDHandle);
}
  • 用LSE作为LCD时钟源并初始化端口
void HAL_LCD_MspInit(LCD_HandleTypeDef *hlcd)
{
    GPIO_InitTypeDef  GPIO_InitStruct;
    RCC_OscInitTypeDef  RCC_OscInitStruct;
    
    if(hlcd->Instance == LCD)
    {
        __HAL_RCC_PWR_CLK_ENABLE();
        
        HAL_PWR_EnableBkUpAccess();
        
        __HAL_RCC_BACKUPRESET_FORCE();
        __HAL_RCC_BACKUPRESET_RELEASE();
        
        /** Enable LCD LSE Clock*/
        RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;
        RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
        RCC_OscInitStruct.LSEState = RCC_LSE_ON;
        if(HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
        {
            Error_Handler();
        }
        __HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_LSE);
        
        LCD_SEG_GPIOA_CLK_ENABLE();
        LCD_SEG_GPIOB_CLK_ENABLE();
        LCD_SEG_GPIOC_CLK_ENABLE();
        
        LCD_COM0_2_GPIO_CLK_ENABLE();
        LCD_COM3_GPIO_CLK_ENABLE();
        
        /** Configure peripheral GPIO*/
        GPIO_InitStruct.Pin         = LCD_SEG0_PIN | LCD_SEG1_PIN | LCD_SEG2_PIN \
                                    | LCD_SEG3_PIN | LCD_SEG4_PIN | LCD_SEG17_PIN;
        GPIO_InitStruct.Mode        = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull        = GPIO_NOPULL;
        GPIO_InitStruct.Speed       = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate   = LCD_SEG_AF;
        HAL_GPIO_Init(LCD_SEG_GPIOA_PORT, &GPIO_InitStruct);
        
        GPIO_InitStruct.Pin         = LCD_SEG5_PIN | LCD_SEG6_PIN | LCD_SEG7_PIN \
                                    | LCD_SEG8_PIN | LCD_SEG9_PIN | LCD_SEG10_PIN \
                                    | LCD_SEG11_PIN | LCD_SEG12_PIN | LCD_SEG13_PIN \
                                    | LCD_SEG14_PIN | LCD_SEG15_PIN | LCD_SEG16_PIN;
        HAL_GPIO_Init(LCD_SEG_GPIOB_PORT, &GPIO_InitStruct);
        
        GPIO_InitStruct.Pin         = LCD_SEG18_PIN | LCD_SEG19_PIN;
        HAL_GPIO_Init(LCD_SEG_GPIOC_PORT, &GPIO_InitStruct);
        
        GPIO_InitStruct.Pin         = LCD_COM0_PIN | LCD_COM1_PIN | LCD_COM2_PIN;
        GPIO_InitStruct.Alternate   = LCD_COM_AF;
        HAL_GPIO_Init(LCD_COM0_2_GPIO_PORT, &GPIO_InitStruct);
        
        GPIO_InitStruct.Pin         = LCD_COM3_PIN;
        GPIO_InitStruct.Alternate   = LCD_COM_AF;
        HAL_GPIO_Init(LCD_COM3_GPIO_PORT, &GPIO_InitStruct);
        
        LCD_CLK_ENABLE();
    }
}
  1. 固件应用实例
    限于篇幅,以下只列举简单的数字显示应用,其他部分的应用,同学可以自行类推研究下。
    在上述配置完成后,接下来编写应用层的函数。
  • 根据段码屏的定义我们可以得到以下MAP信息。
/*!
 * @brief   GLASS LCD MAPPING(YR1433)
 *          LCD allows to display informations on GAS Meter 8-segment digits:
 *
 * 单价 阀开 阀关 透支 异常 *{[][][][]}请换电池 余额不足 WIFI 灯
         1     2     3     4     5    6      7     8
   剩余 ---   ---   ---   ---   ---   ---   ---   ---    日期
       |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
   累计 ---   ---   ---   ---   ---   ---   ---   ---    时间
       |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
   环境 ---   ---   ---   ---   --- 。--- 。---   ---    元/m3
 */
  • 其中数字字符段(暂时排除符号段)的信号关系如下(参考前述的段码屏MAP图)。
/*!  
 * @brief An LCD character coding is based on the following matrix:
 * COM              0   1   2   3
 * SEG(n)         { F , G , E , D }
 * SEG(n+1)       { A , B , C , T/S}
 * n <= 14
 *
 * @brief The character '8' for example is:
    ------------------------------------
 * LSB            { 1 , 1 , 1 , 1 }
 * MSB            { 1 , 1 , 1 , 0 }
    ------------------------------------
 * '8' = 0xEF   hex
 */
  • 定义数字列表
const uint16_t numberMap[16] =
{
    /** 0    1    2    3    4    5    6    7    8    9    a    b    c    d    e    f*/
    0xEB,  0x60,  0xC7,  0xE5,  0x6C,  0xAD,  0xAF,  0xE0,  0xEF,  0xED,  0xEE,  0x2F,  0x8B,  0x67,  0x8F,  0x8E
};
  • 定义寄存器和偏移量
#define fastAbs(n)                      n >= 0 ? n : -n
#define ASCII_CHAR_0                    0x30

#define LCD_COM0                        LCD_RAM_REGISTER0
#define LCD_COM1                        LCD_RAM_REGISTER2
#define LCD_COM2                        LCD_RAM_REGISTER4
#define LCD_COM3                        LCD_RAM_REGISTER6
#define LCD_COM4                        LCD_RAM_REGISTER8

#define LCD_SEG0_SHIFT                  0
#define LCD_SEG1_SHIFT                  1
#define LCD_SEG2_SHIFT                  2
#define LCD_SEG3_SHIFT                  3
#define LCD_SEG4_SHIFT                  4
#define LCD_SEG5_SHIFT                  5
#define LCD_SEG6_SHIFT                  6
#define LCD_SEG7_SHIFT                  7
#define LCD_SEG8_SHIFT                  8
#define LCD_SEG9_SHIFT                  9
#define LCD_SEG10_SHIFT                 10
#define LCD_SEG11_SHIFT                 11
#define LCD_SEG12_SHIFT                 12
#define LCD_SEG13_SHIFT                 13
#define LCD_SEG14_SHIFT                 14
#define LCD_SEG15_SHIFT                 15
#define LCD_SEG16_SHIFT                 16
#define LCD_SEG17_SHIFT                 17
#define LCD_SEG18_SHIFT                 18
#define LCD_SEG19_SHIFT                 19

#define LCD_COM_NUM                     4
#define LCD_SEG_NUM                     20

typedef struct
{
    uint32_t lcdRegData[LCD_COM_NUM];
} LCD_GLASS_T;
  • 浮点数字应用程序
/** public variables */
LCD_GLASS_T lcdGlassInfo;

/*!
 * @brief       LCD data convert
 *
 * @param       data
 *
 * @retval      None
 *
 */
static void LCD_GlassConvert(uint8_t data)
{
    uint16_t character = 0;
    uint8_t i = 0;
    uint8_t j = 0;
    
    switch(data)
    {
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            character = numberMap[data - ASCII_CHAR_0];
        break;
        
        default:
            character = 0x00;
            break;
    }
    
    /** 分离出单个字符的MSB 4bit和LSB 4bit*/
    for(i = 4, j = 0; j < 2; i -= 4, j++)
    {
        digit[j] = (character >> i) & 0x0F;
    }
}

/*!
 * @brief       LCD glass write float number
 *
 * @param       number
 *
 * @param       dotType
 *
 * @retval      None
 *
 */
void LCD_GlassWriteFloat(float data, LCD_DOT_T dotType)
{
    char character[20];
    int i, j = 0;
    
    sprintf(character, "%09.3f", fastAbs(data));
    
    for(i = 0; i < strlen(character); i++)
    {
        if(data < 0)
        {
            if(i == 0)
            {
                LCD_GlassConvert((uint8_t)character[i]);
            }
            else if(character[i] == '.')
            {
                LCD_GlassConvert((uint8_t)character[i + 1]);
                i += 1;
            }
            else
            {
                LCD_GlassConvert((uint8_t)character[i]);
            }
        }
        else if(character[i] == '.')
        {
            LCD_GlassConvert((uint8_t)character[i + 1]);
            i += 1;
        }
        else
        {
            LCD_GlassConvert((uint8_t)character[i]);
        }
        digitData[j] = digit[0];
        digitData[j + 1] = digit[1];
        j += 2;
    }
    /** 转换COM0对应的数据, 每个LSB和MSB的最高位*/
    lcdGlassInfo.lcdRegData[LCD_COM0_TYPE] |= (((digitData[1] & 0x8) >> 3) << LCD_SEG0_SHIFT) | (((digitData[0] & 0x8) >> 3) << LCD_SEG1_SHIFT) |\
        (((digitData[3] & 0x8) >> 3) << LCD_SEG2_SHIFT) | (((digitData[2] & 0x8) >> 3) << LCD_SEG3_SHIFT) |\
        (((digitData[5] & 0x8) >> 3) << LCD_SEG4_SHIFT) | (((digitData[4] & 0x8) >> 3) << LCD_SEG5_SHIFT) |\
        (((digitData[7] & 0x8) >> 3) << LCD_SEG6_SHIFT) | (((digitData[6] & 0x8) >> 3) << LCD_SEG7_SHIFT) |\
        (((digitData[9] & 0x8) >> 3) << LCD_SEG8_SHIFT) | (((digitData[8] & 0x8) >> 3) << LCD_SEG9_SHIFT) |\
        (((digitData[11] & 0x8) >> 3) << LCD_SEG10_SHIFT) | (((digitData[10] & 0x8) >> 3) << LCD_SEG11_SHIFT) |\
        (((digitData[13] & 0x8) >> 3) << LCD_SEG12_SHIFT) | (((digitData[12] & 0x8) >> 3) << LCD_SEG13_SHIFT) |\
        (((digitData[15] & 0x8) >> 3) << LCD_SEG14_SHIFT) | (((digitData[14] & 0x8) >> 3) << LCD_SEG15_SHIFT);
    
    /** 转换COM1对应的数据*/
    lcdGlassInfo.lcdRegData[LCD_COM1_TYPE] |= (((digitData[1] & 0x4) >> 2) << LCD_SEG0_SHIFT) | (((digitData[0] & 0x4) >> 2) << LCD_SEG1_SHIFT) |\
        (((digitData[3] & 0x4) >> 2) << LCD_SEG2_SHIFT) | (((digitData[2] & 0x4) >> 2) << LCD_SEG3_SHIFT) |\
        (((digitData[5] & 0x4) >> 2) << LCD_SEG4_SHIFT) | (((digitData[4] & 0x4) >> 2) << LCD_SEG5_SHIFT) |\
        (((digitData[7] & 0x4) >> 2) << LCD_SEG6_SHIFT) | (((digitData[6] & 0x4) >> 2) << LCD_SEG7_SHIFT) |\
        (((digitData[9] & 0x4) >> 2) << LCD_SEG8_SHIFT) | (((digitData[8] & 0x4) >> 2) << LCD_SEG9_SHIFT) |\
        (((digitData[11] & 0x4) >> 2) << LCD_SEG10_SHIFT) | (((digitData[10] & 0x4) >> 2) << LCD_SEG11_SHIFT) |\
        (((digitData[13] & 0x4) >> 2) << LCD_SEG12_SHIFT) | (((digitData[12] & 0x4) >> 2) << LCD_SEG13_SHIFT) |\
        (((digitData[15] & 0x4) >> 2) << LCD_SEG14_SHIFT) | (((digitData[14] & 0x4) >> 2) << LCD_SEG15_SHIFT);
    
    /** 转换COM2对应的数据*/
    lcdGlassInfo.lcdRegData[LCD_COM2_TYPE] |= (((digitData[1] & 0x2) >> 1) << LCD_SEG0_SHIFT) | (((digitData[0] & 0x2) >> 1) << LCD_SEG1_SHIFT) |\
        (((digitData[3] & 0x2) >> 1) << LCD_SEG2_SHIFT) | (((digitData[2] & 0x2) >> 1) << LCD_SEG3_SHIFT) |\
        (((digitData[5] & 0x2) >> 1) << LCD_SEG4_SHIFT) | (((digitData[4] & 0x2) >> 1) << LCD_SEG5_SHIFT) |\
        (((digitData[7] & 0x2) >> 1) << LCD_SEG6_SHIFT) | (((digitData[6] & 0x2) >> 1) << LCD_SEG7_SHIFT) |\
        (((digitData[9] & 0x2) >> 1) << LCD_SEG8_SHIFT) | (((digitData[8] & 0x2) >> 1) << LCD_SEG9_SHIFT) |\
        (((digitData[11] & 0x2) >> 1) << LCD_SEG10_SHIFT) | (((digitData[10] & 0x2) >> 1) << LCD_SEG11_SHIFT) |\
        (((digitData[13] & 0x2) >> 1) << LCD_SEG12_SHIFT) | (((digitData[12] & 0x2) >> 1) << LCD_SEG13_SHIFT) |\
        (((digitData[15] & 0x2) >> 1) << LCD_SEG14_SHIFT) | (((digitData[14] & 0x2) >> 1) << LCD_SEG15_SHIFT);
        
    /** 转换COM3对应的数据*/
    lcdGlassInfo.lcdRegData[LCD_COM3_TYPE] |= ((digitData[1] & 0x1) << LCD_SEG0_SHIFT) | ((digitData[0] & 0x1) << LCD_SEG1_SHIFT) |\
        ((digitData[3] & 0x1) << LCD_SEG2_SHIFT) | ((digitData[2] & 0x1) << LCD_SEG3_SHIFT) |\
        ((digitData[5] & 0x1) << LCD_SEG4_SHIFT) | ((digitData[4] & 0x1) << LCD_SEG5_SHIFT) |\
        ((digitData[7] & 0x1) << LCD_SEG6_SHIFT) | ((digitData[6] & 0x1)  << LCD_SEG7_SHIFT) |\
        ((digitData[9] & 0x1) << LCD_SEG8_SHIFT) | ((digitData[8] & 0x1) << LCD_SEG9_SHIFT) |\
        ((digitData[11] & 0x1) << LCD_SEG10_SHIFT) | ((digitData[10] & 0x1) << LCD_SEG11_SHIFT) |\
        ((digitData[13] & 0x1) << LCD_SEG12_SHIFT) | ((digitData[12] & 0x1) << LCD_SEG13_SHIFT) |\
        ((digitData[15] & 0x1) << LCD_SEG14_SHIFT) | ((digitData[14] & 0x1) << LCD_SEG15_SHIFT);
        
    /** dot type*/
    if(dotType == LCD_DOT_TYPE1)
    {
        LCD_DisSignal(LCD_DOT1_SG);
    }
    else if(dotType == LCD_DOT_TYPE2)
    {
        LCD_DisSignal(LCD_DOT2_SG);
    }
}
  • 实时刷新LCD显示寄存器
/*!
 * @brief       LCD display
 *
 * @param       data
 *
 * @retval      None
 *
 */
void LCD_GlassDisplay(void)
{
    HAL_LCD_Write(&LCDHandle, LCD_DIGIT_COM0, LCD_DIGIT_COM0_SEG_MASK, lcdGlassInfo.lcdRegData[LCD_COM0_TYPE]);
    HAL_LCD_Write(&LCDHandle, LCD_DIGIT_COM1, LCD_DIGIT_COM1_SEG_MASK, lcdGlassInfo.lcdRegData[LCD_COM1_TYPE]);
    HAL_LCD_Write(&LCDHandle, LCD_DIGIT_COM2, LCD_DIGIT_COM2_SEG_MASK, lcdGlassInfo.lcdRegData[LCD_COM2_TYPE]);
    HAL_LCD_Write(&LCDHandle, LCD_DIGIT_COM3, LCD_DIGIT_COM3_SEG_MASK, lcdGlassInfo.lcdRegData[LCD_COM3_TYPE]);
    
    /** Update the LCD display*/
    HAL_LCD_UpdateDisplayRequest(&LCDHandle);
}
  1. 注意事项
  • 每个COM有两个RAM寄存器(2 x 32bit),分别对应每个像素点
  • LCD接口的时钟使用LSI或者LSE,使用时记得要开启其中一个
  • 有些内阻较高的显示器,需要更长时间驱动才能达到令人满意的对比度,显示不正常时,可开启high drive模式

版权声明:本文为CSDN博主「weiDev101」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_40749320/article/details/122869925

生成海报
点赞 0

weiDev101

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

STM32F1系列的ADC配置

目录 ADC引脚 注入通道与规则通道 1. 注入通道 2. 规则通道 ​ADC时钟 ADC的触发事件 ​ ADC的运行模式 1. 单次转换模式 2. 连续转换模式 3. 扫描模式  ADC的中断 ADC的时钟 RCC

STM32实现SPI通信1

目录 SPI的结构框图 STM32的SPI模块 从选择NSS管理 软件NSS 硬件NSS 时钟信号的相位与极性 数据帧格式 状态标志 SPI中断 STM32F103RCT6的SPI1引脚​ SPI模块引脚配置的GPIO模式

STM32实现软件I2C通信

目录 I2C协议 1. 空闲状态 2. 开始信号 3. 停止信号 4. 应答信号ACK 5. 数据的有效性 6. 数据传输 I2C的实现 IIC_Init II2_START​ IIC_Stop​ IIC_Wait_Ack​

STM32的DMA操作

目录 DMA概述 STM32中的DMA DMA的通道 DMA处理 数据传输方向 DMA_CCRx寄存器 DMA通道x配置寄存器(DMA_CCRx)(x 1...7) 仲裁器 DMA通道x配置寄存器(DMA_CCRx)(x