// note 1: extracted to function & hand-optimized because avr-gcc *duh* // note 2: unroll to get even faster code: // 256 loop itself takes 3 clocks [2 words], non-256 one takes 4 clocks [3 words], // inlined spi_recv() takes *at least* (no SPI wait) 5 clocks [5 words], // ASM_ST_INC is 2 clocks [1 word]; it follows that the loop takes (no SPI wait): // with 2x unrolling: (the default) // 256 * [(5+2) * 2 + 3] = 4352 clocks; (5+1)*2+2 = 14 words // with 4x unrolling: (+6% for ~+25 bytes) // 128 * [(5+2) * 4 + 4] = 4096 clocks; (5+1)*4+3 = 27 words // with 8x unrolling: (another +6% for ~+50 bytes) // 64 * [(5+2) * 8 + 4] = 3840 clocks; (5+1)*8+3 = 51 words // with 16x unrolling: (+3% for ~+100 bytes) // 32 * [(5+2) * 16 + 4] = 3712 clocks; (5+1)*16+3 = 99 words // further unrolling is subject to diminishing results, with maximum speed // at full unroll being only 3588 clocks, i.e. 3% faster than 16x unroll // //__attribute__ ((noinline)) void _spi_recv_block( uint8_t* buf ) { uint8_t j = 0; #ifdef SD_DONT_UNROLL_RECV_BLOCK do { ASM_ST_INC( buf, spi_recv() ); // *(buf++) = spi_recv(); ASM_ST_INC( buf, spi_recv() ); } while ( ++j ); // 512 spi_recv() executions total #else #define SD_UNROLL_REPEAT8(x) x; x; x; x; x; x; x; x do { SD_UNROLL_REPEAT8( ASM_ST_INC( buf, spi_recv() ) ); } while ( ++j < 64 ); // 512 spi_recv() executions total #endif }
void proto_callback (uint8_t cmd, uint8_t size, uint8_t *args) { /* TODO: command to select a slave. */ #define c(cmd, size) (cmd << 8 | size) switch (c (cmd, size)) { case c ('z', 0): utils_reset (); break; case c ('s', 1): spi_send (args[0]); break; case c ('r', 0): proto_send1b ('R', spi_recv ()); break; case c ('r', 1): proto_send1b ('R', spi_send_and_recv (args[0])); break; default: proto_send0 ('?'); return; } proto_send (cmd, size, args); #undef c }
static int atben_buf_read(void *handle, void *buf, int size) { struct atben_dsc *dsc = handle; uint8_t len, i; spi_begin(dsc); spi_send(dsc, AT86RF230_BUF_READ); len = spi_recv(dsc); len++; /* LQI */ if (len > size) len = size; for (i = 0; i != len; i++) *(uint8_t *) buf++ = spi_recv(dsc); spi_end(dsc); return len; }
static void tmp121temp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) { struct tmp121temp_softc *sc = sme->sme_cookie; uint16_t reg; int16_t sreg; int val; if (spi_recv(sc->sc_sh, 2, (uint8_t *)®) != 0) { edata->state = ENVSYS_SINVALID; return; } sreg = (int16_t)be16toh(reg); /* * convert to uK: * * TMP121 bits: * D15 : sign bit * D14-D3 : data (D14 is MSB) * D2-D0 : zero * * The data is represented in units of 0.0625 deg C. */ sreg >>= 3; val = sreg * 62500 + 273150000; edata->value_cur = val; edata->state = ENVSYS_SVALID; }
static inline uint8_t spi_3366_read(const uint8_t addr) { spi_send(addr); delay_us(160); // t_SRAD uint8_t data = spi_recv(); delay_us(20); return data; }
void sd_preinit( void ) { spi_assert_SS(); for( uint8_t i = SD_INIT_IDLE_CYCLES; i > 0; i-- ) { sd_debug_verbose_printf( "~" ); spi_recv(); } sd_debug_verbose_printf( "|\n\r" ); spi_deassert_SS(); }
static uint8_t atben_reg_read(void *handle, uint8_t reg) { struct atben_dsc *dsc = handle; uint8_t res; spi_begin(dsc); spi_send(dsc, AT86RF230_REG_READ | reg); res = spi_recv(dsc); spi_end(dsc); return res; }
static uint8_t atben_sram_read(void *handle, uint8_t addr) { struct atben_dsc *dsc = handle; uint8_t res; spi_begin(dsc); spi_send(dsc, AT86RF230_SRAM_READ); spi_send(dsc, addr); res = spi_recv(dsc); spi_end(dsc); return res; }
// @return response returned from command, or SPI_EMPTY_BYTE (0xFF) if failed // note 1: this code assumes a regular 512-byte block since SDHC allows only this // note 2: this function is slower than sd_readSingleBlockHC uint8_t sd_readPartialBlockHC( uint8_t addr3, uint8_t addr2, uint8_t addr1, uint8_t addr0, uint8_t* buf, uint16_t offset, uint16_t len ) { uint8_t ret = sd_command( SD_READ_SINGLE_BLOCK, addr3, addr2, addr1, addr0 ); if ( ret != SD_RET_READY ) { sd_debug_printf( "RET: not READY; [0x%02hhx] received\n\r", ret ); return SPI_EMPTY_BYTE; } spi_assert_SS(); for( uint8_t i = SD_SPI_RESPONSE_WAIT_MAX; i > 0; i-- ) { *buf = spi_recv(); if ( *buf == SPI_EMPTY_BYTE ) { continue; } if ( *buf != SD_DATA_TOKEN ) { spi_deassert_SS(); sd_debug_printf( "RET: invalid token received\n\r" ); return SPI_EMPTY_BYTE; } uint16_t i = 0, end = offset + len; for( ; i < offset; i++ ) { spi_recv(); } for( ; i < end; i++ ) { *(buf++) = spi_recv(); } for( ; i < SD_HC_BLOCK_LENGTH; i++ ) { spi_recv(); } // CRC values are ignored outside of debug mode; split to avoid comp. warnings #ifdef SD_DEBUG_VERBOSE uint8_t crc1 = spi_recv(); uint8_t crc2 = spi_recv(); sd_debug_verbose_printf( "CRC: 0x%02hhx %02hhx\n\r", crc1, crc2 ); #else spi_recv(); spi_recv(); #endif spi_recv(); // required 8 clock cooldown return ret; } spi_deassert_SS(); sd_debug_printf( "RET: busy\n\r" ); return SPI_EMPTY_BYTE; }
uint8_t flash_init (void) { uint8_t rsp[3]; uint8_t res; AC_FLASH_PORT |= _BV(AC_FLASH_BIT_SS); AC_FLASH_DDR |= _BV(AC_FLASH_BIT_SS); /* send the read-ID instruction. */ spi_init (SPI_MASTER, SPI_CPOL_FALLING | SPI_CPHA_SETUP, SPI_MSB_FIRST, SPI_FOSC_DIV16); FLASH_CS_ENABLE; spi_send (FLASH_SST_CMD_READ_ID); rsp[0] = spi_recv (); rsp[1] = spi_recv (); rsp[2] = spi_recv (); FLASH_CS_DISABLE; switch (rsp[0]) { case FLASH_SST_MANUFACTURER_ID: flash_sst_init (); flash_init_sst (); res = 1; break; case FLASH_AT_MANUFACTURER_ID: flash_at_init (); flash_init_at (); res = 1; break; default: res = 0; } return res; }
int main (void) { init(); led_on(); uart_str("Hello!\r\n"); spi_slave(); led_off(); while (1) { switch (spi_recv()) { case 0: break; case 1: // CPU wants to read a value. this means we are now the master, to transfer the value back. spi_master(); spi_send(uart_read()); spi_slave(); break; case 2: // CPU wrote a value. Output it to serial. uart_write(spi_recv()); break; default: break; } } }
/***************************************************************************** * 函 数 名 : spiDevDetect * * 功能描述 : 动态探测SPI总线上接入的设备类型,验证SPI总线驱动 * * 输入参数 : * 输出参数 : * * 返 回 值 : * * 其它说明 : * *****************************************************************************/ s32 spi_dev_detect( eSpiDevType *devType ) { u16 ucCmdRDID = SPI_DEV_CMD_RDID; u32 ulRecv = 0; /*cprintf("\r\nspiDevDetect begin!" );*/ if(NULL == devType) { cprintf("\r\nPARA *devType is NULL!"); return ERROR; } /* EEPROM对该命令不支持,会收不到数据超时退出 */ if(OK != spi_recv(SPI_NO_DEV, SPI_CS_DEV, (u16*)&ulRecv, 3, &ucCmdRDID, 1)) { cprintf("\r\nread ID err"); *devType = E_SPI_DEV_TYPE_EEPROM; return OK; } cprintf("\r\nID: %x", ulRecv); /* EEPROM不支持该命令,会返回全0或全1 */ if(0 != ulRecv && 0xFFFFFF != ulRecv) { *devType = E_SPI_DEV_TYPE_SFLASH; } else { *devType = E_SPI_DEV_TYPE_EEPROM; } cprintf("\r\ndevType is : @ %x", *devType); return OK; }
// @return response returned from command, or SPI_EMPTY_BYTE (0xFF) if failed // note: this code assumes a regular 512-byte block since SDHC allows only this // pass len == 0 to read all 512 bytes into buf uint8_t sd_readSingleBlockHC( uint8_t addr3, uint8_t addr2, uint8_t addr1, uint8_t addr0, uint8_t* buf ) { uint8_t ret = sd_command( SD_READ_SINGLE_BLOCK, addr3, addr2, addr1, addr0 ); if ( ret != SD_RET_READY ) { sd_debug_printf( "RET: not READY; [0x%02hhx] received\n\r", ret ); return SPI_EMPTY_BYTE; } spi_assert_SS(); for( uint8_t i = SD_SPI_RESPONSE_WAIT_MAX; i > 0; i-- ) { *buf = spi_recv(); if ( *buf == SPI_EMPTY_BYTE ) { continue; } if ( *buf != SD_DATA_TOKEN ) { spi_deassert_SS(); sd_debug_printf( "RET: invalid token received\n\r" ); return SPI_EMPTY_BYTE; } _spi_recv_block( buf ); // CRC values are ignored outside of debug mode; split to avoid comp. warnings #ifdef SD_DEBUG_VERBOSE uint8_t crc1 = spi_recv(); uint8_t crc2 = spi_recv(); sd_debug_verbose_printf( "CRC: 0x%02hhx %02hhx\n\r", crc1, crc2 ); #else spi_recv(); spi_recv(); #endif sd_debug_verbose_printf( "CRC: 0x%02hhx %02hhx\n\r", crc1, crc2 ); // do a CRC check if you're masochistic (or have a GHz-ish CPU) spi_recv(); // required 8 clock cooldown return ret; } spi_deassert_SS(); sd_debug_printf( "RET: busy\n\r" ); return SPI_EMPTY_BYTE; }
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; } } }
// @return first byte of returned response, or SPI_EMPTY_BYTE (0xFF) if failed uint8_t sd_commandEx( uint8_t cmd, uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t arg3, uint8_t crc, uint8_t* retBuf ) { sd_debug_verbose_printf( "CMD: %hhu ARG: 0x%02hhx %02hhx %02hhx %02hhx CRC: 0x%02hhx\n\r", cmd, arg0, arg1, arg2, arg3, crc ); spi_assert_SS(); spi_send( cmd | 0x40 ); spi_send( arg0 ); spi_send( arg1 ); spi_send( arg2 ); spi_send( arg3 ); spi_send( crc ); uint8_t r; bool allowBusyByte = false; switch( cmd ) { case SD_SEND_IF_COND: // CMD8 // R7 case SD_READ_OCR: // CMD58 // R3 r = 5; break; case SD_SEND_STATUS: // CMD13 //case SD_STATUS: // ACMD13 // handled by case above r = 2; // R2 break; case SD_STOP_TRANSMISSION: // CMD12 case SD_SET_WRITE_PROT: // CMD28 case SD_CLR_WRITE_PROT: // CMD29 case SD_ERASE: // CMD38 allowBusyByte = true; // R1b default: // R1 r = 1; break; } for( uint8_t i = SD_SPI_RESPONSE_WAIT_MAX; i > 0; i-- ) { retBuf[0] = spi_recv(); if ( retBuf[0] == SPI_EMPTY_BYTE ) { continue; } if ( allowBusyByte ) { // implies r == 1 retBuf[1] = spi_recv(); if ( !retBuf[1] ) { // busy; don't wait, just return; caller should wait himself spi_deassert_SS(); sd_debug_printf( "RET: busy\n\r" ); return SD_COMMAND_ERROR; // == SPI_EMPTY_BYTE } // else !busy - we already did the wait } else { for( uint8_t j = 1; j < r; j++ ) { retBuf[j] = spi_recv(); } } spi_recv(); // req. 8 cycles wait = 1 SPI output byte spi_deassert_SS(); sd_debug_verbose_printf( "RET: 0x%02hhx 0x%02hhx 0x%02hhx 0x%02hhx 0x%02hhx\n\r", retBuf[0], r > 1 ? retBuf[1] : 0xFF, r > 2 ? retBuf[2] : 0xFF, r > 3 ? retBuf[3] : 0xFF, r > 4 ? retBuf[4] : 0xFF ); return retBuf[0]; } spi_deassert_SS(); sd_debug_printf( "RET: error: response timeout\n\r" ); return SD_COMMAND_ERROR; // == SPI_EMPTY_BYTE }