top of page

Part 2 LCD: LCD Color Selection (Without Library)

Updated: Dec 27, 2021

In this tutorial, we will be discussing on colors for LCDs are derived in Arduino Programs.


Below image shows how each bit represent colors in a RGB orientation.

The first 5 bit on the left represent red, middle 6 bits on the middle represent green color and 5 bits on the right represents blue color. The highest values of each of these portion would contribute to the brightest or darkest of color. For an example, 11111 on the left would produce the brightest red as it is the highest value for 5 bits. The same applies for green and blue portion.


Often, at the beginning of programs, we would certain colors are being defined as shown below :

The binary presentation of the respective Hex color code would help us to understand how the values translate into 16 bits values as shown in 16 bit display data figure above.


Defining colors at the beginning of the program can be limited and tedious as the number of color increases. To some it is not practical to define many colors at the beginning of the program especially for a scenario when we would need tens of colors in our program.


To overcome this limitation, I’ve come up with a function that helps us to generate colors that we need at will without needing to defines numerous color codes at the beginning of our program. The function is shown below :


uint16_t selectColour(float redpercentage, float greenpercentage, float bluepercentage) {
  //use uint16_t instead of void as we need some values in return
  //int does not work with division
  //hence, we're using float instead of int
  redpercentage   = (redpercentage / 100) * 31;     // 11111 = 31
  greenpercentage = (greenpercentage / 100) * 63;   // 111111 = 63 
  bluepercentage  = (bluepercentage / 100) * 31;
  int red = round(redpercentage);
  int green = round(greenpercentage);
  int blue = round(bluepercentage);
  uint16_t color_16_bit = 0;
  color_16_bit = (red << 11) + (green << 5) + blue;
  return color_16_bit;
}

The function above uses composition of red, green and blue in percentage to produce the desired color. Whatever percentage given, is then mapped to respective 5 or 6 bits representing either red, green or blue color.


  redpercentage   = (redpercentage / 100) * 31;     // 11111 = 31
  greenpercentage = (greenpercentage / 100) * 63;   // 111111 = 63 
  bluepercentage  = (bluepercentage / 100) * 31;

Red and blue color equation is multiplied by 31 as 11111 equals to 31 whereas green equation is multiplied by 63 as 111111 equals to 63. As per 16-bit display data figure, we have 5 bits representing red and blue while green is represented by 6 bits. Based on this understanding, I have derived the equation as such.


Once these values are gathered, it is then rounded-off to create a round number which will ease the conversion to 16 bits and at the same reduce unnecessary errors in our code.


To understand the colour function, let’s take a look at the examples below :

uint16_t color = selectColour(100,0,0);

The above will generate red colour as the percentage of red will be 100% while green and blue is 0.


uint16_t color = selectColour(0,100,0);

Similarly, the above will generate green colour as the percentage of red and blue is set to 0.


uint16_t color = selectColour(100,100,0);

For the scenario above, a yellow will be generated as combination of red and green will produce yellow colour. The number can be varied from 0 to 100 to produce difference tones and colour. For instance, 90% red will produce a milder yellow in comparison to 100% red when combined with 100% green.


color_16_bit = (red << 11) + (green << 5) + blue;

This particular line helps to push the bits to the right position in the 16 bits arrangement. For instance, first 5 bits on the left should be representing red color, middle 6 bits would represent green color and then 5 bits on the right would represent blue color.


With that, we have come to the end of our color selection tutorial. I hope you learned something new thru this tutorial. Don’t forget to Like, Comment and Subscribe.


For more detailed explanation, check out my YouTube video on this topic.


Entire Code :


#define BLACK   0x0000
#define BLUE    0x001F
#define RED     0xF800
#define RED2    0x4000
#define GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF
#define GREEN2  0x2FA4
#define CYAN2   0x07FF

#define LCD_RD   A0 //Serves as read signal/MCU read data at the rising edge. Pg11 Datasheet
#define LCD_WR   A1 //Serves as write signal/command at the rising edge
#define LCD_RS   A2 //D/CX (0=Command/1=Data)       
#define LCD_CS   A3 //Chip Select Pin : Active Low
#define LCD_RST  A4 //Shield Reset

void LCD_write(uint8_t d) {
  // Serves as write signal/command at the rising edge
  digitalWrite(LCD_WR, LOW); // WR 0

  // LCD Pins |7|6|5|4|3|2|1|0|
  // Uno Pins |7|6|5|4|3|2|9|8|

  //Arduino Port D : Pin 0 - 7, but Pin 0,1 not used in the LCD Shield
  //Arduino Port B : Pin 8 - 13,but Pin, 10,11,12,13 not used in the LCD Shield
  //Arduino Port C : Analog Pins

  PORTD = (PORTD & B00000011) | ((d) & B11111100);
  PORTB = (PORTB & B11111100) | ((d) & B00000011);

  digitalWrite(LCD_WR, HIGH); // WR 1
}

void LCD_command_write(uint8_t command) {
  // LCD_RS = 0, A2=0, D/CX (0=Command/1=Data) | DataSheet Page 11
  digitalWrite(LCD_RS, LOW);
  LCD_write(command);
}

