Pic18 CTMU demo code

Thread Starter


Joined Aug 27, 2009
The CTMU module available on some newer PIC18 chips is a bag full of tricks.

This simple C18 PIC18F46K22 demo only uses the capacitive touch function to read a single touch pad, return a button pressed flag and a proximity value.
First image:

The basic application is in this Microchip app-note.

This version uses timer0 interrupts instead of delay loops and the ADC interrupt to signal the conversion so it runs as a background process updating a 'button pressed' flag and result variables.

Device setup:

Rich (BB code):
int ctmu_setup(unsigned char current)
  //CTMUCONH/1 - CTMU Control registers
  CTMUCONH = 0x00; //make sure CTMU is disabled
  CTMUCONL = 0x90;
  //CTMU continues to run when emulator is stopped,CTMU continues
  //to run in idle mode,Time Generation mode disabled, Edges are blocked
  //No edge sequence order, Analog current source not grounded, trigger
  //output disabled, Edge2 polarity = positive level, Edge2 source =
  //source 0, Edge1 polarity = positive level, Edge1 source = source 0,
  //CTMUICON - CTMU Current Control Register
  CTMUICON = 0x01;       //.55uA, Nominal - No Adjustment default
  charge_time=TIMERCHARGE_BASE;   // slower
  if (current == 0x02) {
   CTMUICON = 0x02;  //5.5uA, Nominal - No Adjustment
   charge_time=TIMERCHARGE_BASE_X10; // faster
  //Set up AD converter;
  // Configure AN0 as an analog channel
  // ADCON2
  ADCON2bits.ADFM=1; // Results format 1= Right justified
  ADCON2bits.ACQT=1; // Acquition time 7 = 20TAD 2 = 4TAD 1=2TAD
  ADCON2bits.ADCS=2; // Clock conversion bits 6= FOSC/64 2=FOSC/32
  // ADCON1
  ADCON1bits.PVCFG0 =0; // Vref+ = AVdd
  ADCON1bits.NVCFG1 =0; // Vref- = AVss
  // ADCON0
  ADCON0bits.CHS=0;  // Select ADC channel
  ADCON0bits.ADON=1;  // Turn on ADC
  PIE1bits.ADIE=1; // enable ADC int
 return 0;
isr code (with debug code flags and leds on outputs) :
Rich (BB code):
#pragma interrupt high_handler
void high_handler (void)
        if ( INTCONbits.TMR0IF ) {        // check timer0 irq 
   if (!CTMUCONHbits.IDISSEN) {      // charge cycle timer0 int, because not shorting the CTMU voltage.
    CTMUCONLbits.EDG1STAT = 0;       // Stop charging touch circuit
    TIME_CHARGE=FALSE;        // clear charging flag
    CTMU_WORKING=TRUE;        // set working flag, doing touch ADC conversion
    // configure ADC for next reading
    ADCON0bits.CHS=0;         // Select ADC channel
    ADCON0bits.ADON=1;         // Turn on ADC
    ADCON0bits.GO=1;         // and begin A/D conv, will set adc int flag when done.
//    LATCbits.LATC7=!LATCbits.LATC7;     // blink led
   } else {           // discharge cycle timer0 int, because CTMU voltage is shorted 
//    LATCbits.LATC6=!LATCbits.LATC6;     // blink led
    CTMUCONHbits.IDISSEN = 0;       // end drain of touch circuit
    TIME_CHARGE=TRUE;        // set charging flag
    CTMU_WORKING=TRUE;        // set working flag, doing 
          WriteTimer0 ( charge_time );     // set timer to charge rate time
    CTMUCONLbits.EDG1STAT = 1;       // Begin charging the touch circuit
            // clr  TMR0 int flag
         INTCONbits.TMR0IF = 0;         //clear interrupt flag
  if (PIR1bits.ADIF) {         // check ADC irq
   PIR1bits.ADIF = 0;          // clear ADC int flag
//   LATCbits.LATC5=!LATCbits.LATC5;      // blink led
   Vread = ADRES;           // Get the value from the A/D
   Vread= (Vread >>3)&0x007f;       // toss lower bit noise and mask
   if(Vread < (touch_base - TRIP)) {     // see if we have a pressed button
    switchState = PRESSED;
   } else if(Vread > (touch_base - TRIP + HYST)) {
    switchState = UNPRESSED;
   CTMU_ADC_UPDATED=TRUE;        // New data is in Vread, set to FALSE in main program flow
   CTMU_WORKING=FALSE;         // clear working flag, ok to read Vread.
   // config CTMU for next reading
   CTMUCONHbits.CTMUEN = 1;        // Enable the CTMU
   CTMUCONLbits.EDG1STAT = 0;        // Set Edge status bits to zero
   CTMUCONHbits.IDISSEN = 1;        // drain charge on the circuit
         WriteTimer0 ( TIMERDISCHARGE );      // set timer to discharge rate
        if ( PIR1bits.TMR2IF ) {
                PIR1bits.TMR2IF = 0;       // clear TMR2 int flag
Timer0 is set to send a interrupt when the count is done, a small 'charge' value is set/counter started and the CTMU constant current device is enabled. When the timer0 interrupt happens the CTMU current device is stopped and a ADC conversion to read the touch-pad/finger capacitor charge voltage value is started. The ADC has been configured to send a interrupt when the conversion is done. The ADC section of the ISR code reads the ADC value, sets the CTMU discharge flag, shorts the touch-pad ADC input to ground to zero the voltage and sets timer0 again with a longer 'discharge' value. When timer0 again sends a interrupt the ISR looks at the CTMU discharge flag, opens the ground to the touch-pad and reloads timer0 with the 'charge' value and enables the current device again to restart the process.

The o-scope trace below explains the sequence.
Second image:

The demo 'rgbled template code' just reads the touch-pad values and flashes the random RGB outputs as the pad is touched. I'll post a few pictures later.


Last edited:
Hi Nsaspook

I am trying to interface pic18f44k22 with capacitance touch key, before that i am trying to understand your attached zip code and other detail.

Problem faced: unable to build your code
Error [1105] symbol 'CCP_5_SEL_TMR12' has not been defined
Error [1105] symbol 'T2_PS_1_1' has not been defined
Error [1105] symbol 'T2_POST_1_8' has not been defined
Warning [2058] call of function without prototype

Details required:
1)Please give the circuit diagram of your program.
2)the above symbols are defined in timers.h file but why it is still showing error?
3)I am new to this pic micro controller and capacitance touch so can you please share more details about this project ?
4) which compiler has been used in this project?

I appreciate your work in this thread.

Kiran Kumar