Mouse code for the MacroRat
Diff: mbed-dev/targets/TARGET_ARM_SSG/TARGET_BEETLE/i2c_api.c
- 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) { +}