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
|