Hi expert,
I am completing a Design using one C2000 two SPI module connecting two ADS131. two ADS131 are clocking by one external 8M crystal.
I test every single ADS131 can work normally. And I want to enable it in the same code.
So I edit the hal.c / hal.h / ads131m0x.c , but I found it can not work together, which means I can not see the SCLK between C2000 and ADS131.
When I change the sequence of these code in main.c, I can see ADS131-1 SCLK and ADS131-2 SCLK, but not at same time.
writeSingleRegister(0x02, 0x0010);
// writeSingleRegister2(0x02, 0x0010);
writeSingleRegister(0x03, 0xFF43);// external reference ENABLED, OSR 128 32k SAMPLE RATE
// writeSingleRegister2(0x03, 0xFF43);
SysCtl_delay(10000);
initSPI2(SPIA_BASE);
SPI_clearInterruptStatus(SPIA_BASE, SPI_INT_RXFF );
SPI_resetRxFIFO(SPIA_BASE);
SPI_resetTxFIFO(SPIA_BASE);
Interrupt_enable(INT_SPIA_RX);
//
// Enable Global Interrupt (INTM) and realtime interrupt (DBGM)
SPI_resetRxFIFO(SPIA_BASE);
//
EINT;
ERTM;
readDataTx(SPIA_BASE);
//todo add SPIB INIT
///////////////SPIB INIT/////////////////////////
// TODO Change to initSPI2
writeSingleRegister2(0x02, 0x0010);
writeSingleRegister2(0x03, 0xFF43);
SysCtl_delay(10000);
initSPI2(SPIB_BASE);
// SPI_enableInterrupt(SPIA_BASE, SPI_INT_RXFF|SPI_INT_TXFF);
SPI_clearInterruptStatus(SPIB_BASE, SPI_INT_RXFF );
SPI_resetRxFIFO(SPIB_BASE);
SPI_resetTxFIFO(SPIB_BASE);
Interrupt_enable(INT_SPIB_RX);
//Interrupt_enable(INT_SPIA_TX);
// Enable Global Interrupt (INTM) and realtime interrupt (DBGM)
SPI_resetRxFIFO(SPIB_BASE);
//
EINT;
ERTM;
readDataTx(SPIB_BASE);
I almost copied the driver code for every function I used. But not for these
static uint16_t registerMap[NUM_REGISTERS]; void restoreRegisterDefaults(void)
Because I suppose it should be the same for each ADS131, but I can tell which one by call different SPI module to write.
Could you kindly tell me where I did wrong?
Attached relevant code, xTIDA010086.h is writen by our team.
/**
* \copyright Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Texas Instruments Incorporated nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "ads131m0x.h"
//****************************************************************************
//
// Internal variables
//
//****************************************************************************
// Array used to recall device register map configurations */
static uint16_t registerMap[NUM_REGISTERS];
// Array of SPI word lengths
const static unsigned char wlength_byte_values[] = {2, 3, 4, 4};
//****************************************************************************
//
// Internal function prototypes
//
//****************************************************************************
unsigned char buildSPIarray(const uint16_t opcodeArray[], unsigned char numberOpcodes, unsigned char byteArray[]);
uint16_t enforce_selected_device_modes(uint16_t data);
unsigned char getWordByteLength(void);
//*****************************************************************************
//
//! Getter function to access registerMap array from outside of this module.
//!
//! \fn uint16_t getRegisterValue(unsigned char address)
//!
//! NOTE: The internal registerMap arrays stores the last know register value,
//! since the last read or write operation to that register. This function
//! does not communicate with the device to retrieve the current register value.
//! For the most up-to-date register data or retrieving the value of a hardware
//! controlled register, it is recommend to use readSingleRegister() to read the
//! current register value.
//!
//! \return unsigned 16-bit register value.
//
//*****************************************************************************
uint16_t getRegisterValue(unsigned char address)
{
assert(address < NUM_REGISTERS);
return registerMap[address];
}
//*****************************************************************************
//
//! Example start up sequence for the ADS131M0x.
//!
//! \fn void adcStartup(void)
//!
//! Before calling this function, the device must be powered,
//! the SPI/GPIO pins of the MCU must have already been configured,
//! and (if applicable) the external clock source should be provided to CLKIN.
//!
//! \return None.
//
//*****************************************************************************
void adcStartup(void)
{
/* (OPTIONAL) Provide additional delay time for power supply settling */
delay_ms(50);
/* (REQUIRED) Set nRESET pin high for ADC operation */
setSYNC_RESET(HIGH);
/* (OPTIONAL) Toggle nRESET pin to ensure default register settings. */
/* NOTE: This also ensures that the device registers are unlocked. */
toggleRESET();
/* (REQUIRED) Initialize internal 'registerMap' array with device default settings */
restoreRegisterDefaults();
/* (OPTIONAL) Validate first response word when beginning SPI communication: (0xFF20 | CHANCNT) */
uint16_t response = sendCommand(OPCODE_NULL);
uint16_t response2 = sendCommand2(OPCODE_NULL);
/* (OPTIONAL) Define your initial register settings here */
writeSingleRegister(CLOCK_ADDRESS, (CLOCK_DEFAULT & ~CLOCK_OSR_MASK) | CLOCK_OSR_256);
writeSingleRegister2(CLOCK_ADDRESS, (CLOCK_DEFAULT & ~CLOCK_OSR_MASK) | CLOCK_OSR_256);
/* (REQUIRED) Configure MODE register settings
* NOTE: This function call is required here for this particular code implementation to work.
* This function will enforce the MODE register settings as selected in the 'ads131m0x.h' header file.
*/
writeSingleRegister(MODE_ADDRESS, MODE_DEFAULT);
writeSingleRegister2(MODE_ADDRESS, MODE_DEFAULT);
/* (OPTIONAL) Read back all registers */
/* (OPTIONAL) Check STATUS register for faults */
}
//*****************************************************************************
//
//! Reads the contents of a single register at the specified address.
//!
//! \fn uint16_t readSingleRegister(unsigned char address)
//!
//! \param address is the 8-bit address of the register to read.
//!
//! \return Returns the 8-bit register read result.
//
//*****************************************************************************
uint16_t readSingleRegister(unsigned char address)
{
/* Check that the register address is in range */
assert(address < NUM_REGISTERS);
// Build TX and RX byte array
#ifdef ENABLE_CRC_IN
unsigned char dataTx[8] = { 0 }; // 2 words, up to 4 bytes each = 8 bytes maximum
unsigned char dataRx[8] = { 0 };
#else
unsigned char dataTx[4] = { 0 }; // 1 word, up to 4 bytes long = 4 bytes maximum
unsigned char dataRx[4] = { 0 };
#endif
uint16_t opcode = OPCODE_RREG | (((uint16_t) address) << 7);
unsigned char numberOfBytes = buildSPIarray(&opcode, 1, dataTx);
// [FRAME 1] Send RREG command
spiSendReceiveArrays(dataTx, dataRx, numberOfBytes);
// [FRAME 2] Send NULL command to retrieve the register data
registerMap[address] = sendCommand(OPCODE_NULL);
return registerMap[address];
}
uint16_t readSingleRegister2(unsigned char address)
{
/* Check that the register address is in range */
assert(address < NUM_REGISTERS);
// Build TX and RX byte array
#ifdef ENABLE_CRC_IN
unsigned char dataTx[8] = { 0 }; // 2 words, up to 4 bytes each = 8 bytes maximum
unsigned char dataRx[8] = { 0 };
#else
unsigned char dataTx[4] = { 0 }; // 1 word, up to 4 bytes long = 4 bytes maximum
unsigned char dataRx[4] = { 0 };
#endif
uint16_t opcode = OPCODE_RREG | (((uint16_t) address) << 7);
unsigned char numberOfBytes = buildSPIarray(&opcode, 1, dataTx);
// [FRAME 1] Send RREG command
spiBSendReceiveArrays(dataTx, dataRx, numberOfBytes);
// [FRAME 2] Send NULL command to retrieve the register data
registerMap[address] = sendCommand2(OPCODE_NULL);
return registerMap[address];
}
//*****************************************************************************
//
//! Writes data to a single register.
//!
//! \fn void writeSingleRegister(unsigned char address, uint16_t data)
//!
//! \param address is the address of the register to write to.
//! \param data is the value to write.
//!
//! This command will be ignored if device registers are locked.
//!
//! \return None.
//
//*****************************************************************************
void writeSingleRegister(unsigned char address, uint16_t data)
{
/* Check that the register address is in range */
assert(address < NUM_REGISTERS);
// (OPTIONAL) Enforce certain register field values when
// writing to the MODE register to fix the operation mode
if (MODE_ADDRESS == address)
{
data = enforce_selected_device_modes(data);
}
// Build TX and RX byte array
#ifdef ENABLE_CRC_IN
unsigned char dataTx[12] = { 0 }; // 3 words, up to 4 bytes each = 12 bytes maximum
unsigned char dataRx[12] = { 0 };
#else
unsigned char dataTx[8] = { 0 }; // 2 words, up to 4 bytes long = 8 bytes maximum
unsigned char dataRx[8] = { 0 };
#endif
uint16_t opcodes[2];
opcodes[0] = OPCODE_WREG | (((uint16_t) address) << 7);
opcodes[1] = data;
unsigned char numberOfBytes = buildSPIarray(&opcodes[0], 2, dataTx);
// Send command
spiSendReceiveArrays(dataTx, dataRx, numberOfBytes);
// Update internal array
registerMap[address] = data;
// (RECOMMENDED) Read back register to confirm register write was successful
readSingleRegister(address);
// NOTE: Enabling the CRC words in the SPI command will NOT prevent an invalid W
}
void writeSingleRegister2(unsigned char address, uint16_t data)
{
/* Check that the register address is in range */
assert(address < NUM_REGISTERS);
// (OPTIONAL) Enforce certain register field values when
// writing to the MODE register to fix the operation mode
if (MODE_ADDRESS == address)
{
data = enforce_selected_device_modes(data);
}
// Build TX and RX byte array
#ifdef ENABLE_CRC_IN
unsigned char dataTx[12] = { 0 }; // 3 words, up to 4 bytes each = 12 bytes maximum
unsigned char dataRx[12] = { 0 };
#else
unsigned char dataTx[8] = { 0 }; // 2 words, up to 4 bytes long = 8 bytes maximum
unsigned char dataRx[8] = { 0 };
#endif
uint16_t opcodes[2];
opcodes[0] = OPCODE_WREG | (((uint16_t) address) << 7);
opcodes[1] = data;
unsigned char numberOfBytes = buildSPIarray(&opcodes[0], 2, dataTx);
// Send command
spiBSendReceiveArrays(dataTx, dataRx, numberOfBytes);
// Update internal array
registerMap[address] = data;
// (RECOMMENDED) Read back register to confirm register write was successful
readSingleRegister2(address);
// NOTE: Enabling the CRC words in the SPI command will NOT prevent an invalid W
}
//*****************************************************************************
//
//! Reads ADC data.
//!
//! \fn bool readData(adc_channel_data *DataStruct)
//!
//! \param *DataStruct points to an adc_channel_data type-defined structure/
//!
//! NOTE: Should be called after /DRDY goes low, and not during a /DRDY falling edge!
//!
//! \return Returns true if the CRC-OUT of the data read detects an error.
//
//*****************************************************************************
bool readData(adc_channel_data *DataStruct)
{
int i;
unsigned char crcTx[4] = { 0 };
unsigned char dataRx[4] = { 0 };
unsigned char bytesPerWord = getWordByteLength();
#ifdef ENABLE_CRC_IN
// Build CRC word (only if "RX_CRC_EN" register bit is enabled)
uint16_t crcWordIn = calculateCRC(&DataTx[0], bytesPerWord * 2, 0xFFFF);
crcTx[0] = upperByte(crcWordIn);
crcTx[1] = lowerByte(crcWordIn);
#endif
/* Set the nCS pin LOW */
setCS(LOW);
// Send NULL word, receive response word
for (i = 0; i < bytesPerWord; i++)
{
dataRx[i] = spiSendReceiveByte(0x00);
}
DataStruct->response = combineBytes(dataRx[0], dataRx[1]);
// (OPTIONAL) Do something with the response (STATUS) word.
// ...Here we only use the response for calculating the CRC-OUT
//uint16_t crcWord = calculateCRC(&dataRx[0], bytesPerWord, 0xFFFF);
// (OPTIONAL) Ignore CRC error checking
uint16_t crcWord = 0;
// Send 2nd word, receive channel 1 data
for (i = 0; i < bytesPerWord; i++)
{
dataRx[i] = spiSendReceiveByte(crcTx[i]);
}
DataStruct->channel0 = signExtend(&dataRx[0]);
//crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
#if (CHANNEL_COUNT > 1)
// Send 3rd word, receive channel 2 data
for (i = 0; i < bytesPerWord; i++)
{
dataRx[i] = spiSendReceiveByte(0x00);
}
DataStruct->channel1 = signExtend(&dataRx[0]);
//crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
#endif
#if (CHANNEL_COUNT > 2)
// Send 4th word, receive channel 3 data
for (i = 0; i < bytesPerWord; i++)
{
dataRx[i] = spiSendReceiveByte(0x00);
}
DataStruct->channel2 = signExtend(&dataRx[0]);
//crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
#endif
#if (CHANNEL_COUNT > 3)
// Send 5th word, receive channel 4 data
for (i = 0; i < bytesPerWord; i++)
{
dataRx[i] = spiSendReceiveByte(0x00);
}
DataStruct->channel3 = signExtend(&dataRx[0]);
//crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
#endif
#if (CHANNEL_COUNT > 4)
// Send 6th word, receive channel 5 data
for (i = 0; i < bytesPerWord; i++)
{
dataRx[i] = spiSendReceiveByte(0x00);
}
DataStruct->channel4 = signExtend(&dataRx[0]);
//crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
#endif
#if (CHANNEL_COUNT > 5)
// Send 7th word, receive channel 6 data
for (i = 0; i < bytesPerWord; i++)
{
dataRx[i] = spiSendReceiveByte(0x00);
}
DataStruct->channel5 = signExtend(&dataRx[0]);
//crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
#endif
#if (CHANNEL_COUNT > 6)
// Send 8th word, receive channel 7 data
for (i = 0; i < bytesPerWord; i++)
{
dataRx[i] = spiSendReceiveByte(0x00);
}
DataStruct->channel6 = signExtend(&dataRx[0]);
//crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
#endif
#if (CHANNEL_COUNT > 7)
// Send 9th word, receive channel 8 data
for (i = 0; i < bytesPerWord; i++)
{
dataRx[i] = spiSendReceiveByte(0x00);
}
DataStruct->channel7 = signExtend(&dataRx[0]);
//crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
#endif
// Send the next word, receive CRC data
for (i = 0; i < bytesPerWord; i++)
{
dataRx[i] = spiSendReceiveByte(0x00);
}
DataStruct->crc = combineBytes(dataRx[0], dataRx[1]);
/* NOTE: If we continue calculating the CRC with a matching CRC, the result should be zero.
* Any non-zero result will indicate a mismatch.
*/
//crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
/* Set the nCS pin HIGH */
setCS(HIGH);
// Returns true when a CRC error occurs
return ((bool) crcWord);
}
bool readData2(adc_channel_data *DataStruct)
{
int i;
unsigned char crcTx[4] = { 0 };
unsigned char dataRx[4] = { 0 };
unsigned char bytesPerWord = getWordByteLength();
#ifdef ENABLE_CRC_IN
// Build CRC word (only if "RX_CRC_EN" register bit is enabled)
uint16_t crcWordIn = calculateCRC(&DataTx[0], bytesPerWord * 2, 0xFFFF);
crcTx[0] = upperByte(crcWordIn);
crcTx[1] = lowerByte(crcWordIn);
#endif
/* Set the nCS pin LOW */
setCSB(LOW);
// Send NULL word, receive response word
for (i = 0; i < bytesPerWord; i++)
{
dataRx[i] = spiBSendReceiveByte(0x00);
}
DataStruct->response = combineBytes(dataRx[0], dataRx[1]);
// (OPTIONAL) Do something with the response (STATUS) word.
// ...Here we only use the response for calculating the CRC-OUT
//uint16_t crcWord = calculateCRC(&dataRx[0], bytesPerWord, 0xFFFF);
// (OPTIONAL) Ignore CRC error checking
uint16_t crcWord = 0;
// Send 2nd word, receive channel 1 data
for (i = 0; i < bytesPerWord; i++)
{
dataRx[i] = spiBSendReceiveByte(crcTx[i]);
}
DataStruct->channel0 = signExtend(&dataRx[0]);
//crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
#if (CHANNEL_COUNT > 1)
// Send 3rd word, receive channel 2 data
for (i = 0; i < bytesPerWord; i++)
{
dataRx[i] = spiBSendReceiveByte(0x00);
}
DataStruct->channel1 = signExtend(&dataRx[0]);
//crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
#endif
#if (CHANNEL_COUNT > 2)
// Send 4th word, receive channel 3 data
for (i = 0; i < bytesPerWord; i++)
{
dataRx[i] = spiBSendReceiveByte(0x00);
}
DataStruct->channel2 = signExtend(&dataRx[0]);
//crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
#endif
#if (CHANNEL_COUNT > 3)
// Send 5th word, receive channel 4 data
for (i = 0; i < bytesPerWord; i++)
{
dataRx[i] = spiBSendReceiveByte(0x00);
}
DataStruct->channel3 = signExtend(&dataRx[0]);
//crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
#endif
#if (CHANNEL_COUNT > 4)
// Send 6th word, receive channel 5 data
for (i = 0; i < bytesPerWord; i++)
{
dataRx[i] = spiBSendReceiveByte(0x00);
}
DataStruct->channel4 = signExtend(&dataRx[0]);
//crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
#endif
#if (CHANNEL_COUNT > 5)
// Send 7th word, receive channel 6 data
for (i = 0; i < bytesPerWord; i++)
{
dataRx[i] = spiBSendReceiveByte(0x00);
}
DataStruct->channel5 = signExtend(&dataRx[0]);
//crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
#endif
#if (CHANNEL_COUNT > 6)
// Send 8th word, receive channel 7 data
for (i = 0; i < bytesPerWord; i++)
{
dataRx[i] = spiBSendReceiveByte(0x00);
}
DataStruct->channel6 = signExtend(&dataRx[0]);
//crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
#endif
#if (CHANNEL_COUNT > 7)
// Send 9th word, receive channel 8 data
for (i = 0; i < bytesPerWord; i++)
{
dataRx[i] = spiBSendReceiveByte(0x00);
}
DataStruct->channel7 = signExtend(&dataRx[0]);
//crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
#endif
// Send the next word, receive CRC data
for (i = 0; i < bytesPerWord; i++)
{
dataRx[i] = spiBSendReceiveByte(0x00);
}
DataStruct->crc = combineBytes(dataRx[0], dataRx[1]);
/* NOTE: If we continue calculating the CRC with a matching CRC, the result should be zero.
* Any non-zero result will indicate a mismatch.
*/
//crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
/* Set the nCS pin HIGH */
setCSB(HIGH);
// Returns true when a CRC error occurs
return ((bool) crcWord);
}
//*****************************************************************************
//
//! Sends the specified SPI command to the ADC (NULL, STANDBY, or WAKEUP).
//!
//! \fn uint16_t sendCommand(uint16_t opcode)
//!
//! \param opcode SPI command byte.
//!
//! NOTE: Other commands have their own dedicated functions to support
//! additional functionality.
//!
//! \return ADC response byte (typically the STATUS byte).
//
//*****************************************************************************
uint16_t sendCommand(uint16_t opcode)
{
/* Assert if this function is used to send any of the following opcodes */
assert(OPCODE_RREG != opcode); /* Use "readSingleRegister()" */
assert(OPCODE_WREG != opcode); /* Use "writeSingleRegister()" */
assert(OPCODE_LOCK != opcode); /* Use "lockRegisters()" */
assert(OPCODE_UNLOCK != opcode); /* Use "unlockRegisters()" */
assert(OPCODE_RESET != opcode); /* Use "resetDevice()" */
// Build TX and RX byte array
#ifdef ENABLE_CRC_IN
unsigned char dataTx[8] = { 0 }; // 2 words, up to 4 bytes each = 8 bytes maximum
unsigned char dataRx[8] = { 0 };
#else
unsigned char dataTx[4] = { 0 }; // 1 word, up to 4 bytes long = 4 bytes maximum
unsigned char dataRx[4] = { 0 };
#endif
unsigned char numberOfBytes = buildSPIarray(&opcode, 1, dataTx);
/* Set the nCS pin LOW */
setCS(LOW);
// Send the opcode (and crc word, if enabled)
int i;
for (i = 0; i < numberOfBytes; i++)
{
dataRx[i] = spiSendReceiveByte(dataTx[i]);
}
/* Set the nCS pin HIGH */
setCS(HIGH);
// Combine response bytes and return as a 16-bit word
uint16_t adcResponse = combineBytes(dataRx[0], dataRx[1]);
return adcResponse;
}
uint16_t sendCommand2(uint16_t opcode)
{
/* Assert if this function is used to send any of the following opcodes */
assert(OPCODE_RREG != opcode); /* Use "readSingleRegister()" */
assert(OPCODE_WREG != opcode); /* Use "writeSingleRegister()" */
assert(OPCODE_LOCK != opcode); /* Use "lockRegisters()" */
assert(OPCODE_UNLOCK != opcode); /* Use "unlockRegisters()" */
assert(OPCODE_RESET != opcode); /* Use "resetDevice()" */
// Build TX and RX byte array
#ifdef ENABLE_CRC_IN
unsigned char dataTx[8] = { 0 }; // 2 words, up to 4 bytes each = 8 bytes maximum
unsigned char dataRx[8] = { 0 };
#else
unsigned char dataTx[4] = { 0 }; // 1 word, up to 4 bytes long = 4 bytes maximum
unsigned char dataRx[4] = { 0 };
#endif
unsigned char numberOfBytes = buildSPIarray(&opcode, 1, dataTx);
/* Set the nCS pin LOW */
setCSB(LOW);
// Send the opcode (and crc word, if enabled)
int i;
for (i = 0; i < numberOfBytes; i++)
{
dataRx[i] = spiBSendReceiveByte(dataTx[i]);
}
/* Set the nCS pin HIGH */
setCSB(HIGH);
// Combine response bytes and return as a 16-bit word
uint16_t adcResponse = combineBytes(dataRx[0], dataRx[1]);
return adcResponse;
}
//*****************************************************************************
//
//! Resets the device.
//!
//! \fn void resetDevice(void)
//!
//! NOTE: This function does not capture DOUT data, but it could be modified
//! to do so.
//!
//! \return None.
//
//*****************************************************************************
void resetDevice(void)
{
// Build TX and RX byte array
#ifdef ENABLE_CRC_IN
unsigned char dataTx[8] = { 0 }; // 2 words, up to 4 bytes each = 8 bytes maximum
//unsigned char dataRx[8] = { 0 }; // Only needed if capturing data
#else
unsigned char dataTx[4] = { 0 }; // 1 word, up to 4 bytes long = 4 bytes maximum
//unsigned char dataRx[4] = { 0 }; // Only needed if capturing data
#endif
uint16_t opcode = OPCODE_RESET;
unsigned char numberOfBytes = buildSPIarray(&opcode, 1, dataTx);
unsigned char bytesPerWord = wlength_byte_values[WLENGTH];
unsigned char wordsInFrame = CHANNEL_COUNT + 2;
// Set the nCS pin LOW
setCS(LOW);
// Send the opcode (and CRC word, if enabled)
int i;
for (i = 0; i < numberOfBytes; i++)
{
spiSendReceiveByte(dataTx[i]);
}
// Finish sending remaining bytes
for (i = numberOfBytes; i < (wordsInFrame * bytesPerWord); i++)
{
spiSendReceiveByte(0x00);
}
// NOTE: The ADS131M0x's next response word should be (0xFF20 | CHANCNT),
// if the response is 0x0011 (acknowledge of RESET command), then the device
// did not receive a full SPI frame and the reset did not occur!
// Set the nCS pin HIGH
setCS(HIGH);
// tSRLRST delay, ~1ms with 2.048 MHz fCLK
delay_ms(1);
// Update register setting array to keep software in sync with device
restoreRegisterDefaults();
// Write to MODE register to enforce mode settings
writeSingleRegister(MODE_ADDRESS, MODE_DEFAULT);
}
//*****************************************************************************
//
//! Sends the LOCK command and verifies that registers are locked.
//!
//! \fn bool lockRegisters(void)
//!
//! \return boolean to indicate if an error occurred (0 = no error; 1 = error)
//
//*****************************************************************************
bool lockRegisters(void)
{
bool b_lock_error;
// Build TX and RX byte array
#ifdef ENABLE_CRC_IN
unsigned char dataTx[8] = { 0 }; // 2 words, up to 4 bytes each = 8 bytes maximum
unsigned char dataRx[8] = { 0 };
#else
unsigned char dataTx[4] = { 0 }; // 1 word, up to 4 bytes long = 4 bytes maximum
unsigned char dataRx[4] = { 0 };
#endif
uint16_t opcode = OPCODE_LOCK;
unsigned char numberOfBytes = buildSPIarray(&opcode, 1, dataTx);
// Send command
spiSendReceiveArrays(dataTx, dataRx, numberOfBytes);
/* (OPTIONAL) Check for SPI errors by sending the NULL command and checking STATUS */
/* (OPTIONAL) Read back the STATUS register and check if LOCK bit is set... */
readSingleRegister(STATUS_ADDRESS);
if (!SPI_LOCKED) { b_lock_error = true; }
/* If the STATUS register is NOT read back,
* then make sure to manually update the global register map variable... */
//registerMap[STATUS_ADDRESS] |= STATUS_LOCK_LOCKED;
/* (OPTIONAL) Error handler */
if (b_lock_error)
{
// Insert error handler function call here...
}
return b_lock_error;
}
//*****************************************************************************
//
//! Sends the UNLOCK command and verifies that registers are unlocked
//!
//! \fn bool unlockRegisters(void)
//!
//! \return boolean to indicate if an error occurred (0 = no error; 1 = error)
//
//*****************************************************************************
bool unlockRegisters(void)
{
bool b_unlock_error;
// Build TX and RX byte array
#ifdef ENABLE_CRC_IN
unsigned char dataTx[8] = { 0 }; // 2 words, up to 4 bytes each = 8 bytes maximum
unsigned char dataRx[8] = { 0 };
#else
unsigned char dataTx[4] = { 0 }; // 1 word, up to 4 bytes long = 4 bytes maximum
unsigned char dataRx[4] = { 0 };
#endif
uint16_t opcode = OPCODE_UNLOCK;
unsigned char numberOfBytes = buildSPIarray(&opcode, 1, dataTx);
// Send command
spiSendReceiveArrays(dataTx, dataRx, numberOfBytes);
/* (OPTIONAL) Check for SPI errors by sending the NULL command and checking STATUS */
/* (OPTIONAL) Read the STATUS register and check if LOCK bit is cleared... */
readSingleRegister(STATUS_ADDRESS);
if (SPI_LOCKED) { b_unlock_error = true; }
/* If the STATUS register is NOT read back,
* then make sure to manually update the global register map variable... */
//registerMap[STATUS_ADDRESS] &= !STATUS_LOCK_LOCKED;
/* (OPTIONAL) Error handler */
if (b_unlock_error)
{
// Insert error handler function call here...
}
return b_unlock_error;
}
//*****************************************************************************
//
//! Calculates the 16-bit CRC for the selected CRC polynomial.
//!
//! \fn uint16_t calculateCRC(const unsigned char dataBytes[], unsigned char numberBytes, uint16_t initialValue)
//!
//! \param dataBytes[] pointer to first element in the data byte array
//! \param numberBytes number of bytes to be used in CRC calculation
//! \param initialValue the seed value (or partial crc calculation), use 0xFFFF when beginning a new CRC computation
//!
//! NOTE: This calculation is shown as an example and is not optimized for speed.
//!
//! \return 16-bit calculated CRC word
//
//*****************************************************************************
uint16_t calculateCRC(const unsigned char dataBytes[], unsigned char numberBytes, uint16_t initialValue)
{
/* Check that "dataBytes" is not a null pointer */
assert(dataBytes != 0x00);
int bitIndex, byteIndex;
bool dataMSb; /* Most significant bit of data byte */
bool crcMSb; /* Most significant bit of crc byte */
unsigned char bytesPerWord = wlength_byte_values[WLENGTH];
/*
* Initial value of crc register
* NOTE: The ADS131M0x defaults to 0xFFFF,
* but can be set at function call to continue an on-going calculation
*/
uint16_t crc = initialValue;
#ifdef CRC_CCITT
/* CCITT CRC polynomial = x^16 + x^12 + x^5 + 1 */
const uint16_t poly = 0x8005;
#endif
#ifdef CRC_ANSI
/* ANSI CRC polynomial = x^16 + x^15 + x^2 + 1 */
const uint16_t poly = 0x1021;
#endif
//
// CRC algorithm
//
// Loop through all bytes in the dataBytes[] array
for (byteIndex = 0; byteIndex < numberBytes; byteIndex++)
{
// Point to MSb in byte
bitIndex = 0x80u;
// Loop through all bits in the current byte
while (bitIndex > 0)
{
// Check MSB's of data and crc
dataMSb = (bool) (dataBytes[byteIndex] & bitIndex);
crcMSb = (bool) (crc & 0x8000u);
crc <<= 1; /* Left shift CRC register */
// Check if XOR operation of MSBs results in additional XOR operations
if (dataMSb ^ crcMSb)
{
crc ^= poly; /* XOR crc with polynomial */
}
/* Shift MSb pointer to the next data bit */
bitIndex >>= 1;
}
}
return crc;
}
//*****************************************************************************
//
//! Updates the registerMap[] array to its default values.
//!
//! \fn void restoreRegisterDefaults(void)
//!
//! NOTES:
//! - If the MCU keeps a copy of the ADS131M0x register settings in memory,
//! then it is important to ensure that these values remain in sync with the
//! actual hardware settings. In order to help facilitate this, this function
//! should be called after powering up or resetting the device (either by
//! hardware pin control or SPI software command).
//!
//! - Reading back all of the registers after resetting the device can
//! accomplish the same result; however, this might be problematic if the
//! device was previously in CRC mode or the WLENGTH was modified, since
//! resetting the device exits these modes. If the MCU is not aware of this
//! mode change, then read register commands will return invalid data due to
//! the expectation of data appearing in a different byte position.
//!
//! \return None.
//
//*****************************************************************************
void restoreRegisterDefaults(void)
{
registerMap[ID_ADDRESS] = 0x00; /* NOTE: This a read-only register */
registerMap[STATUS_ADDRESS] = STATUS_DEFAULT;
registerMap[MODE_ADDRESS] = MODE_DEFAULT;
registerMap[CLOCK_ADDRESS] = CLOCK_DEFAULT;
registerMap[GAIN1_ADDRESS] = GAIN1_DEFAULT;
registerMap[GAIN2_ADDRESS] = GAIN2_DEFAULT;
registerMap[CFG_ADDRESS] = CFG_DEFAULT;
registerMap[THRSHLD_MSB_ADDRESS] = THRSHLD_MSB_DEFAULT;
registerMap[THRSHLD_LSB_ADDRESS] = THRSHLD_LSB_DEFAULT;
registerMap[CH0_CFG_ADDRESS] = CH0_CFG_DEFAULT;
registerMap[CH0_OCAL_MSB_ADDRESS] = CH0_OCAL_MSB_DEFAULT;
registerMap[CH0_OCAL_LSB_ADDRESS] = CH0_OCAL_LSB_DEFAULT;
registerMap[CH0_GCAL_MSB_ADDRESS] = CH0_GCAL_MSB_DEFAULT;
registerMap[CH0_GCAL_LSB_ADDRESS] = CH0_GCAL_LSB_DEFAULT;
#if (CHANNEL_COUNT > 1)
registerMap[CH1_CFG_ADDRESS] = CH1_CFG_DEFAULT;
registerMap[CH1_OCAL_MSB_ADDRESS] = CH1_OCAL_MSB_DEFAULT;
registerMap[CH1_OCAL_LSB_ADDRESS] = CH1_OCAL_LSB_DEFAULT;
registerMap[CH1_GCAL_MSB_ADDRESS] = CH1_GCAL_MSB_DEFAULT;
registerMap[CH1_GCAL_LSB_ADDRESS] = CH1_GCAL_LSB_DEFAULT;
#endif
#if (CHANNEL_COUNT > 2)
registerMap[CH2_CFG_ADDRESS] = CH2_CFG_DEFAULT;
registerMap[CH2_OCAL_MSB_ADDRESS] = CH2_OCAL_MSB_DEFAULT;
registerMap[CH2_OCAL_LSB_ADDRESS] = CH2_OCAL_LSB_DEFAULT;
registerMap[CH2_GCAL_MSB_ADDRESS] = CH2_GCAL_MSB_DEFAULT;
registerMap[CH2_GCAL_LSB_ADDRESS] = CH2_GCAL_LSB_DEFAULT;
#endif
#if (CHANNEL_COUNT > 3)
registerMap[CH3_CFG_ADDRESS] = CH3_CFG_DEFAULT;
registerMap[CH3_OCAL_MSB_ADDRESS] = CH3_OCAL_MSB_DEFAULT;
registerMap[CH3_OCAL_LSB_ADDRESS] = CH3_OCAL_LSB_DEFAULT;
registerMap[CH3_GCAL_MSB_ADDRESS] = CH3_GCAL_MSB_DEFAULT;
registerMap[CH3_GCAL_LSB_ADDRESS] = CH3_GCAL_LSB_DEFAULT;
#endif
#if (CHANNEL_COUNT > 4)
registerMap[CH4_CFG_ADDRESS] = CH4_CFG_DEFAULT;
registerMap[CH4_OCAL_MSB_ADDRESS] = CH4_OCAL_MSB_DEFAULT;
registerMap[CH4_OCAL_LSB_ADDRESS] = CH4_OCAL_LSB_DEFAULT;
registerMap[CH4_GCAL_MSB_ADDRESS] = CH4_GCAL_MSB_DEFAULT;
registerMap[CH4_GCAL_LSB_ADDRESS] = CH4_GCAL_LSB_DEFAULT;
#endif
#if (CHANNEL_COUNT > 5)
registerMap[CH5_CFG_ADDRESS] = CH5_CFG_DEFAULT;
registerMap[CH5_OCAL_MSB_ADDRESS] = CH5_OCAL_MSB_DEFAULT;
registerMap[CH5_OCAL_LSB_ADDRESS] = CH5_OCAL_LSB_DEFAULT;
registerMap[CH5_GCAL_MSB_ADDRESS] = CH5_GCAL_MSB_DEFAULT;
registerMap[CH5_GCAL_LSB_ADDRESS] = CH5_GCAL_LSB_DEFAULT;
#endif
#if (CHANNEL_COUNT > 6)
registerMap[CH6_CFG_ADDRESS] = CH6_CFG_DEFAULT;
registerMap[CH6_OCAL_MSB_ADDRESS] = CH6_OCAL_MSB_DEFAULT;
registerMap[CH6_OCAL_LSB_ADDRESS] = CH6_OCAL_LSB_DEFAULT;
registerMap[CH6_GCAL_MSB_ADDRESS] = CH6_GCAL_MSB_DEFAULT;
registerMap[CH6_GCAL_LSB_ADDRESS] = CH6_GCAL_LSB_DEFAULT;
#endif
#if (CHANNEL_COUNT > 7)
registerMap[CH7_CFG_ADDRESS] = CH7_CFG_DEFAULT;
registerMap[CH7_OCAL_MSB_ADDRESS] = CH7_OCAL_MSB_DEFAULT;
registerMap[CH7_OCAL_LSB_ADDRESS] = CH7_OCAL_LSB_DEFAULT;
registerMap[CH7_GCAL_MSB_ADDRESS] = CH7_GCAL_MSB_DEFAULT;
registerMap[CH7_GCAL_LSB_ADDRESS] = CH7_GCAL_LSB_DEFAULT;
#endif
registerMap[REGMAP_CRC_ADDRESS] = REGMAP_CRC_DEFAULT;
}
//****************************************************************************
//
// Helper functions
//
//****************************************************************************
//*****************************************************************************
//
//! Takes a 16-bit word and returns the most-significant byte.
//!
//! \fn unsigned char upperByte(uint16_t uint16_Word)
//!
//! \param temp_word is the original 16-bit word.
//!
//! \return 8-bit most-significant byte.
//
//*****************************************************************************
unsigned char upperByte(uint16_t uint16_Word)
{
unsigned char msByte;
msByte = (unsigned char) ((uint16_Word >> 8) & 0x00FF);
return msByte;
}
//*****************************************************************************
//
//! Takes a 16-bit word and returns the least-significant byte.
//!
//! \fn unsigned char lowerByte(uint16_t uint16_Word)
//!
//! \param temp_word is the original 16-bit word.
//!
//! \return 8-bit least-significant byte.
//
//*****************************************************************************
unsigned char lowerByte(uint16_t uint16_Word)
{
unsigned char lsByte;
lsByte = (unsigned char) (uint16_Word & 0x00FF);
return lsByte;
}
//*****************************************************************************
//
//! Takes two 8-bit words and returns a concatenated 16-bit word.
//!
//! \fn uint16_t combineBytes(unsigned char upperByte, unsigned char lowerByte)
//!
//! \param upperByte is the 8-bit value that will become the MSB of the 16-bit word.
//! \param lowerByte is the 8-bit value that will become the LSB of the 16-bit word.
//!
//! \return concatenated 16-bit word.
//
//*****************************************************************************
uint16_t combineBytes(unsigned char upperByte, unsigned char lowerByte)
{
uint16_t combinedValue;
combinedValue = ((uint16_t) upperByte << 8) | ((uint16_t) lowerByte);
return combinedValue;
}
//*****************************************************************************
//
//! Combines ADC data bytes into a single signed 32-bit word.
//!
//! \fn int32_t combineDataBytes(const unsigned char dataBytes[])
//!
//! \param dataBytes is a pointer to unsigned char[] where the first element is the MSB.
//!
//! \return Returns the signed-extend 32-bit result.
//
//*****************************************************************************
int32_t signExtend(const unsigned char dataBytes[])
{
#ifdef WORD_LENGTH_24BIT
int32_t upperByte = ((int32_t) dataBytes[0] << 24);
int32_t middleByte = ((int32_t) dataBytes[1] << 16);
int32_t lowerByte = ((int32_t) dataBytes[2] << 8);
return (((int32_t) (upperByte | middleByte | lowerByte)) >> 8); // Right-shift of signed data maintains signed bit
#elif defined WORD_LENGTH_32BIT_SIGN_EXTEND
int32_t signByte = ((int32_t) dataBytes[0] << 24);
int32_t upperByte = ((int32_t) dataBytes[1] << 16);
int32_t middleByte = ((int32_t) dataBytes[2] << 8);
int32_t lowerByte = ((int32_t) dataBytes[3] << 0);
return (signByte | upperByte | middleByte | lowerByte);
#elif defined WORD_LENGTH_32BIT_ZERO_PADDED
int32_t upperByte = ((int32_t) dataBytes[0] << 24);
int32_t middleByte = ((int32_t) dataBytes[1] << 16);
int32_t lowerByte = ((int32_t) dataBytes[2] << 8);
return (((int32_t) (upperByte | middleByte | lowerByte)) >> 8); // Right-shift of signed data maintains signed bit
#elif defined WORD_LENGTH_16BIT_TRUNCATED
int32_t upperByte = ((int32_t) dataBytes[0] << 24);
int32_t lowerByte = ((int32_t) dataBytes[1] << 16);
return (((int32_t) (upperByte | lowerByte)) >> 16); // Right-shift of signed data maintains signed bit
#endif
}
//****************************************************************************
//
// Internal functions
//
//****************************************************************************
//*****************************************************************************
//
//! Builds SPI TX data arrays according to number of opcodes provided and
//! currently programmed device word length.
//!
//! \fn unsigned char buildSPIarray(const uint16_t opcodeArray[], unsigned char numberOpcodes, unsigned char byteArray[])
//!
//! \param opcodeArray[] pointer to an array of 16-bit opcodes to use in the SPI command.
//! \param numberOpcodes the number of opcodes provided in opcodeArray[].
//! \param byteArray[] pointer to an array of 8-bit SPI bytes to send to the device.
//!
//! NOTE: The calling function must ensure it reserves sufficient memory for byteArray[]!
//!
//! \return number of bytes added to byteArray[].
//
//*****************************************************************************
unsigned char buildSPIarray(const uint16_t opcodeArray[], unsigned char numberOpcodes, unsigned char byteArray[])
{
/*
* Frame size = opcode word(s) + optional CRC word
* Number of bytes per word = 2, 3, or 4
* Total bytes = bytes per word * number of words
*/
unsigned char numberWords = numberOpcodes + (SPI_CRC_ENABLED ? 1 : 0);
unsigned char bytesPerWord = getWordByteLength();
unsigned char numberOfBytes = numberWords * bytesPerWord;
int i;
for (i = 0; i < numberOpcodes; i++)
{
// NOTE: Be careful not to accidentally overflow the array here.
// The array and opcodes are defined in the calling function, so
// we are trusting that no mistakes were made in the calling function!
byteArray[(i*bytesPerWord) + 0] = upperByte(opcodeArray[i]);
byteArray[(i*bytesPerWord) + 1] = lowerByte(opcodeArray[i]);
}
#ifdef ENABLE_CRC_IN
// Calculate CRC and put it into TX array
uint16_t crcWord = calculateCRC(&byteArray[0], numberOfBytes, 0xFFFF);
byteArray[(i*bytesPerWord) + 0] = upperByte(crcWord);
byteArray[(i*bytesPerWord) + 1] = lowerByte(crcWord);
#endif
return numberOfBytes;
}
//*****************************************************************************
//
//! Modifies MODE register data to maintain device operation according to
//! preselected mode(s) (RX_CRC_EN, WLENGTH, etc.).
//!
//! \fn uint16_t enforce_selected_device_mode(uint16_t data)
//!
//! \param data uint16_t register data.
//!
//! \return uint16_t modified register data.
//
//*****************************************************************************
uint16_t enforce_selected_device_modes(uint16_t data)
{
///////////////////////////////////////////////////////////////////////////
// Enforce RX_CRC_EN setting
#ifdef ENABLE_CRC_IN
// When writing to the MODE register, ensure RX_CRC_EN bit is ALWAYS set
data |= MODE_RX_CRC_EN_ENABLED;
#else
// When writing to the MODE register, ensure RX_CRC_EN bit is NEVER set
data &= ~MODE_RX_CRC_EN_ENABLED;
#endif // ENABLE_CRC_IN
///////////////////////////////////////////////////////////////////////////
// Enforce WLENGH setting
#ifdef WORD_LENGTH_24BIT
// When writing to the MODE register, ensure WLENGTH bits are ALWAYS set to 01b
data = (data & ~MODE_WLENGTH_MASK) | MODE_WLENGTH_24BIT;
#elif defined WORD_LENGTH_32BIT_SIGN_EXTEND
// When writing to the MODE register, ensure WLENGH bits are ALWAYS set to 11b
data = (data & ~MODE_WLENGTH_MASK) | MODE_WLENGTH_32BIT_MSB_SIGN_EXT;
#elif defined WORD_LENGTH_32BIT_ZERO_PADDED
// When writing to the MODE register, ensure WLENGH bits are ALWAYS set to 10b
data = (data & ~MODE_WLENGTH_MASK) | MODE_WLENGTH_32BIT_LSB_ZEROES;
#elif defined WORD_LENGTH_16BIT_TRUNCATED
// When writing to the MODE register, ensure WLENGH bits are ALWAYS set to 00b
data = (data & ~MODE_WLENGTH_MASK) | MODE_WLENGTH_16BIT;
#endif
///////////////////////////////////////////////////////////////////////////
// Enforce DRDY_FMT setting
#ifdef DRDY_FMT_PULSE
// When writing to the MODE register, ensure DRDY_FMT bit is ALWAYS set
data = (data & ~MODE_DRDY_FMT_MASK) | MODE_DRDY_FMT_NEG_PULSE_FIXED_WIDTH;
#else
// When writing to the MODE register, ensure DRDY_FMT bit is NEVER set
data = (data & ~MODE_DRDY_FMT_MASK) | MODE_DRDY_FMT_LOGIC_LOW;
#endif
///////////////////////////////////////////////////////////////////////////
// Enforce CRC_TYPE setting
#ifdef CRC_CCITT
// When writing to the MODE register, ensure CRC_TYPE bit is NEVER set
data = (data & ~STATUS_CRC_TYPE_MASK) | STATUS_CRC_TYPE_16BIT_CCITT;
#elif defined CRC_ANSI
// When writing to the MODE register, ensure CRC_TYPE bit is ALWAYS set
data = (data & ~STATUS_CRC_TYPE_MASK) | STATUS_CRC_TYPE_16BIT_ANSI;
#endif
// Return modified register data
return data;
}
//*****************************************************************************
//
//! Returns the ADS131M0x configured word length used for SPI communication.
//!
//! \fn unsigned char getWordByteLength(void)
//!
//! NOTE: It is important that the MODE register value stored in registerMap[]
//! remains in sync with the device. If these values get out of sync then SPI
//! communication may fail!
//!
//! \return SPI word byte length (2, 3, or 4)
//
//*****************************************************************************
unsigned char getWordByteLength(void)
{
return wlength_byte_values[WLENGTH];
}
/**
* \copyright Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Texas Instruments Incorporated nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
//#include "ads131m0x.h"
#include "hal.h"
#include <inttypes.h>
#include <stdint.h>
#include "driverlib.h"
#include "device.h"
#include "ads131m0x.h"
//****************************************************************************
//
// Internal variables
//
//****************************************************************************
// Flag to indicate if a /DRDY interrupt has occurred
static volatile bool flag_nDRDY_INTERRUPT = false;
//****************************************************************************
//
// Internal function prototypes
//
//****************************************************************************
void InitGPIO(void);
void InitSPI(void);
void GPIO_DRDY_IRQHandler(void);
//****************************************************************************
//
// External Functions (prototypes declared in hal.h)
//
//****************************************************************************
//*****************************************************************************
//
//! Initializes MCU peripherals for interfacing with the ADC.
//!
//! \fn void InitADC(void)
//!
//! \return None.
//
//*****************************************************************************
void InitADC(void)
{
// IMPORTANT: Make sure device is powered before setting GPIOs pins to HIGH state.
// Initialize GPIOs pins used by ADS131M0x
InitGPIO();
// Initialize SPI peripheral used by ADS131M0x
// InitSPI();
// Run ADC startup function
adcStartup();
}
//****************************************************************************
//
// Timing functions
//
//****************************************************************************
//*****************************************************************************
//
//! Provides a timing delay with 'ms' resolution.
//!
//! \fn void delay_ms(const uint32_t delay_time_ms)
//!
//! \param delay_time_ms is the number of milliseconds to delay.
//!
//! \return None.
//
//*****************************************************************************
void delay_ms(const uint32_t delay_time_ms)
{
/* --- INSERT YOUR CODE HERE --- */
const uint32_t cycles_per_loop = 3;
SysCtl_delay(delay_time_ms * SysCtl_getClock(100000000U) / (cycles_per_loop * 1000u));
//MAP_SysCtlDelay( delay_time_ms * getSysClockHz() / (cycles_per_loop * 1000u) );
}
//*****************************************************************************
//
//! Provides a timing delay with 'us' resolution.
//!
//! \fn void delay_us(const uint32_t delay_time_us)
//!
//! \param delay_time_us is the number of microseconds to delay.
//!
//! \return None.
//
//*****************************************************************************
void delay_us(const uint32_t delay_time_us)
{
/* --- INSERT YOUR CODE HERE --- */
const uint32_t cycles_per_loop = 3;
SysCtl_delay(delay_time_us * SysCtl_getClock(100000000U)/ (cycles_per_loop * 1000000u));
// MAP_SysCtlDelay( delay_time_us * getSysClockHz() / (cycles_per_loop * 1000000u) );
}
//****************************************************************************
//
// GPIO initialization
//
//****************************************************************************
//*****************************************************************************
//
//! Configures the MCU's GPIO pins that interface with the ADC.
//!
//! \fn void InitGPIO(void)
//!
//! \return None.
//
//*****************************************************************************
void InitGPIO(void)
{
/* --- INSERT YOUR CODE HERE --- */
// NOTE: Not all hardware implementations may control each of these pins...
/* Enable the clock to the GPIO Port K and wait for it to be ready */
// SysCtl_enablePeripheral(SysCtl_PeripheralPCLOCKCR peripheral); // GPIO clock if any
Device_initGPIO();
// *****Delay function*******
// while(!(SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOK)))
// {
//}
/* Configure the GPIO for 'nSYNC_nRESET' as output and set high */
// GPIO_setDirectionMode(nSYNC_nRESET_PIN, GPIO_DIR_MODE_OUT );
// GPIO_writePin(nSYNC_nRESET_PIN, 1);
// MAP_GPIOPinWrite(nSYNC_nRESET_PORT, nSYNC_nRESET_PIN, nSYNC_nRESET_PIN);
//todo add SPIB
GPIO_setPadConfig(nSYNC_nRESET_PIN, GPIO_PIN_TYPE_PULLUP); // Enable pullup on GPIO6
GPIO_writePin(nSYNC_nRESET_PIN, 1); // Load output latch
GPIO_setPinConfig(nSYNC_nRESET_PORT); // GPIO6 = GPIO6
GPIO_setDirectionMode(nSYNC_nRESET_PIN, GPIO_DIR_MODE_OUT); // GPIO6 = output
/*
GPIO_setPadConfig(nSYNC_nRESET_PINB, GPIO_PIN_TYPE_PULLUP); // Enable pullup on GPIO6
GPIO_writePin(nSYNC_nRESET_PINB, 1); // Load output latch
GPIO_setPinConfig(nSYNC_nRESET_PORTB); // GPIO6 = GPIO6
GPIO_setDirectionMode(nSYNC_nRESET_PINB, GPIO_DIR_MODE_OUT); // GPIO6 = output
*/
//todo add SPIB
/* Configure the GPIO for 'nCS' as output and set high */
GPIO_setPadConfig(nCS_PIN, GPIO_PIN_TYPE_PULLUP); // Enable pullup on GPIO4
GPIO_writePin(nCS_PIN, 1); // Load output latch
GPIO_setPinConfig(nCS_PORT); // GPIO4 = GPIO4
GPIO_setDirectionMode(nCS_PIN, GPIO_DIR_MODE_OUT); // GPIO4 = output
GPIO_setPadConfig(nCS_PINB, GPIO_PIN_TYPE_PULLUP); // Enable pullup on GPIO4
GPIO_writePin(nCS_PINB, 1); // Load output latch
GPIO_setPinConfig(nCS_PORTB); // GPIO4 = GPIO4
GPIO_setDirectionMode(nCS_PINB, GPIO_DIR_MODE_OUT); // GPIO4 = output
//GPIO_setDirectionMode(nCS_PIN, GPIO_DIR_MODE_OUT);
// GPIO_setDirectionMode(nCS_PORT, nCS_PIN);
//GPIO_writePin(nCS_PIN, 1);
// MAP_GPIOPinWrite(nCS_PORT, nCS_PIN, nCS_PIN);
/* Enable the clock to the GPIO Port M and wait for it to be ready */
/* SysCtl_enablePeripheral(SysCtl_PeripheralPCLOCKCR peripheral); // GPIO clock if any
*****Delay function*******
while(!(SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOM)))
{
}
*/
/* Configure the GPIO for 'nDRDY' as input with falling edge interrupt */
/* GPIO_setPinConfig(GPIO_15_GPIO15);
GPIO_setDirectionMode(15, GPIO_DIR_MODE_IN);
GPIO_setQualificationMode(15, GPIO_QUAL_SYNC);
GPIO_setInterruptPin(15, GPIO_INT_XINT1);
Interrupt_register(INT_XINT1, &GPIO_DRDY_IRQHandler);
//GPIOIntRegister(nDRDY_PORT, GPIO_DRDY_IRQHandler);
// GPIO_setDirectionMode(nDRDY_PIN, GPIO_DIR_MODE_IN);
// MAP_GPIOPinTypeGPIOInput(nDRDY_PORT, nDRDY_PIN);
GPIO_setInterruptType(GPIO_INT_XINT1, GPIO_INT_TYPE_FALLING_EDGE);
// MAP_GPIOIntTypeSet(nDRDY_PORT, nDRDY_PIN, GPIO_FALLING_EDGE);
GPIO_enableInterrupt(GPIO_INT_XINT1);
//MAP_GPIOIntEnable(nDRDY_PORT, nDRDY_PIN);
Interrupt_enable(INT_XINT1);
// MAP_IntEnable(nDRDY_INT);
*/
}
//*****************************************************************************
//
// Interrupt handler for nDRDY GPIO
//
//*****************************************************************************
//*****************************************************************************
//
//! Interrupt handler for /DRDY falling edge interrupt.
//!
//! \fn void GPIO_DRDY_IRQHandler(void)
//!
//! \return None.
//
//*****************************************************************************
void GPIO_DRDY_IRQHandler(void)
{
/* --- INSERT YOUR CODE HERE --- */
//NOTE: You many need to rename or register this interrupt function for your processor
// Possible ways to handle this interrupt:
// If you decide to read data here, you may want to disable other interrupts to avoid partial data reads.
// In this example we set a flag and exit the interrupt routine. In the main program loop, your application can examine
// all state flags and decide which state (operation) to perform next.
/* Get the interrupt status from the GPIO and clear the status */
// ? uint32_t getIntStatus = MAP_GPIOIntStatus(nDRDY_PORT, true);
/* Check if the nDRDY pin triggered the interrupt */
// GPIO_readPin(GPIO15);
//if(getIntStatus & nDRDY_PIN)
//{
/* Interrupt action: Set a flag */
// flag_nDRDY_INTERRUPT = true;
//}
/* Clear interrupt */
//Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1)
// MAP_GPIOIntClear(nDRDY_PORT, getIntStatus);
// NOTE: We add a short delay at the end to prevent re-entrance. Refer to E2E issue:
// https://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/p/332605/1786938#1786938
//SysCtl_delay(3);
}
//****************************************************************************
//
// GPIO helper functions
//
//****************************************************************************
//*****************************************************************************
//
//! Reads that current state of the /CS GPIO pin.
//!
//! \fn bool getCS(void)
//!
//! \return boolean ('true' if /CS is high, 'false' if /CS is low).
//
//*****************************************************************************
bool getCS(void)
{
/* --- INSERT YOUR CODE HERE --- */
return (bool) GPIO_readPin(nCS_PIN);
// return (bool) GPIOPinRead(nCS_PORT, nCS_PIN);
}
//*****************************************************************************
//
//! Reads that current state of the nSYNC/nRESET GPIO pin.
//!
//! \fn bool getSYNC_RESET(void)
//!
//! \return boolean ('true' if nSYNC/nRESET is high, 'false' if nSYNC/nRESET is low).
//
//*****************************************************************************
bool getSYNC_RESET(void)
{
/* --- INSERT YOUR CODE HERE --- */
return (bool) GPIO_readPin(nSYNC_nRESET_PIN);
// return (bool) GPIOPinRead(nSYNC_nRESET_PORT, nSYNC_nRESET_PIN);
}
//*****************************************************************************
//
//! Controls the state of the /CS GPIO pin.
//!
//! \fn void setCS(const bool state)
//!
//! \param state boolean indicating which state to set the /CS pin (0=low, 1=high)
//!
//! NOTE: The 'HIGH' and 'LOW' macros defined in hal.h can be passed to this
//! function for the 'state' parameter value.
//!
//! \return None.
//
//*****************************************************************************
void setCS(const bool state)
{
/* --- INSERT YOUR CODE HERE --- */
// td(CSSC) delay
if(state) { asm(" NOP"); asm(" NOP");}
// if(state) { SysCtlDelay(2); }
unsigned char value = (unsigned char) (state ? nCS_PIN : 0);
GPIO_writePin(nCS_PIN, value);
//GPIO_writePin(uint32_t pin, uint32_t outVal);
//MAP_GPIOPinWrite(nCS_PORT, nCS_PIN, value);
// td(SCCS) delay
if(!state) { asm(" NOP"); asm(" NOP"); }
//if(!state) { SysCtlDelay(2); }
}
void setCSB(const bool state)
{
/* --- INSERT YOUR CODE HERE --- */
if(state) { asm(" NOP"); asm(" NOP");}
unsigned char value = (unsigned char) (state ? nCS_PINB : 0);
GPIO_writePin(nCS_PINB, value);
// td(SCCS) delay
if(!state) { asm(" NOP"); asm(" NOP"); }
//if(!state) { SysCtlDelay(2); }
}
//*****************************************************************************
//
//! Controls the state of the nSYNC/nRESET GPIO pin.
//!
//! \fn void setSYNC_RESET(const bool state)
//!
//! \param state boolean indicating which state to set the nSYNC/nRESET pin (0=low, 1=high)
//!
//! NOTE: The 'HIGH' and 'LOW' macros defined in hal.h can be passed to this
//! function for the 'state' parameter value.
//!
//! \return None.
//
//*****************************************************************************
void setSYNC_RESET(const bool state)
{
/* --- INSERT YOUR CODE HERE --- */
unsigned char value = (unsigned char) (state ? nSYNC_nRESET_PIN : 0);
GPIO_writePin(nSYNC_nRESET_PIN, value);
//MAP_GPIOPinWrite(nSYNC_nRESET_PORT, nSYNC_nRESET_PIN, value);
}
//*****************************************************************************
//
//! Toggles the "nSYNC/nRESET" pin to trigger a synchronization
//! (LOW, delay 2 us, then HIGH).
//!
//! \fn void toggleSYNC(void)
//!
//! \return None.
//
//*****************************************************************************
void toggleSYNC(void)
{
/* --- INSERT YOUR CODE HERE --- */
GPIO_writePin(nSYNC_nRESET_PIN, 0);
//MAP_GPIOPinWrite(nSYNC_nRESET_PORT, nSYNC_nRESET_PIN, 0);
// nSYNC pulse width must be between 1 and 2,048 CLKIN periods
delay_us(2);
GPIO_writePin(nSYNC_nRESET_PIN, nSYNC_nRESET_PIN);
//MAP_GPIOPinWrite(nSYNC_nRESET_PORT, nSYNC_nRESET_PIN, nSYNC_nRESET_PIN);
}
//*****************************************************************************
//
//! Toggles the "nSYNC/nRESET" pin to trigger a reset
//! (LOW, delay 2 ms, then HIGH).
//!
//! \fn void toggleRESET(void)
//!
//! \return None.
//
//*****************************************************************************
void toggleRESET(void)
{
/* --- INSERT YOUR CODE HERE --- */
GPIO_writePin(nSYNC_nRESET_PIN, 0);
//MAP_GPIOPinWrite(nSYNC_nRESET_PORT, nSYNC_nRESET_PIN, 0);
// Minimum /RESET pulse width (tSRLRST) equals 2,048 CLKIN periods (1 ms @ 2.048 MHz)
delay_ms(2);
GPIO_writePin(nSYNC_nRESET_PIN, nSYNC_nRESET_PIN);
// MAP_GPIOPinWrite(nSYNC_nRESET_PORT, nSYNC_nRESET_PIN, nSYNC_nRESET_PIN);
// tREGACQ delay before communicating with the device again
delay_us(5);
// NOTE: The ADS131M0x's next response word should be (0xFF20 | CHANCNT).
// A different response may be an indication that the device did not reset.
// Update register array
restoreRegisterDefaults();
// Write to MODE register to enforce mode settings
writeSingleRegister(MODE_ADDRESS, MODE_DEFAULT);
writeSingleRegister2(MODE_ADDRESS, MODE_DEFAULT);
}
//*****************************************************************************
//
//! Waits for the nDRDY interrupt or until the specified timeout occurs.
//!
//! \fn bool waitForDRDYinterrupt(const uint32_t timeout_ms)
//!
//! \param timeout_ms number of milliseconds to wait before timeout event.
//!
//! \return Returns 'true' if nDRDY interrupt occurred before the timeout.
//
//*****************************************************************************
//bool waitForDRDYinterrupt(const uint32_t timeout_ms)
//{
/* --- INSERT YOUR CODE HERE ---
* Poll the nDRDY GPIO pin until it goes low. To avoid potential infinite
* loops, you may also want to implement a timer interrupt to occur after
* the specified timeout period, in case the nDRDY pin is not active.
* Return a boolean to indicate if nDRDY went low or if a timeout occurred.
*/
// Convert ms to a # of loop iterations, OR even better use a timer here...
//uint32_t timeout = timeout_ms * 6000; // convert to # of loop iterations
// Reset interrupt flag
//flag_nDRDY_INTERRUPT = false;
// Enable interrupts
//Interrupt_enableMaster(void);
//IntMasterEnable();
// Wait for nDRDY interrupt or timeout - each iteration is about 20 ticks
//do {
// timeout--;
//} while (!flag_nDRDY_INTERRUPT && (timeout > 0));
// Reset interrupt flag
//flag_nDRDY_INTERRUPT = false;
// Timeout counter greater than zero indicates that an interrupt occurred
//return (timeout > 0);
//}
//****************************************************************************
//
// SPI Communication
//
//****************************************************************************
//#define SPI_BASE_ADDR SPIA_BASE
//#define SPI_BASE_ADDR 0x00006100U
//#define SPIA_BASE 0x00006100U // SPI A Registers
//*****************************************************************************
//
//! Configures the MCU's SPI peripheral, for interfacing with the ADC.
//!
//! \fn void InitSPI(void)
//!
//! \return None.
//
//*****************************************************************************
//void InitSPI(void)
//{
//} --- INSERT YOUR CODE HERE ---
/* NOTE: The ADS131M0x operates in SPI mode 1 (CPOL = 0, CPHA = 1).
*/
// SPI_disableModule(SPIA_BASE);
//
// Enable the clock to SSI-3 module and configure the SSI Master
//
// SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_SPIA);
// MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI3);
/* *****Delay Function******
while(!(MAP_SysCtlPeripheralReady(SYSCTL_PERIPH_SSI3)))
{
}
*/
//
// Enable clocks to GPIO Port Q and configure pins as SSI
//
// SysCtl_enablePeripheral(SysCtl_PeripheralPCLOCKCR peripheral); // GPIO clock if any
//MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOQ);
/* *****Delay Function******
while(!(MAP_SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOQ)))
{
}
*/
// GPIO_setPinConfig(GPIO_16_SPISIMOA); // GPIO16 = SPISIMOA
//GPIO_setPinConfig(GPIO_17_SPISOMIA); // GPIO17 = SPIS0MIA
//GPIO_setPinConfig(GPIO_9_SPICLKA); // GPIO18 = SPICLKA
// /GPIO_setPinConfig(GPIO_19_SPISTEA); // GPIO19 = SPISTEA
// MAP_GPIOPinConfigure(GPIO_PQ0_SSI3CLK);
/// was already commented //MAP_GPIOPinConfigure(GPIO_PA3_SSI0FSS); // Using GPIO for nCS instead of the FSS pin.
//MAP_GPIOPinConfigure(GPIO_PQ2_SSI3XDAT0);
// MAP_GPIOPinConfigure(GPIO_PQ3_SSI3XDAT1);
// GPIO_setQualificationMode(16, GPIO_QUAL_ASYNC); // asynch input
// GPIO_setQualificationMode(17, GPIO_QUAL_ASYNC); // asynch input
// GPIO_setQualificationMode(9, GPIO_QUAL_ASYNC); // asynch input
//GPIO_setQualificationMode(19, GPIO_QUAL_ASYNC); // asynch input
//
// SPI configuration. Use a 1MHz SPICLK and 16-bit word size.
//
// SPI_enableHighSpeedMode(SPIA_BASE);
//SPI_setConfig(SPIA_BASE, (DEVICE_OSCSRC_FREQ * 10 * 1) / 2, SPI_PROT_POL0PHA0,
// SPI_MODE_MASTER, 16000000, 16);
//SPI_enableLoopback(SPIA_BASE);
// SPI_setEmulationMode(SPIA_BASE, SPI_EMULATION_FREE_RUN);
// SPI_enableFIFO(SPIA_BASE);
//
// Configuration complete. Enable the module.
//
//SPI_enableModule(SPIA_BASE);
//MUX GPIO_setPadConfig(uint32_t pin, uint32_t pinType);
// MAP_GPIOPinTypeSSI(GPIO_PORTQ_BASE, (GPIO_PIN_0 | GPIO_PIN_2 | GPIO_PIN_3));
// Configure: SPI MODE 1, 5 MHz SCLK, 8-bits per frame
// SPI_setConfig(SPI_BASE_ADDR, (DEVICE_OSCSRC_FREQ * 10 * 1) / 2, SPI_PROT_POL0PHA0,
// SPI_MODE_MASTER, 16000000, 16);
// MAP_SSIConfigSetExpClk(SSI_BASE_ADDR, getSysClockHz(), SSI_FRF_MOTO_MODE_1, \
SSI_MODE_MASTER, (getSysClockHz()/24), 8);
// //was already commented // MAP_SSIEnable(SSI_BASE_ADDR);
//
// Enable the SSI2 module.
//
// SSIEnable(SSI_BASE_ADDR);
/// ConfigurE ALL THE MODE REQUIRED
// ? SSIAdvModeSet(SPI_BASE_ADDR, SSI_ADV_MODE_READ_WRITE);
//? SSIAdvFrameHoldDisable(SPI_BASE_ADDR);
//
// Read any residual data from the SSI port. This makes sure the receive
// FIFOs are empty, so we don't read any unwanted junk. This is done here
// because the SPI SSI mode is full-duplex, which allows you to send and
// receive at the same time. The SSIDataGetNonBlocking function returns
// "true" when data was returned, and "false" when no data was returned.
// The "non-blocking" function checks if there is any data in the receive
// FIFO and does not "hang" if there isn't.
//
// uint32_t junk;
// while(SPI_readDataNonBlocking(SPI_BASE_ADDR));
// while(MAP_SSIDataGetNonBlocking(SSI_BASE_ADDR, &junk));
//
//}
//*****************************************************************************
//
//! Sends SPI byte array on MOSI pin and captures MISO data to a byte array.
//!
//! \fn void spiSendReceiveArrays(const uint8_t dataTx[], uint8_t dataRx[], const uint8_t byteLength)
//!
//! \param const uint8_t dataTx[] byte array of SPI data to send on MOSI.
//!
//! \param uint8_t dataRx[] byte array of SPI data captured on MISO.
//!
//! \param uint8_t byteLength number of bytes to send & receive.
//!
//! NOTE: Make sure 'dataTx[]' and 'dataRx[]' contain at least as many bytes of data,
//! as indicated by 'byteLength'.
//!
//! \return None.
//
//*****************************************************************************
void spiSendReceiveArrays(const unsigned char dataTx[], unsigned char dataRx[], const unsigned char byteLength) {
/* --- INSERT YOUR CODE HERE ---
*
* This function should send and receive multiple bytes over the SPI.
*
* A typical SPI send/receive sequence may look like the following:
* 1) Make sure SPI receive buffer is empty
* 2) Set the /CS pin low (if controlled by GPIO)
* 3) Send command bytes to SPI transmit buffer
* 4) Wait for SPI receive interrupt
* 5) Retrieve data from SPI receive buffer
* 6) Set the /CS pin high (if controlled by GPIO)
*/
// Require that dataTx and dataRx are not NULL pointers
// assert(dataTx && dataRx);
// Set the nCS pin LOW
setCS(LOW);
// Send all dataTx[] bytes on MOSI, and capture all MISO bytes in dataRx[]
int i;
for (i = 0; i < byteLength; i++)
{
dataRx[i] = spiSendReceiveByte(dataTx[i]);
}
// Set the nCS pin HIGH
setCS(HIGH);
}
void spiBSendReceiveArrays(const unsigned char dataTx[], unsigned char dataRx[], const unsigned char byteLength) {
/* --- INSERT YOUR CODE HERE ---
*
* This function should send and receive multiple bytes over the SPI.
*
* A typical SPI send/receive sequence may look like the following:
* 1) Make sure SPI receive buffer is empty
* 2) Set the /CS pin low (if controlled by GPIO)
* 3) Send command bytes to SPI transmit buffer
* 4) Wait for SPI receive interrupt
* 5) Retrieve data from SPI receive buffer
* 6) Set the /CS pin high (if controlled by GPIO)
*/
// Require that dataTx and dataRx are not NULL pointers
// assert(dataTx && dataRx);
// Set the nCS pin LOW
setCSB(LOW);
// Send all dataTx[] bytes on MOSI, and capture all MISO bytes in dataRx[]
int i;
for (i = 0; i < byteLength; i++)
{
dataRx[i] = spiBSendReceiveByte(dataTx[i]);
}
// Set the nCS pin HIGH
setCSB(HIGH);
}
//*****************************************************************************
//
//! Sends SPI byte on MOSI pin and captures MISO return byte value.
//!
//! \fn uint8_t spiSendReceiveByte(const uint8_t dataTx)
//!
//! \param const uint8_t dataTx data byte to send on MOSI pin.
//!
//! NOTE: This function is called by spiSendReceiveArrays(). If it is called
//! directly, then the /CS pin must also be directly controlled.
//!
//! \return Captured MISO response byte.
//
//*****************************************************************************
char spiSendReceiveByte(const unsigned char dataTx)
{
/* --- INSERT YOUR CODE HERE ---
* This function should send and receive single bytes over the SPI.
* NOTE: This function does not control the /CS pin to allow for
* more programming flexibility.
*/
// Remove any residual or old data from the receive FIFO
// uint32_t junk;
// while(junk = SPI_readDataNonBlocking(SPIA_BASE));
//? while (SSIDataGetNonBlocking(SSI_BASE_ADDR, &junk));
// SSI TX & RX
unsigned char dataRx;
SPI_writeDataBlockingFIFO(SPIA_BASE, (uint16_t) (dataTx <<8));
// MAP_SSIDataPut(SPI_BASE_ADDR, (uint32_t) dataTx);
dataRx = SPI_readDataBlockingFIFO(SPIA_BASE);
//?? MAP_SSIDataGet(SSI_BASE_ADDR, (uint32_t *) &dataRx);
return dataRx;
}
char spiBSendReceiveByte(const unsigned char dataTx)
{
/* --- INSERT YOUR CODE HERE ---
* This function should send and receive single bytes over the SPI.
* NOTE: This function does not control the /CS pin to allow for
* more programming flexibility.
*/
// Remove any residual or old data from the receive FIFO
// uint32_t junk;
// while(junk = SPI_readDataNonBlocking(SPIA_BASE));
//? while (SSIDataGetNonBlocking(SSI_BASE_ADDR, &junk));
// SSI TX & RX
unsigned char dataRx;
SPI_writeDataBlockingFIFO(SPIB_BASE, (uint16_t) (dataTx <<8));
// MAP_SSIDataPut(SPI_BASE_ADDR, (uint32_t) dataTx);
dataRx = SPI_readDataBlockingFIFO(SPIB_BASE);
//?? MAP_SSIDataGet(SSI_BASE_ADDR, (uint32_t *) &dataRx);
return dataRx;
}
BR