段式液晶驱动之HT1621

技术经验 dingxiao 阅读数:5066 2019年7月31日 09:31

段式液晶驱动之HT1621

##### 0x01-段式液晶

段式液晶以超低功耗、价格低廉、驱动便利等特点,使它成为众多消费产品显示的首选。

0x02-HT1621

HOLTEK公司的HT1621是驱动段式液晶常用的芯片,它是一款128 点内存映象和多功能的LCD驱动器。HT1621 的软件配置特性使它适用于多种LCD应用场合包括LCD模块和显示子系统,用于连接主控制器和HT1621的管脚只需4 或5 pin,HT1621 还提供节电命令用于降低系统功耗。

0x03-驱动

TB买了一块段式液晶已配置HT1621。

驱动HT1621选择了手头上有的几块ESP8266,ESP8266驱动HT1621挺适合,测试连线:

 D5--->CS
 D6--->WR
 D7--->DA
 供电:3.3V

驱动调试参考网址:

 1)https://github.com/anxzhu/segment-lcd-with-ht1621
 
 2)https://github.com/5N44P/ht1621-7-seg
0x04-驱动程序优化

在Arduion软件库中下载HT1621库(HT1621.cpp和HT1621.h),该库的维护者是参考网址中的第2个作者,但问题是下载的HT1621驱动并不能直接应用到该段式液晶上,需要对驱动程序进行优化。

