STM32F103学习笔记(4)——LCD段码屏HT1621使用
一、简介
HT1621 是 128 点 内存映象和多功能的 LCD 驱动器 HT1621 的软件配置特性使它适用于多种 LCD 应用场合包括 LCD 模块和显示子系统。用于连接主控制器和 HT1621 的管脚只有 4 或 5 条 HT1621,还有一个节电命令用于降低系统功耗。
二、字符显示原理
液晶管点亮和熄灭原理分别为在对应的RAM地址中写1和写0。首先需要清楚所驱动控制的液晶的COM-SEG对应关系,然后需要了解HT1621的32×4RAM地址映射。
例如要控制的液晶的装脚成品图部分如下:
着重看一个液晶数码管,我们了解原理就行。可以看到图3中是第2个液晶数码管,有7段,分别为A,B,C,D,E,F,G。也就分别为下面COM\SEG地址对应关系图中的2A,2B,2C,2D,2E,2F,2G。
液晶的显示字符的部分COM公共端输出口和SEG段输出口的分布如下表所示,同理我们可以看到例如:2D对应(SEG5,COM0),2E对应(SEG5,COM1),2F对应(SEG5,COM2),2A对应(SEG5,COM3),2C对应(SEG4,COM1),2G对应(SEG4,COM2),2B对应(SEG4,COM3)。
HT1621的RAM 地址映射如下图所示:
可以清楚的看到要控制液晶段对应SEG号作为6位地址,COM号对应作为4位数据写入,此时注意4位数据的高低位。写数据到RAM命令格式为:101+6位RAM地址+4位数据,其中RAM地址为SEG序号。
例如我们在图3的第二个液晶数码管上显示数字,首先我们根据图3得到地址映射关系,先写入地址SEG4中的四位数据(COM3,COM2,COM1,COM0),再写如地址SEG5中的四位数据(COM3,COM2,COM1,COM0),对应关系如下:
所以如果在图3中显示“5”,则在显示的液晶段对应地址上写1,不显示写0,如下图所示。所以SEG4地址应写入的数据为0110 ,SEG5地址应写入数据1101。
三、命令格式
HT1621 可以用软件设置 两种模式的命令可以配置 HT1621 和传送 LCD 所显示的数据。HT1621 的配置模式称为命令模式,命令模式类型码为 100。命令模式包括一个系统配置命令,一个系统频率选择命令,一个 LCD 配置命令,一个声音频率选择命令,一个定时器/WDT设置命令和一个操作命令。数据模式包括 READ,WRITE 和 READ-MODIFY-WRITE 操作,下表是数据和命令模式类型码表。
操作 | 模式 | 类型码 |
---|---|---|
READ | 数据 | 110 |
WRITE | 数据 | 101 |
READ-MODIFY-RITE | 数据 | 101 |
COMMAND | 命令 | 100 |
模式命令应在数据或命令传送前运行。如果执行连续的命令,命令模式代码即 100,将被忽略。当系统在不连续命令模式或不连续地址数据模式下,管脚/CS 应设为 “1” 而且先前的操作模式将复位。当管脚/CS 返回 “0” 时,新的操作模式类型码应先运行。
四、时序图及程序流程
五、硬件连接
功能口 | 引脚 |
---|---|
CS | PB.12 |
DATA | PB.14 |
WR | PB.13 |
六、移植文件
注意:延时函数使用了FreeRTOS的vTaskDelay任务延时函数
链接:https://pan.baidu.com/s/1lXWqJIr9Q07al4H3FwNb6Q 提取码:dsuy
将 board_ht1621.c、board_ht1621.h 两个文件加入到工程
6.1 board_ht1621.c
/*********************************************************************
* INCLUDES
*/
#include "FreeRTOS.h"
#include "task.h"
#include "board_ht1621.h"
static void gpioConfig(void);
/*********************************************************************
* PUBLIC FUNCTIONS
*/
/**
@brief 初始化LCD屏,HT1621芯片
@param 无
@return 无
*/
void HT1621_Init(void)
{
gpioConfig();
vTaskDelay(10);
LCD_CS_1();
LCD_DATA_1();
LCD_WR_1();
vTaskDelay(100); // 延时使LCD工作电压稳定
HT1621_WriteCommand(HT1621_SYS_EN); // 打开系统振荡器
HT1621_WriteCommand(HT1621_BIAS); // BIAS 13 4个公共口
HT1621_WriteCommand(HT1621_RC256); // 使用RC_256K系统时钟源,片内RC振荡器
HT1621_WriteCommand(HT1621_WDT_DIS);
HT1621_WriteCommand(HT1621_LCD_ON);
}
/**
@brief 写入命令,命令标志100
@param cmd -[in] 命令数据
@return 无
*/
void HT1621_WriteCommand(uint8_t cmd)
{
uint8_t i;
LCD_CS_0(); // CS = 0
vTaskDelay(1);
// 写入命令标志,DATA:100
LCD_WR_0(); // WR = 0
vTaskDelay(1);
LCD_DATA_1(); // DATA = 1
vTaskDelay(1);
LCD_WR_1(); // WR = 1
vTaskDelay(1);
LCD_WR_0(); // WR = 0
vTaskDelay(1);
LCD_DATA_0(); // DATA = 0
vTaskDelay(1);
LCD_WR_1(); // WR = 1
vTaskDelay(1);
LCD_WR_0(); // WR = 0
vTaskDelay(1);
LCD_DATA_0(); // DATA = 0
vTaskDelay(1);
LCD_WR_1(); // WR = 1
vTaskDelay(1);
// Datasheet中命令后的C8为0
LCD_WR_0(); // WR = 0
vTaskDelay(1);
LCD_DATA_0(); // DATA = 0
vTaskDelay(1);
LCD_WR_1(); // WR = 1
vTaskDelay(1);
// Datasheet中命令后的C7~C0
for(i = 0; i < 8; i++)
{
LCD_WR_0(); // WR = 0
vTaskDelay(1);
if((cmd << i) & 0x80)
{
LCD_DATA_1(); // DATA = 1
}
else
{
LCD_DATA_0(); // DATA = 0
}
vTaskDelay(1);
LCD_WR_1(); // WR = 1
vTaskDelay(1);
}
LCD_CS_1(); // CS = 1
vTaskDelay(1);
}
/**
@brief 写入4位数据,写入数据标志101
@param addr -[in] 写入起始地址
@param data -[in] 写入数据,因为HT1621的数据位4位,所以实际写入数据为参数的后4位
@return 无
*/
void HT1621_WriteData4Bit(uint8_t addr, uint8_t data)
{
uint8_t i;
LCD_CS_0(); // CS = 0
vTaskDelay(1);
// 写入数据标志,DATA:101
LCD_WR_0(); // WR = 0
vTaskDelay(1);
LCD_DATA_1(); // DATA = 1
vTaskDelay(1);
LCD_WR_1(); // WR = 1
vTaskDelay(1);
LCD_WR_0(); // WR = 0
vTaskDelay(1);
LCD_DATA_0(); // DATA = 0
vTaskDelay(1);
LCD_WR_1(); // WR = 1
vTaskDelay(1);
LCD_WR_0(); // WR = 0
vTaskDelay(1);
LCD_DATA_1(); // DATA = 1
vTaskDelay(1);
LCD_WR_1(); // WR = 1
vTaskDelay(1);
// 写入地址,Datasheet中A5~A0
for(i = 0; i < 6; i++)
{
LCD_WR_0(); // WR = 0
vTaskDelay(1);
if((addr << i) & 0x80)
{
LCD_DATA_1(); // DATA = 1
}
else
{
LCD_DATA_0(); // DATA = 0
}
vTaskDelay(1);
LCD_WR_1(); // WR = 1
vTaskDelay(1);
}
// 写入数据,Datasheet中D0~D3
for(i = 0; i < 4; i++)
{
LCD_WR_0(); // WR = 0
vTaskDelay(1);
if((data >> (3 - i)) & 0x01)
{
LCD_DATA_1(); // DATA = 1
}
else
{
LCD_DATA_0(); // DATA = 0
}
vTaskDelay(1);
LCD_WR_1(); // WR = 1
vTaskDelay(1);
}
LCD_CS_1(); // CS = 1
vTaskDelay(1);
}
/**
@brief 写入8位数据,写入数据标志101
@param addr -[in] 写入起始地址
@param data -[in] 写入数据
@return 无
*/
void HT1621_WriteData8Bit(uint8_t addr, uint8_t data)
{
uint8_t i;
LCD_CS_0(); // CS = 0
vTaskDelay(1);
// 写入数据标志,DATA:101
LCD_WR_0(); // WR = 0
vTaskDelay(1);
LCD_DATA_1(); // DATA = 1
vTaskDelay(1);
LCD_WR_1(); // WR = 1
vTaskDelay(1);
LCD_WR_0(); // WR = 0
vTaskDelay(1);
LCD_DATA_0(); // DATA = 0
vTaskDelay(1);
LCD_WR_1(); // WR = 1
vTaskDelay(1);
LCD_WR_0(); // WR = 0
vTaskDelay(1);
LCD_DATA_1(); // DATA = 1
vTaskDelay(1);
LCD_WR_1(); // WR = 1
vTaskDelay(1);
// 写入地址,Datasheet中A5~A0
for(i = 0; i < 6; i++)
{
LCD_WR_0(); // WR = 0
vTaskDelay(1);
if((addr << i) & 0x80)
{
LCD_DATA_1(); // DATA = 1
}
else
{
LCD_DATA_0(); // DATA = 0
}
vTaskDelay(1);
LCD_WR_1(); // WR = 1
vTaskDelay(1);
}
// 写入数据,Datasheet中两组D0~D3
for(i = 0; i < 8; i++)
{
LCD_WR_0(); // WR = 0
vTaskDelay(1);
if((data >> (7 - i)) & 0x01)
{
LCD_DATA_1(); // DATA = 1
}
else
{
LCD_DATA_0(); // DATA = 0
}
vTaskDelay(1);
LCD_WR_1(); // WR = 1
vTaskDelay(1);
}
LCD_CS_1(); // CS = 1
vTaskDelay(1);
}
/*********************************************************************
* LOCAL FUNCTIONS
*/
/**
@brief 引脚初始化
@param 无
@return 无
*/
static void gpioConfig(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(HT1621_CS_CLK|HT1621_DATA_CLK|HT1621_WR_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);
}
/****************************************************END OF FILE****************************************************/
6.2 board_ht1621.h
#ifndef _BOARD_HT1621_H_
#define _BOARD_HT1621_H_
/*********************************************************************
* INCLUDES
*/
#include "stm32f10x.h"
/*********************************************************************
* DEFINITIONS
*/
#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 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 COMMAND_CODE 0x80 // 命令码
#define WRITE_DATA_CODE 0xA0 // 写命令
#define HT1621_BIAS 0x52 // 1/3duty 4com
#define HT1621_SYS_DIS 0x00 // 关振系统荡器和LCD偏压发生器
#define HT1621_SYS_EN 0x02 // 打开系统振荡器
#define HT1621_LCD_OFF 0x04 // 关闭LCD偏压
#define HT1621_LCD_ON 0x06 // 打开LCE偏压
#define HT1621_XTAL 0x28 // 外部接时钟
#define HT1621_RC256 0x30 // 内部时钟
#define HT1621_WDT_DIS 0X0A // 关闭看门狗
#define HT1621_TONE_ON 0x12 // 打开声音输出
#define HT1621_TONE_OFF 0x10 // 关闭声音输出
/*********************************************************************
* API FUNCTIONS
*/
void HT1621_Init(void);
void HT1621_WriteCommand(uint8_t cmd);
void HT1621_WriteData4Bit(uint8_t addr, uint8_t data);
void HT1621_WriteData8Bit(uint8_t addr, uint8_t data);
#endif /* _BOARD_HT1621_H_ */
七、使用例子
/*********************************************************************
* DEFINITIONS
*/
#define TEMP_ADDR 0x00
#define FOURTH_8_MHz_ADDR 0x04
#define FOURTH_8_ADDR 0x08
#define THIRD_8_75us_ADDR 0x0C
#define THIRD_8_ADDR 0x10
#define SECOND_8_POINT_ADDR 0x14
#define SECOND_8_ADDR 0x18
#define FIRST_8_50us_ADDR 0x1C
#define FIRST_8_ADDR 0x20
#define FM_SOUND_1_ADDR 0x24
#define SIGNAL_ADDR 0x28
#define DISPLAY_ALARM 0x03
#define DISPLAY_FM 0x04
#define DISPLAY_1_1 0x08
#define DISPLAY_MHZ 0x10
HT1621_Init();
HT1621_WriteData4Bit(TEMP_ADDR, 0x00); // ℃灭
HT1621_WriteData4Bit(SIGNAL_ADDR, 0x00); // 信号灭
HT1621_WriteData4Bit(FM_SOUND_1_ADDR, DISPLAY_ALARM | DISPLAY_FM); // 报警+FM亮
HT1621_WriteData8Bit(FOURTH_8_MHz_ADDR, DISPLAY_MHZ); // MHz亮
/**
@brief LCD屏显示频率
@param frequency -[in] 频率
@return 无
*/
void LCD_ShowFrequency(uint32 frequency)
{
calculateHundreds(frequency); // 百位
calculateTens(frequency); // 十位
calculateOnes(frequency); // 个位
calculateDecimals(frequency); // 小数位
}
/**
@brief 计算频率百位,如87.5MHz,则百位0
@param frequency -[in] 频率
@return 无
*/
static void calculateHundreds(uint32 frequency)
{
if(frequency / 100000 == 1)
{
s_alarm |= DISPLAY_1_1;
HT1621_WriteData4Bit(FM_SOUND_1_ADDR, s_alarm); // 1亮
}
else
{
s_alarm &= 0x07;
HT1621_WriteData4Bit(FM_SOUND_1_ADDR, s_alarm); // 1灭
}
}
/**
@brief 计算频率十位,如87.5MHz,则十位8
@param frequency -[in] 频率
@return 无
*/
static void calculateTens(uint32 frequency)
{
if(frequency / 10000 % 10 == 0)
{
HT1621_WriteData8Bit(FIRST_8_50us_ADDR, 0xAF); // 0
}
else if(frequency / 10000 % 10 == 8)
{
HT1621_WriteData8Bit(FIRST_8_50us_ADDR, 0xEF); // 8
}
else if(frequency / 10000 % 10 == 9)
{
HT1621_WriteData8Bit(FIRST_8_50us_ADDR, 0xED); // 9
}
}
/**
@brief 计算频率个位,如87.5MHz,则个位7
@param frequency -[in] 频率
@return 无
*/
static void calculateOnes(uint32 frequency)
{
if(frequency / 1000 % 10 == 0)
{
HT1621_WriteData8Bit(SECOND_8_POINT_ADDR, 0xBF); // 0.
}
else if(frequency / 1000 % 10 == 1)
{
HT1621_WriteData8Bit(SECOND_8_POINT_ADDR, 0xB0); // 1.
}
else if(frequency / 1000 % 10 == 2)
{
HT1621_WriteData8Bit(SECOND_8_POINT_ADDR, 0xDB); // 2.
}
else if(frequency / 1000 % 10 == 3)
{
HT1621_WriteData8Bit(SECOND_8_POINT_ADDR, 0xF9); // 3.
}
else if(frequency / 1000 % 10 == 4)
{
HT1621_WriteData8Bit(SECOND_8_POINT_ADDR, 0xF4); // 4.
}
else if(frequency / 1000 % 10 == 5)
{
HT1621_WriteData8Bit(SECOND_8_POINT_ADDR, 0x7D); // 5.
}
else if(frequency / 1000 % 10 == 6)
{
HT1621_WriteData8Bit(SECOND_8_POINT_ADDR, 0x7F); // 6.
}
else if(frequency / 1000 % 10 == 7)
{
HT1621_WriteData8Bit(SECOND_8_POINT_ADDR, 0xB8); // 7.
}
else if(frequency / 1000 % 10 == 8)
{
HT1621_WriteData8Bit(SECOND_8_POINT_ADDR, 0xFF); // 8.
}
else if(frequency / 1000 % 10 == 9)
{
HT1621_WriteData8Bit(SECOND_8_POINT_ADDR, 0xFD); // 9.
}
}
/**
@brief 计算频率小数位,如87.5MHz,则小数位5
@param frequency -[in] 频率
@return 无
*/
static void calculateDecimals(uint32 frequency)
{
if(frequency / 100 % 10 == 0)
{
HT1621_WriteData8Bit(THIRD_8_75us_ADDR, 0xAF); // 0
}
else if(frequency / 100 % 10 == 1)
{
HT1621_WriteData8Bit(THIRD_8_75us_ADDR, 0xA0); // 1
}
else if(frequency / 100 % 10 == 2)
{
HT1621_WriteData8Bit(THIRD_8_75us_ADDR, 0xCB); // 2
}
else if(frequency / 100 % 10 == 3)
{
HT1621_WriteData8Bit(THIRD_8_75us_ADDR, 0xE9); // 3
}
else if(frequency / 100 % 10 == 4)
{
HT1621_WriteData8Bit(THIRD_8_75us_ADDR, 0xE4); // 4
}
else if(frequency / 100 % 10 == 5)
{
HT1621_WriteData8Bit(THIRD_8_75us_ADDR, 0x6D); // 5
}
else if(frequency / 100 % 10 == 6)
{
HT1621_WriteData8Bit(THIRD_8_75us_ADDR, 0x6F); // 6
}
else if(frequency / 100 % 10 == 7)
{
HT1621_WriteData8Bit(THIRD_8_75us_ADDR, 0xA8); // 7
}
else if(frequency / 100 % 10 == 8)
{
HT1621_WriteData8Bit(THIRD_8_75us_ADDR, 0xEF); // 8
}
else if(frequency / 100 % 10 == 9)
{
HT1621_WriteData8Bit(THIRD_8_75us_ADDR, 0xED); // 9
}
}
• 由 Leung 写于 2020 年 7 月 14 日
• 参考:STM32液晶显示HT1621驱动原理及程序代码
HT1621B段码 LCD屏驱动 51单片机驱动程序
HT1621测试程序