|
@@ -0,0 +1,255 @@
|
|
1
|
+/*****************************************************************************
|
|
2
|
+ *
|
|
3
|
+ * Atmel Corporation
|
|
4
|
+ *
|
|
5
|
+ * File : USI_TWI_Master.c
|
|
6
|
+ * Date : $Date: 2016-7-15 $
|
|
7
|
+ * Updated by : $Author: Atmel $
|
|
8
|
+ *
|
|
9
|
+ * Support mail : avr@atmel.com
|
|
10
|
+ *
|
|
11
|
+ * Supported devices : All device with USI module can be used.
|
|
12
|
+ * The example is written for the ATmega169, ATtiny26 and ATtiny2313
|
|
13
|
+ *
|
|
14
|
+ * AppNote : AVR310 - Using the USI module as a TWI Master
|
|
15
|
+ *
|
|
16
|
+ * Description : This is an implementation of an TWI master using
|
|
17
|
+ * the USI module as basis. The implementation assumes the AVR to
|
|
18
|
+ * be the only TWI master in the system and can therefore not be
|
|
19
|
+ * used in a multi-master system.
|
|
20
|
+ * Usage : Initialize the USI module by calling the USI_TWI_Master_Initialise()
|
|
21
|
+ * function. Hence messages/data are transceived on the bus using
|
|
22
|
+ * the USI_TWI_Transceive() function. The transceive function
|
|
23
|
+ * returns a status byte, which can be used to evaluate the
|
|
24
|
+ * success of the transmission.
|
|
25
|
+ *
|
|
26
|
+ ****************************************************************************/
|
|
27
|
+#if __GNUC__
|
|
28
|
+#include <avr/io.h>
|
|
29
|
+#else
|
|
30
|
+#include <inavr.h>
|
|
31
|
+#include <ioavr.h>
|
|
32
|
+#endif
|
|
33
|
+#include "USI_TWI_Master.h"
|
|
34
|
+
|
|
35
|
+unsigned char USI_TWI_Master_Transfer(unsigned char);
|
|
36
|
+unsigned char USI_TWI_Master_Stop(void);
|
|
37
|
+
|
|
38
|
+union USI_TWI_state {
|
|
39
|
+ unsigned char errorState; // Can reuse the TWI_state for error states due to that it will not be need if there
|
|
40
|
+ // exists an error.
|
|
41
|
+ struct {
|
|
42
|
+ unsigned char addressMode : 1;
|
|
43
|
+ unsigned char masterWriteDataMode : 1;
|
|
44
|
+ unsigned char unused : 6;
|
|
45
|
+ };
|
|
46
|
+} USI_TWI_state;
|
|
47
|
+
|
|
48
|
+/*---------------------------------------------------------------
|
|
49
|
+ USI TWI single master initialization function
|
|
50
|
+---------------------------------------------------------------*/
|
|
51
|
+void USI_TWI_Master_Initialise(void)
|
|
52
|
+{
|
|
53
|
+ PORT_USI |= (1 << PIN_USI_SDA); // Enable pullup on SDA, to set high as released state.
|
|
54
|
+ PORT_USI |= (1 << PIN_USI_SCL); // Enable pullup on SCL, to set high as released state.
|
|
55
|
+
|
|
56
|
+ DDR_USI |= (1 << PIN_USI_SCL); // Enable SCL as output.
|
|
57
|
+ DDR_USI |= (1 << PIN_USI_SDA); // Enable SDA as output.
|
|
58
|
+
|
|
59
|
+ USIDR = 0xFF; // Preload dataregister with "released level" data.
|
|
60
|
+ USICR = (0 << USISIE) | (0 << USIOIE) | // Disable Interrupts.
|
|
61
|
+ (1 << USIWM1) | (0 << USIWM0) | // Set USI in Two-wire mode.
|
|
62
|
+ (1 << USICS1) | (0 << USICS0) | (1 << USICLK) | // Software stobe as counter clock source
|
|
63
|
+ (0 << USITC);
|
|
64
|
+ USISR = (1 << USISIF) | (1 << USIOIF) | (1 << USIPF) | (1 << USIDC) | // Clear flags,
|
|
65
|
+ (0x0 << USICNT0); // and reset counter.
|
|
66
|
+}
|
|
67
|
+
|
|
68
|
+/*---------------------------------------------------------------
|
|
69
|
+Use this function to get hold of the error message from the last transmission
|
|
70
|
+---------------------------------------------------------------*/
|
|
71
|
+unsigned char USI_TWI_Get_State_Info(void)
|
|
72
|
+{
|
|
73
|
+ return (USI_TWI_state.errorState); // Return error state.
|
|
74
|
+}
|
|
75
|
+
|
|
76
|
+/*---------------------------------------------------------------
|
|
77
|
+ USI Transmit and receive function. LSB of first byte in data
|
|
78
|
+ indicates if a read or write cycles is performed. If set a read
|
|
79
|
+ operation is performed.
|
|
80
|
+
|
|
81
|
+ Function generates (Repeated) Start Condition, sends address and
|
|
82
|
+ R/W, Reads/Writes Data, and verifies/sends ACK.
|
|
83
|
+
|
|
84
|
+ Success or error code is returned. Error codes are defined in
|
|
85
|
+ USI_TWI_Master.h
|
|
86
|
+---------------------------------------------------------------*/
|
|
87
|
+#ifndef __GNUC__
|
|
88
|
+__x // AVR compiler
|
|
89
|
+#endif
|
|
90
|
+ unsigned char
|
|
91
|
+ USI_TWI_Start_Transceiver_With_Data(unsigned char *msg, unsigned char msgSize)
|
|
92
|
+{
|
|
93
|
+ unsigned char tempUSISR_8bit = (1 << USISIF) | (1 << USIOIF) | (1 << USIPF) | (1 << USIDC)
|
|
94
|
+ | // Prepare register value to: Clear flags, and
|
|
95
|
+ (0x0 << USICNT0); // set USI to shift 8 bits i.e. count 16 clock edges.
|
|
96
|
+ unsigned char tempUSISR_1bit = (1 << USISIF) | (1 << USIOIF) | (1 << USIPF) | (1 << USIDC)
|
|
97
|
+ | // Prepare register value to: Clear flags, and
|
|
98
|
+ (0xE << USICNT0); // set USI to shift 1 bit i.e. count 2 clock edges.
|
|
99
|
+
|
|
100
|
+ USI_TWI_state.errorState = 0;
|
|
101
|
+ USI_TWI_state.addressMode = TRUE;
|
|
102
|
+
|
|
103
|
+#ifdef PARAM_VERIFICATION
|
|
104
|
+ if (msg > (unsigned char *)RAMEND) // Test if address is outside SRAM space
|
|
105
|
+ {
|
|
106
|
+ USI_TWI_state.errorState = USI_TWI_DATA_OUT_OF_BOUND;
|
|
107
|
+ return (FALSE);
|
|
108
|
+ }
|
|
109
|
+ if (msgSize <= 1) // Test if the transmission buffer is empty
|
|
110
|
+ {
|
|
111
|
+ USI_TWI_state.errorState = USI_TWI_NO_DATA;
|
|
112
|
+ return (FALSE);
|
|
113
|
+ }
|
|
114
|
+#endif
|
|
115
|
+
|
|
116
|
+#ifdef NOISE_TESTING // Test if any unexpected conditions have arrived prior to this execution.
|
|
117
|
+ if (USISR & (1 << USISIF)) {
|
|
118
|
+ USI_TWI_state.errorState = USI_TWI_UE_START_CON;
|
|
119
|
+ return (FALSE);
|
|
120
|
+ }
|
|
121
|
+ if (USISR & (1 << USIPF)) {
|
|
122
|
+ USI_TWI_state.errorState = USI_TWI_UE_STOP_CON;
|
|
123
|
+ return (FALSE);
|
|
124
|
+ }
|
|
125
|
+ if (USISR & (1 << USIDC)) {
|
|
126
|
+ USI_TWI_state.errorState = USI_TWI_UE_DATA_COL;
|
|
127
|
+ return (FALSE);
|
|
128
|
+ }
|
|
129
|
+#endif
|
|
130
|
+
|
|
131
|
+ if (!(*msg
|
|
132
|
+ & (1 << TWI_READ_BIT))) // The LSB in the address byte determines if is a masterRead or masterWrite operation.
|
|
133
|
+ {
|
|
134
|
+ USI_TWI_state.masterWriteDataMode = TRUE;
|
|
135
|
+ }
|
|
136
|
+
|
|
137
|
+ /* Release SCL to ensure that (repeated) Start can be performed */
|
|
138
|
+ PORT_USI |= (1 << PIN_USI_SCL); // Release SCL.
|
|
139
|
+ while (!(PIN_USI & (1 << PIN_USI_SCL)))
|
|
140
|
+ ; // Verify that SCL becomes high.
|
|
141
|
+#ifdef TWI_FAST_MODE
|
|
142
|
+ DELAY_T4TWI; // Delay for T4TWI if TWI_FAST_MODE
|
|
143
|
+#else
|
|
144
|
+ DELAY_T2TWI; // Delay for T2TWI if TWI_STANDARD_MODE
|
|
145
|
+#endif
|
|
146
|
+
|
|
147
|
+ /* Generate Start Condition */
|
|
148
|
+ PORT_USI &= ~(1 << PIN_USI_SDA); // Force SDA LOW.
|
|
149
|
+ DELAY_T4TWI;
|
|
150
|
+ PORT_USI &= ~(1 << PIN_USI_SCL); // Pull SCL LOW.
|
|
151
|
+ PORT_USI |= (1 << PIN_USI_SDA); // Release SDA.
|
|
152
|
+
|
|
153
|
+#ifdef SIGNAL_VERIFY
|
|
154
|
+ if (!(USISR & (1 << USISIF))) {
|
|
155
|
+ USI_TWI_state.errorState = USI_TWI_MISSING_START_CON;
|
|
156
|
+ return (FALSE);
|
|
157
|
+ }
|
|
158
|
+#endif
|
|
159
|
+
|
|
160
|
+ /*Write address and Read/Write data */
|
|
161
|
+ do {
|
|
162
|
+ /* If masterWrite cycle (or inital address tranmission)*/
|
|
163
|
+ if (USI_TWI_state.addressMode || USI_TWI_state.masterWriteDataMode) {
|
|
164
|
+ /* Write a byte */
|
|
165
|
+ PORT_USI &= ~(1 << PIN_USI_SCL); // Pull SCL LOW.
|
|
166
|
+ USIDR = *(msg++); // Setup data.
|
|
167
|
+ USI_TWI_Master_Transfer(tempUSISR_8bit); // Send 8 bits on bus.
|
|
168
|
+
|
|
169
|
+ /* Clock and verify (N)ACK from slave */
|
|
170
|
+ DDR_USI &= ~(1 << PIN_USI_SDA); // Enable SDA as input.
|
|
171
|
+ if (USI_TWI_Master_Transfer(tempUSISR_1bit) & (1 << TWI_NACK_BIT)) {
|
|
172
|
+ if (USI_TWI_state.addressMode)
|
|
173
|
+ USI_TWI_state.errorState = USI_TWI_NO_ACK_ON_ADDRESS;
|
|
174
|
+ else
|
|
175
|
+ USI_TWI_state.errorState = USI_TWI_NO_ACK_ON_DATA;
|
|
176
|
+ return (FALSE);
|
|
177
|
+ }
|
|
178
|
+ USI_TWI_state.addressMode = FALSE; // Only perform address transmission once.
|
|
179
|
+ }
|
|
180
|
+ /* Else masterRead cycle*/
|
|
181
|
+ else {
|
|
182
|
+ /* Read a data byte */
|
|
183
|
+ DDR_USI &= ~(1 << PIN_USI_SDA); // Enable SDA as input.
|
|
184
|
+ *(msg++) = USI_TWI_Master_Transfer(tempUSISR_8bit);
|
|
185
|
+
|
|
186
|
+ /* Prepare to generate ACK (or NACK in case of End Of Transmission) */
|
|
187
|
+ if (msgSize == 1) // If transmission of last byte was performed.
|
|
188
|
+ {
|
|
189
|
+ USIDR = 0xFF; // Load NACK to confirm End Of Transmission.
|
|
190
|
+ } else {
|
|
191
|
+ USIDR = 0x00; // Load ACK. Set data register bit 7 (output for SDA) low.
|
|
192
|
+ }
|
|
193
|
+ USI_TWI_Master_Transfer(tempUSISR_1bit); // Generate ACK/NACK.
|
|
194
|
+ }
|
|
195
|
+ } while (--msgSize); // Until all data sent/received.
|
|
196
|
+
|
|
197
|
+ USI_TWI_Master_Stop(); // Send a STOP condition on the TWI bus.
|
|
198
|
+
|
|
199
|
+ /* Transmission successfully completed*/
|
|
200
|
+ return (TRUE);
|
|
201
|
+}
|
|
202
|
+
|
|
203
|
+/*---------------------------------------------------------------
|
|
204
|
+ Core function for shifting data in and out from the USI.
|
|
205
|
+ Data to be sent has to be placed into the USIDR prior to calling
|
|
206
|
+ this function. Data read, will be return'ed from the function.
|
|
207
|
+---------------------------------------------------------------*/
|
|
208
|
+unsigned char USI_TWI_Master_Transfer(unsigned char temp)
|
|
209
|
+{
|
|
210
|
+ USISR = temp; // Set USISR according to temp.
|
|
211
|
+ // Prepare clocking.
|
|
212
|
+ temp = (0 << USISIE) | (0 << USIOIE) | // Interrupts disabled
|
|
213
|
+ (1 << USIWM1) | (0 << USIWM0) | // Set USI in Two-wire mode.
|
|
214
|
+ (1 << USICS1) | (0 << USICS0) | (1 << USICLK) | // Software clock strobe as source.
|
|
215
|
+ (1 << USITC); // Toggle Clock Port.
|
|
216
|
+ do {
|
|
217
|
+ DELAY_T2TWI;
|
|
218
|
+ USICR = temp; // Generate positve SCL edge.
|
|
219
|
+ while (!(PIN_USI & (1 << PIN_USI_SCL)))
|
|
220
|
+ ; // Wait for SCL to go high.
|
|
221
|
+ DELAY_T4TWI;
|
|
222
|
+ USICR = temp; // Generate negative SCL edge.
|
|
223
|
+ } while (!(USISR & (1 << USIOIF))); // Check for transfer complete.
|
|
224
|
+
|
|
225
|
+ DELAY_T2TWI;
|
|
226
|
+ temp = USIDR; // Read out data.
|
|
227
|
+ USIDR = 0xFF; // Release SDA.
|
|
228
|
+ DDR_USI |= (1 << PIN_USI_SDA); // Enable SDA as output.
|
|
229
|
+
|
|
230
|
+ return temp; // Return the data from the USIDR
|
|
231
|
+}
|
|
232
|
+
|
|
233
|
+/*---------------------------------------------------------------
|
|
234
|
+ Function for generating a TWI Stop Condition. Used to release
|
|
235
|
+ the TWI bus.
|
|
236
|
+---------------------------------------------------------------*/
|
|
237
|
+unsigned char USI_TWI_Master_Stop(void)
|
|
238
|
+{
|
|
239
|
+ PORT_USI &= ~(1 << PIN_USI_SDA); // Pull SDA low.
|
|
240
|
+ PORT_USI |= (1 << PIN_USI_SCL); // Release SCL.
|
|
241
|
+ while (!(PIN_USI & (1 << PIN_USI_SCL)))
|
|
242
|
+ ; // Wait for SCL to go high.
|
|
243
|
+ DELAY_T4TWI;
|
|
244
|
+ PORT_USI |= (1 << PIN_USI_SDA); // Release SDA.
|
|
245
|
+ DELAY_T2TWI;
|
|
246
|
+
|
|
247
|
+#ifdef SIGNAL_VERIFY
|
|
248
|
+ if (!(USISR & (1 << USIPF))) {
|
|
249
|
+ USI_TWI_state.errorState = USI_TWI_MISSING_STOP_CON;
|
|
250
|
+ return (FALSE);
|
|
251
|
+ }
|
|
252
|
+#endif
|
|
253
|
+
|
|
254
|
+ return (TRUE);
|
|
255
|
+}
|