对HT1621.cpp的优化,优化点已注释统一标识:@-DX-Test-20190730

 #include <Arduino.h>
 #include "HT1621.h"
 
 HT1621::HT1621(){
   _buffer[0] = 0x00;
   _buffer[1] = 0x00;
   _buffer[2] = 0x00;
   _buffer[3] = 0x00;
 }
 
 void HT1621::begin(int cs_p,int wr_p,int data_p,int backlight_p)
 {
  pinMode(cs_p, OUTPUT);
  pinMode(wr_p, OUTPUT);
  pinMode(data_p, OUTPUT);
  pinMode(backlight_p, OUTPUT);
  _cs_p=cs_p;
  _wr_p=wr_p;
  _data_p=data_p;
  _backlight_p=backlight_p;
  _backlight_en=true;
  config();
 
 }
 
 void HT1621::begin(int cs_p,int wr_p,int data_p)
 {
  pinMode(cs_p, OUTPUT);
  pinMode(wr_p, OUTPUT);
  pinMode(data_p, OUTPUT);
  _cs_p=cs_p;
  _wr_p=wr_p;
  _data_p=data_p;
  _backlight_en = false;
  config();
 }
 
 void HT1621::wrDATA(unsigned char data, unsigned char cnt) {
  unsigned char i;
  for (i = 0; i < cnt; i++) {
   digitalWrite(_wr_p, LOW);
   delayMicroseconds(4);
   if (data & 0x80) {
    digitalWrite(_data_p, HIGH);
   }
   else
   {
    digitalWrite(_data_p, LOW);
   }
   digitalWrite(_wr_p, HIGH);
   delayMicroseconds(4);
   data <<= 1;
  }
 }
 void HT1621::wrclrdata(unsigned char addr, unsigned char sdata)
 {
  addr <<= 2;
  digitalWrite(_cs_p, LOW);
  wrDATA(0xa0, 3);
  wrDATA(addr, 6);
  wrDATA(sdata, 8);
  digitalWrite(_cs_p, HIGH);
 }
 
 void HT1621::display()
 {
  wrCMD(LCDON);
 }
 
 void HT1621::noDisplay()
 {
  wrCMD(LCDOFF);
 }
 
 void HT1621::wrone(unsigned char addr, unsigned char sdata)
 {
  addr <<= 2;
  digitalWrite(_cs_p, LOW);
  wrDATA(0xa0, 3);
  wrDATA(addr, 6);
  wrDATA(sdata, 8);
  digitalWrite(_cs_p, HIGH);
 }
 void HT1621::backlight()
 {
  if (_backlight_en)
   digitalWrite(_backlight_p, HIGH);
  delay(1);
 }
 void HT1621::noBacklight()
 {
  if(_backlight_en)
   digitalWrite(_backlight_p, LOW);
  delay(1);
 }
 void HT1621::wrCMD(unsigned char CMD) {  //100
  digitalWrite(_cs_p, LOW);
  wrDATA(0x80, 4);
  wrDATA(CMD, 8);
  digitalWrite(_cs_p, HIGH);
 }
 void HT1621::config()
 {
  wrCMD(BIAS);
  wrCMD(RC256);
  wrCMD(SYSDIS);
  wrCMD(WDTDIS1);
  wrCMD(SYSEN);
  wrCMD(LCDON);
 
 }
 void HT1621::wrCLR(unsigned char len) {
  unsigned char addr = 0;
  unsigned char i;
  for (i = 0; i < len; i++) {
   wrclrdata(addr, 0x00);
   addr = addr + 2;
  }
 }
 void HT1621::setBatteryLevel(int level) {
  // zero out the previous (otherwise the or couldn't be possible)
   _buffer[0] &= 0x7F;
   _buffer[1] &= 0x7F;
   _buffer[2] &= 0x7F;
 
  switch(level){
   case 3: // battery on and all 3 segments
    _buffer[0] |= 0x80;
   case 2: // battery on and 2 segments
    _buffer[1] |= 0x80;
   case 1: // battery on and 1 segment
    _buffer[2] |= 0x80;
   case 0: // battery indication off
   default:
    break;
  }
 
  update();
 }
 
 
 void HT1621::clear(){
 
  wrCLR(16);
 
 }
 
 /*******************************以下函数需要根据LCD原理图进行修改*****
 
 DX
 1-->可以先使用wrone函数进行测试,测试的主要目的是确定数值显示的正确性
 
 *************************************************/
 
 void HT1621::update(){ // takes the buffer and puts it straight into the driver
   // the buffer is backwards with respect to the lcd. could be improved
 
   wrone(0, _buffer[0]);
   wrone(2, _buffer[1]);
   wrone(4, _buffer[2]);
   wrone(6, _buffer[3]);
 
 }
 
 void HT1621::print(long num){
  if(num > 999999) // basic checks
   num = 999999; // clip into 999999
  if(num < -99999) // basic checks
   num = -99999; // clip into -99999
 
  /*char localbuffer[7]; //buffer to work with in the function
  snprintf(localbuffer,7, "%6li", num); // convert the decimal into string  DX-根据LCD原理图修改-20190730*/
 
  char localbuffer[5]; //buffer to work with in the function
  snprintf(localbuffer,5, "%4li", num); // convert the decimal into string
 
  /*for(int i=0; i<6; i++){  -DX-根据LCD原理图修改-20190730  */
     for(int i=0; i<4; i++){
   /*_buffer[i] &= 0x80; // mask the first bit, used by batter and decimal point*/
   _buffer[i] &= 0x10; // mask the first bit, used by batter and decimal point
   switch(localbuffer[i]){ // map the digits to the seg bits
 
    //@-DX-根据LCD原理图修改-20190730
    case '0':
     _buffer[i] |= 0xeb;
     break;
    case '1':
     _buffer[i] |= 0x0a;
     break;
    case '2':
     _buffer[i] |= 0xad;
     break;
    case '3':
     _buffer[i] |= 0x8f;
     break;
    case '4':
     _buffer[i] |= 0x4e;
     break;
    case '5':
     _buffer[i] |= 0xc7;
     break;
    case '6':
     _buffer[i] |= 0xe7;
     break;
    case '7':
     _buffer[i] |= 0x8a;
     break;
    case '8':
     _buffer[i] |= 0xef;
     break;
    case '9':
     _buffer[i] |= 0xcf;
     break;
    case '-':
     _buffer[i] |= 0x04;
     break;
 
    }
   }
 
   update();
 
 }
 
 void HT1621::print(float num){
  // could be smarter and actually check how many
  // non zeros we have in the decimals
  print(num, 3);
 }
 
 void HT1621::print(float num, int precision){
  if(num > 999999) // basic checks
   num = 999999; // clip into 999999
  if(num < -99999) // basic checks
   num = -99999; // clip into -99999
  if(precision > 3 && num > 0)
   precision = 3; // if positive max precision allowed = 3
  else if(precision > 2 && num < 0)
   precision = 2;// if negative max precision allowed = 2
  if(precision < 0)
   precision = 0; // negative precision?!
 
  long ingegerpart;
  ingegerpart = ((long)(num*pow(10,precision)));
 
  print(ingegerpart); // draw the integerized number
  setdecimalseparator(precision); // draw the decimal point
 
  update();
 }
 
 void HT1621::setdecimalseparator(int decimaldigits) {
   // zero out the eight bit
   /*_buffer[3] &= 0x7F;
   _buffer[4] &= 0x7F;
   _buffer[5] &= 0x7F;  DX-根据LCD原理图修改-20190730*/
 
   _buffer[3] &= 0xeb;
   _buffer[4] &= 0xeb;
   _buffer[5] &= 0xeb;
 
  if( decimaldigits <= 0 || decimaldigits > 3){
   return;
  }
 

  /*_buffer[6-decimaldigits] |= 0x80; DX-根据LCD原理图修改-20190730*/
     _buffer[6-decimaldigits] |= 0x10;
 
  }
 
 char * HT1621::floatToString(char * outstr, float value, int places, int minwidth=0, bool rightjustify=false)
 {
  int digit;
  float tens = 0.1;
  int tenscount = 0;
  int i;
  float tempfloat = value;
  int c = 0;
  int charcount = 1;
  int extra = 0;
  float d = 0.5;
  if (value < 0)
  d *= -1.0;
  for (i = 0; i < places; i++)
  d/= 10.0;    
  tempfloat +=  d;
  if (value < 0)
  tempfloat *= -1.0;
  while ((tens * 10.0) <= tempfloat) {
   tens *= 10.0;
   tenscount += 1;
  }
  if (tenscount > 0)
  charcount += tenscount;
  else
  charcount += 1;
  if (value < 0)
  charcount += 1;
  charcount += 1 + places;
  minwidth += 1;
  if (minwidth > charcount){        
   extra = minwidth - charcount;
   charcount = minwidth;
  }
  if (extra > 0 and rightjustify) {
   for (int i = 0; i< extra; i++) {
    outstr[c++] = ' ';
   }
  }
  if (value < 0)
  outstr[c++] = '-';
  if (tenscount == 0)
  outstr[c++] = '0';
  for (i=0; i< tenscount; i++) {
   digit = (int) (tempfloat/tens);
   itoa(digit, &outstr[c++], 10);
   tempfloat = tempfloat - ((float)digit * tens);
   tens /= 10.0;
  }
  if (places > 0)
  outstr[c++] = '.';
  for (i = 0; i < places; i++) {
   tempfloat *= 10.0;
   digit = (int) tempfloat;
   itoa(digit, &outstr[c++], 10);
   // once written, subtract off that digit
   tempfloat = tempfloat - (float) digit;
  }
  if (extra > 0 and not rightjustify) {
   for (int i = 0; i< extra; i++) {
    outstr[c++] = ' ';
   }
  }
  outstr[c++] = '\0';
  return outstr;
 }
 
 void  HT1621::dispnum_dx(float num){/*传入显示的数据,最高位为小数点和电量显示,显示数据为0.001-99999.9*/
  char buffer1[12];
 
  floatToString(buffer1,num,4);
  String buffer=buffer1;
  int dpposition;
 
  dpposition=buffer.indexOf('.');/*寻找小数点位置  取前七位 因为最多显示七位*/
  /*为4 整数 如1234.
  //3    一位小数 123.4
  //2    两位小数 12.34
  //1    三位小数 1.234 */
 
 
     /*Test = String(dpposition);*/
 
  unsigned  int i;
  for(i=0;i<5;i++){
   
   if(buffer[i]=='0'){
    buffer[i]=0xeb;
   }
   else if(buffer[i]=='1') {
    buffer[i]=0x0a;
   }
   else if (buffer[i]=='2'){
    buffer[i]=0xad;
   }
   else if (buffer[i]=='3'){
    buffer[i]=0x8f;
   }
   else if(buffer[i]=='4') {
    buffer[i]=0x4e;
   }
   else if(buffer[i]=='5') {
    buffer[i]=0xc7;
   }
   else if(buffer[i]=='6') {
    buffer[i]=0xe7;
   }
   else if (buffer[i]=='7'){
    buffer[i]=0x8a;
   }
   else if (buffer[i]=='8'){
    buffer[i]=0xef;
   }
   else if (buffer[i]=='9'){
    buffer[i]=0xcf;
   }
   else if (buffer[i]=='.'){
    buffer[i]=0x10;
   }
  }
 
 
  switch (dpposition){
  case  3: /*123.4*/
   _buffer[0] = buffer[0];
   _buffer[1] = buffer[1];
   _buffer[2] = buffer[2];
   _buffer[3] = buffer[4]|0x10;
   break;
  case  2: /*12.34*/
   _buffer[0] = buffer[0];
   _buffer[1] = buffer[1];
   _buffer[2] = buffer[3]|0x10;
   _buffer[3] = buffer[4];
   break;
  case  1:/*1.234*/
   _buffer[0] = buffer[0];
   _buffer[1] = buffer[2]|0x10;
   _buffer[2] = buffer[3];
   _buffer[3] = buffer[4];
   break;
 
   default:
   break;
  }
 
  update();
 }
 
0x04-需要注意的点

1)HT1621作为LCD的驱动芯片,需要知晓芯片与LCD的连接原理图,根据原理图列出需要显示字符的编码。

2)在测试初期可以先使用lcd.wrone(0,0xc7)函数对一个显示区域进行调试,这样可以快速理解显示规则,对后期优化驱动提供较好的实践支持。



captcha
    暂无评论