Saturday, December 18, 2021

Arduino Uno - 4-Bit LED Digital Module - 3461BS - 5 pins - without library but can display whatever you want


Require components:

1x Arduino Uno

5x wires

1x 4-Bit LED Digital Module using 4-digit LED display model 3461BS


Link 256 states of 7 segment digit

https://zxcongducxz.000webhostapp.com/arduino/SevenSegmentLEDDisplay.html


Video:


Code:

// 4-Bit LED Digital Module with 2x 74HC595D-chip, there are 5 pins from top to bottom:
// VCC  : pin 5V; 
// SCLK : pin A0; 
// SRLK : pin A1; 
// DIO  : pin A3
// GND  : pin GND
#define PIN_SCLK A0 // serial clock, set LOW & HIGH after set PIN_DIO
#define PIN_RCLK A1 // register clock, set LOW & HIGH after write digit
#define PIN_DIO  A2 // set PIN_DIO set index bit from 0,1,2,3,4,5,6,7 = A,B,C,D,E,F,G,DP
const byte TOTAL_DIGITS = 4; // default is 4, you may need to change this value depends on amounts of digits on your module, i'm not sure will it work if changed, good luck ;))
const byte BYTES_0_TO_9[] = { // list of bytes to display from top to bottom, 1 byte = 8 bits, bit starts from right to left, from left to right are DotPoint,G,F,E,D,C,B,A
  B00111111, // 0
  B00000110, // 1
  B01011011, // 2
  B01001111, // 3
  B01100110, // 4 
  B01101101, // 5
  B01111101, // 6
  B00000111, // 7
  B01111111, // 8
  B01101111, // 9
};
const unsigned int POW_10[] = { // cache value for saving performance, https://www.arduino.cc/reference/en/language/functions/math/pow/
  1,        // pow(10, 0);
  10,       // pow(10, 1);
  100,      // pow(10, 2);
  1000,     // pow(10, 3);
  10000,    // pow(10, 4);
  100000,   // pow(10, 5);
  1000000,  // pow(10, 6);
  10000000, // pow(10, 7);
};
const byte BYTE_NEGATIVE = B01000000;
const byte BYTE_CELSIUS = B00111001;
const byte BYTE_FAHRENHEIT = B01110001;
const byte BYTE_DOT_POINT = B10000000;
const byte BYTE_VOLTAGE = B00111110;
const byte BYTE_AMPERE = B01110111;
const byte MIN_PERCENT = 0;
const byte MAX_PERCENT = 100;

void setup() {
  pinMode(PIN_SCLK, OUTPUT);
  pinMode(PIN_RCLK, OUTPUT);
  pinMode(PIN_DIO, OUTPUT);

  digitalWrite(PIN_SCLK, HIGH);
  digitalWrite(PIN_RCLK, HIGH);
  digitalWrite(PIN_DIO, HIGH);
}

void write(unsigned short durationbyte dataPGFEDCBA[]) {
  do {
    for(byte indexDigit = 0; indexDigit < TOTAL_DIGITS; indexDigit++) { // write each digits from most right to most left
      digitalWrite(PIN_RCLK, LOW);
      shiftOut(PIN_DIO, PIN_SCLK, MSBFIRST, ~dataPGFEDCBA[indexDigit]); // shift out byte display 8 bit display PGFEDCBA, because of we are using 4-digit common anode, need inverse bit by using "~" 
      shiftOut(PIN_DIO, PIN_SCLK, MSBFIRST, 1 << indexDigit); // shift out byte display 8 bit display digits
      // there is another way to set 16 bits, instead of shiftOut
      // is make 3 line of code for 16 times, just change isOn variable for each bit of bytePGFEDCBA & byteDigit from most left to most right ;))
      // digitalWrite(PIN_DIO, isOn); digitalWrite(PIN_DIO, isOn); digitalWrite(PIN_SCLK, HIGH);
      digitalWrite(PIN_RCLK, HIGH);
    }
  } while(duration-- > 0); // avoid case duration = 0--
}

void writeInt(unsigned short durationint number) { // 4 digit display from -999 to 9999, short is enough for 4 digit, but i wanted to support up to 8 digits, then i use int
  byte dataPGFEDCBA[TOTAL_DIGITS];
  
  byte totalDigit;  
  if(number >= 0) { // zero and positive numbers
    totalDigit = TOTAL_DIGITS; // show full digits
  } else { // negative numbers
    totalDigit = TOTAL_DIGITS - 1; // can not show full digits because of most left digit display negative symbol
    dataPGFEDCBA[totalDigit] = BYTE_NEGATIVE; // show negative symbol "-";
    number *= -1; // convert to positive number
  }
  for(byte indexDigit = 0; indexDigit < totalDigit; indexDigit++) {
    dataPGFEDCBA[indexDigit] = BYTES_0_TO_9[(unsigned short)(number / POW_10[indexDigit]) % 10];
  }
  
  write(duration, dataPGFEDCBA);
}

