| 347 | |
| 348 | template <u8 _DATA_PIN, u8 _CLOCK_PIN, u32 _SPI_CLOCK_DIVIDER> |
| 349 | class AVRHardwareSPIOutput { |
| 350 | Selectable *mPSelect; |
| 351 | |
| 352 | public: |
| 353 | AVRHardwareSPIOutput() { mPSelect = nullptr; } |
| 354 | AVRHardwareSPIOutput(Selectable *pSelect) { mPSelect = pSelect; } |
| 355 | void setSelect(Selectable *pSelect) { mPSelect = pSelect; } |
| 356 | |
| 357 | void init() FL_NOEXCEPT { |
| 358 | FastPin<_CLOCK_PIN>::setOutput(); |
| 359 | FastPin<_DATA_PIN>::setOutput(); |
| 360 | |
| 361 | // Configure PORTMUX for SPI (varies by chip family) |
| 362 | // megaAVR 0-series (ATmega4809) uses PORTMUX_TWISPIROUTEA |
| 363 | // tinyAVR 0/1-series (ATtiny1604 etc) uses PORTMUX.CTRLB |
| 364 | #if defined(PORTMUX_TWISPIROUTEA) |
| 365 | // Arduino Nano Every documentation lists SPI pins in ALT2 portmux position |
| 366 | PORTMUX_TWISPIROUTEA = PORTMUX_SPI01_bm; |
| 367 | #elif defined(PORTMUX_CTRLB) |
| 368 | // tinyAVR 0/1-series: use default SPI pin positions (no portmux change needed) |
| 369 | // PORTMUX.CTRLB bits are for SPI0 pin swap - we use default (0) |
| 370 | #endif |
| 371 | |
| 372 | // Set SPI master mode and clock scaler. |
| 373 | SPI0_CTRLA = SPI_MASTER_bm; |
| 374 | if(_SPI_CLOCK_DIVIDER >= 128) { SPI0_CTRLA |= SPI_PRESC1_bm|SPI_PRESC0_bm; } |
| 375 | else if (_SPI_CLOCK_DIVIDER >= 64) { SPI0_CTRLA |= SPI_PRESC1_bm; } |
| 376 | else if (_SPI_CLOCK_DIVIDER >= 32) { SPI0_CTRLA |= SPI_PRESC1_bm|SPI_CLK2X_bm; } |
| 377 | else if (_SPI_CLOCK_DIVIDER >= 16) { SPI0_CTRLA |= SPI_PRESC0_bm; } |
| 378 | else if (_SPI_CLOCK_DIVIDER >= 8) { SPI0_CTRLA |= SPI_PRESC0_bm|SPI_CLK2X_bm; } |
| 379 | else if (_SPI_CLOCK_DIVIDER >= 4) { /* default rate */ } |
| 380 | else { SPI0_CTRLA |= SPI_CLK2X_bm; } |
| 381 | |
| 382 | // Set mode 0 and disable slave select. |
| 383 | SPI0_CTRLB = SPI_SSD_bm; |
| 384 | |
| 385 | // Enable SPI. |
| 386 | SPI0_CTRLA |= SPI_ENABLE_bm; |
| 387 | } |
| 388 | |
| 389 | void setSPIRate() FL_NOEXCEPT { |
| 390 | SPI0_CTRLA &= ~ ( (1<<SPI_PRESC1_bp)|(1<<SPI_PRESC0_bp)|(1<<SPI_CLK2X_bp) ); // clear pre-scaler and clock multiplier bits |
| 391 | |
| 392 | if(_SPI_CLOCK_DIVIDER >= 128) { SPI0_CTRLA |= SPI_PRESC1_bm|SPI_PRESC0_bm; } |
| 393 | else if (_SPI_CLOCK_DIVIDER >= 64) { SPI0_CTRLA |= SPI_PRESC1_bm; } |
| 394 | else if (_SPI_CLOCK_DIVIDER >= 32) { SPI0_CTRLA |= SPI_PRESC1_bm|SPI_CLK2X_bm; } |
| 395 | else if (_SPI_CLOCK_DIVIDER >= 16) { SPI0_CTRLA |= SPI_PRESC0_bm; } |
| 396 | else if (_SPI_CLOCK_DIVIDER >= 8) { SPI0_CTRLA |= SPI_PRESC0_bm|SPI_CLK2X_bm; } |
| 397 | else if (_SPI_CLOCK_DIVIDER >= 4) { /* default rate */ } |
| 398 | else { SPI0_CTRLA |= SPI_CLK2X_bm; } |
| 399 | } |
| 400 | |
| 401 | static void stop() FL_NOEXCEPT { |
| 402 | SPI0_CTRLA &= ~(SPI_ENABLE_bm); |
| 403 | } |
| 404 | |
| 405 | static bool shouldWait(bool wait = false) FL_NOEXCEPT __attribute__((always_inline)) { |
| 406 | static bool sWait=false; |