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