1 | /*
|
2 | * SPI.c
|
3 | *
|
4 | * Created: 13.09.2004 12:08:54
|
5 | * Author: Atmel
|
6 | * Changed: 19.08.2014 17:48:49, J.Kathe
|
7 | *
|
8 | * Control and initialization functions for an USI SPI-Interface on an AVR. Written by Atmel
|
9 | */
|
10 |
|
11 | #include "USI_SPI.h"
|
12 | #include <avr/io.h>
|
13 | #include <avr/interrupt.h>
|
14 | #include <util/delay.h>
|
15 |
|
16 | /* USI port and pin definitions.
|
17 | */
|
18 | #define USI_OUT_REG PORTB //!< USI port output register.
|
19 | #define USI_DIR_REG DDRB //!< USI port direction register.
|
20 | #define USI_CLOCK_PIN PB7 //!< USI clock I/O pin.
|
21 | #define USI_DATAIN_PIN PB5 //!< USI data input pin.
|
22 | #define USI_DATAOUT_PIN PB6 //!< USI data output pin.
|
23 | #define USI_nSS_PIN PB4 //!< USI slave-select pin
|
24 |
|
25 |
|
26 |
|
27 |
|
28 | /* Speed configuration:
|
29 | * Bits per second = CPUSPEED / PRESCALER / (COMPAREVALUE+1) / 2.
|
30 | * Maximum = CPUSPEED / 64.
|
31 | */
|
32 | #define TC0_PRESCALER_VALUE 256 //!< Must be 1, 8, 64, 256 or 1024.
|
33 | #define TC0_COMPARE_VALUE 1 //!< Must be 0 to 255. Minimum 31 with prescaler CLK/1.
|
34 |
|
35 |
|
36 |
|
37 |
|
38 | /* Prescaler value converted to bit settings.
|
39 | */
|
40 | #if TC0_PRESCALER_VALUE == 1
|
41 | #define TC0_PS_SETTING (1<<CS00)
|
42 | #elif TC0_PRESCALER_VALUE == 8
|
43 | #define TC0_PS_SETTING (1<<CS01)
|
44 | #elif TC0_PRESCALER_VALUE == 64
|
45 | #define TC0_PS_SETTING (1<<CS01)|(1<<CS00)
|
46 | #elif TC0_PRESCALER_VALUE == 256
|
47 | #define TC0_PS_SETTING (1<<CS02)
|
48 | #elif TC0_PRESCALER_VALUE == 1024
|
49 | #define TC0_PS_SETTING (1<<CS02)|(1<<CS00)
|
50 | #else
|
51 | #error Invalid T/C0 prescaler setting.
|
52 | #endif
|
53 |
|
54 |
|
55 |
|
56 | /*! \brief Data input register buffer.
|
57 | *
|
58 | * Incoming bytes are stored in this byte until the next transfer is complete.
|
59 | * This byte can be used the same way as the SPI data register in the native
|
60 | * SPI module, which means that the byte must be read before the next transfer
|
61 | * completes and overwrites the current value.
|
62 | */
|
63 | unsigned char storedUSIDR;
|
64 |
|
65 |
|
66 |
|
67 | /*! \brief Driver status bit structure.
|
68 | *
|
69 | * This struct contains status flags for the driver.
|
70 | * The flags have the same meaning as the corresponding status flags
|
71 | * for the native SPI module. The flags should not be changed by the user.
|
72 | * The driver takes care of updating the flags when required.
|
73 | */
|
74 | struct usidriverStatus_t {
|
75 | unsigned char masterMode : 1; //!< True if in master mode.
|
76 | unsigned char transferComplete : 1; //!< True when transfer completed.
|
77 | unsigned char writeCollision : 1; //!< True if put attempted during transfer.
|
78 | };
|
79 |
|
80 | volatile struct usidriverStatus_t spiX_status; //!< The driver status bits.
|
81 |
|
82 |
|
83 |
|
84 | // Change for SPI-Slave!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
85 | ISR(TIMER0_COMPA_vect)
|
86 | {
|
87 | USICR |= (1<<USITC); // Toggle clock output pin.
|
88 |
|
89 | //wait for next transmission
|
90 | PORTD |= (1<<PD4);
|
91 | _delay_ms(500);
|
92 | PORTD &= ~(1<<PD4);
|
93 | _delay_ms(500);
|
94 | }
|
95 |
|
96 |
|
97 |
|
98 | /*! \brief USI Timer Overflow Interrupt handler.
|
99 | *
|
100 | * This handler disables the compare match interrupt if in master mode.
|
101 | * When the USI counter overflows, a byte has been transferred, and we
|
102 | * have to stop the timer tick.
|
103 | * For all modes the USIDR contents are stored and flags are updated.
|
104 | */
|
105 | ISR( USI_OVERFLOW_vect )
|
106 | {
|
107 | // Master must now disable the compare match interrupt
|
108 | // to prevent more USI counter clocks.
|
109 | if( spiX_status.masterMode == 1 ) {
|
110 | TIMSK &= ~(1<<OCIE0A);
|
111 | }
|
112 |
|
113 | // Update flags and clear USI counter
|
114 | USISR = (1<<USIOIF);
|
115 | spiX_status.transferComplete = 1;
|
116 |
|
117 | // Copy USIDR to buffer to prevent overwrite on next transfer.
|
118 | storedUSIDR = USIDR;
|
119 | }
|
120 |
|
121 |
|
122 |
|
123 | /*! \brief Initialize USI as SPI master.
|
124 | *
|
125 | * This function sets up all pin directions and module configurations.
|
126 | * Use this function initially or when changing from slave to master mode.
|
127 | * Note that the stored USIDR value is cleared.
|
128 | *
|
129 | * \param spi_mode Required SPI mode, must be 0 or 1.
|
130 | */
|
131 | void spiX_initmaster( char spi_mode )
|
132 | {
|
133 | // Configure port directions.
|
134 | USI_DIR_REG |= (1<<USI_DATAOUT_PIN) | (1<<USI_CLOCK_PIN) | (1<<USI_nSS_PIN); // Outputs.
|
135 | USI_DIR_REG &= ~(1<<USI_DATAIN_PIN); // Inputs.
|
136 | USI_OUT_REG |= (1<<USI_DATAIN_PIN); // Pull-ups.
|
137 |
|
138 | // Pull nSS up, so no slave is selected
|
139 | PORTB |= (1<<USI_nSS_PIN);
|
140 |
|
141 | // Configure USI to 3-wire master mode with overflow interrupt.
|
142 | USICR = (1<<USIOIE) | (1<<USIWM0) | (1<<USICS1) | (spi_mode<<USICS0) | (1<<USICLK);
|
143 |
|
144 | // Enable 'Clear Timer on Compare match' and init prescaler.
|
145 | TCCR0A |= (1<<WGM01) | (1<<WGM00);
|
146 | TCCR0B |= (1<<TC0_PS_SETTING);
|
147 |
|
148 | // Init Output Compare Register.
|
149 | OCR0A = TC0_COMPARE_VALUE;
|
150 |
|
151 | // Init driver status register.
|
152 | spiX_status.masterMode = 1;
|
153 | spiX_status.transferComplete = 0;
|
154 | spiX_status.writeCollision = 0;
|
155 |
|
156 | storedUSIDR = 0;
|
157 | }
|
158 |
|
159 |
|
160 |
|
161 | /*! \brief Initialize USI as SPI slave.
|
162 | *
|
163 | * This function sets up all pin directions and module configurations.
|
164 | * Use this function initially or when changing from master to slave mode.
|
165 | * Note that the stored USIDR value is cleared.
|
166 | *
|
167 | * \param spi_mode Required SPI mode, must be 0 or 1.
|
168 | */
|
169 | void spiX_initslave( char spi_mode )
|
170 | {
|
171 | // Configure port directions.
|
172 | USI_DIR_REG |= (1<<USI_DATAOUT_PIN); // Outputs.
|
173 | USI_DIR_REG &= ~(1<<USI_DATAIN_PIN) | (1<<USI_CLOCK_PIN) | ~(1<<USI_nSS_PIN); // Inputs.
|
174 | USI_OUT_REG |= (1<<USI_DATAIN_PIN) | (1<<USI_CLOCK_PIN) | (1<<USI_nSS_PIN); // Pull-ups.
|
175 |
|
176 | // Configure USI to 3-wire slave mode with overflow interrupt.
|
177 | USICR = (1<<USIOIE) | (1<<USIWM0) | (1<<USICS1) | (spi_mode<<USICS0);
|
178 |
|
179 | // Init driver status register.
|
180 | spiX_status.masterMode = 0;
|
181 | spiX_status.transferComplete = 0;
|
182 | spiX_status.writeCollision = 0;
|
183 |
|
184 | storedUSIDR = 0;
|
185 | }
|
186 |
|
187 |
|
188 |
|
189 | /*! \brief Put one byte on bus.
|
190 | *
|
191 | * Use this function like you would write to the SPDR register in the native SPI module.
|
192 | * Calling this function in master mode starts a transfer, while in slave mode, a
|
193 | * byte will be prepared for the next transfer initiated by the master device.
|
194 | * If a transfer is in progress, this function will set the write collision flag
|
195 | * and return without altering the data registers.
|
196 | *
|
197 | * \returns 0 if a write collision occurred, 1 otherwise.
|
198 | */
|
199 | char spiX_put( unsigned char val )
|
200 | {
|
201 | // Check if transmission in progress,
|
202 | // i.e. USI counter unequal to zero.
|
203 | if( (USISR & 0x0F) != 0 ) {
|
204 | // Indicate write collision and return.
|
205 | spiX_status.writeCollision = 1;
|
206 | return 0;
|
207 | }
|
208 |
|
209 | // Reinit flags.
|
210 | spiX_status.transferComplete = 0;
|
211 | spiX_status.writeCollision = 0;
|
212 |
|
213 | // Put data in USI data register.
|
214 | USIDR = val;
|
215 |
|
216 | // Pull nSS down, so slave is selected
|
217 | PORTB &= ~(1<<USI_nSS_PIN);
|
218 |
|
219 | // Master should now enable compare match interrupts.
|
220 | if( spiX_status.masterMode == 1 ) {
|
221 | TIFR |= (1<<OCF0A); // Clear compare match flag.
|
222 | TIMSK |= (1<<OCIE0A); // Enable compare match interrupt.
|
223 | }
|
224 |
|
225 | if( spiX_status.writeCollision == 0 ) return 1;
|
226 | return 0;
|
227 | }
|
228 |
|
229 |
|
230 |
|
231 | /*! \brief Get one byte from bus.
|
232 | *
|
233 | * This function only returns the previous stored USIDR value.
|
234 | * The transfer complete flag is not checked. Use this function
|
235 | * like you would read from the SPDR register in the native SPI module.
|
236 | */
|
237 | unsigned char spiX_get()
|
238 | {
|
239 | return storedUSIDR;
|
240 | }
|
241 |
|
242 |
|
243 |
|
244 | /*! \brief Wait for transfer to complete.
|
245 | *
|
246 | * This function waits until the transfer complete flag is set.
|
247 | * Use this function like you would wait for the native SPI interrupt flag.
|
248 | */
|
249 | void spiX_wait()
|
250 | {
|
251 | //do {} while( spiX_status.transferComplete == 0 );
|
252 |
|
253 | // Pull nSS up, so no slave is selected
|
254 | PORTB |= (1<<USI_nSS_PIN);
|
255 | }
|
256 |
|
257 |
|
258 |
|
259 | // end of file
|