2025-07-23 2:52 PM - edited 2025-07-25 8:11 AM
tldr; if you're using a task to service the touchscreen (do the I2C reads/writes), how do you ensure it plays well with TouchGFX? I am finding that it would have to consistently run faster than TouchGFX (and perhaps be a higher priority) to be able to get it a new (x, y) coordinate every tick, so that TouchGFX doesn't consider the touch to be over and release the click.
Long version:
I am using a touchscreen with a GT911 controller on a custom ST board. We initially architected it such that a touch generates an interrupt, and in that interrupt we do two I2C reads and one write. Bad practice to do anything long in an interrupt, I know, but we were following guidance that we found elsewhere. This is what our ISR did:
// Function to read out touch coordinates when a rising edge is detected on CTP_INT.
// This is called from the CTP_INT GPIO ISR.
bool Touch_Callback(void) {
uint8_t number_of_coordinates;
uint8_t point_info;
// Read the touchpoint information register
bool success = _ReadReg(&point_info, GT911_POINT_INFO);
//Check the buffer status bit to see if coordinate information is ready to read
if (success && (point_info & TOUCHPOINT_COORD_READY_MASK) != 0) {
// Find the number of coordinates available
number_of_coordinates = point_info & TOUCHPOINT_NUMBER_RCVD_MASK;
// We're set up to read 1 coordinate. TouchGFX does not support multi-touch.
if (number_of_coordinates != 0 && _ReadTouchPoint(GT911_POINT_1, &coordinate_x, &coordinate_y)) {
// Let's TouchGFX know there is data available to be read
touch_data_available = true;
}
// Need to call this to clear the buffer status bit
success &= _WriteReg(GT911_POINT_INFO, 0x00);
}
return success;
}
We're now trying to resolve this goof by setting a boolean flag in the interrupt, but only reading and clearing the registers in a separate task. This task has a lower priority (2) than the TouchGFX task (24) because as far as I remember TouchGFX needs to be the highest priority. New code:
// Function called from the CTP_INT GPIO ISR.
// This function should only set a flag - no I2C operations allowed in ISR.
void Touch_Callback(void) {
// Simply set the flag to indicate processing is needed
// The actual I2C operations will be done in Touch_ProcessLoop
touch_interrupt_pending = true;
}
// Function called continuously from main task to process touch interrupts
// This is where all I2C operations are performed
void Touch_ProcessLoop(void)
{
// Check if we have a pending touch interrupt to process
if (touch_interrupt_pending) {
uint8_t number_of_coordinates;
uint8_t point_info;
touch_interrupt_pending = false;
// Read the touchpoint information register
bool success = _ReadReg(&point_info, GT911_POINT_INFO);
// Check the buffer status bit to see if coordinate information is ready to read
if (success && (point_info & TOUCHPOINT_COORD_READY_MASK) != 0) {
// Find the number of coordinates available
number_of_coordinates = point_info & TOUCHPOINT_NUMBER_RCVD_MASK;
// We're set up to read 1 coordinate. TouchGFX does not support multi-touch.
if (number_of_coordinates != 0 && _ReadTouchPoint(GT911_POINT_1, &coordinate_x, &coordinate_y)) {
// Let's TouchGFX know there is data available to be read
touch_data_available = true;
}
// Need to call this to clear the buffer status bit
_WriteReg(GT911_POINT_INFO, 0x00);
}
}
}
and `Touch_ProcessLoop` gets called from the I2C3 task, as such:
portTASK_FUNCTION(_I2C3ManagerTask, pvParameters)
{
(void) pvParameters;
while (1)
{
Touch_ProcessLoop();
OtherI2C3Thing_ProcessLoop();
osDelay(I2C3MANAGER_TASK_RATE_MSEC / portTICK_PERIOD_MS);
}
}
I understand that the TouchGFX task is running every 16ms, so I optimistically thought perhaps I could run this task at 15ms or 8ms and be able to service TouchGFX every tick in STM32TouchController.cpp, but that's not the case.
Even as the highest priority task (that isn't TouchGFX), and running every 8ms, there's an easy failure case - touching and holding the screen. TouchGFX will occassionally return false from sampleTouch and the touch will be considered a click and processed by the system. In my UI, the Setting's screen's 'X' (cancel) button leads to the home screen, and in that same location on the home sceen is a 'Settings' button, so if I hold my finger on that spot the screens will awkwardly cycle between Settings and Home screens.
Here's the remainder of the code,
// Function called by TouchGFX to determine if there is touch data ready to read
bool Touch_Detected(void) {
if (touch_data_available) {
touch_data_available = false;
return true;
} else {
return false;
}
}
// Function called by TouchGFX to read out coordinate data
void Touch_GFXGetData(int32_t *x, int32_t *y) {
*x = (int32_t) coordinate_x;
*y = (int32_t) coordinate_y;
}
bool STM32TouchController::sampleTouch(int32_t& x, int32_t& y)
{
/**
* By default sampleTouch returns false,
* return true if a touch has been detected, otherwise false.
*
* Coordinates are passed to the caller by reference by x and y.
*
* This function is called by the TouchGFX framework.
* By default sampleTouch is called every tick, this can be adjusted by HAL::setTouchSampleRate(int8_t);
*
*/
int32_t xdata;
int32_t ydata;
// If a touch has been detected then read the coordinates and pass them to TouchGFX
if (Touch_Detected())
{
Touch_GFXGetData(&xdata, &ydata);
x = xdata;
y = ydata;
return true;
}
else
{
return false;
}
}
How is everyone else solving this? I feel like I must be missing something trivial.
Solved! Go to Solution.
2025-07-25 8:39 AM
@jchernus-fikst wrote:I'm thinking for now that I will simply latch whether there's a touch detected, and if TouchGFX asks for a new touch and the touchscreen task hasn't been able to run - I'll offer it the previous touch's coordinates.
That's how it's supposed to be.
If you touch a button and want to cancel a touch you can keep touching the screen and drag your finger outside of the button. If the Touchcontroller reports intermittend touch state to TouchGFX this won't work. So keep reporting the old touch value and touch coordinates. But update them enough.
2025-07-25 8:12 AM
@ferro or @unsigned_char_array , you've both been quite helpful in the past. Any recommendations?
2025-07-25 8:20 AM
I'm thinking for now that I will simply latch whether there's a touch detected, and if TouchGFX asks for a new touch and the touchscreen task hasn't been able to run - I'll offer it the previous touch's coordinates.
2025-07-25 8:27 AM - edited 2025-07-25 8:31 AM
I don't use an RTOS for our custom board. Touch screen is serviced in main loop prior to servicing TouchGFX. Touch I2C is interrupt based so it is not blocking. It is sampled/polled faster than the refresh-rate of the screen to reduce latency. I think I sample it every 3 milliseconds (unless I2C is still in progress), which is a bit overkill. It is resistive touch. The first sample when a touch is detected is different from subsequent coordinates, so I ignore the first touch event and only send touch coordinates to touchgfx if touch event is going on longer than 1 cycle.
I only start touch I2C transactions when FrontPorchEntered is active. I forgot why, but I think I got glitches on the screen once in a while due to the I2C interrupt causing too slow response to LTDC interrupt.
2025-07-25 8:39 AM
@jchernus-fikst wrote:I'm thinking for now that I will simply latch whether there's a touch detected, and if TouchGFX asks for a new touch and the touchscreen task hasn't been able to run - I'll offer it the previous touch's coordinates.
That's how it's supposed to be.
If you touch a button and want to cancel a touch you can keep touching the screen and drag your finger outside of the button. If the Touchcontroller reports intermittend touch state to TouchGFX this won't work. So keep reporting the old touch value and touch coordinates. But update them enough.
2025-07-25 9:02 AM
Hi @jchernus-fikst, I dont use touchscreen so cant help here, sorry.
2025-07-25 10:55 AM
Bad advice in the ToughGFX docs then: