SPI DMA Delays

Addi2438
Posts: 13
Joined: Thu Sep 14, 2017 11:23 am

SPI DMA Delays

Postby Addi2438 » Mon Oct 02, 2017 6:09 pm

Good evening everybody,
currently im writing on a driver for my TFT display and run into some problems.

The basic concept behind my code is the following:

I have an array with 12 instances of the "spi_transaction_t" struct as transmission buffers.
When drawing on the displays the first thing that happens is that I use the first 5 instances to store the address window information and queue them for beeing send via DMA.
After the address window I use the remaining 7 to hold the color data and also queue them for beeing send via DMA.
Each of them point to the same color buffer allocated with "heap_caps_malloc(18 + 2 + 2*w*2 + 2, MALLOC_CAP_DMA)" with enough storage for 2 display lines (at the moment I am testing around with a fillRect function so its only one color).

When all 12 buffers are used the driver will wait until everything is transmitted and then use the 12 now free transmission buffers to transmit possible remaining lines or color data.


So far everything works fine but there are some minor problems with delays during queueing and waiting for the data beeing send.

On the picture above you can clearly see what I mean.


Image


The smaller delays (like the one with the yellow arrow) happen when the driver queues up transmissions using "spi_device_queue_trans(...)".
You can see that there are 7 transmissions queued up before all transmission buffers are used and the driver waits after everything has been sent out resulting in a larger delay (blue arrow). I guess that the larger delay is the result of a task switch to the idle task or something.


The question I have now is:
How can I reduce these delays (especially the larger ones) without having to send more than 2 lines with a single transmission at the same time?

It works but even with 26MHz SPI you can see that the display flickers.
I think this must be because it takes to long to send everything cause of these delays.


The code below is not executeable but it contains the functions everything is based on and the fillRect function to show how it works.
I know its not the best yet but its still in development :D.

Code: Select all

static void ili9163c_send_cmd_dma(ili9163c_t *display, const uint8_t transNr, uint8_t *buffer, const uint8_t cmd) {
	
	// first 2 bytes are used to control the dc pin
	buffer[0] = display->pinDC;
	buffer[1] = 0; // 0 for sending command
	
	
	display->dmaBuffer[transNr].tx_data[0] = cmd;
	display->dmaBuffer[transNr].length	   = 8; 				 	//Data length, in bits
	display->dmaBuffer[transNr].rxlength   = 8;
	display->dmaBuffer[transNr].user	   = (void*)(buffer);
	display->dmaBuffer[transNr].flags	   = SPI_TRANS_USE_TXDATA;

	assert(spi_device_queue_trans(display->spiDevice, &(display->dmaBuffer[transNr]), portMAX_DELAY) == ESP_OK);
}


static void ili9163c_send_data_dma(ili9163c_t *display, const uint8_t transNr, uint8_t *data, uint16_t len) {
	
	// first 2 bytes are used to control the dc pin
	data[0] = display->pinDC;
	data[1] = 1; // 1 for sending data
	
	
	display->dmaBuffer[transNr].tx_buffer = (data + 2);
	display->dmaBuffer[transNr].length	  = len*8; 			//Data length, in bits
	display->dmaBuffer[transNr].rxlength  = len*8;
	display->dmaBuffer[transNr].user	  = (void*)(data);
	display->dmaBuffer[transNr].flags	  = 0; 				
	
	assert(spi_device_queue_trans(display->spiDevice, &(display->dmaBuffer[transNr]), portMAX_DELAY) == ESP_OK);
}


