Files
lemon-pepper-stepper/firmware/lib/Arduino-FOC/src/sensors/MagneticSensorSPI.cpp
2023-11-09 19:02:15 -05:00

162 lines
5.1 KiB
C++

#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();
}