|
|
@@ -0,0 +1,381 @@
|
|
|
+/*
|
|
|
+ * Copyright (c) 2024, Texas Instruments Incorporated
|
|
|
+ * All rights reserved.
|
|
|
+ *
|
|
|
+ * Redistribution and use in source and binary forms, with or without
|
|
|
+ * modification, are permitted provided that the following conditions
|
|
|
+ * are met:
|
|
|
+ *
|
|
|
+ * * Redistributions of source code must retain the above copyright
|
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
|
+ *
|
|
|
+ * * Redistributions in binary form must reproduce the above copyright
|
|
|
+ * notice, this list of conditions and the following disclaimer in the
|
|
|
+ * documentation and/or other materials provided with the distribution.
|
|
|
+ *
|
|
|
+ * * Neither the name of Texas Instruments Incorporated nor the names of
|
|
|
+ * its contributors may be used to endorse or promote products derived
|
|
|
+ * from this software without specific prior written permission.
|
|
|
+ *
|
|
|
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
|
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
|
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
|
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
|
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
|
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
|
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
|
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
|
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
+ */
|
|
|
+
|
|
|
+#include "i2c_comm_controller.h"
|
|
|
+#include <stdio.h>
|
|
|
+#include<stdint.h>
|
|
|
+
|
|
|
+#define LEFT_SHIFT_8(x) ((x) << 8)
|
|
|
+#define RIGHT_SHIFT_8(x) ((x) >> 8)
|
|
|
+
|
|
|
+
|
|
|
+/* Function Declarations */
|
|
|
+/**
|
|
|
+ * @brief Calculates CRC 16 for an array of 8-bit data
|
|
|
+ * @param[in] ptr Pointer to the data to compute CRC
|
|
|
+ * @param[in] size size of the array of data in bytes
|
|
|
+ * @return Returns the CRC value
|
|
|
+ */
|
|
|
+static uint16_t CRC_calc16(uint8_t* ptr,uint8_t size);
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Prepares I2C Tx FIFO and issues I2C write command
|
|
|
+ * @param[in] I2C_handle Pointer to I2C_Instance
|
|
|
+ * @param[in] targetAddr I2C Target Address
|
|
|
+ */
|
|
|
+static void I2C_sendBuffer(I2C_Instance *I2C_handle,uint8_t targetAddr);
|
|
|
+
|
|
|
+
|
|
|
+/* Helper Functions */
|
|
|
+/**
|
|
|
+ * @brief 32 bit to 8 bit
|
|
|
+ * @param[in] b pointer to unsigned 8 bit output
|
|
|
+ * @param[in] u32 unsigned 32 bit input
|
|
|
+ */
|
|
|
+static void u8From32(uint8_t *b, uint32_t u32)
|
|
|
+{
|
|
|
+ b[0] = (uint8_t)u32;
|
|
|
+ b[1] = (uint8_t)(u32 = RIGHT_SHIFT_8(u32));
|
|
|
+ b[2] = (uint8_t)(u32 = RIGHT_SHIFT_8(u32));
|
|
|
+ b[3] = (uint8_t)(u32 = RIGHT_SHIFT_8(u32));
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief 16 bit to 8 bit
|
|
|
+ * @param[in] b pointer to unsigned 8 bit output
|
|
|
+ * @param[in] u16 unsigned 16 bit input
|
|
|
+ */
|
|
|
+static void u8From16(uint8_t *b, uint16_t u16)
|
|
|
+{
|
|
|
+ b[0] = (uint8_t)u16;
|
|
|
+ b[1] = (uint8_t)(u16 = RIGHT_SHIFT_8(u16));
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief 8 bit to 32 bit
|
|
|
+ * @param[in] b pointer to unsigned 8 bit output
|
|
|
+ * @return 32 bit result
|
|
|
+ */
|
|
|
+static uint32_t u32(uint8_t *b)
|
|
|
+{
|
|
|
+ uint32_t u;
|
|
|
+ u = b[3];
|
|
|
+ u = LEFT_SHIFT_8(u) + b[2];
|
|
|
+ u = LEFT_SHIFT_8(u) + b[1];
|
|
|
+ u = LEFT_SHIFT_8(u) + b[0];
|
|
|
+ return u;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief 8 bit to 16 bit
|
|
|
+ * @param[in] b pointer to unsigned 8 bit output
|
|
|
+ * @return 16 bit result
|
|
|
+ */
|
|
|
+static uint16_t u16(uint8_t *b)
|
|
|
+{
|
|
|
+ uint16_t u;
|
|
|
+ u = b[1];
|
|
|
+ u = LEFT_SHIFT_8(u) + b[0];
|
|
|
+ return u;
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef CONFIG_MSPM0G351X
|
|
|
+static uint16_t CRC_calc16(uint8_t* ptr,uint8_t size)
|
|
|
+{
|
|
|
+ uint8_t remainder = (size & 1);
|
|
|
+ uint16_t size16 = size>>1;
|
|
|
+
|
|
|
+ uint16_t checkSum = DL_CRCP_calculateBlock16(CRCP0,CRCP_SEED,
|
|
|
+ (uint16_t*)ptr,size16);
|
|
|
+
|
|
|
+ if(remainder)
|
|
|
+ {
|
|
|
+ DL_CRCP_feedData8(CRCP0,ptr[size - 1]);
|
|
|
+ checkSum = ((uint16_t) DL_CRCP_getResult16(CRCP0));
|
|
|
+ }
|
|
|
+
|
|
|
+ return checkSum;
|
|
|
+}
|
|
|
+
|
|
|
+#else
|
|
|
+static uint16_t CRC_calc16(uint8_t* ptr,uint8_t size)
|
|
|
+{
|
|
|
+ uint8_t remainder = (size & 1);
|
|
|
+ uint16_t size16 = size>>1;
|
|
|
+
|
|
|
+ uint16_t checkSum = DL_CRC_calculateBlock16(CRC,CRC_SEED,
|
|
|
+ (uint16_t*)ptr,size16);
|
|
|
+
|
|
|
+ if(remainder)
|
|
|
+ {
|
|
|
+ DL_CRC_feedData8(CRC,ptr[size - 1]);
|
|
|
+ checkSum = ((uint16_t) DL_CRC_getResult16(CRC));
|
|
|
+ }
|
|
|
+
|
|
|
+ return checkSum;
|
|
|
+}
|
|
|
+
|
|
|
+#endif
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Resets pointer and length of the buffer
|
|
|
+ * @param[in] frame Pointer to BufferInfo (Rx,Tx)
|
|
|
+ */
|
|
|
+__STATIC_INLINE void I2C_clearBuffer(BufferInfo *frame)
|
|
|
+{
|
|
|
+ frame->ptr = 0;
|
|
|
+ frame->len = 0;
|
|
|
+}
|
|
|
+/*Initialize the I2C instance structure:
|
|
|
+* Clears the Receive and Tramnsmit Buffer
|
|
|
+* Resets data length to 0
|
|
|
+* Disables CRC
|
|
|
+* Sets the initial I2C status to IDLE
|
|
|
+* Resets Error Status
|
|
|
+*/
|
|
|
+void I2C_init(I2C_Instance *I2C_handle)
|
|
|
+{
|
|
|
+ I2C_clearBuffer(&I2C_handle->rxMsg);
|
|
|
+ I2C_clearBuffer(&I2C_handle->txMsg);
|
|
|
+
|
|
|
+ I2C_handle->dataLen = 0;
|
|
|
+ I2C_handle->isCrc = false;
|
|
|
+ I2C_handle->status = I2C_STATUS_IDLE;
|
|
|
+ I2C_handle->error = ERROR_TYPE_NONE;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+Send I2C command:
|
|
|
+* Resets the transmit buffer to 0: Prepares for new data
|
|
|
+* (command->commandType): Specifies whether the command is READ or WRITE
|
|
|
+* (command->crcEnable ? CRC_MASK : (0x00)): Adds a CRC FLag if enabled
|
|
|
+* ((command->dataSize - 1) & LEN_MASK) : encodes the data size in control byte
|
|
|
+* I2C_handle->txMsg.ptr += 4 -> moves the pointer forward 4 bytes (since address is 32 bits)
|
|
|
+* READ operation expects dataSize
|
|
|
+* Loops through the command array and stores it to the buffer.
|
|
|
+* I2C_handle->txMsg.len = I2C_handle->txMsg.ptr -> final length of the TX message
|
|
|
+*/
|
|
|
+void I2C_sendCommand(I2C_Instance *I2C_handle,I2C_CommandInfo *command)
|
|
|
+{
|
|
|
+ /* Prepare TX Buffer */
|
|
|
+ I2C_handle->txMsg.ptr = 0;
|
|
|
+
|
|
|
+ /* Control Byte */
|
|
|
+ I2C_handle->txMsg.buffer[I2C_handle->txMsg.ptr++] = (command->commandType) | (command->crcEnable ? CRC_MASK : (0x00)) | ((command->dataSize - 1) & LEN_MASK) ;
|
|
|
+
|
|
|
+ u8From32(&I2C_handle->txMsg.buffer[I2C_handle->txMsg.ptr],command->addr);
|
|
|
+ I2C_handle->txMsg.ptr += 4;
|
|
|
+
|
|
|
+ /* For Read Command */
|
|
|
+ I2C_handle->dataLen = command->dataSize;
|
|
|
+
|
|
|
+ /* For Write Command */
|
|
|
+ if(command->commandType == WRITE_COMMAND)
|
|
|
+ {
|
|
|
+ I2C_handle->dataLen = 1;
|
|
|
+
|
|
|
+ for(int i = 0 ; i < command->dataSize ; i++)
|
|
|
+ {
|
|
|
+ I2C_handle->txMsg.buffer[I2C_handle->txMsg.ptr++] = command->dataArray[i];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* CRC */
|
|
|
+ I2C_handle->isCrc = false;
|
|
|
+ if(command->crcEnable)
|
|
|
+ {
|
|
|
+ I2C_handle->isCrc = true;
|
|
|
+ uint16_t Crc = CRC_calc16((&I2C_handle->txMsg.buffer[0]),I2C_handle->txMsg.ptr);
|
|
|
+ u8From16(&I2C_handle->txMsg.buffer[I2C_handle->txMsg.ptr],Crc);
|
|
|
+ I2C_handle->txMsg.ptr += 2;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ I2C_handle->txMsg.len = I2C_handle->txMsg.ptr;
|
|
|
+
|
|
|
+ I2C_sendBuffer(I2C_handle, command->targetAddr);
|
|
|
+
|
|
|
+ /* Wait until I2C Controller is idle */
|
|
|
+ while (!(
|
|
|
+ DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_IDLE))
|
|
|
+ ;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static void I2C_sendBuffer(I2C_Instance *I2C_handle,uint8_t targetAddr)
|
|
|
+{
|
|
|
+ I2C_handle->txMsg.ptr = 0;
|
|
|
+
|
|
|
+ I2C_handle->txMsg.ptr = DL_I2C_fillControllerTXFIFO(I2C_controller_INST, &I2C_handle->txMsg.buffer[0], I2C_handle->txMsg.len);
|
|
|
+
|
|
|
+ /* Enable TXFIFO trigger interrupt if there are more bytes to send */
|
|
|
+ if (I2C_handle->txMsg.ptr < I2C_handle->txMsg.len)
|
|
|
+ {
|
|
|
+ DL_I2C_enableInterrupt(
|
|
|
+ I2C_controller_INST, DL_I2C_INTERRUPT_CONTROLLER_TXFIFO_TRIGGER);
|
|
|
+ } else
|
|
|
+ {
|
|
|
+ DL_I2C_disableInterrupt(
|
|
|
+ I2C_controller_INST, DL_I2C_INTERRUPT_CONTROLLER_TXFIFO_TRIGGER);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ I2C_handle->status = I2C_STATUS_TX_STARTED;
|
|
|
+
|
|
|
+ /* Wait until I2C Controller is idle */
|
|
|
+ while (!(
|
|
|
+ DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_IDLE))
|
|
|
+ ;
|
|
|
+
|
|
|
+ DL_I2C_startControllerTransfer(
|
|
|
+ I2C_controller_INST, targetAddr, DL_I2C_CONTROLLER_DIRECTION_TX, I2C_handle->txMsg.len);
|
|
|
+
|
|
|
+
|
|
|
+ // added to avoid infinite wait...
|
|
|
+ /* Wait until the Controller sends all bytes */
|
|
|
+ int timeout = 100000;
|
|
|
+ while ((I2C_handle->status != I2C_STATUS_TX_COMPLETE) &&
|
|
|
+ (I2C_handle->status != I2C_STATUS_ERROR) && (--timeout)) {
|
|
|
+ printf("Wait till the bus is idle.\n");
|
|
|
+ __WFE();
|
|
|
+ }
|
|
|
+ /* Handle timeout error */
|
|
|
+ if (timeout == 0) {
|
|
|
+ printf("ERROR: Timeout waiting for I2C transmission to complete!\n");
|
|
|
+ I2C_handle->status = I2C_STATUS_ERROR;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Wait until I2C bus is Idle */
|
|
|
+ while (DL_I2C_getControllerStatus(I2C_controller_INST) &
|
|
|
+ DL_I2C_CONTROLLER_STATUS_BUSY_BUS)
|
|
|
+ ;
|
|
|
+
|
|
|
+
|
|
|
+ /* Trap if there was an error */
|
|
|
+ if (DL_I2C_getControllerStatus(I2C_controller_INST) &
|
|
|
+ DL_I2C_CONTROLLER_STATUS_ERROR) {
|
|
|
+ //Testing for error trap:
|
|
|
+ uint32_t status = DL_I2C_getControllerStatus(I2C_controller_INST);
|
|
|
+ printf("I2C ERROR: Controller status = 0x%X\n", status);
|
|
|
+ __BKPT(0);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+This function returns and receives the data from the I2C peripheral.
|
|
|
+The total expected length of the response is calculated as:
|
|
|
+* CTRL_SIZE: The control byte size
|
|
|
+* I2C_handle->dataLen: The data length that is expected in response
|
|
|
+* (I2C_handle->isCrc ? CRC_SIZE : 0) : if the CRC checking is enabled, add the CRC_Size to the expected length
|
|
|
+* Calls DL_I2C_startControllerTransfer to start an I2C read transaction.
|
|
|
+ * I2C_controller_INST: Reference to the the I2C controller instance
|
|
|
+ * targetAddr: I2C address of the device
|
|
|
+ * DL_I2C_CONTROLLER_DIRECTION_RX: indicates this is a READ operation
|
|
|
+ * expectedLen: The no. of bytes expected in the response
|
|
|
+* Waits until data reception is complete
|
|
|
+* Retries if a NACK error occurs
|
|
|
+* Wait for the bus to become idle.
|
|
|
+*/
|
|
|
+void I2C_getResponse(I2C_Instance* I2C_handle,uint32_t targetAddr)
|
|
|
+{
|
|
|
+
|
|
|
+ uint32_t expectedLen = CTRL_SIZE + I2C_handle->dataLen + (I2C_handle->isCrc ? CRC_SIZE : 0);
|
|
|
+
|
|
|
+ DL_I2C_startControllerTransfer(
|
|
|
+ I2C_controller_INST, targetAddr, DL_I2C_CONTROLLER_DIRECTION_RX, expectedLen);
|
|
|
+
|
|
|
+ if (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_ERROR){
|
|
|
+ printf("ERROR: ADC(0x%X) not responding.\n", targetAddr);
|
|
|
+ return;
|
|
|
+ }else{
|
|
|
+ printf("ADC(0x%X) acknowledged.\n",targetAddr);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /* Wait for all bytes to be received in interrupt */
|
|
|
+ while (I2C_handle->status != I2C_STATUS_RX_COMPLETE)
|
|
|
+ {
|
|
|
+ /* Resend the Read Command if it is N'ACKed */
|
|
|
+ if(I2C_handle->status == I2C_STATUS_ERROR)
|
|
|
+ {
|
|
|
+ I2C_handle->status = I2C_STATUS_RX_STARTED;
|
|
|
+ delay_cycles(10000);
|
|
|
+ DL_I2C_startControllerTransfer(
|
|
|
+ I2C_controller_INST, targetAddr, DL_I2C_CONTROLLER_DIRECTION_RX, expectedLen);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Wait until I2C bus is Idle */
|
|
|
+ while (DL_I2C_getControllerStatus(I2C_controller_INST) &
|
|
|
+ DL_I2C_CONTROLLER_STATUS_BUSY_BUS)
|
|
|
+ ;
|
|
|
+}
|
|
|
+
|
|
|
+void I2C_decodeResponse(I2C_Instance *I2C_handle,I2C_ResponseInfo *response)
|
|
|
+{
|
|
|
+ response->received = true;
|
|
|
+
|
|
|
+ /* Length stored in frame is 1 less than actual length */
|
|
|
+ response->dataSize = ( I2C_handle->rxMsg.buffer[CTRL_IDX] & LEN_MASK ) + 1;
|
|
|
+
|
|
|
+ response->status = ERROR_TYPE_NONE;
|
|
|
+
|
|
|
+ if(I2C_handle->rxMsg.buffer[CTRL_IDX] & ERROR_MASK)
|
|
|
+ {
|
|
|
+ /* Store error code sent by target */
|
|
|
+ response->status = (ErrorType) I2C_handle->rxMsg.buffer[RESP_DATA_IDX];
|
|
|
+ }
|
|
|
+
|
|
|
+ response->frame.ctrl = I2C_handle->rxMsg.buffer[CTRL_IDX];
|
|
|
+
|
|
|
+ for(int i = 0; i < MAX_DATA_SIZE; i++)
|
|
|
+ {
|
|
|
+ if(i < response->dataSize)
|
|
|
+ {
|
|
|
+ response->frame.data[i] = I2C_handle->rxMsg.buffer[RESP_DATA_IDX + i];
|
|
|
+ }
|
|
|
+ /* Resetting extra space to zero*/
|
|
|
+ else
|
|
|
+ {
|
|
|
+ response->frame.data[i] = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(I2C_handle->isCrc)
|
|
|
+ {
|
|
|
+ response->frame.crc = u16(&I2C_handle->rxMsg.buffer[I2C_handle->rxMsg.len - CRC_SIZE]);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|