Daily Archive for July 28th, 2012

How I learned to stop worrying and love crazy, stupid kludges

So, I’m in some of the final development phases of bcard. I’m working on developing an optimal touch sensor. As you can see, I’ve tried a few:

Total attempts so far: 912. That’s right, I’m on the 12th revision.

One of the things I’ve been struggling with involves power consumption. I don’t have much power to work with so I want the micro to use as little power as possible when not running the LEDs.

So I came up with a semi-batshit-insane idea. If I could somehow trigger a level change on thee INT0 pin, I could design the firmware such that I can shut basically the entire micro off until the pin level change fires an external interrupt sensor on the micro. This would basically reduce the power usage of the micro to like nothing.

So my approach would be a kind of hybrid between resistive and capacitive sensing:

  1. Enable pin interrupt, set INT0 pin to be floating. Then power down most of the micro’s functionality.
  2. When the interrupt fires, the micro will wake up and start running the capacitive sense system.
  3. If the capacitive sense system doesn’t see a swipe, then go back to sleep.

This plan at first seems kind of stupid because in general you should never just try to detect something useful from a floating pin. They’re extremely volatile. But I figure that by combining resistive and capacitive touch sensing, I will get the power savings of the former and the reliability of the latter.

So I tried implementing this idea in a few ways. First of all, I built a new board like this:

So now there are three pads and a “ground guard” around the whole thing (this isn’t really necessary). I’ll call the big pads “left” and “right” and the center strip pad “middle.” I’ve built an even newer board since then, but it’s easier to explain what I’m doing with this older one, so I’m going to stick with that (they’re functionally identical; the newer one just looks nicer).

There are basically two options. When I’m in this resistive sensing mode I can either drive left and right high, shut the micro off, and tie center to INT0 so that it detects a float. Or I could do the reverse; I can assume that the user should always start his finger on left and therefore tie that to INT0 as an input and then tie center high.

I chose the former so that I can do a bit of analog magic with the floating pin. I want to be able to add a pull-down resistor of some value such that it prevents spurious interrupts, but still is very sensitive to the press of a finger. The pull-down resistor kind of makes the floating pin more likely to detect “0”, which means that you need a bit more of a “push” to make it detect a “1”. Tuning this is a matter of adjusting that resistor. So, the layout went something like this:

So, I wired it all up like the above diagram (I started out with no resistor on the floating pin) and then had a small test program that basically went as follows:

[cc lang=”c”]
int main(void) {
// initialize UART
uart_init();

// LED output
DDRB |= _BV(PD3);

// start resistive component
// enable INT0 pin
EIMSK = _BV(INT0);
EICRA = _BV(ISC01);
sei();

while (1);

return 0;
}

ISR(INT0_vect) {
uart_tx(‘*’);
PIND |= _BV(PD3); // toggle LED
}
[/cc]

All it does is enable the INT0 pin interrupt vector and then spin in circles. When the interrupt fires, it sends a character over serial.

Results of testing? Pretty dismal actually. The interrupt barely fired… It was more likely to when I pressed a large surface area, but my goal was to make the resistive pads as small as possible and solder mask the rest.

But I had one more trick up my sleeve. If I couldn’t get a reliable level change with just a single press, what if I could simulate a whole bunch of presses and hope like hell that one hit? Enter my next idea.

The left and right pads are just being driven high right now. But what if we could instead make them oscillate between Vcc and ground. Then, since it’s slamming between the two voltage rails, it’s more likely that when we’re pressing it, it is going trouble make enough chatter on the INT0 input pin to kick the interrupt awake.

But this uses more power. After all, I now have to get the micro to continually toggle the pin, right?

Welllllll yes and no. There is going to be more power usage because the pin has to switch. But I can still keep most of the micro off by using the timer to generate a waveform.

At this point, I had to look at the capabilities of the chip I’m using. I’ve been testing with an ATmega168 because it’s easy to debug when you have stuff like serial communication. However as I’ve mentioned before the final target will be the ATtiny10, which is basically the cheapest class of processors that Atmel makes. This chip has a single 16-bit timer that can drive two output ports. Perfect. One can be used to PWM the LEDs and the other can be used to drive the floating pin input. Moreover, given that the timer is 16-bit, even if the system clock speed is kind of fast, I can make the pin oscillate slowly to prevent too much switching loss.

So, I used the 16-bit timer on the ATmega to emulate what I would do on the ATtiny. I wrote a test program that looked like this:
[cc lang=”c”]
#define FLTPORT PORTD
#define FLTDDR DDRD
#define FLT PD2

#define FLTDRVPORT PORTB
#define FLTDRVPIN PINB
#define FLTDRVDDR DDRB
#define FLTDRV PB1

void enable_float_driver(void) {
// Set up floating pins
FLTDDR &= ~_BV(FLT); // float sense input
FLTPORT &= ~_BV(FLT); // don’t you dare pull that shit up
FLTDRVDDR |= _BV(FLTDRV);

OCR1A = 0x0800;
TCCR1A = _BV(COM1A0) | _BV(WGM11) | _BV(WGM10);
TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS12) | _BV(CS10);

// interrupt mask
// enable INT0 pin
EIMSK = _BV(INT0);
EICRA = _BV(ISC01);

return;
}

void disable_float_driver(void) {
// interrupt mask
EIMSK = 0;
EICRA = 0;

// shut off float driver
TCCR1A = 0;
TCCR1B = 0;
FLTDRVDDR &= ~_BV(FLTDRV);

return;
}

int main(void) {
// initialize UART
uart_init();

// LED output
DDRB |= _BV(PD3);

enable_float_driver();

while (1);

return 0;
}

ISR(INT0_vect) {
disable_float_driver();

uart_tx(‘*’);
PIND |= _BV(PD3); // toggle LED

enable_float_driver();
}
[/cc]

The results were almost too good to be true. The interrupt fired unbelievably reliably and there were relatively few spurious interrupts, too. I managed to get it even more stable by adding a 1Mohm pull-down resistor to the floating sensor pin.

So now the final step was to integrate the resistive phase with the capacitive phase, which I was developing earlier. The capacitive phase basically is a state machine. When the interrupt fires and kicks the capacitive phase into gear, the algorithm checks to see if the user is actually pressing the pad by measuring the time constant. If it is then it enters the state machine, which expects the following sequence of events to occur:

  1. User presses left pad
  2. User presses both left and right pads
  3. User presses right pad

In order for there to be a swipe, the user has to be pressing both pads at some point. I’ve designed the sensor with enough overlap between the two that this is guaranteed to happen (unless I just have super fat fingers, which is not unlikely given how shitty the emails I send from my phone are).

If any point the events in this sequence are violated, the micro drops out of the capacitive sensing phase, fires the floating pin driver, and enables the interrupt pin.

And that’s all there is to it. The ATmega implementation works quite nicely. So it’s time to start developing for the ATtiny, which should be more fun. I (unfortunately) have a lot of experience with microcontroller assembly, so I’m hoping it will go somewhat smoothly.

After I finish the implementation, I’m going to do as much power optimization and tweaking as I can. I’m basically pushing this processor to the limit of its capabilities, which is cool because I’ve never had to do something like that before. I’ve definitely learned a lot.

Here’s a preview of the current sensor design:

It’s double sided this time! The islands in the middle are the floating pin inputs. A little more tweaking to this new design and I think I can finally be happy. But I’ll get to how the double-sided business card is going to work out in another blog post.