Frank Duignan
/
NRF52832_ili9341
Basic driver working
display.cpp
- Committer:
- f3d
- Date:
- 2021-02-20
- Revision:
- 81:7087ba9d18bb
- Parent:
- 80:ff42f77928ad
File content as of revision 81:7087ba9d18bb:
#include "mbed.h" #include "display.h" #include "font5x7.h" extern Serial g_Serial_pc; //mosi,miso,clk,ss SPI spi(P0_25,P0_26,P0_27); DigitalOut Rst(P0_7); DigitalOut DC(P0_6); DigitalOut D_CS(P0_28); DigitalOut T_CS(P0_5); DigitalIn PenIRQ(P0_4); void Display::begin() { spi.format(8, 0); spi.frequency(8000000); T_CS = 1; // de-assert the touch interface for now D_CS = 1; resetDisplay(); LCD_Write_Data(0x21); // Set GVDD (varies contrast) LCD_Write_Cmd(0xC0); // Power control LCD_Write_Data(0x21); // Set GVDD (varies contrast) LCD_Write_Cmd(0xC1); // Power control LCD_Write_Data(0x10); // default value LCD_Write_Cmd(0xC5); //VCM control LCD_Write_Data(0x31); // default values LCD_Write_Data(0x3c); // LCD_Write_Cmd(0xC7); //VCM control2 LCD_Write_Data(0xc0); // default value LCD_Write_Cmd(0x36); // Memory Access Control LCD_Write_Data(0x48); // Set display orientation and RGB colour order LCD_Write_Cmd(0x3A); // Set Pixel format LCD_Write_Data(0x55); // To 16 bits per pixel LCD_Write_Cmd(0xB1); // Frame rate control LCD_Write_Data(0x00); // Use Fosc without divisor LCD_Write_Data(0x1B); // set 70Hz refresh rate LCD_Write_Cmd(0xB6); // Display Function Control LCD_Write_Data(0x00); // Use default values LCD_Write_Data(0x82); LCD_Write_Data(0x27); LCD_Write_Cmd(0x11); //Exit Sleep wait(0.120); LCD_Write_Cmd(0x29); //Display on LCD_Write_Cmd(0x2c); wait(0.005); fillRectangle(0,0,SCREEN_WIDTH,SCREEN_HEIGHT,RGBToWord(0x00,0,0)); D_CS = 1; } void Display::CommandMode() { DC = 0; } void Display::DataMode() { DC = 1; } void Display::LCD_Write_Cmd(uint8_t cmd) { CommandMode(); D_CS = 0; spi.write(cmd); D_CS = 1; } void Display::LCD_Write_Data(uint8_t data) { DataMode(); D_CS = 0; spi.write(data); D_CS = 1; } void Display::resetDisplay() { Rst = 0; wait(0.01); Rst = 1; wait(0.12); } void Display::openAperture(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2) { LCD_Write_Cmd(0x2a); LCD_Write_Data(x1>>8); LCD_Write_Data(x1); LCD_Write_Data(x2>>8); LCD_Write_Data(x2); LCD_Write_Cmd(0x2b); LCD_Write_Data(y1>>8); LCD_Write_Data(y1); LCD_Write_Data(y2>>8); LCD_Write_Data(y2); LCD_Write_Cmd(0x2c); } // Simple Pixel drawing routines void Display::putPixel(uint16_t x, uint16_t y, uint16_t Colour) { uint16_t rx; openAperture(x,y,x+1,y+1); DataMode(); D_CS = 0; spi.write((const char *) &Colour,2,(char *)&rx,0); D_CS = 1; } void Display::putImage(uint16_t xofs, uint16_t yofs, uint16_t width, uint16_t height, const uint16_t *Image) { uint16_t rx; uint16_t Colour; uint16_t x,y; for (y = 0; y < height; y++) { for (x=0; x < width; x++) { Colour = *(Image++); putPixel(x+xofs,y+yofs,Colour); } } } void Display::fillRectangle(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t Colour) { /* in an effort to reduce overhead here a large word buffer is created. If the number of words to to write is greater than or equal to the size of this buffer then it is filled with the relevant colour and used as the source of data in the spi.write function. This greatly speeds up the writing of large arease of the screen. The choice of buffer size is interesting. */ #define BUF_SIZE 32 uint16_t rx; uint16_t txBuffer[BUF_SIZE]; int count; openAperture(x,y,width,height); DataMode(); D_CS = 0; count = (width*height)/BUF_SIZE; if (count) { // big area for (int i=0;i<BUF_SIZE;i++) { txBuffer[i]=Colour; } while(count--) { spi.write((const char *)txBuffer,2*BUF_SIZE,(char *)&rx,0); } count = (width*height)%BUF_SIZE; // write remainder of block while (count--) { spi.write((const char *) &Colour,2,(char *)&rx,0); // write a block of colour } } else { // small area so write 1 word at a time. count = (width*height); while(count--) { spi.write((const char *) &Colour,2,(char *)&rx,0); } } D_CS = 1; } void Display::drawLineLowSlope(uint16_t x0, uint16_t y0, uint16_t x1,uint16_t y1, uint16_t Colour) { // Reference : https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm int dx = x1 - x0; int dy = y1 - y0; int yi = 1; if (dy < 0) { yi = -1; dy = -dy; } int D = 2*dy - dx; int y = y0; for (int x=x0; x <= x1;x++) { putPixel(x,y,Colour); if (D > 0) { y = y + yi; D = D - 2*dx; } D = D + 2*dy; } } void Display::drawLineHighSlope(uint16_t x0, uint16_t y0, uint16_t x1,uint16_t y1, uint16_t Colour) { // Reference : https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm int dx = x1 - x0; int dy = y1 - y0; int xi = 1; if (dx < 0) { xi = -1; dx = -dx; } int D = 2*dx - dy; int x = x0; for (int y=y0; y <= y1; y++) { putPixel(x,y,Colour); if (D > 0) { x = x + xi; D = D - 2*dy; } D = D + 2*dx; } } void Display::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t Colour) { // Reference : https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm D_CS = 0; if ( iabs(y1 - y0) < iabs(x1 - x0) ) { if (x0 > x1) { drawLineLowSlope(x1, y1, x0, y0, Colour); } else { drawLineLowSlope(x0, y0, x1, y1, Colour); } } else { if (y0 > y1) { drawLineHighSlope(x1, y1, x0, y0, Colour); } else { drawLineHighSlope(x0, y0, x1, y1, Colour); } } D_CS = 1; } uint16_t Display::RGBToWord(uint16_t R, uint16_t G, uint16_t B) { uint16_t rvalue = 0; rvalue += G >> 5; rvalue += (G & (0b111)) << 13; rvalue += (B >> 3) << 8; rvalue += (R >> 3) << 3; return rvalue; } void Display::drawRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t Colour) { drawLine(x,y,x+w,y,Colour); drawLine(x,y,x,y+h,Colour); drawLine(x+w,y,x+w,y+h,Colour); drawLine(x,y+h,x+w,y+h,Colour); } void Display::drawCircle(uint16_t x0, uint16_t y0, uint16_t radius, uint16_t Colour) { // Reference : https://en.wikipedia.org/wiki/Midpoint_circle_algorithm int x = radius-1; int y = 0; int dx = 1; int dy = 1; int err = dx - (radius << 1); if (radius > x0) return; // don't draw even parially off-screen circles if (radius > y0) return; // don't draw even parially off-screen circles if ((x0+radius) > SCREEN_WIDTH) return; // don't draw even parially off-screen circles if ((y0+radius) > SCREEN_HEIGHT) return; // don't draw even parially off-screen circles while (x >= y) { putPixel(x0 + x, y0 + y, Colour); putPixel(x0 + y, y0 + x, Colour); putPixel(x0 - y, y0 + x, Colour); putPixel(x0 - x, y0 + y, Colour); putPixel(x0 - x, y0 - y, Colour); putPixel(x0 - y, y0 - x, Colour); putPixel(x0 + y, y0 - x, Colour); putPixel(x0 + x, y0 - y, Colour); if (err <= 0) { y++; err += dy; dy += 2; } if (err > 0) { x--; dx += 2; err += dx - (radius << 1); } } } void Display::fillCircle(uint16_t x0, uint16_t y0, uint16_t radius, uint16_t Colour) { // Reference : https://en.wikipedia.org/wiki/Midpoint_circle_algorithm // Similar to drawCircle but fills the circle with lines instead int x = radius-1; int y = 0; int dx = 1; int dy = 1; int err = dx - (radius << 1); if (radius > x0) return; // don't draw even parially off-screen circles if (radius > y0) return; // don't draw even parially off-screen circles if ((x0+radius) > SCREEN_WIDTH) return; // don't draw even parially off-screen circles if ((y0+radius) > SCREEN_HEIGHT) return; // don't draw even parially off-screen circles while (x >= y) { drawLine(x0 - x, y0 + y,x0 + x, y0 + y, Colour); drawLine(x0 - y, y0 + x,x0 + y, y0 + x, Colour); drawLine(x0 - x, y0 - y,x0 + x, y0 - y, Colour); drawLine(x0 - y, y0 - x,x0 + y, y0 - x, Colour); if (err <= 0) { y++; err += dy; dy += 2; } if (err > 0) { x--; dx += 2; err += dx - (radius << 1); } } } void Display::print(const char *Text, uint16_t len, uint16_t x, uint16_t y, uint16_t ForeColour, uint16_t BackColour) { // This function draws each character individually. It uses an array called TextBox as a temporary storage // location to hold the dots for the character in question. It constructs the image of the character and then // calls on putImage to place it on the screen uint8_t Index = 0; uint8_t Row, Col; const uint8_t *CharacterCode = 0; uint16_t TextBox[FONT_WIDTH * FONT_HEIGHT]; for (Index = 0; Index < len; Index++) { CharacterCode = &Font5x7[FONT_WIDTH * (Text[Index] - 32)]; Col = 0; while (Col < FONT_WIDTH) { Row = 0; while (Row < FONT_HEIGHT) { if (CharacterCode[Col] & (1 << Row)) { TextBox[(Row * FONT_WIDTH) + Col] = ForeColour; } else { TextBox[(Row * FONT_WIDTH) + Col] = BackColour; } Row++; } Col++; } putImage(x, y, FONT_WIDTH, FONT_HEIGHT, (const uint16_t *)TextBox); x = x + FONT_WIDTH + 2; } } void Display::print(uint16_t Number, uint16_t x, uint16_t y, uint16_t ForeColour, uint16_t BackColour) { // This function converts the supplied number into a character string and then calls on puText to // write it to the display char Buffer[5]; // Maximum value = 65535 Buffer[4] = Number % 10 + '0'; Number = Number / 10; Buffer[3] = Number % 10 + '0'; Number = Number / 10; Buffer[2] = Number % 10 + '0'; Number = Number / 10; Buffer[1] = Number % 10 + '0'; Number = Number / 10; Buffer[0] = Number % 10 + '0'; print(Buffer, 5, x, y, ForeColour, BackColour); } void Display::initTouch(void) { // This display module has an XPT2046 touch screen controller connected over the SPI interface // The XPT2046 T_CS = 0; spi.write(0b10011000); // 8 bit conversion, ratiometric measurement, read XP //wait_us(100); uint16_t indata,outdata; outdata = 0xffff; spi.write((const char *) &outdata,2,(char *)&indata,2); // write a block of colour T_CS = 1; } uint16_t Display::readYTouch(void) { // This display module has an XPT2046 touch screen controller connected over the SPI interface // The XPT2046 /* Format of command word : b7 : must be 1 b6,5,4 : A2,A1,A0 000 : temperature b3 : Mode b2 : SER,DFR b1,0 : PD1,PD0 */ uint16_t indata,outdata; spi.frequency(2000000); // Can't run the touch screen controller at high speed T_CS = 0; spi.write((uint8_t)0b10011000); // 12 bit conversion, ratiometric measurement, reference off // read XP which tells us Y (see diagram in datasheet) wait_us(20); outdata = 0xffff; spi.write((const char *) &outdata,2,(char *)&indata,2); T_CS = 1; spi.frequency(8000000); int calibrated_return= (indata>>12)+((indata & 0xff)<<4); calibrated_return = (calibrated_return - Y_TOUCH_MIN); if (calibrated_return < 0) calibrated_return = 0; calibrated_return = (calibrated_return * SCREEN_HEIGHT) / (Y_TOUCH_MAX - Y_TOUCH_MIN) ; return calibrated_return; } uint16_t Display::readXTouch(void) { // This display module has an XPT2046 touch screen controller connected over the SPI interface // The XPT2046 /* Format of command word : b7 : must be 1 b6,5,4 : A2,A1,A0 000 : temperature b3 : Mode b2 : SER,DFR b1,0 : PD1,PD0 */ uint16_t indata,outdata; spi.frequency(2000000);// Can't run the touch screen controller at high speed T_CS = 0; spi.write((uint8_t)0b11011000); // 12 bit conversion, ratiometric measurement, reference off // read YP which tells us X (see diagram in datasheet) wait_us(20); outdata = 0xffff; spi.write((const char *) &outdata,2,(char *)&indata,2); T_CS = 1; spi.frequency(8000000); int calibrated_return= (indata>>12)+((indata & 0xff)<<4); calibrated_return = (calibrated_return - X_TOUCH_MIN); if (calibrated_return < 0) calibrated_return = 0; calibrated_return = (calibrated_return * SCREEN_WIDTH) / (X_TOUCH_MAX - X_TOUCH_MIN); // The X reading is backwards so need to subtract it from the screen width calibrated_return = SCREEN_WIDTH - calibrated_return; if (calibrated_return < 0) calibrated_return = 0; return calibrated_return; } int Display::penDown(void) { if (PenIRQ.read()==0) return 1; else return 0; }