How to drive manually HT1621B LCD driver IC?

I should trust myself more often...
So what I did is, I combined all those data bits that I used them into my seg[8] array.
The way I see it, they are disposed as location bits.
I created a (pulsing) number 8 that is using all the segments and is telling me how strong or weak is when displayed fully. And guess what? It is having a very nice contrast.
Next, is a simple task of gathering all the numbers.

  void loop()
  {
    sendData(adr[0],0b0111); //-,c,b,a
    sendData(adr[1],0b1111); //d,e,g,f
    delay(1000);
    Clear();
    delay(1000);
  }

This file has better Clear(), numbers, and refined code.
Test Code that will write 0-9 numbers on all 3 digits of my LCD
LCDB2.ino (8.2 KB)
~
Thank you very much mister @Rintin ! I couldnt make it so far without your starting support !

void n1(int x)
  {
      sendData(adr[x],  0b0110);  //-,c,b,a
  }

You probably want to set sendData(adr[x+1], 0b0000); to avoid stray segments when switching from a different number to 1.

May I bring your attention back to the lookup table...

void n0(int x)
  {
      sendData(adr[x],  0b0111);  //-,c,b,a
      sendData(adr[x+1],0b1101);  //d,e,g,f
  }
uint8_t LutEven[10] =
{
  //   CBA
    0b0111, // 0
...
};

uint8_t LutOdd[10] =
{
  //  DEGF
    0b1101, // 0
...
};
1 Like

If you are trying to tell me something .... put it down plain and clear.
Because Im tired now and I dont see it.
If you're telling me its the same thing, as your Lut array, I agree.

The pattern you use in sendData() are the same pattern that need to go into the lookup table.

That would simplyfy writing a function void setDigit(uint8_t pos, uint8_t value)

void setDigit(uint8_t pos, uint8_t value)
{
  uint8_t addr = pos * 2;
  sendData(addr, LutEven[value]);
  sendData(addr + 1, LutOdd[value]);
}

Challenge to solve !
Everything is working very nice, I even tested all 3 digits at once with some counting and phuuuuhhh, so good ! Good contrast on the LCD - beauty.
But , I want to count --each-- digit at different speed now.
For ex: digit1 at 500ms, digit2 at 300ms, and digit3 at 100ms.
All 3 in the same time.
I managed to change speeds but the "lazy" way.
I'll --update--the last code named LCDB2.ino in post #43 with the latest code version Im having now.Please look into it and think on my challenge how to solve it.
Awesome day my friend ! Super !
Now- its updated.

For every digit you need a variable to store the current value and a variable to store the last time you updated that digit. Take a look at "BlinkWithoutDelay" in the examples.

1 Like

I tried last night, I couldnt sleep right because of it, I tried this morning and
everything boils down to this:

      n0(0);n0(2);n0(4); delay(t); Clear();
      n1(0);n1(2);n1(4); delay(t); Clear();
      n2(0);n2(2);n2(4); delay(t); Clear();

We need to add all 3 numbers at once to the display, like in this example.
But the trick is how to add that timer at different speeds per each digit.
To roll every digit, all at once but at different speed from each other.
I stretched my neurons long and wide and I couldnt find a solution.
Its over my ears.
I made probably 40 programs to solve this problem and every time I failed. So none were good. Either didnt work at all or worked completely wrong and not really in the same time, but 1by1 digit after digit and not in unison. Oh well...
I made circles around your suggested code, "BlinkWithoutDelay" in the examples, I tried everything I could think of and nothing.
I even did a quick and dirty sample code in C#

        int cnt1 = 1, cnt2 = 1, cnt3 = 1;
        private void timer1_Tick(object sender, EventArgs e)
        {
            cnt1+=1; cnt2+=2; cnt3+=3;
            label4.Text = cnt1.ToString() + " | " + cnt2.ToString() + " | " + cnt3.ToString();
            if (cnt1==100) timer1.Stop();
            
        }

And it is indeed running all 3 digits (from a text label) at different speeds.
But...its not the same cheese here at all.
Well, I give it my best at least. It will remain as a challenge for the future generations then- haha. For the younger programmers.

