]>
Commit | Line | Data |
---|---|---|
3608c106 UH |
1 | /** |
2 | * Copyright (C) 2009 Ubixum, Inc. | |
3 | * | |
4 | * This library is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU Lesser General Public | |
6 | * License as published by the Free Software Foundation; either | |
7 | * version 2.1 of the License, or (at your option) any later version. | |
8 | * | |
9 | * This library is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
12 | * Lesser General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU Lesser General Public | |
15 | * License along with this library; if not, write to the Free Software | |
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
17 | **/ | |
18 | ||
19 | #include <stdio.h> // NOTE this needs deleted | |
20 | ||
21 | #include <fx2regs.h> | |
22 | #include <fx2macros.h> | |
23 | #include <i2c.h> | |
24 | #include <delay.h> | |
25 | ||
26 | ||
27 | //#define DEBUG_I2C 1 | |
28 | ||
29 | #ifdef DEBUG_I2C | |
30 | #define i2c_printf(...) printf(__VA_ARGS__) | |
31 | #else | |
32 | #define i2c_printf(...) | |
33 | #endif | |
34 | ||
35 | ||
36 | volatile __xdata BOOL cancel_i2c_trans; | |
37 | #define CHECK_I2C_CANCEL() if (cancel_i2c_trans) return FALSE | |
38 | ||
39 | /** | |
40 | * | |
41 | 1. Set START=1. If BERR=1, start timer*. | |
42 | 2. Write the 7-bit peripheral address and the direction bit (0 for a write) to I2DAT. | |
43 | 3. Wait for DONE=1 or for timer to expire*. If BERR=1, go to step 1. | |
44 | 4. If ACK=0, go to step 9. | |
45 | 5. Load I2DAT with a data byte. | |
46 | 6. Wait for DONE=1*. If BERR=1, go to step 1. | |
47 | 7. If ACK=0, go to step 9. | |
48 | 8. Repeat steps 5-7 for each byte until all bytes have been transferred. | |
49 | 9. Set STOP=1. Wait for STOP = 0 before initiating another transfer. | |
50 | **/ | |
51 | BOOL i2c_write ( BYTE addr, WORD len, BYTE *addr_buf, WORD len2, BYTE* data_buf ) { | |
52 | ||
53 | WORD cur_byte; | |
54 | WORD total_bytes = len+len2; // NOTE overflow error? | |
55 | BYTE retry_count=2; // two tries to write address/read ack | |
56 | cancel_i2c_trans=FALSE; | |
57 | //BOOL wait=FALSE; // use timer if needed | |
58 | ||
59 | // 1. Set START=1. If BERR=1, start timer*. | |
60 | step1: | |
61 | CHECK_I2C_CANCEL(); | |
62 | cur_byte=0; | |
63 | I2CS |= bmSTART; | |
64 | if ( I2CS & bmBERR ) { | |
65 | i2c_printf ( "Woops.. need to do the timer\n" ); | |
66 | delay(10); // way too long probably | |
67 | goto step1; | |
68 | } | |
69 | ||
70 | ||
71 | // 2. Write the 7-bit peripheral address and the direction bit (0 for a write) to I2DAT. | |
72 | I2DAT = addr << 1; | |
73 | ||
74 | // 3. Wait for DONE=1 or for timer to expire*. If BERR=1, go to step 1. | |
75 | while ( !(I2CS & bmDONE) && !cancel_i2c_trans); | |
76 | CHECK_I2C_CANCEL(); | |
77 | if (I2CS&bmBERR) { | |
78 | i2c_printf ( "bmBERR, going to step 1\n" ); | |
79 | goto step1; | |
80 | } | |
81 | ||
82 | ||
83 | // 4. If ACK=0, go to step 9. | |
84 | if ( !(I2CS & bmACK) ) { | |
85 | I2CS |= bmSTOP; | |
86 | while ( (I2CS & bmSTOP) && !cancel_i2c_trans); | |
87 | CHECK_I2C_CANCEL(); | |
88 | --retry_count; | |
89 | if (!retry_count){ | |
90 | i2c_printf ( "No ack after writing address.! Fail\n"); | |
91 | return FALSE; | |
92 | } | |
93 | delay(10); | |
94 | goto step1; | |
95 | } | |
96 | ||
97 | // 8. Repeat steps 5-7 for each byte until all bytes have been transferred. | |
98 | while ( cur_byte < total_bytes ) { | |
99 | // 5. Load I2DAT with a data byte. | |
100 | I2DAT = cur_byte < len ? addr_buf[cur_byte] : data_buf[cur_byte-len]; | |
101 | ++cur_byte; | |
102 | // 6. Wait for DONE=1*. If BERR=1, go to step 1. | |
103 | while (!(I2CS&bmDONE) && !cancel_i2c_trans); CHECK_I2C_CANCEL(); | |
104 | if ( I2CS&bmBERR ) { | |
105 | i2c_printf ( "bmBERR on byte %d. Going to step 1\n" , cur_byte-1 ); | |
106 | goto step1; | |
107 | //return FALSE; | |
108 | } | |
109 | // 7. If ACK=0, go to step 9. | |
110 | if ( !(I2CS & bmACK) ) { | |
111 | I2CS |= bmSTOP; | |
112 | while ( (I2CS&bmSTOP) && !cancel_i2c_trans); | |
113 | i2c_printf ( "No Ack after byte %d. Fail\n", cur_byte-1 ); | |
114 | return FALSE; | |
115 | } | |
116 | } | |
117 | ||
118 | ||
119 | // 9. Set STOP=1. Wait for STOP = 0 before initiating another transfer. | |
120 | //real step 9 | |
121 | I2CS |= bmSTOP; | |
122 | while ( (I2CS & bmSTOP) && !cancel_i2c_trans); | |
123 | CHECK_I2C_CANCEL(); | |
124 | ||
125 | return TRUE; | |
126 | ||
127 | } | |
128 | ||
129 | /* | |
130 | trm 13.4.4 | |
131 | ||
132 | 1. Set START=1. If BERR = 1, start timer*. | |
133 | 2. Write the 7-bit peripheral address and the direction bit (1 for a read) to I2DAT. | |
134 | 3. Wait for DONE=1 or for timer to expire*. If BERR=1, go to step 1. | |
135 | 4. If ACK=0, set STOP=1 and go to step 15. | |
136 | 5. Read I2DAT to initiate the first burst of nine SCL pulses to clock in the first byte from the slave. | |
137 | Discard the value that was read from I2DAT. | |
138 | 6. Wait for DONE=1. If BERR=1, go to step 1. | |
139 | 7. Read the just-received byte of data from I2DAT. This read also initiates the next read transfer. | |
140 | 8. Repeat steps 6 and 7 for each byte until ready to read the second-to-last byte. | |
141 | 9. Wait for DONE=1. If BERR=1, go to step 1. | |
142 | 10. Before reading the second-to-last I2DAT byte, set LASTRD=1. | |
143 | 11. Read the second-to-last byte from I2DAT. With LASTRD=1, this initiates the final byte read on | |
144 | the bus. | |
145 | 12. Wait for DONE=1. If BERR=1, go to step 1. | |
146 | 13. Set STOP=1. | |
147 | 14. Read the final byte from I2DAT immediately (the next instruction) after setting the STOP bit. By | |
148 | reading I2DAT while the "stop" condition is being generated, the just-received data byte will be | |
149 | retrieved without initiating an extra read transaction (nine more SCL pulses) on the I²Cbus. | |
150 | 15. Wait for STOP = 0 before initiating another transfer | |
151 | */ | |
152 | ||
153 | /* | |
154 | * timer should be at least as long as longest start-stop interval on the bus | |
155 | serial clock for i2c bus runs at 100khz by default and can run at 400khz for devices that support it | |
156 | start-stop interval is about 9 serial clock cycles | |
157 | 400KHZ bit 0=100khz, 1=400khz | |
158 | ||
159 | how many cycles at XTAL cycles/second = 9 cycles at 400k (or 100k) cycles/second | |
160 | ||
161 | timeout = n i2c cycles / I2C cycles/sec = timeout seconds | |
162 | timeout seconds * XTAL cycles/sec = XTAL cycles | |
163 | 9 / 400 (or 100) * (XTAL) | |
164 | ||
165 | */ | |
166 | BOOL i2c_read( BYTE addr, WORD len, BYTE* buf) { | |
167 | ||
168 | ||
169 | BYTE tmp; | |
170 | WORD cur_byte; | |
171 | cancel_i2c_trans=FALSE; | |
172 | //WORD timeout_cycles = (WORD)(9.0 * XTAL / I2CFREQ ); | |
173 | ||
174 | // 1. Set START=1. If BERR = 1, start timer*. | |
175 | start: | |
176 | CHECK_I2C_CANCEL(); | |
177 | cur_byte=0; | |
178 | ||
179 | I2CS |= bmSTART; | |
180 | if ( I2CS & bmBERR ) { | |
181 | i2c_printf ( "Woops, step1 BERR, need to do timeout\n"); | |
182 | delay(10); // NOTE way too long | |
183 | goto start; | |
184 | } | |
185 | ||
186 | // 2. Write the 7-bit peripheral address and the direction bit (1 for a read) to I2DAT. | |
187 | I2DAT = (addr << 1) | 1; // last 1 for read | |
188 | ||
189 | // 3. Wait for DONE=1 or for timer to expire*. If BERR=1, go to step 1. | |
190 | ||
191 | while ( !(I2CS & bmDONE) && !cancel_i2c_trans ); CHECK_I2C_CANCEL(); | |
192 | if ( I2CS & bmBERR ) | |
193 | goto start; | |
194 | ||
195 | // 4. If ACK=0, set STOP=1 and go to step 15. | |
196 | if (!(I2CS&bmACK) ) { | |
197 | I2CS |= bmSTOP; | |
198 | while ( (I2CS&bmSTOP) && !cancel_i2c_trans ); | |
199 | return FALSE; | |
200 | } | |
201 | ||
202 | // with only one byte to read, this needs set here. | |
203 | // (In this case, the tmp read is the 2nd to last read) | |
204 | if ( len==1 ) I2CS |= bmLASTRD; | |
205 | ||
206 | // 5. Read I2DAT to initiate the first burst of nine SCL pulses to clock in the first byte from the slave. | |
207 | // Discard the value that was read from I2DAT. | |
208 | tmp = I2DAT; // discard read | |
209 | ||
210 | while (len>cur_byte+1) { // reserve last byte read for after the loop | |
211 | ||
212 | // 6. Wait for DONE=1. If BERR=1, go to step 1. | |
213 | // 9. Wait for DONE=1. If BERR=1, go to step 1. | |
214 | while (!(I2CS&bmDONE) && !cancel_i2c_trans); CHECK_I2C_CANCEL(); | |
215 | if ( I2CS&bmBERR ) goto start; | |
216 | ||
217 | // 10. Before reading the second-to-last I2DAT byte, set LASTRD=1. | |
218 | if (len==cur_byte+2) // 2nd to last byte | |
219 | I2CS |= bmLASTRD; | |
220 | ||
221 | // 7. Read the just-received byte of data from I2DAT. This read also initiates the next read transfer. | |
222 | // 11. Read the second-to-last byte from I2DAT. With LASTRD=1, this initiates the final byte read on | |
223 | // the bus. | |
224 | buf[cur_byte++] = I2DAT; | |
225 | ||
226 | // 8. Repeat steps 6 and 7 for each byte until ready to read the second-to-last byte. | |
227 | } | |
228 | ||
229 | //12. Wait for DONE=1. If BERR=1, go to step 1. | |
230 | while (!(I2CS&bmDONE) && !cancel_i2c_trans); CHECK_I2C_CANCEL(); | |
231 | if ( I2CS&bmBERR ) goto start; | |
232 | // 13. Set STOP=1. | |
233 | I2CS |= bmSTOP; | |
234 | // 14. Read the final byte from I2DAT immediately (the next instruction) after setting the STOP bit. By | |
235 | // reading I2DAT while the "stop" condition is being generated, the just-received data byte will be | |
236 | // retrieved without initiating an extra read transaction (nine more SCL pulses) on the I²Cbus. | |
237 | buf[cur_byte] = I2DAT; // use instead of buffer addressing so next instruction reads I2DAT | |
238 | ||
239 | while ( (I2CS&bmSTOP) && !cancel_i2c_trans); CHECK_I2C_CANCEL(); | |
240 | ||
241 | return TRUE; | |
242 | } | |
243 | ||
244 | ||
245 | ||
246 | BOOL eeprom_write(BYTE prom_addr, WORD addr, WORD length, BYTE* buf) { | |
247 | BYTE addr_len=0; | |
248 | // 1st bytes of buffer are address and next byte is value | |
249 | BYTE data_buffer[3]; | |
250 | WORD cur_byte=0; | |
251 | ||
252 | #ifdef DEBUG_I2C | |
253 | if ( EEPROM_TWO_BYTE ) { | |
254 | i2c_printf ( "Two Byte EEProm Address detected.\n" ); | |
255 | } else { | |
256 | i2c_printf ( "Single Byte EEProm address detected.\n" ); | |
257 | } | |
258 | #endif | |
259 | ||
260 | while ( cur_byte<length ) { | |
261 | addr_len=0; | |
262 | if (EEPROM_TWO_BYTE) { | |
263 | data_buffer[addr_len++] = MSB(addr); | |
264 | } | |
265 | data_buffer[addr_len++] = LSB(addr); | |
266 | data_buffer[addr_len++] = buf[cur_byte++]; | |
267 | ||
268 | i2c_printf ( "%02x " , data_buffer[addr_len-1] ); | |
269 | ||
270 | if ( ! i2c_write ( prom_addr, addr_len, data_buffer, 0, NULL ) ) return FALSE; | |
271 | ++addr; // next byte goes to next address | |
272 | } | |
273 | ||
274 | return TRUE; | |
275 | ||
276 | } | |
277 | ||
278 | ||
279 | BOOL eeprom_read (BYTE prom_addr, WORD addr, WORD length, BYTE *buf) | |
280 | { | |
281 | ||
282 | BYTE eeprom_addr[2]; | |
283 | BYTE addr_len=0; | |
284 | if (EEPROM_TWO_BYTE) | |
285 | eeprom_addr[addr_len++] = MSB(addr); | |
286 | ||
287 | eeprom_addr[addr_len++] = LSB(addr); | |
288 | ||
289 | // write the address we want to read to the prom | |
290 | //printf ("Starting Addr Write with addr len %d\n", addr_len); | |
291 | if ( !i2c_write( prom_addr, addr_len, eeprom_addr, 0, NULL ) ) return FALSE; | |
292 | //printf ( "Starting read\n" ); | |
293 | if ( !i2c_read ( prom_addr, length, buf ) ) return FALSE; | |
294 | ||
295 | return TRUE; | |
296 | ||
297 | } | |
298 |