SSD1322.c 6.3 KB

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