static void ili9163c_setAddr_dma(ili9163c_t *display, const uint8_t transNr, uint8_t *data, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
	// 0 byte offset
	ili9163c_send_cmd_dma(display, transNr, data, CMD_CLMADRS); // Column
	
	if (display->rotation == 0 || display->rotation > 1) {
		uint16_t *data16 = (uint16_t*)(data + 2);
		data16[1] = swapbyte(x0);
		data16[2] = swapbyte(x1);
		
		
		// +2 byte offset cause of userdata from command before
		ili9163c_send_data_dma(display, transNr + 1, (data + 2), sizeof(uint32_t));
	} else {
		uint16_t *data16 = (uint16_t*)(data + 2);
		data16[1] = swapbyte(x0 + __OFFSET);
		data16[2] = swapbyte(x1 + __OFFSET);
		
		// +2 byte offset cause of userdata from command before
		ili9163c_send_data_dma(display, transNr + 1, (data + 2), sizeof(uint32_t));
	}

	// +2 byte additional offset cause of userdata from data command before
	// +4 byte additional offset cause of data from data command before
	ili9163c_send_cmd_dma(display, transNr + 2, (data + (4 + sizeof(uint32_t))), CMD_PGEADRS); // Page
	
	if (display->rotation == 0){
		uint16_t *data16 = (uint16_t*)(data + (6 + sizeof(uint32_t)));
		data16[1] = swapbyte(y0 + __OFFSET);
		data16[2] = swapbyte(y1 + __OFFSET);
		
		
		// +2 byte offset cause of userdata from command before
		ili9163c_send_data_dma(display, transNr + 3, (data + (6 + sizeof(uint32_t))), sizeof(uint32_t));	
	} else {	
		uint16_t *data16 = (uint16_t*)(data + (6 + sizeof(uint32_t)));
		data16[1] = swapbyte(y0);
		data16[2] = swapbyte(y1);
		
		// +2 byte offset cause of userdata from command before
		ili9163c_send_data_dma(display, transNr + 3, (data + (6 + sizeof(uint32_t))), sizeof(uint32_t));	
	}
	
	// +2 byte additional offset cause of userdata from command before
	// +4 byte additional offset cause of data from data command before
	ili9163c_send_cmd_dma(display, transNr + 4, (data + (8 + 2*sizeof(uint32_t))), CMD_RAMWR); //Into RAM
}


void ili9163c_fillRect_dma(ili9163c_t *display, uint16_t x0, uint16_t y0, uint16_t w, uint16_t h, uint16_t color) {
	
	
	// allocate dma useable memory 
	uint8_t *databuffer = (uint8_t*)heap_caps_malloc(18 + 2 + 2*w*2 + 2, MALLOC_CAP_DMA);
	uint16_t *data16 = (uint16_t*)(databuffer + 18 + 2);
	
	color = swapbyte(color);
	for(int i = 0; i <= w*2; i++) data16[i] = color;

	
	// 5 transactions queued here
	ili9163c_setAddr_dma(display, 0, databuffer, x0, y0, x0 + w - 1, y0 + h - 1); // go home
	
	
	// 7 left
	uint8_t maxSlots	   = 12;
	uint8_t remainingSlots = 7;
	
	
	while(h > 0) {
		for( ; remainingSlots > 0 && (h - 2) >= 0; ) {
			ili9163c_send_data_dma(display, (maxSlots - remainingSlots), (databuffer + 20), 2*w*2);
			remainingSlots--; h -= 2;
		} 
		
		// if theres a single line remaining
		if(h == 1 && remainingSlots > 0) {
			ili9163c_send_data_dma(display, (maxSlots - remainingSlots), (databuffer + 20), w*2);
			remainingSlots --; h --;
		}
	
		// wait for the results
		spi_transaction_t *rtrans;
		for(int i = 0; i < (maxSlots - remainingSlots); i++) assert(spi_device_get_trans_result(display->spiDevice, &rtrans, portMAX_DELAY) == ESP_OK);
	
		remainingSlots = maxSlots;
	}
	
	free(databuffer);
}
Hope someone can help me or give me some tips.
If you need executable code to test I can send my whole test project

ESP_Sprite
Posts: 9020
Joined: Thu Nov 26, 2015 4:08 am

Re: SPI DMA Delays

Postby ESP_Sprite » Tue Oct 03, 2017 9:19 am

I think you're right: the code that waits for the transaction to finish does this by context switching while waiting: that can explain the longer duration. The way to solve this within the current SPI driver would be to use the buffers you have as a sort-of ringbuffer: don't explicitly wait until they all are done, but before re-using each buffer, see if it was used by the SPI peripheral before and if so, wait then and there until it finishes.

Addi2438
Posts: 13
Joined: Thu Sep 14, 2017 11:23 am

Re: SPI DMA Delays

Postby Addi2438 » Wed Oct 04, 2017 8:15 pm

Sry for the longer delay.

This sounds like a good idea.

Is there a way to check if a used transaction hadle that was queued is finished?

I tried to use the post_transmission_callback where I set the user field of the handle back to NULL.
On function side I loop through all of the 12 and check whenever the user field is NUL or not.
If it is NULL I reuse it instantly and increment a counter to remember how much transmission are up for beeing finished.
If it is not NULL I wait for it beeing finished and use it afterwards. In this case the counter will be decremented by one.

If everything is queued the code will wait for all transmission to be finished.
Howerver using this method ends in an abort() on the core the driver is running on.


Image

Does someone know what this means?


Code:

Header:

Code: Select all

#ifndef _TFT_ILI9163CLIB_H_
#define _TFT_ILI9163CLIB_H_


//
// include section
//

#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"


#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "esp_heap_caps.h"
#include "esp_log.h"

#include "TFT_ILI9163C_settings.h"
#include "TFT_ILI9163C_registers.h"


