/** * \file * \brief ATCA Hardware abstraction layer for PIC32MX695F512H I2C over plib drivers. * * This code is structured in two parts. Part 1 is the connection of the ATCA HAL API to the physical I2C * implementation. Part 2 is the xxx I2C primitives to set up the interface. * * Prerequisite: * * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. * * \page License * * Subject to your compliance with these terms, you may use Microchip software * and any derivatives exclusively with Microchip products. It is your * responsibility to comply with third party license terms applicable to your * use of third party software (including open source software) that may * accompany Microchip software. * * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR * THIS SOFTWARE. */ #include #include #include #include "hal/atca_hal.h" #include "hal/hal_pic32mx695f512h_i2c.h" /** * \defgroup hal_ Hardware abstraction layer (hal_) * * \brief * These methods define the hardware abstraction layer for communicating with a CryptoAuth device * @{ */ /** * \brief * Logical to physical bus mapping structure */ static ATCAI2CMaster_t i2c_hal_data[MAX_I2C_BUSES]; // map logical, 0-based bus number to index /****** I2C Driver implementation *******/ static bool StartTransfer(I2C_MODULE i2c_id, bool restart) { I2C_STATUS status; // Send the Start (or Restart) signal if (restart) { I2CRepeatStart(i2c_id); } else { // Wait for the bus to be idle, then start the transfer while (!I2CBusIsIdle(i2c_id)) { ; } if (I2CStart(i2c_id) != I2C_SUCCESS) { //DBPRINTF("Error: Bus collision during transfer Start\n"); return FALSE; } } // Wait for the signal to complete do { status = I2CGetStatus(i2c_id); } while (!(status & I2C_START)); return TRUE; } static bool Check_slave_ACK(I2C_MODULE i2c_id) { bool status = false; switch (i2c_id) { case I2C1: if ((I2C1STATbits.ACKSTAT) != 1) { status = TRUE; } break; case I2C3: if ((I2C3STATbits.ACKSTAT) != 1) { status = TRUE; } break; default: status = false; } return status; } static bool TransmitOneByte(I2C_MODULE i2c_id, uint8_t data) { // Wait for the transmitter to be ready while (!I2CTransmitterIsReady(i2c_id)) { ; } // Transmit the byte if (I2CSendByte(i2c_id, data) == I2C_MASTER_BUS_COLLISION) { //DBPRINTF("Error: I2C Master Bus Collision\n"); return FALSE; } // Wait for the transmission to finish while (!I2CTransmissionHasCompleted(i2c_id)) { ; } return Check_slave_ACK(i2c_id); } static uint8_t ReceiveOneByte(I2C_MODULE i2c_id, bool ack) { uint8_t data; // Enable I2C receive I2CReceiverEnable(i2c_id, TRUE); // Wait until 1-byte is fully received while (!I2CReceivedDataIsAvailable(i2c_id)) { ; } // Save the byte received data = I2CGetByte(i2c_id); // Perform acknowledgement sequence I2CAcknowledgeByte(i2c_id, ack); // Wait until acknowledgement is successfully sent while (!I2CAcknowledgeHasCompleted(i2c_id)) { ; } return data; } static void StopTransfer(I2C_MODULE i2c_id) { I2C_STATUS status; // Send the Stop signal I2CStop(i2c_id); // Wait for the signal to complete do { status = I2CGetStatus(i2c_id); } while (!(status & I2C_STOP)); } void i2c_write(I2C_MODULE i2c_id, uint8_t address, uint8_t *data, int len) { uint8_t i2cBuffer[len + 1]; int i; i2cBuffer[0] = address | 0x00; memcpy(&i2cBuffer[1], data, len); if (!StartTransfer(i2c_id, FALSE)) { return; } for (i = 0; i < len + 1; i++) { if (!TransmitOneByte(i2c_id, i2cBuffer[i])) { break; } } StopTransfer(i2c_id); } ATCA_STATUS i2c_read(I2C_MODULE i2c_id, uint8_t address, uint8_t *data, uint16_t len) { uint16_t i; ATCA_STATUS status = !ATCA_SUCCESS; if (!StartTransfer(i2c_id, FALSE)) { return ATCA_COMM_FAIL; } if (!TransmitOneByte(i2c_id, (address | 0x01))) { StopTransfer(i2c_id); return ATCA_COMM_FAIL; } for (i = 0; i < len; i++) { if (i < len - 1) // send ACK { data[i] = ReceiveOneByte(i2c_id, TRUE); } else // send NACK { data[i] = ReceiveOneByte(i2c_id, FALSE); } } StopTransfer(i2c_id); return ATCA_SUCCESS; } /****************************************/ /** * \brief * This HAL implementation assumes you've included the Plib libraries in your project, otherwise, * the HAL layer will not compile because the Plib drivers are a dependency */ /** \brief discover i2c buses available for this hardware * this maintains a list of logical to physical bus mappings freeing the application * of the a-priori knowledge.This function is currently not implemented. * \param[in] i2c_buses - an array of logical bus numbers * \param[in] max_buses - maximum number of buses the app wants to attempt to discover * \return ATCA_UNIMPLEMENTED */ ATCA_STATUS hal_i2c_discover_buses(int i2c_buses[], int max_buses) { return ATCA_UNIMPLEMENTED; } /** \brief discover any CryptoAuth devices on a given logical bus number.This function is currently not implemented. * \param[in] bus_num - logical bus number on which to look for CryptoAuth devices * \param[out] cfg[] - pointer to head of an array of interface config structures which get filled in by this method * \param[out] *found - number of devices found on this bus * \return ATCA_UNIMPLEMENTED */ ATCA_STATUS hal_i2c_discover_devices(int bus_num, ATCAIfaceCfg cfg[], int *found) { return ATCA_UNIMPLEMENTED; } /** * \brief * hal_i2c_init manages requests to initialize a physical interface. It manages use counts so when an interface * has released the physical layer, it will disable the interface for some other use. * You can have multiple ATCAIFace instances using the same bus, and you can have multiple ATCAIFace instances on * multiple i2c buses, so hal_i2c_init manages these things and ATCAIFace is abstracted from the physical details. */ /** * \brief initialize an I2C interface using given config * * \param[in] hal - opaque ptr to HAL data * \param[in] cfg - interface configuration * * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS hal_i2c_init(void *hal, ATCAIfaceCfg *cfg) { if (cfg->atcai2c.bus >= MAX_I2C_BUSES) { return ATCA_COMM_FAIL; } ATCAI2CMaster_t* data = &i2c_hal_data[cfg->atcai2c.bus]; if (data->ref_ct <= 0) { // Bus isn't being used, enable it switch (cfg->atcai2c.bus) { // case 0: // data->id = I2C0; // break; case 1: data->id = I2C1; break; // case 2: // data->id = I2C2; // break; case 3: data->id = I2C3; break; default: return ATCA_COMM_FAIL; } // Set the I2C baudrate I2CSetFrequency(data->id, GetPeripheralClock(), cfg->atcai2c.baud); // Enable the I2C bus I2CEnable(data->id, TRUE); // store this for use during the release phase data->bus_index = cfg->atcai2c.bus; // buses are shared, this is the first instance data->ref_ct = 1; } else { // Bus is already is use, increment reference counter data->ref_ct++; } ((ATCAHAL_t*)hal)->hal_data = data; return ATCA_SUCCESS; } /** * \brief HAL implementation of I2C post init * * \param[in] iface instance * * \return ATCA_SUCCESS */ ATCA_STATUS hal_i2c_post_init(ATCAIface iface) { return ATCA_SUCCESS; } /** * \brief HAL implementation of I2C send over ASF * * \param[in] iface instance * \param[in] txdata pointer to space to bytes to send * \param[in] txlength number of bytes to send * * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS hal_i2c_send(ATCAIface iface, uint8_t *txdata, int txlength) { ATCAIfaceCfg *cfg = atgetifacecfg(iface); int bus = cfg->atcai2c.bus; txdata[0] = 0x03; // insert the Word Address Value, Command token txlength++; // account for word address value byte. i2c_write(i2c_hal_data[bus].id, cfg->atcai2c.slave_address, txdata, txlength); return ATCA_SUCCESS; } /** * \brief HAL implementation of I2C receive function for ASF I2C * \param[in] iface Device to interact with. * \param[out] rxdata Data received will be returned here. * \param[inout] rxlength As input, the size of the rxdata buffer. * As output, the number of bytes received. * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS hal_i2c_receive(ATCAIface iface, uint8_t *rxdata, uint16_t *rxlength) { ATCAIfaceCfg *cfg = atgetifacecfg(iface); int bus = cfg->atcai2c.bus; int count; int retries = 10; int status = !ATCA_SUCCESS; uint16_t rxdata_max_size = *rxlength; *rxlength = 0; if (rxdata_max_size < 1) { return ATCA_SMALL_BUFFER; } while (retries-- > 0 && status != ATCA_SUCCESS) { status = i2c_read(i2c_hal_data[bus].id, cfg->atcai2c.slave_address, rxdata, 1); } if (status != ATCA_SUCCESS) { return ATCA_COMM_FAIL; } if (rxdata[0] < ATCA_RSP_SIZE_MIN) { return ATCA_INVALID_SIZE; } if (rxdata[0] > rxdata_max_size) { return ATCA_SMALL_BUFFER; } count = rxdata[0] - 1; status = i2c_read(i2c_hal_data[bus].id, cfg->atcai2c.slave_address, &rxdata[1], count); if (status != ATCA_SUCCESS) { return ATCA_COMM_FAIL; } *rxlength = rxdata[0]; return ATCA_SUCCESS; } /** * \brief method to change the bus speed of I2C * * \param[in] iface interface on which to change bus speed * \param[in] speed baud rate (typically 100000 or 400000) */ void change_i2c_speed(ATCAIface iface, uint32_t speed) { ATCAIfaceCfg *cfg = atgetifacecfg(iface); int bus = cfg->atcai2c.bus; // Disable the I2C bus I2CEnable(i2c_hal_data[bus].id, FALSE); // Set the I2C baudrate I2CSetFrequency(i2c_hal_data[bus].id, GetPeripheralClock(), speed); // Enable the I2C bus I2CEnable(i2c_hal_data[bus].id, TRUE); } /** * \brief wake up CryptoAuth device using I2C bus * * \param[in] iface interface to logical device to wakeup * * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS hal_i2c_wake(ATCAIface iface) { ATCAIfaceCfg *cfg = atgetifacecfg(iface); int bus = cfg->atcai2c.bus; uint32_t bdrt = cfg->atcai2c.baud; uint8_t data[4]; if (bdrt != 100000) // if not already at 100KHz, change it { change_i2c_speed(iface, 100000); } // Send 0x00 as wake pulse i2c_write(i2c_hal_data[bus].id, 0x00, NULL, NULL); atca_delay_ms(3); // wait tWHI + tWLO which is configured based on device type and configuration structure //atca_delay_us(cfg->wake_delay); // if necessary, revert baud rate to what came in. if (bdrt != 100000) { change_i2c_speed(iface, cfg->atcai2c.baud); } i2c_read(i2c_hal_data[bus].id, cfg->atcai2c.slave_address, data, 4); return hal_check_wake(data, 4); } /** * \brief idle CryptoAuth device using I2C bus * * \param[in] iface interface to logical device to idle * * \return ATCA_SUCCESS */ ATCA_STATUS hal_i2c_idle(ATCAIface iface) { ATCAIfaceCfg *cfg = atgetifacecfg(iface); int bus = cfg->atcai2c.bus; uint8_t data[4]; data[0] = 0x02; // idle word address value i2c_write(i2c_hal_data[bus].id, cfg->atcai2c.slave_address, data, 1); return ATCA_SUCCESS; } /** * \brief sleep CryptoAuth device using I2C bus * * \param[in] iface interface to logical device to sleep * * \return ATCA_SUCESS */ ATCA_STATUS hal_i2c_sleep(ATCAIface iface) { ATCAIfaceCfg *cfg = atgetifacecfg(iface); int bus = cfg->atcai2c.bus; uint8_t data[4]; data[0] = 0x01; // idle word address value i2c_write(i2c_hal_data[bus].id, cfg->atcai2c.slave_address, data, 1); return ATCA_SUCCESS; } /** * \brief manages reference count on given bus and releases resource if no more refences exist * * \param[in] hal_data - opaque pointer to hal data structure - known only to the HAL implementation * * \return ATCA_SUCESS */ ATCA_STATUS hal_i2c_release(void *hal_data) { ATCAI2CMaster_t *hal = (ATCAI2CMaster_t*)hal_data; // if the use count for this bus has gone to 0 references, disable it. protect against an unbracketed release if (hal && --(hal->ref_ct) <= 0) { I2CEnable(hal->id, FALSE); hal->ref_ct = 0; } return ATCA_SUCCESS; } /** @} */