SSD1322.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. /**
  2. * Copyright (c) 2017-2018 Tara Keeling
  3. * 2020 Philippe G.
  4. *
  5. * This software is released under the MIT License.
  6. * https://opensource.org/licenses/MIT
  7. */
  8. #include <stdio.h>
  9. #include <string.h>
  10. #include <stdint.h>
  11. #include <stdbool.h>
  12. #include <esp_heap_caps.h>
  13. #include <esp_log.h>
  14. #include "gds.h"
  15. #include "gds_private.h"
  16. #define SHADOW_BUFFER
  17. #define PAGE_BLOCK 1024
  18. #define min(a,b) (((a) < (b)) ? (a) : (b))
  19. static char TAG[] = "SSD1322";
  20. struct PrivateSpace {
  21. uint8_t *iRAM, *Shadowbuffer;
  22. uint8_t ReMap, PageSize;
  23. uint8_t Offset;
  24. };
  25. // Functions are not declared to minimize # of lines
  26. static void WriteDataByte( struct GDS_Device* Device, uint8_t Data ) {
  27. Device->WriteData( Device, &Data, 1);
  28. }
  29. static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
  30. Device->WriteCommand( Device, 0x15 );
  31. Device->WriteData( Device, &Start, 1 );
  32. Device->WriteData( Device, &End, 1 );
  33. }
  34. static void SetRowAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
  35. Device->WriteCommand( Device, 0x75 );
  36. Device->WriteData( Device, &Start, 1 );
  37. Device->WriteData( Device, &End, 1 );
  38. }
  39. static void Update( struct GDS_Device* Device ) {
  40. struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
  41. // RAM is by columns of 4 pixels ...
  42. SetColumnAddress( Device, Private->Offset, Private->Offset + Device->Width / 4 - 1);
  43. #ifdef SHADOW_BUFFER
  44. uint16_t *optr = (uint16_t*) Private->Shadowbuffer, *iptr = (uint16_t*) Device->Framebuffer;
  45. bool dirty = false;
  46. for (int r = 0, page = 0; r < Device->Height; r++) {
  47. // look for change and update shadow (cheap optimization = width always / by 2)
  48. for (int c = Device->Width / 2 / 2; --c >= 0;) {
  49. if (*optr != *iptr) {
  50. dirty = true;
  51. *optr = *iptr;
  52. }
  53. iptr++; optr++;
  54. }
  55. // one line done, check for page boundary
  56. if (++page == Private->PageSize) {
  57. if (dirty) {
  58. uint16_t *optr = (uint16_t*) Private->iRAM, *iptr = (uint16_t*) (Private->Shadowbuffer + (r - page + 1) * Device->Width / 2);
  59. SetRowAddress( Device, r - page + 1, r );
  60. for (int i = page * Device->Width / 2 / 2; --i >= 0; iptr++) *optr++ = (*iptr >> 8) | (*iptr << 8);
  61. //memcpy(Private->iRAM, Private->Shadowbuffer + (r - page + 1) * Device->Width / 2, page * Device->Width / 2 );
  62. Device->WriteCommand( Device, 0x5c );
  63. Device->WriteData( Device, Private->iRAM, Device->Width * page / 2 );
  64. dirty = false;
  65. }
  66. page = 0;
  67. }
  68. }
  69. #else
  70. for (int r = 0; r < Device->Height; r += Private->PageSize) {
  71. SetRowAddress( Device, r, r + Private->PageSize - 1 );
  72. Device->WriteCommand( Device, 0x5c );
  73. if (Private->iRAM) {
  74. uint16_t *optr = (uint16_t*) Private->iRAM, *iptr = (uint16_t*) (Device->Framebuffer + r * Device->Width / 2);
  75. for (int i = Private->PageSize * Device->Width / 2 / 2; --i >= 0; iptr++) *optr++ = (*iptr >> 8) | (*iptr << 8);
  76. //memcpy(Private->iRAM, Device->Framebuffer + r * Device->Width / 2, Private->PageSize * Device->Width / 2 );
  77. Device->WriteData( Device, Private->iRAM, Private->PageSize * Device->Width / 2 );
  78. } else {
  79. Device->WriteData( Device, Device->Framebuffer + r * Device->Width / 2, Private->PageSize * Device->Width / 2 );
  80. }
  81. }
  82. #endif
  83. }
  84. static void SetLayout( struct GDS_Device* Device, struct GDS_Layout *Layout ) {
  85. struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
  86. Private->ReMap = Layout->HFlip ? (Private->ReMap & ~(1 << 1)) : (Private->ReMap | (1 << 1));
  87. Private->ReMap = Layout->VFlip ? (Private->ReMap | (1 << 4)) : (Private->ReMap & ~(1 << 4));
  88. Device->WriteCommand( Device, 0xA0 );
  89. Device->WriteData( Device, &Private->ReMap, 1 );
  90. WriteDataByte( Device, 0x11 );
  91. Device->WriteCommand( Device, Layout->Invert ? 0xA7 : 0xA6 );
  92. }
  93. static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAF ); }
  94. static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAE ); }
  95. static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
  96. Device->WriteCommand( Device, 0xC1 );
  97. Device->WriteData( Device, &Contrast, 1 );
  98. }
  99. static bool Init( struct GDS_Device* Device ) {
  100. struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
  101. // these displays seems to be layout centered (1 column = 4 pixels of 4 bits each, little endian)
  102. Private->Offset = (480 - Device->Width) / 4 / 2;
  103. // find a page size that is not too small is an integer of height
  104. Private->PageSize = min(8, PAGE_BLOCK / (Device->Width / 2));
  105. while (Private->PageSize && Device->Height != (Device->Height / Private->PageSize) * Private->PageSize) Private->PageSize--;
  106. #ifdef SHADOW_BUFFER
  107. Private->Shadowbuffer = malloc( Device->FramebufferSize );
  108. memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize);
  109. #endif
  110. Private->iRAM = heap_caps_malloc( Private->PageSize * Device->Width / 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
  111. ESP_LOGI(TAG, "SSD1322 with offset %u, page %u, iRAM %p", Private->Offset, Private->PageSize, Private->iRAM);
  112. // need to be off and disable display RAM
  113. Device->DisplayOff( Device );
  114. Device->WriteCommand( Device, 0xA5 );
  115. // Display Offset
  116. Device->WriteCommand( Device, 0xA2 );
  117. WriteDataByte( Device, 0 );
  118. // Display Start Line
  119. Device->WriteCommand( Device, 0xA1 );
  120. WriteDataByte( Device, 0x00 );
  121. // set flip modes
  122. Private->ReMap = 0;
  123. struct GDS_Layout Layout = { };
  124. Device->SetLayout( Device, &Layout );
  125. // set Display Enhancement
  126. Device->WriteCommand( Device, 0xB4 );
  127. WriteDataByte( Device, 0xA0 );
  128. WriteDataByte( Device, 0xB5 );
  129. // set Clocks
  130. Device->WriteCommand( Device, 0xB3 );
  131. WriteDataByte( Device, 0xB2 ); // 0x91 seems to be common but is too slow for 5.5'
  132. // set MUX
  133. Device->WriteCommand( Device, 0xCA );
  134. WriteDataByte( Device, Device->Height - 1 );
  135. // phase 1 & 2 period
  136. Device->WriteCommand( Device, 0xB1 );
  137. WriteDataByte( Device, 0xE3 ); // 0xE2 was recommended
  138. // set pre-charge V
  139. Device->WriteCommand( Device, 0xBB );
  140. WriteDataByte( Device, 0x0F); // 0x1F causes column interferences
  141. // set COM deselect voltage
  142. Device->WriteCommand( Device, 0xBE );
  143. WriteDataByte( Device, 0x07 );
  144. // no Display Inversion
  145. Device->WriteCommand( Device, 0xA6 );
  146. // gone with the wind
  147. Device->DisplayOn( Device );
  148. Device->Update( Device );
  149. return true;
  150. }
  151. static const struct GDS_Device SSD1322 = {
  152. .DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
  153. .SetLayout = SetLayout,
  154. .Update = Update, .Init = Init,
  155. .Mode = GDS_GRAYSCALE, .Depth = 4,
  156. };
  157. struct GDS_Device* SSD1322_Detect(char *Driver, struct GDS_Device* Device) {
  158. if (!strcasestr(Driver, "SSD1322")) return NULL;
  159. if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
  160. *Device = SSD1322;
  161. return Device;
  162. }