2 * Copyright (C) 2009 Ubixum, Inc.
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.
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.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 #include <stdio.h> // NOTE this needs deleted
21 #include <fx2macros.h>
29 #define i2c_printf(...) printf(__VA_ARGS__)
31 #define i2c_printf(...)
35 volatile __xdata BOOL cancel_i2c_trans;
36 #define CHECK_I2C_CANCEL() if (cancel_i2c_trans) return FALSE
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.
50 BOOL i2c_write ( BYTE addr, WORD len, BYTE *addr_buf, WORD len2, BYTE* data_buf ) {
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
58 // 1. Set START=1. If BERR=1, start timer*.
63 if ( I2CS & bmBERR ) {
64 i2c_printf ( "Woops.. need to do the timer\n" );
65 delay(10); // way too long probably
70 // 2. Write the 7-bit peripheral address and the direction bit (0 for a write) to I2DAT.
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);
77 i2c_printf ( "bmBERR, going to step 1\n" );
82 // 4. If ACK=0, go to step 9.
83 if ( !(I2CS & bmACK) ) {
85 while ( (I2CS & bmSTOP) && !cancel_i2c_trans);
89 i2c_printf ( "No ack after writing address.! Fail\n");
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];
101 // 6. Wait for DONE=1*. If BERR=1, go to step 1.
102 while (!(I2CS&bmDONE) && !cancel_i2c_trans); CHECK_I2C_CANCEL();
104 i2c_printf ( "bmBERR on byte %d. Going to step 1\n" , cur_byte-1 );
108 // 7. If ACK=0, go to step 9.
109 if ( !(I2CS & bmACK) ) {
111 while ( (I2CS&bmSTOP) && !cancel_i2c_trans);
112 i2c_printf ( "No Ack after byte %d. Fail\n", cur_byte-1 );
118 // 9. Set STOP=1. Wait for STOP = 0 before initiating another transfer.
121 while ( (I2CS & bmSTOP) && !cancel_i2c_trans);
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
144 12. Wait for DONE=1. If BERR=1, go to step 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
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
158 how many cycles at XTAL cycles/second = 9 cycles at 400k (or 100k) cycles/second
160 timeout = n i2c cycles / I2C cycles/sec = timeout seconds
161 timeout seconds * XTAL cycles/sec = XTAL cycles
162 9 / 400 (or 100) * (XTAL)
165 BOOL i2c_read( BYTE addr, WORD len, BYTE* buf) {
170 cancel_i2c_trans=FALSE;
171 //WORD timeout_cycles = (WORD)(9.0 * XTAL / I2CFREQ );
173 // 1. Set START=1. If BERR = 1, start timer*.
179 if ( I2CS & bmBERR ) {
180 i2c_printf ( "Woops, step1 BERR, need to do timeout\n");
181 delay(10); // NOTE way too long
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
188 // 3. Wait for DONE=1 or for timer to expire*. If BERR=1, go to step 1.
190 while ( !(I2CS & bmDONE) && !cancel_i2c_trans ); CHECK_I2C_CANCEL();
194 // 4. If ACK=0, set STOP=1 and go to step 15.
195 if (!(I2CS&bmACK) ) {
197 while ( (I2CS&bmSTOP) && !cancel_i2c_trans );
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;
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
209 while (len>cur_byte+1) { // reserve last byte read for after the loop
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;
216 // 10. Before reading the second-to-last I2DAT byte, set LASTRD=1.
217 if (len==cur_byte+2) // 2nd to last byte
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
223 buf[cur_byte++] = I2DAT;
225 // 8. Repeat steps 6 and 7 for each byte until ready to read the second-to-last byte.
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;
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
238 while ( (I2CS&bmSTOP) && !cancel_i2c_trans); CHECK_I2C_CANCEL();
245 BOOL eeprom_write(BYTE prom_addr, WORD addr, WORD length, BYTE* buf) {
247 // 1st bytes of buffer are address and next byte is value
252 if ( EEPROM_TWO_BYTE ) {
253 i2c_printf ( "Two Byte EEProm Address detected.\n" );
255 i2c_printf ( "Single Byte EEProm address detected.\n" );
259 while ( cur_byte<length ) {
261 if (EEPROM_TWO_BYTE) {
262 data_buffer[addr_len++] = MSB(addr);
264 data_buffer[addr_len++] = LSB(addr);
265 data_buffer[addr_len++] = buf[cur_byte++];
267 i2c_printf ( "%02x " , data_buffer[addr_len-1] );
269 if ( ! i2c_write ( prom_addr, addr_len, data_buffer, 0, NULL ) ) return FALSE;
270 ++addr; // next byte goes to next address
278 BOOL eeprom_read (BYTE prom_addr, WORD addr, WORD length, BYTE *buf)
284 eeprom_addr[addr_len++] = MSB(addr);
286 eeprom_addr[addr_len++] = LSB(addr);
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;