/* ILI9341 SPI driver inspired from the Teensy version of Frank Bösing, 2017 */ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_system.h" #include "driver/spi_master.h" #include "soc/gpio_struct.h" #include "driver/gpio.h" #include #include "ili9341_t3dma.h" #include "font8x8.h" static spi_device_handle_t lcdspi; static spi_transaction_t trans[MAX_SPI_TRANS]; static uint16_t *blocks[NR_OF_BLOCK]; static uint8_t _rst, _cs, _dc; static uint8_t _miso, _mosi, _clk; static uint8_t _touch_irq, _touch_cs; //DRAM_ATTR static uint16_t block0[320*LINES_PER_BLOCK]; //DRAM_ATTR static uint16_t block1[320*LINES_PER_BLOCK]; static const lcd_init_cmd_t ili_init_cmds[]={ {0xEF, {0x03, 0x80, 0x02}, 3}, {0xCF, {0x00, 0XC1, 0X30}, 3}, {0xED, {0x64, 0x03, 0X12, 0X81}, 4}, {0xE8, {0x85, 0x00, 0x78}, 3}, {0xCB, {0x39, 0x2C, 0x00, 0x34, 0x02}, 5}, {0xF7, {0x20}, 1}, {0xEA, {0x00, 0x00}, 2}, {ILI9341_PWCTR1, {0x23}, 1}, // Power control {ILI9341_PWCTR2, {0x10}, 1}, // Power control {ILI9341_VMCTR1, {0x3e, 0x28}, 2}, // VCM control {ILI9341_VMCTR2, {0x86}, 1}, // VCM control2 {ILI9341_MADCTL, {0x48}, 1}, // Memory Access Control {ILI9341_PIXFMT, {0x55}, 1}, {ILI9341_FRMCTR1, {0x00, 0x18}, 2}, {ILI9341_DFUNCTR, {0x08, 0x82, 0x27}, 3}, // Display Function Control {0xF2, {0x00}, 1}, // Gamma Function Disable {ILI9341_GAMMASET, {0x01}, 1}, // Gamma curve selected {ILI9341_GMCTRP1, {0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00}, 15}, // Set Gamma {ILI9341_GMCTRN1, {0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F}, 15}, // Set Gamma // 3, 0xb1, 0x00, 0x1f, // FrameRate Control 61Hz {0xb1, {0x00, 0x10}, 2}, // FrameRate Control 119Hz {ILI9341_MADCTL, {MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_BGR}, 1}, /* Sleep out */ {ILI9341_SLPOUT, {0}, 0x80}, /* Display on */ {ILI9341_DISPON, {0}, 0x80}, // Area width, hight {ILI9341_CASET, {0, 0, (ILI9341_TFTREALWIDTH)>>8, (ILI9341_TFTREALWIDTH)&0xff}, 4}, {ILI9341_PASET, {0, 0, (ILI9341_TFTREALHEIGHT)>>8, (ILI9341_TFTREALHEIGHT)&0xff}, 4}, {0, {0}, 0xff}, }; static void lcd_cmd(spi_device_handle_t spi, const uint8_t cmd) { esp_err_t ret; spi_transaction_t t; memset(&t, 0, sizeof(t)); //Zero out the transaction t.length=8; //Command is 8 bits t.tx_buffer=&cmd; //The data is the cmd itself t.user=(void*)0; //D/C needs to be set to 0 t.flags=0; ret=spi_device_polling_transmit(spi, &t); //Transmit! assert(ret==ESP_OK); //Should have had no issues. } static void lcd_data(spi_device_handle_t spi, const uint8_t *data, int len) { esp_err_t ret; spi_transaction_t t; if (len==0) return; //no need to send anything memset(&t, 0, sizeof(t)); //Zero out the transaction t.length=len*8; //Len is in bytes, transaction length is in bits. t.tx_buffer=data; //Data t.user=(void*)1; //D/C needs to be set to 1 t.flags=0; ret=spi_device_polling_transmit(spi, &t); //Transmit! assert(ret==ESP_OK); //Should have had no issues. } //This function is called (in irq context!) just before a transmission starts. It will //set the D/C line to the value indicated in the user field. static void lcd_spi_pre_transfer_callback(spi_transaction_t *t) { int dc=(int)t->user; gpio_set_level((gpio_num_t)_dc, dc); } //Initialize the display static void lcd_init(spi_device_handle_t spi) { //Initialize non-SPI GPIOs gpio_set_direction((gpio_num_t)_dc, GPIO_MODE_OUTPUT); printf("LCD ILI9341 initialization.\n"); //Send all the commands //memcpy(ili_init_cmds, ili_init_cmdos, sizeof(ili_init_cmdos)); int cmd=0; while (ili_init_cmds[cmd].databytes!=0xff) { lcd_cmd(spi, ili_init_cmds[cmd].cmd); lcd_data(spi, ili_init_cmds[cmd].data, ili_init_cmds[cmd].databytes&0x1F); if (ili_init_cmds[cmd].databytes&0x80) { vTaskDelay(100 / portTICK_RATE_MS); } cmd++; } //Allocate memory block buffers and DMA transactions printf("Allocate video mem and DMA transactions\n"); int i=0; trans[i].tx_data[0]=ILI9341_RAMWR; trans[i].length=8; trans[i].user=(void*)0; trans[i++].flags=SPI_TRANS_USE_TXDATA; //blocks[0]= &block0[0]; //blocks[1]= &block1[0]; int remaininglines=ILI9341_TFTREALHEIGHT; for (int j=0; j 1)*/ blocks[j]= (uint16_t*)heap_caps_malloc(ILI9341_TFTREALWIDTH*lines_per_block*sizeof(uint16_t), MALLOC_CAP_DMA); assert(blocks[j]!=NULL); trans[i].tx_buffer=blocks[j]; trans[i].length=ILI9341_TFTREALWIDTH*2*8*lines_per_block; trans[i].user=(void*)1; trans[i++].flags=0; //undo SPI_TRANS_USE_TXDATA flag uint16_t color; switch (j) { case 0: color=0xf000; break; case 1: color=0x0f00; break; case 2: color=0x00f0; break; case 3: default: color=0x000f; break; } uint16_t * fb = blocks[j]; for (int y=0;y>6]; return(&block[(j&0x3F)*ILI9341_TFTREALWIDTH]); } void ILI9341_t3DMA::fillScreen(uint16_t color) { int i,j; color=SPI_SWAP_DATA_TX(color,16); for (j=0; j>6]; uint16_t * dst=&block[(j&0x3F)*ILI9341_TFTREALWIDTH]; for (i=0; i>6]; uint16_t * dst=&block[(y&0x3F)*ILI9341_TFTREALWIDTH]; src=buffer; for (i=0; i>6]; dst=&block[(y&0x3F)*ILI9341_TFTREALWIDTH]; src=buffer; for (i=0; i>6]; uint16_t * dst=&block[(y&0x3F)*ILI9341_TFTREALWIDTH+(ILI9341_TFTREALWIDTH-width)/2]; src=buffer; for (i=0; i>6]; dst=&block[(y&0x3F)*ILI9341_TFTREALWIDTH+(ILI9341_TFTREALWIDTH-width)/2]; src=buffer; for (i=0; i height) y += (ILI9341_TFTHEIGHT - height)/2; uint8_t * src=buf; uint16_t * block=blocks[y>>6]; uint16_t * dst=&block[(y&0x3F)*ILI9341_TFTREALWIDTH]; if (ILI9341_TFTWIDTH > width) dst += (ILI9341_TFTWIDTH - width)/2; for (int i=0; i>6]; uint16_t * dst=&block[(l&0x3F)*ILI9341_TFTREALWIDTH+x]; for (i=0; i(arx+arw)) || ((x+w)(ary+arh)) || ((y+h) arx) && (x<(arx+arw)) ) { arw = arw - (x-arx); arx = arx + (x-arx); } else { bmp_offx = arx; } if ( ((x+w) > arx) && ((x+w)<(arx+arw)) ) { arw -= (arx+arw-x-w); } if ( (y > ary) && (y<(ary+arh)) ) { arh = arh - (y-ary); ary = ary + (y-ary); } else { bmp_offy = ary; } if ( ((y+h) > ary) && ((y+h)<(ary+arh)) ) { arh -= (ary+arh-y-h); } } int l=ary; bitmap = bitmap + bmp_offy*w + bmp_offx; for (int row=0;row>6]; uint16_t * dst=&block[(l&0x3F)*ILI9341_TFTREALWIDTH+arx]; bmp_ptr = (uint16_t*)bitmap; for (int col=0;col>6]; dst=&block[(l&0x3F)*ILI9341_TFTREALWIDTH+x]; bits = *charpt; if (bits&0x01) *dst++=fgcolor; else *dst++=bgcolor; bits = bits >> 1; if (bits&0x01) *dst++=fgcolor; else *dst++=bgcolor; bits = bits >> 1; if (bits&0x01) *dst++=fgcolor; else *dst++=bgcolor; bits = bits >> 1; if (bits&0x01) *dst++=fgcolor; else *dst++=bgcolor; bits = bits >> 1; if (bits&0x01) *dst++=fgcolor; else *dst++=bgcolor; bits = bits >> 1; if (bits&0x01) *dst++=fgcolor; else *dst++=bgcolor; bits = bits >> 1; if (bits&0x01) *dst++=fgcolor; else *dst++=bgcolor; bits = bits >> 1; if (bits&0x01) *dst++=fgcolor; else *dst++=bgcolor; l++; } block=blocks[l>>6]; dst=&block[(l&0x3F)*ILI9341_TFTREALWIDTH+x]; bits = *charpt++; if (bits&0x01) *dst++=fgcolor; else *dst++=bgcolor; bits = bits >> 1; if (bits&0x01) *dst++=fgcolor; else *dst++=bgcolor; bits = bits >> 1; if (bits&0x01) *dst++=fgcolor; else *dst++=bgcolor; bits = bits >> 1; if (bits&0x01) *dst++=fgcolor; else *dst++=bgcolor; bits = bits >> 1; if (bits&0x01) *dst++=fgcolor; else *dst++=bgcolor; bits = bits >> 1; if (bits&0x01) *dst++=fgcolor; else *dst++=bgcolor; bits = bits >> 1; if (bits&0x01) *dst++=fgcolor; else *dst++=bgcolor; bits = bits >> 1; if (bits&0x01) *dst++=fgcolor; else *dst++=bgcolor; l++; } x +=8; } } // TOUCH #define _BV(bit) (1 << (bit)) #define XPT2046_CFG_START _BV(7) #define XPT2046_CFG_MUX(v) ((v&0b111) << (4)) #define XPT2046_CFG_8BIT _BV(3) #define XPT2046_CFG_12BIT (0) #define XPT2046_CFG_SER _BV(2) #define XPT2046_CFG_DFR (0) #define XPT2046_CFG_PWR(v) ((v&0b11)) #define XPT2046_MUX_Y 0b101 #define XPT2046_MUX_X 0b001 #define XPT2046_MUX_Z1 0b011 #define XPT2046_MUX_Z2 0b100 static spi_device_handle_t touchspi; //void ILI9341_t3DMA::touchBegin(uint8_t mosi, uint8_t miso, uint8_t clk, uint8_t cs) { void ILI9341_t3DMA::touchBegin() { esp_err_t ret; gpio_set_direction((gpio_num_t)_touch_irq, GPIO_MODE_INPUT); gpio_set_pull_mode((gpio_num_t)_touch_irq, GPIO_PULLUP_ONLY); spi_device_interface_config_t devcfg; memset(&devcfg, 0, sizeof(devcfg)); devcfg.clock_speed_hz=2500000; devcfg.mode=0; devcfg.spics_io_num=_touch_cs; devcfg.queue_size=2; devcfg.flags = SPI_DEVICE_HALFDUPLEX ; /* spi_bus_config_t buscfg; memset(&buscfg, 0, sizeof(buscfg)); buscfg.miso_io_num=miso; buscfg.mosi_io_num=mosi; buscfg.sclk_io_num=clk; buscfg.quadwp_io_num=-1; buscfg.quadhd_io_num=-1; buscfg.max_transfer_sz=48; ret=spi_bus_initialize(HSPI_HOST, &buscfg, 2); ESP_ERROR_CHECK(ret); */ ret=spi_bus_add_device(HSPI_HOST, &devcfg, &touchspi); ESP_ERROR_CHECK(ret); } uint16_t touch_get_data(spi_device_handle_t spi, const uint8_t cmd) { spi_transaction_t t; memset(&t, 0, sizeof(t)); //Zero out the transaction t.length=8; //Command is 8 bits t.tx_buffer=&cmd; //The data is the cmd itself esp_err_t ret=spi_device_polling_transmit(spi, &t); //Transmit! if (ret==ESP_OK) { memset(&t, 0, sizeof(t)); t.rxlength=8*2; t.flags = SPI_TRANS_USE_RXDATA; ret = spi_device_polling_transmit(spi, &t); if (ret==ESP_OK) { printf("touch data failed\n"); return 0; } } else { printf("touch cmd failed\n"); } return *(uint16_t*)t.rx_data; } bool ILI9341_t3DMA::isTouching() { return ((gpio_get_level((gpio_num_t)_touch_irq) == 0 ? true : false)); } void ILI9341_t3DMA::readRo(uint16_t * oX, uint16_t * oY, uint16_t * oZ) { uint16_t x = 0; uint16_t y = 0; uint16_t z1 = 0; uint16_t z2 = 0; uint8_t i = 0; int16_t xraw=0, yraw=0; for(; i < 15; i++) { y += touch_get_data(touchspi, (XPT2046_CFG_START | XPT2046_CFG_12BIT | XPT2046_CFG_DFR | XPT2046_CFG_MUX(XPT2046_MUX_Y) | XPT2046_CFG_PWR(3))); x += touch_get_data(touchspi, (XPT2046_CFG_START | XPT2046_CFG_12BIT | XPT2046_CFG_DFR | XPT2046_CFG_MUX(XPT2046_MUX_X) | XPT2046_CFG_PWR(3))); z1 += touch_get_data(touchspi, (XPT2046_CFG_START | XPT2046_CFG_12BIT | XPT2046_CFG_DFR | XPT2046_CFG_MUX(XPT2046_MUX_Z1)| XPT2046_CFG_PWR(3))); z2 += touch_get_data(touchspi, (XPT2046_CFG_START | XPT2046_CFG_12BIT | XPT2046_CFG_DFR | XPT2046_CFG_MUX(XPT2046_MUX_Z2)| XPT2046_CFG_PWR(3))); } printf("%d %d %d %d \n",x/15,y/15,z1/15,z2/15); /* SPI.beginTransaction(SPI_SETTING); digitalWrite(_touch_cs, LOW); for(; i < 15; i++) { // SPI requirer 32bit aliment uint8_t buf[12] = { (XPT2046_CFG_START | XPT2046_CFG_12BIT | XPT2046_CFG_DFR | XPT2046_CFG_MUX(XPT2046_MUX_Y) | XPT2046_CFG_PWR(3)), 0x00, 0x00, (XPT2046_CFG_START | XPT2046_CFG_12BIT | XPT2046_CFG_DFR | XPT2046_CFG_MUX(XPT2046_MUX_X) | XPT2046_CFG_PWR(3)), 0x00, 0x00, (XPT2046_CFG_START | XPT2046_CFG_12BIT | XPT2046_CFG_DFR | XPT2046_CFG_MUX(XPT2046_MUX_Z1)| XPT2046_CFG_PWR(3)), 0x00, 0x00, (XPT2046_CFG_START | XPT2046_CFG_12BIT | XPT2046_CFG_DFR | XPT2046_CFG_MUX(XPT2046_MUX_Z2)| XPT2046_CFG_PWR(3)), 0x00, 0x00 }; SPI.transfer(&buf[0], &buf[0], 12); y += (buf[1] << 8 | buf[2])>>3; x += (buf[4] << 8 | buf[5])>>3; z1 += (buf[7] << 8 | buf[8])>>3; z2 += (buf[10] << 8 | buf[11])>>3; } */ } void ILI9341_t3DMA::readRaw(uint16_t * oX, uint16_t * oY, uint16_t * oZ) { } void ILI9341_t3DMA::readCal(uint16_t * oX, uint16_t * oY, uint16_t * oZ){ } void ILI9341_t3DMA::callibrateTouch(uint16_t xMin,uint16_t yMin,uint16_t xMax,uint16_t yMax){ }