package csp.scj.watchdog; import javax.realtime.RawInt; import javax.realtime.RawIntRead; import javax.realtime.RawIntWrite; import javax.realtime.RawMemory; import csp.IODevice; public class I2CBusController implements IODevice{ int BASE_ADD; static final int CONTROL_OFF = 0; static final int STATUS_OFF = 1; static final int DEV_ADD_OFF = 2; static final int MSG_SIZE_OFF = 3; static final int TX_FIFO_OFF = 4; static final int RX_FIFO_OFF = 5; static final int TH_OFF = 6; static final int TL_OFF = 7; static final int TX_OCCU_OFF = 8; static final int RX_OCCU_OFF = 9; // TX/RX buffer size in bytes. This size is the size of the FIFO's in // the hardware controller. To modify it you should modify the VHDL files. public static final int BUFFER_SIZE = 32; // Status constants public static final int TBUF_ERR = 0x00000040; public static final int RBUF_ERR = 0x00000020; public static final int ACK_ERR = 0x00000010; public static final int HDR_ERR = 0x00000008; public static final int BUS_BUSY = 0x00000004; public static final int STOP_STAT = 0x00000002; public static final int DATA_RDY = 0x00000001; public static final int OCCU_RD = 0xFFFF0000; public static final int OCCU_WR = 0x0000FFFF; // Control constants public static final int TX_FLUSH = 0x00000080; public static final int RX_FLUSH = 0x00000040; public static final int ACK = 0x00000020; public static final int STRT = 0x00000010; public static final int STOP = 0x00000008; public static final int MASL = 0x00000004; public static final int RSTA = 0x00000002; public static final int ENABLE = 0x00000001; public static final int CLEAR_STRT = 0xFFFFFFEF; // Configuration constants public static final int MASTER = ENABLE | MASL; public static final int SLAVE = ENABLE; public static final int NOT_FLUSH = 0xFFFFFF7F; // Count base for SCL timings, adjust according to // uP clock frequency. public static final int CNT_BASE = 10; // These constants define the timings of the SCL signal public static final int T_HOLD_START = 5 * CNT_BASE - 1; public static final int T_RSTART = 5 * CNT_BASE - 1; public static final int T_LOW = 5 * CNT_BASE - 1; public static final int T_HIGH = 5 * CNT_BASE - 1; public static final int T_HALF_HIGH = 2 * CNT_BASE - 1; public static final int T_SUSTO = 4 * CNT_BASE - 1; public static final int T_WAIT = 8 * CNT_BASE; /** * Control register */ public RawInt control; /** * Status register */ public RawIntRead status; /** * Host slave address */ public RawInt devadd; /** * Size of the message (in bytes) to be transmitted */ public RawInt msg_size; /** * Data to send */ public RawIntWrite tx_fifo_data; /** * Data to receive */ public RawIntRead rx_fifo_data; /** * Timing high */ public RawInt th; /** * Timing low */ public RawInt tl; /** * Tx buffer occupancy */ public RawIntRead tx_occu; /** * Rx buffer occupancy */ public RawIntRead rx_occu; public I2CBusController(int baseAddress) { this.BASE_ADD = baseAddress; control = RawMemory.createRawIntInstance(RawMemory.IO_MEM_MAPPED, BASE_ADD + CONTROL_OFF); status = RawMemory.createRawIntInstance(RawMemory.IO_MEM_MAPPED, BASE_ADD + STATUS_OFF); devadd = RawMemory.createRawIntInstance(RawMemory.IO_MEM_MAPPED, BASE_ADD + DEV_ADD_OFF); msg_size = RawMemory.createRawIntInstance(RawMemory.IO_MEM_MAPPED, BASE_ADD + MSG_SIZE_OFF); tx_fifo_data = RawMemory.createRawIntWriteInstance( RawMemory.IO_MEM_MAPPED, BASE_ADD + TX_FIFO_OFF); rx_fifo_data = RawMemory.createRawIntReadInstance( RawMemory.IO_MEM_MAPPED, BASE_ADD + RX_FIFO_OFF); th = RawMemory.createRawIntInstance(RawMemory.IO_MEM_MAPPED, BASE_ADD + TH_OFF); tl = RawMemory.createRawIntInstance(RawMemory.IO_MEM_MAPPED, BASE_ADD + TL_OFF); tx_occu = RawMemory.createRawIntReadInstance(RawMemory.IO_MEM_MAPPED, BASE_ADD + TX_OCCU_OFF); rx_occu = RawMemory.createRawIntReadInstance(RawMemory.IO_MEM_MAPPED, BASE_ADD + RX_OCCU_OFF); } public void setControl(int i) { control.put(i); } public int readControl() { return control.get(); } public int readStatus() { return status.get(); } public int readBuff() { return rx_fifo_data.get(); } /** * Load the initial configuration to the I2C device. It loads the device * address and the timing constants. * * @param devAdd * Device address. This is the address used when addressed as * slave. * @param isMaster * When true, the device works in master mode, otherwise it works * in slave mode. */ final public void initialize(int devAdd, boolean isMaster) { devadd.put(devAdd); if (isMaster) { control.put(MASTER); } else { control.put(SLAVE); } int temp = (T_HOLD_START << 24) + (T_RSTART << 16) + (T_LOW << 8) + (T_HIGH); th.put(temp); temp = (T_HALF_HIGH << 24) + (T_SUSTO << 16) + (T_WAIT << 8) + (0); tl.put(temp); } /** * Set device in slave mode without changing the device address */ public void slaveMode(){ control.put(SLAVE); } /** * Clear the transmit buffer. */ public void flushTXBuff() { int controlOld = control.get(); control.put(controlOld | TX_FLUSH); control.put(controlOld); } /** * Clear the receive buffer. */ public void flushRXBuff() { int controlOld = control.get(); control.put(controlOld | RX_FLUSH); control.put(controlOld); } /** * Write a single byte in the transmit buffer. * * @param data * Byte to be written to the buffer. */ public void writeBuff(int data) { if (tBuffSpace(data)) { tx_fifo_data.put(data); } else { System.out.println("Not enough space in buffer"); } } /** * Write the elements of the data array into the transmit buffer * * @param dataArray * Array containing the elements to be written in the buffer. */ public void writeBuffer(int[] dataArray) { if (tBuffSpace(dataArray)) { for (int i = 0; i < dataArray.length; i++) { tx_fifo_data.put(dataArray[i]); } } else { System.out.println("Not enough space in buffer"); } } /** * Check that there is still space available in buffer. * * @return True if there is space available to store the required data. */ public boolean tBuffSpace(int[] dataArray) { int occu = (tx_occu.get() & OCCU_WR); int space = BUFFER_SIZE - occu; return (dataArray.length <= space); } /** * Check that there is still space available in buffer. * * @return True if there is space available to store the required data. */ public boolean tBuffSpace(int data) { int occu = (tx_occu.get() & OCCU_WR); int space = BUFFER_SIZE - occu; return (space >= 1); } /** * Read ALL the bytes in the transmit buffer. Data is stored in the data * array. If buffer is empty, the array is filled with zeros. * * @param data */ public void readBuffer(int[] data) { // Read all data in buffer int occu = (rx_occu.get() & OCCU_RD) >>> 16; for (int i = 0; i < occu; i++) { data[i] = rx_fifo_data.get(); } } /** * Write "size" bytes to the slave identified by the address in the first * byte of the transmit buffer.This method is particularly useful if you * have previously written data to the transmit buffer. It assumes that the * first seven bits of the byte to whom the read pointer in the transmit * buffer points to is the slave address. The LSB of this same byte must be * zero. This is a non-blocking operation. Once the transmission is started * the hardware will take care of finishing it and clearing the BUS_BUSY * flag in the status register. * * @param size * How many bytes will be written to the slave. */ public void write(int size) { // Clear STRT bit in case there was a previous transaction control.put(control.get() & CLEAR_STRT); if ((status.get() & BUS_BUSY) == 0) { // Set I2C to master control.put(MASTER); if (size > 1) { msg_size.put(size + 1); } else { msg_size.put(1); } // Initiate transmission, set STRT bit = 1 control.put(control.get() | STRT); } else { System.out.println("Can't start transmission, bus busy"); } } /** * Write one byte to the slave identified with slAddress address. This is a * non-blocking operation. Once the transmission is started the hardware * will take care of finishing it and clearing the BUS_BUSY flag in the * status register. * * @param slAddress * Address of the slave target. * @param data * Byte to be written. * */ public void write(int slAddress, int data) { // Clear STRT bit in case there was a previous transaction control.put(control.get() & CLEAR_STRT); // Start with an empty buffer flushTXBuff(); if ((status.get() & BUS_BUSY) == 0) { // To write, the LSB of the address is set to zero // and the first position of buffer is used to store the // address of the slave we wish to communicate with. tx_fifo_data.put(slAddress * 2); // Write data to tx buffer tx_fifo_data.put(data); } // Set I2C to master control.put(MASTER); msg_size.put(1); // Initiate transmission, set STRT bit = 1 control.put(control.get() | STRT); } /** * Write "N" bytes to the slave identified with slAddress address. "N" is * the size of the "data" array. This is a non-blocking operation. Once the * transmission is started the hardware will take care of finishing it and * clearing the BUS_BUSY flag in the status register. * * @param slAddress * Address of the slave target. * @param data * Array of data to be written. * */ public void write(int slAddress, int[] data) { // Clear STRT bit in case there was a previous transaction control.put(control.get() & CLEAR_STRT); // Start with an empty buffer flushTXBuff(); if ((status.get() & BUS_BUSY) == 0) { // To write, the LSB of the address is set to zero // and the first position of buffer is used to store the // address of the slave we wish to communicate with. tx_fifo_data.put(slAddress * 2); // Write data to tx buffer if (data.length > BUFFER_SIZE - 1) { System.out.println("Data bigger than buffer size"); } else { for (int i = 0; i < data.length; i++) { tx_fifo_data.put(data[i]); } } // Set I2C to master control.put(MASTER); msg_size.put(data.length + 1); // Initiate transmission, set STRT bit = 1 control.put(control.get() | STRT); } else { System.out.println("Can't start transmission, bus busy"); } } /** * This is a very common function in I2C devices, where the master writes * one byte of data to a slave, usually to set a base address to read from, * and then it performs a read operation. This is a non-blocking operation. * The method will return after the read operation is initiated. The * hardware will take care of finishing, clearing the BUS_BUSY flag, and * setting the DATA_RDY flag in the status register. * * @param slAddress * Address of the slave target. * @param data * Base address of slave. * * @param readSize * Size in bytes of the read transaction. * */ public void writeRead(int slAddress, int data, int readSize) { // Clear STRT bit in case there was a previous transaction control.put(control.get() & CLEAR_STRT); // Start with an empty buffer flushTXBuff(); if ((status.get() & BUS_BUSY) == 0) { // To write, the LSB of the address is set to zero // and the first position of buffer is used to store the // address of the slave we wish to communicate with. tx_fifo_data.put(slAddress * 2); tx_fifo_data.put(data); tx_fifo_data.put(slAddress * 2 + 1); // Set I2C to master and initiate transmission control.put(MASTER | STRT); // It is safe to set the repeated start bit here since we wish // to transmit only one byte. Setting the repeated start bit // takes effect in the next WAIT_ACK or SEND_ACK state control.put(control.get() | RSTA); // Message size is reduced by one since byte 0 counts as first // received byte msg_size.put(readSize - 1); // Now we need to wait until the controller leaves the first // ACK_HEADER state to set the master rx mode } else { System.out.println("Can't start transmission, bus busy"); } } /** * Read "N" bytes from the slave identified with slAddress address. "N" is * specified before starting the read operation. This is a non-blocking * operation, once it is started, the hardware will take care of finishing * it, clearing the BUS_BUSY flag and setting the DATA_RDY flag in the * status register. Data in the receive buffer has to be moved explicitly to * e.g. an array for its later use. * * @param slAddress * Target slave address. * @param readSize * Size in bytes of the read transaction. */ public void read(int slAddress, int readSize) { // Clear STRT bit in case there was a previous transaction control.put(control.get() & CLEAR_STRT); // Start with an empty buffer flushTXBuff(); flushRXBuff(); // A read operation starts by transmitting the slave address. // Add 1 to indicate a read transaction. tx_fifo_data.put(slAddress * 2 + 1); control.put(MASTER); msg_size.put(readSize - 1); control.put(control.get() | STRT); } }