From 8697e9aa352429b976ab4f5eca7326b676e04d92 Mon Sep 17 00:00:00 2001 From: cnlohr <lohr85@gmail.com> Date: Wed, 13 Sep 2023 03:45:14 -0400 Subject: [PATCH] Move touch to header. --- examples/cap_touch_adc/cap_touch_adc.c | 123 +-------------------- extralibs/ch32v003_touch.h | 147 +++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 122 deletions(-) create mode 100644 extralibs/ch32v003_touch.h diff --git a/examples/cap_touch_adc/cap_touch_adc.c b/examples/cap_touch_adc/cap_touch_adc.c index 2bde0c9..98457e9 100644 --- a/examples/cap_touch_adc/cap_touch_adc.c +++ b/examples/cap_touch_adc/cap_touch_adc.c @@ -21,101 +21,7 @@ #include "ch32v003fun.h" #include <stdio.h> -#define ADC_SAMPLE_TIME 2 // Tricky: Don't change this without a lot of experimentation. -#define MAX_SCALECHECK 4 - -// 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 - -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. -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; - - ADC1->RSQR3 = adcno; - ADC1->SAMPTR2 = ADC_SAMPLE_TIME<<(3*adcno); - - 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->OUTDR = 1<<(portpin+16*TOUCH_SLOPE); io->CFGLR = CFGFLOAT; -#else -#define RELEASEIO io->CFGLR = CFGFLOAT; io->OUTDR = 1<<(portpin+16*TOUCH_SLOPE); -#endif - -#define INNER_LOOP( n ) \ - { \ - /* Only lock IRQ for a very narrow window. */ \ - __disable_irq(); \ - \ - /* 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->OUTDR = 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; -} +#include "ch32v003_touch.h" int main() { @@ -133,8 +39,6 @@ int main() { uint32_t sum[8] = { 0 }; - int j; - uint32_t start = SysTick->CNT; // Sampling all touch pads, 3x should take 6030 cycles, and runs at about 8kHz @@ -156,28 +60,3 @@ int main() } } -/* - * 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. - */ - - diff --git a/extralibs/ch32v003_touch.h b/extralibs/ch32v003_touch.h new file mode 100644 index 0000000..b0c3e90 --- /dev/null +++ b/extralibs/ch32v003_touch.h @@ -0,0 +1,147 @@ +#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 + +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; + + ADC1->RSQR3 = adcno; + ADC1->SAMPTR2 = TOUCH_ADC_SAMPLE_TIME<<(3*adcno); + + 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->OUTDR = 1<<(portpin+16*TOUCH_SLOPE); io->CFGLR = CFGFLOAT; +#else +#define RELEASEIO io->CFGLR = CFGFLOAT; io->OUTDR = 1<<(portpin+16*TOUCH_SLOPE); +#endif + +#define INNER_LOOP( n ) \ + { \ + /* Only lock IRQ for a very narrow window. */ \ + __disable_irq(); \ + \ + /* 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->OUTDR = 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; +} + +#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. + */ + -- GitLab