#include #include #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; } } }