In this tutorial, we will be looking at how to draw shapes such as lines, rectangles and circles in our LCD.
Let’s begin by drawing a line. To draw a line, simply include the below void in your program.
void drawLine(float x1, float y1, float x2, float y2, uint16_t color) {
if (x2 > x1) {
for (int xinc = x1; xinc < x2; xinc++) { //xinc = x=incremental
float m = (y2 - y1) / (x2 - x1); //m=gradient
float yinc = m * (xinc - x1) + y1;
//Address_set(int16_t y1, int16_t y2, int16_t x1, int16_t x2)
Address_set(y1, yinc, x1, xinc);
LCD_data_write(color >> 8);
LCD_data_write(color);
y1 = yinc;
x1 = xinc;
}
}
else {
float newx1 = x2; float newx2 = x1; float newy1 = y2; float newy2 = y1;
for (int xinc = newx1; xinc < newx2; xinc++) { //xinc = x=incremental
float m = (newy2 - newy1) / (newx2 - newx1); //m=gradient
float yinc = m * (xinc - newx1) + newy1;
//Address_set(int16_t y1, int16_t y2, int16_t x1, int16_t x2)
Address_set(newy1, yinc, newx1, xinc);
LCD_data_write(color >> 8);
LCD_data_write(color);
newy1 = yinc;
newx1 = xinc;
}
}
}
The code above is pretty straightforward to understand as it only uses the straight line formula to deduce the next pixel in drawing a straight line.
m = (y2 - y1) / (x2 - x1); //m=gradient
As an example, you can try running the below code by including it in the void setup as we would only want this code to run once :
uint16_t color = selectColour(100, 0, 0);
drawLine(20,20,300,220,color);
drawLine(20,220,300,20,color);
The above code would give you 2 diagonal lines drawn in color of your choice.
To draw a hollow rectangle, you can make use of the below void :
void hollowRectangle(int16_t rectx1, int16_t recty1, int16_t wide, int16_t tall, uint16_t color) {
//draw left line
int16_t lefty1 = recty1;
int16_t leftx1 = rectx1;
int16_t lefty2 = recty1 + tall;
int16_t leftx2 = rectx1 + 1;
for (int i = lefty1; lefty1 < lefty2; lefty1++) {
//Address_set(int16_t y1, int16_t y2, int16_t x1, int16_t x2)
Address_set(lefty1, lefty2, leftx1, leftx2);
LCD_data_write(color >> 8);
LCD_data_write(color);
}
//draw left line
int16_t righty1 = recty1;
int16_t rightx1 = rectx1;
int16_t righty2 = recty1 + tall;
int16_t rightx2 = rectx1 + wide;
for (int i = righty1; righty1 < righty2; righty1++) {
//Address_set(int16_t y1, int16_t y2, int16_t x1, int16_t x2)
Address_set(righty1, righty2, rightx2, rightx2 + 1);
LCD_data_write(color >> 8);
LCD_data_write(color);
}
//draw top line
int16_t topy1 = recty1 + tall;
int16_t topx1 = rectx1;
int16_t topy2 = recty1 + tall;
int16_t topx2 = rectx1 + wide;
for (int i = topx1; topx1 < topx2 + 1; topx1++) {
//Address_set(int16_t y1, int16_t y2, int16_t x1, int16_t x2)
Address_set(topy1, topy2 + 1, topx1, topx2 + 1);
LCD_data_write(color >> 8);
LCD_data_write(color);
}
//draw bottom line
int16_t bottomy1 = recty1;
int16_t bottomx1 = rectx1;
int16_t bottomy2 = recty1;
int16_t bottomx2 = rectx1 + wide;
for (int i = bottomx1; bottomx1 < bottomx2 + 1; bottomx1++) {
//Address_set(int16_t y1, int16_t y2, int16_t x1, int16_t x2)
Address_set(bottomy1, bottomy2 + 1, bottomx1, bottomx2 + 1);
LCD_data_write(color >> 8);
LCD_data_write(color);
}
}
The above void is built on simple mathematics to construct 4 lines or sides of a rectangle which will construct a rectangle in whole. You can try out the below examples to understand how these codes work:
//Basic Rectangle
uint16_t color = selectColour(100, 0, 0);
hollowRectangle(20, 20, 280, 200, color);
The above example will draw a basic rectangle.
//Example 1
for(int i=20; i < 280; i = i +20){
for(int j=20; j < 200; j = j +20){
uint16_t color = selectColour(100, 0, 0);
hollowRectangle(20, 20, i, j, color);
}
}
The above example will draw rectangle stacked on top of each other.
//Example2
for(int i=20; i < 280; i = i +20){
uint16_t color = selectColour(100, 0, 0);
hollowRectangle(0, 0, i, i, color);
}
The above example will draw multiple rectangle in increasing width and height.
Next, we will be looking at how to draw filled rectangles. To draw a rectangle filled with color, you can use the below function :
void filledRectangle(int16_t rectx1, int16_t recty1, int16_t wide, int16_t tall, uint16_t color) {
//draw left line
int16_t y1 = recty1;
int16_t x1 = rectx1;
int16_t y2 = recty1 + tall;
int16_t x2 = rectx1 + wide;
Address_set(y1, y2, x1, x2);
for (int i = 0; i < wide + 1; i++)
for (int j = 0; j < tall + 1; j++) {
LCD_data_write(color >> 8);
LCD_data_write(color);
}
}
Once included the above void in your program, you can try out the below examples which draw three rectangles in different colors and size.
//Example 1
uint16_t color = selectColour(100, 0, 0);
filledRectangle(0, 0, 320, 240, color);
filledRectangle(40, 40, 240, 160, selectColour(0, 100, 0));
filledRectangle(80, 80, 160, 80, selectColour(0, 0, 100));
Then, let’s look at drawing hollow circles. As usual, include the below void functions if you need to draw hollow circle:
void hollowCircle(float x1, float y1, float radius, uint16_t color) {
float leftx1 = x1 - radius;
float rightx1 = x1 + radius;
float bottomy1 = y1 - radius;
float topy1 = y1 + radius;
for ( float i = leftx1; i < rightx1; i = i + 0.5) {
//circle formula (x-h)^2 + (y-k)^2 = r^2
float movingy = y1 + sqrt(pow(radius, 2) - pow((i - x1), 2));
drawPixel(i, movingy, color);
movingy = y1 - sqrt(pow(radius, 2) - pow((i - x1), 2));
drawPixel(i, movingy, color);
}
for ( float j = bottomy1; j < topy1; j = j + 0.5) {
float movingx = x1 + sqrt(pow(radius, 2) - pow((j - y1), 2));
drawPixel(movingx, j, color);
movingx = x1 - sqrt(pow(radius, 2) - pow((j - y1), 2));
drawPixel(movingx, j, color);
}
}
Once done, you can try out the below examples.
//Example 1
uint16_t color = selectColour(100, 100, 100);
hollowCircle(160, 120, 120, color);
The above example draws a single hollow circle.
//Example 2
uint16_t color = selectColour(100, 100,100);
filledRectangle(0, 0, 320, 240, color);
for(int i=0; i < 120; i = i +20){
uint16_t color = selectColour(50, 35, 80);
hollowCircle(160, 120, i, color);
hollowCircle(160, 120, i+1, color);
}
The example above draws hollow circles in increasing radius from the center point of my LCD Display which is x = 160 and y = 120.
Finally, we will be looking at how to draw filled circles.
void filledCircle(float x1, float y1, float radius, uint16_t color) {
for ( float i = 0; i < radius; i = i + 1) {
hollowCircle(x1, y1, i, color);
}
}
The above void draws filled circle by using void hollowCircle while increasing its radius to the desired length.
Try out the below examples :
//Example 1
uint16_t color = selectColour(100, 100, 0);
filledCircle(160, 120, 80, color);
The above example draws a simple filled circle in your desired color.
//Example 2
uint16_t color = selectColour(83, 84,83);
filledRectangle(0, 0, 320, 240, color);
color = selectColour(0, 0, 100);
filledCircle(160, 120, 120, color);
color = selectColour(100, 0, 0);
filledCircle(160, 120, 80, color);
color = selectColour(100, 100, 100);
filledCircle(160, 120, 40, color);
Whereas this example draws 3 filled circles inside of one another.
With that, we have come to the end of our tutorial. For more detailed explanation, check out my YouTube vide below :
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 24
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 24
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);
//MCU Interface Mode : Page 27
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(0x7F); //VCOM H 1111111
LCD_data_write(0x00); //VCOM L 0000000
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(B00001000); //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 x1
LCD_data_write(y1); //Value of x1
LCD_data_write(y2 >> 8); //8 Bit Shift Right of x2
LCD_data_write(y2); //Value of x2
LCD_command_write(0x2b); //Page Address Set | DataSheet Page 110
LCD_data_write(x1 >> 8); //8 Bit Shift Right of y1
LCD_data_write(x1); //Value of y1
LCD_data_write(x2 >> 8); //8 Bit Shift Right of y2
LCD_data_write(x2); //Value of y2
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;
greenpercentage = (greenpercentage / 100) * 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 drawLine(float x1, float y1, float x2, float y2, uint16_t color) {
if (x2 > x1) {
for (int xinc = x1; xinc < x2; xinc++) { //xinc = x=incremental
float m = (y2 - y1) / (x2 - x1); //m=gradient
float yinc = m * (xinc - x1) + y1;
//Address_set(int16_t y1, int16_t y2, int16_t x1, int16_t x2)
Address_set(y1, yinc, x1, xinc);
LCD_data_write(color >> 8);
LCD_data_write(color);
y1 = yinc;
x1 = xinc;
}
}
else {
float newx1 = x2; float newx2 = x1; float newy1 = y2; float newy2 = y1;
for (int xinc = newx1; xinc < newx2; xinc++) { //xinc = x=incremental
float m = (newy2 - newy1) / (newx2 - newx1); //m=gradient
float yinc = m * (xinc - newx1) + newy1;
//Address_set(int16_t y1, int16_t y2, int16_t x1, int16_t x2)
Address_set(newy1, yinc, newx1, xinc);
LCD_data_write(color >> 8);
LCD_data_write(color);
newy1 = yinc;
newx1 = xinc;
}
}
}
void hollowRectangle(int16_t rectx1, int16_t recty1, int16_t wide, int16_t tall, uint16_t color) {
//draw left line
int16_t lefty1 = recty1;
int16_t leftx1 = rectx1;
int16_t lefty2 = recty1 + tall;
int16_t leftx2 = rectx1 + 1;
for (int i = lefty1; lefty1 < lefty2; lefty1++) {
//Address_set(int16_t y1, int16_t y2, int16_t x1, int16_t x2)
Address_set(lefty1, lefty2, leftx1, leftx2);
LCD_data_write(color >> 8);
LCD_data_write(color);
}
//draw right line
int16_t righty1 = recty1;
int16_t rightx1 = rectx1;
int16_t righty2 = recty1 + tall;
int16_t rightx2 = rectx1 + wide;
for (int i = righty1; righty1 < righty2; righty1++) {
//Address_set(int16_t y1, int16_t y2, int16_t x1, int16_t x2)
Address_set(righty1, righty2, rightx2, rightx2 + 1);
LCD_data_write(color >> 8);
LCD_data_write(color);
}
//draw top line
int16_t topy1 = recty1 + tall;
int16_t topx1 = rectx1;
int16_t topy2 = recty1 + tall;
int16_t topx2 = rectx1 + wide;
for (int i = topx1; topx1 < topx2 + 1; topx1++) {
//Address_set(int16_t y1, int16_t y2, int16_t x1, int16_t x2)
Address_set(topy1, topy2 + 1, topx1, topx2 + 1);
LCD_data_write(color >> 8);
LCD_data_write(color);
}
//draw bottom line
int16_t bottomy1 = recty1;
int16_t bottomx1 = rectx1;
int16_t bottomy2 = recty1;
int16_t bottomx2 = rectx1 + wide;
for (int i = bottomx1; bottomx1 < bottomx2 + 1; bottomx1++) {
//Address_set(int16_t y1, int16_t y2, int16_t x1, int16_t x2)
Address_set(bottomy1, bottomy2 + 1, bottomx1, bottomx2 + 1);
LCD_data_write(color >> 8);
LCD_data_write(color);
}
}
void filledRectangle(int16_t rectx1, int16_t recty1, int16_t wide, int16_t tall, uint16_t color) {
//draw left line
int16_t y1 = recty1;
int16_t x1 = rectx1;
int16_t y2 = recty1 + tall;
int16_t x2 = rectx1 + wide;
Address_set(y1, y2, x1, x2);
for (int i = 0; i < wide + 1; i++)
for (int j = 0; j < tall + 1; j++) {
LCD_data_write(color >> 8);
LCD_data_write(color);
}
}
void hollowCircle(float x1, float y1, float radius, uint16_t color) {
float leftx1 = x1 - radius;
float rightx1 = x1 + radius;
float bottomy1 = y1 - radius;
float topy1 = y1 + radius;
for ( float i = leftx1; i < rightx1; i = i + 0.5) {
//circle formula (x-h)^2 + (y-k)^2 = r^2
float movingy = y1 + sqrt(pow(radius, 2) - pow((i - x1), 2));
drawPixel(i, movingy, color);
movingy = y1 - sqrt(pow(radius, 2) - pow((i - x1), 2));
drawPixel(i, movingy, color);
}
for ( float j = bottomy1; j < topy1; j = j + 0.5) {
float movingx = x1 + sqrt(pow(radius, 2) - pow((j - y1), 2));
drawPixel(movingx, j, color);
movingx = x1 - sqrt(pow(radius, 2) - pow((j - y1), 2));
drawPixel(movingx, j, color);
}
}
void filledCircle(float x1, float y1, float radius, uint16_t color) {
for ( float i = 0; i < radius; i = i + 1) {
hollowCircle(x1, y1, i, color);
}
}
void setup() {
// Setting Pin 2-7 as Output, DDR is PinMode Commnand, 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);
//Example 2
uint16_t color = selectColour(83, 84,83);
filledRectangle(0, 0, 320, 240, color);
color = selectColour(0, 0, 100);
filledCircle(160, 120, 120, color);
color = selectColour(100, 0, 0);
filledCircle(160, 120, 80, color);
color = selectColour(100, 100, 100);
filledCircle(160, 120, 40, color);
}
void loop() {
}
Commenti