The BlinkWithoutDelay boils down to 2 things:

  • Is it time to blink?
    • Do blink

This is the code for Is it time to blink?:

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;

And here the code for Do blink:

    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }
    digitalWrite(ledPin, ledState);

Now your challenge boils down to:

  • Is it time to update digit 0?
    • Do update digit 0
  • Is it time to update digit 1?
    • Do update digit 1
  • Is it time to update digit 2?
    • Do update digit 2

For Is it time to update digit 2? you can use the code from BlinkWithoutDelay and name the variable previousMillis2.

Do update digit 2 need to do the following:

value2++;
check value2 for overflow;
setDigit(2, value2);
1 Like

I managed to make something...not exactly as I imagined but... good enough.
I was actually thinking at a clock code mechanism how it will update sec,min,hrs, so I had this idea that I walked on it.
The idea is also to keep all 3digits activated in the same time.
Also, another blast from the past, is "keep it slow-run it slow", so doing it slowly is letting me concentrating on the essential problem to solve.
Very challenging !!!! Im glad I made it !
Also thanks for your support mister @Rintin !
Getting good vibes, keeps me running.
Here is my code:

void Update(int num1,int num2,int num3)
  {
      Clear();
      switch (num1) 
      {
      case 0: {n0(0);} break;
      case 1: {n1(0);} break;
      case 2: {n2(0);} break;
      case 3: {n3(0);} break;          
      case 4: {n4(0);} break;
      case 5: {n5(0);} break;
      case 6: {n6(0);} break;
      case 7: {n7(0);} break;
      case 8: {n8(0);} break;
      case 9: {n9(0);} break;
      default: break;
      }

      switch (num2) 
      {
      case 0: {n0(2);} break;
      case 1: {n1(2);} break;
      case 2: {n2(2);} break;
      case 3: {n3(2);} break;
      case 4: {n4(2);} break;
      case 5: {n5(2);} break;
      case 6: {n6(2);} break;
      case 7: {n7(2);} break;
      case 8: {n8(2);} break;
      case 9: {n9(2);} break;
      default: break;
      }

      switch (num3) 
      {
      case 0: {n0(4);} break;
      case 1: {n1(4);} break;
      case 2: {n2(4);} break;
      case 3: {n3(4);} break;
      case 4: {n4(4);} break;
      case 5: {n5(4);} break;
      case 6: {n6(4);} break;
      case 7: {n7(4);} break;
      case 8: {n8(4);} break;
      case 9: {n9(4);} break;
      default: break;
      }
  }

  int cnt1 = 0, cnt2 = 0, cnt3 = 0;
  void CountingTestE() //example of counting 0-9 on all 3 digits: digit1,digit2,digit3 at different speeds
  {
      delay(100);//change speed


      cnt1+=1; 
      if(cnt1==10)
      { 
        cnt1=0;//reset
        cnt2+=1;
        if(cnt2==10)
        {
          cnt2=0;//reset
          cnt3+=1;
          if(cnt3==10)
          {
            cnt3=0;//reset
          }
        }
      }

      
      //n0(0);n0(2);n0(4); delay(t1); Clear();
      Update(cnt1,cnt2,cnt3);
      //if (cnt1==9)cnt1=-1; if(cnt2==9)cnt2=-1; if(cnt3==9)cnt3=-1;
  }

The code is very easy to understand and read, no problem there.
The problem was adapting to my problem.
The biggest problem was to deal with the n0(0); which is a void function so in this cases, we need to go around it and use something else that can be easily incremented, read, modify. My n0(0); is nothing of those. And thats 1 of many problems I had to circle around.
I just told you my line of thought. I like your helpful spirit though. Keep it up.

The problem with this is that the pattern it is writing is in the function name. That's why I suggested the setDigit() function.

Here the entire file with everything I did so far:
LCDB2.ino (10.0 KB)

