From 1b6d3a834a4580dee62d1fc77de4ccde054173b5 Mon Sep 17 00:00:00 2001 From: Roaming Date: Sun, 12 Jun 2016 19:19:54 +0530 Subject: [PATCH] This commit allows the user to communicate with I2C slaves devices. It consists of a soft i2c core along with a shift register and an examples directory to show its usage. --- examples/download.sh | 15 + examples/i2c_bitbang/Makefile | 11 + examples/i2c_bitbang/i2c_main.c | 176 +++++++++++ include/i2c/i2c_utils.h | 93 ++++++ lib/Makefile | 2 +- lib/i2c/i2c_utils.c | 521 ++++++++++++++++++++++++++++++++ 6 files changed, 817 insertions(+), 1 deletion(-) create mode 100755 examples/download.sh create mode 100644 examples/i2c_bitbang/Makefile create mode 100644 examples/i2c_bitbang/i2c_main.c create mode 100644 include/i2c/i2c_utils.h create mode 100644 lib/i2c/i2c_utils.c diff --git a/examples/download.sh b/examples/download.sh new file mode 100755 index 0000000..9f0d06b --- /dev/null +++ b/examples/download.sh @@ -0,0 +1,15 @@ +#!/bin/bash -e + +DEVS=$(lsusb|grep -E '(2a19|16c0|04b4|1d50|fb9a|1443)' |sed 's/:.*//;s/Bus //;s/Device //;s/ /\//') + +if [ -z "$1" ]; then + echo "$0: usage: $0 " + exit 1; +fi + +for dev in $DEVS;do + echo "Downloading $1 to $dev" + /sbin/fxload -D /dev/bus/usb/$dev -t fx2lp -I $1 +done + +exit 0 diff --git a/examples/i2c_bitbang/Makefile b/examples/i2c_bitbang/Makefile new file mode 100644 index 0000000..ba0ec29 --- /dev/null +++ b/examples/i2c_bitbang/Makefile @@ -0,0 +1,11 @@ +FX2LIBDIR=../../ +BASENAME = i2c_main +SOURCES=i2c_main.c +CODE_SIZE = --code-size 0x3000 +XRAM_LOC = --xram-loc 0x3200 +XRAM_SIZE = --xram-size 0x700 +DSCR_AREA= +INT2JT= +include $(FX2LIBDIR)/lib/fx2.mk +fx2_download: + ../download.sh build/$(BASENAME).ihx diff --git a/examples/i2c_bitbang/i2c_main.c b/examples/i2c_bitbang/i2c_main.c new file mode 100644 index 0000000..075738a --- /dev/null +++ b/examples/i2c_bitbang/i2c_main.c @@ -0,0 +1,176 @@ +/** + * Copyright (C) 2009 Ubixum, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + **/ +#include +#include +#include +#include +#include +#include + +//DELETE once merged. +void fast_uart(BYTE a); + + +/************************************************** +I2C declarations +***************************************************/ +//Buffer to load up the data and insert into +//the i2c_client queue +__xdata unsigned char write_addr[I2C_ADDR]; +//This is the data buffer. +__xdata unsigned char write_data[I2C_DATA]; +//This is the address length +__xdata unsigned char rx_addr_length; +//This is the data length which needs to be read from or written to. +__xdata unsigned char rx_data_length; +//Implementation specific. Not needed for all examples. Used here +//to read and write from different address locations. +__xdata unsigned char wr_addr; + + +void main() { + SETCPUFREQ(CLK_48M); + EA = 1; // global interrupt enable + /******************************** + I2C BLOCK BITBANG + *********************************/ + //Called with number of retries + i2c_init(3); + configure_start_timer(); + ENABLE_TIMER1(); + wr_addr = 0x03; + while (TRUE) + { + + write_addr[0] = 0xa0; + write_addr[1] = 0x00; + write_addr[2] = wr_addr; + write_data[0] = 0x44; + //Address, data, address length and data length + I2CPutTX(&write_addr[0],&write_data[0],0x03,0x01); + //i2c_control(); + write_addr[0] = 0xa1; + write_data[0] = 0x34; + I2CPutRXRead(&write_addr[0],0x01,0x01); + I2CGetRXData(&write_addr[0],&write_data[0]); + fast_uart(data[0]); + i2c_control(); + if(wr_addr == 0x00) + { + wr_addr = 0x03; + } + else + { + wr_addr--; + } + } +} + +void timer1_isr () +__interrupt TF1_ISR +{ + __asm + mov a,_tx_rx //(2 cycles. Move the state into the accumulator.) + CJNE A, #0x02, state //(4 cycles. If in halt state, do nothing.) + ajmp finish //(3 cycles. Return from ISR if buffer is not ready to shift the data out.) + state: + djnz _bit_count,cont; //(4 cycles. This is sued to keep track of the number of data bytes which need to be shifted out.) + mov _tx_rx,#0x02 //(3 cycles. Set our current status to busy so nothing else can interrupt us till we shift data in or out.) + ajmp finish //(3 cycles. If data has been shifted, but has not been read, then jump to finish.) + cont: + orl _OEA,#0x40 //(3 cycles. First always set out clock pin direction. This is needed as discussed already in comments.) + clr _PA6 //(2 cycles. Set the SCL line low.) + mov a,_tx_rx //(2 cycles. Move the state back into the accumulator.) + CJNE A, #0x00, rx //(4 cycles. Check if we are in RX or TX mode.) + tx: + orl _OEA,#0x80 //(3 cycles. We are in TX mode. Set the SDA direction.) + mov a, _tx_i2c_buffer //(2 cycles. Move the data which needs to be transmitted into the accumulator.) + rlc a //(1 cycles. Rotate and move the bit which needs to be sent out into the carry.) + mov _PA7, c //(2 cycles. Move the data into the SDA pin.) + mov _tx_i2c_buffer,a //(2 cycles. Now move the data back so that the next rotate can be performed when the ISR executes.) + sjmp sclh //(3 cycles. Jump to toggle SCL now.) + rx: + anl _OEA,#0x7f //(3 cycles. Set the mode of SDA pin again.) + mov a, _tx_i2c_buffer //(2 cycles. Move data received into accumulator.) + mov c,_PA7 //(2 cycles. Move the SDA line value into the carry. Delay has already been introduced between setting clock low and reading the first bit of data. Otherwise, follow the MPSSE i2c routine.) + rlc a //(1 cycle. Since the next bit will be received in the same ISR we need to shift the data.) + mov _tx_i2c_buffer,a //(2 cycles. Move the data back into accumulator for next ISR execution routine) + nop //(1 cycle. Wait for 1 cycle before setting the bit high again.) + sclh: + setb _PA6 //(2 cycles. Set the SCL high) + finish: + nop + __endasm; +} + +/************************** +Will be DELETED once the pull requests are merged +**************************/ +void fast_uart(BYTE a) { + OEA |= 0x04; + //An efficient UART bitbang routine in assembly + __asm + //Like #define in C. Can easily be used to change the pin + .equ TX_PIN, _PA2 + //Disable interrupts + clr _EA + //Move the data to be sent into the ACC + mov a , dpl + //Clear carry + clr c + //We need to send out 8 bits of data + //Load r0 with value 8 + mov r0, #0x08; + //Create the start bit + clr TX_PIN; + //Precalculated delay since 1 cycle takes 88.33ns + //Mov takes 2 cycles + mov r1, #0x22; + 0006$: + //DJNZ on Rn takes 3 cycle. This waits for + //23*3, 69 cycles + //71 cycle delay + djnz r1, 0006$; + //Add 2 more cycles of delay + //97 cycles + nop; + nop; + 0001$: + //2 cycles + rrc a; + //Move the carry into the port + mov _PA2, c + //Now we need to add delay for the next + //2 cycles of delay + mov r1, #0x1F; + //31*3 , 93 cycles of delay + 0004$: + djnz r1, 0004$; + //1 more cycle, now upto 94 + nop; + //3 more cycles of delay + //97 cycles + djnz r0, 0001$; + setb _PA2; + mov r1, #0x80; + 0005$: + djnz r1, 0005$; + nop + setb _EA; + __endasm; +} diff --git a/include/i2c/i2c_utils.h b/include/i2c/i2c_utils.h new file mode 100644 index 0000000..1917c8b --- /dev/null +++ b/include/i2c/i2c_utils.h @@ -0,0 +1,93 @@ +#ifndef I2C_UTILS_ +#define I2C_UTILS_ + +#include "fx2regs.h" +#include "fx2types.h" + +#define I2C_ELEMENTS 5 +#define I2C_DATA 5 +#define I2C_ADDR 5 +#define I2C_SIZE (I2C_ELEMENTS + 1) +#define SCL PA6 +#define SDA PA7 +#define SYNCDELAY SYNCDELAY4 + +//Structure for writing the I2C data onto SDA and SCL +struct i2c_client +{ + //Address + unsigned short addr[I2C_ADDR]; + //Upto 5 data bytes can be written + BYTE data[I2C_DATA]; + //The length of data to write to I2C lines + //Does not include the address byte + BYTE data_length; + BYTE addr_length; +}; + +//Structure for reading data from I2C device. +struct i2c_client_read +{ + //Address + unsigned short addr[I2C_ADDR]; + //The length of data to write to I2C lines + //Does not include the address byte + BYTE data_length; + BYTE addr_length; +}; +__bit I2CPutTX(BYTE * addr, BYTE * data, BYTE addr_length, BYTE data_length); +__bit I2CPutRXRead(BYTE * addr, BYTE addr_length, BYTE data_length); +__bit I2CGetRXRead(); +__bit I2CCheckRXRead(); +__bit I2CPutRXData(BYTE * addr, BYTE * data, BYTE addr_length, BYTE data_length); +__bit I2CGetRXData(BYTE * addr, BYTE * data); +__bit I2CCheckRXData(); +__bit I2CGetTX(); +__bit I2CCheckTX(); +void i2c_init(BYTE retry); +void i2c_control(); +void configure_start_timer(); + + + +/************************* +I2C Controller +*************************/ + +enum isr_state +{ + state_tx = 0, //0 + state_rx = 1 , //1 + state_wait = 2 +}; +enum i2c_states +{ + idle = 0, + start = 1, + address = 2, + addr_ack = 3, + data_write = 4, + data_write_ack = 5, + data_read = 6, + data_read_ack = 7, + stop = 8, + read_addr_ack = 9, + read_data_ack = 10, +}; +extern enum isr_state tx_rx; +extern BYTE bit_count; +extern BYTE tx_i2c_buffer; +//Variable declarations for address and data +extern __xdata BYTE addr[I2C_ADDR]; +extern __xdata BYTE data[I2C_DATA]; +extern __xdata BYTE length; +extern __xdata BYTE retries; +/**************************************** +Cant pass so many arguments in a function +*****************************************/ +extern __xdata BYTE write_addr[I2C_ADDR]; +extern __xdata BYTE write_data[I2C_DATA]; +extern __xdata BYTE rx_addr_length; +extern __xdata BYTE rx_data_length; +extern __xdata BYTE wr_addr; +#endif diff --git a/lib/Makefile b/lib/Makefile index 560c500..f98ea05 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -15,7 +15,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA AS8051?=sdas8051 -SOURCES = serial.c i2c.c delay.c setupdat.c gpif.c eputils.c $(wildcard interrupts/*.c) +SOURCES = serial.c i2c.c delay.c setupdat.c gpif.c eputils.c $(wildcard interrupts/*.c) $(wildcard i2c/*.c) FX2_OBJS = $(patsubst %.c,%.rel, $(SOURCES)) usbav.rel INCLUDES = -I../include SDCC = sdcc -mmcs51 $(SDCCFLAGS) diff --git a/lib/i2c/i2c_utils.c b/lib/i2c/i2c_utils.c new file mode 100644 index 0000000..29ff0ce --- /dev/null +++ b/lib/i2c/i2c_utils.c @@ -0,0 +1,521 @@ +/* +I2C Queue for reading and writing data;; +*/ +#include +#include + + +//An I2C client structure. 5 data bytes, 5 address bytes. Used +//by I2C controller for performing writes +__xdata struct i2c_client i2c_queue[I2C_SIZE]; +//An I2C structure for performing reads +__xdata struct i2c_client_read i2c_rx_queue[I2C_SIZE]; +//An I2C structure for holding the data read. +__xdata struct i2c_client i2c_data_queue[I2C_SIZE]; + +//Variables used to keep track of the Queue. +__xdata BYTE I2CTXIn, I2CTXOut; +__xdata BYTE I2CRXIn, I2CRXOut; +__xdata BYTE I2CRXIn_dat, I2CRXOut_dat; + + + +//Variables used by the I2C controller +//These variables hold data for the current +//transaction. Once the transaction is complete +//they are inserted back into the queue or the +//queue is cleared , depending on whether it is +//a read or write operation. +__xdata BYTE addr[I2C_ADDR]; +__xdata BYTE data[I2C_DATA]; +__xdata BYTE data_length; +__xdata BYTE addr_length; +__xdata BYTE retries; +BYTE tx_i2c_buffer; +BYTE bit_count; +//Read write bit for I2C +__bit rw; +__bit rw_bit; +__bit schedule; + + + + + +/************************************************************ +I2C Controller variables. +************************************************************/ +enum isr_state tx_rx; +//I2C state variable +enum i2c_state my_i2c_states; +BYTE tx_i2c_buffer; +BYTE rx_i2c_buffer; +BYTE bit_count; +//Temporary counters for EEPROM access. You can read immediately after a write to EEPROM. +BYTE i ; +BYTE j; +unsigned long delay1; + + + + +/* Very simple queue + * These are FIFO queues which discard the new data when full. + * Snippet taken from StackOverflow + * Queue is empty when in == out. + * If in != out, then + * - items are placed into in before incrementing in + * - items are removed from out before incrementing out + * Queue is full when in == (out-1 + QUEUE_SIZE) % QUEUE_SIZE; + * + * The queue will hold QUEUE_ELEMENTS number of items before the + * calls to QueuePut fail. + */ + +void I2CQueueInit(void) +{ + I2CTXIn = I2CTXOut = 0; + I2CRXIn = I2CRXOut = 0; + I2CRXIn_dat = I2CRXOut_dat = 0; + +} + + + +/******************************************************************************************* + I2C TX Queue - Insert data and address, along with the length. The controller + uses this data to put the data out on the SDA and SCL lines +*******************************************************************************************/ + +//Set up queue structure to write data from +__bit I2CPutTX(BYTE * addr, BYTE * data, BYTE addr_length, BYTE data_length) +{ + BYTE i; + if (I2CTXIn == (( I2CTXOut - 1 + I2C_SIZE) % I2C_SIZE)) + { + return 1; /* Queue Full*/ + } + for (i = 0; i < addr_length; ++i) + { + i2c_queue[I2CTXIn].addr[i] = addr[i]; + } + i2c_queue[I2CTXIn].addr_length = addr_length; + i = 0; + + for (i = 0; i < data_length; ++i) + { + i2c_queue[I2CTXIn].data[i] = data[i]; + } + //Address length + i2c_queue[I2CTXIn].data_length = data_length; + I2CTXIn = (I2CTXIn + 1) % I2C_SIZE; + return 0; // No errors +} + +//This function loads the value of address and 5 data bytes and returns +//This value is then used by the main program +__bit I2CGetTX() +{ + BYTE i; + if ((I2CTXIn == I2CTXOut)) + { + return 1; /* Queue Empty - nothing to get*/ + } + //Load address + for (i = 0; i < i2c_queue[I2CTXOut].addr_length ; ++i) + { + addr[i] = i2c_queue[I2CTXOut].addr[i]; + } + for (i = 0; i < i2c_queue[I2CTXOut].data_length ; ++i) + { + data[i] = i2c_queue[I2CTXOut].data[i]; + } + data_length = i2c_queue[I2CTXOut].data_length; + addr_length = i2c_queue[I2CTXOut].addr_length; + I2CTXOut = (I2CTXOut + 1) % I2C_SIZE; + return 0; // No errors +} + + +__bit I2CCheckTX() +{ + if ((I2CTXIn == I2CTXOut)) + { + return 1; /* Queue Empty - nothing to get*/ + } + return 0; // No errors +} + +/******************************************************************************************* + I2C RX Queue - Insert address,along with the length. The controller + uses this address to put the data into the queue +*******************************************************************************************/ + +//Set up queue structure to read data from +__bit I2CPutRXRead(BYTE * addr, BYTE addr_length, BYTE data_length) +{ + BYTE i; + if (I2CRXIn == (( I2CRXOut - 1 + I2C_SIZE) % I2C_SIZE)) + { + return 1; /* Queue Full*/ + } + for (i = 0; i < addr_length; ++i) + { + i2c_rx_queue[I2CRXIn].addr[i] = addr[i]; + } + //Address length + i2c_rx_queue[I2CRXIn].addr_length = addr_length; + i2c_rx_queue[I2CRXIn].data_length = data_length; + I2CRXIn = (I2CRXIn + 1) % I2C_SIZE; + return 0; // No errors +} + +//Load up the variables in the controller for performing a read +__bit I2CGetRXRead() +{ + BYTE i; + if ((I2CRXIn == I2CRXOut)) + { + return 1; /* Queue Empty - nothing to get*/ + } + //Load address + for (i = 0; i < i2c_rx_queue[I2CRXOut].addr_length ; ++i) + { + addr[i] = i2c_rx_queue[I2CRXOut].addr[i]; + } + data_length = i2c_rx_queue[I2CRXOut].data_length; + addr_length = i2c_rx_queue[I2CRXOut].addr_length; + I2CRXOut = (I2CRXOut + 1) % I2C_SIZE; + return 0; // No errors +} + + +__bit I2CCheckRXRead() +{ + if ((I2CRXIn == I2CRXOut)) + { + return 1; /* Queue Empty - nothing to get*/ + } + return 0; // No errors +} + +/************************************************************************************************ +I2C functions for the data read from I2C slave +************************************************************************************************/ + +__bit I2CPutRXData(BYTE * addr, BYTE * data, BYTE addr_length, BYTE data_length) +{ + BYTE i; + if (I2CRXIn_dat == (( I2CRXOut_dat - 1 + I2C_SIZE) % I2C_SIZE)) + { + return 1; /* Queue Full*/ + } + for (i = 0; i < addr_length; ++i) + { + i2c_data_queue[I2CRXIn_dat].addr[i] = addr[i]; + } + i2c_data_queue[I2CRXIn_dat].addr_length = addr_length; + i = 0; + for (i = 0; i < data_length; ++i) + { + i2c_data_queue[I2CRXIn_dat].data[i] = data[i]; + } + //Address length + i2c_data_queue[I2CRXIn_dat].data_length = data_length; + I2CRXIn_dat = (I2CRXIn_dat + 1) % I2C_SIZE; + return 0; // No errors +} + +//Puts data into the external variables which have been declared +__bit I2CGetRXData(BYTE * read_addr, BYTE * read_data) +{ + BYTE i; + if ((I2CRXIn_dat == I2CRXOut_dat)) + { + return 1; /* Queue Empty - nothing to get*/ + } + //Load address + for (i = 0; i < i2c_data_queue[I2CRXOut_dat].addr_length ; ++i) + { + read_addr[i] = i2c_data_queue[I2CRXOut_dat].addr[i]; + } + for (i = 0; i < i2c_data_queue[I2CRXOut_dat].data_length ; ++i) + { + read_data[i] = i2c_data_queue[I2CRXOut_dat].data[i]; + } + rx_addr_length = i2c_data_queue[I2CRXOut_dat].data_length; + rx_data_length = i2c_data_queue[I2CRXOut_dat].addr_length; + I2CRXOut_dat = (I2CRXOut_dat + 1) % I2C_SIZE; + return 0; // No errors +} + +__bit I2CCheckRXData() +{ + if ((I2CRXIn_dat == I2CRXOut_dat)) + { + return 1; /* Queue Empty - nothing to get*/ + } + return 0; // No errors +} + + + +void i2c_init(BYTE retry) +{ + tx_rx = state_wait; + //Set current state to idle + my_i2c_states = idle; + //Initialize queue + I2CQueueInit(); + //Set OEA register + OEA |= 0xc0; + //Initial value for I2C + SDA = 1; + SCL = 1; + rw = 0; + retries = 0; + //Keep track of variable number + i = 0; + j = 0; + schedule = 0; +} + + +void configure_start_timer() +{ + TMOD = 0x20; //Configure the timer to 8 bit auto reload. + SYNCDELAY; + TR1 = 0; //Stop the timer. + SYNCDELAY; + TH1 = 0xc3; //Load a precalculated delay into the time. + SYNCDELAY; + TL1 = 0xc3; //Load the lower byte also. + SYNCDELAY; + TR1 = 1; //Start the timer. + SYNCDELAY; +} + +/************************************************************************* +I2C Controller: This controls the shift register operation as well +as the number of retries before the I2C stops its operation. +*************************************************************************/ +void i2c_control() +{ + if ((my_i2c_states == idle) && (tx_rx == state_wait)) + { + // Look for non empty queue and pull the SDA low keeping SCL high. + if (schedule == 0) + { + if (I2CCheckTX() == 0) + { + I2CGetTX(); + for (delay1 = 0; delay1 < 600; delay1 ++) + { + __asm + mov r0, #0xFF + 001$: + mul ab + djnz r0, 001$ + __endasm; + } + my_i2c_states = start; + } + rw_bit = 0; + schedule = 1; + } + else + { + if (I2CCheckRXRead()==0) + { + rw_bit = 1; + for (delay1 = 0; delay1 < 600; delay1 ++) + { + __asm + mov r0, #0xFF + 002$: + mul ab + djnz r0, 002$ + __endasm; + } + I2CGetRXRead(); + my_i2c_states = start; + } + schedule = 0; + } + } + else if ((my_i2c_states == start) && (tx_rx == state_wait)) + { + //Send the start bit + OEA |= 0xC0; + SDA = 0 ; + //Initial delay between the SDA and SCL lines for start bit + __asm + mov r0, #0x03 + 003$:djnz r0, 003$ + __endasm; + SCL = 0; + my_i2c_states = address; + } + else if ((my_i2c_states == address) && (tx_rx == state_wait)) + { + //Send the address and check the R/W bit + tx_i2c_buffer = addr[i]; + bit_count = 0x09; + tx_rx = state_tx; + my_i2c_states = read_addr_ack; + } + else if ((my_i2c_states == read_addr_ack) && (tx_rx == state_wait)) + { + //Read the ACK bit. + bit_count = 0x02; + tx_rx = state_rx; + my_i2c_states = addr_ack; + } + else if ((my_i2c_states == addr_ack) && (tx_rx == state_wait)) + { + //Find the number of data bytes to transfer and keep track + if ((tx_i2c_buffer & 0x01) == 0x00) + { + i++; + if (i >= addr_length) + { + i = 0; + + if (rw_bit == 1) + { + my_i2c_states = data_read; + } + else + { + my_i2c_states = data_write; + } + } + else + { + my_i2c_states = address; + } + } + else if ((tx_i2c_buffer & 0x01) == 0x01) + { + j++; + if (j >= retries) + { + my_i2c_states = stop; + } + else + { + my_i2c_states = address; + j = 0; + } + } + } + else if ((my_i2c_states == data_read) && (tx_rx == state_wait)) + { + //Read the data 8 bit. + bit_count = 0x09; + tx_rx = state_rx; + my_i2c_states = data_read_ack; + } + else if ((my_i2c_states == data_read_ack) && (tx_rx == state_wait)) + { + //Acknowledge the read data always. + EA = 0; + OEA |= 0xC0; + SCL = 0 ; + __asm + mov r0, #0x03 + 004$:djnz r0, 004$ + __endasm; + SDA = 1 ; + __asm + mov r0, #0x03 + 005$:djnz r0, 005$ + __endasm; + SCL = 1 ; + EA = 1; + data[i]= tx_i2c_buffer; + i++; + + if (i >= data_length) + { + my_i2c_states = stop; + } + else + { + my_i2c_states = data_read; + } + } + else if ((my_i2c_states == data_write) && (tx_rx == state_wait)) + { + //Send the 8 bits of data + tx_i2c_buffer = data[i]; + bit_count = 0x09; + tx_rx = state_tx; + my_i2c_states = read_data_ack; + } + else if ((my_i2c_states == read_data_ack) && (tx_rx == state_wait)) + { + //Prepare for reading the data ACK bit. + bit_count = 0x02; + tx_rx = state_rx; + my_i2c_states = data_write_ack; + } + else if ((my_i2c_states == data_write_ack) && (tx_rx == state_wait)) + { + //Read the ACK bit for the written data + if ((tx_i2c_buffer & 0x01) == 0x00) + { + i++; + + if (i >= data_length) + { + my_i2c_states = stop; + } + else + { + my_i2c_states = data_write; + } + } + else if ((tx_i2c_buffer & 0x01) == 0x01) + { + j++; + + if (j >= retries) + { + my_i2c_states = stop; + } + else + { + my_i2c_states = data_write; + } + } + } + else if ((my_i2c_states == stop) && (tx_rx == state_wait)) + { + //Send the stop bit + OEA |= 0xC0; + SCL = 0 ; + __asm + mov r0, #0xFF + 006$:djnz r0, 006$ + __endasm; + SDA = 0 ; + __asm + mov r0, #0xFF + 007$:djnz r0, 007$ + __endasm; + SCL = 1 ; + __asm + mov r0, #0xFF + 008$:djnz r0, 008$ + __endasm; + SDA = 1 ; + my_i2c_states = idle; + if (rw == 1) + { + I2CPutRXData(&addr[0], &data[0], addr_length, data_length); + } + i = 0; + j = 0; + } +}