Skip to content
Prev Previous commit
Next Next commit
machine/attiny85: add LSB-first bit order support
Add software-based LSB-first support for USI SPI. The USI hardware only
supports MSB-first, so bit reversal is done in software before sending
and after receiving.

Uses an efficient parallel bit swap algorithm (3 operations) to reverse
the byte.

Co-authored-by: Ona <no-reply@ona.com>
  • Loading branch information
jespino and ona-agent committed Jan 16, 2026
commit a6d2ff5d4d9ed8da5035461c0fd3c6e49cd8b485
33 changes: 29 additions & 4 deletions src/machine/machine_attiny85.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,9 @@ type SPI struct {

// USICR value configured for the selected SPI mode
usicrValue uint8

// LSB-first mode (requires software bit reversal)
lsbFirst bool
}

// SPI0 is the USI-based SPI interface on the ATTiny85
Expand Down Expand Up @@ -500,15 +503,30 @@ func (s *SPI) Configure(config SPIConfig) error {
s.delayCycles = 0
}

// Note: LSBFirst is not directly supported by USI hardware
// It would need to be implemented in software if required
// Store LSBFirst setting for use in Transfer
s.lsbFirst = config.LSBFirst

return nil
}

// reverseByte reverses the bit order of a byte (MSB <-> LSB)
// Used for LSB-first SPI mode since USI hardware only supports MSB-first
func reverseByte(b byte) byte {
b = (b&0xF0)>>4 | (b&0x0F)<<4
b = (b&0xCC)>>2 | (b&0x33)<<2
b = (b&0xAA)>>1 | (b&0x55)<<1
return b
}

// Transfer performs a single byte SPI transfer (send and receive simultaneously)
// This implements the USI-based SPI transfer using the "clock strobing" technique
func (s *SPI) Transfer(b byte) (byte, error) {
// For LSB-first mode, reverse the bits before sending
// USI hardware only supports MSB-first, so we do it in software
if s.lsbFirst {
b = reverseByte(b)
}

// Load the byte to transmit into the USI Data Register
s.usidr.Set(b)

Expand Down Expand Up @@ -542,6 +560,13 @@ func (s *SPI) Transfer(b byte) (byte, error) {
}
}

// After 8 bits are transferred, return the received byte
return s.usidr.Get(), nil
// Get the received byte
result := s.usidr.Get()

// For LSB-first mode, reverse the received bits
if s.lsbFirst {
result = reverseByte(result)
}

return result, nil
}
Loading