Skip to content

wagiminator/ATtiny13-TinyTouchLight

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TinyTouchLight - Dimmable USB Night Light with Capacitive Touch Control

TinyTouchLight is a dimmable USB night light with capacitive touch control based on the ATtiny13A. It plugs into any USB charger or power bank.

TinyTouchLight_pic3.jpg

Capacitive Touch Button

Introduction

This implementation of a touch-sensitive button (touchkey) is based on the charge sharing approach similar to Atmel's QTouchADC and Tim's TinyTouchLib. It works without external components, only a resistor for noise reduction is used, which can also be dispensed with. The touchkey itself is a small copper area on the PCB (sense electrode), which is covered with solder mask. This sense electrode is connected to a single ADC-capable pin of the ATtiny via the resistor.

Basic Principle

The touch sense pin (here PB4) is connected through series resistor RS to the sensor electrode capacitance, represented by Cx. The switch SW represents a finger touching the key. The capacitance introduced by the finger is represented as Ct. When the key is touched, Ct is switched into the circuit forming a parallel capacitor with Cx, changing the effective sensor capacitance to Cx + Ct.

TinyTouchLight_principle.png

It should be noted that Cx and Ct are not physical capacitors. Cx is the effective capacitance of the sense electrode and Cx + Ct is the effective capacitance of the human finger touching the sensor.

Component Selection

The series resistor RS is nominally 1kΩ, but it may be increased to higher values to improve the noise immunity of the circuit. The value of RS should be increased in steps to find the lowest value that provides adequate noise immunity. Resistance values of up to 100kΩ have proven to be useful in extremely noisy environments. For this application a 47kΩ resistor was chosen.

The value of Cx should be close to that of the ADC’s internal sample-and-hold capacitor CS/H (~14pF). For best performance it is recommended that Cx+t should not be greater than ~60pF. If the sensor electrode is designed as a copper surface on the PCB, then it should be roughly as large as the contact surface of a finger (8-15 mm in diameter if the touchkey sensor is round, or with a 8-15 mm side if the touchkey sensor is square). There shouldn't be any traces on the other side of the PCB. The back side can have a ground plane, but this should not be a solid fill.

Sensor Acquisition

The acquisition method works by sharing charge between the ADC’s internal sample-and-hold capacitor (CS/H) and the sense electrode capacitance (Cx). When the sensor is touched the effective capacitance of the sensor electrode increases and becomes Cx + Ct. This affects the amount of charge shared between the capacitors. When pre-charging Cx and sharing with CS/H, charge transferred to CS/H increases on touch and ADC input voltage increases. When pre-charging CS/H and sharing with Cx, charge remaining on CS/H decreases on touch and ADC input voltage decreases. But the resulting signal from the averaged ADC values increases on touch. If the difference between signal and reference is greater than the user-determined threshold (delta), a touch is reported.

The charge sharing is carried out in the following sequence:

  1. Precharge touchkey LOW and S/H cap HIGH.
  2. Connect touchkey and S/H cap in parallel. Read the voltage via the ADC.
  3. Precharge touchkey HIGH and S/H cap LOW.
  4. Connect touchkey and S/H cap in parallel. Read the voltage via the ADC.
  5. Calculate the voltage difference and compare it with the user-determined threshold.

Step 1: Precharge touchkey LOW and S/H cap HIGH.

By setting the touch sense pin (PB4) to OUTPUT LOW, the touchkey is discharged. By setting the ADC muxer to a spare pin (here PB3) and setting this pin to OUTPUT HIGH, the internal S/H capacitor is charged.

TinyTouchLight_step1.png

ADMUX   =  TC_SHC_ADC;              // connect spare pin to S/H cap
PORTB  |=  (1<<TC_SHC_PIN);         // charge S/H cap
PORTB  &= ~(1<<TC_PAD_PIN);         // prepare discharge touch pad
DDRB   |=  (1<<TC_PAD_PIN);         // discharge touch pad
_delay_us(TC_CHARGETIME);           // wait for precharge complete

Step 2: Connect touchkey and S/H cap in parallel. Read the voltage via the ADC.

By setting the touch sense pin (PB4) to INPUT (no pullup) and setting the ADC muxer to this pin, charge is flowing between the capacitors until the charge is distributed proportionally between them - the charge is shared. The voltage across CS/H, due to the remaining charge, is sampled by the ADC.

TinyTouchLight_step2.png

DDRB   &= ~(1<<TC_PAD_PIN);         // float pad input (pull up is off)
ADMUX   =  (1<<ADLAR) | TC_PAD_ADC; // connect touch pad to S/H and ADC
ADCSRA |=  (1<<ADSC)  | (1<<ADIF);  // start voltage sampling
while (!(ADCSRA & (1<<ADIF)));      // wait for sampling complete
uint8_t dat1 = ADCH;                // read sampling result (voltage)

Step 3: Precharge touchkey HIGH and S/H cap LOW.

By setting the touch sense pin (PB4) to OUTPUT HIGH, the touchkey is scharged, by setting the the ADC muxer to the spare pin (PB3) and setting this pin to OUTPUT HIGH, the internal S/H capacitor is discharged.

TinyTouchLight_step3.png

ADMUX   =  TC_SHC_ADC;              // connect spare pin to S/H cap
PORTB  &= ~(1<<TC_SHC_PIN);         // discharge S/H cap
PORTB  |=  (1<<TC_PAD_PIN);         // prepare charge touch pad
DDRB   |=  (1<<TC_PAD_PIN);         // charge touch pad
_delay_us(TC_CHARGETIME);           // wait for precharge complete

Step 4: Connect touchkey and S/H cap in parallel. Read the voltage via the ADC.

