The Universal Serial Interface (USI) is a multi purpose hardware resource which provide the basics hardware for various serial communications and is faster and reliable then implementing it in software.
You mainly find the USI on ATTINY devices but also for example on ATMEGA169.
USI Features:
• | Two-wire Synchronous Data Transfer |
• Three-wire Synchronous Data Transfer
• Data Received Interrupt
• Wakeup from Idle Mode
The USI can be used in Two wire mode and in three wire mode:
• | 2 wire mode --> I2C/TWI |
• | 3 wire mode --> SPI |
The USI handle only the low level communication. High level communication for example for 2 wire mode (I2C) like address setting, message interpreting or preparing of data needs to be handled by software in the main loop.
There are Application Notes from Atmel available:
AVR312: Using the USI module as a I2C slave
AVR310: Using the USI module as a I2C master
The 3 wire mode (SPI) is easier to implement and therefore shown here as an example.
The Slave Select (SS) needs to be implemented in software if needed.
The USI Pin names are: DI, DO and USCK.
AVR319: Using the USI module for SPI communication
See also:
Using the SPI protocol, SPISLAVE, Using I2C Protocol, confiig TWISLAVE, I2C TWI Slave , USI as TWI Slave
Following an example how to use an ATTINY as an SPI Master and another example show an SPI Slave over USI.
Example (SPI Master with USI):
1. Configure the port pin's:
'---------Using ATTINY as SPI MASTER over USI-----------------------------------
Config Portb.2 = Output 'USCK ----> SCK (Slave)
Config Portb.1 = Output 'DO ----> SDI (Slave)
Config Portb.0 = Input 'DI ----> SDO (Slave)
Set Portb.0
Sdo Alias Pinb.0 'Pullup
2. Configure the Slave Select
Config Portb.3 = Output 'Slave Select (SS) ----> SEL (Slave)
Set Portb.3
Sel Alias Portb.3
3. Configure the 3 wire mode
Set Usicr.usiwm0 'Three-wire mode. Uses DO, DI, and USCK pins.
'The Data Output (DO) pin overrides the corresponding bit in the PORTA
'register. However, the corresponding DDRA bit still controls the data direction.
'When the port pin is set as input the pin pull-up is controlled by the PORTA bit.
'The Data Input (DI) and Serial Clock (USCK) pins do not affect the normal port
'operation. When operating as master, clock pulses are software generated by
'toggling the PORTA register, while the data direction is set to output. The
'USITC bit in the USICR Register can be used for this purpose.
4. Function for send or receive a byte over USI (SPI Master mode)
Const Usi_clk_low = &B0001_0001
Const Usi_clk_high = &B0001_0011
'Wirte or read a byte over USI in SPI Master Mode
Function Usi_byte(usi_out As Byte) As Byte
Local I As Byte
Usidr = Usi_out 'Byte to write over USI
For I = 1 To 8
Usicr = Usi_clk_low 'Toggle the USI Clock to send or receive the single bits over USI (8 Bit)
Usicr = Usi_clk_high
Next
Usi_byte = Usidr 'Byte received over USI
End Function
5. call the function to send/receive a byte
Reset Sel
Usi_return = Usi_byte(my_byte)
Set Sel
Example (SPI Slave with USI):
The following example show how to use an USI of ATTINY85 as SPI SLAVE.
(you will also find the SPI Master for this USI of ATTINY85 as SPI SLAVE example)
ATXMEGA (SPI Master) <-----SPI------> (SPI Slave over USI) ATTIN85
1. First we configure the USI in Three-wire Mode
2. Setup the USI Overflow Interrupt
3. And wait until the USI Oveflow Interrupt is fired
4. Then we read the USI Data-Register and clear the USI Interrupt flag
' Using USI as an SPI slave with Attiny85
' The ATTINY85 work with 3.3 V so we can direct connect it to an ATXMEGA
' Following you find also a SPI configuration with an XMEGA as SPI Master which I have tested with this SPI Slave
'(
Config Spid = Hard , Master = Yes , Mode = 0 , Clockdiv = Clk128 , Data_order = Msb , Ss = Auto
'SS = Auto set the Slave Select (SS) automatically before a print #X or input #X command (including initialization of the pin)
'Master SPI clock = 32MHz/Clk128 = 250KHz
Open "SPID" For Binary As #12
')
$regfile = "ATtiny85.DAT"
$crystal = 8000000 'internal crystal
$hwstack = 32
$swstack = 10
$framesize = 30
Dim B As Byte
Dim Usi_data_ready As Bit
Config Portb.1 = Output 'DO ---> MISO of ATXMEGA (PD6)
Config Portb.2 = Output 'USCK ---> SCK of ATXMEGA (PD7)
Set Portb.2 'enable Pullup
Config Portb.0 = Input 'DI ---> MOSI of ATXMEGA (PD5)
Set Portb.0 'enable Pullup
'We do not use Slave Select in this example but this would be the configuration
Config Portb.4 = Input 'Slave Select
Set Portb.4 ' enable Pullup
Ss Alias Pinb.4
Config Portb.3 = Output 'Serial Debug output
Open "comb.3:9600,8,n,1" For Output As #1
Print #1 , "serial output"
'Init USI as SPI Slave in USICR = USI Control Register
Set Usicr.usiwm0 'Three-wire mode. Uses DO, DI, and USCK pins.
Set Usicr.usics1 'Clock Source: External, positive edge ; External, both edges
Set Usicr.usioie 'USI Counter Overflow Interrupt Enable
On Usi_ovf Usi_overflow_int
Enable Usi_ovf
Enable Interrupts
Do
If Usi_data_ready = 1 Then
Reset Usi_data_ready
Print #1 , B 'print the received byte over debug output
End If
Loop
End 'end program
' After eight clock pulses (i.e., 16 clock edges) the 4-Bit USI counter will generate an overflow interrupt
' A USI Overflow Int can also wakeup the Attiny from Idle mode if needed
Usi_overflow_int:
Set Usi_data_ready
B = Usidr
Usisr = &B01_000000 'Reset Overflow Flag and reset 4-Bit USI counter
Return
SPI Master for the ATTIN85 as SPI Slave over USI:
'This is the SPI MASTER for the ATTINY85 with USI in SPI Slave Mode
$regfile = "xm256a3bdef.dat"
$crystal = 32000000 '32MHz
$hwstack = 64
$swstack = 40
$framesize = 80
Config Osc = Disabled , 32mhzosc = Enabled
Config Sysclock = 32mhz '--> 32MHz
'configure the priority
Config Priority = Static , Vector = Application , Lo = Enabled , Med = Enabled
Enable Interrupts
Config Com7 = 57600 , Mode = Asynchroneous , Parity = None , Stopbits = 1 , Databits = 8
Waitms 2
Open "COM7:" For Binary As #1
Print #1 ,
Print #1 , "------------SPI MASTER-Slave Test----------------"
'We use Port D for SPI
Config Pind.7 = Output
Config Pind.6 = Input
Config Pind.5 = Output
Config Pind.4 = Output
'Bit7 = SCK = Output ------> USCK (ATTINY85) (PinB.2)
'Bit6 = MISO = Input ------> DO (ATTINY85) (PinB.1)
'Bit5 = MOSI = Output ------> DI (ATTINY85) (PinB.0)
'Bit4 = SS = Output ------> SS (ATTINY85) (PinB.4)
Slave_select Alias Portd.4
Set Slave_select
Portd_pin4ctrl = Bits(3 , 4) ' Enalbe Pullup
Dim Bspivar As Byte
Dim Spi_send_byte As Byte
Dim Spi_receive_byte As Byte
Dim Spi_master_want_send As Byte
'SPI, Master|Slave , MODE, clock division
Config Spid = Hard , Master = Yes , Mode = 0 , Clockdiv = Clk128 , Data_order = Msb , Ss = Auto
'SS = Auto set the Slave Select (SS) automatically before a print #X or input #X command (including initialization of the pin)
'Master SPI clock = 32MHz/Clk128 = 250KHz
Open "SPID" For Binary As #12
Main:
Do
Wait 3 'Every 3 seconds
Incr Spi_send_byte
Print #1 , "Spi_send_byte = " ; Spi_send_byte
'SEND TO SLAVE
Print #12 , Spi_send_byte 'SEND one BYTE TO SLAVE
Waitms 10
Input #12 , Spi_receive_byte
Print #1 , Spi_receive_byte
Loop
End 'end program
'there is NO CLOSE for SPI
'