//
// define section
//

	// Color definitions
#define	BLACK   0x0000
#define	BLUE    0x001F
#define	RED     0xF800
#define	GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0  
#define WHITE   0xFFFF



#define swapbyte(num) (((num) >> 8)|((num) << 8))
#define swap(a, b) { int16_t t = a; a = b; b = t; }

#define bitClear(num, bit) ((num) &= ~(1 << (bit)))
#define bitSet(num, bit) ((num) |= (1 << (bit)))

//
// typedef section
//

// display init sequence
typedef struct {
	uint8_t cmd;
	uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds.
	uint8_t data[16];
} init_cmd_t;


// struct for the displays
typedef struct ili9163c_t{
	
	// spi stuff
	spi_host_device_t 				spiHost;	// storing spi host pheripheral
	
	spi_device_interface_config_t 	devcfg;		// storing the spi device configuration
	spi_device_handle_t 			spiDevice;  // storing the spi device handler
	
	// spi transaction buffer
	spi_transaction_t dmaBuffer[12];
	
	// non spi pins
	gpio_num_t pinDC;		// pin to select if spi data is data or a command
	gpio_num_t pinRst;		// pin for resetting the tft controller
	gpio_num_t pinBckLight; // pin for the backlight
	
	
	// settings
	uint8_t rotation;
	uint8_t maCtrlData;
	uint8_t colorSpaceData;
	
	uint16_t width;
	uint16_t height;
	
	uint8_t sem;
	
} ili9163c_t;


//
// var section
//

const static init_cmd_t init_cmds[] = {
	{0x01,  0, {}},     // Software reset
	{0x11,  0, {}},     // Exit sleep mode
	{0x3A,  1, {0x05}}, // Set pixel format
	{0x26,  1, {0x04}}, // Set Gamma curve
	{0xF2,  1, {0x01}}, // Gamma adjustment enabled
	{0xE0, 15, {0x3F, 0x25, 0x1C, 0x1E, 0x20, 0x12, 0x2A, 0x90, 0x24, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00}}, // Positive Gamma
	{0xE1, 15, {0x20, 0x20, 0x20, 0x20, 0x05, 0x00, 0x15, 0xA7, 0x3D, 0x18, 0x25, 0x2A, 0x2B, 0x2B, 0x3A}}, // Negative Gamma
	{0xB1,  2, {17,20}}, // Frame rate control 1  0x08, 0x08
	{0xB4,  1, {0x07}},       // Display inversion
	{0xC0,  2, {0x0A, 0x02}}, // Power control 1
	{0xC1,  1, {0x02}},       // Power control 2
	{0xC5,  2, {0x50, 0x5B}}, // Vcom control 1
	{0xC7,  1, {0x40}},       // Vcom offset
	{0x2A,  5, {0x00, 0x00, 0x00, 0x7F, 250}}, // Set column address
	{0x2B,  4, {0x00, 0x00, 0x00, 0x9F}},      // Set page address
	{0x36,  1, {0xC8}}, // Set address mode
	{0x13,  0, {}}, // Set display on
	{0x29,  0, {}}, // Set display on
	{0, 0xff, {}}
};


//
// function section
//

// regular functions
ili9163c_t ili9163c_createDisplay(spi_host_device_t hostspi, gpio_num_t cspin, gpio_num_t dcpin, gpio_num_t rstpin, gpio_num_t bcklightpin);

void ili9163c_init(ili9163c_t *display);

// clear screen
void ili9163c_clearScreen_dma(ili9163c_t *display, uint16_t color); 

// rect
void ili9163c_fillRect_dma(ili9163c_t *display, uint16_t x0, uint16_t y0, uint16_t w, uint16_t h, uint16_t color);

// color code
uint16_t ili9163c_color565(uint8_t r, uint8_t g, uint8_t b);


#endif // _TFT_ILI9163CLIB_H_

C File:

Code: Select all

#include "TFT_ILI9163C.h"

//---------------------------------------------------------------------------------------------------------------------------
// 
// helper functions
// 
//---------------------------------------------------------------------------------------------------------------------------

// send a command to the LCD
static void ili9163c_send_cmd(ili9163c_t *display, const uint8_t cmd) {
	uint8_t info[] = {display->pinDC, 0};
	
	spi_transaction_t t;
	
    memset(&t, 0, sizeof(t));				// zero out the transaction
	t.tx_data[0] = cmd;						// the data is the cmd itself
    t.length	 = 8;						// command is 8 bits
    t.user		 = (void*)info;
	t.flags		 = SPI_TRANS_USE_TXDATA;
	
	assert(spi_device_transmit(display->spiDevice, &t) == ESP_OK); //Should have had no issues.
}

