An alternative title for this article would be: “How to design a crappy touchpad and rip out your hair afterwards”
As I mentioned in my previous post I’m working on something that needs two XMEGAs to talk to each other over TWI. One of them will have a touchpad for human input and a HD44780 compatible 4×20 display. The reason I went this route is because when sourcing buttons to put on the front panel I quickly realized that it would be cheaper to use a capacitive touch solution. Enter the MTCH6102 part from Microchip.
The way this works is you make a touchpad (or touchscreen) so that the sensors are aligned in a matrix of rows and collumns. The capacitive touch IC then detects when one or more fingers touch this surface and save the position in 3 registers: TOUCHX (0x11) and TOUCHY (0x12) hold the MSB and TOUCHLSB (0x13) holds the LSB for X in the upper nibble and the LSB for Y in the lower nibble. The total resolution for each axis is 12 bits.
The way I made this touchpad can be seen in the picture below
As you can see I have 5 rows (rx8 to rx12) and 8 columns (rx0 to rx7) wired up as a matrix on the Front copper layer. The screenshot is from the Kicad pcb editor. So far so good. The shape of the individual pads is just like in the MTCH6102 datasheet, the rows and columns are connected correctly to the part, the TWI interface (see below) works fine and I can read/write from/to the part.
Except that almost 30% of the surface area produces a non monotonic response. What this means is that as I sweep a finger from left to right the value in the X registers goes like this:
0 900 600 500 200 100 0 10 20 50 90 129 … 512
For a response to be monotonic it would have to either decrease or increase but never change direction.The left part of the pad has this weird response almost like the sensor overflows and then after a while it goes back to normal. The very same thing happens on the Y axis. The blue area in the picture below shows where this problem occurs.
As it turns out, the part and the touchpad both work fine. There is something wrong in my conversion from uint8_t to string that I do to display the values on the LCD. Reading the TOUCHX,TOUCHY registers with a buspirate gives the correct values, even when the switching regulator is on the board.
The list of problems below remains valid, but as it turns out that’s not what’s wrong in this instance.
For now I’m leaving this aside. I’m going to focus on getting a temporary interface up and running, but I plan on building a better prototype for the touchpad alone because the one pictured above has some problems.
The individual pads have sharp 90 degree corners.
The pads are squares tilted to one side by 45 degrees. It would be better to have them be hexagons.
The wires that connect the rows and columns are not the minimum technology width and are not spaced by 2x their width from each other and 3x their width from adjacent but unrelated signals.
There is no ground shield on the other side. This shield needs to have a 20 – 30% hatched fill to prevent excessive loss in sensitivity.
Right next to the touchpad is a 900KHz switching regulator. Taking it out and replacing it with a linear one did nothing (there is a full ground plane around the touchpad, it’s just hidden in the picture).
My next revision on this touchpad will have the artwork done in GIMP and the pads added in the Kicad footprint editor somewhere to the side for easy connection. The current version was made using the new array generating feature in the footprint editor. To use this (and other) new feature you need to make sure you’re using the opengl canvas.
The Microchip part was the other reason for my premature hair loss. Unfortunately for me Microchip has some of the worst datasheets in the industry and this one was no exception. The people who write think it’s a good idea to have the user go back and forth over and over just to find where to write the config, but that’s a rant for another day.
After a few days of wasting time and trying all sorts of combinations I managed to get this thing to talk. The code explained below is a snippet of the full code I used for this test. For those that are impatient, here are the pastebin links to the complete thing.
EDIT:In the main function, ignore the itoa() calls. These cause problems for some reason.
The xlcd.c and xlcd.h are just a modified library to interface to a HD44780 compatible LCD. I made these starting from Peter Fleury‘s excellent ATmega library. The modification to the xlcd.c file was found on avrfreaks.net. I don’t remember which user provided this, so I apologize for not giving proper credit.
Be aware that XMEGAs run at a maximum of 3.6V, but not all LCDs work with such a low voltage. If you have a 5V only LCD you need to add level shifting between the uC and the LCD. I’ll make this the subject of a future article. In my case there was no need for level shifting, but the contrast pin needed a negative voltage. This circuit generates this voltage by pumping charge into a capacitor from the main AC input to the board. This technique is only good for negative (or positive) rails that supply low currents.
You might be tempted to replace the 1N4148 with Schottkys. This is a bad idea because Schottky diodes have much higher leakage current than normal silicon diodes. The more current you need to draw from this rail, the bigger C2 has to be. This has the downside of reducing the startup time. It also only works up to a certain point. You’re never going to draw amps from this type of circuit. Getting back to the TWI side of things, the following code is what I used to write a single byte to a location of the MTCH6102 memory map.
void twi_write(uint8_t addr, uint8_t byte){ /*SEND SLAVE ADDRESS + W BIT */ TWIC_MASTER_ADDR = 0X4a; /*WAIT FOR THE WRITE INTERUPT FLAG TO SET HIGH*/ while(!(TWIC_MASTER_STATUS & TWI_MASTER_WIF_bm)); /*SEND MEMORY ADDRESS*/ TWIC_MASTER_DATA = addr; while(!(TWIC_MASTER_STATUS & TWI_MASTER_WIF_bm)); /*SEND BYTE*/ TWIC_MASTER_DATA = byte; while(!(TWIC_MASTER_STATUS & TWI_MASTER_WIF_bm)); TWIC_MASTER_CTRLC = 0X03; }
It’s a lot simpler than the slave interface. You can tweak this function to write any number of bytes to any number of address locations, depending on what part you’re talking to. The address at the top of the function is the default I2C address for the MTCH6102 IC. In the datasheet it’s listed as 0x25. This means that to get the actual address for writing to the part you need to multiply by 2 (shift left 1 bit) and append the R/!W flag. This means 0x4A for writing and 0x4B for reading in this example.
The read function is just as simple. It returns one byte from the specified address.
uint8_t twi_read(uint8_t addr){ TWIC_MASTER_ADDR = 0X4a; /*WAIT FOR THE WRITE INTERUPT FLAG TO SET HIGH*/ while(!(TWIC_MASTER_STATUS & TWI_MASTER_WIF_bm)); TWIC_MASTER_DATA = addr; while(!(TWIC_MASTER_STATUS & TWI_MASTER_WIF_bm)); TWIC_MASTER_ADDR = 0X4b; while(!(TWIC_MASTER_STATUS & TWI_MASTER_RIF_bm)); TWIC_MASTER_CTRLC = TWI_MASTER_ACKACT_bm | TWI_MASTER_CMD_STOP_gc; return TWIC_MASTER_DATA; }
As you can see, first you have to start the transaction to write the address where you want to read from, then repeat start with the read bit set in the address.
In the main function below you can see that I sent quite a few configuration bytes to the IC. Most of these can be left with their default values. The ones that are critical to set correctly are the number of channels for X (0x20) and Y (0x21) and the behaviour of the SYNC and INT signals when a touch is detected (0x05). After you’re done sending the config values you have to write 0x20 to the register at 0x04. Setting this last bit applies the settings.
You can also write a bit that saves these registers in the nonvolatile memory. I didn’t need to do this yet, so I didn’t try but it should be very easy with the above write function.
One other peculiarity of this chip is the way you must wire the channels. For some reason you can’t just write into a couple of registers what each channel is, you have to follow a certain sequence. The datasheet is pretty clear on this, but I’ll add the following picture to show you how I did it.
You have to start with RX0 as X0 and work your way sequentially untill all the X channels are connected and only then start with the Y channels.
Conclusions
The previous post showed how to get the TWI on the XMEGA to work as a slave with interrupts. In this post I showed you how to send and receive from the master side.
This article should hopefully be useful for anyone who wants to start designing capacitive touch pads to replace the old-fashioned buttons.
Stay tuned for an update regarding an improved (fingers crossed) touch pad that is consistent on 100% of its surface.
Thank you for sharing. What’s next?