Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
33ba1246aThis file is pinned to the selected release. Download the original to open it in your design tool.
//*****************************************************************************
//
// smbus.c - SMBus protocol layer API.
//
// Copyright (c) 2010-2013 Texas Instruments Incorporated. All rights reserved.
// Software License Agreement
//
// Texas Instruments (TI) is supplying this software for use solely and
// exclusively on TI's microcontroller products. The software is owned by
// TI and/or its suppliers, and is protected under applicable copyright
// laws. You may not combine this software with "viral" open-source
// software in order to form a larger program.
//
// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES, FOR ANY REASON WHATSOEVER.
//
// This is part of revision 1.1 of the Tiva Utility Library.
//
//*****************************************************************************
#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_i2c.h"
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_sysctl.h"
#include "inc/hw_types.h"
#include "driverlib/debug.h"
#include "driverlib/interrupt.h"
#include "driverlib/i2c.h"
#include "driverlib/sw_crc.h"
#include "driverlib/sysctl.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/udma.h"
#include "utils/smbus.h"
//*****************************************************************************
//
//! \addtogroup smbus_api
//! @{
//
//*****************************************************************************
//*****************************************************************************
//
// The states for the master and slave interrupt handler state machines.
//
//*****************************************************************************
#define SMBUS_STATE_IDLE 0
#define SMBUS_STATE_SLAVE_POST_COMMAND 1
#define SMBUS_STATE_WRITE_BLOCK_SIZE 2
#define SMBUS_STATE_WRITE_NEXT 3
#define SMBUS_STATE_WRITE_FINAL 4
#define SMBUS_STATE_WRITE_DONE 5
#define SMBUS_STATE_READ_ONE 6
#define SMBUS_STATE_READ_FIRST 7
#define SMBUS_STATE_READ_BLOCK_SIZE 8
#define SMBUS_STATE_READ_NEXT 9
#define SMBUS_STATE_READ_FINAL 10
#define SMBUS_STATE_READ_WAIT 11
#define SMBUS_STATE_READ_PEC 12
#define SMBUS_STATE_READ_DONE 13
#define SMBUS_STATE_READ_ERROR_STOP 14
//*****************************************************************************
//
// Status flags for various instance-specific tasks.
//
//*****************************************************************************
#define FLAG_PEC 0
#define FLAG_PROCESS_CALL 1
#define FLAG_BLOCK_TRANSFER 2
#define FLAG_TRANSFER_IN_PROGRESS 3
#define FLAG_RAW_I2C 4
#define FLAG_ADDRESS_RESOLVED 5
#define FLAG_ADDRESS_VALID 6
#define FLAG_ARP 7
//*****************************************************************************
//
//! Enables Packet Error Checking (PEC).
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! This function enables the transmission and checking of a PEC byte in SMBus
//! transactions.
//!
//! \return None.
//
//*****************************************************************************
void
SMBusPECEnable(tSMBus *psSMBus)
{
//
// Set the PEC flag in the configuration structure.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC) = 1;
}
//*****************************************************************************
//
//! Disables Packet Error Checking (PEC).
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! This function disables the transmission and checking of a PEC byte in SMBus
//! transactions.
//!
//! \return None.
//
//*****************************************************************************
void
SMBusPECDisable(tSMBus *psSMBus)
{
//
// Clear the PEC flag in the configuration structure.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC) = 0;
}
//*****************************************************************************
//
//! Sets the ARP flag in the configuration structure.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! This function sets the Address Resolution Protocol (ARP) flag in the
//! configuration structure. This flag can be used to track the state of a
//! device during the ARP process.
//!
//! \return None.
//
//*****************************************************************************
void
SMBusARPEnable(tSMBus *psSMBus)
{
//
// Set the ARP flag in the configuration structure.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_ARP) = 1;
}
//*****************************************************************************
//
//! Clears the ARP flag in the configuration structure.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! This function clears the Address Resolution Protocol (ARP) flag in the
//! configuration structure. This flag can be used to track the state of a
//! device during the ARP process.
//!
//! \return None.
//
//*****************************************************************************
void
SMBusARPDisable(tSMBus *psSMBus)
{
//
// Clear the ARP flag in the configuration structure.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_ARP) = 0;
}
//*****************************************************************************
//
//! Returns the number of bytes in the receive buffer.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! This function returns the number of bytes in the active receive buffer.
//! It can be used to determine how many bytes have been received in the slave
//! receive or master block read configurations.
//!
//! \return Number of bytes in the buffer.
//
//*****************************************************************************
uint8_t
SMBusRxPacketSizeGet(tSMBus *psSMBus)
{
//
// Return the number of bytes received.
//
return(psSMBus->ui8RxIndex);
}
//*****************************************************************************
//
//! Returns the state of an SMBus transfer.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! This function returns the status of an SMBus transaction. It can be used
//! to determine whether a transfer is ongoing or complete.
//!
//! \return Returns \b SMBUS_TRANSFER_IN_PROGRESS if transfer is ongoing, or
//! \b SMBUS_TRANSFER_COMPLETE if transfer has completed.
//
//*****************************************************************************
tSMBusStatus
SMBusStatusGet(tSMBus *psSMBus)
{
//
// Check to see if there is an ongoing transfer.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS))
{
//
// If the flag is set, return in progress status.
//
return(SMBUS_TRANSFER_IN_PROGRESS);
}
//
// If the transfer complete flag is cleared, transfer is done.
//
else
{
//
// If the flag isn't set, return complete status.
//
return(SMBUS_TRANSFER_COMPLETE);
}
}
//*****************************************************************************
//
//! Encodes a UDID structure and address into SMBus-transferable byte order.
//!
//! \param pUDID specifies the structure to encode.
//! \param ui8Address specifies the address to send with the UDID (byte 17).
//! \param pui8Data specifies the location of the destination data buffer.
//!
//! This function takes a tSMBusUDID structure and re-orders the bytes so that
//! it can be transferred on the bus. The destination data buffer must contain
//! at least 17 bytes.
//!
//! \return None.
//
//*****************************************************************************
void
SMBusARPUDIDPacketEncode(tSMBusUDID *pUDID, uint8_t ui8Address,
uint8_t *pui8Data)
{
//
// Place data from the UDID structure and address into the data buffer
// using the correct MSB->LSB + address order.
//
pui8Data[0] = pUDID->ui8DeviceCapabilities;
pui8Data[1] = pUDID->ui8Version;
pui8Data[2] = (uint8_t)((pUDID->ui16VendorID & 0xff00) >> 8);
pui8Data[3] = (uint8_t)(pUDID->ui16VendorID & 0x00ff);
pui8Data[4] = (uint8_t)((pUDID->ui16DeviceID & 0xff00) >> 8);
pui8Data[5] = (uint8_t)(pUDID->ui16DeviceID & 0x00ff);
pui8Data[6] = (uint8_t)((pUDID->ui16Interface & 0xff00) >> 8);
pui8Data[7] = (uint8_t)(pUDID->ui16Interface & 0x00ff);
pui8Data[8] = (uint8_t)((pUDID->ui16SubSystemVendorID & 0xff00) >> 8);
pui8Data[9] = (uint8_t)(pUDID->ui16SubSystemVendorID & 0x00ff);
pui8Data[10] = (uint8_t)((pUDID->ui16SubSystemDeviceID & 0xff00) >> 8);
pui8Data[11] = (uint8_t)(pUDID->ui16SubSystemDeviceID & 0x00ff);
pui8Data[12] = (uint8_t)((pUDID->ui32VendorSpecificID & 0xff000000) >>
24);
pui8Data[13] = (uint8_t)((pUDID->ui32VendorSpecificID & 0x00ff0000) >>
16);
pui8Data[14] = (uint8_t)((pUDID->ui32VendorSpecificID & 0x0000ff00) >>
8);
pui8Data[15] = (uint8_t)(pUDID->ui32VendorSpecificID & 0x000000ff);
pui8Data[16] = ui8Address;
}
//*****************************************************************************
//
//! Decodes an SMBus packet into a UDID structure and address.
//!
//! \param pUDID specifies the structure that is updated with new data.
//! \param pui8Address specifies the location of the variable that holds the
//! the address sent with the UDID (byte 17).
//! \param pui8Data specifies the location of the source data.
//!
//! This function takes a data buffer and decodes it into a tSMBusUDID
//! structure and an address variable. It is assumed that there are 17 bytes
//! in the data buffer.
//!
//! \return None.
//
//*****************************************************************************
void
SMBusARPUDIDPacketDecode(tSMBusUDID *pUDID, uint8_t *pui8Address,
uint8_t *pui8Data)
{
//
// Populate the UDID structure with data from the input data buffer.
//
pUDID->ui8DeviceCapabilities = pui8Data[0];
pUDID->ui8Version = pui8Data[1];
pUDID->ui16VendorID = (uint16_t)((pui8Data[2] << 8) | pui8Data[3]);
pUDID->ui16DeviceID = (uint16_t)((pui8Data[4] << 8) | pui8Data[5]);
pUDID->ui16Interface = (uint16_t)((pui8Data[6] << 8) | pui8Data[7]);
pUDID->ui16SubSystemVendorID = (uint16_t)((pui8Data[8] << 8) |
pui8Data[9]);
pUDID->ui16SubSystemDeviceID = (uint16_t)((pui8Data[10] << 8) |
pui8Data[11]);
pUDID->ui32VendorSpecificID = (uint32_t)((pui8Data[12] << 24) |
(pui8Data[13] << 16) |
(pui8Data[14] << 8) |
pui8Data[15]);
//
// Populate the address.
//
*pui8Address = pui8Data[16];
}
//*****************************************************************************
//
//! Initiates a master Quick Command transfer to an SMBus slave.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param ui8TargetAddress specifies the slave address of the target device.
//! \param bData is the value of the single data bit sent to the slave.
//!
//! Quick Command is an SMBus protocol that sends a single data bit using the
//! I2C R/S bit. This function issues a single I2C transfer with the slave
//! address and data bit.
//!
//! This protocol does not support PEC. The PEC flag is explicitly cleared
//! within this function, so if PEC is enabled prior to calling it, it must
//! be re-enabled afterwards.
//!
//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently
//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if
//! the transfer has successfully been initiated.
//
//*****************************************************************************
tSMBusStatus
SMBusMasterQuickCommand(tSMBus *psSMBus, uint8_t ui8TargetAddress,
bool bData)
{
//
// Make sure that the peripheral is not currently active.
//
if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_PERIPHERAL_BUSY);
}
//
// Update the configuration structure with the data for this transfer.
//
psSMBus->ui8TargetSlaveAddress = ui8TargetAddress;
psSMBus->ui8TxSize = 0;
psSMBus->ui8RxSize = 0;
psSMBus->ui8RxIndex = 0;
psSMBus->ui8CalculatedCRC = 0;
//
// Clear the block transfer, process call and raw I2C flags.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0;
HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0;
HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0;
//
// This protocol does NOT support PEC, so the flag must be cleared. If
// PEC is needed again after this transaction, it should be explicitly
// enabled again.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC) = 0;
//
// Initialize the buffer index to 0 and the interrupt state machine to
// the appropriate state so that there is a known starting point
// for each transaction.
//
psSMBus->ui8TxIndex = 0;
psSMBus->ui8MasterState = SMBUS_STATE_IDLE;
//
// Set the slave address and R/S bit.
//
MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase,
psSMBus->ui8TargetSlaveAddress, bData);
//
// Make sure that the bus is idle.
//
if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_BUS_BUSY);
}
//
// Initiate the write operation.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase, I2C_MASTER_CMD_QUICK_COMMAND);
//
// Set the transfer in progress flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1;
//
// Return to the caller.
//
return(SMBUS_OK);
}
//*****************************************************************************
//
//! Initiates a master Host Notify transfer to the SMBus Host.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param ui8OwnSlaveAddress specifies the peripheral's own slave address.
//! \param pui8Data is a pointer to the two byte data payload.
//!
//! The Host Notify protocol is used by SMBus slaves to alert the bus Host
//! about an event. Most slave devices that operate in this environment only
//! become a bus master when this packet type is used. Host Notify always
//! sends two data bytes to the host along with the peripheral's own slave
//! address so that the Host knows which peripheral requested the Host's
//! attention.
//!
//! This protocol does not support PEC. The PEC flag is explicitly cleared
//! within this function, so if PEC is enabled prior to calling it, it must
//! be re-enabled afterwards.
//!
//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently
//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if
//! the transfer has successfully been initiated.
//
//*****************************************************************************
tSMBusStatus
SMBusMasterHostNotify(tSMBus *psSMBus, uint8_t ui8OwnSlaveAddress,
uint8_t *pui8Data)
{
//
// Make sure that the peripheral is not currently active.
//
if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_PERIPHERAL_BUSY);
}
//
// Update the configuration structure with the data for this transfer.
//
psSMBus->ui8TargetSlaveAddress = SMBUS_ADR_HOST;
psSMBus->pui8TxBuffer = pui8Data;
psSMBus->ui8TxSize = 2;
psSMBus->ui8RxSize = 0;
psSMBus->ui8RxIndex = 0;
psSMBus->ui8CalculatedCRC = 0;
//
// Clear the block transfer, process call and raw I2C flags.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0;
HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0;
HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0;
//
// This protocol does NOT support PEC, so the flag must be cleared. If
// PEC is needed again after this transaction, it should be explicitly
// enabled again.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC) = 0;
//
// Initialize the buffer index to 0 and the interrupt state machine to
// the appropriate state so that there is a known starting point
// for each transaction.
//
psSMBus->ui8TxIndex = 0;
psSMBus->ui8MasterState = SMBUS_STATE_WRITE_NEXT;
//
// Set the slave address and R/S bit.
//
MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase,
psSMBus->ui8TargetSlaveAddress, false);
//
// Put the SMBus command code on the bus.
//
MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, ui8OwnSlaveAddress);
//
// Make sure that the bus is idle.
//
if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_BUS_BUSY);
}
//
// Initiate the write operation.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_SEND_START);
//
// Set the transfer in progress flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1;
//
// Return to the caller.
//
return(SMBUS_OK);
}
//*****************************************************************************
//
//! Initiates a master Send Byte transfer to an SMBus slave.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param ui8TargetAddress specifies the slave address of the target device.
//! \param ui8Data is the data byte to send to the slave.
//!
//! The Send Byte protocol is a basic SMBus protocol that sends a single data
//! byte to the slave. Unlike most of the other SMBus protocols, Send Byte
//! does not send a ``command'' byte before the data payload and is intended
//! for basic communication.
//!
//! This protocol supports the optional PEC byte for error checking. To use
//! PEC, SMBusPECEnable() must be called before this function.
//!
//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently
//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if
//! the transfer has successfully been initiated.
//
//*****************************************************************************
tSMBusStatus
SMBusMasterByteSend(tSMBus *psSMBus, uint8_t ui8TargetAddress,
uint8_t ui8Data)
{
uint8_t ui8TempData;
//
// Make sure that the peripheral is not currently active.
//
if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_PERIPHERAL_BUSY);
}
//
// Update the configuration structure with the data for this transfer.
//
psSMBus->ui8TargetSlaveAddress = ui8TargetAddress;
psSMBus->ui8CurrentCommand = ui8Data;
psSMBus->pui8TxBuffer = &ui8Data;
psSMBus->ui8TxSize = 0;
psSMBus->ui8TxIndex = 0;
psSMBus->ui8RxSize = 0;
psSMBus->ui8RxIndex = 0;
psSMBus->ui8CalculatedCRC = 0;
//
// Clear the block transfer, process call and raw I2C flags.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0;
HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0;
HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0;
//
// Set the slave address and R/S bit.
//
MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase,
psSMBus->ui8TargetSlaveAddress, false);
//
// Put the data byte on the bus.
//
MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, ui8Data);
//
// Calculate the CRC for PEC (if used).
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// Place the target slave address into a temporary data variable and
// make sure the R/S bit is set to '0' for the CRC calculation.
//
ui8TempData = (psSMBus->ui8TargetSlaveAddress << 1) & 0xfe;
//
// Start off by calculating the CRC of the target slave address with
// an initial value of 0.
//
psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(0, &ui8TempData, 1);
//
// Add the data to the running CRC calculation.
//
psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
&psSMBus->pui8TxBuffer[0],
1);
//
// Update the state machine.
//
psSMBus->ui8MasterState = SMBUS_STATE_WRITE_FINAL;
//
// Make sure that the bus is idle.
//
if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_BUS_BUSY);
}
//
// Initiate the write operation.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_SEND_START);
}
else
{
//
// Update the state machine. Since it's the only byte being sent,
// the state machine's next state is idle.
//
psSMBus->ui8MasterState = SMBUS_STATE_IDLE;
//
// Make sure that the bus is idle.
//
if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_BUS_BUSY);
}
//
// Initiate the write operation.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase, I2C_MASTER_CMD_SINGLE_SEND);
}
//
// Set the transfer in progress flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1;
//
// Return to the caller.
//
return(SMBUS_OK);
}
//*****************************************************************************
//
//! Initiates a master Receive Byte transfer to an SMBus slave.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param ui8TargetAddress specifies the slave address of the target device.
//! \param pui8Data is a pointer to the location to store the received data
//! byte.
//!
//! The Receive Byte protocol is a basic SMBus protocol that receives a single
//! data byte from the slave. Unlike most of the other SMBus protocols,
//! Receive Byte does not send a ``command'' byte before the data payload and
//! is intended for basic communication.
//!
//! This protocol supports the optional PEC byte for error checking. To use
//! PEC, SMBusPECEnable() must be called before this function.
//!
//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently
//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if
//! the transfer has successfully been initiated.
//
//*****************************************************************************
tSMBusStatus
SMBusMasterByteReceive(tSMBus *psSMBus, uint8_t ui8TargetAddress,
uint8_t *pui8Data)
{
uint8_t ui8TempData;
//
// Make sure that the peripheral is not currently active.
//
if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_PERIPHERAL_BUSY);
}
//
// Update the configuration structure with the data for this transfer.
//
psSMBus->ui8TargetSlaveAddress = ui8TargetAddress;
psSMBus->ui8TxSize = 0;
psSMBus->ui8TxIndex = 0;
psSMBus->pui8RxBuffer = pui8Data;
psSMBus->ui8RxSize = 1;
psSMBus->ui8CalculatedCRC = 0;
//
// Clear the block transfer, process call and raw I2C flags.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0;
HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0;
HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0;
//
// Set the slave address and R/S bit.
//
MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase,
psSMBus->ui8TargetSlaveAddress, true);
//
// Calculate the CRC for PEC (if used).
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// Place the target slave address into a temporary data variable and
// set the R/S bit to '1' for the CRC calculation.
//
ui8TempData = ((psSMBus->ui8TargetSlaveAddress << 1) & 0xfe) | 1;
//
// Start off by calculating the CRC of the target slave address with
// an initial value of 0.
//
psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(0, &ui8TempData, 1);
//
// Update the state machine.
//
psSMBus->ui8MasterState = SMBUS_STATE_READ_FINAL;
//
// Make sure that the bus is idle.
//
if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_BUS_BUSY);
}
//
// Initiate the read operation.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_RECEIVE_START);
}
else
{
//
// Update the state machine.
//
psSMBus->ui8MasterState = SMBUS_STATE_READ_WAIT;
//
// Make sure that the bus is idle.
//
if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_BUS_BUSY);
}
//
// Initiate the read operation.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_SINGLE_RECEIVE);
}
//
// Set the transfer in progress flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1;
//
// Return to the caller.
//
return(SMBUS_OK);
}
//*****************************************************************************
//
//! Initiates a master Write Byte or Write Word transfer to an SMBus slave.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param ui8TargetAddress specifies the slave address of the target device.
//! \param ui8Command is the command byte sent before the data payload.
//! \param pui8Data is a pointer to the transmit data buffer.
//! \param ui8Size is the number of bytes to send to the slave.
//!
//! This function supports both the Write Byte and Write Word protocols. The
//! amount of data to send is user defined, but limited to 1 or 2 bytes.
//!
//! This protocol supports the optional PEC byte for error checking. To use
//! PEC, SMBusPECEnable() must be called before this function.
//!
//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently
//! active, \b SMBUS_BUS_BUSY if the bus is already in use,
//! \b SMBUS_DATA_SIZE_ERROR if ui8Size is greater than 2, or \b SMBUS_OK if
//! the transfer has successfully been initiated.
//
//*****************************************************************************
tSMBusStatus
SMBusMasterByteWordWrite(tSMBus *psSMBus, uint8_t ui8TargetAddress,
uint8_t ui8Command, uint8_t *pui8Data,
uint8_t ui8Size)
{
uint8_t ui8TempData;
//
// Make sure that the peripheral is not currently active.
//
if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_PERIPHERAL_BUSY);
}
//
// If more than 2 bytes are requested, indicate error.
//
if(ui8Size > 2)
{
return(SMBUS_DATA_SIZE_ERROR);
}
//
// Update the configuration structure with the data for this transfer.
//
psSMBus->ui8TargetSlaveAddress = ui8TargetAddress;
psSMBus->ui8CurrentCommand = ui8Command;
psSMBus->pui8TxBuffer = pui8Data;
psSMBus->ui8TxSize = ui8Size;
psSMBus->ui8RxSize = 0;
psSMBus->ui8RxIndex = 0;
psSMBus->ui8CalculatedCRC = 0;
//
// Clear the block transfer, process call and raw I2C flags.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0;
HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0;
HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0;
//
// Initialize the buffer index to 0 and the interrupt state machine to
// the appropriate state so that there is a known starting point
// for each transaction.
//
psSMBus->ui8TxIndex = 0;
//
// Set the slave address and R/S bit.
//
MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase,
psSMBus->ui8TargetSlaveAddress, false);
//
// Calculate the CRC for PEC (if used).
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// Place the target slave address into a temporary data variable and
// make sure the R/S bit is set to '0' for the CRC calculation.
//
ui8TempData = (psSMBus->ui8TargetSlaveAddress << 1) & 0xfe;
//
// Start off by calculating the CRC of the target slave address with
// an initial value of 0.
//
psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(0, &ui8TempData, 1);
//
// Add the command to the running CRC calculation.
//
psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
&psSMBus->ui8CurrentCommand,
1);
//
// Add the data array to the calculation.
//
psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
psSMBus->pui8TxBuffer,
psSMBus->ui8TxSize);
//
// Set the next state.
//
psSMBus->ui8MasterState = SMBUS_STATE_WRITE_NEXT;
}
else
{
//
// If only one byte to send, move to the final state.
//
if(ui8Size == 1)
{
//
// Set the next state.
//
psSMBus->ui8MasterState = SMBUS_STATE_WRITE_FINAL;
}
else
{
//
// Set the next state.
//
psSMBus->ui8MasterState = SMBUS_STATE_WRITE_NEXT;
}
}
//
// Put the SMBus command code on the bus.
//
MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->ui8CurrentCommand);
//
// Make sure that the bus is idle.
//
if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_BUS_BUSY);
}
//
// Initiate the write operation.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_SEND_START);
//
// Set the transfer in progress flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1;
//
// Return to the caller.
//
return(SMBUS_OK);
}
//*****************************************************************************
//
//! Initiates a master Read Byte or Read Word transfer to an SMBus slave.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param ui8TargetAddress specifies the slave address of the target device.
//! \param ui8Command is the command byte sent before the data is requested.
//! \param pui8Data is a pointer to the receive data buffer.
//! \param ui8Size is the number of bytes to receive from the slave.
//!
//! This function supports both the Read Byte and Read Word protocols. The
//! amount of data to receive is user defined, but limited to 1 or 2 bytes.
//!
//! This protocol supports the optional PEC byte for error checking. To use
//! PEC, SMBusPECEnable() must be called before this function.
//!
//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently
//! active, \b SMBUS_BUS_BUSY if the bus is already in use,
//! \b SMBUS_DATA_SIZE_ERROR if ui8Size is greater than 2, or \b SMBUS_OK if
//! the transfer has successfully been initiated.
//
//*****************************************************************************
tSMBusStatus
SMBusMasterByteWordRead(tSMBus *psSMBus, uint8_t ui8TargetAddress,
uint8_t ui8Command, uint8_t *pui8Data,
uint8_t ui8Size)
{
uint8_t ui8TempData;
//
// Make sure that the peripheral is not currently active.
//
if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_PERIPHERAL_BUSY);
}
//
// If more than 2 bytes are requested, indicate error.
//
if(ui8Size > 2)
{
return(SMBUS_DATA_SIZE_ERROR);
}
//
// Update the configuration structure with the data for this transfer.
//
psSMBus->ui8TargetSlaveAddress = ui8TargetAddress;
psSMBus->ui8CurrentCommand = ui8Command;
psSMBus->pui8RxBuffer = pui8Data;
psSMBus->ui8TxSize = 0;
psSMBus->ui8TxIndex = 0;
psSMBus->ui8RxIndex = 0;
psSMBus->ui8RxSize = ui8Size;
psSMBus->ui8CalculatedCRC = 0;
//
// Clear the block transfer, process call and raw I2C flags.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0;
HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0;
HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0;
//
// Set the slave address and R/S bit.
//
MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase,
psSMBus->ui8TargetSlaveAddress, false);
//
// Put the SMBus command code on the bus.
//
MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->ui8CurrentCommand);
//
// Calculate the CRC for PEC (if used).
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// Place the target slave address into a temporary data variable and
// set the R/S bit to '1' for the CRC calculation.
//
ui8TempData = psSMBus->ui8TargetSlaveAddress << 1;
//
// Start off by calculating the CRC of the target slave address with
// an initial value of 0.
//
psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(0, &ui8TempData, 1);
//
// Add the command to the running CRC calculation.
//
psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
&psSMBus->ui8CurrentCommand,
1);
//
// Update the state machine.
//
psSMBus->ui8MasterState = SMBUS_STATE_READ_FIRST;
}
else
{
//
// Update the state machine.
//
if(psSMBus->ui8RxSize == 2)
{
psSMBus->ui8MasterState = SMBUS_STATE_READ_FIRST;
}
else
{
psSMBus->ui8MasterState = SMBUS_STATE_READ_ONE;
}
}
//
// Make sure that the bus is idle.
//
if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_BUS_BUSY);
}
//
// Initiate the write operation.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_SEND_START);
//
// Set the transfer in progress flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1;
//
// Return to the caller.
//
return(SMBUS_OK);
}
//*****************************************************************************
//
//! Initiates a master Block Write transfer to an SMBus slave.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param ui8TargetAddress specifies the slave address of the target device.
//! \param ui8Command is the command byte sent before the data is requested.
//! \param pui8Data is a pointer to the transmit data buffer.
//! \param ui8Size is the number of bytes to send to the slave.
//!
//! This function supports the Block Write protocol. The amount of data sent
//! to the slave is user defined, but limited to 32 bytes per the SMBus spec.
//!
//! This protocol supports the optional PEC byte for error checking. To use
//! PEC, SMBusPECEnable() must be called before this function.
//!
//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently
//! active, \b SMBUS_BUS_BUSY if the bus is already in use,
//! \b SMBUS_DATA_SIZE_ERROR if ui8Size is greater than 32, or \b SMBUS_OK if
//! the transfer has successfully been initiated.
//
//*****************************************************************************
tSMBusStatus
SMBusMasterBlockWrite(tSMBus *psSMBus, uint8_t ui8TargetAddress,
uint8_t ui8Command, uint8_t *pui8Data,
uint8_t ui8Size)
{
uint8_t ui8TempData;
//
// Make sure that the peripheral is not currently active.
//
if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_PERIPHERAL_BUSY);
}
//
// If more than 32 bytes are requested, indicate error.
//
if(ui8Size > 32)
{
return(SMBUS_DATA_SIZE_ERROR);
}
//
// Update the configuration structure with the data for this transfer.
//
psSMBus->ui8TargetSlaveAddress = ui8TargetAddress;
psSMBus->ui8CurrentCommand = ui8Command;
psSMBus->pui8TxBuffer = pui8Data;
psSMBus->ui8TxSize = ui8Size;
psSMBus->ui8RxSize = 0;
psSMBus->ui8RxIndex = 0;
psSMBus->ui8CalculatedCRC = 0;
//
// Set the block transfer flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 1;
//
// Clear the process call and raw I2C flags.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0;
HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0;
//
// Initialize the buffer index to 0 and the interrupt state machine to
// the appropriate state so that there is a known starting point
// for each transaction.
//
psSMBus->ui8TxIndex = 0;
//
// Calculate the CRC for PEC (if used).
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// Place the target slave address into a temporary data variable and
// make sure the R/S bit is set to '0' for the CRC calculation.
//
ui8TempData = (psSMBus->ui8TargetSlaveAddress << 1) & 0xfe;
//
// Start off by calculating the CRC of the target slave address with
// an initial value of 0.
//
psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(0, &ui8TempData, 1);
//
// Add the command to the running CRC calculation.
//
psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
&psSMBus->ui8CurrentCommand,
1);
//
// Add the size to the running CRC calculation.
//
psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
&psSMBus->ui8TxSize, 1);
//
// Add the data array to the calculation.
//
psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
psSMBus->pui8TxBuffer,
psSMBus->ui8TxSize);
}
//
// Set the slave address and R/S bit.
//
MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase,
psSMBus->ui8TargetSlaveAddress, false);
//
// Write the first byte of the data.
//
MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->ui8CurrentCommand);
//
// Update the state machine.
//
psSMBus->ui8MasterState = SMBUS_STATE_WRITE_BLOCK_SIZE;
//
// Make sure that the bus is idle.
//
if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_BUS_BUSY);
}
//
// Set the transfer in progress flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1;
//
// Initiate the write operation.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_SEND_START);
//
// Return to the caller.
//
return(SMBUS_OK);
}
//*****************************************************************************
//
//! Initiates a master Block Read transfer to an SMBus slave.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param ui8TargetAddress specifies the slave address of the target device.
//! \param ui8Command is the command byte sent before the data is requested.
//! \param pui8Data is a pointer to the receive data buffer.
//!
//! This function supports the Block Read protocol. The amount of data read
//! is defined by the slave device, but should never exceed 32 bytes per the
//! SMBus spec. The receive size is the first data byte returned by the slave,
//! so this function assumes a size of 3 until the actual number is sent by
//! the slave. In the application interrupt handler, SMBusRxPacketSizeGet()
//! can be used to obtain the amount of data sent by the slave.
//!
//! This protocol supports the optional PEC byte for error checking. To use
//! PEC, SMBusPECEnable() must be called before this function.
//!
//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently
//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if
//! the transfer has successfully been initiated.
//
//*****************************************************************************
tSMBusStatus
SMBusMasterBlockRead(tSMBus *psSMBus, uint8_t ui8TargetAddress,
uint8_t ui8Command, uint8_t *pui8Data)
{
uint8_t ui8TempData;
//
// Make sure that the peripheral is not currently active.
//
if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_PERIPHERAL_BUSY);
}
//
// Update the configuration structure with the data for this transfer.
//
psSMBus->ui8TargetSlaveAddress = ui8TargetAddress;
psSMBus->ui8CurrentCommand = ui8Command;
psSMBus->pui8RxBuffer = pui8Data;
psSMBus->ui8RxIndex = 0;
psSMBus->ui8TxSize = 0;
psSMBus->ui8TxIndex = 0;
psSMBus->ui8CalculatedCRC = 0;
//
// Set the block transfer flag..
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 1;
//
// Clear the process call and raw I2C flags.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0;
HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0;
//
// Set the slave address and R/S bit.
//
MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase,
psSMBus->ui8TargetSlaveAddress, false);
//
// Put the SMBus command code on the bus.
//
MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->ui8CurrentCommand);
//
// Initially set the RX size to 3 to make the state machine work.
// The slave will respond with the actual size of the transfer in the
// first byte and that data will replace this initial value.
//
psSMBus->ui8RxSize = 3;
//
// Calculate the CRC for PEC (if used).
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// Place the target slave address into a temporary data variable and
// set the R/S bit to '1' for the CRC calculation.
//
ui8TempData = psSMBus->ui8TargetSlaveAddress << 1;
//
// Start off by calculating the CRC of the target slave address with
// an initial value of 0.
//
psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(0, &ui8TempData, 1);
//
// Add the command to the running CRC calculation.
//
psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
&psSMBus->ui8CurrentCommand,
1);
}
//
// Update the state machine.
//
psSMBus->ui8MasterState = SMBUS_STATE_READ_FIRST;
//
// Make sure that the bus is idle.
//
if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_BUS_BUSY);
}
//
// Set the transfer in progress flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1;
//
// Initiate the write operation.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_SEND_START);
//
// Return to the caller.
//
return(SMBUS_OK);
}
//*****************************************************************************
//
//! Initiates a master Process Call transfer to an SMBus slave.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param ui8TargetAddress specifies the slave address of the target device.
//! \param ui8Command is the command byte sent before the data is requested.
//! \param pui8TxData is a pointer to the transmit data buffer.
//! \param pui8RxData is a pointer to the receive data buffer.
//!
//! This function supports the Process Call protocol. The amount of data sent
//! to and received from the slave is fixed to 2 bytes per direction (2 sent,
//! 2 received).
//!
//! This protocol supports the optional PEC byte for error checking. To use
//! PEC, SMBusPECEnable() must be called before this function.
//!
//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently
//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if
//! the transfer has successfully been initiated.
//
//*****************************************************************************
tSMBusStatus
SMBusMasterProcessCall(tSMBus *psSMBus, uint8_t ui8TargetAddress,
uint8_t ui8Command, uint8_t *pui8TxData,
uint8_t *pui8RxData)
{
uint8_t ui8TempData;
//
// Make sure that the peripheral is not currently active.
//
if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_PERIPHERAL_BUSY);
}
//
// Update the configuration structure with the data for this transfer.
//
psSMBus->ui8TargetSlaveAddress = ui8TargetAddress;
psSMBus->ui8CurrentCommand = ui8Command;
psSMBus->pui8TxBuffer = pui8TxData;
psSMBus->pui8RxBuffer = pui8RxData;
psSMBus->ui8TxIndex = 0;
psSMBus->ui8TxSize = 2;
psSMBus->ui8RxIndex = 0;
psSMBus->ui8RxSize = 2;
psSMBus->ui8CalculatedCRC = 0;
//
// Set the process call flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 1;
//
// Clear the block transfer and raw I2C flags.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0;
HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0;
//
// Set the slave address and R/S bit.
//
MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase,
psSMBus->ui8TargetSlaveAddress, false);
//
// Calculate the CRC for PEC (if used).
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// Place the target slave address into a temporary data variable and
// make sure the R/S bit is set to '0' for the CRC calculation.
//
ui8TempData = (psSMBus->ui8TargetSlaveAddress << 1) & 0xfe;
//
// Start off by calculating the CRC of the target slave address with
// an initial value of 0.
//
psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(0, &ui8TempData, 1);
//
// Add the command to the running CRC calculation.
//
psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
&psSMBus->ui8CurrentCommand,
1);
//
// Add the data array to the calculation.
//
psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
psSMBus->pui8TxBuffer,
psSMBus->ui8TxSize);
}
//
// Put the SMBus command code on the bus.
//
MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->ui8CurrentCommand);
//
// Update the state machine.
//
psSMBus->ui8MasterState = SMBUS_STATE_WRITE_NEXT;
//
// Make sure that the bus is idle.
//
if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_BUS_BUSY);
}
//
// Initiate the write operation.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_SEND_START);
//
// Set the transfer in progress flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1;
//
// Return to the caller.
//
return(SMBUS_OK);
}
//*****************************************************************************
//
//! Initiates a master Block Process Call transfer to an SMBus slave.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param ui8TargetAddress specifies the slave address of the target device.
//! \param ui8Command is the command byte sent before the data is requested.
//! \param pui8TxData is a pointer to the transmit data buffer.
//! \param ui8TxSize is the number of bytes to send to the slave.
//! \param pui8RxData is a pointer to the receive data buffer.
//!
//! This function supports the Block Write/Block Read Process Call protocol.
//! The amount of data sent to the slave is user defined but limited to 32 data
//! bytes. The amount of data read is defined by the slave device, but should
//! never exceed 32 bytes per the SMBus spec. The receive size is the first
//! data byte returned by the slave, so the actual size is populated in
//! SMBusMasterISRProcess(). In the application interrupt handler,
//! SMBusRxPacketSizeGet() can be used to obtain the amount of data sent by
//! the slave.
//!
//! This protocol supports the optional PEC byte for error checking. To use
//! PEC, SMBusPECEnable() must be called before this function.
//!
//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently
//! active, \b SMBUS_BUS_BUSY if the bus is already in use,
//! \b SMBUS_DATA_SIZE_ERROR if ui8TxSize is greater than 32, or \b SMBUS_OK if
//! the transfer has successfully been initiated.
//
//*****************************************************************************
tSMBusStatus
SMBusMasterBlockProcessCall(tSMBus *psSMBus, uint8_t ui8TargetAddress,
uint8_t ui8Command, uint8_t *pui8TxData,
uint8_t ui8TxSize, uint8_t *pui8RxData)
{
uint8_t ui8TempData;
//
// Make sure that the peripheral is not currently active.
//
if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_PERIPHERAL_BUSY);
}
//
// If more than 32 bytes are requested, indicate error.
//
if(ui8TxSize > 32)
{
return(SMBUS_DATA_SIZE_ERROR);
}
//
// Update the configuration structure with the data for this transfer.
//
psSMBus->ui8TargetSlaveAddress = ui8TargetAddress;
psSMBus->ui8CurrentCommand = ui8Command;
psSMBus->pui8TxBuffer = pui8TxData;
psSMBus->pui8RxBuffer = pui8RxData;
psSMBus->ui8TxIndex = 0;
psSMBus->ui8TxSize = ui8TxSize;
psSMBus->ui8RxIndex = 0;
psSMBus->ui8RxSize = 3;
psSMBus->ui8CalculatedCRC = 0;
//
// Set the process call and block transfer flags.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 1;
HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 1;
//
// Clear the raw I2C flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0;
//
// Calculate the CRC for PEC (if used).
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// Place the target slave address into a temporary data variable and
// make sure the R/S bit is set to '0' for the CRC calculation.
//
ui8TempData = (psSMBus->ui8TargetSlaveAddress << 1) & 0xfe;
//
// Start off by calculating the CRC of the target slave address with
// an initial value of 0.
//
psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(0, &ui8TempData, 1);
//
// Add the command to the running CRC calculation.
//
psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
&psSMBus->ui8CurrentCommand,
1);
//
// Add the size to the running CRC calculation.
//
psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
&psSMBus->ui8TxSize, 1);
//
// Add the data array to the calculation.
//
psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
psSMBus->pui8TxBuffer,
psSMBus->ui8TxSize);
}
//
// Set the slave address and R/S bit.
//
MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase,
psSMBus->ui8TargetSlaveAddress, false);
//
// Put the SMBus command code on the bus.
//
MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->ui8CurrentCommand);
//
// Update the state machine.
//
psSMBus->ui8MasterState = SMBUS_STATE_WRITE_BLOCK_SIZE;
//
// Make sure that the bus is idle.
//
if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_BUS_BUSY);
}
//
// Initiate the write operation.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_SEND_START);
//
// Set the transfer in progress flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1;
//
// Return to the caller.
//
return(SMBUS_OK);
}
//*****************************************************************************
//
//! Initiates a ``raw'' I2C write transfer to a slave device.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param ui8TargetAddress specifies the slave address of the target device.
//! \param pui8Data is a pointer to the transmit data buffer.
//! \param ui8Size is the number of bytes to send to the slave.
//!
//! This function sends a user-defined number of bytes to an I2C slave without
//! using an SMBus protocol. The data size is only limited to the size of the
//! ui8Size variable, which is an unsigned character (8 bits, value of 255).
//!
//! Because this function uses ``raw'' I2C, PEC is not supported.
//!
//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently
//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if
//! the transfer has successfully been initiated.
//
//*****************************************************************************
tSMBusStatus
SMBusMasterI2CWrite(tSMBus *psSMBus, uint8_t ui8TargetAddress,
uint8_t *pui8Data, uint8_t ui8Size)
{
//
// Make sure that the peripheral is not currently active.
//
if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_PERIPHERAL_BUSY);
}
//
// Update the configuration structure with the data for this transfer.
//
psSMBus->ui8TargetSlaveAddress = ui8TargetAddress;
psSMBus->pui8TxBuffer = pui8Data;
psSMBus->ui8TxSize = ui8Size;
psSMBus->ui8TxIndex = 1;
psSMBus->ui8RxSize = 0;
psSMBus->ui8RxIndex = 0;
//
// PEC is not supported by raw I2C transfers, so force it to be disabled.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC) = 0;
//
// Clear the block transfer and process call flags.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0;
HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0;
//
// Set the raw I2C flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 1;
//
// Set the slave address and R/S bit.
//
MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase,
psSMBus->ui8TargetSlaveAddress, false);
//
// Put the first byte on the bus.
//
MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->pui8TxBuffer[0]);
//
// Choose what to do based on the transmit size.
//
if(ui8Size == 1)
{
//
// Update the state machine. Since it's the only byte being sent,
// the state machine's next state is idle.
//
psSMBus->ui8MasterState = SMBUS_STATE_IDLE;
//
// Make sure that the bus is idle.
//
if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_BUS_BUSY);
}
//
// Initiate the write operation.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase, I2C_MASTER_CMD_SINGLE_SEND);
}
else if(ui8Size == 2)
{
//
// If there are only 2 bytes to send just jump to the final write
// state.
//
psSMBus->ui8MasterState = SMBUS_STATE_WRITE_FINAL;
//
// Make sure that the bus is idle.
//
if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_BUS_BUSY);
}
//
// Initiate the write operation.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_SEND_START);
}
else
{
//
// Set the next state.
//
psSMBus->ui8MasterState = SMBUS_STATE_WRITE_NEXT;
//
// Make sure that the bus is idle.
//
if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_BUS_BUSY);
}
//
// Initiate the write operation.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_SEND_START);
}
//
// Set the transfer in progress flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1;
//
// Return to the caller.
//
return(SMBUS_OK);
}
//*****************************************************************************
//
//! Initiates a ``raw'' I2C read transfer to a slave device.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param ui8TargetAddress specifies the slave address of the target device.
//! \param pui8Data is a pointer to the receive data buffer.
//! \param ui8Size is the number of bytes to send to the slave.
//!
//! This function receives a user-defined number of bytes from an I2C slave
//! without using an SMBus protocol. The data size is only limited to the size
//! of the ui8Size variable, which is an unsigned character (8 bits, value of
//! 255).
//!
//! Because this function uses ``raw'' I2C, PEC is not supported.
//!
//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently
//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if
//! the transfer has successfully been initiated.
//
//*****************************************************************************
tSMBusStatus
SMBusMasterI2CRead(tSMBus *psSMBus, uint8_t ui8TargetAddress,
uint8_t *pui8Data, uint8_t ui8Size)
{
//
// Make sure that the peripheral is not currently active.
//
if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_PERIPHERAL_BUSY);
}
//
// Update the configuration structure with the data for this transfer.
//
psSMBus->ui8TargetSlaveAddress = ui8TargetAddress;
psSMBus->pui8RxBuffer = pui8Data;
psSMBus->ui8TxSize = 0;
psSMBus->ui8TxIndex = 0;
psSMBus->ui8RxIndex = 0;
psSMBus->ui8RxSize = ui8Size;
//
// PEC is not supported by raw I2C transfers, so force it to be disabled.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC) = 0;
//
// Clear the block transfer and process call flags.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0;
HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0;
//
// Set the raw I2C flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 1;
//
// Set the slave address and R/S bit.
//
MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase,
psSMBus->ui8TargetSlaveAddress, true);
//
// Make sure that the bus is idle.
//
if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_BUS_BUSY);
}
//
// Choose what to do based on the receive size.
//
if(ui8Size == 1)
{
//
// Update the state machine.
//
psSMBus->ui8MasterState = SMBUS_STATE_READ_WAIT;
}
else if(ui8Size == 2)
{
psSMBus->ui8MasterState = SMBUS_STATE_READ_FINAL;
}
else
{
//
// Set the next state.
//
psSMBus->ui8MasterState = SMBUS_STATE_READ_NEXT;
}
if(ui8Size == 1)
{
//
// Start the single receive.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_SINGLE_RECEIVE);
}
else
{
//
// Start the burst receive.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_RECEIVE_START);
}
//
// Set the transfer in progress flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1;
//
// Return to the caller.
//
return(SMBUS_OK);
}
//*****************************************************************************
//
//! Initiates a ``raw'' I2C write-read transfer to a slave device.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param ui8TargetAddress specifies the slave address of the target device.
//! \param pui8TxData is a pointer to the transmit data buffer.
//! \param ui8TxSize is the number of bytes to send to the slave.
//! \param pui8RxData is a pointer to the receive data buffer.
//! \param ui8RxSize is the number of bytes to receive from the slave.
//!
//! This function initiates a write-read transfer to an I2C slave without using
//! an SMBus protocol. The user-defined number of bytes is written to the
//! slave first, followed by the reception of the user-defined number of bytes.
//! The transmit and receive data sizes are only limited to the size of the
//! ui8TxSize and ui8RxSize variables, which are unsigned characters (8 bits,
//! value of 255).
//!
//! Because this function uses ``raw'' I2C, PEC is not supported.
//!
//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently
//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if
//! the transfer has successfully been initiated.
//
//*****************************************************************************
tSMBusStatus
SMBusMasterI2CWriteRead(tSMBus *psSMBus, uint8_t ui8TargetAddress,
uint8_t *pui8TxData, uint8_t ui8TxSize,
uint8_t *pui8RxData, uint8_t ui8RxSize)
{
//
// Make sure that the peripheral is not currently active.
//
if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_PERIPHERAL_BUSY);
}
//
// Update the configuration structure with the data for this transfer.
//
psSMBus->ui8TargetSlaveAddress = ui8TargetAddress;
psSMBus->pui8TxBuffer = pui8TxData;
psSMBus->pui8RxBuffer = pui8RxData;
psSMBus->ui8TxIndex = 1;
psSMBus->ui8TxSize = ui8TxSize;
psSMBus->ui8RxIndex = 0;
psSMBus->ui8RxSize = ui8RxSize;
//
// PEC is not supported by raw I2C transfers, so force it to be disabled.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC) = 0;
//
// Set the process call flag. Even though this is technically not an SMBus
// process call, this flag is used in the interrupt state machine for
// the bus turn around.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 1;
//
// Clear the block transfer flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0;
//
// Set the raw I2C flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 1;
//
// Set the slave address and R/S bit.
//
MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase,
psSMBus->ui8TargetSlaveAddress, false);
//
// Write the first byte of the data.
//
MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->pui8TxBuffer[0]);
//
// Choose what to do based on the transmit size.
//
if(ui8TxSize == 1)
{
//
// Move to the read first state for the turn around.
//
psSMBus->ui8MasterState = SMBUS_STATE_READ_FIRST;
}
else if(ui8TxSize == 2)
{
psSMBus->ui8MasterState = SMBUS_STATE_WRITE_FINAL;
}
else
{
//
// Set the next state.
//
psSMBus->ui8MasterState = SMBUS_STATE_WRITE_NEXT;
}
//
// Make sure that the bus is idle.
//
if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase))
{
return(SMBUS_BUS_BUSY);
}
//
// Initiate the write operation.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_SEND_START);
//
// Set the transfer in progress flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1;
//
// Return to the caller.
//
return(SMBUS_OK);
}
//*****************************************************************************
//
//! \internal
//! Sends a ``general'' Get UDID packet.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param pui8Data is a pointer to the receive data buffer.
//!
//! This function sends a ``general'' Get UDID packet, used during Address
//! Resolution Protocol (ARP). Since SMBus requires that data bytes be
//! transmitted in a certain order, the raw data in the pui8Data needs to be
//! treated as such. To put the data in a known order, use
//! SMBusARPUDIDPacketDecode().
//!
//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently
//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if
//! the transfer has successfully been initiated.
//
//*****************************************************************************
tSMBusStatus
SMBusMasterARPGetUDIDGen(tSMBus *psSMBus, uint8_t *pui8Data)
{
//
// Use the block read protocol to receive the UDID.
//
return(SMBusMasterBlockRead(psSMBus, SMBUS_ADR_DEFAULT_DEVICE,
SMBUS_CMD_ARP_GET_UDID, pui8Data));
}
//*****************************************************************************
//
//! \internal
//! Sends a ``directed'' Get UDID packet.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param ui8TargetAddress specifies the slave address of the target device.
//! \param pui8Data is a pointer to the receive data buffer.
//!
//! This function sends a ``directed'' Get UDID packet, used during Address
//! Resolution Protocol (ARP). A directed packet differs from a general packet
//! in that it targets a specific slave device. Since SMBus requires that data
//! bytes be transmitted in a certain order, the raw data in the pui8Data needs
//! to be treated as such. To put the data in a known order, use
//! SMBusARPUDIDPacketDecode().
//!
//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently
//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if
//! the transfer has successfully been initiated.
//
//*****************************************************************************
tSMBusStatus
SMBusMasterARPGetUDIDDir(tSMBus *psSMBus, uint8_t ui8TargetAddress,
uint8_t *pui8Data)
{
//
// Use the block read protocol to receive the UDID.
//
return(SMBusMasterBlockRead(psSMBus, SMBUS_ADR_DEFAULT_DEVICE,
(ui8TargetAddress << 1 | 1), pui8Data));
}
//*****************************************************************************
//
//! \internal
//! Sends a ``general'' Reset Device packet.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! This function sends a ``general'' Reset Device packet, used during Address
//! Resolution Protocol (ARP). This packet is used by an ARP Master to force
//! all non-PSA (Persistent Slave Address), ARP-capable devices to return to
//! their initial state. This packet also tells the devices to clear their
//! Address Resolved (AR) and Address Valid (AV) flags.
//!
//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently
//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if
//! the transfer has successfully been initiated.
//
//*****************************************************************************
tSMBusStatus
SMBusMasterARPResetDeviceGen(tSMBus *psSMBus)
{
//
// Use the Send Byte protocol to send the packet.
//
return(SMBusMasterByteSend(psSMBus, SMBUS_ADR_DEFAULT_DEVICE,
SMBUS_CMD_ARP_RESET_DEVICE));
}
//*****************************************************************************
//
//! \internal
//! Sends a ``directed'' Reset Device packet.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! This function sends a ``directed'' Reset Device packet, used during Address
//! Resolution Protocol (ARP). This packet is used by an ARP Master to force
//! a specific non-PSA (Persistent Slave Address), ARP-capable device to return
//! to its initial state. This packet also tells the device to clear its
//! Address Resolved (AR) and Address Valid (AV) flags.
//!
//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently
//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if
//! the transfer has successfully been initiated.
//
//*****************************************************************************
tSMBusStatus
SMBusMasterARPResetDeviceDir(tSMBus *psSMBus, uint8_t ui8TargetAddress)
{
//
// Use the Send Byte protocol to send the packet.
//
return(SMBusMasterByteSend(psSMBus, SMBUS_ADR_DEFAULT_DEVICE,
(ui8TargetAddress << 1)));
}
//*****************************************************************************
//
//! Sends an ARP Assign Address packet.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param pui8Data is a pointer to the transmit data buffer. This buffer
//! should be correctly formatted using SMBusARPUDIDPacketEncode() and
//! should contain the UDID data and the address for the slave.
//!
//! This function sends an Assign Address packet, used during Address
//! Resolution Protocol (ARP). Because SMBus requires data bytes be sent out
//! MSB first, the UDID and target address should be formatted correctly by the
//! application or using SMBusARPUDIDPacketEncode() and placed into a data
//! buffer pointed to by pui8Data.
//!
//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently
//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if
//! the transfer has successfully been initiated.
//
//*****************************************************************************
tSMBusStatus
SMBusMasterARPAssignAddress(tSMBus *psSMBus, uint8_t *pui8Data)
{
//
// Use the Block Write protocol to send the packet.
//
return(SMBusMasterBlockWrite(psSMBus, SMBUS_ADR_DEFAULT_DEVICE,
SMBUS_CMD_ARP_ASSIGN_ADDRESS, pui8Data, 17));
}
//*****************************************************************************
//
//! Sends a Notify ARP Master packet.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param pui8Data is a pointer to the transmit data buffer. The data payload
//! should be 0x0000 for this packet.
//!
//! This function sends a Notify ARP Master packet, used during Address
//! Resolution Protocol (ARP). This packet is used by a slave to indicate
//! to the ARP Master that it needs attention.
//!
//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently
//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if
//! the transfer has successfully been initiated.
//
//*****************************************************************************
tSMBusStatus
SMBusMasterARPNotifyMaster(tSMBus *psSMBus, uint8_t *pui8Data)
{
//
// Use the Host Notify protocol to send the packet.
//
return(SMBusMasterHostNotify(psSMBus, (SMBUS_ADR_DEFAULT_DEVICE << 1),
pui8Data));
}
//*****************************************************************************
//
//! Sends a Prepare to ARP packet.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! This function sends a Prepare to ARP packet, used during Address Resolution
//! Protocol (ARP). This packet is used by an ARP Master to alert devices on
//! the bus that ARP is about to begin. All ARP-capable devices must
//! acknowledge all bytes in this packet and clear their Address Resolved (AR)
//! flag.
//!
//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently
//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if
//! the transfer has successfully been initiated.
//
//*****************************************************************************
tSMBusStatus
SMBusMasterARPPrepareToARP(tSMBus *psSMBus)
{
//
// Use the Send Byte protocol to send the packet.
//
return(SMBusMasterByteSend(psSMBus, SMBUS_ADR_DEFAULT_DEVICE,
SMBUS_CMD_PREPARE_TO_ARP));
}
//*****************************************************************************
//
//! Master ISR processing function for the SMBus application.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! This function must be called in the application interrupt service routine
//! (ISR) to process SMBus master interrupts.
//!
//! \return Returns \b SMBUS_TIMEOUT if a bus timeout is detected,
//! \b SMBUS_ARB_LOST if I2C bus arbitration lost is detected,
//! \b SMBUS_ADDR_ACK_ERROR if the address phase of a transfer results in a
//! NACK, \b SMBUS_DATA_ACK_ERROR if the data phase of a transfer results in a
//! NACK, \b SMBUS_DATA_SIZE_ERROR if a receive buffer overrun is detected or
//! if a transmit operation tries to write more data than is allowed,
//! \b SMBUS_MASTER_ERROR if an unknown error occurs, \b SMBUS_PEC_ERROR if the
//! received PEC byte does not match the locally calculated value, or
//! \b SMBUS_OK if processing finished successfully.
//
//*****************************************************************************
tSMBusStatus
SMBusMasterIntProcess(tSMBus *psSMBus)
{
uint32_t ui32IntStatus;
uint32_t ui32ErrorStatus;
uint8_t ui8TempData;
//
// Determine which interrupt made us get here.
//
ui32IntStatus = MAP_I2CMasterIntStatusEx(psSMBus->ui32I2CBase, true);
//
// Check for the timeout interrupt. Since the peripheral will
// automatically issue a stop, just clear the interrupt and return.
//
if(ui32IntStatus & I2C_MASTER_INT_TIMEOUT)
{
//
// Clear all pending interrupts and wait for the bus to become
// free so we can issue a STOP.
//
MAP_I2CMasterIntClearEx(psSMBus->ui32I2CBase, I2C_MASTER_INT_TIMEOUT |
I2C_MASTER_INT_DATA);
//
// Clear the transfer in progress flag. New transactions will
// be aborted until the bus is free.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 0;
//
// Return to caller.
//
return(SMBUS_TIMEOUT);
}
else
{
//
// Clear the data interrupt.
//
MAP_I2CMasterIntClearEx(psSMBus->ui32I2CBase, I2C_MASTER_INT_DATA);
}
//
// Read the master interrupt status bits.
//
ui32ErrorStatus = HWREG(psSMBus->ui32I2CBase + I2C_O_MCS);
//
// Check for arbitration lost.
//
if(ui32ErrorStatus & I2C_MCS_ARBLST)
{
//
// Put the state machine back in the idle state.
//
psSMBus->ui8MasterState = SMBUS_STATE_IDLE;
//
// Clear the transfer in progress flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 0;
//
// Return to caller.
//
return(SMBUS_ARB_LOST);
}
//
// Check for an error.
//
if(ui32ErrorStatus & I2C_MCS_ERROR)
{
//
// Put the state machine back in the idle state.
//
psSMBus->ui8MasterState = SMBUS_STATE_IDLE;
//
// Check to see if the bus is free. There are two interrupts when a
// NACK happens, and the bus should only be free during the second
// interrupt. During the first interrupt (when the bus is busy),
// generate the necessary STOP condition.
//
if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase))
{
//
// Issue a STOP.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_SEND_ERROR_STOP);
}
else
{
//
// Clear the transfer in progress flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 0;
}
//
// Check for ACK errors.
//
if(ui32ErrorStatus & I2C_MCS_ADRACK)
{
//
// Return to caller.
//
return(SMBUS_ADDR_ACK_ERROR);
}
else if(ui32ErrorStatus & I2C_MCS_DATACK)
{
//
// Return to caller.
//
return(SMBUS_DATA_ACK_ERROR);
}
else
{
//
// Return to caller. Should never get here.
//
return(SMBUS_MASTER_ERROR);
}
}
//
// If no error conditions, determine what to do based on the state.
//
switch(psSMBus->ui8MasterState)
{
//
// The idle state. This state should only be reached after the last
// byte of a master transmit.
//
case SMBUS_STATE_IDLE:
{
//
// If the peripheral is not busy clear the transfer in progress
// flag. This means that the peripheral has given up the bus,
// most likely due to the end of a transmit operation.
//
if(!MAP_I2CMasterBusy(psSMBus->ui32I2CBase))
{
//
// Clear the transfer in progress flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 0;
}
//
// This state is done.
//
break;
}
//
// When using a block write, the transfer size must be sent before the
// data payload.
//
case SMBUS_STATE_WRITE_BLOCK_SIZE:
{
//
// Write the block write size to the data register.
//
MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->ui8TxSize);
//
// Continue the burst write.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_SEND_CONT);
//
// The next data byte is from the data payload.
//
if((psSMBus->ui8TxSize == 1) &&
!(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)))
{
psSMBus->ui8MasterState = SMBUS_STATE_WRITE_FINAL;
}
else
{
psSMBus->ui8MasterState = SMBUS_STATE_WRITE_NEXT;
}
//
// This state is done.
//
break;
}
//
// The state for the middle of a burst write.
//
case SMBUS_STATE_WRITE_NEXT:
{
//
// Write the next byte to the data register.
//
MAP_I2CMasterDataPut(psSMBus->ui32I2CBase,
psSMBus->pui8TxBuffer[psSMBus->ui8TxIndex++]);
//
// Continue the burst write.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_SEND_CONT);
//
// Determine the next state based on the values of the PEC and
// process call flags.
//
//
// If PEC is active and process call is not active.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// If a process call, there is no PEC byte on the transmit.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL))
{
//
// Check to see if the TX index is equal to size minus 1.
//
if(psSMBus->ui8TxIndex == (psSMBus->ui8TxSize - 1))
{
psSMBus->ui8MasterState = SMBUS_STATE_WRITE_FINAL;
}
}
else
{
//
// If the TX index is the same as the size, we're done.
//
if(psSMBus->ui8TxIndex == psSMBus->ui8TxSize)
{
psSMBus->ui8MasterState = SMBUS_STATE_WRITE_FINAL;
}
}
}
//
// If PEC is not used, regardless of whether this is a process
// call.
//
else
{
//
// Check to see if the TX index is equal to the size minus 1.
//
if(psSMBus->ui8TxIndex == (psSMBus->ui8TxSize - 1))
{
psSMBus->ui8MasterState = SMBUS_STATE_WRITE_FINAL;
}
}
//
// This state is done.
//
break;
}
//
// The state for the final write of a burst sequence.
//
case SMBUS_STATE_WRITE_FINAL:
{
//
// Determine what data to write to the data register based
// on the values of the PEC and process call flags.
//
//
// If PEC is active, write the PEC byte to the data register.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// If a process call is active, send data, not CRC.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL))
{
//
// Write the final byte from TX buffer to the data
// register.
//
MAP_I2CMasterDataPut(psSMBus->ui32I2CBase,
psSMBus->pui8TxBuffer[psSMBus->
ui8TxIndex++]);
}
else
{
//
// Write the calculated CRC (PEC) byte to the data
// register.
//
MAP_I2CMasterDataPut(psSMBus->ui32I2CBase,
psSMBus->ui8CalculatedCRC);
}
}
else
{
//
// Write the final byte from TX buffer to the data register.
//
MAP_I2CMasterDataPut(psSMBus->ui32I2CBase,
psSMBus->pui8TxBuffer[psSMBus->
ui8TxIndex++]);
}
//
// If a process call is active, send out the repeated start to
// begin the RX portion.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL))
{
//
// Move to the read first "turnaround" state.
//
psSMBus->ui8MasterState = SMBUS_STATE_READ_FIRST;
//
// Continue the burst write.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_SEND_CONT);
}
else
{
//
// Finish the burst write.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_SEND_FINISH);
//
// Since we end the transaction after the last byte is sent,
// the next state is idle.
//
psSMBus->ui8MasterState = SMBUS_STATE_IDLE;
}
//
// This state is done.
//
break;
}
//
// The state for a single byte read.
//
case SMBUS_STATE_READ_ONE:
{
//
// Put the I2C master into receive mode.
//
MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase,
psSMBus->ui8TargetSlaveAddress, true);
//
// Perform a single byte read.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_SINGLE_RECEIVE);
//
// The next state is the wait for final read state.
//
psSMBus->ui8MasterState = SMBUS_STATE_READ_WAIT;
//
// This state is done.
//
break;
}
//
// The state for the start of a burst read.
//
case SMBUS_STATE_READ_FIRST:
{
//
// Put the I2C master into receive mode.
//
MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase,
psSMBus->ui8TargetSlaveAddress, true);
//
// Handle the case where PEC is used.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// Add the target address and R/S bit to the running CRC
// calculation.
//
ui8TempData =
((psSMBus->ui8TargetSlaveAddress << 1) & 0xfe) | 1;
//
// Update the calculated CRC value in the configuration
// structure.
//
psSMBus->ui8CalculatedCRC =
MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, &ui8TempData, 1);
//
// Set the next state in the state machine.
//
if(psSMBus->ui8RxSize > 1)
{
//
// If this is a block transfer, the next state is to read
// back the number of bytes that the slave will be sending.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER))
{
psSMBus->ui8MasterState = SMBUS_STATE_READ_BLOCK_SIZE;
}
//
// For every other case...
//
else
{
psSMBus->ui8MasterState = SMBUS_STATE_READ_NEXT;
}
}
//
// If 1 byte remains, move to the final read state.
//
else
{
psSMBus->ui8MasterState = SMBUS_STATE_READ_FINAL;
}
}
else
{
//
// Set the next state in the state machine.
//
if(psSMBus->ui8RxSize > 2)
{
//
// If this is a block transfer, the next state is to read
// back the number of bytes that the slave will be sending.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER))
{
psSMBus->ui8MasterState = SMBUS_STATE_READ_BLOCK_SIZE;
}
//
// For every other case...
//
else
{
psSMBus->ui8MasterState = SMBUS_STATE_READ_NEXT;
}
}
//
// If 2 bytes remain, move to the final read state.
//
else
{
psSMBus->ui8MasterState = SMBUS_STATE_READ_FINAL;
}
}
//
// Start the burst receive.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_RECEIVE_START);
//
// This state is done.
//
break;
}
//
// The state for the size of a block read.
//
case SMBUS_STATE_READ_BLOCK_SIZE:
{
//
// Update the RX size with the data byte.
//
psSMBus->ui8RxSize = MAP_I2CMasterDataGet(psSMBus->ui32I2CBase);
//
// If more than 32 bytes are going to be sent, error.
//
if((psSMBus->ui8RxSize > 32) || (psSMBus->ui8RxSize == 0))
{
//
// Set the next state.
//
psSMBus->ui8MasterState = SMBUS_STATE_READ_ERROR_STOP;
//
// If too many or too few bytes, error.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_SINGLE_RECEIVE);
//
// Break from this case.
//
break;
}
//
// If PEC is enabled, add the size byte to the calculation and
// add one to the size variable to account for the extra PEC byte.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// Calculate the new CRC and update configuration structure.
//
psSMBus->ui8CalculatedCRC =
MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
&psSMBus->ui8RxSize, 1);
}
//
// Update the state machine.
//
switch(psSMBus->ui8RxSize)
{
//
// 1 byte remaining.
//
case 1:
{
//
// If only one byte remains and PEC, go to the second
// to last byte state.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
psSMBus->ui8MasterState = SMBUS_STATE_READ_FINAL;
}
//
// If only one byte remains and no PEC, end the burst
// transfer.
//
else
{
psSMBus->ui8MasterState = SMBUS_STATE_READ_WAIT;
}
//
// This switch is done.
//
break;
}
//
// 2 bytes remaining.
//
case 2:
{
//
// If two bytes and PEC remain, move to read next
// state.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
psSMBus->ui8MasterState = SMBUS_STATE_READ_NEXT;
}
//
// If two bytes remain, move to the final read state.
//
else
{
psSMBus->ui8MasterState = SMBUS_STATE_READ_FINAL;
}
//
// This switch is done.
//
break;
}
//
// For every other situation (in other words, remaining bytes
// is greater than 2).
//
default:
{
//
// If more than 2 bytes to read, move to the next byte
// state.
//
psSMBus->ui8MasterState = SMBUS_STATE_READ_NEXT;
//
// This switch is done.
//
break;
}
}
//
// Determine how to step the I2C state machine.
//
if((psSMBus->ui8RxSize == 1) &&
!HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// If exactly 1 byte remains, read the byte and send a STOP.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_SEND_FINISH);
}
else
{
//
// Otherwise, continue the burst read.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_RECEIVE_CONT);
}
//
// This state is done.
//
break;
}
//
// The state for the middle of a burst read.
//
case SMBUS_STATE_READ_NEXT:
{
//
// Check for a buffer overrun.
//
if(psSMBus->ui8RxIndex >= psSMBus->ui8RxSize)
{
//
// Dummy read of data register.
//
ui8TempData = MAP_I2CMasterDataGet(psSMBus->ui32I2CBase);
//
// If too many or too few bytes, error.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_RECEIVE_FINISH);
//
// Set the next state.
//
psSMBus->ui8MasterState = SMBUS_STATE_READ_ERROR_STOP;
//
// Break from this case.
//
break;
}
//
// Read the received character.
//
psSMBus->pui8RxBuffer[psSMBus->ui8RxIndex] =
MAP_I2CMasterDataGet(psSMBus->ui32I2CBase);
//
// Continue the burst read.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_RECEIVE_CONT);
//
// If PEC is enabled, add the received byte to the calculation.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// Calculate the new CRC and update configuration structure.
//
psSMBus->ui8CalculatedCRC =
MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
&psSMBus->pui8RxBuffer[psSMBus->ui8RxIndex],
1);
//
// Increment the receive buffer index.
//
psSMBus->ui8RxIndex++;
//
// If there is 1 byte remaining, make next state be the
// end of burst read state.
//
if((psSMBus->ui8RxSize - psSMBus->ui8RxIndex) == 1)
{
psSMBus->ui8MasterState = SMBUS_STATE_READ_FINAL;
}
}
else
{
//
// Increment the receive buffer index.
//
psSMBus->ui8RxIndex++;
//
// If there are two bytes remaining, make next state be the
// end of burst read state.
//
if((psSMBus->ui8RxSize - psSMBus->ui8RxIndex) == 2)
{
psSMBus->ui8MasterState = SMBUS_STATE_READ_FINAL;
}
}
//
// This state is done.
//
break;
}
//
// The state for the end of a burst read.
//
case SMBUS_STATE_READ_FINAL:
{
//
// Check for a buffer overrun.
//
if(psSMBus->ui8RxIndex >= psSMBus->ui8RxSize)
{
//
// Dummy read of data register.
//
ui8TempData = MAP_I2CMasterDataGet(psSMBus->ui32I2CBase);
//
// If too many or too few bytes, error.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_RECEIVE_FINISH);
//
// Set the next state.
//
psSMBus->ui8MasterState = SMBUS_STATE_READ_ERROR_STOP;
//
// Break from this case.
//
break;
}
//
// Read the received character.
//
psSMBus->pui8RxBuffer[psSMBus->ui8RxIndex] =
MAP_I2CMasterDataGet(psSMBus->ui32I2CBase);
//
// The next state is the wait for final read state.
//
psSMBus->ui8MasterState = SMBUS_STATE_READ_WAIT;
//
// Finish the burst read.
//
MAP_I2CMasterControl(psSMBus->ui32I2CBase,
I2C_MASTER_CMD_BURST_RECEIVE_FINISH);
//
// If PEC is enabled, add the received byte to the calculation.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// Calculate the new CRC and update configuration structure.
//
psSMBus->ui8CalculatedCRC =
MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
&psSMBus->pui8RxBuffer[psSMBus->ui8RxIndex],
1);
}
//
// Increment the receive buffer index.
//
psSMBus->ui8RxIndex++;
//
// This state is done.
//
break;
}
//
// This state is for the final read of a single or burst read.
//
case SMBUS_STATE_READ_WAIT:
{
//
// Read the received byte.
//
ui8TempData = MAP_I2CMasterDataGet(psSMBus->ui32I2CBase);
//
// If PEC is enabled, check the value that just came in to see
// if it matches.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// Check for a buffer overrun.
//
if(psSMBus->ui8RxIndex > psSMBus->ui8RxSize)
{
//
// Clear the transfer in progress flag.
//
HWREGBITB(&psSMBus->ui16Flags,
FLAG_TRANSFER_IN_PROGRESS) = 0;
//
// Return the error condition.
//
return(SMBUS_DATA_SIZE_ERROR);
}
//
// Store the received CRC byte.
//
psSMBus->ui8ReceivedCRC = ui8TempData;
//
// If the CRC doesn't match, send a NACK and indicate the
// failure to the application.
//
if(psSMBus->ui8ReceivedCRC != psSMBus->ui8CalculatedCRC)
{
//
// Clear the transfer in progress flag.
//
HWREGBITB(&psSMBus->ui16Flags,
FLAG_TRANSFER_IN_PROGRESS) = 0;
//
// Return the error condition.
//
return(SMBUS_PEC_ERROR);
}
}
else
{
//
// Check for a buffer overrun.
//
if(psSMBus->ui8RxIndex >= psSMBus->ui8RxSize)
{
//
// Clear the transfer in progress flag.
//
HWREGBITB(&psSMBus->ui16Flags,
FLAG_TRANSFER_IN_PROGRESS) = 0;
//
// Return the error condition.
//
return(SMBUS_DATA_SIZE_ERROR);
}
//
// Read the received byte.
//
psSMBus->pui8RxBuffer[psSMBus->ui8RxIndex] = ui8TempData;
//
// Increment the receive buffer index.
//
psSMBus->ui8RxIndex++;
}
//
// The state machine is now idle.
//
psSMBus->ui8MasterState = SMBUS_STATE_IDLE;
//
// Clear the transfer in progress flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 0;
//
// This state is done.
//
break;
}
//
// This state is for a transaction that needed to end due to a
// size error.
//
case SMBUS_STATE_READ_ERROR_STOP:
{
//
// Dummy read the received byte.
//
ui8TempData = MAP_I2CMasterDataGet(psSMBus->ui32I2CBase);
//
// The state machine is now idle.
//
psSMBus->ui8MasterState = SMBUS_STATE_IDLE;
//
// Clear the transfer in progress flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 0;
//
// Return the error condition.
//
return(SMBUS_DATA_SIZE_ERROR);
}
}
//
// Return to caller.
//
return(SMBUS_OK);
}
//*****************************************************************************
//
//! Enables the appropriate master interrupts for stack processing.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! This function enables the I2C interrupts used by the SMBus master. Both
//! the peripheral-level and NVIC-level interrupts are enabled.
//! SMBusMasterInit() must be called before this function because this function
//! relies on the I2C base address being defined.
//!
//! \return None.
//
//*****************************************************************************
void
SMBusMasterIntEnable(tSMBus *psSMBus)
{
//
// Enable the master interrupts.
//
MAP_I2CMasterIntEnableEx(psSMBus->ui32I2CBase, I2C_MASTER_INT_DATA |
I2C_MASTER_INT_TIMEOUT);
//
// Enable the interrupt in the NVIC.
//
switch(psSMBus->ui32I2CBase)
{
case I2C0_BASE:
{
MAP_IntEnable(INT_I2C0);
break;
}
case I2C1_BASE:
{
MAP_IntEnable(INT_I2C1);
break;
}
case I2C2_BASE:
{
if(CLASS_IS_BLIZZARD)
{
MAP_IntEnable(INT_I2C2_BLIZZARD);
}
break;
}
case I2C3_BASE:
{
if(CLASS_IS_BLIZZARD)
{
MAP_IntEnable(INT_I2C3_BLIZZARD);
}
break;
}
case I2C4_BASE:
{
if(CLASS_IS_BLIZZARD)
{
MAP_IntEnable(INT_I2C4_BLIZZARD);
}
break;
}
case I2C5_BASE:
{
if(CLASS_IS_BLIZZARD)
{
MAP_IntEnable(INT_I2C5_BLIZZARD);
}
break;
}
}
}
//*****************************************************************************
//
//! Initializes an I2C master peripheral for SMBus functionality.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param ui32I2CBase specifies the base address of the I2C master peripheral.
//! \param ui32SMBusClock specifies the system clock speed of the MCU.
//!
//! This function initializes an I2C peripheral for SMBus master use. The
//! instance-specific configuration structure is initialized to a set of known
//! values and the I2C peripheral is configured for 100kHz use, which is
//! required by the SMBus specification.
//!
//! \return None.
//
//*****************************************************************************
void
SMBusMasterInit(tSMBus *psSMBus, uint32_t ui32I2CBase,
uint32_t ui32SMBusClock)
{
//
// Initialize the configuration structure.
//
psSMBus->pUDID = 0;
psSMBus->ui32I2CBase = ui32I2CBase;
psSMBus->ui16Flags = 0;
psSMBus->ui8MasterState = SMBUS_STATE_IDLE;
psSMBus->ui8OwnSlaveAddress = 0;
psSMBus->ui8TargetSlaveAddress = 0;
psSMBus->ui8CurrentCommand = 0;
psSMBus->ui8CalculatedCRC = 0;
psSMBus->ui8TxSize = 0;
psSMBus->ui8TxIndex = 0;
psSMBus->ui8RxSize = 0;
psSMBus->ui8RxIndex = 0;
//
// Enable and initialize the I2C master module Using the system clock.
// The I2C transfer rate will always be 100kHz since fast mode is not
// supported by SMBus.
//
MAP_I2CMasterInitExpClk(psSMBus->ui32I2CBase, ui32SMBusClock, false);
//
// Configure bus timeout to 25ms. 12-bit value for 25ms is 0x9C4 (2500
// clocks), so round upper 8 bits to 0x9C. Each clock is 10us since
// 100kHz I2C is required for SMBus.
//
MAP_I2CMasterTimeoutSet(psSMBus->ui32I2CBase, 0x9C);
}
//*****************************************************************************
//
//! Slave ISR processing function for the SMBus application.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! This function must be called in the application interrupt service routine
//! (ISR) to process SMBus slave interrupts.
//!
//! If manual acknowledge is enabled using SMBusSlaveManualACKEnable(), this
//! function processes the data byte, but does not send the ACK/NACK value. In
//! this case, the user application is responsible for sending the acknowledge
//! bit based on the return code of this function.
//!
//! When receiving a Quick Command from the master, the slave has some set-up
//! requirements. When the master sends the R/S (data) bit as '0', nothing
//! additional needs to be done in the slave and SMBusSlaveIntProcess() returns
//! \b SMBUS_SLAVE_QCMD_0. However, when the master sends the R/S (data) bit
//! as '1', the slave must write the data register with data containing a '1'
//! in bit 7. This means that when receiving a Quick Command, the slave must
//! set up the TX buffer to either have 1 data byte with bit 7 set to '1' or
//! set up the TX buffer to be zero length. In the case where 1 data byte is
//! put in the TX buffer, SMBusSlaveIntProcess() returns \b SMBUS_OK the first
//! time its called and \b SMBUS_SLAVE_QCMD_0 the second. In the case where
//! the TX buffer has no data, SMBusSlaveIntProcess() will return
//! \b SMBUS_SLAVE_ERROR the first time its called, and \b SMBUS_SLAVE_QCMD_1
//! the second time.
//!
//! \return Returns \b SMBUS_SLAVE_FIRST_BYTE if the first byte (typically the
//! SMBus command) has been received; \b SMBUS_SLAVE_NOT_READY if the slave's
//! transmit buffer is not yet initialized when the master requests data from
//! the slave; \b SMBUS_DATA_SIZE_ERROR if during a master block write, the
//! size sent by the master is greater than the amount of available space in
//! the receive buffer; \b SMBUS_SLAVE_ERROR if a buffer overrun is detected
//! during a slave receive operation or if data is sent and was not expected;
//! \b SMBUS_SLAVE_QCMD_0 if a Quick Command was received with data '0';
//! \b SMBUS_SLAVE_QCMD_1 if a Quick Command was received with data '1';
//! \b SMBUS_TRANSFER_COMPLETE if a STOP is detected on the bus, marking the
//! end of a transfer; \b SMBUS_PEC_ERROR if the received PEC byte does not
//! match the locally calculated value; or \b SMBUS_OK if processing finished
//! successfully.
//
//*****************************************************************************
tSMBusStatus
SMBusSlaveIntProcess(tSMBus *psSMBus)
{
uint32_t ui32InterruptStatus;
uint32_t ui32SlaveStatus = 0;
uint8_t ui8CRCTemp;
uint8_t ui8DataTemp;
//
// Determine which interrupt was asserted.
//
ui32InterruptStatus = I2CSlaveIntStatusEx(psSMBus->ui32I2CBase, true);
//
// Check the status register.
//
ui32SlaveStatus = I2CSlaveStatus(psSMBus->ui32I2CBase);
//
// Check for the START interrupt.
//
if(ui32InterruptStatus & I2C_SLAVE_INT_START)
{
//
// Clear the interrupt.
//
I2CSlaveIntClearEx(psSMBus->ui32I2CBase, I2C_SLAVE_INT_START);
//
// This interrupt is not supported outside of using the FIFO.
//
return(SMBUS_OK);
}
//
// Check for the STOP interrupt.
//
if(ui32InterruptStatus & I2C_SLAVE_INT_STOP)
{
//
// Make sure the transfer in progress flag is cleared. In the case
// of Quick Command, it should never be set, so this is safe.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 0;
//
// Clear the interrupt.
//
I2CSlaveIntClearEx(psSMBus->ui32I2CBase, I2C_SLAVE_INT_STOP);
//
// Check to see if a Quick Command was sent.
//
if(ui32SlaveStatus & 0x10)
{
//
// Make sure the TX/RX index is 0. If not, we should not be here.
// Other data should not have been sent or received during a Quick
// Command.
//
if((psSMBus->ui8RxIndex != 0) || (psSMBus->ui8TxIndex != 0))
{
//
// Return an error.
//
return(SMBUS_SLAVE_ERROR);
}
//
// Tell caller a Quick Command has occurred and the data value.
//
if(ui32SlaveStatus & 0x20)
{
return(SMBUS_SLAVE_QCMD_1);
}
else
{
return(SMBUS_SLAVE_QCMD_0);
}
}
//
// Move to the idle state.
//
psSMBus->ui8SlaveState = SMBUS_STATE_IDLE;
//
// Return end of transfer.
//
return(SMBUS_TRANSFER_COMPLETE);
}
//
// Check for the DATA interrupt.
//
if(ui32InterruptStatus & I2C_SLAVE_INT_DATA)
{
//
// Clear the I2C interrupt.
//
I2CSlaveIntClearEx(psSMBus->ui32I2CBase, I2C_SLAVE_INT_DATA);
//
// Make sure that at least one of the relevant status bits is set.
//
if(!(ui32SlaveStatus & 0x07))
{
//
// No status bits were set - this is bad. Should never get here.
//
return(SMBUS_SLAVE_ERROR);
}
//
// Every time this interrupt occurs, a transfer is in progress. Make
// sure the flag is set appropriately.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1;
//
// Handle the request type.
//
switch((ui32SlaveStatus & 0x07))
{
//
// The first byte after the slave's own address has been received.
// This is almost always the command byte in SMBus. The only
// exception is when the Send Byte protocol is used by the master.
//
case I2C_SLAVE_ACT_RREQ_FBR:
{
//
// Check which slave address was called out. Set the active
// address to the matched address.
//
if(I2CSlaveStatus(psSMBus->ui32I2CBase) & I2C_SCSR_OAR2SEL)
{
psSMBus->ui8OwnSlaveAddress =
HWREG(psSMBus->ui32I2CBase + I2C_O_SOAR2) & 0x7f;
}
else
{
psSMBus->ui8OwnSlaveAddress =
HWREG(psSMBus->ui32I2CBase + I2C_O_SOAR);
}
//
// If raw I2C, data goes into buffer.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C))
{
psSMBus->pui8RxBuffer[psSMBus->ui8RxIndex++] =
I2CSlaveDataGet(psSMBus->ui32I2CBase);
}
//
// Read the first byte into the ui8CurrentCommand member.
//
else
{
psSMBus->ui8CurrentCommand =
I2CSlaveDataGet(psSMBus->ui32I2CBase);
}
//
// If PEC is enabled, add the address to the CRC calculation.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// Add the address to the CRC calculation. In this case
// R/S will always be 0. Also, this is the start of the
// CRC calculation, so the initial value is 0.
//
ui8CRCTemp = psSMBus->ui8OwnSlaveAddress << 1;
//
// Calculate new CRC.
//
psSMBus->ui8CalculatedCRC = Crc8CCITT(0, &ui8CRCTemp, 1);
//
// Add the data byte (ui8CurrentCommand) to the CRC
// calculation.
//
psSMBus->ui8CalculatedCRC =
MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
&psSMBus->ui8CurrentCommand, 1);
}
//
// Update the state machine.
//
psSMBus->ui8SlaveState = SMBUS_STATE_SLAVE_POST_COMMAND;
//
// Actions for this case are complete.
//
return(SMBUS_SLAVE_FIRST_BYTE);
}
//
// A data byte other than the first data byte has been received.
//
case I2C_SLAVE_ACT_RREQ:
{
//
// Determine what to do based on the current state.
//
switch(psSMBus->ui8SlaveState)
{
//
// Receive first post-command byte.
//
case SMBUS_STATE_SLAVE_POST_COMMAND:
{
//
// Read the data into the a temporary variable.
//
ui8DataTemp = I2CSlaveDataGet(psSMBus->ui32I2CBase);
//
// Check if this is a block transfer.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER))
{
//
// Make sure there is enough space in the buffer.
// If not, NACK. If there is, overwrite the
// current size with the size sent by the master.
//
if(ui8DataTemp > psSMBus->ui8RxSize)
{
//
// Update the state machine.
//
psSMBus->ui8SlaveState = SMBUS_STATE_READ_DONE;
//
// Indicate a size error.
//
return(SMBUS_DATA_SIZE_ERROR);
}
else
{
//
// Update the size.
//
psSMBus->ui8RxSize = ui8DataTemp;
//
// Check to see if PEC is enabled.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// Add the size byte to the CRC
// calculation.
//
psSMBus->ui8CalculatedCRC =
MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
&ui8DataTemp, 1);
}
//
// Update the state machine.
//
psSMBus->ui8SlaveState = SMBUS_STATE_READ_NEXT;
}
//
// This state is done.
//
break;
}
//
// If there is no data to receive and no PEC, nothing
// to do. Software should never get here.
//
if(psSMBus->ui8RxIndex == psSMBus->ui8RxSize)
{
//
// Update the state machine.
//
psSMBus->ui8SlaveState = SMBUS_STATE_READ_DONE;
//
// Report an error.
//
return(SMBUS_SLAVE_ERROR);
}
else
{
//
// Put the data in the buffer.
//
psSMBus->pui8RxBuffer[psSMBus->ui8RxIndex++] =
ui8DataTemp;
//
// If this is the last data byte.
//
if(psSMBus->ui8RxIndex == psSMBus->ui8RxSize)
{
//
// Check for PEC usage.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// Add the size byte to the CRC
// calculation.
//
psSMBus->ui8CalculatedCRC =
MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
&ui8DataTemp, 1);
//
// Update the state machine.
//
psSMBus->ui8SlaveState =
SMBUS_STATE_READ_PEC;
}
else
{
//
// Update the state machine.
//
psSMBus->ui8SlaveState =
SMBUS_STATE_READ_DONE;
}
}
//
// All other cases.
//
else
{
//
// Check for PEC usage.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// Add the size byte to the CRC
// calculation.
//
psSMBus->ui8CalculatedCRC =
MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
&ui8DataTemp, 1);
}
//
// Update the state machine.
//
psSMBus->ui8SlaveState = SMBUS_STATE_READ_NEXT;
}
}
//
// Actions for this case are complete.
//
break;
}
//
// Read the next byte into the buffer.
//
case SMBUS_STATE_READ_NEXT:
{
//
// Read the data into the a temporary variable.
//
ui8DataTemp = I2CSlaveDataGet(psSMBus->ui32I2CBase);
//
// If there is no data to receive and no PEC, nothing
// to do. Software should never get here.
//
if(psSMBus->ui8RxIndex == psSMBus->ui8RxSize)
{
//
// Update the state machine.
//
psSMBus->ui8SlaveState = SMBUS_STATE_READ_DONE;
//
// Report an error.
//
return(SMBUS_SLAVE_ERROR);
}
else
{
//
// Put the data in the buffer.
//
psSMBus->pui8RxBuffer[psSMBus->ui8RxIndex++] =
ui8DataTemp;
//
// If this is the last data byte.
//
if(psSMBus->ui8RxIndex == psSMBus->ui8RxSize)
{
//
// Check for PEC usage.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// Add the size byte to the CRC
// calculation.
//
psSMBus->ui8CalculatedCRC =
MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
&ui8DataTemp, 1);
//
// Update the state machine.
//
if(HWREGBITB(&psSMBus->ui16Flags,
FLAG_PROCESS_CALL))
{
psSMBus->ui8SlaveState =
SMBUS_STATE_READ_DONE;
}
else
{
psSMBus->ui8SlaveState =
SMBUS_STATE_READ_PEC;
}
}
else
{
//
// Update the state machine.
//
psSMBus->ui8SlaveState =
SMBUS_STATE_READ_DONE;
}
}
//
// All other cases.
//
else
{
//
// Check for PEC usage.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// Add the size byte to the CRC
// calculation.
//
psSMBus->ui8CalculatedCRC =
MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
&ui8DataTemp, 1);
}
//
// Update the state machine.
//
psSMBus->ui8SlaveState = SMBUS_STATE_READ_NEXT;
}
}
break;
}
//
// Read the PEC byte and compare it.
//
case SMBUS_STATE_READ_PEC:
{
//
// Read the data into the a temporary variable.
//
ui8DataTemp = I2CSlaveDataGet(psSMBus->ui32I2CBase);
//
// Compare PEC.
//
if(psSMBus->ui8CalculatedCRC != ui8DataTemp)
{
//
// Indicate PEC error.
//
return(SMBUS_PEC_ERROR);
}
//
// Update the state machine.
//
psSMBus->ui8SlaveState = SMBUS_STATE_READ_DONE;
break;
}
//
// No more data to receive. If we get here, read data
// into a dummy variable and NACK.
//
case SMBUS_STATE_READ_DONE:
{
//
// Read the data into the a temporary variable.
//
ui8DataTemp = I2CSlaveDataGet(psSMBus->ui32I2CBase);
//
// Report an error.
//
return(SMBUS_SLAVE_ERROR);
}
}
//
// Actions for this case are complete.
//
break;
}
//
// The master has requested that the slave transmit data back to
// master.
//
case I2C_SLAVE_ACT_TREQ:
{
//
// Initialize temporary variable that stores transmit byte to
// 0xff. If data is not set by another condition, the 0xff
// carries through. This happens if ui8TxIndex is equal to or
// greater than ui8TxSize.
//
ui8DataTemp = 0xff;
//
// Determine what to do based on the current state.
//
switch(psSMBus->ui8SlaveState)
{
//
// The state machine is currently idle, or if the last
// state was SMBUS_STATE_SLAVE_POST_COMMAND or
// SMBUS_READ_DONE, this is the first byte transmitted. In
// the case of slave post command, this means that the
// command was received followed by a repeated start (with
// R/S = 1). In the case of read next, this means that a
// raw I2C master transmit changed direction with a
// repeated start and is now a master receive. In the case
// of read done, this means that a previous master transmit
// was finished (non-command followed by a repeated start).
//
case SMBUS_STATE_IDLE:
case SMBUS_STATE_SLAVE_POST_COMMAND:
case SMBUS_STATE_READ_NEXT:
case SMBUS_STATE_READ_DONE:
{
//
// Check which slave address was called out. Set the
// active address to the matched address.
//
if(I2CSlaveStatus(psSMBus->ui32I2CBase) &
I2C_SCSR_OAR2SEL)
{
psSMBus->ui8OwnSlaveAddress =
(HWREG(psSMBus->ui32I2CBase + I2C_O_SOAR2) &
0x7f);
}
else
{
psSMBus->ui8OwnSlaveAddress =
HWREG(psSMBus->ui32I2CBase + I2C_O_SOAR);
}
//
// Check to see if the TX buffer is populated. If not,
// return not ready without writing to the data
// register.
//
if(psSMBus->ui8TxSize == 0)
{
return(SMBUS_SLAVE_NOT_READY);
}
//
// Is this a block transfer?
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER))
{
//
// The first byte to send is the size.
//
ui8DataTemp = psSMBus->ui8TxSize;
}
else
{
//
// Is there data to send?
//
if(psSMBus->ui8TxIndex < psSMBus->ui8TxSize)
{
//
// Set the transmit data to the next item in
// the buffer.
//
ui8DataTemp =
psSMBus->pui8TxBuffer[psSMBus->
ui8TxIndex++];
}
else
{
//
// Send 0xff per spec.
//
ui8DataTemp = 0xff;
}
}
//
// Check to see if PEC is required.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// Start calculating the CRC with the address.
//
ui8CRCTemp =
(psSMBus->ui8OwnSlaveAddress << 1) | 1;
//
// Add the address and R/S bit to the CRC.
//
psSMBus->ui8CalculatedCRC =
MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
&ui8CRCTemp, 1);
//
// Add the data byte to the CRC calculation.
//
psSMBus->ui8CalculatedCRC =
MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
&ui8DataTemp, 1);
//
// Move to the next state.
//
if(psSMBus->ui8TxIndex == psSMBus->ui8TxSize)
{
//
// Final byte is the CRC byte.
//
psSMBus->ui8SlaveState =
SMBUS_STATE_WRITE_FINAL;
}
else
{
//
// All other cases, move to the next byte
// state.
//
psSMBus->ui8SlaveState =
SMBUS_STATE_WRITE_NEXT;
}
}
else
{
//
// Move to the next state.
//
switch(psSMBus->ui8TxSize - psSMBus->ui8TxIndex)
{
//
// If all of the data has been sent, move to
// the done state.
//
case 0:
{
psSMBus->ui8SlaveState =
SMBUS_STATE_WRITE_DONE;
break;
}
//
// If 1 left, move to the final byte state.
//
case 1:
{
psSMBus->ui8SlaveState =
SMBUS_STATE_WRITE_FINAL;
break;
}
//
// All other cases, move to the next byte
// state.
//
default:
{
psSMBus->ui8SlaveState =
SMBUS_STATE_WRITE_NEXT;
break;
}
}
}
//
// Send the data.
//
I2CSlaveDataPut(psSMBus->ui32I2CBase, ui8DataTemp);
//
// This state is done.
//
break;
}
//
// The first byte has already been sent, handle the rest.
//
case SMBUS_STATE_WRITE_NEXT:
{
//
// Set the transmit data to the next item in the
// buffer.
//
ui8DataTemp =
psSMBus->pui8TxBuffer[psSMBus->ui8TxIndex++];
//
// Check to see if PEC is required.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// Add the byte to the CRC calculation.
//
psSMBus->ui8CalculatedCRC =
MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
&ui8DataTemp, 1);
//
// Check if it's time to move to the next state.
//
if(psSMBus->ui8TxIndex == psSMBus->ui8TxSize)
{
//
// Final byte is the CRC byte.
//
psSMBus->ui8SlaveState =
SMBUS_STATE_WRITE_FINAL;
}
}
else
{
//
// Move to the next state.
//
if((psSMBus->ui8TxSize - psSMBus->ui8TxIndex) == 1)
{
//
// If only 1 byte remains, move to the final
// state.
//
psSMBus->ui8SlaveState =
SMBUS_STATE_WRITE_FINAL;
}
}
//
// Send the data.
//
I2CSlaveDataPut(psSMBus->ui32I2CBase, ui8DataTemp);
//
// This state is done.
//
break;
}
//
// Write the final byte, whether PEC or data.
//
case SMBUS_STATE_WRITE_FINAL:
{
//
// Check to see if PEC is required.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// Send the CRC byte.
//
ui8DataTemp = psSMBus->ui8CalculatedCRC;
}
else
{
//
// Send the last data byte.
//
ui8DataTemp =
psSMBus->pui8TxBuffer[psSMBus->ui8TxIndex++];
}
//
// Send the data.
//
I2CSlaveDataPut(psSMBus->ui32I2CBase, ui8DataTemp);
//
// Move to the write done state.
//
psSMBus->ui8SlaveState = SMBUS_STATE_WRITE_DONE;
//
// This state is done.
//
break;
}
//
// All data has been sent, send 0xff.
//
case SMBUS_STATE_WRITE_DONE:
{
//
// Send 0xff because there is no more data to send.
//
I2CSlaveDataPut(psSMBus->ui32I2CBase, 0xff);
//
// This state is done.
//
break;
}
}
//
// Actions for this case are complete.
//
break;
}
}
//
// Return OK status.
//
return(SMBUS_OK);
}
//
// Return OK. Should never get here.
//
return(SMBUS_OK);
}
//*****************************************************************************
//
//! Sends data outside of the interrupt processing function.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! This function sends data outside the interrupt processing function, and
//! should only be used when SMBusSlaveIntProcess() returns
//! \b SMBUS_SLAVE_NOT_READY. At this point, the application should set up the
//! transfer and call this function (it assumes that the transmit buffer has
//! already been populated when called). When called, this function updates
//! the slave state machine as if SMBusSlaveIntProcess() were called.
//!
//! \return Returns \b SMBUS_SLAVE_NOT_READY if the slave's transmit buffer is
//! not yet initialized (ui8TxSize is 0), or \b SMBUS_OK if processing finished
//! successfully.
//
//*****************************************************************************
tSMBusStatus
SMBusSlaveDataSend(tSMBus *psSMBus)
{
uint8_t ui8CRCTemp;
uint8_t ui8DataTemp;
//
// Check to see if the TX buffer is populated. If not,
// return not ready without writing to the data register.
//
if(psSMBus->ui8TxSize == 0)
{
return(SMBUS_SLAVE_NOT_READY);
}
//
// Is this a block transfer?
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER))
{
//
// The first byte to send is the size.
//
ui8DataTemp = psSMBus->ui8TxSize;
}
else
{
//
// Is there data to send?
//
if(psSMBus->ui8TxIndex < psSMBus->ui8TxSize)
{
//
// Set the transmit data to the next item in
// the buffer.
//
ui8DataTemp = psSMBus->pui8TxBuffer[psSMBus->ui8TxIndex++];
}
else
{
//
// Send 0xff per spec. Should not get here.
//
ui8DataTemp = 0xff;
}
}
//
// Check to see if PEC is required.
//
if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))
{
//
// Start calculating the CRC with the address.
//
ui8CRCTemp = (psSMBus->ui8OwnSlaveAddress << 1) | 1;
//
// Add the address and R/S bit to the CRC.
//
psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
&ui8CRCTemp, 1);
//
// Add the data byte to the CRC calculation.
//
psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC,
&ui8DataTemp, 1);
//
// Move to the next state.
//
if(psSMBus->ui8TxIndex == psSMBus->ui8TxSize)
{
//
// Final byte is the CRC byte.
//
psSMBus->ui8SlaveState = SMBUS_STATE_WRITE_FINAL;
}
else
{
//
// All other cases, move to the next byte state.
//
psSMBus->ui8SlaveState = SMBUS_STATE_WRITE_NEXT;
}
}
else
{
//
// Move to the next state.
//
switch(psSMBus->ui8TxSize - psSMBus->ui8TxIndex)
{
//
// If all of the data has been sent, move to the
// done state.
//
case 0:
{
psSMBus->ui8SlaveState = SMBUS_STATE_WRITE_DONE;
break;
}
//
// If 1 left, move to the final byte state.
//
case 1:
{
psSMBus->ui8SlaveState = SMBUS_STATE_WRITE_FINAL;
break;
}
//
// All other cases, move to the next byte state.
//
default:
{
psSMBus->ui8SlaveState = SMBUS_STATE_WRITE_NEXT;
break;
}
}
}
//
// Send the data.
//
I2CSlaveDataPut(psSMBus->ui32I2CBase, ui8DataTemp);
//
// Return to caller.
//
return(SMBUS_OK);
}
//*****************************************************************************
//
//! Set the address and size of the slave transmit buffer.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param pui8Data is a pointer to the transmit data buffer.
//! \param ui8Size is the number of bytes in the buffer.
//!
//! This function sets the address and size of the slave transmit buffer.
//!
//! \return None.
//
//*****************************************************************************
void
SMBusSlaveTxBufferSet(tSMBus *psSMBus, uint8_t *pui8Data,
uint8_t ui8Size)
{
//
// Set the trasmit buffer.
//
psSMBus->pui8TxBuffer = pui8Data;
//
// Set the size.
//
psSMBus->ui8TxSize = ui8Size;
}
//*****************************************************************************
//
//! Set the address and size of the slave receive buffer.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param pui8Data is a pointer to the receive data buffer.
//! \param ui8Size is the number of bytes in the buffer.
//!
//! This function sets the address and size of the slave receive buffer.
//!
//! \return None.
//
//*****************************************************************************
void
SMBusSlaveRxBufferSet(tSMBus *psSMBus, uint8_t *pui8Data,
uint8_t ui8Size)
{
//
// Set the receive buffer.
//
psSMBus->pui8RxBuffer = pui8Data;
//
// Set the size.
//
psSMBus->ui8RxSize = ui8Size;
}
//*****************************************************************************
//
//! Get the current command byte.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! Returns the current value of the ui8CurrentCommand variable in the SMBus
//! configuration structure. This can be used to help the user application
//! set up the SMBus slave transmit and receive buffers.
//!
//! \return None.
//
//*****************************************************************************
uint8_t
SMBusSlaveCommandGet(tSMBus *psSMBus)
{
//
// Return the current command.
//
return(psSMBus->ui8CurrentCommand);
}
//*****************************************************************************
//
//! Sets the process call flag for an SMBus slave transfer.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! Sets the process call flag in the configuration structure so that the SMBus
//! slave can respond correctly to a Process Call request. This flag must be
//! set prior to the data portion of the packet.
//!
//! \return None.
//
//*****************************************************************************
void
SMBusSlaveProcessCallEnable(tSMBus *psSMBus)
{
//
// Set the block transfer flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 1;
}
//*****************************************************************************
//
//! Clears the process call flag for an SMBus slave transfer.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! Clears the process call flag in the configuration structure. The user
//! application can either call this function to clear the flag, or use
//! SMBusSlaveTransferInit() to clear out all transfer-specific flags.
//!
//! \return None.
//
//*****************************************************************************
void
SMBusSlaveProcessCallDisable(tSMBus *psSMBus)
{
//
// Clear the block transfer flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0;
}
//*****************************************************************************
//
//! Sets the block transfer flag for an SMBus slave transfer.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! Sets the block transfer flag in the configuration structure so that the
//! SMBus slave can respond correctly to a Block Write or Block Read request.
//! This flag must be set prior to the data portion of the packet.
//!
//! \return None.
//
//*****************************************************************************
void
SMBusSlaveBlockTransferEnable(tSMBus *psSMBus)
{
//
// Set the block transfer flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 1;
}
//*****************************************************************************
//
//! Clears the block transfer flag for an SMBus slave transfer.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! Clears the block transfer flag in the configuration structure. The user
//! application can either call this function to clear the flag, or use
//! SMBusSlaveTransferInit() to clear out all transfer-specific flags.
//!
//! \return None.
//
//*****************************************************************************
void
SMBusSlaveBlockTransferDisable(tSMBus *psSMBus)
{
//
// Clear the block transfer flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0;
}
//*****************************************************************************
//
//! Sets the ``raw'' I2C flag for an SMBus slave transfer.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! Sets the raw I2C flag in the configuration structure so that the
//! SMBus slave can respond correctly to raw I2C (non-SMBus protocol) requests.
//! This flag must be set prior to the transfer, and is a global setting.
//!
//! \return None.
//
//*****************************************************************************
void
SMBusSlaveI2CEnable(tSMBus *psSMBus)
{
//
// Set the block transfer flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 1;
}
//*****************************************************************************
//
//! Clears the ``raw'' I2C flag for an SMBus slave transfer.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! Clears the raw I2C flag in the configuration structure. This flag is a
//! global setting similar to the PEC flag and cannot be cleared using
//! SMBusSlaveTransferInit().
//!
//! \return None.
//
//*****************************************************************************
void
SMBusSlaveI2CDisable(tSMBus *psSMBus)
{
//
// Clear the block transfer flag.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0;
}
//*****************************************************************************
//
//! Sets the value of the AR (Address Resolved) flag.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param bValue is the value to set the flag.
//!
//! This function allows the application to set the value of the AR flag. All
//! SMBus slaves must support the AR and AV flags. On POR, the AR flag is
//! cleared. It is also cleared when a slave receives the ARP Reset Device
//! command.
//!
//! \return None.
//
//*****************************************************************************
void
SMBusSlaveARPFlagARSet(tSMBus *psSMBus, bool bValue)
{
//
// Set the block address resolved flag to the desired value.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_ADDRESS_RESOLVED) = bValue;
}
//*****************************************************************************
//
//! Returns the current value of the AR (Address Resolved) flag.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! This returns the value of the AR (Address Resolved) flag.
//!
//! \return Returns \b true if set, \b false if cleared.
//
//*****************************************************************************
bool
SMBusSlaveARPFlagARGet(tSMBus *psSMBus)
{
//
// Get the value of the block address resolved flag.
//
return(HWREGBITB(&psSMBus->ui16Flags, FLAG_ADDRESS_RESOLVED));
}
//*****************************************************************************
//
//! Sets the value of the AV (Address Valid) flag.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param bValue is the value to set the flag.
//!
//! This function allows the application to set the value of the AV flag. All
//! SMBus slaves must support the AR and AV flags. On POR, the AV flag is
//! cleared. It is also cleared when a slave receives the ARP Reset Device
//! command.
//!
//! \return None.
//
//*****************************************************************************
void
SMBusSlaveARPFlagAVSet(tSMBus *psSMBus, bool bValue)
{
//
// Set the block address valid flag to the desired value.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_ADDRESS_VALID) = bValue;
}
//*****************************************************************************
//
//! Returns the current value of the AV (Address Valid) flag.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! This returns the value of the AV (Address Valid) flag.
//!
//! \return Returns \b true if set, or \b false if cleared.
//
//*****************************************************************************
bool
SMBusSlaveARPFlagAVGet(tSMBus *psSMBus)
{
//
// Get the value of the block address valid flag.
//
return(HWREGBITB(&psSMBus->ui16Flags, FLAG_ADDRESS_VALID));
}
//*****************************************************************************
//
//! Sets up the SMBus slave for a new transfer.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! This function is used to re-initialize the configuration structure for a
//! new transfer. Once a transfer is complete and the data has been processed,
//! unused flags, states, the data buffers and buffer indexes should be reset
//! to a known state before a new transfer.
//!
//! \return None.
//
//*****************************************************************************
void
SMBusSlaveTransferInit(tSMBus *psSMBus)
{
//
// Clear the block transfer, process call and transfer in progress flags.
//
HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0;
HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0;
HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 0;
//
// Set the configuration structure to a known, zeroed state.
//
psSMBus->ui8MasterState = SMBUS_STATE_IDLE;
psSMBus->ui8SlaveState = SMBUS_STATE_IDLE;
psSMBus->ui8CurrentCommand = 0;
psSMBus->ui8CalculatedCRC = 0;
psSMBus->ui8TxSize = 0;
psSMBus->ui8TxIndex = 0;
psSMBus->ui8RxSize = 0;
psSMBus->ui8RxIndex = 0;
}
//*****************************************************************************
//
//! Sets the value of the ACK bit when using manual acknowledgement.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param bACK specifies whether to ACK (\b true) or NACK (\b false).
//!
//! This function sets the value of the ACK bit. In order for the ACK bit to
//! take effect, manual acknowledgement must be enabled on the slave using
//! SMBusSlaveManualACKEnable().
//!
//! \return None.
//
//*****************************************************************************
void
SMBusSlaveACKSend(tSMBus *psSMBus, bool bACK)
{
//
// Send ACK or NACK based on the value of bACK.
//
if(bACK)
{
I2CSlaveACKValueSet(psSMBus->ui32I2CBase, true);
}
else
{
I2CSlaveACKValueSet(psSMBus->ui32I2CBase, false);
}
}
//*****************************************************************************
//
//! Enables manual acknowledgement for the SMBus slave.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! This function enables manual acknowledge capability in the slave. If the
//! application requires that the slave NACK on a bad command or a bad PEC
//! calculation, manual acknowledgement allows this to happen.
//!
//! In the case of responding to a bad command with a NACK, the application
//! should use SMBusSlaveACKSend() to ACK/NACK the command. The slave ISR
//! should check for the SMBUS_SLAVE_FIRST_BYTE return code from
//! SMBusSlaveISRProcess() and ACK/NACK accordingly. All other cases should be
//! handled in the application based on the return code of
//! SMBusSlaveISRProcess().
//!
//! \return None.
//
//*****************************************************************************
void
SMBusSlaveManualACKEnable(tSMBus *psSMBus)
{
//
// Enable manual acknowledge.
//
I2CSlaveACKOverride(psSMBus->ui32I2CBase, true);
}
//*****************************************************************************
//
//! Disables manual acknowledgement for the SMBus slave.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! This function disables manual acknowledge capability in the slave. When
//! manual acknowledgement is disabled, the slave automatically ACKs every
//! byte sent by the master.
//!
//! \return None.
//
//*****************************************************************************
void
SMBusSlaveManualACKDisable(tSMBus *psSMBus)
{
//
// Disable manual acknowledge.
//
I2CSlaveACKOverride(psSMBus->ui32I2CBase, false);
}
//*****************************************************************************
//
//! Returns the manual acknowledgement status of the SMBus slave.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! This function returns the state of the I2C ACKOEN bit in the I2CSACKCTL
//! register. This feature is disabled out of reset and must be enabled
//! using SMBusSlaveManualACKEnable().
//!
//! \return Returns \b true if manual acknowledge is enabled, or \b false if
//! manual acknowledge is disabled.
//
//*****************************************************************************
bool
SMBusSlaveManualACKStatusGet(tSMBus *psSMBus)
{
//
// Return the value of the bit.
//
return(HWREG(psSMBus->ui32I2CBase + I2C_O_SACKCTL) & 0x1);
}
//*****************************************************************************
//
//! Determine whether primary or secondary slave address has been requested by
//! the master.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! Tells the caller whether the I2C slave address requested by the master or
//! SMBus Host is the primary or secondary I2C slave address of the peripheral.
//! The primary is defined as the address programmed into I2CSOAR, and the
//! secondary as the address programmed into I2CSOAR2.
//!
//! \return Returns \b SMBUS_SLAVE_ADDR_PRIMARY if the primary address is
//! called out or \b SMBUS_SLAVE_ADDR_SECONDARY if the secondary address is
//! called out.
//
//*****************************************************************************
tSMBusStatus
SMBusSlaveIntAddressGet(tSMBus *psSMBus)
{
//
// Determine whether the primary or secondary address was called out.
//
if(I2CSlaveStatus(psSMBus->ui32I2CBase) & I2C_SCSR_OAR2SEL)
{
return(SMBUS_SLAVE_ADDR_SECONDARY);
}
else
{
return(SMBUS_SLAVE_ADDR_PRIMARY);
}
}
//*****************************************************************************
//
//! Enables the appropriate slave interrupts for stack processing.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//!
//! This function enables the I2C interrupts used by the SMBus slave. Both
//! the peripheral-level and NVIC-level interrupts are enabled.
//! SMBusSlaveInit() must be called before this function because this function
//! relies on the I2C base address being defined.
//!
//! \return None.
//
//*****************************************************************************
void
SMBusSlaveIntEnable(tSMBus *psSMBus)
{
//
// Enable the slave interrupts.
//
I2CSlaveIntEnableEx(psSMBus->ui32I2CBase,
I2C_SLAVE_INT_DATA | I2C_SLAVE_INT_STOP);
//
// Enable the interrupt in the NVIC.
//
switch(psSMBus->ui32I2CBase)
{
case I2C0_BASE:
{
MAP_IntEnable(INT_I2C0);
break;
}
case I2C1_BASE:
{
MAP_IntEnable(INT_I2C1);
break;
}
case I2C2_BASE:
{
if(CLASS_IS_BLIZZARD)
{
MAP_IntEnable(INT_I2C2_BLIZZARD);
}
break;
}
case I2C3_BASE:
{
if(CLASS_IS_BLIZZARD)
{
MAP_IntEnable(INT_I2C3_BLIZZARD);
}
break;
}
case I2C4_BASE:
{
if(CLASS_IS_BLIZZARD)
{
MAP_IntEnable(INT_I2C4_BLIZZARD);
}
break;
}
case I2C5_BASE:
{
if(CLASS_IS_BLIZZARD)
{
MAP_IntEnable(INT_I2C5_BLIZZARD);
}
break;
}
}
}
//*****************************************************************************
//
//! Sets the slave address for an SMBus slave peripheral.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param ui8AddressNum specifies which address (primary or secondary)
//! \param ui8SlaveAddress is the address of the slave.
//!
//! This function sets the slave address. Both the primary and secondary
//! addresses can be set using this function. To set the primary address
//! (stored in I2CSOAR), ui8AddressNum should be '0'. To set the secondary
//! address (stored in I2CSOAR2), ui8AddressNum should be '1'.
//!
//! \return None.
//
//*****************************************************************************
void
SMBusSlaveAddressSet(tSMBus *psSMBus, uint8_t ui8AddressNum,
uint8_t ui8SlaveAddress)
{
//
// Write the slave address.
//
I2CSlaveAddressSet(psSMBus->ui32I2CBase, ui8AddressNum, ui8SlaveAddress);
}
//*****************************************************************************
//
//! Sets a slave's UDID structure.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param pUDID is a pointer to the UDID configuration for the slave. This
//! is only needed if the slave is on a bus that uses ARP.
//!
//! This function sets the UDID for a slave instance.
//!
//! \return None.
//
//*****************************************************************************
void
SMBusSlaveUDIDSet(tSMBus *psSMBus, tSMBusUDID *pUDID)
{
psSMBus->pUDID = pUDID;
}
//*****************************************************************************
//
//! Initializes an I2C slave peripheral for SMBus functionality.
//!
//! \param psSMBus specifies the SMBus configuration structure.
//! \param ui32I2CBase specifies the base address of the I2C slave peripheral.
//!
//! This function initializes an I2C peripheral for SMBus slave use. The
//! instance-specific configuration structure is initialized to a set of known
//! values and the I2C peripheral is configured based on the input arguments.
//!
//! The default configuration of the SMBus slave uses automatic
//! acknowledgement. If manual acknowledgement is required, call
//! SMBusSlaveManualACKEnable().
//!
//! \return None.
//
//*****************************************************************************
void
SMBusSlaveInit(tSMBus *psSMBus, uint32_t ui32I2CBase)
{
//
// Initialize the configuration structure.
//
psSMBus->pUDID = 0;
psSMBus->ui32I2CBase = ui32I2CBase;
psSMBus->ui16Flags = 0;
psSMBus->ui8MasterState = SMBUS_STATE_IDLE;
psSMBus->ui8SlaveState = SMBUS_STATE_IDLE;
psSMBus->ui8OwnSlaveAddress = 0;
psSMBus->ui8TargetSlaveAddress = 0;
psSMBus->ui8CurrentCommand = 0;
psSMBus->ui8CalculatedCRC = 0;
psSMBus->ui8TxSize = 0;
psSMBus->ui8TxIndex = 0;
psSMBus->ui8RxSize = 0;
psSMBus->ui8RxIndex = 0;
//
// Enable the I2C slave module. The slave is always enabled because the
// SMBus spec requires that all devices respond whne their slave address
// is put on the bus.
//
I2CSlaveEnable(psSMBus->ui32I2CBase);
}
//*****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//*****************************************************************************