static void ili9163c_send_cmd_dma(ili9163c_t *display, const uint8_t transNr, uint8_t *buffer, const uint8_t cmd) {
	buffer[0] = display->pinDC;
	buffer[1] = 0;
	
	
	display->dmaBuffer[transNr].tx_data[0] = cmd;
	display->dmaBuffer[transNr].length	   = 8;						// Data length, in bits
	display->dmaBuffer[transNr].user	   = (void*)(buffer);
	display->dmaBuffer[transNr].flags	   = SPI_TRANS_USE_TXDATA;	// undo SPI_TRANS_USE_TXDATA flag

	assert(spi_device_queue_trans(display->spiDevice, &(display->dmaBuffer[transNr]), portMAX_DELAY) == ESP_OK); //portMAX_DELAY
}



// send data to the LCD
static void ili9163c_send_data(ili9163c_t *display, const uint8_t *data, int len) {
	uint8_t info[] = {display->pinDC, 1};
	
	spi_transaction_t t;
	
    if (len == 0) return;
    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*)info;	
	
    assert(spi_device_transmit(display->spiDevice, &t) == ESP_OK); // Should have had no issues.
}


static void ili9163c_send_data_dma(ili9163c_t *display, const uint8_t transNr, uint8_t *data, uint16_t len) {
	data[0] = display->pinDC;
	data[1] = 1;
	
	
	
	display->dmaBuffer[transNr].tx_buffer = (data + 2);
	display->dmaBuffer[transNr].length	  = len*8; 			// Data length, in bits
	display->dmaBuffer[transNr].user	  = (void*)(data);
	display->dmaBuffer[transNr].flags	  = 0; 				// undo SPI_TRANS_USE_TXDATA flag
	
	assert(spi_device_queue_trans(display->spiDevice, &(display->dmaBuffer[transNr]), portMAX_DELAY) == ESP_OK); //portMAX_DELAY
}



static void ili9163c_pre_transfer_callback_dma(spi_transaction_t *t) {
    gpio_set_level(((uint8_t*)(t->user))[0], ((uint8_t*)(t->user))[1]);
}

static void ili9163c_post_transfer_callback_dma(spi_transaction_t *t) {
	t->user = NULL;
}



static void ili9163c_setAddr(ili9163c_t *display, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
	ili9163c_send_cmd(display, CMD_CLMADRS); // Column
	if (display->rotation == 0 || display->rotation > 1){
		uint32_t tx = (swapbyte(x1) << 16) | swapbyte(x0);
		ili9163c_send_data(display, (uint8_t*)(&tx), 4); // (uint8_t*)(&x0)	
	} else {
		uint32_t tx = (swapbyte(x1 + __OFFSET) << 16) | swapbyte(x0 + __OFFSET);
		ili9163c_send_data(display, (uint8_t*)(&tx), 4);
	}

	ili9163c_send_cmd(display, CMD_PGEADRS); // Page
	if (display->rotation == 0){
		uint32_t ty = (swapbyte(y1 + __OFFSET) << 16) | swapbyte(y0 + __OFFSET);
		ili9163c_send_data(display, (uint8_t*)(&ty), 4); //		
	} else {	
		uint32_t ty = (swapbyte(y1) << 16) | swapbyte(y0);
		ili9163c_send_data(display, (uint8_t*)(&ty), 4);
	}
	
	ili9163c_send_cmd(display, CMD_RAMWR); //Into RAM
}


static void ili9163c_setAddr_dma(ili9163c_t *display, const uint8_t transNr, uint8_t *data, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
	// 0 byte offset
	ili9163c_send_cmd_dma(display, transNr, data, CMD_CLMADRS); // Column
	
	if (display->rotation == 0 || display->rotation > 1) {
		uint16_t *data16 = (uint16_t*)(data + 2);
		data16[1] = swapbyte(x0);
		data16[2] = swapbyte(x1);
		
		
		// +2 byte offset cause of userdata from command before
		ili9163c_send_data_dma(display, transNr + 1, (data + 2), sizeof(uint32_t));
	} else {
		uint16_t *data16 = (uint16_t*)(data + 2);
		data16[1] = swapbyte(x0 + __OFFSET);
		data16[2] = swapbyte(x1 + __OFFSET);
		
		// +2 byte offset cause of userdata from command before
		ili9163c_send_data_dma(display, transNr + 1, (data + 2), sizeof(uint32_t));
	}

	// +2 byte additional offset cause of userdata from data command before
	// +4 byte additional offset cause of data from data command before
	ili9163c_send_cmd_dma(display, transNr + 2, (data + (4 + sizeof(uint32_t))), CMD_PGEADRS); // Page
	
	if (display->rotation == 0){
		uint16_t *data16 = (uint16_t*)(data + (6 + sizeof(uint32_t)));
		data16[1] = swapbyte(y0 + __OFFSET);
		data16[2] = swapbyte(y1 + __OFFSET);
		
		
		// +2 byte offset cause of userdata from command before
		ili9163c_send_data_dma(display, transNr + 3, (data + (6 + sizeof(uint32_t))), sizeof(uint32_t));	
	} else {	
		uint16_t *data16 = (uint16_t*)(data + (6 + sizeof(uint32_t)));
		data16[1] = swapbyte(y0);
		data16[2] = swapbyte(y1);
		
		// +2 byte offset cause of userdata from command before
		ili9163c_send_data_dma(display, transNr + 3, (data + (6 + sizeof(uint32_t))), sizeof(uint32_t));	
	}
	
	// +2 byte additional offset cause of userdata from command before
	// +4 byte additional offset cause of data from data command before
	ili9163c_send_cmd_dma(display, transNr + 4, (data + (8 + 2*sizeof(uint32_t))), CMD_RAMWR); //Into RAM
}



