void footSwitchCallback(){ DEBOUNCE(bypass, 200); updateBypassMode(); }
void pushButtonCallback(){ DEBOUNCE(pushbutton, 200); if(isPushButtonPressed() && settings.patch_mode != PATCHMODE_SINGLE) toggleActiveSlot(); }
int main(void) { // set clock prescaler for 8MHz CLKPR = 0x80; CLKPR = 0x01; cli(); power_all_disable(); power_spi_enable(); power_timer1_enable(); set_sleep_mode(SLEEP_MODE_IDLE); pins_init(); delay_ms(345); // arbitrary uint8_t dpi = 0x0f; // default to 800 if (!(PIND & (1<<0))) // if left is pressed at boot dpi = 0xff; // set to 12800 pmw3366_init(dpi); nrf24_init(); // button stuff // previous debounced state uint8_t btn_prev = ~(PIND); // time (in 125us) button has been unpressed. // consider button to be released if this time exceeds DEBOUNCE_TIME. uint8_t btn_time[3] = {0, 0, 0}; // absolute positions. relies on integer overflow union motion_data x = {0}, y = {0}; // wheel stuff uint8_t whl_prev_same = 0; // what A was the last time A == B uint8_t whl_prev_diff = 0; // what A was the last time A != B // absolute scroll position. relies on integer overflow int8_t whl = 0; // begin burst mode for 3366 spi_set3366mode(); SS_3366_LOW; spi_3366_write(0x50, 0x00); SS_3366_HIGH; // set up timer1 to set OCF0A in TIFR0 every 1ms TCCR1A = 0x00; TCCR1B = 0x09; // CTC, 8MHz OCR1A = 7999; // main loop nominal period (7999 + 1) / 8MHz = 1ms OCR1B = 320; // timing of when to read burst mode data from sensor OCR1C = 800; // timing of when to load nrf24l01+ with data // let receiver know if it's the first time sending data, so that it // can reset the reference for absolute position and that there's no // jump when rebooting the mouse // uint8_t first = 0x80; // transmitted as MSB with button data below. // when sync reaches 0, always send a packet with bit 6 in btn set, to // tell the receiver to calculate the timing offset. // when sync reaches 1, always send a packet requesting ACK to load the // timing offset // i.e. when it overflows, so 256ms periodicity. // when sync reaches 0, afk increments. // afk is cleared by any motion or button press. // when afk reaches AFK_TIMEOUT, go into powerdown mode. for (uint8_t first = 0x80, sync = 0, afk = 0; ; first = 0, sync++) { // sync to 1ms intervals using timer1 // if (TIFR1 & (1<<OCF1A)) PORTD |= (1<<6); TIMSK1 |= (1<<OCIE1A); sei(); sleep_mode(); cli(); TIMSK1 &= ~(1<<OCIE1A); TIFR1 |= (1<<OCF1A); TIFR1 |= (1<<OCF1B); TIFR1 |= (1<<OCF1C); // begin burst mode read spi_set3366mode(); SS_3366_LOW; spi_send(0x50); // do stuff here instead of busy waiting for 35us // read wheel int8_t dwhl = 0; const uint8_t whl_a = WHL_A_IS_HIGH; const uint8_t whl_b = WHL_B_IS_HIGH; // if (whl_a == whl_b) { // if (whl_a != whl_prev_same) { // dwhl = 2 * (whl_a ^ whl_prev_diff) - 1; // whl += dwhl; // whl_prev_same = whl_a; // } // } else // whl_prev_diff = whl_a; if (whl_a != whl_b) whl_prev_diff = whl_a; else if (whl_a != whl_prev_same) { dwhl = 2 * (whl_a ^ whl_prev_diff) - 1; whl += dwhl; whl_prev_same = whl_a; } // read buttons /* PIND 0 EIFR 0: low, no edges -> is low PIND 0 EIFR 1: low, edge -> is low PIND 1 EIFR 0: high, no edges -> always high during last 1ms PIND 1 EIFR 1: high, edge -> low at some point in the last 1ms */ const uint8_t btn_unpressed = PIND & ~(EIFR); EIFR = 0b00000111; // clear EIFR // manual loop debouncing for every button uint8_t btn_dbncd = 0x00; #define DEBOUNCE(index) \ if ((btn_prev & (1<<index)) && (btn_unpressed & (1<<index))) { \ btn_time[index]++; \ if (btn_time[index] < DEBOUNCE_TIME) \ btn_dbncd |= (1<<index); \ } else { \ btn_time[index] = 0; \ btn_dbncd |= (~btn_unpressed) & (1<<index); \ } DEBOUNCE(0); DEBOUNCE(1); DEBOUNCE(2); #undef DEBOUNCE // wait until 35us have elapsed since spi_send(0x50) // if (TIFR1 & (1<<OCF1B)) PORTD |= (1<<6); TIMSK1 |= (1<<OCIE1B); sei(); sleep_mode(); cli(); TIMSK1 &= ~(1<<OCIE1B); union motion_data dx, dy; spi_send(0x00); // motion, not used spi_send(0x00); // observation, not used dx.lo = spi_recv(); dx.hi = spi_recv(); dy.lo = spi_recv(); dy.hi = spi_recv(); SS_3366_HIGH; x.all += dx.all; y.all += dy.all; if (sync == 0) afk++; const uint8_t changed = (btn_dbncd != btn_prev) || dx.all || dy.all || dwhl; if (changed) afk = 0; if (changed || (sync <= 1)) { btn_prev = btn_dbncd; // W_TX_PAYLOAD if sync == 1, W_TX_PAYLOAD_NOACK otherwise const uint8_t mode = (sync == 1) ? 0b10100000 : 0b10110000; // send miscellaneous info using top bits of btn byte uint8_t btn_send = btn_dbncd | first; // first is either 0x80 or 0 if (sync == 0) btn_send |= 0x40; // try to transmit at the same time every frame // if (TIFR1 & (1<<OCF1C)) {PORTD |= (1<<6);} TIMSK1 |= (1<<OCIE1C); sei(); sleep_mode(); cli(); TIMSK1 &= ~(1<<OCIE1C); spi_setnrf24mode(); SS_NRF24_LOW; spi_send(0x20 | 0x07); // STATUS spi_send(0b01110000); // clear IRQ SS_NRF24_HIGH; SS_NRF24_LOW; spi_send(0b11100001); // flush tx SS_NRF24_HIGH; SS_NRF24_LOW; spi_send(0b11100010); // flush rx SS_NRF24_HIGH; SS_NRF24_LOW; spi_send(mode); spi_send(btn_send); spi_send(x.lo); spi_send(x.hi); spi_send(y.lo); spi_send(y.hi); spi_send(whl); SS_NRF24_HIGH; // pulse CE to transmit CE_HIGH; delay_us(12); CE_LOW; if (sync == 1) { // get ack payload of timing offset delay_us(400); if (IRQ_IS_LOW) { // recycle motion_data union for timing union motion_data offset; SS_NRF24_LOW; spi_send(0b01100001); offset.lo = spi_recv(); offset.hi = spi_recv(); SS_NRF24_HIGH; // shift TCNT1 by the offset, plus a // little more because of the time it // takes to add stuff to TCNT1. TCNT1 += offset.all + 11; } } } // power down if afk if (afk > AFK_TIMEOUT) { // enable external interrupts on INT0/1/2/3, PCINT0 EIMSK = 0b00000111; PCICR = 0x01; // go power down mode; wake up on interrupt set_sleep_mode(SLEEP_MODE_PWR_DOWN); sei(); sleep_mode(); cli(); // disable external interrupts PCICR = 0; EIMSK = 0; // restore state set_sleep_mode(SLEEP_MODE_IDLE); sync = 0; afk = 0; } } }