使用HT1621B驱动段码液晶屏的STM32教程【02】
【STM32】_02_使用HT1621B驱动段码液晶屏
一、简介
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个点)
在编写具体的显示程序时,要根据厂商给出的真值表进行编写,如下图是我所用的,
我的思路是,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