#define F_CPU 16000000 #include #include #define CSBAR 0 //PC0 for CS #define SKBAR 1 //PC1 for SK #define DIO 2 //PC2 for DIO #define Tclk 2 // clock period 4us //ASSUMES PORTC USED FOR ALL CSBAR, SKBAR, DIO //REQUIRES MODIFICATION void write16bit(uint16_t data){ for(uint8_t i = 0; i < 16; i++){ // write out data onto DIO if(data & (0x8000)){ // set DIO pin to data MSB PORTC |= (1 << DIO); }else{ PORTC &= ~(1 << DIO); } data = data << 1; // shift MSB next in line PORTC |= (1 << SKBAR); // pulse clock high _delay_us(Tclk); PORTC &= ~(1 << SKBAR); // set clock low _delay_us(Tclk); } } void WriteEnable(uint8_t status){ // status = 1 for write, 0 for read PORTC &= ~(1 << CSBAR); // enable chip PORTC &= ~(1 << SKBAR); // clock must start low _delay_us(Tclk); write16bit(status ? 0xA300 : 0xA000); // 1010 0011 XXXX XXXX per datasheet is write // 1010 0000 XXXX XXXX per datasheet is read PORTC |= (1 << SKBAR); // end clock high _delay_us(Tclk); PORTC |= (1 << CSBAR); // set CS off _delay_us(5*Tclk); // short delay to keep CS high for a required amount of time, can likely be lessened } uint8_t WCRead(){ // return wiper position 0-127 PORTC &= ~(1 << CSBAR); // enable chip PORTC &= ~(1 << SKBAR); // clock must start low _delay_us(Tclk); write16bit(0xAB00); // 1010 1011 XXXX XXXX per datasheet DDRC &= ~(1 << DIO); // set DIO for reading uint8_t buf = 0; // output buffer for(uint8_t i = 0; i < 8; i++){ // read 8 bits in via DIO if(PINC & (1 << DIO)){ // read bit is a 1 buf |= (1 << i); } PORTC |= (1 << SKBAR); // set clock high _delay_us(Tclk); PORTC &= ~(1 << SKBAR); // set clock low _delay_us(Tclk); } DDRC |= (1 << DIO); // set DIO for writing (rest of commands) PORTC |= (1 << SKBAR) | (1 << CSBAR); // end clock high and turn chip off return buf; } uint8_t WCWrite(uint8_t step){ // write wiper, step 0 to 127 step = (step & 0xF0) >> 4 | (step & 0x0F) << 4; // byte indicating step count is sent out LSB-first step = (step & 0xCC) >> 2 | (step & 0x33) << 2; // meaning it must be reversed as commands are sent MSB-first step = (step & 0xAA) >> 1 | (step & 0x55) << 1; uint16_t data_fix = step << 8; // needs empty byte afterward PORTC &= ~(1 << CSBAR); // enable chip PORTC &= ~(1 << SKBAR); // set clock low _delay_us(Tclk); write16bit(0xA600); // send write command then empty address byte write16bit(data_fix); // send wiper value then empty unused byte PORTC |= (1 << SKBAR); // datasheet shows two extra empty clock pulses _delay_us(Tclk); // seem to be ignored, but kept just in case PORTC &= ~(1 << SKBAR); _delay_us(Tclk); PORTC |= (1 << SKBAR); // end clock high _delay_us(Tclk); PORTC |= (1 << CSBAR); // disable chip } int main(){ DDRC = (1 << CSBAR) | (1 << SKBAR) | (1 << DIO); PORTC = (1 << CSBAR) | (1 << SKBAR); // example read uint8_t read_wiper = WCRead(); // chip starts in read-mode so this is okay // can use tmk/qmk to write value as keystrokes to screen // is good to know default value // example write WriteEnable(1); // put chip into write mode _delay_us(Tclk*5); // let settle, just in case, likely not needed WCWrite(0x40); // uses example value 0x40, my "default" value was 0x27 but it likely differs per-board _delay_us(Tclk*5); // let settle, likely not needed WriteEnable(0); // put back in read_mode // here would be a good place to verify that it wrote correctly with WCRead() }