spi_via_usi_driver.c


1
#include "spi_via_usi_driver.h"
2
3
/*! \brief  Data input register buffer.
4
 *
5
 *  Incoming bytes are stored in this byte until the next transfer is complete.
6
 *  This byte can be used the same way as the SPI data register in the native
7
 *  SPI module, which means that the byte must be read before the next transfer
8
 *  completes and overwrites the current value.
9
 */
10
unsigned char storedUSIDR;
11
12
13
14
/*! \brief  Driver status bit structure.
15
 *
16
 *  This struct contains status flags for the driver.
17
 *  The flags have the same meaning as the corresponding status flags
18
 *  for the native SPI module. The flags should not be changed by the user.
19
 *  The driver takes care of updating the flags when required.
20
 */
21
volatile struct usidriverStatus_t spiX_status; //!< The driver status bits.
22
23
24
25
/*! \brief  Timer/Counter 0 Compare Match Interrupt handler.
26
 *
27
 *  This interrupt handler is only enabled when transferring data
28
 *  in master mode. It toggles the USI clock pin, i.e. two interrupts
29
 *  results in one clock period on the clock pin and for the USI counter.
30
 */
31
ISR(SIG_TIMER0_COMPA)
32
{
33
  USICR |= (1<<USITC);  // Toggle clock output pin.
34
}
35
36
37
38
/*! \brief  USI Timer Overflow Interrupt handler.
39
 *
40
 *  This handler disables the compare match interrupt if in master mode.
41
 *  When the USI counter overflows, a byte has been transferred, and we
42
 *  have to stop the timer tick.
43
 *  For all modes the USIDR contents are stored and flags are updated.
44
 */
45
ISR(USI_OVERFLOW_vect)
46
{
47
  // Master must now disable the compare match interrupt
48
  // to prevent more USI counter clocks.
49
  if( spiX_status.masterMode == 1 ) {
50
    TIMSK &= ~(1<<OCIE0A);
51
  }
52
  
53
  // Update flags and clear USI counter
54
  USISR = (1<<USIOIF);
55
  spiX_status.transferComplete = 1;
56
57
  // Copy USIDR to buffer to prevent overwrite on next transfer.
58
  storedUSIDR = USIDR;
59
}
60
61
62
void spiX_initmaster( char spi_mode )
63
{
64
  // Configure port directions.
65
  USI_DIR_REG |= (1<<USI_DATAOUT_PIN) | (1<<USI_CLOCK_PIN); // Outputs.
66
  USI_DIR_REG &= ~(1<<USI_DATAIN_PIN);                      // Inputs.
67
  USI_OUT_REG |= (1<<USI_DATAIN_PIN);                       // Pull-ups.
68
  
69
  // Configure USI to 3-wire master mode with overflow interrupt.
70
  USICR = (1<<USIOIE) | (1<<USIWM0) |
71
          (1<<USICS1) | (spi_mode<<USICS0) |
72
          (1<<USICLK);
73
74
  // Enable 'Clear Timer on Compare match' and init prescaler.
75
  TCCR0A = (1<<WGM01) | TC0_PS_SETTING;
76
  
77
  // Init Output Compare Register.
78
  OCR0A = TC0_COMPARE_VALUE;
79
  
80
  // Init driver status register.
81
  spiX_status.masterMode       = 1;
82
  spiX_status.transferComplete = 0;
83
  spiX_status.writeCollision   = 0;
84
  
85
  storedUSIDR = 0;
86
}
87
88
void spiX_initslave( char spi_mode )
89
{
90
  // Configure port directions.
91
  USI_DIR_REG |= (1<<USI_DATAOUT_PIN);                      // Outputs.
92
  USI_DIR_REG &= ~(1<<USI_DATAIN_PIN) | (1<<USI_CLOCK_PIN); // Inputs.
93
  USI_OUT_REG |= (1<<USI_DATAIN_PIN) | (1<<USI_CLOCK_PIN);  // Pull-ups.
94
  
95
  // Configure USI to 3-wire slave mode with overflow interrupt.
96
  USICR = (1<<USIOIE) | (1<<USIWM0) |
97
          (1<<USICS1) | (spi_mode<<USICS0);
98
  
99
  // Init driver status register.
100
  spiX_status.masterMode       = 0;
101
  spiX_status.transferComplete = 0;
102
  spiX_status.writeCollision   = 0;
103
  
104
  storedUSIDR = 0;
105
}
106
107
char spiX_put( unsigned char val )
108
{
109
  // Check if transmission in progress,
110
  // i.e. USI counter unequal to zero.
111
  if( (USISR & 0x0F) != 0 ) {
112
    // Indicate write collision and return.
113
    spiX_status.writeCollision = 1;
114
    return 0;
115
  }
116
  
117
  // Reinit flags.
118
  spiX_status.transferComplete = 0;
119
  spiX_status.writeCollision = 0;
120
121
  // Put data in USI data register.
122
  USIDR = val;
123
  
124
  // Master should now enable compare match interrupts.
125
  if( spiX_status.masterMode == 1 ) {
126
    TIFR |= (1<<OCF0A);   // Clear compare match flag.
127
    TIMSK |= (1<<OCIE0A); // Enable compare match interrupt.
128
  }
129
130
  if( spiX_status.writeCollision == 0 ) return 1;
131
  return 0;
132
}
133
134
unsigned char spiX_get()
135
{
136
  return storedUSIDR;
137
}
138
139
void spiX_wait()
140
{
141
  do {} while( spiX_status.transferComplete == 0 );
142
}