ILI9341.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  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 "freertos/FreeRTOS.h"
  13. #include "freertos/task.h"
  14. #include "driver/gpio.h"
  15. #include <esp_heap_caps.h>
  16. #include <esp_log.h>
  17. #include "gds.h"
  18. #include "gds_private.h"
  19. //#define SHADOW_BUFFER
  20. #define PAGE_BLOCK 1024
  21. #define min(a,b) (((a) < (b)) ? (a) : (b))
  22. static char TAG[] = "ILI9341";
  23. #define L1_CMD_NOP 0X00
  24. #define L1_CMD_SOFTWARE_RESET 0X01
  25. #define L1_CMD_READ_DISPLAY_IDENTIFICATION_INFORMATION 0X04
  26. #define L1_CMD_READ_DISPLAY_STATUS 0X09
  27. #define L1_CMD_READ_DISPLAY_POWER_MODE 0X0A
  28. #define L1_CMD_READ_DISPLAY_MADCTL 0X0B
  29. #define L1_CMD_READ_DISPLAY_PIXEL_FORMAT 0X0C
  30. #define L1_CMD_READ_DISPLAY_IMAGE_FORMAT 0X0D
  31. #define L1_CMD_READ_DISPLAY_SIGNAL_MODE 0X0E
  32. #define L1_CMD_READ_DISPLAY_SELF_DIAGNOSTIC_RESULT 0X0F
  33. #define L1_CMD_ENTER_SLEEP_MODE 0X10
  34. #define L1_CMD_SLEEP_OUT 0X11
  35. #define L1_CMD_PARTIAL_MODE_ON 0X12
  36. #define L1_CMD_NORMAL_DISPLAY_MODE_ON 0X13
  37. #define L1_CMD_DISPLAY_INVERSION_OFF 0X20
  38. #define L1_CMD_DISPLAY_INVERSION_ON 0X21
  39. #define L1_CMD_GAMMA_SET 0X26
  40. #define L1_CMD_DISPLAY_OFF 0X28
  41. #define L1_CMD_DISPLAY_ON 0X29
  42. #define L1_CMD_COLUMN_ADDRESS_SET 0X2A
  43. #define L1_CMD_PAGE_ADDRESS_SET 0X2B
  44. #define L1_CMD_MEMORY_WRITE 0X2C
  45. #define L1_CMD_COLOR_SET 0X2D
  46. #define L1_CMD_MEMORY_READ 0X2E
  47. #define L1_CMD_PARTIAL_AREA 0X30
  48. #define L1_CMD_VERTICAL_SCROLLING_DEFINITION 0X33
  49. #define L1_CMD_TEARING_EFFECT_LINE_OFF 0X34
  50. #define L1_CMD_TEARING_EFFECT_LINE_ON 0X35
  51. #define L1_CMD_MEMORY_ACCESS_CONTROL 0X36
  52. #define L1_CMD_VERTICAL_SCROLLING_START_ADDRESS 0X37
  53. #define L1_CMD_IDLE_MODE_OFF 0X38
  54. #define L1_CMD_IDLE_MODE_ON 0X39
  55. #define L1_CMD_COLMOD_PIXEL_FORMAT_SET 0X3A
  56. #define L1_CMD_WRITE_MEMORY_CONTINUE 0X3C
  57. #define L1_CMD_READ_MEMORY_CONTINUE 0X3E
  58. #define L1_CMD_SET_TEAR_SCANLINE 0X44
  59. #define L1_CMD_GET_SCANLINE 0X45
  60. #define L1_CMD_WRITE_DISPLAY_BRIGHTNESS 0X51
  61. #define L1_CMD_READ_DISPLAY_BRIGHTNESS 0X52
  62. #define L1_CMD_WRITE_CTRL_DISPLAY 0X53
  63. #define L1_CMD_READ_CTRL_DISPLAY 0X54
  64. #define L1_CMD_WRITE_CONTENT_ADAPTIVE_BRIGHTNESS_CONTROL 0X55
  65. #define L1_CMD_READ_CONTENT_ADAPTIVE_BRIGHTNESS_CONTROL 0X56
  66. #define L1_CMD_WRITE_CABC_MINIMUM_BRIGHTNESS 0X5E
  67. #define L1_CMD_READ_CABC_MINIMUM_BRIGHTNESS 0X5F
  68. #define L1_CMD_READ_ID1 0XDA
  69. #define L1_CMD_READ_ID2 0XDB
  70. #define L1_CMD_READ_ID3 0XDC
  71. #define L2_CMD_RGB_INTERFACE_SIGNAL_CONTROL 0XB0
  72. #define L2_CMD_FRAME_RATE_CONTROL_IN_NORMAL_MODE_FULL_COLORS 0XB1
  73. #define L2_CMD_FRAME_RATE_CONTROL_IN_IDLE_MODE_8_COLORS 0XB2
  74. #define L2_CMD_FRAME_RATE_CONTROL_IN_PARTIAL_MODE_FULL_COLORS 0XB3
  75. #define L2_CMD_DISPLAY_INVERSION_CONTROL 0XB4
  76. #define L2_CMD_BLANKING_PORCH_CONTROL 0XB5
  77. #define L2_CMD_DISPLAY_FUNCTION_CONTROL 0XB6
  78. #define L2_CMD_ENTRY_MODE_SET 0XB7
  79. #define L2_CMD_BACKLIGHT_CONTROL_1 0XB8
  80. #define L2_CMD_BACKLIGHT_CONTROL_2 0XB9
  81. #define L2_CMD_BACKLIGHT_CONTROL_3 0XBA
  82. #define L2_CMD_BACKLIGHT_CONTROL_4 0XBB
  83. #define L2_CMD_BACKLIGHT_CONTROL_5 0XBC
  84. #define L2_CMD_BACKLIGHT_CONTROL_7 0XBE
  85. #define L2_CMD_BACKLIGHT_CONTROL_8 0XBF
  86. #define L2_CMD_POWER_CONTROL_1 0XC0
  87. #define L2_CMD_POWER_CONTROL_2 0XC1
  88. #define L2_CMD_VCOM_CONTROL_1 0XC5
  89. #define L2_CMD_VCOM_CONTROL_2 0XC7
  90. #define L2_CMD_NV_MEMORY_WRITE 0XD0
  91. #define L2_CMD_NV_MEMORY_PROTECTION_KEY 0XD1
  92. #define L2_CMD_NV_MEMORY_STATUS_READ 0XD2
  93. #define L2_CMD_READ_ID4 0XD3
  94. #define L2_CMD_POSITIVE_GAMMA_CORRECTION 0XE0
  95. #define L2_CMD_NEGATIVE_GAMMA_CORRECTION 0XE1
  96. #define L2_CMD_DIGITAL_GAMMA_CONTROL_1 0XE2
  97. #define L2_CMD_DIGITAL_GAMMA_CONTROL_2 0XE3
  98. #define L2_CMD_INTERFACE_CONTROL 0XF6
  99. /*
  100. The LCD needs a bunch of command/argument values to be initialized. They are stored in this struct.
  101. */
  102. typedef struct {
  103. uint8_t cmd;
  104. uint8_t data[16];
  105. uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds.
  106. } lcd_init_cmd_t;
  107. static const lcd_init_cmd_t ili_init_cmds[]={
  108. /* Power contorl B, power control = 0, DC_ENA = 1 */
  109. {0xCF, {0x00, 0x83, 0X30}, 3},
  110. /* Power on sequence control,
  111. * cp1 keeps 1 frame, 1st frame enable
  112. * vcl = 0, ddvdh=3, vgh=1, vgl=2
  113. * DDVDH_ENH=1
  114. */
  115. {0xED, {0x64, 0x03, 0X12, 0X81}, 4},
  116. /* Driver timing control A,
  117. * non-overlap=default +1
  118. * EQ=default - 1, CR=default
  119. * pre-charge=default - 1
  120. */
  121. {0xE8, {0x85, 0x01, 0x79}, 3},
  122. /* Power control A, Vcore=1.6V, DDVDH=5.6V */
  123. {0xCB, {0x39, 0x2C, 0x00, 0x34, 0x02}, 5},
  124. /* Pump ratio control, DDVDH=2xVCl */
  125. {0xF7, {0x20}, 1},
  126. /* Driver timing control, all=0 unit */
  127. {0xEA, {0x00, 0x00}, 2},
  128. /* Power control 1, GVDD=4.75V */
  129. {0xC0, {0x26}, 1},
  130. /* Power control 2, DDVDH=VCl*2, VGH=VCl*7, VGL=-VCl*3 */
  131. {0xC1, {0x11}, 1},
  132. /* VCOM control 1, VCOMH=4.025V, VCOML=-0.950V */
  133. {0xC5, {0x35, 0x3E}, 2},
  134. /* VCOM control 2, VCOMH=VMH-2, VCOML=VML-2 */
  135. {0xC7, {0xBE}, 1},
  136. /* Memory access contorl, MX=MY=0, MV=1, ML=0, BGR=1, MH=0 */
  137. {0x36, {0x28}, 1},
  138. /* Pixel format, 16bits/pixel for RGB/MCU interface */
  139. {0x3A, {0x55}, 1},
  140. /* Frame rate control, f=fosc, 70Hz fps */
  141. {0xB1, {0x00, 0x1B}, 2},
  142. /* Enable 3G, disabled */
  143. {0xF2, {0x08}, 1},
  144. /* Gamma set, curve 1 */
  145. {0x26, {0x01}, 1},
  146. /* Positive gamma correction */
  147. {0xE0, {0x1F, 0x1A, 0x18, 0x0A, 0x0F, 0x06, 0x45, 0X87, 0x32, 0x0A, 0x07, 0x02, 0x07, 0x05, 0x00}, 15},
  148. /* Negative gamma correction */
  149. {0XE1, {0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3A, 0x78, 0x4D, 0x05, 0x18, 0x0D, 0x38, 0x3A, 0x1F}, 15},
  150. /* Column address set, SC=0, EC=0xEF */
  151. {0x2A, {0x00, 0x00, 0x00, 0xEF}, 4},
  152. /* Page address set, SP=0, EP=0x013F */
  153. {0x2B, {0x00, 0x00, 0x01, 0x3f}, 4},
  154. /* Memory write */
  155. {0x2C, {0}, 0},
  156. /* Entry mode set, Low vol detect disabled, normal display */
  157. {0xB7, {0x07}, 1},
  158. /* Display function control */
  159. {0xB6, {0x0A, 0x82, 0x27, 0x00}, 4},
  160. /* Sleep out */
  161. {0x11, {0}, 0x80},
  162. /* Display on */
  163. {0x29, {0}, 0x80},
  164. {0, {0}, 0xff},
  165. };
  166. //To speed up transfers, every SPI transfer sends a bunch of lines. This define specifies how many. More means more memory use,
  167. //but less overhead for setting up / finishing transfers. Make sure 240 is dividable by this.
  168. #define PARALLEL_LINES 16
  169. struct PrivateSpace {
  170. uint8_t *iRAM, *Shadowbuffer;
  171. uint8_t ReMap, PageSize;
  172. uint8_t Offset;
  173. };
  174. // Functions are not declared to minimize # of lines
  175. static void WriteDataByte( struct GDS_Device* Device, uint8_t Data ) {
  176. Device->WriteData( Device, &Data, 1);
  177. }
  178. static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
  179. Device->WriteCommand( Device, L1_CMD_COLUMN_ADDRESS_SET );
  180. Device->WriteData( Device, &Start, 1 );
  181. Device->WriteData( Device, &End, 1 );
  182. }
  183. static void SetRowAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
  184. Device->WriteCommand( Device, L1_CMD_PAGE_ADDRESS_SET );
  185. Device->WriteData( Device, &Start, 1 );
  186. Device->WriteData( Device, &End, 1 );
  187. }
  188. static void Update( struct GDS_Device* Device ) {
  189. struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
  190. //SetColumnAddress( Device, Private->Offset, Private->Offset + Device->Width / 4 - 1);
  191. SetColumnAddress( Device, Private->Offset, Private->Offset + Device->Width - 1);
  192. #ifdef SHADOW_BUFFER
  193. uint16_t *optr = (uint16_t*) Private->Shadowbuffer, *iptr = (uint16_t*) Device->Framebuffer;
  194. bool dirty = false;
  195. for (int r = 0, page = 0; r < Device->Height; r++) {
  196. // look for change and update shadow (cheap optimization = width always / by 2)
  197. for (int c = Device->Width / 2 / 2; --c >= 0;) {
  198. if (*optr != *iptr) {
  199. dirty = true;
  200. *optr = *iptr;
  201. }
  202. iptr++; optr++;
  203. }
  204. // one line done, check for page boundary
  205. if (++page == Private->PageSize) {
  206. if (dirty) {
  207. uint16_t *optr = (uint16_t*) Private->iRAM, *iptr = (uint16_t*) (Private->Shadowbuffer + (r - page + 1) * Device->Width / 2);
  208. SetRowAddress( Device, r - page + 1, r );
  209. for (int i = page * Device->Width / 2 / 2; --i >= 0; iptr++) *optr++ = (*iptr >> 8) | (*iptr << 8);
  210. //memcpy(Private->iRAM, Private->Shadowbuffer + (r - page + 1) * Device->Width / 2, page * Device->Width / 2 );
  211. Device->WriteCommand( Device, 0x5c );
  212. Device->WriteData( Device, Private->iRAM, Device->Width * page / 2 );
  213. dirty = false;
  214. }
  215. page = 0;
  216. }
  217. }
  218. #else
  219. for (int r = 0; r < Device->Height; r += Private->PageSize) {
  220. SetRowAddress( Device, r, r + Private->PageSize - 1 );
  221. Device->WriteCommand( Device, L1_CMD_MEMORY_WRITE );
  222. if (Private->iRAM) {
  223. uint16_t *optr = (uint16_t*) Private->iRAM, *iptr = (uint16_t*) (Device->Framebuffer + r * Device->Width / 2);
  224. for (int i = Private->PageSize * Device->Width / 2 / 2; --i >= 0; iptr++) *optr++ = (*iptr >> 8) | (*iptr << 8);
  225. //memcpy(Private->iRAM, Device->Framebuffer + r * Device->Width / 2, Private->PageSize * Device->Width / 2 );
  226. Device->WriteData( Device, Private->iRAM, Private->PageSize * Device->Width / 2 );
  227. } else {
  228. Device->WriteData( Device, Device->Framebuffer + r * Device->Width / 2, Private->PageSize * Device->Width / 2 );
  229. }
  230. }
  231. #endif
  232. }
  233. //Bit Name Description
  234. //--- --------------------------- ------------------------------------------------------
  235. //MY Row Address Order MCU to memory write/read direction.
  236. //MX Column Address Order MCU to memory write/read direction.
  237. //MV Row / Column Exchange MCU to memory write/read direction.
  238. //ML Vertical Refresh Order LCD vertical refresh direction control.
  239. //BGR RGB-BGR Order Color selector switch control
  240. // (0=RGB color filter panel, 1=BGR color filter panel)
  241. //MH Horizontal Refresh ORDER LCD horizontal refreshing direction control.
  242. // Bits 17-0
  243. // XX XX XX XX XX XX XX XX XX XX MY MX MV ML BGR MH 0 0
  244. typedef enum {
  245. MAC_BIT_MH=2,
  246. MAC_BIT_BGR,
  247. MAC_BIT_ML,
  248. MAC_BIT_MV,
  249. MAC_BIT_MX,
  250. MAC_BIT_MY,
  251. } mac_bits;
  252. uint16_t set_mac_bit(mac_bits bit, uint16_t val){
  253. return (1 << bit) | val;
  254. }
  255. uint16_t unset_mac_bit(mac_bits bit, uint16_t val){
  256. return ~(1 << bit) & val;
  257. }
  258. static void SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate ) {
  259. struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
  260. Private->ReMap = HFlip ? (Private->ReMap & ~(1 << MAC_BIT_MX)) : (Private->ReMap | (1 << MAC_BIT_MX));
  261. Private->ReMap = VFlip ? (Private->ReMap | (1 << MAC_BIT_MY)) : (Private->ReMap & ~(1 << MAC_BIT_MY));
  262. Device->WriteCommand( Device, L1_CMD_MEMORY_ACCESS_CONTROL );
  263. Device->WriteData( Device, &Private->ReMap, 1 );
  264. WriteDataByte(Device,0x00);
  265. }
  266. static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, L1_CMD_DISPLAY_ON ); }
  267. static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, L1_CMD_DISPLAY_OFF ); }
  268. static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
  269. Device->WriteCommand( Device, L1_CMD_WRITE_DISPLAY_BRIGHTNESS );
  270. uint8_t loc_contrast = (uint8_t)((float)Contrast/5.0f* 255.0f);
  271. Device->WriteData( Device, &loc_contrast , 1 );
  272. WriteDataByte(Device,0x00);
  273. }
  274. static bool Init( struct GDS_Device* Device ) {
  275. struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
  276. // Private->Offset = (480 - Device->Width) / 4 / 2;
  277. // find a page size that is not too small is an integer of height
  278. Private->PageSize = min(8, PAGE_BLOCK / (Device->Width / 2));
  279. Private->PageSize = Device->Height / (Device->Height / Private->PageSize) ;
  280. #ifdef SHADOW_BUFFER
  281. // Private->Shadowbuffer = malloc( Device->FramebufferSize );
  282. // memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize);
  283. #endif
  284. Private->iRAM =NULL;
  285. //Private->iRAM =heap_caps_malloc(320*PARALLEL_LINES*sizeof(uint16_t), MALLOC_CAP_DMA);
  286. //ESP_LOGI(TAG, "ILI9341 with offset %u, page %u, iRAM %p", Private->Offset, Private->PageSize, Private->iRAM);
  287. ESP_LOGI(TAG, "ILI9341 ");
  288. // need to be off and disable display RAM
  289. Device->DisplayOff( Device );
  290. int cmd=0;
  291. //Send all the commands
  292. while (ili_init_cmds[cmd].databytes!=0xff) {
  293. Device->WriteCommand( Device, ili_init_cmds[cmd].cmd );
  294. Device->WriteData(Device,ili_init_cmds[cmd].data,ili_init_cmds[cmd].databytes&0x1F);
  295. if (ili_init_cmds[cmd].databytes&0x80) {
  296. vTaskDelay(100 / portTICK_RATE_MS);
  297. }
  298. cmd++;
  299. }
  300. // gone with the wind
  301. Device->DisplayOn( Device );
  302. Device->Update( Device );
  303. return true;
  304. }
  305. static const struct GDS_Device ILI9341 = {
  306. .DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
  307. .SetLayout = SetLayout,
  308. .Update = Update, .Init = Init,
  309. };
  310. struct GDS_Device* ILI9341_Detect(char *Driver, struct GDS_Device* Device) {
  311. if (!strcasestr(Driver, "ILI9341")) return NULL;
  312. if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
  313. *Device = ILI9341;
  314. Device->Depth = 4;
  315. return Device;
  316. }