From d0ec8c8bb897f612400c2abd5895d8a77878d557 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Wed, 13 Mar 2019 19:33:20 +0000 Subject: [PATCH 01/52] Change init for KeDei 3.5" 480x320 V6.3 2018/4/9 --- mpi3501.cpp | 287 ++++++++++++++++++++++++++++------------------------ 1 file changed, 152 insertions(+), 135 deletions(-) diff --git a/mpi3501.cpp b/mpi3501.cpp index 67178a1..51a6fff 100644 --- a/mpi3501.cpp +++ b/mpi3501.cpp @@ -1,135 +1,152 @@ -#include "config.h" - -#ifdef MPI3501 - -#include "spi.h" - -#include -#include - -void ChipSelectHigh() -{ - WAIT_SPI_FINISHED(); - CLEAR_GPIO(GPIO_SPI0_CE0); // Enable Touch - SET_GPIO(GPIO_SPI0_CE0); // Disable Touch - __sync_synchronize(); - SET_GPIO(GPIO_SPI0_CE1); // Disable Display - CLEAR_GPIO(GPIO_SPI0_CE1); // Enable Display - __sync_synchronize(); -} - -void InitKeDeiV63() -{ - // If a Reset pin is defined, toggle it briefly high->low->high to enable the device. Some devices do not have a reset pin, in which case compile with GPIO_TFT_RESET_PIN left undefined. -#if defined(GPIO_TFT_RESET_PIN) && GPIO_TFT_RESET_PIN >= 0 - printf("Resetting display at reset GPIO pin %d\n", GPIO_TFT_RESET_PIN); - SET_GPIO_MODE(GPIO_TFT_RESET_PIN, 1); - SET_GPIO(GPIO_TFT_RESET_PIN); - usleep(120 * 1000); - CLEAR_GPIO(GPIO_TFT_RESET_PIN); - usleep(120 * 1000); - SET_GPIO(GPIO_TFT_RESET_PIN); - usleep(120 * 1000); -#endif - - // For sanity, start with both Chip selects high to ensure that the display will see a high->low enable transition when we start. - SET_GPIO(GPIO_SPI0_CE0); // Disable Touch - SET_GPIO(GPIO_SPI0_CE1); // Disable Display - usleep(1000); - - // Do the initialization with a very low SPI bus speed, so that it will succeed even if the bus speed chosen by the user is too high. - spi->clk = 34; - __sync_synchronize(); - - BEGIN_SPI_COMMUNICATION(); - { - CLEAR_GPIO(GPIO_SPI0_CE0); // Enable Touch - CLEAR_GPIO(GPIO_SPI0_CE1); // Enable Display - - BEGIN_SPI_COMMUNICATION(); - - usleep(25*1000); - - SET_GPIO(GPIO_SPI0_CE0); // Disable Touch - usleep(25*1000); - - SPI_TRANSFER(0x00000000); // This command seems to be Reset - usleep(120*1000); - - SPI_TRANSFER(0x00000100); - usleep(50*1000); - SPI_TRANSFER(0x00001100); - usleep(60*1000); - - SPI_TRANSFER(0xB9001100, 0x00, 0xFF, 0x00, 0x83, 0x00, 0x57); - usleep(5*1000); - - SPI_TRANSFER(0xB6001100, 0x00, 0x2C); - SPI_TRANSFER(0x11001100/*Sleep Out*/); - usleep(150*1000); - - SPI_TRANSFER(0x3A001100/*Interface Pixel Format*/, 0x00, 0x55); - SPI_TRANSFER(0xB0001100, 0x00, 0x68); - SPI_TRANSFER(0xCC001100, 0x00, 0x09); - SPI_TRANSFER(0xB3001100, 0x00, 0x43, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06); - SPI_TRANSFER(0xB1001100, 0x00, 0x00, 0x00, 0x15, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x83, 0x00, 0x44); - SPI_TRANSFER(0xC0001100, 0x00, 0x24, 0x00, 0x24, 0x00, 0x01, 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x08); - SPI_TRANSFER(0xB4001100, 0x00, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x2A, 0x00, 0x0D, 0x00, 0x4F); - SPI_TRANSFER(0xE0001100, 0x00, 0x02, 0x00, 0x08, 0x00, 0x11, 0x00, 0x23, 0x00, 0x2C, 0x00, 0x40, 0x00, 0x4A, 0x00, 0x52, 0x00, 0x48, 0x00, 0x41, 0x00, 0x3C, 0x00, 0x33, 0x00, 0x2E, 0x00, 0x28, 0x00, 0x27, 0x00, 0x1B, 0x00, 0x02, 0x00, 0x08, 0x00, 0x11, 0x00, 0x23, 0x00, 0x2C, 0x00, 0x40, 0x00, 0x4A, 0x00, 0x52, 0x00, 0x48, 0x00, 0x41, 0x00, 0x3C, 0x00, 0x33, 0x00, 0x2E, 0x00, 0x28, 0x00, 0x27, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x01); - -#define MADCTL_BGR_PIXEL_ORDER (1<<3) -#define MADCTL_ROW_COLUMN_EXCHANGE (1<<5) -#define MADCTL_COLUMN_ADDRESS_ORDER_SWAP (1<<6) -#define MADCTL_ROW_ADDRESS_ORDER_SWAP (1<<7) -#define MADCTL_ROTATE_180_DEGREES (MADCTL_COLUMN_ADDRESS_ORDER_SWAP | MADCTL_ROW_ADDRESS_ORDER_SWAP) - - uint8_t madctl = 0; -#ifndef DISPLAY_SWAP_BGR - madctl |= MADCTL_BGR_PIXEL_ORDER; -#endif -#if defined(DISPLAY_FLIP_ORIENTATION_IN_HARDWARE) - madctl |= MADCTL_ROW_COLUMN_EXCHANGE; -#endif -#ifdef DISPLAY_ROTATE_180_DEGREES - madctl ^= MADCTL_ROTATE_180_DEGREES; -#endif - SPI_TRANSFER(0x36001100/*MADCTL: Memory Access Control*/, 0x00, madctl); - - SPI_TRANSFER(0x29001100/*Display ON*/); - - usleep(200*1000); - - ClearScreen(); - } -#ifndef USE_DMA_TRANSFERS // For DMA transfers, keep SPI CS & TA active. - END_SPI_COMMUNICATION(); -#endif - - // And speed up to the desired operation speed finally after init is done. - usleep(10 * 1000); // Delay a bit before restoring CLK, or otherwise this has been observed to cause the display not init if done back to back after the clear operation above. - spi->clk = SPI_BUS_CLOCK_DIVISOR; -} - -void TurnBacklightOff() -{ -} - -void TurnBacklightOn() -{ -} - -void TurnDisplayOff() -{ -} - -void TurnDisplayOn() -{ -} - -void DeinitSPIDisplay() -{ - ClearScreen(); - TurnDisplayOff(); -} - -#endif +#include "config.h" + +#ifdef MPI3501 + +#include "spi.h" + +#include +#include + +void ChipSelectHigh() +{ + WAIT_SPI_FINISHED(); + CLEAR_GPIO(GPIO_SPI0_CE0); // Enable Touch + SET_GPIO(GPIO_SPI0_CE0); // Disable Touch + __sync_synchronize(); + SET_GPIO(GPIO_SPI0_CE1); // Disable Display + CLEAR_GPIO(GPIO_SPI0_CE1); // Enable Display + __sync_synchronize(); +} + +void InitKeDeiV63() +{ + // If a Reset pin is defined, toggle it briefly high->low->high to enable the device. Some devices do not have a reset pin, in which case compile with GPIO_TFT_RESET_PIN left undefined. +#if defined(GPIO_TFT_RESET_PIN) && GPIO_TFT_RESET_PIN >= 0 + printf("Resetting display at reset GPIO pin %d\n", GPIO_TFT_RESET_PIN); + SET_GPIO_MODE(GPIO_TFT_RESET_PIN, 1); + SET_GPIO(GPIO_TFT_RESET_PIN); + usleep(120 * 1000); + CLEAR_GPIO(GPIO_TFT_RESET_PIN); + usleep(120 * 1000); + SET_GPIO(GPIO_TFT_RESET_PIN); + usleep(120 * 1000); +#endif + + // For sanity, start with both Chip selects high to ensure that the display will see a high->low enable transition when we start. + SET_GPIO(GPIO_SPI0_CE0); // Disable Touch + SET_GPIO(GPIO_SPI0_CE1); // Disable Display + usleep(1000); + + // Do the initialization with a very low SPI bus speed, so that it will succeed even if the bus speed chosen by the user is too high. + spi->clk = 34; + __sync_synchronize(); + + BEGIN_SPI_COMMUNICATION(); + { + CLEAR_GPIO(GPIO_SPI0_CE0); // Enable Touch + CLEAR_GPIO(GPIO_SPI0_CE1); // Enable Display + + BEGIN_SPI_COMMUNICATION(); + + usleep(25*1000); + + SET_GPIO(GPIO_SPI0_CE0); // Disable Touch + usleep(25*1000); + + SPI_TRANSFER(0x00001100); // Oy! Now this is a reset + usleep(10*1000); + SPI_TRANSFER(0xff001100); + SPI_TRANSFER(0xff001100); + usleep(10*1000); + SPI_TRANSFER(0xff001100); + SPI_TRANSFER(0xff001100); + SPI_TRANSFER(0xff001100); + SPI_TRANSFER(0xff001100); + usleep(15*1000); + SPI_TRANSFER(0x11001100/*Sleep Out*/); + usleep(150*1000); + + SPI_TRANSFER(0xB0001100, 0x00, 0x00); // CLK 30Hz? + SPI_TRANSFER(0xB3001100, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); // set RGB? + SPI_TRANSFER(0xB9001100, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0f); // ext cmd? + SPI_TRANSFER(0xC0001100, 0x00, 0x13, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x02 + , 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x43); // Timing? + SPI_TRANSFER(0xC1001100, 0x00, 0x08, 0x00, 0x0F, 0x00, 0x08, 0x00, 0x08); // gamma? + SPI_TRANSFER(0xC4001100, 0x00, 0x11, 0x00, 0x07, 0x00, 0x03, 0x00, 0x04); // sleep modes? + SPI_TRANSFER(0xC6001100, 0x00, 0x00); // ? + SPI_TRANSFER(0xC8001100, 0x00, 0x03, 0x00, 0x03, 0x00, 0x13, 0x00, 0x5C + , 0x00, 0x03, 0x00, 0x07, 0x00, 0x14, 0x00, 0x08 + , 0x00, 0x00, 0x00, 0x21, 0x00, 0x08, 0x00, 0x14 + , 0x00, 0x07, 0x00, 0x53, 0x00, 0x0C, 0x00, 0x13 + , 0x00, 0x03, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00); // ? + SPI_TRANSFER(0x35001100, 0x00, 0x00); + +#define MADCTL_BGR_PIXEL_ORDER (1<<3) +#define MADCTL_ROW_COLUMN_EXCHANGE (1<<5) +#define MADCTL_COLUMN_ADDRESS_ORDER_SWAP (1<<6) +#define MADCTL_ROW_ADDRESS_ORDER_SWAP (1<<7) +#define MADCTL_ROTATE_180_DEGREES (MADCTL_COLUMN_ADDRESS_ORDER_SWAP | MADCTL_ROW_ADDRESS_ORDER_SWAP) + + uint8_t madctl = 0; +#ifndef DISPLAY_SWAP_BGR + madctl |= MADCTL_BGR_PIXEL_ORDER; +#endif +#if defined(DISPLAY_FLIP_ORIENTATION_IN_HARDWARE) + madctl |= MADCTL_ROW_COLUMN_EXCHANGE; +#endif +#ifdef DISPLAY_ROTATE_180_DEGREES + madctl ^= MADCTL_ROTATE_180_DEGREES; +#endif + //SPI_TRANSFER(0x36001100/*MADCTL: Memory Access Control*/, 0x00, madctl); + SPI_TRANSFER(0x36001100/*MADCTL: Memory Access Control*/, 0x00, 0x60); + + SPI_TRANSFER(0x3A001100/*Interface Pixel Format*/, 0x00, 0x55); + SPI_TRANSFER(0x44001100, 0x00, 0x00, 0x00, 0x01); + SPI_TRANSFER(0xD0001100, 0x00, 0x07, 0x00, 0x07, 0x00, 0x1D, 0x00, 0x03); // ? + SPI_TRANSFER(0xD1001100, 0x00, 0x03, 0x00, 0x30, 0x00, 0x10); // ? + SPI_TRANSFER(0xD2001100, 0x00, 0x03, 0x00, 0x14, 0x00, 0x04); // ? + SPI_TRANSFER(0x29001100/*Display ON*/); + + usleep(30*1000); + + SPI_TRANSFER(0x2A001100, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x3F); // ? + SPI_TRANSFER(0x2B001100, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xE0); // ? + SPI_TRANSFER(0xB4001100, 0x00, 0x00); + SPI_TRANSFER(0x2C001100); // x-fer mcu to frame memory? + + usleep(10*1000); + + ClearScreen(); + } +#ifndef USE_DMA_TRANSFERS // For DMA transfers, keep SPI CS & TA active. + END_SPI_COMMUNICATION(); +#endif + + // And speed up to the desired operation speed finally after init is done. + usleep(10 * 1000); // Delay a bit before restoring CLK, or otherwise this has been observed to cause the display not init if done back to back after the clear operation above. + spi->clk = SPI_BUS_CLOCK_DIVISOR; +} + +void TurnBacklightOff() +{ +} + +void TurnBacklightOn() +{ +} + +void TurnDisplayOff() +{ +} + +void TurnDisplayOn() +{ +} + +void DeinitSPIDisplay() +{ + ClearScreen(); + TurnDisplayOff(); +} + +#endif From 756190d39ca01e168a1faa36276308e3f0213861 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Wed, 27 Mar 2019 03:41:10 +0000 Subject: [PATCH 02/52] apply suggested changes of juj --- mpi3501.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/mpi3501.cpp b/mpi3501.cpp index 51a6fff..6de4ba8 100644 --- a/mpi3501.cpp +++ b/mpi3501.cpp @@ -53,7 +53,7 @@ void InitKeDeiV63() SET_GPIO(GPIO_SPI0_CE0); // Disable Touch usleep(25*1000); - SPI_TRANSFER(0x00001100); // Oy! Now this is a reset + SPI_TRANSFER(0x00001100); // Reset usleep(10*1000); SPI_TRANSFER(0xff001100); SPI_TRANSFER(0xff001100); @@ -82,10 +82,11 @@ void InitKeDeiV63() SPI_TRANSFER(0x35001100, 0x00, 0x00); #define MADCTL_BGR_PIXEL_ORDER (1<<3) +#define MADCTL_LINE_ADDRESS_ORDER_SWAP (1<<4) #define MADCTL_ROW_COLUMN_EXCHANGE (1<<5) #define MADCTL_COLUMN_ADDRESS_ORDER_SWAP (1<<6) #define MADCTL_ROW_ADDRESS_ORDER_SWAP (1<<7) -#define MADCTL_ROTATE_180_DEGREES (MADCTL_COLUMN_ADDRESS_ORDER_SWAP | MADCTL_ROW_ADDRESS_ORDER_SWAP) +#define MADCTL_ROTATE_180_DEGREES ( MADCTL_ROW_ADDRESS_ORDER_SWAP ) uint8_t madctl = 0; #ifndef DISPLAY_SWAP_BGR @@ -97,8 +98,7 @@ void InitKeDeiV63() #ifdef DISPLAY_ROTATE_180_DEGREES madctl ^= MADCTL_ROTATE_180_DEGREES; #endif - //SPI_TRANSFER(0x36001100/*MADCTL: Memory Access Control*/, 0x00, madctl); - SPI_TRANSFER(0x36001100/*MADCTL: Memory Access Control*/, 0x00, 0x60); + SPI_TRANSFER(0x36001100/*MADCTL: Memory Access Control*/, 0x00, madctl); SPI_TRANSFER(0x3A001100/*Interface Pixel Format*/, 0x00, 0x55); SPI_TRANSFER(0x44001100, 0x00, 0x00, 0x00, 0x01); @@ -109,10 +109,7 @@ void InitKeDeiV63() usleep(30*1000); - SPI_TRANSFER(0x2A001100, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x3F); // ? - SPI_TRANSFER(0x2B001100, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xE0); // ? - SPI_TRANSFER(0xB4001100, 0x00, 0x00); - SPI_TRANSFER(0x2C001100); // x-fer mcu to frame memory? + SPI_TRANSFER(0xB4001100, 0x00, 0x00); // ? usleep(10*1000); From 710fcf48993c58dafccfbc61fb6f058ce432a137 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Sat, 11 May 2019 17:35:05 +0000 Subject: [PATCH 03/52] split spi user and kernel parts --- spi_kernel.h | 7 ++ spi_user.h | 310 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 317 insertions(+) create mode 100644 spi_kernel.h create mode 100644 spi_user.h diff --git a/spi_kernel.h b/spi_kernel.h new file mode 100644 index 0000000..530fc1c --- /dev/null +++ b/spi_kernel.h @@ -0,0 +1,7 @@ +#pragma once + +#include "spi.h" + +//#include +//#include +#define VIRT_TO_BUS(ptr) ((uintptr_t)(ptr) | 0xC0000000U) diff --git a/spi_user.h b/spi_user.h new file mode 100644 index 0000000..92901b4 --- /dev/null +++ b/spi_user.h @@ -0,0 +1,310 @@ +#pragma once + +#include +#include // FUTEX_WAKE +#include +#include "dma.h" +#include "spi.h" +#include "display.h" +#include "tick.h" +#include "display.h" + +extern volatile void *bcm2835; + +typedef struct GPIORegisterFile +{ + uint32_t gpfsel[6], reserved0; // GPIO Function Select registers, 3 bits per pin, 10 pins in an uint32_t + uint32_t gpset[2], reserved1; // GPIO Pin Output Set registers, write a 1 to bit at index I to set the pin at index I high + uint32_t gpclr[2], reserved2; // GPIO Pin Output Clear registers, write a 1 to bit at index I to set the pin at index I low + uint32_t gplev[2]; +} GPIORegisterFile; +extern volatile GPIORegisterFile *gpio; + +#define SET_GPIO_MODE(pin, mode) gpio->gpfsel[(pin)/10] = (gpio->gpfsel[(pin)/10] & ~(0x7 << ((pin) % 10) * 3)) | ((mode) << ((pin) % 10) * 3) +#define GET_GPIO_MODE(pin) ((gpio->gpfsel[(pin)/10] & (0x7 << ((pin) % 10) * 3)) >> (((pin) % 10) * 3)) +#define GET_GPIO(pin) (gpio->gplev[0] & (1 << (pin))) // Pin must be (0-31) +#define SET_GPIO(pin) gpio->gpset[0] = 1 << (pin) // Pin must be (0-31) +#define CLEAR_GPIO(pin) gpio->gpclr[0] = 1 << (pin) // Pin must be (0-31) + +typedef struct SPIRegisterFile +{ + uint32_t cs; // SPI Master Control and Status register + uint32_t fifo; // SPI Master TX and RX FIFOs + uint32_t clk; // SPI Master Clock Divider + uint32_t dlen; // SPI Master Number of DMA Bytes to Write +} SPIRegisterFile; +extern volatile SPIRegisterFile *spi; + +// Defines the size of the SPI task memory buffer in bytes. This memory buffer can contain two frames worth of tasks at maximum, +// so for best performance, should be at least ~DISPLAY_WIDTH*DISPLAY_HEIGHT*BYTES_PER_PIXEL*2 bytes in size, plus some small +// amount for structuring each SPITask command. Technically this can be something very small, like 4096b, and not need to contain +// even a single full frame of data, but such small buffers can cause performance issues from threads starving. +#define SHARED_MEMORY_SIZE (DISPLAY_DRAWABLE_WIDTH*DISPLAY_DRAWABLE_HEIGHT*SPI_BYTESPERPIXEL*3) +#define SPI_QUEUE_SIZE (SHARED_MEMORY_SIZE - sizeof(SharedMemory)) + +#if defined(SPI_3WIRE_DATA_COMMAND_FRAMING_BITS) && SPI_3WIRE_DATA_COMMAND_FRAMING_BITS == 1 +// Need a byte of padding for 8-bit -> 9-bit expansion for performance +#define SPI_9BIT_TASK_PADDING_BYTES 1 +#else +#define SPI_9BIT_TASK_PADDING_BYTES 0 +#endif + +// Defines the maximum size of a single SPI task, in bytes. This excludes the command byte. If MAX_SPI_TASK_SIZE +// is not defined, there is no length limit that applies. (In ALL_TASKS_SHOULD_DMA version of DMA transfer, +// there is DMA chaining, so SPI tasks can be arbitrarily long) +#ifndef ALL_TASKS_SHOULD_DMA +#define MAX_SPI_TASK_SIZE 65528 +#endif + +typedef struct __attribute__((packed)) SPITask +{ + uint32_t size; // Size, including both 8-bit and 9-bit tasks +#ifdef SPI_3WIRE_PROTOCOL + uint32_t sizeExpandedTaskWithPadding; // Size of the expanded 9-bit/32-bit task. The expanded task starts at address spiTask->data + spiTask->size - spiTask->sizeExpandedTaskWithPadding; +#endif +#ifdef SPI_32BIT_COMMANDS + uint32_t cmd; +#else + uint8_t cmd; +#endif + uint32_t dmaSpiHeader; +#ifdef OFFLOAD_PIXEL_COPY_TO_DMA_CPP + uint8_t *fb; + uint8_t *prevFb; + uint16_t width; +#endif + uint8_t data[]; // Contains both 8-bit and 9-bit tasks back to back, 8-bit first, then 9-bit. + +#ifdef SPI_3WIRE_PROTOCOL + inline uint8_t *PayloadStart() { return data + (size - sizeExpandedTaskWithPadding); } + inline uint8_t *PayloadEnd() { return data + (size - SPI_9BIT_TASK_PADDING_BYTES); } + inline uint32_t PayloadSize() const { return sizeExpandedTaskWithPadding - SPI_9BIT_TASK_PADDING_BYTES; } + inline uint32_t *DmaSpiHeaderAddress() { return (uint32_t*)(PayloadStart()-4); } +#else + inline uint8_t *PayloadStart() { return data; } + inline uint8_t *PayloadEnd() { return data + size; } + inline uint32_t PayloadSize() const { return size; } + inline uint32_t *DmaSpiHeaderAddress() { return &dmaSpiHeader; } +#endif + +} SPITask; + +#define BEGIN_SPI_COMMUNICATION() do { spi->cs = BCM2835_SPI0_CS_TA | DISPLAY_SPI_DRIVE_SETTINGS; } while(0) +#define END_SPI_COMMUNICATION() do { \ + uint32_t cs; \ + while (!(((cs = spi->cs) ^ BCM2835_SPI0_CS_TA) & (BCM2835_SPI0_CS_DONE | BCM2835_SPI0_CS_TA))) /* While TA=1 and DONE=0*/ \ + { \ + if ((cs & (BCM2835_SPI0_CS_RXR | BCM2835_SPI0_CS_RXF))) \ + spi->cs = BCM2835_SPI0_CS_CLEAR_RX | BCM2835_SPI0_CS_TA | DISPLAY_SPI_DRIVE_SETTINGS; \ + } \ + spi->cs = BCM2835_SPI0_CS_CLEAR_RX | DISPLAY_SPI_DRIVE_SETTINGS; /* Clear TA and any pending bytes */ \ + } while(0) + +#define WAIT_SPI_FINISHED() do { \ + uint32_t cs; \ + while (!((cs = spi->cs) & BCM2835_SPI0_CS_DONE)) /* While DONE=0*/ \ + { \ + if ((cs & (BCM2835_SPI0_CS_RXR | BCM2835_SPI0_CS_RXF))) \ + spi->cs = BCM2835_SPI0_CS_CLEAR_RX | BCM2835_SPI0_CS_TA | DISPLAY_SPI_DRIVE_SETTINGS; \ + } \ + } while(0) + + +// A convenience for defining and dispatching SPI task bytes inline +#define SPI_TRANSFER(command, ...) do { \ + char data_buffer[] = { __VA_ARGS__ }; \ + SPITask *t = AllocTask(sizeof(data_buffer)); \ + t->cmd = (command); \ + memcpy(t->data, data_buffer, sizeof(data_buffer)); \ + CommitTask(t); \ + RunSPITask(t); \ + DoneTask(t); \ + } while(0) + +#define QUEUE_SPI_TRANSFER(command, ...) do { \ + char data_buffer[] = { __VA_ARGS__ }; \ + SPITask *t = AllocTask(sizeof(data_buffer)); \ + t->cmd = (command); \ + memcpy(t->data, data_buffer, sizeof(data_buffer)); \ + CommitTask(t); \ + } while(0) + +#ifdef DISPLAY_SPI_BUS_IS_16BITS_WIDE // For displays that have their command register set be 16 bits word size width (ILI9486) + +#define QUEUE_MOVE_CURSOR_TASK(cursor, pos) do { \ + SPITask *task = AllocTask(4); \ + task->cmd = (cursor); \ + task->data[0] = 0; \ + task->data[1] = (pos) >> 8; \ + task->data[2] = 0; \ + task->data[3] = (pos) & 0xFF; \ + bytesTransferred += 6; \ + CommitTask(task); \ + } while(0) + +#define QUEUE_SET_WRITE_WINDOW_TASK(cursor, x, endX) do { \ + SPITask *task = AllocTask(8); \ + task->cmd = (cursor); \ + task->data[0] = 0; \ + task->data[1] = (x) >> 8; \ + task->data[2] = 0; \ + task->data[3] = (x) & 0xFF; \ + task->data[4] = 0; \ + task->data[5] = (endX) >> 8; \ + task->data[6] = 0; \ + task->data[7] = (endX) & 0xFF; \ + bytesTransferred += 10; \ + CommitTask(task); \ + } while(0) + +#elif defined(DISPLAY_SET_CURSOR_IS_8_BIT) // For displays that have their set cursor commands be a uint8 instead of uint16 (SSD1351) + +#define QUEUE_SET_WRITE_WINDOW_TASK(cursor, x, endX) do { \ + SPITask *task = AllocTask(2); \ + task->cmd = (cursor); \ + task->data[0] = (x); \ + task->data[1] = (endX); \ + bytesTransferred += 3; \ + CommitTask(task); \ + } while(0) + +#else // Regular 8-bit interface with 16bits wide set cursor commands (most displays) + +#define QUEUE_MOVE_CURSOR_TASK(cursor, pos) do { \ + SPITask *task = AllocTask(2); \ + task->cmd = (cursor); \ + task->data[0] = (pos) >> 8; \ + task->data[1] = (pos) & 0xFF; \ + bytesTransferred += 3; \ + CommitTask(task); \ + } while(0) + +#define QUEUE_SET_WRITE_WINDOW_TASK(cursor, x, endX) do { \ + SPITask *task = AllocTask(4); \ + task->cmd = (cursor); \ + task->data[0] = (x) >> 8; \ + task->data[1] = (x) & 0xFF; \ + task->data[2] = (endX) >> 8; \ + task->data[3] = (endX) & 0xFF; \ + bytesTransferred += 5; \ + CommitTask(task); \ + } while(0) +#endif + +#ifdef STATISTICS +extern volatile uint64_t spiThreadIdleUsecs; +extern volatile uint64_t spiThreadSleepStartTime; +extern volatile int spiThreadSleeping; +#endif + +#ifdef SPI_3WIRE_PROTOCOL + +// Converts the given SPI task in-place from an 8-bit task to a 9-bit task. +void Interleave8BitSPITaskTo9Bit(SPITask *task); + +// Converts the given SPI task in-place from a 16-bit task to a 32-bit task. +void Interleave16BitSPITaskTo32Bit(SPITask *task); + +// If the given display is a 3-wire SPI display (9 bits/task instead of 8 bits/task), this function computes the byte size of the 8-bit task when it is converted to a 9-bit task. +uint32_t NumBytesNeededFor9BitSPITask(uint32_t byteSizeFor8BitTask); + +// If the given display is a 3-wire SPI display with 32 bits bus width, this function computes the byte size of the task when it is converted to a 32-bit task. +uint32_t NumBytesNeededFor32BitSPITask(uint32_t byteSizeFor8BitTask); + +#endif + +static inline SPITask *AllocTask(uint32_t bytes) // Returns a pointer to a new SPI task block, called on main thread +{ +#ifdef SPI_3WIRE_PROTOCOL + // For 3-wire/9-bit tasks, store the converted task right at the end of the 8-bit task. +#ifdef SPI_32BIT_COMMANDS + uint32_t sizeExpandedTaskWithPadding = NumBytesNeededFor32BitSPITask(bytes) + SPI_9BIT_TASK_PADDING_BYTES; +#else + uint32_t sizeExpandedTaskWithPadding = NumBytesNeededFor9BitSPITask(bytes) + SPI_9BIT_TASK_PADDING_BYTES; +#endif + bytes += sizeExpandedTaskWithPadding; +#else +// const uint32_t totalBytesFor9BitTask = 0; +#endif + + uint32_t bytesToAllocate = sizeof(SPITask) + bytes;// + totalBytesFor9BitTask; + uint32_t tail = spiTaskMemory->queueTail; + uint32_t newTail = tail + bytesToAllocate; + // Is the new task too large to write contiguously into the ring buffer, that it's split into two parts? We never split, + // but instead write a sentinel at the end of the ring buffer, and jump the tail back to the beginning of the buffer and + // allocate the new task there. However in doing so, we must make sure that we don't write over the head marker. + if (newTail + sizeof(SPITask)/*Add extra SPITask size so that there will always be room for eob marker*/ >= SPI_QUEUE_SIZE) + { + uint32_t head = spiTaskMemory->queueHead; + // Write a sentinel, but wait for the head to advance first so that it is safe to write. + while(head > tail || head == 0/*Head must move > 0 so that we don't stomp on it*/) + { + head = spiTaskMemory->queueHead; + } + SPITask *endOfBuffer = (SPITask*)(spiTaskMemory->buffer + tail); + endOfBuffer->cmd = 0; // Use cmd=0x00 to denote "end of buffer, wrap to beginning" + __sync_synchronize(); + spiTaskMemory->queueTail = 0; + __sync_synchronize(); + if (spiTaskMemory->queueHead == tail) syscall(SYS_futex, &spiTaskMemory->queueTail, FUTEX_WAKE, 1, 0, 0, 0); // Wake the SPI thread if it was sleeping to get new tasks + tail = 0; + newTail = bytesToAllocate; + } + + // If the SPI task queue is full, wait for the SPI thread to process some tasks. This throttles the main thread to not run too fast. + uint32_t head = spiTaskMemory->queueHead; + while(head > tail && head <= newTail) + { + usleep(100); // Since the SPI queue is full, we can afford to sleep a bit on the main thread without introducing lag. + head = spiTaskMemory->queueHead; + } + + SPITask *task = (SPITask*)(spiTaskMemory->buffer + tail); + task->size = bytes; +#ifdef SPI_3WIRE_PROTOCOL + task->sizeExpandedTaskWithPadding = sizeExpandedTaskWithPadding; +#endif +#ifdef OFFLOAD_PIXEL_COPY_TO_DMA_CPP + task->fb = &task->data[0]; + task->prevFb = 0; +#endif + return task; +} + +static inline void CommitTask(SPITask *task) // Advertises the given SPI task from main thread to worker, called on main thread +{ +#ifdef SPI_3WIRE_PROTOCOL +#ifdef SPI_32BIT_COMMANDS + Interleave16BitSPITaskTo32Bit(task); +#else + Interleave8BitSPITaskTo9Bit(task); +#endif +#endif + __sync_synchronize(); + uint32_t tail = spiTaskMemory->queueTail; + spiTaskMemory->queueTail = (uint32_t)((uint8_t*)task - spiTaskMemory->buffer) + sizeof(SPITask) + task->size; + __atomic_fetch_add(&spiTaskMemory->spiBytesQueued, task->PayloadSize()+1, __ATOMIC_RELAXED); + __sync_synchronize(); + if (spiTaskMemory->queueHead == tail) syscall(SYS_futex, &spiTaskMemory->queueTail, FUTEX_WAKE, 1, 0, 0, 0); // Wake the SPI thread if it was sleeping to get new tasks +} + +#ifdef USE_SPI_THREAD +#define IN_SINGLE_THREADED_MODE_RUN_TASK() ((void)0) +#else +#define IN_SINGLE_THREADED_MODE_RUN_TASK() { \ + SPITask *t = GetTask(); \ + RunSPITask(t); \ + DoneTask(t); \ +} +#endif + +int InitSPI(void); +void DeinitSPI(void); +void ExecuteSPITasks(void); +void RunSPITask(SPITask *task); +SPITask *GetTask(void); +void DoneTask(SPITask *task); +void DumpSPICS(uint32_t reg); +#ifdef RUN_WITH_REALTIME_THREAD_PRIORITY +void SetRealtimeThreadPriority(); +#endif From 5b155f37298f7ea22b79c766ba399433c29c14bb Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Sat, 11 May 2019 17:39:14 +0000 Subject: [PATCH 04/52] split spi file for kernal and user space --- diff.cpp | 2 +- display.cpp | 2 +- dma.cpp | 4 +- fbcp-ili9341.cpp | 2 +- hx8357d.cpp | 2 +- ili9341.cpp | 2 +- ili9486.cpp | 2 +- kernel/Makefile | 2 +- kernel/bcm2835_spi_display.c | 348 +------------------------ kernel/build_kernel_module.sh | 2 +- mpi3501.cpp | 2 +- mz61581.cpp | 2 +- spi.cpp | 71 ++---- spi.h | 467 +++++----------------------------- ssd1351.cpp | 2 +- st7735r.cpp | 2 +- statistics.cpp | 2 +- 17 files changed, 111 insertions(+), 805 deletions(-) diff --git a/diff.cpp b/diff.cpp index 8966a15..54e711f 100644 --- a/diff.cpp +++ b/diff.cpp @@ -3,7 +3,7 @@ #include "util.h" #include "display.h" #include "gpu.h" -#include "spi.h" +#include "spi_user.h" Span *spans = 0; diff --git a/display.cpp b/display.cpp index 48787f8..c33e212 100644 --- a/display.cpp +++ b/display.cpp @@ -1,6 +1,6 @@ #include "config.h" #include "display.h" -#include "spi.h" +#include "spi_user.h" #include diff --git a/dma.cpp b/dma.cpp index d2844e2..3084f91 100644 --- a/dma.cpp +++ b/dma.cpp @@ -5,11 +5,13 @@ #include // uint32_t #include // syslog #include // mmap, munmap, PROT_READ, PROT_WRITE +#include "spi_kernel.h" +#else +#include "spi_user.h" #endif #include "config.h" #include "dma.h" -#include "spi.h" #include "gpu.h" #include "util.h" #include "mailbox.h" diff --git a/fbcp-ili9341.cpp b/fbcp-ili9341.cpp index a7d324f..40715ad 100644 --- a/fbcp-ili9341.cpp +++ b/fbcp-ili9341.cpp @@ -18,7 +18,7 @@ #include "config.h" #include "text.h" -#include "spi.h" +#include "spi_user.h" #include "gpu.h" #include "statistics.h" #include "tick.h" diff --git a/hx8357d.cpp b/hx8357d.cpp index 5d9562c..e86d1c3 100644 --- a/hx8357d.cpp +++ b/hx8357d.cpp @@ -4,7 +4,7 @@ #ifdef HX8357D -#include "spi.h" +#include "spi_user.h" #include #include diff --git a/ili9341.cpp b/ili9341.cpp index 5af3363..823ac68 100644 --- a/ili9341.cpp +++ b/ili9341.cpp @@ -2,7 +2,7 @@ #if defined(ILI9341) || defined(ILI9340) -#include "spi.h" +#include "spi_user.h" #include #include diff --git a/ili9486.cpp b/ili9486.cpp index 5c52a72..0908196 100644 --- a/ili9486.cpp +++ b/ili9486.cpp @@ -2,7 +2,7 @@ #if defined(ILI9486) || defined(ILI9486L) -#include "spi.h" +#include "spi_user.h" #include #include diff --git a/kernel/Makefile b/kernel/Makefile index 6ffe5c1..8aaca22 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -1,3 +1,3 @@ obj-m := bcm2835_spi_display.o -CFLAGS_bcm2835_spi_display.o := -O3 -std=gnu99 -Wno-declaration-after-statement -DKERNEL_MODULE -DILI9341=1 -DADAFRUIT_ILI9341_PITFT=1 +CFLAGS_bcm2835_spi_display.o := -O3 -std=gnu99 -Wno-declaration-after-statement -DKERNEL_MODULE=1 -DILI9341=1 -DADAFRUIT_ILI9341_PITFT=1 -DSPI_BUS_CLOCK_DIVISOR=48 diff --git a/kernel/bcm2835_spi_display.c b/kernel/bcm2835_spi_display.c index 346c33e..6ba0e5c 100644 --- a/kernel/bcm2835_spi_display.c +++ b/kernel/bcm2835_spi_display.c @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include @@ -13,35 +12,16 @@ #include #include #include -#include #include #include #include #include -#include #include #include #include -#include "../config.h" -#include "../display.h" +//#include "../config.h" #include "../spi.h" -#include "../util.h" -#include "../dma.h" - -static inline uint64_t tick(void) -{ - struct timespec start = current_kernel_time(); - return start.tv_sec * 1000000 + start.tv_nsec / 1000; -} - -// TODO: Super-dirty temp, factor this into kbuild Makefile. -#include "../spi.cpp" -#include "../dma.cpp" - -volatile SPITask *currentTask = 0; -volatile uint8_t *taskNextByte = 0; -volatile uint8_t *taskEndByte = 0; #define SPI_BUS_PROC_ENTRY_FILENAME "bcm2835_spi_display_bus" @@ -89,7 +69,7 @@ static int p_mmap(struct file *filp, struct vm_area_struct *vma) static int p_open(struct inode *inode, struct file *filp) { mmap_info *info = kmalloc(sizeof(mmap_info), GFP_KERNEL); - info->data = (void*)spiTaskMemory; + info->data = (void*)spiFlagMemory; filp->private_data = info; return 0; } @@ -110,81 +90,10 @@ static const struct file_operations fops = .release = p_release, }; -#ifdef KERNEL_DRIVE_WITH_IRQ static irqreturn_t irq_handler(int irq, void* dev_id) { -#ifndef KERNEL_MODULE_CLIENT_DRIVES - uint32_t cs = spi->cs; - if (!taskNextByte) - { - if (currentTask) DoneTask((SPITask*)currentTask); - currentTask = GetTask(); - if (!currentTask) - { - spi->cs = (cs & ~BCM2835_SPI0_CS_TA) | BCM2835_SPI0_CS_CLEAR; - return IRQ_HANDLED; - } - - if ((cs & (BCM2835_SPI0_CS_RXF|BCM2835_SPI0_CS_RXR))) (void)spi->fifo; - while (!(spi->cs & BCM2835_SPI0_CS_DONE)) - { - if ((spi->cs & (BCM2835_SPI0_CS_RXF|BCM2835_SPI0_CS_RXR|BCM2835_SPI0_CS_RXD))) - (void)spi->fifo; - } - CLEAR_GPIO(GPIO_TFT_DATA_CONTROL); - spi->fifo = currentTask->cmd; - if (currentTask->size == 0) // Was this a task without data bytes? If so, nothing more to do here, go to sleep to wait for next IRQ event - { - DoneTask((SPITask*)currentTask); - taskNextByte = 0; - currentTask = 0; - } - else - { - taskNextByte = currentTask->data; - taskEndByte = currentTask->data + currentTask->size; - } -#if 0 // Testing overhead of not returning after command byte, but synchronously polling it out.. - while (!(spi->cs & BCM2835_SPI0_CS_DONE)) ; - (void)spi->fifo; -#else - return IRQ_HANDLED; -#endif - } - if (taskNextByte == currentTask->data) - { - SET_GPIO(GPIO_TFT_DATA_CONTROL); - __sync_synchronize(); - } - - // Test code: write and read from FIFO as many bytes as spec says we should be allowed to, without checking CS in between. -// int maxBytesToSend = (cs & BCM2835_SPI0_CS_DONE) ? 16 : 12; -// if ((cs & BCM2835_SPI0_CS_RXF)) (void)spi->fifo; -// if ((cs & BCM2835_SPI0_CS_RXR)) for(int i = 0; i < MIN(maxBytesToSend, taskEndByte-taskNextByte); ++i) { spi->fifo = *taskNextByte++; (void)spi->fifo; } -// else for(int i = 0; i < MIN(maxBytesToSend, taskEndByte-taskNextByte); ++i) { spi->fifo = *taskNextByte++; } - - while(taskNextByte < taskEndByte) - { - uint32_t cs = spi->cs; - if ((cs & (BCM2835_SPI0_CS_RXR | BCM2835_SPI0_CS_RXF))) spi->cs = cs | BCM2835_SPI0_CS_CLEAR_RX; - if ((cs & BCM2835_SPI0_CS_TXD)) spi->fifo = *taskNextByte++; - if ((cs & BCM2835_SPI0_CS_RXD)) (void)spi->fifo; - else break; - } - - if (taskNextByte >= taskEndByte) - { - if ((cs & BCM2835_SPI0_CS_INTR)) spi->cs = (cs & ~BCM2835_SPI0_CS_INTR) | BCM2835_SPI0_CS_INTD; - taskNextByte = 0; - } - else - { - if (!(cs & BCM2835_SPI0_CS_INTR)) spi->cs = (cs | BCM2835_SPI0_CS_INTR) & ~BCM2835_SPI0_CS_INTR; - } -#endif return IRQ_HANDLED; } -#endif #define req(cnd) if (!(cnd)) { LOG("!!!%s!!!\n", #cnd);} @@ -194,258 +103,16 @@ uint32_t virt_to_bus_address(volatile void *virtAddress) } volatile int shuttingDown = 0; -dma_addr_t spiTaskMemoryPhysical = 0; - -#ifdef USE_DMA_TRANSFERS - -void DMATest(void); - -// Debug code to verify memory->memory streaming of DMA, no SPI peripheral interaction (remove this) -void DMATest() -{ - LOG("Testing DMA transfers"); - - dma_addr_t dma_mem_phys = 0; - void *dma_mem = dma_alloc_writecombine(0, SHARED_MEMORY_SIZE, &dma_mem_phys, GFP_KERNEL); - LOG("Allocated DMA memory: mem: %p, phys: %p", dma_mem, (void*)dma_mem_phys); - - spiTaskMemory = (SharedMemory *)dma_mem; - while(!shuttingDown) - { - msleep(100); - static int ctr = 0; - uint32_t base = (ctr++ * 34153) % SPI_QUEUE_SIZE; - uint32_t size = 65; - uint32_t base2 = base + size; - if (base2 + size > SPI_QUEUE_SIZE) continue; - - memset((void*)spiTaskMemory->buffer, 0xCB, SPI_QUEUE_SIZE); - - uint8_t *src = (uint8_t *)(spiTaskMemory->buffer + base); - src = (uint8_t *)((uintptr_t)src); - for(int i = 0; i < size; ++i) - src[i] = i; - - uint8_t *dst = (uint8_t *)(spiTaskMemory->buffer + base2); - dst = (uint8_t *)((uintptr_t)dst); - -#define TO_BUS(ptr) (( ((uint32_t)dma_mem_phys + ((uintptr_t)(ptr) - (uintptr_t)dma_mem))) | 0xC0000000U) - - volatile DMAChannelRegisterFile *dmaCh = dma+dmaTxChannel; -// printk(KERN_INFO "CS: %x, cbAddr: %p, ti: %x, src: %p, dst: %p, len: %u, stride: %u, nextConBk: %p, debug: %x", -// dmaCh->cs, (void*)dmaCh->cbAddr, dmaCh->cb.ti, (void*)dmaCh->cb.src, (void*)dmaCh->cb.dst, dmaCh->cb.len, dmaCh->cb.stride, (void*)dmaCh->cb.next, dmaCh->cb.debug); - - volatile DMAControlBlock *cb = &spiTaskMemory->cb[0].cb; - req(((uintptr_t)cb) % 256 == 0); - cb->ti = BCM2835_DMA_TI_SRC_INC | BCM2835_DMA_TI_DEST_INC; - cb->src = TO_BUS(src); - cb->dst = TO_BUS(dst); - cb->len = size; - cb->stride = 0; - cb->next = 0; - cb->debug = 0; - cb->reserved = 0; -// DumpCS(dmaCh->cs); -// DumpDebug(dmaCh->cb.debug); -// DumpTI(dmaCh->cb.ti); - LOG("Waiting for transfer %d, src:%p(phys:%p) to dst:%p (phys:%p)", ctr, (void*)src, (void*)cb->src, (void*)dst, (void*)cb->dst); - writel(TO_BUS(cb), &dmaCh->cbAddr); - writel(BCM2835_DMA_CS_ACTIVE | BCM2835_DMA_CS_END | BCM2835_DMA_CS_INT | BCM2835_DMA_CS_WAIT_FOR_OUTSTANDING_WRITES | BCM2835_DMA_CS_SET_PRIORITY(0xF) | BCM2835_DMA_CS_SET_PANIC_PRIORITY(0xF), &dmaCh->cs); - - while((readl(&dmaCh->cs) & BCM2835_DMA_CS_ACTIVE) && !shuttingDown) - { - cpu_relax(); - } - - if (shuttingDown) - { - LOG("Module shutdown"); - spiTaskMemory = 0; - return; - } - int errors = 0; - for(int i = 0; i < size; ++i) - if (dst[i] != src[i]) - { - errors = true; - break; - } - - if (errors) - { - printk(KERN_INFO "CS: %x, cbAddr: %p, ti: %x, src: %p, dst: %p, len: %u, stride: %u, nextConBk: %p, debug: %x", - dmaCh->cs, (void*)dmaCh->cbAddr, dmaCh->cb.ti, (void*)dmaCh->cb.src, (void*)dmaCh->cb.dst, dmaCh->cb.len, dmaCh->cb.stride, (void*)dmaCh->cb.next, dmaCh->cb.debug); - for(int i = 0; i < size; ++i) - { - printk(KERN_INFO "Result %p %d: %x vs dst %p %x\n", (void*)virt_to_phys(src+i), i, src[i], (void*)virt_to_phys(dst+i), dst[i]); - } - DumpCS(dmaCh->cs); - DumpDebug(dmaCh->cb.debug); - DumpTI(dmaCh->cb.ti); - LOG("Abort"); - break; - } - } - LOG("DMA transfer test done"); - spiTaskMemory = 0; -} -#endif - -void PumpSPI(void) -{ -#ifdef KERNEL_DRIVE_WITH_IRQ - spi->cs = BCM2835_SPI0_CS_CLEAR | BCM2835_SPI0_CS_TA | BCM2835_SPI0_CS_INTR | BCM2835_SPI0_CS_INTD; // Initialize the Control and Status register to defaults: CS=0 (Chip Select), CPHA=0 (Clock Phase), CPOL=0 (Clock Polarity), CSPOL=0 (Chip Select Polarity), TA=0 (Transfer not active), and reset TX and RX queues. -#else - if (spiTaskMemory->queueTail != spiTaskMemory->queueHead) - { - BEGIN_SPI_COMMUNICATION(); - { - int i = 0; - while(spiTaskMemory->queueTail != spiTaskMemory->queueHead) - { - ++i; - if (i > 500) break; - SPITask *task = GetTask(); - if (task) - { - RunSPITask(task); - DoneTask(task); - } - else - break; - } - } - END_SPI_COMMUNICATION(); - } -#endif -} - -static struct timer_list my_timer; - void my_timer_callback( unsigned long data ) -{ - if (shuttingDown) return; - - PumpSPI(); - int ret = mod_timer( &my_timer, jiffies + msecs_to_jiffies(1) ); - if (ret) printk("Error in mod_timer\n"); -} - -static int display_initialization_thread(void *unused) -{ - printk(KERN_INFO "BCM2835 SPI Display driver thread started"); - -#ifndef KERNEL_MODULE_CLIENT_DRIVES - - // Initialize display. TODO: Move to be shared with ili9341.cpp. - QUEUE_SPI_TRANSFER(0xC0/*Power Control 1*/, 0x23/*VRH=4.60V*/); // Set the GVDD level, which is a reference level for the VCOM level and the grayscale voltage level. - QUEUE_SPI_TRANSFER(0xC1/*Power Control 2*/, 0x10/*AVCC=VCIx2,VGH=VCIx7,VGL=-VCIx4*/); // Sets the factor used in the step-up circuits. To reduce power consumption, set a smaller factor. - QUEUE_SPI_TRANSFER(0xC5/*VCOM Control 1*/, 0x3e/*VCOMH=4.250V*/, 0x28/*VCOML=-1.500V*/); // Adjusting VCOM 1 and 2 can control display brightness - QUEUE_SPI_TRANSFER(0xC7/*VCOM Control 2*/, 0x86/*VCOMH=VMH-58,VCOML=VML-58*/); - -#define MADCTL_ROW_COLUMN_EXCHANGE (1<<5) -#define MADCTL_BGR_PIXEL_ORDER (1<<3) -#define MADCTL_ROTATE_180_DEGREES (MADCTL_COLUMN_ADDRESS_ORDER_SWAP | MADCTL_ROW_ADDRESS_ORDER_SWAP) - uint8_t madctl = MADCTL_BGR_PIXEL_ORDER; -#ifdef DISPLAY_OUTPUT_LANDSCAPE - madctl |= MADCTL_ROW_COLUMN_EXCHANGE; -#endif -#ifdef DISPLAY_ROTATE_180_DEGREES - madctl ^= MADCTL_ROTATE_180_DEGREES; -#endif - QUEUE_SPI_TRANSFER(0x36/*MADCTL: Memory Access Control*/, madctl); - QUEUE_SPI_TRANSFER(0x3A/*COLMOD: Pixel Format Set*/, 0x55/*DPI=16bits/pixel,DBI=16bits/pixel*/); - QUEUE_SPI_TRANSFER(0xB1/*Frame Rate Control (In Normal Mode/Full Colors)*/, 0x00/*DIVA=fosc*/, 0x18/*RTNA(Frame Rate)=79Hz*/); - QUEUE_SPI_TRANSFER(0xB6/*Display Function Control*/, 0x08/*PTG=Interval Scan,PT=V63/V0/VCOML/VCOMH*/, 0x82/*REV=1(Normally white),ISC(Scan Cycle)=5 frames*/, 0x27/*LCD Driver Lines=320*/); - QUEUE_SPI_TRANSFER(0x26/*Gamma Set*/, 0x01/*Gamma curve 1 (G2.2)*/); - QUEUE_SPI_TRANSFER(0xE0/*Positive Gamma Correction*/, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00); - QUEUE_SPI_TRANSFER(0xE1/*Negative Gamma Correction*/, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F); - QUEUE_SPI_TRANSFER(0x11/*Sleep Out*/); - - PumpSPI(); - msleep(1000); - QUEUE_SPI_TRANSFER(/*Display ON*/0x29); - -#if 1 - // XXX Debug: Random garbage to verify screen updates working - for(int y = 0; y < DISPLAY_HEIGHT; ++y) - { - QUEUE_SPI_TRANSFER(DISPLAY_SET_CURSOR_X, 0, 0, DISPLAY_WIDTH >> 8, DISPLAY_WIDTH & 0xFF); - QUEUE_SPI_TRANSFER(DISPLAY_SET_CURSOR_Y, y >> 8, y & 0xFF, DISPLAY_HEIGHT >> 8, DISPLAY_HEIGHT & 0xFF); - SPITask *clearLine = AllocTask(DISPLAY_SCANLINE_SIZE); - clearLine->cmd = DISPLAY_WRITE_PIXELS; - clearLine->size = DISPLAY_SCANLINE_SIZE; - for(int i = 0; i < DISPLAY_SCANLINE_SIZE; ++i) - clearLine->data[i] = tick() * y + i; - CommitTask(clearLine); - } - PumpSPI(); - msleep(1000); -#endif - - // Initial screen clear - for(int y = 0; y < DISPLAY_HEIGHT; ++y) - { - QUEUE_SPI_TRANSFER(DISPLAY_SET_CURSOR_X, 0, 0, DISPLAY_WIDTH >> 8, DISPLAY_WIDTH & 0xFF); - QUEUE_SPI_TRANSFER(DISPLAY_SET_CURSOR_Y, y >> 8, y & 0xFF, DISPLAY_HEIGHT >> 8, DISPLAY_HEIGHT & 0xFF); - SPITask *clearLine = AllocTask(DISPLAY_SCANLINE_SIZE); - clearLine->cmd = DISPLAY_WRITE_PIXELS; - clearLine->size = DISPLAY_SCANLINE_SIZE; - memset((void*)clearLine->data, 0, DISPLAY_SCANLINE_SIZE); - CommitTask(clearLine); - } - PumpSPI(); - - QUEUE_SPI_TRANSFER(DISPLAY_SET_CURSOR_X, 0, 0, DISPLAY_WIDTH >> 8, DISPLAY_WIDTH & 0xFF); - QUEUE_SPI_TRANSFER(DISPLAY_SET_CURSOR_Y, 0, 0, DISPLAY_HEIGHT >> 8, DISPLAY_HEIGHT & 0xFF); - - spi->cs = BCM2835_SPI0_CS_CLEAR | BCM2835_SPI0_CS_TA | BCM2835_SPI0_CS_INTR | BCM2835_SPI0_CS_INTD; -#endif - - PumpSPI(); - - // Expose SPI worker ring bus to user space driver application. - proc_create(SPI_BUS_PROC_ENTRY_FILENAME, 0, NULL, &fops); - -#if 0 - // XXX Debug: - DMATest(); -#endif +dma_addr_t spiFlagMemoryPhysical = 0; - setup_timer(&my_timer, my_timer_callback, 0); - printk("Starting timer to fire in 200ms (%ld)\n", jiffies); - int ret = mod_timer( &my_timer, jiffies + msecs_to_jiffies(200) ); - if (ret) printk("Error in mod_timer\n"); - - return 0; -} - -static struct task_struct *displayThread = 0; static uint32_t irqHandlerCookie = 0; static uint32_t irqRegistered = 0; int bcm2835_spi_display_init(void) { - InitSPI(); -#ifdef KERNEL_DRIVE_WITH_IRQ int ret = request_irq(84, irq_handler, IRQF_SHARED, "spi_handler", &irqHandlerCookie); - if (ret != 0) FATAL_ERROR("request_irq failed!"); + if (ret != 0) printk("request_irq failed!"); irqRegistered = 1; -#endif - - if (!spiTaskMemory) FATAL_ERROR("Shared memory block not initialized!"); - -#ifdef USE_DMA_TRANSFERS - printk(KERN_INFO "DMA TX channel: %d, irq: %d", dmaTxChannel, dmaTxIrq); - printk(KERN_INFO "DMA RX channel: %d, irq: %d", dmaRxChannel, dmaRxIrq); - spiTaskMemory->dmaTxChannel = dmaTxChannel; - spiTaskMemory->dmaRxChannel = dmaRxChannel; -#endif - - spiTaskMemory->sharedMemoryBaseInPhysMemory = (uint32_t)virt_to_phys(spiTaskMemory) | 0x40000000U; - LOG("PhysBase: %p", (void*)spiTaskMemory->sharedMemoryBaseInPhysMemory); - - displayThread = kthread_create(display_initialization_thread, NULL, "display_thread"); - if (displayThread) wake_up_process(displayThread); return 0; } @@ -454,9 +121,6 @@ void bcm2835_spi_display_exit(void) { shuttingDown = 1; msleep(2000); - spi->cs = BCM2835_SPI0_CS_CLEAR; - msleep(200); - DeinitSPI(); if (irqRegistered) { @@ -465,9 +129,7 @@ void bcm2835_spi_display_exit(void) } remove_proc_entry(SPI_BUS_PROC_ENTRY_FILENAME, NULL); - - int ret = del_timer( &my_timer ); - if (ret) printk("The timer is still in use...\n");} +} module_init(bcm2835_spi_display_init); module_exit(bcm2835_spi_display_exit); diff --git a/kernel/build_kernel_module.sh b/kernel/build_kernel_module.sh index 98a3804..d3d3dd3 100755 --- a/kernel/build_kernel_module.sh +++ b/kernel/build_kernel_module.sh @@ -1,5 +1,5 @@ sudo ./stop_kernel_module.sh -sudo make -C /lib/modules/$(uname -r)/build M=$(pwd) modules +sudo make VERBOSE=1 -C /lib/modules/$(uname -r)/build M=$(pwd) -I/usr/include/ modules #For debugging: generate disassembly output: #objdump -dS bcm2835_spi_display.ko > bcm2835_spi_display.S diff --git a/mpi3501.cpp b/mpi3501.cpp index 6de4ba8..6509655 100644 --- a/mpi3501.cpp +++ b/mpi3501.cpp @@ -2,7 +2,7 @@ #ifdef MPI3501 -#include "spi.h" +#include "spi_user.h" #include #include diff --git a/mz61581.cpp b/mz61581.cpp index d7ae001..88950f7 100644 --- a/mz61581.cpp +++ b/mz61581.cpp @@ -2,7 +2,7 @@ #ifdef MZ61581 -#include "spi.h" +#include "spi_user.h" #include #include diff --git a/spi.cpp b/spi.cpp index 064b30d..059aa15 100644 --- a/spi.cpp +++ b/spi.cpp @@ -5,10 +5,11 @@ #include // mmap, munmap #include // pthread_create #include // bcm_host_get_peripheral_address, bcm_host_get_peripheral_size, bcm_host_get_sdram_address +#include "config.h" #endif - + #include "config.h" -#include "spi.h" +#include "spi_user.h" #include "util.h" #include "dma.h" #include "mailbox.h" @@ -403,6 +404,7 @@ void RunSPITask(SPITask *task) #endif SharedMemory *spiTaskMemory = 0; +SharedMemory *spiFlagMemory = 0; volatile uint64_t spiThreadIdleUsecs = 0; volatile uint64_t spiThreadSleepStartTime = 0; volatile int spiThreadSleeping = 0; @@ -454,7 +456,7 @@ void ExecuteSPITasks() #endif } -#if !defined(KERNEL_MODULE) && defined(USE_SPI_THREAD) +#if defined(USE_SPI_THREAD) pthread_t spiThread; // A worker thread that keeps the SPI bus filled at all times @@ -489,19 +491,6 @@ void *spi_thread(void *unused) int InitSPI() { -#ifdef KERNEL_MODULE - -#define BCM2835_PERI_BASE 0x3F000000 -#define BCM2835_GPIO_BASE 0x200000 -#define BCM2835_SPI0_BASE 0x204000 - printk("ioremapping %p\n", (void*)(BCM2835_PERI_BASE+BCM2835_GPIO_BASE)); - void *bcm2835 = ioremap(BCM2835_PERI_BASE+BCM2835_GPIO_BASE, 32768); - printk("Got bcm address %p\n", bcm2835); - if (!bcm2835) FATAL_ERROR("Failed to map BCM2835 address!"); - spi = (volatile SPIRegisterFile*)((uintptr_t)bcm2835 + BCM2835_SPI0_BASE - BCM2835_GPIO_BASE); - gpio = (volatile GPIORegisterFile*)((uintptr_t)bcm2835); - -#else // Userland version // Memory map GPIO and SPI peripherals for direct access mem_fd = open("/dev/mem", O_RDWR|O_SYNC); if (mem_fd < 0) FATAL_ERROR("can't open /dev/mem (run as sudo)"); @@ -512,7 +501,6 @@ int InitSPI() gpio = (volatile GPIORegisterFile*)((uintptr_t)bcm2835 + BCM2835_GPIO_BASE); systemTimerRegister = (volatile uint64_t*)((uintptr_t)bcm2835 + BCM2835_TIMER_BASE + 0x04); // Generates an unaligned 64-bit pointer, but seems to be fine. // TODO: On graceful shutdown, (ctrl-c signal?) close(mem_fd) -#endif uint32_t currentBcmCoreSpeed = MailboxRet2(0x00030002/*Get Clock Rate*/, 0x4/*CORE*/); uint32_t maxBcmCoreTurboSpeed = MailboxRet2(0x00030004/*Get Max Clock Rate*/, 0x4/*CORE*/); @@ -522,7 +510,6 @@ int InitSPI() printf("BCM core speed: current: %uhz, max turbo: %uhz. SPI CDIV: %d, SPI max frequency: %.0fhz\n", currentBcmCoreSpeed, maxBcmCoreTurboSpeed, SPI_BUS_CLOCK_DIVISOR, (double)maxBcmCoreTurboSpeed / SPI_BUS_CLOCK_DIVISOR); -#if !defined(KERNEL_MODULE_CLIENT) || defined(KERNEL_MODULE_CLIENT_DRIVES) // By default all GPIO pins are in input mode (0x00), initialize them for SPI and GPIO writes #ifdef GPIO_TFT_DATA_CONTROL SET_GPIO_MODE(GPIO_TFT_DATA_CONTROL, 0x01); // Data/Control pin to output (0x01) @@ -552,38 +539,27 @@ int InitSPI() spi->cs = BCM2835_SPI0_CS_CLEAR | DISPLAY_SPI_DRIVE_SETTINGS; // Initialize the Control and Status register to defaults: CS=0 (Chip Select), CPHA=0 (Clock Phase), CPOL=0 (Clock Polarity), CSPOL=0 (Chip Select Polarity), TA=0 (Transfer not active), and reset TX and RX queues. spi->clk = SPI_BUS_CLOCK_DIVISOR; // Clock Divider determines SPI bus speed, resulting speed=256MHz/clk -#endif // Initialize SPI thread task buffer memory #ifdef KERNEL_MODULE_CLIENT - int driverfd = open("/proc/bcm2835_spi_display_bus", O_RDWR|O_SYNC); - if (driverfd < 0) FATAL_ERROR("Could not open SPI ring buffer - kernel driver module not running?"); - spiTaskMemory = (SharedMemory*)mmap(NULL, SHARED_MEMORY_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED/* | MAP_NORESERVE | MAP_POPULATE | MAP_LOCKED*/, driverfd, 0); - close(driverfd); - if (spiTaskMemory == MAP_FAILED) FATAL_ERROR("Could not mmap SPI ring buffer!"); - printf("Got shared memory block %p, ring buffer head %p, ring buffer tail %p, shared memory block phys address: %p\n", (const char *)spiTaskMemory, spiTaskMemory->queueHead, spiTaskMemory->queueTail, spiTaskMemory->sharedMemoryBaseInPhysMemory); - -#ifdef USE_DMA_TRANSFERS - printf("DMA TX channel: %d, DMA RX channel: %d\n", spiTaskMemory->dmaTxChannel, spiTaskMemory->dmaRxChannel); + spiFlagMemory = (SharedMemory*)mmap(NULL, sizeof(char), PROT_READ|PROT_WRITE, MAP_SHARED/* | MAP_NORESERVE | MAP_POPULATE | MAP_LOCKED*/, driverfd, 0); + if (spiFlagMemory == MAP_FAILED) FATAL_ERROR("Could not mmap SPI flag buffer!"); + printf("Got shared memory block %p\n", (const char *)spiFlagMemory); #endif -#else - #ifdef KERNEL_MODULE - spiTaskMemory = (SharedMemory*)kmalloc(SHARED_MEMORY_SIZE, GFP_KERNEL | GFP_DMA); - // TODO: Ideally we would be able to directly perform the DMA from the SPI ring buffer in 'spiTaskMemory'. However - // that pointer is shared to userland, and it is proving troublesome to make it both userland-writable as well as cache-bypassing DMA coherent. - // Therefore these two memory areas are separate for now, and we memcpy() from SPI ring buffer to the following intermediate 'dmaSourceMemory' - // memory area to perform the DMA transfer. Is there a way to avoid this intermediate buffer? That would improve performance a bit. - dmaSourceMemory = (SharedMemory*)dma_alloc_writecombine(0, SHARED_MEMORY_SIZE, &spiTaskMemoryPhysical, GFP_KERNEL); - LOG("Allocated DMA memory: mem: %p, phys: %p", spiTaskMemory, (void*)spiTaskMemoryPhysical); - memset((void*)spiTaskMemory, 0, SHARED_MEMORY_SIZE); -#else - spiTaskMemory = (SharedMemory*)Malloc(SHARED_MEMORY_SIZE, "spi.cpp shared task memory"); + spiFlagMemory = (SharedMemory*)kmalloc(sizeof(char), GFP_KERNEL | GFP_DMA); + dmaSourceMemory = (SharedMemory*)dma_alloc_writecombine(0, sizeof(char), &spiFlagMemoryPhysical, GFP_KERNEL); + LOG("Allocated Flag memory: mem: %p, phys: %p", spiFlagMemory, (void*)spiFlagMemoryPhysical); + memset((void*)spiFlagMemory, 0, sizeof(char)); +#endif + +#if !defined(KERNEL_MODULE) && !defined(KERNEL_MODULE_CLIENT) + spiFlagMemory = (SharedMemory*)Malloc(sizeof(char), "spi.cpp shared flag memory"); #endif + spiTaskMemory = (SharedMemory*)Malloc(SHARED_MEMORY_SIZE, "spi.cpp shared task memory"); spiTaskMemory->queueHead = spiTaskMemory->queueTail = spiTaskMemory->spiBytesQueued = 0; -#endif #ifdef USE_DMA_TRANSFERS InitDMA(); @@ -592,7 +568,6 @@ int InitSPI() // Enable fast 8 clocks per byte transfer mode, instead of slower 9 clocks per byte. UNLOCK_FAST_8_CLOCKS_SPI(); -#if !defined(KERNEL_MODULE) && (!defined(KERNEL_MODULE_CLIENT) || defined(KERNEL_MODULE_CLIENT_DRIVES)) printf("Initializing display\n"); InitSPIDisplay(); @@ -607,7 +582,6 @@ int InitSPI() BEGIN_SPI_COMMUNICATION(); #endif -#endif LOG("InitSPI done"); return 0; @@ -626,7 +600,6 @@ void DeinitSPI() spi->cs = BCM2835_SPI0_CS_CLEAR | DISPLAY_SPI_DRIVE_SETTINGS; -#ifndef KERNEL_MODULE_CLIENT #ifdef GPIO_TFT_DATA_CONTROL SET_GPIO_MODE(GPIO_TFT_DATA_CONTROL, 0); #endif @@ -635,7 +608,6 @@ void DeinitSPI() SET_GPIO_MODE(GPIO_SPI0_MISO, 0); SET_GPIO_MODE(GPIO_SPI0_MOSI, 0); SET_GPIO_MODE(GPIO_SPI0_CLK, 0); -#endif if (bcm2835) { @@ -649,15 +621,14 @@ void DeinitSPI() mem_fd = -1; } -#ifndef KERNEL_MODULE_CLIENT #ifdef KERNEL_MODULE - kfree(spiTaskMemory); - dma_free_writecombine(0, SHARED_MEMORY_SIZE, dmaSourceMemory, spiTaskMemoryPhysical); - spiTaskMemoryPhysical = 0; + kfree(spiFlagMemory); + spiFlagMemoryPhysical = 0; #else free(spiTaskMemory); -#endif + free(spiFlagMemory); #endif spiTaskMemory = 0; + spiFlagMemory = 0; } diff --git a/spi.h b/spi.h index e88c8d0..52a6719 100644 --- a/spi.h +++ b/spi.h @@ -1,398 +1,69 @@ -#pragma once - -#ifndef KERNEL_MODULE -#include -#include -#endif -#include - -#include "display.h" -#include "tick.h" -#include "dma.h" -#include "display.h" - -#define BCM2835_GPIO_BASE 0x200000 // Address to GPIO register file -#define BCM2835_SPI0_BASE 0x204000 // Address to SPI0 register file -#define BCM2835_TIMER_BASE 0x3000 // Address to System Timer register file - -#define BCM2835_SPI0_CS_RXF 0x00100000 // Receive FIFO is full -#define BCM2835_SPI0_CS_RXR 0x00080000 // FIFO needs reading -#define BCM2835_SPI0_CS_TXD 0x00040000 // TXD TX FIFO can accept Data -#define BCM2835_SPI0_CS_RXD 0x00020000 // RXD RX FIFO contains Data -#define BCM2835_SPI0_CS_DONE 0x00010000 // Done transfer Done -#define BCM2835_SPI0_CS_ADCS 0x00000800 // Automatically Deassert Chip Select -#define BCM2835_SPI0_CS_INTR 0x00000400 // Fire interrupts on RXR? -#define BCM2835_SPI0_CS_INTD 0x00000200 // Fire interrupts on DONE? -#define BCM2835_SPI0_CS_DMAEN 0x00000100 // Enable DMA transfers? -#define BCM2835_SPI0_CS_TA 0x00000080 // Transfer Active -#define BCM2835_SPI0_CS_CLEAR 0x00000030 // Clear FIFO Clear RX and TX -#define BCM2835_SPI0_CS_CLEAR_RX 0x00000020 // Clear FIFO Clear RX -#define BCM2835_SPI0_CS_CLEAR_TX 0x00000010 // Clear FIFO Clear TX -#define BCM2835_SPI0_CS_CPOL 0x00000008 // Clock Polarity -#define BCM2835_SPI0_CS_CPHA 0x00000004 // Clock Phase -#define BCM2835_SPI0_CS_CS 0x00000003 // Chip Select - -#define BCM2835_SPI0_CS_RXF_SHIFT 20 -#define BCM2835_SPI0_CS_RXR_SHIFT 19 -#define BCM2835_SPI0_CS_TXD_SHIFT 18 -#define BCM2835_SPI0_CS_RXD_SHIFT 17 -#define BCM2835_SPI0_CS_DONE_SHIFT 16 -#define BCM2835_SPI0_CS_ADCS_SHIFT 11 -#define BCM2835_SPI0_CS_INTR_SHIFT 10 -#define BCM2835_SPI0_CS_INTD_SHIFT 9 -#define BCM2835_SPI0_CS_DMAEN_SHIFT 8 -#define BCM2835_SPI0_CS_TA_SHIFT 7 -#define BCM2835_SPI0_CS_CLEAR_RX_SHIFT 5 -#define BCM2835_SPI0_CS_CLEAR_TX_SHIFT 4 -#define BCM2835_SPI0_CS_CPOL_SHIFT 3 -#define BCM2835_SPI0_CS_CPHA_SHIFT 2 -#define BCM2835_SPI0_CS_CS_SHIFT 0 - -#define GPIO_SPI0_MOSI 10 // Pin P1-19, MOSI when SPI0 in use -#define GPIO_SPI0_MISO 9 // Pin P1-21, MISO when SPI0 in use -#define GPIO_SPI0_CLK 11 // Pin P1-23, CLK when SPI0 in use -#define GPIO_SPI0_CE0 8 // Pin P1-24, CE0 when SPI0 in use -#define GPIO_SPI0_CE1 7 // Pin P1-26, CE1 when SPI0 in use - -extern volatile void *bcm2835; - -typedef struct GPIORegisterFile -{ - uint32_t gpfsel[6], reserved0; // GPIO Function Select registers, 3 bits per pin, 10 pins in an uint32_t - uint32_t gpset[2], reserved1; // GPIO Pin Output Set registers, write a 1 to bit at index I to set the pin at index I high - uint32_t gpclr[2], reserved2; // GPIO Pin Output Clear registers, write a 1 to bit at index I to set the pin at index I low - uint32_t gplev[2]; -} GPIORegisterFile; -extern volatile GPIORegisterFile *gpio; - -#define SET_GPIO_MODE(pin, mode) gpio->gpfsel[(pin)/10] = (gpio->gpfsel[(pin)/10] & ~(0x7 << ((pin) % 10) * 3)) | ((mode) << ((pin) % 10) * 3) -#define GET_GPIO_MODE(pin) ((gpio->gpfsel[(pin)/10] & (0x7 << ((pin) % 10) * 3)) >> (((pin) % 10) * 3)) -#define GET_GPIO(pin) (gpio->gplev[0] & (1 << (pin))) // Pin must be (0-31) -#define SET_GPIO(pin) gpio->gpset[0] = 1 << (pin) // Pin must be (0-31) -#define CLEAR_GPIO(pin) gpio->gpclr[0] = 1 << (pin) // Pin must be (0-31) - -typedef struct SPIRegisterFile -{ - uint32_t cs; // SPI Master Control and Status register - uint32_t fifo; // SPI Master TX and RX FIFOs - uint32_t clk; // SPI Master Clock Divider - uint32_t dlen; // SPI Master Number of DMA Bytes to Write -} SPIRegisterFile; -extern volatile SPIRegisterFile *spi; - -// Defines the size of the SPI task memory buffer in bytes. This memory buffer can contain two frames worth of tasks at maximum, -// so for best performance, should be at least ~DISPLAY_WIDTH*DISPLAY_HEIGHT*BYTES_PER_PIXEL*2 bytes in size, plus some small -// amount for structuring each SPITask command. Technically this can be something very small, like 4096b, and not need to contain -// even a single full frame of data, but such small buffers can cause performance issues from threads starving. -#define SHARED_MEMORY_SIZE (DISPLAY_DRAWABLE_WIDTH*DISPLAY_DRAWABLE_HEIGHT*SPI_BYTESPERPIXEL*3) -#define SPI_QUEUE_SIZE (SHARED_MEMORY_SIZE - sizeof(SharedMemory)) - -#if defined(SPI_3WIRE_DATA_COMMAND_FRAMING_BITS) && SPI_3WIRE_DATA_COMMAND_FRAMING_BITS == 1 -// Need a byte of padding for 8-bit -> 9-bit expansion for performance -#define SPI_9BIT_TASK_PADDING_BYTES 1 -#else -#define SPI_9BIT_TASK_PADDING_BYTES 0 -#endif - -// Defines the maximum size of a single SPI task, in bytes. This excludes the command byte. If MAX_SPI_TASK_SIZE -// is not defined, there is no length limit that applies. (In ALL_TASKS_SHOULD_DMA version of DMA transfer, -// there is DMA chaining, so SPI tasks can be arbitrarily long) -#ifndef ALL_TASKS_SHOULD_DMA -#define MAX_SPI_TASK_SIZE 65528 -#endif - -typedef struct __attribute__((packed)) SPITask -{ - uint32_t size; // Size, including both 8-bit and 9-bit tasks -#ifdef SPI_3WIRE_PROTOCOL - uint32_t sizeExpandedTaskWithPadding; // Size of the expanded 9-bit/32-bit task. The expanded task starts at address spiTask->data + spiTask->size - spiTask->sizeExpandedTaskWithPadding; -#endif -#ifdef SPI_32BIT_COMMANDS - uint32_t cmd; -#else - uint8_t cmd; -#endif - uint32_t dmaSpiHeader; -#ifdef OFFLOAD_PIXEL_COPY_TO_DMA_CPP - uint8_t *fb; - uint8_t *prevFb; - uint16_t width; -#endif - uint8_t data[]; // Contains both 8-bit and 9-bit tasks back to back, 8-bit first, then 9-bit. - -#ifdef SPI_3WIRE_PROTOCOL - inline uint8_t *PayloadStart() { return data + (size - sizeExpandedTaskWithPadding); } - inline uint8_t *PayloadEnd() { return data + (size - SPI_9BIT_TASK_PADDING_BYTES); } - inline uint32_t PayloadSize() const { return sizeExpandedTaskWithPadding - SPI_9BIT_TASK_PADDING_BYTES; } - inline uint32_t *DmaSpiHeaderAddress() { return (uint32_t*)(PayloadStart()-4); } -#else - inline uint8_t *PayloadStart() { return data; } - inline uint8_t *PayloadEnd() { return data + size; } - inline uint32_t PayloadSize() const { return size; } - inline uint32_t *DmaSpiHeaderAddress() { return &dmaSpiHeader; } -#endif - -} SPITask; - -#define BEGIN_SPI_COMMUNICATION() do { spi->cs = BCM2835_SPI0_CS_TA | DISPLAY_SPI_DRIVE_SETTINGS; } while(0) -#define END_SPI_COMMUNICATION() do { \ - uint32_t cs; \ - while (!(((cs = spi->cs) ^ BCM2835_SPI0_CS_TA) & (BCM2835_SPI0_CS_DONE | BCM2835_SPI0_CS_TA))) /* While TA=1 and DONE=0*/ \ - { \ - if ((cs & (BCM2835_SPI0_CS_RXR | BCM2835_SPI0_CS_RXF))) \ - spi->cs = BCM2835_SPI0_CS_CLEAR_RX | BCM2835_SPI0_CS_TA | DISPLAY_SPI_DRIVE_SETTINGS; \ - } \ - spi->cs = BCM2835_SPI0_CS_CLEAR_RX | DISPLAY_SPI_DRIVE_SETTINGS; /* Clear TA and any pending bytes */ \ - } while(0) - -#define WAIT_SPI_FINISHED() do { \ - uint32_t cs; \ - while (!((cs = spi->cs) & BCM2835_SPI0_CS_DONE)) /* While DONE=0*/ \ - { \ - if ((cs & (BCM2835_SPI0_CS_RXR | BCM2835_SPI0_CS_RXF))) \ - spi->cs = BCM2835_SPI0_CS_CLEAR_RX | BCM2835_SPI0_CS_TA | DISPLAY_SPI_DRIVE_SETTINGS; \ - } \ - } while(0) - - -// A convenience for defining and dispatching SPI task bytes inline -#define SPI_TRANSFER(command, ...) do { \ - char data_buffer[] = { __VA_ARGS__ }; \ - SPITask *t = AllocTask(sizeof(data_buffer)); \ - t->cmd = (command); \ - memcpy(t->data, data_buffer, sizeof(data_buffer)); \ - CommitTask(t); \ - RunSPITask(t); \ - DoneTask(t); \ - } while(0) - -#define QUEUE_SPI_TRANSFER(command, ...) do { \ - char data_buffer[] = { __VA_ARGS__ }; \ - SPITask *t = AllocTask(sizeof(data_buffer)); \ - t->cmd = (command); \ - memcpy(t->data, data_buffer, sizeof(data_buffer)); \ - CommitTask(t); \ - } while(0) - -#ifdef DISPLAY_SPI_BUS_IS_16BITS_WIDE // For displays that have their command register set be 16 bits word size width (ILI9486) - -#define QUEUE_MOVE_CURSOR_TASK(cursor, pos) do { \ - SPITask *task = AllocTask(4); \ - task->cmd = (cursor); \ - task->data[0] = 0; \ - task->data[1] = (pos) >> 8; \ - task->data[2] = 0; \ - task->data[3] = (pos) & 0xFF; \ - bytesTransferred += 6; \ - CommitTask(task); \ - } while(0) - -#define QUEUE_SET_WRITE_WINDOW_TASK(cursor, x, endX) do { \ - SPITask *task = AllocTask(8); \ - task->cmd = (cursor); \ - task->data[0] = 0; \ - task->data[1] = (x) >> 8; \ - task->data[2] = 0; \ - task->data[3] = (x) & 0xFF; \ - task->data[4] = 0; \ - task->data[5] = (endX) >> 8; \ - task->data[6] = 0; \ - task->data[7] = (endX) & 0xFF; \ - bytesTransferred += 10; \ - CommitTask(task); \ - } while(0) - -#elif defined(DISPLAY_SET_CURSOR_IS_8_BIT) // For displays that have their set cursor commands be a uint8 instead of uint16 (SSD1351) - -#define QUEUE_SET_WRITE_WINDOW_TASK(cursor, x, endX) do { \ - SPITask *task = AllocTask(2); \ - task->cmd = (cursor); \ - task->data[0] = (x); \ - task->data[1] = (endX); \ - bytesTransferred += 3; \ - CommitTask(task); \ - } while(0) - -#else // Regular 8-bit interface with 16bits wide set cursor commands (most displays) - -#define QUEUE_MOVE_CURSOR_TASK(cursor, pos) do { \ - SPITask *task = AllocTask(2); \ - task->cmd = (cursor); \ - task->data[0] = (pos) >> 8; \ - task->data[1] = (pos) & 0xFF; \ - bytesTransferred += 3; \ - CommitTask(task); \ - } while(0) - -#define QUEUE_SET_WRITE_WINDOW_TASK(cursor, x, endX) do { \ - SPITask *task = AllocTask(4); \ - task->cmd = (cursor); \ - task->data[0] = (x) >> 8; \ - task->data[1] = (x) & 0xFF; \ - task->data[2] = (endX) >> 8; \ - task->data[3] = (endX) & 0xFF; \ - bytesTransferred += 5; \ - CommitTask(task); \ - } while(0) -#endif - -typedef struct SharedMemory -{ -#ifdef USE_DMA_TRANSFERS - volatile DMAControlBlock cb[2]; - volatile uint32_t dummyDMADestinationWriteAddress; - volatile uint32_t dmaTxChannel, dmaRxChannel; -#endif - volatile uint32_t queueHead; - volatile uint32_t queueTail; - volatile uint32_t spiBytesQueued; // Number of actual payload bytes in the queue - volatile uint32_t interruptsRaised; - volatile uintptr_t sharedMemoryBaseInPhysMemory; - volatile uint8_t buffer[]; -} SharedMemory; - -#ifdef KERNEL_MODULE -extern dma_addr_t spiTaskMemoryPhysical; -#define VIRT_TO_BUS(ptr) ((uintptr_t)(ptr) | 0xC0000000U) -#endif -extern SharedMemory *spiTaskMemory; -extern double spiUsecsPerByte; - -extern SharedMemory *dmaSourceMemory; // TODO: Optimize away the need to have this at all, instead DMA directly from SPI ring buffer if possible - -#ifdef STATISTICS -extern volatile uint64_t spiThreadIdleUsecs; -extern volatile uint64_t spiThreadSleepStartTime; -extern volatile int spiThreadSleeping; -#endif - -extern int mem_fd; - -#ifdef SPI_3WIRE_PROTOCOL - -// Converts the given SPI task in-place from an 8-bit task to a 9-bit task. -void Interleave8BitSPITaskTo9Bit(SPITask *task); - -// Converts the given SPI task in-place from a 16-bit task to a 32-bit task. -void Interleave16BitSPITaskTo32Bit(SPITask *task); - -// If the given display is a 3-wire SPI display (9 bits/task instead of 8 bits/task), this function computes the byte size of the 8-bit task when it is converted to a 9-bit task. -uint32_t NumBytesNeededFor9BitSPITask(uint32_t byteSizeFor8BitTask); - -// If the given display is a 3-wire SPI display with 32 bits bus width, this function computes the byte size of the task when it is converted to a 32-bit task. -uint32_t NumBytesNeededFor32BitSPITask(uint32_t byteSizeFor8BitTask); - -#endif - -static inline SPITask *AllocTask(uint32_t bytes) // Returns a pointer to a new SPI task block, called on main thread -{ -#ifdef SPI_3WIRE_PROTOCOL - // For 3-wire/9-bit tasks, store the converted task right at the end of the 8-bit task. -#ifdef SPI_32BIT_COMMANDS - uint32_t sizeExpandedTaskWithPadding = NumBytesNeededFor32BitSPITask(bytes) + SPI_9BIT_TASK_PADDING_BYTES; -#else - uint32_t sizeExpandedTaskWithPadding = NumBytesNeededFor9BitSPITask(bytes) + SPI_9BIT_TASK_PADDING_BYTES; -#endif - bytes += sizeExpandedTaskWithPadding; -#else -// const uint32_t totalBytesFor9BitTask = 0; -#endif - - uint32_t bytesToAllocate = sizeof(SPITask) + bytes;// + totalBytesFor9BitTask; - uint32_t tail = spiTaskMemory->queueTail; - uint32_t newTail = tail + bytesToAllocate; - // Is the new task too large to write contiguously into the ring buffer, that it's split into two parts? We never split, - // but instead write a sentinel at the end of the ring buffer, and jump the tail back to the beginning of the buffer and - // allocate the new task there. However in doing so, we must make sure that we don't write over the head marker. - if (newTail + sizeof(SPITask)/*Add extra SPITask size so that there will always be room for eob marker*/ >= SPI_QUEUE_SIZE) - { - uint32_t head = spiTaskMemory->queueHead; - // Write a sentinel, but wait for the head to advance first so that it is safe to write. - while(head > tail || head == 0/*Head must move > 0 so that we don't stomp on it*/) - { -#if defined(KERNEL_MODULE_CLIENT) && !defined(KERNEL_MODULE) - // Hack: Pump the kernel module to start transferring in case it has stopped. TODO: Remove this line: - if (!(spi->cs & BCM2835_SPI0_CS_TA)) spi->cs |= BCM2835_SPI0_CS_TA; - // Wait until there are no remaining bytes to process in the far right end of the buffer - we'll write an eob marker there as soon as the read pointer has cleared it. - // At this point the SPI queue may actually be quite empty, so don't sleep (except for now in kernel client app) - usleep(100); -#endif - head = spiTaskMemory->queueHead; - } - SPITask *endOfBuffer = (SPITask*)(spiTaskMemory->buffer + tail); - endOfBuffer->cmd = 0; // Use cmd=0x00 to denote "end of buffer, wrap to beginning" - __sync_synchronize(); - spiTaskMemory->queueTail = 0; - __sync_synchronize(); -#if !defined(KERNEL_MODULE_CLIENT) && !defined(KERNEL_MODULE) - if (spiTaskMemory->queueHead == tail) syscall(SYS_futex, &spiTaskMemory->queueTail, FUTEX_WAKE, 1, 0, 0, 0); // Wake the SPI thread if it was sleeping to get new tasks -#endif - tail = 0; - newTail = bytesToAllocate; - } - - // If the SPI task queue is full, wait for the SPI thread to process some tasks. This throttles the main thread to not run too fast. - uint32_t head = spiTaskMemory->queueHead; - while(head > tail && head <= newTail) - { -#if defined(KERNEL_MODULE_CLIENT) && !defined(KERNEL_MODULE) - // Hack: Pump the kernel module to start transferring in case it has stopped. TODO: Remove this line: - if (!(spi->cs & BCM2835_SPI0_CS_TA)) spi->cs |= BCM2835_SPI0_CS_TA; -#endif - usleep(100); // Since the SPI queue is full, we can afford to sleep a bit on the main thread without introducing lag. - head = spiTaskMemory->queueHead; - } - - SPITask *task = (SPITask*)(spiTaskMemory->buffer + tail); - task->size = bytes; -#ifdef SPI_3WIRE_PROTOCOL - task->sizeExpandedTaskWithPadding = sizeExpandedTaskWithPadding; -#endif -#ifdef OFFLOAD_PIXEL_COPY_TO_DMA_CPP - task->fb = &task->data[0]; - task->prevFb = 0; -#endif - return task; -} - -static inline void CommitTask(SPITask *task) // Advertises the given SPI task from main thread to worker, called on main thread -{ -#ifdef SPI_3WIRE_PROTOCOL -#ifdef SPI_32BIT_COMMANDS - Interleave16BitSPITaskTo32Bit(task); -#else - Interleave8BitSPITaskTo9Bit(task); -#endif -#endif - __sync_synchronize(); -#if !defined(KERNEL_MODULE_CLIENT) && !defined(KERNEL_MODULE) - uint32_t tail = spiTaskMemory->queueTail; -#endif - spiTaskMemory->queueTail = (uint32_t)((uint8_t*)task - spiTaskMemory->buffer) + sizeof(SPITask) + task->size; - __atomic_fetch_add(&spiTaskMemory->spiBytesQueued, task->PayloadSize()+1, __ATOMIC_RELAXED); - __sync_synchronize(); -#if !defined(KERNEL_MODULE_CLIENT) && !defined(KERNEL_MODULE) - if (spiTaskMemory->queueHead == tail) syscall(SYS_futex, &spiTaskMemory->queueTail, FUTEX_WAKE, 1, 0, 0, 0); // Wake the SPI thread if it was sleeping to get new tasks -#endif -} - -#ifdef USE_SPI_THREAD -#define IN_SINGLE_THREADED_MODE_RUN_TASK() ((void)0) -#else -#define IN_SINGLE_THREADED_MODE_RUN_TASK() { \ - SPITask *t = GetTask(); \ - RunSPITask(t); \ - DoneTask(t); \ -} -#endif - -int InitSPI(void); -void DeinitSPI(void); -void ExecuteSPITasks(void); -void RunSPITask(SPITask *task); -SPITask *GetTask(void); -void DoneTask(SPITask *task); -void DumpSPICS(uint32_t reg); -#ifdef RUN_WITH_REALTIME_THREAD_PRIORITY -void SetRealtimeThreadPriority(); -#endif +#pragma once + +#define BCM2835_GPIO_BASE 0x200000 // Address to GPIO register file +#define BCM2835_SPI0_BASE 0x204000 // Address to SPI0 register file +#define BCM2835_TIMER_BASE 0x3000 // Address to System Timer register file + +#define BCM2835_SPI0_CS_RXF 0x00100000 // Receive FIFO is full +#define BCM2835_SPI0_CS_RXR 0x00080000 // FIFO needs reading +#define BCM2835_SPI0_CS_TXD 0x00040000 // TXD TX FIFO can accept Data +#define BCM2835_SPI0_CS_RXD 0x00020000 // RXD RX FIFO contains Data +#define BCM2835_SPI0_CS_DONE 0x00010000 // Done transfer Done +#define BCM2835_SPI0_CS_ADCS 0x00000800 // Automatically Deassert Chip Select +#define BCM2835_SPI0_CS_INTR 0x00000400 // Fire interrupts on RXR? +#define BCM2835_SPI0_CS_INTD 0x00000200 // Fire interrupts on DONE? +#define BCM2835_SPI0_CS_DMAEN 0x00000100 // Enable DMA transfers? +#define BCM2835_SPI0_CS_TA 0x00000080 // Transfer Active +#define BCM2835_SPI0_CS_CLEAR 0x00000030 // Clear FIFO Clear RX and TX +#define BCM2835_SPI0_CS_CLEAR_RX 0x00000020 // Clear FIFO Clear RX +#define BCM2835_SPI0_CS_CLEAR_TX 0x00000010 // Clear FIFO Clear TX +#define BCM2835_SPI0_CS_CPOL 0x00000008 // Clock Polarity +#define BCM2835_SPI0_CS_CPHA 0x00000004 // Clock Phase +#define BCM2835_SPI0_CS_CS 0x00000003 // Chip Select + +#define BCM2835_SPI0_CS_RXF_SHIFT 20 +#define BCM2835_SPI0_CS_RXR_SHIFT 19 +#define BCM2835_SPI0_CS_TXD_SHIFT 18 +#define BCM2835_SPI0_CS_RXD_SHIFT 17 +#define BCM2835_SPI0_CS_DONE_SHIFT 16 +#define BCM2835_SPI0_CS_ADCS_SHIFT 11 +#define BCM2835_SPI0_CS_INTR_SHIFT 10 +#define BCM2835_SPI0_CS_INTD_SHIFT 9 +#define BCM2835_SPI0_CS_DMAEN_SHIFT 8 +#define BCM2835_SPI0_CS_TA_SHIFT 7 +#define BCM2835_SPI0_CS_CLEAR_RX_SHIFT 5 +#define BCM2835_SPI0_CS_CLEAR_TX_SHIFT 4 +#define BCM2835_SPI0_CS_CPOL_SHIFT 3 +#define BCM2835_SPI0_CS_CPHA_SHIFT 2 +#define BCM2835_SPI0_CS_CS_SHIFT 0 + +#define GPIO_SPI0_MOSI 10 // Pin P1-19, MOSI when SPI0 in use +#define GPIO_SPI0_MISO 9 // Pin P1-21, MISO when SPI0 in use +#define GPIO_SPI0_INTR 25 // Pin P1-22, INTR when SPI0 in use +#define GPIO_SPI0_CLK 11 // Pin P1-23, CLK when SPI0 in use +#define GPIO_SPI0_CE0 8 // Pin P1-24, CE0 when SPI0 in use +#define GPIO_SPI0_CE1 7 // Pin P1-26, CE1 when SPI0 in use + +typedef struct SharedMemory +{ +#ifdef USE_DMA_TRANSFERS + volatile DMAControlBlock cb[2]; + volatile uint32_t dummyDMADestinationWriteAddress; + volatile uint32_t dmaTxChannel, dmaRxChannel; +#endif + volatile uint32_t queueHead; + volatile uint32_t queueTail; + volatile uint32_t spiBytesQueued; // Number of actual payload bytes in the queue + volatile uint32_t interruptsRaised; + volatile uintptr_t sharedMemoryBaseInPhysMemory; + volatile uint8_t buffer[]; +} SharedMemory; + +extern SharedMemory *dmaSourceMemory; // TODO: Optimize away the need to have this at all, instead DMA directly from SPI ring buffer if possible + +extern SharedMemory *spiFlagemory; +extern SharedMemory *spiTaskMemory; +extern double spiUsecsPerByte; + +extern int mem_fd; + diff --git a/ssd1351.cpp b/ssd1351.cpp index 1417986..f153a01 100644 --- a/ssd1351.cpp +++ b/ssd1351.cpp @@ -2,7 +2,7 @@ #ifdef SSD1351 -#include "spi.h" +#include "spi_user.h" #include #include diff --git a/st7735r.cpp b/st7735r.cpp index 5bf4811..026e8f8 100644 --- a/st7735r.cpp +++ b/st7735r.cpp @@ -2,7 +2,7 @@ #if defined(ST7735R) || defined(ST7735S) || defined(ST7789) -#include "spi.h" +#include "spi_user.h" #include #include diff --git a/statistics.cpp b/statistics.cpp index 8d55a79..0a7ca3c 100644 --- a/statistics.cpp +++ b/statistics.cpp @@ -12,7 +12,7 @@ #include "tick.h" #include "text.h" -#include "spi.h" +#include "spi_user.h" #include "util.h" #include "mailbox.h" #include "mem_alloc.h" From 9a07fed692fd06a5cc3dfa492093a2e0e908ba1f Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Sat, 11 May 2019 17:46:24 +0000 Subject: [PATCH 05/52] both kernel and user code build -- kernal driver does nothing --- kernel/bcm2835_spi_display.c | 4 +--- spi.cpp | 26 -------------------------- 2 files changed, 1 insertion(+), 29 deletions(-) diff --git a/kernel/bcm2835_spi_display.c b/kernel/bcm2835_spi_display.c index 6ba0e5c..57279ee 100644 --- a/kernel/bcm2835_spi_display.c +++ b/kernel/bcm2835_spi_display.c @@ -69,7 +69,7 @@ static int p_mmap(struct file *filp, struct vm_area_struct *vma) static int p_open(struct inode *inode, struct file *filp) { mmap_info *info = kmalloc(sizeof(mmap_info), GFP_KERNEL); - info->data = (void*)spiFlagMemory; + //info->data = (void*)spiFlagMemory; filp->private_data = info; return 0; } @@ -103,8 +103,6 @@ uint32_t virt_to_bus_address(volatile void *virtAddress) } volatile int shuttingDown = 0; -dma_addr_t spiFlagMemoryPhysical = 0; - static uint32_t irqHandlerCookie = 0; static uint32_t irqRegistered = 0; diff --git a/spi.cpp b/spi.cpp index 059aa15..3d01c75 100644 --- a/spi.cpp +++ b/spi.cpp @@ -404,7 +404,6 @@ void RunSPITask(SPITask *task) #endif SharedMemory *spiTaskMemory = 0; -SharedMemory *spiFlagMemory = 0; volatile uint64_t spiThreadIdleUsecs = 0; volatile uint64_t spiThreadSleepStartTime = 0; volatile int spiThreadSleeping = 0; @@ -540,24 +539,6 @@ int InitSPI() spi->cs = BCM2835_SPI0_CS_CLEAR | DISPLAY_SPI_DRIVE_SETTINGS; // Initialize the Control and Status register to defaults: CS=0 (Chip Select), CPHA=0 (Clock Phase), CPOL=0 (Clock Polarity), CSPOL=0 (Chip Select Polarity), TA=0 (Transfer not active), and reset TX and RX queues. spi->clk = SPI_BUS_CLOCK_DIVISOR; // Clock Divider determines SPI bus speed, resulting speed=256MHz/clk - // Initialize SPI thread task buffer memory -#ifdef KERNEL_MODULE_CLIENT - spiFlagMemory = (SharedMemory*)mmap(NULL, sizeof(char), PROT_READ|PROT_WRITE, MAP_SHARED/* | MAP_NORESERVE | MAP_POPULATE | MAP_LOCKED*/, driverfd, 0); - if (spiFlagMemory == MAP_FAILED) FATAL_ERROR("Could not mmap SPI flag buffer!"); - printf("Got shared memory block %p\n", (const char *)spiFlagMemory); -#endif - -#ifdef KERNEL_MODULE - spiFlagMemory = (SharedMemory*)kmalloc(sizeof(char), GFP_KERNEL | GFP_DMA); - dmaSourceMemory = (SharedMemory*)dma_alloc_writecombine(0, sizeof(char), &spiFlagMemoryPhysical, GFP_KERNEL); - LOG("Allocated Flag memory: mem: %p, phys: %p", spiFlagMemory, (void*)spiFlagMemoryPhysical); - memset((void*)spiFlagMemory, 0, sizeof(char)); -#endif - -#if !defined(KERNEL_MODULE) && !defined(KERNEL_MODULE_CLIENT) - spiFlagMemory = (SharedMemory*)Malloc(sizeof(char), "spi.cpp shared flag memory"); -#endif - spiTaskMemory = (SharedMemory*)Malloc(SHARED_MEMORY_SIZE, "spi.cpp shared task memory"); spiTaskMemory->queueHead = spiTaskMemory->queueTail = spiTaskMemory->spiBytesQueued = 0; @@ -622,13 +603,6 @@ void DeinitSPI() } -#ifdef KERNEL_MODULE - kfree(spiFlagMemory); - spiFlagMemoryPhysical = 0; -#else free(spiTaskMemory); - free(spiFlagMemory); -#endif spiTaskMemory = 0; - spiFlagMemory = 0; } From 9f24b566e8caa7010d69a01513a9114d9fded386 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Sat, 11 May 2019 22:25:53 +0000 Subject: [PATCH 06/52] reduce kernal module to what is needed --- kernel/bcm2835_spi_display.c | 110 ++++++----------------------------- 1 file changed, 19 insertions(+), 91 deletions(-) diff --git a/kernel/bcm2835_spi_display.c b/kernel/bcm2835_spi_display.c index 57279ee..fab640c 100644 --- a/kernel/bcm2835_spi_display.c +++ b/kernel/bcm2835_spi_display.c @@ -1,111 +1,41 @@ -#include -#include +//#include +//#include #include -#include +//#include #include -#include -#include +//#include +//#include #include #include #include -#include -#include -#include +//#include +//#include +//#include #include -#include +//#include #include -#include -#include -#include -#include +//#include +//#include +//#include +//#include #include -//#include "../config.h" #include "../spi.h" #define SPI_BUS_PROC_ENTRY_FILENAME "bcm2835_spi_display_bus" +#define req(cnd) if (!(cnd)) { LOG("!!!%s!!!\n", #cnd);} -typedef struct mmap_info -{ - char *data; -} mmap_info; - -static void p_vm_open(struct vm_area_struct *vma) -{ -} - -static void p_vm_close(struct vm_area_struct *vma) -{ -} - -static int p_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - mmap_info *info = (mmap_info *)vma->vm_private_data; - if (info->data) - { - struct page *page = virt_to_page(info->data + vmf->pgoff*PAGE_SIZE); - get_page(page); - vmf->page = page; - } - return 0; -} - -static struct vm_operations_struct vm_ops = -{ - .open = p_vm_open, - .close = p_vm_close, - .fault = p_vm_fault, -}; - -static int p_mmap(struct file *filp, struct vm_area_struct *vma) -{ - vma->vm_ops = &vm_ops; - vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; - vma->vm_private_data = filp->private_data; - p_vm_open(vma); - return 0; -} - -static int p_open(struct inode *inode, struct file *filp) -{ - mmap_info *info = kmalloc(sizeof(mmap_info), GFP_KERNEL); - //info->data = (void*)spiFlagMemory; - filp->private_data = info; - return 0; -} - -static int p_release(struct inode *inode, struct file *filp) -{ - mmap_info *info; - info = filp->private_data; - kfree(info); - filp->private_data = NULL; - return 0; -} - -static const struct file_operations fops = -{ - .mmap = p_mmap, - .open = p_open, - .release = p_release, -}; +volatile int shuttingDown = 0; +static uint32_t irqHandlerCookie = 0; +static uint32_t irqRegistered = 0; static irqreturn_t irq_handler(int irq, void* dev_id) { + if(shuttingDown) return IRQ_HANDLED; + return IRQ_HANDLED; } -#define req(cnd) if (!(cnd)) { LOG("!!!%s!!!\n", #cnd);} - -uint32_t virt_to_bus_address(volatile void *virtAddress) -{ - return (uint32_t)virt_to_phys((void*)virtAddress) | 0x40000000U; -} - -volatile int shuttingDown = 0; -static uint32_t irqHandlerCookie = 0; -static uint32_t irqRegistered = 0; - int bcm2835_spi_display_init(void) { int ret = request_irq(84, irq_handler, IRQF_SHARED, "spi_handler", &irqHandlerCookie); @@ -125,8 +55,6 @@ void bcm2835_spi_display_exit(void) free_irq(84, &irqHandlerCookie); irqRegistered = 0; } - - remove_proc_entry(SPI_BUS_PROC_ENTRY_FILENAME, NULL); } module_init(bcm2835_spi_display_init); From 54ccff21ba5af39eb21fb61ead4a32dedb2c7570 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Fri, 17 May 2019 02:52:03 +0000 Subject: [PATCH 07/52] kernal part building again and outputting interrupts on screen touch --- kernel/bcm2835_spi_display.c | 255 ++++++++++++++++++++++++++++------- 1 file changed, 208 insertions(+), 47 deletions(-) diff --git a/kernel/bcm2835_spi_display.c b/kernel/bcm2835_spi_display.c index fab640c..9df4c0e 100644 --- a/kernel/bcm2835_spi_display.c +++ b/kernel/bcm2835_spi_display.c @@ -1,61 +1,222 @@ -//#include -//#include -#include -//#include -#include -//#include -//#include -#include -#include -#include -//#include -//#include -//#include +#include #include -//#include -#include -//#include -//#include -//#include -//#include -#include +#include +#include // Required for the GPIO functions +#include // Required for the IRQ code +#include // Using kobjects for the sysfs bindings +#include // Using the clock to measure time between button presses +#define DEBOUNCE_TIME 200 ///< The default bounce time -- 200ms #include "../spi.h" -#define SPI_BUS_PROC_ENTRY_FILENAME "bcm2835_spi_display_bus" -#define req(cnd) if (!(cnd)) { LOG("!!!%s!!!\n", #cnd);} +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Kevin Peck"); // Thanks to Derek Molloy! +MODULE_DESCRIPTION("Touch screen interrupt driver"); +MODULE_VERSION("0.1"); -volatile int shuttingDown = 0; -static uint32_t irqHandlerCookie = 0; -static uint32_t irqRegistered = 0; +static bool isRising = 0; ///< Rising edge is the default IRQ property +module_param(isRising, bool, S_IRUGO); ///< Param desc. S_IRUGO can be read/not changed +MODULE_PARM_DESC(isRising, " Rising edge = 1, Falling edge = 0 (default)"); ///< parameter description -static irqreturn_t irq_handler(int irq, void* dev_id) -{ - if(shuttingDown) return IRQ_HANDLED; - - return IRQ_HANDLED; +static unsigned int gpioTouch = GPIO_SPI0_INTR; +module_param(gpioTouch, uint, S_IRUGO); ///< Param desc. S_IRUGO can be read/not changed +MODULE_PARM_DESC(gpioTouch, " GPIO Touch number (default=GPIO_SPI0_INTR)"); ///< parameter description + +static bool isDebug = 0; +module_param(isDebug, bool, S_IRUGO); +MODULE_PARM_DESC(isDebug, " debug = 1, no-debug = 0 (default)"); + +static char gpioName[8] = "gpioXXX"; ///< Null terminated default string -- just in case +static int irqNumber; ///< Used to share the IRQ number within this file +static int numberPresses = 0; ///< For information, store the number of button presses +static bool isDebounce = 1; ///< Use to store the debounce state (on by default) +static struct timespec ts_last, ts_current, ts_diff; ///< timespecs from linux/time.h (has nano precision) + +/// Function prototype for the custom IRQ handler function -- see below for the implementation +static irq_handler_t tftgpio_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs); + +/** @brief A callback function to output the numberPresses variable + * @param kobj represents a kernel object device that appears in the sysfs filesystem + * @param attr the pointer to the kobj_attribute struct + * @param buf the buffer to which to write the number of presses + * @return return the total number of characters written to the buffer (excluding null) + */ +static ssize_t numberPresses_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ + return sprintf(buf, "%d\n", numberPresses); +} + +/** @brief A callback function to read in the numberPresses variable + * @param kobj represents a kernel object device that appears in the sysfs filesystem + * @param attr the pointer to the kobj_attribute struct + * @param buf the buffer from which to read the number of presses (e.g., reset to 0). + * @param count the number characters in the buffer + * @return return should return the total number of characters used from the buffer + */ +static ssize_t numberPresses_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count){ + sscanf(buf, "%du", &numberPresses); + return count; +} + +/** @brief Displays the last time the button was pressed -- manually output the date (no localization) */ +static ssize_t lastTime_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ + return sprintf(buf, "%.2lu:%.2lu:%.2lu:%.9lu \n", (ts_last.tv_sec/3600)%24, + (ts_last.tv_sec/60) % 60, ts_last.tv_sec % 60, ts_last.tv_nsec ); +} + +/** @brief Display the time difference in the form secs.nanosecs to 9 places */ +static ssize_t diffTime_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ + return sprintf(buf, "%lu.%.9lu\n", ts_diff.tv_sec, ts_diff.tv_nsec); +} + +/** @brief Displays if button debouncing is on or off */ +static ssize_t isDebounce_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ + return sprintf(buf, "%d\n", isDebounce); +} + +/** @brief Stores and sets the debounce state */ +static ssize_t isDebounce_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count){ + unsigned int temp; + sscanf(buf, "%du", &temp); // use a temp varable for correct int->bool + gpio_set_debounce(gpioTouch,0); + isDebounce = temp; + if(isDebounce) { gpio_set_debounce(gpioTouch, DEBOUNCE_TIME); + printk(KERN_INFO "TFT Display: Debounce on\n"); + } + else { gpio_set_debounce(gpioTouch, 0); // set the debounce time to 0 + printk(KERN_INFO "TFT Display: Debounce off\n"); + } + return count; } -int bcm2835_spi_display_init(void) -{ - int ret = request_irq(84, irq_handler, IRQF_SHARED, "spi_handler", &irqHandlerCookie); - if (ret != 0) printk("request_irq failed!"); - irqRegistered = 1; +/** Use these helper macros to define the name and access levels of the kobj_attributes + * The kobj_attribute has an attribute attr (name and mode), show and store function pointers + * The count variable is associated with the numberPresses variable and it is to be exposed + * with mode 0666 using the numberPresses_show and numberPresses_store functions above + */ +#undef VERIFY_OCTAL_PERMISSIONS +#define VERIFY_OCTAL_PERMISSIONS(perms) (perms) +static struct kobj_attribute count_attr = __ATTR(numberPresses, 0666, numberPresses_show, numberPresses_store); +static struct kobj_attribute debounce_attr = __ATTR(isDebounce, 0666, isDebounce_show, isDebounce_store); + +/** The __ATTR_RO macro defines a read-only attribute. There is no need to identify that the + * function is called _show, but it must be present. __ATTR_WO can be used for a write-only + * attribute but only in Linux 3.11.x on. + */ +static struct kobj_attribute time_attr = __ATTR_RO(lastTime); ///< the last time pressed kobject attr +static struct kobj_attribute diff_attr = __ATTR_RO(diffTime); ///< the difference in time attr + +/** The tft_attrs[] is an array of attributes that is used to create the attribute group below. + * The attr property of the kobj_attribute is used to extract the attribute struct + */ +static struct attribute *tft_attrs[] = { + &count_attr.attr, ///< The number of button presses + &time_attr.attr, ///< Time of the last button press in HH:MM:SS:NNNNNNNNN + &diff_attr.attr, ///< The difference in time between the last two presses + &debounce_attr.attr, ///< Is the debounce state true or false + NULL, +}; + +/** The attribute group uses the attribute array and a name, which is exposed on sysfs -- in this + * case it is gpio115, which is automatically defined in the tftDisplay_init() function below + * using the custom kernel parameter that can be passed when the module is loaded. + */ +static struct attribute_group attr_group = { + .name = gpioName, ///< The name is generated in tftDisplay_init() + .attrs = tft_attrs, ///< The attributes array defined just above +}; + +static struct kobject *tft_kobj; - return 0; +/** @brief The LKM initialization function + * The static keyword restricts the visibility of the function to within this C file. The __init + * macro means that for a built-in driver (not a LKM) the function is only used at initialization + * time and that it can be discarded and its memory freed up after that point. In this example this + * function sets up the GPIOs and the IRQ + * @return returns 0 if successful + */ +static int __init tftDisplay_init(void){ + int result = 0; + unsigned long IRQflags = IRQF_TRIGGER_FALLING; // The default is a falling-edge interrupt + + printk(KERN_INFO "TFT Disply: Initializing the Touch interrupt\n"); + sprintf(gpioName, "gpio%d", gpioTouch); // Create the gpioXXX name for /sys/tft/gpioXXX + + // create the kobject sysfs entry at /sys/tft + tft_kobj = kobject_create_and_add("tft", kernel_kobj->parent); // kernel_kobj points to /sys/kernel + if(!tft_kobj){ + printk(KERN_ALERT "TFT Display: failed to create kobject mapping\n"); + return -ENOMEM; + } + // add the attributes to /sys/tft/ -- for example, /sys/tft/gpio115/numberPresses + result = sysfs_create_group(tft_kobj, &attr_group); + if(result) { + printk(KERN_ALERT "TFT Display: failed to create sysfs group\n"); + kobject_put(tft_kobj); // clean up -- remove the kobject sysfs entry + return result; + } + getnstimeofday(&ts_last); // set the last time to be the current time + ts_diff = timespec_sub(ts_last, ts_last); // set the initial time difference to be 0 + + // the bool argument prevents the direction from being changed + gpio_request(gpioTouch, "sysfs"); // Set up the gpioTouch + gpio_direction_input(gpioTouch); // Set the button GPIO to be an input + gpio_set_debounce(gpioTouch, DEBOUNCE_TIME); // Debounce the button with a delay of 200ms + gpio_export(gpioTouch, false); // Causes gpio115 to appear in /sys/class/gpio + // the bool argument prevents the direction from being changed + + // Perform a quick test to see that the button is working as expected on LKM load + printk(KERN_INFO "TFT Display: The touch state is currently: %d\n", gpio_get_value(gpioTouch)); + + /// GPIO numbers and IRQ numbers are not the same! This function performs the mapping for us + irqNumber = gpio_to_irq(gpioTouch); + printk(KERN_INFO "TFT Display: The touch is mapped to IRQ: %d\n", irqNumber); + + if(isRising){ // If the kernel parameter isRising=0 is supplied + IRQflags = IRQF_TRIGGER_RISING; // Set the interrupt to be on the falling edge + } + // This next call requests an interrupt line + result = request_irq(irqNumber, // The interrupt number requested + (irq_handler_t) tftgpio_irq_handler, // The pointer to the handler function below + IRQflags, // Use the custom kernel param to set interrupt type + "tft_touch_handler", // Used in /proc/interrupts to identify the owner + NULL); // The *dev_id for shared interrupt lines, NULL is okay + return result; } -void bcm2835_spi_display_exit(void) -{ - shuttingDown = 1; - msleep(2000); +/** @brief The LKM cleanup function + * Similar to the initialization function, it is static. The __exit macro notifies that if this + * code is used for a built-in driver (not a LKM) that this function is not required. + */ +static void __exit tftDisplay_exit(void){ + printk(KERN_INFO "TFT Display: The touch was pressed %d times\n", numberPresses); + kobject_put(tft_kobj); // clean up -- remove the kobject sysfs entry + free_irq(irqNumber, NULL); // Free the IRQ number, no *dev_id required in this case + gpio_unexport(gpioTouch); // Unexport the Button GPIO + gpio_free(gpioTouch); // Free the Button GPIO + printk(KERN_INFO "TFT Display: Goodbye from the touch driver!\n"); +} - if (irqRegistered) - { - free_irq(84, &irqHandlerCookie); - irqRegistered = 0; - } +/** @brief The GPIO IRQ Handler function + * This function is a custom interrupt handler that is attached to the GPIO above. The same interrupt + * handler cannot be invoked concurrently as the interrupt line is masked out until the function is complete. + * This function is static as it should not be invoked directly from outside of this file. + * @param irq the IRQ number that is associated with the GPIO -- useful for logging. + * @param dev_id the *dev_id that is provided -- can be used to identify which device caused the interrupt + * Not used in this example as NULL is passed. + * @param regs h/w specific register values -- only really ever used for debugging. + * return returns IRQ_HANDLED if successful -- should return IRQ_NONE otherwise. + */ +static irq_handler_t tftgpio_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs){ + getnstimeofday(&ts_current); // Get the current time as ts_current + ts_diff = timespec_sub(ts_current, ts_last); // Determine the time difference between last 2 presses + ts_last = ts_current; // Store the current time as the last time ts_last + if(isDebug) printk(KERN_INFO "TFT Display: The touch state is currently: %d\n", gpio_get_value(gpioTouch)); + numberPresses++; // Global counter, will be outputted when the module is unloaded + return (irq_handler_t) IRQ_HANDLED; // Announce that the IRQ has been handled correctly } -module_init(bcm2835_spi_display_init); -module_exit(bcm2835_spi_display_exit); +// This next calls are mandatory -- they identify the initialization function +// and the cleanup function (as above). +module_init(tftDisplay_init); +module_exit(tftDisplay_exit); From 6495917fceb84bf1802b855cdae763c7fcf03fa7 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Fri, 17 May 2019 04:02:01 +0000 Subject: [PATCH 08/52] add hooks in client to read interrupt from kernel --- mpi3501.cpp | 4 ++++ spi.cpp | 28 +++++++++++++++++++++++++--- spi_user.h | 1 + 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/mpi3501.cpp b/mpi3501.cpp index 6509655..1e93b17 100644 --- a/mpi3501.cpp +++ b/mpi3501.cpp @@ -11,6 +11,10 @@ void ChipSelectHigh() { WAIT_SPI_FINISHED(); CLEAR_GPIO(GPIO_SPI0_CE0); // Enable Touch + if(hasInterrupt()) { + // TODO: Read touch display here on spi +fprintf( stderr, "."); + } SET_GPIO(GPIO_SPI0_CE0); // Disable Touch __sync_synchronize(); SET_GPIO(GPIO_SPI0_CE1); // Disable Display diff --git a/spi.cpp b/spi.cpp index 3d01c75..f8da36d 100644 --- a/spi.cpp +++ b/spi.cpp @@ -7,7 +7,7 @@ #include // bcm_host_get_peripheral_address, bcm_host_get_peripheral_size, bcm_host_get_sdram_address #include "config.h" #endif - + #include "config.h" #include "spi_user.h" #include "util.h" @@ -44,6 +44,9 @@ static uint32_t writeCounter = 0; } while(0) int mem_fd = -1; +int intr_fd = -1; +static int numberPress = 0; +static char numberAsString[21] = " "; volatile void *bcm2835 = 0; volatile GPIORegisterFile *gpio = 0; volatile SPIRegisterFile *spi = 0; @@ -52,6 +55,19 @@ volatile SPIRegisterFile *spi = 0; // that Pi 3 Model B does allow reading this as a u64 load, and even when unaligned, it is around 30% faster to do so compared to loading in parts "lo | (hi << 32)". volatile uint64_t *systemTimerRegister = 0; +bool hasInterrupt() { + int lastNumberPress = numberPress; + + if ((read(intr_fd, numberAsString, sizeof(numberAsString))) < 0) { + if (errno != EWOULDBLOCK) { + perror("read/intr_fd"); + } + } else { + numberPress = atoi(numberAsString); + } + return (numberPress != lastNumberPress); +} + void DumpSPICS(uint32_t reg) { PRINT_FLAG(BCM2835_SPI0_CS_CS); @@ -501,6 +517,10 @@ int InitSPI() systemTimerRegister = (volatile uint64_t*)((uintptr_t)bcm2835 + BCM2835_TIMER_BASE + 0x04); // Generates an unaligned 64-bit pointer, but seems to be fine. // TODO: On graceful shutdown, (ctrl-c signal?) close(mem_fd) + // Touch screen interrupt + intr_fd = open("/sys/tft/gpio25/numberPresses", O_RDONLY|O_NONBLOCK); + if (intr_fd < 0) FATAL_ERROR("can't open /sys/tft/gpio25/numberPresses (run as sudo)"); + uint32_t currentBcmCoreSpeed = MailboxRet2(0x00030002/*Get Clock Rate*/, 0x4/*CORE*/); uint32_t maxBcmCoreTurboSpeed = MailboxRet2(0x00030004/*Get Max Clock Rate*/, 0x4/*CORE*/); @@ -601,8 +621,10 @@ void DeinitSPI() close(mem_fd); mem_fd = -1; } - - + if (intr_fd >= 0) { + close(intr_fd); + intr_fd = -1; + } free(spiTaskMemory); spiTaskMemory = 0; } diff --git a/spi_user.h b/spi_user.h index 92901b4..413ef42 100644 --- a/spi_user.h +++ b/spi_user.h @@ -299,6 +299,7 @@ static inline void CommitTask(SPITask *task) // Advertises the given SPI task fr #endif int InitSPI(void); +bool hasInterrupt(); void DeinitSPI(void); void ExecuteSPITasks(void); void RunSPITask(SPITask *task); From f832499b0276e4bc793ed99461abcbe2c9189335 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Sat, 18 May 2019 03:24:37 +0000 Subject: [PATCH 09/52] user space experimental file writingwq --- mpi3501.cpp | 23 +++++++++++++++++++++-- spi.cpp | 3 ++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/mpi3501.cpp b/mpi3501.cpp index 1e93b17..6e9e1b9 100644 --- a/mpi3501.cpp +++ b/mpi3501.cpp @@ -7,13 +7,23 @@ #include #include +#include + +int fd_touch = -1; +static int counter = 0; +char buffer[20]; +short bufLen = 0; + void ChipSelectHigh() { WAIT_SPI_FINISHED(); CLEAR_GPIO(GPIO_SPI0_CE0); // Enable Touch - if(hasInterrupt()) { + if(hasInterrupt() && fd_touch >= 0) { // TODO: Read touch display here on spi -fprintf( stderr, "."); + counter = counter + 1; + bufLen = sprintf(buffer,"%04X %04X\n",fd_touch, counter); + lseek(fd_touch,0,SEEK_SET); + write(fd_touch,buffer,bufLen); } SET_GPIO(GPIO_SPI0_CE0); // Disable Touch __sync_synchronize(); @@ -24,6 +34,12 @@ fprintf( stderr, "."); void InitKeDeiV63() { + // Open output device + fd_touch = open("/dev/mpi3501_touch",O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ); + if (fd_touch < 0) { + perror("uio open:"); + } + // If a Reset pin is defined, toggle it briefly high->low->high to enable the device. Some devices do not have a reset pin, in which case compile with GPIO_TFT_RESET_PIN left undefined. #if defined(GPIO_TFT_RESET_PIN) && GPIO_TFT_RESET_PIN >= 0 printf("Resetting display at reset GPIO pin %d\n", GPIO_TFT_RESET_PIN); @@ -146,6 +162,9 @@ void TurnDisplayOn() void DeinitSPIDisplay() { + if(fd_touch >=0) { + close(fd_touch); + } ClearScreen(); TurnDisplayOff(); } diff --git a/spi.cpp b/spi.cpp index f8da36d..8e23391 100644 --- a/spi.cpp +++ b/spi.cpp @@ -57,7 +57,8 @@ volatile uint64_t *systemTimerRegister = 0; bool hasInterrupt() { int lastNumberPress = numberPress; - + + lseek(intr_fd,0,SEEK_SET); if ((read(intr_fd, numberAsString, sizeof(numberAsString))) < 0) { if (errno != EWOULDBLOCK) { perror("read/intr_fd"); From 9f90f5eaf300dec92967ae1954640ce564d1c6bb Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Mon, 20 May 2019 12:30:26 +0000 Subject: [PATCH 10/52] read/write working nicely with kernal! --- spi.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spi.cpp b/spi.cpp index 8e23391..925e3dc 100644 --- a/spi.cpp +++ b/spi.cpp @@ -58,12 +58,13 @@ volatile uint64_t *systemTimerRegister = 0; bool hasInterrupt() { int lastNumberPress = numberPress; - lseek(intr_fd,0,SEEK_SET); + if(intr_fd<0) return false; if ((read(intr_fd, numberAsString, sizeof(numberAsString))) < 0) { if (errno != EWOULDBLOCK) { perror("read/intr_fd"); } } else { + lseek(intr_fd,0,SEEK_SET); numberPress = atoi(numberAsString); } return (numberPress != lastNumberPress); @@ -520,7 +521,7 @@ int InitSPI() // Touch screen interrupt intr_fd = open("/sys/tft/gpio25/numberPresses", O_RDONLY|O_NONBLOCK); - if (intr_fd < 0) FATAL_ERROR("can't open /sys/tft/gpio25/numberPresses (run as sudo)"); + //if (intr_fd < 0) FATAL_ERROR("can't open /sys/tft/gpio25/numberPresses (run as sudo)"); uint32_t currentBcmCoreSpeed = MailboxRet2(0x00030002/*Get Clock Rate*/, 0x4/*CORE*/); uint32_t maxBcmCoreTurboSpeed = MailboxRet2(0x00030004/*Get Max Clock Rate*/, 0x4/*CORE*/); From cffb099f9690699223b510c65410992b829284c7 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Tue, 21 May 2019 10:02:31 +0000 Subject: [PATCH 11/52] kind of works but there is a bug --- XPT2046.cpp | 245 ++++++++++++++++++++++++++++++++++++++++++++++++++++ XPT2046.h | 106 +++++++++++++++++++++++ mpi3501.cpp | 31 +++---- 3 files changed, 363 insertions(+), 19 deletions(-) create mode 100644 XPT2046.cpp create mode 100644 XPT2046.h diff --git a/XPT2046.cpp b/XPT2046.cpp new file mode 100644 index 0000000..e9d7a66 --- /dev/null +++ b/XPT2046.cpp @@ -0,0 +1,245 @@ +/** + * @file XPT2046.cpp + * @date 19.02.2016 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is originally part of the XPT2046 driver for Arduino. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "XPT2046.h" + +#define XPT2046_CFG_START 1<<7 + +#define XPT2046_CFG_MUX(v) ((v&0b111) << (4)) + +#define XPT2046_CFG_8BIT 1<<3 +#define XPT2046_CFG_12BIT (0) + +#define XPT2046_CFG_SER 1<<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 + +XPT2046::XPT2046() { + spi_cs = 0; + z_average = 0; + tcfifo = "/tmp/TCfifo"; + + _maxValue = 0x0fff; + _width = 480; + _height = 320; + _rotation = 0; + + _minX = 0; + _minY = 0; + + _maxX = _maxValue; + _maxY = _maxValue; + + _lastX = -1; + _lastY = -1; + + _minChange = 10; + + spi_cs = + 1 << 0 | //Chip select 1 + 0 << 3 | //low idle clock polarity + 0 << 6 | //chip select active low + 0 << 22 | //chip select low polarity + 0 << 8 | //DMA disabled + 0 << 11; //Manual chip select + + + // FIFO file path + // Creating the named file(FIFO) + // mkfifo(, ) + mkfifo(tcfifo, 0666); +} + + +XPT2046::~XPT2046() { + +} + +int XPT2046::SpiWriteAndRead(unsigned char *data, int length) +{ + uint32_t old_spi_cs = spi->cs; + + for (int i = 0; i < length; i++) + { + spi->cs = spi_cs | 1 << 7;//Set TA to high + + while (!(spi->cs & (1 << 18))) ; //Poll TX Fifo until it has space + + spi->fifo = data[i]; + + while(! (spi->cs & (1 << 17))) ; //Wait until RX FIFO contains data + + //while(!(spi->cs & (1 << 16))); //Wait until DONE + + uint32_t spi_fifo = spi->fifo; + + spi->cs = (spi_cs & (~(1 << 7))) | 1 << 5 | 1 << 4; //Set TA to LOW //Clear Fifox + + data[i] = spi_fifo; + } + + return 0; +} + +void XPT2046::read_touchscreen() { + + uint32_t old_spi_cs = spi->cs; + uint32_t old_spi_clk = spi->clk; + //printBits(sizeof(old_spi_cs), (void*)&(old_spi_cs)); + spi->clk = 256; + SET_GPIO_MODE(GPIO_SPI0_CE0, 0x00); + // SET_GPIO_MODE(GPIO_SPI0_CE1, 0x04); + // touch on low + + uint16_t x, y, z; + read(&x, &y, &z); + if (abs(x - _lastX) > _minChange || abs(y - _lastY) > _minChange) { + _lastX = x; + _lastY = y; + } + + if (z > 100) + { + char output[30] = ""; + sprintf(output, "x:%d, y:%d, z:%d\n", x, y, z); + int fd = open(tcfifo, O_WRONLY); + write(fd, output, strlen(output) + 1); + close(fd); + } + + + //spi->cs = (old_spi_cs | 1 << 4 | 1 << 5) & (~(1 << 7)); //Clear Fifos and TA + spi->cs = old_spi_cs; + spi->clk = old_spi_clk; + + SET_GPIO_MODE(GPIO_SPI0_CE0, 0x04); + //SET_GPIO_MODE(GPIO_SPI0_CE1, 0x00); +} + +void XPT2046::setRotation(uint8_t m) { + _rotation = m % 4; +} + +void XPT2046::setCalibration(uint16_t minX, uint16_t minY, uint16_t maxX, uint16_t maxY) { + _minX = minX; + _minY = minY; + _maxX = maxX; + _maxY = maxY; +} + +void XPT2046::read(uint16_t * oX, uint16_t * oY, uint16_t * oZ) { + uint16_t x, y; + readRaw(&x, &y, oZ); + + uint32_t cX = x; + uint32_t cY = y; + + if(cX < _minX) { + cX = 0; + } else if(cX > _maxX) { + cX = _width; + } else { + cX -= _minX; + cX = ((cX << 8) / (((_maxX - _minX) << 8) / (_width << 8)) )>> 8; + } + + if(cY < _minY) { + cY = 0; + } else if(cY > _maxY) { + cY = _height; + } else { + cY -= _minY; + cY = ((cY << 8) / (((_maxY - _minY) << 8) / (_height << 8))) >> 8; + } + + *oX = cX; + *oY = cY; + *oZ = std::max(0,4096 - (int)(*oZ)); +} + +void XPT2046::readRaw(uint16_t * oX, uint16_t * oY, uint16_t * oZ) { + uint32_t x = 0; + uint32_t y = 0; + uint32_t z1 = 0; + uint32_t z2 = 0; + uint8_t i = 0; + + for(; i < 15; i++) { + // SPI requires 32bit alignment + 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 + }; + + SpiWriteAndRead(buf, 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; + } + + if(i == 0) { + *oX = 0; + *oY = 0; + *oZ = 0; + return; + } + + x /= i; + y /= i; + z1 /= i; + z2 /= i; + + switch(_rotation) { + case 0: + default: + break; + case 1: + x = (_maxValue - x); + y = (_maxValue - y); + break; + case 2: + y = (_maxValue - y); + break; + case 3: + x = (_maxValue - x); + break; + } + + int z = z1 + _maxValue - z2; + + *oX = x; + *oY = y; + *oZ = z2; +} diff --git a/XPT2046.h b/XPT2046.h new file mode 100644 index 0000000..e772563 --- /dev/null +++ b/XPT2046.h @@ -0,0 +1,106 @@ +/** + * @file XPT2046.h + * @date 19.02.2016 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the XPT2046 driver for Arduino. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef XPT2046_H_ +#define XPT2046_H_ + +#include +#include //Needed for SPI port +#include //Needed for SPI port +#include //Needed for SPI port +#include +#include +#include +#include +#include +#include +#include + +#include "spi_user.h" + +class XPT2046 { + public: + + XPT2046(); + + ~XPT2046(); + + int SpiWriteAndRead(unsigned char *data, int length); + + void read_touchscreen(); + + void setRotation(uint8_t m); + void setCalibration(uint16_t minX, uint16_t minY, uint16_t maxX, uint16_t maxY); + + void read(uint16_t * oX, uint16_t * oY, uint16_t * oZ); + void readRaw(uint16_t * oX, uint16_t * oY, uint16_t * oZ); + + static void printBits(size_t const size, void const * const ptr) + { + unsigned char *b = (unsigned char*) ptr; + unsigned char byte; + int i, j; + + for (i = size - 1; i >= 0; i--) + { + for (j = 7; j >= 0; j--) + { + byte = (b[i] >> j) & 1; + printf("%u", byte); + } + printf(" "); + } + puts(""); + } + + protected: + + uint16_t _width; + uint16_t _height; + + uint16_t _rotation; + + uint16_t _minX; + uint16_t _minY; + + uint16_t _maxX; + uint16_t _maxY; + + uint16_t _maxValue; + + int _lastX; + int _lastY; + + uint16_t _minChange; + + uint32_t spi_cs ; + + uint32_t z_average ; + + const char * tcfifo ; +}; + + + +#endif /* XPT2046_H_ */ diff --git a/mpi3501.cpp b/mpi3501.cpp index 6e9e1b9..4d6dac6 100644 --- a/mpi3501.cpp +++ b/mpi3501.cpp @@ -3,13 +3,14 @@ #ifdef MPI3501 #include "spi_user.h" +#include "XPT2046.h" #include #include #include -int fd_touch = -1; +XPT2046 touch; static int counter = 0; char buffer[20]; short bufLen = 0; @@ -17,29 +18,24 @@ short bufLen = 0; void ChipSelectHigh() { WAIT_SPI_FINISHED(); - CLEAR_GPIO(GPIO_SPI0_CE0); // Enable Touch - if(hasInterrupt() && fd_touch >= 0) { - // TODO: Read touch display here on spi - counter = counter + 1; - bufLen = sprintf(buffer,"%04X %04X\n",fd_touch, counter); - lseek(fd_touch,0,SEEK_SET); - write(fd_touch,buffer,bufLen); + SET_GPIO(GPIO_SPI0_CE1); // Disable Display + __sync_synchronize(); + if(hasInterrupt()) { + touch.read_touchscreen(); + } else { + CLEAR_GPIO(GPIO_SPI0_CE0); // Enable Touch + SET_GPIO(GPIO_SPI0_CE0); // Disable Touch } - SET_GPIO(GPIO_SPI0_CE0); // Disable Touch __sync_synchronize(); - SET_GPIO(GPIO_SPI0_CE1); // Disable Display CLEAR_GPIO(GPIO_SPI0_CE1); // Enable Display __sync_synchronize(); } void InitKeDeiV63() { - // Open output device - fd_touch = open("/dev/mpi3501_touch",O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ); - if (fd_touch < 0) { - perror("uio open:"); - } - + // output device + touch = XPT2046(); + // If a Reset pin is defined, toggle it briefly high->low->high to enable the device. Some devices do not have a reset pin, in which case compile with GPIO_TFT_RESET_PIN left undefined. #if defined(GPIO_TFT_RESET_PIN) && GPIO_TFT_RESET_PIN >= 0 printf("Resetting display at reset GPIO pin %d\n", GPIO_TFT_RESET_PIN); @@ -162,9 +158,6 @@ void TurnDisplayOn() void DeinitSPIDisplay() { - if(fd_touch >=0) { - close(fd_touch); - } ClearScreen(); TurnDisplayOff(); } From 61424ad8e22f612adea04d9c15524d05f2701e49 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Sat, 25 May 2019 14:06:25 +0000 Subject: [PATCH 12/52] alright, data flows but values not right. Also, touch only responsive while screen is updating --- XPT2046.cpp | 24 +++++++++++------------- XPT2046.h | 3 ++- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/XPT2046.cpp b/XPT2046.cpp index e9d7a66..6d22a8b 100644 --- a/XPT2046.cpp +++ b/XPT2046.cpp @@ -75,12 +75,11 @@ XPT2046::XPT2046() { // FIFO file path // Creating the named file(FIFO) // mkfifo(, ) - mkfifo(tcfifo, 0666); + mkfifo(tcfifo, 0666); } XPT2046::~XPT2046() { - } int XPT2046::SpiWriteAndRead(unsigned char *data, int length) @@ -110,16 +109,16 @@ int XPT2046::SpiWriteAndRead(unsigned char *data, int length) } void XPT2046::read_touchscreen() { - + uint16_t x, y, z; + uint32_t old_spi_cs = spi->cs; uint32_t old_spi_clk = spi->clk; //printBits(sizeof(old_spi_cs), (void*)&(old_spi_cs)); spi->clk = 256; - SET_GPIO_MODE(GPIO_SPI0_CE0, 0x00); - // SET_GPIO_MODE(GPIO_SPI0_CE1, 0x04); + //SET_GPIO_MODE(GPIO_SPI0_CE0, 0x00); + //SET_GPIO_MODE(GPIO_SPI0_CE1, 0x04); // touch on low - - uint16_t x, y, z; + read(&x, &y, &z); if (abs(x - _lastX) > _minChange || abs(y - _lastY) > _minChange) { _lastX = x; @@ -129,18 +128,17 @@ void XPT2046::read_touchscreen() { if (z > 100) { char output[30] = ""; + fd = open(tcfifo, O_WRONLY | O_NONBLOCK); sprintf(output, "x:%d, y:%d, z:%d\n", x, y, z); - int fd = open(tcfifo, O_WRONLY); - write(fd, output, strlen(output) + 1); - close(fd); + write(fd, output, strlen(output) + 1); + close(fd); } - //spi->cs = (old_spi_cs | 1 << 4 | 1 << 5) & (~(1 << 7)); //Clear Fifos and TA + spi->cs = (old_spi_cs | 1 << 4 | 1 << 5) & (~(1 << 7)); //Clear Fifos and TA spi->cs = old_spi_cs; spi->clk = old_spi_clk; - - SET_GPIO_MODE(GPIO_SPI0_CE0, 0x04); + //SET_GPIO_MODE(GPIO_SPI0_CE0, 0x04); //SET_GPIO_MODE(GPIO_SPI0_CE1, 0x00); } diff --git a/XPT2046.h b/XPT2046.h index e772563..a9619d0 100644 --- a/XPT2046.h +++ b/XPT2046.h @@ -91,7 +91,8 @@ class XPT2046 { int _lastX; int _lastY; - + int fd; + uint16_t _minChange; uint32_t spi_cs ; From 6f413a42b20a9210b5d0520689918fb659c8f9e6 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Sun, 26 May 2019 12:11:09 +0000 Subject: [PATCH 13/52] no blank screen, looks good! Only get one measurement between cold resets from touch screen --- XPT2046.cpp | 2 +- mpi3501.cpp | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/XPT2046.cpp b/XPT2046.cpp index 6d22a8b..f939e93 100644 --- a/XPT2046.cpp +++ b/XPT2046.cpp @@ -135,7 +135,7 @@ void XPT2046::read_touchscreen() { } - spi->cs = (old_spi_cs | 1 << 4 | 1 << 5) & (~(1 << 7)); //Clear Fifos and TA + //spi->cs = (old_spi_cs | 1 << 4 | 1 << 5) & (~(1 << 7)); //Clear Fifos and TA spi->cs = old_spi_cs; spi->clk = old_spi_clk; //SET_GPIO_MODE(GPIO_SPI0_CE0, 0x04); diff --git a/mpi3501.cpp b/mpi3501.cpp index 4d6dac6..5480406 100644 --- a/mpi3501.cpp +++ b/mpi3501.cpp @@ -19,14 +19,13 @@ void ChipSelectHigh() { WAIT_SPI_FINISHED(); SET_GPIO(GPIO_SPI0_CE1); // Disable Display + CLEAR_GPIO(GPIO_SPI0_CE0); // Enable Touch __sync_synchronize(); if(hasInterrupt()) { touch.read_touchscreen(); - } else { - CLEAR_GPIO(GPIO_SPI0_CE0); // Enable Touch - SET_GPIO(GPIO_SPI0_CE0); // Disable Touch + __sync_synchronize(); } - __sync_synchronize(); + SET_GPIO(GPIO_SPI0_CE0); // Disable Touch CLEAR_GPIO(GPIO_SPI0_CE1); // Enable Display __sync_synchronize(); } From 90b3d99542eb5e5960756f8b7a83c30a3e8bc8d9 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Tue, 28 May 2019 11:31:17 +0000 Subject: [PATCH 14/52] it is now WORKING! But, is way too slow. Appears that interrupt must be enabled after aquisition by some delay. Need to find suitable delay. --- XPT2046.cpp | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/XPT2046.cpp b/XPT2046.cpp index f939e93..0bbb28f 100644 --- a/XPT2046.cpp +++ b/XPT2046.cpp @@ -84,8 +84,6 @@ XPT2046::~XPT2046() { int XPT2046::SpiWriteAndRead(unsigned char *data, int length) { - uint32_t old_spi_cs = spi->cs; - for (int i = 0; i < length; i++) { spi->cs = spi_cs | 1 << 7;//Set TA to high @@ -96,16 +94,14 @@ int XPT2046::SpiWriteAndRead(unsigned char *data, int length) while(! (spi->cs & (1 << 17))) ; //Wait until RX FIFO contains data - //while(!(spi->cs & (1 << 16))); //Wait until DONE - - uint32_t spi_fifo = spi->fifo; + uint32_t spi_fifo = spi->fifo; - spi->cs = (spi_cs & (~(1 << 7))) | 1 << 5 | 1 << 4; //Set TA to LOW //Clear Fifox + spi->cs = (spi_cs & (~(1 << 7))) | 1 << 5 | 1 << 4; //Set TA to LOW //Clear Fifo data[i] = spi_fifo; } - - return 0; + + return 0; } void XPT2046::read_touchscreen() { @@ -115,10 +111,8 @@ void XPT2046::read_touchscreen() { uint32_t old_spi_clk = spi->clk; //printBits(sizeof(old_spi_cs), (void*)&(old_spi_cs)); spi->clk = 256; - //SET_GPIO_MODE(GPIO_SPI0_CE0, 0x00); - //SET_GPIO_MODE(GPIO_SPI0_CE1, 0x04); - // touch on low + // touch on low read(&x, &y, &z); if (abs(x - _lastX) > _minChange || abs(y - _lastY) > _minChange) { _lastX = x; @@ -133,13 +127,10 @@ void XPT2046::read_touchscreen() { write(fd, output, strlen(output) + 1); close(fd); } - - + //spi->cs = (old_spi_cs | 1 << 4 | 1 << 5) & (~(1 << 7)); //Clear Fifos and TA spi->cs = old_spi_cs; spi->clk = old_spi_clk; - //SET_GPIO_MODE(GPIO_SPI0_CE0, 0x04); - //SET_GPIO_MODE(GPIO_SPI0_CE1, 0x00); } void XPT2046::setRotation(uint8_t m) { @@ -192,14 +183,16 @@ void XPT2046::readRaw(uint16_t * oX, uint16_t * oY, uint16_t * oZ) { for(; i < 15; i++) { // SPI requires 32bit alignment - uint8_t buf[12] = { + uint8_t buf[15] = { (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 + (XPT2046_CFG_START | XPT2046_CFG_12BIT | XPT2046_CFG_DFR | XPT2046_CFG_MUX(XPT2046_MUX_Z2)| XPT2046_CFG_PWR(3)), 0x00, 0x00, + // re-enable interrupt + (XPT2046_CFG_START | XPT2046_CFG_12BIT | XPT2046_CFG_DFR | XPT2046_CFG_MUX(0)| XPT2046_CFG_PWR(0)), 0x00, 0x00 }; - SpiWriteAndRead(buf, 12); + SpiWriteAndRead(buf, 15); y += (buf[1] << 8 | buf[2])>>3; x += (buf[4] << 8 | buf[5])>>3; From dcf2b7172909dbcce7662f29e40f45ac295562ea Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Thu, 30 May 2019 01:16:12 +0000 Subject: [PATCH 15/52] kind of working, stil feedback loop on interrupt slows it down --- XPT2046.cpp | 35 ++++++++++++++++++++++++++++------- XPT2046.h | 2 ++ mpi3501.cpp | 4 ++++ 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/XPT2046.cpp b/XPT2046.cpp index 0bbb28f..948112c 100644 --- a/XPT2046.cpp +++ b/XPT2046.cpp @@ -71,7 +71,7 @@ XPT2046::XPT2046() { 0 << 8 | //DMA disabled 0 << 11; //Manual chip select - + interruptEnabled = false; // FIFO file path // Creating the named file(FIFO) // mkfifo(, ) @@ -103,7 +103,23 @@ int XPT2046::SpiWriteAndRead(unsigned char *data, int length) return 0; } +bool XPT2046::armInterrupt() { +/* + uint8_t i = 0; + // SPI requires 32bit alignment + uint8_t buf[3] = { + // re-enable interrupt + (XPT2046_CFG_START | XPT2046_CFG_12BIT | XPT2046_CFG_DFR | XPT2046_CFG_MUX(XPT2046_MUX_Z2)| XPT2046_CFG_PWR(0)), 0x00, 0x00 + }; + */ + if(interruptEnabled) return false; + + //SpiWriteAndRead(buf, 3); + interruptEnabled = true; + + return true; +} void XPT2046::read_touchscreen() { uint16_t x, y, z; @@ -181,18 +197,16 @@ void XPT2046::readRaw(uint16_t * oX, uint16_t * oY, uint16_t * oZ) { uint32_t z2 = 0; uint8_t i = 0; - for(; i < 15; i++) { + for(; i < 4; i++) { // Sampling // SPI requires 32bit alignment - uint8_t buf[15] = { + 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, - // re-enable interrupt - (XPT2046_CFG_START | XPT2046_CFG_12BIT | XPT2046_CFG_DFR | XPT2046_CFG_MUX(0)| XPT2046_CFG_PWR(0)), 0x00, 0x00 + (XPT2046_CFG_START | XPT2046_CFG_12BIT | XPT2046_CFG_DFR | XPT2046_CFG_MUX(XPT2046_MUX_Z2)| XPT2046_CFG_PWR(3)), 0x00, 0x00 }; - SpiWriteAndRead(buf, 15); + SpiWriteAndRead(buf, 12); y += (buf[1] << 8 | buf[2])>>3; x += (buf[4] << 8 | buf[5])>>3; @@ -200,6 +214,13 @@ void XPT2046::readRaw(uint16_t * oX, uint16_t * oY, uint16_t * oZ) { z2 += (buf[10] << 8 | buf[11])>>3; } + // SPI requires 32bit alignment + uint8_t buf[3] = { + // re-enable interrupt + (XPT2046_CFG_START | XPT2046_CFG_12BIT | XPT2046_CFG_DFR | XPT2046_CFG_MUX(XPT2046_MUX_Z2)| XPT2046_CFG_PWR(0)), 0x00, 0x00 + }; + SpiWriteAndRead(buf, 3); + if(i == 0) { *oX = 0; *oY = 0; diff --git a/XPT2046.h b/XPT2046.h index a9619d0..0b34744 100644 --- a/XPT2046.h +++ b/XPT2046.h @@ -49,6 +49,7 @@ class XPT2046 { int SpiWriteAndRead(unsigned char *data, int length); void read_touchscreen(); + bool armInterrupt(); void setRotation(uint8_t m); void setCalibration(uint16_t minX, uint16_t minY, uint16_t maxX, uint16_t maxY); @@ -100,6 +101,7 @@ class XPT2046 { uint32_t z_average ; const char * tcfifo ; + bool interruptEnabled ; }; diff --git a/mpi3501.cpp b/mpi3501.cpp index 5480406..c702ce7 100644 --- a/mpi3501.cpp +++ b/mpi3501.cpp @@ -24,6 +24,10 @@ void ChipSelectHigh() if(hasInterrupt()) { touch.read_touchscreen(); __sync_synchronize(); + } else { + if(touch.armInterrupt()) { + __sync_synchronize(); + } } SET_GPIO(GPIO_SPI0_CE0); // Disable Touch CLEAR_GPIO(GPIO_SPI0_CE1); // Enable Display From 1dcc1a9e43b699f87d49a77a340920637b9f1a05 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Fri, 31 May 2019 01:16:20 +0000 Subject: [PATCH 16/52] it works! touch screen and fast driver. --- XPT2046.cpp | 43 ++++++++++++++++--------------------------- XPT2046.h | 5 ++--- mpi3501.cpp | 14 ++++++++------ 3 files changed, 26 insertions(+), 36 deletions(-) diff --git a/XPT2046.cpp b/XPT2046.cpp index 948112c..5e8e3cd 100644 --- a/XPT2046.cpp +++ b/XPT2046.cpp @@ -42,6 +42,8 @@ #define XPT2046_MUX_Z1 0b011 #define XPT2046_MUX_Z2 0b100 +#define INTERRUPTDEC 10 + XPT2046::XPT2046() { spi_cs = 0; z_average = 0; @@ -63,6 +65,8 @@ XPT2046::XPT2046() { _minChange = 10; + interruptpoll = INTERRUPTDEC; + spi_cs = 1 << 0 | //Chip select 1 0 << 3 | //low idle clock polarity @@ -71,8 +75,7 @@ XPT2046::XPT2046() { 0 << 8 | //DMA disabled 0 << 11; //Manual chip select - interruptEnabled = false; - // FIFO file path + // FIFO file path // Creating the named file(FIFO) // mkfifo(, ) mkfifo(tcfifo, 0666); @@ -103,24 +106,8 @@ int XPT2046::SpiWriteAndRead(unsigned char *data, int length) return 0; } -bool XPT2046::armInterrupt() { -/* - uint8_t i = 0; - // SPI requires 32bit alignment - uint8_t buf[3] = { - // re-enable interrupt - (XPT2046_CFG_START | XPT2046_CFG_12BIT | XPT2046_CFG_DFR | XPT2046_CFG_MUX(XPT2046_MUX_Z2)| XPT2046_CFG_PWR(0)), 0x00, 0x00 - }; - */ - if(interruptEnabled) return false; - - //SpiWriteAndRead(buf, 3); - interruptEnabled = true; - - return true; -} -void XPT2046::read_touchscreen() { +void XPT2046::read_touchscreen(bool interruptEnable) { uint16_t x, y, z; uint32_t old_spi_cs = spi->cs; @@ -134,7 +121,16 @@ void XPT2046::read_touchscreen() { _lastX = x; _lastY = y; } - + + if(interruptEnable) { + // SPI requires 32bit alignment + uint8_t buf[3] = { + // re-enable interrupt + (XPT2046_CFG_START | XPT2046_CFG_12BIT | XPT2046_CFG_DFR | XPT2046_CFG_MUX(XPT2046_MUX_Z2)| XPT2046_CFG_PWR(0)), 0x00, 0x00 + }; + SpiWriteAndRead(buf, 3); + } + if (z > 100) { char output[30] = ""; @@ -214,13 +210,6 @@ void XPT2046::readRaw(uint16_t * oX, uint16_t * oY, uint16_t * oZ) { z2 += (buf[10] << 8 | buf[11])>>3; } - // SPI requires 32bit alignment - uint8_t buf[3] = { - // re-enable interrupt - (XPT2046_CFG_START | XPT2046_CFG_12BIT | XPT2046_CFG_DFR | XPT2046_CFG_MUX(XPT2046_MUX_Z2)| XPT2046_CFG_PWR(0)), 0x00, 0x00 - }; - SpiWriteAndRead(buf, 3); - if(i == 0) { *oX = 0; *oY = 0; diff --git a/XPT2046.h b/XPT2046.h index 0b34744..6487e5b 100644 --- a/XPT2046.h +++ b/XPT2046.h @@ -48,8 +48,7 @@ class XPT2046 { int SpiWriteAndRead(unsigned char *data, int length); - void read_touchscreen(); - bool armInterrupt(); + void read_touchscreen(bool interruptEnable); void setRotation(uint8_t m); void setCalibration(uint16_t minX, uint16_t minY, uint16_t maxX, uint16_t maxY); @@ -101,7 +100,7 @@ class XPT2046 { uint32_t z_average ; const char * tcfifo ; - bool interruptEnabled ; + int interruptpoll ; }; diff --git a/mpi3501.cpp b/mpi3501.cpp index c702ce7..1ea0d2f 100644 --- a/mpi3501.cpp +++ b/mpi3501.cpp @@ -14,6 +14,8 @@ XPT2046 touch; static int counter = 0; char buffer[20]; short bufLen = 0; +#define LOOP_INTERVAL 500 +static int loop = 0; void ChipSelectHigh() { @@ -21,13 +23,13 @@ void ChipSelectHigh() SET_GPIO(GPIO_SPI0_CE1); // Disable Display CLEAR_GPIO(GPIO_SPI0_CE0); // Enable Touch __sync_synchronize(); + if(loop++ % LOOP_INTERVAL == 0) { + touch.read_touchscreen(true); + __sync_synchronize(); + } if(hasInterrupt()) { - touch.read_touchscreen(); - __sync_synchronize(); - } else { - if(touch.armInterrupt()) { - __sync_synchronize(); - } + touch.read_touchscreen(false); + __sync_synchronize(); } SET_GPIO(GPIO_SPI0_CE0); // Disable Touch CLEAR_GPIO(GPIO_SPI0_CE1); // Enable Display From 31485d901e489ad3649f8c7a0ef972f956fe4199 Mon Sep 17 00:00:00 2001 From: kpishere Date: Thu, 30 May 2019 22:08:23 -0400 Subject: [PATCH 17/52] Update README.md --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 218fb86..869e39e 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ This driver does not utilize the [notro/fbtft](https://github.com/notro/fbtft) f This program neither utilizes the default SPI driver, so a line such as `dtparam=spi=on` in `/boot/config.txt` should also be removed so that it will not cause conflicts. -Likewise, if you have any touch controller related dtoverlays active, such as `dtoverlay=ads7846,...` or anything that has a `penirq=` directive, those should be removed as well to avoid conflicts. It would be possible to add touch support to fbcp-ili9341 if someone wants to take a stab at it. +Likewise, if you have any touch controller related dtoverlays active, such as `dtoverlay=ads7846,...` or anything that has a `penirq=` directive, those should be removed as well to avoid conflicts. The driver has its own touch screen driver (thanks to contributors). ##### Building and running @@ -90,8 +90,12 @@ mkdir build cd build cmake [options] .. make -j -sudo ./fbcp-ili9341 -``` +cd ../kernel +./start_kernel_module.sh +cd ../build +tail -f /tmp/TCfifo & +sudo ./fbcp-ili9341 & + Note especially the two dots `..` on the CMake line, which denote "up one directory" in this case (instead of referring to "more items go here"). @@ -592,8 +596,7 @@ If you would like to help push Raspberry Pi SPI display support further, there a - Implement support for reading the MISO line for display identification numbers/strings for potentially interesting statistics (could some of the displays be autodetected this way?) - Add support for other color modes, like RGB666 or RGB888. Currently fbcp-ili9341 only knows about RGB565 display mode. - Implement a kernel module that enables userland programs to allocate DMA channels, which fbcp-ili9341 could use to amicably reserve its own DMA channels without danger of conflicting. - - Implement support for touch control while fbcp-ili9341 is active. ([#33](https://github.com/juj/fbcp-ili9341/issues/33)) - - Implement support for SPI-based SD card readers that are sometimes attached to displays. + - üImplement support for SPI-based SD card readers that are sometimes attached to displays. - Port fbcp-ili9341 to work with I2C displays. - Port more key algorithms to ARM assembly to optimize performance of fbcp-ili9341 in hotspots, or optimize execution in some other ways? - Add support to building fbcp-ili9341 on another operating system than Raspbian. (see [#43](https://github.com/juj/fbcp-ili9341/issues/43)) From 23b846c1674f6a5f461d12245ead4769e17cba67 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Sat, 15 Jun 2019 17:52:47 +0000 Subject: [PATCH 18/52] timer for poling touch display and a bunch of debugging printf() statements --- CMakeLists.txt | 2 +- dma.cpp | 2 ++ fbcp-ili9341.cpp | 31 +++++++++++++---- spi.cpp | 91 ++++++++++++++++++++++++++++++++++++++++++------ spi_user.h | 6 ++++ util.h | 3 ++ 6 files changed, 117 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b8711f7..835e339 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -256,4 +256,4 @@ endif() add_executable(fbcp-ili9341 ${sourceFiles}) -target_link_libraries(fbcp-ili9341 pthread bcm_host atomic) +target_link_libraries(fbcp-ili9341 pthread bcm_host atomic rt) diff --git a/dma.cpp b/dma.cpp index 3084f91..7adc74c 100644 --- a/dma.cpp +++ b/dma.cpp @@ -375,6 +375,7 @@ void WaitForDMAFinished() uint64_t t0 = tick(); while((dmaTx->cs & BCM2835_DMA_CS_ACTIVE) && programRunning) { +printf("b"); usleep(100); if (tick() - t0 > 2000000) { @@ -387,6 +388,7 @@ void WaitForDMAFinished() t0 = tick(); while((dmaRx->cs & BCM2835_DMA_CS_ACTIVE) && programRunning) { +printf("c"); usleep(100); if (tick() - t0 > 2000000) { diff --git a/fbcp-ili9341.cpp b/fbcp-ili9341.cpp index 40715ad..a382853 100644 --- a/fbcp-ili9341.cpp +++ b/fbcp-ili9341.cpp @@ -95,6 +95,7 @@ int main() signal(SIGUSR1, ProgramInterruptHandler); signal(SIGUSR2, ProgramInterruptHandler); signal(SIGTERM, ProgramInterruptHandler); + #ifdef RUN_WITH_REALTIME_THREAD_PRIORITY SetRealtimeThreadPriority(); #endif @@ -136,6 +137,7 @@ int main() bool interlacedUpdate = false; // True if the previous update we did was an interlaced half field update. int frameParity = 0; // For interlaced frame updates, this is either 0 or 1 to denote evens or odds. OpenKeyboard(); + printf("All initialized, now running main loop...\n"); while(programRunning) { @@ -150,7 +152,10 @@ int main() #ifdef THROTTLE_INTERLACING timespec timeout = {}; timeout.tv_nsec = 1000 * MIN(1000000, MAX(1, 750/*0.75ms extra sleep so we know we should likely sleep long enough to see the next frame*/ + PredictNextFrameArrivalTime() - tick())); - if (programRunning) syscall(SYS_futex, &numNewGpuFrames, FUTEX_WAIT, 0, &timeout, 0, 0); // Start sleeping until we get new tasks + if (programRunning) + { +//syscall(SYS_futex, &numNewGpuFrames, FUTEX_WAIT, 0, &timeout, 0, 0); // Start sleeping until we get new tasks + } #endif // If THROTTLE_INTERLACING is not defined, we'll fall right through and immediately submit the rest of the remaining content on screen to attempt to minimize the visual // observable effect of interlacing, although at the expense of smooth animation (falling through here causes jitter) @@ -172,14 +177,19 @@ int main() timespec timeout = {}; timeout.tv_sec = ((uint64_t)TURN_DISPLAY_OFF_AFTER_USECS_OF_INACTIVITY * 1000) / 1000000000; timeout.tv_nsec = ((uint64_t)TURN_DISPLAY_OFF_AFTER_USECS_OF_INACTIVITY * 1000) % 1000000000; - if (programRunning) syscall(SYS_futex, &numNewGpuFrames, FUTEX_WAIT, 0, &timeout, 0, 0); // Sleep until the next frame arrives + if (programRunning) { + //syscall(SYS_futex, &numNewGpuFrames, FUTEX_WAIT, 0, &timeout, 0, 0); // Sleep until the next frame arrives + } } - else + else + { #endif - if (programRunning) syscall(SYS_futex, &numNewGpuFrames, FUTEX_WAIT, 0, 0, 0, 0); // Sleep until the next frame arrives - } + if (programRunning) { + //syscall(SYS_futex, &numNewGpuFrames, FUTEX_WAIT, 0, 0, 0, 0); // Sleep until the next frame arrives + } + } } - +printf("1"); bool spiThreadWasWorkingHardBefore = false; // At all times keep at most two rendered frames in the SPI task queue pending to be displayed. Only proceed to submit a new frame @@ -199,6 +209,7 @@ int main() #ifdef STATISTICS uint64_t t0 = tick(); #endif +printf("2"); if (sleepUsecs > 1000) usleep(500); #ifdef STATISTICS @@ -238,6 +249,7 @@ int main() for(int i = 0; i < frameSkipTimeHistorySize; ++i) frameSkipTimeHistory[i] = frameSkipTimeHistory[i+expiredSkippedFrames]; } #endif +printf("3"); int numNewFrames = __atomic_load_n(&numNewGpuFrames, __ATOMIC_SEQ_CST); bool gotNewFramebuffer = (numNewFrames > 0); @@ -256,8 +268,9 @@ int main() #if defined(SAVE_BATTERY_BY_PREDICTING_FRAME_ARRIVAL_TIMES) || defined(SAVE_BATTERY_BY_SLEEPING_WHEN_IDLE) uint64_t nextFrameArrivalTime = PredictNextFrameArrivalTime(); int64_t timeToSleep = nextFrameArrivalTime - tick(); - if (timeToSleep > 0) + if (timeToSleep > 0) { usleep(timeToSleep); +} #endif framebufferHasNewChangedPixels = SnapshotFramebuffer(framebuffer[0]); @@ -288,6 +301,7 @@ int main() uint64_t timeToGiveUpThereIsNotGoingToBeANewFrame = framePollingStartTime + 1000000/TARGET_FRAME_RATE/2; while(!framebufferHasNewChangedPixels && tick() < timeToGiveUpThereIsNotGoingToBeANewFrame) { +printf("4"); usleep(2000); frameObtainedTime = tick(); framebufferHasNewChangedPixels = SnapshotFramebuffer(framebuffer[0]); @@ -457,6 +471,7 @@ int main() spiX = i->x; } #endif +printf("5"); } // Submit the span pixels @@ -511,6 +526,7 @@ int main() #endif } #endif +printf("6"); CommitTask(task); IN_SINGLE_THREADED_MODE_RUN_TASK(); } @@ -563,6 +579,7 @@ int main() } statsBytesTransferred += bytesTransferred; #endif +printf("7"); } DeinitGPU(); diff --git a/spi.cpp b/spi.cpp index 925e3dc..bb77c72 100644 --- a/spi.cpp +++ b/spi.cpp @@ -17,7 +17,20 @@ // Uncomment this to print out all bytes sent to the SPI bus // #define DEBUG_SPI_BUS_WRITES - + +/* timer config for touchscreen */ +#include +#define TOUCH_WAKEUPINTERVAL 100000 /* ms */ +#define CLOCKID CLOCK_REALTIME +#define SIG SIGRTMIN + +timer_t timerid; +struct sigevent sev; +sigset_t mask; +struct itimerspec its; +struct sigaction sa; +bool timerActive; + #ifdef DEBUG_SPI_BUS_WRITES #define DEBUG_PRINT_WRITTEN_BYTE(byte) do { \ printf("%02X", byte); \ @@ -34,6 +47,56 @@ void ChipSelectHigh(); #define TOGGLE_CHIP_SELECT_LINE() ((void)0) #endif +void timerHandler(int sig, siginfo_t *si, void *uc) +{ + ChipSelectHigh(); +} + +void initTimer() { + /* Timer for touch */ + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = timerHandler; + sigemptyset(&sa.sa_mask); + if (sigaction(SIG, &sa, NULL) == -1) errExit("sigaction"); + + /* Block timer signal temporarily */ + sigemptyset(&mask); + sigaddset(&mask, SIG); + if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1) errExit("sigprocmask"); + + /* Set timer frequency values */ + its.it_value.tv_sec = 0; + its.it_value.tv_nsec = TOUCH_WAKEUPINTERVAL; + its.it_interval.tv_sec = its.it_value.tv_sec; + its.it_interval.tv_nsec = its.it_value.tv_nsec; + + /* Create the timer */ + sev.sigev_notify = SIGEV_SIGNAL; + sev.sigev_signo = SIG; + sev.sigev_value.sival_ptr = &timerid; + if (timer_create(CLOCKID, &sev, &timerid) == -1) errExit("timer_create"); + + timerActive = false; + + printf("touch timer initialized\n"); +} + +void startTimer() { + if(timerActive) return; + if (timer_settime(timerid, 0, &its, NULL) == -1) errExit("timer_settime"); +printf(">"); + timerActive = true; +} +void stopTimer() { + if(!timerActive) return; + /* Block timer signal temporarily */ + sigemptyset(&mask); + sigaddset(&mask, SIG); + if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1) errExit("sigprocmask"); +printf("|"); + timerActive = false; +} + static uint32_t writeCounter = 0; #define WRITE_FIFO(word) do { \ @@ -42,7 +105,7 @@ static uint32_t writeCounter = 0; TOGGLE_CHIP_SELECT_LINE(); \ DEBUG_PRINT_WRITTEN_BYTE(w); \ } while(0) - + int mem_fd = -1; int intr_fd = -1; static int numberPress = 0; @@ -50,7 +113,7 @@ static char numberAsString[21] = " "; volatile void *bcm2835 = 0; volatile GPIORegisterFile *gpio = 0; volatile SPIRegisterFile *spi = 0; - + // Points to the system timer register. N.B. spec sheet says this is two low and high parts, in an 32-bit aligned (but not 64-bit aligned) address. Profiling shows // that Pi 3 Model B does allow reading this as a u64 load, and even when unaligned, it is around 30% faster to do so compared to loading in parts "lo | (hi << 32)". volatile uint64_t *systemTimerRegister = 0; @@ -296,7 +359,7 @@ void RunSPITask(SPITask *task) { if (previousTaskWasSPI) WaitForPolledSPITransferToFinish(); -// printf("DMA cmd=0x%x, data=%d bytes\n", task->cmd, task->PayloadSize()); + //printf("DMA cmd=0x%x, data=%d bytes\n", task->cmd, task->PayloadSize()); SPIDMATransfer(task); previousTaskWasSPI = false; } @@ -312,7 +375,7 @@ void RunSPITask(SPITask *task) else WaitForPolledSPITransferToFinish(); -// printf("SPI cmd=0x%x, data=%d bytes\n", task->cmd, task->PayloadSize()); + //printf("SPI cmd=0x%x, data=%d bytes\n", task->cmd, task->PayloadSize()); // Send the command word if display is 4-wire (3-wire displays can omit this, commands are interleaved in the data payload stream above) #ifndef SPI_3WIRE_PROTOCOL @@ -463,10 +526,11 @@ void ExecuteSPITasks() SPITask *task = GetTask(); if (task) { +printf("a"); RunSPITask(task); DoneTask(task); } - } + } } #ifndef USE_DMA_TRANSFERS END_SPI_COMMUNICATION(); @@ -482,20 +546,26 @@ void *spi_thread(void *unused) #ifdef RUN_WITH_REALTIME_THREAD_PRIORITY SetRealtimeThreadPriority(); #endif +startTimer(); while(programRunning) { if (spiTaskMemory->queueTail != spiTaskMemory->queueHead) { - ExecuteSPITasks(); +stopTimer(); + ExecuteSPITasks(); +startTimer(); } else { +startTimer(); #ifdef STATISTICS uint64_t t0 = tick(); spiThreadSleepStartTime = t0; __atomic_store_n(&spiThreadSleeping, 1, __ATOMIC_RELAXED); #endif - if (programRunning) syscall(SYS_futex, &spiTaskMemory->queueTail, FUTEX_WAIT, spiTaskMemory->queueHead, 0, 0, 0); // Start sleeping until we get new tasks + if (programRunning) { + syscall(SYS_futex, &spiTaskMemory->queueTail, FUTEX_WAIT, spiTaskMemory->queueHead, 0, 0, 0); // Start sleeping until we get new tasks + } #ifdef STATISTICS __atomic_store_n(&spiThreadSleeping, 0, __ATOMIC_RELAXED); uint64_t t1 = tick(); @@ -505,6 +575,8 @@ void *spi_thread(void *unused) } } #endif + + int InitSPI() { @@ -584,8 +656,7 @@ int InitSPI() // We will be running SPI tasks continuously from the main thread, so keep SPI Transfer Active throughout the lifetime of the driver. BEGIN_SPI_COMMUNICATION(); #endif - - + initTimer(); LOG("InitSPI done"); return 0; } diff --git a/spi_user.h b/spi_user.h index 413ef42..f55c448 100644 --- a/spi_user.h +++ b/spi_user.h @@ -9,6 +9,10 @@ #include "tick.h" #include "display.h" +extern void initTimer(); +extern void startTimer(); +extern void stopTimer(); + extern volatile void *bcm2835; typedef struct GPIORegisterFile @@ -292,9 +296,11 @@ static inline void CommitTask(SPITask *task) // Advertises the given SPI task fr #define IN_SINGLE_THREADED_MODE_RUN_TASK() ((void)0) #else #define IN_SINGLE_THREADED_MODE_RUN_TASK() { \ + stopTimer();\ SPITask *t = GetTask(); \ RunSPITask(t); \ DoneTask(t); \ + startTimer();\ } #endif diff --git a/util.h b/util.h index e3ce3d3..1665473 100644 --- a/util.h +++ b/util.h @@ -10,6 +10,9 @@ #define ABS(x) ((x) < 0 ? (-(x)) : (x)) #define SWAPU32(x, y) { uint32_t tmp = x; x = y; y = tmp; } + +#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \ + } while (0) #ifndef ALIGN_DOWN #define ALIGN_DOWN(ptr, alignment) (((ptr)) & ~((alignment)-1)) From d0fd72ee4890af0250246164b14f8ac6cee64b44 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Sat, 15 Jun 2019 20:28:16 +0000 Subject: [PATCH 19/52] more messing about --- config.h | 2 +- dma.cpp | 2 -- fbcp-ili9341.cpp | 47 +++++++++++++++++++++++++---------------------- spi.cpp | 39 +++++++++++++-------------------------- spi_user.h | 2 -- 5 files changed, 39 insertions(+), 53 deletions(-) diff --git a/config.h b/config.h index 75b802b..1a8033a 100644 --- a/config.h +++ b/config.h @@ -33,7 +33,7 @@ // output new frames at this vsync-detached interval, so there's a 50 Hz vs 60 Hz mismatch that results // in visible microstuttering. Still, providing this as an option, this might be good for content that // is known to run at native 60Hz. -// #define USE_GPU_VSYNC +//#define USE_GPU_VSYNC // Always enable GPU VSync on the Pi Zero. Even though it is suboptimal and can cause stuttering, it saves battery. #if defined(SINGLE_CORE_BOARD) diff --git a/dma.cpp b/dma.cpp index 7adc74c..3084f91 100644 --- a/dma.cpp +++ b/dma.cpp @@ -375,7 +375,6 @@ void WaitForDMAFinished() uint64_t t0 = tick(); while((dmaTx->cs & BCM2835_DMA_CS_ACTIVE) && programRunning) { -printf("b"); usleep(100); if (tick() - t0 > 2000000) { @@ -388,7 +387,6 @@ printf("b"); t0 = tick(); while((dmaRx->cs & BCM2835_DMA_CS_ACTIVE) && programRunning) { -printf("c"); usleep(100); if (tick() - t0 > 2000000) { diff --git a/fbcp-ili9341.cpp b/fbcp-ili9341.cpp index a382853..5c4b2e7 100644 --- a/fbcp-ili9341.cpp +++ b/fbcp-ili9341.cpp @@ -137,7 +137,7 @@ int main() bool interlacedUpdate = false; // True if the previous update we did was an interlaced half field update. int frameParity = 0; // For interlaced frame updates, this is either 0 or 1 to denote evens or odds. OpenKeyboard(); - + initTimer(); printf("All initialized, now running main loop...\n"); while(programRunning) { @@ -189,7 +189,6 @@ int main() } } } -printf("1"); bool spiThreadWasWorkingHardBefore = false; // At all times keep at most two rendered frames in the SPI task queue pending to be displayed. Only proceed to submit a new frame @@ -209,7 +208,6 @@ printf("1"); #ifdef STATISTICS uint64_t t0 = tick(); #endif -printf("2"); if (sleepUsecs > 1000) usleep(500); #ifdef STATISTICS @@ -249,34 +247,39 @@ printf("2"); for(int i = 0; i < frameSkipTimeHistorySize; ++i) frameSkipTimeHistory[i] = frameSkipTimeHistory[i+expiredSkippedFrames]; } #endif -printf("3"); +printf("3"); +startTimer(); int numNewFrames = __atomic_load_n(&numNewGpuFrames, __ATOMIC_SEQ_CST); bool gotNewFramebuffer = (numNewFrames > 0); bool framebufferHasNewChangedPixels = true; uint64_t frameObtainedTime; if (gotNewFramebuffer) { +printf("x"); #ifdef USE_GPU_VSYNC - // TODO: Hardcoded vsync interval to 60 for now. Would be better to compute yet another histogram of the vsync arrival times, if vsync is not set to 60hz. - // N.B. copying directly to videoCoreFramebuffer[1] that may be directly accessed by the main thread, so this could - // produce a visible tear between two adjacent frames, but since we don't have vsync anyways, currently not caring too much. +// TODO: Hardcoded vsync interval to 60 for now. Would be better to compute yet another histogram of the vsync arrival times, if vsync is not set to 60hz. +// N.B. copying directly to videoCoreFramebuffer[1] that may be directly accessed by the main thread, so this could +// produce a visible tear between two adjacent frames, but since we don't have vsync anyways, currently not caring too much. frameObtainedTime = tick(); uint64_t framePollingStartTime = frameObtainedTime; -#if defined(SAVE_BATTERY_BY_PREDICTING_FRAME_ARRIVAL_TIMES) || defined(SAVE_BATTERY_BY_SLEEPING_WHEN_IDLE) + #if defined(SAVE_BATTERY_BY_PREDICTING_FRAME_ARRIVAL_TIMES) || defined(SAVE_BATTERY_BY_SLEEPING_WHEN_IDLE) uint64_t nextFrameArrivalTime = PredictNextFrameArrivalTime(); int64_t timeToSleep = nextFrameArrivalTime - tick(); if (timeToSleep > 0) { +printf("y"); usleep(timeToSleep); -} -#endif + stopTimer(); + } + #endif framebufferHasNewChangedPixels = SnapshotFramebuffer(framebuffer[0]); #else memcpy(framebuffer[0], videoCoreFramebuffer[1], gpuFramebufferSizeBytes); #endif +printf("z"); #ifdef STATISTICS uint64_t now = tick(); @@ -286,43 +289,44 @@ printf("3"); __atomic_fetch_sub(&numNewGpuFrames, numNewFrames, __ATOMIC_SEQ_CST); DrawStatisticsOverlay(framebuffer[0]); - +printf("b"); #ifdef USE_GPU_VSYNC -#ifdef STATISTICS + #ifdef STATISTICS uint64_t completelyUnnecessaryTimeWastedPollingGPUStart = tick(); -#endif + #endif // DispmanX PROBLEM! When latching onto the vsync signal, the DispmanX API sends the signal at arbitrary phase with respect to the application actually producing its frames. // Therefore even while we do get a smooth 16.666.. msec interval vsync signal, we have no idea whether the application has actually produced a new frame at that time. Therefore // we must keep polling for frames until we find one that it has produced. -#ifdef SELF_SYNCHRONIZE_TO_GPU_VSYNC_PRODUCED_NEW_FRAMES + #ifdef SELF_SYNCHRONIZE_TO_GPU_VSYNC_PRODUCED_NEW_FRAMES framebufferHasNewChangedPixels = framebufferHasNewChangedPixels && IsNewFramebuffer(framebuffer[0], framebuffer[1]); uint64_t timeToGiveUpThereIsNotGoingToBeANewFrame = framePollingStartTime + 1000000/TARGET_FRAME_RATE/2; while(!framebufferHasNewChangedPixels && tick() < timeToGiveUpThereIsNotGoingToBeANewFrame) { -printf("4"); usleep(2000); frameObtainedTime = tick(); framebufferHasNewChangedPixels = SnapshotFramebuffer(framebuffer[0]); DrawStatisticsOverlay(framebuffer[0]); framebufferHasNewChangedPixels = framebufferHasNewChangedPixels && IsNewFramebuffer(framebuffer[0], framebuffer[1]); } -#else + #else framebufferHasNewChangedPixels = true; -#endif + #endif +printf("a"); numNewFrames = __atomic_load_n(&numNewGpuFrames, __ATOMIC_SEQ_CST); __atomic_fetch_sub(&numNewGpuFrames, numNewFrames, __ATOMIC_SEQ_CST); -#ifdef STATISTICS +printf("c"); + #ifdef STATISTICS now = tick(); for(int i = 0; i < numNewFrames - 1 && frameSkipTimeHistorySize < FRAMERATE_HISTORY_LENGTH; ++i) frameSkipTimeHistory[frameSkipTimeHistorySize++] = now; uint64_t completelyUnnecessaryTimeWastedPollingGPUStop = tick(); __atomic_fetch_add(&timeWastedPollingGPU, completelyUnnecessaryTimeWastedPollingGPUStop-completelyUnnecessaryTimeWastedPollingGPUStart, __ATOMIC_RELAXED); -#endif + #endif #else // !USE_GPU_VSYNC if (!displayOff) @@ -340,6 +344,7 @@ printf("4"); #else const double timesliceToUseForScreenUpdates = 1500000; #endif +printf("5"); const double tooMuchToUpdateUsecs = timesliceToUseForScreenUpdates / desiredTargetFps; // If updating the current and new frame takes too many frames worth of allotted time, drop to interlacing. #if !defined(NO_INTERLACING) || (defined(BACKLIGHT_CONTROL) && defined(TURN_DISPLAY_OFF_AFTER_USECS_OF_INACTIVITY)) @@ -471,9 +476,9 @@ printf("4"); spiX = i->x; } #endif -printf("5"); } +printf("6"); // Submit the span pixels SPITask *task = AllocTask(i->size*SPI_BYTESPERPIXEL); task->cmd = DISPLAY_WRITE_PIXELS; @@ -526,7 +531,6 @@ printf("5"); #endif } #endif -printf("6"); CommitTask(task); IN_SINGLE_THREADED_MODE_RUN_TASK(); } @@ -544,7 +548,6 @@ printf("6"); prevFrameEnd = curFrameEnd; curFrameEnd = spiTaskMemory->queueTail; } - #if defined(BACKLIGHT_CONTROL) && defined(TURN_DISPLAY_OFF_AFTER_USECS_OF_INACTIVITY) double percentageOfScreenChanged = (double)numChangedPixels/(DISPLAY_DRAWABLE_WIDTH*DISPLAY_DRAWABLE_HEIGHT); bool displayIsActive = percentageOfScreenChanged > DISPLAY_CONSIDERED_INACTIVE_PERCENTAGE; diff --git a/spi.cpp b/spi.cpp index bb77c72..a0b055f 100644 --- a/spi.cpp +++ b/spi.cpp @@ -29,7 +29,6 @@ struct sigevent sev; sigset_t mask; struct itimerspec its; struct sigaction sa; -bool timerActive; #ifdef DEBUG_SPI_BUS_WRITES #define DEBUG_PRINT_WRITTEN_BYTE(byte) do { \ @@ -49,7 +48,7 @@ void ChipSelectHigh(); void timerHandler(int sig, siginfo_t *si, void *uc) { - ChipSelectHigh(); + printf(","); ChipSelectHigh(); } void initTimer() { @@ -75,37 +74,27 @@ void initTimer() { sev.sigev_signo = SIG; sev.sigev_value.sival_ptr = &timerid; if (timer_create(CLOCKID, &sev, &timerid) == -1) errExit("timer_create"); - - timerActive = false; - - printf("touch timer initialized\n"); } +static uint32_t writeCounter = 0; + +#define WRITE_FIFO(word) do { \ + uint8_t w = (word); \ + spi->fifo = w; \ + TOGGLE_CHIP_SELECT_LINE(); \ + DEBUG_PRINT_WRITTEN_BYTE(w); \ + } while(0) + void startTimer() { - if(timerActive) return; if (timer_settime(timerid, 0, &its, NULL) == -1) errExit("timer_settime"); -printf(">"); - timerActive = true; } void stopTimer() { - if(!timerActive) return; /* Block timer signal temporarily */ sigemptyset(&mask); sigaddset(&mask, SIG); if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1) errExit("sigprocmask"); -printf("|"); - timerActive = false; } -static uint32_t writeCounter = 0; - -#define WRITE_FIFO(word) do { \ - uint8_t w = (word); \ - spi->fifo = w; \ - TOGGLE_CHIP_SELECT_LINE(); \ - DEBUG_PRINT_WRITTEN_BYTE(w); \ - } while(0) - int mem_fd = -1; int intr_fd = -1; static int numberPress = 0; @@ -352,7 +341,7 @@ void RunSPITask(SPITask *task) uint8_t *tEnd = task->PayloadEnd(); const uint32_t payloadSize = tEnd - tStart; uint8_t *tPrefillEnd = tStart + MIN(15, payloadSize); - + stopTimer(); #define TASK_SIZE_TO_USE_DMA 4 // Do a DMA transfer if this task is suitable in size for DMA to handle if (payloadSize >= TASK_SIZE_TO_USE_DMA && (task->cmd == DISPLAY_WRITE_PIXELS || task->cmd == DISPLAY_SET_CURSOR_X || task->cmd == DISPLAY_SET_CURSOR_Y)) @@ -414,7 +403,8 @@ void RunSPITask(SPITask *task) #else void RunSPITask(SPITask *task) -{ +{ + stopTimer(); WaitForPolledSPITransferToFinish(); // The Adafruit 1.65" 240x240 ST7789 based display is unique compared to others that it does want to see the Chip Select line go @@ -526,7 +516,6 @@ void ExecuteSPITasks() SPITask *task = GetTask(); if (task) { -printf("a"); RunSPITask(task); DoneTask(task); } @@ -546,7 +535,6 @@ void *spi_thread(void *unused) #ifdef RUN_WITH_REALTIME_THREAD_PRIORITY SetRealtimeThreadPriority(); #endif -startTimer(); while(programRunning) { if (spiTaskMemory->queueTail != spiTaskMemory->queueHead) @@ -656,7 +644,6 @@ int InitSPI() // We will be running SPI tasks continuously from the main thread, so keep SPI Transfer Active throughout the lifetime of the driver. BEGIN_SPI_COMMUNICATION(); #endif - initTimer(); LOG("InitSPI done"); return 0; } diff --git a/spi_user.h b/spi_user.h index f55c448..ca85693 100644 --- a/spi_user.h +++ b/spi_user.h @@ -296,11 +296,9 @@ static inline void CommitTask(SPITask *task) // Advertises the given SPI task fr #define IN_SINGLE_THREADED_MODE_RUN_TASK() ((void)0) #else #define IN_SINGLE_THREADED_MODE_RUN_TASK() { \ - stopTimer();\ SPITask *t = GetTask(); \ RunSPITask(t); \ DoneTask(t); \ - startTimer();\ } #endif From 9e056cfb00f8aaa93f42a7465cd4004da1dd3350 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Mon, 17 Jun 2019 11:36:36 +0000 Subject: [PATCH 20/52] Added noop when in busy loop, touch works when not drawing --- fbcp-ili9341.cpp | 20 ++++----------- mpi3501.cpp | 3 ++- mpi3501.h | 1 + spi.cpp | 67 +++++++----------------------------------------- spi_user.h | 4 +-- 5 files changed, 18 insertions(+), 77 deletions(-) diff --git a/fbcp-ili9341.cpp b/fbcp-ili9341.cpp index 5c4b2e7..36ca02a 100644 --- a/fbcp-ili9341.cpp +++ b/fbcp-ili9341.cpp @@ -137,11 +137,11 @@ int main() bool interlacedUpdate = false; // True if the previous update we did was an interlaced half field update. int frameParity = 0; // For interlaced frame updates, this is either 0 or 1 to denote evens or odds. OpenKeyboard(); - initTimer(); printf("All initialized, now running main loop...\n"); while(programRunning) { prevFrameWasInterlacedUpdate = interlacedUpdate; +sendNoOpCommand(); // If last update was interlaced, it means we still have half of the image pending to be updated. In such a case, // sleep only until when we expect the next new frame of data to appear, and then continue independent of whether @@ -165,6 +165,7 @@ int main() uint64_t waitStart = tick(); while(__atomic_load_n(&numNewGpuFrames, __ATOMIC_SEQ_CST) == 0) { +sendNoOpCommand(); #if defined(BACKLIGHT_CONTROL) && defined(TURN_DISPLAY_OFF_AFTER_USECS_OF_INACTIVITY) if (!displayOff && tick() - waitStart > TURN_DISPLAY_OFF_AFTER_USECS_OF_INACTIVITY) { @@ -178,7 +179,7 @@ int main() timeout.tv_sec = ((uint64_t)TURN_DISPLAY_OFF_AFTER_USECS_OF_INACTIVITY * 1000) / 1000000000; timeout.tv_nsec = ((uint64_t)TURN_DISPLAY_OFF_AFTER_USECS_OF_INACTIVITY * 1000) % 1000000000; if (programRunning) { - //syscall(SYS_futex, &numNewGpuFrames, FUTEX_WAIT, 0, &timeout, 0, 0); // Sleep until the next frame arrives + syscall(SYS_futex, &numNewGpuFrames, FUTEX_WAIT, 0, &timeout, 0, 0); // Sleep until the next frame arrives } } else @@ -229,6 +230,8 @@ int main() } } +sendNoOpCommand(); + int expiredFrames = 0; uint64_t now = tick(); while(expiredFrames < frameTimeHistorySize && now - frameTimeHistory[expiredFrames].time >= FRAMERATE_HISTORY_LENGTH) ++expiredFrames; @@ -248,15 +251,12 @@ int main() } #endif -printf("3"); -startTimer(); int numNewFrames = __atomic_load_n(&numNewGpuFrames, __ATOMIC_SEQ_CST); bool gotNewFramebuffer = (numNewFrames > 0); bool framebufferHasNewChangedPixels = true; uint64_t frameObtainedTime; if (gotNewFramebuffer) { -printf("x"); #ifdef USE_GPU_VSYNC // TODO: Hardcoded vsync interval to 60 for now. Would be better to compute yet another histogram of the vsync arrival times, if vsync is not set to 60hz. // N.B. copying directly to videoCoreFramebuffer[1] that may be directly accessed by the main thread, so this could @@ -269,9 +269,7 @@ printf("x"); uint64_t nextFrameArrivalTime = PredictNextFrameArrivalTime(); int64_t timeToSleep = nextFrameArrivalTime - tick(); if (timeToSleep > 0) { -printf("y"); usleep(timeToSleep); - stopTimer(); } #endif @@ -279,8 +277,6 @@ printf("y"); #else memcpy(framebuffer[0], videoCoreFramebuffer[1], gpuFramebufferSizeBytes); #endif -printf("z"); - #ifdef STATISTICS uint64_t now = tick(); for(int i = 0; i < numNewFrames - 1 && frameSkipTimeHistorySize < FRAMERATE_HISTORY_LENGTH; ++i) @@ -289,7 +285,6 @@ printf("z"); __atomic_fetch_sub(&numNewGpuFrames, numNewFrames, __ATOMIC_SEQ_CST); DrawStatisticsOverlay(framebuffer[0]); -printf("b"); #ifdef USE_GPU_VSYNC #ifdef STATISTICS @@ -314,11 +309,9 @@ printf("b"); framebufferHasNewChangedPixels = true; #endif -printf("a"); numNewFrames = __atomic_load_n(&numNewGpuFrames, __ATOMIC_SEQ_CST); __atomic_fetch_sub(&numNewGpuFrames, numNewFrames, __ATOMIC_SEQ_CST); -printf("c"); #ifdef STATISTICS now = tick(); for(int i = 0; i < numNewFrames - 1 && frameSkipTimeHistorySize < FRAMERATE_HISTORY_LENGTH; ++i) @@ -344,7 +337,6 @@ printf("c"); #else const double timesliceToUseForScreenUpdates = 1500000; #endif -printf("5"); const double tooMuchToUpdateUsecs = timesliceToUseForScreenUpdates / desiredTargetFps; // If updating the current and new frame takes too many frames worth of allotted time, drop to interlacing. #if !defined(NO_INTERLACING) || (defined(BACKLIGHT_CONTROL) && defined(TURN_DISPLAY_OFF_AFTER_USECS_OF_INACTIVITY)) @@ -478,7 +470,6 @@ printf("5"); #endif } -printf("6"); // Submit the span pixels SPITask *task = AllocTask(i->size*SPI_BYTESPERPIXEL); task->cmd = DISPLAY_WRITE_PIXELS; @@ -582,7 +573,6 @@ printf("6"); } statsBytesTransferred += bytesTransferred; #endif -printf("7"); } DeinitGPU(); diff --git a/mpi3501.cpp b/mpi3501.cpp index 1ea0d2f..1522288 100644 --- a/mpi3501.cpp +++ b/mpi3501.cpp @@ -74,7 +74,7 @@ void InitKeDeiV63() SET_GPIO(GPIO_SPI0_CE0); // Disable Touch usleep(25*1000); - SPI_TRANSFER(0x00001100); // Reset + SPI_TRANSFER(DISPLAY_NO_OPERATION); // Reset usleep(10*1000); SPI_TRANSFER(0xff001100); SPI_TRANSFER(0xff001100); @@ -167,4 +167,5 @@ void DeinitSPIDisplay() TurnDisplayOff(); } + #endif diff --git a/mpi3501.h b/mpi3501.h index 5a67d7d..14307c7 100644 --- a/mpi3501.h +++ b/mpi3501.h @@ -5,6 +5,7 @@ #ifdef MPI3501 // Data specific to the KeDei v6.3 display +#define DISPLAY_NO_OPERATION 0x00001100 #define DISPLAY_SET_CURSOR_X 0x2A001100 #define DISPLAY_SET_CURSOR_Y 0x2B001100 #define DISPLAY_WRITE_PIXELS 0x2C001100 diff --git a/spi.cpp b/spi.cpp index a0b055f..8ccae68 100644 --- a/spi.cpp +++ b/spi.cpp @@ -18,18 +18,6 @@ // Uncomment this to print out all bytes sent to the SPI bus // #define DEBUG_SPI_BUS_WRITES -/* timer config for touchscreen */ -#include -#define TOUCH_WAKEUPINTERVAL 100000 /* ms */ -#define CLOCKID CLOCK_REALTIME -#define SIG SIGRTMIN - -timer_t timerid; -struct sigevent sev; -sigset_t mask; -struct itimerspec its; -struct sigaction sa; - #ifdef DEBUG_SPI_BUS_WRITES #define DEBUG_PRINT_WRITTEN_BYTE(byte) do { \ printf("%02X", byte); \ @@ -46,36 +34,6 @@ void ChipSelectHigh(); #define TOGGLE_CHIP_SELECT_LINE() ((void)0) #endif -void timerHandler(int sig, siginfo_t *si, void *uc) -{ - printf(","); ChipSelectHigh(); -} - -void initTimer() { - /* Timer for touch */ - sa.sa_flags = SA_SIGINFO; - sa.sa_sigaction = timerHandler; - sigemptyset(&sa.sa_mask); - if (sigaction(SIG, &sa, NULL) == -1) errExit("sigaction"); - - /* Block timer signal temporarily */ - sigemptyset(&mask); - sigaddset(&mask, SIG); - if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1) errExit("sigprocmask"); - - /* Set timer frequency values */ - its.it_value.tv_sec = 0; - its.it_value.tv_nsec = TOUCH_WAKEUPINTERVAL; - its.it_interval.tv_sec = its.it_value.tv_sec; - its.it_interval.tv_nsec = its.it_value.tv_nsec; - - /* Create the timer */ - sev.sigev_notify = SIGEV_SIGNAL; - sev.sigev_signo = SIG; - sev.sigev_value.sival_ptr = &timerid; - if (timer_create(CLOCKID, &sev, &timerid) == -1) errExit("timer_create"); -} - static uint32_t writeCounter = 0; #define WRITE_FIFO(word) do { \ @@ -85,16 +43,6 @@ static uint32_t writeCounter = 0; DEBUG_PRINT_WRITTEN_BYTE(w); \ } while(0) -void startTimer() { - if (timer_settime(timerid, 0, &its, NULL) == -1) errExit("timer_settime"); -} -void stopTimer() { - /* Block timer signal temporarily */ - sigemptyset(&mask); - sigaddset(&mask, SIG); - if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1) errExit("sigprocmask"); -} - int mem_fd = -1; int intr_fd = -1; static int numberPress = 0; @@ -330,6 +278,14 @@ void WaitForPolledSPITransferToFinish() if ((cs & BCM2835_SPI0_CS_RXD)) spi->cs = BCM2835_SPI0_CS_CLEAR_RX | BCM2835_SPI0_CS_TA | DISPLAY_SPI_DRIVE_SETTINGS; } + +void sendNoOpCommand() { + // Send a no-operation command to display (side effect is Touch display is polled) + SPITask *task = AllocTask(0); + task->cmd = DISPLAY_NO_OPERATION; + CommitTask(task); + IN_SINGLE_THREADED_MODE_RUN_TASK(); +} #ifdef ALL_TASKS_SHOULD_DMA @@ -341,7 +297,6 @@ void RunSPITask(SPITask *task) uint8_t *tEnd = task->PayloadEnd(); const uint32_t payloadSize = tEnd - tStart; uint8_t *tPrefillEnd = tStart + MIN(15, payloadSize); - stopTimer(); #define TASK_SIZE_TO_USE_DMA 4 // Do a DMA transfer if this task is suitable in size for DMA to handle if (payloadSize >= TASK_SIZE_TO_USE_DMA && (task->cmd == DISPLAY_WRITE_PIXELS || task->cmd == DISPLAY_SET_CURSOR_X || task->cmd == DISPLAY_SET_CURSOR_Y)) @@ -404,7 +359,6 @@ void RunSPITask(SPITask *task) void RunSPITask(SPITask *task) { - stopTimer(); WaitForPolledSPITransferToFinish(); // The Adafruit 1.65" 240x240 ST7789 based display is unique compared to others that it does want to see the Chip Select line go @@ -519,7 +473,7 @@ void ExecuteSPITasks() RunSPITask(task); DoneTask(task); } - } + } } #ifndef USE_DMA_TRANSFERS END_SPI_COMMUNICATION(); @@ -539,13 +493,10 @@ void *spi_thread(void *unused) { if (spiTaskMemory->queueTail != spiTaskMemory->queueHead) { -stopTimer(); ExecuteSPITasks(); -startTimer(); } else { -startTimer(); #ifdef STATISTICS uint64_t t0 = tick(); spiThreadSleepStartTime = t0; diff --git a/spi_user.h b/spi_user.h index ca85693..0bb8cf5 100644 --- a/spi_user.h +++ b/spi_user.h @@ -9,9 +9,7 @@ #include "tick.h" #include "display.h" -extern void initTimer(); -extern void startTimer(); -extern void stopTimer(); +extern void sendNoOpCommand(); extern volatile void *bcm2835; From 5c6e8080c972787e6a65a03a005a3d11c3d78942 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Tue, 18 Jun 2019 03:58:15 +0000 Subject: [PATCH 21/52] tidy up, adjust WAIT delay, enable awake on touch of screen --- XPT2046.cpp | 5 +++-- XPT2046.h | 24 +++++++++++++++--------- config.h | 6 +++++- fbcp-ili9341.cpp | 36 +++++++++++++++++++++--------------- keyboard.cpp | 2 +- mpi3501.cpp | 4 ++++ spi_user.h | 4 ++++ 7 files changed, 53 insertions(+), 28 deletions(-) diff --git a/XPT2046.cpp b/XPT2046.cpp index 5e8e3cd..0b87bb2 100644 --- a/XPT2046.cpp +++ b/XPT2046.cpp @@ -134,10 +134,11 @@ void XPT2046::read_touchscreen(bool interruptEnable) { if (z > 100) { char output[30] = ""; - fd = open(tcfifo, O_WRONLY | O_NONBLOCK); + fd = open(tcfifo, O_WRONLY | O_NONBLOCK); sprintf(output, "x:%d, y:%d, z:%d\n", x, y, z); write(fd, output, strlen(output) + 1); - close(fd); + close(fd); + this->lastTouchTick = tick(); } //spi->cs = (old_spi_cs | 1 << 4 | 1 << 5) & (~(1 << 7)); //Clear Fifos and TA diff --git a/XPT2046.h b/XPT2046.h index 6487e5b..bf2dcdd 100644 --- a/XPT2046.h +++ b/XPT2046.h @@ -46,7 +46,7 @@ class XPT2046 { ~XPT2046(); - int SpiWriteAndRead(unsigned char *data, int length); + int SpiWriteAndRead(unsigned char *data, int length); void read_touchscreen(bool interruptEnable); @@ -55,9 +55,14 @@ class XPT2046 { void read(uint16_t * oX, uint16_t * oY, uint16_t * oZ); void readRaw(uint16_t * oX, uint16_t * oY, uint16_t * oZ); - - static void printBits(size_t const size, void const * const ptr) - { + + uint64_t ticksSinceLastTouch() { + uint64_t now = tick(); + return now - this->lastTouchTick; + } + + static void printBits(size_t const size, void const * const ptr) + { unsigned char *b = (unsigned char*) ptr; unsigned char byte; int i, j; @@ -72,7 +77,7 @@ class XPT2046 { printf(" "); } puts(""); - } + } protected: @@ -92,14 +97,15 @@ class XPT2046 { int _lastX; int _lastY; int fd; - + uint64_t lastTouchTick; + uint16_t _minChange; - uint32_t spi_cs ; + uint32_t spi_cs ; - uint32_t z_average ; + uint32_t z_average ; - const char * tcfifo ; + const char * tcfifo ; int interruptpoll ; }; diff --git a/config.h b/config.h index 1a8033a..b519fc5 100644 --- a/config.h +++ b/config.h @@ -196,7 +196,7 @@ // #define USE_DMA_TRANSFERS // If defined, enables code to manage the backlight. -// #define BACKLIGHT_CONTROL +#define BACKLIGHT_CONTROL #if defined(BACKLIGHT_CONTROL) @@ -212,6 +212,10 @@ #define TURN_DISPLAY_OFF_AFTER_USECS_OF_INACTIVITY (1 * 60 * 1000000) #endif +// +// Sleep for up to 1 / N of a second. With sendNoOpCommand() to Display, the side effect is sampling +// the touch screen N times per second. +#define SLEEP_TIME_USECS_NODRAWING (1) // If less than this much % of the screen changes per frame, the screen is considered to be inactive, and // the display backlight can automatically turn off, if TURN_DISPLAY_OFF_AFTER_USECS_OF_INACTIVITY is diff --git a/fbcp-ili9341.cpp b/fbcp-ili9341.cpp index 36ca02a..d3256cc 100644 --- a/fbcp-ili9341.cpp +++ b/fbcp-ili9341.cpp @@ -140,8 +140,8 @@ int main() printf("All initialized, now running main loop...\n"); while(programRunning) { + sendNoOpCommand(); prevFrameWasInterlacedUpdate = interlacedUpdate; -sendNoOpCommand(); // If last update was interlaced, it means we still have half of the image pending to be updated. In such a case, // sleep only until when we expect the next new frame of data to appear, and then continue independent of whether @@ -154,7 +154,7 @@ sendNoOpCommand(); timeout.tv_nsec = 1000 * MIN(1000000, MAX(1, 750/*0.75ms extra sleep so we know we should likely sleep long enough to see the next frame*/ + PredictNextFrameArrivalTime() - tick())); if (programRunning) { -//syscall(SYS_futex, &numNewGpuFrames, FUTEX_WAIT, 0, &timeout, 0, 0); // Start sleeping until we get new tasks + syscall(SYS_futex, &numNewGpuFrames, FUTEX_WAIT, 0, &timeout, 0, 0); // Start sleeping until we get new tasks } #endif // If THROTTLE_INTERLACING is not defined, we'll fall right through and immediately submit the rest of the remaining content on screen to attempt to minimize the visual @@ -165,7 +165,7 @@ sendNoOpCommand(); uint64_t waitStart = tick(); while(__atomic_load_n(&numNewGpuFrames, __ATOMIC_SEQ_CST) == 0) { -sendNoOpCommand(); + sendNoOpCommand(); #if defined(BACKLIGHT_CONTROL) && defined(TURN_DISPLAY_OFF_AFTER_USECS_OF_INACTIVITY) if (!displayOff && tick() - waitStart > TURN_DISPLAY_OFF_AFTER_USECS_OF_INACTIVITY) { @@ -176,22 +176,30 @@ sendNoOpCommand(); if (!displayOff) { timespec timeout = {}; - timeout.tv_sec = ((uint64_t)TURN_DISPLAY_OFF_AFTER_USECS_OF_INACTIVITY * 1000) / 1000000000; - timeout.tv_nsec = ((uint64_t)TURN_DISPLAY_OFF_AFTER_USECS_OF_INACTIVITY * 1000) % 1000000000; - if (programRunning) { - syscall(SYS_futex, &numNewGpuFrames, FUTEX_WAIT, 0, &timeout, 0, 0); // Sleep until the next frame arrives + timeout.tv_nsec = ((uint64_t)SLEEP_TIME_USECS_NODRAWING * 1000) % 1000000000; + if (programRunning) + { + // Sleep until the next frame arrives or up to SLEEP_TIME_USECS_NODRAWING time + syscall(SYS_futex, &numNewGpuFrames, FUTEX_WAIT, 0, &timeout, 0, 0); } } else - { #endif - if (programRunning) { - //syscall(SYS_futex, &numNewGpuFrames, FUTEX_WAIT, 0, 0, 0, 0); // Sleep until the next frame arrives - } - } + { + if (programRunning) + { + timespec timeout = {}; + timeout.tv_nsec = ((uint64_t)SLEEP_TIME_USECS_NODRAWING * 1000) % 1000000000; + // Sleep until the next frame arrives or up to SLEEP_TIME_USECS_NODRAWING time + syscall(SYS_futex, &numNewGpuFrames, FUTEX_WAIT, 0, &timeout, 0, 0); + } + } + } } bool spiThreadWasWorkingHardBefore = false; + sendNoOpCommand(); + // At all times keep at most two rendered frames in the SPI task queue pending to be displayed. Only proceed to submit a new frame // once the older of those has been displayed. bool once = true; @@ -230,8 +238,6 @@ sendNoOpCommand(); } } -sendNoOpCommand(); - int expiredFrames = 0; uint64_t now = tick(); while(expiredFrames < frameTimeHistorySize && now - frameTimeHistory[expiredFrames].time >= FRAMERATE_HISTORY_LENGTH) ++expiredFrames; @@ -546,7 +552,7 @@ sendNoOpCommand(); displayContentsLastChanged = tick(); bool keyboardIsActive = TimeSinceLastKeyboardPress() < TURN_DISPLAY_OFF_AFTER_USECS_OF_INACTIVITY; - if (displayIsActive || keyboardIsActive) + if (displayIsActive || keyboardIsActive || activeTouchscreen() ) { if (displayOff) { diff --git a/keyboard.cpp b/keyboard.cpp index 4b83ac4..77d5582 100644 --- a/keyboard.cpp +++ b/keyboard.cpp @@ -18,7 +18,7 @@ void OpenKeyboard() { #ifdef READ_KEYBOARD_ENABLED key_fd = open(KEYBOARD_INPUT_FILE, O_RDONLY|O_NONBLOCK); - if (key_fd < 0) printf("Warning: cannot open keyboard input file " KEYBOARD_INPUT_FILE "! Try double checking that it exists, or reconfigure it in keyboard.cpp, or remove line '#define BACKLIGHT_CONTROL_FROM_KEYBOARD' in config.h if you do not want keyboard activity to factor into backlight control.\n"); + if (key_fd < 0) printf("Warning: cannot open keyboard input file " KEYBOARD_INPUT_FILE "! Try double checking that it exists, or reconfigure it in keyboard.cpp.\n"); #endif } diff --git a/mpi3501.cpp b/mpi3501.cpp index 1522288..5d41ce5 100644 --- a/mpi3501.cpp +++ b/mpi3501.cpp @@ -17,6 +17,10 @@ short bufLen = 0; #define LOOP_INTERVAL 500 static int loop = 0; +bool activeTouchscreen() { + return (touch.ticksSinceLastTouch() < TURN_DISPLAY_OFF_AFTER_USECS_OF_INACTIVITY); +} + void ChipSelectHigh() { WAIT_SPI_FINISHED(); diff --git a/spi_user.h b/spi_user.h index 0bb8cf5..9790f4f 100644 --- a/spi_user.h +++ b/spi_user.h @@ -9,8 +9,12 @@ #include "tick.h" #include "display.h" +// Sends no-Op command wich enables sensing of touch screen on some displays extern void sendNoOpCommand(); +// Checks last time touch screen sensed +extern bool activeTouchscreen(); + extern volatile void *bcm2835; typedef struct GPIORegisterFile From 04483f5a8cb2b39fd3bbf4dbfea7e25f2bff37c5 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Wed, 19 Jun 2019 01:46:24 +0000 Subject: [PATCH 22/52] blanks screen on inactivity but backlight still on --- mpi3501.cpp | 6 ++++++ mpi3501.h | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/mpi3501.cpp b/mpi3501.cpp index 5d41ce5..781d24f 100644 --- a/mpi3501.cpp +++ b/mpi3501.cpp @@ -151,18 +151,24 @@ void InitKeDeiV63() void TurnBacklightOff() { + SPI_TRANSFER(DISPLAY_BACKLIT_BRIT,0xFF); + SPI_TRANSFER(DISPLAY_BACKLIT_CTRL,0x00); // 5-Backlight contro, 3-Display dimming, 2-BacklihtOff } void TurnBacklightOn() { + SPI_TRANSFER(DISPLAY_BACKLIT_BRIT,0x00); + SPI_TRANSFER(DISPLAY_BACKLIT_CTRL,0x2C); // 5-Backlight contro, 3-Display dimming, 2-BacklihtON } void TurnDisplayOff() { + SPI_TRANSFER(DISPLAY_OFF); } void TurnDisplayOn() { + SPI_TRANSFER(DISPLAY_ON); } void DeinitSPIDisplay() diff --git a/mpi3501.h b/mpi3501.h index 14307c7..ade61ef 100644 --- a/mpi3501.h +++ b/mpi3501.h @@ -6,6 +6,10 @@ // Data specific to the KeDei v6.3 display #define DISPLAY_NO_OPERATION 0x00001100 +#define DISPLAY_BACKLIT_CTRL 0x53001100 +#define DISPLAY_BACKLIT_BRIT 0x51001100 +#define DISPLAY_ON 0x29001100 +#define DISPLAY_OFF 0x28001100 #define DISPLAY_SET_CURSOR_X 0x2A001100 #define DISPLAY_SET_CURSOR_Y 0x2B001100 #define DISPLAY_WRITE_PIXELS 0x2C001100 From 680e33b776da7b7d7781c177682a28c4cb45767e Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Thu, 20 Jun 2019 03:27:50 +0000 Subject: [PATCH 23/52] added macros for documentation from datasheet --- mpi3501.cpp | 65 +++++++++++++++++++++++++++++++---------------------- mpi3501.h | 27 +++++++++++++++++----- 2 files changed, 60 insertions(+), 32 deletions(-) diff --git a/mpi3501.cpp b/mpi3501.cpp index 781d24f..17eecbc 100644 --- a/mpi3501.cpp +++ b/mpi3501.cpp @@ -80,31 +80,31 @@ void InitKeDeiV63() SPI_TRANSFER(DISPLAY_NO_OPERATION); // Reset usleep(10*1000); - SPI_TRANSFER(0xff001100); - SPI_TRANSFER(0xff001100); + SPI_TRANSFER(DISPLAY_GETSPIREAD); + SPI_TRANSFER(DISPLAY_GETSPIREAD); usleep(10*1000); - SPI_TRANSFER(0xff001100); - SPI_TRANSFER(0xff001100); - SPI_TRANSFER(0xff001100); - SPI_TRANSFER(0xff001100); + SPI_TRANSFER(DISPLAY_GETSPIREAD); + SPI_TRANSFER(DISPLAY_GETSPIREAD); + SPI_TRANSFER(DISPLAY_GETSPIREAD); + SPI_TRANSFER(DISPLAY_GETSPIREAD); usleep(15*1000); - SPI_TRANSFER(0x11001100/*Sleep Out*/); + SPI_TRANSFER(DISPLAY_SLPOUT); usleep(150*1000); - SPI_TRANSFER(0xB0001100, 0x00, 0x00); // CLK 30Hz? - SPI_TRANSFER(0xB3001100, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); // set RGB? - SPI_TRANSFER(0xB9001100, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0f); // ext cmd? - SPI_TRANSFER(0xC0001100, 0x00, 0x13, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x02 - , 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x43); // Timing? - SPI_TRANSFER(0xC1001100, 0x00, 0x08, 0x00, 0x0F, 0x00, 0x08, 0x00, 0x08); // gamma? - SPI_TRANSFER(0xC4001100, 0x00, 0x11, 0x00, 0x07, 0x00, 0x03, 0x00, 0x04); // sleep modes? + SPI_TRANSFER(DISPLAY_SETOSC, 0x00, 0x00); + SPI_TRANSFER(DISPLAY_SETRGB, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + SPI_TRANSFER(DISPLAY_SETEXTC, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0f); + SPI_TRANSFER(DISPLAY_SETSTBA, 0x00, 0x13, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x02 + , 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x43); + SPI_TRANSFER(DISPLAY_SETDGC, 0x00, 0x08, 0x00, 0x0F, 0x00, 0x08, 0x00, 0x08); + SPI_TRANSFER(DISPLAY_SETDDB, 0x00, 0x11, 0x00, 0x07, 0x00, 0x03, 0x00, 0x04); SPI_TRANSFER(0xC6001100, 0x00, 0x00); // ? SPI_TRANSFER(0xC8001100, 0x00, 0x03, 0x00, 0x03, 0x00, 0x13, 0x00, 0x5C , 0x00, 0x03, 0x00, 0x07, 0x00, 0x14, 0x00, 0x08 , 0x00, 0x00, 0x00, 0x21, 0x00, 0x08, 0x00, 0x14 , 0x00, 0x07, 0x00, 0x53, 0x00, 0x0C, 0x00, 0x13 , 0x00, 0x03, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00); // ? - SPI_TRANSFER(0x35001100, 0x00, 0x00); + SPI_TRANSFER(DISPLAY_TEON, 0x00, 0x00); #define MADCTL_BGR_PIXEL_ORDER (1<<3) #define MADCTL_LINE_ADDRESS_ORDER_SWAP (1<<4) @@ -123,14 +123,17 @@ void InitKeDeiV63() #ifdef DISPLAY_ROTATE_180_DEGREES madctl ^= MADCTL_ROTATE_180_DEGREES; #endif - SPI_TRANSFER(0x36001100/*MADCTL: Memory Access Control*/, 0x00, madctl); - - SPI_TRANSFER(0x3A001100/*Interface Pixel Format*/, 0x00, 0x55); - SPI_TRANSFER(0x44001100, 0x00, 0x00, 0x00, 0x01); - SPI_TRANSFER(0xD0001100, 0x00, 0x07, 0x00, 0x07, 0x00, 0x1D, 0x00, 0x03); // ? + SPI_TRANSFER(DISPLAY_MADCTL, 0x00, madctl); + + SPI_TRANSFER(DSPLAY_COLMOD, 0x00, 0x55); + SPI_TRANSFER(DISPLAY_WRCABC,0x00,0x01); // 00 - default, 01 - UI, 02 - still pic, 03 - video + //SPI_TRANSFER(DISPLAY_IDMON); // Increases brightness+contrast + //SPI_TRANSFER(DISPLAY_IDMOFF); // Darker but more accurate colour + SPI_TRANSFER(DISPLAY_TESL, 0x00, 0x00, 0x00, 0x01); + SPI_TRANSFER(DISPLAY_GETICID, 0x00, 0x07, 0x00, 0x07, 0x00, 0x1D, 0x00, 0x03); // ? SPI_TRANSFER(0xD1001100, 0x00, 0x03, 0x00, 0x30, 0x00, 0x10); // ? SPI_TRANSFER(0xD2001100, 0x00, 0x03, 0x00, 0x14, 0x00, 0x04); // ? - SPI_TRANSFER(0x29001100/*Display ON*/); + SPI_TRANSFER(DISPLAY_ON); usleep(30*1000); @@ -151,24 +154,32 @@ void InitKeDeiV63() void TurnBacklightOff() { - SPI_TRANSFER(DISPLAY_BACKLIT_BRIT,0xFF); - SPI_TRANSFER(DISPLAY_BACKLIT_CTRL,0x00); // 5-Backlight contro, 3-Display dimming, 2-BacklihtOff } void TurnBacklightOn() { - SPI_TRANSFER(DISPLAY_BACKLIT_BRIT,0x00); - SPI_TRANSFER(DISPLAY_BACKLIT_CTRL,0x2C); // 5-Backlight contro, 3-Display dimming, 2-BacklihtON } void TurnDisplayOff() { - SPI_TRANSFER(DISPLAY_OFF); + // TODO: Reference data sheet (another similar device) isn't accurate for these settings + SPI_TRANSFER(DISPLAY_WRCTRLD,0x00,0x2C); // 5-Backlight contro, 3-Display dimming, 2-BacklihtOff + SPI_TRANSFER(DISPLAY_WRDISBV,0x00,0x00); + + SPI_TRANSFER(DISPLAY_OFF); // Works but whith backlight on, it goes white -- a good 'light' + SPI_TRANSFER(DISPLAY_SLPIN); + usleep(120*1000); } void TurnDisplayOn() { - SPI_TRANSFER(DISPLAY_ON); + SPI_TRANSFER(DISPLAY_SLPOUT); + usleep(120*1000); + SPI_TRANSFER(DISPLAY_ON); + + // TODO: Reference data sheet (another similar device) isn't accurate for these settings + SPI_TRANSFER(DISPLAY_WRCTRLD,0x00,0x00); // 5-Backlight contro, 3-Display dimming, 2-BacklihtON + SPI_TRANSFER(DISPLAY_WRDISBV,0x00,0xFF); } void DeinitSPIDisplay() diff --git a/mpi3501.h b/mpi3501.h index ade61ef..7b2010f 100644 --- a/mpi3501.h +++ b/mpi3501.h @@ -5,14 +5,31 @@ #ifdef MPI3501 // Data specific to the KeDei v6.3 display -#define DISPLAY_NO_OPERATION 0x00001100 -#define DISPLAY_BACKLIT_CTRL 0x53001100 -#define DISPLAY_BACKLIT_BRIT 0x51001100 -#define DISPLAY_ON 0x29001100 -#define DISPLAY_OFF 0x28001100 +#define DISPLAY_NO_OPERATION 0x00001100 +#define DISPLAY_SLPIN 0x10001100 +#define DISPLAY_SLPOUT 0x11001100 +#define DISPLAY_ON 0x29001100 +#define DISPLAY_OFF 0x28001100 #define DISPLAY_SET_CURSOR_X 0x2A001100 #define DISPLAY_SET_CURSOR_Y 0x2B001100 #define DISPLAY_WRITE_PIXELS 0x2C001100 +#define DISPLAY_SETOSC 0xB0001100 +#define DISPLAY_SETRGB 0xB3001100 +#define DISPLAY_SETEXTC 0xB9001100 +#define DISPLAY_SETSTBA 0xC0001100 +#define DISPLAY_SETDGC 0xC1001100 +#define DISPLAY_SETDDB 0xC4001100 +#define DISPLAY_TEON 0x35001100 +#define DISPLAY_MADCTL 0x36001100 +#define DISPLAY_IDMOFF 0x38001100 +#define DISPLAY_IDMON 0x39001100 +#define DSPLAY_COLMOD 0x3A001100 +#define DISPLAY_TESL 0x44001100 +#define DISPLAY_WRDISBV 0x51001100 +#define DISPLAY_WRCTRLD 0x53001100 +#define DISPLAY_WRCABC 0x55001100 +#define DISPLAY_GETICID 0xD0001100 +#define DISPLAY_GETSPIREAD 0xff001100 #define DISPLAY_NATIVE_WIDTH 320 #define DISPLAY_NATIVE_HEIGHT 480 From 5cf3f6008f44d8f8610a346069b406260181efdf Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Fri, 21 Jun 2019 03:49:35 +0000 Subject: [PATCH 24/52] set orientation to match display --- mpi3501.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mpi3501.cpp b/mpi3501.cpp index 17eecbc..6d3c6d7 100644 --- a/mpi3501.cpp +++ b/mpi3501.cpp @@ -44,6 +44,11 @@ void InitKeDeiV63() { // output device touch = XPT2046(); + + touch.setRotation(0); +#ifdef DISPLAY_ROTATE_180_DEGREES + touch.setRotation(1); +#endif // If a Reset pin is defined, toggle it briefly high->low->high to enable the device. Some devices do not have a reset pin, in which case compile with GPIO_TFT_RESET_PIN left undefined. #if defined(GPIO_TFT_RESET_PIN) && GPIO_TFT_RESET_PIN >= 0 From 69754d33ed658e17a4d47f09bcbbb33ff96f94be Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Mon, 24 Jun 2019 11:04:04 +0000 Subject: [PATCH 25/52] calibration utillity --- util/Makefile | 14 ++++ util/tcCalib.cpp | 182 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+) create mode 100644 util/Makefile create mode 100644 util/tcCalib.cpp diff --git a/util/Makefile b/util/Makefile new file mode 100644 index 0000000..75c74d4 --- /dev/null +++ b/util/Makefile @@ -0,0 +1,14 @@ +LIBFLAGS=-L/opt/vc/lib -lEGL -lGLESv2 -lbcm_host -lpthread -ljpeg -lshapes +CXXFLAGS=-DDEBUG -fno-exceptions -DPI -std=gnu++11 -I/opt/vc/include -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/interface/vcos/pthreads -I.. + +objs=tcCalib.o + +all: tcCalib + +clean: $(objs) + rm $(objs) + rm tcCalib + +tcCalib: $(objs) + g++ -Wall $(CXXFLAGS) $(LIBFLAGS) $(objs) -o tcCalib + diff --git a/util/tcCalib.cpp b/util/tcCalib.cpp new file mode 100644 index 0000000..29aadce --- /dev/null +++ b/util/tcCalib.cpp @@ -0,0 +1,182 @@ + + +#include +#include +#include +#include + +#include "VG/openvg.h" +#include "VG/vgu.h" +#include "fontinfo.h" +#include "shapes.h" + +#include +#include +#include + +#define xHairLines_ROWS 8 +int xHairLines[8][4] = { + 10, 15, 20, 15, // lower left + 15, 10, 15, 20, + -20, 15, -10, 15, // lower right + -15, 10, -15, 20, + 10, -15, 20, -15, // upper left + 15, -10, 15, -20, + -20, -15, -10, -15, // upper right + -15, -10, -15, -20 +}; + +// touch state structure +typedef struct { + int fd; + char* evbuff[80]; + VGfloat x, y, z; + int max_x, max_y; + void parse(const char *buff) { + std::regex re( "x:(\\d+),\\s*y:(\\d+),\\s*z:(\\d+)\\s*" ) ; + std::cmatch match_results; + + x = 0; + y = 0; + z = 0; + + if( std::regex_match( (const char *)buff, match_results, re ) && match_results.size() >= 3 ) { + x = std::stoi(match_results[1].str()); + y = std::stoi(match_results[2].str()); + z = std::stoi(match_results[3].str()); + } + } +} touch_t; + +touch_t touch; // global touch state +int left_count = 0; +int quitState = 0; +#define CUR_SIZ 16 // cursor size, pixels beyond centre dot + +// evenThread reads from the touch input file +void *eventThread(void *arg) { + + // Open touch driver + if ((touch.fd = open("/tmp/TCfifo", O_RDONLY)) < 0) { + fprintf(stderr, "Error opening touch!\n"); + quitState = 1; + return &quitState; + } + touch.x = touch.max_x / 2; //Reset touch + touch.y = touch.max_y / 2; + + while (1) { + read(touch.fd, &touch.evbuff, sizeof(char[80])); + touch.parse((const char *)touch.evbuff); + printf("[%4.0f,%4.0f]\r",touch.x,touch.y); + } +} + +static int cur_sx, cur_sy, cur_w, cur_h; // cursor location and dimensions +static int cur_saved = 0; // amount of data saved in cursor image backup + +// saveCursor saves the pixels under the touch cursor +void saveCursor(VGImage CursorBuffer, int curx, int cury, int screen_width, int screen_height, int s) { + int sx, sy, ex, ey; + + sx = curx - s; // horizontal + if (sx < 0) { + sx = 0; + } + ex = curx + s; + if (ex > screen_width) { + ex = screen_width; + } + cur_sx = sx; + cur_w = ex - sx; + + sy = cury - s; // vertical + if (sy < 0) { + sy = 0; + } + ey = cury + s; + if (ey > screen_height) { + ey = screen_height; + } + cur_sy = sy; + cur_h = ey - sy; + + vgGetPixels(CursorBuffer, 0, 0, cur_sx, cur_sy, cur_w, cur_h); + cur_saved = cur_w * cur_h; +} + +// restoreCursor restores the pixels under the touch cursor +void restoreCursor(VGImage CursorBuffer) { + if (cur_saved != 0) { + vgSetPixels(cur_sx, cur_sy, CursorBuffer, 0, 0, cur_w, cur_h); + } +} + +// circleCursor draws a translucent circle as the touch cursor +void circleCursor(int curx, int cury, int width, int height, int s) { + Fill(100, 0, 0, 0.50); + Circle(curx, cury, s); + Fill(0, 0, 0, 1); + Circle(curx, cury, 2); +} + +// touchinit starts the touch event thread +int touchinit(int w, int h) { + pthread_t inputThread; + touch.max_x = w; + touch.max_y = h; + return pthread_create(&inputThread, NULL, &eventThread, NULL); +} + +int main() { + int width, height, cursorx, cursory, cbsize; + + init(&width, &height); // Graphics initialization + cursorx = width / 2; + cursory = height / 2; + cbsize = (CUR_SIZ * 2) + 1; + VGImage CursorBuffer = vgCreateImage(VG_sABGR_8888, cbsize, cbsize, VG_IMAGE_QUALITY_BETTER); + + if (touchinit(width, height) != 0) { + fprintf(stderr, "Unable to initialize the touch\n"); + exit(1); + } + Start(width, height); // Start the picture + Background(0, 0, 0); // Black background + Fill(44, 77, 232, 1); // Big blue marble + Circle(width / 2, 0, width); // The "world" + Fill(255, 255, 255, 1); // White text + TextMid(width / 2, height / 2, "Screen Calibration", SerifTypeface, width / 15); // Greetings + + // Draw lines + Stroke(255, 255, 255, 0.5); + StrokeWidth(2); + for( int i = 0; i < xHairLines_ROWS; i++) { + Line( ( xHairLines[i][0] < 0 ? width + xHairLines[i][0] : xHairLines[i][0] ) + , ( xHairLines[i][1] < 0 ? height + xHairLines[i][1] : xHairLines[i][1] ) + , ( xHairLines[i][2] < 0 ? width + xHairLines[i][2] : xHairLines[i][2] ) + , ( xHairLines[i][3] < 0 ? height + xHairLines[i][3] : xHairLines[i][3] ) + ); + } + + End(); // update picture + + + + // MAIN LOOP + while (left_count < 2) { // Loop until the left touch button pressed & released + // if the touch moved... + if (touch.x != cursorx || touch.y != cursory) { + restoreCursor(CursorBuffer); + cursorx = touch.x; + cursory = touch.y; + saveCursor(CursorBuffer, cursorx, cursory, width, height, CUR_SIZ); + circleCursor(cursorx, cursory, width, height, CUR_SIZ); + End(); // update picture + } + } + restoreCursor(CursorBuffer); // not strictly necessary as display will be closed + vgDestroyImage(CursorBuffer); // tidy up memory + finish(); // Graphics cleanup + exit(0); +} From 1c4a318ca785c3bfdd8f93d83627c59f2ba88085 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Tue, 25 Jun 2019 10:57:45 +0000 Subject: [PATCH 26/52] added calibration fnction --- File | 0 Makefile | 0 calibrate.c | 1 + calibrate.cpp | 363 +++++++++++++++++++++++++++++++++++++++++++++++ calibrate.h | 118 +++++++++++++++ tcCalib.c | 0 util/tcCalib.cpp | 185 ++++++++++++++++++++++++ 7 files changed, 667 insertions(+) create mode 100644 File create mode 100644 Makefile create mode 100755 calibrate.c create mode 100755 calibrate.cpp create mode 100755 calibrate.h create mode 100644 tcCalib.c create mode 100644 util/tcCalib.cpp diff --git a/File b/File new file mode 100644 index 0000000..e69de29 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/calibrate.c b/calibrate.c new file mode 100755 index 0000000..6781eeb --- /dev/null +++ b/calibrate.c @@ -0,0 +1 @@ +/* * * Copyright (c) 2001, Carlos E. Vidales. All rights reserved. * * This sample program was written and put in the public domain * by Carlos E. Vidales. The program is provided "as is" * without warranty of any kind, either expressed or implied. * If you choose to use the program within your own products * you do so at your own risk, and assume the responsibility * for servicing, repairing or correcting the program should * it prove defective in any manner. * You may copy and distribute the program's source code in any * medium, provided that you also include in each copy an * appropriate copyright notice and disclaimer of warranty. * You may also modify this program and distribute copies of * it provided that you include prominent notices stating * that you changed the file(s) and the date of any change, * and that you do not charge any royalties or licenses for * its use. * * * * File Name: calibrate.c * * * This file contains functions that implement calculations * necessary to obtain calibration factors for a touch screen * that suffers from multiple distortion effects: namely, * translation, scaling and rotation. * * The following set of equations represent a valid display * point given a corresponding set of touch screen points: * * * /- -\ * /- -\ /- -\ | | * | | | | | Xs | * | Xd | | A B C | | | * | | = | | * | Ys | * | Yd | | D E F | | | * | | | | | 1 | * \- -/ \- -/ | | * \- -/ * * * where: * * (Xd,Yd) represents the desired display point * coordinates, * * (Xs,Ys) represents the available touch screen * coordinates, and the matrix * * /- -\ * |A,B,C| * |D,E,F| represents the factors used to translate * \- -/ the available touch screen point values * into the corresponding display * coordinates. * * * Note that for practical considerations, the utilitities * within this file do not use the matrix coefficients as * defined above, but instead use the following * equivalents, since floating point math is not used: * * A = An/Divider * B = Bn/Divider * C = Cn/Divider * D = Dn/Divider * E = En/Divider * F = Fn/Divider * * * * The functions provided within this file are: * * setCalibrationMatrix() - calculates the set of factors * in the above equation, given * three sets of test points. * getDisplayPoint() - returns the actual display * coordinates, given a set of * touch screen coordinates. * translateRawScreenCoordinates() - helper function to transform * raw screen points into values * scaled to the desired display * resolution. * * */ #define _CALIBRATE_C_ /****************************************************/ /* */ /* Included files */ /* */ /****************************************************/ #include "Calibrate.h" /****************************************************/ /* */ /* Local Definitions and macros */ /* */ /****************************************************/ /****************************************************/ /* */ /* Global variables */ /* */ /****************************************************/ /****************************************************/ /* */ /* Forward Declaration of local functions */ /* */ /****************************************************/ /********************************************************************** * * Function: setCalibrationMatrix() * * Description: Calling this function with valid input data * in the display and screen input arguments * causes the calibration factors between the * screen and display points to be calculated, * and the output argument - matrixPtr - to be * populated. * * This function needs to be called only when new * calibration factors are desired. * * * Argument(s): displayPtr (input) - Pointer to an array of three * sample, reference points. * screenPtr (input) - Pointer to the array of touch * screen points corresponding * to the reference display points. * matrixPtr (output) - Pointer to the calibration * matrix computed for the set * of points being provided. * * * From the article text, recall that the matrix coefficients are * resolved to be the following: * * * Divider = (Xs0 - Xs2)*(Ys1 - Ys2) - (Xs1 - Xs2)*(Ys0 - Ys2) * * * * (Xd0 - Xd2)*(Ys1 - Ys2) - (Xd1 - Xd2)*(Ys0 - Ys2) * A = --------------------------------------------------- * Divider * * * (Xs0 - Xs2)*(Xd1 - Xd2) - (Xd0 - Xd2)*(Xs1 - Xs2) * B = --------------------------------------------------- * Divider * * * Ys0*(Xs2*Xd1 - Xs1*Xd2) + * Ys1*(Xs0*Xd2 - Xs2*Xd0) + * Ys2*(Xs1*Xd0 - Xs0*Xd1) * C = --------------------------------------------------- * Divider * * * (Yd0 - Yd2)*(Ys1 - Ys2) - (Yd1 - Yd2)*(Ys0 - Ys2) * D = --------------------------------------------------- * Divider * * * (Xs0 - Xs2)*(Yd1 - Yd2) - (Yd0 - Yd2)*(Xs1 - Xs2) * E = --------------------------------------------------- * Divider * * * Ys0*(Xs2*Yd1 - Xs1*Yd2) + * Ys1*(Xs0*Yd2 - Xs2*Yd0) + * Ys2*(Xs1*Yd0 - Xs0*Yd1) * F = --------------------------------------------------- * Divider * * * Return: OK - the calibration matrix was correctly * calculated and its value is in the * output argument. * NOT_OK - an error was detected and the * function failed to return a valid * set of matrix values. * The only time this sample code returns * NOT_OK is when Divider == 0 * * * * NOTE! NOTE! NOTE! * * setCalibrationMatrix() and getDisplayPoint() will do fine * for you as they are, provided that your digitizer * resolution does not exceed 10 bits (1024 values). Higher * resolutions may cause the integer operations to overflow * and return incorrect values. If you wish to use these * functions with digitizer resolutions of 12 bits (4096 * values) you will either have to a) use 64-bit signed * integer variables and math, or b) judiciously modify the * operations to scale results by a factor of 2 or even 4. * * */ int setCalibrationMatrix( POINT * displayPtr, POINT * screenPtr, MATRIX * matrixPtr) { int retValue = OK ; matrixPtr->Divider = ((screenPtr[0].x - screenPtr[2].x) * (screenPtr[1].y - screenPtr[2].y)) - ((screenPtr[1].x - screenPtr[2].x) * (screenPtr[0].y - screenPtr[2].y)) ; if( matrixPtr->Divider == 0 ) { retValue = NOT_OK ; } else { matrixPtr->An = ((displayPtr[0].x - displayPtr[2].x) * (screenPtr[1].y - screenPtr[2].y)) - ((displayPtr[1].x - displayPtr[2].x) * (screenPtr[0].y - screenPtr[2].y)) ; matrixPtr->Bn = ((screenPtr[0].x - screenPtr[2].x) * (displayPtr[1].x - displayPtr[2].x)) - ((displayPtr[0].x - displayPtr[2].x) * (screenPtr[1].x - screenPtr[2].x)) ; matrixPtr->Cn = (screenPtr[2].x * displayPtr[1].x - screenPtr[1].x * displayPtr[2].x) * screenPtr[0].y + (screenPtr[0].x * displayPtr[2].x - screenPtr[2].x * displayPtr[0].x) * screenPtr[1].y + (screenPtr[1].x * displayPtr[0].x - screenPtr[0].x * displayPtr[1].x) * screenPtr[2].y ; matrixPtr->Dn = ((displayPtr[0].y - displayPtr[2].y) * (screenPtr[1].y - screenPtr[2].y)) - ((displayPtr[1].y - displayPtr[2].y) * (screenPtr[0].y - screenPtr[2].y)) ; matrixPtr->En = ((screenPtr[0].x - screenPtr[2].x) * (displayPtr[1].y - displayPtr[2].y)) - ((displayPtr[0].y - displayPtr[2].y) * (screenPtr[1].x - screenPtr[2].x)) ; matrixPtr->Fn = (screenPtr[2].x * displayPtr[1].y - screenPtr[1].x * displayPtr[2].y) * screenPtr[0].y + (screenPtr[0].x * displayPtr[2].y - screenPtr[2].x * displayPtr[0].y) * screenPtr[1].y + (screenPtr[1].x * displayPtr[0].y - screenPtr[0].x * displayPtr[1].y) * screenPtr[2].y ; } return( retValue ) ; } /* end of setCalibrationMatrix() */ /********************************************************************** * * Function: getDisplayPoint() * * Description: Given a valid set of calibration factors and a point * value reported by the touch screen, this function * calculates and returns the true (or closest to true) * display point below the spot where the touch screen * was touched. * * * * Argument(s): displayPtr (output) - Pointer to the calculated * (true) display point. * screenPtr (input) - Pointer to the reported touch * screen point. * matrixPtr (input) - Pointer to calibration factors * matrix previously calculated * from a call to * setCalibrationMatrix() * * * The function simply solves for Xd and Yd by implementing the * computations required by the translation matrix. * * /- -\ * /- -\ /- -\ | | * | | | | | Xs | * | Xd | | A B C | | | * | | = | | * | Ys | * | Yd | | D E F | | | * | | | | | 1 | * \- -/ \- -/ | | * \- -/ * * It must be kept brief to avoid consuming CPU cycles. * * * Return: OK - the display point was correctly calculated * and its value is in the output argument. * NOT_OK - an error was detected and the function * failed to return a valid point. * * * * NOTE! NOTE! NOTE! * * setCalibrationMatrix() and getDisplayPoint() will do fine * for you as they are, provided that your digitizer * resolution does not exceed 10 bits (1024 values). Higher * resolutions may cause the integer operations to overflow * and return incorrect values. If you wish to use these * functions with digitizer resolutions of 12 bits (4096 * values) you will either have to a) use 64-bit signed * integer variables and math, or b) judiciously modify the * operations to scale results by a factor of 2 or even 4. * * */ int getDisplayPoint( POINT * displayPtr, POINT * screenPtr, MATRIX * matrixPtr ) { int retValue = OK ; if( matrixPtr->Divider != 0 ) { /* Operation order is important since we are doing integer */ /* math. Make sure you add all terms together before */ /* dividing, so that the remainder is not rounded off */ /* prematurely. */ displayPtr->x = ( (matrixPtr->An * screenPtr->x) + (matrixPtr->Bn * screenPtr->y) + matrixPtr->Cn ) / matrixPtr->Divider ; displayPtr->y = ( (matrixPtr->Dn * screenPtr->x) + (matrixPtr->En * screenPtr->y) + matrixPtr->Fn ) / matrixPtr->Divider ; } else { retValue = NOT_OK ; } return( retValue ) ; } /* end of getDisplayPoint() */ \ No newline at end of file diff --git a/calibrate.cpp b/calibrate.cpp new file mode 100755 index 0000000..ae211dd --- /dev/null +++ b/calibrate.cpp @@ -0,0 +1,363 @@ +/* + * + * Copyright (c) 2001, Carlos E. Vidales. All rights reserved. + * + * This sample program was written and put in the public domain + * by Carlos E. Vidales. The program is provided "as is" + * without warranty of any kind, either expressed or implied. + * If you choose to use the program within your own products + * you do so at your own risk, and assume the responsibility + * for servicing, repairing or correcting the program should + * it prove defective in any manner. + * You may copy and distribute the program's source code in any + * medium, provided that you also include in each copy an + * appropriate copyright notice and disclaimer of warranty. + * You may also modify this program and distribute copies of + * it provided that you include prominent notices stating + * that you changed the file(s) and the date of any change, + * and that you do not charge any royalties or licenses for + * its use. + * + * + * + * File Name: calibrate.c + * + * + * This file contains functions that implement calculations + * necessary to obtain calibration factors for a touch screen + * that suffers from multiple distortion effects: namely, + * translation, scaling and rotation. + * + * The following set of equations represent a valid display + * point given a corresponding set of touch screen points: + * + * + * /- -\ + * /- -\ /- -\ | | + * | | | | | Xs | + * | Xd | | A B C | | | + * | | = | | * | Ys | + * | Yd | | D E F | | | + * | | | | | 1 | + * \- -/ \- -/ | | + * \- -/ + * + * + * where: + * + * (Xd,Yd) represents the desired display point + * coordinates, + * + * (Xs,Ys) represents the available touch screen + * coordinates, and the matrix + * + * /- -\ + * |A,B,C| + * |D,E,F| represents the factors used to translate + * \- -/ the available touch screen point values + * into the corresponding display + * coordinates. + * + * + * Note that for practical considerations, the utilitities + * within this file do not use the matrix coefficients as + * defined above, but instead use the following + * equivalents, since floating point math is not used: + * + * A = An/Divider + * B = Bn/Divider + * C = Cn/Divider + * D = Dn/Divider + * E = En/Divider + * F = Fn/Divider + * + * + * + * The functions provided within this file are: + * + * setCalibrationMatrix() - calculates the set of factors + * in the above equation, given + * three sets of test points. + * getDisplayPoint() - returns the actual display + * coordinates, given a set of + * touch screen coordinates. + * translateRawScreenCoordinates() - helper function to transform + * raw screen points into values + * scaled to the desired display + * resolution. + * + * + */ + + +#define _CALIBRATE_C_ + + + +/****************************************************/ +/* */ +/* Included files */ +/* */ +/****************************************************/ + +#include "calibrate.h" + + +/****************************************************/ +/* */ +/* Local Definitions and macros */ +/* */ +/****************************************************/ + + + +/****************************************************/ +/* */ +/* Global variables */ +/* */ +/****************************************************/ + + + +/****************************************************/ +/* */ +/* Forward Declaration of local functions */ +/* */ +/****************************************************/ + + + + + + +/********************************************************************** + * + * Function: setCalibrationMatrix() + * + * Description: Calling this function with valid input data + * in the display and screen input arguments + * causes the calibration factors between the + * screen and display points to be calculated, + * and the output argument - matrixPtr - to be + * populated. + * + * This function needs to be called only when new + * calibration factors are desired. + * + * + * Argument(s): displayPtr (input) - Pointer to an array of three + * sample, reference points. + * screenPtr (input) - Pointer to the array of touch + * screen points corresponding + * to the reference display points. + * matrixPtr (output) - Pointer to the calibration + * matrix computed for the set + * of points being provided. + * + * + * From the article text, recall that the matrix coefficients are + * resolved to be the following: + * + * + * Divider = (Xs0 - Xs2)*(Ys1 - Ys2) - (Xs1 - Xs2)*(Ys0 - Ys2) + * + * + * + * (Xd0 - Xd2)*(Ys1 - Ys2) - (Xd1 - Xd2)*(Ys0 - Ys2) + * A = --------------------------------------------------- + * Divider + * + * + * (Xs0 - Xs2)*(Xd1 - Xd2) - (Xd0 - Xd2)*(Xs1 - Xs2) + * B = --------------------------------------------------- + * Divider + * + * + * Ys0*(Xs2*Xd1 - Xs1*Xd2) + + * Ys1*(Xs0*Xd2 - Xs2*Xd0) + + * Ys2*(Xs1*Xd0 - Xs0*Xd1) + * C = --------------------------------------------------- + * Divider + * + * + * (Yd0 - Yd2)*(Ys1 - Ys2) - (Yd1 - Yd2)*(Ys0 - Ys2) + * D = --------------------------------------------------- + * Divider + * + * + * (Xs0 - Xs2)*(Yd1 - Yd2) - (Yd0 - Yd2)*(Xs1 - Xs2) + * E = --------------------------------------------------- + * Divider + * + * + * Ys0*(Xs2*Yd1 - Xs1*Yd2) + + * Ys1*(Xs0*Yd2 - Xs2*Yd0) + + * Ys2*(Xs1*Yd0 - Xs0*Yd1) + * F = --------------------------------------------------- + * Divider + * + * + * Return: OK - the calibration matrix was correctly + * calculated and its value is in the + * output argument. + * NOT_OK - an error was detected and the + * function failed to return a valid + * set of matrix values. + * The only time this sample code returns + * NOT_OK is when Divider == 0 + * + * + * + * NOTE! NOTE! NOTE! + * + * setCalibrationMatrix() and getDisplayPoint() will do fine + * for you as they are, provided that your digitizer + * resolution does not exceed 10 bits (1024 values). Higher + * resolutions may cause the integer operations to overflow + * and return incorrect values. If you wish to use these + * functions with digitizer resolutions of 12 bits (4096 + * values) you will either have to a) use 64-bit signed + * integer variables and math, or b) judiciously modify the + * operations to scale results by a factor of 2 or even 4. + * + * + */ +int setCalibrationMatrix( POINT * displayPtr, + POINT * screenPtr, + MATRIX * matrixPtr) +{ + + int retValue = OK ; + + + + matrixPtr->Divider = ((screenPtr[0].x - screenPtr[2].x) * (screenPtr[1].y - screenPtr[2].y)) - + ((screenPtr[1].x - screenPtr[2].x) * (screenPtr[0].y - screenPtr[2].y)) ; + + if( matrixPtr->Divider == 0 ) + { + retValue = NOT_OK ; + } + else + { + matrixPtr->An = ((displayPtr[0].x - displayPtr[2].x) * (screenPtr[1].y - screenPtr[2].y)) - + ((displayPtr[1].x - displayPtr[2].x) * (screenPtr[0].y - screenPtr[2].y)) ; + + matrixPtr->Bn = ((screenPtr[0].x - screenPtr[2].x) * (displayPtr[1].x - displayPtr[2].x)) - + ((displayPtr[0].x - displayPtr[2].x) * (screenPtr[1].x - screenPtr[2].x)) ; + + matrixPtr->Cn = (screenPtr[2].x * displayPtr[1].x - screenPtr[1].x * displayPtr[2].x) * screenPtr[0].y + + (screenPtr[0].x * displayPtr[2].x - screenPtr[2].x * displayPtr[0].x) * screenPtr[1].y + + (screenPtr[1].x * displayPtr[0].x - screenPtr[0].x * displayPtr[1].x) * screenPtr[2].y ; + + matrixPtr->Dn = ((displayPtr[0].y - displayPtr[2].y) * (screenPtr[1].y - screenPtr[2].y)) - + ((displayPtr[1].y - displayPtr[2].y) * (screenPtr[0].y - screenPtr[2].y)) ; + + matrixPtr->En = ((screenPtr[0].x - screenPtr[2].x) * (displayPtr[1].y - displayPtr[2].y)) - + ((displayPtr[0].y - displayPtr[2].y) * (screenPtr[1].x - screenPtr[2].x)) ; + + matrixPtr->Fn = (screenPtr[2].x * displayPtr[1].y - screenPtr[1].x * displayPtr[2].y) * screenPtr[0].y + + (screenPtr[0].x * displayPtr[2].y - screenPtr[2].x * displayPtr[0].y) * screenPtr[1].y + + (screenPtr[1].x * displayPtr[0].y - screenPtr[0].x * displayPtr[1].y) * screenPtr[2].y ; + } + + return( retValue ) ; + +} /* end of setCalibrationMatrix() */ + + + +/********************************************************************** + * + * Function: getDisplayPoint() + * + * Description: Given a valid set of calibration factors and a point + * value reported by the touch screen, this function + * calculates and returns the true (or closest to true) + * display point below the spot where the touch screen + * was touched. + * + * + * + * Argument(s): displayPtr (output) - Pointer to the calculated + * (true) display point. + * screenPtr (input) - Pointer to the reported touch + * screen point. + * matrixPtr (input) - Pointer to calibration factors + * matrix previously calculated + * from a call to + * setCalibrationMatrix() + * + * + * The function simply solves for Xd and Yd by implementing the + * computations required by the translation matrix. + * + * /- -\ + * /- -\ /- -\ | | + * | | | | | Xs | + * | Xd | | A B C | | | + * | | = | | * | Ys | + * | Yd | | D E F | | | + * | | | | | 1 | + * \- -/ \- -/ | | + * \- -/ + * + * It must be kept brief to avoid consuming CPU cycles. + * + * + * Return: OK - the display point was correctly calculated + * and its value is in the output argument. + * NOT_OK - an error was detected and the function + * failed to return a valid point. + * + * + * + * NOTE! NOTE! NOTE! + * + * setCalibrationMatrix() and getDisplayPoint() will do fine + * for you as they are, provided that your digitizer + * resolution does not exceed 10 bits (1024 values). Higher + * resolutions may cause the integer operations to overflow + * and return incorrect values. If you wish to use these + * functions with digitizer resolutions of 12 bits (4096 + * values) you will either have to a) use 64-bit signed + * integer variables and math, or b) judiciously modify the + * operations to scale results by a factor of 2 or even 4. + * + * + */ +int getDisplayPoint( POINT * displayPtr, + POINT * screenPtr, + MATRIX * matrixPtr ) +{ + int retValue = OK ; + + + if( matrixPtr->Divider != 0 ) + { + + /* Operation order is important since we are doing integer */ + /* math. Make sure you add all terms together before */ + /* dividing, so that the remainder is not rounded off */ + /* prematurely. */ + + displayPtr->x = ( (matrixPtr->An * screenPtr->x) + + (matrixPtr->Bn * screenPtr->y) + + matrixPtr->Cn + ) / matrixPtr->Divider ; + + displayPtr->y = ( (matrixPtr->Dn * screenPtr->x) + + (matrixPtr->En * screenPtr->y) + + matrixPtr->Fn + ) / matrixPtr->Divider ; + } + else + { + retValue = NOT_OK ; + } + + return( retValue ) ; + +} /* end of getDisplayPoint() */ + + diff --git a/calibrate.h b/calibrate.h new file mode 100755 index 0000000..2835761 --- /dev/null +++ b/calibrate.h @@ -0,0 +1,118 @@ +/* + * + * Copyright (c) 2001, Carlos E. Vidales. All rights reserved. + * + * This sample program was written and put in the public domain + * by Carlos E. Vidales. The program is provided "as is" + * without warranty of any kind, either expressed or implied. + * If you choose to use the program within your own products + * you do so at your own risk, and assume the responsibility + * for servicing, repairing or correcting the program should + * it prove defective in any manner. + * You may copy and distribute the program's source code in any + * medium, provided that you also include in each copy an + * appropriate copyright notice and disclaimer of warranty. + * You may also modify this program and distribute copies of + * it provided that you include prominent notices stating + * that you changed the file(s) and the date of any change, + * and that you do not charge any royalties or licenses for + * its use. + * + * + * File Name: calibrate.h + * + * + * Definition of constants and structures, and declaration of functions + * in Calibrate.c + * + * Revisions: Kevin Peck 2019 + */ + +#ifndef _CALIBRATE_H_ + +#define _CALIBRATE_H_ + +/****************************************************/ +/* */ +/* Included files */ +/* */ +/****************************************************/ + +#include + + +/****************************************************/ +/* */ +/* Definitions */ +/* */ +/****************************************************/ + +#ifndef _CALIBRATE_C_ + #define EXTERN extern +#else + #define EXTERN +#endif + + + +#ifndef OK + #define OK 0 + #define NOT_OK -1 +#endif + + + +#define INT32 long + + + + +/****************************************************/ +/* */ +/* Structures */ +/* */ +/****************************************************/ + + +typedef struct Point { + INT32 x, + y ; + } POINT ; + + + +typedef struct Matrix { + /* This arrangement of values facilitates + * calculations within getDisplayPoint() + */ + INT32 An, /* A = An/Divider */ + Bn, /* B = Bn/Divider */ + Cn, /* C = Cn/Divider */ + Dn, /* D = Dn/Divider */ + En, /* E = En/Divider */ + Fn, /* F = Fn/Divider */ + Divider ; + } MATRIX ; + + + + +/****************************************************/ +/* */ +/* Function declarations */ +/* */ +/****************************************************/ + + +EXTERN int setCalibrationMatrix( POINT * display, + POINT * screen, + MATRIX * matrix) ; + + +EXTERN int getDisplayPoint( POINT * display, + POINT * screen, + MATRIX * matrix ) ; + + +#endif /* _CALIBRATE_H_ */ + diff --git a/tcCalib.c b/tcCalib.c new file mode 100644 index 0000000..e69de29 diff --git a/util/tcCalib.cpp b/util/tcCalib.cpp new file mode 100644 index 0000000..991d4e6 --- /dev/null +++ b/util/tcCalib.cpp @@ -0,0 +1,185 @@ + + +#include +#include +#include + +#include "VG/openvg.h" +#include "VG/vgu.h" +#include "fontinfo.h" +#include "shapes.h" + +#include +#include +#include + +// touch state structure +typedef struct { + int fd; + char[80] evbuff; + VGfloat x, y, z; + int max_x, max_y; + void parse(const char *buff) { + + } +} touch_t; + +touch_t touch; // global touch state +int left_count = 0; +int quitState = 0; +#define CUR_SIZ 16 // cursor size, pixels beyond centre dot + +// evenThread reads from the touch input file +void *eventThread(void *arg) { + + // Open touch driver + if ((touch.fd = open("/tmp/TCfifo", O_RDONLY)) < 0) { + fprintf(stderr, "Error opening touch!\n"); + quitState = 1; + return &quitState; + } + touch.x = touch.max_x / 2; //Reset touch + touch.y = touch.max_y / 2; + + while (1) { + read(touch.fd, &touch.evbuff, sizeof(char[80])); + // printf("[%4.0f,%4.0f]\r",touch.x,touch.y); + + // Check events + touch.left = CUR_SIZ * 2; // Reset touch button states + touch.right = CUR_SIZ * 2; + + if (touch.ev.type == EV_REL) { + if (touch.ev.code == REL_X) { + touch.x += (VGfloat) touch.ev.value; + if (touch.x < 0) { + touch.x = 0; + } + if (touch.x > touch.max_x) { + touch.x = touch.max_x; + } + } + if (touch.ev.code == REL_Y) { //This ones goes backwards hence the minus + touch.y -= (VGfloat) touch.ev.value; + if (touch.y < 0) { + touch.y = 0; + } + if (touch.y > touch.max_y) { + touch.y = touch.max_y; + } + } + } + + if (touch.ev.type == EV_KEY) { + //printf("Time Stamp:%d - type %d, code %d, value %d\n", + // touch.ev.time.tv_usec,touch.ev.type,touch.ev.code,touch.ev.value); + if (touch.ev.code == BTN_LEFT) { + touch.left = 1; + // printf("Left button\n"); + left_count++; + // printf("User Quit\n"); + // quitState = 1; + // return &quitState; //Left touch to quit + } + if (touch.ev.code == BTN_RIGHT) { + touch.right = 1; + // printf("Right button\n"); + } + } + } +} + +static int cur_sx, cur_sy, cur_w, cur_h; // cursor location and dimensions +static int cur_saved = 0; // amount of data saved in cursor image backup + +// saveCursor saves the pixels under the touch cursor +void saveCursor(VGImage CursorBuffer, int curx, int cury, int screen_width, int screen_height, int s) { + int sx, sy, ex, ey; + + sx = curx - s; // horizontal + if (sx < 0) { + sx = 0; + } + ex = curx + s; + if (ex > screen_width) { + ex = screen_width; + } + cur_sx = sx; + cur_w = ex - sx; + + sy = cury - s; // vertical + if (sy < 0) { + sy = 0; + } + ey = cury + s; + if (ey > screen_height) { + ey = screen_height; + } + cur_sy = sy; + cur_h = ey - sy; + + vgGetPixels(CursorBuffer, 0, 0, cur_sx, cur_sy, cur_w, cur_h); + cur_saved = cur_w * cur_h; +} + +// restoreCursor restores the pixels under the touch cursor +void restoreCursor(VGImage CursorBuffer) { + if (cur_saved != 0) { + vgSetPixels(cur_sx, cur_sy, CursorBuffer, 0, 0, cur_w, cur_h); + } +} + +// circleCursor draws a translucent circle as the touch cursor +void circleCursor(int curx, int cury, int width, int height, int s) { + Fill(100, 0, 0, 0.50); + Circle(curx, cury, s); + Fill(0, 0, 0, 1); + Circle(curx, cury, 2); +} + +// touchinit starts the touch event thread +int touchinit(int w, int h) { + pthread_t inputThread; + touch.max_x = w; + touch.max_y = h; + return pthread_create(&inputThread, NULL, &eventThread, NULL); +} + +int main() { + int width, height, cursorx, cursory, cbsize; + + init(&width, &height); // Graphics initialization + cursorx = width / 2; + cursory = height / 2; + cbsize = (CUR_SIZ * 2) + 1; + VGImage CursorBuffer = vgCreateImage(VG_sABGR_8888, cbsize, cbsize, VG_IMAGE_QUALITY_BETTER); + + if (touchinit(width, height) != 0) { + fprintf(stderr, "Unable to initialize the touch\n"); + exit(1); + } + Start(width, height); // Start the picture + Background(0, 0, 0); // Black background + Fill(44, 77, 232, 1); // Big blue marble + Circle(width / 2, 0, width); // The "world" + Fill(255, 255, 255, 1); // White text + TextMid(width / 2, height / 2, "hello, world", SerifTypeface, width / 10); // Greetings + End(); // update picture + + // MAIN LOOP + while (left_count < 2) { // Loop until the left touch button pressed & released + // if the touch moved... + if (touch.x != cursorx || touch.y != cursory) { + restoreCursor(CursorBuffer); + cursorx = touch.x; + cursory = touch.y; + saveCursor(CursorBuffer, cursorx, cursory, width, height, CUR_SIZ); + circleCursor(cursorx, cursory, width, height, CUR_SIZ); + End(); // update picture + } + } + restoreCursor(CursorBuffer); // not strictly necessary as display will be closed + vgDestroyImage(CursorBuffer); // tidy up memory + finish(); // Graphics cleanup + exit(0); +} From f3234db87d364a527a2020440891fddc435408f7 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Tue, 25 Jun 2019 10:59:22 +0000 Subject: [PATCH 27/52] cleanup --- XPT2046.cpp | 4 ++- calibrate.c | 1 - mpi3501.cpp | 70 +++++++++++++++++++++++--------------- mpi3501.h | 27 ++++++++++++--- tcCalib.c | 0 util/tcCalib.cpp | 87 +++++++++++++++++++++++------------------------- 6 files changed, 110 insertions(+), 79 deletions(-) delete mode 100755 calibrate.c delete mode 100644 tcCalib.c diff --git a/XPT2046.cpp b/XPT2046.cpp index 0b87bb2..1231b06 100644 --- a/XPT2046.cpp +++ b/XPT2046.cpp @@ -23,7 +23,9 @@ */ #include "XPT2046.h" - +extern "C" { + #include "calibrate.h" +} #define XPT2046_CFG_START 1<<7 #define XPT2046_CFG_MUX(v) ((v&0b111) << (4)) diff --git a/calibrate.c b/calibrate.c deleted file mode 100755 index 6781eeb..0000000 --- a/calibrate.c +++ /dev/null @@ -1 +0,0 @@ -/* * * Copyright (c) 2001, Carlos E. Vidales. All rights reserved. * * This sample program was written and put in the public domain * by Carlos E. Vidales. The program is provided "as is" * without warranty of any kind, either expressed or implied. * If you choose to use the program within your own products * you do so at your own risk, and assume the responsibility * for servicing, repairing or correcting the program should * it prove defective in any manner. * You may copy and distribute the program's source code in any * medium, provided that you also include in each copy an * appropriate copyright notice and disclaimer of warranty. * You may also modify this program and distribute copies of * it provided that you include prominent notices stating * that you changed the file(s) and the date of any change, * and that you do not charge any royalties or licenses for * its use. * * * * File Name: calibrate.c * * * This file contains functions that implement calculations * necessary to obtain calibration factors for a touch screen * that suffers from multiple distortion effects: namely, * translation, scaling and rotation. * * The following set of equations represent a valid display * point given a corresponding set of touch screen points: * * * /- -\ * /- -\ /- -\ | | * | | | | | Xs | * | Xd | | A B C | | | * | | = | | * | Ys | * | Yd | | D E F | | | * | | | | | 1 | * \- -/ \- -/ | | * \- -/ * * * where: * * (Xd,Yd) represents the desired display point * coordinates, * * (Xs,Ys) represents the available touch screen * coordinates, and the matrix * * /- -\ * |A,B,C| * |D,E,F| represents the factors used to translate * \- -/ the available touch screen point values * into the corresponding display * coordinates. * * * Note that for practical considerations, the utilitities * within this file do not use the matrix coefficients as * defined above, but instead use the following * equivalents, since floating point math is not used: * * A = An/Divider * B = Bn/Divider * C = Cn/Divider * D = Dn/Divider * E = En/Divider * F = Fn/Divider * * * * The functions provided within this file are: * * setCalibrationMatrix() - calculates the set of factors * in the above equation, given * three sets of test points. * getDisplayPoint() - returns the actual display * coordinates, given a set of * touch screen coordinates. * translateRawScreenCoordinates() - helper function to transform * raw screen points into values * scaled to the desired display * resolution. * * */ #define _CALIBRATE_C_ /****************************************************/ /* */ /* Included files */ /* */ /****************************************************/ #include "Calibrate.h" /****************************************************/ /* */ /* Local Definitions and macros */ /* */ /****************************************************/ /****************************************************/ /* */ /* Global variables */ /* */ /****************************************************/ /****************************************************/ /* */ /* Forward Declaration of local functions */ /* */ /****************************************************/ /********************************************************************** * * Function: setCalibrationMatrix() * * Description: Calling this function with valid input data * in the display and screen input arguments * causes the calibration factors between the * screen and display points to be calculated, * and the output argument - matrixPtr - to be * populated. * * This function needs to be called only when new * calibration factors are desired. * * * Argument(s): displayPtr (input) - Pointer to an array of three * sample, reference points. * screenPtr (input) - Pointer to the array of touch * screen points corresponding * to the reference display points. * matrixPtr (output) - Pointer to the calibration * matrix computed for the set * of points being provided. * * * From the article text, recall that the matrix coefficients are * resolved to be the following: * * * Divider = (Xs0 - Xs2)*(Ys1 - Ys2) - (Xs1 - Xs2)*(Ys0 - Ys2) * * * * (Xd0 - Xd2)*(Ys1 - Ys2) - (Xd1 - Xd2)*(Ys0 - Ys2) * A = --------------------------------------------------- * Divider * * * (Xs0 - Xs2)*(Xd1 - Xd2) - (Xd0 - Xd2)*(Xs1 - Xs2) * B = --------------------------------------------------- * Divider * * * Ys0*(Xs2*Xd1 - Xs1*Xd2) + * Ys1*(Xs0*Xd2 - Xs2*Xd0) + * Ys2*(Xs1*Xd0 - Xs0*Xd1) * C = --------------------------------------------------- * Divider * * * (Yd0 - Yd2)*(Ys1 - Ys2) - (Yd1 - Yd2)*(Ys0 - Ys2) * D = --------------------------------------------------- * Divider * * * (Xs0 - Xs2)*(Yd1 - Yd2) - (Yd0 - Yd2)*(Xs1 - Xs2) * E = --------------------------------------------------- * Divider * * * Ys0*(Xs2*Yd1 - Xs1*Yd2) + * Ys1*(Xs0*Yd2 - Xs2*Yd0) + * Ys2*(Xs1*Yd0 - Xs0*Yd1) * F = --------------------------------------------------- * Divider * * * Return: OK - the calibration matrix was correctly * calculated and its value is in the * output argument. * NOT_OK - an error was detected and the * function failed to return a valid * set of matrix values. * The only time this sample code returns * NOT_OK is when Divider == 0 * * * * NOTE! NOTE! NOTE! * * setCalibrationMatrix() and getDisplayPoint() will do fine * for you as they are, provided that your digitizer * resolution does not exceed 10 bits (1024 values). Higher * resolutions may cause the integer operations to overflow * and return incorrect values. If you wish to use these * functions with digitizer resolutions of 12 bits (4096 * values) you will either have to a) use 64-bit signed * integer variables and math, or b) judiciously modify the * operations to scale results by a factor of 2 or even 4. * * */ int setCalibrationMatrix( POINT * displayPtr, POINT * screenPtr, MATRIX * matrixPtr) { int retValue = OK ; matrixPtr->Divider = ((screenPtr[0].x - screenPtr[2].x) * (screenPtr[1].y - screenPtr[2].y)) - ((screenPtr[1].x - screenPtr[2].x) * (screenPtr[0].y - screenPtr[2].y)) ; if( matrixPtr->Divider == 0 ) { retValue = NOT_OK ; } else { matrixPtr->An = ((displayPtr[0].x - displayPtr[2].x) * (screenPtr[1].y - screenPtr[2].y)) - ((displayPtr[1].x - displayPtr[2].x) * (screenPtr[0].y - screenPtr[2].y)) ; matrixPtr->Bn = ((screenPtr[0].x - screenPtr[2].x) * (displayPtr[1].x - displayPtr[2].x)) - ((displayPtr[0].x - displayPtr[2].x) * (screenPtr[1].x - screenPtr[2].x)) ; matrixPtr->Cn = (screenPtr[2].x * displayPtr[1].x - screenPtr[1].x * displayPtr[2].x) * screenPtr[0].y + (screenPtr[0].x * displayPtr[2].x - screenPtr[2].x * displayPtr[0].x) * screenPtr[1].y + (screenPtr[1].x * displayPtr[0].x - screenPtr[0].x * displayPtr[1].x) * screenPtr[2].y ; matrixPtr->Dn = ((displayPtr[0].y - displayPtr[2].y) * (screenPtr[1].y - screenPtr[2].y)) - ((displayPtr[1].y - displayPtr[2].y) * (screenPtr[0].y - screenPtr[2].y)) ; matrixPtr->En = ((screenPtr[0].x - screenPtr[2].x) * (displayPtr[1].y - displayPtr[2].y)) - ((displayPtr[0].y - displayPtr[2].y) * (screenPtr[1].x - screenPtr[2].x)) ; matrixPtr->Fn = (screenPtr[2].x * displayPtr[1].y - screenPtr[1].x * displayPtr[2].y) * screenPtr[0].y + (screenPtr[0].x * displayPtr[2].y - screenPtr[2].x * displayPtr[0].y) * screenPtr[1].y + (screenPtr[1].x * displayPtr[0].y - screenPtr[0].x * displayPtr[1].y) * screenPtr[2].y ; } return( retValue ) ; } /* end of setCalibrationMatrix() */ /********************************************************************** * * Function: getDisplayPoint() * * Description: Given a valid set of calibration factors and a point * value reported by the touch screen, this function * calculates and returns the true (or closest to true) * display point below the spot where the touch screen * was touched. * * * * Argument(s): displayPtr (output) - Pointer to the calculated * (true) display point. * screenPtr (input) - Pointer to the reported touch * screen point. * matrixPtr (input) - Pointer to calibration factors * matrix previously calculated * from a call to * setCalibrationMatrix() * * * The function simply solves for Xd and Yd by implementing the * computations required by the translation matrix. * * /- -\ * /- -\ /- -\ | | * | | | | | Xs | * | Xd | | A B C | | | * | | = | | * | Ys | * | Yd | | D E F | | | * | | | | | 1 | * \- -/ \- -/ | | * \- -/ * * It must be kept brief to avoid consuming CPU cycles. * * * Return: OK - the display point was correctly calculated * and its value is in the output argument. * NOT_OK - an error was detected and the function * failed to return a valid point. * * * * NOTE! NOTE! NOTE! * * setCalibrationMatrix() and getDisplayPoint() will do fine * for you as they are, provided that your digitizer * resolution does not exceed 10 bits (1024 values). Higher * resolutions may cause the integer operations to overflow * and return incorrect values. If you wish to use these * functions with digitizer resolutions of 12 bits (4096 * values) you will either have to a) use 64-bit signed * integer variables and math, or b) judiciously modify the * operations to scale results by a factor of 2 or even 4. * * */ int getDisplayPoint( POINT * displayPtr, POINT * screenPtr, MATRIX * matrixPtr ) { int retValue = OK ; if( matrixPtr->Divider != 0 ) { /* Operation order is important since we are doing integer */ /* math. Make sure you add all terms together before */ /* dividing, so that the remainder is not rounded off */ /* prematurely. */ displayPtr->x = ( (matrixPtr->An * screenPtr->x) + (matrixPtr->Bn * screenPtr->y) + matrixPtr->Cn ) / matrixPtr->Divider ; displayPtr->y = ( (matrixPtr->Dn * screenPtr->x) + (matrixPtr->En * screenPtr->y) + matrixPtr->Fn ) / matrixPtr->Divider ; } else { retValue = NOT_OK ; } return( retValue ) ; } /* end of getDisplayPoint() */ \ No newline at end of file diff --git a/mpi3501.cpp b/mpi3501.cpp index 781d24f..6d3c6d7 100644 --- a/mpi3501.cpp +++ b/mpi3501.cpp @@ -44,6 +44,11 @@ void InitKeDeiV63() { // output device touch = XPT2046(); + + touch.setRotation(0); +#ifdef DISPLAY_ROTATE_180_DEGREES + touch.setRotation(1); +#endif // If a Reset pin is defined, toggle it briefly high->low->high to enable the device. Some devices do not have a reset pin, in which case compile with GPIO_TFT_RESET_PIN left undefined. #if defined(GPIO_TFT_RESET_PIN) && GPIO_TFT_RESET_PIN >= 0 @@ -80,31 +85,31 @@ void InitKeDeiV63() SPI_TRANSFER(DISPLAY_NO_OPERATION); // Reset usleep(10*1000); - SPI_TRANSFER(0xff001100); - SPI_TRANSFER(0xff001100); + SPI_TRANSFER(DISPLAY_GETSPIREAD); + SPI_TRANSFER(DISPLAY_GETSPIREAD); usleep(10*1000); - SPI_TRANSFER(0xff001100); - SPI_TRANSFER(0xff001100); - SPI_TRANSFER(0xff001100); - SPI_TRANSFER(0xff001100); + SPI_TRANSFER(DISPLAY_GETSPIREAD); + SPI_TRANSFER(DISPLAY_GETSPIREAD); + SPI_TRANSFER(DISPLAY_GETSPIREAD); + SPI_TRANSFER(DISPLAY_GETSPIREAD); usleep(15*1000); - SPI_TRANSFER(0x11001100/*Sleep Out*/); + SPI_TRANSFER(DISPLAY_SLPOUT); usleep(150*1000); - SPI_TRANSFER(0xB0001100, 0x00, 0x00); // CLK 30Hz? - SPI_TRANSFER(0xB3001100, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); // set RGB? - SPI_TRANSFER(0xB9001100, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0f); // ext cmd? - SPI_TRANSFER(0xC0001100, 0x00, 0x13, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x02 - , 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x43); // Timing? - SPI_TRANSFER(0xC1001100, 0x00, 0x08, 0x00, 0x0F, 0x00, 0x08, 0x00, 0x08); // gamma? - SPI_TRANSFER(0xC4001100, 0x00, 0x11, 0x00, 0x07, 0x00, 0x03, 0x00, 0x04); // sleep modes? + SPI_TRANSFER(DISPLAY_SETOSC, 0x00, 0x00); + SPI_TRANSFER(DISPLAY_SETRGB, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + SPI_TRANSFER(DISPLAY_SETEXTC, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0f); + SPI_TRANSFER(DISPLAY_SETSTBA, 0x00, 0x13, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x02 + , 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x43); + SPI_TRANSFER(DISPLAY_SETDGC, 0x00, 0x08, 0x00, 0x0F, 0x00, 0x08, 0x00, 0x08); + SPI_TRANSFER(DISPLAY_SETDDB, 0x00, 0x11, 0x00, 0x07, 0x00, 0x03, 0x00, 0x04); SPI_TRANSFER(0xC6001100, 0x00, 0x00); // ? SPI_TRANSFER(0xC8001100, 0x00, 0x03, 0x00, 0x03, 0x00, 0x13, 0x00, 0x5C , 0x00, 0x03, 0x00, 0x07, 0x00, 0x14, 0x00, 0x08 , 0x00, 0x00, 0x00, 0x21, 0x00, 0x08, 0x00, 0x14 , 0x00, 0x07, 0x00, 0x53, 0x00, 0x0C, 0x00, 0x13 , 0x00, 0x03, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00); // ? - SPI_TRANSFER(0x35001100, 0x00, 0x00); + SPI_TRANSFER(DISPLAY_TEON, 0x00, 0x00); #define MADCTL_BGR_PIXEL_ORDER (1<<3) #define MADCTL_LINE_ADDRESS_ORDER_SWAP (1<<4) @@ -123,14 +128,17 @@ void InitKeDeiV63() #ifdef DISPLAY_ROTATE_180_DEGREES madctl ^= MADCTL_ROTATE_180_DEGREES; #endif - SPI_TRANSFER(0x36001100/*MADCTL: Memory Access Control*/, 0x00, madctl); - - SPI_TRANSFER(0x3A001100/*Interface Pixel Format*/, 0x00, 0x55); - SPI_TRANSFER(0x44001100, 0x00, 0x00, 0x00, 0x01); - SPI_TRANSFER(0xD0001100, 0x00, 0x07, 0x00, 0x07, 0x00, 0x1D, 0x00, 0x03); // ? + SPI_TRANSFER(DISPLAY_MADCTL, 0x00, madctl); + + SPI_TRANSFER(DSPLAY_COLMOD, 0x00, 0x55); + SPI_TRANSFER(DISPLAY_WRCABC,0x00,0x01); // 00 - default, 01 - UI, 02 - still pic, 03 - video + //SPI_TRANSFER(DISPLAY_IDMON); // Increases brightness+contrast + //SPI_TRANSFER(DISPLAY_IDMOFF); // Darker but more accurate colour + SPI_TRANSFER(DISPLAY_TESL, 0x00, 0x00, 0x00, 0x01); + SPI_TRANSFER(DISPLAY_GETICID, 0x00, 0x07, 0x00, 0x07, 0x00, 0x1D, 0x00, 0x03); // ? SPI_TRANSFER(0xD1001100, 0x00, 0x03, 0x00, 0x30, 0x00, 0x10); // ? SPI_TRANSFER(0xD2001100, 0x00, 0x03, 0x00, 0x14, 0x00, 0x04); // ? - SPI_TRANSFER(0x29001100/*Display ON*/); + SPI_TRANSFER(DISPLAY_ON); usleep(30*1000); @@ -151,24 +159,32 @@ void InitKeDeiV63() void TurnBacklightOff() { - SPI_TRANSFER(DISPLAY_BACKLIT_BRIT,0xFF); - SPI_TRANSFER(DISPLAY_BACKLIT_CTRL,0x00); // 5-Backlight contro, 3-Display dimming, 2-BacklihtOff } void TurnBacklightOn() { - SPI_TRANSFER(DISPLAY_BACKLIT_BRIT,0x00); - SPI_TRANSFER(DISPLAY_BACKLIT_CTRL,0x2C); // 5-Backlight contro, 3-Display dimming, 2-BacklihtON } void TurnDisplayOff() { - SPI_TRANSFER(DISPLAY_OFF); + // TODO: Reference data sheet (another similar device) isn't accurate for these settings + SPI_TRANSFER(DISPLAY_WRCTRLD,0x00,0x2C); // 5-Backlight contro, 3-Display dimming, 2-BacklihtOff + SPI_TRANSFER(DISPLAY_WRDISBV,0x00,0x00); + + SPI_TRANSFER(DISPLAY_OFF); // Works but whith backlight on, it goes white -- a good 'light' + SPI_TRANSFER(DISPLAY_SLPIN); + usleep(120*1000); } void TurnDisplayOn() { - SPI_TRANSFER(DISPLAY_ON); + SPI_TRANSFER(DISPLAY_SLPOUT); + usleep(120*1000); + SPI_TRANSFER(DISPLAY_ON); + + // TODO: Reference data sheet (another similar device) isn't accurate for these settings + SPI_TRANSFER(DISPLAY_WRCTRLD,0x00,0x00); // 5-Backlight contro, 3-Display dimming, 2-BacklihtON + SPI_TRANSFER(DISPLAY_WRDISBV,0x00,0xFF); } void DeinitSPIDisplay() diff --git a/mpi3501.h b/mpi3501.h index ade61ef..7b2010f 100644 --- a/mpi3501.h +++ b/mpi3501.h @@ -5,14 +5,31 @@ #ifdef MPI3501 // Data specific to the KeDei v6.3 display -#define DISPLAY_NO_OPERATION 0x00001100 -#define DISPLAY_BACKLIT_CTRL 0x53001100 -#define DISPLAY_BACKLIT_BRIT 0x51001100 -#define DISPLAY_ON 0x29001100 -#define DISPLAY_OFF 0x28001100 +#define DISPLAY_NO_OPERATION 0x00001100 +#define DISPLAY_SLPIN 0x10001100 +#define DISPLAY_SLPOUT 0x11001100 +#define DISPLAY_ON 0x29001100 +#define DISPLAY_OFF 0x28001100 #define DISPLAY_SET_CURSOR_X 0x2A001100 #define DISPLAY_SET_CURSOR_Y 0x2B001100 #define DISPLAY_WRITE_PIXELS 0x2C001100 +#define DISPLAY_SETOSC 0xB0001100 +#define DISPLAY_SETRGB 0xB3001100 +#define DISPLAY_SETEXTC 0xB9001100 +#define DISPLAY_SETSTBA 0xC0001100 +#define DISPLAY_SETDGC 0xC1001100 +#define DISPLAY_SETDDB 0xC4001100 +#define DISPLAY_TEON 0x35001100 +#define DISPLAY_MADCTL 0x36001100 +#define DISPLAY_IDMOFF 0x38001100 +#define DISPLAY_IDMON 0x39001100 +#define DSPLAY_COLMOD 0x3A001100 +#define DISPLAY_TESL 0x44001100 +#define DISPLAY_WRDISBV 0x51001100 +#define DISPLAY_WRCTRLD 0x53001100 +#define DISPLAY_WRCABC 0x55001100 +#define DISPLAY_GETICID 0xD0001100 +#define DISPLAY_GETSPIREAD 0xff001100 #define DISPLAY_NATIVE_WIDTH 320 #define DISPLAY_NATIVE_HEIGHT 480 diff --git a/tcCalib.c b/tcCalib.c deleted file mode 100644 index e69de29..0000000 diff --git a/util/tcCalib.cpp b/util/tcCalib.cpp index 991d4e6..29aadce 100644 --- a/util/tcCalib.cpp +++ b/util/tcCalib.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "VG/openvg.h" #include "VG/vgu.h" @@ -13,14 +14,37 @@ #include #include +#define xHairLines_ROWS 8 +int xHairLines[8][4] = { + 10, 15, 20, 15, // lower left + 15, 10, 15, 20, + -20, 15, -10, 15, // lower right + -15, 10, -15, 20, + 10, -15, 20, -15, // upper left + 15, -10, 15, -20, + -20, -15, -10, -15, // upper right + -15, -10, -15, -20 +}; + // touch state structure typedef struct { int fd; - char[80] evbuff; + char* evbuff[80]; VGfloat x, y, z; int max_x, max_y; void parse(const char *buff) { + std::regex re( "x:(\\d+),\\s*y:(\\d+),\\s*z:(\\d+)\\s*" ) ; + std::cmatch match_results; + + x = 0; + y = 0; + z = 0; + if( std::regex_match( (const char *)buff, match_results, re ) && match_results.size() >= 3 ) { + x = std::stoi(match_results[1].str()); + y = std::stoi(match_results[2].str()); + z = std::stoi(match_results[3].str()); + } } } touch_t; @@ -43,49 +67,8 @@ void *eventThread(void *arg) { while (1) { read(touch.fd, &touch.evbuff, sizeof(char[80])); - // printf("[%4.0f,%4.0f]\r",touch.x,touch.y); - - // Check events - touch.left = CUR_SIZ * 2; // Reset touch button states - touch.right = CUR_SIZ * 2; - - if (touch.ev.type == EV_REL) { - if (touch.ev.code == REL_X) { - touch.x += (VGfloat) touch.ev.value; - if (touch.x < 0) { - touch.x = 0; - } - if (touch.x > touch.max_x) { - touch.x = touch.max_x; - } - } - if (touch.ev.code == REL_Y) { //This ones goes backwards hence the minus - touch.y -= (VGfloat) touch.ev.value; - if (touch.y < 0) { - touch.y = 0; - } - if (touch.y > touch.max_y) { - touch.y = touch.max_y; - } - } - } - - if (touch.ev.type == EV_KEY) { - //printf("Time Stamp:%d - type %d, code %d, value %d\n", - // touch.ev.time.tv_usec,touch.ev.type,touch.ev.code,touch.ev.value); - if (touch.ev.code == BTN_LEFT) { - touch.left = 1; - // printf("Left button\n"); - left_count++; - // printf("User Quit\n"); - // quitState = 1; - // return &quitState; //Left touch to quit - } - if (touch.ev.code == BTN_RIGHT) { - touch.right = 1; - // printf("Right button\n"); - } - } + touch.parse((const char *)touch.evbuff); + printf("[%4.0f,%4.0f]\r",touch.x,touch.y); } } @@ -163,9 +146,23 @@ int main() { Fill(44, 77, 232, 1); // Big blue marble Circle(width / 2, 0, width); // The "world" Fill(255, 255, 255, 1); // White text - TextMid(width / 2, height / 2, "hello, world", SerifTypeface, width / 10); // Greetings + TextMid(width / 2, height / 2, "Screen Calibration", SerifTypeface, width / 15); // Greetings + + // Draw lines + Stroke(255, 255, 255, 0.5); + StrokeWidth(2); + for( int i = 0; i < xHairLines_ROWS; i++) { + Line( ( xHairLines[i][0] < 0 ? width + xHairLines[i][0] : xHairLines[i][0] ) + , ( xHairLines[i][1] < 0 ? height + xHairLines[i][1] : xHairLines[i][1] ) + , ( xHairLines[i][2] < 0 ? width + xHairLines[i][2] : xHairLines[i][2] ) + , ( xHairLines[i][3] < 0 ? height + xHairLines[i][3] : xHairLines[i][3] ) + ); + } + End(); // update picture + + // MAIN LOOP while (left_count < 2) { // Loop until the left touch button pressed & released // if the touch moved... From 4cb0e132298423ab000b9d25cec361bae5f96195 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Tue, 25 Jun 2019 10:59:38 +0000 Subject: [PATCH 28/52] cleanup --- File | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 File diff --git a/File b/File deleted file mode 100644 index e69de29..0000000 From cb182e714bdaa85006c798a8a23c5c4646184ae0 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Tue, 25 Jun 2019 11:01:10 +0000 Subject: [PATCH 29/52] . --- util/Makefile | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 util/Makefile diff --git a/util/Makefile b/util/Makefile new file mode 100644 index 0000000..75c74d4 --- /dev/null +++ b/util/Makefile @@ -0,0 +1,14 @@ +LIBFLAGS=-L/opt/vc/lib -lEGL -lGLESv2 -lbcm_host -lpthread -ljpeg -lshapes +CXXFLAGS=-DDEBUG -fno-exceptions -DPI -std=gnu++11 -I/opt/vc/include -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/interface/vcos/pthreads -I.. + +objs=tcCalib.o + +all: tcCalib + +clean: $(objs) + rm $(objs) + rm tcCalib + +tcCalib: $(objs) + g++ -Wall $(CXXFLAGS) $(LIBFLAGS) $(objs) -o tcCalib + From 6a062aaf17c0dff2502eb943664c413ab0c1cbf0 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Fri, 30 Aug 2019 19:01:01 -0400 Subject: [PATCH 30/52] retain intermediate changes --- .gitignore | 4 + calibrate.c | 1 + calibrate.h | 1 + kernel/gpioirq.c | 378 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/gpioirq.h | 23 +++ mpi3501.cpp | 70 +++++---- mpi3501.h | 27 +++- util/Makefile | 14 ++ util/tcCalib.cpp | 182 +++++++++++++++++++++++ 9 files changed, 668 insertions(+), 32 deletions(-) create mode 100755 calibrate.c create mode 100755 calibrate.h create mode 100644 kernel/gpioirq.c create mode 100644 kernel/gpioirq.h create mode 100644 util/Makefile create mode 100644 util/tcCalib.cpp diff --git a/.gitignore b/.gitignore index 06fb2a5..bae4d0c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,7 @@ *.S *.symvers *.order +kernel/bcm2835_spi_display.mod.c +build +.DS_Store +util/tcCalib diff --git a/calibrate.c b/calibrate.c new file mode 100755 index 0000000..6781eeb --- /dev/null +++ b/calibrate.c @@ -0,0 +1 @@ +/* * * Copyright (c) 2001, Carlos E. Vidales. All rights reserved. * * This sample program was written and put in the public domain * by Carlos E. Vidales. The program is provided "as is" * without warranty of any kind, either expressed or implied. * If you choose to use the program within your own products * you do so at your own risk, and assume the responsibility * for servicing, repairing or correcting the program should * it prove defective in any manner. * You may copy and distribute the program's source code in any * medium, provided that you also include in each copy an * appropriate copyright notice and disclaimer of warranty. * You may also modify this program and distribute copies of * it provided that you include prominent notices stating * that you changed the file(s) and the date of any change, * and that you do not charge any royalties or licenses for * its use. * * * * File Name: calibrate.c * * * This file contains functions that implement calculations * necessary to obtain calibration factors for a touch screen * that suffers from multiple distortion effects: namely, * translation, scaling and rotation. * * The following set of equations represent a valid display * point given a corresponding set of touch screen points: * * * /- -\ * /- -\ /- -\ | | * | | | | | Xs | * | Xd | | A B C | | | * | | = | | * | Ys | * | Yd | | D E F | | | * | | | | | 1 | * \- -/ \- -/ | | * \- -/ * * * where: * * (Xd,Yd) represents the desired display point * coordinates, * * (Xs,Ys) represents the available touch screen * coordinates, and the matrix * * /- -\ * |A,B,C| * |D,E,F| represents the factors used to translate * \- -/ the available touch screen point values * into the corresponding display * coordinates. * * * Note that for practical considerations, the utilitities * within this file do not use the matrix coefficients as * defined above, but instead use the following * equivalents, since floating point math is not used: * * A = An/Divider * B = Bn/Divider * C = Cn/Divider * D = Dn/Divider * E = En/Divider * F = Fn/Divider * * * * The functions provided within this file are: * * setCalibrationMatrix() - calculates the set of factors * in the above equation, given * three sets of test points. * getDisplayPoint() - returns the actual display * coordinates, given a set of * touch screen coordinates. * translateRawScreenCoordinates() - helper function to transform * raw screen points into values * scaled to the desired display * resolution. * * */ #define _CALIBRATE_C_ /****************************************************/ /* */ /* Included files */ /* */ /****************************************************/ #include "Calibrate.h" /****************************************************/ /* */ /* Local Definitions and macros */ /* */ /****************************************************/ /****************************************************/ /* */ /* Global variables */ /* */ /****************************************************/ /****************************************************/ /* */ /* Forward Declaration of local functions */ /* */ /****************************************************/ /********************************************************************** * * Function: setCalibrationMatrix() * * Description: Calling this function with valid input data * in the display and screen input arguments * causes the calibration factors between the * screen and display points to be calculated, * and the output argument - matrixPtr - to be * populated. * * This function needs to be called only when new * calibration factors are desired. * * * Argument(s): displayPtr (input) - Pointer to an array of three * sample, reference points. * screenPtr (input) - Pointer to the array of touch * screen points corresponding * to the reference display points. * matrixPtr (output) - Pointer to the calibration * matrix computed for the set * of points being provided. * * * From the article text, recall that the matrix coefficients are * resolved to be the following: * * * Divider = (Xs0 - Xs2)*(Ys1 - Ys2) - (Xs1 - Xs2)*(Ys0 - Ys2) * * * * (Xd0 - Xd2)*(Ys1 - Ys2) - (Xd1 - Xd2)*(Ys0 - Ys2) * A = --------------------------------------------------- * Divider * * * (Xs0 - Xs2)*(Xd1 - Xd2) - (Xd0 - Xd2)*(Xs1 - Xs2) * B = --------------------------------------------------- * Divider * * * Ys0*(Xs2*Xd1 - Xs1*Xd2) + * Ys1*(Xs0*Xd2 - Xs2*Xd0) + * Ys2*(Xs1*Xd0 - Xs0*Xd1) * C = --------------------------------------------------- * Divider * * * (Yd0 - Yd2)*(Ys1 - Ys2) - (Yd1 - Yd2)*(Ys0 - Ys2) * D = --------------------------------------------------- * Divider * * * (Xs0 - Xs2)*(Yd1 - Yd2) - (Yd0 - Yd2)*(Xs1 - Xs2) * E = --------------------------------------------------- * Divider * * * Ys0*(Xs2*Yd1 - Xs1*Yd2) + * Ys1*(Xs0*Yd2 - Xs2*Yd0) + * Ys2*(Xs1*Yd0 - Xs0*Yd1) * F = --------------------------------------------------- * Divider * * * Return: OK - the calibration matrix was correctly * calculated and its value is in the * output argument. * NOT_OK - an error was detected and the * function failed to return a valid * set of matrix values. * The only time this sample code returns * NOT_OK is when Divider == 0 * * * * NOTE! NOTE! NOTE! * * setCalibrationMatrix() and getDisplayPoint() will do fine * for you as they are, provided that your digitizer * resolution does not exceed 10 bits (1024 values). Higher * resolutions may cause the integer operations to overflow * and return incorrect values. If you wish to use these * functions with digitizer resolutions of 12 bits (4096 * values) you will either have to a) use 64-bit signed * integer variables and math, or b) judiciously modify the * operations to scale results by a factor of 2 or even 4. * * */ int setCalibrationMatrix( POINT * displayPtr, POINT * screenPtr, MATRIX * matrixPtr) { int retValue = OK ; matrixPtr->Divider = ((screenPtr[0].x - screenPtr[2].x) * (screenPtr[1].y - screenPtr[2].y)) - ((screenPtr[1].x - screenPtr[2].x) * (screenPtr[0].y - screenPtr[2].y)) ; if( matrixPtr->Divider == 0 ) { retValue = NOT_OK ; } else { matrixPtr->An = ((displayPtr[0].x - displayPtr[2].x) * (screenPtr[1].y - screenPtr[2].y)) - ((displayPtr[1].x - displayPtr[2].x) * (screenPtr[0].y - screenPtr[2].y)) ; matrixPtr->Bn = ((screenPtr[0].x - screenPtr[2].x) * (displayPtr[1].x - displayPtr[2].x)) - ((displayPtr[0].x - displayPtr[2].x) * (screenPtr[1].x - screenPtr[2].x)) ; matrixPtr->Cn = (screenPtr[2].x * displayPtr[1].x - screenPtr[1].x * displayPtr[2].x) * screenPtr[0].y + (screenPtr[0].x * displayPtr[2].x - screenPtr[2].x * displayPtr[0].x) * screenPtr[1].y + (screenPtr[1].x * displayPtr[0].x - screenPtr[0].x * displayPtr[1].x) * screenPtr[2].y ; matrixPtr->Dn = ((displayPtr[0].y - displayPtr[2].y) * (screenPtr[1].y - screenPtr[2].y)) - ((displayPtr[1].y - displayPtr[2].y) * (screenPtr[0].y - screenPtr[2].y)) ; matrixPtr->En = ((screenPtr[0].x - screenPtr[2].x) * (displayPtr[1].y - displayPtr[2].y)) - ((displayPtr[0].y - displayPtr[2].y) * (screenPtr[1].x - screenPtr[2].x)) ; matrixPtr->Fn = (screenPtr[2].x * displayPtr[1].y - screenPtr[1].x * displayPtr[2].y) * screenPtr[0].y + (screenPtr[0].x * displayPtr[2].y - screenPtr[2].x * displayPtr[0].y) * screenPtr[1].y + (screenPtr[1].x * displayPtr[0].y - screenPtr[0].x * displayPtr[1].y) * screenPtr[2].y ; } return( retValue ) ; } /* end of setCalibrationMatrix() */ /********************************************************************** * * Function: getDisplayPoint() * * Description: Given a valid set of calibration factors and a point * value reported by the touch screen, this function * calculates and returns the true (or closest to true) * display point below the spot where the touch screen * was touched. * * * * Argument(s): displayPtr (output) - Pointer to the calculated * (true) display point. * screenPtr (input) - Pointer to the reported touch * screen point. * matrixPtr (input) - Pointer to calibration factors * matrix previously calculated * from a call to * setCalibrationMatrix() * * * The function simply solves for Xd and Yd by implementing the * computations required by the translation matrix. * * /- -\ * /- -\ /- -\ | | * | | | | | Xs | * | Xd | | A B C | | | * | | = | | * | Ys | * | Yd | | D E F | | | * | | | | | 1 | * \- -/ \- -/ | | * \- -/ * * It must be kept brief to avoid consuming CPU cycles. * * * Return: OK - the display point was correctly calculated * and its value is in the output argument. * NOT_OK - an error was detected and the function * failed to return a valid point. * * * * NOTE! NOTE! NOTE! * * setCalibrationMatrix() and getDisplayPoint() will do fine * for you as they are, provided that your digitizer * resolution does not exceed 10 bits (1024 values). Higher * resolutions may cause the integer operations to overflow * and return incorrect values. If you wish to use these * functions with digitizer resolutions of 12 bits (4096 * values) you will either have to a) use 64-bit signed * integer variables and math, or b) judiciously modify the * operations to scale results by a factor of 2 or even 4. * * */ int getDisplayPoint( POINT * displayPtr, POINT * screenPtr, MATRIX * matrixPtr ) { int retValue = OK ; if( matrixPtr->Divider != 0 ) { /* Operation order is important since we are doing integer */ /* math. Make sure you add all terms together before */ /* dividing, so that the remainder is not rounded off */ /* prematurely. */ displayPtr->x = ( (matrixPtr->An * screenPtr->x) + (matrixPtr->Bn * screenPtr->y) + matrixPtr->Cn ) / matrixPtr->Divider ; displayPtr->y = ( (matrixPtr->Dn * screenPtr->x) + (matrixPtr->En * screenPtr->y) + matrixPtr->Fn ) / matrixPtr->Divider ; } else { retValue = NOT_OK ; } return( retValue ) ; } /* end of getDisplayPoint() */ \ No newline at end of file diff --git a/calibrate.h b/calibrate.h new file mode 100755 index 0000000..e2ef436 --- /dev/null +++ b/calibrate.h @@ -0,0 +1 @@ +/* * * Copyright (c) 2001, Carlos E. Vidales. All rights reserved. * * This sample program was written and put in the public domain * by Carlos E. Vidales. The program is provided "as is" * without warranty of any kind, either expressed or implied. * If you choose to use the program within your own products * you do so at your own risk, and assume the responsibility * for servicing, repairing or correcting the program should * it prove defective in any manner. * You may copy and distribute the program's source code in any * medium, provided that you also include in each copy an * appropriate copyright notice and disclaimer of warranty. * You may also modify this program and distribute copies of * it provided that you include prominent notices stating * that you changed the file(s) and the date of any change, * and that you do not charge any royalties or licenses for * its use. * * * File Name: calibrate.h * * * Definition of constants and structures, and declaration of functions * in Calibrate.c * * Revisions: Kevin Peck 2019 */ #ifndef _CALIBRATE_H_ #define _CALIBRATE_H_ /****************************************************/ /* */ /* Included files */ /* */ /****************************************************/ #include /****************************************************/ /* */ /* Definitions */ /* */ /****************************************************/ #ifndef _CALIBRATE_C_ #define EXTERN extern #else #define EXTERN #endif #ifndef OK #define OK 0 #define NOT_OK -1 #endif #define INT32 long /****************************************************/ /* */ /* Structures */ /* */ /****************************************************/ typedef struct Point { uint16_t x, y ; } POINT ; typedef struct Matrix { /* This arrangement of values facilitates * calculations within getDisplayPoint() */ uint16_t An, /* A = An/Divider */ Bn, /* B = Bn/Divider */ Cn, /* C = Cn/Divider */ Dn, /* D = Dn/Divider */ En, /* E = En/Divider */ Fn, /* F = Fn/Divider */ Divider ; } MATRIX ; /****************************************************/ /* */ /* Function declarations */ /* */ /****************************************************/ EXTERN int setCalibrationMatrix( POINT * display, POINT * screen, MATRIX * matrix) ; EXTERN int getDisplayPoint( POINT * display, POINT * screen, MATRIX * matrix ) ; #endif /* _CALIBRATE_H_ */ \ No newline at end of file diff --git a/kernel/gpioirq.c b/kernel/gpioirq.c new file mode 100644 index 0000000..7b983ee --- /dev/null +++ b/kernel/gpioirq.c @@ -0,0 +1,378 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gpioirq.h" + +#define CLASS_NAME "GPIO_IRQ" + +#define N_GPIO_HEADER 26 + +#define GPFSEL0 (0x00) +#define GPFSEL1 (0x04) +#define GPFSEL2 (0x08) +#define GPFSEL3 (0x0C) +#define GPFSEL4 (0x10) +#define GPFSEL5 (0x14) + +#define GPREN0 (0X4C) +#define GPFEN0 (0X58) +#define GPHEN0 (0X64) +#define GPLEN0 (0X70) + +#define IRQEN2 (0x14) +#define IRQDS2 (0x20) + +#define GPPUD (0x94) +#define GPPUDCLK0 (0x98) + +// Original credits to this person +// MODULE_AUTHOR("Jose Martins - josemartins90@gmail.com"); +// +MODULE_AUTHOR("Kevin Peck - kevindpeck@gmail.com"); +MODULE_LICENSE("GPL"); + +static const int +gpio_header_pins[N_GPIO_HEADER] = {22}; + +struct gpio_irq { + int pin; + int irq; + struct cdev c_dev; + struct fasync_struct *fa; + dev_t majorminor; + atomic_t atom; + unsigned type; + unsigned pull; + +}; + +static struct gpio_irq *gpio_irq_list; + +static struct class *gpioirq_class = NULL; +static dev_t majorminor; + +static void *gpio_base, *irqctrl_base; + +irqreturn_t myhandler(int irq, void *dev_id){ + + struct gpio_irq *gpir = dev_id; + + printk(KERN_INFO "GPIO interrupt generated by pin %d\n", gpir->pin); + + if(gpir->fa){ + kill_fasync(&(gpir->fa), SIGIO, POLL_IN); + } + + return IRQ_HANDLED; +} + + +/* THIS WAS NOT TESTED */ +/* +static int gpioirq_setpull(struct gpio_irq *gpir, int type){ + + if(type == GPIOIRQ_PULLUP){ + iowrite32(0x02, gpio_base + GPPUD); + } else if (type == GPIOIRQ_PULLUP){ + iowrite32(0x01, gpio_base + GPPUD); + } else if (type == GPIOIRQ_PULLUP) { + iowrite32(0x00, gpio_base + GPPUD); + } else { + return -EINVAL; + } + + udelay(1); + iowrite32(1 << gpir->pin, gpio_base + GPPUDCLK0); + udelay(1); + iowrite32(0, gpio_base + GPPUDCLK0); + + return 0; +} +*/ + +static int gpioirq_settype(struct gpio_irq *gpir, unsigned long type){ + + unsigned long temp = 0; + + if((type & GPIOIRQ_FALLING_EDGE) && !(gpir->type & type)){ + temp = ioread32(gpio_base + GPFEN0); + temp |= (1 << gpir->pin); + iowrite32(temp, gpio_base + GPFEN0); + gpir->type |= GPIOIRQ_FALLING_EDGE; + } else if(!(type & GPIOIRQ_FALLING_EDGE) && (gpir->type & GPIOIRQ_FALLING_EDGE)){ + temp = ioread32(gpio_base + GPFEN0); + temp &= ~(1 << gpir->pin); + iowrite32(temp, gpio_base + GPFEN0); + gpir->type &= ~GPIOIRQ_FALLING_EDGE; + } + + if(type & GPIOIRQ_RISING_EDGE && !(gpir->type & type)){ + temp = ioread32(gpio_base + GPREN0); + temp |= (1 << gpir->pin); + iowrite32(temp, gpio_base + GPREN0); + gpir->type |= GPIOIRQ_RISING_EDGE; + } else if(!(type & GPIOIRQ_RISING_EDGE) && (gpir->type & GPIOIRQ_RISING_EDGE)){ + temp = ioread32(gpio_base + GPREN0); + temp &= ~(1 << gpir->pin); + iowrite32(temp, gpio_base + GPREN0); + gpir->type &= ~GPIOIRQ_RISING_EDGE; + } + + if(type & GPIOIRQ_HIGH && !(gpir->type & type)){ + temp = ioread32(gpio_base + GPHEN0); + temp |= (1 << gpir->pin); + iowrite32(temp, gpio_base + GPHEN0); + gpir->type |= GPIOIRQ_HIGH; + } else if(!(type & GPIOIRQ_HIGH) && (gpir->type & GPIOIRQ_HIGH)){ + temp = ioread32(gpio_base + GPHEN0); + temp &= ~(1 << gpir->pin); + iowrite32(temp, gpio_base + GPHEN0); + gpir->type &= ~GPIOIRQ_HIGH; + } + + if(type & GPIOIRQ_LOW && !(gpir->type & type)){ + temp = ioread32(gpio_base + GPLEN0); + temp |= (1 << gpir->pin); + iowrite32(temp, gpio_base + GPLEN0); + gpir->type |= GPIOIRQ_LOW; + + } else if(!(type & GPIOIRQ_LOW) && (gpir->type & GPIOIRQ_LOW)){ + temp = ioread32(gpio_base + GPLEN0); + temp &= ~(1 << gpir->pin); + iowrite32(temp, gpio_base + GPLEN0); + gpir->type &= ~GPIOIRQ_LOW; + } + + + return 0; +} + +static int my_open(struct inode *node, struct file *file){ + + unsigned long temp = 0; + int offset; + struct gpio_irq *gpir = container_of(node->i_cdev, struct gpio_irq, c_dev); + + if(!atomic_dec_and_test(&(gpir->atom))){ + atomic_inc(&(gpir->atom)); + return -EBUSY; + } + + file->private_data = gpir; + gpir->irq = gpio_to_irq(gpir->pin); + + if(request_irq(gpir->irq, myhandler, 0, "GPIO_IRQ", gpir)){ + printk(KERN_INFO "Failed Requesting Interrupt Line..."); + return -1; + } + + if(gpir->pin >= 0 && gpir->pin <= 9){ + offset = GPFSEL0; + } else if (gpir->pin >= 10 && gpir->pin <= 19){ + offset = GPFSEL1; + } else { + offset = GPFSEL2; + } + + /* Configure pin as input */ + temp = ioread32(gpio_base + offset); + temp &= (7 << ((gpir->pin % 10)) * 3); + iowrite32(temp, gpio_base + offset); + + /* Interrupt type is set falling edge by default */ + gpir->type = GPIOIRQ_NONE; + gpioirq_settype(gpir, GPIOIRQ_FALLING_EDGE); + + return 0; +} + +static int my_release(struct inode *node, struct file *file){ + + struct gpio_irq *gpir = file->private_data; + + //Disbales Interrupt for pin + gpioirq_settype(gpir, GPIOIRQ_NONE); + + fasync_helper(-1, file, 0, &(gpir->fa)); + free_irq(gpir->irq, gpir); + atomic_inc(&(gpir->atom)); + + return 0; +} + +static int my_fasync(int fd, struct file *filp, int mode){ + + int ret; + struct gpio_irq *gpir = filp->private_data; + + if((ret = fasync_helper(fd, filp, mode, &(gpir->fa))) < 0){ + printk(KERN_INFO "Fasync Failed: %d\n", ret); + return ret; + } + + return 0; +} + + + +static long my_ioctl(struct file *fp, unsigned int cmd, unsigned long arg){ + + struct gpio_irq *gpir = fp->private_data; + + switch(cmd){ + + case GPIOIRQ_IOC_SETTYPE : + return gpioirq_settype(gpir, arg); + break; + case GPIOIRQ_IOC_SETPULL: + //return gpioirq_setpull(gpir, arg); + break; + default: + return -ENOTTY; + } + + return 0; +} + +static struct file_operations fops = { + + .owner = THIS_MODULE, + .open = my_open, + .fasync = my_fasync, + .release = my_release, + .unlocked_ioctl = my_ioctl + +}; + + +static __init int my_init(void){ + + unsigned long temp = 0; + int i, j, ret = 0; + dev_t tempmajorminor; + char device_name[11]; + + if((ret = alloc_chrdev_region(&majorminor, 0, N_GPIO_HEADER, CLASS_NAME)) < 0){ + printk(KERN_INFO "Failed allocating major...\n"); + return ret; + } + + if(IS_ERR(gpioirq_class = class_create(THIS_MODULE, CLASS_NAME))){ + printk(KERN_INFO "Failed creating class..\n"); + ret = PTR_ERR(gpioirq_class); + goto classcreat_err; + } + + gpio_irq_list = kmalloc(N_GPIO_HEADER * sizeof(struct gpio_irq), GFP_KERNEL); + if(!gpio_irq_list){ + ret = ENOMEM; + goto classcreat_err; + } + memset(gpio_irq_list, 0, N_GPIO_HEADER * sizeof(struct gpio_irq)); + + + for(i = 0; i < N_GPIO_HEADER; i++){ + + gpio_irq_list[i].pin = gpio_header_pins[i]; + gpio_irq_list[i].fa = NULL; + + snprintf(device_name, 11, "gpio_irq%d", gpio_header_pins[i]); + + tempmajorminor = MKDEV(MAJOR(majorminor), i ); + gpio_irq_list[i].majorminor = tempmajorminor; + + if(IS_ERR(device_create(gpioirq_class, NULL, tempmajorminor, NULL, device_name))){ + printk(KERN_INFO "Failed creating device...\n"); + ret = PTR_ERR(gpioirq_class); + goto devicecreat_err; + } + + cdev_init(&(gpio_irq_list[i].c_dev), &fops); + gpio_irq_list[i].c_dev.owner = THIS_MODULE; + gpio_irq_list[i].c_dev.ops = &fops; + + if((ret = (cdev_add(&(gpio_irq_list[i].c_dev), tempmajorminor, 1))) < 0){ + printk(KERN_INFO "Failed register device...\n"); + goto devadd_err; + } + + atomic_set(&(gpio_irq_list[i].atom), 1); + + } + + if((gpio_base = ioremap(GPIO_BASE, 0x60)) == NULL){ + printk(KERN_INFO "Failed mapping gpio registers...\n"); + ret = -1; + goto devicecreat_err; + } + if((irqctrl_base = ioremap(ARMCTRL_IC_BASE, 0x28)) == NULL){ + printk(KERN_INFO "Failed mapping interrupt control registers...\n"); + ret = -1; + goto ioremap_err; + } + + /* Enables interrupts for gpio 0-31*/ + temp = ioread32(irqctrl_base + IRQEN2); + temp = (1 << 17); + iowrite32(temp, irqctrl_base + IRQEN2); + + printk(KERN_INFO "GPIO_IRQ Module initialized.\n"); + return 0; + +ioremap_err: + iounmap(gpio_base); +devadd_err: + device_destroy(gpioirq_class, gpio_irq_list[i].majorminor); +devicecreat_err: + for(j = 0; j < i; j++){ + cdev_del(&(gpio_irq_list[j].c_dev)); + device_destroy(gpioirq_class, gpio_irq_list[j].majorminor); + } + class_destroy(gpioirq_class); +classcreat_err: + unregister_chrdev_region(majorminor, 1); + return ret; +} + +static __exit void my_exit(void){ + + int i; + unsigned long temp = 0; + + for(i = 0; i < N_GPIO_HEADER; i++){ + cdev_del(&(gpio_irq_list[i].c_dev)); + device_destroy(gpioirq_class, gpio_irq_list[i].majorminor); + } + + class_destroy(gpioirq_class); + unregister_chrdev_region(majorminor, 1); + + /* Disables interrupts for gpio 0-31*/ + temp = ioread32(irqctrl_base + IRQDS2); + temp = (1 << 17); + iowrite32(temp, irqctrl_base + IRQDS2); + + iounmap(gpio_base); + iounmap(irqctrl_base); + + + printk(KERN_INFO "GPIO_IRQ Module exits...\n"); + +} + +module_init(my_init); +module_exit(my_exit); diff --git a/kernel/gpioirq.h b/kernel/gpioirq.h new file mode 100644 index 0000000..b52ac2b --- /dev/null +++ b/kernel/gpioirq.h @@ -0,0 +1,23 @@ +#ifndef __GPIO_IRQ_H__ +#define __GPIO_IRQ_H__ + +#include +#include + +#define GPIOIRQ_MAGIC ('x') + +#define GPIOIRQ_FALLING_EDGE (0x08) +#define GPIOIRQ_RISING_EDGE (0x04) +#define GPIOIRQ_HIGH (0x02) +#define GPIOIRQ_LOW (0x01) +#define GPIOIRQ_NONE (0x00) + +#define GPIOIRQ_PULLUP 2 +#define GPIOIRQ_PULLDOWN 1 +#define GPIOIRQ_NOPULL 0 + +#define GPIOIRQ_IOC_SETTYPE _IOW(GPIOIRQ_MAGIC, 1, __u8) +#define GPIOIRQ_IOC_SETPULL _IOW(GPIOIRQ_MAGIC, 2, __u8) + + +#endif diff --git a/mpi3501.cpp b/mpi3501.cpp index 781d24f..6d3c6d7 100644 --- a/mpi3501.cpp +++ b/mpi3501.cpp @@ -44,6 +44,11 @@ void InitKeDeiV63() { // output device touch = XPT2046(); + + touch.setRotation(0); +#ifdef DISPLAY_ROTATE_180_DEGREES + touch.setRotation(1); +#endif // If a Reset pin is defined, toggle it briefly high->low->high to enable the device. Some devices do not have a reset pin, in which case compile with GPIO_TFT_RESET_PIN left undefined. #if defined(GPIO_TFT_RESET_PIN) && GPIO_TFT_RESET_PIN >= 0 @@ -80,31 +85,31 @@ void InitKeDeiV63() SPI_TRANSFER(DISPLAY_NO_OPERATION); // Reset usleep(10*1000); - SPI_TRANSFER(0xff001100); - SPI_TRANSFER(0xff001100); + SPI_TRANSFER(DISPLAY_GETSPIREAD); + SPI_TRANSFER(DISPLAY_GETSPIREAD); usleep(10*1000); - SPI_TRANSFER(0xff001100); - SPI_TRANSFER(0xff001100); - SPI_TRANSFER(0xff001100); - SPI_TRANSFER(0xff001100); + SPI_TRANSFER(DISPLAY_GETSPIREAD); + SPI_TRANSFER(DISPLAY_GETSPIREAD); + SPI_TRANSFER(DISPLAY_GETSPIREAD); + SPI_TRANSFER(DISPLAY_GETSPIREAD); usleep(15*1000); - SPI_TRANSFER(0x11001100/*Sleep Out*/); + SPI_TRANSFER(DISPLAY_SLPOUT); usleep(150*1000); - SPI_TRANSFER(0xB0001100, 0x00, 0x00); // CLK 30Hz? - SPI_TRANSFER(0xB3001100, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); // set RGB? - SPI_TRANSFER(0xB9001100, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0f); // ext cmd? - SPI_TRANSFER(0xC0001100, 0x00, 0x13, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x02 - , 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x43); // Timing? - SPI_TRANSFER(0xC1001100, 0x00, 0x08, 0x00, 0x0F, 0x00, 0x08, 0x00, 0x08); // gamma? - SPI_TRANSFER(0xC4001100, 0x00, 0x11, 0x00, 0x07, 0x00, 0x03, 0x00, 0x04); // sleep modes? + SPI_TRANSFER(DISPLAY_SETOSC, 0x00, 0x00); + SPI_TRANSFER(DISPLAY_SETRGB, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + SPI_TRANSFER(DISPLAY_SETEXTC, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0f); + SPI_TRANSFER(DISPLAY_SETSTBA, 0x00, 0x13, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x02 + , 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x43); + SPI_TRANSFER(DISPLAY_SETDGC, 0x00, 0x08, 0x00, 0x0F, 0x00, 0x08, 0x00, 0x08); + SPI_TRANSFER(DISPLAY_SETDDB, 0x00, 0x11, 0x00, 0x07, 0x00, 0x03, 0x00, 0x04); SPI_TRANSFER(0xC6001100, 0x00, 0x00); // ? SPI_TRANSFER(0xC8001100, 0x00, 0x03, 0x00, 0x03, 0x00, 0x13, 0x00, 0x5C , 0x00, 0x03, 0x00, 0x07, 0x00, 0x14, 0x00, 0x08 , 0x00, 0x00, 0x00, 0x21, 0x00, 0x08, 0x00, 0x14 , 0x00, 0x07, 0x00, 0x53, 0x00, 0x0C, 0x00, 0x13 , 0x00, 0x03, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00); // ? - SPI_TRANSFER(0x35001100, 0x00, 0x00); + SPI_TRANSFER(DISPLAY_TEON, 0x00, 0x00); #define MADCTL_BGR_PIXEL_ORDER (1<<3) #define MADCTL_LINE_ADDRESS_ORDER_SWAP (1<<4) @@ -123,14 +128,17 @@ void InitKeDeiV63() #ifdef DISPLAY_ROTATE_180_DEGREES madctl ^= MADCTL_ROTATE_180_DEGREES; #endif - SPI_TRANSFER(0x36001100/*MADCTL: Memory Access Control*/, 0x00, madctl); - - SPI_TRANSFER(0x3A001100/*Interface Pixel Format*/, 0x00, 0x55); - SPI_TRANSFER(0x44001100, 0x00, 0x00, 0x00, 0x01); - SPI_TRANSFER(0xD0001100, 0x00, 0x07, 0x00, 0x07, 0x00, 0x1D, 0x00, 0x03); // ? + SPI_TRANSFER(DISPLAY_MADCTL, 0x00, madctl); + + SPI_TRANSFER(DSPLAY_COLMOD, 0x00, 0x55); + SPI_TRANSFER(DISPLAY_WRCABC,0x00,0x01); // 00 - default, 01 - UI, 02 - still pic, 03 - video + //SPI_TRANSFER(DISPLAY_IDMON); // Increases brightness+contrast + //SPI_TRANSFER(DISPLAY_IDMOFF); // Darker but more accurate colour + SPI_TRANSFER(DISPLAY_TESL, 0x00, 0x00, 0x00, 0x01); + SPI_TRANSFER(DISPLAY_GETICID, 0x00, 0x07, 0x00, 0x07, 0x00, 0x1D, 0x00, 0x03); // ? SPI_TRANSFER(0xD1001100, 0x00, 0x03, 0x00, 0x30, 0x00, 0x10); // ? SPI_TRANSFER(0xD2001100, 0x00, 0x03, 0x00, 0x14, 0x00, 0x04); // ? - SPI_TRANSFER(0x29001100/*Display ON*/); + SPI_TRANSFER(DISPLAY_ON); usleep(30*1000); @@ -151,24 +159,32 @@ void InitKeDeiV63() void TurnBacklightOff() { - SPI_TRANSFER(DISPLAY_BACKLIT_BRIT,0xFF); - SPI_TRANSFER(DISPLAY_BACKLIT_CTRL,0x00); // 5-Backlight contro, 3-Display dimming, 2-BacklihtOff } void TurnBacklightOn() { - SPI_TRANSFER(DISPLAY_BACKLIT_BRIT,0x00); - SPI_TRANSFER(DISPLAY_BACKLIT_CTRL,0x2C); // 5-Backlight contro, 3-Display dimming, 2-BacklihtON } void TurnDisplayOff() { - SPI_TRANSFER(DISPLAY_OFF); + // TODO: Reference data sheet (another similar device) isn't accurate for these settings + SPI_TRANSFER(DISPLAY_WRCTRLD,0x00,0x2C); // 5-Backlight contro, 3-Display dimming, 2-BacklihtOff + SPI_TRANSFER(DISPLAY_WRDISBV,0x00,0x00); + + SPI_TRANSFER(DISPLAY_OFF); // Works but whith backlight on, it goes white -- a good 'light' + SPI_TRANSFER(DISPLAY_SLPIN); + usleep(120*1000); } void TurnDisplayOn() { - SPI_TRANSFER(DISPLAY_ON); + SPI_TRANSFER(DISPLAY_SLPOUT); + usleep(120*1000); + SPI_TRANSFER(DISPLAY_ON); + + // TODO: Reference data sheet (another similar device) isn't accurate for these settings + SPI_TRANSFER(DISPLAY_WRCTRLD,0x00,0x00); // 5-Backlight contro, 3-Display dimming, 2-BacklihtON + SPI_TRANSFER(DISPLAY_WRDISBV,0x00,0xFF); } void DeinitSPIDisplay() diff --git a/mpi3501.h b/mpi3501.h index ade61ef..7b2010f 100644 --- a/mpi3501.h +++ b/mpi3501.h @@ -5,14 +5,31 @@ #ifdef MPI3501 // Data specific to the KeDei v6.3 display -#define DISPLAY_NO_OPERATION 0x00001100 -#define DISPLAY_BACKLIT_CTRL 0x53001100 -#define DISPLAY_BACKLIT_BRIT 0x51001100 -#define DISPLAY_ON 0x29001100 -#define DISPLAY_OFF 0x28001100 +#define DISPLAY_NO_OPERATION 0x00001100 +#define DISPLAY_SLPIN 0x10001100 +#define DISPLAY_SLPOUT 0x11001100 +#define DISPLAY_ON 0x29001100 +#define DISPLAY_OFF 0x28001100 #define DISPLAY_SET_CURSOR_X 0x2A001100 #define DISPLAY_SET_CURSOR_Y 0x2B001100 #define DISPLAY_WRITE_PIXELS 0x2C001100 +#define DISPLAY_SETOSC 0xB0001100 +#define DISPLAY_SETRGB 0xB3001100 +#define DISPLAY_SETEXTC 0xB9001100 +#define DISPLAY_SETSTBA 0xC0001100 +#define DISPLAY_SETDGC 0xC1001100 +#define DISPLAY_SETDDB 0xC4001100 +#define DISPLAY_TEON 0x35001100 +#define DISPLAY_MADCTL 0x36001100 +#define DISPLAY_IDMOFF 0x38001100 +#define DISPLAY_IDMON 0x39001100 +#define DSPLAY_COLMOD 0x3A001100 +#define DISPLAY_TESL 0x44001100 +#define DISPLAY_WRDISBV 0x51001100 +#define DISPLAY_WRCTRLD 0x53001100 +#define DISPLAY_WRCABC 0x55001100 +#define DISPLAY_GETICID 0xD0001100 +#define DISPLAY_GETSPIREAD 0xff001100 #define DISPLAY_NATIVE_WIDTH 320 #define DISPLAY_NATIVE_HEIGHT 480 diff --git a/util/Makefile b/util/Makefile new file mode 100644 index 0000000..75c74d4 --- /dev/null +++ b/util/Makefile @@ -0,0 +1,14 @@ +LIBFLAGS=-L/opt/vc/lib -lEGL -lGLESv2 -lbcm_host -lpthread -ljpeg -lshapes +CXXFLAGS=-DDEBUG -fno-exceptions -DPI -std=gnu++11 -I/opt/vc/include -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/interface/vcos/pthreads -I.. + +objs=tcCalib.o + +all: tcCalib + +clean: $(objs) + rm $(objs) + rm tcCalib + +tcCalib: $(objs) + g++ -Wall $(CXXFLAGS) $(LIBFLAGS) $(objs) -o tcCalib + diff --git a/util/tcCalib.cpp b/util/tcCalib.cpp new file mode 100644 index 0000000..29aadce --- /dev/null +++ b/util/tcCalib.cpp @@ -0,0 +1,182 @@ + + +#include +#include +#include +#include + +#include "VG/openvg.h" +#include "VG/vgu.h" +#include "fontinfo.h" +#include "shapes.h" + +#include +#include +#include + +#define xHairLines_ROWS 8 +int xHairLines[8][4] = { + 10, 15, 20, 15, // lower left + 15, 10, 15, 20, + -20, 15, -10, 15, // lower right + -15, 10, -15, 20, + 10, -15, 20, -15, // upper left + 15, -10, 15, -20, + -20, -15, -10, -15, // upper right + -15, -10, -15, -20 +}; + +// touch state structure +typedef struct { + int fd; + char* evbuff[80]; + VGfloat x, y, z; + int max_x, max_y; + void parse(const char *buff) { + std::regex re( "x:(\\d+),\\s*y:(\\d+),\\s*z:(\\d+)\\s*" ) ; + std::cmatch match_results; + + x = 0; + y = 0; + z = 0; + + if( std::regex_match( (const char *)buff, match_results, re ) && match_results.size() >= 3 ) { + x = std::stoi(match_results[1].str()); + y = std::stoi(match_results[2].str()); + z = std::stoi(match_results[3].str()); + } + } +} touch_t; + +touch_t touch; // global touch state +int left_count = 0; +int quitState = 0; +#define CUR_SIZ 16 // cursor size, pixels beyond centre dot + +// evenThread reads from the touch input file +void *eventThread(void *arg) { + + // Open touch driver + if ((touch.fd = open("/tmp/TCfifo", O_RDONLY)) < 0) { + fprintf(stderr, "Error opening touch!\n"); + quitState = 1; + return &quitState; + } + touch.x = touch.max_x / 2; //Reset touch + touch.y = touch.max_y / 2; + + while (1) { + read(touch.fd, &touch.evbuff, sizeof(char[80])); + touch.parse((const char *)touch.evbuff); + printf("[%4.0f,%4.0f]\r",touch.x,touch.y); + } +} + +static int cur_sx, cur_sy, cur_w, cur_h; // cursor location and dimensions +static int cur_saved = 0; // amount of data saved in cursor image backup + +// saveCursor saves the pixels under the touch cursor +void saveCursor(VGImage CursorBuffer, int curx, int cury, int screen_width, int screen_height, int s) { + int sx, sy, ex, ey; + + sx = curx - s; // horizontal + if (sx < 0) { + sx = 0; + } + ex = curx + s; + if (ex > screen_width) { + ex = screen_width; + } + cur_sx = sx; + cur_w = ex - sx; + + sy = cury - s; // vertical + if (sy < 0) { + sy = 0; + } + ey = cury + s; + if (ey > screen_height) { + ey = screen_height; + } + cur_sy = sy; + cur_h = ey - sy; + + vgGetPixels(CursorBuffer, 0, 0, cur_sx, cur_sy, cur_w, cur_h); + cur_saved = cur_w * cur_h; +} + +// restoreCursor restores the pixels under the touch cursor +void restoreCursor(VGImage CursorBuffer) { + if (cur_saved != 0) { + vgSetPixels(cur_sx, cur_sy, CursorBuffer, 0, 0, cur_w, cur_h); + } +} + +// circleCursor draws a translucent circle as the touch cursor +void circleCursor(int curx, int cury, int width, int height, int s) { + Fill(100, 0, 0, 0.50); + Circle(curx, cury, s); + Fill(0, 0, 0, 1); + Circle(curx, cury, 2); +} + +// touchinit starts the touch event thread +int touchinit(int w, int h) { + pthread_t inputThread; + touch.max_x = w; + touch.max_y = h; + return pthread_create(&inputThread, NULL, &eventThread, NULL); +} + +int main() { + int width, height, cursorx, cursory, cbsize; + + init(&width, &height); // Graphics initialization + cursorx = width / 2; + cursory = height / 2; + cbsize = (CUR_SIZ * 2) + 1; + VGImage CursorBuffer = vgCreateImage(VG_sABGR_8888, cbsize, cbsize, VG_IMAGE_QUALITY_BETTER); + + if (touchinit(width, height) != 0) { + fprintf(stderr, "Unable to initialize the touch\n"); + exit(1); + } + Start(width, height); // Start the picture + Background(0, 0, 0); // Black background + Fill(44, 77, 232, 1); // Big blue marble + Circle(width / 2, 0, width); // The "world" + Fill(255, 255, 255, 1); // White text + TextMid(width / 2, height / 2, "Screen Calibration", SerifTypeface, width / 15); // Greetings + + // Draw lines + Stroke(255, 255, 255, 0.5); + StrokeWidth(2); + for( int i = 0; i < xHairLines_ROWS; i++) { + Line( ( xHairLines[i][0] < 0 ? width + xHairLines[i][0] : xHairLines[i][0] ) + , ( xHairLines[i][1] < 0 ? height + xHairLines[i][1] : xHairLines[i][1] ) + , ( xHairLines[i][2] < 0 ? width + xHairLines[i][2] : xHairLines[i][2] ) + , ( xHairLines[i][3] < 0 ? height + xHairLines[i][3] : xHairLines[i][3] ) + ); + } + + End(); // update picture + + + + // MAIN LOOP + while (left_count < 2) { // Loop until the left touch button pressed & released + // if the touch moved... + if (touch.x != cursorx || touch.y != cursory) { + restoreCursor(CursorBuffer); + cursorx = touch.x; + cursory = touch.y; + saveCursor(CursorBuffer, cursorx, cursory, width, height, CUR_SIZ); + circleCursor(cursorx, cursory, width, height, CUR_SIZ); + End(); // update picture + } + } + restoreCursor(CursorBuffer); // not strictly necessary as display will be closed + vgDestroyImage(CursorBuffer); // tidy up memory + finish(); // Graphics cleanup + exit(0); +} From 81d95b4f1184ce820699dcabdaaa615a3ed6ec79 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Fri, 30 Aug 2019 19:24:42 -0400 Subject: [PATCH 31/52] calibration components --- XPT2046.cpp | 4 +- calibrate.cpp | 363 ++++++++++++++++++++++++++++++++++++++++++++++++++ calibrate.h | 119 ++++++++++++++++- 3 files changed, 484 insertions(+), 2 deletions(-) create mode 100755 calibrate.cpp diff --git a/XPT2046.cpp b/XPT2046.cpp index 0b87bb2..1231b06 100644 --- a/XPT2046.cpp +++ b/XPT2046.cpp @@ -23,7 +23,9 @@ */ #include "XPT2046.h" - +extern "C" { + #include "calibrate.h" +} #define XPT2046_CFG_START 1<<7 #define XPT2046_CFG_MUX(v) ((v&0b111) << (4)) diff --git a/calibrate.cpp b/calibrate.cpp new file mode 100755 index 0000000..ae211dd --- /dev/null +++ b/calibrate.cpp @@ -0,0 +1,363 @@ +/* + * + * Copyright (c) 2001, Carlos E. Vidales. All rights reserved. + * + * This sample program was written and put in the public domain + * by Carlos E. Vidales. The program is provided "as is" + * without warranty of any kind, either expressed or implied. + * If you choose to use the program within your own products + * you do so at your own risk, and assume the responsibility + * for servicing, repairing or correcting the program should + * it prove defective in any manner. + * You may copy and distribute the program's source code in any + * medium, provided that you also include in each copy an + * appropriate copyright notice and disclaimer of warranty. + * You may also modify this program and distribute copies of + * it provided that you include prominent notices stating + * that you changed the file(s) and the date of any change, + * and that you do not charge any royalties or licenses for + * its use. + * + * + * + * File Name: calibrate.c + * + * + * This file contains functions that implement calculations + * necessary to obtain calibration factors for a touch screen + * that suffers from multiple distortion effects: namely, + * translation, scaling and rotation. + * + * The following set of equations represent a valid display + * point given a corresponding set of touch screen points: + * + * + * /- -\ + * /- -\ /- -\ | | + * | | | | | Xs | + * | Xd | | A B C | | | + * | | = | | * | Ys | + * | Yd | | D E F | | | + * | | | | | 1 | + * \- -/ \- -/ | | + * \- -/ + * + * + * where: + * + * (Xd,Yd) represents the desired display point + * coordinates, + * + * (Xs,Ys) represents the available touch screen + * coordinates, and the matrix + * + * /- -\ + * |A,B,C| + * |D,E,F| represents the factors used to translate + * \- -/ the available touch screen point values + * into the corresponding display + * coordinates. + * + * + * Note that for practical considerations, the utilitities + * within this file do not use the matrix coefficients as + * defined above, but instead use the following + * equivalents, since floating point math is not used: + * + * A = An/Divider + * B = Bn/Divider + * C = Cn/Divider + * D = Dn/Divider + * E = En/Divider + * F = Fn/Divider + * + * + * + * The functions provided within this file are: + * + * setCalibrationMatrix() - calculates the set of factors + * in the above equation, given + * three sets of test points. + * getDisplayPoint() - returns the actual display + * coordinates, given a set of + * touch screen coordinates. + * translateRawScreenCoordinates() - helper function to transform + * raw screen points into values + * scaled to the desired display + * resolution. + * + * + */ + + +#define _CALIBRATE_C_ + + + +/****************************************************/ +/* */ +/* Included files */ +/* */ +/****************************************************/ + +#include "calibrate.h" + + +/****************************************************/ +/* */ +/* Local Definitions and macros */ +/* */ +/****************************************************/ + + + +/****************************************************/ +/* */ +/* Global variables */ +/* */ +/****************************************************/ + + + +/****************************************************/ +/* */ +/* Forward Declaration of local functions */ +/* */ +/****************************************************/ + + + + + + +/********************************************************************** + * + * Function: setCalibrationMatrix() + * + * Description: Calling this function with valid input data + * in the display and screen input arguments + * causes the calibration factors between the + * screen and display points to be calculated, + * and the output argument - matrixPtr - to be + * populated. + * + * This function needs to be called only when new + * calibration factors are desired. + * + * + * Argument(s): displayPtr (input) - Pointer to an array of three + * sample, reference points. + * screenPtr (input) - Pointer to the array of touch + * screen points corresponding + * to the reference display points. + * matrixPtr (output) - Pointer to the calibration + * matrix computed for the set + * of points being provided. + * + * + * From the article text, recall that the matrix coefficients are + * resolved to be the following: + * + * + * Divider = (Xs0 - Xs2)*(Ys1 - Ys2) - (Xs1 - Xs2)*(Ys0 - Ys2) + * + * + * + * (Xd0 - Xd2)*(Ys1 - Ys2) - (Xd1 - Xd2)*(Ys0 - Ys2) + * A = --------------------------------------------------- + * Divider + * + * + * (Xs0 - Xs2)*(Xd1 - Xd2) - (Xd0 - Xd2)*(Xs1 - Xs2) + * B = --------------------------------------------------- + * Divider + * + * + * Ys0*(Xs2*Xd1 - Xs1*Xd2) + + * Ys1*(Xs0*Xd2 - Xs2*Xd0) + + * Ys2*(Xs1*Xd0 - Xs0*Xd1) + * C = --------------------------------------------------- + * Divider + * + * + * (Yd0 - Yd2)*(Ys1 - Ys2) - (Yd1 - Yd2)*(Ys0 - Ys2) + * D = --------------------------------------------------- + * Divider + * + * + * (Xs0 - Xs2)*(Yd1 - Yd2) - (Yd0 - Yd2)*(Xs1 - Xs2) + * E = --------------------------------------------------- + * Divider + * + * + * Ys0*(Xs2*Yd1 - Xs1*Yd2) + + * Ys1*(Xs0*Yd2 - Xs2*Yd0) + + * Ys2*(Xs1*Yd0 - Xs0*Yd1) + * F = --------------------------------------------------- + * Divider + * + * + * Return: OK - the calibration matrix was correctly + * calculated and its value is in the + * output argument. + * NOT_OK - an error was detected and the + * function failed to return a valid + * set of matrix values. + * The only time this sample code returns + * NOT_OK is when Divider == 0 + * + * + * + * NOTE! NOTE! NOTE! + * + * setCalibrationMatrix() and getDisplayPoint() will do fine + * for you as they are, provided that your digitizer + * resolution does not exceed 10 bits (1024 values). Higher + * resolutions may cause the integer operations to overflow + * and return incorrect values. If you wish to use these + * functions with digitizer resolutions of 12 bits (4096 + * values) you will either have to a) use 64-bit signed + * integer variables and math, or b) judiciously modify the + * operations to scale results by a factor of 2 or even 4. + * + * + */ +int setCalibrationMatrix( POINT * displayPtr, + POINT * screenPtr, + MATRIX * matrixPtr) +{ + + int retValue = OK ; + + + + matrixPtr->Divider = ((screenPtr[0].x - screenPtr[2].x) * (screenPtr[1].y - screenPtr[2].y)) - + ((screenPtr[1].x - screenPtr[2].x) * (screenPtr[0].y - screenPtr[2].y)) ; + + if( matrixPtr->Divider == 0 ) + { + retValue = NOT_OK ; + } + else + { + matrixPtr->An = ((displayPtr[0].x - displayPtr[2].x) * (screenPtr[1].y - screenPtr[2].y)) - + ((displayPtr[1].x - displayPtr[2].x) * (screenPtr[0].y - screenPtr[2].y)) ; + + matrixPtr->Bn = ((screenPtr[0].x - screenPtr[2].x) * (displayPtr[1].x - displayPtr[2].x)) - + ((displayPtr[0].x - displayPtr[2].x) * (screenPtr[1].x - screenPtr[2].x)) ; + + matrixPtr->Cn = (screenPtr[2].x * displayPtr[1].x - screenPtr[1].x * displayPtr[2].x) * screenPtr[0].y + + (screenPtr[0].x * displayPtr[2].x - screenPtr[2].x * displayPtr[0].x) * screenPtr[1].y + + (screenPtr[1].x * displayPtr[0].x - screenPtr[0].x * displayPtr[1].x) * screenPtr[2].y ; + + matrixPtr->Dn = ((displayPtr[0].y - displayPtr[2].y) * (screenPtr[1].y - screenPtr[2].y)) - + ((displayPtr[1].y - displayPtr[2].y) * (screenPtr[0].y - screenPtr[2].y)) ; + + matrixPtr->En = ((screenPtr[0].x - screenPtr[2].x) * (displayPtr[1].y - displayPtr[2].y)) - + ((displayPtr[0].y - displayPtr[2].y) * (screenPtr[1].x - screenPtr[2].x)) ; + + matrixPtr->Fn = (screenPtr[2].x * displayPtr[1].y - screenPtr[1].x * displayPtr[2].y) * screenPtr[0].y + + (screenPtr[0].x * displayPtr[2].y - screenPtr[2].x * displayPtr[0].y) * screenPtr[1].y + + (screenPtr[1].x * displayPtr[0].y - screenPtr[0].x * displayPtr[1].y) * screenPtr[2].y ; + } + + return( retValue ) ; + +} /* end of setCalibrationMatrix() */ + + + +/********************************************************************** + * + * Function: getDisplayPoint() + * + * Description: Given a valid set of calibration factors and a point + * value reported by the touch screen, this function + * calculates and returns the true (or closest to true) + * display point below the spot where the touch screen + * was touched. + * + * + * + * Argument(s): displayPtr (output) - Pointer to the calculated + * (true) display point. + * screenPtr (input) - Pointer to the reported touch + * screen point. + * matrixPtr (input) - Pointer to calibration factors + * matrix previously calculated + * from a call to + * setCalibrationMatrix() + * + * + * The function simply solves for Xd and Yd by implementing the + * computations required by the translation matrix. + * + * /- -\ + * /- -\ /- -\ | | + * | | | | | Xs | + * | Xd | | A B C | | | + * | | = | | * | Ys | + * | Yd | | D E F | | | + * | | | | | 1 | + * \- -/ \- -/ | | + * \- -/ + * + * It must be kept brief to avoid consuming CPU cycles. + * + * + * Return: OK - the display point was correctly calculated + * and its value is in the output argument. + * NOT_OK - an error was detected and the function + * failed to return a valid point. + * + * + * + * NOTE! NOTE! NOTE! + * + * setCalibrationMatrix() and getDisplayPoint() will do fine + * for you as they are, provided that your digitizer + * resolution does not exceed 10 bits (1024 values). Higher + * resolutions may cause the integer operations to overflow + * and return incorrect values. If you wish to use these + * functions with digitizer resolutions of 12 bits (4096 + * values) you will either have to a) use 64-bit signed + * integer variables and math, or b) judiciously modify the + * operations to scale results by a factor of 2 or even 4. + * + * + */ +int getDisplayPoint( POINT * displayPtr, + POINT * screenPtr, + MATRIX * matrixPtr ) +{ + int retValue = OK ; + + + if( matrixPtr->Divider != 0 ) + { + + /* Operation order is important since we are doing integer */ + /* math. Make sure you add all terms together before */ + /* dividing, so that the remainder is not rounded off */ + /* prematurely. */ + + displayPtr->x = ( (matrixPtr->An * screenPtr->x) + + (matrixPtr->Bn * screenPtr->y) + + matrixPtr->Cn + ) / matrixPtr->Divider ; + + displayPtr->y = ( (matrixPtr->Dn * screenPtr->x) + + (matrixPtr->En * screenPtr->y) + + matrixPtr->Fn + ) / matrixPtr->Divider ; + } + else + { + retValue = NOT_OK ; + } + + return( retValue ) ; + +} /* end of getDisplayPoint() */ + + diff --git a/calibrate.h b/calibrate.h index e2ef436..2835761 100755 --- a/calibrate.h +++ b/calibrate.h @@ -1 +1,118 @@ -/* * * Copyright (c) 2001, Carlos E. Vidales. All rights reserved. * * This sample program was written and put in the public domain * by Carlos E. Vidales. The program is provided "as is" * without warranty of any kind, either expressed or implied. * If you choose to use the program within your own products * you do so at your own risk, and assume the responsibility * for servicing, repairing or correcting the program should * it prove defective in any manner. * You may copy and distribute the program's source code in any * medium, provided that you also include in each copy an * appropriate copyright notice and disclaimer of warranty. * You may also modify this program and distribute copies of * it provided that you include prominent notices stating * that you changed the file(s) and the date of any change, * and that you do not charge any royalties or licenses for * its use. * * * File Name: calibrate.h * * * Definition of constants and structures, and declaration of functions * in Calibrate.c * * Revisions: Kevin Peck 2019 */ #ifndef _CALIBRATE_H_ #define _CALIBRATE_H_ /****************************************************/ /* */ /* Included files */ /* */ /****************************************************/ #include /****************************************************/ /* */ /* Definitions */ /* */ /****************************************************/ #ifndef _CALIBRATE_C_ #define EXTERN extern #else #define EXTERN #endif #ifndef OK #define OK 0 #define NOT_OK -1 #endif #define INT32 long /****************************************************/ /* */ /* Structures */ /* */ /****************************************************/ typedef struct Point { uint16_t x, y ; } POINT ; typedef struct Matrix { /* This arrangement of values facilitates * calculations within getDisplayPoint() */ uint16_t An, /* A = An/Divider */ Bn, /* B = Bn/Divider */ Cn, /* C = Cn/Divider */ Dn, /* D = Dn/Divider */ En, /* E = En/Divider */ Fn, /* F = Fn/Divider */ Divider ; } MATRIX ; /****************************************************/ /* */ /* Function declarations */ /* */ /****************************************************/ EXTERN int setCalibrationMatrix( POINT * display, POINT * screen, MATRIX * matrix) ; EXTERN int getDisplayPoint( POINT * display, POINT * screen, MATRIX * matrix ) ; #endif /* _CALIBRATE_H_ */ \ No newline at end of file +/* + * + * Copyright (c) 2001, Carlos E. Vidales. All rights reserved. + * + * This sample program was written and put in the public domain + * by Carlos E. Vidales. The program is provided "as is" + * without warranty of any kind, either expressed or implied. + * If you choose to use the program within your own products + * you do so at your own risk, and assume the responsibility + * for servicing, repairing or correcting the program should + * it prove defective in any manner. + * You may copy and distribute the program's source code in any + * medium, provided that you also include in each copy an + * appropriate copyright notice and disclaimer of warranty. + * You may also modify this program and distribute copies of + * it provided that you include prominent notices stating + * that you changed the file(s) and the date of any change, + * and that you do not charge any royalties or licenses for + * its use. + * + * + * File Name: calibrate.h + * + * + * Definition of constants and structures, and declaration of functions + * in Calibrate.c + * + * Revisions: Kevin Peck 2019 + */ + +#ifndef _CALIBRATE_H_ + +#define _CALIBRATE_H_ + +/****************************************************/ +/* */ +/* Included files */ +/* */ +/****************************************************/ + +#include + + +/****************************************************/ +/* */ +/* Definitions */ +/* */ +/****************************************************/ + +#ifndef _CALIBRATE_C_ + #define EXTERN extern +#else + #define EXTERN +#endif + + + +#ifndef OK + #define OK 0 + #define NOT_OK -1 +#endif + + + +#define INT32 long + + + + +/****************************************************/ +/* */ +/* Structures */ +/* */ +/****************************************************/ + + +typedef struct Point { + INT32 x, + y ; + } POINT ; + + + +typedef struct Matrix { + /* This arrangement of values facilitates + * calculations within getDisplayPoint() + */ + INT32 An, /* A = An/Divider */ + Bn, /* B = Bn/Divider */ + Cn, /* C = Cn/Divider */ + Dn, /* D = Dn/Divider */ + En, /* E = En/Divider */ + Fn, /* F = Fn/Divider */ + Divider ; + } MATRIX ; + + + + +/****************************************************/ +/* */ +/* Function declarations */ +/* */ +/****************************************************/ + + +EXTERN int setCalibrationMatrix( POINT * display, + POINT * screen, + MATRIX * matrix) ; + + +EXTERN int getDisplayPoint( POINT * display, + POINT * screen, + MATRIX * matrix ) ; + + +#endif /* _CALIBRATE_H_ */ + From 1965c9f576898e11277a21af7c8e609b3d0830ae Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Fri, 30 Aug 2019 19:55:04 -0400 Subject: [PATCH 32/52] reamed to cpp --- calibrate.c | 1 - 1 file changed, 1 deletion(-) delete mode 100755 calibrate.c diff --git a/calibrate.c b/calibrate.c deleted file mode 100755 index 6781eeb..0000000 --- a/calibrate.c +++ /dev/null @@ -1 +0,0 @@ -/* * * Copyright (c) 2001, Carlos E. Vidales. All rights reserved. * * This sample program was written and put in the public domain * by Carlos E. Vidales. The program is provided "as is" * without warranty of any kind, either expressed or implied. * If you choose to use the program within your own products * you do so at your own risk, and assume the responsibility * for servicing, repairing or correcting the program should * it prove defective in any manner. * You may copy and distribute the program's source code in any * medium, provided that you also include in each copy an * appropriate copyright notice and disclaimer of warranty. * You may also modify this program and distribute copies of * it provided that you include prominent notices stating * that you changed the file(s) and the date of any change, * and that you do not charge any royalties or licenses for * its use. * * * * File Name: calibrate.c * * * This file contains functions that implement calculations * necessary to obtain calibration factors for a touch screen * that suffers from multiple distortion effects: namely, * translation, scaling and rotation. * * The following set of equations represent a valid display * point given a corresponding set of touch screen points: * * * /- -\ * /- -\ /- -\ | | * | | | | | Xs | * | Xd | | A B C | | | * | | = | | * | Ys | * | Yd | | D E F | | | * | | | | | 1 | * \- -/ \- -/ | | * \- -/ * * * where: * * (Xd,Yd) represents the desired display point * coordinates, * * (Xs,Ys) represents the available touch screen * coordinates, and the matrix * * /- -\ * |A,B,C| * |D,E,F| represents the factors used to translate * \- -/ the available touch screen point values * into the corresponding display * coordinates. * * * Note that for practical considerations, the utilitities * within this file do not use the matrix coefficients as * defined above, but instead use the following * equivalents, since floating point math is not used: * * A = An/Divider * B = Bn/Divider * C = Cn/Divider * D = Dn/Divider * E = En/Divider * F = Fn/Divider * * * * The functions provided within this file are: * * setCalibrationMatrix() - calculates the set of factors * in the above equation, given * three sets of test points. * getDisplayPoint() - returns the actual display * coordinates, given a set of * touch screen coordinates. * translateRawScreenCoordinates() - helper function to transform * raw screen points into values * scaled to the desired display * resolution. * * */ #define _CALIBRATE_C_ /****************************************************/ /* */ /* Included files */ /* */ /****************************************************/ #include "Calibrate.h" /****************************************************/ /* */ /* Local Definitions and macros */ /* */ /****************************************************/ /****************************************************/ /* */ /* Global variables */ /* */ /****************************************************/ /****************************************************/ /* */ /* Forward Declaration of local functions */ /* */ /****************************************************/ /********************************************************************** * * Function: setCalibrationMatrix() * * Description: Calling this function with valid input data * in the display and screen input arguments * causes the calibration factors between the * screen and display points to be calculated, * and the output argument - matrixPtr - to be * populated. * * This function needs to be called only when new * calibration factors are desired. * * * Argument(s): displayPtr (input) - Pointer to an array of three * sample, reference points. * screenPtr (input) - Pointer to the array of touch * screen points corresponding * to the reference display points. * matrixPtr (output) - Pointer to the calibration * matrix computed for the set * of points being provided. * * * From the article text, recall that the matrix coefficients are * resolved to be the following: * * * Divider = (Xs0 - Xs2)*(Ys1 - Ys2) - (Xs1 - Xs2)*(Ys0 - Ys2) * * * * (Xd0 - Xd2)*(Ys1 - Ys2) - (Xd1 - Xd2)*(Ys0 - Ys2) * A = --------------------------------------------------- * Divider * * * (Xs0 - Xs2)*(Xd1 - Xd2) - (Xd0 - Xd2)*(Xs1 - Xs2) * B = --------------------------------------------------- * Divider * * * Ys0*(Xs2*Xd1 - Xs1*Xd2) + * Ys1*(Xs0*Xd2 - Xs2*Xd0) + * Ys2*(Xs1*Xd0 - Xs0*Xd1) * C = --------------------------------------------------- * Divider * * * (Yd0 - Yd2)*(Ys1 - Ys2) - (Yd1 - Yd2)*(Ys0 - Ys2) * D = --------------------------------------------------- * Divider * * * (Xs0 - Xs2)*(Yd1 - Yd2) - (Yd0 - Yd2)*(Xs1 - Xs2) * E = --------------------------------------------------- * Divider * * * Ys0*(Xs2*Yd1 - Xs1*Yd2) + * Ys1*(Xs0*Yd2 - Xs2*Yd0) + * Ys2*(Xs1*Yd0 - Xs0*Yd1) * F = --------------------------------------------------- * Divider * * * Return: OK - the calibration matrix was correctly * calculated and its value is in the * output argument. * NOT_OK - an error was detected and the * function failed to return a valid * set of matrix values. * The only time this sample code returns * NOT_OK is when Divider == 0 * * * * NOTE! NOTE! NOTE! * * setCalibrationMatrix() and getDisplayPoint() will do fine * for you as they are, provided that your digitizer * resolution does not exceed 10 bits (1024 values). Higher * resolutions may cause the integer operations to overflow * and return incorrect values. If you wish to use these * functions with digitizer resolutions of 12 bits (4096 * values) you will either have to a) use 64-bit signed * integer variables and math, or b) judiciously modify the * operations to scale results by a factor of 2 or even 4. * * */ int setCalibrationMatrix( POINT * displayPtr, POINT * screenPtr, MATRIX * matrixPtr) { int retValue = OK ; matrixPtr->Divider = ((screenPtr[0].x - screenPtr[2].x) * (screenPtr[1].y - screenPtr[2].y)) - ((screenPtr[1].x - screenPtr[2].x) * (screenPtr[0].y - screenPtr[2].y)) ; if( matrixPtr->Divider == 0 ) { retValue = NOT_OK ; } else { matrixPtr->An = ((displayPtr[0].x - displayPtr[2].x) * (screenPtr[1].y - screenPtr[2].y)) - ((displayPtr[1].x - displayPtr[2].x) * (screenPtr[0].y - screenPtr[2].y)) ; matrixPtr->Bn = ((screenPtr[0].x - screenPtr[2].x) * (displayPtr[1].x - displayPtr[2].x)) - ((displayPtr[0].x - displayPtr[2].x) * (screenPtr[1].x - screenPtr[2].x)) ; matrixPtr->Cn = (screenPtr[2].x * displayPtr[1].x - screenPtr[1].x * displayPtr[2].x) * screenPtr[0].y + (screenPtr[0].x * displayPtr[2].x - screenPtr[2].x * displayPtr[0].x) * screenPtr[1].y + (screenPtr[1].x * displayPtr[0].x - screenPtr[0].x * displayPtr[1].x) * screenPtr[2].y ; matrixPtr->Dn = ((displayPtr[0].y - displayPtr[2].y) * (screenPtr[1].y - screenPtr[2].y)) - ((displayPtr[1].y - displayPtr[2].y) * (screenPtr[0].y - screenPtr[2].y)) ; matrixPtr->En = ((screenPtr[0].x - screenPtr[2].x) * (displayPtr[1].y - displayPtr[2].y)) - ((displayPtr[0].y - displayPtr[2].y) * (screenPtr[1].x - screenPtr[2].x)) ; matrixPtr->Fn = (screenPtr[2].x * displayPtr[1].y - screenPtr[1].x * displayPtr[2].y) * screenPtr[0].y + (screenPtr[0].x * displayPtr[2].y - screenPtr[2].x * displayPtr[0].y) * screenPtr[1].y + (screenPtr[1].x * displayPtr[0].y - screenPtr[0].x * displayPtr[1].y) * screenPtr[2].y ; } return( retValue ) ; } /* end of setCalibrationMatrix() */ /********************************************************************** * * Function: getDisplayPoint() * * Description: Given a valid set of calibration factors and a point * value reported by the touch screen, this function * calculates and returns the true (or closest to true) * display point below the spot where the touch screen * was touched. * * * * Argument(s): displayPtr (output) - Pointer to the calculated * (true) display point. * screenPtr (input) - Pointer to the reported touch * screen point. * matrixPtr (input) - Pointer to calibration factors * matrix previously calculated * from a call to * setCalibrationMatrix() * * * The function simply solves for Xd and Yd by implementing the * computations required by the translation matrix. * * /- -\ * /- -\ /- -\ | | * | | | | | Xs | * | Xd | | A B C | | | * | | = | | * | Ys | * | Yd | | D E F | | | * | | | | | 1 | * \- -/ \- -/ | | * \- -/ * * It must be kept brief to avoid consuming CPU cycles. * * * Return: OK - the display point was correctly calculated * and its value is in the output argument. * NOT_OK - an error was detected and the function * failed to return a valid point. * * * * NOTE! NOTE! NOTE! * * setCalibrationMatrix() and getDisplayPoint() will do fine * for you as they are, provided that your digitizer * resolution does not exceed 10 bits (1024 values). Higher * resolutions may cause the integer operations to overflow * and return incorrect values. If you wish to use these * functions with digitizer resolutions of 12 bits (4096 * values) you will either have to a) use 64-bit signed * integer variables and math, or b) judiciously modify the * operations to scale results by a factor of 2 or even 4. * * */ int getDisplayPoint( POINT * displayPtr, POINT * screenPtr, MATRIX * matrixPtr ) { int retValue = OK ; if( matrixPtr->Divider != 0 ) { /* Operation order is important since we are doing integer */ /* math. Make sure you add all terms together before */ /* dividing, so that the remainder is not rounded off */ /* prematurely. */ displayPtr->x = ( (matrixPtr->An * screenPtr->x) + (matrixPtr->Bn * screenPtr->y) + matrixPtr->Cn ) / matrixPtr->Divider ; displayPtr->y = ( (matrixPtr->Dn * screenPtr->x) + (matrixPtr->En * screenPtr->y) + matrixPtr->Fn ) / matrixPtr->Divider ; } else { retValue = NOT_OK ; } return( retValue ) ; } /* end of getDisplayPoint() */ \ No newline at end of file From 59a92f346549e37b96cf94e45b67ace70dc94e6b Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Fri, 6 Sep 2019 05:10:00 +0000 Subject: [PATCH 33/52] functional calibration utillity with bugs --- Makefile | 0 util/Makefile | 5 +- util/tcCalib.cpp | 143 ++++++++++++++++++++++++++++++++++++----------- 3 files changed, 113 insertions(+), 35 deletions(-) delete mode 100644 Makefile diff --git a/Makefile b/Makefile deleted file mode 100644 index e69de29..0000000 diff --git a/util/Makefile b/util/Makefile index 75c74d4..f36d7c4 100644 --- a/util/Makefile +++ b/util/Makefile @@ -1,7 +1,7 @@ LIBFLAGS=-L/opt/vc/lib -lEGL -lGLESv2 -lbcm_host -lpthread -ljpeg -lshapes CXXFLAGS=-DDEBUG -fno-exceptions -DPI -std=gnu++11 -I/opt/vc/include -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/interface/vcos/pthreads -I.. -objs=tcCalib.o +objs=tcCalib.o ../calibrate.o all: tcCalib @@ -9,6 +9,9 @@ clean: $(objs) rm $(objs) rm tcCalib +../calibrate.o: ../calibrate.cpp + g++ $(CXXFLAGS) -c -o ../calibrate.o ../calibrate.cpp + tcCalib: $(objs) g++ -Wall $(CXXFLAGS) $(LIBFLAGS) $(objs) -o tcCalib diff --git a/util/tcCalib.cpp b/util/tcCalib.cpp index 29aadce..3a34c1b 100644 --- a/util/tcCalib.cpp +++ b/util/tcCalib.cpp @@ -14,6 +14,21 @@ #include #include +#include "calibrate.h" + +// Target on-screen points +POINT pointTargets[4] = { + {15, 15}, + {-15, 15}, + {-15, -15}, + {15, -15} +}; + +// Recorded touch points +POINT pointTouches[4]; + +MATRIX calibMatrix; + #define xHairLines_ROWS 8 int xHairLines[8][4] = { 10, 15, 20, 15, // lower left @@ -55,7 +70,7 @@ int quitState = 0; // evenThread reads from the touch input file void *eventThread(void *arg) { - + int count = 0, ptr = 0; // Open touch driver if ((touch.fd = open("/tmp/TCfifo", O_RDONLY)) < 0) { fprintf(stderr, "Error opening touch!\n"); @@ -66,9 +81,17 @@ void *eventThread(void *arg) { touch.y = touch.max_y / 2; while (1) { - read(touch.fd, &touch.evbuff, sizeof(char[80])); + usleep(100); + do { + count = read(touch.fd, (&touch.evbuff + ptr), sizeof(char[80])-ptr ); + ptr = ptr + count; + } while (ptr >0 && ((const char *)touch.evbuff)[ptr-1] != '\n'); + + fprintf(stdout, (const char *)touch.evbuff); + touch.parse((const char *)touch.evbuff); - printf("[%4.0f,%4.0f]\r",touch.x,touch.y); + memset(touch.evbuff, 0, sizeof(char[80])); + count = 0, ptr = 0; } } @@ -128,6 +151,60 @@ int touchinit(int w, int h) { return pthread_create(&inputThread, NULL, &eventThread, NULL); } +void getTouches(int captures, int width, int height, int &cursorx, int &cursory, VGImage &CursorBuffer) { + int changeCount = 0; + POINT pointCorrected, pointRaw; + int ztouch_thold = 50; + + do { + usleep(100000); // usec - slow loop for other threads + pointRaw.x = touch.x; + pointRaw.y = touch.y; + getDisplayPoint(&pointCorrected,&pointRaw,&calibMatrix); + + // Loop until a touch is registered by a change in cursor value + if ((pointCorrected.x != cursorx || pointCorrected.y != cursory) && ((int)touch.z) > ztouch_thold ) { + fprintf(stdout,"%d != %d || %d != %d) && %d > %d\n", + pointCorrected.x, cursorx , pointCorrected.y, cursory, ((int)touch.z) , ztouch_thold); + + restoreCursor(CursorBuffer); + cursorx = pointCorrected.x; + cursory = pointCorrected.y; + saveCursor(CursorBuffer, cursorx, cursory, width, height, CUR_SIZ); + circleCursor(cursorx, cursory, width, height, CUR_SIZ); + changeCount = changeCount + 1; + touch.z = 0.0; + End(); // update picture + } + } while (changeCount < captures); +} + +void drawBackground(int width, int height) { + Background(0, 0, 0); // Black background + Fill(44, 77, 232, 1); // Big blue marble + Circle(width / 2, 0, width); // The "world" + Fill(255, 255, 255, 1); // White text + TextMid(width / 2, height / 2, "Screen Calibration", SerifTypeface, width / 15); // Greetings + End(); // update picture +} + +void drawCrosshair(int width, int height, int i) { + Stroke(255, 255, 255, 0.5); + StrokeWidth(2); + Line( ( xHairLines[i][0] < 0 ? width + xHairLines[i][0] : xHairLines[i][0] ) + , ( xHairLines[i][1] < 0 ? height + xHairLines[i][1] : xHairLines[i][1] ) + , ( xHairLines[i][2] < 0 ? width + xHairLines[i][2] : xHairLines[i][2] ) + , ( xHairLines[i][3] < 0 ? height + xHairLines[i][3] : xHairLines[i][3] ) + ); + + Line( ( xHairLines[i+1][0] < 0 ? width + xHairLines[i+1][0] : xHairLines[i+1][0] ) + , ( xHairLines[i+1][1] < 0 ? height + xHairLines[i+1][1] : xHairLines[i+1][1] ) + , ( xHairLines[i+1][2] < 0 ? width + xHairLines[i+1][2] : xHairLines[i+1][2] ) + , ( xHairLines[i+1][3] < 0 ? height + xHairLines[i+1][3] : xHairLines[i+1][3] ) + ); + End(); // update picture +} + int main() { int width, height, cursorx, cursory, cbsize; @@ -142,40 +219,38 @@ int main() { exit(1); } Start(width, height); // Start the picture - Background(0, 0, 0); // Black background - Fill(44, 77, 232, 1); // Big blue marble - Circle(width / 2, 0, width); // The "world" - Fill(255, 255, 255, 1); // White text - TextMid(width / 2, height / 2, "Screen Calibration", SerifTypeface, width / 15); // Greetings - + drawBackground(width,height); + + // Set curser values initially + setCalibrationMatrix( (POINT*)&pointTargets, (POINT*)&pointTargets, &calibMatrix); // initialized 1:1 matrix + fprintf(stdout,"M: %d,%d,%d,%d,%d,%d,%d\r\n" + ,calibMatrix.An,calibMatrix.Bn,calibMatrix.Cn,calibMatrix.Dn,calibMatrix.En,calibMatrix.Fn,calibMatrix.Divider ); + // Draw lines - Stroke(255, 255, 255, 0.5); - StrokeWidth(2); - for( int i = 0; i < xHairLines_ROWS; i++) { - Line( ( xHairLines[i][0] < 0 ? width + xHairLines[i][0] : xHairLines[i][0] ) - , ( xHairLines[i][1] < 0 ? height + xHairLines[i][1] : xHairLines[i][1] ) - , ( xHairLines[i][2] < 0 ? width + xHairLines[i][2] : xHairLines[i][2] ) - , ( xHairLines[i][3] < 0 ? height + xHairLines[i][3] : xHairLines[i][3] ) - ); + for( int i = 0; i < xHairLines_ROWS - 2; i=i+2) { + drawCrosshair(width,height,i); + + getTouches(1,width,height,cursorx,cursory,CursorBuffer); + fprintf(stdout,"p:%d %d, %d \r\n",i, cursorx,cursory); + /* + // save touch points + pointTouches[i/2].x = cursorx; + pointTouches[i/2].y = cursory; + // Update for screen dimensions + pointTargets[i/2].x = (pointTargets[i/2].x < 0 ? width + pointTargets[i/2].x : pointTargets[i/2].x); + pointTargets[i/2].y = (pointTargets[i/2].y < 0 ? height + pointTargets[i/2].y : pointTargets[i/2].y); + */ } + + setCalibrationMatrix( (POINT*)&pointTargets, (POINT*)&pointTargets, &calibMatrix); // update matrix + fprintf(stdout,"M: %d,%d,%d,%d,%d,%d,%d\r\n" + ,calibMatrix.An,calibMatrix.Bn,calibMatrix.Cn,calibMatrix.Dn,calibMatrix.En,calibMatrix.Fn,calibMatrix.Divider ); + + // MAIN LOOP - show some more points + getTouches(10,width,height,cursorx,cursory,CursorBuffer); - End(); // update picture - - - - // MAIN LOOP - while (left_count < 2) { // Loop until the left touch button pressed & released - // if the touch moved... - if (touch.x != cursorx || touch.y != cursory) { - restoreCursor(CursorBuffer); - cursorx = touch.x; - cursory = touch.y; - saveCursor(CursorBuffer, cursorx, cursory, width, height, CUR_SIZ); - circleCursor(cursorx, cursory, width, height, CUR_SIZ); - End(); // update picture - } - } - restoreCursor(CursorBuffer); // not strictly necessary as display will be closed + //restoreCursor(CursorBuffer); // not strictly necessary as display will be closed + //End(); vgDestroyImage(CursorBuffer); // tidy up memory finish(); // Graphics cleanup exit(0); From e7d91bc2ac226b2820e0711076428a7b438d9748 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Fri, 6 Sep 2019 01:12:05 -0400 Subject: [PATCH 34/52] calibration utility with bugs --- calibrate.cpp | 3 +- calibrate.h | 3 +- util/Makefile | 5 +- util/tcCalib.cpp | 142 +++++++++++++++++++++++++++++++++++------------ 4 files changed, 115 insertions(+), 38 deletions(-) diff --git a/calibrate.cpp b/calibrate.cpp index ae211dd..590e4d5 100755 --- a/calibrate.cpp +++ b/calibrate.cpp @@ -20,7 +20,7 @@ * * * - * File Name: calibrate.c + * File Name: calibrate.cpp * * * This file contains functions that implement calculations @@ -87,6 +87,7 @@ * resolution. * * + * Revisions: Kevin Peck 2019 */ diff --git a/calibrate.h b/calibrate.h index 2835761..2648b3a 100755 --- a/calibrate.h +++ b/calibrate.h @@ -75,8 +75,7 @@ typedef struct Point { - INT32 x, - y ; + INT32 x,y ; } POINT ; diff --git a/util/Makefile b/util/Makefile index 75c74d4..f36d7c4 100644 --- a/util/Makefile +++ b/util/Makefile @@ -1,7 +1,7 @@ LIBFLAGS=-L/opt/vc/lib -lEGL -lGLESv2 -lbcm_host -lpthread -ljpeg -lshapes CXXFLAGS=-DDEBUG -fno-exceptions -DPI -std=gnu++11 -I/opt/vc/include -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/interface/vcos/pthreads -I.. -objs=tcCalib.o +objs=tcCalib.o ../calibrate.o all: tcCalib @@ -9,6 +9,9 @@ clean: $(objs) rm $(objs) rm tcCalib +../calibrate.o: ../calibrate.cpp + g++ $(CXXFLAGS) -c -o ../calibrate.o ../calibrate.cpp + tcCalib: $(objs) g++ -Wall $(CXXFLAGS) $(LIBFLAGS) $(objs) -o tcCalib diff --git a/util/tcCalib.cpp b/util/tcCalib.cpp index 29aadce..1531f6f 100644 --- a/util/tcCalib.cpp +++ b/util/tcCalib.cpp @@ -14,6 +14,21 @@ #include #include +#include "calibrate.h" + +// Target on-screen points +POINT pointTargets[4] = { + {15, 15}, + {-15, 15}, + {-15, -15}, + {15, -15} +}; + +// Recorded touch points +POINT pointTouches[4]; + +MATRIX calibMatrix; + #define xHairLines_ROWS 8 int xHairLines[8][4] = { 10, 15, 20, 15, // lower left @@ -55,7 +70,7 @@ int quitState = 0; // evenThread reads from the touch input file void *eventThread(void *arg) { - + int count = 0, ptr = 0; // Open touch driver if ((touch.fd = open("/tmp/TCfifo", O_RDONLY)) < 0) { fprintf(stderr, "Error opening touch!\n"); @@ -66,9 +81,16 @@ void *eventThread(void *arg) { touch.y = touch.max_y / 2; while (1) { - read(touch.fd, &touch.evbuff, sizeof(char[80])); + do { + count = read(touch.fd, (&touch.evbuff + ptr), sizeof(char[80])-ptr ); + ptr = ptr + count; + } while (ptr >0 && ((const char *)touch.evbuff)[ptr-1] != '\n'); + + fprintf(stdout, (const char *)touch.evbuff); + touch.parse((const char *)touch.evbuff); - printf("[%4.0f,%4.0f]\r",touch.x,touch.y); + memset(touch.evbuff, 0, sizeof(char[80])); + count = 0, ptr = 0; } } @@ -128,6 +150,60 @@ int touchinit(int w, int h) { return pthread_create(&inputThread, NULL, &eventThread, NULL); } +void getTouches(int captures, int width, int height, int &cursorx, int &cursory, VGImage &CursorBuffer) { + int changeCount = 0; + POINT pointCorrected, pointRaw; + int ztouch_thold = 50; + + do { + usleep(100000); // usec - slow loop for other threads + pointRaw.x = touch.x; + pointRaw.y = touch.y; + getDisplayPoint(&pointCorrected,&pointRaw,&calibMatrix); + + // Loop until a touch is registered by a change in cursor value + if ((pointCorrected.x != cursorx || pointCorrected.y != cursory) && ((int)touch.z) > ztouch_thold ) { + fprintf(stdout,"%d != %d || %d != %d) && %d > %d\n", + pointCorrected.x, cursorx , pointCorrected.y, cursory, ((int)touch.z) , ztouch_thold); + + restoreCursor(CursorBuffer); + cursorx = pointCorrected.x; + cursory = pointCorrected.y; + saveCursor(CursorBuffer, cursorx, cursory, width, height, CUR_SIZ); + circleCursor(cursorx, cursory, width, height, CUR_SIZ); + changeCount = changeCount + 1; + touch.z = 0.0; + End(); // update picture + } + } while (changeCount < captures); +} + +void drawBackground(int width, int height) { + Background(0, 0, 0); // Black background + Fill(44, 77, 232, 1); // Big blue marble + Circle(width / 2, 0, width); // The "world" + Fill(255, 255, 255, 1); // White text + TextMid(width / 2, height / 2, "Screen Calibration", SerifTypeface, width / 15); // Greetings + End(); // update picture +} + +void drawCrosshair(int width, int height, int i) { + Stroke(255, 255, 255, 0.5); + StrokeWidth(2); + Line( ( xHairLines[i][0] < 0 ? width + xHairLines[i][0] : xHairLines[i][0] ) + , ( xHairLines[i][1] < 0 ? height + xHairLines[i][1] : xHairLines[i][1] ) + , ( xHairLines[i][2] < 0 ? width + xHairLines[i][2] : xHairLines[i][2] ) + , ( xHairLines[i][3] < 0 ? height + xHairLines[i][3] : xHairLines[i][3] ) + ); + + Line( ( xHairLines[i+1][0] < 0 ? width + xHairLines[i+1][0] : xHairLines[i+1][0] ) + , ( xHairLines[i+1][1] < 0 ? height + xHairLines[i+1][1] : xHairLines[i+1][1] ) + , ( xHairLines[i+1][2] < 0 ? width + xHairLines[i+1][2] : xHairLines[i+1][2] ) + , ( xHairLines[i+1][3] < 0 ? height + xHairLines[i+1][3] : xHairLines[i+1][3] ) + ); + End(); // update picture +} + int main() { int width, height, cursorx, cursory, cbsize; @@ -142,40 +218,38 @@ int main() { exit(1); } Start(width, height); // Start the picture - Background(0, 0, 0); // Black background - Fill(44, 77, 232, 1); // Big blue marble - Circle(width / 2, 0, width); // The "world" - Fill(255, 255, 255, 1); // White text - TextMid(width / 2, height / 2, "Screen Calibration", SerifTypeface, width / 15); // Greetings - + drawBackground(width,height); + + // Set curser values initially + setCalibrationMatrix( (POINT*)&pointTargets, (POINT*)&pointTargets, &calibMatrix); // initialized 1:1 matrix + fprintf(stdout,"M: %d,%d,%d,%d,%d,%d,%d\r\n" + ,calibMatrix.An,calibMatrix.Bn,calibMatrix.Cn,calibMatrix.Dn,calibMatrix.En,calibMatrix.Fn,calibMatrix.Divider ); + // Draw lines - Stroke(255, 255, 255, 0.5); - StrokeWidth(2); - for( int i = 0; i < xHairLines_ROWS; i++) { - Line( ( xHairLines[i][0] < 0 ? width + xHairLines[i][0] : xHairLines[i][0] ) - , ( xHairLines[i][1] < 0 ? height + xHairLines[i][1] : xHairLines[i][1] ) - , ( xHairLines[i][2] < 0 ? width + xHairLines[i][2] : xHairLines[i][2] ) - , ( xHairLines[i][3] < 0 ? height + xHairLines[i][3] : xHairLines[i][3] ) - ); + for( int i = 0; i < xHairLines_ROWS - 2; i=i+2) { + drawCrosshair(width,height,i); + + getTouches(1,width,height,cursorx,cursory,CursorBuffer); + fprintf(stdout,"p:%d %d, %d \r\n",i, cursorx,cursory); + /* + // save touch points + pointTouches[i/2].x = cursorx; + pointTouches[i/2].y = cursory; + // Update for screen dimensions + pointTargets[i/2].x = (pointTargets[i/2].x < 0 ? width + pointTargets[i/2].x : pointTargets[i/2].x); + pointTargets[i/2].y = (pointTargets[i/2].y < 0 ? height + pointTargets[i/2].y : pointTargets[i/2].y); + */ } + + setCalibrationMatrix( (POINT*)&pointTargets, (POINT*)&pointTargets, &calibMatrix); // update matrix + fprintf(stdout,"M: %d,%d,%d,%d,%d,%d,%d\r\n" + ,calibMatrix.An,calibMatrix.Bn,calibMatrix.Cn,calibMatrix.Dn,calibMatrix.En,calibMatrix.Fn,calibMatrix.Divider ); + + // MAIN LOOP - show some more points + getTouches(10,width,height,cursorx,cursory,CursorBuffer); - End(); // update picture - - - - // MAIN LOOP - while (left_count < 2) { // Loop until the left touch button pressed & released - // if the touch moved... - if (touch.x != cursorx || touch.y != cursory) { - restoreCursor(CursorBuffer); - cursorx = touch.x; - cursory = touch.y; - saveCursor(CursorBuffer, cursorx, cursory, width, height, CUR_SIZ); - circleCursor(cursorx, cursory, width, height, CUR_SIZ); - End(); // update picture - } - } - restoreCursor(CursorBuffer); // not strictly necessary as display will be closed + //restoreCursor(CursorBuffer); // not strictly necessary as display will be closed + //End(); vgDestroyImage(CursorBuffer); // tidy up memory finish(); // Graphics cleanup exit(0); From 438237fc0cc639fbc0ed39dddbcdd8e38f1d48e7 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Fri, 6 Sep 2019 05:22:17 +0000 Subject: [PATCH 35/52] fix merge conflicts --- calibrate.cpp | 7 ------- calibrate.h | 5 ----- util/tcCalib.cpp | 3 --- 3 files changed, 15 deletions(-) diff --git a/calibrate.cpp b/calibrate.cpp index 888884a..590e4d5 100755 --- a/calibrate.cpp +++ b/calibrate.cpp @@ -20,11 +20,7 @@ * * * -<<<<<<< HEAD - * File Name: calibrate.c -======= * File Name: calibrate.cpp ->>>>>>> e7d91bc2ac226b2820e0711076428a7b438d9748 * * * This file contains functions that implement calculations @@ -91,10 +87,7 @@ * resolution. * * -<<<<<<< HEAD -======= * Revisions: Kevin Peck 2019 ->>>>>>> e7d91bc2ac226b2820e0711076428a7b438d9748 */ diff --git a/calibrate.h b/calibrate.h index ef8a13d..2648b3a 100755 --- a/calibrate.h +++ b/calibrate.h @@ -75,12 +75,7 @@ typedef struct Point { -<<<<<<< HEAD - INT32 x, - y ; -======= INT32 x,y ; ->>>>>>> e7d91bc2ac226b2820e0711076428a7b438d9748 } POINT ; diff --git a/util/tcCalib.cpp b/util/tcCalib.cpp index c21383f..3a34c1b 100644 --- a/util/tcCalib.cpp +++ b/util/tcCalib.cpp @@ -81,10 +81,7 @@ void *eventThread(void *arg) { touch.y = touch.max_y / 2; while (1) { -<<<<<<< HEAD usleep(100); -======= ->>>>>>> e7d91bc2ac226b2820e0711076428a7b438d9748 do { count = read(touch.fd, (&touch.evbuff + ptr), sizeof(char[80])-ptr ); ptr = ptr + count; From 972a90858c30945ad925a580741c2e77c2a6c710 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Wed, 11 Sep 2019 07:12:40 -0400 Subject: [PATCH 36/52] much better response math issue still --- XPT2046.cpp | 10 +++---- util/tcCalib.cpp | 73 +++++++++++++++++++++++------------------------- 2 files changed, 40 insertions(+), 43 deletions(-) diff --git a/XPT2046.cpp b/XPT2046.cpp index 1231b06..e72c1f0 100644 --- a/XPT2046.cpp +++ b/XPT2046.cpp @@ -135,11 +135,11 @@ void XPT2046::read_touchscreen(bool interruptEnable) { if (z > 100) { - char output[30] = ""; - fd = open(tcfifo, O_WRONLY | O_NONBLOCK); - sprintf(output, "x:%d, y:%d, z:%d\n", x, y, z); - write(fd, output, strlen(output) + 1); - close(fd); + uint16_t words16Write[4] = { x, y, z, 0x0000FFFF}; + fd = open(tcfifo, O_WRONLY | O_NONBLOCK); + + write(fd, words16Write, 4 * sizeof(uint16_t)); + close(fd); this->lastTouchTick = tick(); } diff --git a/util/tcCalib.cpp b/util/tcCalib.cpp index 3a34c1b..db89ff6 100644 --- a/util/tcCalib.cpp +++ b/util/tcCalib.cpp @@ -47,20 +47,6 @@ typedef struct { char* evbuff[80]; VGfloat x, y, z; int max_x, max_y; - void parse(const char *buff) { - std::regex re( "x:(\\d+),\\s*y:(\\d+),\\s*z:(\\d+)\\s*" ) ; - std::cmatch match_results; - - x = 0; - y = 0; - z = 0; - - if( std::regex_match( (const char *)buff, match_results, re ) && match_results.size() >= 3 ) { - x = std::stoi(match_results[1].str()); - y = std::stoi(match_results[2].str()); - z = std::stoi(match_results[3].str()); - } - } } touch_t; touch_t touch; // global touch state @@ -70,28 +56,26 @@ int quitState = 0; // evenThread reads from the touch input file void *eventThread(void *arg) { - int count = 0, ptr = 0; +#define OPENDEVICE_TOUCH if ((touch.fd = open("/tmp/TCfifo", O_RDONLY)) < 0) { \ + fprintf(stderr, "Error opening touch!\n"); \ + quitState = 1; \ + return &quitState;} + // Open touch driver - if ((touch.fd = open("/tmp/TCfifo", O_RDONLY)) < 0) { - fprintf(stderr, "Error opening touch!\n"); - quitState = 1; - return &quitState; - } + OPENDEVICE_TOUCH touch.x = touch.max_x / 2; //Reset touch touch.y = touch.max_y / 2; while (1) { - usleep(100); - do { - count = read(touch.fd, (&touch.evbuff + ptr), sizeof(char[80])-ptr ); - ptr = ptr + count; - } while (ptr >0 && ((const char *)touch.evbuff)[ptr-1] != '\n'); - - fprintf(stdout, (const char *)touch.evbuff); - - touch.parse((const char *)touch.evbuff); - memset(touch.evbuff, 0, sizeof(char[80])); - count = 0, ptr = 0; + uint16_t words16read[4]; + usleep(500); + if(read(touch.fd, words16read, sizeof(uint16_t) * 4 )) { + touch.x = words16read[0]; + touch.y = words16read[1]; + touch.z = words16read[2]; + close(touch.fd); + OPENDEVICE_TOUCH + } } } @@ -157,16 +141,15 @@ void getTouches(int captures, int width, int height, int &cursorx, int &cursory, int ztouch_thold = 50; do { - usleep(100000); // usec - slow loop for other threads + usleep(10000); // usec - slow loop for other threads pointRaw.x = touch.x; pointRaw.y = touch.y; getDisplayPoint(&pointCorrected,&pointRaw,&calibMatrix); // Loop until a touch is registered by a change in cursor value if ((pointCorrected.x != cursorx || pointCorrected.y != cursory) && ((int)touch.z) > ztouch_thold ) { - fprintf(stdout,"%d != %d || %d != %d) && %d > %d\n", - pointCorrected.x, cursorx , pointCorrected.y, cursory, ((int)touch.z) , ztouch_thold); - + //fprintf(stdout,"%d != %d || %d != %d) && %d > %d\n", + // pointCorrected.x, cursorx , pointCorrected.y, cursory, ((int)touch.z) , ztouch_thold); restoreCursor(CursorBuffer); cursorx = pointCorrected.x; cursory = pointCorrected.y; @@ -227,19 +210,33 @@ int main() { ,calibMatrix.An,calibMatrix.Bn,calibMatrix.Cn,calibMatrix.Dn,calibMatrix.En,calibMatrix.Fn,calibMatrix.Divider ); // Draw lines - for( int i = 0; i < xHairLines_ROWS - 2; i=i+2) { + for( int i = 0; i < xHairLines_ROWS - 2; ) { drawCrosshair(width,height,i); getTouches(1,width,height,cursorx,cursory,CursorBuffer); fprintf(stdout,"p:%d %d, %d \r\n",i, cursorx,cursory); - /* + // save touch points pointTouches[i/2].x = cursorx; pointTouches[i/2].y = cursory; // Update for screen dimensions pointTargets[i/2].x = (pointTargets[i/2].x < 0 ? width + pointTargets[i/2].x : pointTargets[i/2].x); pointTargets[i/2].y = (pointTargets[i/2].y < 0 ? height + pointTargets[i/2].y : pointTargets[i/2].y); - */ + + // Conditionally increment to next sample point + switch(i) { + case 0: + i = i + 2 * ( (cursorx < width / 2) && (cursory < height / 2) ); + break; + case 2: + i = i + 2 * ( (cursorx > width / 2) && (cursory < height / 2) ); + break; + case 4: + i = i + 2 * ( (cursorx < width / 2) && (cursory > height / 2) ); + break; + default: + i = i + 2; + } } setCalibrationMatrix( (POINT*)&pointTargets, (POINT*)&pointTargets, &calibMatrix); // update matrix From 890148285fdb18da639b173c3bed561842db67ff Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Tue, 17 Sep 2019 23:37:47 -0400 Subject: [PATCH 37/52] calibration utillity working --- util/tcCalib.cpp | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/util/tcCalib.cpp b/util/tcCalib.cpp index db89ff6..b7eeae8 100644 --- a/util/tcCalib.cpp +++ b/util/tcCalib.cpp @@ -16,16 +16,17 @@ #include "calibrate.h" +#define POINT_SAMPLES 4 // Target on-screen points -POINT pointTargets[4] = { +POINT pointTargets[POINT_SAMPLES] = { {15, 15}, {-15, 15}, - {-15, -15}, - {15, -15} + {15, -15}, + {-15, -15} }; // Recorded touch points -POINT pointTouches[4]; +POINT pointTouches[POINT_SAMPLES]; MATRIX calibMatrix; @@ -144,12 +145,11 @@ void getTouches(int captures, int width, int height, int &cursorx, int &cursory, usleep(10000); // usec - slow loop for other threads pointRaw.x = touch.x; pointRaw.y = touch.y; - getDisplayPoint(&pointCorrected,&pointRaw,&calibMatrix); + getDisplayPoint((POINT *)&pointCorrected,(POINT *)&pointRaw,&calibMatrix); // Loop until a touch is registered by a change in cursor value if ((pointCorrected.x != cursorx || pointCorrected.y != cursory) && ((int)touch.z) > ztouch_thold ) { - //fprintf(stdout,"%d != %d || %d != %d) && %d > %d\n", - // pointCorrected.x, cursorx , pointCorrected.y, cursory, ((int)touch.z) , ztouch_thold); + fprintf(stdout,"actual (%d, %d) => corrected (%d, %d) \n", pointRaw.x, pointRaw.y, pointCorrected.x, pointCorrected.y); restoreCursor(CursorBuffer); cursorx = pointCorrected.x; cursory = pointCorrected.y; @@ -205,7 +205,12 @@ int main() { drawBackground(width,height); // Set curser values initially - setCalibrationMatrix( (POINT*)&pointTargets, (POINT*)&pointTargets, &calibMatrix); // initialized 1:1 matrix + // Update for screen dimensions + for(int i = 0; i (%d, %d) \r\n",i, pointTouches[i].x, pointTouches[i].y, pointTargets[i].x, pointTargets[i].y); + } + setCalibrationMatrix( (POINT*)pointTargets, (POINT*)pointTouches , &calibMatrix); // update matrix fprintf(stdout,"M: %d,%d,%d,%d,%d,%d,%d\r\n" ,calibMatrix.An,calibMatrix.Bn,calibMatrix.Cn,calibMatrix.Dn,calibMatrix.En,calibMatrix.Fn,calibMatrix.Divider ); // MAIN LOOP - show some more points - getTouches(10,width,height,cursorx,cursory,CursorBuffer); + getTouches(100,width,height,cursorx,cursory,CursorBuffer); //restoreCursor(CursorBuffer); // not strictly necessary as display will be closed //End(); From 6732e57d5553bcf936fe2cc35550378a6c8c5a0c Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Wed, 18 Sep 2019 01:49:38 -0400 Subject: [PATCH 38/52] add calibration in driver --- XPT2046.cpp | 61 +++++++------- XPT2046.h | 9 +- calibrate.h | 7 +- util/Makefile | 7 +- util/tcTest.cpp | 217 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 267 insertions(+), 34 deletions(-) create mode 100644 util/tcTest.cpp diff --git a/XPT2046.cpp b/XPT2046.cpp index e72c1f0..56e6ba8 100644 --- a/XPT2046.cpp +++ b/XPT2046.cpp @@ -23,9 +23,10 @@ */ #include "XPT2046.h" -extern "C" { - #include "calibrate.h" -} + +#include +#include + #define XPT2046_CFG_START 1<<7 #define XPT2046_CFG_MUX(v) ((v&0b111) << (4)) @@ -45,11 +46,13 @@ extern "C" { #define XPT2046_MUX_Z2 0b100 #define INTERRUPTDEC 10 +#define MAXLINE 128 XPT2046::XPT2046() { spi_cs = 0; z_average = 0; tcfifo = "/tmp/TCfifo"; + calibFile = "/etc/xpt2046.conf"; _maxValue = 0x0fff; _width = 480; @@ -81,6 +84,7 @@ XPT2046::XPT2046() { // Creating the named file(FIFO) // mkfifo(, ) mkfifo(tcfifo, 0666); + initCalibration(); } @@ -152,40 +156,37 @@ void XPT2046::setRotation(uint8_t m) { _rotation = m % 4; } -void XPT2046::setCalibration(uint16_t minX, uint16_t minY, uint16_t maxX, uint16_t maxY) { - _minX = minX; - _minY = minY; - _maxX = maxX; - _maxY = maxY; +void XPT2046::initCalibration() { + FILE *stream; + char *line = NULL; + int reti; + size_t len = 0; + ssize_t nread; + + stream = fopen(calibFile, "r"); + if(stream != NULL) + { + while ((nread = getline(&line, &len, stream)) != -1) { + reti = sscanf((const char*)line,"%l,%l,%l,%l,%l,%l,%l", + calib.An, calib.Bn, calib.Cn, calib.Dn, calib.En, calib.Fn); + } + free(line); + fclose(stream); + } } + void XPT2046::read(uint16_t * oX, uint16_t * oY, uint16_t * oZ) { uint16_t x, y; + POINT pointCorrected, pointRaw; readRaw(&x, &y, oZ); - uint32_t cX = x; - uint32_t cY = y; - - if(cX < _minX) { - cX = 0; - } else if(cX > _maxX) { - cX = _width; - } else { - cX -= _minX; - cX = ((cX << 8) / (((_maxX - _minX) << 8) / (_width << 8)) )>> 8; - } - - if(cY < _minY) { - cY = 0; - } else if(cY > _maxY) { - cY = _height; - } else { - cY -= _minY; - cY = ((cY << 8) / (((_maxY - _minY) << 8) / (_height << 8))) >> 8; - } + pointRaw.x = x; + pointRaw.y = y; + getDisplayPoint((POINT *)&pointCorrected,(POINT *)&pointRaw,&calib); - *oX = cX; - *oY = cY; + *oX = pointCorrected.x; + *oY = pointCorrected.y; *oZ = std::max(0,4096 - (int)(*oZ)); } diff --git a/XPT2046.h b/XPT2046.h index bf2dcdd..1da4c9f 100644 --- a/XPT2046.h +++ b/XPT2046.h @@ -38,6 +38,8 @@ #include #include "spi_user.h" +#include "calibrate.h" + class XPT2046 { public: @@ -51,7 +53,7 @@ class XPT2046 { void read_touchscreen(bool interruptEnable); void setRotation(uint8_t m); - void setCalibration(uint16_t minX, uint16_t minY, uint16_t maxX, uint16_t maxY); + void initCalibration(); void read(uint16_t * oX, uint16_t * oY, uint16_t * oZ); void readRaw(uint16_t * oX, uint16_t * oY, uint16_t * oZ); @@ -104,8 +106,11 @@ class XPT2046 { uint32_t spi_cs ; uint32_t z_average ; - + + MATRIX calib; + const char * tcfifo ; + const char *calibFile ; int interruptpoll ; }; diff --git a/calibrate.h b/calibrate.h index 2648b3a..ad34618 100755 --- a/calibrate.h +++ b/calibrate.h @@ -27,7 +27,9 @@ * * Revisions: Kevin Peck 2019 */ - +#ifdef __cplusplus +//extern "C" { +#endif #ifndef _CALIBRATE_H_ #define _CALIBRATE_H_ @@ -115,3 +117,6 @@ EXTERN int getDisplayPoint( POINT * display, #endif /* _CALIBRATE_H_ */ +#ifdef __cplusplus +//} +#endif diff --git a/util/Makefile b/util/Makefile index f36d7c4..dd44a96 100644 --- a/util/Makefile +++ b/util/Makefile @@ -2,8 +2,9 @@ LIBFLAGS=-L/opt/vc/lib -lEGL -lGLESv2 -lbcm_host -lpthread -ljpeg -lshapes CXXFLAGS=-DDEBUG -fno-exceptions -DPI -std=gnu++11 -I/opt/vc/include -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/interface/vcos/pthreads -I.. objs=tcCalib.o ../calibrate.o +objs2=tcTest.o -all: tcCalib +all: tcCalib tcTest clean: $(objs) rm $(objs) @@ -15,3 +16,7 @@ clean: $(objs) tcCalib: $(objs) g++ -Wall $(CXXFLAGS) $(LIBFLAGS) $(objs) -o tcCalib +tcTest: $(objs2) + g++ -Wall $(CXXFLAGS) $(LIBFLAGS) $(objs) -o tcTest + + diff --git a/util/tcTest.cpp b/util/tcTest.cpp new file mode 100644 index 0000000..8478ea9 --- /dev/null +++ b/util/tcTest.cpp @@ -0,0 +1,217 @@ + + +#include +#include +#include +#include + +#include "VG/openvg.h" +#include "VG/vgu.h" +#include "fontinfo.h" +#include "shapes.h" + +#include +#include +#include + +#define xHairLines_ROWS 8 +int xHairLines[8][4] = { + 10, 15, 20, 15, // lower left + 15, 10, 15, 20, + -20, 15, -10, 15, // lower right + -15, 10, -15, 20, + 10, -15, 20, -15, // upper left + 15, -10, 15, -20, + -20, -15, -10, -15, // upper right + -15, -10, -15, -20 +}; + +// touch state structure +typedef struct { + int fd; + char* evbuff[80]; + VGfloat x, y, z; + int max_x, max_y; +} touch_t; + +touch_t touch; // global touch state +int left_count = 0; +int quitState = 0; +#define CUR_SIZ 16 // cursor size, pixels beyond centre dot + +// evenThread reads from the touch input file +void *eventThread(void *arg) { +#define OPENDEVICE_TOUCH if ((touch.fd = open("/tmp/TCfifo", O_RDONLY)) < 0) { \ + fprintf(stderr, "Error opening touch!\n"); \ + quitState = 1; \ + return &quitState;} + + // Open touch driver + OPENDEVICE_TOUCH + touch.x = touch.max_x / 2; //Reset touch + touch.y = touch.max_y / 2; + + while (1) { + uint16_t words16read[4]; + usleep(500); + if(read(touch.fd, words16read, sizeof(uint16_t) * 4 )) { + touch.x = words16read[0]; + touch.y = words16read[1]; + touch.z = words16read[2]; + close(touch.fd); + OPENDEVICE_TOUCH + } + } +} + +static int cur_sx, cur_sy, cur_w, cur_h; // cursor location and dimensions +static int cur_saved = 0; // amount of data saved in cursor image backup + +// saveCursor saves the pixels under the touch cursor +void saveCursor(VGImage CursorBuffer, int curx, int cury, int screen_width, int screen_height, int s) { + int sx, sy, ex, ey; + + sx = curx - s; // horizontal + if (sx < 0) { + sx = 0; + } + ex = curx + s; + if (ex > screen_width) { + ex = screen_width; + } + cur_sx = sx; + cur_w = ex - sx; + + sy = cury - s; // vertical + if (sy < 0) { + sy = 0; + } + ey = cury + s; + if (ey > screen_height) { + ey = screen_height; + } + cur_sy = sy; + cur_h = ey - sy; + + vgGetPixels(CursorBuffer, 0, 0, cur_sx, cur_sy, cur_w, cur_h); + cur_saved = cur_w * cur_h; +} + +// restoreCursor restores the pixels under the touch cursor +void restoreCursor(VGImage CursorBuffer) { + if (cur_saved != 0) { + vgSetPixels(cur_sx, cur_sy, CursorBuffer, 0, 0, cur_w, cur_h); + } +} + +// circleCursor draws a translucent circle as the touch cursor +void circleCursor(int curx, int cury, int width, int height, int s) { + Fill(100, 0, 0, 0.50); + Circle(curx, cury, s); + Fill(0, 0, 0, 1); + Circle(curx, cury, 2); +} + +// touchinit starts the touch event thread +int touchinit(int w, int h) { + pthread_t inputThread; + touch.max_x = w; + touch.max_y = h; + return pthread_create(&inputThread, NULL, &eventThread, NULL); +} + +void getTouches(int captures, int width, int height, int &cursorx, int &cursory, VGImage &CursorBuffer) { + int changeCount = 0; + int ztouch_thold = 50; + + do { + usleep(10000); // usec - slow loop for other threads + + // Loop until a touch is registered by a change in cursor value + if ((touch.x != cursorx || touch.y != cursory) && ((int)touch.z) > ztouch_thold ) { + restoreCursor(CursorBuffer); + cursorx = touch.x; + cursory = touch.y; + saveCursor(CursorBuffer, cursorx, cursory, width, height, CUR_SIZ); + circleCursor(cursorx, cursory, width, height, CUR_SIZ); + changeCount = changeCount + 1; + touch.z = 0.0; + End(); // update picture + } + } while (changeCount < captures); +} + +void drawBackground(int width, int height) { + Background(0, 0, 0); // Black background + Fill(44, 77, 232, 1); // Big blue marble + Circle(width / 2, 0, width); // The "world" + Fill(255, 255, 255, 1); // White text + TextMid(width / 2, height / 2, "Screen Calibration", SerifTypeface, width / 15); // Greetings + End(); // update picture +} + +void drawCrosshair(int width, int height, int i) { + Stroke(255, 255, 255, 0.5); + StrokeWidth(2); + Line( ( xHairLines[i][0] < 0 ? width + xHairLines[i][0] : xHairLines[i][0] ) + , ( xHairLines[i][1] < 0 ? height + xHairLines[i][1] : xHairLines[i][1] ) + , ( xHairLines[i][2] < 0 ? width + xHairLines[i][2] : xHairLines[i][2] ) + , ( xHairLines[i][3] < 0 ? height + xHairLines[i][3] : xHairLines[i][3] ) + ); + + Line( ( xHairLines[i+1][0] < 0 ? width + xHairLines[i+1][0] : xHairLines[i+1][0] ) + , ( xHairLines[i+1][1] < 0 ? height + xHairLines[i+1][1] : xHairLines[i+1][1] ) + , ( xHairLines[i+1][2] < 0 ? width + xHairLines[i+1][2] : xHairLines[i+1][2] ) + , ( xHairLines[i+1][3] < 0 ? height + xHairLines[i+1][3] : xHairLines[i+1][3] ) + ); + End(); // update picture +} + +int main() { + int width, height, cursorx, cursory, cbsize; + + init(&width, &height); // Graphics initialization + cursorx = width / 2; + cursory = height / 2; + cbsize = (CUR_SIZ * 2) + 1; + VGImage CursorBuffer = vgCreateImage(VG_sABGR_8888, cbsize, cbsize, VG_IMAGE_QUALITY_BETTER); + + if (touchinit(width, height) != 0) { + fprintf(stderr, "Unable to initialize the touch\n"); + exit(1); + } + Start(width, height); // Start the picture + drawBackground(width,height); + + // Draw lines + for( int i = 0; i < xHairLines_ROWS - 2; ) { + drawCrosshair(width,height,i); + + getTouches(1,width,height,cursorx,cursory,CursorBuffer); + //fprintf(stdout,"p:%d %d, %d \r\n",i, cursorx,cursory); + + // Conditionally increment to next sample point + switch(i) { + case 0: + i = i + 2 * ( (cursorx < width / 2) && (cursory < height / 2) ); + break; + case 2: + i = i + 2 * ( (cursorx > width / 2) && (cursory < height / 2) ); + break; + case 4: + i = i + 2 * ( (cursorx < width / 2) && (cursory > height / 2) ); + break; + default: + i = i + 2; + } + } + + // MAIN LOOP - show some more points + getTouches(100,width,height,cursorx,cursory,CursorBuffer); + + //restoreCursor(CursorBuffer); // not strictly necessary as display will be closed + //End(); + vgDestroyImage(CursorBuffer); // tidy up memory + finish(); // Graphics cleanup + exit(0); +} From 5549b5e53427a4fb2e8f0622f3f313c19d704f8d Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Sat, 21 Sep 2019 23:17:13 -0400 Subject: [PATCH 39/52] calibration now in driver --- XPT2046.cpp | 48 +++++++++++++++++++++++++++++++++++++++++++----- util/Makefile | 2 +- util/tcTest.cpp | 1 + 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/XPT2046.cpp b/XPT2046.cpp index 56e6ba8..3c37d73 100644 --- a/XPT2046.cpp +++ b/XPT2046.cpp @@ -167,27 +167,65 @@ void XPT2046::initCalibration() { if(stream != NULL) { while ((nread = getline(&line, &len, stream)) != -1) { - reti = sscanf((const char*)line,"%l,%l,%l,%l,%l,%l,%l", - calib.An, calib.Bn, calib.Cn, calib.Dn, calib.En, calib.Fn); + //fprintf(stdout,"scanline: '%s'", line); + reti = sscanf((const char*)line,"%d,%d,%d,%d,%d,%d,%d", + &calib.An, &calib.Bn, &calib.Cn, &calib.Dn, &calib.En, &calib.Fn,&calib.Divider); } free(line); fclose(stream); + } else { // Make standard identity calibration -- no translation + // Target on-screen points + POINT pointTargets[3] = { + {15, 15}, + {-15, 15}, + {15, -15}, + }; + // Set curser values initially + // Update for screen dimensions + for(int i = 0; i<3;i++ ) { + pointTargets[i].x = (pointTargets[i].x < 0 ? _width + pointTargets[i].x : pointTargets[i].x); + pointTargets[i].y = (pointTargets[i].y < 0 ? _height + pointTargets[i].y : pointTargets[i].y); + fprintf(stdout, "%d,%d\n", pointTargets[i].x, pointTargets[i].y); + } + setCalibrationMatrix( (POINT*)pointTargets, (POINT*)pointTargets, &calib); // initialized 1:1 matrix } + fprintf(stdout,"M: %d,%d,%d,%d,%d,%d,%d\r\n" + ,calib.An,calib.Bn,calib.Cn,calib.Dn,calib.En,calib.Fn,calib.Divider ); } void XPT2046::read(uint16_t * oX, uint16_t * oY, uint16_t * oZ) { uint16_t x, y; POINT pointCorrected, pointRaw; + readRaw(&x, &y, oZ); - + pointRaw.x = x; - pointRaw.y = y; + pointRaw.y = y; + + if(pointRaw.x < _minX) { + pointRaw.x = 0; + } else if(pointRaw.x > _maxX) { + pointRaw.x = _width; + } else { + pointRaw.x -= _minX; + pointRaw.x = ((pointRaw.x << 8) / (((_maxX - _minX) << 8) / (_width << 8)) )>> 8; + } + + if(pointRaw.y < _minY) { + pointRaw.y = 0; + } else if(pointRaw.y > _maxY) { + pointRaw.y = _height; + } else { + pointRaw.y -= _minY; + pointRaw.y = ((pointRaw.y << 8) / (((_maxY - _minY) << 8) / (_height << 8))) >> 8; + } + getDisplayPoint((POINT *)&pointCorrected,(POINT *)&pointRaw,&calib); *oX = pointCorrected.x; *oY = pointCorrected.y; - *oZ = std::max(0,4096 - (int)(*oZ)); + *oZ = std::max(0,4096 - (int)(*oZ)); } void XPT2046::readRaw(uint16_t * oX, uint16_t * oY, uint16_t * oZ) { diff --git a/util/Makefile b/util/Makefile index dd44a96..b8b05e9 100644 --- a/util/Makefile +++ b/util/Makefile @@ -17,6 +17,6 @@ tcCalib: $(objs) g++ -Wall $(CXXFLAGS) $(LIBFLAGS) $(objs) -o tcCalib tcTest: $(objs2) - g++ -Wall $(CXXFLAGS) $(LIBFLAGS) $(objs) -o tcTest + g++ -Wall $(CXXFLAGS) $(LIBFLAGS) $(objs2) -o tcTest diff --git a/util/tcTest.cpp b/util/tcTest.cpp index 8478ea9..74428ce 100644 --- a/util/tcTest.cpp +++ b/util/tcTest.cpp @@ -129,6 +129,7 @@ void getTouches(int captures, int width, int height, int &cursorx, int &cursory, // Loop until a touch is registered by a change in cursor value if ((touch.x != cursorx || touch.y != cursory) && ((int)touch.z) > ztouch_thold ) { + fprintf(stdout,"corrected (%f, %f) \n", touch.x, touch.y); restoreCursor(CursorBuffer); cursorx = touch.x; cursory = touch.y; From f83855c4cd733d64c656afdf3c161e106609a0b2 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Sat, 21 Sep 2019 23:26:33 -0400 Subject: [PATCH 40/52] calibration now in driver, readme update --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index afcbc1d..aa546b1 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ cd ../kernel cd ../build tail -f /tmp/TCfifo & sudo ./fbcp-ili9341 & - +``` Note especially the two dots `..` on the CMake line, which denote "up one directory" in this case (instead of referring to "more items go here"). @@ -270,6 +270,10 @@ On the other hand, it is desirable to control how much CPU time fbcp-ili9341 is - In `display.h` there is an option `#define TARGET_FRAME_RATE `. Setting this to a smaller value, such as 30, will trade refresh rate to reduce CPU consumption. +### Configuring touch screen and calibration + +In the 'util' folder, there is a calibration utility and a test utillity that assist with determining the 7 matrix parameters for calibration. Wth the tcCalibration utillity, you touch three points and the values are output to the console. These seven values are placed within a one-line file of just comma separated numbers and named '/etc/xpt2046.conf'. The ctTest program is a demo program showing cursor tracking with the pointing device on based on the driver-calibrated values. + ### About Input Latency A pleasing aspect of fbcp-ili9341 is that it introduces very little latency overhead: on a 119Hz refreshing ILI9341 display, [fbcp-ili9341 gets pixels as response from GPIO input to screen in well less than 16.66 msecs](https://www.youtube.com/watch?v=EOICdpjiqv8) time. I only have a 120fps recording camera, so can't easily measure delays shorter than that, but rough statistical estimate of slow motion video footage suggests this delay could be as low as 2-3 msecs, dominated by the ~8.4msecs panel refresh rate of the ILI9341. From fad48e45e636082ea9f926138dcc2d024c9d453a Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Wed, 25 Sep 2019 23:42:40 -0400 Subject: [PATCH 41/52] calibration utillity complete --- README.md | 2 +- fbcp-ili9341.cpp | 3 +- mpi3501.cpp | 30 +++++++----- util/tcCalib.cpp | 117 +++++++++++++++++++++++++++++++++++++++++++++-- util/tcTest.cpp | 3 +- 5 files changed, 134 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index aa546b1..15fbd08 100644 --- a/README.md +++ b/README.md @@ -272,7 +272,7 @@ On the other hand, it is desirable to control how much CPU time fbcp-ili9341 is ### Configuring touch screen and calibration -In the 'util' folder, there is a calibration utility and a test utillity that assist with determining the 7 matrix parameters for calibration. Wth the tcCalibration utillity, you touch three points and the values are output to the console. These seven values are placed within a one-line file of just comma separated numbers and named '/etc/xpt2046.conf'. The ctTest program is a demo program showing cursor tracking with the pointing device on based on the driver-calibrated values. +In the 'util' folder, there is a calibration utility and a test utillity that assist with determining the 7 matrix parameters for calibration. Wth the tcCalibration utillity, the program on startup, deletes the config file ('/etc/xpt2046.config' -- it needs permission to do this), sends a USER1 signal to the display driver to signal it to re-load the driver (it won't find it and will therefore use a 1 to 1 mapping), you touch the three points indicated, the new matrix is written to the config file location, and another signal is sent to the driver to re-load the configuration. Your touch display is now calibrated. The ctTest program is a demo program showing cursor tracking with the pointing device based on the driver-calibrated values. ### About Input Latency diff --git a/fbcp-ili9341.cpp b/fbcp-ili9341.cpp index d3256cc..be5191f 100644 --- a/fbcp-ili9341.cpp +++ b/fbcp-ili9341.cpp @@ -92,7 +92,8 @@ int main() { signal(SIGINT, ProgramInterruptHandler); signal(SIGQUIT, ProgramInterruptHandler); - signal(SIGUSR1, ProgramInterruptHandler); + // This signal is used by XPT2046 class as signal to re-load config file + //signal(SIGUSR1, ProgramInterruptHandler); signal(SIGUSR2, ProgramInterruptHandler); signal(SIGTERM, ProgramInterruptHandler); diff --git a/mpi3501.cpp b/mpi3501.cpp index 6d3c6d7..5348625 100644 --- a/mpi3501.cpp +++ b/mpi3501.cpp @@ -7,7 +7,7 @@ #include #include - +#include #include XPT2046 touch; @@ -21,6 +21,10 @@ bool activeTouchscreen() { return (touch.ticksSinceLastTouch() < TURN_DISPLAY_OFF_AFTER_USECS_OF_INACTIVITY); } +void ReloadCalibration(int signal) { + touch.initCalibration(); +} + void ChipSelectHigh() { WAIT_SPI_FINISHED(); @@ -42,8 +46,12 @@ void ChipSelectHigh() void InitKeDeiV63() { - // output device - touch = XPT2046(); + // output device + touch = XPT2046(); + + //Register for system signal + signal(SIGUSR1, ReloadCalibration); + touch.setRotation(0); #ifdef DISPLAY_ROTATE_180_DEGREES @@ -131,7 +139,7 @@ void InitKeDeiV63() SPI_TRANSFER(DISPLAY_MADCTL, 0x00, madctl); SPI_TRANSFER(DSPLAY_COLMOD, 0x00, 0x55); - SPI_TRANSFER(DISPLAY_WRCABC,0x00,0x01); // 00 - default, 01 - UI, 02 - still pic, 03 - video + SPI_TRANSFER(DISPLAY_WRCABC,0x00,0x00); // 00 - default, 01 - UI, 02 - still pic, 03 - video //SPI_TRANSFER(DISPLAY_IDMON); // Increases brightness+contrast //SPI_TRANSFER(DISPLAY_IDMOFF); // Darker but more accurate colour SPI_TRANSFER(DISPLAY_TESL, 0x00, 0x00, 0x00, 0x01); @@ -159,18 +167,20 @@ void InitKeDeiV63() void TurnBacklightOff() { + // TODO: Reference data sheet (another similar device) isn't accurate for these settings + SPI_TRANSFER(DISPLAY_WRCTRLD,0x00,0x08); // 5bit-Backlight control is DISPLAY_WRDISBV, 3bit-Display dimming On, 2bit-Backliht Off + SPI_TRANSFER(DISPLAY_WRDISBV,0x00,0x7F); } void TurnBacklightOn() { + // TODO: Reference data sheet (another similar device) isn't accurate for these settings + SPI_TRANSFER(DISPLAY_WRCTRLD,0x00,0x0C); // 5bit-Backlight control is DISPLAY_WRDISBV, 3bit-Display dimming, 2bit-Backliht + SPI_TRANSFER(DISPLAY_WRDISBV,0x00,0x7F); } void TurnDisplayOff() { - // TODO: Reference data sheet (another similar device) isn't accurate for these settings - SPI_TRANSFER(DISPLAY_WRCTRLD,0x00,0x2C); // 5-Backlight contro, 3-Display dimming, 2-BacklihtOff - SPI_TRANSFER(DISPLAY_WRDISBV,0x00,0x00); - SPI_TRANSFER(DISPLAY_OFF); // Works but whith backlight on, it goes white -- a good 'light' SPI_TRANSFER(DISPLAY_SLPIN); usleep(120*1000); @@ -181,10 +191,6 @@ void TurnDisplayOn() SPI_TRANSFER(DISPLAY_SLPOUT); usleep(120*1000); SPI_TRANSFER(DISPLAY_ON); - - // TODO: Reference data sheet (another similar device) isn't accurate for these settings - SPI_TRANSFER(DISPLAY_WRCTRLD,0x00,0x00); // 5-Backlight contro, 3-Display dimming, 2-BacklihtON - SPI_TRANSFER(DISPLAY_WRDISBV,0x00,0xFF); } void DeinitSPIDisplay() diff --git a/util/tcCalib.cpp b/util/tcCalib.cpp index b7eeae8..92c779c 100644 --- a/util/tcCalib.cpp +++ b/util/tcCalib.cpp @@ -4,6 +4,9 @@ #include #include #include +#include +#include +#include #include "VG/openvg.h" #include "VG/vgu.h" @@ -16,6 +19,10 @@ #include "calibrate.h" +#define FILE_COFIG (const char *)"/etc/xpt2046.conf" +#define PROC_NAME (char *)"fbcp-ili9341" +#define READ_BUF_SIZE 1024 + #define POINT_SAMPLES 4 // Target on-screen points POINT pointTargets[POINT_SAMPLES] = { @@ -188,6 +195,102 @@ void drawCrosshair(int width, int height, int i) { End(); // update picture } +/* This part of code is originated from busybox library + A memory location must be free() on return + */ +static pid_t *get_pid_by_name(char *process, int *len = 0) +{ + DIR *dir; + struct dirent *next; + pid_t* pidList = NULL; + int i = 0; + char *pidName; + + pidName = process; + + dir = opendir("/proc"); + if (!dir) + printf("Cannot open /proc"); + + while ((next = readdir(dir)) != NULL) + { + FILE *status; + char filename[READ_BUF_SIZE]; + char buffer[READ_BUF_SIZE]; + char name[READ_BUF_SIZE]; + + /* Must skip ".." since that is outside /proc */ + if (strcmp(next->d_name, "..") == 0) + continue; + + /* If it isn't a number, we don't want it */ + if (!isdigit(*next->d_name)) + continue; + + sprintf(filename, "/proc/%s/status", next->d_name); + if (! (status = fopen(filename, "r")) ) + { + continue; + } + if (fgets(buffer, READ_BUF_SIZE-1, status) == NULL) + { + fclose(status); + continue; + } + fclose(status); + + /* Buffer should contain a string like "Name: binary_name" */ + sscanf(buffer, "%*s %s", name); + if (strcmp(name, pidName) == 0) + { + pidList=(pid_t *)realloc( pidList, sizeof(pid_t) * (i+2)); + pidList[i++]=(pid_t)strtol(next->d_name, NULL, 0); + } + } + if (pidList) + { + pidList[i]=0; + *len = i; + } + else + { + pidList=(pid_t *)realloc( pidList, sizeof(pid_t)); + pidList[0]=(pid_t)-1; + *len = 1; + } + return pidList; +} + +void reloadScreenConfig() { + pid_t *pid; + int i, icnt; + pid = get_pid_by_name(PROC_NAME,&icnt); + for(i=0; i<1; i++) // Send signal to only first instance of process (the parent) + { + fprintf(stdout, "SIGUSR1 to %d\n",pid[i]); + kill(pid[i],SIGUSR1); + } + free(pid); +} + +void writeConfig(const char *fname, MATRIX *config) { + FILE *stream; + char *line = NULL; + int reti; + size_t len = 0; + ssize_t nread; + + stream = fopen(fname, "w"); + if(stream != NULL) + { + fprintf(stream,"%d,%d,%d,%d,%d,%d,%d\n", + config->An, config->Bn, config->Cn, config->Dn, config->En, config->Fn,config->Divider); + } + free(line); + fclose(stream); +} + + int main() { int width, height, cursorx, cursory, cbsize; @@ -204,6 +307,10 @@ int main() { Start(width, height); // Start the picture drawBackground(width,height); + // Remove driver config, trigger reload on display + remove(FILE_COFIG); + reloadScreenConfig(); + // Set curser values initially // Update for screen dimensions for(int i = 0; i ztouch_thold ) { - fprintf(stdout,"corrected (%f, %f) \n", touch.x, touch.y); restoreCursor(CursorBuffer); cursorx = touch.x; cursory = touch.y; @@ -147,7 +146,7 @@ void drawBackground(int width, int height) { Fill(44, 77, 232, 1); // Big blue marble Circle(width / 2, 0, width); // The "world" Fill(255, 255, 255, 1); // White text - TextMid(width / 2, height / 2, "Screen Calibration", SerifTypeface, width / 15); // Greetings + TextMid(width / 2, height / 2, "Screen touch test", SerifTypeface, width / 15); // Greetings End(); // update picture } From 6f2f007c9f0b9f7cfc7339c1d752cc5f2a997c69 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Thu, 26 Sep 2019 00:01:48 -0400 Subject: [PATCH 42/52] type-o fix in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 15fbd08..ef867b1 100644 --- a/README.md +++ b/README.md @@ -272,7 +272,7 @@ On the other hand, it is desirable to control how much CPU time fbcp-ili9341 is ### Configuring touch screen and calibration -In the 'util' folder, there is a calibration utility and a test utillity that assist with determining the 7 matrix parameters for calibration. Wth the tcCalibration utillity, the program on startup, deletes the config file ('/etc/xpt2046.config' -- it needs permission to do this), sends a USER1 signal to the display driver to signal it to re-load the driver (it won't find it and will therefore use a 1 to 1 mapping), you touch the three points indicated, the new matrix is written to the config file location, and another signal is sent to the driver to re-load the configuration. Your touch display is now calibrated. The ctTest program is a demo program showing cursor tracking with the pointing device based on the driver-calibrated values. +In the 'util' folder, there is a calibration utility and a test utillity that assist with determining the 7 matrix parameters for calibration. Wth the tcCalibration utillity, the program on startup, deletes the config file ('/etc/xpt2046.config' -- it needs permission to do this), sends a USER1 signal to the display driver to signal it to re-load the driver config (it won't find it and will therefore use a 1 to 1 mapping), you touch the three points indicated, the new matrix is written to the config file location, and another signal is sent to the driver to re-load the configuration. Your touch display is now calibrated. The ctTest program is a demo program showing cursor tracking with the pointing device based on the driver-calibrated values. ### About Input Latency From ded94656208c0877a19a034a40a81735da109b4e Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Sun, 29 Sep 2019 21:33:15 -0400 Subject: [PATCH 43/52] done with backlight --- mpi3501.cpp | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/mpi3501.cpp b/mpi3501.cpp index 5348625..0e17cd6 100644 --- a/mpi3501.cpp +++ b/mpi3501.cpp @@ -143,6 +143,7 @@ void InitKeDeiV63() //SPI_TRANSFER(DISPLAY_IDMON); // Increases brightness+contrast //SPI_TRANSFER(DISPLAY_IDMOFF); // Darker but more accurate colour SPI_TRANSFER(DISPLAY_TESL, 0x00, 0x00, 0x00, 0x01); + SPI_TRANSFER(DISPLAY_GETICID, 0x00, 0x07, 0x00, 0x07, 0x00, 0x1D, 0x00, 0x03); // ? SPI_TRANSFER(0xD1001100, 0x00, 0x03, 0x00, 0x30, 0x00, 0x10); // ? SPI_TRANSFER(0xD2001100, 0x00, 0x03, 0x00, 0x14, 0x00, 0x04); // ? @@ -167,30 +168,35 @@ void InitKeDeiV63() void TurnBacklightOff() { - // TODO: Reference data sheet (another similar device) isn't accurate for these settings - SPI_TRANSFER(DISPLAY_WRCTRLD,0x00,0x08); // 5bit-Backlight control is DISPLAY_WRDISBV, 3bit-Display dimming On, 2bit-Backliht Off - SPI_TRANSFER(DISPLAY_WRDISBV,0x00,0x7F); } void TurnBacklightOn() { - // TODO: Reference data sheet (another similar device) isn't accurate for these settings - SPI_TRANSFER(DISPLAY_WRCTRLD,0x00,0x0C); // 5bit-Backlight control is DISPLAY_WRDISBV, 3bit-Display dimming, 2bit-Backliht - SPI_TRANSFER(DISPLAY_WRDISBV,0x00,0x7F); } void TurnDisplayOff() { - SPI_TRANSFER(DISPLAY_OFF); // Works but whith backlight on, it goes white -- a good 'light' - SPI_TRANSFER(DISPLAY_SLPIN); - usleep(120*1000); + // These settings appear to to work as per data sheet + SPI_TRANSFER(DISPLAY_WRCTRLD,0x00,0x10); // 5bit-Backlight control is DISPLAY_WRDISBV, 3bit-Display dimming off, 2bit-Backliht Off + SPI_TRANSFER(DISPLAY_WRDISBV,0x00,0x00); + + // This does turn display off but you end up with white screen as backlight remains on + +// SPI_TRANSFER(DISPLAY_OFF); // Works but whith backlight on, it goes white -- a good 'light' +// SPI_TRANSFER(DISPLAY_SLPIN); +// usleep(120*1000); } void TurnDisplayOn() { - SPI_TRANSFER(DISPLAY_SLPOUT); - usleep(120*1000); - SPI_TRANSFER(DISPLAY_ON); + // These settings appear to to work as per data sheet + SPI_TRANSFER(DISPLAY_WRCTRLD,0x00,0x1C); // 5bit-Backlight control is DISPLAY_WRDISBV, 3bit-Display dimming on, 2bit-Backliht on + SPI_TRANSFER(DISPLAY_WRDISBV,0x00,0xFF); + + // This does turn on display, but no need if you're not turning it off (TurnDisplayOff()) +// SPI_TRANSFER(DISPLAY_SLPOUT); +// usleep(120*1000); +// SPI_TRANSFER(DISPLAY_ON); } void DeinitSPIDisplay() From 3b950b85870f371f853443e192aed4a5004e0017 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Wed, 2 Oct 2019 00:03:06 -0400 Subject: [PATCH 44/52] update Kedei comments --- README.md | 203 +++++++++++++++++++++++++++--------------------------- 1 file changed, 102 insertions(+), 101 deletions(-) diff --git a/README.md b/README.md index ef867b1..b598049 100644 --- a/README.md +++ b/README.md @@ -5,33 +5,33 @@ This repository implements a driver for certain SPI-based LCD displays for Raspb ![PiTFT display](/example.jpg "Adafruit PiTFT 2.8 with ILI9341 controller") The work was motivated by curiosity after seeing this series of videos on the RetroManCave YouTube channel: - - [RetroManCave: Waveshare 3.5" Raspberry Pi Screen | Review](https://www.youtube.com/watch?v=SGMC0t33C50) - - [RetroManCave: Waveshare 3.2" vs 3.5" LCD screen gaming test | Raspberry Pi / RetroPie](https://www.youtube.com/watch?v=8bazEcXemiA) - - [Elecrow 5 Inch LCD Review | RetroPie & Raspberry Pi](https://www.youtube.com/watch?v=8VgNBDMOssg) +- [RetroManCave: Waveshare 3.5" Raspberry Pi Screen | Review](https://www.youtube.com/watch?v=SGMC0t33C50) +- [RetroManCave: Waveshare 3.2" vs 3.5" LCD screen gaming test | Raspberry Pi / RetroPie](https://www.youtube.com/watch?v=8bazEcXemiA) +- [Elecrow 5 Inch LCD Review | RetroPie & Raspberry Pi](https://www.youtube.com/watch?v=8VgNBDMOssg) In these videos, the SPI (GPIO) bus is referred to being the bottleneck. SPI based displays update over a serial data bus, transmitting one bit per clock cycle on the bus. A 320x240x16bpp display hence requires a SPI bus clock rate of 73.728MHz to achieve a full 60fps refresh frequency. Not many SPI LCD controllers can communicate this fast in practice, but are constrained to e.g. a 16-50MHz SPI bus clock speed, capping the maximum update rate significantly. Can we do anything about this? The fbcp-ili9341 project started out as a display driver for the [Adafruit 2.8" 320x240 TFT w/ Touch screen for Raspberry Pi](https://www.adafruit.com/product/1601) display that utilizes the ILI9341 controller. On that display, fbcp-ili9341 can achieve a 60fps update rate, depending on the content that is being displayed. Check out these videos for examples of the driver in action: - - [fbcp-ili9341 frame delivery smoothness test on Pi 3B and Adafruit ILI9341 at 119Hz](https://youtu.be/IqzKT33Rwjc) - - [Latency and tearing test #2: GPIO input to display latency in fbcp-ili9341 and tearing modes](https://www.youtube.com/watch?v=EOICdpjiqv8) - - [Latency and tearing test: KeDei 3.5" 320x480 HDMI vs Adafruit 2.8" PiTFT ILI9341 240x320 SPI](https://www.youtube.com/watch?v=1yvmvv0KtNs) - - [fbcp-ili9341 ported to ILI9486 WaveShare 3.5" (B) SpotPear 320x480 SPI display](https://www.youtube.com/watch?v=dqOLIHOjLq4) - - [Quake 60 fps inside Gameboy Advance (ILI9341)](https://www.youtube.com/watch?v=xmO8t3XlxVM) - - First implementation of a statistics overlay: [fbcp-ili9341 SPI display driver on Adafruit PiTFT 2.8"](http://youtu.be/rKSH048XRjA) - - Initial proof of concept video: [fbcp-ili9341 driver first demo](https://youtu.be/h1jhuR-oZm0) +- [fbcp-ili9341 frame delivery smoothness test on Pi 3B and Adafruit ILI9341 at 119Hz](https://youtu.be/IqzKT33Rwjc) +- [Latency and tearing test #2: GPIO input to display latency in fbcp-ili9341 and tearing modes](https://www.youtube.com/watch?v=EOICdpjiqv8) +- [Latency and tearing test: KeDei 3.5" 320x480 HDMI vs Adafruit 2.8" PiTFT ILI9341 240x320 SPI](https://www.youtube.com/watch?v=1yvmvv0KtNs) +- [fbcp-ili9341 ported to ILI9486 WaveShare 3.5" (B) SpotPear 320x480 SPI display](https://www.youtube.com/watch?v=dqOLIHOjLq4) +- [Quake 60 fps inside Gameboy Advance (ILI9341)](https://www.youtube.com/watch?v=xmO8t3XlxVM) +- First implementation of a statistics overlay: [fbcp-ili9341 SPI display driver on Adafruit PiTFT 2.8"](http://youtu.be/rKSH048XRjA) +- Initial proof of concept video: [fbcp-ili9341 driver first demo](https://youtu.be/h1jhuR-oZm0) ### How It Works Given that the SPI bus can be so constrained on bandwidth, how come fbcp-ili9341 seems to be able to update at up to 60fps? The way this is achieved is by what could be called *adaptive display stream updates*. Instead of uploading each pixel at each display refresh cycle, only the actually changed pixels on screen are submitted to the display. This is doable because the ILI9341 controller, as many other popular controllers, have communication interface functions that allow specifying partial screen updates, down to subrectangles or even individual pixel levels. This allows beating the bandwidth limit: for example in Quake, even though it is a fast pacing game, on average only about 46% of all pixels on screen change each rendered frame. Some parts, such as the UI stay practically constant across multiple frames. Other optimizations are also utilized to squeeze out even more performance: - - The program directly communicates with the BCM2835 ARM Peripherals controller registers, bypassing the usual Linux software stack. - - A hybrid of both Polled Mode SPI and DMA based transfers are utilized. Long sequential transfer bursts are performed using DMA, and when DMA would have too much latency, Polled Mode SPI is applied instead. - - Undocumented BCM2835 features are used to squeeze out maximum bandwidth: [SPI CDIV is driven at even numbers](https://www.raspberrypi.org/forums/viewtopic.php?t=43442) (and not just powers of two), and the [SPI DLEN register is forced in non-DMA mode](https://www.raspberrypi.org/forums/viewtopic.php?t=181154) to avoid an idle 9th clock cycle for each transferred byte. - - Good old **interlacing** is added into the mix: if the amount of pixels that needs updating is detected to be too much that the SPI bus cannot handle it, the driver adaptively resorts to doing an interlaced update, uploading even and odd scanlines at subsequent frames. Once the number of pending pixels to write returns to manageable amounts, progressive updating is resumed. This effectively doubles the maximum display update rate. (If you do not like the visual appearance that interlacing causes, it is easy to disable this by uncommenting the line `#define NO_INTERLACING` in file `config.h`) - - A dedicated SPI communication thread is used in order to keep the SPI bus active at all times. - - A number of other micro-optimization techniques are used, such as batch updating rectangular spans of pixels, merging disjoint-but-close spans of pixels on the same scanline, and latching Column and Page End Addresses to bottom-right corner of the display to be able to cut CASET and PASET messages in mid-communication. +- The program directly communicates with the BCM2835 ARM Peripherals controller registers, bypassing the usual Linux software stack. +- A hybrid of both Polled Mode SPI and DMA based transfers are utilized. Long sequential transfer bursts are performed using DMA, and when DMA would have too much latency, Polled Mode SPI is applied instead. +- Undocumented BCM2835 features are used to squeeze out maximum bandwidth: [SPI CDIV is driven at even numbers](https://www.raspberrypi.org/forums/viewtopic.php?t=43442) (and not just powers of two), and the [SPI DLEN register is forced in non-DMA mode](https://www.raspberrypi.org/forums/viewtopic.php?t=181154) to avoid an idle 9th clock cycle for each transferred byte. +- Good old **interlacing** is added into the mix: if the amount of pixels that needs updating is detected to be too much that the SPI bus cannot handle it, the driver adaptively resorts to doing an interlaced update, uploading even and odd scanlines at subsequent frames. Once the number of pending pixels to write returns to manageable amounts, progressive updating is resumed. This effectively doubles the maximum display update rate. (If you do not like the visual appearance that interlacing causes, it is easy to disable this by uncommenting the line `#define NO_INTERLACING` in file `config.h`) +- A dedicated SPI communication thread is used in order to keep the SPI bus active at all times. +- A number of other micro-optimization techniques are used, such as batch updating rectangular spans of pixels, merging disjoint-but-close spans of pixels on the same scanline, and latching Column and Page End Addresses to bottom-right corner of the display to be able to cut CASET and PASET messages in mid-communication. The result is that the SPI bus can be kept close to 100% saturation, ~94-97% usual, to maximize the utilization rate of the bus, while only transmitting practically the minimum number of bytes needed to describe each new frame. @@ -39,11 +39,11 @@ The result is that the SPI bus can be kept close to 100% saturation, ~94-97% usu The driver has been checked to work (at least some point in the past) on the following systems: - - Raspberry Pi 3 Model B+ with Raspbian Stretch (GCC 6.3.0) - - Raspberry Pi 3 Model B Rev 1.2 with Raspbian Jessie (GCC 4.9.2) and Raspbian Stretch (GCC 6.3.0) - - Raspberry Pi Zero W with Raspbian Jessie (GCC 4.9.2) and Raspbian Stretch (GCC 6.3.0) - - Raspberry Pi 2 Model B - - Raspberry Pi B Rev. 2.0 (old board from Q4 2012, board revision ID 000e) +- Raspberry Pi 3 Model B+ with Raspbian Stretch (GCC 6.3.0) +- Raspberry Pi 3 Model B Rev 1.2 with Raspbian Jessie (GCC 4.9.2) and Raspbian Stretch (GCC 6.3.0) +- Raspberry Pi Zero W with Raspbian Jessie (GCC 4.9.2) and Raspbian Stretch (GCC 6.3.0) +- Raspberry Pi 2 Model B +- Raspberry Pi B Rev. 2.0 (old board from Q4 2012, board revision ID 000e) although not all boards are actively tested on, so ymmv especially on older boards. (Bug fixes welcome, use https://elinux.org/RPi_HardwareHistory to identify which board you are running on) @@ -51,20 +51,20 @@ although not all boards are actively tested on, so ymmv especially on older boar The following LCD displays have been tested: - - [Adafruit 2.8" 320x240 TFT w/ Touch screen for Raspberry Pi](https://www.adafruit.com/product/1601) with ILI9341 controller - - [Adafruit PiTFT 2.2" HAT Mini Kit - 320x240 2.2" TFT - No Touch](https://www.adafruit.com/product/2315) with ILI9340 controller - - [Adafruit PiTFT - Assembled 480x320 3.5" TFT+Touchscreen for Raspberry Pi](https://www.adafruit.com/product/2097) with HX8357D controller - - [Adafruit 128x96 OLED Breakout Board - 16-bit Color 1.27" w/microSD holder](https://www.adafruit.com/product/1673) with SSD1351 controller - - [Waveshare 3.5inch RPi LCD (B) 320*480 Resolution Touch Screen IPS TFT Display](https://www.amazon.co.uk/dp/B01N48NOXI/ref=pe_3187911_185740111_TE_item) with ILI9486 controller - - [maithoga 3.5 inch 8PIN SPI TFT LCD Color Screen with Adapter Board ILI9486](https://www.aliexpress.com/item/3-5-inch-8P-SPI-TFT-LCD-Color-Screen-Module-ILI9486-Drive-IC-320-480-RGB/32828284227.html) with **ILI9486L** controller - - [BuyDisplay.com 320x480 Serial SPI 3.2"TFT LCD Module Display](https://www.buydisplay.com/default/serial-spi-3-2-inch-tft-lcd-module-display-ili9341-power-than-sainsmart) with ILI9341 controller - - [Arduino A000096 1.77" 160x128 LCD Screen](https://store.arduino.cc/arduino-lcd-screen) with ST7735R controller - - [Tontec 3.5" 320x480 LCD Display](https://www.ebay.com/p/Tontec-3-5-Inches-Touch-Screen-for-Raspberry-Pi-Display-TFT-Monitor-480x320-LCD/1649448059) with MZ61581-PI-EXT 2016.1.28 controller - - [Adafruit 1.54" 240x240 Wide Angle TFT LCD Display with MicroSD](https://www.adafruit.com/product/3787) with ST7789 controller - - [WaveShare 240x240, 1.3inch IPS LCD display HAT for Raspberry Pi](https://www.waveshare.com/1.3inch-lcd-hat.htm) with ST7789VW controller - - [WaveShare 128x128, 1.44inch LCD display HAT for Raspberry Pi](https://www.waveshare.com/1.44inch-lcd-hat.htm) with ST7735S controller - - [KeDei 3.5 inch SPI TFTLCD 480*320 16bit/18bit version 6.3 2018/4/9](https://github.com/juj/fbcp-ili9341/issues/40) with MPI3501 controller - - Unbranded 2.8" 320x240 display with ILI9340 controller +- [Adafruit 2.8" 320x240 TFT w/ Touch screen for Raspberry Pi](https://www.adafruit.com/product/1601) with ILI9341 controller +- [Adafruit PiTFT 2.2" HAT Mini Kit - 320x240 2.2" TFT - No Touch](https://www.adafruit.com/product/2315) with ILI9340 controller +- [Adafruit PiTFT - Assembled 480x320 3.5" TFT+Touchscreen for Raspberry Pi](https://www.adafruit.com/product/2097) with HX8357D controller +- [Adafruit 128x96 OLED Breakout Board - 16-bit Color 1.27" w/microSD holder](https://www.adafruit.com/product/1673) with SSD1351 controller +- [Waveshare 3.5inch RPi LCD (B) 320*480 Resolution Touch Screen IPS TFT Display](https://www.amazon.co.uk/dp/B01N48NOXI/ref=pe_3187911_185740111_TE_item) with ILI9486 controller +- [maithoga 3.5 inch 8PIN SPI TFT LCD Color Screen with Adapter Board ILI9486](https://www.aliexpress.com/item/3-5-inch-8P-SPI-TFT-LCD-Color-Screen-Module-ILI9486-Drive-IC-320-480-RGB/32828284227.html) with **ILI9486L** controller +- [BuyDisplay.com 320x480 Serial SPI 3.2"TFT LCD Module Display](https://www.buydisplay.com/default/serial-spi-3-2-inch-tft-lcd-module-display-ili9341-power-than-sainsmart) with ILI9341 controller +- [Arduino A000096 1.77" 160x128 LCD Screen](https://store.arduino.cc/arduino-lcd-screen) with ST7735R controller +- [Tontec 3.5" 320x480 LCD Display](https://www.ebay.com/p/Tontec-3-5-Inches-Touch-Screen-for-Raspberry-Pi-Display-TFT-Monitor-480x320-LCD/1649448059) with MZ61581-PI-EXT 2016.1.28 controller +- [Adafruit 1.54" 240x240 Wide Angle TFT LCD Display with MicroSD](https://www.adafruit.com/product/3787) with ST7789 controller +- [WaveShare 240x240, 1.3inch IPS LCD display HAT for Raspberry Pi](https://www.waveshare.com/1.3inch-lcd-hat.htm) with ST7789VW controller +- [WaveShare 128x128, 1.44inch LCD display HAT for Raspberry Pi](https://www.waveshare.com/1.44inch-lcd-hat.htm) with ST7735S controller +- [KeDei 3.5 inch SPI TFTLCD 480*320 16bit/18bit version 6.3 2018/4/9](https://github.com/juj/fbcp-ili9341/issues/40) with MPI3501 controller +- Unbranded 2.8" 320x240 display with ILI9340 controller ### Installation @@ -311,71 +311,71 @@ There are two other main options that affect frame delivery timings, `#define SE This mode uses the DispmanX HDMI vsync signal callback to drive frames to the display. Pros: - - least CPU overhead if content runs at 60Hz - - works on Pi Zero +- least CPU overhead if content runs at 60Hz +- works on Pi Zero Cons: - - animation stutters badly on content that is < 60Hz but also on 60Hz content - - excessive +1 vsync interval input to display latency - - wastes CPU overhead if content runs at less than 60Hz +- animation stutters badly on content that is < 60Hz but also on 60Hz content +- excessive +1 vsync interval input to display latency +- wastes CPU overhead if content runs at less than 60Hz **2. vc_dispmanx_vsync_callback() + self synchronization (top right)**, set `#define USE_GPU_VSYNC` and `#define SELF_SYNCHRONIZE_TO_GPU_VSYNC_PRODUCED_NEW_FRAMES`: This mode uses the GPU vsync signal, but also aims to find and synchronize to the edge trigger when content is producing frames. This is the default build mode on Pi Zero. Pros: - - works on Pi Zero - - reduced input to display latency compared to previous mode - - content that runs at 60hz stutters less +- works on Pi Zero +- reduced input to display latency compared to previous mode +- content that runs at 60hz stutters less Cons: - - content that runs < 60 Hz still stutters badly - - wastes CPU overhead if content runs at less than 60Hz - - consumes slightly extra CPU compared to previous method +- content that runs < 60 Hz still stutters badly +- wastes CPU overhead if content runs at less than 60Hz +- consumes slightly extra CPU compared to previous method **3. gpu polling thread + sleep heuristic (bottom left)**, unset `#define USE_GPU_VSYNC` and set `#define SAVE_BATTERY_BY_PREDICTING_FRAME_ARRIVAL_TIMES`: This mode runs a dedicated background thread that drives frames from the GPU to the SPI display. This is the default build mode on Pi 3B. Pros: - - smooth animation at all content frame rates - - low input to display latency +- smooth animation at all content frame rates +- low input to display latency Cons: - - uses excessive CPU time, around +34% more CPU than the vsync signal based approach - - uses excessive GPU time, the VideoCore GPU will be downscaling and snapshotting redundant frames - - when content changes frame rate, has difficulties to adjust quickly - takes a bit of time to ramp to the new frame rate - - requires a continuously running background thread, not feasible on Pi Zero +- uses excessive CPU time, around +34% more CPU than the vsync signal based approach +- uses excessive GPU time, the VideoCore GPU will be downscaling and snapshotting redundant frames +- when content changes frame rate, has difficulties to adjust quickly - takes a bit of time to ramp to the new frame rate +- requires a continuously running background thread, not feasible on Pi Zero **4. gpu polling thread without sleeping (bottom right)**, unset `#define USE_GPU_VSYNC` and unset `#define SAVE_BATTERY_BY_PREDICTING_FRAME_ARRIVAL_TIMES`: This mode runs the dedicated GPU thread as fast as possible, without attempting to sleep CPU. Pros: - - smoothest animation at all content frame rates - - lowest input to display latency - - adapts instantaneously to variable frame rate content +- smoothest animation at all content frame rates +- lowest input to display latency +- adapts instantaneously to variable frame rate content Cons: - - uses ridiculously much CPU overhead, a full 100% core - - uses ridiculously much GPU overhead, the VideoCore GPU will be very busy downscaling and snapshotting redundant frames - - requires a continuously running background thread, not feasible on Pi Zero +- uses ridiculously much CPU overhead, a full 100% core +- uses ridiculously much GPU overhead, the VideoCore GPU will be very busy downscaling and snapshotting redundant frames +- requires a continuously running background thread, not feasible on Pi Zero ### Known Issues Be aware of the following limitations: ###### No rendered frame delivery via events from VideoCore IV GPU - - The codebase captures screen framebuffers by snapshotting via the VideoCore `vc_dispmanx_snapshot()` API, and the obtained pixels are then routed on to the SPI-based display. This kind of polling is performed, since there does not exist an event-based mechanism to get new frames from the GPU as they are produced. The result is inefficient and can easily cause stuttering, since different applications produce frames at different paces. **Ideally the code would ask the VideoCore API to receive finished frames in callback notifications immediately after they are rendered**, but this kind of functionality does not exist in the current GPU driver stack. In the absence of such event delivery mechanism, the code has to resort to polling snapshots of the display framebuffer using carefully timed heuristics to balance between keeping latency and stuttering low, while not causing excessive power consumption. These heuristics keep continuously guessing the update rate of the animation on screen, and they have been tuned to ensure that CPU usage goes down to 0% when there is no detected activity on screen, but it is certainly not perfect. This GPU limitation is discussed at https://github.com/raspberrypi/userland/issues/440. If you'd like to see fbcp-ili9341 operation reduce latency, stuttering and power consumption, please throw a (kind!) comment or a thumbs up emoji in that bug thread to share that you care about this, and perhaps Raspberry Pi engineers might pick the improvement up on the development roadmap. If this issue is resolved, all of the `#define USE_GPU_VSYNC`, `#define SAVE_BATTERY_BY_PREDICTING_FRAME_ARRIVAL_TIMES` and `#define SELF_SYNCHRONIZE_TO_GPU_VSYNC_PRODUCED_NEW_FRAMES` hacks from the previous section could be deleted from the driver, hopefully leading to a best of all worlds scenario without drawbacks. +- The codebase captures screen framebuffers by snapshotting via the VideoCore `vc_dispmanx_snapshot()` API, and the obtained pixels are then routed on to the SPI-based display. This kind of polling is performed, since there does not exist an event-based mechanism to get new frames from the GPU as they are produced. The result is inefficient and can easily cause stuttering, since different applications produce frames at different paces. **Ideally the code would ask the VideoCore API to receive finished frames in callback notifications immediately after they are rendered**, but this kind of functionality does not exist in the current GPU driver stack. In the absence of such event delivery mechanism, the code has to resort to polling snapshots of the display framebuffer using carefully timed heuristics to balance between keeping latency and stuttering low, while not causing excessive power consumption. These heuristics keep continuously guessing the update rate of the animation on screen, and they have been tuned to ensure that CPU usage goes down to 0% when there is no detected activity on screen, but it is certainly not perfect. This GPU limitation is discussed at https://github.com/raspberrypi/userland/issues/440. If you'd like to see fbcp-ili9341 operation reduce latency, stuttering and power consumption, please throw a (kind!) comment or a thumbs up emoji in that bug thread to share that you care about this, and perhaps Raspberry Pi engineers might pick the improvement up on the development roadmap. If this issue is resolved, all of the `#define USE_GPU_VSYNC`, `#define SAVE_BATTERY_BY_PREDICTING_FRAME_ARRIVAL_TIMES` and `#define SELF_SYNCHRONIZE_TO_GPU_VSYNC_PRODUCED_NEW_FRAMES` hacks from the previous section could be deleted from the driver, hopefully leading to a best of all worlds scenario without drawbacks. ###### Screen resize freezes DispmanX - - Currently if one resizes the video frame size at runtime, this causes DispmanX API to go sideways. See https://github.com/raspberrypi/userland/issues/461 for more information. Best workaround is to set the desired screen resolution in `/boot/config.txt` and configure all applications to never change that at runtime. +- Currently if one resizes the video frame size at runtime, this causes DispmanX API to go sideways. See https://github.com/raspberrypi/userland/issues/461 for more information. Best workaround is to set the desired screen resolution in `/boot/config.txt` and configure all applications to never change that at runtime. ###### CPU Turbo is needed for good SPI bus bandwidth - - The speed of the SPI bus is linked to the BCM2835 core frequency. This frequency is at 250MHz by default (on e.g. Pi Zero, 3B and 3B+), and under CPU load, the core turbos up to 400MHz. This turboing directly scales up the SPI bus speed by `400/250=+60%` as well. Therefore when choosing the SPI `CDIV` value to use, one has to pick one that works for both idle and turbo clock speeds. Conversely, the BCM core reverts to non-turbo speed when there is only light CPU load active, and this slows down the display, so if an application is graphically intensive but light on CPU, the SPI display bus does not get a chance to run at maximum speeds. A way to work around this is to force the BCM core to always stay in its turbo state with `force_turbo=1` option in `/boot/config.txt`, but this has an unfortunate effect of causing the ARM CPU to always run in turbo speed as well, consuming excessive amounts of power. At the time of writing, there does not yet exist a good solution to have both power saving and good performance. This limitation is being discussed in more detail at https://github.com/raspberrypi/firmware/issues/992. +- The speed of the SPI bus is linked to the BCM2835 core frequency. This frequency is at 250MHz by default (on e.g. Pi Zero, 3B and 3B+), and under CPU load, the core turbos up to 400MHz. This turboing directly scales up the SPI bus speed by `400/250=+60%` as well. Therefore when choosing the SPI `CDIV` value to use, one has to pick one that works for both idle and turbo clock speeds. Conversely, the BCM core reverts to non-turbo speed when there is only light CPU load active, and this slows down the display, so if an application is graphically intensive but light on CPU, the SPI display bus does not get a chance to run at maximum speeds. A way to work around this is to force the BCM core to always stay in its turbo state with `force_turbo=1` option in `/boot/config.txt`, but this has an unfortunate effect of causing the ARM CPU to always run in turbo speed as well, consuming excessive amounts of power. At the time of writing, there does not yet exist a good solution to have both power saving and good performance. This limitation is being discussed in more detail at https://github.com/raspberrypi/firmware/issues/992. ###### Raspbian + 32-bit only(?) - - At the moment fbcp-ili9341 is only likely to work on 32-bit OSes, on Raspbian/Ubuntu/Debian family of distributions, where Broadcom and DispmanX libraries are available. 64-bit operating systems do not currently work (see [issue #43](https://github.com/juj/fbcp-ili9341/issues/43)). It should be possible to port the driver to 64-bit and other OSes, though the amount of work has not been explored. +- At the moment fbcp-ili9341 is only likely to work on 32-bit OSes, on Raspbian/Ubuntu/Debian family of distributions, where Broadcom and DispmanX libraries are available. 64-bit operating systems do not currently work (see [issue #43](https://github.com/juj/fbcp-ili9341/issues/43)). It should be possible to port the driver to 64-bit and other OSes, though the amount of work has not been explored. For more known issues and limitations, check out the [bug tracker](https://github.com/juj/fbcp-ili9341/issues), especially the entries marked *retired*, for items that are beyond current scope. @@ -436,10 +436,10 @@ If fbcp-ili9341 does not support your display controller, you will have to write Perhaps. This is a more recent experimental feature that may not be as stable, and there are some limitations, but 3-wire ("9-bit") SPI display support is now available. If you have a 3-wire SPI display, i.e. one that does not have a Data/Control (DC) GPIO pin to connect, configure it via CMake with directive `-DGPIO_TFT_DATA_CONTROL=-1` to tell fbcp-ili9341 that it should be driving the display with 3-wire protocol. Current limitations of 3-wire communication are: - - The performance option `ALL_TASKS_SHOULD_DMA` is currently not supported, there is an issue with DMA chaining that prevents this from being enabled. As result, CPU usage on 3-wire displays will be slightly higher than on 4-wire displays. - - The performance option `OFFLOAD_PIXEL_COPY_TO_DMA_CPP` is currently not supported. As a result, 3-wire displays may not work that well on single core Pis like Pi Zero. - - This has only been tested on my Adafruit SSD1351 128x96 RGB OLED display, which can be soldered to operate in 3-wire SPI mode, so testing has not been particularly extensive. - - Displays that have a 16-bit wide command word, such as ILI9486, do not currently work in 3-wire ("17-bit") mode. (But ILI9486L has 8-bit command word, so that does work) +- The performance option `ALL_TASKS_SHOULD_DMA` is currently not supported, there is an issue with DMA chaining that prevents this from being enabled. As result, CPU usage on 3-wire displays will be slightly higher than on 4-wire displays. +- The performance option `OFFLOAD_PIXEL_COPY_TO_DMA_CPP` is currently not supported. As a result, 3-wire displays may not work that well on single core Pis like Pi Zero. +- This has only been tested on my Adafruit SSD1351 128x96 RGB OLED display, which can be soldered to operate in 3-wire SPI mode, so testing has not been particularly extensive. +- Displays that have a 16-bit wide command word, such as ILI9486, do not currently work in 3-wire ("17-bit") mode. (But ILI9486L has 8-bit command word, so that does work) #### Does fbcp-ili9341 work with I2C, DPI, MIPI DSI or USB connected displays? @@ -562,7 +562,7 @@ The ILI9486L controller based maithoga display runs a bit faster than ILI9486 Wa If manufacturing variances turn out not to be high between copies, and you'd like to have a bigger 320x480 display instead of a 240x320 one, then it is recommended to avoid ILI9486, they indeed are slow. -The KeDei v6.3 display with MPI3501 controller takes the crown of being horrible, in all aspects imaginable. It is able to run at 33.33 MHz, but due to technical design limitations of the display (see [#40](https://github.com/juj/fbcp-ili9341/issues/40#issuecomment-441480557)), effective bus speed is halved, and only about 72% utilization of the remaining bus rate is achieved. DMA cannot be used, so CPU usage will be off the charts. Even though fbcp-ili9341 supports this display, level of support is expected to be poor, because the hardware design is a closed secret without open documentation publicly available from the manufacturer. Stay clear of KeDei or MPI3501 displays. +The KeDei v6.3 display with MPI3501 controller takes the crown of being horrible, in all aspects imaginable. It is able to run at 33.33 MHz, but due to technical design limitations of the display (see [#40](https://github.com/juj/fbcp-ili9341/issues/40#issuecomment-441480557)), effective bus speed is halved, and only about 72% utilization of the remaining bus rate is achieved. DMA cannot be used, so CPU usage will be off the charts. Even though fbcp-ili9341 supports this display. The hardware design is closed BUT it is not that special and is so close to other data sheets of like chips, 98% of the commands work as described on other data sheets. What does NOT work is backlight control by software, the commands for that don't cause errors but they also have no effect. Stay clear of KeDei or MPI3501 displays if you need any sort of motion. They are fine for control displays that are nearly static and have only partial screen refreshes. The Tontec MZ61581 controller based 320x480 3.5" display on the other hand can be driven insanely fast at up to 140MHz! These seem to be quite hard to come by though and they are expensive. Tontec seems to have gone out of business and for example the domain itontec.com from which the supplied instructions sheet asks to download original drivers from is no longer registered. I was able to find one from eBay for testing. @@ -575,45 +575,45 @@ Ultimately, it should be noted that parallel displays (DPI) are the proper metho ### Resources The following links proved helpful when writing this: - - [ARM BCM2835 Peripherals Manual PDF](https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf), - - [ILI9341 Display Controller Manual PDF](https://cdn-shop.adafruit.com/datasheets/ILI9341.pdf), - - [notro/fbtft](https://github.com/notro/fbtft): Linux Framebuffer drivers for small TFT LCD display modules, - - [BCM2835 driver](http://www.airspayce.com/mikem/bcm2835/) for Raspberry Pi, - - [tasanakorn/rpi-fbcp](https://github.com/tasanakorn/rpi-fbcp), original framebuffer driver, - - [tasanakorn/rpi-fbcp/#16](https://github.com/tasanakorn/rpi-fbcp/issues/16), discussion about performance, - - [Tomáš Suk, Cyril Höschl IV, and Jan Flusser, Rectangular Decomposition of Binary Images.](http://library.utia.cas.cz/separaty/2012/ZOI/suk-rectangular%20decomposition%20of%20binary%20images.pdf), a useful research paper about merging monochrome bitmap images to rectangles, which gave good ideas for optimizing SPI span merges across multiple scan lines, - - [VC DispmanX source code](https://github.com/raspberrypi/userland/blob/master/interface/vmcs_host/vc_vchi_dispmanx.c) (more or less the only official documentation bit on DispmanX I could ever find) +- [ARM BCM2835 Peripherals Manual PDF](https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf), +- [ILI9341 Display Controller Manual PDF](https://cdn-shop.adafruit.com/datasheets/ILI9341.pdf), +- [notro/fbtft](https://github.com/notro/fbtft): Linux Framebuffer drivers for small TFT LCD display modules, +- [BCM2835 driver](http://www.airspayce.com/mikem/bcm2835/) for Raspberry Pi, +- [tasanakorn/rpi-fbcp](https://github.com/tasanakorn/rpi-fbcp), original framebuffer driver, +- [tasanakorn/rpi-fbcp/#16](https://github.com/tasanakorn/rpi-fbcp/issues/16), discussion about performance, +- [Tomáš Suk, Cyril Höschl IV, and Jan Flusser, Rectangular Decomposition of Binary Images.](http://library.utia.cas.cz/separaty/2012/ZOI/suk-rectangular%20decomposition%20of%20binary%20images.pdf), a useful research paper about merging monochrome bitmap images to rectangles, which gave good ideas for optimizing SPI span merges across multiple scan lines, +- [VC DispmanX source code](https://github.com/raspberrypi/userland/blob/master/interface/vmcs_host/vc_vchi_dispmanx.c) (more or less the only official documentation bit on DispmanX I could ever find) ### I Want To Contribute / Future Work / TODOs If you would like to help push Raspberry Pi SPI display support further, there are always more things to do in the project. Here is a list of ideas and TODOs for recognized work items to contribute, roughly rated in order of increasing difficulty. - - Vote up issue [raspberrypi/userland/#440](https://github.com/raspberrypi/userland/issues/440) if you would like to see Raspberry Pi Foundation improve CPU performance and reduce latency of the Pi when used with SPI displays. - - Vote up issue [raspberrypi/userland/#461](https://github.com/raspberrypi/userland/issues/461) if you would like to see fbcp-ili9341 not die (due to DispmanX dying) when HDMI display resolution changes. - - Vote up issue [raspberrypi/firmware/#992](https://github.com/raspberrypi/firmware/issues/992) if you would like to see Raspberry Pi SPI bus to have high throughput even when the Pi CPU is not under heavy CPU load (better SPI throughput with lower power consumption), a performance feature only SDHOST on the Pi currently enjoys. - - Benchmark fbcp-ili9341 performance in your use case with CPU tool `top`/`htop`, or with a power meter off the wall and report the results. - - Do you have a display with an unlisted or unknown display controller? Post close up photos of it to an issue in the tracker, and report if you were able to make it work with fbcp-ili9341? - - Did you have to do something unexpected or undocumented to get fbcp-ili9341 to work in your environment or use case? Write up a tutorial or record a video to let people know about the gotchas. - - If you have access to a high frequency scope/logic analyzer (~128MHz), audit the utilization of the SPI MOSI bus to find any remaining idle times on the bus, and analyze their sources. - - Port fbcp-ili9341 to work as a static code library that one can link to another application for CPU-based drawing directly to display, bypassing inefficiencies and latency of the general purpose Linux DispmanX/graphics stack. - - Improve existing display initialization routines with options to control e.g. gamma curves, color saturation, driving voltages, refresh rates or other potentially useful features that the display controller protocols expose. - - Add support to fbcp-ili9341 to a new display controller. (e.g. [#26](https://github.com/juj/fbcp-ili9341/issues/26)) - - Implement support for reading the MISO line for display identification numbers/strings for potentially interesting statistics (could some of the displays be autodetected this way?) - - Add support for other color modes, like RGB666 or RGB888. Currently fbcp-ili9341 only knows about RGB565 display mode. - - Implement a kernel module that enables userland programs to allocate DMA channels, which fbcp-ili9341 could use to amicably reserve its own DMA channels without danger of conflicting. - - üImplement support for SPI-based SD card readers that are sometimes attached to displays. - - Port fbcp-ili9341 to work with I2C displays. - - Port more key algorithms to ARM assembly to optimize performance of fbcp-ili9341 in hotspots, or optimize execution in some other ways? - - Add support to building fbcp-ili9341 on another operating system than Raspbian. (see [#43](https://github.com/juj/fbcp-ili9341/issues/43)) - - Add support for building on 64-bit operating systems. (see [#43](https://github.com/juj/fbcp-ili9341/issues/43)) - - Port fbcp-ili9341 over to a new single-board computer hardware. (e.g. [#30](https://github.com/juj/fbcp-ili9341/issues/30)) - - Improve support for 3-wire displays, e.g. for 1) "17-bit" 3-wire communication, 2) fix up `SPI_3WIRE_PROTOCOL` + `ALL_TASKS_SHOULD_DMA` to work together, or 3) fix up `SPI_3WIRE_PROTOCOL` + `OFFLOAD_PIXEL_COPY_TO_DMA_CPP` to work together. - - Optimize away unnecessary zero padding that 3-wire communication currently incurs, by keeping a queue of leftover untransmitted partial bits of a byte, and piggybacking them onto the next transfer that comes in. - - Port the high performance DMA-based SPI communication technique from fbcp-ili9341 over to another project that uses the SPI bus for something else, for close to 100% saturation of the SPI bus in the project. - - Improve the implementation of chaining DMA transfers to not only chain transfers within a single SPI task, but also across multiple SPI tasks. - - Optimize `ALL_TASKS_SHOULD_DMA` mode to be always superior in performance and CPU usage so that the non-`ALL_TASKS_SHOULD_DMA` path can be dropped from the codebase. (probably requires the above chaining to function efficiently) - - If you are knowledgeable with BCM2835 DMA, investigate whether the hacky dance where two DMA channels need to be used to reset and resume DMA SPI transfers when chaining, can be avoided? - - If you have contacts with Broadcom, ask them to promote use of the SoC hardware with DMA chaining + mixed SPI & non-SPI tasks as a first class tested use case. Current DMA SPI hardware behavior of BCM2835 is, to say the least, surprising. +- Vote up issue [raspberrypi/userland/#440](https://github.com/raspberrypi/userland/issues/440) if you would like to see Raspberry Pi Foundation improve CPU performance and reduce latency of the Pi when used with SPI displays. +- Vote up issue [raspberrypi/userland/#461](https://github.com/raspberrypi/userland/issues/461) if you would like to see fbcp-ili9341 not die (due to DispmanX dying) when HDMI display resolution changes. +- Vote up issue [raspberrypi/firmware/#992](https://github.com/raspberrypi/firmware/issues/992) if you would like to see Raspberry Pi SPI bus to have high throughput even when the Pi CPU is not under heavy CPU load (better SPI throughput with lower power consumption), a performance feature only SDHOST on the Pi currently enjoys. +- Benchmark fbcp-ili9341 performance in your use case with CPU tool `top`/`htop`, or with a power meter off the wall and report the results. +- Do you have a display with an unlisted or unknown display controller? Post close up photos of it to an issue in the tracker, and report if you were able to make it work with fbcp-ili9341? +- Did you have to do something unexpected or undocumented to get fbcp-ili9341 to work in your environment or use case? Write up a tutorial or record a video to let people know about the gotchas. +- If you have access to a high frequency scope/logic analyzer (~128MHz), audit the utilization of the SPI MOSI bus to find any remaining idle times on the bus, and analyze their sources. +- Port fbcp-ili9341 to work as a static code library that one can link to another application for CPU-based drawing directly to display, bypassing inefficiencies and latency of the general purpose Linux DispmanX/graphics stack. +- Improve existing display initialization routines with options to control e.g. gamma curves, color saturation, driving voltages, refresh rates or other potentially useful features that the display controller protocols expose. +- Add support to fbcp-ili9341 to a new display controller. (e.g. [#26](https://github.com/juj/fbcp-ili9341/issues/26)) +- Implement support for reading the MISO line for display identification numbers/strings for potentially interesting statistics (could some of the displays be autodetected this way?) +- Add support for other color modes, like RGB666 or RGB888. Currently fbcp-ili9341 only knows about RGB565 display mode. +- Implement a kernel module that enables userland programs to allocate DMA channels, which fbcp-ili9341 could use to amicably reserve its own DMA channels without danger of conflicting. +- üImplement support for SPI-based SD card readers that are sometimes attached to displays. +- Port fbcp-ili9341 to work with I2C displays. +- Port more key algorithms to ARM assembly to optimize performance of fbcp-ili9341 in hotspots, or optimize execution in some other ways? +- Add support to building fbcp-ili9341 on another operating system than Raspbian. (see [#43](https://github.com/juj/fbcp-ili9341/issues/43)) +- Add support for building on 64-bit operating systems. (see [#43](https://github.com/juj/fbcp-ili9341/issues/43)) +- Port fbcp-ili9341 over to a new single-board computer hardware. (e.g. [#30](https://github.com/juj/fbcp-ili9341/issues/30)) +- Improve support for 3-wire displays, e.g. for 1) "17-bit" 3-wire communication, 2) fix up `SPI_3WIRE_PROTOCOL` + `ALL_TASKS_SHOULD_DMA` to work together, or 3) fix up `SPI_3WIRE_PROTOCOL` + `OFFLOAD_PIXEL_COPY_TO_DMA_CPP` to work together. +- Optimize away unnecessary zero padding that 3-wire communication currently incurs, by keeping a queue of leftover untransmitted partial bits of a byte, and piggybacking them onto the next transfer that comes in. +- Port the high performance DMA-based SPI communication technique from fbcp-ili9341 over to another project that uses the SPI bus for something else, for close to 100% saturation of the SPI bus in the project. +- Improve the implementation of chaining DMA transfers to not only chain transfers within a single SPI task, but also across multiple SPI tasks. +- Optimize `ALL_TASKS_SHOULD_DMA` mode to be always superior in performance and CPU usage so that the non-`ALL_TASKS_SHOULD_DMA` path can be dropped from the codebase. (probably requires the above chaining to function efficiently) +- If you are knowledgeable with BCM2835 DMA, investigate whether the hacky dance where two DMA channels need to be used to reset and resume DMA SPI transfers when chaining, can be avoided? +- If you have contacts with Broadcom, ask them to promote use of the SoC hardware with DMA chaining + mixed SPI & non-SPI tasks as a first class tested use case. Current DMA SPI hardware behavior of BCM2835 is, to say the least, surprising. ### License @@ -622,3 +622,4 @@ This driver is licensed under the MIT License. See LICENSE.txt. In nonlegal term If you found fbcp-ili9341 useful, it makes me happy to hear back about the projects it found a home in. If you did a build or a project where fbcp-ili9341 worked out, it'd be great to see a video or some photos or read about your experiences. I hope you build something you enjoy! + From be9e548ee259701f6d8d14872e3b74395fd31e62 Mon Sep 17 00:00:00 2001 From: Markus Date: Wed, 2 Oct 2019 20:29:12 +0200 Subject: [PATCH 45/52] change to MIT --- XPT2046.cpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/XPT2046.cpp b/XPT2046.cpp index 3c37d73..07c8c57 100644 --- a/XPT2046.cpp +++ b/XPT2046.cpp @@ -6,19 +6,21 @@ * Copyright (c) 2015 Markus Sattler. All rights reserved. * This file is originally part of the XPT2046 driver for Arduino. * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the “Software”), to deal in the Software without restriction, + * including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ From 9b54d8ae834a714e4e2564dd501c41c6e2978514 Mon Sep 17 00:00:00 2001 From: Markus Date: Wed, 2 Oct 2019 20:29:40 +0200 Subject: [PATCH 46/52] Update XPT2046.h --- XPT2046.h | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/XPT2046.h b/XPT2046.h index 1da4c9f..af517a2 100644 --- a/XPT2046.h +++ b/XPT2046.h @@ -6,19 +6,21 @@ * Copyright (c) 2015 Markus Sattler. All rights reserved. * This file is part of the XPT2046 driver for Arduino. * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the “Software”), to deal in the Software without restriction, + * including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ From 3eab4558acc0fdda74f1dc34bdcecec640fd6ba7 Mon Sep 17 00:00:00 2001 From: Kunzol Date: Sat, 5 Oct 2019 16:37:07 +0200 Subject: [PATCH 47/52] some cosmetic changes for the util part: * missing ignore for tcTest added * library changed to use EGL and GLESv2 from libraspberrypi0 package. --- .gitignore | 1 + util/Makefile | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index bae4d0c..4b54a02 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ kernel/bcm2835_spi_display.mod.c build .DS_Store util/tcCalib +util/tcTest diff --git a/util/Makefile b/util/Makefile index b8b05e9..37dba36 100644 --- a/util/Makefile +++ b/util/Makefile @@ -1,4 +1,4 @@ -LIBFLAGS=-L/opt/vc/lib -lEGL -lGLESv2 -lbcm_host -lpthread -ljpeg -lshapes +LIBFLAGS=-L/opt/vc/lib -lbrcmEGL -lbrcmGLESv2 -lbcm_host -lpthread -ljpeg -lshapes CXXFLAGS=-DDEBUG -fno-exceptions -DPI -std=gnu++11 -I/opt/vc/include -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/interface/vcos/pthreads -I.. objs=tcCalib.o ../calibrate.o From 317d4eaa03e459abc564eb6a52b5dfd9c19e3369 Mon Sep 17 00:00:00 2001 From: kunzol Date: Sun, 6 Oct 2019 16:43:52 +0200 Subject: [PATCH 48/52] full fletched Makefile for kernel with features: * make - builds the kernel module * make install - installs the kernel module * make uninstall - uninstalls the kernel module * make start - loads the module * make stop - stops fbcp-ili9341 process and unloads the module * make status - shows module info and if loaded in kernel * make debug - generates disassembly output for debugging * make clean - remove all files generates by this Makefile --- kernel/Makefile | 33 +++++++++++++++++++++++++++++++++ kernel/build_kernel_module.sh | 5 ++--- kernel/start_kernel_module.sh | 2 +- kernel/stop_kernel_module.sh | 9 +-------- 4 files changed, 37 insertions(+), 12 deletions(-) diff --git a/kernel/Makefile b/kernel/Makefile index 8aaca22..0df7743 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -1,3 +1,36 @@ obj-m := bcm2835_spi_display.o CFLAGS_bcm2835_spi_display.o := -O3 -std=gnu99 -Wno-declaration-after-statement -DKERNEL_MODULE=1 -DILI9341=1 -DADAFRUIT_ILI9341_PITFT=1 -DSPI_BUS_CLOCK_DIVISOR=48 + +KDIR ?= /lib/modules/$(shell uname -r) + +all $(obj-m:%.o=%.ko): + $(MAKE) -C "$(KDIR)/build" M="$(CURDIR)" modules + +install: stop $(obj-m:%.o=%.ko) + $(MAKE) -C "$(KDIR)/build" M="$(CURDIR)" modules_install + @depmod -a + +uninstall: stop + @rm -vf -- "$(KDIR)/extra/$(obj-m:%.o=%.ko)" + @depmod -a + +start: + @modprobe -v $(obj-m:%.o=%) + +stop: + @pgrep "fbcp-ili9341.*" && pkill "fbcp-ili9341.*" || true + @pgrep "fbcp-ili9341.*" && sleep 5 && pkill -KILL "fbcp-ili9341.*" || true + @modprobe -vr $(obj-m:%.o=%) + +status: + @modinfo $(obj-m:%.o=%) + @echo loaded: + @lsmod | grep $(obj-m:%.o=%) || echo not loaded + +debug: $(obj-m:%.o=%.ko) + @objdump -dS $< > $(obj-m:%.o=%.S) + +clean: + $(MAKE) -C "$(KDIR)/build" M="$(CURDIR)" clean + @rm -rf -- $(obj-m:%.o=%.S) diff --git a/kernel/build_kernel_module.sh b/kernel/build_kernel_module.sh index d3d3dd3..c022897 100755 --- a/kernel/build_kernel_module.sh +++ b/kernel/build_kernel_module.sh @@ -1,6 +1,5 @@ -sudo ./stop_kernel_module.sh -sudo make VERBOSE=1 -C /lib/modules/$(uname -r)/build M=$(pwd) -I/usr/include/ modules +sudo make #For debugging: generate disassembly output: -#objdump -dS bcm2835_spi_display.ko > bcm2835_spi_display.S +#make debug diff --git a/kernel/start_kernel_module.sh b/kernel/start_kernel_module.sh index 617337e..2425586 100755 --- a/kernel/start_kernel_module.sh +++ b/kernel/start_kernel_module.sh @@ -1,3 +1,3 @@ echo Starting kernel module bcm2835_spi_display.ko -sudo insmod bcm2835_spi_display.ko +sudo make install start diff --git a/kernel/stop_kernel_module.sh b/kernel/stop_kernel_module.sh index 417d30a..6bbef24 100755 --- a/kernel/stop_kernel_module.sh +++ b/kernel/stop_kernel_module.sh @@ -1,10 +1,3 @@ -# Kill user space driver program first if it happens to be running, because otherwise shutting down the kernel -# module would crash the system if the userland program was still accessing it. -echo Killing existing instances of user space driver program fbcp-ili9341 -sudo pkill fbcp-ili9341 -sudo pkill fbcp-ili9341-stable - -# Now safe to tear down the module echo Stopping kernel module bcm2835_spi_display.ko -sudo rmmod bcm2835_spi_display.ko +sudo make stop From c55dc622791c17c7079b91d20926303339ef0169 Mon Sep 17 00:00:00 2001 From: kunzol Date: Sun, 6 Oct 2019 17:28:40 +0200 Subject: [PATCH 49/52] Bugfix: make clean The clean-rule in the makefile tried to create the object files before removing them. Additionally tcTest was not handled correct in clean-rule. --- util/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/util/Makefile b/util/Makefile index 37dba36..16a7592 100644 --- a/util/Makefile +++ b/util/Makefile @@ -6,9 +6,9 @@ objs2=tcTest.o all: tcCalib tcTest -clean: $(objs) - rm $(objs) - rm tcCalib +clean: + rm -f -- $(objs) tcCalib + rm -f -- $(objs2) tcTest ../calibrate.o: ../calibrate.cpp g++ $(CXXFLAGS) -c -o ../calibrate.o ../calibrate.cpp From 82739e0dc89b2fffa3ea28de65fab4781bee9e64 Mon Sep 17 00:00:00 2001 From: Kunzol Date: Sun, 13 Oct 2019 00:09:10 +0200 Subject: [PATCH 50/52] A first try to configure the repo for building a debian package. The complex part is that on one side a linux module should be created with dkms. On the other sider there is a binary which needs to be compiled on package install because it depends on the installed hardware. For compilation the user has to provide some additional information, which is fetched via config-package questions and answers. Besides this some small test and calibration binaries are created during the package build process. Could only test this with my single hardware (KeDei) and hope that it works similar with the other types of displays/touchscreens. --- debian/README.Debian | 6 ++ debian/changelog | 5 + debian/compat | 1 + debian/control | 20 ++++ debian/copyright | 31 ++++++ debian/fbcp-ili9341-module | 65 +++++++++++++ debian/fbcp-ili9341-module.1 | 20 ++++ debian/fbcp-ili9341.conf | 4 + debian/fbcp-ili9341.config | 52 ++++++++++ debian/fbcp-ili9341.dirs | 2 + debian/fbcp-ili9341.dkms | 9 ++ debian/fbcp-ili9341.lintian-overrides | 1 + debian/fbcp-ili9341.manpages | 3 + debian/fbcp-ili9341.modprobe | 3 + debian/fbcp-ili9341.postinst | 133 ++++++++++++++++++++++++++ debian/fbcp-ili9341.postrm | 33 +++++++ debian/fbcp-ili9341.prerm | 34 +++++++ debian/fbcp-ili9341.templates | 42 ++++++++ debian/rules | 50 ++++++++++ debian/source/format | 1 + debian/tcCalib.1 | 10 ++ debian/tcTest.1 | 11 +++ 22 files changed, 536 insertions(+) create mode 100644 debian/README.Debian create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100755 debian/fbcp-ili9341-module create mode 100644 debian/fbcp-ili9341-module.1 create mode 100644 debian/fbcp-ili9341.conf create mode 100644 debian/fbcp-ili9341.config create mode 100644 debian/fbcp-ili9341.dirs create mode 100644 debian/fbcp-ili9341.dkms create mode 100644 debian/fbcp-ili9341.lintian-overrides create mode 100644 debian/fbcp-ili9341.manpages create mode 100644 debian/fbcp-ili9341.modprobe create mode 100644 debian/fbcp-ili9341.postinst create mode 100644 debian/fbcp-ili9341.postrm create mode 100644 debian/fbcp-ili9341.prerm create mode 100644 debian/fbcp-ili9341.templates create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100644 debian/tcCalib.1 create mode 100644 debian/tcTest.1 diff --git a/debian/README.Debian b/debian/README.Debian new file mode 100644 index 0000000..ad29cc2 --- /dev/null +++ b/debian/README.Debian @@ -0,0 +1,6 @@ +fbcp-ili9341 for Debian +---------------------- + +https://github.com/kpishere/fbcp-ili9341 + + -- kunzol Sat, 05 Oct 2019 18:24:59 +0200 diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..3a54d7f --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +fbcp-ili9341 (0.1) unstable; urgency=medium + + * Initial Release. + + -- kunzol Sat, 05 Oct 2019 18:24:59 +0200 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +9 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..2a13aac --- /dev/null +++ b/debian/control @@ -0,0 +1,20 @@ +Source: fbcp-ili9341 +Section: misc +Priority: optional +Maintainer: kunzol +Build-Depends: debhelper (>= 9), dkms, config-package-dev, debhelper +Standards-Version: 3.9.8 +Homepage: https://github.com/kpishere/fbcp-ili9341 +#Vcs-Git: https://anonscm.debian.org/collab-maint/fbcp-ili9341.git +#Vcs-Browser: https://anonscm.debian.org/cgit/collab-maint/fbcp-ili9341.git + +Package: fbcp-ili9341 +Architecture: armhf +Depends: ${shlibs:Depends}, ${misc:Depends}, libc6, raspberrypi-kernel-headers, cmake, g++, make, binutils, kmod, libraspberrypi0, crudini, procps +Description: SPI-based LCD displays for Raspberry Pi + This repository implements a driver for + certain SPI-based LCD displays for + Raspberry Pi A, B, 2, 3 and Zero + via dkms. + . + diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..87e2491 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,31 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: fbcp-ili9341 +Source: + +Files: * +Copyright: 2018 Jukka Jylänki +License: MIT + +Files: debian/* +Copyright: 2019 kunzol https://github.com/Kunzol +License: MIT + +License: MIT + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/debian/fbcp-ili9341-module b/debian/fbcp-ili9341-module new file mode 100755 index 0000000..3f5169b --- /dev/null +++ b/debian/fbcp-ili9341-module @@ -0,0 +1,65 @@ +#!/bin/bash +command="${1}" +shift +fbcpmodopt="$*" +fbcplog="/var/log/fbcp-ili9341-dkms.log" +fbcpmod="bcm2835_spi_display" +fbcpcmd="fbcp-ili9341" +fbcpscript="$( readlink -f $0 )" + +function fbcp_log() { + logger -e -t "${fbcpcmd}" -p user.info -- "$$ $*" +} + +case "${command,,}" in + run) + fbcp_log "${command}" + while IFS= read -r fbcp + do + fbcp_log "${fbcp}" + done < <( /usr/sbin/${fbcpcmd} ) & + ;; + start) + fbcp_log "${command}" + fbcp_log "$( modprobe -v --ignore-install ${fbcpmod} ${fbcpmodopt} )" +# module loaded and fbcpcmd not running + if lsmod | grep -q ${fbcpmod} && pgrep -v ${fbcpcmd} > /dev/null ; then + exec ${fbcpscript} run + fi + ;; + stop) + fbcp_log "${command}" +# try killing fbcpcmd not more than 7 times + declare -i i=0 + while pgrep -x ${fbcpcmd} > /dev/null && [ ${i} -lt 7 ] + do + pkill -x -TERM ${fbcpcmd} + if pgrep -x ${fbcpcmd} > /dev/null; then + ((i++)) + sleep 1 + else + break + fi + done + +# force kill fbcpcmd + pgrep -x ${fbcpcmd} > /dev/null && pkill -x -KILL ${fbcpcmd} || true + + if pgrep -x ${fbcpcmd} > /dev/null; then + echo "fbcp-ili9341 still running. can not unload module." >&2 + exit 1 + else + modprobe -r --ignore-remove ${fbcpmod} + fi + ;; + status) + pgrep -x -l ${fbcpcmd} || echo "fbcp-ili9341 not running." + lsmod | grep ${fbcpmod} || echo "${fbcpmod} not loaded." + ;; + *) + echo "usage: $0 start|stop|status" >&2 + exit 1 + ;; +esac + +fbcp_log "${command} end" diff --git a/debian/fbcp-ili9341-module.1 b/debian/fbcp-ili9341-module.1 new file mode 100644 index 0000000..42352cc --- /dev/null +++ b/debian/fbcp-ili9341-module.1 @@ -0,0 +1,20 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" (C) Copyright 2019 kunzol , +.\" +.TH Fbcp-ili9341-module 1 "October 5 2019" +.SH NAME +fbcp-ili9341-module \- start and stop fbcp-ili9341 +.SH SYNOPSIS +.B fbcp-ili9341-module +.RI (start | stop) +.SH DESCRIPTION +Start or stop +.B fbcp-ili9341 +.PP +.SH OPTIONS +.TP +.B stop +start fbcp-ili9341 including loading module into kernel. +.TP +.B stop +stop fbcp-ili9341 including unloading module from kernel. diff --git a/debian/fbcp-ili9341.conf b/debian/fbcp-ili9341.conf new file mode 100644 index 0000000..da3d234 --- /dev/null +++ b/debian/fbcp-ili9341.conf @@ -0,0 +1,4 @@ +# +# fbcp-ili9341 +# +bcm2835_spi_display diff --git a/debian/fbcp-ili9341.config b/debian/fbcp-ili9341.config new file mode 100644 index 0000000..3db5e1b --- /dev/null +++ b/debian/fbcp-ili9341.config @@ -0,0 +1,52 @@ +#!/bin/bash +# vim:syntax=sh:ai:et:si:sts=4:sw=4 +# +# config script for fbcp-ili9341 +# +# see: dh_installdeb(1) + +set -e + +. /usr/share/debconf/confmodule + +case "$1" in + configure|reconfigure) + db_input critical fbcp-ili9341/ishat || true + db_go || true + + db_get fbcp-ili9341/ishat || true + ishat="${RET}" + + case "${ishat,,}" in + true) + db_input critical fbcp-ili9341/hat || true + db_go || true + + ;; + false) + db_input critical fbcp-ili9341/wired || true + db_go || true + ;; + *) + exit 1 + ;; + esac + + db_input critical fbcp-ili9341/divisor || true + db_go || true + db_input high fbcp-ili9341/statistics || true + db_go || true + db_input high fbcp-ili9341/hdmi || true + db_go || true + ;; + + *) + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/debian/fbcp-ili9341.dirs b/debian/fbcp-ili9341.dirs new file mode 100644 index 0000000..592a9d9 --- /dev/null +++ b/debian/fbcp-ili9341.dirs @@ -0,0 +1,2 @@ +/usr/share/fbcp-ili9341 +/usr/sbin diff --git a/debian/fbcp-ili9341.dkms b/debian/fbcp-ili9341.dkms new file mode 100644 index 0000000..753fed1 --- /dev/null +++ b/debian/fbcp-ili9341.dkms @@ -0,0 +1,9 @@ +PACKAGE_NAME="fbcp-ili9341" +PACKAGE_VERSION=#MODULE_VERSION# +#BUILD_EXCLUSIVE_ARCH="armhf" +BUILT_MODULE_NAME[0]="bcm2835_spi_display" +MAKE[0]="cd kernel; KDIR=${install_tree}/${kernelver} make; cd .." +BUILT_MODULE_LOCATION[0]="kernel/" +CLEAN="cd kernel; KDIR=${install_tree}/${kernelver} make clean; cd .." +DEST_MODULE_LOCATION[0]=/extra +AUTOINSTALL=yes diff --git a/debian/fbcp-ili9341.lintian-overrides b/debian/fbcp-ili9341.lintian-overrides new file mode 100644 index 0000000..6c3a75f --- /dev/null +++ b/debian/fbcp-ili9341.lintian-overrides @@ -0,0 +1 @@ +fbcp-ili9341: extra-license-file usr/share/fbcp-ili9341/LICENSE.txt diff --git a/debian/fbcp-ili9341.manpages b/debian/fbcp-ili9341.manpages new file mode 100644 index 0000000..723b559 --- /dev/null +++ b/debian/fbcp-ili9341.manpages @@ -0,0 +1,3 @@ +debian/fbcp-ili9341-module.1 +debian/tcTest.1 +debian/tcCalib.1 diff --git a/debian/fbcp-ili9341.modprobe b/debian/fbcp-ili9341.modprobe new file mode 100644 index 0000000..38883a1 --- /dev/null +++ b/debian/fbcp-ili9341.modprobe @@ -0,0 +1,3 @@ + +install bcm2835_spi_display /usr/sbin/fbcp-ili9341-module start $CMDLINE_OPTS +remove bcm2835_spi_display /usr/sbin/fbcp-ili9341-module stop diff --git a/debian/fbcp-ili9341.postinst b/debian/fbcp-ili9341.postinst new file mode 100644 index 0000000..9657b1c --- /dev/null +++ b/debian/fbcp-ili9341.postinst @@ -0,0 +1,133 @@ +#!/bin/bash +# vim:syntax=sh:ai:et:si:sts=4:sw=4 +# +# postinst script for fbcp-ili9341 +# + +set -e +. /usr/share/debconf/confmodule + +# list of display resolution +declare -A displays=( +[ILI9341]="320 240 60 1 0 0 0" +[ILI9340]="320 240 60 1 0 0 0" +[HX8357D]="480 320 60 1 0 0 0" +[SSD1351]="320 240 1 0 0 0" +[ILI9486]="320 480 60 1 0 0 0" +[ILI9486L]="320 240 60 1 0 0 0" +[ST7735R]="320 240 60 1 0 0 0" +[MZ61581]="320 240 60 1 0 0 0" +[ST7789]="320 240 60 1 0 0 0" +[ST7789VW]="320 240 60 1 0 0 0" +[ST7735S]="320 240 60 1 0 0 0" +[MPI3501]="320 240 60 1 0 0 0" +[ADAFRUIT_ILI9341_PITFT]="320 240 60 1 0 0 0" +[ADAFRUIT_HX8357D_PITFT]="480 320 60 1 0 0 0" +[FREEPLAYTECH_WAVESHARE32B]="480 320 60 1 0 0 0" +[WAVESHARE35B_ILI9486]="320 480 60 1 0 0 0" +[TONTEC_MZ61581]="320 480 60 1 0 0 0" +[WAVESHARE_ST7789VW_HAT]="240 240 60 1 0 0 0" +[WAVESHARE_ST7735S_HAT]="128 128 60 1 0 0 0" +[KEDEI_V63_MPI3501]="480 320 60 1 0 0 0" +) + +sourcedir="/usr/share/${DPKG_MAINTSCRIPT_PACKAGE}/build" +sharedir="/usr/share/${DPKG_MAINTSCRIPT_PACKAGE}" + +case "$1" in + configure) + declare -a cmakeopt=( ) + + db_get fbcp-ili9341/statistics || true + statistics="${RET}" + case "${statistics,,}" in + 0|1|2) + cmakeopt+=( "-DSTATISTICS=${statistics}" ) + ;; + *) + cmakeopt+=( "-DSTATISTICS=0" ) + ;; + esac + +# get the controller from the selection + db_get fbcp-ili9341/ishat || true + ishat="${RET}" + case "${ishat,,}" in + true) + db_get fbcp-ili9341/hat || true + controller="${RET}" + ;; + false) + db_get fbcp-ili9341/wired || true + controller="${RET}" + ;; + esac + cmakeopt+=( "-D${controller}=ON" ) + +# get the divisor from the selection + db_get fbcp-ili9341/divisor || true + declare -i divisor="${RET}" + if [ ${divisor} -eq 0 ]; then + divisor=30 + elif [ $(( divisor % 2 )) -gt 0 ]; then +# use next higher even number + (( divisor++ )) + fi + cmakeopt+=( "-DSPI_BUS_CLOCK_DIVISOR=${RET}" ) + +# build the binary + rm -rf -- "${sourcedir}" + mkdir -pv "${sourcedir}" + pushd "${sourcedir}" > /dev/null 2>&1 + echo "cmake ${cmakeopt[@]}" >&2 + cmake ${cmakeopt[@]} .. + make + if [ -f "fbcp-ili9341" ]; then + cp -vfT "fbcp-ili9341" "/usr/sbin/fbcp-ili9341" + else + echo "could not build fbcp-ili9341 binary." >&2 + echo "check for the reason in ${sourcedir}" >&2 + exit 1 + fi + make clean + popd > /dev/null 2>&1 + +# change config.txt + db_get fbcp-ili9341/hdmi || true + hdmi="${RET}" + case "${hdmi,,}" in + true) + cp -f --backup=numbered "/boot/config.txt" "/var/backups/config.txt" + cp -f "/boot/config.txt" "${sharedir}/config.txt.new" + crudini --set "${sharedir}/config.txt.new" "" hdmi_group 2 + crudini --set "${sharedir}/config.txt.new" "" hdmi_mode 87 + crudini --set "${sharedir}/config.txt.new" "" hdmi_cvt "${displays[${controller}]:-320 240 60 1 0 0 0}" + crudini --set "${sharedir}/config.txt.new" "" hdmi_force_hotplug 1 + cp -f "${sharedir}/config.txt.new" "/boot/config.txt.fbcp" + ;; + false) + ;; + *) + echo "Ignoring HDMI value: ${hdmi}" >&2 + ;; + esac + + cp -nv "/boot/config.txt" "/boot/config.txt.fbcp-orig" + cp -fv "/boot/config.txt.fbcp" "/boot/config.txt" + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/debian/fbcp-ili9341.postrm b/debian/fbcp-ili9341.postrm new file mode 100644 index 0000000..d5a75d9 --- /dev/null +++ b/debian/fbcp-ili9341.postrm @@ -0,0 +1,33 @@ +#!/bin/sh +# postrm script for fbcp-ili9341 +# +# see: dh_installdeb(1) + +set -e +. /usr/share/debconf/confmodule + +sharedir="/usr/share/${DPKG_MAINTSCRIPT_PACKAGE}" + +case "$1" in + purge) + rm -f "/boot/config.txt.fbcp" + rm -f "/usr/sbin/fbcp-ili9341" + ;; + remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) + rm -f "${sharedir}/config.txt.new" + rm -f "/usr/sbin/fbcp-ili9341" + rm -rf "${sharedir}/build" + ;; + + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/debian/fbcp-ili9341.prerm b/debian/fbcp-ili9341.prerm new file mode 100644 index 0000000..f05df39 --- /dev/null +++ b/debian/fbcp-ili9341.prerm @@ -0,0 +1,34 @@ +#!/bin/sh +# prerm script for fbcp-ili9341 +# +# see: dh_installdeb(1) + +set -e + +case "$1" in + remove|upgrade|deconfigure) + if [ -f "/usr/sbin/fbcp-ili9341-module" ]; then + "/usr/sbin/fbcp-ili9341-module" stop + fi + if [ -f "/boot/config.txt.fbcp-orig" ]; then + rm -fv "/boot/config.txt" && mv -v "/boot/config.txt.fbcp-orig" "/boot/config.txt" + else + echo "/boot/config.txt.fbcp-orig not found." >&2 + fi + ;; + + failed-upgrade) + ;; + + *) + echo "prerm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/debian/fbcp-ili9341.templates b/debian/fbcp-ili9341.templates new file mode 100644 index 0000000..fd6cbe8 --- /dev/null +++ b/debian/fbcp-ili9341.templates @@ -0,0 +1,42 @@ +Template: fbcp-ili9341/ishat +Type: boolean +Default: true +Description: Is your controller stacked on top of the Pi? + +Template: fbcp-ili9341/hat +Type: select +Choices: ADAFRUIT_ILI9341_PITFT, ADAFRUIT_HX8357D_PITFT, FREEPLAYTECH_WAVESHARE32B, WAVESHARE35B_ILI9486, TONTEC_MZ61581, WAVESHARE_ST7789VW_HAT, WAVESHARE_ST7735S_HAT, KEDEI_V63_MPI3501 +Description: Which type of LCD display: + see the list at: + https://github.com/kpishere/fbcp-ili9341 + +Template: fbcp-ili9341/wired +Type: select +Choices: ILI9341, ILI9340, HX8357D, SSD1351, ILI9486, ILI9486L, ST7735R, MZ61581, ST7789, ST7789VW, ST7735S, MPI3501 +Description: Which type of LCD display: + see the list at: + https://github.com/kpishere/fbcp-ili9341 + +Template: fbcp-ili9341/divisor +Type: string +Default: 12 +Description: Enter divisor of the core_freq: + This must be an even number. + Sets the clock divisor number used with + core_freq from config.txt. + +Template: fbcp-ili9341/statistics +Type: select +Choices: 0, 1, 2 +Default: 0 +Description: Show statistics overlay? + Enable this to show a statistics overlay on + top of the screen. + The higher the number the more is shown. + . + +Template: fbcp-ili9341/hdmi +Type: boolean +Default: true +Description: Configure config.txt for hdmi size? + diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..1e595a6 --- /dev/null +++ b/debian/rules @@ -0,0 +1,50 @@ +#!/usr/bin/make -f +# See debhelper(7) (uncomment to enable) +# output every command that modifies files on the build system. +#export DH_VERBOSE = 1 + +include /usr/share/dpkg/default.mk + +# see FEATURE AREAS in dpkg-buildflags(1) +#export DEB_BUILD_MAINT_OPTIONS = hardening=+all + +# see ENVIRONMENT in dpkg-buildflags(1) +# package maintainers to append CFLAGS +#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic +# package maintainers to append LDFLAGS +#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed + + +%: + dh $@ --with dkms + +override_dh_auto_install: + cp -f debian/$(DEB_SOURCE)-module debian/$(DEB_SOURCE)/usr/sbin/$(DEB_SOURCE)-module + install -d debian/$(DEB_SOURCE)/etc/modules-load.d/ + cp -f debian/$(DEB_SOURCE).conf debian/$(DEB_SOURCE)/etc/modules-load.d/ + install -d debian/$(DEB_SOURCE)/usr/src/$(DEB_SOURCE)-$(DEB_VERSION) + cp -f spi.h debian/$(DEB_SOURCE)/usr/src/$(DEB_SOURCE)-$(DEB_VERSION) + mv -f util/tcTest debian/$(DEB_SOURCE)/usr/sbin/ + mv -f util/tcCalib debian/$(DEB_SOURCE)/usr/sbin/ + + +override_dh_install: + dh_install -Xdebian -X.git -X.o * usr/share/$(DEB_SOURCE) + dh_install -X.sh kernel usr/src/$(DEB_SOURCE)-$(DEB_VERSION) + find debian/$(DEB_SOURCE)/usr/share/$(DEB_SOURCE) -type f -perm -5 -print0 2>/dev/null | xargs -0r chmod a-X + +override_dh_dkms: + dh_dkms -V "${DEB_VERSION}" + +# Configure and compile util +override_dh_auto_configure: + dh_auto_configure --builddirectory=util --buildsystem=makefile -- + +override_dh_auto_build: + dh_auto_build --builddirectory=util --buildsystem=makefile -- + +override_dh_strip: + strip debian/$(DEB_SOURCE)/usr/sbin/tcTest + strip debian/$(DEB_SOURCE)/usr/sbin/tcCalib + +override_dh_makeshlibs override_dh_shlibdeps: diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/debian/tcCalib.1 b/debian/tcCalib.1 new file mode 100644 index 0000000..6189d91 --- /dev/null +++ b/debian/tcCalib.1 @@ -0,0 +1,10 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" (C) Copyright 2019 kunzol , +.\" +.TH tcCalib 1 "October 5 2019" +.SH NAME +tcCalib \- calibrate touchscreen +.SH SYNOPSIS +.B tcCalib +.SH DESCRIPTION +With this program you calibrate the touchscreen. diff --git a/debian/tcTest.1 b/debian/tcTest.1 new file mode 100644 index 0000000..faf9201 --- /dev/null +++ b/debian/tcTest.1 @@ -0,0 +1,11 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" (C) Copyright 2019 kunzol , +.\" +.TH tcTest 1 "October 5 2019" +.SH NAME +tcTest \- test display +.SH SYNOPSIS +.B tcTest +.SH DESCRIPTION +With this program you send test patterns to +the display. From 7cced72ffcb7950994569e09b71cc22e67cd0ea3 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Tue, 29 Oct 2019 01:53:40 +0000 Subject: [PATCH 51/52] remove bloker for uninstalled state --- kernel/Makefile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/kernel/Makefile b/kernel/Makefile index 0df7743..e60f1eb 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -1,4 +1,4 @@ -obj-m := bcm2835_spi_display.o +obj-m:= bcm2835_spi_display.o CFLAGS_bcm2835_spi_display.o := -O3 -std=gnu99 -Wno-declaration-after-statement -DKERNEL_MODULE=1 -DILI9341=1 -DADAFRUIT_ILI9341_PITFT=1 -DSPI_BUS_CLOCK_DIVISOR=48 @@ -16,17 +16,17 @@ uninstall: stop @depmod -a start: - @modprobe -v $(obj-m:%.o=%) + modprobe -v $(obj-m:%.o=%) stop: @pgrep "fbcp-ili9341.*" && pkill "fbcp-ili9341.*" || true @pgrep "fbcp-ili9341.*" && sleep 5 && pkill -KILL "fbcp-ili9341.*" || true - @modprobe -vr $(obj-m:%.o=%) + @modprobe -vr $(obj-m:%.o=%) || true status: - @modinfo $(obj-m:%.o=%) - @echo loaded: - @lsmod | grep $(obj-m:%.o=%) || echo not loaded + modinfo $(obj-m:%.o=%) + echo loaded: + lsmod | grep $(obj-m:%.o=%) || echo not loaded debug: $(obj-m:%.o=%.ko) @objdump -dS $< > $(obj-m:%.o=%.S) From b96c4d187cad60fa9952f041cf609bcdf643ed84 Mon Sep 17 00:00:00 2001 From: Kevin Peck Date: Tue, 29 Oct 2019 01:57:03 +0000 Subject: [PATCH 52/52] rehide commands --- kernel/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/Makefile b/kernel/Makefile index e60f1eb..be8ba68 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -16,7 +16,7 @@ uninstall: stop @depmod -a start: - modprobe -v $(obj-m:%.o=%) + @modprobe -v $(obj-m:%.o=%) stop: @pgrep "fbcp-ili9341.*" && pkill "fbcp-ili9341.*" || true @@ -24,9 +24,9 @@ stop: @modprobe -vr $(obj-m:%.o=%) || true status: - modinfo $(obj-m:%.o=%) - echo loaded: - lsmod | grep $(obj-m:%.o=%) || echo not loaded + @modinfo $(obj-m:%.o=%) + @echo loaded: + @lsmod | grep $(obj-m:%.o=%) || echo not loaded debug: $(obj-m:%.o=%.ko) @objdump -dS $< > $(obj-m:%.o=%.S)