#include "MagneticSensorSPI.h" /** Typical configuration for the 14bit AMS AS5147 magnetic sensor over SPI interface */ MagneticSensorSPIConfig_s AS5147_SPI = { .spi_mode = SPI_MODE1, .clock_speed = 1000000, .bit_resolution = 14, .angle_register = 0x3FFF, .data_start_bit = 13, .command_rw_bit = 14, .command_parity_bit = 15 }; // AS5048 and AS5047 are the same as AS5147 MagneticSensorSPIConfig_s AS5048_SPI = AS5147_SPI; MagneticSensorSPIConfig_s AS5047_SPI = AS5147_SPI; /** Typical configuration for the 14bit MonolithicPower MA730 magnetic sensor over SPI interface */ MagneticSensorSPIConfig_s MA730_SPI = { .spi_mode = SPI_MODE0, .clock_speed = 1000000, .bit_resolution = 14, .angle_register = 0x0000, .data_start_bit = 15, .command_rw_bit = 0, // not required .command_parity_bit = 0 // parity not implemented }; // MagneticSensorSPI(int cs, float _bit_resolution, int _angle_register) // cs - SPI chip select pin // _bit_resolution sensor resolution bit number // _angle_register - (optional) angle read register - default 0x3FFF MagneticSensorSPI::MagneticSensorSPI(int cs, int _bit_resolution, int _angle_register){ chip_select_pin = cs; // angle read register of the magnetic sensor angle_register = _angle_register ? _angle_register : DEF_ANGLE_REGISTER; // register maximum value (counts per revolution) cpr = _powtwo(_bit_resolution); spi_mode = SPI_MODE1; clock_speed = 1000000; bit_resolution = _bit_resolution; command_parity_bit = 15; // for backwards compatibilty command_rw_bit = 14; // for backwards compatibilty data_start_bit = 13; // for backwards compatibilty } MagneticSensorSPI::MagneticSensorSPI(MagneticSensorSPIConfig_s config, int cs){ chip_select_pin = cs; // angle read register of the magnetic sensor angle_register = config.angle_register ? config.angle_register : DEF_ANGLE_REGISTER; // register maximum value (counts per revolution) cpr = _powtwo(config.bit_resolution); spi_mode = config.spi_mode; clock_speed = config.clock_speed; bit_resolution = config.bit_resolution; command_parity_bit = config.command_parity_bit; // for backwards compatibilty command_rw_bit = config.command_rw_bit; // for backwards compatibilty data_start_bit = config.data_start_bit; // for backwards compatibilty } void MagneticSensorSPI::init(SPIClass* _spi){ spi = _spi; // 1MHz clock (AMS should be able to accept up to 10MHz) settings = SPISettings(clock_speed, MSBFIRST, spi_mode); //setup pins pinMode(chip_select_pin, OUTPUT); //SPI has an internal SPI-device counter, it is possible to call "begin()" from different devices spi->begin(); // do any architectures need to set the clock divider for SPI? Why was this in the code? //spi->setClockDivider(SPI_CLOCK_DIV8); digitalWrite(chip_select_pin, HIGH); this->Sensor::init(); // call base class init } // Shaft angle calculation // angle is in radians [rad] float MagneticSensorSPI::getSensorAngle(){ return (getRawCount() / (float)cpr) * _2PI; } // function reading the raw counter of the magnetic sensor int MagneticSensorSPI::getRawCount(){ return (int)MagneticSensorSPI::read(angle_register); } // SPI functions /** * Utility function used to calculate even parity of word */ byte MagneticSensorSPI::spiCalcEvenParity(word value){ byte cnt = 0; byte i; for (i = 0; i < 16; i++) { if (value & 0x1) cnt++; value >>= 1; } return cnt & 0x1; } /* * Read a register from the sensor * Takes the address of the register as a 16 bit word * Returns the value of the register */ word MagneticSensorSPI::read(word angle_register){ word command = angle_register; if (command_rw_bit > 0) { command = angle_register | (1 << command_rw_bit); } if (command_parity_bit > 0) { //Add a parity bit on the the MSB command |= ((word)spiCalcEvenParity(command) << command_parity_bit); } //SPI - begin transaction spi->beginTransaction(settings); //Send the command digitalWrite(chip_select_pin, LOW); spi->transfer16(command); digitalWrite(chip_select_pin,HIGH); #if defined(ESP_H) && defined(ARDUINO_ARCH_ESP32) // if ESP32 board delayMicroseconds(50); // why do we need to delay 50us on ESP32? In my experience no extra delays are needed, on any of the architectures I've tested... #else delayMicroseconds(1); // delay 1us, the minimum time possible in plain arduino. 350ns is the required time for AMS sensors, 80ns for MA730, MA702 #endif //Now read the response digitalWrite(chip_select_pin, LOW); word register_value = spi->transfer16(0x00); digitalWrite(chip_select_pin, HIGH); //SPI - end transaction spi->endTransaction(); register_value = register_value >> (1 + data_start_bit - bit_resolution); //this should shift data to the rightmost bits of the word const static word data_mask = 0xFFFF >> (16 - bit_resolution); return register_value & data_mask; // Return the data, stripping the non data (e.g parity) bits } /** * Closes the SPI connection * SPI has an internal SPI-device counter, for each init()-call the close() function must be called exactly 1 time */ void MagneticSensorSPI::close(){ spi->end(); }