//---------------------------------------------------------------------------------------------------------------------------
// 
// regular functions
// 
//---------------------------------------------------------------------------------------------------------------------------

ili9163c_t ili9163c_createDisplay(spi_host_device_t hostspi, gpio_num_t cspin, gpio_num_t dcpin, gpio_num_t rstpin, gpio_num_t bcklightpin) {
	ili9163c_t display;
	memset(&display, 0, sizeof(ili9163c_t));
	
	// spi stuff
	display.spiHost = hostspi;
	
	display.devcfg.clock_speed_hz	= 26000000;								// clock out at 26 MHz
	display.devcfg.mode				= 0;									// spi mode 0
	display.devcfg.spics_io_num		= cspin;								// cs pin
	display.devcfg.queue_size		= 13;									// we want to be able to queue 7 transactions at a time
	display.devcfg.pre_cb			= ili9163c_pre_transfer_callback_dma;	// specify pre-transfer callback to handle D/C line
	display.devcfg.post_cb			= ili9163c_post_transfer_callback_dma;	// specify post-transfer callback to give the transaction handle free
	display.devcfg.flags			= SPI_DEVICE_HALFDUPLEX;

	assert(spi_bus_add_device(hostspi, &display.devcfg, &display.spiDevice) == ESP_OK);
	
	for(int i = 0; i < 12; i++) memset(&(display.dmaBuffer[i]), 0, sizeof(spi_transaction_t));
	
	// non spi pins
	display.pinDC 		= dcpin;
	display.pinRst		= rstpin;
	display.pinBckLight	= bcklightpin;
	
    gpio_set_direction(display.pinDC , GPIO_MODE_OUTPUT);
    gpio_set_direction(display.pinRst, GPIO_MODE_OUTPUT);
    gpio_set_direction(display.pinBckLight, GPIO_MODE_OUTPUT);
	
	
	// general stuff
	display.maCtrlData 		= 0x00;
	display.colorSpaceData 	= __COLORSPC; //start with default data;
	
	
	display.width  = _TFTWIDTH;
	display.height = _TFTHEIGHT;

	return display;
}


void ili9163c_init(ili9163c_t *display) {
	uint8_t cmd = 0;
	
	// reset display
    gpio_set_level(display->pinRst, 0);
    vTaskDelay(100 / portTICK_RATE_MS);
	
    gpio_set_level(display->pinRst, 1);
    vTaskDelay(100 / portTICK_RATE_MS);
	
	
	// send init sequence
	while (init_cmds[cmd].databytes != 0xff)
	{
        ili9163c_send_cmd (display, init_cmds[cmd].cmd);
        ili9163c_send_data(display, init_cmds[cmd].data, init_cmds[cmd].databytes&0x1F);
		
        if (init_cmds[cmd].databytes&0x80) {
            vTaskDelay(100 / portTICK_RATE_MS);
        }
		
        cmd ++;
    }
	
	gpio_set_level(display->pinBckLight, 0);
}


// clear the screen
void ili9163c_clearScreen_dma(ili9163c_t *display, uint16_t color) {
	ili9163c_fillRect_dma(display, 0, 0, display->width, display->height, color);
}



