Arduino_Core_STM32 icon indicating copy to clipboard operation
Arduino_Core_STM32 copied to clipboard

FR: TSC

Open sixtyfive opened this issue 4 years ago • 6 comments

Knowing about #247, which seems to be about touch screens only, I'd like to request the feature of touch sensing control to be added to Arduino_Core_STM32. The TSC is part of many STM32s, even down to the STM32F042x4 series, and using touch buttons is quite popular, so I think it would be a good thing to have available without having to resort to direct HAL programming.

sixtyfive avatar Jun 12 '20 14:06 sixtyfive

Any contribution are welcome.

fpistm avatar Jun 12 '20 15:06 fpistm

According to one of the datasheets, it's as simple as copy-pasting the following init code:

/* Configure TCS */
/* With a charge transfer around 2.5μs */
/* (1) Select fPGCLK = fHCLK/32,
       Set pulse high = 2xtPGCLK,Master
       Set pulse low = 2xtPGCLK
       Set Max count value = 16383 pulses
       Enable TSC */
/* (2) Disable hysteresis */
/* (3) Enable end of acquisition IT */
/* (4) Sampling enabled, G2IO4 */
/* (5) Channel enabled, G2IO3 */
/* (6) Enable group, G2 */
TSC->CR = TSC_CR_PGPSC_2 | TSC_CR_PGPSC_0 | TSC_CR_CTPH_0 | TSC_CR_CTPL_0 
        | TSC_CR_MCV_2 | TSC_CR_MCV_1 | TSC_CR_TSCE; /* (1) */
TSC->IOHCR &= (uint32_t)(~(TSC_IOHCR_G2_IO4 | TSC_IOHCR_G2_IO3)); /* (2) */
TSC->IER = TSC_IER_EOAIE; /* (3) */
TSC->IOSCR = TSC_IOSCR_G2_IO4; /* (4) */
TSC->IOCCR = TSC_IOCCR_G2_IO3; /* (5) */
TSC->IOGCSR |= TSC_IOGCSR_G2E; /* (5) */

And then creating (???) an interrupt:

/* End of acquisition flag */
if((TSC->ISR & TSC_ISR_EOAF) == TSC_ISR_EOAF) {
  TSC->ICR = TSC_ICR_EOAIC; /* Clear flag */
  AcquisitionValue = TSC->IOGXCR[1]; /* Get G2 counter value */
}

In its most simple form, what would be required to get from that to something like this:

bool led_state = false;

void toggle_led()
{
  led_state = !led_state;
}

void main()
{
  /* i didn't check if PB14 and PB15 match with the above example! */
  pinMode(PB14, INPUT);
  pinMode(PB15, INPUT);
  attachTSC(digitalPinToInterrupt(PB14), digitalPinToInterrupt(PB15)), toggle_led, CHANGE);
}

void loop()
{
  digitalWrite(LED_BUILTIN, led_state);
}

With my extremely limited understanding of the code from the datasheet, it seems to me that just implementing that imaginary attachTSC function might somehow already be enough?

sixtyfive avatar Jun 12 '20 16:06 sixtyfive

Sorry to revive an old issue. I don't think it's necessary to play around with the registers, or sift through the 1000 page reference manuals; The repository already hosts the *_hal_tsc.c drivers. While I don't use stm32duino, I have this bit of code in my STM32CubeIDE project that returns the value of the counter, based entirely off the instructions given at the top of this file specifically (my project uses the F303K8 Nucleo board):

HAL_StatusTypeDef tsc_status; /* HAL_OK       = 0x00U,
                                 HAL_ERROR    = 0x01U,
                                 HAL_BUSY     = 0x02U,
                                 HAL_TIMEOUT  = 0x03 */
TSC_GroupStatusTypeDef tsc_groupstatus; /* TSC_GROUP_ONGOING   = 0x00UL,
                                           TSC_GROUP_COMPLETED = 0x01UL */
tsc_status = HAL_TSC_IODischarge(&htsc,ENABLE); // DISCHARGE STATUS
HAL_Delay(1);
tsc_status = HAL_TSC_Start(&htsc); // START STATUS
tsc_status = HAL_TSC_PollForAcquisition(&htsc); // POLL STATUS
tsc_groupstatus = HAL_TSC_GroupGetStatus(&htsc,TSC_GROUP3_IDX); // GROUP STATUS
tsc_value = HAL_TSC_GroupGetValue(&htsc,TSC_GROUP3_IDX); // VALUE

The TSC configuration and initialization is done automatically by configuring the .ioc file in ST's IDE (even the htsc variable comes predefined), so I'm not really sure if anything extra is required in stm32duino.

NB: In my project there is also a tsl_user.c file with some supposedly easy-to-use functions, but I can't make heads or tails from it. One should be able to use those functions alongside the "STM Studio" program to help calibrate the design (as per AN4316), but since I can't get it to work, I've opted to ignore the whole shebang.

Hope any of this helps. I agree it would be great to have touch sensing implemented in a project like this.

someone755 avatar Jul 13 '22 21:07 someone755

I have a similar issue with a NUCLEO-F072RB
The HAL_TSC_GroupGetStatus(&TscHandle, TSC_GROUP1_IDX) always returns TSC_GROUP_ONGOING

did you make it work?

johnnytolengo avatar Apr 02 '23 21:04 johnnytolengo