Hi @Rintin (and happy new year 2025)
I mentioned in the OP, that I want to drive this HT1621+LCD through a Parallel EEPROM ATMEGA. Specifically AT28C64 . Because I have 10pcs of them.
The code so far, in this discussion, is really great, truly.
I also dig a few weeks ago into output from UNO R3 commands from a C# application, also here in the arduino forums.
So, as you can see everything is pointing towards --really-- bitbanging this HT1621 IC.
Now, in the code we did so far, I encounter this function, that Im not very sure what is doing under the hood.
I want to manually drive the pins instead of this shiftOutBits function.
I also understand it is like a rats nest of wires inside it.

shiftOutBits(3, 0b100);

My impression and mostly my guess it is like this; I also read the shiftOutBits page:
We have 9bits to shift in the config command
sendCommand(0b000000110); // LCD on
The shiftOutBits function only shifts 1byte or 8bits.
I believe you write 9bits because of the HT1621 bit diagram. Where that X is 'dont care' bit but it must be always included.
sendCommand(0b000000110); // LCD on
image
So this part is clarified for me.
sendCommand "Shifts out a byte of data one bit at a time"
In my mind, I see it like this:
for each of those bits, 0b000000110, it will shift 1bit at a time through Data wire.
First 0, then 1, then 1, then 0, then 0, then 0, then 0, then 0, then 0. Correct?


But they say it is sending --1 byte-- (8bits at a time) how it can do that? In a single clock perhaps? or each bit per 1 clock? I could not visualize this detail from HT1621 datasheet.
Please make for me a descriptive , step by step, line by line, of how the internal code of this sendCommand function should work.
Thank you !

Not sure what you are trying to do here. Replacing the Arduino with state machines and 74xx logic chips?

Please take a look at post #8.

Please don't confuse shiftOutBits() with the framework provided shiftout().

Exactly. The self implemented shiftOutBits() also really does shift out 9 bits.

The order is reversed.

This. One bit per 1 clock cycle.

See post #8. There you can find the timing diagram and sendCommand().

you made that function ... right.
my mistake.

Please take a look at post #8 for the implementation of shiftOutBits().

my dear friend @Rintin
Please take a look on this "disassembled" code of the
sendCommand(0b000000110); // LCD on
command line.
Please correct it where you see it's wrong.
This is my chance to learn a little bit more about shifting bits !!!
I never had the chance or the project for it.
Thank you.

   //most basic segment driver

//HT1621B pin = Arduino pin
//dt=2, wr=3,rd=4,cs=5,    ,irq=6

  int _pin_data=2;
  int _pin_wr=3;
  int _pin_cs=5;


    void BitByBit()
    {
  
          //config()
      pinMode(_pin_cs, OUTPUT);
      pinMode(_pin_wr, OUTPUT);
      pinMode(_pin_data, OUTPUT);
      digitalWrite(_pin_cs, HIGH);
      digitalWrite(_pin_wr, HIGH);
      digitalWrite(_pin_data, HIGH);
        
 
          //sendCommand(0b000000110);   // LCD on 
      digitalWrite(_pin_cs, LOW);
 
          //shiftOutBits(3, 0b100);
        //val = (bit(x - 1) & value) != 0;
      //1
      digitalWrite(_pin_wr, LOW);   
      digitalWrite(_pin_data, 0b010); 
      digitalWrite(_pin_wr, HIGH);
      //2
      digitalWrite(_pin_wr, LOW);
      digitalWrite(_pin_data, 0b001);
      digitalWrite(_pin_wr, HIGH);
      //3
      digitalWrite(_pin_wr, LOW);
      digitalWrite(_pin_data, 0b000); 
      digitalWrite(_pin_wr, HIGH);
          
          //shiftOutBits(9, cmd); 
      //1
      digitalWrite(_pin_wr, LOW);   
      digitalWrite(_pin_data, 0b000000011); 
      digitalWrite(_pin_wr, HIGH);
      //2
      digitalWrite(_pin_wr, LOW);
      digitalWrite(_pin_data, 0b000000001);
      digitalWrite(_pin_wr, HIGH);
      //3
      digitalWrite(_pin_wr, LOW);
      digitalWrite(_pin_data, 0b000000000); 
      digitalWrite(_pin_wr, HIGH);
      //4
      digitalWrite(_pin_wr, LOW);
      digitalWrite(_pin_data, 0b000000000); 
      digitalWrite(_pin_wr, HIGH);
      //5
      digitalWrite(_pin_wr, LOW);
      digitalWrite(_pin_data, 0b000000000); 
      digitalWrite(_pin_wr, HIGH);
      //6
      digitalWrite(_pin_wr, LOW);
      digitalWrite(_pin_data, 0b000000000); 
      digitalWrite(_pin_wr, HIGH);
      //7
      digitalWrite(_pin_wr, LOW);
      digitalWrite(_pin_data, 0b000000000); 
      digitalWrite(_pin_wr, HIGH);
      //8
      digitalWrite(_pin_wr, LOW);
      digitalWrite(_pin_data, 0b000000000); 
      digitalWrite(_pin_wr, HIGH);
      //9
      digitalWrite(_pin_wr, LOW);
      digitalWrite(_pin_data, 0b000000000); 
      digitalWrite(_pin_wr, HIGH);



      digitalWrite(_pin_cs, HIGH);
     //END of sendCommand() 
      
    }

    void Clear()
    {
      for(uint8_t x=0; x<6; x++)  //Im using only (0-5) 6 addr for this 3digit LCD
      sendData(adr[x],seg[0]);    //set to 0 each addr clearing the LCD segments
    }


    void setup()
    {
      BitByBit();
      Clear();


                //ADR   DATA
          sendData(0,  0b0011);  //-,c,b,a
          sendData(1,  0b0011);  //d,e,g,f
    }


    void loop()
    {
       
    }