// draw a rect on the screen
void ili9163c_fillRect_dma(ili9163c_t *display, uint16_t x0, uint16_t y0, uint16_t w, uint16_t h, uint16_t color) {
	uint8_t *databuffer = (uint8_t*)heap_caps_malloc(18 + 2 + 2*w*2 + 2, MALLOC_CAP_DMA);
	uint16_t *data16 	= (uint16_t*)(databuffer + 18 + 2);
	
	color = swapbyte(color);
	for(int i = 0; i <= w*2; i++) data16[i] = color;

	// 5 transactions are queued here
	ili9163c_setAddr_dma(display, 0, databuffer, x0, y0, x0 + w - 1, y0 + h - 1); // go home
	
	

	uint8_t totalTransactions = 5;
	uint8_t currentSlot	   	  = 5;
	
	
	ESP_LOGI("tft driver", "Sending data");
	while(h > 0) {
		while (display->dmaBuffer[currentSlot].user != NULL) {
			spi_transaction_t *rtrans;
			assert(spi_device_get_trans_result(display->spiDevice, &rtrans, portMAX_DELAY) == ESP_OK);
			totalTransactions --;
			ESP_LOGI("tft driver", "Freed transaction %d", currentSlot);
		}
		
		if(h > 2) {
			ESP_LOGI("tft driver", "Use transaction %d", currentSlot);
			ili9163c_send_data_dma(display, currentSlot, (databuffer + 20), 2*w*2);
			h -= 2;
		} else {
			ESP_LOGI("tft driver", "Use transaction %d", currentSlot);
			
			ili9163c_send_data_dma(display, currentSlot, (databuffer + 20), w*2);
			h --;
		}
		
		totalTransactions ++;
		ESP_LOGI("tft driver", "Transactions to wait for after %d", totalTransactions);
		
		currentSlot = (currentSlot + 1)%12;
	}
	
	// clear remaining transactions
	spi_transaction_t *rtrans;
	for(int i = 0; i < totalTransactions; i ++) {
		ESP_LOGI("tft driver", "Wait for Transaction %d to be finished", i);
		assert(spi_device_get_trans_result(display->spiDevice, &rtrans, portMAX_DELAY) == ESP_OK);
	}
	
	free(databuffer);
}









// get color code
uint16_t ili9163c_color565(uint8_t r, uint8_t g, uint8_t b) {
  return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}


//1552
Settings:

Code: Select all

#ifndef _TFT_ILI9163C_USETT_H_
#define _TFT_ILI9163C_USETT_H_





#if defined(__144_RED_PCB__)

	#define _TFTWIDTH  		128//the REAL W resolution of the TFT
	#define _TFTHEIGHT 		128//the REAL H resolution of the TFT
	#define _GRAMWIDTH      128
	#define _GRAMHEIGH      160//160
	#define _GRAMSIZE		_GRAMWIDTH * _GRAMHEIGH//*see note 1
	#define __COLORSPC		1// 1:GBR - 0:RGB
	#define __GAMMASET3		//uncomment for another gamma
	#define __OFFSET		32//*see note 2
	//Tested!
#elif defined (__144_BLACK_PCB__)
	#define _TFTWIDTH  		128//the REAL W resolution of the TFT
	#define _TFTHEIGHT 		160//128//the REAL H resolution of the TFT
	#define _GRAMWIDTH      128
	#define _GRAMHEIGH      160//128
	#define _GRAMSIZE		_GRAMWIDTH * _GRAMHEIGH//*see note 1
	#define __COLORSPC		1// 1:GBR - 0:RGB
	#define __GAMMASET1		//uncomment for another gamma
	#define __OFFSET		0
	//not tested
#elif defined (__22_RED_PCB__)

	#define _TFTWIDTH  		240//the REAL W resolution of the TFT
	#define _TFTHEIGHT 		320//the REAL H resolution of the TFT
	#define _GRAMWIDTH      240
	#define _GRAMHEIGH      320
	#define _GRAMSIZE		_GRAMWIDTH * _GRAMHEIGH
	#define __COLORSPC		1// 1:GBR - 0:RGB
	#define __GAMMASET1		//uncomment for another gamma
	#define __OFFSET		0
#else
	#define _TFTWIDTH  		128//128
	#define _TFTHEIGHT 		160//160
	#define _GRAMWIDTH      128
	#define _GRAMHEIGH      160
	#define _GRAMSIZE		_GRAMWIDTH * _GRAMHEIGH
	#define __COLORSPC		1// 1:GBR - 0:RGB
	#define __GAMMASET1
	#define __OFFSET		0
