最近做的项目里面使用的控制芯片为TI公司的28035,在控制程序以外,需要用该芯片控制显示LCD屏幕,其中驱动LCD屏幕的是HT1621B芯片,由于这也是我第一次写驱动LCD的程序以及第一次接触1621这款芯片,在网上参考了很多例程,以及对DATASHEET的学习,遇到了一些问题,在下文中会详细介绍整个调试过程。
首先对于HT1621,是一款驱动LCD屏幕常见的驱动芯片,在本项目中使用的是以下封装,48引脚,其中有SEG0~31和COM0~4,一共可以驱动32*4=128个LED灯。
其中CS,RD,WR,DATA ,9、10、11、12这四个引脚是HT1621与MCU之间进行通讯的媒介,在该项目中,由于不需要读LCD屏幕的数据,因此只连接了CS,WR,DATA这三条线,硬件连接原理图如图所示。
可以对比上面的引脚进行分析,以及针对HT1621的DATASHEET进行分析,在本项目中RD引脚没有使用直接置高,14和15引脚悬空,使用HT1621片内的RC振荡电路,而不使用外部时钟。
HT1621芯片右侧接的LCD段码屏,一般情况下是厂家定制好的段码屏,厂家会提供一份原理图,引脚对应图,本项目使用的原理图如下图:
包含各种图案的点亮,一共是32*4,刚好是128个,也是HT1621能够驱动的最大数量的灯管数量。针对上面厂家给的原理图和硬件原理图,需要得到具体每一个SEG和COM对应的灯管,上述表格进行对照后,会得到对应表(因为厂家给的PIN脚和SEG有时候会不对应)
得到上述对应的表格后,才能精确地驱动每一个灯,对于显示数字的数码管来说:根据原理图中的映射关系,一共八个数字数码管可以得到:
1----亮,0----灭
数字0:A B C D E F为1 其他都为0 对应SEG2:1101 SEG3:0111
高位在前 因此改为16进制为 0x0b 0x0e
数字1:B C为1 其他都为0 对应SEG2:0000 SEG3:0110 0x00 0x06
数字2:A B G E D 为1 其他为0 对应SEG2:1110 SEG3:0011 0x07 0x0c
数字3:A B C D G为1 其他为0 对应SEG2:1010 SEG3:0111 0x05 0x0e
数字4:F G B C为1 其他为0 对应SEG2:0011 SEG3:0110 0x0c 0x06
数字5:A F G C D为1 其他为0 对应SEG2:1011 SEG3:0101 0x0d 0x0a
数字6:A F G C D E为1 其他为0 对应SEG2:1111 SEG3:0101 0x0f 0x0a
数字7:A B C 为1 其他为0 对应SEG2:0000 SEG3:0111 0x00 0x0e
数字8:A B C D E G F为1 其他为0 对应SEG2:1111 SEG3:0111 0x0f 0x0e
数字9:A B C D F G为1 其他为0 对应SEG2:1011 SEG3:0111 0x0d 0x0e
对于SEG的地址码是由6个二进制位构成 00 0000 一共有31个,可以得到下面的对应表
如果想要点亮数字 1,那么就需要在SEG2的地址写入0x00,而在SEG3写入 0x06,即可点亮。
上述主要叙述了HT1621的基本构造和点亮数字和图案的基本原理,下文叙述HT1621的驱动程序和驱动原理。
对于HT1621,首先需要进行对其进行初始化,即对其写入命令,才能进一步给HT1621写数据点亮各种灯,因此初始化是最关键的一步。
对于HT1621,有四种模式,在本项目中只用到了命令模式和写入模式
针对初始化,需要写入命令集,命令的代码为100,而且存在很多不同的命令,构成了命令表格,完整的命令表格可以通过查阅1621的DATASHEET来查看。
HT1621的初始化也就是对其发送一系列命令的过程,上图的CS相当于使能端,当CS处于低电平时,1621才能跟MCU进行通讯,而WR充当时钟信号,在WR的上升沿时,将DATA的电平状态传送给1621,进行数据传输。
//HT1621的初始化
void HT1621_Init(void)
{
HT1621_CS_H();
HT1621_WR_H();
HT1621_DAT_H();
DelayMs(20);
HT1621_WriteCMD(0x52); //0b1000 0101 0010 1/3duty 4com
DelayMs(1);
HT1621_WriteCMD(0x30); //0b1000 0011 0000 内部时钟
DelayMs(1);
HT1621_WriteCMD(0x00); //0b1000 0000 0000 关振系统荡器和LCD偏压发生器
DelayMs(1);
HT1621_WriteCMD(0x0A); //0b1000 0000 1010 禁止看门狗
DelayMs(1);
HT1621_WriteCMD(0x02); //0b1000 0000 0010 打开系统振荡器
DelayMs(1);
HT1621_WriteCMD(0x06); //0b1000 0000 0110 打开LCD偏压
DelayMs(1);
}
要形成上述的时序图,写入命令的代码为:
void HT1621_SendBit(uchar sdat,uchar cnt)
{
uchar i;
for(i=0;i<cnt;i++)
{
HT1621_WR_L();
HT1621_Delay(DELAY_TEST);
if(sdat&0x80)
{
HT1621_DAT_H();
}
else
{
HT1621_DAT_L();
}
HT1621_Delay(DELAY_TEST);
HT1621_WR_H();
HT1621_Delay(DELAY_TEST);
sdat<<=1;
}
}
对于上述的延时,在网上找到的资料中显示,如果不加延时,数据传输会有一定影响,因此在这里我加上了延时。
void HT1621_WriteCMD(uchar command)
{
HT1621_CS_L();
HT1621_Delay(DELAY_TEST);
HT1621_SendBit(0x80,4);
HT1621_SendBit(command,8);
HT1621_CS_H();
}
此时需要注意的是,对于上述数据手册中,发送命令的全部数据为加起来一定是12位,可以是3+9或者4+8,但是不能小于12位,如果发送的数据位11位,即3+8,会导致下图的时序图,其中从上到下分别为CS WR DATA。
发现时序会乱掉,当发送位数为12位时,时序正常,因此发送命令时3+9和4+8原则上都能成功,在此我使用的是4+8,不同的格式发送的命令码也是不同的,根据位数去计算。
如果初始化成功,那么就可以给1621写入数据了,写入数据的命令码为101
其中储存地址1就是上文中的SEG的地址,而数据就是点亮COM0~3中哪一个,拿上文中举例子, 如果想要点亮数字 1,那么就需要在SEG2的地址写入0x00,而在SEG3写入 0x06,即可点亮。
即发出的DATA为 101 000010 0000 和 101 000011 0110,具体程序代码为:
//addr为写入数据的地址,即对应的SEGx,而写入的数据sdat为数码管亮时对应的数据
void HT1621_WirteData(uchar addr,uchar sdat)
{
//addr<<=2;//因为地址为6位 32个寄存器的地址为6位
//sdat<<=4;//数据为4位 4位对应COM
HT1621_CS_L();
HT1621_SendBit(0xA0,3);//101 写命令
HT1621_SendBit(addr<<=2,6);
HT1621_SendBit(sdat<<=4,4); //一次只发送四位数据,一个地址发送一次,低四位
HT1621_CS_H();
}
需要左移是因为,16进制的情况下地址和数据初始化,如0x02即对应的SEG2是8位,而写入1621的地址是6位,数据只有4位,因此需要左移。
最后写入地址和需要发送的数据,即可点亮LCD上对应的数字。
附上全部代码,包括GPIO的配置:
void UserInitAllGpio(void)
{
EALLOW;
//20230322 for LCD GPIO25、26、27
GpioCtrlRegs.GPAPUD.bit.GPIO25 = 1; // 1禁止上拉 DATA引脚
GpioCtrlRegs.GPAMUX2.bit.GPIO25 = 0;
GpioCtrlRegs.GPADIR.bit.GPIO25 = 1; // 1 output
GpioDataRegs.GPACLEAR.bit.GPIO25 = 1;
GpioCtrlRegs.GPAPUD.bit.GPIO26 = 1; // 1禁止上拉 WR引脚
GpioCtrlRegs.GPAMUX2.bit.GPIO26 = 0;
GpioCtrlRegs.GPADIR.bit.GPIO26 = 1; // 1 output
GpioDataRegs.GPACLEAR.bit.GPIO26 = 1;
GpioCtrlRegs.GPAPUD.bit.GPIO27 = 1; // 1禁止上拉 CS引脚
GpioCtrlRegs.GPAMUX2.bit.GPIO27 = 0;
GpioCtrlRegs.GPADIR.bit.GPIO27 = 1; // 1 output
GpioDataRegs.GPACLEAR.bit.GPIO27 = 1;
EDIS;
}
#define DELAY_TEST 10
void Delay_(uchar us)
{
while(--us);
}
void DelayMs(Uint16 iMs)
{
Uint16 i,j;
for(i=0;i<iMs;i++)
for(j=0;j<65;j++)
{
Delay_(1);
}
}
//引脚定义
void HT1621_DAT_H(void)
{
EALLOW;
GpioDataRegs.GPASET.bit.GPIO25 = 1;
EDIS;
}
void HT1621_DAT_L(void)
{
EALLOW;
GpioDataRegs.GPACLEAR.bit.GPIO25 = 1;
EDIS;
}
void HT1621_WR_H(void)
{
EALLOW;
GpioDataRegs.GPASET.bit.GPIO26 = 1;
EDIS;
}
void HT1621_WR_L(void)
{
EALLOW;
GpioDataRegs.GPACLEAR.bit.GPIO26 = 1;
EDIS;
}
void HT1621_CS_H(void)
{
EALLOW;
GpioDataRegs.GPASET.bit.GPIO27 = 1;
EDIS;
}
void HT1621_CS_L(void)
{
EALLOW;
GpioDataRegs.GPACLEAR.bit.GPIO27 = 1;
EDIS;
}
void HT1621_Delay(Uint32 n)
{
while(n--);
}
void HT1621_SendBit(uchar sdat,uchar cnt)
{
uchar i;
for(i=0;i<cnt;i++)
{
HT1621_WR_L();
HT1621_Delay(DELAY_TEST);
if(sdat&0x80)
{
HT1621_DAT_H();
}
else
{
HT1621_DAT_L();
}
HT1621_Delay(DELAY_TEST);
HT1621_WR_H();
HT1621_Delay(DELAY_TEST);
sdat<<=1;
}
}
//addr为写入数据的地址,即对应的SEGx,而写入的数据sdat为数码管亮时对应的数据
void HT1621_WirteData(uchar addr,uchar sdat)
{
//addr<<=2;//因为地址为6位 32个寄存器的地址为6位
//sdat<<=4;//数据为4位 4位对应COM
HT1621_CS_L();
HT1621_SendBit(0xA0,3);//101 写命令
HT1621_SendBit(addr<<=2,6);
HT1621_SendBit(sdat<<=4,4); //一次只发送四位数据,一个地址发送一次,低四位
HT1621_CS_H();
}
void HT1621_WriteAllData(void)
{
uchar i;
HT1621_CS_L();
HT1621_SendBit(0xA0,3);
HT1621_SendBit(0x00<<2,6);
for(i=0;i<32;i++)
{
HT1621_SendBit(0x00,8);
}
HT1621_CS_H();
}
void HT1621_WriteCMD(uchar command)
{
HT1621_CS_L();
HT1621_Delay(DELAY_TEST);
HT1621_SendBit(0x80,4);
HT1621_SendBit(command,8);
HT1621_CS_H();
}
void HT1621_Init(void)
{
HT1621_CS_H();
HT1621_WR_H();
HT1621_DAT_H();
DelayMs(20);
HT1621_WriteCMD(0x52); //0b1000 0101 0010 1/3duty 4com
DelayMs(1);
HT1621_WriteCMD(0x30); //0b1000 0011 0000 内部时钟
DelayMs(1);
HT1621_WriteCMD(0x00); //0b1000 0000 0000 关振系统荡器和LCD偏压发生器
DelayMs(1);
HT1621_WriteCMD(0x0A); //0b1000 0000 1010 禁止看门狗
DelayMs(1);
HT1621_WriteCMD(0x02); //0b1000 0000 0010 打开系统振荡器
DelayMs(1);
HT1621_WriteCMD(0x06); //0b1000 0000 0110 打开LCD偏压
DelayMs(1);
}
主函数:
{
UserInitAllGpio();
HT1621_Init();
DelayMs(50);
HT1621_WriteAllData();//清屏
while(1){
HT1621_WirteData(SEG_addr[2],Data_lowSEG[0]);
DelayMs(1);
HT1621_WirteData(SEG_addr[3],Data_highSEG[0]);
DelayMs(0xcff);
HT1621_WirteData(SEG_addr[2],Data_lowSEG[1]);
DelayMs(1);
HT1621_WirteData(SEG_addr[3],Data_highSEG[1]);
DelayMs(0xcff);
HT1621_WirteData(SEG_addr[2],Data_lowSEG[2]);
DelayMs(1);
HT1621_WirteData(SEG_addr[3],Data_highSEG[2]);
DelayMs(0xcff);
HT1621_WirteData(SEG_addr[2],Data_lowSEG[3]);
DelayMs(1);
HT1621_WirteData(SEG_addr[3],Data_highSEG[3]);
DelayMs(0xcff);
HT1621_WirteData(SEG_addr[2],Data_lowSEG[4]);
DelayMs(1);
HT1621_WirteData(SEG_addr[3],Data_highSEG[4]);
DelayMs(0xcff);
HT1621_WirteData(SEG_addr[2],Data_lowSEG[5]);
DelayMs(1);
HT1621_WirteData(SEG_addr[3],Data_highSEG[5]);
DelayMs(0xcff);
HT1621_WirteData(SEG_addr[2],Data_lowSEG[6]);
DelayMs(1);
HT1621_WirteData(SEG_addr[3],Data_highSEG[6]);
DelayMs(0xcff);
HT1621_WirteData(SEG_addr[2],Data_lowSEG[7]);
DelayMs(1);
HT1621_WirteData(SEG_addr[3],Data_highSEG[7]);
DelayMs(0xcff);
HT1621_WirteData(SEG_addr[2],Data_lowSEG[8]);
DelayMs(1);
HT1621_WirteData(SEG_addr[3],Data_highSEG[8]);
DelayMs(0xcff);
HT1621_WirteData(SEG_addr[2],Data_lowSEG[9]);
DelayMs(1);
HT1621_WirteData(SEG_addr[3],Data_highSEG[9]);
DelayMs(0xcff);
}
}
数组中的数据根据对应需要点亮数字的高低电平来设置文章来源:https://www.toymoban.com/news/detail-497521.html
uchar SEG_addr[]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1AD,0x1E,0x1F};
uchar Data_highSEG[]={ 0x0e,0x06,0x0c,0x0e,0x06,0x0a,0x0a,0x0e,0x0e,0x0e};
uchar Data_lowSEG[]={0x0b,0x00,0x07,0x05,0x0c,0x0d,0x0f,0x00,0x0f,0x0d};
最终实现的是数字从0到9的循环显示文章来源地址https://www.toymoban.com/news/detail-497521.html
到了这里,关于DSP28035驱动HT1621B显示段码LCD屏的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!