void writeFloat(unsigned short durationfloat number) { // 4 digit display from -99.9 to 999.9
  byte dataPGFEDCBA[TOTAL_DIGITS];
  
  byte totalDigit;  
  if(number >= 0) { // zero and positive numbers
    totalDigit = TOTAL_DIGITS; // show full digits
  } else { // negative numbers
    totalDigit = TOTAL_DIGITS - 1; // can not show full digits because of most left digit display negative symbol
    dataPGFEDCBA[totalDigit] = BYTE_NEGATIVE; // show negative symbol "-";
    number *= -1; // convert to positive number
  }
  dataPGFEDCBA[0] = BYTES_0_TO_9[(unsigned short)(number * 10) % 10]; // number after dot
  dataPGFEDCBA[1] = BYTES_0_TO_9[(unsigned short)(number) % 10] | BYTE_DOT_POINT;  // number before dot  
  for(byte indexDigit = 2; indexDigit < totalDigit; indexDigit++) {
    dataPGFEDCBA[indexDigit] = BYTES_0_TO_9[(unsigned short)(number / POW_10[indexDigit - 1]) % 10]; // remain numbers
  }
  
  write(duration, dataPGFEDCBA);
}

void writeIntUnit(unsigned short durationint numberbyte byteUnit) { // 4 digit display from -99 to 999, short is enough for 4 digit, but i wanted to support up to 8 digits, then i use int
  byte dataPGFEDCBA[TOTAL_DIGITS];
  dataPGFEDCBA[0] = byteUnit; // write unit
  
  byte totalDigit;  
  if(number >= 0) { // zero and positive numbers
    totalDigit = TOTAL_DIGITS; // show full digits
  } else { // negative numbers
    totalDigit = TOTAL_DIGITS - 1; // can not show full digits because of most left digit display negative symbol
    dataPGFEDCBA[totalDigit] = BYTE_NEGATIVE; // show negative symbol "-";
    number *= -1; // convert to positive number
  }
  for(byte indexDigit = 1; indexDigit < totalDigit; indexDigit++) {
    dataPGFEDCBA[indexDigit] = BYTES_0_TO_9[(unsigned short)(number / POW_10[indexDigit - 1]) % 10];
  }
  write(duration, dataPGFEDCBA);
}

void writeFloatUnit(unsigned short durationfloat numberbyte byteUnit) { // 4 digit display from -9.9 to 99.9
  byte dataPGFEDCBA[TOTAL_DIGITS];

  if(number >= 0) { // zero and positive numbers
    dataPGFEDCBA[3] = BYTES_0_TO_9[(unsigned short)(number / 10) % 10];
  } else { // negative numbers
    dataPGFEDCBA[3] = BYTE_NEGATIVE; // show negative symbol "-";
    number *= -1; // convert to positive number
  }
  dataPGFEDCBA[0] = byteUnit; // write symbol
  dataPGFEDCBA[1] = BYTES_0_TO_9[(unsigned short)(number * 10) % 10]; // number after dot
  dataPGFEDCBA[2] = BYTES_0_TO_9[(unsigned short)(number) % 10] | BYTE_DOT_POINT; // number before dot
  
  write(duration, dataPGFEDCBA);
}

void writeTime(unsigned short durationbyte hoursbyte minutes) {
  byte dataPGFEDCBA[TOTAL_DIGITS];
  
  dataPGFEDCBA[0] = BYTES_0_TO_9[minutes % 10];
  dataPGFEDCBA[1] = BYTES_0_TO_9[minutes / 10 % 10];
  dataPGFEDCBA[2] = BYTES_0_TO_9[hours % 10] | BYTE_DOT_POINT;
  dataPGFEDCBA[3] = BYTES_0_TO_9[hours / 10 % 10];
  
  write(duration, dataPGFEDCBA);
}

void writeText(unsigned short durationbyte data[]) { // length of data == TOTAL_DIGITS
  byte dataPGFEDCBA[TOTAL_DIGITS];
  for(byte indexDigit = 0; indexDigit < TOTAL_DIGITS; indexDigit++) {
    dataPGFEDCBA[indexDigit] = data[TOTAL_DIGITS - indexDigit - 1];
  }
  write(duration, dataPGFEDCBA);
}

void writePercent(unsigned short durationbyte data[], byte animationLengthbyte percent) { // support percent from 0 to 100
  byte dataPGFEDCBA[TOTAL_DIGITS];
  for(byte indexDigit = 0; indexDigit < TOTAL_DIGITS; indexDigit++) { // check all digits from most right to most left
    dataPGFEDCBA[indexDigit] = 0; // initialize value = 0, change later
    for(byte indexAnimation = 0; indexAnimation < animationLength; indexAnimation++) {
      byte percentCurrent = MAX_PERCENT * ((TOTAL_DIGITS - indexDigit) * animationLength - indexAnimation) / (TOTAL_DIGITS * animationLength);
      if(percentCurrent <= percent) { // is percent can be display
        dataPGFEDCBA[indexDigit] = data[indexAnimation];
        break
      }
    }
  }
  write(duration, dataPGFEDCBA);
}