#endif

	//#if defined(__GAMMASET1)
	//	const uint8_t pGammaSet[15]= {0x36,0x29,0x12,0x22,0x1C,0x15,0x42,0xB7,0x2F,0x13,0x12,0x0A,0x11,0x0B,0x06};
	//	const uint8_t nGammaSet[15]= {0x09,0x16,0x2D,0x0D,0x13,0x15,0x40,0x48,0x53,0x0C,0x1D,0x25,0x2E,0x34,0x39};
	//#elif defined(__GAMMASET2)
	//	const uint8_t pGammaSet[15]= {0x3F,0x21,0x12,0x22,0x1C,0x15,0x42,0xB7,0x2F,0x13,0x02,0x0A,0x01,0x00,0x00};
	//	const uint8_t nGammaSet[15]= {0x09,0x18,0x2D,0x0D,0x13,0x15,0x40,0x48,0x53,0x0C,0x1D,0x25,0x2E,0x24,0x29};
	//#elif defined(__GAMMASET3)
	//	const uint8_t pGammaSet[15]= {0x3F,0x26,0x23,0x30,0x28,0x10,0x55,0xB7,0x40,0x19,0x10,0x1E,0x02,0x01,0x00};
	//	const uint8_t nGammaSet[15]= {0x09,0x18,0x2D,0x0D,0x13,0x15,0x40,0x48,0x53,0x0C,0x1D,0x25,0x2E,0x24,0x29};
	//#else
	//	const uint8_t pGammaSet[15]= {0x3F,0x25,0x1C,0x1E,0x20,0x12,0x2A,0x90,0x24,0x11,0x00,0x00,0x00,0x00,0x00};
	//	const uint8_t nGammaSet[15]= {0x20,0x20,0x20,0x20,0x05,0x15,0x00,0xA7,0x3D,0x18,0x25,0x2A,0x2B,0x2B,0x3A};
	//#endif
/*
	Note 1: The __144_RED_PCB__ display has hardware addressing of 128 x 160
	but the tft resolution it's 128 x 128 so the dram should be set correctly
	
	Note 2: This is the offset between image in RAM and TFT. In that case 160 - 128 = 32;
*/
#endif // _TFT_ILI9163C_USETT_H_
Registers

Code: Select all

#ifndef _TFT_ILI9163C_REG_H_
#define _TFT_ILI9163C_REG_H_


//ILI9163C registers-----------------------
#define CMD_NOP     	0x00//Non operation
#define CMD_SWRESET 	0x01//Soft Reset
#define CMD_SLPIN   	0x10//Sleep ON
#define CMD_SLPOUT  	0x11//Sleep OFF
#define CMD_PTLON   	0x12//Partial Mode ON
#define CMD_NORML   	0x13//Normal Display ON
#define CMD_DINVOF  	0x20//Display Inversion OFF
#define CMD_DINVON   	0x21//Display Inversion ON
#define CMD_GAMMASET 	0x26//Gamma Set (0x01[1],0x02[2],0x04[3],0x08[4])
#define CMD_DISPOFF 	0x28//Display OFF
#define CMD_DISPON  	0x29//Display ON
#define CMD_IDLEON  	0x39//Idle Mode ON
#define CMD_IDLEOF  	0x38//Idle Mode OFF
#define CMD_CLMADRS   	0x2A//Column Address Set
#define CMD_PGEADRS   	0x2B//Page Address Set

#define CMD_RAMWR   	0x2C//Memory Write
#define CMD_RAMRD   	0x2E//Memory Read
#define CMD_CLRSPACE   	0x2D//Color Space : 4K/65K/262K
#define CMD_PARTAREA	0x30//Partial Area
#define CMD_VSCLLDEF	0x33//Vertical Scroll Definition
#define CMD_TEFXLON		0x35//Tearing Effect Line ON
#define CMD_TEFXLOF		0x34//Tearing Effect Line OFF
#define CMD_MADCTL  	0x36//Memory Access Control
#define CMD_VSSTADRS	0x37//Vertical Scrolling Start address
#define CMD_PIXFMT  	0x3A//Interface Pixel Format
#define CMD_FRMCTR1 	0xB1//Frame Rate Control (In normal mode/Full colors)
#define CMD_FRMCTR2 	0xB2//Frame Rate Control(In Idle mode/8-colors)
#define CMD_FRMCTR3 	0xB3//Frame Rate Control(In Partial mode/full colors)
#define CMD_DINVCTR		0xB4//Display Inversion Control
#define CMD_RGBBLK		0xB5//RGB Interface Blanking Porch setting
#define CMD_DFUNCTR 	0xB6//Display Fuction set 5
#define CMD_SDRVDIR 	0xB7//Source Driver Direction Control
#define CMD_GDRVDIR 	0xB8//Gate Driver Direction Control 

