SPI_treiber.c


1
// This file has been prepared for Doxygen automatic documentation generation.
2
/*! \file ********************************************************************
3
*
4
* Atmel Corporation
5
*
6
* \li File:               spi_via_usi_driver.c
7
* \li Compiler:           IAR EWAAVR 3.10c
8
* \li Support mail:       avr@atmel.com
9
*
10
* \li Supported devices:  All devices with Universal Serial Interface (USI)
11
*                         capabilities can be used.
12
*                         The example is written for ATmega169.
13
*
14
* \li AppNote:            AVR319 - Using the USI module for SPI communication.
15
*
16
* \li Description:        Example on how to use the USI module for communicating
17
*                         with SPI compatible devices. The functions and variables
18
*                         prefixed 'spiX_' can be renamed to be able to use several
19
*                         spi drivers (using different interfaces) with similar names.
20
*                         Some basic SPI knowledge is assumed.
21
*
22
*                         $Revision: 1.4 $
23
*                         $Date: Monday, September 13, 2004 12:08:54 UTC $
24
****************************************************************************/
25
#include <avr/io.h>
26
#include <avr/interrupt.h>
27
28
29
30
/* USI port and pin definitions.
31
 */
32
33
#define SCK    PB2    // Clock
34
#define SDO    PB1    // MISO  -> MasterIN SlaveOut
35
#define  SDI    PB0    // MOSI -> MasterOut SlaveIn
36
#define CS    PB5    // Chip Select
37
#define WP    PB4    // Schreibschutz
38
#define Hold  PB6    // Hold
39
40
#define USI_OUT_REG  PORTB  //!< USI port output register.
41
#define USI_IN_REG  PINB  //!< USI port input register.
42
#define USI_DIR_REG  DDRB  //!< USI port direction register.
43
#define USI_CLOCK_PIN  PB2  //!< USI clock I/O pin.
44
#define USI_DATAIN_PIN  PB1  //!< USI data input pin.
45
#define USI_DATAOUT_PIN  PB0  //!< USI data output pin.
46
47
48
49
50
/*  Speed configuration:
51
 *  Bits per second = CPUSPEED / PRESCALER / (COMPAREVALUE+1) / 2.
52
 *  Maximum = CPUSPEED / 64.
53
 */
54
#define TC0_PRESCALER_VALUE 256  //!< Must be 1, 8, 64, 256 or 1024.
55
#define TC0_COMPARE_VALUE   1  //!< Must be 0 to 255. Minimum 31 with prescaler CLK/1.
56
57
58
59
60
/*  Prescaler value converted to bit settings.
61
 */
62
#if TC0_PRESCALER_VALUE == 1
63
  #define TC0_PS_SETTING (1<<CS00)
64
#elif TC0_PRESCALER_VALUE == 8
65
  #define TC0_PS_SETTING (1<<CS01)
66
#elif TC0_PRESCALER_VALUE == 64
67
  #define TC0_PS_SETTING (1<<CS01)|(1<<CS00)
68
#elif TC0_PRESCALER_VALUE == 256
69
  #define TC0_PS_SETTING (1<<CS02)
70
#elif TC0_PRESCALER_VALUE == 1024
71
  #define TC0_PS_SETTING (1<<CS02)|(1<<CS00)
72
#else
73
  #error Invalid T/C0 prescaler setting.
74
#endif
75
76
77
78
79
void spiX_wait();
80
unsigned char spiX_get();
81
char spiX_put( unsigned char val );
82
void spiX_initslave( char spi_mode );
83
void spiX_initmaster( char spi_mode );
84
85
86
87
88
/*! \brief  Data input register buffer.
89
 *
90
 *  Incoming bytes are stored in this byte until the next transfer is complete.
91
 *  This byte can be used the same way as the SPI data register in the native
92
 *  SPI module, which means that the byte must be read before the next transfer
93
 *  completes and overwrites the current value.
94
 */
95
unsigned char storedUSIDR;
96
97
98
99
/*! \brief  Driver status bit structure.
100
 *
101
 *  This struct contains status flags for the driver.
102
 *  The flags have the same meaning as the corresponding status flags
103
 *  for the native SPI module. The flags should not be changed by the user.
104
 *  The driver takes care of updating the flags when required.
105
 */
106
struct usidriverStatus_t {
107
  unsigned char masterMode : 1;       //!< True if in master mode.
108
  unsigned char transferComplete : 1; //!< True when transfer completed.
109
  unsigned char writeCollision : 1;   //!< True if put attempted during transfer.
110
};
111
112
volatile struct usidriverStatus_t spiX_status; //!< The driver status bits.
113
114
115
116
/*! \brief  Timer/Counter 0 Compare Match Interrupt handler.
117
 *
118
 *  This interrupt handler is only enabled when transferring data
119
 *  in master mode. It toggles the USI clock pin, i.e. two interrupts
120
 *  results in one clock period on the clock pin and for the USI counter.
121
 */
122
ISR (TIMER0_COMPA_vect)
123
{
124
  USICR |= (1<<USITC);  // Toggle clock output pin.
125
}
126
127
128
129
/*! \brief  USI Timer Overflow Interrupt handler.
130
 *
131
 *  This handler disables the compare match interrupt if in master mode.
132
 *  When the USI counter overflows, a byte has been transferred, and we
133
 *  have to stop the timer tick.
134
 *  For all modes the USIDR contents are stored and flags are updated.
135
 */