By setting the touch sense pin (PB4) to INPUT (no pullup) and setting the ADC muxer to this pin, charge is flowing between the capacitors until the charge is distributed proportionally between them - the charge is shared again. The resulting voltage is sampled by the ADC.

TinyTouchLight_step4.png

DDRB   &= ~(1<<TC_PAD_PIN);         // float touch pad input
PORTB  &= ~(1<<TC_PAD_PIN);         // pull up off
ADMUX   =  (1<<ADLAR) | TC_PAD_ADC; // connect touch pad to S/H and ADC
ADCSRA |=  (1<<ADSC)  | (1<<ADIF);  // start voltage sampling
while (!(ADCSRA & (1<<ADIF)));      // wait for sampling complete
uint8_t dat2 = ADCH;                // read sampling result (voltage)

Step 5: Calculate the voltage difference and compare it with the user-determined threshold.

The measured and calculated voltage difference is returned by the function TC_getDelta(), which includes the code shown above. The function TC_sense() averages 16 of these measurements, calculates the difference with the non-touch bias and compares this difference with the user-defined thresholds. It also makes sure that there is no drift or stuck button. The function returns a value which represents if the touchkey is pressed, released or hold.

uint8_t TC_sense(void) {
  uint16_t tmp = 0;
  for (uint8_t i=16; i; i--) {
    tmp += TC_getDelta();             // average 16 samples
    _delay_us(100);
  }
  int16_t diff = tmp - (TC_bias>>4);  // difference with bias value
  if (!TC_touch) {                    // not touched previously?
    if (diff > TC_THRESHOLD_ON) {     // new touch detected?
      TC_touch = 1;                   // set "is touched" flag
      TC_timer = 0;                   // reset touch timer
      return TC_PUSH;                 // return "new push"
    }
    TC_bias = (TC_bias - (TC_bias>>6)) + (tmp>>2); // update bias (low pass)
    return TC_OFF;                    // return "still not pushed" 
  }
  else {                              // touched previously?
    if (diff < TC_THRESHOLD_OFF) {    // touch button released?
      TC_touch = 0;                   // clear "is touched" flag
      return TC_RELEASE;              // return "touch pad released"
    }
    if (TC_timer == TC_TIMEOUT) {     // still touched but for too long?
      TC_bias  = TC_getDelta()<<8;    // maybe stuck situation, read new bias
      return TC_FAIL;                 // return "fail"
    }
    TC_timer++;                       // increase timer
    return TC_ON;                     // return "still pushed"
  }
}

LED Dimmer

The LEDs are controlled via a PWM-capable pin (PB0) and a MOSFET. Timer0 generates the PWM signal, the duty cycle is controlled via the OCR0A register. The main function brings everything together:

int main(void) {
  // Local variables
  uint8_t bright = 64;                // current brightness of LEDs 
  uint8_t dir    = 0;                 // current fade direction

  // Setup
  TCCR0A = (1<<COM0A1)                // clear OC0A on compare match, set at TOP
         | (1<<WGM01) | (1<<WGM00);   // fast PWM
  TCCR0B = (1<<CS00);                 // start timer without prescaler
  DDRB   = (1<<LED_PIN);              // set LED pin as output
  TC_init();                          // setup touch control
  
  // Loop
  while(1) {
    uint8_t sense = TC_sense();       // read touch pad
    if(sense == TC_PUSH) dir = !dir;  // change fade direction on new push
    if(sense == TC_ON) {              // fade on touch hold
      if(dir) {                       // fade in?
        if (bright < 196) bright++;   // increase brightness
      }
      else {                          // fade out?
        if (bright) bright--;         // decrease brightness
      }
    }
    OCR0A = bright;                   // set brightness (PWM)
  }
}

Compiling and Uploading the Firmware

Since there is no ICSP header on the board, you have to program the ATtiny either before soldering using an SOP adapter, or after soldering using an EEPROM clip. The AVR Programmer Adapter can help with this.

If using the Arduino IDE

  • Make sure you have installed MicroCore.
  • Go to Tools -> Board -> MicroCore and select ATtiny13.
  • Go to Tools and choose the following board options:
    • Clock: 128 kHz internal osc.
    • BOD: disabled
    • Timing: Micros disabled
  • Connect your programmer to your PC and to the ATtiny.
  • Go to Tools -> Programmer and select your ISP programmer (e.g. USBasp).
  • Go to Tools -> Burn Bootloader to burn the fuses.
  • Open the TinySat sketch and click Upload.

If using the precompiled hex-file

  • Make sure you have installed avrdude.
  • Connect your programmer to your PC and to the ATtiny.
  • Open a terminal.
  • Navigate to the folder with the hex-file.
  • Execute the following command (if necessary replace "usbasp" with the programmer you use):
    avrdude -c usbasp -p t13 -U lfuse:w:0x3b:m -U hfuse:w:0xff:m -U flash:w:tinytouchlight.hex
    

If using the makefile (Linux/Mac)

  • Make sure you have installed avr-gcc toolchain and avrdude.
  • Connect your programmer to your PC and to the ATtiny.
  • Open a terminal.
  • Navigate to the folder with the makefile and the sketch.
  • Run PROGRMR=usbasp make install to compile, burn the fuses and upload the firmware (change PROGRMR accordingly).

References

  1. Tim's TinyTouchLib
  2. AVR3001: QTouchADC
  3. AN2934: Capacitive Touch Sensor Design Guide
  4. ATtiny13A Datasheet

TinyTouchLight_pic5.jpg TinyTouchLight_pic2.jpg TinyTouchLight_pic4.jpg

License

license.png

This work is licensed under Creative Commons Attribution-ShareAlike 3.0 Unported License. (http://creativecommons.org/licenses/by-sa/3.0/)