void LCD_data_write(uint8_t data) {
  // LCD_RS = 1, A2=1, D/CX (0=Command/1=Data) | DataSheet Page 11
  digitalWrite(LCD_RS, HIGH);
  LCD_write(data);
}

void Lcd_Init(void) {
  //void does not return any value
  //void only execute instruction within it
  //similar to void setup and loop
  //This function will have LCD initialization measures
  //Only the necessary Commands are covered
  //Eventho there are so many more in DataSheet

  //Reset Signal is Active LOW
  digitalWrite(LCD_RST, HIGH);
  delay(5);
  digitalWrite(LCD_RST, LOW); //Actual Reset Done Here
  delay(15);
  digitalWrite(LCD_RST, HIGH);
  delay(15);

  //The below is just preparation for Write Cycle Seq
  digitalWrite(LCD_CS, HIGH); //Chip-Select Active Low Signal
  digitalWrite(LCD_WR, HIGH);
  digitalWrite(LCD_RD, HIGH);
  digitalWrite(LCD_CS, LOW);  //Chip-Select Active Low Signal

  LCD_command_write(0xC5);    //Test this Out | VCOM Control 1 : Colour Contrast Maybe
  LCD_data_write(0x54);       //VCOM H 1111111 0x7F
  LCD_data_write(0x00);       //VCOM L 0000000
  //LCD_data_write(B1010011);
  
  LCD_command_write(0x36);    //Memory Access Control | DataSheet Page 127
  LCD_data_write(0x48);       //Adjust this value to get right color and starting point of x and y
  //LCD_data_write(B0000100);     //Example

  LCD_command_write(0x3A);    //COLMOD: Pixel Format Set | DataSheet Page 134
  LCD_data_write(0x55);       //16 Bit RGB and MCU

  LCD_command_write(0x11);    //Sleep Out | DataSheet Page 245
  delay(10);                  //Necessary to wait 5msec before sending next command

  LCD_command_write(0x29);    //Display on.

  LCD_command_write(0x2c);    //Memory Write | DataSheet Page 245
}

void Address_set(int16_t y1, int16_t y2, int16_t x1, int16_t x2) {
  LCD_command_write(0x2a);  //Column Address Set | DataSheet Page 110
  LCD_data_write(y1 >> 8);  //8 Bit Shift Right of y1
  LCD_data_write(y1);       //Value of y1
  LCD_data_write(y2 >> 8);  //8 Bit Shift Right of y2
  LCD_data_write(y2);       //Value of y2

  LCD_command_write(0x2b);  //Page Address Set | DataSheet Page 110
  LCD_data_write(x1 >> 8);  //8 Bit Shift Right of x1
  LCD_data_write(x1);       //Value of x1
  LCD_data_write(x2 >> 8);  //8 Bit Shift Right of x2
  LCD_data_write(x2);       //Value of x2

  LCD_command_write(0x2c); // REG 2Ch = Memory Write
}

void drawPixel(int16_t x, int16_t y, uint16_t color) {
  digitalWrite(LCD_CS, LOW);// Chip Select active
  Address_set(y, y + 1, x, x + 1);
  //LCD_command_write(0x2C);
  LCD_data_write(color >> 8);
  LCD_data_write(color);
}

uint16_t selectColour(float redpercentage, float greenpercentage, float bluepercentage) {
  //use uint16_t instead of void as we need some values in return
  //int does not work with division
  //hence, we're using float instead of int

  redpercentage   = (redpercentage / 100) * 31;     // 11111 = 31
  greenpercentage = (greenpercentage / 100) * 63;   // 111111 = 63 
  bluepercentage  = (bluepercentage / 100) * 31;

  int red = round(redpercentage);
  int green = round(greenpercentage);
  int blue = round(bluepercentage);

  uint16_t color_16_bit = 0;
  color_16_bit = (red << 11) + (green << 5) + blue;
  return color_16_bit;
}

void setup() {
  // Setting Pin 2-7 as Output, DDR is PinMode Command, Pin0,1 Untouched
  DDRD = DDRD | B11111100;
  // Setting Pin 8-9 as Output
  DDRB = DDRB | B00000011;
  //Setting Analog Pins A4-A0 as Output
  DDRC = DDRC | B00011111;
  //Setting Analog Pins A4-A0 as HIGH
  //PORTC = PORTC | B00011111;
  Lcd_Init();
  Serial.begin(9600);
}

void loop() {

  uint16_t color = selectColour(100,100,100);
  
      for (int i = 50; i < 300; i++) {
        drawPixel(i, 60, selectColour(100,100,0));
        drawPixel(i, 70, color);
        drawPixel(i, 80, color);
        drawPixel(i, 90, color);
        drawPixel(i, 100, color);
        drawPixel(i, 110, color);
        drawPixel(i, 120, color);
        drawPixel(i, 130, color);
        drawPixel(i, 140, color);
        drawPixel(i, 150, color);
      }
}

댓글


bottom of page