Sorry, I moved on to esp32.

sixtyfive avatar Apr 10 '23 17:04 sixtyfive

I was able to get this to work with a STM32WB5MM-DK.

I did the following (it took me awhile and I went through quite a bit of trial and error before I was able to get things working):

  1. Copied the default conf for my hal https://github.com/stm32duino/Arduino_Core_STM32/blob/main/system/STM32WBxx/stm32wbxx_hal_conf_default.h adding it to the project as hal_conf_custom.h (see https://github.com/stm32duino/Arduino_Core_STM32/blob/main/system/STM32WBxx/stm32wbxx_hal_conf.h#L7)
  2. Moved the following out of the #if 0 block to under the #include "stm32yyxx_hal_conf.h":
#define HAL_GPIO_MODULE_ENABLED
#define HAL_TSC_MODULE_ENABLED
  1. Added some global variables to the top of my ino file:
const int touchThreshold = 50;
TSC_HandleTypeDef htsc;
TSC_IOConfigTypeDef IoConfig;
int baselineTouch = 0;
  1. I added this logic for the interrupt handler but I couldn't get the interrupt logic itself to work (would just trigger immediately for me when I started with interrupt enabled and I don't understand the logic well enough at this point to debug in further). Instead of relying on the interrupt for now I'm handling the logic in a way based on approach given via someone755's comment (a step further below in my loop logic).
extern "C" {
  void TSC_IRQHandler(void) {
    HAL_TSC_IRQHandler(&htsc);
  }
}
  1. In my setup section setup the pins for my motherboard based on what I saw in the schematic (PC7 is the shield one)
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  GPIO_InitStruct.Pin = GPIO_PIN_6;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF9_TSC;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_7;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF9_TSC;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
  
  GPIO_InitStruct.Pin = GPIO_PIN_10;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF9_TSC;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_11;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF9_TSC;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

  /* TSC interrupt Init */  
  __HAL_RCC_TSC_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();

  htsc.Instance = TSC;
  htsc.Init.CTPulseHighLength = TSC_CTPH_1CYCLE;
  htsc.Init.CTPulseLowLength = TSC_CTPL_1CYCLE;
  htsc.Init.SpreadSpectrum = DISABLE;
  htsc.Init.SpreadSpectrumDeviation = 1;
  htsc.Init.SpreadSpectrumPrescaler = TSC_SS_PRESC_DIV1;
  htsc.Init.PulseGeneratorPrescaler = TSC_PG_PRESC_DIV4;
  htsc.Init.MaxCountValue = TSC_MCV_8191;
  htsc.Init.IODefaultMode = TSC_IODEF_OUT_PP_LOW;
  htsc.Init.SynchroPinPolarity = TSC_SYNC_POLARITY_FALLING;
  htsc.Init.AcquisitionMode = TSC_ACQ_MODE_NORMAL;
  htsc.Init.MaxCountInterrupt = DISABLE;
  htsc.Init.ChannelIOs = 0;
  htsc.Init.ShieldIOs = 0;
  htsc.Init.SamplingIOs = 0;
  if (HAL_TSC_Init(&htsc) != HAL_OK)
  {
    Error_Handler();
  }
  HAL_NVIC_SetPriority(TSC_IRQn, 1, 0);
  HAL_NVIC_EnableIRQ(TSC_IRQn);

  IoConfig.ChannelIOs = TSC_GROUP6_IO2;
  IoConfig.ShieldIOs = TSC_GROUP4_IO2;
  IoConfig.SamplingIOs = TSC_GROUP4_IO1|TSC_GROUP6_IO1;

  if (HAL_TSC_IOConfig(&htsc, &IoConfig) != HAL_OK) {
    /* Initialization Error */
    Error_Handler();
  }
  1. In my loop logic to fetch if I have a touch:
  uint32_t tsc_status;
  uint32_t tsc_groupstatus;
  uint32_t tsc_value;
  tsc_status = HAL_TSC_IODischarge(&htsc,ENABLE); // DISCHARGE STATUS
  HAL_Delay(1);
  tsc_status = HAL_TSC_Start_IT(&htsc); // START STATUS
  tsc_status = HAL_TSC_PollForAcquisition(&htsc); // POLL STATUS
  tsc_groupstatus = HAL_TSC_GroupGetStatus(&htsc,TSC_GROUP6_IDX); // GROUP STATUS
  tsc_value = HAL_TSC_GroupGetValue(&htsc,TSC_GROUP6_IDX); // VALUE
  baselineTouch = max<uint32_t>(baselineTouch, tsc_value);
  if (baselineTouch > 0 && tsc_value <= baselineTouch - touchThreshold) {
    STLink.println("Touch detected");
  }

Logic works for me with the tsc_value returned when not touching as a mostly consistent value and once I touch it I noticed it drops about 80 or so which I then used as the trigger. There was additional touch sensing logic from the STM32 examples but I struggled to even get this part and touching itself working so I am just happy it works consistently with this approach and stm32duino.

Passing along in case it's helpful to anyone else. Works with touch detection of button presses at least as a start here. Quite a bit of code to figure out if one button is pressed but I felt absolutely terrible about leaving the giant touch button unused on my development kit so pressed on to this point of at least working.

Timo614 avatar Jan 30 '24 00:01 Timo614