void writeTextRunLeft(unsigned short delayMovebyte data[], unsigned short length) {
  for(unsigned short offset = 0; offset < length; offset++) {
    // slice
    byte dataPGFEDCBA[TOTAL_DIGITS];
    for(byte indexDigit = 0; indexDigit < TOTAL_DIGITS && indexDigit < length; indexDigit++) {
      dataPGFEDCBA[indexDigit] = data[(offset + indexDigit) % length];
    }
    writeText(delayMove, dataPGFEDCBA);
  }
}

void testInt() {
  for(int value = -999; value <= 9999; value++){
    writeInt(0, value);
  }
}

void testFloat() {
  for(float value = -99.9; value <= 999.9; value+=0.5){
    writeFloat(10, value);
  }
}

void testIntUnit() {
  for(int value = -99; value <= 999; value++){
    writeIntUnit(10, value, BYTE_CELSIUS);
  }
  for(int value = -99; value <= 999; value++){
    writeIntUnit(10, value, BYTE_FAHRENHEIT);
  }
  for(int value = -99; value <= 999; value++){
    writeIntUnit(10, value, BYTE_VOLTAGE);
  }
  for(int value = -99; value <= 999; value++){
    writeIntUnit(10, value, BYTE_AMPERE);
  }
}

void testFloatUnit() {
  for(float value = -9.9; value <= 99.9; value += 0.5){
    writeFloatUnit(20, value, BYTE_CELSIUS);
  }
  for(float value = -9.9; value <= 99.9; value += 0.5){
    writeFloatUnit(20, value, BYTE_FAHRENHEIT);
  }
  for(float value = -9.9; value <= 99.9; value += 0.5){
    writeFloatUnit(20, value, BYTE_VOLTAGE);
  }
  for(float value = -9.9; value <= 99.9; value += 0.5){
    writeFloatUnit(20, value, BYTE_AMPERE);
  }
}

void testTime() {
  for(byte hour = 0; hour < 24; hour++) {
    for(byte minute = 0; minute < 60; minute++){
      writeTime(10, hour, minute);
    }
  }
}

void testProgress() {
  byte durationPercent = 20;
  
  byte percent = 0;
  byte dataPGFEDCBA1[] = {B00110110, B00110000};
  do { writePercent(durationPercent, dataPGFEDCBA1, sizeof(dataPGFEDCBA1), percent); } while(++percent <= MAX_PERCENT); // fade in
  do { writePercent(durationPercent, dataPGFEDCBA1, sizeof(dataPGFEDCBA1), percent); } while(percent-- > 0); // fade out

  percent = 0;
  byte dataPGFEDCBA2[] = {B00110110, B00110010, B00110000, B00100000};
  do { writePercent(durationPercent, dataPGFEDCBA2, sizeof(dataPGFEDCBA2), percent); } while(++percent <= MAX_PERCENT); // fade in
  do { writePercent(durationPercent, dataPGFEDCBA2, sizeof(dataPGFEDCBA2), percent); } while(percent-- > 0); // fade out

  percent = 0;
  byte dataPGFEDCBA3[] = {B01001001,B01000001,B00000001};
  do { writePercent(durationPercent, dataPGFEDCBA3, sizeof(dataPGFEDCBA3), percent); } while(++percent <= MAX_PERCENT); // fade in
  do { writePercent(durationPercent, dataPGFEDCBA3, sizeof(dataPGFEDCBA3), percent); } while(percent-- > 0); // fade out
}

void testText() {
  byte dataPGFEDCBA[] = {
    B01110011, // P
    B00110000, // l, I (left)
    B11011100, // a
    B01101110, // Y, y
  };  
  writeText(1000, dataPGFEDCBA);
}

void testRunningText() {
  byte dataPGFEDCBA[] = {
    B01110110, // H
    B01111001, // E
    B00110110, // ll
    B01011100, // o
    B00000000, // (space)
    B00111110, // U,V
    B00111110, // U,V
    B01011100, // o
    B01010000, // r
    B00110000, // l, I (left)
    B01011110, // d
    B00000000, // (space)
  };
  writeTextRunLeft(1000, dataPGFEDCBA, sizeof(dataPGFEDCBA));
}

void loop() 
{
  testInt();
  testFloat();
  testIntUnit();
  testFloatUnit();
  testTime();
  testProgress();
  testText();
  testRunningText();
}

If my code can not be used, you may need to use other libraries on the internet:

https://github.com/monotok/FourBitDisplay

https://github.com/0xF6/TM74

https://github.com/avishorp/TM1637

https://github.com/DeanIsMe/SevSeg

https://github.com/Erriez/ErriezRobotDyn4DigitDisplay

https://github.com/bremme/arduino-tm1637


No comments: