1 | /**
|
2 | * Name: USI TWI Slave driver - I2C/TWI-EEPROM
|
3 | * Version: 1.3 - Stable
|
4 | * Autor: Martin Junghans jtronics@gmx.de
|
5 | * Page: www.jtronics.de
|
6 | * License: GNU General Public License
|
7 | *
|
8 | * Update: 09.09.2011 - ATtiny24, ATtiny44, ATtiny88
|
9 | *
|
10 | * Created from Atmel source files for Application Note AVR312:
|
11 | * Using the USI Module as an I2C slave like an I2C-EEPROM.
|
12 | * 02.02.2013 - reworked by Willi Thiel
|
13 | */
|
14 |
|
15 | #include <avr/io.h>
|
16 | #include <avr/interrupt.h>
|
17 | #include "i2cslave.h"
|
18 |
|
19 | #define SET_USI_TO_SEND_ACK( ) { USIDR = 0; \
|
20 | DDR_USI |= ( 1 << PORT_USI_SDA ); \
|
21 | USISR = ( 0 << USI_START_COND_INT ) | \
|
22 | ( 1 << USIOIF ) | ( 1 << USIPF ) | \
|
23 | ( 1 << USIDC )| \
|
24 | ( 0x0E << USICNT0 );} /*!< Prepare ACK; Set SDA as output; Clear all interrupt flags, except Start Cond; Set USI counter to shift 1 bit */
|
25 |
|
26 | #define SET_USI_TO_READ_ACK( ) { USIDR = 0; \
|
27 | DDR_USI &= ~( 1 << PORT_USI_SDA ); \
|
28 | USISR = ( 0 << USI_START_COND_INT ) | \
|
29 | ( 1 << USIOIF) | \
|
30 | ( 1 << USIPF ) | \
|
31 | ( 1 << USIDC ) | \
|
32 | ( 0x0E << USICNT0 );} /*!< Set SDA as input; Prepare ACK; Clear all interrupt flags, except Start Cond; Set USI counter to shift 1 bit */
|
33 |
|
34 | #define SET_USI_TO_TWI_START_CONDITION_MODE( ) { \
|
35 | USICR = ( 1 << USISIE ) | ( 0 << USIOIE ) | \
|
36 | ( 1 << USIWM1 ) | ( 0 << USIWM0 ) | \
|
37 | ( 1 << USICS1 ) | ( 0 << USICS0 ) | ( 0 << USICLK ) | \
|
38 | ( 0 << USITC ); \
|
39 | USISR = ( 0 << USI_START_COND_INT ) | ( 1 << USIOIF ) | ( 1 << USIPF ) | \
|
40 | ( 1 << USIDC ) | ( 0x0 << USICNT0 ); } /*!< Enable Start Condition Interrupt, disable Overflow Interrupt; Set USI in Two-wire mode, no USI Counter overflow hold; Shift Register Clock Source = External, positive edge; 4-Bit Counter Source = external, both edges; No toggle clock-port pin; Clear all interrupt flags, except Start Cond */
|
41 |
|
42 | #define SET_USI_TO_SEND_DATA( ) { DDR_USI |= ( 1 << PORT_USI_SDA ); \
|
43 | USISR = ( 0 << USI_START_COND_INT ) | ( 1 << USIOIF ) | ( 1 << USIPF ) | \
|
44 | ( 1 << USIDC) | \
|
45 | ( 0x0 << USICNT0 ); \
|
46 | } /*!< Set SDA as output; Clear all interrupt flags, except Start Cond; Set USI to shift out 8 bits */
|
47 |
|
48 | #define SET_USI_TO_READ_DATA( ) { DDR_USI &= ~( 1 << PORT_USI_SDA ); \
|
49 | USISR = ( 0 << USI_START_COND_INT ) | ( 1 << USIOIF ) | \
|
50 | ( 1 << USIPF ) | ( 1 << USIDC ) | \
|
51 | ( 0x0 << USICNT0 ); \
|
52 | } /*!< Set SDA as input; Clear all interrupt flags, except Start Cond; Set USI to shift out 8 bits */
|
53 |
|
54 | typedef enum {
|
55 | USI_SLAVE_CHECK_ADDRESS = 0x00, USI_SLAVE_SEND_DATA = 0x01, USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA = 0x02, USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA = 0x03, USI_SLAVE_REQUEST_DATA = 0x04, USI_SLAVE_GET_DATA_AND_SEND_ACK = 0x05
|
56 | } overflow_state_t;
|
57 |
|
58 | volatile uint8_t slave_address; /*!< Address of the I2C slave */
|
59 | volatile overflow_state_t overflow_state; /*!< overflow state of the I2C slave */
|
60 |
|
61 | /**
|
62 | * Initialize slave
|
63 | *
|
64 | * @param address The address of the I2C slave device
|
65 | */
|
66 | void i2c_init(uint8_t address) {
|
67 | slave_address = address;
|
68 |
|
69 | // In Two Wire mode (USIWM1, USIWM0 = 1X), the slave USI will pull SCL
|
70 | // low when a start condition is detected or a counter overflow (only
|
71 | // for USIWM1, USIWM0 = 11). This inserts a wait state. SCL is released
|
72 | // by the ISRs (USI_START_vect and USI_OVERFLOW_vect).
|
73 |
|
74 | // Set SCL and SDA as output
|
75 | DDR_USI |= (1 << PORT_USI_SCL) | (1 << PORT_USI_SDA);
|
76 |
|
77 | // Set SCL high
|
78 | PORT_USI |= (1 << PORT_USI_SCL);
|
79 |
|
80 | // Set SDA high
|
81 | PORT_USI |= (1 << PORT_USI_SDA);
|
82 |
|
83 | // Set SDA as input
|
84 | DDR_USI &= ~(1 << PORT_USI_SDA);
|
85 |
|
86 | USICR = (1 << USISIE) | // Enable Start Condition Interrupt
|
87 | (0 << USIOIE) | // Disable Overflow Interrupt
|
88 | (1 << USIWM1) | (0 << USIWM0) | // Set USI in Two-wire mode, no USI Counter overflow hold
|
89 | (1 << USICS1) | (0 << USICS0) | (0 << USICLK) | // Shift Register Clock Source = external, positive edge 4-Bit Counter Source = external, both edges
|
90 | (0 << USITC); // No toggle clock-port pin
|
91 |
|
92 | // clear all interrupt flags and reset overflow counter
|
93 | USISR = (1 << USI_START_COND_INT) | (1 << USIOIF) | (1 << USIPF) | (1 << USIDC);
|
94 | }
|
95 |
|
96 | /**
|
97 | * USI Start Condition ISR
|
98 | */
|
99 | ISR( USI_START_VECTOR) {
|
100 |
|
101 | // Set default starting conditions for new TWI package
|
102 | overflow_state = USI_SLAVE_CHECK_ADDRESS;
|
103 |
|
104 | // Set SDA as input
|
105 | DDR_USI &= ~(1 << PORT_USI_SDA);
|
106 |
|
107 | // Wait for SCL to go low to ensure the Start Condition has completed (the
|
108 | // Start detector will hold SCL low ) - if a Stop Condition arises then leave
|
109 | // The interrupt to prevent waiting forever - don't use USISR to test for Stop
|
110 | // Condition as in Application Note AVR312 because the Stop Condition Flag is
|
111 | // going to be set from the last TWI sequence
|
112 |
|
113 | while ((PIN_USI & (1 << PIN_USI_SCL)) && !((PIN_USI & (1 << PIN_USI_SDA))))
|
114 | ; // SCL his high and SDA is low
|
115 |
|
116 | // A Stop Condition did not occur
|
117 | if (!(PIN_USI & (1 << PIN_USI_SDA))) {
|
118 | USICR = (1 << USISIE) | // Keep Start Condition Interrupt enabled to detect RESTART
|
119 | (1 << USIOIE) | // Enable Overflow Interrupt
|
120 | (1 << USIWM1) | (1 << USIWM0) | // Set USI in Two-wire mode, hold SCL low on USI Counter overflow
|
121 | (1 << USICS1) | (0 << USICS0) | (0 << USICLK) | // 4-Bit Counter Source = external, both edges; Clock Source = External, positive edge
|
122 | (0 << USITC); // No toggle clock-port pin
|
123 |
|
124 | } else {
|
125 | // A Stop Condition did occur
|
126 | USICR = (1 << USISIE) | // Enable Start Condition Interrupt
|
127 | (0 << USIOIE) | // Disable Overflow Interrupt
|
128 | (1 << USIWM1) | (0 << USIWM0) | // Set USI in Two-wire mode, no USI Counter overflow hold
|
129 | (1 << USICS1) | (0 << USICS0) | (0 << USICLK) | // 4-Bit Counter Source = external, both edges; Clock Source = external, positive edge
|
130 | (0 << USITC); // No toggle clock-port pin
|
131 | }
|
132 |
|
133 | USISR = (1 << USI_START_COND_INT) | (1 << USIOIF) | // Clear interrupt flags - resetting the Start Condition Flag will release SCL
|
134 | (1 << USIPF) | (1 << USIDC) | (0x0 << USICNT0); // Set USI to sample 8 bits (count 16 external SCL pin toggles)
|
135 | }
|
136 |
|
137 | /**
|
138 | * ISR( USI_OVERFLOW_VECTOR )
|
139 | *
|
140 | * Handles all the communication. Only disabled when waiting for a new Start Condition.
|
141 | */
|
142 | ISR( USI_OVERFLOW_VECTOR) {
|
143 | uint8_t data = 0;
|
144 | switch (overflow_state) {
|
145 |
|
146 | /**
|
147 | * Address mode: check address and send ACK (and next USI_SLAVE_SEND_DATA) if OK, else reset USI
|
148 | */
|
149 | case USI_SLAVE_CHECK_ADDRESS:
|
150 | // If address is either 0 or own address
|
151 | if (USIDR == 0 || (USIDR & ~1) == slave_address) {
|
152 | if (USIDR & 0x01) {
|
153 | // Master Write Data Mode - Slave transmit
|
154 | overflow_state = USI_SLAVE_SEND_DATA;
|
155 | } else {
|
156 | // Master Read Data Mode - Slave receive
|
157 | overflow_state = USI_SLAVE_REQUEST_DATA;
|
158 | // Buffer position undefined
|
159 | buffer_adr = 0xFF;
|
160 | } // end if
|
161 | SET_USI_TO_SEND_ACK()
|
162 | } else {
|
163 | SET_USI_TO_TWI_START_CONDITION_MODE()
|
164 | }
|
165 | break;
|
166 |
|
167 | /**
|
168 | * Master Write Data Mode - Slave transmit
|
169 | */
|
170 | // Check reply and goto USI_SLAVE_SEND_DATA if OK, else reset USI
|
171 | case USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA:
|
172 | if (USIDR) {
|
173 | // If NACK, the master does not want more data
|
174 | SET_USI_TO_TWI_START_CONDITION_MODE()
|
175 | return;
|
176 | }
|
177 |
|
178 | // From here we just drop straight into USI_SLAVE_SEND_DATA if the master sent an ACK
|
179 | case USI_SLAVE_SEND_DATA:
|
180 | // No buffer position given, set buffer address to 0
|
181 | if (buffer_adr == 0xFF) {
|
182 | buffer_adr = 0;
|
183 | }
|
184 |
|
185 | // Send data byte
|
186 | USIDR = i2c_buffer[buffer_adr];
|
187 |
|
188 | // Increment buffer address for next byte
|
189 | buffer_adr++;
|
190 |
|
191 | overflow_state = USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA;
|
192 | SET_USI_TO_SEND_DATA()
|
193 | break;
|
194 |
|
195 | // Set USI to sample reply from master
|
196 | // Next USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA
|
197 | case USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA:
|
198 | overflow_state = USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA;
|
199 | SET_USI_TO_READ_ACK()
|
200 | break;
|
201 |
|
202 | /**
|
203 | * Master Read Data Mode - Slave receive
|
204 | */
|
205 | // Set USI to sample data from master,
|
206 | // Next USI_SLAVE_GET_DATA_AND_SEND_ACK
|
207 | case USI_SLAVE_REQUEST_DATA:
|
208 | overflow_state = USI_SLAVE_GET_DATA_AND_SEND_ACK;
|
209 | SET_USI_TO_READ_DATA( )
|
210 | break;
|
211 |
|
212 | // Copy data from USIDR and send ACK
|
213 | // Next USI_SLAVE_REQUEST_DATA
|
214 | case USI_SLAVE_GET_DATA_AND_SEND_ACK:
|
215 | // Read data received
|
216 | data = USIDR;
|
217 |
|
218 | // First access, read buffer position
|
219 | if (buffer_adr == 0xFF) {
|
220 | // Check if address within buffer size
|
221 | if (data <= buffer_size) {
|
222 | // Set position as received
|
223 | buffer_adr = data;
|
224 | } else {
|
225 | // Set address to 0
|
226 | buffer_adr = 0;
|
227 | }
|
228 | } else {
|
229 | // Ongoing access, receive data
|
230 |
|
231 | // Write data to buffer
|
232 | i2c_buffer[buffer_adr] = data;
|
233 |
|
234 | // Increment buffer address for next write access
|
235 | buffer_adr++;
|
236 | }
|
237 | // Next USI_SLAVE_REQUEST_DATA
|
238 | overflow_state = USI_SLAVE_REQUEST_DATA;
|
239 | SET_USI_TO_SEND_ACK( )
|
240 | break;
|
241 | } // end switch
|
242 | } // end ISR( USI_OVERFLOW_VECTOR )
|