t
Fork of mbed-dev by
targets/TARGET_Atmel/TARGET_SAM_CortexM0P/i2c_api.c
- Committer:
- <>
- Date:
- 2016-10-28
- Revision:
- 149:156823d33999
- Parent:
- targets/hal/TARGET_Atmel/TARGET_SAM_CortexM0P/i2c_api.c@ 64:41a834223ea3
- Child:
- 171:89b338f31ef1
File content as of revision 149:156823d33999:
/* mbed Microcontroller Library * Copyright (c) 2006-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 "mbed_assert.h" #include "i2c_api.h" #include <math.h> #include "cmsis.h" #include "pinmap.h" #include "sercom.h" #include "i2c_master.h" #include "i2c_slave.h" #include "pinmap_function.h" #if DEVICE_I2C_ASYNCH #include "i2c_master_interrupt.h" #endif #if DEVICE_I2C_ASYNCH #define pI2C_S(obj) (&obj->i2c) #else #define pI2C_S(obj) obj #endif #define I2C_MASTER_DEFAULT_BAUD 100000 /** * \brief I2C modes enum * * I2C mode selection. */ enum i2c_mode { /** Master mode. */ I2C_MODE_MASTER = 0x5, /** Slave mode. */ I2C_MODE_SLAVE = 0x4, }; /* Extern Variables */ extern uint8_t g_sys_init; typedef void (*I2CHandler)(void); #if DEVICE_I2C_ASYNCH #define _SERCOM_INTERRUPT_HANDLERS(n, unused) (uint32_t)SERCOM##n##_Handler, /* To save the i2c objects */ static uint32_t i2c_instances[SERCOM_INST_NUM] = {0}; const uint32_t sercom_irq_handlers[SERCOM_INST_NUM] = { MREPEAT(SERCOM_INST_NUM, _SERCOM_INTERRUPT_HANDLERS, ~) }; #endif /* Forward declaration */ enum status_code _i2c_master_wait_for_bus( struct i2c_master_module *const module); enum status_code _i2c_master_address_response( struct i2c_master_module *const module); enum status_code _i2c_master_send_hs_master_code( struct i2c_master_module *const module, uint8_t hs_master_code); /* Adding function from ASF for compatibility */ static enum status_code _i2c_slave_wait_for_bus( struct i2c_slave_module *const module) { /* Sanity check arguments. */ MBED_ASSERT(module); MBED_ASSERT(module->hw); SercomI2cs *const i2c_hw = &(module->hw->I2CS); /* Wait for reply. */ uint16_t timeout_counter = 0; while ((!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_DRDY)) && (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC)) && (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH))) { /* Check timeout condition. */ if (++timeout_counter >= module->buffer_timeout) { return STATUS_ERR_TIMEOUT; } } return STATUS_OK; } /** Initialize the I2C peripheral * * Configures the pins used by I2C, sets a default format and frequency, and enables the peripheral * @param[out] obj The I2C object to initialize * @param[in] sda The pin to use for SDA * @param[in] scl The pin to use for SCL * @return void */ void i2c_init(i2c_t *obj, PinName sda, PinName scl) { Sercom* hw; uint32_t mux_func; struct i2c_master_config config_i2c_master; /* Sanity check arguments */ MBED_ASSERT(obj); MBED_ASSERT(sda != NC); MBED_ASSERT(scl != NC); if (g_sys_init == 0) { system_init(); g_sys_init = 1; } pI2C_S(obj)->pins[0] = sda; pI2C_S(obj)->pins[1] = scl; /* Calculate SERCOM instance from pins */ uint32_t sercom_index = pinmap_merge_sercom(sda, scl); if (sercom_index == (uint32_t)NC) { return; } hw = (Sercom*)pinmap_peripheral_sercom(NC, sercom_index); i2c_master_get_config_defaults(&config_i2c_master); /* SERCOM PAD0 - SDA */ mux_func = pinmap_function_sercom(sda, sercom_index); if (mux_func == (uint32_t)NC) return; config_i2c_master.pinmux_pad0 = (sda << 16) | (mux_func & 0xFFFF); /* SERCOM PAD1 - SCL */ mux_func = pinmap_function_sercom(scl, sercom_index); if (mux_func == (uint32_t)NC) return; config_i2c_master.pinmux_pad1 = (scl << 16) | (mux_func & 0xFFFF); /* Default baud rate is set to 100kHz */ pI2C_S(obj)->baud_rate = I2C_MASTER_DEFAULT_BAUD; config_i2c_master.baud_rate = pI2C_S(obj)->baud_rate / 1000; while(i2c_master_init(&pI2C_S(obj)->master, hw, &config_i2c_master) != STATUS_OK); pI2C_S(obj)->mode = I2C_MODE_MASTER; #if DEVICE_I2C_ASYNCH /* Save the i2c object */ i2c_instances[sercom_index] = (uint32_t)obj; #endif i2c_master_enable(&pI2C_S(obj)->master); } /** Configure the I2C frequency. * @param obj The i2c object * @param hz Frequency in Hz */ void i2c_frequency(i2c_t *obj, int hz) { /* Temporary variables. */ int32_t baud_rate; int32_t tmp_baud; int32_t tmp_baud_hs; enum status_code tmp_status_code; /* Sanity check arguments */ MBED_ASSERT(obj); MBED_ASSERT(pI2C_S(obj)->master.hw); /* Return if in Slave mode, slave do not have any baud to set */ if (pI2C_S(obj)->mode != I2C_MODE_MASTER) return; SercomI2cm *const i2c_module = &(pI2C_S(obj)->master.hw->I2CM); /* Disable I2C Module */ i2c_master_disable(&pI2C_S(obj)->master); baud_rate = hz / 1000; /* To kHz */ /* Give a dummy supported value */ pI2C_S(obj)->baud_rate_high_speed = I2C_MASTER_BAUD_RATE_3400KHZ; uint32_t sercom_index = _sercom_get_sercom_inst_index(pI2C_S(obj)->master.hw); /* Find and set baudrate. */ tmp_baud = (int32_t)(div_ceil( system_gclk_chan_get_hz(SERCOM0_GCLK_ID_CORE + sercom_index), (2000*(baud_rate))) - 5); /* Check that baudrate is supported at current speed. */ if (tmp_baud > 255 || tmp_baud < 0) { /* Baud rate not supported. */ tmp_status_code = STATUS_ERR_BAUDRATE_UNAVAILABLE; } else { /* Find baudrate for high speed */ tmp_baud_hs = (int32_t)(div_ceil( system_gclk_chan_get_hz(SERCOM0_GCLK_ID_CORE + sercom_index), (2000*(pI2C_S(obj)->baud_rate_high_speed))) - 1); /* Check that baudrate is supported at current speed. */ if (tmp_baud_hs > 255 || tmp_baud_hs < 0) { /* Baud rate not supported. */ tmp_status_code = STATUS_ERR_BAUDRATE_UNAVAILABLE; } } if (tmp_status_code != STATUS_ERR_BAUDRATE_UNAVAILABLE) { /* Baud rate acceptable. */ i2c_module->BAUD.reg = SERCOM_I2CM_BAUD_BAUD(tmp_baud) | SERCOM_I2CM_BAUD_HSBAUD(tmp_baud_hs); pI2C_S(obj)->baud_rate = hz; } /* Enable back the I2C Module */ i2c_master_enable(&pI2C_S(obj)->master); } /** Send START command. * @param obj The i2c object */ int i2c_start(i2c_t *obj) { /* Sanity check arguments */ MBED_ASSERT(obj); if (pI2C_S(obj)->mode == I2C_MODE_MASTER) { pI2C_S(obj)->start_pending = 1; } return 0; } /** Send STOP command. * @param obj The i2c object */ int i2c_stop(i2c_t *obj) { /* Sanity check arguments */ MBED_ASSERT(obj); if (pI2C_S(obj)->mode == I2C_MODE_MASTER) { /* Send STOP command */ i2c_master_send_stop(&pI2C_S(obj)->master); } else { SercomI2cs *const i2c_hw = &(pI2C_S(obj)->slave.hw->I2CS); /* Release line and wait for next start condition */ i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x2); } pI2C_S(obj)->start_pending = 0; /* TODO: Wait till STOP is send */ return 0; } /** Blocking reading data. * @param obj The i2c object * @param address 7-bit address (last bit is 1) * @param data The buffer for receiving * @param length Number of bytes to read * @param stop Stop to be generated after the transfer is done * @return Number of read bytes */ int i2c_read(i2c_t *obj, int address, char *data, int length, int stop) { /* Sanity check arguments */ MBED_ASSERT(obj); MBED_ASSERT(pI2C_S(obj)->master.hw); enum status_code tmp_status; #if DEVICE_I2C_ASYNCH if (i2c_active(obj)) { /* I2C is busy with a job */ return 0; } #endif struct i2c_master_packet packet; packet.address = (address & 0xFF) >> 1; packet.data_length = length; packet.data = (uint8_t*)data; packet.ten_bit_address = false; packet.high_speed = false; if (stop) { tmp_status = i2c_master_read_packet_wait(&pI2C_S(obj)->master, &packet); } else { tmp_status = i2c_master_read_packet_wait_no_stop(&pI2C_S(obj)->master, &packet); } if (tmp_status == STATUS_OK) { return length; } else { /* Currently, no way to track no of bytes received, so return 0 if fail */ return 0; } } /** Blocking sending data. * @param obj The i2c object * @param address 7-bit address (last bit is 0) * @param data The buffer for sending * @param length Number of bytes to write * @param stop Stop to be generated after the transfer is done * @return Number of written bytes */ int i2c_write(i2c_t *obj, int address, const char *data, int length, int stop) { /* Sanity check arguments */ MBED_ASSERT(obj); MBED_ASSERT(pI2C_S(obj)->master.hw); enum status_code tmp_status; #if DEVICE_I2C_ASYNCH if (i2c_active(obj)) { /* I2C is busy with a job */ return 0; } #endif struct i2c_master_packet packet; packet.address = (address & 0xFF) >> 1; packet.data_length = length; packet.data = (uint8_t *)data; packet.ten_bit_address = false; packet.high_speed = false; if (stop) { tmp_status = i2c_master_write_packet_wait(&pI2C_S(obj)->master, &packet); } else { tmp_status = i2c_master_write_packet_wait_no_stop(&pI2C_S(obj)->master, &packet); } if (tmp_status == STATUS_OK) { return length; } else { /* Currently, no way to track no of bytes transmitted, so return 0 if fail */ return 0; } } /** Reset I2C peripheral. * @param obj The i2c object */ void i2c_reset(i2c_t *obj) { /* Sanity check arguments */ MBED_ASSERT(obj); /* Send STOP */ i2c_stop(obj); pI2C_S(obj)->start_pending = 0; } /** Write address preceded by START condition. * @param obj The i2c object * @param address Address to be placed * @param rw_flag read or write flag * @return 1 if NAK was received, 0 if ACK was received, 2 for timeout. */ int i2c_write_address(i2c_t *obj, int address, int rw_flag) { /* Sanity check arguments */ MBED_ASSERT(obj); MBED_ASSERT(pI2C_S(obj)->master.hw); enum status_code tmp_status; SercomI2cm *const i2c_module = &(pI2C_S(obj)->master.hw->I2CM); _i2c_master_wait_for_sync(&pI2C_S(obj)->master); /* Set action to ACK. */ i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; /* Write 7-bit address + read/write flag */ i2c_module->ADDR.reg = ((address & 0x7F) << 1) | (rw_flag & 0x01) | (0 << SERCOM_I2CM_ADDR_HS_Pos); /* Wait for response on bus. */ tmp_status = _i2c_master_wait_for_bus(&pI2C_S(obj)->master); /* Check for error. */ if (tmp_status != STATUS_OK) { return I2C_ERROR_BUS_BUSY; } /* Check for address response error unless previous error is detected. */ tmp_status = _i2c_master_address_response(&pI2C_S(obj)->master); if (tmp_status != STATUS_OK) { return I2C_ERROR_NO_SLAVE; } return 0; } /** Read one byte. * @param obj The i2c object * @param last Acknowledge * @return The read byte */ int i2c_byte_read(i2c_t *obj, int last) { int data = -1; /* Sanity check arguments */ MBED_ASSERT(obj); MBED_ASSERT(pI2C_S(obj)->master.hw); enum status_code tmp_status; if (pI2C_S(obj)->mode == I2C_MODE_MASTER) { SercomI2cm *const i2c_module = &(pI2C_S(obj)->master.hw->I2CM); if (last) { /* Set action to nack. */ i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; } else { /* Set action to ack. */ i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; } /* Check that bus ownership is not lost. */ if (!(i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE(2))) { return -1; /* Return invalid data*/ } /* Save data to buffer. */ _i2c_master_wait_for_sync(&pI2C_S(obj)->master); data = i2c_module->DATA.reg; /* Wait for response. */ tmp_status = _i2c_master_wait_for_bus(&pI2C_S(obj)->master); /* Check for error. */ if (tmp_status != STATUS_OK) { return -1; /* Return invalid data*/ } } else { #if DEVICE_I2CSLAVE SercomI2cs *const i2c_hw = &(pI2C_S(obj)->slave.hw->I2CS); /* Check direction */ if (i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_DIR) { /* Write request from master, send NACK and return */ i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_ACKACT; i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3); return -1; /* Return invalid data*/ } if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH) { /* Request from master, Address not yet acknowledged */ i2c_hw->CTRLB.reg &= ~SERCOM_I2CS_CTRLB_ACKACT; i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3); i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_AMATCH; } if (last) { /* Set action to nack. */ i2c_hw->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; } else { /* Set action to ack. */ i2c_hw->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; } /* Wait for next byte or stop condition */ tmp_status = _i2c_slave_wait_for_bus(&pI2C_S(obj)->slave); if (tmp_status != STATUS_OK) { /* Timeout, return */ return -1; } if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC) { /* Master sent stop condition, or repeated start, read done */ /* Clear stop flag */ i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; return -1; } /* Read data */ _i2c_slave_wait_for_sync(&pI2C_S(obj)->slave); data = i2c_hw->DATA.reg; #endif } return data; } /** Write one byte. * @param obj The i2c object * @param data Byte to be written * @return 1 if NAK was received, 0 if ACK was received, 2 for timeout. */ int i2c_byte_write(i2c_t *obj, int data) { /* Sanity check arguments */ MBED_ASSERT(obj); MBED_ASSERT(pI2C_S(obj)->master.hw); enum status_code tmp_status; data = data & 0xFF; if (pI2C_S(obj)->mode == I2C_MODE_MASTER) { SercomI2cm *const i2c_module = &(pI2C_S(obj)->master.hw->I2CM); if (pI2C_S(obj)->start_pending) { pI2C_S(obj)->start_pending = 0; /* Write address */ return i2c_write_address(obj, (data >> 1), (data & 0x01)); } else { /* Write data */ /* Check that bus ownership is not lost. */ if (!(i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE(2))) { return I2C_ERROR_NO_SLAVE; } /* Write byte to slave. */ _i2c_master_wait_for_sync(&pI2C_S(obj)->master); i2c_module->DATA.reg = data; /* Wait for response. */ tmp_status = _i2c_master_wait_for_bus(&pI2C_S(obj)->master); /* Check for error. */ if (tmp_status != STATUS_OK) { return I2C_ERROR_BUS_BUSY; } /* Check for NACK from slave. */ if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) { /* Return bad data value. */ return I2C_ERROR_NO_SLAVE; } } } else { #if DEVICE_I2CSLAVE SercomI2cs *const i2c_hw = &(pI2C_S(obj)->slave.hw->I2CS); if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH) { /* Read request from master, Address not yet acknowledged */ i2c_hw->CTRLB.reg &= ~SERCOM_I2CS_CTRLB_ACKACT; i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3); i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_AMATCH; } /* Write data */ _i2c_slave_wait_for_sync(&pI2C_S(obj)->slave); i2c_hw->DATA.reg = data; /* Wait for response from master */ tmp_status = _i2c_slave_wait_for_bus(&pI2C_S(obj)->slave); if (tmp_status != STATUS_OK) { /* Timeout, return */ return I2C_ERROR_BUS_BUSY; } if (i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_RXNACK) { /* NACK from master, abort */ /* Release line */ i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x02); return I2C_ERROR_NO_SLAVE; } #endif } return 0; } #if DEVICE_I2CSLAVE /** * \defgroup SynchI2C Synchronous I2C Hardware Abstraction Layer for slave * @{ */ /** Configure I2C as slave or master. * @param obj The I2C object * @param enable_slave configure I2C in slave mode or not * @return void */ void i2c_slave_mode(i2c_t *obj, int enable_slave) { int i; /* Sanity check arguments */ MBED_ASSERT(obj); MBED_ASSERT(pI2C_S(obj)->master.hw); uint32_t mux_func[2]; uint32_t sercom_index = _sercom_get_sercom_inst_index(pI2C_S(obj)->master.hw); for (i=0; i<2; i++) { mux_func[i] = pinmap_function_sercom(pI2C_S(obj)->pins[0], sercom_index); if (mux_func[i] == (uint32_t)NC) return; } if (enable_slave) { /* Disable I2C Master module if active */ i2c_master_disable(&pI2C_S(obj)->master); struct i2c_slave_config config_i2c_slave; i2c_slave_get_config_defaults(&config_i2c_slave); /* SERCOM PAD0 - SDA */ config_i2c_slave.pinmux_pad0 = (pI2C_S(obj)->pins[0] << 16) | (mux_func[0] & 0xFFFF); /* SERCOM PAD1 - SCL */ config_i2c_slave.pinmux_pad1 = (pI2C_S(obj)->pins[1] << 16) | (mux_func[1] & 0xFFFF); i2c_slave_init(&pI2C_S(obj)->slave, pI2C_S(obj)->master.hw, &config_i2c_slave); pI2C_S(obj)->mode = I2C_MODE_SLAVE; i2c_slave_enable(&pI2C_S(obj)->slave); } else { if ((pI2C_S(obj)->master.hw) && (pI2C_S(obj)->mode == I2C_MODE_MASTER)) { /* Already configured, enable and return */ i2c_master_enable(&pI2C_S(obj)->master); return; } else if ((pI2C_S(obj)->slave.hw) && (pI2C_S(obj)->mode == I2C_MODE_SLAVE)) { /* Disable slave */ i2c_slave_disable(&pI2C_S(obj)->slave); } struct i2c_master_config config_i2c_master; /* SERCOM PAD0 - SDA */ config_i2c_master.pinmux_pad0 = (pI2C_S(obj)->pins[0] << 16) | (mux_func[0] & 0xFFFF); /* SERCOM PAD1 - SCL */ config_i2c_master.pinmux_pad1 = (pI2C_S(obj)->pins[1] << 16) | (mux_func[1] & 0xFFFF); /* Baud rate */ config_i2c_master.baud_rate = pI2C_S(obj)->baud_rate / 1000; while(i2c_master_init(&pI2C_S(obj)->master, pI2C_S(obj)->master.hw, &config_i2c_master) != STATUS_OK); pI2C_S(obj)->mode = I2C_MODE_MASTER; i2c_master_enable(&pI2C_S(obj)->master); } } /** Check to see if the I2C slave has been addressed. * @param obj The I2C object * @return The status - 1 - read addresses, 2 - write to all slaves, * 3 write addressed, 0 - the slave has not been addressed */ int i2c_slave_receive(i2c_t *obj) { /* Sanity check arguments */ MBED_ASSERT(obj); MBED_ASSERT(pI2C_S(obj)->slave.hw); SercomI2cs *const i2c_module = &(pI2C_S(obj)->slave.hw->I2CS); if (i2c_module->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH) { if (i2c_module->STATUS.reg & SERCOM_I2CS_STATUS_DIR) { /* Slave is read addressed */ return 1; } else { if (!(i2c_module->DATA.reg & 0xFF)) { /* General call address detected */ return 2; } else { /* Slave is write addressed */ return 3; } } } return 0; } /** Blocking reading data. * @param obj The i2c slave object * @param data The buffer for receiving * @param length Number of bytes to read * @return Number of read bytes */ int i2c_slave_read(i2c_t *obj, char *data, int length) { /* Sanity check arguments */ MBED_ASSERT(obj); MBED_ASSERT(pI2C_S(obj)->slave.hw); if (!data || !length) return 0; enum status_code tmp_status; struct i2c_slave_packet packet; packet.data_length = length; packet.data = (uint8_t*)data; tmp_status = i2c_slave_read_packet_wait(&pI2C_S(obj)->slave, &packet); if (tmp_status == STATUS_OK) { return length; } else { /* Currently, no way to track no of bytes transmitted, so return 0 */ return 0; } } /** Blocking writing data. * @param obj The i2c slave object * @param data The buffer for transmitting * @param length Number of bytes to write * @return Number of bytes written */ int i2c_slave_write(i2c_t *obj, const char *data, int length) { /* Sanity check arguments */ MBED_ASSERT(obj); MBED_ASSERT(pI2C_S(obj)->slave.hw); if (!data || !length) return 0; enum status_code tmp_status; struct i2c_slave_packet packet; packet.data_length = length; packet.data = (uint8_t *)data; tmp_status = i2c_slave_write_packet_wait(&pI2C_S(obj)->slave, &packet); if (tmp_status == STATUS_OK) { return length; } else { /* Currently, no way to track no of bytes transmitted, so return 0 */ return 0; } } /** Configure I2C slave address. * @param obj The I2C object * @param idx Currently not used * @param address The address to be set * @param mask Currently not used */ void i2c_slave_address(i2c_t *obj, int idx, uint32_t address, uint32_t mask) { /* Sanity check arguments */ MBED_ASSERT(obj); MBED_ASSERT(pI2C_S(obj)->slave.hw); /* Disable I2C Module */ i2c_slave_disable(&pI2C_S(obj)->slave); SercomI2cs *const i2c_hw = &(pI2C_S(obj)->slave.hw->I2CS); address = (address & 0xFF) >> 1; if (!mask) { mask = (0xFE >> 1); } /* Set the address and address mask */ i2c_hw->ADDR.reg = (address << SERCOM_I2CS_ADDR_ADDR_Pos) | (mask << SERCOM_I2CS_ADDR_ADDRMASK_Pos) | (0 << SERCOM_I2CS_ADDR_TENBITEN_Pos) | (1 << SERCOM_I2CS_ADDR_GENCEN_Pos); /* Enable I2C Module */ i2c_slave_enable(&pI2C_S(obj)->slave); } #endif /* DEVICE_I2CSLAVE */ /**@}*/ #if DEVICE_I2C_ASYNCH /** * \defgroup AsynchI2C Asynchronous I2C Hardware Abstraction Layer * @{ */ /** * \internal * Callback for transfer finish. * * \param[in,out] module Pointer to SPI software instance struct */ void i2c_transfer_complete_callback(struct i2c_master_module *const module) { uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); if (sercom_index >= SERCOM_INST_NUM) { /* TODO: Abort operation */ return; } i2c_t *obj = (i2c_t*)i2c_instances[sercom_index]; i2c_master_disable_callback(&pI2C_S(obj)->master, I2C_MASTER_CALLBACK_WRITE_COMPLETE); i2c_master_disable_callback(&pI2C_S(obj)->master, I2C_MASTER_CALLBACK_READ_COMPLETE); i2c_master_disable_callback(&pI2C_S(obj)->master, I2C_MASTER_CALLBACK_ERROR); /* Call the handler function */ if (pI2C_S(obj)->handler) { ((I2CHandler)pI2C_S(obj)->handler)(); } } /** * \internal * Callback for write complete. Initiate read from here. * * \param[in,out] module Pointer to SPI software instance struct */ void i2c_write_complete_callback(struct i2c_master_module *const module) { uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); if (sercom_index >= SERCOM_INST_NUM) { /* TODO: Abort operation */ return; } i2c_t *obj = (i2c_t*)i2c_instances[sercom_index]; if (!(pI2C_S(obj)->rd_packet.data) || (pI2C_S(obj)->rd_packet.data_length == 0)) { /* Call the handler function */ i2c_transfer_complete_callback(module); } else { i2c_master_disable_callback(&pI2C_S(obj)->master, I2C_MASTER_CALLBACK_WRITE_COMPLETE); /* Register read complete callback */ i2c_master_register_callback(&pI2C_S(obj)->master, i2c_transfer_complete_callback, I2C_MASTER_CALLBACK_READ_COMPLETE); i2c_master_enable_callback(&pI2C_S(obj)->master, I2C_MASTER_CALLBACK_READ_COMPLETE); /* Initiate read operation */ if (pI2C_S(obj)->master.send_stop) { i2c_master_read_packet_job(&pI2C_S(obj)->master,&pI2C_S(obj)->rd_packet); } else { i2c_master_read_packet_job_no_stop(&pI2C_S(obj)->master, &pI2C_S(obj)->rd_packet); } } } /** Start i2c asynchronous transfer. * @param obj The I2C object * @param tx The buffer to send * @param tx_length The number of words to transmit * @param rx The buffer to receive * @param rx_length The number of words to receive * @param address The address to be set - 7bit or 9 bit * @param stop If true, stop will be generated after the transfer is done * @param handler The I2C IRQ handler to be set * @param hint DMA hint usage */ void i2c_transfer_asynch(i2c_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint32_t address, uint32_t stop, uint32_t handler, uint32_t event, DMAUsage hint) { /* Sanity check arguments */ MBED_ASSERT(obj); MBED_ASSERT(pI2C_S(obj)->master.hw); /* Return if in Slave mode */ if (pI2C_S(obj)->mode != I2C_MODE_MASTER) return; uint32_t sercom_index = _sercom_get_sercom_inst_index(pI2C_S(obj)->master.hw); /* Init i2c packet. */ pI2C_S(obj)->wr_packet.address = address >> 1; pI2C_S(obj)->wr_packet.data_length = tx_length; pI2C_S(obj)->wr_packet.data = (uint8_t *)tx; pI2C_S(obj)->rd_packet.address = address >> 1; pI2C_S(obj)->rd_packet.data_length = rx_length; pI2C_S(obj)->rd_packet.data = rx; /* Save event mask and handler function pointer */ pI2C_S(obj)->events = event; pI2C_S(obj)->handler = handler; /* TODO: Current implementation is interrupt based only */ /* Set interrupt handler to default handler of ASF */ /* Enable interrupt */ NVIC_SetVector((IRQn_Type)((uint32_t)SERCOM0_IRQn + sercom_index), sercom_irq_handlers[sercom_index]); NVIC_EnableIRQ((IRQn_Type)((uint32_t)SERCOM0_IRQn + sercom_index)); /* Register callbacks */ i2c_master_register_callback(&pI2C_S(obj)->master, i2c_transfer_complete_callback, I2C_MASTER_CALLBACK_ERROR); i2c_master_enable_callback(&pI2C_S(obj)->master, I2C_MASTER_CALLBACK_ERROR); if (tx && tx_length) { i2c_master_register_callback(&pI2C_S(obj)->master, i2c_write_complete_callback, I2C_MASTER_CALLBACK_WRITE_COMPLETE); i2c_master_enable_callback(&pI2C_S(obj)->master, I2C_MASTER_CALLBACK_WRITE_COMPLETE); /* Start I2C write */ if (stop) { i2c_master_write_packet_job(&pI2C_S(obj)->master, &pI2C_S(obj)->wr_packet); } else { i2c_master_write_packet_job_no_stop(&pI2C_S(obj)->master, &pI2C_S(obj)->wr_packet); } } else if (rx && rx_length) { i2c_master_register_callback(&pI2C_S(obj)->master, i2c_transfer_complete_callback, I2C_MASTER_CALLBACK_READ_COMPLETE); i2c_master_enable_callback(&pI2C_S(obj)->master, I2C_MASTER_CALLBACK_READ_COMPLETE); /* Start I2C read */ if (stop) { i2c_master_read_packet_job(&pI2C_S(obj)->master,&pI2C_S(obj)->rd_packet); } else { i2c_master_read_packet_job_no_stop(&pI2C_S(obj)->master, &pI2C_S(obj)->rd_packet); } } else { /* Nothing to transfer, invoke callback */ i2c_transfer_complete_callback(&pI2C_S(obj)->master); } } /** The asynchronous IRQ handler * @param obj The I2C object which holds the transfer information * @return event flags if a transfer termination condition was met or 0 otherwise. */ uint32_t i2c_irq_handler_asynch(i2c_t *obj) { /* Sanity check arguments */ MBED_ASSERT(obj); MBED_ASSERT(pI2C_S(obj)->master.hw); uint32_t event_mask = pI2C_S(obj)->events; /* TODO: Current implementation is interrupt based only */ switch (pI2C_S(obj)->master.status) { case STATUS_OK: /* Transfer is complete */ return (I2C_EVENT_TRANSFER_COMPLETE & event_mask); case STATUS_ERR_BAD_ADDRESS: /* Received a NACK */ return (I2C_EVENT_ERROR_NO_SLAVE & event_mask); case STATUS_ERR_PACKET_COLLISION: /* An error occurred in between transfer */ return (I2C_EVENT_ERROR & event_mask); default: return 0; } //return 0; } /** Attempts to determine if I2C peripheral is already in use. * @param obj The I2C object * @return non-zero if the I2C module is active or zero if it is not */ uint8_t i2c_active(i2c_t *obj) { /* Sanity check arguments */ MBED_ASSERT(obj); MBED_ASSERT(pI2C_S(obj)->master.hw); return (pI2C_S(obj)->master.status == STATUS_BUSY); } /** Abort ongoing asynchronous transaction. * @param obj The I2C object */ void i2c_abort_asynch(i2c_t *obj) { /* Sanity check arguments */ MBED_ASSERT(obj); MBED_ASSERT(pI2C_S(obj)->master.hw); /* Pointer to the hardware module instance */ SercomI2cm *const i2c_module = &(pI2C_S(obj)->master.hw->I2CM); /* Abort ongoing job */ /* Stop packet operation */ i2c_module->INTENCLR.reg = SERCOM_I2CM_INTENCLR_MB | SERCOM_I2CM_INTENCLR_SB; pI2C_S(obj)->master.buffer_length = 0; pI2C_S(obj)->master.buffer_remaining = 0; /* Send nack and stop command unless arbitration is lost */ if ((pI2C_S(obj)->master.status != STATUS_ERR_PACKET_COLLISION) && pI2C_S(obj)->master.send_stop) { _i2c_master_wait_for_sync(&pI2C_S(obj)->master); i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT | SERCOM_I2CM_CTRLB_CMD(3); } /* Disable any registered callback */ i2c_master_disable_callback(&pI2C_S(obj)->master, I2C_MASTER_CALLBACK_WRITE_COMPLETE); i2c_master_disable_callback(&pI2C_S(obj)->master, I2C_MASTER_CALLBACK_READ_COMPLETE); i2c_master_disable_callback(&pI2C_S(obj)->master, I2C_MASTER_CALLBACK_ERROR); pI2C_S(obj)->master.status = STATUS_ABORTED; } #endif