cmake_cpputest_template/inc/extralibs/ch32v003_touch.h

224 lines
10 KiB
C

#ifndef _CH32V003_TOUCH_H
#define _CH32V003_TOUCH_H
/** ADC-based Capactive Touch Control.
see cap_touch_adc.c for an example.
// Enable GPIOD, C and ADC
RCC->APB2PCENR |= RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOC | RCC_APB2Periph_ADC1;
InitTouchADC();
// Then do this any time you want to read some touches.
sum[0] += ReadTouchPin( GPIOA, 2, 0, iterations );
sum[1] += ReadTouchPin( GPIOA, 1, 1, iterations );
sum[2] += ReadTouchPin( GPIOC, 4, 2, iterations );
sum[3] += ReadTouchPin( GPIOD, 2, 3, iterations );
sum[4] += ReadTouchPin( GPIOD, 3, 4, iterations );
sum[5] += ReadTouchPin( GPIOD, 5, 5, iterations );
sum[6] += ReadTouchPin( GPIOD, 6, 6, iterations );
sum[7] += ReadTouchPin( GPIOD, 4, 7, iterations );
*/
#define TOUCH_ADC_SAMPLE_TIME 2 // Tricky: Don't change this without a lot of experimentation.
// Can either be 0 or 1.
// If 0: Measurement low and rises high. So more pressed is smaller number.
// If 1: Higher number = harder press. Good to pair with TOUCH_FLAT.
// If you are doing more prox, use mode 0, otherwise, use mode 1.
#define TOUCH_SLOPE 1
// If you set this to 1, it will glitch the line, so it will only read
// anything reasonable if the capacitance can overcome that initial spike.
// Typically, it seems if you use this you probbly don't need to do
// any pre-use calibration.
#define TOUCH_FLAT 0
// Macro used for force-alingining ADC timing
#define FORCEALIGNADC \
asm volatile( \
"\n\
.balign 4\n\
andi a2, %[cyccnt], 3\n\
c.slli a2, 1\n\
c.addi a2, 12\n\
auipc a1, 0\n\
c.add a2, a1\n\
jalr a2, 1\n\
.long 0x00010001\n\
.long 0x00010001\n\
" ::[cyccnt] "r"(SysTick->CNT) : "a1", "a2");
static void InitTouchADC();
void InitTouchADC()
{
// ADCCLK = 24 MHz => RCC_ADCPRE = 0: divide sys clock by 2
RCC->CFGR0 &= ~(0x1F << 11);
// Set up single conversion on chl 2
ADC1->RSQR1 = 0;
ADC1->RSQR2 = 0;
// turn on ADC and set rule group to sw trig
ADC1->CTLR2 |= ADC_ADON | ADC_EXTSEL;
// Reset calibration
ADC1->CTLR2 |= ADC_RSTCAL;
while (ADC1->CTLR2 & ADC_RSTCAL)
;
// Calibrate
ADC1->CTLR2 |= ADC_CAL;
while (ADC1->CTLR2 & ADC_CAL)
;
}
// Run from RAM to get even more stable timing.
// This function call takes about 8.1uS to execute.
static uint32_t ReadTouchPin(GPIO_TypeDef *io, int portpin, int adcno, int iterations) __attribute__((noinline, section(".srodata")));
uint32_t ReadTouchPin(GPIO_TypeDef *io, int portpin, int adcno, int iterations)
{
uint32_t ret = 0;
__disable_irq();
FORCEALIGNADC
ADC1->RSQR3 = adcno;
ADC1->SAMPTR2 = TOUCH_ADC_SAMPLE_TIME << (3 * adcno);
__enable_irq();
uint32_t CFGBASE = io->CFGLR & (~(0xf << (4 * portpin)));
uint32_t CFGFLOAT = ((GPIO_CFGLR_IN_PUPD) << (4 * portpin)) | CFGBASE;
uint32_t CFGDRIVE = (GPIO_CFGLR_OUT_2Mhz_PP) << (4 * portpin) | CFGBASE;
// If we run multiple times with slightly different wait times, we can
// reduce the impact of the ADC's DNL.
#if TOUCH_FLAT == 1
#define RELEASEIO \
io->BSHR = 1 << (portpin + 16 * TOUCH_SLOPE); \
io->CFGLR = CFGFLOAT;
#else
#define RELEASEIO \
io->CFGLR = CFGFLOAT; \
io->BSHR = 1 << (portpin + 16 * TOUCH_SLOPE);
#endif
#define INNER_LOOP(n) \
{ \
/* Only lock IRQ for a very narrow window. */ \
__disable_irq(); \
FORCEALIGNADC \
\
/* Tricky - we start the ADC BEFORE we transition the pin. By doing \
this We are catching it onthe slope much more effectively. */ \
ADC1->CTLR2 = ADC_SWSTART | ADC_ADON | ADC_EXTSEL; \
\
ADD_N_NOPS(n) \
\
RELEASEIO \
\
/* Sampling actually starts here, somewhere, so we can let other \
interrupts run */ \
__enable_irq(); \
while (!(ADC1->STATR & ADC_EOC)) \
; \
io->CFGLR = CFGDRIVE; \
io->BSHR = 1 << (portpin + (16 * (1 - TOUCH_SLOPE))); \
ret += ADC1->RDATAR; \
}
int i;
for (i = 0; i < iterations; i++)
{
// Wait a variable amount of time based on loop iteration, in order
// to get a variety of RC points and minimize DNL.
INNER_LOOP(0);
INNER_LOOP(2);
INNER_LOOP(4);
}
return ret;
}
// Run from RAM to get even more stable timing.
// This function call takes about 8.1uS to execute.
static uint32_t ReadTouchPinSafe(GPIO_TypeDef *io, int portpin, int adcno, int iterations) __attribute__((noinline, section(".srodata")));
uint32_t ReadTouchPinSafe(GPIO_TypeDef *io, int portpin, int adcno, int iterations)
{
uint32_t ret = 0;
ADC1->RSQR3 = adcno;
ADC1->SAMPTR2 = TOUCH_ADC_SAMPLE_TIME << (3 * adcno);
// If we run multiple times with slightly different wait times, we can
// reduce the impact of the ADC's DNL.
#define INNER_LOOP_SAFE(n) \
{ \
/* Only lock IRQ for a very narrow window. */ \
__disable_irq(); \
\
FORCEALIGNADC \
\
/* Tricky - we start the ADC BEFORE we transition the pin. By doing \
this We are catching it onthe slope much more effectively. */ \
ADC1->CTLR2 = ADC_SWSTART | ADC_ADON | ADC_EXTSEL; \
\
ADD_N_NOPS(n) \
\
io->CFGLR = ((GPIO_CFGLR_IN_PUPD) << (4 * portpin)) | (io->CFGLR & (~(0xf << (4 * portpin)))); \
io->BSHR = 1 << (portpin + 16 * TOUCH_SLOPE); \
\
/* Sampling actually starts here, somewhere, so we can let other \
interrupts run */ \
__enable_irq(); \
while (!(ADC1->STATR & ADC_EOC)) \
; \
__disable_irq(); \
io->CFGLR = (GPIO_CFGLR_OUT_2Mhz_PP) << (4 * portpin) | (io->CFGLR & (~(0xf << (4 * portpin)))); \
__enable_irq(); \
io->BSHR = 1 << (portpin + (16 * (1 - TOUCH_SLOPE))); \
ret += ADC1->RDATAR; \
}
int i;
for (i = 0; i < iterations; i++)
{
// Wait a variable amount of time based on loop iteration, in order
// to get a variety of RC points and minimize DNL.
INNER_LOOP_SAFE(0);
INNER_LOOP_SAFE(2);
INNER_LOOP_SAFE(4);
}
return ret;
}
#endif
/*
* MIT License
*
* Copyright (c) 2023 Valve Corporation
*
* 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.
*/