使用HT1621B驱动段码液晶屏的STM32教程【02】

【STM32】_02_使用HT1621B驱动段码液晶屏

  • 一、简介
  • 1.1 什么是段码液晶屏
  • 1.2 HT1621B
  • 二、HT1621B的驱动电路
  • 三、驱动程序
  • 3.1 写一位数据到HT1621B
  • 3.2 向HT1621B相应地址写入数据,来控制段码屏显示相应字符
  • 3.3 向HT1621B写入命令
  • 3.4 ht1621.h
  • 3.5 ht1621.c
  • 一、简介

    1.1 什么是段码液晶屏

    详细的介绍可以看这位博主的帖子:那头时空。
    着急的朋友就不用看了,简单来说段码液晶屏的驱动需要交流电,有些MCU会自带LCD液晶驱动,而通常我们则会采用像HT1621B这样的LCD驱动器来控制段码屏。

    1.2 HT1621B


    简单来说,HT1621B最多可以控制具有128个显示点位的段码屏,与MCU之间的通信仅需三线即可CS片选线,低电平有效、WR写时钟线,上升沿写入、DATA线,串行数据输入/输出
    详细的内容请看数据手册:HT1621B数据手册。

    二、HT1621B的驱动电路

    最简单的连接方式就是将段码屏SEG0-SEG13(我这个屏SEG脚有14个,这种屏都是定制的,具体你要用几个脚要看你定制的屏有几个) 和HT1621B的SEG0-SEG13按照顺序对应的连到一起 (如果你的硬件是乱序相连的,那你就只能在软件上进行一下转换了,也就是你写入数据的地址可能不是从0开始的,也可能不连续),将段码屏COM0-COM3和HT1621B的COM0-COM3按照顺序对应的连到一起 。将HT1621B的CS、WR、DATA与MCU的3个IO口(任选)相连,这样硬件的连接就完成了。

    三、驱动程序

    3.1 写一位数据到HT1621B

    时序图如下

    从图中可看出,在片选为低电平且写时钟线的上升沿时才能写入数据。

    /**
     * @brief   写数据到HT1621
     * @param   Data:要写入的数据bits
     * @param   num:传送数据位数
     * @retval  无
     */
    void write_bit_ht1621(uint8_t data, uint8_t num)
    {
        uint8_t i;
    
        for (i = 0; i < num; i++)
        {
            LCD_WR_0();                     /* 拉低WR脚  Data线上的数据在WR脚位上升沿写入   */
            if (((data & 0x80) >> 7) == 1)  /* 如果当前bit为1,就拉高DATA引脚 */
            {
                LCD_DATA_1();
            }
            else if (((data & 0x80) >> 7) == 0)  /* 如果当前bit为0,就拉低DATA引脚 */
            {
                LCD_DATA_0();
            }
            LCD_WR_1();                     /* 当前这一bit写完,拉高WR脚,为下次WR为上升沿做准备 */
            data <<= 1;                     /* 当前位用完了,移到下一位继续上述动作 */
        }
    }
    

    3.2 向HT1621B相应地址写入数据,来控制段码屏显示相应字符

    首先,我们先了解一下HT1621B的地址映射,如下图

    这个图的意思就是SEG0就是地址0,程序中传地址时你可以写为十进制的0也可以写为十六进制的0,每个地址位可以控制4个位,对应LCD屏上4个点的显示(在下面数码管中,a、b、c、d就代表4个点)
    ![在这里插入图片描述](https://i3.wp.com/img-blog.csdnimg.cn/direct/3939a5a9cfab49c797012b87318c39b0.png
    在编写具体的显示程序时,要根据厂商给出的真值表进行编写,如下图是我所用的,
    请添加图片描述
    我的思路是,14个地址,一个地址4bit,那就相当于7个8bit数据,所以我定义了1个char型的数组有7个元素。HT1621B是支持连续写的。

    /* 真值表顺序  FGEABCD */
    /* 0,1,2,3,4,5,6,7,8,9,灭 */
    const char num[] = {0x5F, 0x06, 0x3D, 0x2F, 0x66, 0x6B, 0x7B, 0x0E, 0x7F, 0x6F, 0x00};
    char dispnum[7] = {0x00};
    
    /**
     * @brief   将要显示的数据写到HT1621
     * @param   Addr:写入初始地址
     * @param   Data:要写入的数据
     * @param   Mode:COMMAND_CODE(0X80) / WRITE_DATA_CODE(0XA0)
     * @retval  无
     */
    void write_ht1621(uint8_t addr, uint8_t data)
    {
        LCD_CS_0();
        write_bit_ht1621(WRITE_DATA_CODE, 3); /* 命令模式或数据模式 */
        write_bit_ht1621(addr << 2, 6);       /* 写入地址 */
        write_bit_ht1621(data, 8);             /* 写入数据  */
        LCD_CS_1();
    }
    

    这里可能会有人有疑问,时序图中数据是低位先传,而我们这里却是高位先传,不用担心我们在编写真值表时已经将低位放在了高位,所以完全没问题。

    3.3 向HT1621B写入命令



    从时序图中可以看出,命令模式码有3位,而命令代码有9位,但最后一位0、1都行,有些朋友喜欢直接舍弃不管它,而我则是把C8和3位命令码放在一起 C0和C7放在一起注意不同的排序方法,命令代码的值不同,数据手册有详细的命令代码

    /*  HT1621 命令定义 */
    #define COMMAND_CODE                0x80        /* 命令码 */
    #define WRITE_DATA_CODE            0xA0        /* 写命令 */
    
    #define HT1621_SYS_EN               0x02        /* 打开系统振荡器 */
    #define HT1621_RC256                	0x30        /* 内部时钟 */
    #define HT1621_BIAS                 	0x52        /* 1/3duty 4com LCD偏置 */
    
    /**
     * @brief   写命令到HT1621
     * @param   Cmd:命令
     * @retval  无
     */
    void write_cmd_ht1621(uint8_t cmd)
    {
        LCD_CS_0();                        /* 拉低CS脚  CS拉低时WR有效 */
        write_bit_ht1621(COMMAND_CODE, 4); /* 写入命令标志100   0x80  */
        write_bit_ht1621(cmd, 8);          /* 写入命令数据 */
        LCD_CS_1();                        /* 拉高CS脚 */
    }
    

    从真值表来看,有些单个符号在和数码管显示混在了一起,这时只需使用位与和位或就可以单独控制它了,可以看下lcd_display(),看下各位就有思路了。

    3.4 ht1621.h

    #ifndef __HT1621_H
    #define __HT1621_H
    
     #include "stm32f10x.h"
    
    /*  HT1621硬件连接端口定义 */
    #define HT1621_CS_PORT              GPIOB                     /* GPIO端口 */
    #define HT1621_CS_CLK               RCC_APB2Periph_GPIOB      /* GPIO端口时钟 */
    #define HT1621_CS_PIN               GPIO_Pin_12               /* 定义HT1621的CS管脚 */
        
    #define HT1621_DATA_PORT            GPIOB                     /* GPIO端口 */
    #define HT1621_DATA_CLK             RCC_APB2Periph_GPIOB      /* GPIO端口时钟 */
    #define HT1621_DATA_PIN             GPIO_Pin_14               /* 定义HT1621的DATA管脚 */
    
    #define HT1621_WR_PORT              GPIOB                     /* GPIO端口 */
    #define HT1621_WR_CLK               RCC_APB2Periph_GPIOB      /* GPIO端口时钟 */
    #define HT1621_WR_PIN               GPIO_Pin_13               /* 定义HT1621的WR管脚 */
    
    #define HT1621_RD_PORT              GPIOB                     /* GPIO端口 */
    #define HT1621_RD_CLK               RCC_APB2Periph_GPIOB      /* GPIO端口时钟 */
    #define HT1621_RD_PIN               GPIO_Pin_15               /* 定义HT1621的WR管脚 */
    
    #define LCD_CS_0()                  GPIO_ResetBits(HT1621_CS_PORT, HT1621_CS_PIN) 
    #define LCD_CS_1()                  GPIO_SetBits(HT1621_CS_PORT, HT1621_CS_PIN) 
    #define LCD_DATA_0()                GPIO_ResetBits(HT1621_DATA_PORT, HT1621_DATA_PIN) 
    #define LCD_DATA_1()                GPIO_SetBits(HT1621_DATA_PORT, HT1621_DATA_PIN)
    #define LCD_WR_0()                  GPIO_ResetBits(HT1621_WR_PORT, HT1621_WR_PIN) 
    #define LCD_WR_1()                  GPIO_SetBits(HT1621_WR_PORT, HT1621_WR_PIN)
    #define LCD_RD_0()                  GPIO_ResetBits(HT1621_RD_PORT, HT1621_RD_PIN) 
    #define LCD_RD_1()                  GPIO_SetBits(HT1621_RD_PORT, HT1621_RD_PIN)
    
    /*  HT1621 命令定义 */
    #define COMMAND_CODE                0x80        /* 命令码 */
    #define WRITE_DATA_CODE             0xA0        /* 写命令 */
    
    #define HT1621_SYS_EN               0x02        /* 打开系统振荡器 */
    #define HT1621_RC256                0x30        /* 内部时钟 */
    #define HT1621_BIAS                 0x52        /* 1/3duty 4com LCD偏置 */
    
    #define HT1621_SYS_DIS              0x00        /* 关振系统荡器和LCD偏压发生器 */
    #define HT1621_LCD_OFF              0x20        /* 关闭LCD偏压 */
    #define HT1621_LCD_ON               0x03        /* 打开LCD偏压 */
    #define HT1621_XTAL                     0x14        /* 外部接时钟 */
    #define HT1621_WDT_DIS              0X05        /* 关闭看门狗 */
    #define HT1621_TONE_ON              0x09        /* 打开声音输出 */
    #define HT1621_TONE_OFF             0x08        /* 关闭声音输出 */
    
    #define  LCD_ON                     0x06        /* 打开LCD 偏压发生器1000     0000 0 11 0  */
    #define  LCD_OFF                    0x04        /* 关闭LCD显示 */ 
    
    /* 设置变量寄存器函数 */
    #define SBI(x, y)       (x |= (1 << y))   /*置位寄器x的第y位*/
    #define CBI(x, y)     (x &= ~(1 <<y ))  /*清零寄器x的第y位*/  
    
    static void gpio_config(void);   
    static void read_gpio_config(void);
    
    void ht1621_init(void);
    void ht1621_all_on(uint8_t num);                 /* 全部显示 */
    void ht1621_all_off(uint8_t num);                /* 清除显示 */
    void HT1621_all_on_num(uint8_t num, uint8_t xx); /* 全部点亮,显示相同数字 */
    void lcd_display(long int t, char p, char s6, char s7, char s8, char s9, char s10, char s11,char s12)  ;
    uint8_t read_ht1621(uint8_t addr);               /* 读HT1621返回的数据 */
    
    void lcd_on(void);                                /* 液晶显示打开 */
    void lcd_off(void);                               /* 液晶显示关闭 */
    void write_ht1621(uint8_t addr, uint8_t data);    /* 将要显示的数据写到HT1621 */
    void write_cmd_ht1621(uint8_t cmd);               /* 写命令到HT1621 */
    void write_bit_ht1621(uint8_t data, uint8_t num); /* 写数据到HT1621 */
    
    #endif /* _BOARD_HT1621_H */
    
    

    3.5 ht1621.c

    /**
    ****************************************************************************
    * @file     ht1621.c
    * @author   BJX
    * @version  V1.0
    * @date     2024-3-23
    * @brief    HT1621段码液晶屏驱动
    ****************************************************************************
    * @attention
    *
    *   平台:STM F103C8
    *
    ****************************************************************************
    */
    #include "./ht1621/ht1621.h"
    #include "./delay/delay.h"
    #include "./usart/usart.h"
    
    
    uint8_t ram_buf[14] = {0};  /* ht1621缓存数组 */
    
    /* 真值表顺序AFEHBGCD */
    /*0,1,2,3,4,5,6,7,8,9,*/
    const char num[] = {0xeb, 0x0a, 0xad, 0x8f, 0x4e, 0xc7, 0xe7, 0x8a, 0xef, 0xcf};
    char dispnum[7] = {0x00, 0x00, 0x00, 0x00, 0x00};
    
    /**
     * @brief   lcd段码液晶屏显示函数
     * @param   无
     * @retval  无
     */
    void lcd_display(long int t, char p, char s6, char s7, char s8, char s9, char s10, char s11,char s12)   
    {
        unsigned char i;
    
        dispnum[0] = 0x11;
        dispnum[1] = num[t / 1000];
        dispnum[2] = num[(t / 100) % 10];
        dispnum[3] = num[(t / 10) % 10];
        dispnum[4] = num[t%10];
          
        if( dispnum[1] == num[0])
        {
            dispnum[1] = num[10];
        }
    
        switch (p)
        {
        case 1:
            SBI(dispnum[0], 5); /* S2点亮 */
            break;
    
        case 2:
            SBI(dispnum[0], 5); /* S2点亮 */
            SBI(dispnum[0], 6); /* S3点亮 */
            break;
    
        case 3:
            SBI(dispnum[0], 5); /* S2点亮 */
            SBI(dispnum[0], 6); /* S3点亮 */
            SBI(dispnum[0], 7); /* S4点亮 */
            break;
    
        case 4:
            SBI(dispnum[0], 5); /* S2点亮 */
            SBI(dispnum[0], 6); /* S3点亮 */
            SBI(dispnum[0], 7); /* S4点亮 */
            SBI(dispnum[0], 3); /* S5点亮 */
            break;
    
        default:
            CBI(dispnum[0], 5);
            CBI(dispnum[0], 6);
            CBI(dispnum[0], 7);
            CBI(dispnum[0], 3);
            break;
        }
        if (s6 == 1)
            SBI(dispnum[0], 2);
        else
            CBI(dispnum[0], 2);
    
        if (s7 == 1)
            SBI(dispnum[1], 7);
        else
            CBI(dispnum[1], 7);
    
        if (s8 == 1)
            SBI(dispnum[2], 7);
        else
            CBI(dispnum[2], 7);
        
        if (s9 == 1)
            SBI(dispnum[3], 7);
        else
            CBI(dispnum[3], 7);
    
        if (s10 == 1)
            SBI(dispnum[4], 7);
        else
            CBI(dispnum[4], 7);
    
        if (s11 == 1)
            SBI(dispnum[5], 7);
        else
            CBI(dispnum[5], 7);
    
        if (s12 == 1)
            SBI(dispnum[6], 7);
        else
            CBI(dispnum[6], 7);
    
        for (i = 0; i < 7; i++)
        {
            write_ht1621(i * 2, dispnum[i]);
        }
    }
    
    /**
     * @brief   HT1621初始化
     * @param   无
     * @retval  无
     */
    void ht1621_init(void)
    {
        /* 引脚配置相关 */
        gpio_config();
        Delay_ms(500); /* 延时使LCD工作电压稳定 */
    
        /* 配置HT1621 */
        lcd_off();
        write_cmd_ht1621(HT1621_SYS_EN); /* 打开系统振荡器 */
        write_cmd_ht1621(HT1621_RC256);  /* 使用RC_256K系统时钟源,片内RC振荡器 */
        write_cmd_ht1621(HT1621_BIAS);   /* BIAS 13 4个公共口 */
        lcd_on();  
    }
    
    /**
     * @brief   液晶显示关闭
     * @param   无
     * @retval  无
     */
    void lcd_off(void)
    {
        write_cmd_ht1621(LCD_OFF);
    }
    
    /**
     * @brief   液晶显示打开
     * @param   无
     * @retval  无
     */
    void lcd_on(void)
    {
        write_cmd_ht1621(LCD_ON);
    }
    
    /**
     * @brief   清除显示
     * @param   无
     * @retval  无
     */
    void ht1621_all_off(uint8_t num)
    {
        uint8_t i;
        uint8_t addr = 0;
    
        for (i = 0; i < num; i++)
        {
            write_ht1621(addr, 0x00);
            addr += 2;
        }
    }
    
    /**
     * @brief   全部显示
     * @param   无
     * @retval  无
     */
    void ht1621_all_on(uint8_t num)
    {
        uint8_t i;
        uint8_t addr = 0;
        for (i = 0; i < num; i++)
        {
            write_ht1621(addr, 0xff);
            addr += 2;
        }
    }
    
    /**
     * @brief   全部点亮,显示相同数字
     * @param   无
     * @retval  无
     */
    void HT1621_all_on_num(uint8_t num, uint8_t xx)
    {
        uint8_t i;
        uint8_t addr = 0;
        for (i = 0; i < num; i++)
        {
            write_ht1621(addr, xx);
            addr += 2;
        }
    }
    
    
    /**
     * @brief   将要显示的数据写到HT1621
     * @param   Addr:写入初始地址
     * @param   Data:要写入的数据
     * @param   Mode:COMMAND_CODE(0X80) / WRITE_DATA_CODE(0XA0)
     * @retval  无
     */
    void write_ht1621(uint8_t addr, uint8_t data)
    {
        LCD_CS_0();
        write_bit_ht1621(WRITE_DATA_CODE, 3); /* 命令模式或数据模式 */
        write_bit_ht1621(addr << 2, 6);       /* 写入地址 */
        write_bit_ht1621(data, 8);            /* 写入数据  这里是对应单个SEG写的 故而为4 */
        LCD_CS_1();
    }
    
    /**
     * @brief   读HT1621返回的数据
     * @param   Addr:读数据的地址
     * @retval  读到的数据
     */
    uint8_t read_ht1621(uint8_t addr)
    {
        uint8_t data = 0;
        uint8_t i;
    
        LCD_CS_0();
        Delay_us(10);
        write_bit_ht1621(0XC0, 3);      /* 命令模式或数据模式 */
        write_bit_ht1621(addr << 2, 6); /* 写入地址 */
    
        read_gpio_config();
    
        for (i = 0; i < 4; i++)
        {
            LCD_RD_1();
            Delay_us(10);
            
            if (GPIO_ReadInputDataBit(HT1621_DATA_PORT, HT1621_DATA_PIN) == 1)
            {
                data |= 0x08;
            }
            
            LCD_RD_0();
            Delay_us(10);
            
            data >>= 1;
        }
        
        LCD_CS_1();
        Delay_us(10);
    
        gpio_config();
        
        return data;
    }
    
    /**
     * @brief   写数据到HT1621
     * @param   Data:要写入的数据bits
     * @param   num:传送数据位数
     * @retval  无
     */
    void write_bit_ht1621(uint8_t data, uint8_t num)
    {
        uint8_t i;
    
        for (i = 0; i < num; i++)
        {
            LCD_WR_0();                     /* 拉低WR脚  Data线上的数据在WR脚位上升沿写入   */
            if (((data & 0x80) >> 7) == 1)  /* 如果当前bit为1,就拉高DATA引脚 */
            {
                LCD_DATA_1();
            }
            else if (((data & 0x80) >> 7) == 0)  /* 如果当前bit为0,就拉低DATA引脚 */
            {
                LCD_DATA_0();
            }
            LCD_WR_1();                     /* 当前这一bit写完,拉高WR脚,为下次WR为上升沿做准备 */
            data <<= 1;                     /* 当前位用完了,移到下一位继续上述动作 */
        }
    }
    
    
    /**
     * @brief   写命令到HT1621
     * @param   Cmd:命令
     * @retval  无
     */
    void write_cmd_ht1621(uint8_t cmd)
    {
        LCD_CS_0();                        /* 拉低CS脚  CS拉低时WR有效 */
        write_bit_ht1621(COMMAND_CODE, 4); /* 写入命令标志100   0x80  */
        write_bit_ht1621(cmd, 8);          /* 写入命令数据 */
        LCD_CS_1();                        /* 拉高CS脚 */
    }
    
    /**
     * @brief   引脚初始化
     * @param   无
     * @retval  无
     */
    static void gpio_config(void)
    {
        GPIO_InitTypeDef GPIO_InitStructure;
    
        RCC_APB2PeriphClockCmd(HT1621_CS_CLK | HT1621_DATA_CLK | HT1621_WR_CLK | HT1621_RD_CLK, ENABLE);
    
        GPIO_InitStructure.GPIO_Pin = HT1621_CS_PIN;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(HT1621_CS_PORT, &GPIO_InitStructure);
    
        GPIO_InitStructure.GPIO_Pin = HT1621_DATA_PIN;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(HT1621_DATA_PORT, &GPIO_InitStructure);
    
        GPIO_InitStructure.GPIO_Pin = HT1621_WR_PIN;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(HT1621_WR_PORT, &GPIO_InitStructure);
    
        GPIO_InitStructure.GPIO_Pin = HT1621_RD_PIN;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(HT1621_RD_PORT, &GPIO_InitStructure);
        
        LCD_CS_1();
        LCD_DATA_1();
        LCD_WR_1();
        LCD_RD_1();
    }
    
    /**
     * @brief   读引脚初始化
     * @param   无
     * @retval  无
     */
    static void read_gpio_config(void)
    {
        GPIO_InitTypeDef GPIO_InitStructure;
    
        GPIO_InitStructure.GPIO_Pin = HT1621_DATA_PIN;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
        GPIO_Init(HT1621_DATA_PORT, &GPIO_InitStructure);
    }
    
    /****************************** END OF FILE ******************************/
    
    

    作者:W99BJX

    物联沃分享整理
    物联沃-IOTWORD物联网 » 使用HT1621B驱动段码液晶屏的STM32教程【02】

    发表评论