570 lines
14 KiB
C++
570 lines
14 KiB
C++
#include <Arduino.h>
|
|
#include <SPI.h>
|
|
|
|
#define kNOERROR 0
|
|
#define kPRIMARYREADERROR 1
|
|
#define kEXTENDEDREADTIMEOUTERROR 2
|
|
#define kPRIMARYWRITEERROR 3
|
|
#define kEXTENDEDWRITETIMEOUTERROR 4
|
|
#define kCRCERROR 5
|
|
|
|
#define kUNABLETOCHANGEPROCESSORSTATE 6
|
|
|
|
const uint16_t ChipSelectPin = 5;
|
|
const uint16_t LEDPin = 13;
|
|
|
|
const uint32_t WRITE = 0x40;
|
|
const uint32_t READ = 0x00;
|
|
const uint32_t COMMAND_MASK = 0xC0;
|
|
const uint32_t ADDRESS_MASK = 0x3F;
|
|
|
|
unsigned long nextTime;
|
|
bool ledOn = false;
|
|
|
|
bool includeCRC = false;
|
|
|
|
/*
|
|
* CalculateParity
|
|
*
|
|
* From the 16 bit input, calculate the parity
|
|
*/
|
|
bool CalculateParity(uint16_t input)
|
|
{
|
|
uint16_t count = 0;
|
|
|
|
// Count up the number of 1s in the input
|
|
for (int index = 0; index < 16; ++index)
|
|
{
|
|
if ((input & 1) == 1)
|
|
{
|
|
++count;
|
|
}
|
|
|
|
input >>= 1;
|
|
}
|
|
|
|
// return true if there is an odd number of 1s
|
|
return (count & 1) != 0;
|
|
}
|
|
|
|
/*
|
|
* CalculateCRC
|
|
*
|
|
* Take the 16bit input and generate a 4bit CRC
|
|
* Polynomial = x^4 + x^1 + 1
|
|
* LFSR preset to all 1's
|
|
*/
|
|
uint8_t CalculateCRC(uint16_t input)
|
|
{
|
|
bool CRC0 = true;
|
|
bool CRC1 = true;
|
|
bool CRC2 = true;
|
|
bool CRC3 = true;
|
|
int i;
|
|
bool DoInvert;
|
|
uint16_t mask = 0x8000;
|
|
|
|
for (i = 0; i < 16; ++i)
|
|
{
|
|
DoInvert = ((input & mask) != 0) ^ CRC3; // XOR required?
|
|
|
|
CRC3 = CRC2;
|
|
CRC2 = CRC1;
|
|
CRC1 = CRC0 ^ DoInvert;
|
|
CRC0 = DoInvert;
|
|
mask >>= 1;
|
|
}
|
|
|
|
return (CRC3 ? 8U : 0U) + (CRC2 ? 4U : 0U) + (CRC1 ? 2U : 0U) + (CRC0 ? 1U : 0U);
|
|
}
|
|
|
|
|
|
/*
|
|
* PrimaryRead
|
|
*
|
|
* Read from the primary serial registers
|
|
*/
|
|
uint16_t PrimaryRead(uint16_t cs, uint16_t address, uint16_t& value)
|
|
{
|
|
if (includeCRC)
|
|
{
|
|
uint8_t crcValue;
|
|
uint8_t crcCommand;
|
|
|
|
SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE3));
|
|
|
|
// On the Teensy, SPI0_CTAR0 is used to describe the SPI transaction for transfer (byte)
|
|
// while SPI0_CTAR1 is used to describe the SPI transaction for transfer16.
|
|
// To do a 20 bit transfer, change the length of the transaction to 4 bits for transfer
|
|
// and do a transfer16 followed by a transfer.
|
|
//uint32_t oldSPI0_CTAR0 = SPI0_CTAR0;
|
|
// SPI0_CTAR0 = (SPI0_CTAR0 & 0x87FFFFFF) | SPI_CTAR_FMSZ(3); // using SPI0_CTAR0 to send 4 bits
|
|
|
|
// Combine the register address and the command into one byte
|
|
uint16_t command = ((address & ADDRESS_MASK) | READ) << 8;
|
|
|
|
crcCommand = CalculateCRC(command);
|
|
|
|
// take the chip select low to select the device
|
|
digitalWrite(cs, LOW);
|
|
|
|
// send the device the register you want to read
|
|
SPI.transfer16(command);
|
|
SPI.transfer(crcCommand);
|
|
|
|
digitalWrite(cs, HIGH);
|
|
digitalWrite(cs, LOW);
|
|
|
|
// send the command again to read the contents
|
|
value = SPI.transfer16(command);
|
|
crcValue = SPI.transfer(crcCommand);
|
|
|
|
// take the chip select high to de-select
|
|
digitalWrite(cs, HIGH);
|
|
|
|
// Restore the 8 bit description
|
|
//SPI0_CTAR0 = oldSPI0_CTAR0;
|
|
|
|
SPI.endTransaction();
|
|
|
|
// Check the CRC value
|
|
if (CalculateCRC(value) != crcValue)
|
|
{
|
|
return kCRCERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE3));
|
|
|
|
// Combine the register address and the command into one byte
|
|
uint16_t command = ((address & ADDRESS_MASK) | READ) << 8;
|
|
|
|
// take the chip select low to select the device
|
|
digitalWrite(cs, LOW);
|
|
|
|
// send the device the register you want to read
|
|
SPI.transfer16(command);
|
|
|
|
digitalWrite(cs, HIGH);
|
|
digitalWrite(cs, LOW);
|
|
|
|
// send the command again to read the contents
|
|
value = SPI.transfer16(command);
|
|
|
|
// take the chip select high to de-select
|
|
digitalWrite(cs, HIGH);
|
|
|
|
SPI.endTransaction();
|
|
}
|
|
|
|
return kNOERROR;
|
|
}
|
|
|
|
/*
|
|
* PrimaryWrite
|
|
*
|
|
* Write to the primary serial registers
|
|
*/
|
|
uint16_t PrimaryWrite(uint16_t cs, uint16_t address, uint16_t value)
|
|
{
|
|
if (includeCRC)
|
|
{
|
|
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE3));
|
|
|
|
// On the Teensy, SPI0_CTAR0 is used to describe the SPI transaction for transfer
|
|
// while SPI0_CTAR1 is used to describe the SPI transaction for transfer16.
|
|
// To do a 20 bit transfer, change the length of the transaction to 4 bits for transfer
|
|
// and do a transfer16 followed by a transfer.
|
|
//uint32_t oldSPI0_CTAR0 = SPI0_CTAR0;
|
|
// SPI0_CTAR0 = (SPI0_CTAR0 & 0x87FFFFFF) | SPI_CTAR_FMSZ(3); // using SPI0_CTAR0 to send 4 bits
|
|
|
|
// Combine the register address and the command into one byte
|
|
uint16_t command = (((address & ADDRESS_MASK) | WRITE) << 8) | ((value >> 8) & 0x0FF);
|
|
uint8_t crcCommand = CalculateCRC(command);
|
|
|
|
// take the chip select low to select the device:
|
|
digitalWrite(cs, LOW);
|
|
|
|
SPI.transfer16(command); // Send most significant byte of register data
|
|
SPI.transfer(crcCommand); // Send the crc
|
|
|
|
// take the chip select high to de-select:
|
|
digitalWrite(cs, HIGH);
|
|
|
|
command = ((((address + 1) & ADDRESS_MASK) | WRITE) << 8 ) | (value & 0x0FF);
|
|
crcCommand = CalculateCRC(command);
|
|
|
|
// take the chip select low to select the device:
|
|
digitalWrite(cs, LOW);
|
|
|
|
SPI.transfer16(command); // Send least significant byte of register data
|
|
SPI.transfer(crcCommand); // Send the crc
|
|
|
|
// take the chip select high to de-select:
|
|
digitalWrite(cs, HIGH);
|
|
|
|
// Restore the 8 bit description
|
|
//SPI0_CTAR0 = oldSPI0_CTAR0;
|
|
|
|
SPI.endTransaction();
|
|
}
|
|
else
|
|
{
|
|
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE3));
|
|
|
|
// Combine the register address and the command into one byte
|
|
uint16_t command = ((address & ADDRESS_MASK) | WRITE) << 8;
|
|
|
|
// take the chip select low to select the device:
|
|
digitalWrite(cs, LOW);
|
|
|
|
SPI.transfer16(command | ((value >> 8) & 0x0FF)); // Send most significant byte of register data
|
|
|
|
// take the chip select high to de-select:
|
|
digitalWrite(cs, HIGH);
|
|
|
|
command = (((address + 1) & ADDRESS_MASK) | WRITE) << 8;
|
|
// take the chip select low to select the device:
|
|
digitalWrite(cs, LOW);
|
|
|
|
SPI.transfer16(command | (value & 0x0FF)); // Send least significant byte of register data
|
|
|
|
// take the chip select high to de-select:
|
|
digitalWrite(cs, HIGH);
|
|
|
|
SPI.endTransaction();
|
|
}
|
|
|
|
return kNOERROR;
|
|
}
|
|
|
|
/*
|
|
* ExtendedRead
|
|
*
|
|
* Read from the SRAM, EEPROM, AUX or Extended Registers
|
|
*/
|
|
uint16_t ExtendedRead(uint16_t cs, uint16_t address, uint32_t& value)
|
|
{
|
|
uint16_t results;
|
|
uint16_t readFlags;
|
|
uint32_t timeout;
|
|
uint16_t valueMSW;
|
|
uint16_t valueLSW;
|
|
uint32_t currentTime;
|
|
|
|
// Write the address to the Extended Read Address register
|
|
results = PrimaryWrite(cs, 0x0A, address & 0xFFFF);
|
|
|
|
if (results != kNOERROR)
|
|
{
|
|
return results;
|
|
}
|
|
|
|
// Initiate the extended read
|
|
results = PrimaryWrite(cs, 0x0C, 0x8000);
|
|
|
|
if (results != kNOERROR)
|
|
{
|
|
return results;
|
|
}
|
|
|
|
timeout = millis() + 100L;
|
|
|
|
do // Wait for the read to be complete
|
|
{
|
|
results = PrimaryRead(cs, 0x0C, readFlags);
|
|
|
|
if (results != kNOERROR)
|
|
{
|
|
return results;
|
|
}
|
|
|
|
// Make sure the read is not taking too long
|
|
currentTime = millis();
|
|
if (timeout < currentTime)
|
|
{
|
|
return kEXTENDEDREADTIMEOUTERROR;
|
|
}
|
|
} while ((readFlags & 0x0001) != 0x0001);
|
|
|
|
// Read the most significant word from the extended read data
|
|
results = PrimaryRead(cs, 0x0E, valueMSW);
|
|
|
|
if (results != kNOERROR)
|
|
{
|
|
return results;
|
|
}
|
|
|
|
// Read the least significant word from the extended read data
|
|
results = PrimaryRead(cs, 0x10, valueLSW);
|
|
|
|
// Combine them
|
|
value = ((uint32_t)valueMSW << 16) + valueLSW;
|
|
|
|
return results;
|
|
}
|
|
|
|
/*
|
|
* ExtendedWrite
|
|
*
|
|
* Write to the SRAM, EEPROM, AUX or Extended Access Commands
|
|
*/
|
|
uint16_t ExtendedWrite(uint16_t cs, uint16_t address, uint32_t value)
|
|
{
|
|
uint16_t results;
|
|
uint16_t writeFlags;
|
|
uint32_t timeout;
|
|
|
|
// Write into the extended address register
|
|
results = PrimaryWrite(cs, 0x02, address & 0xFFFF);
|
|
|
|
if (results != kNOERROR)
|
|
{
|
|
return results;
|
|
}
|
|
|
|
// Write the MSW (Most significant word) into the high order write data register
|
|
results = PrimaryWrite(cs, 0x04, (value >> 16) & 0xFFFF);
|
|
|
|
if (results != kNOERROR)
|
|
{
|
|
return results;
|
|
}
|
|
|
|
// Write the LSW (Least significant word) into the low order write data register
|
|
results = PrimaryWrite(cs, 0x06, value & 0xFFFF);
|
|
|
|
if (results != kNOERROR)
|
|
{
|
|
return results;
|
|
}
|
|
|
|
// Start the write process
|
|
results = PrimaryWrite(cs, 0x08, 0x8000);
|
|
|
|
if (results != kNOERROR)
|
|
{
|
|
return results;
|
|
}
|
|
|
|
// If writing to the EEPROM, generate the Program
|
|
// Pulses on Vcc
|
|
if ((address >= 0x300) && (address < 0x320))
|
|
{
|
|
// Send the Program Pulses
|
|
// Need hardware which this example does not have.
|
|
}
|
|
|
|
timeout = millis() + 100;
|
|
|
|
// Wait for the write to complete
|
|
do
|
|
{
|
|
results = PrimaryRead(cs, 0x08, writeFlags);
|
|
|
|
if (results != kNOERROR)
|
|
{
|
|
return results;
|
|
}
|
|
|
|
if (timeout < millis())
|
|
{
|
|
return kEXTENDEDWRITETIMEOUTERROR;
|
|
}
|
|
} while ((writeFlags & 0x0001) != 0x0001);
|
|
|
|
return results;
|
|
}
|
|
|
|
/*
|
|
* SetProcessorStateToIdle
|
|
*
|
|
* Change the processor state to idle
|
|
* This is needed to write EEPROM, change ORATE or perform certain self tests
|
|
*/
|
|
uint16_t SetProcessorStateToIdle(uint8_t cs)
|
|
{
|
|
uint16_t results;
|
|
uint16_t value;
|
|
|
|
// Write the enter idle state command into the control register
|
|
results = PrimaryWrite(cs, 0x1F, 0x8046);
|
|
|
|
if (results == kNOERROR)
|
|
{
|
|
delay(1);
|
|
|
|
// Read the status register to see if the processor is in the idle state
|
|
results = PrimaryRead(cs, 0x22, value);
|
|
|
|
if (results == kNOERROR)
|
|
{
|
|
if ((value & 0x00FF) != 0x0010)
|
|
{
|
|
return kUNABLETOCHANGEPROCESSORSTATE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
/*
|
|
* SetProcessorStateToRun
|
|
*
|
|
* Change the processor state to run
|
|
* This is needed to process angles
|
|
*/
|
|
uint16_t SetProcessorStateToRun(uint8_t cs)
|
|
{
|
|
uint16_t results;
|
|
uint16_t value;
|
|
|
|
// Write the enter idle state command into the control register
|
|
results = PrimaryWrite(cs, 0x1F, 0xC046);
|
|
|
|
if (results == kNOERROR)
|
|
{
|
|
delay(1);
|
|
|
|
// Read the status register to see if the processor is in the idle state
|
|
results = PrimaryRead(cs, 0x22, value);
|
|
|
|
if (results == kNOERROR)
|
|
{
|
|
if ((value & 0x00FF) != 0x0011)
|
|
{
|
|
return kUNABLETOCHANGEPROCESSORSTATE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
|
|
|
|
void setup() {
|
|
|
|
uint16_t unused;
|
|
uint32_t flags;
|
|
uint16_t angle;
|
|
uint32_t flagsAndZeroOffset;
|
|
|
|
SPI.begin();
|
|
// put your setup code here, to run once:
|
|
Serial.begin(19200);
|
|
|
|
while (!Serial);
|
|
|
|
|
|
pinMode(SS, OUTPUT);
|
|
digitalWrite(SS, HIGH);
|
|
|
|
// Make sure all of the SPI pins are
|
|
// ready by doing a read
|
|
//PrimaryRead(ChipSelectPin, 0x0, unused);
|
|
|
|
// Unlock the device
|
|
ExtendedWrite(ChipSelectPin, 0xFFFE, 0x27811F77);
|
|
|
|
// Make sure the device is unlocked
|
|
ExtendedRead(ChipSelectPin, 0x22, flags);
|
|
if (!((flags & 0x0022) == 0x0020))
|
|
{
|
|
Serial.println("Device is not Unlocked");
|
|
}
|
|
|
|
// Zero the angle
|
|
// Extended location 0x06 contains flags in the MSW and the Zero Angle values in the LSW
|
|
// so get both and zero out ZeroAngle
|
|
ExtendedRead(ChipSelectPin, 0x06, flagsAndZeroOffset);
|
|
flagsAndZeroOffset = (flagsAndZeroOffset & 0xFFFF0000);
|
|
ExtendedWrite(ChipSelectPin, 0x06, flagsAndZeroOffset);
|
|
|
|
// Get the current angle. It is now without the ZeroAngle correction
|
|
PrimaryRead(ChipSelectPin, 0x20, angle);
|
|
|
|
// Copy the read angle into location 0x5C preserving the flags
|
|
flagsAndZeroOffset = (flagsAndZeroOffset & 0xFFFF0000) | ((angle << 4) & 0x0000FFFF);
|
|
ExtendedWrite(ChipSelectPin, 0x06, flagsAndZeroOffset);
|
|
|
|
/* uint16_t angle_read;
|
|
Serial.println("Init Complete");
|
|
PrimaryRead(ChipSelectPin, 0x20, angle_read);
|
|
Serial.print("Angle= ");
|
|
Serial.println(angle_read); */
|
|
|
|
|
|
}
|
|
|
|
void loop() {
|
|
uint16_t angle;
|
|
uint16_t temperature;
|
|
uint16_t fieldStrength;
|
|
|
|
// Every second, read the angle, temperature and field strength
|
|
if (nextTime < millis())
|
|
{
|
|
if (PrimaryRead(ChipSelectPin, 0x20, angle) == kNOERROR)
|
|
{
|
|
if (CalculateParity(angle))
|
|
{
|
|
Serial.print("Angle = ");
|
|
Serial.print((float)(angle & 0x0FFF) * 360.0 / 4096.0);
|
|
Serial.println(" Degrees");
|
|
}
|
|
else
|
|
{
|
|
Serial.println("Parity error on Angle read");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Serial.println("Unable to read Angle");
|
|
}
|
|
|
|
if (PrimaryRead(ChipSelectPin, 0x28, temperature) == kNOERROR)
|
|
{
|
|
Serial.print("Temperature = ");
|
|
Serial.print(((float)(temperature & 0x0FFF) / 8.0) - 273.15);
|
|
Serial.println(" C");
|
|
}
|
|
else
|
|
{
|
|
Serial.println("Unable to read Temperature");
|
|
}
|
|
|
|
if (PrimaryRead(ChipSelectPin, 0x2A, fieldStrength) == kNOERROR)
|
|
{
|
|
Serial.print("Field Strength = ");
|
|
Serial.print(fieldStrength & 0x0FFF);
|
|
Serial.println(" Gauss");
|
|
}
|
|
else
|
|
{
|
|
Serial.println("Unable to read Field Strength");
|
|
}
|
|
|
|
nextTime = millis() + 500L;
|
|
|
|
// Blink the LED every half second
|
|
if (ledOn)
|
|
{
|
|
digitalWrite(LEDPin, LOW);
|
|
ledOn = false;
|
|
}
|
|
else
|
|
{
|
|
digitalWrite(LEDPin, HIGH);
|
|
ledOn = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|