Mouse code for the MacroRat

Dependencies:   ITG3200 QEI

Revision:
18:6a4db94011d3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-dev/targets/TARGET_ARM_SSG/TARGET_BEETLE/i2c_api.c	Sun May 14 23:18:57 2017 +0000
@@ -0,0 +1,508 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2015 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "i2c_api.h"
+#include "i2c_def.h"
+#include "cmsis.h"
+#include "pinmap.h"
+#include "mbed_error.h"
+#include "mbed_wait_api.h"
+/* States of a possibly combined I2C transfer */
+typedef enum i2c_transfer_state_t {
+    I2C_TRANSFER_SINGLE, /* Non combined transfer */
+    I2C_TRANSFER_COMBINED_FIRST_MESSAGE, /*
+                                          * First message of a
+                                          * combined transfer
+                                          */
+    I2C_TRANSFER_COMBINED_INTERMEDIATE_MESSAGE, /*
+                                                 * Message in the middle
+                                                 * of a combined
+                                                 * transfer
+                                                 */
+    I2C_TRANSFER_COMBINED_LAST_MESSAGE, /*
+                                         * Last message of a combined
+                                         * transfer
+                                         */
+} i2c_transfer_state_t;
+
+/*
+ * Driver private data structure that should not be shared by multiple
+ * instances of the driver
+ * (same driver for multiple instances of the IP)
+ */
+typedef struct private_i2c_t {
+    /* State of a possibly combined ongoing i2c transfer */
+    i2c_transfer_state_t transfer_state;
+}private_i2c_t;
+
+
+/*
+ * Retrieve the private data of the instance related to a given IP
+ */
+static private_i2c_t* get_i2c_private(i2c_t *obj) {
+    static private_i2c_t data0, data1;
+    /*
+     * Select which instance to give using the base
+     * address of registers
+     */
+    switch((intptr_t)obj->i2c) {
+        case I2C0_BASE:
+            return &data0;
+        case I2C1_BASE:
+            return &data1;
+        default:
+            error("i2c driver private data structure not found for this registers base address");
+            return (void*)0;
+    }
+}
+
+/*
+ * Infer the current state of a possibly combined transfer
+ * (repeated restart) from the current state and the "stop" parameter
+ * of read and write functions
+ * MUST be called ONCE AND ONLY ONCE at the beginning of i2c transfer
+ * functions (read and write)
+ */
+static i2c_transfer_state_t update_transfer_state(i2c_t *obj, int stop) {
+    private_i2c_t* private_data = get_i2c_private(obj);
+    i2c_transfer_state_t *state = &private_data->transfer_state;
+
+    /*
+     * Choose the current and next state depending on the current state
+     * This basically implements rising and falling edge detection on
+     * "stop" variable
+     */
+    switch(*state) {
+        /* This is the default state for non restarted repeat transfer */
+        default:
+        case I2C_TRANSFER_SINGLE: /* Not a combined transfer */
+            if (stop) {
+                *state = I2C_TRANSFER_SINGLE;
+            } else {
+                *state = I2C_TRANSFER_COMBINED_FIRST_MESSAGE;
+            }
+            break;
+
+        /* First message of a combined transfer */
+        case I2C_TRANSFER_COMBINED_FIRST_MESSAGE:
+        /* Message in the middle of a combined transfer */
+        case I2C_TRANSFER_COMBINED_INTERMEDIATE_MESSAGE:
+            if (stop) {
+                *state = I2C_TRANSFER_COMBINED_LAST_MESSAGE;
+            } else {
+                *state = I2C_TRANSFER_COMBINED_INTERMEDIATE_MESSAGE;
+            }
+            break;
+
+        /* Last message of a combined transfer */
+        case I2C_TRANSFER_COMBINED_LAST_MESSAGE:
+            if (stop) {
+                *state = I2C_TRANSFER_SINGLE;
+            } else {
+                *state = I2C_TRANSFER_COMBINED_FIRST_MESSAGE;
+            }
+            break;
+    }
+
+    return *state;
+}
+
+
+static const PinMap PinMap_I2C_SDA[] = {
+    {SHIELD_SDA, I2C_0, 0},
+    {SENSOR_SDA, I2C_1, 0},
+    {NC, NC , 0}
+};
+
+static const PinMap PinMap_I2C_SCL[] = {
+    {SHIELD_SCL, I2C_0, 0},
+    {SENSOR_SCL, I2C_1, 0},
+    {NC, NC, 0}
+};
+
+static void clear_isr(i2c_t *obj) {
+    /*
+     * Writing to the IRQ status register clears set bits. Therefore, to
+     * clear indiscriminately, just read the register and write it back.
+     */
+    uint32_t reg = obj->i2c->IRQ_STATUS;
+    obj->i2c->IRQ_STATUS = reg;
+}
+
+void i2c_init(i2c_t *obj, PinName sda, PinName scl) {
+    /* Determine the I2C to use */
+    I2CName i2c_sda = (I2CName)pinmap_peripheral(sda, PinMap_I2C_SDA);
+    I2CName i2c_scl = (I2CName)pinmap_peripheral(scl, PinMap_I2C_SCL);
+    obj->i2c = (I2C_TypeDef *)pinmap_merge(i2c_sda, i2c_scl);
+
+    if ((int)obj->i2c == NC) {
+        error("I2C pin mapping failed");
+    }
+
+    pinmap_pinout(sda, PinMap_I2C_SDA);
+    pinmap_pinout(scl, PinMap_I2C_SCL);
+
+    /*
+     * Default configuration:
+     * - MS    : Master mode
+     * - NEA   : Normal (7-bit) addressing
+     * - ACKEN : Send ACKs when reading from slave
+     * - CLR_FIFO : Not a configuration bit => clears the FIFO
+     */
+    uint32_t reg = I2C_CTRL_MS | \
+                   I2C_CTRL_NEA | \
+                   I2C_CTRL_ACKEN | \
+                   I2C_CTRL_CLR_FIFO;
+
+    obj->i2c->CONTROL = reg;
+
+    get_i2c_private(obj)->transfer_state = I2C_TRANSFER_SINGLE;
+
+    i2c_frequency(obj, 100000); /* Default to 100kHz SCL frequency */
+}
+
+int i2c_start(i2c_t *obj) {
+    return 0;
+}
+
+int i2c_stop(i2c_t *obj) {
+    /* Clear the hardware FIFO */
+    obj->i2c->CONTROL |= I2C_CTRL_CLR_FIFO;
+    /* Clear the HOLD bit used for performing combined transfers */
+    obj->i2c->CONTROL &= ~I2C_CTRL_HOLD;
+    /* Reset the transfer size (read and write) */
+    obj->i2c->TRANSFER_SIZE = 0;
+    /* Clear interrupts */
+    clear_isr(obj);
+    return 0;
+}
+
+void i2c_frequency(i2c_t *obj, int hz) {
+    /*
+     * Divider is split in two halfs : A and B
+     * A is 2 bits wide and B is 6 bits wide
+     * The Fscl frequency (SCL clock) is calculated with the following
+     * equation:
+     * Fscl=SystemCoreClock/(22*(A+1)*(B+1))
+     * Here, we only calculate the B divisor which already enables a
+     * wide enough range of values
+     */
+    uint32_t divisor_a = 0; /* Could be changed if a wider range of hz
+                               is needed */
+    uint32_t divisor_b = (SystemCoreClock / (22.0 * hz)) - 1;
+
+    /* Clamp the divisors to their maximal value */
+    divisor_a = divisor_a > I2C_CTRL_DIVISOR_A_BIT_MASK ?
+                I2C_CTRL_DIVISOR_A_BIT_MASK : divisor_a;
+    divisor_b = divisor_b > I2C_CTRL_DIVISOR_B_BIT_MASK ?
+                I2C_CTRL_DIVISOR_B_BIT_MASK : divisor_b;
+
+    uint8_t divisor_combinded = (divisor_a & I2C_CTRL_DIVISOR_A_BIT_MASK)
+                              | (divisor_b & I2C_CTRL_DIVISOR_B_BIT_MASK);
+
+    obj->i2c->CONTROL = (obj->i2c->CONTROL & ~I2C_CTRL_DIVISORS)
+        | (divisor_combinded << I2C_CTRL_DIVISOR_OFFSET);
+}
+
+int i2c_read(i2c_t *obj, int address, char *data, int length, int stop) {
+    int bytes_read = 0;
+    int length_backup = length;
+    char *data_backup = data;
+    obj->last_xfer_address = address;
+    i2c_transfer_state_t transfer_state = update_transfer_state(obj, stop);
+
+    /* Try to write until it finally succeed or times out */
+    int main_timeout = 10;
+    int retry = 0;
+    do {
+        main_timeout--;
+
+        retry = 0;
+        bytes_read = 0;
+        length = length_backup;
+        data = data_backup;
+
+        uint32_t reg = obj->i2c->CONTROL & 0xff7f;
+        reg |= I2C_CTRL_RW | \
+               I2C_CTRL_CLR_FIFO;
+        /*
+         * Only touch the HOLD bit at the beginning of
+         * (possibly combined) transactions
+         */
+        if(transfer_state == I2C_TRANSFER_COMBINED_FIRST_MESSAGE
+            || transfer_state == I2C_TRANSFER_SINGLE) {
+
+            reg |= I2C_CTRL_HOLD;
+        }
+        obj->i2c->CONTROL = reg;
+
+        /* Set the expected number of bytes to be received */
+        if (length > I2C_TRANSFER_SIZE) {
+            error("I2C transfer size too big for the FIFO");
+        }
+        obj->i2c->TRANSFER_SIZE = length & I2C_TRANSFER_SIZE;
+
+        clear_isr(obj);
+
+        /*
+         * Start the transaction by writing address.
+         * Discard the lower bit as it is automatically set
+         * by the controller based on I2C_CTRL_RW bit in CONTROL
+         * register
+         */
+        obj->i2c->ADDRESS = (address & 0xFF) >> 1;
+
+        if(transfer_state == I2C_TRANSFER_COMBINED_LAST_MESSAGE
+           || transfer_state == I2C_TRANSFER_SINGLE) {
+
+            /* Clear the hold bit before reading the DATA register */
+            obj->i2c->CONTROL &= ~I2C_CTRL_HOLD;
+        }
+
+        /* Wait for completion of the address transfer */
+        int completion_timeout = 1000;
+        while (completion_timeout) {
+            completion_timeout--;
+
+            uint32_t irq_status = obj->i2c->IRQ_STATUS;
+            if (irq_status & I2C_IRQ_NACK
+                || irq_status & I2C_IRQ_ARB_LOST) {
+
+                retry = 1;
+                break;
+            }
+
+            if(irq_status & I2C_IRQ_COMP) {
+                break;
+            }
+        }
+
+        /* If retry, jump to the beginning and try again */
+        if (retry || !completion_timeout) {
+            retry = 1;
+            continue;
+        }
+
+        clear_isr(obj);
+
+        /* Read the data from the DATA register */
+        completion_timeout = 1000;
+        while (length && completion_timeout) {
+            completion_timeout--;
+
+            uint32_t irq_status = obj->i2c->IRQ_STATUS;
+            uint32_t status = obj->i2c->STATUS;
+
+            if(irq_status & I2C_IRQ_NACK ||
+               irq_status & I2C_IRQ_ARB_LOST) {
+
+                retry = 1;
+                break;
+            }
+
+            /*
+             * Just wait for RXDV because COMP is only risen at the end
+             * of the transfer
+             */
+            if (status & I2C_STATUS_RXDV) {
+                *data++ = obj->i2c->DATA & 0xFF;
+                length--;
+                bytes_read++;
+            }
+
+            if (irq_status & I2C_IRQ_RX_UNF) {
+                error("Reading more bytes than the I2C transfer size");
+                retry = 1;
+                break;
+            }
+        }
+
+        /* If retry, jump to the beginning and try again */
+        if (retry || !completion_timeout) {
+            retry = 1;
+            continue;
+        }
+    } while(retry && main_timeout);
+
+    if (!main_timeout) {
+        bytes_read = 0;
+        data = data_backup;
+    }
+
+    obj->i2c->CONTROL |= I2C_CTRL_CLR_FIFO;
+    clear_isr(obj);
+    return bytes_read;
+}
+
+int i2c_write(i2c_t *obj, int address, const char *data, int length,
+              int stop) {
+
+    int bytes_written = 0;
+    int length_backup = length;
+    const char *data_backup = data;
+    obj->last_xfer_address = address;
+    i2c_transfer_state_t transfer_state = update_transfer_state(obj, stop);
+
+    /* Try to write until it finally succeed or times out */
+    int main_timeout = 10;
+    int retry = 0;
+    do {
+        main_timeout--;
+
+        retry = 0;
+        bytes_written = 0;
+        length = length_backup;
+        data = data_backup;
+
+        /* Read the defined bits in the control register */
+        uint32_t reg = obj->i2c->CONTROL & 0xff7f;
+        reg |= I2C_CTRL_CLR_FIFO;
+        reg &= ~I2C_CTRL_RW;
+
+        /*
+         * Only touch the HOLD bit at the beginning of
+         * (possibly combined) transactions
+         */
+        if(transfer_state == I2C_TRANSFER_COMBINED_FIRST_MESSAGE
+           || transfer_state == I2C_TRANSFER_SINGLE) {
+
+            reg |= I2C_CTRL_HOLD;
+        }
+        obj->i2c->CONTROL = reg;
+
+        clear_isr(obj);
+
+        /* Set the expected number of bytes to be transmitted */
+        if (length > I2C_TRANSFER_SIZE) {
+            error("I2C transfer size too big for the FIFO");
+        }
+
+        /* Set the expected number of bytes to be transmitted */
+        obj->i2c->TRANSFER_SIZE = length & I2C_TRANSFER_SIZE;
+
+        /*
+         * Write the address, triggering the start of the transfer
+         * Discard the lower bit as it is automatically set
+         * by the controller based on I2C_CTRL_RW bit in CONTROL
+         * register
+         */
+        obj->i2c->ADDRESS = (address & 0xFF) >> 1;
+
+        /* Send the data bytes */
+        int write_timeout = 1000 + length;
+        while (length && write_timeout) {
+            write_timeout--;
+            uint32_t irq_status = obj->i2c->IRQ_STATUS;
+            /* If overflow, undo last step */
+            if (irq_status & I2C_IRQ_TX_OVF) {
+                *data--;
+                length++;
+                bytes_written--;
+                /* Clear the bit by writing 1 to it */
+                obj->i2c->IRQ_STATUS |= I2C_IRQ_TX_OVF;
+            }
+
+            if (irq_status & I2C_IRQ_NACK
+                || irq_status & I2C_IRQ_ARB_LOST) {
+
+                retry = 1;
+                break;
+            }
+
+            obj->i2c->DATA = *data++;
+            length--;
+            bytes_written++;
+        }
+
+        /* If retry, jump to the beginning and try again */
+        if (retry || !write_timeout) {
+            retry = 1;
+            continue;
+        }
+
+        if(transfer_state == I2C_TRANSFER_COMBINED_LAST_MESSAGE
+           || transfer_state == I2C_TRANSFER_SINGLE) {
+            /*
+             * Clear the hold bit to signify the end
+             * of the write sequence
+             */
+            obj->i2c->CONTROL &= ~I2C_CTRL_HOLD;
+        }
+
+
+        /* Wait for transfer completion */
+        int completion_timeout = 1000;
+        while (completion_timeout) {
+            completion_timeout--;
+
+            uint32_t irq_status = obj->i2c->IRQ_STATUS;
+            if(irq_status & I2C_IRQ_NACK
+               || irq_status & I2C_IRQ_ARB_LOST) {
+                retry = 1;
+                break;
+            }
+            if(irq_status & I2C_IRQ_COMP) {
+                break;
+            }
+        }
+
+        /* If retry, jump to the beginning and try again */
+        if (retry || !completion_timeout) {
+            continue;
+        }
+
+        obj->i2c->CONTROL |= I2C_CTRL_CLR_FIFO;
+        clear_isr(obj);
+    } while(retry && main_timeout);
+
+    return bytes_written;
+}
+
+void i2c_reset(i2c_t *obj) {
+    i2c_stop(obj);
+}
+
+int i2c_byte_read(i2c_t *obj, int last) {
+    char i2c_ret = 0;
+    i2c_read(obj, obj->last_xfer_address, &i2c_ret, 1, last);
+    return i2c_ret;
+}
+
+int i2c_byte_write(i2c_t *obj, int data) {
+    /* Store the number of written bytes */
+    uint32_t wb = i2c_write(obj, obj->last_xfer_address, (char*)&data, 1, 0);
+    if (wb == 1)
+        return 1;
+    else
+        return 0;
+}
+
+void i2c_slave_mode(i2c_t *obj, int enable_slave) {
+}
+
+int i2c_slave_receive(i2c_t *obj) {
+    return 0;
+}
+
+int i2c_slave_read(i2c_t *obj, char *data, int length) {
+    return 0;
+}
+
+int i2c_slave_write(i2c_t *obj, const char *data, int length) {
+    return 0;
+}
+
+void i2c_slave_address(i2c_t *obj, int idx, uint32_t address, uint32_t mask) {
+}