In this guide, we shall interface STM32 with GLCD12864 using serial method. We shall display some texts, shapes and graphics.
In the guide, we will cover the following:
- GLCD 12864.
- Interface with STM32.
- Code.
- Demo.
1. GLCD 12864:
Ordinary LCDs can only display simple text or numbers within a fixed size. But in 128×64 graphical LCD display, there is 128×64 = 8192 dots, which is equivalent to 8242/8 = 1024 pixels. So, it can display not only simple text or numbers within a fixed size but also simple graphics.
128×64 Graphical LCD Display Pinout
This module has 20 pins:
- GND: Ground
- VCC: Module power supply – 5 V
- VO: LCD Contrast
- RS: Register Select Pin
- R/W: Write/ Read selection
- E: Enable Signal
- D0-7: Data Bus
- PSB: Interface selection (0 for serial communication, 1 for 8-bit parallel communication)
- NC: Not Connected
- RST: Reset
- Vout: LCD Voltage Output (Vout < 7V)
- BLA: Power Supply for Backlight+
- BLK: Power Supply for Backlight-
You can see the pinout of this module in the image below.
2. Interface with STM32:
setup below is as follows
PA0 ———> SCLK (EN)
PA1 ———> CS (RS)
PA4 ———> SID (RW)
PA8 ———> RST (RST)
3. Code:
We start of by defining some marcos fpr SCLK, CS, SID and RST as following:
#define SCK_LOW (GPIOA->BSRR=GPIO_BSRR_BR0) #define SCK_HIGH (GPIOA->BSRR=GPIO_BSRR_BS0) #define CS_LOW (GPIOA->BSRR=GPIO_BSRR_BR1) #define CS_HIGH (GPIOA->BSRR=GPIO_BSRR_BS1) #define SID_LOW (GPIOA->BSRR=GPIO_BSRR_BR4) #define SID_HIGH (GPIOA->BSRR=GPIO_BSRR_BS4) #define RST_LOW (GPIOA->BSRR=GPIO_BSRR_BR8) #define RST_HIGH (GPIOA->BSRR=GPIO_BSRR_BS8)
Now we can need some variables as following:
uint8_t startRow, startCol, endRow, endCol; // coordinates of the dirty rectangle uint8_t numRows = 64; uint8_t numCols = 128; uint8_t Graphic_Check = 0; uint8_t image[(128 * 64)/8];
For initializing the lcd:
void ST7920_Init (void) { RCC->AHB1ENR|=RCC_AHB1ENR_GPIOAEN; GPIOA->MODER|=(GPIO_MODER_MODE0_0|GPIO_MODER_MODE1_0|GPIO_MODER_MODE4_0|GPIO_MODER_MODE8_0); GPIOA->MODER&=~(GPIO_MODER_MODE0_1|GPIO_MODER_MODE1_1|GPIO_MODER_MODE4_1|GPIO_MODER_MODE8_1); GPIOA->OSPEEDR|=GPIO_OSPEEDER_OSPEEDR0|GPIO_OSPEEDER_OSPEEDR1|GPIO_OSPEEDER_OSPEEDR4|GPIO_OSPEEDER_OSPEEDR8; RST_LOW; // RESET=0 delay(10); // wait for 10ms RST_HIGH; delay(50); //wait for >40 ms ST7920_SendCmd(0x30); // 8bit mode delayUs(110); // >100us delay ST7920_SendCmd(0x30); // 8bit mode delayUs(40); // >37us delay ST7920_SendCmd(0x08); // D=0, C=0, B=0 delayUs(110); // >100us delay ST7920_SendCmd(0x01); // clear screen delay(12); // >10 ms delay ST7920_SendCmd(0x06); // cursor increment right no shift delay(1); // 1ms delay ST7920_SendCmd(0x0C); // D=1, C=0, B=0 delay(1); // 1ms delay ST7920_SendCmd(0x02); // return to home delay(1); // 1ms delay }
Since we are using serial mode, we need to use software SPI to send data to the LCD as following:
static void SendByteSPI(uint8_t byte) { for(int i=0;i<8;i++) { if((byte<<i)&0x80) { SID_HIGH; // SID=1 OR MOSI } else {SID_LOW;} // SID=0 SCK_LOW; // SCLK =0 OR SCK SCK_HIGH; // SCLK=1 } }
For sending data and command as following:
static void ST7920_SendCmd (uint8_t cmd) { CS_HIGH; // PUll the CS high SendByteSPI(0xf8+(0<<1)); // send the SYNC + RS(0) SendByteSPI(cmd&0xf0); // send the higher nibble first SendByteSPI((cmd<<4)&0xf0); // send the lower nibble delayUs(50); CS_LOW; // PUll the CS LOW } static void ST7920_SendData (uint8_t data) { CS_HIGH; SendByteSPI(0xf8+(1<<1)); // send the SYNC + RS(1) SendByteSPI(data&0xf0); // send the higher nibble first SendByteSPI((data<<4)&0xf0); // send the lower nibble delayUs(50); CS_LOW; // PUll the CS LOW }
For displaying text as following:
void ST7920_SendString(int row, int col, char* string) { switch (row) { case 0: col |= 0x80; break; case 1: col |= 0x90; break; case 2: col |= 0x88; break; case 3: col |= 0x98; break; default: col |= 0x80; break; } ST7920_SendCmd(col); while (*string) { ST7920_SendData(*string++); } }
Switch to graphic mode:
void ST7920_GraphicMode (int enable) // 1-enable, 0-disable { if (enable == 1) { ST7920_SendCmd(0x30); // 8 bit mode delay (1); ST7920_SendCmd(0x34); // switch to Extended instructions delay (1); ST7920_SendCmd(0x36); // enable graphics delay (1); Graphic_Check = 1; // update the variable } else if (enable == 0) { ST7920_SendCmd(0x30); // 8 bit mode delay (1); Graphic_Check = 0; // update the variable } }
For drawing some graphics:
void ST7920_DrawBitmap(const unsigned char* graphic) { uint8_t x, y; for(y = 0; y < 64; y++) { if(y < 32) { for(x = 0; x < 8; x++) // Draws top half of the screen. { // In extended instruction mode, vertical and horizontal coordinates must be specified before sending data in. ST7920_SendCmd(0x80 | y); // Vertical coordinate of the screen is specified first. (0-31) ST7920_SendCmd(0x80 | x); // Then horizontal coordinate of the screen is specified. (0-8) ST7920_SendData(graphic[2*x + 16*y]); // Data to the upper byte is sent to the coordinate. ST7920_SendData(graphic[2*x+1 + 16*y]); // Data to the lower byte is sent to the coordinate. } } else { for(x = 0; x < 8; x++) // Draws bottom half of the screen. { // Actions performed as same as the upper half screen. ST7920_SendCmd(0x80 | (y-32)); // Vertical coordinate must be scaled back to 0-31 as it is dealing with another half of the screen. ST7920_SendCmd(0x88 | x); ST7920_SendData(graphic[2*x + 16*y]); ST7920_SendData(graphic[2*x+1 + 16*y]); } } } } // Update the display with the selected graphics void ST7920_Update(void) { ST7920_DrawBitmap(image); }
Clearing the display:
void ST7920_Clear() { if (Graphic_Check == 1) // if the graphic mode is set { uint8_t x, y; for(y = 0; y < 64; y++) { if(y < 32) { ST7920_SendCmd(0x80 | y); ST7920_SendCmd(0x80); } else { ST7920_SendCmd(0x80 | (y-32)); ST7920_SendCmd(0x88); } for(x = 0; x < 8; x++) { ST7920_SendData(0); ST7920_SendData(0); } } } else { ST7920_SendCmd(0x01); // clear the display using command //delay(2); // delay >1.6 ms } }
Extra functions needed:
// set Pixel void SetPixel(uint8_t x, uint8_t y) { if (y < numRows && x < numCols) { uint8_t *p = image + ((y * (numCols/8)) + (x/8)); *p |= 0x80u >> (x%8); *image = *p; // Change the dirty rectangle to account for a pixel being dirty (we assume it was changed) if (startRow > y) { startRow = y; } if (endRow <= y) { endRow = y + 1; } if (startCol > x) { startCol = x; } if (endCol <= x) { endCol = x + 1; } } } /* draw a line * start point (X0, Y0) * end point (X1, Y1) */ void DrawLine(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) { int dx = (x1 >= x0) ? x1 - x0 : x0 - x1; int dy = (y1 >= y0) ? y1 - y0 : y0 - y1; int sx = (x0 < x1) ? 1 : -1; int sy = (y0 < y1) ? 1 : -1; int err = dx - dy; for (;;) { SetPixel(x0, y0); if (x0 == x1 && y0 == y1) break; int e2 = err + err; if (e2 > -dy) { err -= dy; x0 += sx; } if (e2 < dx) { err += dx; y0 += sy; } } } /* Draw rectangle * start point (x,y) * w -> width * h -> height */ void DrawRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { /* Check input parameters */ if ( x >= numCols || y >= numRows ) { /* Return error */ return; } /* Check width and height */ if ((x + w) >= numCols) { w = numCols - x; } if ((y + h) >= numRows) { h = numRows - y; } /* Draw 4 lines */ DrawLine(x, y, x + w, y); /* Top line */ DrawLine(x, y + h, x + w, y + h); /* Bottom line */ DrawLine(x, y, x, y + h); /* Left line */ DrawLine(x + w, y, x + w, y + h); /* Right line */ } /* Draw filled rectangle * Start point (x,y) * w -> width * h -> height */ void DrawFilledRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { uint8_t i; /* Check input parameters */ if ( x >= numCols || y >= numRows ) { /* Return error */ return; } /* Check width and height */ if ((x + w) >= numCols) { w = numCols - x; } if ((y + h) >= numRows) { h = numRows - y; } /* Draw lines */ for (i = 0; i <= h; i++) { /* Draw lines */ DrawLine(x, y + i, x + w, y + i); } } /* draw circle * centre (x0,y0) * radius = radius */ void DrawCircle(uint8_t x0, uint8_t y0, uint8_t radius) { int f = 1 - (int)radius; int ddF_x = 1; int ddF_y = -2 * (int)radius; int x = 0; SetPixel(x0, y0 + radius); SetPixel(x0, y0 - radius); SetPixel(x0 + radius, y0); SetPixel(x0 - radius, y0); int y = radius; while(x < y) { if(f >= 0) { y--; ddF_y += 2; f += ddF_y; } x++; ddF_x += 2; f += ddF_x; SetPixel(x0 + x, y0 + y); SetPixel(x0 - x, y0 + y); SetPixel(x0 + x, y0 - y); SetPixel(x0 - x, y0 - y); SetPixel(x0 + y, y0 + x); SetPixel(x0 - y, y0 + x); SetPixel(x0 + y, y0 - x); SetPixel(x0 - y, y0 - x); } } // Draw Filled Circle void DrawFilledCircle(int16_t x0, int16_t y0, int16_t r) { int16_t f = 1 - r; int16_t ddF_x = 1; int16_t ddF_y = -2 * r; int16_t x = 0; int16_t y = r; SetPixel(x0, y0 + r); SetPixel(x0, y0 - r); SetPixel(x0 + r, y0); SetPixel(x0 - r, y0); DrawLine(x0 - r, y0, x0 + r, y0); while (x < y) { if (f >= 0) { y--; ddF_y += 2; f += ddF_y; } x++; ddF_x += 2; f += ddF_x; DrawLine(x0 - x, y0 + y, x0 + x, y0 + y); DrawLine(x0 + x, y0 - y, x0 - x, y0 - y); DrawLine(x0 + y, y0 + x, x0 - y, y0 + x); DrawLine(x0 + y, y0 - x, x0 - y, y0 - x); } } // Draw Traingle with coordimates (x1, y1), (x2, y2), (x3, y3) void DrawTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3) { /* Draw lines */ DrawLine(x1, y1, x2, y2); DrawLine(x2, y2, x3, y3); DrawLine(x3, y3, x1, y1); } // Draw Filled Traingle with coordimates (x1, y1), (x2, y2), (x3, y3) void DrawFilledTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3) { int16_t deltax = 0, deltay = 0, x = 0, y = 0, xinc1 = 0, xinc2 = 0, yinc1 = 0, yinc2 = 0, den = 0, num = 0, numadd = 0, numpixels = 0, curpixel = 0; #define ABS(x) ((x) > 0 ? (x) : -(x)) deltax = ABS(x2 - x1); deltay = ABS(y2 - y1); x = x1; y = y1; if (x2 >= x1) { xinc1 = 1; xinc2 = 1; } else { xinc1 = -1; xinc2 = -1; } if (y2 >= y1) { yinc1 = 1; yinc2 = 1; } else { yinc1 = -1; yinc2 = -1; } if (deltax >= deltay){ xinc1 = 0; yinc2 = 0; den = deltax; num = deltax / 2; numadd = deltay; numpixels = deltax; } else { xinc2 = 0; yinc1 = 0; den = deltay; num = deltay / 2; numadd = deltax; numpixels = deltay; } for (curpixel = 0; curpixel <= numpixels; curpixel++) { DrawLine(x, y, x3, y3); num += numadd; if (num >= den) { num -= den; x += xinc1; y += yinc1; } x += xinc2; y += yinc2; } }
You may download the entire code from here:
4. Demo:
Happy coding 🙂
3 Comments
Hi, nice work
thank you
how to set cursor position at particular row and column position
Hi,
take a look at the revised version of this guide.
https://blog.embeddedexpert.io/?p=2030
Add Comment