#define CMD_PWCTR1  	0xC0//Power_Control1
#define CMD_PWCTR2  	0xC1//Power_Control2
#define CMD_PWCTR3  	0xC2//Power_Control3
#define CMD_PWCTR4  	0xC3//Power_Control4
#define CMD_PWCTR5  	0xC4//Power_Control5
#define CMD_VCOMCTR1  	0xC5//VCOM_Control 1
#define CMD_VCOMCTR2  	0xC6//VCOM_Control 2
#define CMD_VCOMOFFS  	0xC7//VCOM Offset Control
#define CMD_PGAMMAC		0xE0//Positive Gamma Correction Setting
#define CMD_NGAMMAC		0xE1//Negative Gamma Correction Setting
#define CMD_GAMRSEL		0xF2//GAM_R_SEL


#endif

ESP_Sprite
Posts: 9020
Joined: Thu Nov 26, 2015 4:08 am

Re: SPI DMA Delays

Postby ESP_Sprite » Thu Oct 05, 2017 11:08 am

Not sure, something you did made the memory allocator unhappy; maybe a pointer that points wrong somewhere.

What I mean is essentially like this (for 3 transactions):
Fill transaction 1
Queue transaction 1
Fill transaction 2
Queue transaction 2
Fill transaction 2
Queue transaction 3
Get result for transaction 1
Re-fill transaction 1
Queue transaction 1
Get result for transaction 2
Re-fill transaction 2
Queue transaction 2
Get result for transaction 3
Re-fill transaction 3
Queue transaction 3
Get result for transaction 1
Re-fill transaction 1
Queue transaction 1
Get result for transaction 2
Re-fill transaction 2
Queue transaction 2
...

Essentially, you always queue a transaction after getting the result of the transaction first. The only exceptions are the first time you use a transaction: because there's no result yet, you can't get the result there. No need to see if a transaction is 'in use'; because of the linear nature of SPI transactions, you can just use them all one after another in the same pattern. All you need is a marker to see if a transaction has not been used ever yet, and you're done.

Addi2438
Posts: 13
Joined: Thu Sep 14, 2017 11:23 am

Re: SPI DMA Delays

Postby Addi2438 » Thu Oct 05, 2017 12:18 pm

Ok this makes sense.

So I do have to get the result of a transaction before I can reuse it?

I gonna try that out this evening.

When it is right that getting the transaction result using
spi_device_get_trans_result results in a task switch then it would make it even slower because then
after each queueing it would do a taskswitch.
Gonna try and scope that tonight.

ESP_Sprite
Posts: 9020
Joined: Thu Nov 26, 2015 4:08 am

Re: SPI DMA Delays

Postby ESP_Sprite » Fri Oct 06, 2017 3:27 am

As far as I know, it'll only do a task switch if the transaction is not done yet; it swaps out while waiting to let other tasks do stuff.

Addi2438
Posts: 13
Joined: Thu Sep 14, 2017 11:23 am

Re: SPI DMA Delays

Postby Addi2438 » Tue Oct 10, 2017 8:17 pm

Again sorry for the longer delay,

I have tried it out and it seems to work.

Image

No longer delay between the transmission
only the smaller 10µSec delay during the queueing.

The code should be "super fast" now :D.

However the flickering is still there.
Maybe its because I am filling the GBuffer of the display out of synch.
Gonna need to figure that out and compare the transmission with some of the Arduino Drivers for the Arduino Uno.


As a last question I wanted to ask if it is necessary for freeRTOS to do a taskswitch to the IDLE task from time to time.
If so after what amount of time? I remember having read something about that.

Reason for this question is I did rewrote the functions like you suggested and had the problem that after drawing a rect the esp crashed when I wanted to draw an other without wainting a periode of time using "vTaskDelay()".
This happend for smaller rects only below 10MHz SPI above it worked fine.
When I wanted to clear the whole screen the esp crashed too when I want to draw afterwards without a delay.
This time even with 30MHz SPI.

I solved this problem by simply waiting for every transmision that was not finished yet after the data of a whole primitive was send.
Similar to my first attempt but this time I wait for the single transaction to be send if the transaction was used before while sending
and after everything is done I wait for all transaction.


Edit:

I think I know what happened.
At the end of the function for drawing the primitives I free the used memory.
If DMA is not fast enough it will end up in trying to send freed memory which then causes the crash (abort()) I belive.

Hmm need to find a solution for that.


Thanks for everyones help.

ESP_Sprite
Posts: 9020
Joined: Thu Nov 26, 2015 4:08 am

Re: SPI DMA Delays

Postby ESP_Sprite » Wed Oct 11, 2017 2:03 am

Yes, that is necessary. Not very often, however, I think the task WDT that catches the program not doing this is set to 5 seconds or so. Be aware that you may also inadvertently starve other, non-WDT-watched, tasks, though. Not sure if that's much of an issue in what you're doing though.

Who is online

Users browsing this forum: Bing [Bot], Phillip and 152 guests