136
ISR (USI_OVF_vect)
137
{
138
  // Master must now disable the compare match interrupt
139
  // to prevent more USI counter clocks.
140
  if( spiX_status.masterMode == 1 ) {
141
    TIMSK &= ~(1<<OCIE0A);
142
  }
143
  
144
  // Update flags and clear USI counter
145
  USISR = (1<<USIOIF);
146
  spiX_status.transferComplete = 1;
147
148
  // Copy USIDR to buffer to prevent overwrite on next transfer.
149
  storedUSIDR = USIDR;
150
}
151
152
153
154
/*! \brief  Initialize USI as SPI master.
155
 *
156
 *  This function sets up all pin directions and module configurations.
157
 *  Use this function initially or when changing from slave to master mode.
158
 *  Note that the stored USIDR value is cleared.
159
 *
160
 *  \param spi_mode  Required SPI mode, must be 0 or 1.
161
 */
162
void spiX_initmaster( char spi_mode )
163
{
164
  // Configure port directions.
165
  USI_DIR_REG |= (1<<USI_DATAOUT_PIN) | (1<<USI_CLOCK_PIN); // Outputs.
166
  USI_DIR_REG &= ~(1<<USI_DATAIN_PIN);                      // Inputs.
167
  USI_OUT_REG |= (1<<USI_DATAIN_PIN);                       // Pull-ups.
168
  
169
  // Configure USI to 3-wire master mode with overflow interrupt.
170
  USICR = (1<<USIOIE) | (1<<USIWM0) |
171
          (1<<USICS1) | (spi_mode<<USICS0) |
172
          (1<<USICLK);
173
174
  // Enable 'Clear Timer on Compare match' and init prescaler.
175
  TCCR0A = 0b00000001;
176
  TCCR0B = TC0_PS_SETTING;
177
  
178
  // Init Output Compare Register.
179
  OCR0A = TC0_COMPARE_VALUE;
180
  
181
  // Init driver status register.
182
  spiX_status.masterMode       = 1;
183
  spiX_status.transferComplete = 0;
184
  spiX_status.writeCollision   = 0;
185
  
186
  storedUSIDR = 0;
187
}
188
189
190
191
/*! \brief  Initialize USI as SPI slave.
192
 *
193
 *  This function sets up all pin directions and module configurations.
194
 *  Use this function initially or when changing from master to slave mode.
195
 *  Note that the stored USIDR value is cleared.
196
 *
197
 *  \param spi_mode  Required SPI mode, must be 0 or 1.
198
 */
199
void spiX_initslave( char spi_mode )
200
{
201
  // Configure port directions.
202
  USI_DIR_REG |= (1<<USI_DATAOUT_PIN);                      // Outputs.
203
  USI_DIR_REG &= ~(1<<USI_DATAIN_PIN) | (1<<USI_CLOCK_PIN); // Inputs.
204
  USI_OUT_REG |= (1<<USI_DATAIN_PIN) | (1<<USI_CLOCK_PIN);  // Pull-ups.
205
  
206
  // Configure USI to 3-wire slave mode with overflow interrupt.
207
  USICR = (1<<USIOIE) | (1<<USIWM0) |
208
          (1<<USICS1) | (spi_mode<<USICS0);
209
  
210
  // Init driver status register.
211
  spiX_status.masterMode       = 0;
212
  spiX_status.transferComplete = 0;
213
  spiX_status.writeCollision   = 0;
214
  
215
  storedUSIDR = 0;
216
}
217
218
219
220
/*! \brief  Put one byte on bus.
221
 *
222
 *  Use this function like you would write to the SPDR register in the native SPI module.
223
 *  Calling this function in master mode starts a transfer, while in slave mode, a
224
 *  byte will be prepared for the next transfer initiated by the master device.
225
 *  If a transfer is in progress, this function will set the write collision flag
226
 *  and return without altering the data registers.
227
 *
228
 *  \returns  0 if a write collision occurred, 1 otherwise.
229
 */
230
char spiX_put( unsigned char val )
231
{
232
  // Check if transmission in progress,
233
  // i.e. USI counter unequal to zero.
234
  if( (USISR & 0x0F) != 0 ) {
235
    // Indicate write collision and return.
236
    spiX_status.writeCollision = 1;
237
    return;
238
  }
239
  
240
  // Reinit flags.
241
  spiX_status.transferComplete = 0;
242
  spiX_status.writeCollision = 0;
243
244
  // Put data in USI data register.
245
  USIDR = val;
246
  
247
  // Master should now enable compare match interrupts.
248
  if( spiX_status.masterMode == 1 ) {
249
    TIFR |= (1<<OCF0A);   // Clear compare match flag.
250
    TIMSK |= (1<<OCIE0A); // Enable compare match interrupt.
251
  }
252
253
  if( spiX_status.writeCollision == 0 ) return 1;
254
  return 0;
255
}
256
257
258
259
/*! \brief  Get one byte from bus.
260
 *
261
 *  This function only returns the previous stored USIDR value.
262
 *  The transfer complete flag is not checked. Use this function
263
 *  like you would read from the SPDR register in the native SPI module.
264
 */
265
unsigned char spiX_get()
266
{
267
  return storedUSIDR;
268
}
269
270
271
272
/*! \brief  Wait for transfer to complete.
273
 *
274
 *  This function waits until the transfer complete flag is set.
275
 *  Use this function like you would wait for the native SPI interrupt flag.
276
 */
277
void spiX_wait()
278
{
279
  do {} while( spiX_status.transferComplete == 0 );
280
}
281
282
283
284
// end of file