I believe it works !
I just made a test with this code:
LCDBitB.ino (4.5 KB)

digitaWrite() takes only one bit. sendCommand(0b001010010); will translate to:

      digitalWrite(_pin_cs, LOW);
      
// shiftOutBits(3, 0b100);

      digitalWrite(_pin_wr, LOW);   
      digitalWrite(_pin_data, HIGH); // 0bX00
      digitalWrite(_pin_wr, HIGH);

      digitalWrite(_pin_wr, LOW);
      digitalWrite(_pin_data, LOW); // 0b1x0
      digitalWrite(_pin_wr, HIGH);

      digitalWrite(_pin_wr, LOW);
      digitalWrite(_pin_data, LOW); // 0b10x
      digitalWrite(_pin_wr, HIGH);

// shiftOutBits(9, 0b001010010);

      digitalWrite(_pin_wr, LOW);
      digitalWrite(_pin_data, LOW); // 0bx01010010
      digitalWrite(_pin_wr, HIGH);

      digitalWrite(_pin_wr, LOW);
      digitalWrite(_pin_data, LOW); // 0b0x1010010
      digitalWrite(_pin_wr, HIGH);

      digitalWrite(_pin_wr, LOW);   
      digitalWrite(_pin_data, HIGH); // 0b00X010010
      digitalWrite(_pin_wr, HIGH);

      digitalWrite(_pin_wr, LOW);
      digitalWrite(_pin_data, LOW); // 0b001x10010
      digitalWrite(_pin_wr, HIGH);

      digitalWrite(_pin_wr, LOW);   
      digitalWrite(_pin_data, HIGH); // 0b0010X0010
      digitalWrite(_pin_wr, HIGH);

      digitalWrite(_pin_wr, LOW);
      digitalWrite(_pin_data, LOW); // 0b00101x010
      digitalWrite(_pin_wr, HIGH);

      digitalWrite(_pin_wr, LOW);
      digitalWrite(_pin_data, LOW); // 0b001010x10
      digitalWrite(_pin_wr, HIGH);

      digitalWrite(_pin_wr, LOW);   
      digitalWrite(_pin_data, HIGH); // 0b0010100X0
      digitalWrite(_pin_wr, HIGH);

      digitalWrite(_pin_wr, LOW);
      digitalWrite(_pin_data, LOW); // 0b00101001x
      digitalWrite(_pin_wr, HIGH);

      digitalWrite(_pin_cs, HIGH);
1 Like

I notice it too, a bit later than you but I did it.
I managed to actually write to the LCD, to send data and that corrected my code as you mentioned too.
Very good !
Thank you !