spi_master_example_main.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. /* SPI Master example
  2. This example code is in the Public Domain (or CC0 licensed, at your option.)
  3. Unless required by applicable law or agreed to in writing, this
  4. software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  5. CONDITIONS OF ANY KIND, either express or implied.
  6. */
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include "freertos/FreeRTOS.h"
  11. #include "freertos/task.h"
  12. #include "esp_system.h"
  13. #include "driver/spi_master.h"
  14. #include "driver/gpio.h"
  15. #include "pretty_effect.h"
  16. /*
  17. This code displays some fancy graphics on the 320x240 LCD on an ESP-WROVER_KIT board.
  18. This example demonstrates the use of both spi_device_transmit as well as
  19. spi_device_queue_trans/spi_device_get_trans_result and pre-transmit callbacks.
  20. Some info about the ILI9341/ST7789V: It has an C/D line, which is connected to a GPIO here. It expects this
  21. line to be low for a command and high for data. We use a pre-transmit callback here to control that
  22. line: every transaction has as the user-definable argument the needed state of the D/C line and just
  23. before the transaction is sent, the callback will set this line to the correct state.
  24. */
  25. #ifdef CONFIG_IDF_TARGET_ESP32
  26. #define LCD_HOST HSPI_HOST
  27. #define DMA_CHAN 2
  28. #define PIN_NUM_MISO 25
  29. #define PIN_NUM_MOSI 23
  30. #define PIN_NUM_CLK 19
  31. #define PIN_NUM_CS 22
  32. #define PIN_NUM_DC 21
  33. #define PIN_NUM_RST 18
  34. #define PIN_NUM_BCKL 5
  35. #elif defined CONFIG_IDF_TARGET_ESP32S2
  36. #define LCD_HOST SPI2_HOST
  37. #define DMA_CHAN LCD_HOST
  38. #define PIN_NUM_MISO 37
  39. #define PIN_NUM_MOSI 35
  40. #define PIN_NUM_CLK 36
  41. #define PIN_NUM_CS 34
  42. #define PIN_NUM_DC 4
  43. #define PIN_NUM_RST 5
  44. #define PIN_NUM_BCKL 6
  45. #elif defined CONFIG_IDF_TARGET_ESP32C3
  46. #define LCD_HOST SPI2_HOST
  47. #define DMA_CHAN LCD_HOST
  48. #define PIN_NUM_MISO 2
  49. #define PIN_NUM_MOSI 7
  50. #define PIN_NUM_CLK 6
  51. #define PIN_NUM_CS 10
  52. #define PIN_NUM_DC 9
  53. #define PIN_NUM_RST 18
  54. #define PIN_NUM_BCKL 19
  55. #endif
  56. //To speed up transfers, every SPI transfer sends a bunch of lines. This define specifies how many. More means more memory use,
  57. //but less overhead for setting up / finishing transfers. Make sure 240 is dividable by this.
  58. #define PARALLEL_LINES 16
  59. /*
  60. The LCD needs a bunch of command/argument values to be initialized. They are stored in this struct.
  61. */
  62. typedef struct {
  63. uint8_t cmd;
  64. uint8_t data[16];
  65. uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds.
  66. } lcd_init_cmd_t;
  67. typedef enum {
  68. LCD_TYPE_ILI = 1,
  69. LCD_TYPE_ST,
  70. LCD_TYPE_MAX,
  71. } type_lcd_t;
  72. //Place data into DRAM. Constant data gets placed into DROM by default, which is not accessible by DMA.
  73. DRAM_ATTR static const lcd_init_cmd_t st_init_cmds[]={
  74. /* Memory Data Access Control, MX=MV=1, MY=ML=MH=0, RGB=0 */
  75. {0x36, {(1<<5)|(1<<6)}, 1},
  76. /* Interface Pixel Format, 16bits/pixel for RGB/MCU interface */
  77. {0x3A, {0x55}, 1},
  78. /* Porch Setting */
  79. {0xB2, {0x0c, 0x0c, 0x00, 0x33, 0x33}, 5},
  80. /* Gate Control, Vgh=13.65V, Vgl=-10.43V */
  81. {0xB7, {0x45}, 1},
  82. /* VCOM Setting, VCOM=1.175V */
  83. {0xBB, {0x2B}, 1},
  84. /* LCM Control, XOR: BGR, MX, MH */
  85. {0xC0, {0x2C}, 1},
  86. /* VDV and VRH Command Enable, enable=1 */
  87. {0xC2, {0x01, 0xff}, 2},
  88. /* VRH Set, Vap=4.4+... */
  89. {0xC3, {0x11}, 1},
  90. /* VDV Set, VDV=0 */
  91. {0xC4, {0x20}, 1},
  92. /* Frame Rate Control, 60Hz, inversion=0 */
  93. {0xC6, {0x0f}, 1},
  94. /* Power Control 1, AVDD=6.8V, AVCL=-4.8V, VDDS=2.3V */
  95. {0xD0, {0xA4, 0xA1}, 1},
  96. /* Positive Voltage Gamma Control */
  97. {0xE0, {0xD0, 0x00, 0x05, 0x0E, 0x15, 0x0D, 0x37, 0x43, 0x47, 0x09, 0x15, 0x12, 0x16, 0x19}, 14},
  98. /* Negative Voltage Gamma Control */
  99. {0xE1, {0xD0, 0x00, 0x05, 0x0D, 0x0C, 0x06, 0x2D, 0x44, 0x40, 0x0E, 0x1C, 0x18, 0x16, 0x19}, 14},
  100. /* Sleep Out */
  101. {0x11, {0}, 0x80},
  102. /* Display On */
  103. {0x29, {0}, 0x80},
  104. {0, {0}, 0xff}
  105. };
  106. DRAM_ATTR static const lcd_init_cmd_t ili_init_cmds[]={
  107. /* Power contorl B, power control = 0, DC_ENA = 1 */
  108. {0xCF, {0x00, 0x83, 0X30}, 3},
  109. /* Power on sequence control,
  110. * cp1 keeps 1 frame, 1st frame enable
  111. * vcl = 0, ddvdh=3, vgh=1, vgl=2
  112. * DDVDH_ENH=1
  113. */
  114. {0xED, {0x64, 0x03, 0X12, 0X81}, 4},
  115. /* Driver timing control A,
  116. * non-overlap=default +1
  117. * EQ=default - 1, CR=default
  118. * pre-charge=default - 1
  119. */
  120. {0xE8, {0x85, 0x01, 0x79}, 3},
  121. /* Power control A, Vcore=1.6V, DDVDH=5.6V */
  122. {0xCB, {0x39, 0x2C, 0x00, 0x34, 0x02}, 5},
  123. /* Pump ratio control, DDVDH=2xVCl */
  124. {0xF7, {0x20}, 1},
  125. /* Driver timing control, all=0 unit */
  126. {0xEA, {0x00, 0x00}, 2},
  127. /* Power control 1, GVDD=4.75V */
  128. {0xC0, {0x26}, 1},
  129. /* Power control 2, DDVDH=VCl*2, VGH=VCl*7, VGL=-VCl*3 */
  130. {0xC1, {0x11}, 1},
  131. /* VCOM control 1, VCOMH=4.025V, VCOML=-0.950V */
  132. {0xC5, {0x35, 0x3E}, 2},
  133. /* VCOM control 2, VCOMH=VMH-2, VCOML=VML-2 */
  134. {0xC7, {0xBE}, 1},
  135. /* Memory access contorl, MX=MY=0, MV=1, ML=0, BGR=1, MH=0 */
  136. {0x36, {0x28}, 1},
  137. /* Pixel format, 16bits/pixel for RGB/MCU interface */
  138. {0x3A, {0x55}, 1},
  139. /* Frame rate control, f=fosc, 70Hz fps */
  140. {0xB1, {0x00, 0x1B}, 2},
  141. /* Enable 3G, disabled */
  142. {0xF2, {0x08}, 1},
  143. /* Gamma set, curve 1 */
  144. {0x26, {0x01}, 1},
  145. /* Positive gamma correction */
  146. {0xE0, {0x1F, 0x1A, 0x18, 0x0A, 0x0F, 0x06, 0x45, 0X87, 0x32, 0x0A, 0x07, 0x02, 0x07, 0x05, 0x00}, 15},
  147. /* Negative gamma correction */
  148. {0XE1, {0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3A, 0x78, 0x4D, 0x05, 0x18, 0x0D, 0x38, 0x3A, 0x1F}, 15},
  149. /* Column address set, SC=0, EC=0xEF */
  150. {0x2A, {0x00, 0x00, 0x00, 0xEF}, 4},
  151. /* Page address set, SP=0, EP=0x013F */
  152. {0x2B, {0x00, 0x00, 0x01, 0x3f}, 4},
  153. /* Memory write */
  154. {0x2C, {0}, 0},
  155. /* Entry mode set, Low vol detect disabled, normal display */
  156. {0xB7, {0x07}, 1},
  157. /* Display function control */
  158. {0xB6, {0x0A, 0x82, 0x27, 0x00}, 4},
  159. /* Sleep out */
  160. {0x11, {0}, 0x80},
  161. /* Display on */
  162. {0x29, {0}, 0x80},
  163. {0, {0}, 0xff},
  164. };
  165. /* Send a command to the LCD. Uses spi_device_polling_transmit, which waits
  166. * until the transfer is complete.
  167. *
  168. * Since command transactions are usually small, they are handled in polling
  169. * mode for higher speed. The overhead of interrupt transactions is more than
  170. * just waiting for the transaction to complete.
  171. */
  172. void lcd_cmd(spi_device_handle_t spi, const uint8_t cmd)
  173. {
  174. esp_err_t ret;
  175. spi_transaction_t t;
  176. memset(&t, 0, sizeof(t)); //Zero out the transaction
  177. t.length=8; //Command is 8 bits
  178. t.tx_buffer=&cmd; //The data is the cmd itself
  179. t.user=(void*)0; //D/C needs to be set to 0
  180. ret=spi_device_polling_transmit(spi, &t); //Transmit!
  181. assert(ret==ESP_OK); //Should have had no issues.
  182. }
  183. /* Send data to the LCD. Uses spi_device_polling_transmit, which waits until the
  184. * transfer is complete.
  185. *
  186. * Since data transactions are usually small, they are handled in polling
  187. * mode for higher speed. The overhead of interrupt transactions is more than
  188. * just waiting for the transaction to complete.
  189. */
  190. void lcd_data(spi_device_handle_t spi, const uint8_t *data, int len)
  191. {
  192. esp_err_t ret;
  193. spi_transaction_t t;
  194. if (len==0) return; //no need to send anything
  195. memset(&t, 0, sizeof(t)); //Zero out the transaction
  196. t.length=len*8; //Len is in bytes, transaction length is in bits.
  197. t.tx_buffer=data; //Data
  198. t.user=(void*)1; //D/C needs to be set to 1
  199. ret=spi_device_polling_transmit(spi, &t); //Transmit!
  200. assert(ret==ESP_OK); //Should have had no issues.
  201. }
  202. //This function is called (in irq context!) just before a transmission starts. It will
  203. //set the D/C line to the value indicated in the user field.
  204. void lcd_spi_pre_transfer_callback(spi_transaction_t *t)
  205. {
  206. int dc=(int)t->user;
  207. gpio_set_level(PIN_NUM_DC, dc);
  208. }
  209. uint32_t lcd_get_id(spi_device_handle_t spi)
  210. {
  211. //get_id cmd
  212. lcd_cmd(spi, 0x04);
  213. spi_transaction_t t;
  214. memset(&t, 0, sizeof(t));
  215. t.length=8*3;
  216. t.flags = SPI_TRANS_USE_RXDATA;
  217. t.user = (void*)1;
  218. esp_err_t ret = spi_device_polling_transmit(spi, &t);
  219. assert( ret == ESP_OK );
  220. return *(uint32_t*)t.rx_data;
  221. }
  222. //Initialize the display
  223. void lcd_init(spi_device_handle_t spi)
  224. {
  225. int cmd=0;
  226. const lcd_init_cmd_t* lcd_init_cmds;
  227. //Initialize non-SPI GPIOs
  228. gpio_set_direction(PIN_NUM_DC, GPIO_MODE_OUTPUT);
  229. gpio_set_direction(PIN_NUM_RST, GPIO_MODE_OUTPUT);
  230. gpio_set_direction(PIN_NUM_BCKL, GPIO_MODE_OUTPUT);
  231. //Reset the display
  232. gpio_set_level(PIN_NUM_RST, 0);
  233. vTaskDelay(100 / portTICK_RATE_MS);
  234. gpio_set_level(PIN_NUM_RST, 1);
  235. vTaskDelay(100 / portTICK_RATE_MS);
  236. //detect LCD type
  237. uint32_t lcd_id = lcd_get_id(spi);
  238. int lcd_detected_type = 0;
  239. int lcd_type;
  240. printf("LCD ID: %08X\n", lcd_id);
  241. if ( lcd_id == 0 ) {
  242. //zero, ili
  243. lcd_detected_type = LCD_TYPE_ILI;
  244. printf("ILI9341 detected.\n");
  245. } else {
  246. // none-zero, ST
  247. lcd_detected_type = LCD_TYPE_ST;
  248. printf("ST7789V detected.\n");
  249. }
  250. #ifdef CONFIG_LCD_TYPE_AUTO
  251. lcd_type = lcd_detected_type;
  252. #elif defined( CONFIG_LCD_TYPE_ST7789V )
  253. printf("kconfig: force CONFIG_LCD_TYPE_ST7789V.\n");
  254. lcd_type = LCD_TYPE_ST;
  255. #elif defined( CONFIG_LCD_TYPE_ILI9341 )
  256. printf("kconfig: force CONFIG_LCD_TYPE_ILI9341.\n");
  257. lcd_type = LCD_TYPE_ILI;
  258. #endif
  259. if ( lcd_type == LCD_TYPE_ST ) {
  260. printf("LCD ST7789V initialization.\n");
  261. lcd_init_cmds = st_init_cmds;
  262. } else {
  263. printf("LCD ILI9341 initialization.\n");
  264. lcd_init_cmds = ili_init_cmds;
  265. }
  266. //Send all the commands
  267. while (lcd_init_cmds[cmd].databytes!=0xff) {
  268. lcd_cmd(spi, lcd_init_cmds[cmd].cmd);
  269. lcd_data(spi, lcd_init_cmds[cmd].data, lcd_init_cmds[cmd].databytes&0x1F);
  270. if (lcd_init_cmds[cmd].databytes&0x80) {
  271. vTaskDelay(100 / portTICK_RATE_MS);
  272. }
  273. cmd++;
  274. }
  275. ///Enable backlight
  276. gpio_set_level(PIN_NUM_BCKL, 0);
  277. }
  278. /* To send a set of lines we have to send a command, 2 data bytes, another command, 2 more data bytes and another command
  279. * before sending the line data itself; a total of 6 transactions. (We can't put all of this in just one transaction
  280. * because the D/C line needs to be toggled in the middle.)
  281. * This routine queues these commands up as interrupt transactions so they get
  282. * sent faster (compared to calling spi_device_transmit several times), and at
  283. * the mean while the lines for next transactions can get calculated.
  284. */
  285. static void send_lines(spi_device_handle_t spi, int ypos, uint16_t *linedata)
  286. {
  287. esp_err_t ret;
  288. int x;
  289. //Transaction descriptors. Declared static so they're not allocated on the stack; we need this memory even when this
  290. //function is finished because the SPI driver needs access to it even while we're already calculating the next line.
  291. static spi_transaction_t trans[6];
  292. //In theory, it's better to initialize trans and data only once and hang on to the initialized
  293. //variables. We allocate them on the stack, so we need to re-init them each call.
  294. for (x=0; x<6; x++) {
  295. memset(&trans[x], 0, sizeof(spi_transaction_t));
  296. if ((x&1)==0) {
  297. //Even transfers are commands
  298. trans[x].length=8;
  299. trans[x].user=(void*)0;
  300. } else {
  301. //Odd transfers are data
  302. trans[x].length=8*4;
  303. trans[x].user=(void*)1;
  304. }
  305. trans[x].flags=SPI_TRANS_USE_TXDATA;
  306. }
  307. trans[0].tx_data[0]=0x2A; //Column Address Set
  308. trans[1].tx_data[0]=0; //Start Col High
  309. trans[1].tx_data[1]=0; //Start Col Low
  310. trans[1].tx_data[2]=(320)>>8; //End Col High
  311. trans[1].tx_data[3]=(320)&0xff; //End Col Low
  312. trans[2].tx_data[0]=0x2B; //Page address set
  313. trans[3].tx_data[0]=ypos>>8; //Start page high
  314. trans[3].tx_data[1]=ypos&0xff; //start page low
  315. trans[3].tx_data[2]=(ypos+PARALLEL_LINES)>>8; //end page high
  316. trans[3].tx_data[3]=(ypos+PARALLEL_LINES)&0xff; //end page low
  317. trans[4].tx_data[0]=0x2C; //memory write
  318. trans[5].tx_buffer=linedata; //finally send the line data
  319. trans[5].length=320*2*8*PARALLEL_LINES; //Data length, in bits
  320. trans[5].flags=0; //undo SPI_TRANS_USE_TXDATA flag
  321. //Queue all transactions.
  322. for (x=0; x<6; x++) {
  323. ret=spi_device_queue_trans(spi, &trans[x], portMAX_DELAY);
  324. assert(ret==ESP_OK);
  325. }
  326. //When we are here, the SPI driver is busy (in the background) getting the transactions sent. That happens
  327. //mostly using DMA, so the CPU doesn't have much to do here. We're not going to wait for the transaction to
  328. //finish because we may as well spend the time calculating the next line. When that is done, we can call
  329. //send_line_finish, which will wait for the transfers to be done and check their status.
  330. }
  331. static void send_line_finish(spi_device_handle_t spi)
  332. {
  333. spi_transaction_t *rtrans;
  334. esp_err_t ret;
  335. //Wait for all 6 transactions to be done and get back the results.
  336. for (int x=0; x<6; x++) {
  337. ret=spi_device_get_trans_result(spi, &rtrans, portMAX_DELAY);
  338. assert(ret==ESP_OK);
  339. //We could inspect rtrans now if we received any info back. The LCD is treated as write-only, though.
  340. }
  341. }
  342. //Simple routine to generate some patterns and send them to the LCD. Don't expect anything too
  343. //impressive. Because the SPI driver handles transactions in the background, we can calculate the next line
  344. //while the previous one is being sent.
  345. static void display_pretty_colors(spi_device_handle_t spi)
  346. {
  347. uint16_t *lines[2];
  348. //Allocate memory for the pixel buffers
  349. for (int i=0; i<2; i++) {
  350. lines[i]=heap_caps_malloc(320*PARALLEL_LINES*sizeof(uint16_t), MALLOC_CAP_DMA);
  351. assert(lines[i]!=NULL);
  352. }
  353. int frame=0;
  354. //Indexes of the line currently being sent to the LCD and the line we're calculating.
  355. int sending_line=-1;
  356. int calc_line=0;
  357. while(1) {
  358. frame++;
  359. for (int y=0; y<240; y+=PARALLEL_LINES) {
  360. //Calculate a line.
  361. pretty_effect_calc_lines(lines[calc_line], y, frame, PARALLEL_LINES);
  362. //Finish up the sending process of the previous line, if any
  363. if (sending_line!=-1) send_line_finish(spi);
  364. //Swap sending_line and calc_line
  365. sending_line=calc_line;
  366. calc_line=(calc_line==1)?0:1;
  367. //Send the line we currently calculated.
  368. send_lines(spi, y, lines[sending_line]);
  369. //The line set is queued up for sending now; the actual sending happens in the
  370. //background. We can go on to calculate the next line set as long as we do not
  371. //touch line[sending_line]; the SPI sending process is still reading from that.
  372. }
  373. }
  374. }
  375. void app_main(void)
  376. {
  377. esp_err_t ret;
  378. spi_device_handle_t spi;
  379. spi_bus_config_t buscfg={
  380. .miso_io_num=PIN_NUM_MISO,
  381. .mosi_io_num=PIN_NUM_MOSI,
  382. .sclk_io_num=PIN_NUM_CLK,
  383. .quadwp_io_num=-1,
  384. .quadhd_io_num=-1,
  385. .max_transfer_sz=PARALLEL_LINES*320*2+8
  386. };
  387. spi_device_interface_config_t devcfg={
  388. #ifdef CONFIG_LCD_OVERCLOCK
  389. .clock_speed_hz=26*1000*1000, //Clock out at 26 MHz
  390. #else
  391. .clock_speed_hz=10*1000*1000, //Clock out at 10 MHz
  392. #endif
  393. .mode=0, //SPI mode 0
  394. .spics_io_num=PIN_NUM_CS, //CS pin
  395. .queue_size=7, //We want to be able to queue 7 transactions at a time
  396. .pre_cb=lcd_spi_pre_transfer_callback, //Specify pre-transfer callback to handle D/C line
  397. };
  398. //Initialize the SPI bus
  399. ret=spi_bus_initialize(LCD_HOST, &buscfg, DMA_CHAN);
  400. ESP_ERROR_CHECK(ret);
  401. //Attach the LCD to the SPI bus
  402. ret=spi_bus_add_device(LCD_HOST, &devcfg, &spi);
  403. ESP_ERROR_CHECK(ret);
  404. //Initialize the LCD
  405. lcd_init(spi);
  406. //Initialize the effect displayed
  407. ret=pretty_effect_init();
  408. ESP_ERROR_CHECK(ret);
  409. //Go do nice stuff.
  410. display_pretty_colors(spi);
  411. }