iPod Clickwheel Hack
CLICKWHEEL CONNECTOR PINOUT
PIN# * FUNCTION
1 —– Vbatt (was connedted directly to li-po in ipod; connected to 3V3 supply on board)
2 —– CW_SCL_PIN (clock from clickwheel)
3 —– hw enable? (connected to 3V3)
4—– N/C
5 —– N/C
6 —– CW_SDA_PIN (clickwheel serial data; pulled up with 100K)
7—– hw enable? (connected to 3V3)
8 —– GND
*as indicated on ribbon cable of clickwheel module
*.LOGICSESSION Files Uploaded!
The .logicsession files can be downloaded at the link below. Just remember to change the .jpg extension to .zip. To view the files the logic software can be downloaded for free at www.saleae.com.
SOURCE CODE!!!
Putting the cart before the horse I have cleaned up my source for this project and turned it in to a driver that can be added easily to any AVR core with little to no modification to the code. As mentioned in previous posts, details on the reverse engineering process will be added soon. Untill then feel free to use and modify my code for private non commercial purposes only.
here are the two source files for interfacing the clickwheel. I have added my ‘main.c’ at the bottom as a usage sample.
clickwheel.h
#ifndef CLICKWHEEL_H
#define CLICKWHEEL_H
#endif//clickwheel data lines
#define DATA_PORT PORTD
#define DATA_PIN PIND
#define CW_SDA_PIN 4
#define CW_SCL_PIN 3 //INT1
#define CW_SDA PIND & (1<<CW_SDA_PIN)
#define CW_SCL PIND & (1<<CW_SCL_PIN)
//#define CW_SCL_LOW ((PIND & _BV(7)))
//clickwheel packets
#define CW_CMD_P1 3 //first byte sent in packet
#define CW_BTN_CMD 2 //second byte sent in packet
#define CW_WHEEL_CMD 1 //second byte sent in packet
#define CW_CMD_P4 0 //second byte sent in packet//clickwheel command ‘cmd’ formatting
#define CW_CMD_CW 0x80 //clockwise scroll event (used for ‘cmd’ stuffing only)
#define CW_CMD_CCW 0x40 //counter-clockwise scroll event (used for ‘cmd’ stuffing only)
#define CW_CMD_FLAG 0x20 //clickwheel new packet arrived flag
#define CW_CMD_PP 0x08 //clickwheel play >>|| pause button pressed
#define CW_CMD_MENU 0x10 //clickwheel menu button pressed
#define CW_CMD_BACK 0x04 //clickwheel back button pressed
#define CW_CMD_FORWARD 0x02 //clickwheel forward button pressed
#define CW_CMD_CBTN 0x01 //clickwheel center button pressed#define CW_DEADBAND 7 //adjust the sensitivity/speed of the clickwheel. lower=more sensitive
//initialize interrupt and port
extern void uiinit(void);//function return cmd byte following the clickwheel ‘cmd’ formatting defines
extern unsigned char uiaction(void);//reset the ui action recieve buffer and cnd flag byte
//this function should be called after responding the the new action
extern void uireset(void);
|
|
clickwheel.c
#include “clickwheel.h”
#include “avr/interrupt.h”volatile unsigned char cmd;
volatile char posn1, db_compare;
volatile union {
unsigned char byte[4];
unsigned long int word;
} cmd_packet;ISR(INT1_vect) {
if (CW_SDA) {
cmd_packet.byte[0] |= 0x80;
}if (((cmd_packet.byte[0] == 128) || (cmd_packet.byte[0] == 192)) && (cmd_packet.byte[3] == 26)) {
cmd = 0x20;
EIMSK &= ~(0x02);
}
else {cmd_packet.byte[3] = (cmd_packet.byte[3]>>1);
cmd_packet.byte[3] |= (cmd_packet.byte[2]<<7);cmd_packet.byte[2] = (cmd_packet.byte[2]>>1);
cmd_packet.byte[2] |= (cmd_packet.byte[1]<<7);cmd_packet.byte[1] = (cmd_packet.byte[1]>>1);
cmd_packet.byte[1] |= (cmd_packet.byte[0]<<7);cmd_packet.byte[0] = (cmd_packet.byte[0]>>1);
}
}unsigned char uiaction(void) {
if (cmd == CW_CMD_FLAG ) {if(cmd_packet.byte[CW_BTN_CMD] != 0){
cmd = cmd_packet.byte[2];
return;
}
else{
if (cmd_packet.byte[CW_WHEEL_CMD] > posn1) {
while (db_compare > CW_DEADBAND) {
cmd = CW_CMD_CW;
db_compare = CW_DEADBAND / 2;
break;
}
db_compare += 1;
}
if (cmd_packet.byte[CW_WHEEL_CMD] < posn1) {
while (db_compare <= 0) {
cmd = CW_CMD_CCW;
db_compare = CW_DEADBAND / 2;
break;
}
db_compare -= 1;
}
posn1 = cmd_packet.byte[CW_WHEEL_CMD];}
cmd &= ~CW_CMD_FLAG;
cmd_packet.word = 0x00000000;
}
EIMSK |= 0x02;
return cmd;
}void uiinit() {
//initialize clickwheel port as
DATA_PORT |= (1<<CW_SDA_PIN) | (1<<CW_SCL_PIN);//configure INT1_vect and pins for sampling clickwheel data transmissions
//on rising edges of the clock
EICRA = 0x0C; //sets for rising edge interrupt on INT1
EIMSK = 0x02; //enable interrupts on port INT1
sei();
}void uireset() {
cmd_packet.word = 0x00000000;
cmd = 0;
}
|
|
main.c
#include “stdlib.h”
#include “avr/io.h”
#include “lcd.h”
#include “clickwheel.h”#define F_CPU 8000000UL // 8 MHz
void main(void) {
char buffer[32];
int count;uiinit();
lcd_init(LCD_DISP_ON);for(;;) {
//check to see if a new command has been recieved and process command
//and print appropriate button responseswitch(uiaction()) {
case CW_CMD_CW:
lcd_gotoxy(0,0);
lcd_puts(“clockwise “);
count = count + 10;
utoa(count, buffer, 10);
lcd_gotoxy(6,1);
lcd_puts(buffer);
lcd_puts(” “);
uireset();
break;
case CW_CMD_CCW:
lcd_gotoxy(0,0);
lcd_puts(“counterclockwise”);
count = count – 10;
utoa(count, buffer, 10);
lcd_gotoxy(6,1);
lcd_puts(buffer);
lcd_puts(” “);
uireset();
break;
case CW_CMD_PP:
lcd_gotoxy(0,0);
lcd_puts(“play/pause “);uireset();
break;
case CW_CMD_MENU:
lcd_gotoxy(0,0);
lcd_puts(“menu “);
uireset();
break;
case CW_CMD_BACK:
lcd_gotoxy(0,0);
lcd_puts(“back “);
count –;
utoa(count, buffer, 10);
lcd_gotoxy(6,1);
lcd_puts(buffer);
lcd_puts(” “);
uireset();
break;
case CW_CMD_FORWARD:
lcd_gotoxy(0,0);
lcd_puts(“forward “);
count ++;
utoa(count, buffer, 10);
lcd_gotoxy(6,1);
lcd_puts(buffer);
lcd_puts(” “);
uireset();
break;
case CW_CMD_CBTN:
lcd_gotoxy(0,0);
lcd_puts(“center button “);
uireset();
count = 0;
break;
default:
1;
}}
}
Hi Jason, great work! You have inspired me also to use an iPod ClickWheel in my projekt and i have ordered one on eBay. As i know apple uses a Cypress PSoC Controller inside the ClickWheel. This kind of controller uses an I2C-interface to communicate with the main conrtoller. So I would be happy to help implementing the corresponding TWI-functionality for the Atmel controller family. Is it possible to give (send) me more information about the signal pattern you have evaluated with your logic analyzer? It would be great if you can answer me directly Uwe.Klaus@t-online.de
Thank you very much for the great work!
Uwe
As i have mentioned I will be detailing the reverse engineering process soon. I plan on including , session files from my Logic capture, as well as the python script that was written to originally decode/analyze the *.csv generated by the Logic software and copies of my own notes that have been made through the process.
This is an astounding bit of work! This page is literally a wish come true. Thanks!
I have a 5G clickwheel I was hoping to use in a project, but it seems that is has 14 pins [13 on the ribbon + 1 ground[?]] instead of your 8.
http://static.howstuffworks.com/gif/ipod-12.jpg [best photo I could find]
What generation iPod is your wheel from?
Interesting, i have a 5G as well (still functional) so i may do some ND testing to see if its similar implementation.
The clickwheel that i used is actially from a 4th gen wheel such as this one:
http://cgi.ebay.ca/iPod-4th-Gen-Clickwheel-Photo-Click-Wheel-ORIGINLAL_W0QQitemZ300328399951QQcmdZViewItemQQptZOther_MP3_Player_Accessories?hash=item45ecf7b44f.
Can you tell me the frequency of the clock signal?
~60kHz
How often is the information being send from the clickwheel?
IIRC each cmd packet is sent every few tens of ms if scrolling continuously. Unfortunately all my .logicsession files for this project are all on ta machine thats down right now.
As mentioned above i meanwhile also believe it is the MEP protocol from synaptics. The ASICs from synaptics are supporting several communication protocols – also SPI. In auto-mode, with is the default mode after reset the controller is sending its information with a frequency of 80Hz. You can configure the behaviour via special commands with the SPI-interface. On the synaptics webpage i have downloaded all the datasheets, but i cant figure out all details till now. Your measurements would be really very helpfull. I hope you can repair your machine. Best regards…
Jason – any chance you might still post additional information about your hack? I know it’s been a few months… Nice job with the reverse engi!
At this point in time this project has been put on the back back burner unfortunately. I have plans currently for doing some work with some MSP430 and TI’s SimpliciTI stack/ low power wireless products for another project(cant disclose on this) though that is not to say that this code wont get ported to the MSP430 for some alternate project use.
Since it has been requested and files have been recovered i have now uploaded the .locgisession files to the main clickwwheel project page.
Here some additional information for everybody how wants to use an iPod Clickwheel.
The Clickwheel is driven by an ASIC from Synaptics – it is called T1005.
After Reset the Chlickwheel is in AutoReporting mode which means, that it is sending information whenever a status-change happens. Such a change can be a position change on the wheel, pressing a key or releasing a key.
The Clickwheel always sends 32-bit information-packets via its SPI interface. High byte first, MSB first, leading edge of the clock.
To identify the packet just wait until the lowest byte has the value 0x1a. This is common to all packets.
The ClickWheel ist very sensitive so be carefull, because the single packets can contain position and keypress-informations at the same time. Just implement a kind of deadband to identify explicit the key presses.
Here the definition of the 32-bit packets:
Position change or keypress status with wheel contact:
0xc0 0xpp 0xkk 0x1a
0xpp: Position information (0x01..0x5f)
0xkk: Keypress information (0x01 Center,0x02 Forward, 0x04 Back, 0x08 Play, 0x10 Menu)
Be carefull: The clickwheel sends 0x00 for 0xkk when the key is released. This works not with the center button when you have no contact with the wheel area.
Key information with no wheel contact:
0x80 0xnn 0xkk 0x1a
0xnn: dont care
0xkk: see above
So for the center button this is the more important packet, cause you normaly do not touch the wheel while pressing. For the other keys this packet is being send when you press the keys for example with a pen. A 0x00 for 0xkk is also send, when you release a button.
So for simply recognizing the keypresses wait until the ClickWheel sends the 0x00. During that time ignore the other packets. You also can implement an autorepeat funktionality this way.
By the way the supply voltage can be up to +5V.
You can use a standard 8 pin 0.5mm FPC SMD connector with top or bottom contact.
I am using an 8 pin ATtiny45 controller to completely evaluate the ClickWheel to protect the main controller of my project from the interrupt load. So I can check the Wheel at defined times via a standard SPI-Interface with a dedicated chip enable signal that goes to the ATtiny. Works fine…
For further questions I published my Email-adress in one of the above comments…
Jason, what type of wire did you use? Did you solder your wires to the ribbon cable? I was thinking about using a ribbon cable connector.
JRMN: The wire thats in the pic is just some thin magnet wire that I had soldered from the ribbon cable to a pin header as a breakout. This is not a robust solution by any means ans was just done to see what was going on, a proper zif ribbon connector is definitely the way to go you you plan on actually using a clickwheel in application.
Uwe: Thanks for sharing that additional information!
pKosB2 http://fnYwlOpd2n9t4Vx6A3lbk.com
Jason:
In case you are still working on this project… I thought I’d give you some information on the 14 pin version with the Cypress CY8C21434 chipset.
Pin 1* – GND
Pin 2 – Pin 1 on Cypress CY8C21434 chip – A, I, M, P0[1]
Pin 3 – Pin 2 on Cypress CY8C21434 chip – M, P2[7]
Pin 4 – Pin 3 on Cypress CY8C21434 chip – M, P2[5]
Pin 5 – Pin 4 on Cypress CY8C21434 chip – M, P2[3]
Pin 6 – Pin 5 on Cypress CY8C21434 chip – M, P2[1]
Pin 7 – Pin 6 on Cypress CY8C21434 chip – M, P3[3]
Pin 8 – Pin 7 on Cypress CY8C21434 chip – M, P3[1]
Pin 9 – Pin 8 on Cypress CY8C21434 chip – M, I2C SCL, P1[7]
Pin 10 – Pin 9 on Cypress CY8C21434 chip – M, I2C SDA, P1[5]
Pin 11 – Pin 10 on Cypress CY8C21434 chip – M, P1[3]
Pin 12 – Pin 11 on Cypress CY8C21434 chip – M, I2C SCL, P1[1]
Pin 13 – Pin 13 on Cypress CY8C21434 chip – M, I2C SDA, P1[0]
Pin 14 – Looks to be power, connected via capacitor and an unidentified (4pin?) IC
*as identified by the iPod’s PCB
Seems odd that they seem to use 2 separate I2C busses on this version.
As for the other pins… my guess would be that they use those pins in analog mode… and output some kind of positioning data along with pressure data… but that’s only a guess.
Good luck with your project