675
/* * KS0108.c * * Created on: May 13, 2024 * Author: PC-MAGHREBI */ #include "KS0108.h" // Delay in milliseconds void _delay_ms(uint32_t ms) { HAL_Delay(ms); } // Delay in microseconds void _delay_us(uint32_t us) { volatile uint32_t delay = us * (SystemCoreClock / 1000000) / 5; // Adjust the divisor to fit your system while (delay--); } //----- Auxiliary data ------// uint8_t __GLCD_Buffer[__GLCD_Screen_Width][__GLCD_Screen_Lines]; GLCD_t __GLCD; #define __GLCD_XtoChip(X) ((X < (__GLCD_Screen_Width / __GLCD_Screen_Chips)) ? Chip_1 : Chip_2) #define __GLCD_Min(X, Y) ((X < Y) ? X : Y) #define __GLCD_AbsDiff(X, Y) ((X > Y) ? (X - Y) : (Y - X)) #define __GLCD_Swap(X, Y) do { typeof(X) t = X; X = Y; Y = t; } while (0) //---------------------------// //----- Prototypes ----------------------------// static void GLCD_Send(const uint8_t Data); static void GLCD_WaitBusy(enum Chip_t Chip); static void GLCD_BufferWrite(const uint8_t X, const uint8_t Y, const uint8_t Data); static uint8_t GLCD_BufferRead(const uint8_t X, const uint8_t Y); static void GLCD_SelectChip(enum Chip_t Chip); static void __GLCD_GotoX(const uint8_t X); static void __GLCD_GotoY(const uint8_t Y); static void GLCD_DrawHLine(uint8_t X1, uint8_t X2, const uint8_t Y, enum Color_t Color); static void GLCD_DrawVLine(uint8_t Y1, uint8_t Y2, const uint8_t X, enum Color_t Color); static void Int2bcd(int32_t Value, char BCD[]); static inline void Pulse_En(void); //---------------------------------------------// //----- Functions -------------// void GLCD_SendCommand(const uint8_t Command, enum Chip_t Chip) { //Check if busy if (Chip != Chip_All) { GLCD_WaitBusy(Chip); } else { GLCD_WaitBusy(Chip_1); GLCD_WaitBusy(Chip_2); } GLCD_SelectChip(Chip); DigitalWrite(GPIOB,GLCD_DI, Low); //RS = 0 DigitalWrite(GPIOB,GLCD_RW, Low); //RW = 0 //Send data GLCD_Send(Command); } void GLCD_SendData(const uint8_t Data, enum Chip_t Chip) { //Check if busy if (Chip != Chip_All) { GLCD_WaitBusy(Chip); } else { GLCD_WaitBusy(Chip_1); GLCD_WaitBusy(Chip_2); } GLCD_SelectChip(Chip); DigitalWrite(GPIOB,GLCD_DI, High); //RS = 1 DigitalWrite(GPIOB,GLCD_RW, Low); //RW = 0 //Send data GLCD_Send(__GLCD.Mode == GLCD_Non_Inverted ? Data : ~Data); __GLCD.X++; if (__GLCD.X == (__GLCD_Screen_Width / __GLCD_Screen_Chips)) __GLCD_GotoX(__GLCD.X); else if (__GLCD.X >= __GLCD_Screen_Width) __GLCD.X = __GLCD_Screen_Width - 1; } void GLCD_Setup(void) { //Setup pins PinMode(GPIOA,GLCD_D0, Output); //GLCD pins = Outputs PinMode(GPIOA,GLCD_D1, Output); PinMode(GPIOA,GLCD_D2, Output); PinMode(GPIOA,GLCD_D3, Output); PinMode(GPIOA,GLCD_D4, Output); PinMode(GPIOA,GLCD_D5, Output); PinMode(GPIOA,GLCD_D6, Output); PinMode(GPIOA,GLCD_D7, Output); PinMode(GPIOB,GLCD_CS1, Output); PinMode(GPIOB,GLCD_CS2, Output); PinMode(GPIOB,GLCD_DI, Output); PinMode(GPIOB,GLCD_EN, Output); PinMode(GPIOB,GLCD_RW, Output); PinMode(GPIOB,GLCD_RST, Output); DigitalWrite(GPIOB,GLCD_DI, Low); //GLCD pins = 0 DigitalWrite(GPIOB,GLCD_RW, Low); DigitalWrite(GPIOB,GLCD_EN, Low); DigitalWrite(GPIOB,GLCD_RST, Low); //!RST _delay_ms(5); DigitalWrite(GPIOB,GLCD_RST, High); _delay_ms(50); //Initialize chips GLCD_SendCommand(__GLCD_Command_On, Chip_All); GLCD_SendCommand(__GLCD_Command_Display_Start, Chip_All); //Go to 0,0 GLCD_GotoXY(0, 0); //Reset GLCD structure __GLCD.Mode = GLCD_Non_Inverted; __GLCD.X = __GLCD.Y = __GLCD.Font.Width = __GLCD.Font.Height = __GLCD.Font.Lines = 0; } void GLCD_Render(void) { uint8_t i, j; for (j = 0 ; j < __GLCD_Screen_Height ; j += __GLCD_Screen_Line_Height) { __GLCD_GotoX(0); __GLCD_GotoY(j); for (i = 0 ; i < __GLCD_Screen_Width ; i++) GLCD_SendData(GLCD_BufferRead(i, __GLCD.Y), __GLCD_XtoChip(i)); } } void GLCD_InvertMode(void) { if (__GLCD.Mode == GLCD_Inverted) __GLCD.Mode = GLCD_Non_Inverted; else __GLCD.Mode = GLCD_Inverted; } void GLCD_Clear(void) { GLCD_FillScreen(__GLCD.Mode == GLCD_Non_Inverted ? GLCD_White : GLCD_Black); } void GLCD_ClearLine(const uint8_t Line) { if (Line < __GLCD_Screen_Lines) { uint8_t i, color; i = 0; color = __GLCD.Mode == GLCD_Non_Inverted ? GLCD_White : GLCD_Black; GLCD_GotoXY(0, Line * __GLCD_Screen_Line_Height); for (i = 0 ; i < __GLCD_Screen_Width ; i++) GLCD_BufferWrite(i, __GLCD.Y, color); } } void GLCD_GotoX(const uint8_t X) { if (X < __GLCD_Screen_Width) __GLCD.X = X; } void GLCD_GotoY(const uint8_t Y) { if (__GLCD.Y < __GLCD_Screen_Height) __GLCD.Y = Y; } void GLCD_GotoXY(const uint8_t X, const uint8_t Y) { GLCD_GotoX(X); GLCD_GotoY(Y); } void GLCD_GotoLine(const uint8_t Line) { if (Line < __GLCD_Screen_Lines) __GLCD.Y = Line * __GLCD_Screen_Line_Height; } uint8_t GLCD_GetX(void) { return __GLCD.X; } uint8_t GLCD_GetY(void) { return __GLCD.Y; } uint8_t GLCD_GetLine(void) { return (__GLCD.Y / __GLCD_Screen_Line_Height); } void GLCD_SetPixel(const uint8_t X, const uint8_t Y, enum Color_t Color) { uint8_t data = 0; //Goto to point GLCD_GotoXY(X, Y); //Read data data = GLCD_BufferRead(__GLCD.X, __GLCD.Y); //Set pixel if (Color == GLCD_Black) BitSet(data, Y % 8); else BitClear(data, Y % 8); //Sent data GLCD_BufferWrite(__GLCD.X, __GLCD.Y, data); } void GLCD_SetPixels(const uint8_t X1, uint8_t Y1, const uint8_t X2, const uint8_t Y2, enum Color_t Color) { if ((X1 < __GLCD_Screen_Width) && (X2 < __GLCD_Screen_Width) && (Y1 < __GLCD_Screen_Height) && (Y2 < __GLCD_Screen_Height)) { uint8_t height, width, offset, mask, h, i, data; height = Y2 - Y1 + 1; width = X2 - X1 + 1; offset = Y1 % __GLCD_Screen_Line_Height; Y1 -= offset; mask = 0xFF; data = 0; //Calculate mask for top fractioned region if (height <(__GLCD_Screen_Line_Height - offset)) { mask >>=(__GLCD_Screen_Line_Height - height); h = height; } else h = __GLCD_Screen_Line_Height - offset; mask <<= offset; //Draw fractional rows at the top of the region GLCD_GotoXY(X1, Y1); for (i = 0 ; i < width ; i++) { //Read data = GLCD_BufferRead(__GLCD.X, __GLCD.Y); //Mask data = ((Color == GLCD_Black) ? (data | mask) : (data & ~mask)); //Write GLCD_BufferWrite(__GLCD.X++, __GLCD.Y, data); } //Full rows while ((h + __GLCD_Screen_Line_Height) <= height) { h += __GLCD_Screen_Line_Height; Y1 += __GLCD_Screen_Line_Height; GLCD_GotoXY(X1, Y1); for (i = 0 ; i < width ; i++) GLCD_BufferWrite(__GLCD.X++, __GLCD.Y, Color); } //Fractional rows at the bottom of the region if (h < height) { mask = ~(0xFF << (height - h)); GLCD_GotoXY(X1, Y1 + __GLCD_Screen_Line_Height); for (i = 0 ; i < width ; i++) { //Read data = GLCD_BufferRead(__GLCD.X, __GLCD.Y); //Mask data = ((Color == GLCD_Black) ? (data | mask) : (data & ~mask)); //Write GLCD_BufferWrite(__GLCD.X++, __GLCD.Y, data); } } } } void GLCD_DrawBitmap(const uint8_t *Bitmap, uint8_t Width, const uint8_t Height, enum PrintMode_t Mode) { uint16_t lines, bmpRead, bmpReadPrev; uint8_t x, y, y2, i, j, overflow, data, dataPrev; lines = bmpRead = bmpReadPrev = x = y = i = j = overflow = data = dataPrev = 0; //#1 - Save current position x = __GLCD.X; y = y2 = __GLCD.Y; //#2 - Read width - First two bytes data = __GLCD.X + Width; //"data" is used temporarily //If character exceed screen bounds, reduce if (data >= __GLCD_Screen_Width) Width -= data - __GLCD_Screen_Width; //#3 - Read height - Second two bytes - Convert to lines lines = (Height + __GLCD_Screen_Line_Height - 1) / __GLCD_Screen_Line_Height; //lines = Ceiling(A/B) = (A+B-1)/B data = __GLCD.Y / __GLCD_Screen_Line_Height + lines; //"data" is used temporarily //If bitmap exceed screen bounds, reduce if (data > __GLCD_Screen_Lines) lines -= data - __GLCD_Screen_Lines; //#4 - Calculate overflowing bits overflow = __GLCD.Y % __GLCD_Screen_Line_Height; //#5 - Print the character //Scan the lines needed for (j = 0 ; j < lines ; j++) { //Go to the start of the line GLCD_GotoXY(x, y); //Update the indices for reading the line bmpRead = j * Width; bmpReadPrev = bmpRead - Width; //Previous = 4 + (j - 1) * width = Current - width //Scan bytes of selected line for (i = 0 ; i < Width ; i++) { //Read byte data = pgm_read_byte(&(Bitmap[bmpRead++])); //Shift byte data <<= overflow; //Merge byte with previous one if (j > 0) { dataPrev = pgm_read_byte(&(Bitmap[bmpReadPrev++])); dataPrev >>= __GLCD_Screen_Line_Height - overflow; data |= dataPrev; } //Edit byte depending on the mode if (Mode == GLCD_Merge) data |= GLCD_BufferRead(__GLCD.X, __GLCD.Y); //Send byte GLCD_BufferWrite(__GLCD.X++, __GLCD.Y, data); } //Send an empty column of 1px in the end' if (__GLCD.Font.Mode == GLCD_Overwrite) data = GLCD_White; else data = GLCD_BufferRead(__GLCD.X, __GLCD.Y); GLCD_BufferWrite(__GLCD.X, __GLCD.Y, data); //Increase line counter y += __GLCD_Screen_Line_Height; } //#6 - Update last line, if needed if (lines > 1) { //Go to the start of the line GLCD_GotoXY(x, y); //Update the index for reading the last printed line bmpReadPrev = (j - 1) * Width; //Scan bytes of selected line for (i = 0 ; i < Width ; i++) { //Read byte data = GLCD_BufferRead(__GLCD.X, __GLCD.Y); //Merge byte with previous one dataPrev = pgm_read_byte(&(Bitmap[bmpReadPrev++])); dataPrev >>= __GLCD_Screen_Line_Height - overflow; data |= dataPrev; //Edit byte depending on the mode if (Mode == GLCD_Merge) data |= GLCD_BufferRead(__GLCD.X, __GLCD.Y); //Send byte GLCD_BufferWrite(__GLCD.X++, __GLCD.Y, data); } //Send an empty column of 1px in the end if (__GLCD.Font.Mode == GLCD_Overwrite) data = GLCD_White; else if (__GLCD.Font.Mode == GLCD_Merge) data = GLCD_BufferRead(__GLCD.X, __GLCD.Y); else data = ~GLCD_BufferRead(__GLCD.X, __GLCD.Y); GLCD_BufferWrite(__GLCD.X++, __GLCD.Y,data); } //Go to the upper-right corner of the printed bitmap GLCD_GotoXY(GLCD_GetX(), y2); } void GLCD_DrawLine(uint8_t X1, uint8_t Y1, uint8_t X2, uint8_t Y2, enum Color_t Color) { if ((X1 < __GLCD_Screen_Width) && (X2 < __GLCD_Screen_Width) && (Y1 < __GLCD_Screen_Height) && (Y2 < __GLCD_Screen_Height)) { if (X1 == X2) GLCD_DrawVLine(Y1, Y2, X1, Color); else if (Y1 == Y2) GLCD_DrawHLine(X1, X2, Y1, Color); else { uint8_t deltax, deltay, x, y, slope; int8_t error, ystep; slope = ((__GLCD_AbsDiff(Y1, Y2) > __GLCD_AbsDiff(X1,X2)) ? 1 : 0); if (slope) { //Swap x1, y1 __GLCD_Swap(X1, Y1); //Swap x2, y2 __GLCD_Swap(X2, Y2); } if (X1 > X2) { //Swap x1, x2 __GLCD_Swap(X1, X2); //Swap y1,y2 __GLCD_Swap(Y1, Y2); } deltax = X2 - X1; deltay = __GLCD_AbsDiff(Y2, Y1); error = deltax / 2; y = Y1; ystep = ((Y1 < Y2) ? 1 : -1); for (x=X1 ; x<=X2 ; x++) { if (slope) GLCD_SetPixel(y, x, Color); else GLCD_SetPixel(x, y, Color); error -= deltay; if (error < 0) { y = y + ystep; error = error + deltax; } } } } } void GLCD_DrawRectangle(const uint8_t X1, const uint8_t Y1, const uint8_t X2, const uint8_t Y2, enum Color_t Color) { if ((X1 < __GLCD_Screen_Width) && (X2 < __GLCD_Screen_Width) && (Y1 < __GLCD_Screen_Height) && (Y2 < __GLCD_Screen_Height)) { GLCD_DrawHLine(X1, X2, Y1, Color); GLCD_DrawHLine(X1, X2, Y2, Color); GLCD_DrawVLine(Y1, Y2, X1, Color); GLCD_DrawVLine(Y1, Y2, X2, Color); } } void GLCD_DrawRoundRectangle(const uint8_t X1, const uint8_t Y1, const uint8_t X2, const uint8_t Y2, const uint8_t Radius, enum Color_t Color) { if ((X1<__GLCD_Screen_Width) && (X2<__GLCD_Screen_Width) && (Y1<__GLCD_Screen_Height) && (Y2<__GLCD_Screen_Height)) { int16_t tSwitch = 3 - 2 * Radius; uint8_t width, height, x, y; width = X2-X1; height = Y2-Y1; x = 0; y = Radius; //Draw perimeter GLCD_DrawHLine(X1+Radius, X2-Radius, Y1, Color); //Top GLCD_DrawHLine(X1+Radius, X2-Radius, Y2, Color); //Bottom GLCD_DrawVLine(Y1+Radius, Y2-Radius, X1, Color); //Left GLCD_DrawVLine(Y1+Radius, Y2-Radius, X2, Color); //Right while (x <= y) { //Upper left corner GLCD_SetPixel(X1+Radius-x, Y1+Radius-y, Color); GLCD_SetPixel(X1+Radius-y, Y1+Radius-x, Color); //Upper right corner GLCD_SetPixel(X1+width-Radius+x, Y1+Radius-y, Color); GLCD_SetPixel(X1+width-Radius+y, Y1+Radius-x, Color); //Lower left corner GLCD_SetPixel(X1+Radius-x, Y1+height-Radius+y, Color); GLCD_SetPixel(X1+Radius-y, Y1+height-Radius+x, Color); //Lower right corner GLCD_SetPixel(X1+width-Radius+x, Y1+height-Radius+y, Color); GLCD_SetPixel(X1+width-Radius+y, Y1+height-Radius+x, Color); if (tSwitch < 0) tSwitch += 4 * x + 6; else { tSwitch += 4 * (x - y) + 10; y--; } x++; } } } void GLCD_DrawTriangle(const uint8_t X1, const uint8_t Y1, const uint8_t X2, const uint8_t Y2, const uint8_t X3, const uint8_t Y3, enum Color_t Color) { if (((X1 < __GLCD_Screen_Width) && (X2 < __GLCD_Screen_Width) && (X3 < __GLCD_Screen_Width) && (Y1 < __GLCD_Screen_Height) && (Y2 < __GLCD_Screen_Height) && (Y3 < __GLCD_Screen_Height))) { GLCD_DrawLine(X1, Y1, X2, Y2, Color); GLCD_DrawLine(X2, Y2, X3, Y3, Color); GLCD_DrawLine(X3, Y3, X1, Y1, Color); } }; void GLCD_DrawCircle(const uint8_t CenterX, const uint8_t CenterY, const uint8_t Radius, enum Color_t Color) { if (((CenterX + Radius) < __GLCD_Screen_Width) && ((CenterY + Radius) < __GLCD_Screen_Height)) { uint8_t x, y; int16_t xChange, radiusError; uint16_t yChange; x = Radius; y = 0; xChange = 1 - 2 * Radius; yChange = 1; radiusError = 0; while (x >= y) { GLCD_SetPixel(CenterX+x, CenterY+y, Color); GLCD_SetPixel(CenterX-x, CenterY+y, Color); GLCD_SetPixel(CenterX-x, CenterY-y, Color); GLCD_SetPixel(CenterX+x, CenterY-y, Color); GLCD_SetPixel(CenterX+y, CenterY+x, Color); GLCD_SetPixel(CenterX-y, CenterY+x, Color); GLCD_SetPixel(CenterX-y, CenterY-x, Color); GLCD_SetPixel(CenterX+y, CenterY-x, Color); y++; radiusError += yChange; yChange += 2; if ((2 * radiusError + xChange) > 0) { x--; radiusError += xChange; xChange += 2; } } } } void GLCD_FillScreen(enum Color_t Color) { uint8_t i, j; for (j = 0 ; j < __GLCD_Screen_Height ; j += __GLCD_Screen_Line_Height) for (i = 0 ; i < __GLCD_Screen_Width ; i++) GLCD_BufferWrite(i, j, Color); } void GLCD_FillRectangle(const uint8_t X1, const uint8_t Y1, const uint8_t X2, const uint8_t Y2, enum Color_t Color) { GLCD_SetPixels(X1, Y1, X2, Y2, Color); } void GLCD_FillRoundRectangle(const uint8_t X1, const uint8_t Y1, const uint8_t X2, const uint8_t Y2, const uint8_t Radius, enum Color_t Color) { if ((X1 < __GLCD_Screen_Width) && (X2 < __GLCD_Screen_Width) && (Y1 < __GLCD_Screen_Height) && (Y2 < __GLCD_Screen_Height)) { int16_t tSwitch = 3 - 2 * Radius; uint8_t width, height, x, y; width = X2 - X1; height = Y2 - Y1; x = 0; y = Radius; //Fill center block GLCD_FillRectangle(X1+Radius, Y1, X2 - Radius, Y2, Color); while (x <= y) { //Left side GLCD_DrawLine( X1 + Radius - x, Y1 + Radius - y, //Upper left corner upper half X1 + Radius - x, Y1 + height - Radius + y, //Lower left corner lower half Color); GLCD_DrawLine( X1 + Radius - y, Y1 + Radius - x, //Upper left corner lower half X1 + Radius - y, Y1 + height - Radius + x, //Lower left corner upper half Color); //Right side GLCD_DrawLine( X1 + width - Radius + x, Y1 + Radius - y, //Upper right corner upper half X1 + width - Radius + x, Y1 + height - Radius + y, //Lower right corner lower half Color); GLCD_DrawLine( X1 + width - Radius + y, Y1 + Radius - x, //Upper right corner lower half X1 + width - Radius + y, Y1 + height - Radius + x, //Lower right corner upper half Color); if (tSwitch < 0) { tSwitch += 4 * x +6; } else { tSwitch += 4 * (x - y) + 10; y--; } x++; } } } void GLCD_FillTriangle(uint8_t X1, uint8_t Y1, uint8_t X2, uint8_t y2, uint8_t X3, uint8_t Y3, enum Color_t Color) { if (((X1 < __GLCD_Screen_Width) && (X2 < __GLCD_Screen_Width) && (X3 < __GLCD_Screen_Width) && (Y1 < __GLCD_Screen_Height) && (y2 < __GLCD_Screen_Height) && (Y3 < __GLCD_Screen_Height))) { uint8_t sl, sx1, sx2; double m1, m2, m3; sl = sx1 = sx2 = m1 = m2 = m3 = 0; if (y2 > Y3) { __GLCD_Swap(X2, X3); __GLCD_Swap(y2, Y3); } if (Y1 > y2) { __GLCD_Swap(X1, X2); __GLCD_Swap(Y1, y2); } m1 = (double)(X1 - X2) / (Y1 - y2); m2 = (double)(X2 - X3) / (y2 - Y3); m3 = (double)(X3 - X1) / (Y3 - Y1); for(sl = Y1 ; sl <= y2 ; sl++) { sx1= m1 * (sl - Y1) + X1; sx2= m3 * (sl - Y1) + X1; if (sx1> sx2) __GLCD_Swap(sx1, sx2); GLCD_DrawLine(sx1, sl, sx2, sl, Color); } for (sl = y2 ; sl <= Y3 ; sl++) { sx1= m2 * (sl - Y3) + X3; sx2= m3 * (sl - Y1) + X1; if (sx1 > sx2) __GLCD_Swap(sx1, sx2); GLCD_DrawLine(sx1, sl, sx2, sl, Color); } } } void GLCD_FillCircle(const uint8_t CenterX, const uint8_t CenterY, const uint8_t Radius, enum Color_t Color) { if (((CenterX + Radius) < __GLCD_Screen_Width) && ((CenterY + Radius) < __GLCD_Screen_Height)) { int8_t f, ddF_x, ddF_y; uint8_t x, y; f = 1 - Radius; ddF_x = 1; ddF_y = -2 * Radius; x = 0; y = Radius; //Fill in the center between the two halves GLCD_DrawLine(CenterX, CenterY - Radius, CenterX, CenterY + Radius, Color); while(x < y) { //ddF_x = 2 * x + 1; //ddF_y = -2 * y; //f = x*x + y*y - radius*radius + 2*x - y + 1; if (f >= 0) { y--; ddF_y += 2; f += ddF_y; } x++; ddF_x += 2; f += ddF_x; //Now draw vertical lines between the points on the circle rather than //draw the points of the circle. This draws lines between the //perimeter points on the upper and lower quadrants of the 2 halves of the circle. GLCD_DrawVLine(CenterY + y, CenterY - y, CenterX + x, Color); GLCD_DrawVLine(CenterY + y, CenterY - y, CenterX - x, Color); GLCD_DrawVLine(CenterY + x, CenterY - x, CenterX + y, Color); GLCD_DrawVLine(CenterY + x, CenterY - x, CenterX - y, Color); } } } void GLCD_InvertRect(uint8_t X1, uint8_t Y1, uint8_t X2, uint8_t Y2) { uint8_t width, height, offset, mask, h, i, data; width = X2 - X1 + 1; height = Y2 - Y1 + 1; offset = Y1 % __GLCD_Screen_Line_Height; Y1 -= offset; mask = 0xFF; data = 0; //Calculate mask for top fractioned region if (height < (__GLCD_Screen_Line_Height - offset)) { mask >>= (__GLCD_Screen_Line_Height - height); h = height; } else h = __GLCD_Screen_Line_Height - offset; mask <<= offset; //Draw fractional rows at the top of the region GLCD_GotoXY(X1, Y1); for (i = 0 ; i < width ; i++) { data = GLCD_BufferRead(__GLCD.X, __GLCD.Y); data = ((~data) & mask) | (data & (~mask)); GLCD_BufferWrite(__GLCD.X++, __GLCD.Y, data); } //Full rows while ((h + __GLCD_Screen_Line_Height) <= height) { h += __GLCD_Screen_Line_Height; Y1 += __GLCD_Screen_Line_Height; GLCD_GotoXY(X1, Y1); for (i=0 ; i < width ; i++) { data = ~GLCD_BufferRead(__GLCD.X, __GLCD.Y); GLCD_BufferWrite(__GLCD.X++, __GLCD.Y, data); } } //Fractional rows at the bottom of the region if (h < height) { mask = ~(0xFF<<(height - h)); GLCD_GotoXY(X1, (Y1 + __GLCD_Screen_Line_Height)); for (i = 0 ; i < width ; i++) { data = GLCD_BufferRead(__GLCD.X, __GLCD.Y); data = ((~data) & mask) | (data & (~mask)); GLCD_BufferWrite(__GLCD.X++, __GLCD.Y, data); } } } void GLCD_SetFont(const uint8_t *Name, const uint8_t Width, const uint8_t Height, enum PrintMode_t Mode) { if ((Width < __GLCD_Screen_Width) && (Height < __GLCD_Screen_Height) && ((Mode == GLCD_Overwrite) || (Mode == GLCD_Merge))) { //Change font pointer to new font __GLCD.Font.Name = (uint8_t *)(Name); //Update font's size __GLCD.Font.Width = Width; __GLCD.Font.Height = Height; //Update lines required for a character to be fully displayed __GLCD.Font.Lines = (Height - 1) / __GLCD_Screen_Line_Height + 1; //Update blending mode __GLCD.Font.Mode = Mode; } } uint8_t GLCD_GetWidthChar(const char Character) { return (pgm_read_byte(&(__GLCD.Font.Name[(Character - 32) * (__GLCD.Font.Width * __GLCD.Font.Lines + 1)]))); } uint16_t GLCD_GetWidthString(const char *Text) { uint16_t width = 0; while (*Text) width += GLCD_GetWidthChar(*Text++); return width; } uint16_t GLCD_GetWidthString_P(const char *Text) { uint16_t width = 0; char r = pgm_read_byte(Text++); while (r) { width += GLCD_GetWidthChar(r); r = pgm_read_byte(Text++); } return width; } void GLCD_PrintChar(char Character) { uint16_t fontStart, fontRead, fontReadPrev; uint8_t x, y, y2, i, j, width, lines, overflow, data, dataPrev; fontStart = fontRead = fontReadPrev = x = y = y2 = i = j = width = lines = overflow = data = dataPrev = 0; //#1 - Save current position x = __GLCD.X; y = y2 = __GLCD.Y; //#2 - Remove leading empty characters Character -= 32; //32 is the ASCII of the first printable character //#3 - Find the start of the character in the font array fontStart = Character * (__GLCD.Font.Width * __GLCD.Font.Lines + 1); //+1 due to first byte of each array line being the width //#4 - Update width - First byte of each line is the width of the character width = pgm_read_byte(&(__GLCD.Font.Name[fontStart++])); data = __GLCD.X + width; //"data" is used temporarily //If character exceed screen bounds, reduce if (data >= __GLCD_Screen_Width) width -= data-__GLCD_Screen_Width; //#5 - Update lines lines = __GLCD.Font.Lines; data = __GLCD.Y / __GLCD_Screen_Line_Height + lines; //"data" is used temporarily //If character exceed screen bounds, reduce if (data > __GLCD_Screen_Lines) lines -= data - __GLCD_Screen_Lines; //#6 - Calculate overflowing bits overflow = __GLCD.Y % __GLCD_Screen_Line_Height; //#7 - Print the character //Scan the lines needed for (j = 0 ; j < lines ; j++) { //Go to the start of the line GLCD_GotoXY(x, y); //Update the indices for reading the line fontRead = fontStart + j; fontReadPrev = fontRead - 1; //Scan bytes of selected line for (i = 0 ; i < width ; i++) { //Read byte data = pgm_read_byte(&(__GLCD.Font.Name[fontRead])); //Shift byte data <<= overflow; //Merge byte with previous one if (j > 0) { dataPrev = pgm_read_byte(&(__GLCD.Font.Name[fontReadPrev])); dataPrev >>= __GLCD_Screen_Line_Height - overflow; data |= dataPrev; fontReadPrev += __GLCD.Font.Lines; } //Edit byte depending on the mode if (__GLCD.Font.Mode == GLCD_Merge) data |= GLCD_BufferRead(__GLCD.X, __GLCD.Y); //Send byte GLCD_BufferWrite(__GLCD.X++, __GLCD.Y, data); //Increase index fontRead += __GLCD.Font.Lines; } //Send an empty column of 1px in the end if (__GLCD.Font.Mode == GLCD_Overwrite) data = GLCD_White; else data = GLCD_BufferRead(__GLCD.X, __GLCD.Y); GLCD_BufferWrite(__GLCD.X, __GLCD.Y, data); //Increase line counter y += __GLCD_Screen_Line_Height; } //#8 - Update last line, if needed if (lines > 1) { //Go to the start of the line GLCD_GotoXY(x, y); //Update the index for reading the last printed line fontReadPrev = fontStart + j - 1; //Scan bytes of selected line for (i = 0 ; i < width ; i++) { //Read byte data = GLCD_BufferRead(__GLCD.X, __GLCD.Y); //Merge byte with previous one dataPrev = pgm_read_byte(&(__GLCD.Font.Name[fontReadPrev])); dataPrev >>= __GLCD_Screen_Line_Height - overflow; data |= dataPrev; //Edit byte depending on the mode if (__GLCD.Font.Mode == GLCD_Merge) data |= GLCD_BufferRead(__GLCD.X, __GLCD.Y); //Send byte GLCD_BufferWrite(__GLCD.X, __GLCD.Y, data); //Increase index fontReadPrev += __GLCD.Font.Lines; } //Send an empty column of 1px in the end if (__GLCD.Font.Mode == GLCD_Overwrite) data = GLCD_White; else if (__GLCD.Font.Mode == GLCD_Merge) data = GLCD_BufferRead(__GLCD.X, __GLCD.Y); else data = ~GLCD_BufferRead(__GLCD.X, __GLCD.Y); GLCD_BufferWrite(__GLCD.X++, __GLCD.Y,data); } //Move cursor to the end of the printed character GLCD_GotoXY(x + width + 1, y2); } void GLCD_PrintString(const char *Text) { while(*Text) { if ((__GLCD.X + __GLCD.Font.Width) >= __GLCD_Screen_Width) break; GLCD_PrintChar(*Text++); } } void GLCD_PrintString_P(const char *Text) { char r = pgm_read_byte(Text++); while(r) { if ((__GLCD.X + __GLCD.Font.Width) >= __GLCD_Screen_Width) break; GLCD_PrintChar(r); r = pgm_read_byte(Text++); } } void GLCD_PrintInteger(const int32_t Value) { if (Value == 0) { GLCD_PrintChar('0'); } else if ((Value > INT32_MIN) && (Value <= INT32_MAX)) { //int32_max + sign + null = 12 bytes char bcd[12] = { '\0' }; //Convert integer to array Int2bcd(Value, bcd); //Print from first non-zero digit GLCD_PrintString(bcd); } } void GLCD_PrintDouble(double Value, const uint32_t Tens) { if (Value == 0) { //Print characters individually so no string is stored in RAM GLCD_PrintChar('0'); GLCD_PrintChar('.'); GLCD_PrintChar('0'); } else if ((Value >= (-2147483647)) && (Value < 2147483648)) { //Print sign if (Value<0) { Value = -Value; GLCD_PrintChar('-'); } //Print integer part GLCD_PrintInteger(Value); //Print dot GLCD_PrintChar('.'); //Print decimal part GLCD_PrintInteger((Value - (uint32_t)(Value)) * Tens); } } static void GLCD_Send(const uint8_t Data) { //Send nibble DigitalWrite(GPIOA,GLCD_D0, BitCheck(Data, 0)); DigitalWrite(GPIOA,GLCD_D1, BitCheck(Data, 1)); DigitalWrite(GPIOA,GLCD_D2, BitCheck(Data, 2)); DigitalWrite(GPIOA,GLCD_D3, BitCheck(Data, 3)); DigitalWrite(GPIOA,GLCD_D4, BitCheck(Data, 4)); DigitalWrite(GPIOA,GLCD_D5, BitCheck(Data, 5)); DigitalWrite(GPIOA,GLCD_D6, BitCheck(Data, 6)); DigitalWrite(GPIOA,GLCD_D7, BitCheck(Data, 7)); Pulse_En(); } static void GLCD_WaitBusy(enum Chip_t Chip) { uint8_t status = 0; GLCD_SelectChip(Chip); //Busy pin = Input PinMode(GPIOA,GLCD_D7, Input); DigitalWrite(GPIOB,GLCD_DI, Low); DigitalWrite(GPIOB,GLCD_RW, High); DigitalWrite(GPIOB,GLCD_EN, Low); _delay_us(__GLCD_Pulse_En); //Send Enable pulse and wait till busy flag goes Low do { DigitalWrite(GPIOB,GLCD_EN, High); _delay_us(__GLCD_Pulse_En); status = DigitalRead(GPIOA,GLCD_D7) << 7; DigitalWrite(GPIOB,GLCD_EN, Low); _delay_us(__GLCD_Pulse_En<<3); } while(BitCheck(status, __GLCD_BUSY_FLAG)); DigitalWrite(GPIOB,GLCD_RW, Low); //Busy pin = Output PinMode(GPIOA,GLCD_D7, Output); } static void GLCD_BufferWrite(const uint8_t X, const uint8_t Y, const uint8_t Data) { //a>>3 = a/8 __GLCD_Buffer[X][Y>>3] = Data; } static uint8_t GLCD_BufferRead(const uint8_t X, const uint8_t Y) { //a>>3 = a/8 return (__GLCD_Buffer[X][Y>>3]); } static void GLCD_SelectChip(enum Chip_t Chip) { uint8_t on, off; #if (GLCD_ACTIVE_LOW != 0) on = 0; off = 1; #else on = 1; off = 0; #endif if(Chip == Chip_1) { DigitalWrite(GPIOB,GLCD_CS2, off); DigitalWrite(GPIOB,GLCD_CS1, on); } else if (Chip == Chip_2) { DigitalWrite(GPIOB,GLCD_CS1, off); DigitalWrite(GPIOB,GLCD_CS2, on); } else { DigitalWrite(GPIOB,GLCD_CS1, on); DigitalWrite(GPIOB,GLCD_CS2, on); } } static void __GLCD_GotoX(const uint8_t X) { if (X < __GLCD_Screen_Width) { uint8_t chip, cmd; //Update command chip = __GLCD_XtoChip(X); cmd = ((chip == Chip_1) ? (__GLCD_Command_Set_Address | X) : (__GLCD_Command_Set_Address | (X - __GLCD_Screen_Width / __GLCD_Screen_Chips))); //Update tracker __GLCD.X = X; //Send command GLCD_SendCommand(cmd, chip); } } static void __GLCD_GotoY(const uint8_t Y) { if (Y < __GLCD_Screen_Height) { uint8_t cmd; //Update command cmd = __GLCD_Command_Set_Page | (Y / __GLCD_Screen_Line_Height); //Update tracker __GLCD.Y = Y; //Send command GLCD_SendCommand(cmd, Chip_All); } } static inline void GLCD_DrawHLine(uint8_t X1, uint8_t X2, const uint8_t Y, enum Color_t Color) { if (X1 > X2) __GLCD_Swap(X1, X2); while (X1 <= X2) { GLCD_SetPixel(X1, Y, Color); X1++; } } static inline void GLCD_DrawVLine(uint8_t Y1, uint8_t Y2, const uint8_t X, enum Color_t Color) { if (Y1 > Y2) __GLCD_Swap(Y1, Y2); GLCD_SetPixels(X, Y1, X, Y2, Color); } static inline void Pulse_En(void) { DigitalWrite(GPIOB,GLCD_EN, High); _delay_us(__GLCD_Pulse_En); DigitalWrite(GPIOB,GLCD_EN, Low); _delay_us(__GLCD_Pulse_En); } static void Int2bcd(int32_t Value, char BCD[]) { uint8_t isNegative = 0; BCD[0] = BCD[1] = BCD[2] = BCD[3] = BCD[4] = BCD[5] = BCD[6] = BCD[7] = BCD[8] = BCD[9] = BCD[10] = '0'; if (Value < 0) { isNegative = 1; Value = -Value; } while (Value > 1000000000) { Value -= 1000000000; BCD[1]++; } while (Value >= 100000000) { Value -= 100000000; BCD[2]++; } while (Value >= 10000000) { Value -= 10000000; BCD[3]++; } while (Value >= 1000000) { Value -= 1000000; BCD[4]++; } while (Value >= 100000) { Value -= 100000; BCD[5]++; } while (Value >= 10000) { Value -= 10000; BCD[6]++; } while (Value >= 1000) { Value -= 1000; BCD[7]++; } while (Value >= 100) { Value -= 100; BCD[8]++; } while (Value >= 10) { Value -= 10; BCD[9]++; } while (Value >= 1) { Value -= 1; BCD[10]++; } uint8_t i = 0; //Find first non zero digit while (BCD[i] == '0') i++; //Add sign if (isNegative) { i--; BCD[i] = '-'; } //Shift array uint8_t end = 10 - i; uint8_t offset = i; i = 0; while (i <= end) { BCD[i] = BCD[i + offset]; i++; } BCD[i] = '\0'; } //-----------------------------//