SSD1322.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  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, bool HFlip, bool VFlip, bool Rotate ) {
  85. struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
  86. Private->ReMap = HFlip ? (Private->ReMap & ~(1 << 1)) : (Private->ReMap | (1 << 1));
  87. Private->ReMap = 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. }
  92. static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAF ); }
  93. static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAE ); }
  94. static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
  95. Device->WriteCommand( Device, 0xC1 );
  96. Device->WriteData( Device, &Contrast, 1 );
  97. }
  98. static bool Init( struct GDS_Device* Device ) {
  99. struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
  100. // these displays seems to be layout centered (1 column = 4 pixels of 4 bits each, little endian)
  101. Private->Offset = (480 - Device->Width) / 4 / 2;
  102. // find a page size that is not too small is an integer of height
  103. Private->PageSize = min(8, PAGE_BLOCK / (Device->Width / 2));
  104. while (Private->PageSize && Device->Height != (Device->Height / Private->PageSize) * Private->PageSize) Private->PageSize--;
  105. #ifdef SHADOW_BUFFER
  106. Private->Shadowbuffer = malloc( Device->FramebufferSize );
  107. memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize);
  108. #endif
  109. Private->iRAM = heap_caps_malloc( Private->PageSize * Device->Width / 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
  110. ESP_LOGI(TAG, "SSD1322 with offset %u, page %u, iRAM %p", Private->Offset, Private->PageSize, Private->iRAM);
  111. // need to be off and disable display RAM
  112. Device->DisplayOff( Device );
  113. Device->WriteCommand( Device, 0xA5 );
  114. // Display Offset
  115. Device->WriteCommand( Device, 0xA2 );
  116. WriteDataByte( Device, 0 );
  117. // Display Start Line
  118. Device->WriteCommand( Device, 0xA1 );
  119. WriteDataByte( Device, 0x00 );
  120. // set flip modes
  121. Private->ReMap = 0;
  122. Device->SetLayout( Device, false, false, false);
  123. // set Display Enhancement
  124. Device->WriteCommand( Device, 0xB4 );
  125. WriteDataByte( Device, 0xA0 );
  126. WriteDataByte( Device, 0xB5 );
  127. // set Clocks
  128. Device->WriteCommand( Device, 0xB3 );
  129. WriteDataByte( Device, 0xB2 ); // 0x91 seems to be common but is too slow for 5.5'
  130. // set MUX
  131. Device->WriteCommand( Device, 0xCA );
  132. WriteDataByte( Device, Device->Height - 1 );
  133. // phase 1 & 2 period
  134. Device->WriteCommand( Device, 0xB1 );
  135. WriteDataByte( Device, 0xE3 ); // 0xE2 was recommended
  136. // set pre-charge V
  137. Device->WriteCommand( Device, 0xBB );
  138. WriteDataByte( Device, 0x0F); // 0x1F causes column interferences
  139. // set COM deselect voltage
  140. Device->WriteCommand( Device, 0xBE );
  141. WriteDataByte( Device, 0x07 );
  142. // no Display Inversion
  143. Device->WriteCommand( Device, 0xA6 );
  144. // gone with the wind
  145. Device->DisplayOn( Device );
  146. Device->Update( Device );
  147. return true;
  148. }
  149. static const struct GDS_Device SSD1322 = {
  150. .DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
  151. .SetLayout = SetLayout,
  152. .Update = Update, .Init = Init,
  153. .Mode = GDS_GRAYSCALE, .Depth = 4,
  154. };
  155. struct GDS_Device* SSD1322_Detect(char *Driver, struct GDS_Device* Device) {
  156. if (!strcasestr(Driver, "SSD1322")) return NULL;
  157. if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
  158. *Device = SSD1322;
  159. return Device;
  160. }