From 66378b808c266e90de86af5f8a894d759e860c6c Mon Sep 17 00:00:00 2001 From: Adrian Soundy Date: Mon, 23 Nov 2020 18:59:05 +1300 Subject: [PATCH 1/5] Display Flush fix plus extra SPI displays --- .../Graphics/Displays/Display.h | 26 +- .../Graphics/Displays/DisplayInterface.h | 35 +- .../Graphics/Displays/ILI9341_240x320_SPI.cpp | 292 +++++++++---- .../Displays/Otm8009a_DSI_Video_Mode.cpp | 410 ++++++++++++++---- .../Graphics/Displays/SSD1306_128x64.cpp | 253 +++++++++++ .../Graphics/Displays/SSD1331_94x64_SPI.cpp | 321 ++++++++++++++ .../Graphics/Displays/ST7789V_240x320_SPI.cpp | 261 +++++++---- .../Graphics/Displays/Spi_To_Display.cpp | 170 +++++--- .../DSI_To_Display_Video_Mode.cpp | 369 ++++++++-------- .../ESP32_WROOM_32/nanoCLR/app_main.c | 2 +- .../ESP32_WROOM_32/nanoCLR/targetHAL.cpp | 13 +- 11 files changed, 1630 insertions(+), 522 deletions(-) create mode 100644 src/nanoFramework.Graphics/Graphics/Displays/SSD1306_128x64.cpp create mode 100644 src/nanoFramework.Graphics/Graphics/Displays/SSD1331_94x64_SPI.cpp 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..72eb5c30ef --- /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..833fe11842 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,100 @@ 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::SendCommand2(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); + +// CPU_GPIO_SetPinState(LcdDC, GpioPinValue_High); // Data mode + +// int parameterCount = arg_count - 1; +// if (parameterCount) +// { +// if ( CommandMode == 1) +// { +// CPU_GPIO_SetPinState(LcdDC, GpioPinValue_Low); // Command mode + +// for (int i = 0; i < parameterCount; i++) +// { +// command = va_arg(ap, int); +// SendBytes(&command, 1); +// } + +// // All command & parameters use D/C signal +// CPU_GPIO_SetPinState(LcdDC, GpioPinValue_High); // Data mode +// } +// else +// { +// // Following bytes are data related to the command +// CLR_UINT8 parameters[parameterCount]; +// for (int i = 0; i < parameterCount; i++) +// { +// parameters[i] = va_arg(ap, int); +// } + +// SendBytes(parameters, parameterCount); +// } +// } +// return; +// } + 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 +205,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..d43408f15c 100644 --- a/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/app_main.c +++ b/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/app_main.c @@ -50,7 +50,7 @@ void main_task(void *pvParameter) void app_main() { // Switch off logging so as not to interfere with WireProtocol over Uart0 - esp_log_level_set("*", ESP_LOG_NONE); + esp_log_level_set("*", ESP_LOG_NONE); ESP_ERROR_CHECK( nvs_flash_init() ); diff --git a/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/targetHAL.cpp b/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/targetHAL.cpp index f68f1f14b3..328d8d65ff 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; - 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 + // 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 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 From 79d981c9c65fe9d5d1d32c405e4d3cb1e009449f Mon Sep 17 00:00:00 2001 From: nfbot Date: Mon, 23 Nov 2020 06:17:19 +0000 Subject: [PATCH 2/5] Code style fixes Automated fixes for code style. --- .../Graphics/Displays/SSD1306_128x64.cpp | 2 +- .../ESP32_WROOM_32/nanoCLR/app_main.c | 58 +++++++++---------- .../ESP32_WROOM_32/nanoCLR/targetHAL.cpp | 10 ++-- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/nanoFramework.Graphics/Graphics/Displays/SSD1306_128x64.cpp b/src/nanoFramework.Graphics/Graphics/Displays/SSD1306_128x64.cpp index 72eb5c30ef..1c177e8f6f 100644 --- a/src/nanoFramework.Graphics/Graphics/Displays/SSD1306_128x64.cpp +++ b/src/nanoFramework.Graphics/Graphics/Displays/SSD1306_128x64.cpp @@ -123,7 +123,7 @@ bool DisplayDriver::ChangeOrientation(DisplayOrientation orientation) { case PORTRAIT: case PORTRAIT180: - return false; + return false; case LANDSCAPE: case LANDSCAPE180: 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 d43408f15c..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 328d8d65ff..b57bb3a5b4 100644 --- a/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/targetHAL.cpp +++ b/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/targetHAL.cpp @@ -101,11 +101,11 @@ void nanoHAL_Initialize() 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.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 g_DisplayInterface.Initialize(displayConfig); g_DisplayDriver.Initialize(); From 361d59e3fa0d2c8a6fecd2da402836ffd632edeb Mon Sep 17 00:00:00 2001 From: Adrian Soundy Date: Mon, 23 Nov 2020 22:39:57 +1300 Subject: [PATCH 3/5] Auto stash before merge of "ui-fixes" and "upstream/nfbot/clang-format-fix/875c8a43-7dbb-4855-adf7-46061a013be0" --- CMake/Modules/FindWindows.Devices.I2c.cmake | 4 +- src/HAL/Include/nanoHAL.h | 4 + src/HAL/Include/nanoHAL_I2C.h | 54 +++++ src/PAL/Include/CPU_I2C_decl.h | 33 +++ src/Windows.Devices.I2c/nanoHAL_I2C.cpp | 199 ++++++++++++++++++ src/Windows.Devices.I2c/win_dev_i2c_native.h | 16 +- .../Graphics/Displays/I2C_To_Display.cpp | 114 ++++++++++ .../ESP32_WROOM_32/Include/targetHAL_I2c.h | 10 + .../nanoCLR/Windows.Devices.I2c/cpu_i2c.cpp | 140 ++++++++++++ .../ESP32_WROOM_32/nanoCLR/targetHAL.cpp | 12 +- 10 files changed, 571 insertions(+), 15 deletions(-) create mode 100644 src/HAL/Include/nanoHAL_I2C.h create mode 100644 src/PAL/Include/CPU_I2C_decl.h create mode 100644 src/Windows.Devices.I2c/nanoHAL_I2C.cpp create mode 100644 src/nanoFramework.Graphics/Graphics/Displays/I2C_To_Display.cpp create mode 100644 targets/FreeRTOS_ESP32/ESP32_WROOM_32/Include/targetHAL_I2c.h create mode 100644 targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/Windows.Devices.I2c/cpu_i2c.cpp diff --git a/CMake/Modules/FindWindows.Devices.I2c.cmake b/CMake/Modules/FindWindows.Devices.I2c.cmake index ac64849602..be8fa6b2af 100644 --- a/CMake/Modules/FindWindows.Devices.I2c.cmake +++ b/CMake/Modules/FindWindows.Devices.I2c.cmake @@ -19,6 +19,8 @@ list(APPEND Windows.Devices.I2c_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/src/Windows.Dev # source files set(Windows.Devices.I2c_SRCS + cpu_i2c.cpp + nanoHAL_i2c.cpp win_dev_i2c_native.cpp win_dev_i2c_native_Windows_Devices_I2C_I2cDevice.cpp win_dev_i2c_native_Windows_Devices_I2c_I2cController.cpp @@ -36,7 +38,7 @@ foreach(SRC_FILE ${Windows.Devices.I2c_SRCS}) CMAKE_FIND_ROOT_PATH_BOTH ) - # message("${SRC_FILE} >> ${Windows.Devices.I2c_SRC_FILE}") # debug helper + message("${SRC_FILE} >> ${Windows.Devices.I2c_SRC_FILE}") # debug helper list(APPEND Windows.Devices.I2c_SOURCES ${Windows.Devices.I2c_SRC_FILE}) endforeach() diff --git a/src/HAL/Include/nanoHAL.h b/src/HAL/Include/nanoHAL.h index 8961c9419f..e3052d3f6e 100644 --- a/src/HAL/Include/nanoHAL.h +++ b/src/HAL/Include/nanoHAL.h @@ -842,6 +842,10 @@ extern bool g_fDoNotUninitializeDebuggerPort; // SPI driver #include +// +// I2C driver +#include + // //// External bus interface driver //#include diff --git a/src/HAL/Include/nanoHAL_I2C.h b/src/HAL/Include/nanoHAL_I2C.h new file mode 100644 index 0000000000..affd575cd0 --- /dev/null +++ b/src/HAL/Include/nanoHAL_I2C.h @@ -0,0 +1,54 @@ +// +// 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. +// + +#ifndef _NANO_I2C_DECL_H_ +#define _NANO_I2C_DECL_H_ 1 + +#include "TargetHAL_I2c.h" + +#include "CPU_I2C_decl.h" + +#define CPU_DEVICE_TYPE_I2C 2 + +// enum I2CResult +// { +// I2CResult_OK, +// I2CResult_FAIL, +// I2CResult_TIMEOUT +// }; + +struct I2C_DEVICE_CONFIGURATION +{ + int8_t i2cBus; // I2C Bus number + uint8_t address; // Device address on bus + bool fastMode; // Speed false=normal(100Khz), true=fast(400khz) +}; + + +// Initialise the i2cConfig structure +// Called on CLR startup +bool nanoI2C_Initialize(); +void nanoI2C_Uninitialize(); + + // Open I2C bus / device using device configuration +// Register GPIO pins as in use. +// Return result, if S_OK then deviceHandle returned +HRESULT nanoI2C_OpenDevice(I2C_DEVICE_CONFIGURATION &config, uint32_t &deviceHandle); + +// Execute a I2C write/read operation +// return status of operation as I2CResult +I2cTransferStatus nanoI2C_WriteRead( + uint32_t deviceHandle, + uint8_t *writeBuffer, + uint16_t writeSize, + uint8_t *readBuffer, + uint16_t readSize); + +// Close I2C device +// When last device closed bus is closed +HRESULT nanoI2C_CloseDevice(uint32_t deviceHandle); + +#endif // _NANO_I2C_DECL_H_ diff --git a/src/PAL/Include/CPU_I2C_decl.h b/src/PAL/Include/CPU_I2C_decl.h new file mode 100644 index 0000000000..7927fea013 --- /dev/null +++ b/src/PAL/Include/CPU_I2C_decl.h @@ -0,0 +1,33 @@ +// +// 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. +// + +#ifndef _DRIVERS_I2C_DECL_H_ +#define _DRIVERS_I2C_DECL_H_ 1 + +enum I2cTransferStatus +{ + I2cTransferStatus_FullTransfer = 0, + I2cTransferStatus_ClockStretchTimeout, + I2cTransferStatus_PartialTransfer, + I2cTransferStatus_SlaveAddressNotAcknowledged, + I2cTransferStatus_UnknownError +}; + +bool CPU_I2C_Initialize(uint8_t i2cBus, bool fastSpeed); +bool CPU_I2C_Uninitialize(uint8_t i2cBus); + +void CPU_I2C_GetPins(uint8_t i2cBusIndex, GPIO_PIN &clockPin, GPIO_PIN &dataPin); + +I2cTransferStatus CPU_I2C_WriteRead( + uint8_t i2cBus, + int slaveAddress, + unsigned char *writeData, + int writeSize, + unsigned char *readData, + int readSize); + + +#endif // _DRIVERS_I2C_DECL_H_ diff --git a/src/Windows.Devices.I2c/nanoHAL_I2C.cpp b/src/Windows.Devices.I2c/nanoHAL_I2C.cpp new file mode 100644 index 0000000000..48c80f6faa --- /dev/null +++ b/src/Windows.Devices.I2c/nanoHAL_I2C.cpp @@ -0,0 +1,199 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +#include +#include + +#include +#include +#include + +#define NUM_I2C_BUSES 2 +#define MAX_I2C_DEVICES 5 + + +// Create a handle built from device type, I2C bus number and device index +#define CreateI2cHandle(i2cBusIndex, deviceIndex) ((CPU_DEVICE_TYPE_I2C << 16) + (i2cBusIndex << 8) + deviceIndex) +#define GetBusFromHandle(handle) ((handle >> 8) & 0x00ff) +#define GetDeviceIndexFromHandle(handle) (handle & 0x00f) + + +struct nanoI2cConfig +{ + bool busInited; + uint8_t devicesInUse; + uint8_t slaveAddress[MAX_I2C_DEVICES]; +}; + +nanoI2cConfig i2cConfig[NUM_I2C_BUSES]; + +// Initialise the i2cConfig structure +// Called on CLR startup +bool nanoI2C_Initialize() +{ + for (int i2cBusIndex = 0; i2cBusIndex < NUM_I2C_BUSES; i2cBusIndex++) + { + i2cConfig[i2cBusIndex].busInited = false; + i2cConfig[i2cBusIndex].devicesInUse = 0; + memset(&i2cConfig[i2cBusIndex].slaveAddress, 0, sizeof(i2cConfig[i2cBusIndex].slaveAddress)); + } + return true; +} + +// Uninitializes (resets) all SPI devices. +// Called on CLR closedown +void nanoI2C_Uninitialize() +{ + for (int i2cBusIndex = 0; i2cBusIndex < NUM_I2C_BUSES; i2cBusIndex++) + { + if (i2cConfig[i2cBusIndex].busInited) + { + // Remove any devices + // Bus will be closed when last device closed in SPI_CloseDevice + for (int deviceIndex = 0; deviceIndex < MAX_I2C_DEVICES; deviceIndex++) + { + uint8_t address = i2cConfig[i2cBusIndex].slaveAddress[deviceIndex]; + if (address != 0) + nanoI2C_CloseDevice(CreateI2cHandle(i2cBusIndex, deviceIndex)); + } + } + } +} + +// Reserve SPI bus pins +HRESULT nanoI2C_ReserveBusPins(int i2cBus, bool reserve) +{ + GPIO_PIN pins[2]; + + CPU_I2C_GetPins(i2cBus, pins[0], pins[1]); + + if (reserve) + { + // if reserve , Check can reserve + for (int i = 0; i < 2; i++) + { + if (pins[0] != GPIO_PIN_NONE) + { + if (CPU_GPIO_PinIsBusy(pins[i])) + return CLR_E_INVALID_PARAMETER; + } + } + } + + // Reserve / UnReserve pins + for (int i = 0; i < 2; i++) + { + if (pins[i] != GPIO_PIN_NONE) + { + if (CPU_GPIO_ReservePin(pins[i], reserve) == false) + return CLR_E_INVALID_PARAMETER; + } + } + + return S_OK; +} + +// Find a free slot in the device table +// Return index or -1 if no free slots +static int FindFreeDeviceSlotI2C(int i2CBus, int slaveAddress) +{ + for (int deviceIndex = 0; deviceIndex < MAX_I2C_DEVICES; deviceIndex++) + { + uint8_t adr = i2cConfig[i2CBus].slaveAddress[deviceIndex]; + // Free slot ? + if (adr == 0) + { + return deviceIndex; + } + // Already in opened + if (adr == slaveAddress) + { + return -1; + } + } + return -1; +} + +// Open I2C device on bus +// +HRESULT nanoI2C_OpenDevice(I2C_DEVICE_CONFIGURATION &config, uint32_t &deviceHandle) +{ + (void)config; + (void)deviceHandle; + + // i2cBus 0 to (number of buses - 1) + uint8_t i2cBusIndex = (uint8_t)config.i2cBus - 1; + if (i2cBusIndex >= NUM_I2C_BUSES) + return CLR_E_INVALID_PARAMETER; + + // Initialise Bus if not already initialised + if (!i2cConfig[i2cBusIndex].busInited) + { + if (!CPU_I2C_Initialize(i2cBusIndex, config.fastMode)) + return CLR_E_INVALID_PARAMETER; + + // Reserve pins used by SPI bus + HRESULT hr = nanoI2C_ReserveBusPins(i2cBusIndex, true); + if (hr != S_OK) + return hr; + + i2cConfig[i2cBusIndex].busInited = true; + } + + int deviceIndex = FindFreeDeviceSlotI2C(i2cBusIndex, config.address); + if (deviceIndex < 0) + { + if (deviceIndex == -1) + // No device slots left + return CLR_E_INDEX_OUT_OF_RANGE; + else + // Return NOT_SUPPORTED when Device already in use. Not really any other relevant exception that's + // currently raised in managed code + return CLR_E_NOT_SUPPORTED; + } + + // Return device hanlde + deviceHandle = CreateI2cHandle(i2cBusIndex, deviceIndex); + + i2cConfig[i2cBusIndex].slaveAddress[deviceIndex] = config.address; + + return S_OK; +} + +I2cTransferStatus nanoI2C_WriteRead( + uint32_t deviceHandle, + uint8_t *writeBuffer, + uint16_t writeSize, + uint8_t *readBuffer, + uint16_t readSize) +{ + int i2cBusIndex = GetBusFromHandle(deviceHandle); + int deviceIndex = GetDeviceIndexFromHandle(deviceHandle); + + return CPU_I2C_WriteRead( + i2cBusIndex, + i2cConfig[i2cBusIndex].slaveAddress[deviceIndex], + writeBuffer, + writeSize, + readBuffer, + readSize); +} + +HRESULT nanoI2C_CloseDevice(uint32_t deviceHandle) +{ + int i2cBusIndex = GetBusFromHandle(deviceHandle); + int deviceIndex = GetDeviceIndexFromHandle(deviceHandle); + + i2cConfig[i2cBusIndex].slaveAddress[deviceIndex] = 0; + i2cConfig[i2cBusIndex].devicesInUse--; + if (i2cConfig[i2cBusIndex].devicesInUse <=0) + { + CPU_I2C_Uninitialize(i2cBusIndex); + i2cConfig[i2cBusIndex].busInited = false; + } + + return S_OK; +} + diff --git a/src/Windows.Devices.I2c/win_dev_i2c_native.h b/src/Windows.Devices.I2c/win_dev_i2c_native.h index e4e04e2953..0555583b6d 100644 --- a/src/Windows.Devices.I2c/win_dev_i2c_native.h +++ b/src/Windows.Devices.I2c/win_dev_i2c_native.h @@ -22,14 +22,14 @@ enum I2cSharingMode /////////////////////////////////////////////////////////////////////////////////////// // !!! KEEP IN SYNC WITH Windows.Devices.I2c.I2cTransferStatus (in managed code) !!! // /////////////////////////////////////////////////////////////////////////////////////// - enum I2cTransferStatus -{ - I2cTransferStatus_FullTransfer = 0, - I2cTransferStatus_ClockStretchTimeout, - I2cTransferStatus_PartialTransfer, - I2cTransferStatus_SlaveAddressNotAcknowledged, - I2cTransferStatus_UnknownError -}; +// enum I2cTransferStatus +// { +// I2cTransferStatus_FullTransfer = 0, +// I2cTransferStatus_ClockStretchTimeout, +// I2cTransferStatus_PartialTransfer, +// I2cTransferStatus_SlaveAddressNotAcknowledged, +// I2cTransferStatus_UnknownError +// }; /////////////////////////////////////////////////////////////////////////////////////// // !!! KEEP IN SYNC WITH Windows.Devices.I2c.I2cBusSpeed (in managed code) !!! // diff --git a/src/nanoFramework.Graphics/Graphics/Displays/I2C_To_Display.cpp b/src/nanoFramework.Graphics/Graphics/Displays/I2C_To_Display.cpp new file mode 100644 index 0000000000..e8a0e3de58 --- /dev/null +++ b/src/nanoFramework.Graphics/Graphics/Displays/I2C_To_Display.cpp @@ -0,0 +1,114 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +#ifndef _I2C_TO_DISPLAY_ +#define _I2C_TO_DISPLAY_ 1 + +#include "DisplayInterface.h" + +#include +#include + + +#define NUMBER_OF_LINES 8 +#define I2C_MAX_TRANSFER_SIZE (16 * 2 * NUMBER_OF_LINES) + +struct DisplayInterface g_DisplayInterface; + + +CLR_UINT32 i2cDeviceHandle = 0; + +CLR_UINT8 I2cBuffer[I2C_MAX_TRANSFER_SIZE]; + +// Display Interface +void DisplayInterface::Initialize(DisplayInterfaceConfig& config) +{ + (void)config; + + I2C_DEVICE_CONFIGURATION i2cConfig; + + i2cConfig.i2cBus = config.I2c.i2cBus; + i2cConfig.address = config.I2c.address; + i2cConfig.fastMode = true; + + HRESULT hr = nanoI2C_OpenDevice(i2cConfig, i2cDeviceHandle); + ASSERT(hr == S_OK); + if ( hr == S_OK ) + { + // TODO Reserve other display Pins + + // Reset the display + } +} + +// Not used for I2C +void DisplayInterface::SetCommandMode(int mode) +{ + (void)mode; +} + +void DisplayInterface::GetTransferBuffer(CLR_UINT8*& TransferBuffer, CLR_UINT32& TransferBufferSize) +{ + TransferBuffer = I2cBuffer; + TransferBufferSize = sizeof(I2cBuffer); +} +void DisplayInterface::ClearFrameBuffer() +{ + // Not Used +} + +void DisplayInterface::WriteToFrameBuffer(CLR_UINT8 command, CLR_UINT8 data[], CLR_UINT32 dataCount, CLR_UINT32 frameOffset) +{ + (void)frameOffset; + + CLR_UINT8 * outputData = (CLR_UINT8 *)platform_malloc(dataCount + 1); + if ( outputData ) + { + outputData[0] = command; + memcpy(outputData + 1, data, dataCount); + SendBytes(outputData, dataCount + 1); + platform_free((void*)outputData); + } +} + +void DisplayInterface::SendCommand(CLR_UINT8 arg_count, ...) +{ + va_list ap; + va_start(ap, arg_count); + + // Following bytes are data related to the command + int parameterCount = arg_count; + CLR_UINT8 parameters[parameterCount + 1]; + + parameters[0] = 0; // Control register + + for (int i = 1; i <= parameterCount; i++) + { + parameters[i] = va_arg(ap, int); + } + + SendBytes(parameters, parameterCount + 1); +} + +void DisplayInterface::DisplayBacklight(bool on) // true = on +{ + (void)on; +} + +void DisplayInterface::SendBytes(CLR_UINT8 *data, CLR_UINT32 length) +{ + if (length == 0) return; //no need to send anything + + // Currently ignores errors + nanoI2C_WriteRead( + i2cDeviceHandle, + data, + (CLR_UINT16)length, + (CLR_UINT8 *)0, + 0); +} + +#endif // _I2C_TO_DISPLAY_ + diff --git a/targets/FreeRTOS_ESP32/ESP32_WROOM_32/Include/targetHAL_I2c.h b/targets/FreeRTOS_ESP32/ESP32_WROOM_32/Include/targetHAL_I2c.h new file mode 100644 index 0000000000..f9c398c983 --- /dev/null +++ b/targets/FreeRTOS_ESP32/ESP32_WROOM_32/Include/targetHAL_I2c.h @@ -0,0 +1,10 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +#ifndef _TARGET_HAL_I2C_H_ +#define _TARGET_HAL_I2C_H_ 1 + + +#endif //_TARGET_HAL_I2C_H_ diff --git a/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/Windows.Devices.I2c/cpu_i2c.cpp b/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/Windows.Devices.I2c/cpu_i2c.cpp new file mode 100644 index 0000000000..90f6ddcb9e --- /dev/null +++ b/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/Windows.Devices.I2c/cpu_i2c.cpp @@ -0,0 +1,140 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// +// +// CPU_I2C +// +// Read / Write physical I2C bus +// + +#include +#include +#include +#include + +#include +#include "Esp32_DeviceMapping.h" + +// Tag for ESP32 logging +static const char *TAG = "I2cDevice"; + + +void CPU_I2C_GetPins(uint8_t i2cBusIndex, GPIO_PIN &clockPin, GPIO_PIN &dataPin) +{ + dataPin = (GPIO_PIN)Esp32_GetMappedDevicePins(DEV_TYPE_I2C, i2cBusIndex, 0); + clockPin = (GPIO_PIN)Esp32_GetMappedDevicePins(DEV_TYPE_I2C, i2cBusIndex, 1); +} + + +bool CPU_I2C_Initialize(uint8_t i2cBus, bool fastSpeed) +{ + GPIO_PIN clockPin, dataPin; + esp_err_t res; + + // Get pins used by i2c bus + CPU_I2C_GetPins(i2cBus, clockPin, dataPin); + + // Check pins have been configured + if (clockPin == GPIO_PIN_NONE || dataPin == GPIO_PIN_NONE ) + { + ESP_LOGE(TAG, "I2C pins for I2C%d not configured", i2cBus); + return false; + } + + i2c_config_t conf; + + conf.mode = I2C_MODE_MASTER; + conf.sda_io_num = (gpio_num_t)dataPin; + conf.sda_pullup_en = GPIO_PULLUP_ENABLE; + conf.scl_io_num = (gpio_num_t)clockPin; + conf.scl_pullup_en = GPIO_PULLUP_ENABLE; + conf.master.clk_speed = (fastSpeed) ? 400000 : 100000; + + res = i2c_param_config((i2c_port_t)i2cBus, &conf); + if (res != ESP_OK) + { + return false; + } + + res = i2c_driver_install((i2c_port_t)i2cBus, I2C_MODE_MASTER, 0, 0, 0); + if (res != ESP_OK) + { + return false; + } + + return true; +} + +// Uninitialise the I2C bus +bool CPU_I2C_Uninitialize(uint8_t i2cBus) +{ + esp_err_t ret = i2c_driver_delete((i2c_port_t)i2cBus); + if (ret != ESP_OK) + { + return false; + } + + return true; +} + +I2cTransferStatus CPU_I2C_WriteRead( + uint8_t i2cBus, + int slaveAddress, + unsigned char *writeData, + int writeSize, + unsigned char *readData, + int readSize) +{ + esp_err_t i2cStatus; + I2cTransferStatus transferResult = I2cTransferStatus_FullTransfer; + + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + + if (writeSize != 0) // Write + { + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (slaveAddress << 1) | I2C_MASTER_WRITE, 1); + i2cStatus = i2c_master_write(cmd, &writeData[0], writeSize, true); + if (i2cStatus != ESP_OK) + { + ESP_LOGE(TAG, "i2c_master_write error:%d", i2cStatus); + } + } + + if (readSize != 0) // Read + { + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (slaveAddress << 1) | I2C_MASTER_READ, 1); + if (readSize > 1) + { + // Additional read bytes with ACK + i2c_master_read(cmd, &readData[0], readSize - 1, I2C_MASTER_ACK); + } + // Last read byte with NACK + i2c_master_read_byte(cmd, &readData[readSize - 1], I2C_MASTER_NACK); + } + + i2c_master_stop(cmd); + + i2cStatus = i2c_master_cmd_begin((i2c_port_t)i2cBus, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + + switch (i2cStatus) + { + default: + transferResult = I2cTransferStatus_UnknownError; + break; + case ESP_FAIL : + transferResult = I2cTransferStatus_SlaveAddressNotAcknowledged; + break; + case ESP_ERR_TIMEOUT: + transferResult = I2cTransferStatus_ClockStretchTimeout; + break; + case ESP_OK: + transferResult = I2cTransferStatus_FullTransfer; + break; + } + + return transferResult; +} \ No newline at end of file diff --git a/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/targetHAL.cpp b/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/targetHAL.cpp index b57bb3a5b4..687ef36e44 100644 --- a/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/targetHAL.cpp +++ b/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/targetHAL.cpp @@ -100,12 +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.spiBus = 1; // Spi Bus + // Define SPI display configuration for Wrover or any other using same pins + 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 g_DisplayInterface.Initialize(displayConfig); g_DisplayDriver.Initialize(); From ee4850ddb14f67b946ed33e0eb6543f32e9a778a Mon Sep 17 00:00:00 2001 From: Adrian Soundy Date: Mon, 23 Nov 2020 22:55:02 +1300 Subject: [PATCH 4/5] Revert "Auto stash before merge of "ui-fixes" and "upstream/nfbot/clang-format-fix/875c8a43-7dbb-4855-adf7-46061a013be0"" This reverts commit 361d59e3fa0d2c8a6fecd2da402836ffd632edeb. --- CMake/Modules/FindWindows.Devices.I2c.cmake | 4 +- src/HAL/Include/nanoHAL.h | 4 - src/HAL/Include/nanoHAL_I2C.h | 54 ----- src/PAL/Include/CPU_I2C_decl.h | 33 --- src/Windows.Devices.I2c/nanoHAL_I2C.cpp | 199 ------------------ src/Windows.Devices.I2c/win_dev_i2c_native.h | 16 +- .../Graphics/Displays/I2C_To_Display.cpp | 114 ---------- .../ESP32_WROOM_32/Include/targetHAL_I2c.h | 10 - .../nanoCLR/Windows.Devices.I2c/cpu_i2c.cpp | 140 ------------ .../ESP32_WROOM_32/nanoCLR/targetHAL.cpp | 12 +- 10 files changed, 15 insertions(+), 571 deletions(-) delete mode 100644 src/HAL/Include/nanoHAL_I2C.h delete mode 100644 src/PAL/Include/CPU_I2C_decl.h delete mode 100644 src/Windows.Devices.I2c/nanoHAL_I2C.cpp delete mode 100644 src/nanoFramework.Graphics/Graphics/Displays/I2C_To_Display.cpp delete mode 100644 targets/FreeRTOS_ESP32/ESP32_WROOM_32/Include/targetHAL_I2c.h delete mode 100644 targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/Windows.Devices.I2c/cpu_i2c.cpp diff --git a/CMake/Modules/FindWindows.Devices.I2c.cmake b/CMake/Modules/FindWindows.Devices.I2c.cmake index be8fa6b2af..ac64849602 100644 --- a/CMake/Modules/FindWindows.Devices.I2c.cmake +++ b/CMake/Modules/FindWindows.Devices.I2c.cmake @@ -19,8 +19,6 @@ list(APPEND Windows.Devices.I2c_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/src/Windows.Dev # source files set(Windows.Devices.I2c_SRCS - cpu_i2c.cpp - nanoHAL_i2c.cpp win_dev_i2c_native.cpp win_dev_i2c_native_Windows_Devices_I2C_I2cDevice.cpp win_dev_i2c_native_Windows_Devices_I2c_I2cController.cpp @@ -38,7 +36,7 @@ foreach(SRC_FILE ${Windows.Devices.I2c_SRCS}) CMAKE_FIND_ROOT_PATH_BOTH ) - message("${SRC_FILE} >> ${Windows.Devices.I2c_SRC_FILE}") # debug helper + # message("${SRC_FILE} >> ${Windows.Devices.I2c_SRC_FILE}") # debug helper list(APPEND Windows.Devices.I2c_SOURCES ${Windows.Devices.I2c_SRC_FILE}) endforeach() diff --git a/src/HAL/Include/nanoHAL.h b/src/HAL/Include/nanoHAL.h index e3052d3f6e..8961c9419f 100644 --- a/src/HAL/Include/nanoHAL.h +++ b/src/HAL/Include/nanoHAL.h @@ -842,10 +842,6 @@ extern bool g_fDoNotUninitializeDebuggerPort; // SPI driver #include -// -// I2C driver -#include - // //// External bus interface driver //#include diff --git a/src/HAL/Include/nanoHAL_I2C.h b/src/HAL/Include/nanoHAL_I2C.h deleted file mode 100644 index affd575cd0..0000000000 --- a/src/HAL/Include/nanoHAL_I2C.h +++ /dev/null @@ -1,54 +0,0 @@ -// -// 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. -// - -#ifndef _NANO_I2C_DECL_H_ -#define _NANO_I2C_DECL_H_ 1 - -#include "TargetHAL_I2c.h" - -#include "CPU_I2C_decl.h" - -#define CPU_DEVICE_TYPE_I2C 2 - -// enum I2CResult -// { -// I2CResult_OK, -// I2CResult_FAIL, -// I2CResult_TIMEOUT -// }; - -struct I2C_DEVICE_CONFIGURATION -{ - int8_t i2cBus; // I2C Bus number - uint8_t address; // Device address on bus - bool fastMode; // Speed false=normal(100Khz), true=fast(400khz) -}; - - -// Initialise the i2cConfig structure -// Called on CLR startup -bool nanoI2C_Initialize(); -void nanoI2C_Uninitialize(); - - // Open I2C bus / device using device configuration -// Register GPIO pins as in use. -// Return result, if S_OK then deviceHandle returned -HRESULT nanoI2C_OpenDevice(I2C_DEVICE_CONFIGURATION &config, uint32_t &deviceHandle); - -// Execute a I2C write/read operation -// return status of operation as I2CResult -I2cTransferStatus nanoI2C_WriteRead( - uint32_t deviceHandle, - uint8_t *writeBuffer, - uint16_t writeSize, - uint8_t *readBuffer, - uint16_t readSize); - -// Close I2C device -// When last device closed bus is closed -HRESULT nanoI2C_CloseDevice(uint32_t deviceHandle); - -#endif // _NANO_I2C_DECL_H_ diff --git a/src/PAL/Include/CPU_I2C_decl.h b/src/PAL/Include/CPU_I2C_decl.h deleted file mode 100644 index 7927fea013..0000000000 --- a/src/PAL/Include/CPU_I2C_decl.h +++ /dev/null @@ -1,33 +0,0 @@ -// -// 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. -// - -#ifndef _DRIVERS_I2C_DECL_H_ -#define _DRIVERS_I2C_DECL_H_ 1 - -enum I2cTransferStatus -{ - I2cTransferStatus_FullTransfer = 0, - I2cTransferStatus_ClockStretchTimeout, - I2cTransferStatus_PartialTransfer, - I2cTransferStatus_SlaveAddressNotAcknowledged, - I2cTransferStatus_UnknownError -}; - -bool CPU_I2C_Initialize(uint8_t i2cBus, bool fastSpeed); -bool CPU_I2C_Uninitialize(uint8_t i2cBus); - -void CPU_I2C_GetPins(uint8_t i2cBusIndex, GPIO_PIN &clockPin, GPIO_PIN &dataPin); - -I2cTransferStatus CPU_I2C_WriteRead( - uint8_t i2cBus, - int slaveAddress, - unsigned char *writeData, - int writeSize, - unsigned char *readData, - int readSize); - - -#endif // _DRIVERS_I2C_DECL_H_ diff --git a/src/Windows.Devices.I2c/nanoHAL_I2C.cpp b/src/Windows.Devices.I2c/nanoHAL_I2C.cpp deleted file mode 100644 index 48c80f6faa..0000000000 --- a/src/Windows.Devices.I2c/nanoHAL_I2C.cpp +++ /dev/null @@ -1,199 +0,0 @@ -// -// Copyright (c) .NET Foundation and Contributors -// See LICENSE file in the project root for full license information. -// - -#include -#include - -#include -#include -#include - -#define NUM_I2C_BUSES 2 -#define MAX_I2C_DEVICES 5 - - -// Create a handle built from device type, I2C bus number and device index -#define CreateI2cHandle(i2cBusIndex, deviceIndex) ((CPU_DEVICE_TYPE_I2C << 16) + (i2cBusIndex << 8) + deviceIndex) -#define GetBusFromHandle(handle) ((handle >> 8) & 0x00ff) -#define GetDeviceIndexFromHandle(handle) (handle & 0x00f) - - -struct nanoI2cConfig -{ - bool busInited; - uint8_t devicesInUse; - uint8_t slaveAddress[MAX_I2C_DEVICES]; -}; - -nanoI2cConfig i2cConfig[NUM_I2C_BUSES]; - -// Initialise the i2cConfig structure -// Called on CLR startup -bool nanoI2C_Initialize() -{ - for (int i2cBusIndex = 0; i2cBusIndex < NUM_I2C_BUSES; i2cBusIndex++) - { - i2cConfig[i2cBusIndex].busInited = false; - i2cConfig[i2cBusIndex].devicesInUse = 0; - memset(&i2cConfig[i2cBusIndex].slaveAddress, 0, sizeof(i2cConfig[i2cBusIndex].slaveAddress)); - } - return true; -} - -// Uninitializes (resets) all SPI devices. -// Called on CLR closedown -void nanoI2C_Uninitialize() -{ - for (int i2cBusIndex = 0; i2cBusIndex < NUM_I2C_BUSES; i2cBusIndex++) - { - if (i2cConfig[i2cBusIndex].busInited) - { - // Remove any devices - // Bus will be closed when last device closed in SPI_CloseDevice - for (int deviceIndex = 0; deviceIndex < MAX_I2C_DEVICES; deviceIndex++) - { - uint8_t address = i2cConfig[i2cBusIndex].slaveAddress[deviceIndex]; - if (address != 0) - nanoI2C_CloseDevice(CreateI2cHandle(i2cBusIndex, deviceIndex)); - } - } - } -} - -// Reserve SPI bus pins -HRESULT nanoI2C_ReserveBusPins(int i2cBus, bool reserve) -{ - GPIO_PIN pins[2]; - - CPU_I2C_GetPins(i2cBus, pins[0], pins[1]); - - if (reserve) - { - // if reserve , Check can reserve - for (int i = 0; i < 2; i++) - { - if (pins[0] != GPIO_PIN_NONE) - { - if (CPU_GPIO_PinIsBusy(pins[i])) - return CLR_E_INVALID_PARAMETER; - } - } - } - - // Reserve / UnReserve pins - for (int i = 0; i < 2; i++) - { - if (pins[i] != GPIO_PIN_NONE) - { - if (CPU_GPIO_ReservePin(pins[i], reserve) == false) - return CLR_E_INVALID_PARAMETER; - } - } - - return S_OK; -} - -// Find a free slot in the device table -// Return index or -1 if no free slots -static int FindFreeDeviceSlotI2C(int i2CBus, int slaveAddress) -{ - for (int deviceIndex = 0; deviceIndex < MAX_I2C_DEVICES; deviceIndex++) - { - uint8_t adr = i2cConfig[i2CBus].slaveAddress[deviceIndex]; - // Free slot ? - if (adr == 0) - { - return deviceIndex; - } - // Already in opened - if (adr == slaveAddress) - { - return -1; - } - } - return -1; -} - -// Open I2C device on bus -// -HRESULT nanoI2C_OpenDevice(I2C_DEVICE_CONFIGURATION &config, uint32_t &deviceHandle) -{ - (void)config; - (void)deviceHandle; - - // i2cBus 0 to (number of buses - 1) - uint8_t i2cBusIndex = (uint8_t)config.i2cBus - 1; - if (i2cBusIndex >= NUM_I2C_BUSES) - return CLR_E_INVALID_PARAMETER; - - // Initialise Bus if not already initialised - if (!i2cConfig[i2cBusIndex].busInited) - { - if (!CPU_I2C_Initialize(i2cBusIndex, config.fastMode)) - return CLR_E_INVALID_PARAMETER; - - // Reserve pins used by SPI bus - HRESULT hr = nanoI2C_ReserveBusPins(i2cBusIndex, true); - if (hr != S_OK) - return hr; - - i2cConfig[i2cBusIndex].busInited = true; - } - - int deviceIndex = FindFreeDeviceSlotI2C(i2cBusIndex, config.address); - if (deviceIndex < 0) - { - if (deviceIndex == -1) - // No device slots left - return CLR_E_INDEX_OUT_OF_RANGE; - else - // Return NOT_SUPPORTED when Device already in use. Not really any other relevant exception that's - // currently raised in managed code - return CLR_E_NOT_SUPPORTED; - } - - // Return device hanlde - deviceHandle = CreateI2cHandle(i2cBusIndex, deviceIndex); - - i2cConfig[i2cBusIndex].slaveAddress[deviceIndex] = config.address; - - return S_OK; -} - -I2cTransferStatus nanoI2C_WriteRead( - uint32_t deviceHandle, - uint8_t *writeBuffer, - uint16_t writeSize, - uint8_t *readBuffer, - uint16_t readSize) -{ - int i2cBusIndex = GetBusFromHandle(deviceHandle); - int deviceIndex = GetDeviceIndexFromHandle(deviceHandle); - - return CPU_I2C_WriteRead( - i2cBusIndex, - i2cConfig[i2cBusIndex].slaveAddress[deviceIndex], - writeBuffer, - writeSize, - readBuffer, - readSize); -} - -HRESULT nanoI2C_CloseDevice(uint32_t deviceHandle) -{ - int i2cBusIndex = GetBusFromHandle(deviceHandle); - int deviceIndex = GetDeviceIndexFromHandle(deviceHandle); - - i2cConfig[i2cBusIndex].slaveAddress[deviceIndex] = 0; - i2cConfig[i2cBusIndex].devicesInUse--; - if (i2cConfig[i2cBusIndex].devicesInUse <=0) - { - CPU_I2C_Uninitialize(i2cBusIndex); - i2cConfig[i2cBusIndex].busInited = false; - } - - return S_OK; -} - diff --git a/src/Windows.Devices.I2c/win_dev_i2c_native.h b/src/Windows.Devices.I2c/win_dev_i2c_native.h index 0555583b6d..e4e04e2953 100644 --- a/src/Windows.Devices.I2c/win_dev_i2c_native.h +++ b/src/Windows.Devices.I2c/win_dev_i2c_native.h @@ -22,14 +22,14 @@ enum I2cSharingMode /////////////////////////////////////////////////////////////////////////////////////// // !!! KEEP IN SYNC WITH Windows.Devices.I2c.I2cTransferStatus (in managed code) !!! // /////////////////////////////////////////////////////////////////////////////////////// -// enum I2cTransferStatus -// { -// I2cTransferStatus_FullTransfer = 0, -// I2cTransferStatus_ClockStretchTimeout, -// I2cTransferStatus_PartialTransfer, -// I2cTransferStatus_SlaveAddressNotAcknowledged, -// I2cTransferStatus_UnknownError -// }; + enum I2cTransferStatus +{ + I2cTransferStatus_FullTransfer = 0, + I2cTransferStatus_ClockStretchTimeout, + I2cTransferStatus_PartialTransfer, + I2cTransferStatus_SlaveAddressNotAcknowledged, + I2cTransferStatus_UnknownError +}; /////////////////////////////////////////////////////////////////////////////////////// // !!! KEEP IN SYNC WITH Windows.Devices.I2c.I2cBusSpeed (in managed code) !!! // diff --git a/src/nanoFramework.Graphics/Graphics/Displays/I2C_To_Display.cpp b/src/nanoFramework.Graphics/Graphics/Displays/I2C_To_Display.cpp deleted file mode 100644 index e8a0e3de58..0000000000 --- a/src/nanoFramework.Graphics/Graphics/Displays/I2C_To_Display.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// -// Copyright (c) .NET Foundation and Contributors -// See LICENSE file in the project root for full license information. -// - -#ifndef _I2C_TO_DISPLAY_ -#define _I2C_TO_DISPLAY_ 1 - -#include "DisplayInterface.h" - -#include -#include - - -#define NUMBER_OF_LINES 8 -#define I2C_MAX_TRANSFER_SIZE (16 * 2 * NUMBER_OF_LINES) - -struct DisplayInterface g_DisplayInterface; - - -CLR_UINT32 i2cDeviceHandle = 0; - -CLR_UINT8 I2cBuffer[I2C_MAX_TRANSFER_SIZE]; - -// Display Interface -void DisplayInterface::Initialize(DisplayInterfaceConfig& config) -{ - (void)config; - - I2C_DEVICE_CONFIGURATION i2cConfig; - - i2cConfig.i2cBus = config.I2c.i2cBus; - i2cConfig.address = config.I2c.address; - i2cConfig.fastMode = true; - - HRESULT hr = nanoI2C_OpenDevice(i2cConfig, i2cDeviceHandle); - ASSERT(hr == S_OK); - if ( hr == S_OK ) - { - // TODO Reserve other display Pins - - // Reset the display - } -} - -// Not used for I2C -void DisplayInterface::SetCommandMode(int mode) -{ - (void)mode; -} - -void DisplayInterface::GetTransferBuffer(CLR_UINT8*& TransferBuffer, CLR_UINT32& TransferBufferSize) -{ - TransferBuffer = I2cBuffer; - TransferBufferSize = sizeof(I2cBuffer); -} -void DisplayInterface::ClearFrameBuffer() -{ - // Not Used -} - -void DisplayInterface::WriteToFrameBuffer(CLR_UINT8 command, CLR_UINT8 data[], CLR_UINT32 dataCount, CLR_UINT32 frameOffset) -{ - (void)frameOffset; - - CLR_UINT8 * outputData = (CLR_UINT8 *)platform_malloc(dataCount + 1); - if ( outputData ) - { - outputData[0] = command; - memcpy(outputData + 1, data, dataCount); - SendBytes(outputData, dataCount + 1); - platform_free((void*)outputData); - } -} - -void DisplayInterface::SendCommand(CLR_UINT8 arg_count, ...) -{ - va_list ap; - va_start(ap, arg_count); - - // Following bytes are data related to the command - int parameterCount = arg_count; - CLR_UINT8 parameters[parameterCount + 1]; - - parameters[0] = 0; // Control register - - for (int i = 1; i <= parameterCount; i++) - { - parameters[i] = va_arg(ap, int); - } - - SendBytes(parameters, parameterCount + 1); -} - -void DisplayInterface::DisplayBacklight(bool on) // true = on -{ - (void)on; -} - -void DisplayInterface::SendBytes(CLR_UINT8 *data, CLR_UINT32 length) -{ - if (length == 0) return; //no need to send anything - - // Currently ignores errors - nanoI2C_WriteRead( - i2cDeviceHandle, - data, - (CLR_UINT16)length, - (CLR_UINT8 *)0, - 0); -} - -#endif // _I2C_TO_DISPLAY_ - diff --git a/targets/FreeRTOS_ESP32/ESP32_WROOM_32/Include/targetHAL_I2c.h b/targets/FreeRTOS_ESP32/ESP32_WROOM_32/Include/targetHAL_I2c.h deleted file mode 100644 index f9c398c983..0000000000 --- a/targets/FreeRTOS_ESP32/ESP32_WROOM_32/Include/targetHAL_I2c.h +++ /dev/null @@ -1,10 +0,0 @@ -// -// Copyright (c) .NET Foundation and Contributors -// See LICENSE file in the project root for full license information. -// - -#ifndef _TARGET_HAL_I2C_H_ -#define _TARGET_HAL_I2C_H_ 1 - - -#endif //_TARGET_HAL_I2C_H_ diff --git a/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/Windows.Devices.I2c/cpu_i2c.cpp b/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/Windows.Devices.I2c/cpu_i2c.cpp deleted file mode 100644 index 90f6ddcb9e..0000000000 --- a/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/Windows.Devices.I2c/cpu_i2c.cpp +++ /dev/null @@ -1,140 +0,0 @@ -// -// Copyright (c) .NET Foundation and Contributors -// See LICENSE file in the project root for full license information. -// -// -// CPU_I2C -// -// Read / Write physical I2C bus -// - -#include -#include -#include -#include - -#include -#include "Esp32_DeviceMapping.h" - -// Tag for ESP32 logging -static const char *TAG = "I2cDevice"; - - -void CPU_I2C_GetPins(uint8_t i2cBusIndex, GPIO_PIN &clockPin, GPIO_PIN &dataPin) -{ - dataPin = (GPIO_PIN)Esp32_GetMappedDevicePins(DEV_TYPE_I2C, i2cBusIndex, 0); - clockPin = (GPIO_PIN)Esp32_GetMappedDevicePins(DEV_TYPE_I2C, i2cBusIndex, 1); -} - - -bool CPU_I2C_Initialize(uint8_t i2cBus, bool fastSpeed) -{ - GPIO_PIN clockPin, dataPin; - esp_err_t res; - - // Get pins used by i2c bus - CPU_I2C_GetPins(i2cBus, clockPin, dataPin); - - // Check pins have been configured - if (clockPin == GPIO_PIN_NONE || dataPin == GPIO_PIN_NONE ) - { - ESP_LOGE(TAG, "I2C pins for I2C%d not configured", i2cBus); - return false; - } - - i2c_config_t conf; - - conf.mode = I2C_MODE_MASTER; - conf.sda_io_num = (gpio_num_t)dataPin; - conf.sda_pullup_en = GPIO_PULLUP_ENABLE; - conf.scl_io_num = (gpio_num_t)clockPin; - conf.scl_pullup_en = GPIO_PULLUP_ENABLE; - conf.master.clk_speed = (fastSpeed) ? 400000 : 100000; - - res = i2c_param_config((i2c_port_t)i2cBus, &conf); - if (res != ESP_OK) - { - return false; - } - - res = i2c_driver_install((i2c_port_t)i2cBus, I2C_MODE_MASTER, 0, 0, 0); - if (res != ESP_OK) - { - return false; - } - - return true; -} - -// Uninitialise the I2C bus -bool CPU_I2C_Uninitialize(uint8_t i2cBus) -{ - esp_err_t ret = i2c_driver_delete((i2c_port_t)i2cBus); - if (ret != ESP_OK) - { - return false; - } - - return true; -} - -I2cTransferStatus CPU_I2C_WriteRead( - uint8_t i2cBus, - int slaveAddress, - unsigned char *writeData, - int writeSize, - unsigned char *readData, - int readSize) -{ - esp_err_t i2cStatus; - I2cTransferStatus transferResult = I2cTransferStatus_FullTransfer; - - i2c_cmd_handle_t cmd = i2c_cmd_link_create(); - - if (writeSize != 0) // Write - { - i2c_master_start(cmd); - i2c_master_write_byte(cmd, (slaveAddress << 1) | I2C_MASTER_WRITE, 1); - i2cStatus = i2c_master_write(cmd, &writeData[0], writeSize, true); - if (i2cStatus != ESP_OK) - { - ESP_LOGE(TAG, "i2c_master_write error:%d", i2cStatus); - } - } - - if (readSize != 0) // Read - { - i2c_master_start(cmd); - i2c_master_write_byte(cmd, (slaveAddress << 1) | I2C_MASTER_READ, 1); - if (readSize > 1) - { - // Additional read bytes with ACK - i2c_master_read(cmd, &readData[0], readSize - 1, I2C_MASTER_ACK); - } - // Last read byte with NACK - i2c_master_read_byte(cmd, &readData[readSize - 1], I2C_MASTER_NACK); - } - - i2c_master_stop(cmd); - - i2cStatus = i2c_master_cmd_begin((i2c_port_t)i2cBus, cmd, 1000 / portTICK_RATE_MS); - i2c_cmd_link_delete(cmd); - - switch (i2cStatus) - { - default: - transferResult = I2cTransferStatus_UnknownError; - break; - case ESP_FAIL : - transferResult = I2cTransferStatus_SlaveAddressNotAcknowledged; - break; - case ESP_ERR_TIMEOUT: - transferResult = I2cTransferStatus_ClockStretchTimeout; - break; - case ESP_OK: - transferResult = I2cTransferStatus_FullTransfer; - break; - } - - return transferResult; -} \ No newline at end of file diff --git a/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/targetHAL.cpp b/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/targetHAL.cpp index 687ef36e44..b57bb3a5b4 100644 --- a/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/targetHAL.cpp +++ b/targets/FreeRTOS_ESP32/ESP32_WROOM_32/nanoCLR/targetHAL.cpp @@ -100,12 +100,12 @@ void nanoHAL_Initialize() // Initialise Graphics after devices initialised DisplayInterfaceConfig displayConfig; - displayConfig.Spi.spiBus = 1; // Spi Bus - // Define SPI display configuration for Wrover or any other using same pins - 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 + // 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 g_DisplayInterface.Initialize(displayConfig); g_DisplayDriver.Initialize(); From cacaa389504ceb1c55ea5a7c1ea9b482a7109453 Mon Sep 17 00:00:00 2001 From: Adrian Soundy Date: Tue, 24 Nov 2020 08:35:37 +1300 Subject: [PATCH 5/5] Remove comment --- .../Graphics/Displays/Spi_To_Display.cpp | 43 ------------------- 1 file changed, 43 deletions(-) diff --git a/src/nanoFramework.Graphics/Graphics/Displays/Spi_To_Display.cpp b/src/nanoFramework.Graphics/Graphics/Displays/Spi_To_Display.cpp index 833fe11842..cdaa1e22f1 100644 --- a/src/nanoFramework.Graphics/Graphics/Displays/Spi_To_Display.cpp +++ b/src/nanoFramework.Graphics/Graphics/Displays/Spi_To_Display.cpp @@ -133,49 +133,6 @@ void DisplayInterface::SendCommand(CLR_UINT8 arg_count, ...) } } -// void DisplayInterface::SendCommand2(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); - -// CPU_GPIO_SetPinState(LcdDC, GpioPinValue_High); // Data mode - -// int parameterCount = arg_count - 1; -// if (parameterCount) -// { -// if ( CommandMode == 1) -// { -// CPU_GPIO_SetPinState(LcdDC, GpioPinValue_Low); // Command mode - -// for (int i = 0; i < parameterCount; i++) -// { -// command = va_arg(ap, int); -// SendBytes(&command, 1); -// } - -// // All command & parameters use D/C signal -// CPU_GPIO_SetPinState(LcdDC, GpioPinValue_High); // Data mode -// } -// else -// { -// // Following bytes are data related to the command -// CLR_UINT8 parameters[parameterCount]; -// for (int i = 0; i < parameterCount; i++) -// { -// parameters[i] = va_arg(ap, int); -// } - -// SendBytes(parameters, parameterCount); -// } -// } -// return; -// } - void DisplayInterface::DisplayBacklight(bool on) // true = on { if (on)