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 | }
|