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.

https://jasongarr.wordpress.com/wp-content/uploads/2010/01/clickwheel_logicsessionschange-extension-to-zip.jpg

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 response

switch(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;
}

}
}

  1. Uwe
    February 9, 2010 at 4:16 am

    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

  2. February 9, 2010 at 10:08 am

    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.

  3. Hubie
    February 11, 2010 at 9:50 pm

    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?

  4. Uwe
    February 27, 2010 at 12:39 pm

    Can you tell me the frequency of the clock signal?

  5. Uwe
    February 27, 2010 at 12:55 pm

    How often is the information being send from the clickwheel?

    • February 27, 2010 at 1:05 pm

      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.

  6. Uwe
    February 27, 2010 at 2:01 pm

    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…

  7. Riotstarter
    October 27, 2010 at 2:49 pm

    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!

    • October 27, 2010 at 5:57 pm

      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.

  8. Uwe
    January 7, 2011 at 3:37 am

    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…

  9. JRMN
    January 22, 2011 at 11:16 pm

    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.

    • January 23, 2011 at 12:29 am

      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!

  10. July 28, 2011 at 9:40 am
  11. Lyos Gemini Norezel
    March 25, 2012 at 10:40 pm

    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

  1. January 29, 2010 at 11:19 am
  2. February 4, 2010 at 3:17 pm
  3. February 5, 2010 at 12:46 pm

Leave a comment