diff --git a/src/nanoFramework.Graphics/Graphics/Displays/Display.h b/src/nanoFramework.Graphics/Graphics/Displays/Display.h index 35aa7d903e..83710472d6 100644 --- a/src/nanoFramework.Graphics/Graphics/Displays/Display.h +++ b/src/nanoFramework.Graphics/Graphics/Displays/Display.h @@ -8,8 +8,6 @@ #include "nanoCLR_Types.h" -#define LCD_COLOUR_RGB(R,G,B) ((CLR_UINT16)((((R) / 8) << 11) + (((G) / 4) << 5) + ((B) / 8))) - enum DisplayOrientation : CLR_INT16 { PORTRAIT, @@ -17,20 +15,22 @@ enum DisplayOrientation : CLR_INT16 LANDSCAPE, LANDSCAPE180 }; -enum PixelFormat : CLR_UINT8 { - FORMAT_RGB888 = 0, // Pixel format chosen is RGB888 : 24 bpp - FORMAT_RBG565 = 2, // Pixel format chosen is RGB565 : 16 bpp +enum PixelFormat : CLR_UINT8 +{ + FORMAT_RGB888 = 0, // Pixel format chosen is RGB888 : 24 bpp + FORMAT_RBG565 = 2, // Pixel format chosen is RGB565 : 16 bpp }; -enum PowerSaveState : CLR_UINT8 { +enum PowerSaveState : CLR_UINT8 +{ NORMAL = 0, SLEEP = 1 }; struct DisplayAttributes { - CLR_UINT8* TransferBuffer = NULL; + CLR_UINT8 *TransferBuffer = NULL; CLR_UINT32 TransferBufferSize; PowerSaveState PowerSave; - DisplayOrientation Orientation; //Future + DisplayOrientation Orientation; // Future CLR_INT16 Height; CLR_INT16 Width; CLR_INT16 BitsPerPixel; @@ -39,10 +39,6 @@ struct DisplayAttributes }; struct DisplayDriver { - //CLR_UINT32* g_FrameBuffer; // = NULL -- copied from netmg ( not sure it is needed based on new implementation) - //CLR_UINT32 g_FrameBufferSize; // = 0; -- copied from netmg ( not sure it is needed) - //bool g_LcdInitialized; // = false; ---copied from netmg ( not sure it is needed) - DisplayAttributes Attributes; void SetupDisplayAttributes(); @@ -50,12 +46,16 @@ struct DisplayDriver bool Uninitialize(); void Clear(); void DisplayBrightness(CLR_INT16 brightness); + bool SetWindow(CLR_INT16 x1, CLR_INT16 y1, CLR_INT16 x2, CLR_INT16 y2); void BitBlt(int x, int y, int width, int height, CLR_UINT32 data[]); void PowerSave(PowerSaveState powerState); void SetDefaultOrientation(); + bool ChangeOrientation(DisplayOrientation orientation); + CLR_UINT32 PixelsPerWord(); CLR_UINT32 WidthInWords(); CLR_UINT32 SizeInWords(); CLR_UINT32 SizeInBytes(); }; -#endif \ No newline at end of file + +#endif \ No newline at end of file diff --git a/src/nanoFramework.Graphics/Graphics/Displays/DisplayInterface.h b/src/nanoFramework.Graphics/Graphics/Displays/DisplayInterface.h index 81905b41ca..b9402e189e 100644 --- a/src/nanoFramework.Graphics/Graphics/Displays/DisplayInterface.h +++ b/src/nanoFramework.Graphics/Graphics/Displays/DisplayInterface.h @@ -8,35 +8,34 @@ #include "nanoCLR_Types.h" -// DIsplay configuration -union DisplayInterfaceConfig -{ - struct +// Display configuration +union DisplayInterfaceConfig { + struct { - CLR_INT8 spiBus; - GPIO_PIN chipSelect; - GPIO_PIN dataCommand; - GPIO_PIN reset; - GPIO_PIN backLight; + CLR_INT8 spiBus; + GPIO_PIN chipSelect; + GPIO_PIN dataCommand; + GPIO_PIN reset; + GPIO_PIN backLight; } Spi; - struct + struct { - CLR_INT8 i2cBus; - CLR_INT8 address; + CLR_INT8 i2cBus; + CLR_INT8 address; + CLR_INT8 fastMode; } I2c; }; - struct DisplayInterface { - void Initialize(DisplayInterfaceConfig& config); - void GetTransferBuffer(CLR_UINT8*& BufferAddress, CLR_UINT32& sizeInBytes); + void Initialize(DisplayInterfaceConfig &config); + void GetTransferBuffer(CLR_UINT8 *&BufferAddress, CLR_UINT32 &sizeInBytes); void ClearFrameBuffer(); - void WriteToFrameBuffer(CLR_UINT8 command, CLR_UINT8 data[], CLR_UINT32 dataCount); + void WriteToFrameBuffer(CLR_UINT8 command, CLR_UINT8 data[], CLR_UINT32 dataCount, CLR_UINT32 frameOffset = 0); void DisplayBacklight(bool on); // true = on void SendCommand(CLR_UINT8 arg_count, ...); void SendBytes(CLR_UINT8 *data, CLR_UINT32 length); + void SetCommandMode(int mode); }; - -#endif // _DISPLAY_INTERFACE_H_ +#endif // _DISPLAY_INTERFACE_H_ diff --git a/src/nanoFramework.Graphics/Graphics/Displays/ILI9341_240x320_SPI.cpp b/src/nanoFramework.Graphics/Graphics/Displays/ILI9341_240x320_SPI.cpp index 01a6eda3fe..279e6c897f 100644 --- a/src/nanoFramework.Graphics/Graphics/Displays/ILI9341_240x320_SPI.cpp +++ b/src/nanoFramework.Graphics/Graphics/Displays/ILI9341_240x320_SPI.cpp @@ -8,7 +8,6 @@ #include "DisplayInterface.h" #include "Display.h" - /* ILI9341 is a 262,144-color single-chip SOC driver for a-TFT liquid crystal display with resolution of 240RGBx320 dots, comprising a 720-channel source driver, a 320-channel gate driver, 172,800 bytes GRAM for graphic @@ -26,13 +25,13 @@ Power saving mode: This implementation was initially written for 16 bit colour. */ - /* Using the default endian order for transferring bytes Normal (MSB first, default) */ -#define CommandData(c) c,(CLR_UINT8 *)(CLR_UINT8[c]) // Macro to simplify visualisation of passing pointer to parameters; +#define CommandData(c) \ + c, (CLR_UINT8 *)(CLR_UINT8[c]) // Macro to simplify visualisation of passing pointer to parameters; struct DisplayDriver g_DisplayDriver; extern DisplayInterface g_DisplayInterface; @@ -54,6 +53,7 @@ enum ILI9341_CMD : CLR_UINT8 Partial_Area = 0x30, Memory_Access_Control = 0x36, Pixel_Format_Set = 0x3A, + Memory_Write_Continue = 0x3C, Write_Display_Brightness = 0x51, Frame_Rate_Control_Normal = 0xB1, Display_Function_Control = 0xB6, @@ -93,36 +93,70 @@ bool DisplayDriver::Initialize() // g_DisplayInterface.SendCommand(SOFTWARE_RESET); // g_DisplayInterface.SendCommand(Display_OFF); - g_DisplayInterface.SendCommand(4,Power_Control_B, 0x00, 0x83, 0X30); - g_DisplayInterface.SendCommand(5,Power_On_Sequence, 0x64, 0x03, 0X12, 0X81); - g_DisplayInterface.SendCommand(4,Driver_Timing_Control_A, 0x85, 0x01, 0x79); - g_DisplayInterface.SendCommand(6,Power_Control_A, 0x39, 0x2C, 0x00, 0x34, 0x02); - - g_DisplayInterface.SendCommand(2,Pump_Ratio_Control, 0x20); - g_DisplayInterface.SendCommand(3,Driver_Timing_Control_B, 0x00, 0x00); - g_DisplayInterface.SendCommand(2,Power_Control_1, 0x26); - g_DisplayInterface.SendCommand(2,Power_Control_2, 0x11); - g_DisplayInterface.SendCommand(3,VCOM_Control_1, 0x35, 0x3E); - g_DisplayInterface.SendCommand(2,VCOM_Control_2, 0xBE); - g_DisplayInterface.SendCommand(2,Memory_Access_Control, 0x28); // Portrait? - g_DisplayInterface.SendCommand(2,Pixel_Format_Set, 0x55); // 0x55 -> 16 bit - g_DisplayInterface.SendCommand(3,Frame_Rate_Control_Normal, 0x00, 0x1B); - g_DisplayInterface.SendCommand(2,Enable_3G, 0x08); - g_DisplayInterface.SendCommand(2,Gamma_Set, 0x01); // Gamma curve selected (0x01, 0x02, 0x04, 0x08) - g_DisplayInterface.SendCommand(16,Positive_Gamma_Correction, 0x1F, 0x1A, 0x18, 0x0A, 0x0F, 0x06, 0x45, 0X87, 0x32, 0x0A, 0x07, 0x02, 0x07, 0x05, 0x00); //gamma set 4 - g_DisplayInterface.SendCommand(16,Negative_Gamma_Correction, 0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3A, 0x78, 0x4D, 0x05, 0x18, 0x0D, 0x38, 0x3A, 0x1F); - g_DisplayInterface.SendCommand(5,Column_Address_Set, 0x00, 0x00, 0x00, 0xEF); // Size = 239 - g_DisplayInterface.SendCommand(5,Page_Address_Set, 0x00, 0x00, 0x01, 0x3f); // Size = 319 - g_DisplayInterface.SendCommand(1,Memory_Write); - g_DisplayInterface.SendCommand(2,Entry_Mode_Set, 0x07); // Entry mode set - g_DisplayInterface.SendCommand(5,Display_Function_Control, 0x0A, 0x82, 0x27, 0x00); - - g_DisplayInterface.SendCommand(1,Sleep_Out); - OS_DELAY(20); // Send Sleep Out command to display : no parameter - g_DisplayInterface.SendCommand(1,Display_ON); - OS_DELAY(20); // Send Sleep Out command to display : no parameter - g_DisplayInterface.SendCommand(1,NOP); // End of sequence - OS_DELAY(20); // Send Sleep Out command to display : no parameter + g_DisplayInterface.SendCommand(4, Power_Control_B, 0x00, 0x83, 0X30); + g_DisplayInterface.SendCommand(5, Power_On_Sequence, 0x64, 0x03, 0X12, 0X81); + g_DisplayInterface.SendCommand(4, Driver_Timing_Control_A, 0x85, 0x01, 0x79); + g_DisplayInterface.SendCommand(6, Power_Control_A, 0x39, 0x2C, 0x00, 0x34, 0x02); + + g_DisplayInterface.SendCommand(2, Pump_Ratio_Control, 0x20); + g_DisplayInterface.SendCommand(3, Driver_Timing_Control_B, 0x00, 0x00); + g_DisplayInterface.SendCommand(2, Power_Control_1, 0x26); + g_DisplayInterface.SendCommand(2, Power_Control_2, 0x11); + g_DisplayInterface.SendCommand(3, VCOM_Control_1, 0x35, 0x3E); + g_DisplayInterface.SendCommand(2, VCOM_Control_2, 0xBE); + g_DisplayInterface.SendCommand(2, Memory_Access_Control, 0x28); // Portrait? + g_DisplayInterface.SendCommand(2, Pixel_Format_Set, 0x55); // 0x55 -> 16 bit + g_DisplayInterface.SendCommand(3, Frame_Rate_Control_Normal, 0x00, 0x1B); + g_DisplayInterface.SendCommand(2, Enable_3G, 0x08); + g_DisplayInterface.SendCommand(2, Gamma_Set, 0x01); // Gamma curve selected (0x01, 0x02, 0x04, 0x08) + g_DisplayInterface.SendCommand( + 16, + Positive_Gamma_Correction, + 0x1F, + 0x1A, + 0x18, + 0x0A, + 0x0F, + 0x06, + 0x45, + 0X87, + 0x32, + 0x0A, + 0x07, + 0x02, + 0x07, + 0x05, + 0x00); // gamma set 4 + g_DisplayInterface.SendCommand( + 16, + Negative_Gamma_Correction, + 0x00, + 0x25, + 0x27, + 0x05, + 0x10, + 0x09, + 0x3A, + 0x78, + 0x4D, + 0x05, + 0x18, + 0x0D, + 0x38, + 0x3A, + 0x1F); + g_DisplayInterface.SendCommand(5, Column_Address_Set, 0x00, 0x00, 0x00, 0xEF); // Size = 239 + g_DisplayInterface.SendCommand(5, Page_Address_Set, 0x00, 0x00, 0x01, 0x3f); // Size = 319 + g_DisplayInterface.SendCommand(1, Memory_Write); + g_DisplayInterface.SendCommand(2, Entry_Mode_Set, 0x07); // Entry mode set + g_DisplayInterface.SendCommand(5, Display_Function_Control, 0x0A, 0x82, 0x27, 0x00); + + g_DisplayInterface.SendCommand(1, Sleep_Out); + OS_DELAY(20); // Send Sleep Out command to display : no parameter + g_DisplayInterface.SendCommand(1, Display_ON); + OS_DELAY(20); // Send Sleep Out command to display : no parameter + g_DisplayInterface.SendCommand(1, NOP); // End of sequence + OS_DELAY(20); // Send Sleep Out command to display : no parameter SetDefaultOrientation(); @@ -140,16 +174,36 @@ void DisplayDriver::SetupDisplayAttributes() return; } +bool DisplayDriver::ChangeOrientation(DisplayOrientation orientation) +{ + switch (orientation) + { + case PORTRAIT: + case PORTRAIT180: + return false; + + case LANDSCAPE: + case LANDSCAPE180: + Attributes.Height = Attributes.ShorterSide; + Attributes.Width = Attributes.LongerSide; + g_DisplayInterface.SendCommand( + 2, + Memory_Access_Control, + (MADCTL_MY | MADCTL_MX | MADCTL_MV | MADCTL_BGR)); // Landscape + BGR + break; + } + return true; +} + void DisplayDriver::SetDefaultOrientation() { - Attributes.Height = Attributes.ShorterSide; - Attributes.Width = Attributes.LongerSide; - g_DisplayInterface.SendCommand(2, Memory_Access_Control, 0xE8); // Landscape + ChangeOrientation(LANDSCAPE); } bool DisplayDriver::Uninitialize() { Clear(); + // Anything else to Uninitialize? return TRUE; } @@ -158,85 +212,143 @@ void DisplayDriver::PowerSave(PowerSaveState powerState) { switch (powerState) { - default: - // Illegal fall through to Power on - case PowerSaveState::NORMAL: - g_DisplayInterface.SendCommand(3,POWER_STATE, 0x00,0x00); // leave sleep mode - break; - case PowerSaveState::SLEEP: - g_DisplayInterface.SendCommand(3, POWER_STATE, 0x00,0x01); // enter sleep mode - break; + default: + // Illegal fall through to Power on + case PowerSaveState::NORMAL: + g_DisplayInterface.SendCommand(3, POWER_STATE, 0x00, 0x00); // leave sleep mode + break; + case PowerSaveState::SLEEP: + g_DisplayInterface.SendCommand(3, POWER_STATE, 0x00, 0x01); // enter sleep mode + break; } return; } void DisplayDriver::Clear() { - //reset the cursor pos to the begining - // Clear the ILI9341 controller - return; + // Clear the ILI9341 controller frame + SetWindow(0, 0, Attributes.Width - 1, Attributes.Height - 1); + + // Clear buffer + memset(Attributes.TransferBuffer, 0, Attributes.TransferBufferSize); + + int totalBytesToClear = Attributes.Width * Attributes.Height * 2; + int fullTransferBuffersCount = totalBytesToClear / Attributes.TransferBufferSize; + int remainderTransferBuffer = totalBytesToClear % Attributes.TransferBufferSize; + CLR_UINT8 command = Memory_Write; + for (int i = 0; i < fullTransferBuffersCount; i++) + { + g_DisplayInterface.WriteToFrameBuffer(command, Attributes.TransferBuffer, Attributes.TransferBufferSize); + command = Memory_Write_Continue; + } + + if (remainderTransferBuffer > 0) + { + g_DisplayInterface.WriteToFrameBuffer(command, Attributes.TransferBuffer, remainderTransferBuffer); + } } void DisplayDriver::DisplayBrightness(CLR_INT16 brightness) { _ASSERTE(brightness >= 0 && brightness <= 100); - g_DisplayInterface.SendCommand(2,Write_Display_Brightness, (CLR_UINT8)brightness); - return; + g_DisplayInterface.SendCommand(2, Write_Display_Brightness, (CLR_UINT8)brightness); +} +bool DisplayDriver::SetWindow(CLR_INT16 x1, CLR_INT16 y1, CLR_INT16 x2, CLR_INT16 y2) +{ + CLR_UINT8 Column_Address_Set_Data[4]; + Column_Address_Set_Data[0] = (x1 >> 8) & 0xFF; + Column_Address_Set_Data[1] = x1 & 0xFF; + Column_Address_Set_Data[2] = (x2 >> 8) & 0xFF; + Column_Address_Set_Data[3] = x2 & 0xFF; + g_DisplayInterface.SendCommand( + 5, + Column_Address_Set, + Column_Address_Set_Data[0], + Column_Address_Set_Data[1], + Column_Address_Set_Data[2], + Column_Address_Set_Data[3]); + + CLR_UINT8 Page_Address_Set_Data[4]; + Page_Address_Set_Data[0] = (y1 >> 8) & 0xFF; + Page_Address_Set_Data[1] = y1 & 0xFF; + Page_Address_Set_Data[2] = (y2 >> 8) & 0xFF; + Page_Address_Set_Data[3] = y2 & 0xFF; + g_DisplayInterface.SendCommand( + 5, + Page_Address_Set, + Page_Address_Set_Data[0], + Page_Address_Set_Data[1], + Page_Address_Set_Data[2], + Page_Address_Set_Data[3]); + return true; } void DisplayDriver::BitBlt(int x, int y, int width, int height, CLR_UINT32 data[]) { - // With the current design the Colour data is packed into the lower two bytes of each data array element // 16 bit colour RRRRRGGGGGGBBBBB mode 565 ASSERT((x >= 0) && ((x + width) <= Attributes.Width)); ASSERT((y >= 0) && ((y + height) <= Attributes.Height)); - CLR_UINT8* pui8Data = (CLR_UINT8*)data; - - CLR_UINT8 Column_Address_Set_Data[4]; - Column_Address_Set_Data[0] = (x >> 8) & 0xFF; - Column_Address_Set_Data[1] = x & 0xFF; - Column_Address_Set_Data[2] = ((x + width) >> 8) & 0xFF; - Column_Address_Set_Data[3] = (x + width) & 0xFF; - g_DisplayInterface.SendCommand(5, Column_Address_Set, Column_Address_Set_Data[0], Column_Address_Set_Data[1], Column_Address_Set_Data[2], Column_Address_Set_Data[3]); - - CLR_INT16 numberOfBytesPerPage = width * 2; //16 bit colour - CLR_INT16 TotalNumberOfPages = Attributes.Height; - CLR_INT16 NumberOfPagesPerTransferBuffer = Attributes.TransferBufferSize / numberOfBytesPerPage; // Maximum pages per buffer transfer - CLR_INT16 TotalNumberOfSpiTransfers = TotalNumberOfPages / NumberOfPagesPerTransferBuffer; - CLR_INT16 numberOfBytesPerTransfer = numberOfBytesPerPage * NumberOfPagesPerTransferBuffer; //16 bit colour - int iFirstPageOfTransfer = 0; - - for (int iSpiTransfer = 0; iSpiTransfer < TotalNumberOfSpiTransfers; iSpiTransfer++) { - - // Change endian for ILI9341 SPI mode and store in Spi transfer buffer before SPI Transfer - CLR_UINT8* transferBufferIndex = Attributes.TransferBuffer; - CLR_INT16 numberOf16BitWordsPerTransfer = numberOfBytesPerTransfer / 2; - for (int idataByte = 0; idataByte < numberOf16BitWordsPerTransfer; idataByte++) + + SetWindow(x, y, (x + width - 1), (y + height - 1)); + + CLR_UINT16 *StartOfLine_src = (CLR_UINT16 *)&data[0]; + + // Position to offset in data[] for start of window + CLR_UINT16 offset = (y * Attributes.Width) + x; + StartOfLine_src += offset; + + CLR_UINT8 *transferBufferIndex = Attributes.TransferBuffer; + CLR_UINT32 transferBufferCount = Attributes.TransferBufferSize; + CLR_UINT8 command = Memory_Write; + + while (height--) + { + CLR_UINT16 *src; + int xCount; + + src = StartOfLine_src; + xCount = width; + + while (xCount--) { - // Swap bytes of the word to match endianess of the ILI9341 - *transferBufferIndex = *(pui8Data + 1); - transferBufferIndex++; - *transferBufferIndex = *(pui8Data); - transferBufferIndex++; - pui8Data += 2; + CLR_UINT16 data = *src++; + *transferBufferIndex++ = (data >> 8); + *transferBufferIndex++ = data & 0xff; + transferBufferCount -= 2; + + // Send over SPI if no room for another 2 bytes + if (transferBufferCount < 1) + { + // Transfer buffer full, send it + g_DisplayInterface.WriteToFrameBuffer( + command, + Attributes.TransferBuffer, + (Attributes.TransferBufferSize - transferBufferCount)); + + // Reset transfer ptrs/count + transferBufferIndex = Attributes.TransferBuffer; + transferBufferCount = Attributes.TransferBufferSize; + command = Memory_Write_Continue; + } } - // Setup the area to update the ILI9341 - CLR_UINT8 Page_Address_Set_Data[4]; - Page_Address_Set_Data[0] = (iFirstPageOfTransfer >> 8) & 0xFF; - Page_Address_Set_Data[1] = iFirstPageOfTransfer & 0xFF; - Page_Address_Set_Data[2] = ((iFirstPageOfTransfer + NumberOfPagesPerTransferBuffer) >> 8) & 0xFF; - Page_Address_Set_Data[3] = (iFirstPageOfTransfer + NumberOfPagesPerTransferBuffer) & 0xFF; - g_DisplayInterface.SendCommand(5, Page_Address_Set, Page_Address_Set_Data[0], Page_Address_Set_Data[1], Page_Address_Set_Data[2], Page_Address_Set_Data[3]); - - // Send the data to the ILI9341 via SPI - g_DisplayInterface.WriteToFrameBuffer(Memory_Write, Attributes.TransferBuffer, numberOfBytesPerTransfer); + // Next row in data[] + StartOfLine_src += Attributes.Width; + } - iFirstPageOfTransfer += NumberOfPagesPerTransferBuffer; + // Send remaining data in transfer buffer to SPI + if (transferBufferCount < Attributes.TransferBufferSize) + { + // Transfer buffer full, send it + g_DisplayInterface.WriteToFrameBuffer( + command, + Attributes.TransferBuffer, + (Attributes.TransferBufferSize - transferBufferCount)); } + return; } @@ -259,5 +371,3 @@ CLR_UINT32 DisplayDriver::SizeInBytes() { return (SizeInWords() * sizeof(CLR_UINT32)); } - - diff --git a/src/nanoFramework.Graphics/Graphics/Displays/Otm8009a_DSI_Video_Mode.cpp b/src/nanoFramework.Graphics/Graphics/Displays/Otm8009a_DSI_Video_Mode.cpp index 05e253e6d6..ae346e0d59 100644 --- a/src/nanoFramework.Graphics/Graphics/Displays/Otm8009a_DSI_Video_Mode.cpp +++ b/src/nanoFramework.Graphics/Graphics/Displays/Otm8009a_DSI_Video_Mode.cpp @@ -16,54 +16,52 @@ // The OTM8009A is able to operate with low IO interface power supply and incorporate with several charge pumps // to generate various voltage levels that form an on - chip power management system for gate driverand source driver. - - // Packet Transmission -// ------------------- +// ------------------- // Display Command Set(DCS) is used from the MCU to the display module. // Both Short Packet(SPa) and Long packet(LPa) transmission is used. -// +// // REFERENCE : Section "6.2.4.2. Packet transmissions" of OTM8009A PDF by OriseTech // - struct DisplayDriver g_DisplayDriver; extern DisplayInterface g_DisplayInterface; - -// List of OTM8009A used commands -// Detailed in OTM8009A Data Sheet 'DATA_SHEET_OTM8009A_V0 92.pdf' -// Version of 14 June 2012 -#define COLMOD_RGB565 0x55 // COLMOD pixel format -#define OTM8009A_CMD_SLPOUT 0x11 // Sleep Out command -#define OTM8009A_CMD_DISPON 0x29 // Display On command -#define OTM8009A_CMD_RAMWR 0x2C // Memory (GRAM) write command -#define OTM8009A_CMD_RAMRD 0x2E // Memory (GRAM) read command -#define OTM8009A_CMD_WRTESCN 0x44 // Write Tearing Effect Scan line command -#define OTM8009A_CMD_WRCTRLD 0x53 // Write CTRL Display command -#define OTM8009A_CMD_WRCABC 0x55 // Write Content Adaptive Brightness command -#define OTM8009A_CMD_WRCABCMB 0x5E // Write CABC Minimum Brightness command -#define OTM8009A_CMD_WRDISBV 0x51 // Write Display Brightness command - -#define COLMOD 0x3A // Interface Pixel format command (12/16/18/24 bits per pixel) -#define CASET 0x2A // Column address set command (used to define area of frame memory where MCU can access) -#define PASET 0x2B // Page address set command (used to define area of frame memory where MCU can access) -#define MADCTR 0x36 // Memory Access control (defines read/ write scanning direction of frame memory) +// List of OTM8009A used commands +// Detailed in OTM8009A Data Sheet 'DATA_SHEET_OTM8009A_V0 92.pdf' +// Version of 14 June 2012 +#define COLMOD_RGB565 0x55 // COLMOD pixel format +#define OTM8009A_CMD_SLPOUT 0x11 // Sleep Out command +#define OTM8009A_CMD_DISPON 0x29 // Display On command +#define OTM8009A_CMD_RAMWR 0x2C // Memory (GRAM) write command +#define OTM8009A_CMD_RAMRD 0x2E // Memory (GRAM) read command +#define OTM8009A_CMD_WRTESCN 0x44 // Write Tearing Effect Scan line command +#define OTM8009A_CMD_WRCTRLD 0x53 // Write CTRL Display command +#define OTM8009A_CMD_WRCABC 0x55 // Write Content Adaptive Brightness command +#define OTM8009A_CMD_WRCABCMB 0x5E // Write CABC Minimum Brightness command +#define OTM8009A_CMD_WRDISBV 0x51 // Write Display Brightness command + +#define COLMOD 0x3A // Interface Pixel format command (12/16/18/24 bits per pixel) +#define CASET 0x2A // Column address set command (used to define area of frame memory where MCU can access) +#define PASET 0x2B // Page address set command (used to define area of frame memory where MCU can access) +#define MADCTR 0x36 // Memory Access control (defines read/ write scanning direction of frame memory) #define Register0xFF 0xFF #define Register0x00 0x00 +#define LCD_X_SIZE 800 +#define LCD_Y_SIZE 480 bool DisplayDriver::Initialize() { SetupDisplayAttributes(); - // Enter in command 2 mode and set EXTC to enable address shift function (0x00) + // Enter in command 2 mode and set EXTC to enable address shift function (0x00) g_DisplayInterface.SendCommand(2, 0x00, 0x00); - g_DisplayInterface.SendCommand(4, Register0xFF,0x80, 0x09, 0x01); + g_DisplayInterface.SendCommand(4, Register0xFF, 0x80, 0x09, 0x01); - // Enter ORISE Command 2 - g_DisplayInterface.SendCommand(2, 0x00, 0x00); //? + // Enter ORISE Command 2 + g_DisplayInterface.SendCommand(2, 0x00, 0x00); //? g_DisplayInterface.SendCommand(2, Register0x00, 0x80); g_DisplayInterface.SendCommand(3, Register0xFF, 0x80, 0x09); @@ -104,44 +102,44 @@ bool DisplayDriver::Initialize() g_DisplayInterface.SendCommand(2, 0x00, 0x00); g_DisplayInterface.SendCommand(2, 0xD9, 0x4E); - // Oscillator adjustment for Idle/Normal mode (LPDT only) set to 65Hz (default is 60Hz) + // Oscillator adjustment for Idle/Normal mode (LPDT only) set to 65Hz (default is 60Hz) g_DisplayInterface.SendCommand(2, 0x00, 0x81); g_DisplayInterface.SendCommand(2, 0xC1, 0x66); - // Video mode internal + // Video mode internal g_DisplayInterface.SendCommand(2, 0x00, 0xA1); g_DisplayInterface.SendCommand(2, 0xC1, 0x08); - // PWR_CTRL2 - 0xC590h - 147h parameter - Default 0x00 - // Set pump 4&5 x6 - // -> ONLY VALID when PUMP4_EN_ASDM_HV = "0" + // PWR_CTRL2 - 0xC590h - 147h parameter - Default 0x00 + // Set pump 4&5 x6 + // -> ONLY VALID when PUMP4_EN_ASDM_HV = "0" g_DisplayInterface.SendCommand(2, 0x00, 0x92); g_DisplayInterface.SendCommand(2, 0xC5, 0x01); - // PWR_CTRL2 - 0xC590h - 150th parameter - Default 0x33h - // Change pump4 clock ratio - // -> from 1 line to 1/2 line + // PWR_CTRL2 - 0xC590h - 150th parameter - Default 0x33h + // Change pump4 clock ratio + // -> from 1 line to 1/2 line g_DisplayInterface.SendCommand(2, 0x00, 0x95); g_DisplayInterface.SendCommand(2, 0xC5, 0x34); - // GVDD/NGVDD settings + // GVDD/NGVDD settings g_DisplayInterface.SendCommand(2, 0x00, 0x00); - g_DisplayInterface.SendCommand(3, 0xD8,0x79, 0x79 ); + g_DisplayInterface.SendCommand(3, 0xD8, 0x79, 0x79); - // PWR_CTRL2 - 0xC590h - 149th parameter - Default 0x33h - // Rewrite the default value ! + // PWR_CTRL2 - 0xC590h - 149th parameter - Default 0x33h + // Rewrite the default value ! g_DisplayInterface.SendCommand(2, 0x00, 0x94); g_DisplayInterface.SendCommand(2, 0xC5, 0x33); - // Panel display timing Setting 3 + // Panel display timing Setting 3 g_DisplayInterface.SendCommand(2, 0x00, 0xA3); g_DisplayInterface.SendCommand(2, 0xC0, 0x1B); - // Power control 1 + // Power control 1 g_DisplayInterface.SendCommand(2, 0x00, 0x82); g_DisplayInterface.SendCommand(2, 0xC5, 0x83); - // Source driver precharge + // Source driver precharge g_DisplayInterface.SendCommand(2, 0x00, 0x81); g_DisplayInterface.SendCommand(2, 0xC4, 0x83); @@ -150,15 +148,17 @@ bool DisplayDriver::Initialize() g_DisplayInterface.SendCommand(2, 0x00, 0xA6); - // GOAVST + // GOAVST g_DisplayInterface.SendCommand(2, 0x00, 0x80); g_DisplayInterface.SendCommand(7, 0xCE, 0x85, 0x01, 0x00, 0x84, 0x01, 0x00); g_DisplayInterface.SendCommand(2, 0x00, 0xA0); - g_DisplayInterface.SendCommand(15, 0xCE, 0x18, 0x04, 0x03, 0x39, 0x00, 0x00, 0x00, 0x18, 0x03, 0x03, 0x3A, 0x00, 0x00, 0x00); + g_DisplayInterface + .SendCommand(15, 0xCE, 0x18, 0x04, 0x03, 0x39, 0x00, 0x00, 0x00, 0x18, 0x03, 0x03, 0x3A, 0x00, 0x00, 0x00); g_DisplayInterface.SendCommand(2, 0x00, 0xB0); - g_DisplayInterface.SendCommand(15, 0xCE, 0x18, 0x02, 0x03, 0x3B, 0x00, 0x00, 0x00, 0x18, 0x01, 0x03, 0x3C, 0x00, 0x00, 0x00); + g_DisplayInterface + .SendCommand(15, 0xCE, 0x18, 0x02, 0x03, 0x3B, 0x00, 0x00, 0x00, 0x18, 0x01, 0x03, 0x3C, 0x00, 0x00, 0x00); g_DisplayInterface.SendCommand(2, 0x00, 0xC0); g_DisplayInterface.SendCommand(11, 0xCF, 0x01, 0x01, 0x20, 0x20, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00); @@ -170,19 +170,87 @@ bool DisplayDriver::Initialize() g_DisplayInterface.SendCommand(11, 0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); g_DisplayInterface.SendCommand(2, 0x00, 0x90); - g_DisplayInterface.SendCommand(16, 0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + g_DisplayInterface.SendCommand( + 16, + 0xCB, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00); g_DisplayInterface.SendCommand(2, 0x00, 0xA0); - g_DisplayInterface.SendCommand(16, 0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + g_DisplayInterface.SendCommand( + 16, + 0xCB, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00); g_DisplayInterface.SendCommand(2, 0x00, 0xB0); g_DisplayInterface.SendCommand(11, 0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); g_DisplayInterface.SendCommand(2, 0x00, 0xC0); - g_DisplayInterface.SendCommand(16, 0xCB, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + g_DisplayInterface.SendCommand( + 16, + 0xCB, + 0x00, + 0x04, + 0x04, + 0x04, + 0x04, + 0x04, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00); g_DisplayInterface.SendCommand(2, 0x00, 0xD0); - g_DisplayInterface.SendCommand(16, 0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00); + g_DisplayInterface.SendCommand( + 16, + 0xCB, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x04, + 0x04, + 0x04, + 0x04, + 0x04, + 0x00, + 0x00, + 0x00, + 0x00); g_DisplayInterface.SendCommand(2, 0x00, 0xE0); g_DisplayInterface.SendCommand(11, 0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); @@ -194,81 +262,185 @@ bool DisplayDriver::Initialize() g_DisplayInterface.SendCommand(11, 0xCC, 0x00, 0x26, 0x09, 0x0B, 0x01, 0x25, 0x00, 0x00, 0x00, 0x00); g_DisplayInterface.SendCommand(2, 0x00, 0x90); - g_DisplayInterface.SendCommand(16, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0A, 0x0C, 0x02); + g_DisplayInterface.SendCommand( + 16, + 0xCC, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x26, + 0x0A, + 0x0C, + 0x02); g_DisplayInterface.SendCommand(2, 0x00, 0xA0); - g_DisplayInterface.SendCommand(16, 0xCC, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + g_DisplayInterface.SendCommand( + 16, + 0xCC, + 0x25, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00); g_DisplayInterface.SendCommand(2, 0x00, 0xB0); g_DisplayInterface.SendCommand(11, 0xCC, 0x00, 0x25, 0x0C, 0x0A, 0x02, 0x26, 0x00, 0x00, 0x00, 0x00); g_DisplayInterface.SendCommand(2, 0x00, 0xC0); - g_DisplayInterface.SendCommand(16, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x0B, 0x09, 0x01); + g_DisplayInterface.SendCommand( + 16, + 0xCC, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x25, + 0x0B, + 0x09, + 0x01); g_DisplayInterface.SendCommand(2, 0x00, 0xD0); - g_DisplayInterface.SendCommand(16, 0xCC, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); - - // PWR_CTRL1 - 0xc580h - 130th parameter - default 0x00 - // Pump 1 min and max DM + g_DisplayInterface.SendCommand( + 16, + 0xCC, + 0x26, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00); + + // PWR_CTRL1 - 0xc580h - 130th parameter - default 0x00 + // Pump 1 min and max DM g_DisplayInterface.SendCommand(2, 0x00, 0x81); g_DisplayInterface.SendCommand(2, 0xC5, 0x66); g_DisplayInterface.SendCommand(2, 0x00, 0xB6); g_DisplayInterface.SendCommand(2, 0xF5, 0x06); - // CABC LEDPWM frequency adjusted to 19,5kHz + // CABC LEDPWM frequency adjusted to 19,5kHz g_DisplayInterface.SendCommand(2, 0x00, 0xB1); g_DisplayInterface.SendCommand(2, 0xC6, 0x06); - // Exit CMD2 mode + // Exit CMD2 mode g_DisplayInterface.SendCommand(2, 0x00, 0x00); g_DisplayInterface.SendCommand(4, 0xFF, 0xFF, 0xFF, 0xFF); - // NOP - goes back to DCS std command ? + // NOP - goes back to DCS std command ? g_DisplayInterface.SendCommand(2, 0x00, 0x00); - // Gamma correction 2.2+ table (HSDT possible) + // Gamma correction 2.2+ table (HSDT possible) g_DisplayInterface.SendCommand(2, 0x00, 0x00); - g_DisplayInterface.SendCommand(17, 0xE1, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, 0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A, 0x01); - - // Gamma correction 2.2- table (HSDT possible) + g_DisplayInterface.SendCommand( + 17, + 0xE1, + 0x00, + 0x09, + 0x0F, + 0x0E, + 0x07, + 0x10, + 0x0B, + 0x0A, + 0x04, + 0x07, + 0x0B, + 0x08, + 0x0F, + 0x10, + 0x0A, + 0x01); + + // Gamma correction 2.2- table (HSDT possible) g_DisplayInterface.SendCommand(2, 0x00, 0x00); - g_DisplayInterface.SendCommand(17, 0xE2, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, 0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A, 0x01); - - // Send Sleep Out command to display : no parameter + g_DisplayInterface.SendCommand( + 17, + 0xE2, + 0x00, + 0x09, + 0x0F, + 0x0E, + 0x07, + 0x10, + 0x0B, + 0x0A, + 0x04, + 0x07, + 0x0B, + 0x08, + 0x0F, + 0x10, + 0x0A, + 0x01); + + // Send Sleep Out command to display : no parameter g_DisplayInterface.SendCommand(2, OTM8009A_CMD_SLPOUT, 0x00); - // Wait for sleep out exit + // Wait for sleep out exit OS_DELAY(120); - // Set Pixel color format to RGB565 + // Set Pixel color format to RGB565 g_DisplayInterface.SendCommand(2, COLMOD, COLMOD_RGB565); - //* CABC : Content Adaptive Backlight Control section start >> - // Note : defaut is 0 (lowest Brightness), 0xFF is highest Brightness, try 0x7F : intermediate value + //* CABC : Content Adaptive Backlight Control section start >> + // Note : defaut is 0 (lowest Brightness), 0xFF is highest Brightness, try 0x7F : intermediate value g_DisplayInterface.SendCommand(2, OTM8009A_CMD_WRDISBV, 0x7F); - // defaut is 0, try 0x2C - Brightness Control Block, Display Dimming & BackLight on + // defaut is 0, try 0x2C - Brightness Control Block, Display Dimming & BackLight on g_DisplayInterface.SendCommand(2, OTM8009A_CMD_WRCTRLD, 0x2C); - // defaut is 0, try 0x02 - image Content based Adaptive Brightness [Still Picture] + // defaut is 0, try 0x02 - image Content based Adaptive Brightness [Still Picture] g_DisplayInterface.SendCommand(2, OTM8009A_CMD_WRCABC, 0x02); - // defaut is 0 (lowest Brightness), 0xFF is highest Brightness + // defaut is 0 (lowest Brightness), 0xFF is highest Brightness g_DisplayInterface.SendCommand(2, OTM8009A_CMD_WRCABCMB, 0xFF); - g_DisplayInterface.SendCommand(2, OTM8009A_CMD_DISPON, 0x00); // Send Command Display On - g_DisplayInterface.SendCommand(2, 0x00, 0x00); // NOP command + g_DisplayInterface.SendCommand(2, OTM8009A_CMD_DISPON, 0x00); // Send Command Display On + g_DisplayInterface.SendCommand(2, 0x00, 0x00); // NOP command - // Send Command GRAM memory write (no parameters) : this initiates frame write via other DSI commands sent by - // DSI host from LTDC incoming pixels in video mode + // Send Command GRAM memory write (no parameters) : this initiates frame write via other DSI commands sent by + // DSI host from LTDC incoming pixels in video mode g_DisplayInterface.SendCommand(2, OTM8009A_CMD_RAMWR, 0x00); - // Setup to Landscape + // Setup to default Orientation(Landscape) //___________________ SetDefaultOrientation(); - g_DisplayInterface.SendCommand(2, MADCTR, 0x60); - g_DisplayInterface.SendCommand(5, CASET, 0x00, 0x00, 0x03, 0x1F); - g_DisplayInterface.SendCommand(5, PASET, 0x00, 0x00, 0x01, 0xDF); + + g_DisplayInterface.SendCommand(5, CASET, 0x00, 0x00, (LCD_X_SIZE - 1) >> 8, (LCD_X_SIZE - 1) & 0xff); + g_DisplayInterface.SendCommand(5, PASET, 0x00, 0x00, (LCD_Y_SIZE - 1) >> 8, (LCD_Y_SIZE - 1) & 0xff); return 0; } @@ -276,34 +448,80 @@ bool DisplayDriver::Initialize() void DisplayDriver::SetupDisplayAttributes() { // Define the LCD/TFT resolution - Attributes.LongerSide = 800; - Attributes.ShorterSide = 480; + Attributes.LongerSide = LCD_X_SIZE; + Attributes.ShorterSide = LCD_Y_SIZE; Attributes.PowerSave = PowerSaveState::NORMAL; Attributes.BitsPerPixel = 16; + g_DisplayInterface.GetTransferBuffer(Attributes.TransferBuffer, Attributes.TransferBufferSize); +} + +bool DisplayDriver::ChangeOrientation(DisplayOrientation orientation) +{ + switch (orientation) + { + default: + return false; + + // Landscape only at the moment + case LANDSCAPE: + Attributes.Height = Attributes.ShorterSide; + Attributes.Width = Attributes.LongerSide; + g_DisplayInterface.SendCommand(2, MADCTR, 0x60); + break; + } + return true; } + void DisplayDriver::SetDefaultOrientation() { - Attributes.Height = Attributes.ShorterSide; - Attributes.Width = Attributes.LongerSide; - return; + ChangeOrientation(LANDSCAPE); +} + +void DisplayDriver::Clear() +{ + // Clear Frame buffer + g_DisplayInterface.ClearFrameBuffer(); } + void DisplayDriver::DisplayBrightness(CLR_INT16 brightness) { _ASSERTE(brightness >= 0 && brightness <= 100); } + void DisplayDriver::BitBlt(int x, int y, int width, int height, CLR_UINT32 data[]) { - UNUSED(x); - UNUSED(y); - UNUSED(width); - UNUSED(height); - - CLR_UINT8* pui8Data = (CLR_UINT8*)data; CLR_UINT32 dataSize = width * height * 2; - - g_DisplayInterface.WriteToFrameBuffer(0,pui8Data, dataSize); + if (dataSize == (LCD_X_SIZE * LCD_Y_SIZE * 2)) + { + // Full screen + g_DisplayInterface.WriteToFrameBuffer(0, (CLR_UINT8 *)data, dataSize, 0); + } + else + { + // Partial bitblt + CLR_UINT16 *p16data = (CLR_UINT16 *)&data[0]; + CLR_UINT32 srcOffset = (y * (CLR_UINT32)Attributes.Width) + x; + p16data += srcOffset; + + dataSize = width * 2; + + // Target 16bit offset in frame + CLR_UINT32 targetOffset = srcOffset; + + while (height--) + { + g_DisplayInterface.WriteToFrameBuffer(0, (CLR_UINT8 *)p16data, dataSize, targetOffset); + + // Next display row in data[] + p16data += Attributes.Width; + + // Next offset in target frame + targetOffset += Attributes.Width; + } + } } + CLR_UINT32 DisplayDriver::PixelsPerWord() { return (32 / Attributes.BitsPerPixel); @@ -319,4 +537,4 @@ CLR_UINT32 DisplayDriver::SizeInWords() CLR_UINT32 DisplayDriver::SizeInBytes() { return (SizeInWords() * sizeof(CLR_UINT32)); -} +} \ No newline at end of file diff --git a/src/nanoFramework.Graphics/Graphics/Displays/SSD1306_128x64.cpp b/src/nanoFramework.Graphics/Graphics/Displays/SSD1306_128x64.cpp new file mode 100644 index 0000000000..1c177e8f6f --- /dev/null +++ b/src/nanoFramework.Graphics/Graphics/Displays/SSD1306_128x64.cpp @@ -0,0 +1,253 @@ +// +// Copyright (c) .NET Foundation and Contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +#include "Graphics.h" +#include "DisplayInterface.h" +#include "Display.h" + +/* +SSD1306 ar small mono display of either 128x64 or 128x32 + +The interface is I2C + +*/ + +struct DisplayDriver g_DisplayDriver; +extern DisplayInterface g_DisplayInterface; + +enum SSD1306_CMD : CLR_UINT8 +{ + Set_Memory_Addressing_Mode = 0x20, + + // Set Column address - 0x21, start adr, end address + Set_Column_Address = 0x21, + + // Set Page address - 0x22, page start adr, page end address + Set_Page_address = 0x22, + + Memory_Write = 0x40, + + // Set contrast 0x81 xx + Set_Contrast = 0x81, + + // Enable / Disable charge pump + Charge_Pump = 0x8D, + + Set_Segment_remap127 = 0xA1, + + // Display Normal / Inverse + Set_Normal = 0xA6, + Set_Inversion = 0xA6, + + // Display On/Off + Display_OFF = 0xAE, + Display_ON = 0xAF, + + Set_COM_Scan_x = 0xC8 +}; + +enum _Orientation : CLR_UINT8 +{ + MADCTL_MH = 0x04, // sets the Horizontal Refresh, 0=Left-Right and 1=Right-Left + MADCTL_ML = 0x10, // sets the Vertical Refresh, 0=Top-Bottom and 1=Bottom-Top + MADCTL_MV = 0x20, // sets the Row/Column Swap, 0=Normal and 1=Swapped + MADCTL_MX = 0x40, // sets the Column Order, 0=Left-Right and 1=Right-Left + MADCTL_MY = 0x80, // sets the Row Order, 0=Top-Bottom and 1=Bottom-Top + + MADCTL_BGR = 0x08 // Blue-Green-Red pixel order, 0 = RGB, 1 = BGR +}; + +bool DisplayDriver::Initialize() +{ + // Required for SPI interface only, I2C does nothing + g_DisplayInterface.SetCommandMode(1); + + SetupDisplayAttributes(); + + // Initialize SSD1306 registers + g_DisplayInterface.SendCommand(2, SSD1306_CMD::Charge_Pump, 0x14); // Turn on the charge pump + g_DisplayInterface.SendCommand(2, SSD1306_CMD::Set_Memory_Addressing_Mode, 0x00); // Set horizontal addressing mode + + SetDefaultOrientation(); + + g_DisplayInterface.SendCommand(1, SSD1306_CMD::Display_ON); // Display on + + return true; +} +void DisplayDriver::SetupDisplayAttributes() +{ + // Define the Display resolution + Attributes.LongerSide = 128; + Attributes.ShorterSide = 64; + Attributes.PowerSave = PowerSaveState::NORMAL; + Attributes.BitsPerPixel = 1; + g_DisplayInterface.GetTransferBuffer(Attributes.TransferBuffer, Attributes.TransferBufferSize); + return; +} + +bool DisplayDriver::Uninitialize() +{ + Clear(); + // Anything else to Uninitialize? + return TRUE; +} +void DisplayDriver::PowerSave(PowerSaveState powerState) +{ + switch (powerState) + { + default: + // illegal fall through to Power on + case PowerSaveState::NORMAL: + // leave sleep mode + break; + case PowerSaveState::SLEEP: + // enter sleep mode + break; + } + return; +} + +void DisplayDriver::DisplayBrightness(CLR_INT16 brightness) +{ + _ASSERTE(brightness >= 0 && brightness <= 100); + + return; +} + +bool DisplayDriver::ChangeOrientation(DisplayOrientation orientation) +{ + switch (orientation) + { + case PORTRAIT: + case PORTRAIT180: + return false; + + case LANDSCAPE: + case LANDSCAPE180: + Attributes.Height = Attributes.ShorterSide; + Attributes.Width = Attributes.LongerSide; + + g_DisplayInterface.SendCommand(1, SSD1306_CMD::Set_Segment_remap127); // Flip the display horizontally + g_DisplayInterface.SendCommand(1, SSD1306_CMD::Set_COM_Scan_x); // Flip the display vertically + break; + } + return true; +} + +void DisplayDriver::SetDefaultOrientation() +{ + ChangeOrientation(LANDSCAPE); +} + +bool DisplayDriver::SetWindow(CLR_INT16 x1, CLR_INT16 y1, CLR_INT16 x2, CLR_INT16 y2) +{ + // Start & End column address + g_DisplayInterface.SendCommand(3, Set_Column_Address, x1, x2); + + // Start & End page address 0 to 7 + g_DisplayInterface.SendCommand(3, Set_Page_address, (y1 / 8), (y2 / 8)); + + return true; +} + +void DisplayDriver::Clear() +{ + // Clear the SSD1306 controller + SetWindow(0, 0, Attributes.Width - 1, Attributes.Height - 1); + memset(Attributes.TransferBuffer, 0, Attributes.TransferBufferSize); + + for (int row = 0; row < Attributes.Height / 8; row++) + { + g_DisplayInterface.WriteToFrameBuffer(Memory_Write, Attributes.TransferBuffer, Attributes.Width); + } +} +// +// +// +// data[] reference to whole screen bitmap +// +void DisplayDriver::BitBlt(int x, int y, int width, int height, CLR_UINT32 data[]) +{ + // // With the current design the Colour data is packed into the lower two bytes of each data array element + // // 16 bit colour RRRRRGGGGGGBBBBB mode 565 + ASSERT((x >= 0) && ((x + width) <= Attributes.Width)); + ASSERT((y >= 0) && ((y + height) <= Attributes.Height)); + + CLR_UINT16 *pui16Data = (CLR_UINT16 *)data; + + // Round y position down as we can only deal with rows of 8 at a time + int offfset = y % 8; + y -= offfset; + + // Increase height for change in y + height += offfset; + + // Round up height to multiple of 8 + height = (height + 7) & 0xfffffff8; + + // Check not too big + if ((y + height) > Attributes.Height) + { + height = Attributes.Height - y; + } + + // Find position in buffer for start of data in window + pui16Data += x + (y * Attributes.Width); + + // Set window for bitblt + SetWindow(x, y, x + width - 1, y + height - 1); + + CLR_INT16 firstPageToUpdate = y / 8; + CLR_INT16 lastPageToUpdate = (y + height - 1) / 8; + + CLR_UINT8 *pui8Buf = Attributes.TransferBuffer; + CLR_UINT32 numberOfBytesPerTransfer = width; + + for (int page = firstPageToUpdate; page <= lastPageToUpdate; page++) + { + // Convert internal bitmap to dispay Page of 8 lines + memset(pui8Buf, 0, 128); + uint8_t mask = 0x01; + + // For each line in a page + for (uint y = 0; y < 8; y++) + { + CLR_UINT16 *pData = pui16Data; + + // for each pixel of width + for (int i = 0; i < width; i++) + { + if (*pData++) + { + pui8Buf[i] = pui8Buf[i] | mask; + } + } + mask <<= 1; + pui16Data += Attributes.Width; // Next row + } + + g_DisplayInterface.WriteToFrameBuffer(Memory_Write, Attributes.TransferBuffer, numberOfBytesPerTransfer); + } + + return; +} + +CLR_UINT32 DisplayDriver::PixelsPerWord() +{ + return (32 / Attributes.BitsPerPixel); +} +CLR_UINT32 DisplayDriver::WidthInWords() +{ + return ((Attributes.Width + (PixelsPerWord() - 1)) / PixelsPerWord()); +} +CLR_UINT32 DisplayDriver::SizeInWords() +{ + return (WidthInWords() * Attributes.Height); +} +CLR_UINT32 DisplayDriver::SizeInBytes() +{ + return (SizeInWords() * sizeof(CLR_UINT32)); +} diff --git a/src/nanoFramework.Graphics/Graphics/Displays/SSD1331_94x64_SPI.cpp b/src/nanoFramework.Graphics/Graphics/Displays/SSD1331_94x64_SPI.cpp new file mode 100644 index 0000000000..28da716a11 --- /dev/null +++ b/src/nanoFramework.Graphics/Graphics/Displays/SSD1331_94x64_SPI.cpp @@ -0,0 +1,321 @@ +// +// Copyright (c) .NET Foundation and Contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +#include "Graphics.h" +#include "DisplayInterface.h" +#include "Display.h" + +/* +SSD1331 is a 96RGB x 64 Dot Matrix OLED/PLED Segment/Common Driver with Controller + +SSD1331 supports parallel 8-/9-/16-bit data bus MCU interface and serial peripheral interface (SPI). + +SSD1331 supports full color, 8-color display mode and sleep mode for precise power control by software. + +Power saving mode: + 1. Sleep + 2. Deep standby + +This implementation was initially written for 16 bit colour (665) SPI mode. +*/ + +struct DisplayDriver g_DisplayDriver; +extern DisplayInterface g_DisplayInterface; + +enum SSD1331_CMD : CLR_UINT8 +{ + Set_Column_Address = 0x15, + Set_Row_Address = 0x75, + + Set_Contrast_A = 0x81, + Set_Contrast_B = 0x82, + Set_Contrast_C = 0x83, + + Master_Current_Control = 0x87, + + Set_Second_Pre_charge_Speed_A = 0x8A, + Set_Second_Pre_charge_Speed_B = 0x8B, + Set_Second_Pre_charge_Speed_C = 0x8C, + + Remap_and_Colour_Depth_setting = 0xA0, + + Set_Display_Start_Line = 0xA1, + Set_Display_Offset = 0xA2, + Set_Display_Mode_Normal = 0xA4, + Set_Display_Mode_Entire_On = 0xA5, + Set_Display_Mode_Entire_Off = 0xA6, + Set_Display_Mode_Inverse = 0xA7, + + Set_Multiplex_Ratio = 0xA8, + Dim_Mode_Setting = 0xAB, + Set_Master_Configuration = 0xAD, + Set_Display_On_Dim = 0xAC, + Set_Display_Off_Sleep = 0xAE, + Set_Display_On_Normal = 0xAF, + Power_Save_Mode = 0xB0, + Phase1_2_Period = 0xB1, + Display_Clock_Divider = 0xB3, + Set_Gray_Scale_Table = 0xB8, + Enable_Linear_Gray_Scale_Table = 0xB9, + Set_Pre_Charge_Level = 0xBB, + Set_Vcomh = 0xBE, + Set_Command_Lock = 0xFD, + + Draw_Line = 0x21, + Draw_Rectangle = 0x22, + Copy = 0x23, + Dim_Window = 0x24, + Clear_Window = 0x25, + Fill_Enable_Disable = 0x26, + Scrolling_Setup = 0x27, + Deactivate_Scolling = 0x2E, + Activate_Scrolling = 0x2F +}; + +enum SSD1331_Orientation : CLR_UINT8 +{ + MADCTL_MH = 0x04, // sets the Horizontal Refresh, 0=Left-Right and 1=Right-Left + MADCTL_ML = 0x10, // sets the Vertical Refresh, 0=Top-Bottom and 1=Bottom-Top + MADCTL_MV = 0x20, // sets the Row/Column Swap, 0=Normal and 1=Swapped + MADCTL_MX = 0x40, // sets the Column Order, 0=Left-Right and 1=Right-Left + MADCTL_MY = 0x80, // sets the Row Order, 0=Top-Bottom and 1=Bottom-Top + + MADCTL_BGR = 0x08 // Blue-Green-Red pixel order +}; + +bool DisplayDriver::Initialize() +{ + // Set SPI interface to Command mode for all command bytes + g_DisplayInterface.SetCommandMode(1); + + // Initialize SSD1331 registers + SetupDisplayAttributes(); + + g_DisplayInterface.SendCommand(1, SSD1331_CMD::Set_Display_Off_Sleep); + + // Orientation & colour mapping + SetDefaultOrientation(); + + g_DisplayInterface.SendCommand(2, SSD1331_CMD::Set_Display_Start_Line, 0x00); + + g_DisplayInterface.SendCommand(2, SSD1331_CMD::Set_Display_Offset, 0x00); + + g_DisplayInterface.SendCommand(1, SSD1331_CMD::Set_Display_Mode_Normal); + + g_DisplayInterface.SendCommand(2, SSD1331_CMD::Set_Multiplex_Ratio, 0x3F); + + g_DisplayInterface.SendCommand(2, SSD1331_CMD::Set_Master_Configuration, 0x8E); + + g_DisplayInterface.SendCommand(2, SSD1331_CMD::Power_Save_Mode, 0x0B); + + g_DisplayInterface.SendCommand(2, SSD1331_CMD::Phase1_2_Period, 0x31); + + g_DisplayInterface.SendCommand(2, SSD1331_CMD::Display_Clock_Divider, 0xF0); + + g_DisplayInterface.SendCommand(2, SSD1331_CMD::Set_Second_Pre_charge_Speed_A, 0x64); + + g_DisplayInterface.SendCommand(2, SSD1331_CMD::Set_Second_Pre_charge_Speed_B, 0x78); + + g_DisplayInterface.SendCommand(2, SSD1331_CMD::Set_Second_Pre_charge_Speed_C, 0x64); + + g_DisplayInterface.SendCommand(2, SSD1331_CMD::Set_Pre_Charge_Level, 0x3A); + + g_DisplayInterface.SendCommand(2, SSD1331_CMD::Set_Vcomh, 0x3E); + + g_DisplayInterface.SendCommand(2, SSD1331_CMD::Master_Current_Control, 0x06); + + g_DisplayInterface.SendCommand(2, SSD1331_CMD::Set_Contrast_A, 0x91); + + g_DisplayInterface.SendCommand(2, SSD1331_CMD::Set_Contrast_B, 0x50); + + g_DisplayInterface.SendCommand(2, SSD1331_CMD::Set_Contrast_C, 0x7D); + + g_DisplayInterface.SendCommand(1, SSD1331_CMD::Set_Display_On_Normal); + + return true; +} + +void DisplayDriver::SetupDisplayAttributes() +{ + // Define the LCD/TFT resolution + Attributes.LongerSide = 96; + Attributes.ShorterSide = 64; + Attributes.PowerSave = PowerSaveState::NORMAL; + Attributes.BitsPerPixel = 16; + g_DisplayInterface.GetTransferBuffer(Attributes.TransferBuffer, Attributes.TransferBufferSize); + return; +} + +bool DisplayDriver::ChangeOrientation(DisplayOrientation orientation) +{ + uint8_t options = 0; + + options |= 0x40; // RGB mapping, 65K colour + + switch (orientation) + { + case PORTRAIT: + case PORTRAIT180: + return false; + + case LANDSCAPE180: + options |= 0x70; + Attributes.Height = Attributes.ShorterSide; + Attributes.Width = Attributes.LongerSide; + break; + + case LANDSCAPE: + options |= 0x72; + Attributes.Height = Attributes.ShorterSide; + Attributes.Width = Attributes.LongerSide; + break; + } + + g_DisplayInterface.SendCommand(1, SSD1331_CMD::Remap_and_Colour_Depth_setting); + g_DisplayInterface.SendCommand(1, options); + return true; +} + +void DisplayDriver::SetDefaultOrientation() +{ + ChangeOrientation(LANDSCAPE); +} + +bool DisplayDriver::SetWindow(CLR_INT16 x1, CLR_INT16 y1, CLR_INT16 x2, CLR_INT16 y2) +{ + // Start & End column address + g_DisplayInterface.SendCommand(3, SSD1331_CMD::Set_Column_Address, x1, x2); + + // Start & End row address + g_DisplayInterface.SendCommand(3, SSD1331_CMD::Set_Row_Address, y1, y2); + + return true; +} + +bool DisplayDriver::Uninitialize() +{ + Clear(); + // Anything else to Uninitialize? + return TRUE; +} + +void DisplayDriver::PowerSave(PowerSaveState powerState) +{ + switch (powerState) + { + default: + // Illegal fall through to Power on + case PowerSaveState::NORMAL: + g_DisplayInterface.SendCommand(1, SSD1331_CMD::Power_Save_Mode); + g_DisplayInterface.SendCommand(1, 0x0B); + break; + case PowerSaveState::SLEEP: + g_DisplayInterface.SendCommand(1, SSD1331_CMD::Power_Save_Mode); + g_DisplayInterface.SendCommand(1, 0x1A); + break; + } + return; +} + +void DisplayDriver::Clear() +{ + // Clear the SSD1331 controller + g_DisplayInterface.SendCommand(1, SSD1331_CMD::Clear_Window); + g_DisplayInterface.SendCommand(1, 0); + g_DisplayInterface.SendCommand(1, 0); + g_DisplayInterface.SendCommand(1, Attributes.Width - 1); + g_DisplayInterface.SendCommand(1, Attributes.Height - 1); + OS_DELAY(1); + return; +} + +void DisplayDriver::DisplayBrightness(CLR_INT16 brightness) +{ + _ASSERTE(brightness >= 0 && brightness <= 100); + // g_DisplayInterface.SendCommand(2,Write_Display_Brightness, (CLR_UINT8)brightness); + return; +} + +void DisplayDriver::BitBlt(int x, int y, int width, int height, CLR_UINT32 data[]) +{ + // 16 bit colour RRRRRGGGGGGBBBBB mode 565 + + ASSERT((x >= 0) && ((x + width) <= Attributes.Width)); + ASSERT((y >= 0) && ((y + height) <= Attributes.Height)); + + CLR_UINT16 *StartOfLine_src = (CLR_UINT16 *)&data[0]; + + SetWindow(x, y, (x + width - 1), (y + height - 1)); + + // Position to offset in data[] for stat of window + CLR_UINT16 offset = (y * Attributes.Width) + x; + StartOfLine_src += offset; + + CLR_UINT8 *transferBufferIndex = Attributes.TransferBuffer; + CLR_UINT32 transferBufferCount = Attributes.TransferBufferSize; + + while (height--) + { + CLR_UINT16 *src; + int xCount; + + src = StartOfLine_src; + xCount = width; + + while (xCount--) + { + CLR_UINT16 data = *src++; + *transferBufferIndex++ = (data >> 8); + *transferBufferIndex++ = data & 0xff; + transferBufferCount -= 2; + + // Send over SPI if no room for another 2 bytes + if (transferBufferCount < 1) + { + // Transfer buffer full, send it + g_DisplayInterface.SendBytes( + Attributes.TransferBuffer, + (Attributes.TransferBufferSize - transferBufferCount)); + + // Reset transfer ptrs/count + transferBufferIndex = Attributes.TransferBuffer; + transferBufferCount = Attributes.TransferBufferSize; + } + } + + // Next row in data[] + StartOfLine_src += Attributes.Width; + } + + // Send remaining data in transfer buffer to SPI + if (transferBufferCount < Attributes.TransferBufferSize) + { + // Transfer buffer full, send it + g_DisplayInterface.SendBytes(Attributes.TransferBuffer, (Attributes.TransferBufferSize - transferBufferCount)); + } + + return; +} + +CLR_UINT32 DisplayDriver::PixelsPerWord() +{ + return (32 / Attributes.BitsPerPixel); +} + +CLR_UINT32 DisplayDriver::WidthInWords() +{ + return ((Attributes.Width + (PixelsPerWord() - 1)) / PixelsPerWord()); +} + +CLR_UINT32 DisplayDriver::SizeInWords() +{ + return (WidthInWords() * Attributes.Height); +} + +CLR_UINT32 DisplayDriver::SizeInBytes() +{ + return (SizeInWords() * sizeof(CLR_UINT32)); +} diff --git a/src/nanoFramework.Graphics/Graphics/Displays/ST7789V_240x320_SPI.cpp b/src/nanoFramework.Graphics/Graphics/Displays/ST7789V_240x320_SPI.cpp index 0ed1a56dcd..1142467b1a 100644 --- a/src/nanoFramework.Graphics/Graphics/Displays/ST7789V_240x320_SPI.cpp +++ b/src/nanoFramework.Graphics/Graphics/Displays/ST7789V_240x320_SPI.cpp @@ -26,13 +26,13 @@ This implementation was initially written for 16 bit colour, SPI interface of a */ - /* Using the default endian order for transferring bytes Normal (MSB first, default) */ -#define CommandData(c) c,(CLR_UINT8 *)(CLR_UINT8[c]) // Macro to simplify visualisation of passing pointer to parameters; +#define CommandData(c) \ + c, (CLR_UINT8 *)(CLR_UINT8[c]) // Macro to simplify visualisation of passing pointer to parameters; struct DisplayDriver g_DisplayDriver; extern DisplayInterface g_DisplayInterface; @@ -41,7 +41,7 @@ enum ST7789V_CMD : CLR_UINT8 { NOP = 0x00, SOFTWARE_RESET = 0x01, - Sleep_IN = 0x10, + Sleep_IN = 0x10, Sleep_OUT = 0x11, Display_OFF = 0x28, Display_ON = 0x29, @@ -50,8 +50,9 @@ enum ST7789V_CMD : CLR_UINT8 Memory_Write = 0x2C, Memory_Read = 0x2E, Partial_Area = 0x30, - Memory_Access_Control = 0x36, + Memory_Access_Control = 0x36, Pixel_Format_Set = 0x3A, + Memory_Write_Continue = 0x3C, Write_Display_Brightness = 0x51, Porch_Setting = 0xB2, Gate_Control = 0xB7, @@ -86,7 +87,9 @@ bool DisplayDriver::Initialize() SetupDisplayAttributes(); - g_DisplayInterface.SendCommand(2, Memory_Access_Control, MADCTL_MV | MADCTL_MX | MADCTL_BGR); + // g_DisplayInterface.SendCommand(2, Memory_Access_Control, MADCTL_MV | MADCTL_MX | MADCTL_BGR); + SetDefaultOrientation(); + g_DisplayInterface.SendCommand(2, Pixel_Format_Set, 0x55); g_DisplayInterface.SendCommand(6, Porch_Setting, 0x0c, 0x0c, 0x00, 0x33, 0x33); g_DisplayInterface.SendCommand(2, Gate_Control, 0x35); @@ -95,19 +98,49 @@ bool DisplayDriver::Initialize() g_DisplayInterface.SendCommand(3, VDV_VRH_Command_Enable, 0x01, 0xFF); g_DisplayInterface.SendCommand(2, VRH_Set, 0x11); g_DisplayInterface.SendCommand(2, VDV_Set, 0x20); - g_DisplayInterface.SendCommand(2, Frame_Rate_Control, 0x0f); // 39Hz + g_DisplayInterface.SendCommand(2, Frame_Rate_Control, 0x0f); // 39Hz g_DisplayInterface.SendCommand(3, Power_Control_1, 0xA4, 0xA1); - g_DisplayInterface.SendCommand(15,Positive_Voltage_Gamma, 0xD0, 0x00, 0x05, 0x0E, 0x15, 0x0D, 0x37, 0x43, 0x47, 0x09, 0x15, 0x12, 0x16, 0x19); - g_DisplayInterface.SendCommand(15,Negative_Voltage_Gamma, 0xD0, 0x00, 0x05, 0x0D, 0x0C, 0x06, 0x2D, 0x44, 0x40, 0x0E, 0x1C, 0x18, 0x16, 0x19); - - g_DisplayInterface.SendCommand(1,Sleep_OUT); - OS_DELAY(20); // Send Sleep Out command to display : no parameter - g_DisplayInterface.SendCommand(1,Display_ON); - OS_DELAY(20); // Send Display ON command to display : no parameter - g_DisplayInterface.SendCommand(1,NOP); // End of sequence - OS_DELAY(20); - - SetDefaultOrientation(); + g_DisplayInterface.SendCommand( + 15, + Positive_Voltage_Gamma, + 0xD0, + 0x00, + 0x05, + 0x0E, + 0x15, + 0x0D, + 0x37, + 0x43, + 0x47, + 0x09, + 0x15, + 0x12, + 0x16, + 0x19); + g_DisplayInterface.SendCommand( + 15, + Negative_Voltage_Gamma, + 0xD0, + 0x00, + 0x05, + 0x0D, + 0x0C, + 0x06, + 0x2D, + 0x44, + 0x40, + 0x0E, + 0x1C, + 0x18, + 0x16, + 0x19); + + g_DisplayInterface.SendCommand(1, Sleep_OUT); + OS_DELAY(20); // Send Sleep Out command to display : no parameter + g_DisplayInterface.SendCommand(1, Display_ON); + OS_DELAY(20); // Send Display ON command to display : no parameter + g_DisplayInterface.SendCommand(1, NOP); // End of sequence + OS_DELAY(20); return true; } @@ -121,12 +154,30 @@ void DisplayDriver::SetupDisplayAttributes() g_DisplayInterface.GetTransferBuffer(Attributes.TransferBuffer, Attributes.TransferBufferSize); return; } + +bool DisplayDriver::ChangeOrientation(DisplayOrientation orientation) +{ + switch (orientation) + { + case PORTRAIT: + case PORTRAIT180: + return false; + + case LANDSCAPE: + case LANDSCAPE180: + Attributes.Height = Attributes.ShorterSide; + Attributes.Width = Attributes.LongerSide; + g_DisplayInterface.SendCommand(2, Memory_Access_Control, (MADCTL_MV | MADCTL_MX)); // Landscape + break; + } + return true; +} + void DisplayDriver::SetDefaultOrientation() { - Attributes.Height = Attributes.ShorterSide; - Attributes.Width = Attributes.LongerSide; - g_DisplayInterface.SendCommand(2, Memory_Access_Control, (MADCTL_MV | MADCTL_MX | MADCTL_BGR) ); // Landscape + ChangeOrientation(LANDSCAPE); } + bool DisplayDriver::Uninitialize() { Clear(); @@ -137,84 +188,148 @@ void DisplayDriver::PowerSave(PowerSaveState powerState) { switch (powerState) { - default: - // illegal fall through to Power on - case PowerSaveState::NORMAL: - g_DisplayInterface.SendCommand(3, Sleep_IN, 0x00,0x00); // leave sleep mode - break; - case PowerSaveState::SLEEP: - g_DisplayInterface.SendCommand(3, Sleep_IN, 0x00,0x01); // enter sleep mode - break; + default: + // illegal fall through to Power on + case PowerSaveState::NORMAL: + g_DisplayInterface.SendCommand(3, Sleep_IN, 0x00, 0x00); // leave sleep mode + break; + case PowerSaveState::SLEEP: + g_DisplayInterface.SendCommand(3, Sleep_IN, 0x00, 0x01); // enter sleep mode + break; } return; } void DisplayDriver::Clear() { - //reset the cursor pos to the begining - // Clear the ST7789V controller - return; + // Clear the ST7789V controller + SetWindow(0, 0, Attributes.Width - 1, Attributes.Height - 1); + + // Clear buffer + memset(Attributes.TransferBuffer, 0, Attributes.TransferBufferSize); + + int totalBytesToClear = Attributes.Width * Attributes.Height * 2; + int fullTransferBuffersCount = totalBytesToClear / Attributes.TransferBufferSize; + int remainderTransferBuffer = totalBytesToClear % Attributes.TransferBufferSize; + CLR_UINT8 command = Memory_Write; + for (int i = 0; i < fullTransferBuffersCount; i++) + { + g_DisplayInterface.WriteToFrameBuffer(command, Attributes.TransferBuffer, Attributes.TransferBufferSize); + command = Memory_Write_Continue; + } + + if (remainderTransferBuffer > 0) + { + g_DisplayInterface.WriteToFrameBuffer(command, Attributes.TransferBuffer, remainderTransferBuffer); + } + return; } + void DisplayDriver::DisplayBrightness(CLR_INT16 brightness) { _ASSERTE(brightness >= 0 && brightness <= 100); - g_DisplayInterface.SendCommand(2,Write_Display_Brightness, (CLR_UINT8)brightness); + g_DisplayInterface.SendCommand(2, Write_Display_Brightness, (CLR_UINT8)brightness); return; +} +bool DisplayDriver::SetWindow(CLR_INT16 x1, CLR_INT16 y1, CLR_INT16 x2, CLR_INT16 y2) +{ + CLR_UINT8 Column_Address_Set_Data[4]; + Column_Address_Set_Data[0] = (x1 >> 8) & 0xFF; + Column_Address_Set_Data[1] = x1 & 0xFF; + Column_Address_Set_Data[2] = (x2 >> 8) & 0xFF; + Column_Address_Set_Data[3] = x2 & 0xFF; + g_DisplayInterface.SendCommand( + 5, + Column_Address_Set, + Column_Address_Set_Data[0], + Column_Address_Set_Data[1], + Column_Address_Set_Data[2], + Column_Address_Set_Data[3]); + + CLR_UINT8 Row_Address_Set_Data[4]; + Row_Address_Set_Data[0] = (y1 >> 8) & 0xFF; + Row_Address_Set_Data[1] = y1 & 0xFF; + Row_Address_Set_Data[2] = (y2 >> 8) & 0xFF; + Row_Address_Set_Data[3] = y2 & 0xFF; + g_DisplayInterface.SendCommand( + 5, + Row_Address_Set, + Row_Address_Set_Data[0], + Row_Address_Set_Data[1], + Row_Address_Set_Data[2], + Row_Address_Set_Data[3]); + return true; } + void DisplayDriver::BitBlt(int x, int y, int width, int height, CLR_UINT32 data[]) { - // With the current design the Colour data is packed into the lower two bytes of each data array element // 16 bit colour RRRRRGGGGGGBBBBB mode 565 ASSERT((x >= 0) && ((x + width) <= Attributes.Width)); ASSERT((y >= 0) && ((y + height) <= Attributes.Height)); - CLR_UINT8* pui8Data = (CLR_UINT8*)data; - - CLR_UINT8 Column_Address_Set_Data[4]; - Column_Address_Set_Data[0] = (x >> 8) & 0xFF; - Column_Address_Set_Data[1] = x & 0xFF; - Column_Address_Set_Data[2] = ((x + width) >> 8) & 0xFF; - Column_Address_Set_Data[3] = (x + width) & 0xFF; - g_DisplayInterface.SendCommand(5, Column_Address_Set, Column_Address_Set_Data[0], Column_Address_Set_Data[1], Column_Address_Set_Data[2], Column_Address_Set_Data[3]); - - CLR_INT16 numberOfBytesPerPage = width * 2; //16 bit colour - CLR_INT16 TotalNumberOfPages = Attributes.Height; - CLR_INT16 NumberOfPagesPerTransferBuffer = Attributes.TransferBufferSize / numberOfBytesPerPage; // Maximum pages per buffer transfer - CLR_INT16 TotalNumberOfSpiTransfers = TotalNumberOfPages / NumberOfPagesPerTransferBuffer; - CLR_INT16 numberOfBytesPerTransfer = numberOfBytesPerPage * NumberOfPagesPerTransferBuffer; //16 bit colour - int iFirstPageOfTransfer = 0; - - for (int iSpiTransfer = 0; iSpiTransfer < TotalNumberOfSpiTransfers; iSpiTransfer++) { - - // Change endian for ST7789V SPI mode and store in Spi transfer buffer before SPI Transfer - CLR_UINT8* transferBufferIndex = Attributes.TransferBuffer; - CLR_INT16 numberOf16BitWordsPerTransfer = numberOfBytesPerTransfer / 2; - for (int idataByte = 0; idataByte < numberOf16BitWordsPerTransfer; idataByte++) + + SetWindow(x, y, (x + width - 1), (y + height - 1)); + + CLR_UINT16 *StartOfLine_src = (CLR_UINT16 *)&data[0]; + + // Position to offset in data[] for start of window + CLR_UINT16 offset = (y * Attributes.Width) + x; + StartOfLine_src += offset; + + CLR_UINT8 *transferBufferIndex = Attributes.TransferBuffer; + CLR_UINT32 transferBufferCount = Attributes.TransferBufferSize; + CLR_UINT8 command = Memory_Write; + + while (height--) + { + CLR_UINT16 *src; + int xCount; + + src = StartOfLine_src; + xCount = width; + + while (xCount--) { - // Swap bytes of the word to match endianess of the ST7789V - *transferBufferIndex = *(pui8Data + 1); - transferBufferIndex++; - *transferBufferIndex = *(pui8Data); - transferBufferIndex++; - pui8Data += 2; + CLR_UINT16 data = *src++; + // Swap bytes + *transferBufferIndex++ = (data >> 8); + *transferBufferIndex++ = data & 0xff; + transferBufferCount -= 2; + + // Send over SPI if no room for another 2 bytes + if (transferBufferCount < 1) + { + // Transfer buffer full, send it + g_DisplayInterface.WriteToFrameBuffer( + command, + Attributes.TransferBuffer, + (Attributes.TransferBufferSize - transferBufferCount)); + + // Reset transfer ptrs/count + transferBufferIndex = Attributes.TransferBuffer; + transferBufferCount = Attributes.TransferBufferSize; + command = Memory_Write_Continue; + } } - // Setup the area to update the ST7789V - CLR_UINT8 Page_Address_Set_Data[4]; - Page_Address_Set_Data[0] = (iFirstPageOfTransfer >> 8) & 0xFF; - Page_Address_Set_Data[1] = iFirstPageOfTransfer & 0xFF; - Page_Address_Set_Data[2] = ((iFirstPageOfTransfer + NumberOfPagesPerTransferBuffer) >> 8) & 0xFF; - Page_Address_Set_Data[3] = (iFirstPageOfTransfer + NumberOfPagesPerTransferBuffer) & 0xFF; - g_DisplayInterface.SendCommand(5, Row_Address_Set, Page_Address_Set_Data[0], Page_Address_Set_Data[1], Page_Address_Set_Data[2], Page_Address_Set_Data[3]); - - // Send the data to the ST7789V via SPI - g_DisplayInterface.WriteToFrameBuffer(Memory_Write, Attributes.TransferBuffer, numberOfBytesPerTransfer); + // Next row in data[] + StartOfLine_src += Attributes.Width; + } - iFirstPageOfTransfer += NumberOfPagesPerTransferBuffer; + // Send remaining data in transfer buffer to SPI + if (transferBufferCount < Attributes.TransferBufferSize) + { + // Transfer buffer full, send it + g_DisplayInterface.WriteToFrameBuffer( + command, + Attributes.TransferBuffer, + (Attributes.TransferBufferSize - transferBufferCount)); } + return; } + CLR_UINT32 DisplayDriver::PixelsPerWord() { return (32 / Attributes.BitsPerPixel); @@ -231,5 +346,3 @@ CLR_UINT32 DisplayDriver::SizeInBytes() { return (SizeInWords() * sizeof(CLR_UINT32)); } - - diff --git a/src/nanoFramework.Graphics/Graphics/Displays/Spi_To_Display.cpp b/src/nanoFramework.Graphics/Graphics/Displays/Spi_To_Display.cpp index 206c2e4755..cdaa1e22f1 100644 --- a/src/nanoFramework.Graphics/Graphics/Displays/Spi_To_Display.cpp +++ b/src/nanoFramework.Graphics/Graphics/Displays/Spi_To_Display.cpp @@ -11,78 +11,90 @@ #include #include -#define NUMBER_OF_LINES 16 -#define SPI_MAX_TRANSFER_SIZE (320 * 2 * NUMBER_OF_LINES) // 320 pixels 2 words wide ( 16 bit colour) +#define NUMBER_OF_LINES 16 +#define SPI_MAX_TRANSFER_SIZE (320 * 2 * NUMBER_OF_LINES) // 320 pixels 2 words wide ( 16 bit colour) struct DisplayInterface g_DisplayInterface; // Saved gpio pins -CLR_INT16 LcdReset; -CLR_INT16 LcdDC; -CLR_INT16 LcdBacklight; +CLR_INT16 lcdReset; +CLR_INT16 lcdDC; +CLR_INT16 lcdBacklight; -CLR_UINT32 SpiDeviceHandle = 0; -CLR_INT16 OutputBufferSize; -CLR_UINT8 SpiBuffer[SPI_MAX_TRANSFER_SIZE]; +CLR_UINT32 spiDeviceHandle = 0; +CLR_INT16 outputBufferSize; +CLR_UINT8 spiBuffer[SPI_MAX_TRANSFER_SIZE]; +CLR_UINT8 spiCommandMode = 0; // 0 Command first byte, 1 = Command all bytes // Display Interface -void DisplayInterface::Initialize(DisplayInterfaceConfig& config) +void DisplayInterface::Initialize(DisplayInterfaceConfig &config) { SPI_DEVICE_CONFIGURATION spiConfig; - + spiConfig.BusMode = SpiBusMode::SpiBusMode_master; - spiConfig.Spi_Bus = config.Spi.spiBus; + spiConfig.Spi_Bus = config.Spi.spiBus; spiConfig.DeviceChipSelect = config.Spi.chipSelect; - spiConfig.ChipSelectActive = false; - spiConfig.Spi_Mode = SpiMode::SpiMode_Mode0; + spiConfig.ChipSelectActive = false; + spiConfig.Spi_Mode = SpiMode::SpiMode_Mode0; spiConfig.DataOrder16 = DataBitOrder::DataBitOrder_MSB; - - spiConfig.Clock_RateHz = 40 * 1000 * 1000; // Spi clock speed. - HRESULT hr = nanoSPI_OpenDevice(spiConfig, SpiDeviceHandle); + spiConfig.Clock_RateHz = 40 * 1000 * 1000; // Spi clock speed. + + HRESULT hr = nanoSPI_OpenDevice(spiConfig, spiDeviceHandle); ASSERT(hr == ESP_OK); - if ( hr == S_OK ) + if (hr == S_OK) { // TODO Reserve Pins // Save pin numbers - LcdReset = config.Spi.reset; - LcdDC = config.Spi.dataCommand; - LcdBacklight = config.Spi.backLight; + lcdReset = config.Spi.reset; + lcdDC = config.Spi.dataCommand; + lcdBacklight = config.Spi.backLight; // Initialize non-SPI GPIOs - CPU_GPIO_SetDriveMode(LcdDC, GpioPinDriveMode::GpioPinDriveMode_Output); - CPU_GPIO_SetDriveMode(LcdReset, GpioPinDriveMode::GpioPinDriveMode_Output); - CPU_GPIO_SetDriveMode(LcdBacklight, GpioPinDriveMode::GpioPinDriveMode_Output); + CPU_GPIO_SetDriveMode(lcdDC, GpioPinDriveMode::GpioPinDriveMode_Output); + CPU_GPIO_SetDriveMode(lcdReset, GpioPinDriveMode::GpioPinDriveMode_Output); + CPU_GPIO_SetDriveMode(lcdBacklight, GpioPinDriveMode::GpioPinDriveMode_Output); // Reset the display - CPU_GPIO_SetPinState(LcdReset, GpioPinValue_Low); + CPU_GPIO_SetPinState(lcdReset, GpioPinValue_Low); OS_DELAY(100); - CPU_GPIO_SetPinState(LcdReset, GpioPinValue_High); + CPU_GPIO_SetPinState(lcdReset, GpioPinValue_High); OS_DELAY(100); } return; } -void DisplayInterface::GetTransferBuffer(CLR_UINT8*& TransferBuffer, CLR_UINT32& TransferBufferSize) +void DisplayInterface::SetCommandMode(int mode) +{ + spiCommandMode = mode; +} + +void DisplayInterface::GetTransferBuffer(CLR_UINT8 *&TransferBuffer, CLR_UINT32 &TransferBufferSize) { - TransferBuffer = SpiBuffer; - TransferBufferSize = sizeof(SpiBuffer); + TransferBuffer = spiBuffer; + TransferBufferSize = sizeof(spiBuffer); } void DisplayInterface::ClearFrameBuffer() { - // Not Used + // Not Used } -void DisplayInterface::WriteToFrameBuffer(CLR_UINT8 command, CLR_UINT8 data[], CLR_UINT32 dataCount) +void DisplayInterface::WriteToFrameBuffer( + CLR_UINT8 command, + CLR_UINT8 data[], + CLR_UINT32 dataCount, + CLR_UINT32 frameOffset) { - CPU_GPIO_SetPinState(LcdDC, GpioPinValue_Low); //Command mode + (void)frameOffset; + + CPU_GPIO_SetPinState(lcdDC, GpioPinValue_Low); // Command mode SendBytes(&command, 1); - CPU_GPIO_SetPinState(LcdDC, GpioPinValue_High); // Data mode - + CPU_GPIO_SetPinState(lcdDC, GpioPinValue_High); // Data mode + SendBytes(data, dataCount); return; } @@ -92,47 +104,57 @@ void DisplayInterface::SendCommand(CLR_UINT8 arg_count, ...) va_list ap; va_start(ap, arg_count); - // First byte is the command - CLR_UINT8 command = va_arg(ap, int); - CPU_GPIO_SetPinState(LcdDC, GpioPinValue_Low); // Command mode - SendBytes(&command, 1); - - // Following bytes are data related to the command - int parameterCount = arg_count - 1; - CLR_UINT8 parameters[parameterCount]; - for (int i = 0; i < parameterCount; i++) + // Parse arguments into parameters buffer + CLR_UINT8 parameters[arg_count]; + for (int i = 0; i < arg_count; i++) { parameters[i] = va_arg(ap, int); } - CPU_GPIO_SetPinState(LcdDC, GpioPinValue_High); // Data mode - SendBytes(parameters, parameterCount); + CPU_GPIO_SetPinState(lcdDC, GpioPinValue_Low); // Command mode + if (spiCommandMode == 0) + { + // Send only first byte (command) with D/C signal low + SendBytes(¶meters[0], 1); - return; + CPU_GPIO_SetPinState(lcdDC, GpioPinValue_High); // Data mode + + // Send remaining parameters ( if any ) + if (arg_count > 1) + { + SendBytes(¶meters[1], arg_count - 1); + } + } + else + { + // Send all Command bytes with D/C signal low + SendBytes(parameters, arg_count); + CPU_GPIO_SetPinState(lcdDC, GpioPinValue_High); // Data mode + } } void DisplayInterface::DisplayBacklight(bool on) // true = on { if (on) { - CPU_GPIO_SetPinState(LcdBacklight, GpioPinValue_High); + CPU_GPIO_SetPinState(lcdBacklight, GpioPinValue_High); } else { - CPU_GPIO_SetPinState(LcdBacklight, GpioPinValue_Low); + CPU_GPIO_SetPinState(lcdBacklight, GpioPinValue_Low); } return; } - void DisplayInterface::SendBytes(CLR_UINT8 *data, CLR_UINT32 length) { - if (length == 0) return; //no need to send anything + if (length == 0) + return; // no need to send anything SPI_WRITE_READ_SETTINGS wrc; wrc.Bits16ReadWrite = false; - wrc.callback = 0; // No callback, synchronous + wrc.callback = 0; // No callback, synchronous wrc.fullDuplex = false; wrc.readOffset = 0; @@ -140,9 +162,8 @@ void DisplayInterface::SendBytes(CLR_UINT8 *data, CLR_UINT32 length) // double buffering. // Start a synchronous read / write spi job - nanoSPI_Write_Read( SpiDeviceHandle, wrc, data, length, NULL, 0); + nanoSPI_Write_Read(spiDeviceHandle, wrc, data, length, NULL, 0); return; } -#endif // _SPI_TO_DISPLAY_ - +#endif // _SPI_TO_DISPLAY_ diff --git a/targets/CMSIS-OS/ChibiOS/ST_STM32F769I_DISCOVERY/nanoCLR/nanoFramework.Graphics/DSI_To_Display_Video_Mode.cpp b/targets/CMSIS-OS/ChibiOS/ST_STM32F769I_DISCOVERY/nanoCLR/nanoFramework.Graphics/DSI_To_Display_Video_Mode.cpp index ddf3bc2c08..7226c9d22b 100644 --- a/targets/CMSIS-OS/ChibiOS/ST_STM32F769I_DISCOVERY/nanoCLR/nanoFramework.Graphics/DSI_To_Display_Video_Mode.cpp +++ b/targets/CMSIS-OS/ChibiOS/ST_STM32F769I_DISCOVERY/nanoCLR/nanoFramework.Graphics/DSI_To_Display_Video_Mode.cpp @@ -8,43 +8,46 @@ // This driver is used to drive directly in video mode a LCD TFT using the DSI interface. #define STM32F769xx -#define UNUSED(X) (void)X // To avoid gcc/g++ warnings +#define UNUSED(X) (void)X // To avoid gcc/g++ warnings #include "stm32f769xx.h" #include "stm32f7xx.h" #include "Core_cm7.h" #include "DisplayInterface.h" #include -#include +#include #include #ifdef __cplusplus -extern "C" { uint32_t HAL_GetTick(void); } +extern "C" +{ + uint32_t HAL_GetTick(void); +} #endif void WaitForFIFOEmpty(); -extern CLR_UINT32 GraphicsVideoFrameBufferBegin; // Framebuffer set externally +extern CLR_UINT32 GraphicsVideoFrameBufferBegin; // Framebuffer set externally struct DisplayInterface g_DisplayInterface; -DSI_TypeDef* DsiRegister; // Structure to help access registers by name -LTDC_TypeDef* LtdcRegister; // Structure to help access registers by name -LTDC_Layer_TypeDef* LTDC_Layer_Register; // Structure to help access registers by name +DSI_TypeDef *DsiRegister; // Structure to help access registers by name +LTDC_TypeDef *LtdcRegister; // Structure to help access registers by name +LTDC_Layer_TypeDef *LTDC_Layer_Register; // Structure to help access registers by name -CLR_UINT32 LcdClock = 27429; // LcdClk = 27429 kHz -CLR_UINT32 laneByteClk_kHz = 62500; // 500 MHz / 8 = 62.5 MHz = 62500 kHz +CLR_UINT32 LcdClock = 27429; // LcdClk = 27429 kHz +CLR_UINT32 laneByteClk_kHz = 62500; // 500 MHz / 8 = 62.5 MHz = 62500 kHz // Default to landscape CLR_UINT32 lcd_x_size = 800; CLR_UINT32 lcd_y_size = 480; -void DisplayInterface::Initialize(DisplayInterfaceConfig& config) +void DisplayInterface::Initialize(DisplayInterfaceConfig &config) { (void)config; - - DsiRegister = DSI; // Base address of DSI Host/Wrapper registers - LtdcRegister = LTDC; // Base addres of LTDC registers - LTDC_Layer_Register = (LTDC_Layer_TypeDef*)((CLR_UINT32)LTDC + 0x84U); // Base address of layer registers (layer 0) + + DsiRegister = DSI; // Base address of DSI Host/Wrapper registers + LtdcRegister = LTDC; // Base addres of LTDC registers + LTDC_Layer_Register = (LTDC_Layer_TypeDef *)((CLR_UINT32)LTDC + 0x84U); // Base address of layer registers (layer 0) // Toggle Hardware Reset of the DSI LCD using its XRES signal (active low) palWritePad(GPIOJ, GPIOJ_DSI_RESET, PAL_LOW); @@ -53,27 +56,37 @@ void DisplayInterface::Initialize(DisplayInterfaceConfig& config) OS_DELAY(10); // LCD MspInit - SET_BIT(RCC->APB2ENR, RCC_APB2ENR_LTDCEN); // Enable the LTDC clock - RCC->APB2RSTR |= RCC_APB2RSTR_LTDCRST; // Toggle Sw reset of LTDC IP - RCC->APB2RSTR &= ~RCC_APB2RSTR_LTDCRST; // LTDC release reset - SET_BIT(RCC->APB2ENR, RCC_APB2ENR_DSIEN); // Enable DSI Host and wrapper clocks - RCC->APB2RSTR |= RCC_APB2RSTR_DSIRST; // Soft Reset the DSI Host and wrapper + SET_BIT(RCC->APB2ENR, RCC_APB2ENR_LTDCEN); // Enable the LTDC clock + RCC->APB2RSTR |= RCC_APB2RSTR_LTDCRST; // Toggle Sw reset of LTDC IP + RCC->APB2RSTR &= ~RCC_APB2RSTR_LTDCRST; // LTDC release reset + SET_BIT(RCC->APB2ENR, RCC_APB2ENR_DSIEN); // Enable DSI Host and wrapper clocks + RCC->APB2RSTR |= RCC_APB2RSTR_DSIRST; // Soft Reset the DSI Host and wrapper RCC->APB2RSTR &= ~RCC_APB2RSTR_DSIRST; - __NVIC_SetPriority(LTDC_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 3, 0)); // NVIC configuration for LTDC interrupt that is now enabled + __NVIC_SetPriority( + LTDC_IRQn, + NVIC_EncodePriority( + NVIC_GetPriorityGrouping(), + 3, + 0)); // NVIC configuration for LTDC interrupt that is now enabled NVIC_EnableIRQ(LTDC_IRQn); - __NVIC_SetPriority(DSI_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 3, 0)); // NVIC configuration for DSI interrupt that is now enabled + __NVIC_SetPriority( + DSI_IRQn, + NVIC_EncodePriority( + NVIC_GetPriorityGrouping(), + 3, + 0)); // NVIC configuration for DSI interrupt that is now enabled NVIC_EnableIRQ(DSI_IRQn); - CLEAR_BIT(DsiRegister->WCR, DSI_WCR_DSIEN); // Disable the DSI wrapper - CLEAR_BIT(DsiRegister->CR, DSI_CR_EN); // Disable the DSI host - DsiRegister->PCTLR &= ~(DSI_PCTLR_CKE | DSI_PCTLR_DEN); // D-PHY clock and digital disable - CLEAR_BIT(DsiRegister->WRPCR, DSI_WRPCR_PLLEN); // Turn off the DSI PLL - CLEAR_BIT(DsiRegister->WRPCR, DSI_WRPCR_REGEN); // Disable the regulator + CLEAR_BIT(DsiRegister->WCR, DSI_WCR_DSIEN); // Disable the DSI wrapper + CLEAR_BIT(DsiRegister->CR, DSI_CR_EN); // Disable the DSI host + DsiRegister->PCTLR &= ~(DSI_PCTLR_CKE | DSI_PCTLR_DEN); // D-PHY clock and digital disable + CLEAR_BIT(DsiRegister->WRPCR, DSI_WRPCR_PLLEN); // Turn off the DSI PLL + CLEAR_BIT(DsiRegister->WRPCR, DSI_WRPCR_REGEN); // Disable the regulator - SET_BIT(DsiRegister->WRPCR, DSI_WRPCR_REGEN); // Turn on the regulator and enable the DSI PLL + SET_BIT(DsiRegister->WRPCR, DSI_WRPCR_REGEN); // Turn on the regulator and enable the DSI PLL uint32_t tickstart = HAL_GetTick(); - while ((DsiRegister->WISR & DSI_WISR_RRS) == 0U) // Wait until the regulator is ready + while ((DsiRegister->WISR & DSI_WISR_RRS) == 0U) // Wait until the regulator is ready { if ((HAL_GetTick() - tickstart) > ((uint32_t)1000U)) { @@ -89,7 +102,7 @@ void DisplayInterface::Initialize(DisplayInterfaceConfig& config) SET_BIT(DsiRegister->WRPCR, DSI_WRPCR_PLLEN); tickstart = HAL_GetTick(); - while ((DsiRegister->WISR & DSI_WISR_PLLLS) == 0U) // Enable the DSI PLL and Wait until ready + while ((DsiRegister->WISR & DSI_WISR_PLLLS) == 0U) // Enable the DSI PLL and Wait until ready { if ((HAL_GetTick() - tickstart) > ((uint32_t)1000U)) { @@ -98,91 +111,94 @@ void DisplayInterface::Initialize(DisplayInterfaceConfig& config) } //************************** Set the PHY parameters ************************** - DsiRegister->PCTLR |= (DSI_PCTLR_CKE | DSI_PCTLR_DEN); // D-PHY clock and digital enable - DsiRegister->CLCR &= ~(DSI_CLCR_DPCC | DSI_CLCR_ACR); // Clock lane configuration - CLR_UINT32 AutomaticClockLaneControl = 0; // Disable automatic clock land control - DsiRegister->CLCR |= (DSI_CLCR_DPCC | AutomaticClockLaneControl); // AutomaticClockLaneControl is disabled - DsiRegister->PCONFR &= ~DSI_PCONFR_NL; // Configure the number of active data lanes - DsiRegister->PCONFR |= 1U; // Set number of Lanes to 2 + DsiRegister->PCTLR |= (DSI_PCTLR_CKE | DSI_PCTLR_DEN); // D-PHY clock and digital enable + DsiRegister->CLCR &= ~(DSI_CLCR_DPCC | DSI_CLCR_ACR); // Clock lane configuration + CLR_UINT32 AutomaticClockLaneControl = 0; // Disable automatic clock land control + DsiRegister->CLCR |= (DSI_CLCR_DPCC | AutomaticClockLaneControl); // AutomaticClockLaneControl is disabled + DsiRegister->PCONFR &= ~DSI_PCONFR_NL; // Configure the number of active data lanes + DsiRegister->PCONFR |= 1U; // Set number of Lanes to 2 //*********************** Set the DSI clock parameters *********************** - // Set the TX escape clock division factor + // Set the TX escape clock division factor DsiRegister->CCR &= ~DSI_CCR_TXECKDIV; - DsiRegister->CCR |= laneByteClk_kHz / 15620; //TXEscapeCkdiv = f(LaneByteClk)/15.62 = 4 + DsiRegister->CCR |= laneByteClk_kHz / 15620; // TXEscapeCkdiv = f(LaneByteClk)/15.62 = 4 - // Calculate the bit period in high-speed mode in unit of 0.25 ns (UIX4) - // The equation is : UIX4 = IntegerPart( (1000/F_PHY_Mhz) * 4 ) - // Where : F_PHY_Mhz = (NDIV * HSE_Mhz) / (IDF * ODF) + // Calculate the bit period in high-speed mode in unit of 0.25 ns (UIX4) + // The equation is : UIX4 = IntegerPart( (1000/F_PHY_Mhz) * 4 ) + // Where : F_PHY_Mhz = (NDIV * HSE_Mhz) / (IDF * ODF) uint32_t tempIDF = (PLLIDF > 0U) ? PLLIDF : 1U; DsiRegister->WPCR[0U] &= ~DSI_WPCR0_UIX4; - DsiRegister->WPCR[0U] |= (4000000U * tempIDF * ((1UL << (0x3U & PLLODF)))) / ((((uint32_t)25000000) / 1000U) * PLLNDIV); + DsiRegister->WPCR[0U] |= + (4000000U * tempIDF * ((1UL << (0x3U & PLLODF)))) / ((((uint32_t)25000000) / 1000U) * PLLNDIV); - // Disable all error interrupts and reset the Error Mask + // Disable all error interrupts and reset the Error Mask DsiRegister->IER[0U] = 0U; DsiRegister->IER[1U] = 0U; // Set Timing parameters of LTDC depending based on landscape 800x480 - CLR_UINT32 VSA = 1; // Vertical start active time in units of lines - CLR_UINT32 VBP = 15; // Vertical Back Porch time in units of lines ` - CLR_UINT32 VFP = 16; // Vertical Front Porch time in units of lines - CLR_UINT32 HSA = 2; // Horizontal start active time in units of lcdClk - CLR_UINT32 HBP = 34; // Horizontal Back Porch time in units of lcdClk - CLR_UINT32 HFP = 34; // Horizontal Front Porch time in units of lcdClk - CLR_UINT32 HACT = lcd_x_size; // Horizontal Active time in units of lcdClk = imageSize X in pixels to display - CLR_UINT32 VACT = lcd_y_size; // Vertical Active time in units of lines = imageSize Y in pixels to display - - // Configure DSI Video mode timings with settings set above - - // Select video mode by resetting CMDM and DSIM bits + CLR_UINT32 VSA = 1; // Vertical start active time in units of lines + CLR_UINT32 VBP = 15; // Vertical Back Porch time in units of lines ` + CLR_UINT32 VFP = 16; // Vertical Front Porch time in units of lines + CLR_UINT32 HSA = 2; // Horizontal start active time in units of lcdClk + CLR_UINT32 HBP = 34; // Horizontal Back Porch time in units of lcdClk + CLR_UINT32 HFP = 34; // Horizontal Front Porch time in units of lcdClk + CLR_UINT32 HACT = lcd_x_size; // Horizontal Active time in units of lcdClk = imageSize X in pixels to display + CLR_UINT32 VACT = lcd_y_size; // Vertical Active time in units of lines = imageSize Y in pixels to display + + // Configure DSI Video mode timings with settings set above + + // Select video mode by resetting CMDM and DSIM bits DsiRegister->MCR &= ~DSI_MCR_CMDM; DsiRegister->WCFGR &= ~DSI_WCFGR_DSIM; - // Configure the video mode transmission type + // Configure the video mode transmission type DsiRegister->VMCR &= ~DSI_VMCR_VMT; - DsiRegister->VMCR |= 2U; // Dsi Mode Video burst ie : one LgP per line; + DsiRegister->VMCR |= 2U; // Dsi Mode Video burst ie : one LgP per line; - // Configure the video packet size + // Configure the video packet size DsiRegister->VPCR &= ~DSI_VPCR_VPSIZE; - DsiRegister->VPCR |= HACT; // Display dependent + DsiRegister->VPCR |= HACT; // Display dependent - // Set the chunks number to be transmitted through the DSI link + // Set the chunks number to be transmitted through the DSI link DsiRegister->VCCR &= ~DSI_VCCR_NUMC; - DsiRegister->VCCR |= 0; // zero chunks + DsiRegister->VCCR |= 0; // zero chunks - // Set the size of the null packet + // Set the size of the null packet DsiRegister->VNPCR &= ~DSI_VNPCR_NPSIZE; DsiRegister->VNPCR |= 0xFFF; - // Select the virtual channel for the LTDC interface traffic + // Select the virtual channel for the LTDC interface traffic DsiRegister->LVCIDR &= ~DSI_LVCIDR_VCID; - DsiRegister->LVCIDR |= 0; // LCD Display OTM8009A DSI Virtual Channel ID + DsiRegister->LVCIDR |= 0; // LCD Display OTM8009A DSI Virtual Channel ID - // Configure the polarity of control signals + // Configure the polarity of control signals DsiRegister->LPCR &= ~(DSI_LPCR_DEP | DSI_LPCR_VSP | DSI_LPCR_HSP); - DsiRegister->LPCR |= (0x00000000U | 0x00000000U | 0x00000000U); // Vertical, Horizontal active high, DSI data enable active high + DsiRegister->LPCR |= + (0x00000000U | 0x00000000U | 0x00000000U); // Vertical, Horizontal active high, DSI data enable active high - // Select the color coding for the host + // Select the color coding for the host DsiRegister->LCOLCR &= ~DSI_LCOLCR_COLC; - DsiRegister->LCOLCR |= 0x00000000U; // RGB565 + DsiRegister->LCOLCR |= 0x00000000U; // RGB565 - // Select the color coding for the wrapper + // Select the color coding for the wrapper DsiRegister->WCFGR &= ~DSI_WCFGR_COLMUX; - DsiRegister->WCFGR |= (0x00000000U << 1U); // RGB565 + DsiRegister->WCFGR |= (0x00000000U << 1U); // RGB565 - // Set the Horizontal Synchronization Active (HSA) in lane byte clock cycles + // Set the Horizontal Synchronization Active (HSA) in lane byte clock cycles DsiRegister->VHSACR &= ~DSI_VHSACR_HSA; DsiRegister->VHSACR |= (HSA * laneByteClk_kHz) / LcdClock; - // Set the Horizontal Back Porch (HBP) in lane byte clock cycles + // Set the Horizontal Back Porch (HBP) in lane byte clock cycles DsiRegister->VHBPCR &= ~DSI_VHBPCR_HBP; DsiRegister->VHBPCR |= (HBP * laneByteClk_kHz) / LcdClock; - // Set the total line time (HLINE=HSA+HBP+HACT+HFP) in lane byte clock cycles + // Set the total line time (HLINE=HSA+HBP+HACT+HFP) in lane byte clock cycles DsiRegister->VLCR &= ~DSI_VLCR_HLINE; - DsiRegister->VLCR |= ((HACT + HSA + HBP + HFP) * laneByteClk_kHz) / LcdClock; // Value depending on display orientation choice portrait/landscape + DsiRegister->VLCR |= ((HACT + HSA + HBP + HFP) * laneByteClk_kHz) / + LcdClock; // Value depending on display orientation choice portrait/landscape - // Set the Vertical Synchronization Active (VSA) + // Set the Vertical Synchronization Active (VSA) DsiRegister->VVSACR &= ~DSI_VVSACR_VSA; DsiRegister->VVSACR |= VSA; @@ -198,43 +214,43 @@ void DisplayInterface::Initialize(DisplayInterfaceConfig& config) DsiRegister->VVACR &= ~DSI_VVACR_VA; DsiRegister->VVACR |= VACT; - // Configure the command transmission mode + // Configure the command transmission mode DsiRegister->VMCR &= ~DSI_VMCR_LPCE; DsiRegister->VMCR |= DSI_VMCR_LPCE; - // Low power largest packet size + // Low power largest packet size DsiRegister->LPMCR &= ~DSI_LPMCR_LPSIZE; - DsiRegister->LPMCR |= ((16) << 16U); // Largest packet size possible to transmit in LP mode in VSA, VBP, VFP regions + DsiRegister->LPMCR |= ((16) << 16U); // Largest packet size possible to transmit in LP mode in VSA, VBP, VFP regions - // Low power VACT largest packet size + // Low power VACT largest packet size DsiRegister->LPMCR &= ~DSI_LPMCR_VLPSIZE; - DsiRegister->LPMCR |= 0; // Only useful when sending LP packets is allowed while streaming is active in video mode + DsiRegister->LPMCR |= 0; // Only useful when sending LP packets is allowed while streaming is active in video mode - // Enable LP transition in HFP period + // Enable LP transition in HFP period DsiRegister->VMCR &= ~DSI_VMCR_LPHFPE; - DsiRegister->VMCR |= DSI_VMCR_LPHFPE; // Allow sending LP commands during HFP period + DsiRegister->VMCR |= DSI_VMCR_LPHFPE; // Allow sending LP commands during HFP period - // Enable LP transition in HBP period + // Enable LP transition in HBP period DsiRegister->VMCR &= ~DSI_VMCR_LPHBPE; - DsiRegister->VMCR |= DSI_VMCR_LPHBPE; // Allow sending LP commands during HBP period + DsiRegister->VMCR |= DSI_VMCR_LPHBPE; // Allow sending LP commands during HBP period - // Enable LP transition in VACT period + // Enable LP transition in VACT period DsiRegister->VMCR &= ~DSI_VMCR_LPVAE; - DsiRegister->VMCR |= DSI_VMCR_LPVAE; // Allow sending LP commands during VACT period + DsiRegister->VMCR |= DSI_VMCR_LPVAE; // Allow sending LP commands during VACT period - // Enable LP transition in VFP period + // Enable LP transition in VFP period DsiRegister->VMCR &= ~DSI_VMCR_LPVFPE; - DsiRegister->VMCR |= DSI_VMCR_LPVFPE; // Allow sending LP commands during VFP period + DsiRegister->VMCR |= DSI_VMCR_LPVFPE; // Allow sending LP commands during VFP period - // Enable LP transition in VBP period + // Enable LP transition in VBP period DsiRegister->VMCR &= ~DSI_VMCR_LPVBPE; - DsiRegister->VMCR |= DSI_VMCR_LPVBPE; // Allow sending LP commands during VBP period + DsiRegister->VMCR |= DSI_VMCR_LPVBPE; // Allow sending LP commands during VBP period - // Enable LP transition in vertical sync period + // Enable LP transition in vertical sync period DsiRegister->VMCR &= ~DSI_VMCR_LPVSAE; - DsiRegister->VMCR |= DSI_VMCR_LPVSAE; // Allow sending LP commands during VSync = VSA period + DsiRegister->VMCR |= DSI_VMCR_LPVSAE; // Allow sending LP commands during VSync = VSA period - // Enable the request for an acknowledge response at the end of a frame + // Enable the request for an acknowledge response at the end of a frame DsiRegister->VMCR &= ~DSI_VMCR_FBTAAE; DsiRegister->VMCR |= 0; @@ -250,8 +266,9 @@ void DisplayInterface::Initialize(DisplayInterfaceConfig& config) // What to do here? } } - // Read PLLSAIP and PLLSAIQ value from PLLSAICFGR register (these value are not needed for LTDC configuration) - RCC->PLLSAICFGR = (384 << RCC_PLLSAICFGR_PLLSAIN_Pos) | + // Read PLLSAIP and PLLSAIQ value from PLLSAICFGR register (these value are not needed for LTDC configuration) + RCC->PLLSAICFGR = + (384 << RCC_PLLSAICFGR_PLLSAIN_Pos) | (((RCC->PLLSAICFGR & RCC_PLLSAICFGR_PLLSAIP) >> RCC_PLLSAICFGR_PLLSAIP_Pos) << RCC_PLLSAICFGR_PLLSAIP_Pos) | (((RCC->PLLSAICFGR & RCC_PLLSAICFGR_PLLSAIQ) >> RCC_PLLSAICFGR_PLLSAIQ_Pos) << RCC_PLLSAICFGR_PLLSAIQ_Pos) | (7 << RCC_PLLSAICFGR_PLLSAIR_Pos); @@ -266,53 +283,56 @@ void DisplayInterface::Initialize(DisplayInterfaceConfig& config) // what to do here? } } - // To avoid any synchronization issue, the DSI shall be started after enabling the LTDC + // To avoid any synchronization issue, the DSI shall be started after enabling the LTDC - // Configure the HS, VS, DE and PC polarity + // Configure the HS, VS, DE and PC polarity LtdcRegister->GCR &= ~(LTDC_GCR_HSPOL | LTDC_GCR_VSPOL | LTDC_GCR_DEPOL | LTDC_GCR_PCPOL); LtdcRegister->GCR |= (LTDC_GCR_HSPOL | LTDC_GCR_VSPOL); // PCPolarity = 0, DEPolarity = 0; - // Set Synchronization size + // Set Synchronization size LtdcRegister->SSCR &= ~(LTDC_SSCR_VSH | LTDC_SSCR_HSW); LtdcRegister->SSCR |= (((HSA - 1) << 16U) | (VSA - 1U)); - // Set Accumulated Back porch + // Set Accumulated Back porch LtdcRegister->BPCR &= ~(LTDC_BPCR_AVBP | LTDC_BPCR_AHBP); LtdcRegister->BPCR |= (((HSA + HBP - 1) << 16U) | (VSA + VBP - 1U)); - // Set Accumulated Active Width + // Set Accumulated Active Width LtdcRegister->AWCR &= ~(LTDC_AWCR_AAH | LTDC_AWCR_AAW); LtdcRegister->AWCR |= (((lcd_x_size + HSA + HBP - 1) << 16U) | (VSA + VBP + VACT - 1U)); - // Set Total Width + // Set Total Width LtdcRegister->TWCR &= ~(LTDC_TWCR_TOTALH | LTDC_TWCR_TOTALW); LtdcRegister->TWCR |= (((lcd_x_size + HSA + HBP + HFP - 1) << 16U) | (VSA + VBP + VACT + VFP - 1U)); - // Set the background color value + // Set the background color value LtdcRegister->BCCR &= ~(LTDC_BCCR_BCBLUE | LTDC_BCCR_BCGREEN | LTDC_BCCR_BCRED); - LtdcRegister->BCCR |= 0; // White background + LtdcRegister->BCCR |= 0; // White background - LtdcRegister->IER |= (LTDC_IER_TERRIE | LTDC_IER_FUIE); // Enable the Transfer Error and FIFO underrun interrupts - LtdcRegister->GCR |= LTDC_GCR_LTDCEN; // Enable LTDC by setting LTDCEN bit + LtdcRegister->IER |= (LTDC_IER_TERRIE | LTDC_IER_FUIE); // Enable the Transfer Error and FIFO underrun interrupts + LtdcRegister->GCR |= LTDC_GCR_LTDCEN; // Enable LTDC by setting LTDCEN bit // Start DSI - SET_BIT(DsiRegister->CR, DSI_CR_EN); // Enable the DSI host - SET_BIT(DsiRegister->WCR, DSI_WCR_DSIEN); // Enable the DSI wrapper + SET_BIT(DsiRegister->CR, DSI_CR_EN); // Enable the DSI host + SET_BIT(DsiRegister->WCR, DSI_WCR_DSIEN); // Enable the DSI wrapper // Layer Init - CLR_UINT32 PixelFormat = 0x00000002U; // PIXEL FORMAT RGB565; + CLR_UINT32 PixelFormat = 0x00000002U; // PIXEL FORMAT RGB565; CLR_UINT32 Alpha = 255; - CLR_UINT32 BlendingFactor1 = 0x00000600U; // Blending factor : Cte Alpha x Pixel Alpha - CLR_UINT32 BlendingFactor2 = 0x00000007U; // Blending factor : Cte Alpha x Pixel Alpha + CLR_UINT32 BlendingFactor1 = 0x00000600U; // Blending factor : Cte Alpha x Pixel Alpha + CLR_UINT32 BlendingFactor2 = 0x00000007U; // Blending factor : Cte Alpha x Pixel Alpha - //LCD - TFT Display layer x Controller + // LCD - TFT Display layer x Controller // Configure the horizontal start and stop position LTDC_Layer_Register->WHPCR &= ~LTDC_LxWHPCR_WHSTPOS | LTDC_LxWHPCR_WHSPPOS; - LTDC_Layer_Register->WHPCR = ((((LtdcRegister->BPCR & LTDC_BPCR_AHBP) >> 16U) + 1U) | ((lcd_x_size + ((LtdcRegister->BPCR & LTDC_BPCR_AHBP) >> 16U)) << 16U)); + LTDC_Layer_Register->WHPCR = + ((((LtdcRegister->BPCR & LTDC_BPCR_AHBP) >> 16U) + 1U) | + ((lcd_x_size + ((LtdcRegister->BPCR & LTDC_BPCR_AHBP) >> 16U)) << 16U)); // Configure the vertical start and stop position LTDC_Layer_Register->WVPCR &= ~LTDC_LxWVPCR_WVSTPOS | LTDC_LxWVPCR_WVSPPOS; - LTDC_Layer_Register->WVPCR = (((LtdcRegister->BPCR & LTDC_BPCR_AVBP) + 1U) | ((lcd_y_size + (LtdcRegister->BPCR & LTDC_BPCR_AVBP)) << 16U)); + LTDC_Layer_Register->WVPCR = + (((LtdcRegister->BPCR & LTDC_BPCR_AVBP) + 1U) | ((lcd_y_size + (LtdcRegister->BPCR & LTDC_BPCR_AVBP)) << 16U)); // Specify the pixel format LTDC_Layer_Register->PFCR &= ~LTDC_LxPFCR_PF; @@ -320,7 +340,8 @@ void DisplayInterface::Initialize(DisplayInterfaceConfig& config) // Configure the default color values LTDC_Layer_Register->DCCR &= ~(LTDC_LxDCCR_DCBLUE | LTDC_LxDCCR_DCGREEN | LTDC_LxDCCR_DCRED | LTDC_LxDCCR_DCALPHA); - LTDC_Layer_Register->DCCR = 0;; + LTDC_Layer_Register->DCCR = 0; + ; // Specify the constant alpha value LTDC_Layer_Register->CACR &= ~(LTDC_LxCACR_CONSTA); @@ -344,21 +365,22 @@ void DisplayInterface::Initialize(DisplayInterfaceConfig& config) // Enable LTDC_Layer by setting LEN bit LTDC_Layer_Register->CR |= (uint32_t)LTDC_LxCR_LEN; - LtdcRegister->SRCR = LTDC_SRCR_IMR; // Set the Immediate Reload type + LtdcRegister->SRCR = LTDC_SRCR_IMR; // Set the Immediate Reload type ClearFrameBuffer(); return; } -void DisplayInterface::GetTransferBuffer(CLR_UINT8*& TransferBuffer, CLR_UINT32& TransferBufferSize) + +void DisplayInterface::GetTransferBuffer(CLR_UINT8 *&TransferBuffer, CLR_UINT32 &TransferBufferSize) { - UNUSED(TransferBuffer); - UNUSED(TransferBufferSize); - return; + TransferBuffer = (CLR_UINT8 *)&GraphicsVideoFrameBufferBegin; + TransferBufferSize = (lcd_x_size * lcd_y_size * 2); } + void DisplayInterface::ClearFrameBuffer() { - CLR_UINT32* frameBuffer = (CLR_UINT32*)&GraphicsVideoFrameBufferBegin; + CLR_UINT32 *frameBuffer = (CLR_UINT32 *)&GraphicsVideoFrameBufferBegin; CLR_INT32 DataCount32 = (lcd_x_size * lcd_y_size * 2) / 4; for (CLR_INT32 i = 0; i < DataCount32; i++) { @@ -367,21 +389,27 @@ void DisplayInterface::ClearFrameBuffer() frameBuffer++; } } -void DisplayInterface::WriteToFrameBuffer(CLR_UINT8 command, CLR_UINT8 data[], CLR_UINT32 dataCount) + +void DisplayInterface::WriteToFrameBuffer( + CLR_UINT8 command, + CLR_UINT8 data[], + CLR_UINT32 dataCount, + CLR_UINT32 frameOffset) { UNUSED(command); - CLR_UINT32* frameBuffer = (CLR_UINT32*)&GraphicsVideoFrameBufferBegin; - CLR_UINT32* source = (CLR_UINT32*)&data[0]; - CLR_INT32 DataCount32 = dataCount / 4; - for (CLR_INT32 i = 0; i < DataCount32; i++) + + CLR_UINT16 *pFrameBuffer = (CLR_UINT16 *)&GraphicsVideoFrameBufferBegin; + pFrameBuffer += frameOffset; + + CLR_UINT16 *p16Data = (CLR_UINT16 *)&data[0]; + dataCount /= 2; // copy 16 bits + + for (CLR_UINT32 i = 0; i < dataCount; i++) { - // Note: Write out 4 byte ints measured to be faster than bytes (* 3.5-4 times) - *frameBuffer = *source; - frameBuffer++; - source++; + *pFrameBuffer++ = *p16Data++; } - return; } + void DisplayInterface::DisplayBacklight(bool on) { uint8_t OTM8009A_CMD_DISPOFF = 0x28; // Display Off command @@ -400,59 +428,61 @@ void DisplayInterface::SendCommand(CLR_UINT8 NbrParams, ...) va_list ap; va_start(ap, NbrParams); volatile CLR_UINT8 command; - const int DSI_DCS_SHORT_PKT_WRITE_P1 = 0x00000015U; // DCS short write, one parameter - const int DSI_DCS_LONG_PKT_WRITE = 0x00000039U; // DCS long write, multiple parameters - const uint32_t LCD_OTM8009A_ID = 0; // LCD Display OTM8009A DSI Virtual Channel ID + const int DSI_DCS_SHORT_PKT_WRITE_P1 = 0x00000015U; // DCS short write, one parameter + const int DSI_DCS_LONG_PKT_WRITE = 0x00000039U; // DCS long write, multiple parameters + const uint32_t LCD_OTM8009A_ID = 0; // LCD Display OTM8009A DSI Virtual Channel ID WaitForFIFOEmpty(); switch (NbrParams) { - case 1: - { - command = va_arg(ap, int); - DsiRegister->GHCR = (DSI_DCS_SHORT_PKT_WRITE_P1 | (LCD_OTM8009A_ID << 6U) | (command << 8U)); - break; - } - case 2: - { - command = va_arg(ap, int); - volatile CLR_UINT8 parameter = va_arg(ap, int); - DsiRegister->GHCR = (DSI_DCS_SHORT_PKT_WRITE_P1 | (LCD_OTM8009A_ID << 6U) | (command << 8U) | (parameter << 16U)); - break; - } - default: - { - - WaitForFIFOEmpty(); - - // Set the DCS code on payload byte 1, and the other parameters on the write FIFO command - uint32_t fifoword = va_arg(ap, int); - uint32_t nbBytes = (NbrParams < 3U) ? NbrParams : 3U; - for (CLR_UINT8 count = 0U; count < nbBytes; count++) + case 1: { - CLR_UINT8 parameter = va_arg(ap, int); - fifoword |= parameter << (8U + (8U * count)); + command = va_arg(ap, int); + DsiRegister->GHCR = (DSI_DCS_SHORT_PKT_WRITE_P1 | (LCD_OTM8009A_ID << 6U) | (command << 8U)); + break; } - DsiRegister->GPDR = fifoword; - - uint32_t uicounter = NbrParams - nbBytes; - while (uicounter != 0U) // Set the Next parameters on the write FIFO command + case 2: { - nbBytes = (uicounter < 4U) ? uicounter : 4U; - fifoword = 0U; + command = va_arg(ap, int); + volatile CLR_UINT8 parameter = va_arg(ap, int); + DsiRegister->GHCR = + (DSI_DCS_SHORT_PKT_WRITE_P1 | (LCD_OTM8009A_ID << 6U) | (command << 8U) | (parameter << 16U)); + break; + } + default: + { + + WaitForFIFOEmpty(); + + // Set the DCS code on payload byte 1, and the other parameters on the write FIFO command + uint32_t fifoword = va_arg(ap, int); + uint32_t nbBytes = (NbrParams < 3U) ? NbrParams : 3U; for (CLR_UINT8 count = 0U; count < nbBytes; count++) { CLR_UINT8 parameter = va_arg(ap, int); - fifoword |= parameter << (8U * count); + fifoword |= parameter << (8U + (8U * count)); } DsiRegister->GPDR = fifoword; - uicounter -= nbBytes; - } - - DsiRegister->GHCR = (DSI_DCS_LONG_PKT_WRITE | (LCD_OTM8009A_ID << 6U) | (((NbrParams + 1U) & 0x00FFU) << 8U) | ((((NbrParams + 1U) & 0xFF00U) >> 8U) << 16U)); // Update the DSI packet header with new information + uint32_t uicounter = NbrParams - nbBytes; + while (uicounter != 0U) // Set the Next parameters on the write FIFO command + { + nbBytes = (uicounter < 4U) ? uicounter : 4U; + fifoword = 0U; + for (CLR_UINT8 count = 0U; count < nbBytes; count++) + { + CLR_UINT8 parameter = va_arg(ap, int); + fifoword |= parameter << (8U * count); + } + DsiRegister->GPDR = fifoword; + + uicounter -= nbBytes; + } - } + DsiRegister->GHCR = + (DSI_DCS_LONG_PKT_WRITE | (LCD_OTM8009A_ID << 6U) | (((NbrParams + 1U) & 0x00FFU) << 8U) | + ((((NbrParams + 1U) & 0xFF00U) >> 8U) << 16U)); // Update the DSI packet header with new information + } } } void WaitForFIFOEmpty() @@ -466,6 +496,3 @@ void WaitForFIFOEmpty() } } } - - - diff --git a/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/app_main.c b/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/app_main.c index 58b0feeb85..964c53fccb 100644 --- a/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/app_main.c +++ b/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/app_main.c @@ -12,50 +12,50 @@ #include #include -extern void CLRStartupThread(void const * argument); +extern void CLRStartupThread(void const *argument); // Mutex for GLOBAL_LOCK / GLOBAL_UNLOCK portMUX_TYPE globalLockMutex = portMUX_INITIALIZER_UNLOCKED; void receiver_task(void *pvParameter) { - (void)pvParameter; + (void)pvParameter; - ReceiverThread( 0); - - vTaskDelete(NULL); + ReceiverThread(0); + + vTaskDelete(NULL); } // Main task start point void main_task(void *pvParameter) { - (void)pvParameter; - - // CLR settings to launch CLR thread - CLR_SETTINGS clrSettings; - (void)memset(&clrSettings, 0, sizeof(CLR_SETTINGS)); - - clrSettings.MaxContextSwitches = 50; - clrSettings.WaitForDebugger = false; - clrSettings.EnterDebuggerLoopAfterExit = true; - - CLRStartupThread(&clrSettings); - - vTaskDelete(NULL); + (void)pvParameter; + + // CLR settings to launch CLR thread + CLR_SETTINGS clrSettings; + (void)memset(&clrSettings, 0, sizeof(CLR_SETTINGS)); + + clrSettings.MaxContextSwitches = 50; + clrSettings.WaitForDebugger = false; + clrSettings.EnterDebuggerLoopAfterExit = true; + + CLRStartupThread(&clrSettings); + + vTaskDelete(NULL); } -// App_main +// App_main // Called from Esp32 IDF start up code before scheduler starts // -void app_main() +void app_main() { - // Switch off logging so as not to interfere with WireProtocol over Uart0 - esp_log_level_set("*", ESP_LOG_NONE); - - ESP_ERROR_CHECK( nvs_flash_init() ); - - xTaskCreatePinnedToCore(&receiver_task, "ReceiverThread", 2048, NULL, 5, NULL, 1); - - // Start the main task pinned to 2nd core - xTaskCreatePinnedToCore(&main_task, "main_task", 15000, NULL, 5, NULL, 1); + // Switch off logging so as not to interfere with WireProtocol over Uart0 + esp_log_level_set("*", ESP_LOG_NONE); + + ESP_ERROR_CHECK(nvs_flash_init()); + + xTaskCreatePinnedToCore(&receiver_task, "ReceiverThread", 2048, NULL, 5, NULL, 1); + + // Start the main task pinned to 2nd core + xTaskCreatePinnedToCore(&main_task, "main_task", 15000, NULL, 5, NULL, 1); } diff --git a/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/targetHAL.cpp b/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/targetHAL.cpp index f68f1f14b3..b57bb3a5b4 100644 --- a/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/targetHAL.cpp +++ b/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/targetHAL.cpp @@ -100,11 +100,12 @@ void nanoHAL_Initialize() // Initialise Graphics after devices initialised DisplayInterfaceConfig displayConfig; + // Define SPI display configuration for Wrover displayConfig.Spi.spiBus = 1; // Spi Bus displayConfig.Spi.chipSelect = GPIO_NUM_22; // CS_1 GPIO22 CS displayConfig.Spi.dataCommand = GPIO_NUM_21; // D/CX_1 GPIO21 D/C - displayConfig.Spi.reset = GPIO_NUM_18; // RST_1 GPIO18 RESET - displayConfig.Spi.backLight = GPIO_NUM_5; // GPIO5 Backlight + displayConfig.Spi.reset = GPIO_NUM_18; // RST_1 GPIO18 RESET + displayConfig.Spi.backLight = GPIO_NUM_5; // GPIO5 Backlight g_DisplayInterface.Initialize(displayConfig); g_DisplayDriver.Initialize(); @@ -116,6 +117,8 @@ void nanoHAL_Initialize() // Gesture_Initialize(); // Ink_Initialize(); + g_DisplayDriver.Clear(); + #endif // no PAL events required until now