Library for SPI communication with the AMS AS5048 rotary sensor
Dependents: heros_leg_readout_torque_addition heros_leg_readout_torque_addition heros_leg_readout_torque_addition_V3
Revision 5:9df31d15f3fa, committed 2016-08-25
- Comitter:
- megrootens
- Date:
- Thu Aug 25 14:34:39 2016 +0000
- Parent:
- 4:56d59ce73270
- Commit message:
- C++ Style guide: file names lower case
Changed in this revision
As5048.h | Show diff for this revision Revisions of this file |
as5048.h | Show annotated file Show diff for this revision Revisions of this file |
diff -r 56d59ce73270 -r 9df31d15f3fa As5048.h --- a/As5048.h Tue Aug 23 15:11:48 2016 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,351 +0,0 @@ -#ifndef _AS5048_H_ -#define _AS5048_H_ - -#include "mbed.h" -/** - * Interfacing with the AMS AS5048A magnetic rotary sensor using SPI protocol - * AS5048 uses 16-bit transfer; - * We use two 8-bit transfers for compatibility with 8-bit SPI master devices - * SPI protocol: - * Mode = 1: - * clock polarity = 0 --> clock pulse is high - * clock phase = 1 --> sample on falling edge of clock pulse - * Code was succesfully tested on the FRDM KL25Z and K22F. The same code fails - * on the K64F for some reason. Sampling using a logic analyzer does however - * show the same results for al three boards. - */ -class As5048 { - - -public: - - static const int kNumSensorBits = 14; // 14-bits sensor - static const uint16_t kCountsPerRev = 0x4000; // 2**NUM_SENSOR_BITS - static const uint16_t kMask = 0x3FFF; // 2**NUM_SENSOR_BITS - 1 - static const int kParity = 1; // even parity - - static const int kSpiFrequency = 1000000; // AS5048 max 10 MHz - static const int kSpiBitsPerTransfer = 8; - static const int kSpiMode = 1; - - static const float kDegPerRev = 360.0f; // 360 degrees/rev - static const float kRadPerRev = 6.28318530718f; // 2*pi rad/rev - - // AS5048 flags - typedef enum { - AS_FLAG_PARITY = 0x8000, - AS_FLAG_READ = 0x4000, - } As5048Flag; - - // AS5048 commands - typedef enum { - AS_CMD_NOP = 0x0000, - AS_CMD_ERROR = 0x0001 | AS_FLAG_READ, // Reads error register of sensor and clear error flags - AS_CMD_DIAGNOSTICS = 0x3FFD | AS_FLAG_READ, // Reads automatic gain control and diagnostics info - AS_CMD_MAGNITUDE = 0x3FFE | AS_FLAG_READ, - AS_CMD_ANGLE = 0x3FFF | AS_FLAG_PARITY | AS_FLAG_READ, - } As5048Command; - - // AS5048 diagnostics - typedef enum { - AS_DIAG_CORDIC_OVERFLOW = 0x0200, - AS_DIAG_HIGH_MAGNETIC = 0x0400, - AS_DIAG_LOW_MAGNETIC = 0x0800, - } As5048Diagnostics; - - /** - * Creates an object of num_sensors daisy chained AS5048 sensors; - * default number of sensors in chain is 1 - * @param mosi: pinname of the mosi pin of the spi communication - * @param miso: pinname of the miso pin of the spi communication - * @param sck: pinname of the clock pin of the spi communication - * @param cs: pinname of the chip select pin of the spi communication - * @param num_sensors = 1: number of sensors in daisy chain - */ - As5048(PinName mosi, PinName miso, PinName sck, PinName cs, int num_sensors = 1): - kNumSensors_(num_sensors), - chip_(cs), - spi_(mosi, miso, sck) - { - DeselectChip(); - - spi_.format(kSpiBitsPerTransfer, kSpiMode); - spi_.frequency(kSpiFrequency); - - read_buffer_ = new uint16_t[kNumSensors_]; - angle_buffer_ = new uint16_t[kNumSensors_]; - angle_offset_ = new uint16_t[kNumSensors_]; - directions_ = new bool[kNumSensors_]; - - for (int i=0; i<kNumSensors_; ++i) { - read_buffer_[i] = 0; - angle_buffer_[i] = 0; - angle_offset_[i] = 0; - directions_[i] = true; - } - - last_command_ = AS_CMD_NOP; - } - - - /** - * Destructor, memory deallocation - */ - ~As5048() - { - delete [] read_buffer_; - delete [] angle_buffer_; - delete [] angle_offset_; - delete [] directions_; - } - - /** - * Parity check - * @param n: integer to check - * @return: true if ok - */ - static bool CheckParity(int n) - { - int parity = n; - for(int i=1; i <= kNumSensorBits+1; ++i) { - n >>= 1; - parity ^= n; - } - return (parity & kParity) == 0; - } - - /** - * Update the buffer with angular measurements - * NOTE 1: - * If the last command sent through Transfer was *not* AS_CMD_ANGLE - * then we need an additional Transfer; this takes more time! - * This should not occur, since Transfer is not *yet* used elsewhere. - * NOTE 2: - * We run a parity check on the results from the transfer. We only - * update the angle_buffer_ with values that pass the parity check. - * Measurement using Timer on K64F for last_command_ == AS_CMD_ANGLE - * shows this function takes 87 or 88 us. - */ - void UpdateAngleBuffer() - { - // ensure that the new results indeed will be angles - if (last_command_ != AS_CMD_ANGLE) { - Transfer(AS_CMD_ANGLE); - } - - // update the read buffer - Transfer(AS_CMD_ANGLE); - - // update the angle buffer with parity checked values - for (int i=0; i<kNumSensors_; ++i) { - if (CheckParity(read_buffer_[i])) { - // only update angles when parity is correct - angle_buffer_[i] = read_buffer_[i]; - } - } - } - - /** - * @return: pointer to read_buffer_ - */ - const uint16_t* get_read_buffer() { return read_buffer_; } - - /** - * @return: pointer to angle_buffer_ - */ - const uint16_t* get_angle_buffer() { return angle_buffer_; } - - /** - * @return: pointer to angle_offet_ - */ - const uint16_t* get_angle_offset() { return angle_offset_; } - - /** - * @return: pointer to directions_ - */ - const bool * get_directions_() { return directions_;} - - /** - * You get the angles from two UpdateAngleBuffer() calls before - * @return: 14 bits absolute position - */ - int getAngle(int i_sensor=0) - { - int ans = ((int) (angle_buffer_[i_sensor] & kMask)) - angle_offset_[i_sensor]; - return directions_[i_sensor]?ans:-ans; - } - - /** - * You get the angles from two UpdateAngleBuffer() calls before - * @return: revolution ratio in [0,1] - */ - float getAngleRatio(int i_sensor=0) { return (float) getAngle(i_sensor) / kCountsPerRev; } - - /** - * You get the angles from two UpdateAngleBuffer() calls before - * @return: angle in degrees - */ - float getAngleDegrees(int i_sensor=0) { return getAngleRatio(i_sensor) * kDegPerRev; } - - /** - * You get the angles from two UpdateAngleBuffer() calls before - * @return: angle in radians - */ - float getAngleRadians(int i_sensor=0) { return getAngleRatio(i_sensor) * kRadPerRev; } - - /** - * Set direction for a sensor - * @param i_sensor: id of sensor for which the offset is to be set - * @param dir: true positive, false negative - * @return: true if i_sensor in [0,kNumSensor_) - */ - bool setDirection(int i_sensor, bool dir) - { - if (i_sensor>-1 and i_sensor<kNumSensors_) { - directions_[i_sensor] = dir; - return true; - } - return false; - } - - /** - * Set direction for the first sensor - * @param dir: true positive, false negative - * @return: true if i_sensor in [0,kNumSensor_) - */ - bool setDirection(bool dir) - { - return setDirection(0,dir); - } - - - /** - * Set offset for a sensor - * @param i_sensor: id of sensor for which the offset is to be set - * @param offset: offset in counts [0,2**14-1] - * @return: true if i_sensor in [0,kNumSensor_) - */ - bool setOffset(int i_sensor, uint16_t offset) - { - if (i_sensor>-1 and i_sensor<kNumSensors_) { - angle_offset_[i_sensor] = offset; - return true; - } - return false; - } - - /** - * Set offset for the first sensor - * @param offset: offset in counts [0,2**14-1] - * @return: true if i_sensor in [0,kNumSensor_) - */ - bool setOffset(uint16_t offset) { return setOffset(0,offset); } - - /** - * Set offset for a sensor - * @param i_sensor: id of sensor for which the offset is to be set - * @param offset_ratio: offset in ratio in [0,1] - * @return: true if i_sensor in [0,kNumSensor_) - */ - bool setOffsetRatio (int i_sensor, float offset_ratio) - { - return setOffset(i_sensor,offset_ratio*kCountsPerRev); - } - - /** - * Set offset for the first sensor - * @param offset_ratio: offset in ratio in [0,1] - * @return: true if i_sensor in [0,kNumSensor_) - */ - bool setOffsetRatio(float offset_ratio) - { - return setOffsetRatio(0,offset_ratio); - } - - /** - * Set offset for a sensor - * @param i_sensor: id of sensor for which the offset is to be set - * @param offset_degrees: offset in degrees in [0,360] - * @return: true if i_sensor in [0,kNumSensor_) - */ - bool setOffsetDegrees(int i_sensor, float offset_degrees) - { - return setOffsetRatio(i_sensor,offset_degrees / kDegPerRev); - } - - /** - * Set offset for the first sensor - * @param offset_degrees: offset in degrees in [0,360] - * @return: true if i_sensor in [0,kNumSensor_) - */ - bool setOffsetDegrees(float offset_degrees) - { - return setOffsetDegrees(0, offset_degrees); - } - - /** - * Set offset for a sensor - * @param i_sensor: id of sensor for which the offset is to be set - * @param offset_radians: offset in radians in [0,2*pi] - * @return: true if i_sensor in [0,kNumSensor_) - */ - bool setOffsetRadians(int i_sensor, float offset_radians) - { - return setOffsetRatio(i_sensor, offset_radians / kRadPerRev); - } - - /** - * Set offset for the first sensor - * @param offset_radians: offset in radians in [0,2*pi] - * @return: true if i_sensor in [0,kNumSensor_) - */ - bool setOffsetRadians(float offset_radians) - { - return setOffsetRadians(0, offset_radians); - } - - - - -protected: - - - - /** - * Select (low) chip, and wait 1 us (at least 350 ns) - */ - void SelectChip() { chip_.write(0); wait_us(1); } - - /** - * Deselect (high) chip, and wait 1 us (at least 350 ns) - */ - void DeselectChip() { chip_.write(1); wait_us(1); } - - /** - * SPI transfer between each of the daisy chained sensors - * @param cmd: Command to send - */ - void Transfer(As5048Command cmd) - { - SelectChip(); - for(int i=0; i<kNumSensors_; ++i){ - read_buffer_[i] = spi_.write(cmd>>8) << 8; - read_buffer_[i] |= spi_.write(cmd & 0x00FF); - } - DeselectChip(); - last_command_ = cmd; - } - - const int kNumSensors_; // number of sensors in daisy chain - DigitalOut chip_; // chip select port - SPI spi_; // mbed spi communiation object - - uint16_t* read_buffer_; // buffer for results from last transfer - uint16_t* angle_buffer_; // buffer for angle results from last transfer - uint16_t* angle_offset_; // offset array for each sensor - bool* directions_; // direction true positive, false negative - - As5048Command last_command_;// command sent during last Transfer - -}; -#endif \ No newline at end of file
diff -r 56d59ce73270 -r 9df31d15f3fa as5048.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/as5048.h Thu Aug 25 14:34:39 2016 +0000 @@ -0,0 +1,351 @@ +#ifndef _AS5048_H_ +#define _AS5048_H_ + +#include "mbed.h" +/** + * Interfacing with the AMS AS5048A magnetic rotary sensor using SPI protocol + * AS5048 uses 16-bit transfer; + * We use two 8-bit transfers for compatibility with 8-bit SPI master devices + * SPI protocol: + * Mode = 1: + * clock polarity = 0 --> clock pulse is high + * clock phase = 1 --> sample on falling edge of clock pulse + * Code was succesfully tested on the FRDM KL25Z and K22F. The same code fails + * on the K64F for some reason. Sampling using a logic analyzer does however + * show the same results for al three boards. + */ +class As5048 { + + +public: + + static const int kNumSensorBits = 14; // 14-bits sensor + static const uint16_t kCountsPerRev = 0x4000; // 2**NUM_SENSOR_BITS + static const uint16_t kMask = 0x3FFF; // 2**NUM_SENSOR_BITS - 1 + static const int kParity = 1; // even parity + + static const int kSpiFrequency = 1000000; // AS5048 max 10 MHz + static const int kSpiBitsPerTransfer = 8; + static const int kSpiMode = 1; + + static const float kDegPerRev = 360.0f; // 360 degrees/rev + static const float kRadPerRev = 6.28318530718f; // 2*pi rad/rev + + // AS5048 flags + typedef enum { + AS_FLAG_PARITY = 0x8000, + AS_FLAG_READ = 0x4000, + } As5048Flag; + + // AS5048 commands + typedef enum { + AS_CMD_NOP = 0x0000, + AS_CMD_ERROR = 0x0001 | AS_FLAG_READ, // Reads error register of sensor and clear error flags + AS_CMD_DIAGNOSTICS = 0x3FFD | AS_FLAG_READ, // Reads automatic gain control and diagnostics info + AS_CMD_MAGNITUDE = 0x3FFE | AS_FLAG_READ, + AS_CMD_ANGLE = 0x3FFF | AS_FLAG_PARITY | AS_FLAG_READ, + } As5048Command; + + // AS5048 diagnostics + typedef enum { + AS_DIAG_CORDIC_OVERFLOW = 0x0200, + AS_DIAG_HIGH_MAGNETIC = 0x0400, + AS_DIAG_LOW_MAGNETIC = 0x0800, + } As5048Diagnostics; + + /** + * Creates an object of num_sensors daisy chained AS5048 sensors; + * default number of sensors in chain is 1 + * @param mosi: pinname of the mosi pin of the spi communication + * @param miso: pinname of the miso pin of the spi communication + * @param sck: pinname of the clock pin of the spi communication + * @param cs: pinname of the chip select pin of the spi communication + * @param num_sensors = 1: number of sensors in daisy chain + */ + As5048(PinName mosi, PinName miso, PinName sck, PinName cs, int num_sensors = 1): + kNumSensors_(num_sensors), + chip_(cs), + spi_(mosi, miso, sck) + { + DeselectChip(); + + spi_.format(kSpiBitsPerTransfer, kSpiMode); + spi_.frequency(kSpiFrequency); + + read_buffer_ = new uint16_t[kNumSensors_]; + angle_buffer_ = new uint16_t[kNumSensors_]; + angle_offset_ = new uint16_t[kNumSensors_]; + directions_ = new bool[kNumSensors_]; + + for (int i=0; i<kNumSensors_; ++i) { + read_buffer_[i] = 0; + angle_buffer_[i] = 0; + angle_offset_[i] = 0; + directions_[i] = true; + } + + last_command_ = AS_CMD_NOP; + } + + + /** + * Destructor, memory deallocation + */ + ~As5048() + { + delete [] read_buffer_; + delete [] angle_buffer_; + delete [] angle_offset_; + delete [] directions_; + } + + /** + * Parity check + * @param n: integer to check + * @return: true if ok + */ + static bool CheckParity(int n) + { + int parity = n; + for(int i=1; i <= kNumSensorBits+1; ++i) { + n >>= 1; + parity ^= n; + } + return (parity & kParity) == 0; + } + + /** + * Update the buffer with angular measurements + * NOTE 1: + * If the last command sent through Transfer was *not* AS_CMD_ANGLE + * then we need an additional Transfer; this takes more time! + * This should not occur, since Transfer is not *yet* used elsewhere. + * NOTE 2: + * We run a parity check on the results from the transfer. We only + * update the angle_buffer_ with values that pass the parity check. + * Measurement using Timer on K64F for last_command_ == AS_CMD_ANGLE + * shows this function takes 87 or 88 us. + */ + void UpdateAngleBuffer() + { + // ensure that the new results indeed will be angles + if (last_command_ != AS_CMD_ANGLE) { + Transfer(AS_CMD_ANGLE); + } + + // update the read buffer + Transfer(AS_CMD_ANGLE); + + // update the angle buffer with parity checked values + for (int i=0; i<kNumSensors_; ++i) { + if (CheckParity(read_buffer_[i])) { + // only update angles when parity is correct + angle_buffer_[i] = read_buffer_[i]; + } + } + } + + /** + * @return: pointer to read_buffer_ + */ + const uint16_t* get_read_buffer() { return read_buffer_; } + + /** + * @return: pointer to angle_buffer_ + */ + const uint16_t* get_angle_buffer() { return angle_buffer_; } + + /** + * @return: pointer to angle_offet_ + */ + const uint16_t* get_angle_offset() { return angle_offset_; } + + /** + * @return: pointer to directions_ + */ + const bool * get_directions_() { return directions_;} + + /** + * You get the angles from two UpdateAngleBuffer() calls before + * @return: 14 bits absolute position + */ + int getAngle(int i_sensor=0) + { + int ans = ((int) (angle_buffer_[i_sensor] & kMask)) - angle_offset_[i_sensor]; + return directions_[i_sensor]?ans:-ans; + } + + /** + * You get the angles from two UpdateAngleBuffer() calls before + * @return: revolution ratio in [0,1] + */ + float getAngleRatio(int i_sensor=0) { return (float) getAngle(i_sensor) / kCountsPerRev; } + + /** + * You get the angles from two UpdateAngleBuffer() calls before + * @return: angle in degrees + */ + float getAngleDegrees(int i_sensor=0) { return getAngleRatio(i_sensor) * kDegPerRev; } + + /** + * You get the angles from two UpdateAngleBuffer() calls before + * @return: angle in radians + */ + float getAngleRadians(int i_sensor=0) { return getAngleRatio(i_sensor) * kRadPerRev; } + + /** + * Set direction for a sensor + * @param i_sensor: id of sensor for which the offset is to be set + * @param dir: true positive, false negative + * @return: true if i_sensor in [0,kNumSensor_) + */ + bool setDirection(int i_sensor, bool dir) + { + if (i_sensor>-1 and i_sensor<kNumSensors_) { + directions_[i_sensor] = dir; + return true; + } + return false; + } + + /** + * Set direction for the first sensor + * @param dir: true positive, false negative + * @return: true if i_sensor in [0,kNumSensor_) + */ + bool setDirection(bool dir) + { + return setDirection(0,dir); + } + + + /** + * Set offset for a sensor + * @param i_sensor: id of sensor for which the offset is to be set + * @param offset: offset in counts [0,2**14-1] + * @return: true if i_sensor in [0,kNumSensor_) + */ + bool setOffset(int i_sensor, uint16_t offset) + { + if (i_sensor>-1 and i_sensor<kNumSensors_) { + angle_offset_[i_sensor] = offset; + return true; + } + return false; + } + + /** + * Set offset for the first sensor + * @param offset: offset in counts [0,2**14-1] + * @return: true if i_sensor in [0,kNumSensor_) + */ + bool setOffset(uint16_t offset) { return setOffset(0,offset); } + + /** + * Set offset for a sensor + * @param i_sensor: id of sensor for which the offset is to be set + * @param offset_ratio: offset in ratio in [0,1] + * @return: true if i_sensor in [0,kNumSensor_) + */ + bool setOffsetRatio (int i_sensor, float offset_ratio) + { + return setOffset(i_sensor,offset_ratio*kCountsPerRev); + } + + /** + * Set offset for the first sensor + * @param offset_ratio: offset in ratio in [0,1] + * @return: true if i_sensor in [0,kNumSensor_) + */ + bool setOffsetRatio(float offset_ratio) + { + return setOffsetRatio(0,offset_ratio); + } + + /** + * Set offset for a sensor + * @param i_sensor: id of sensor for which the offset is to be set + * @param offset_degrees: offset in degrees in [0,360] + * @return: true if i_sensor in [0,kNumSensor_) + */ + bool setOffsetDegrees(int i_sensor, float offset_degrees) + { + return setOffsetRatio(i_sensor,offset_degrees / kDegPerRev); + } + + /** + * Set offset for the first sensor + * @param offset_degrees: offset in degrees in [0,360] + * @return: true if i_sensor in [0,kNumSensor_) + */ + bool setOffsetDegrees(float offset_degrees) + { + return setOffsetDegrees(0, offset_degrees); + } + + /** + * Set offset for a sensor + * @param i_sensor: id of sensor for which the offset is to be set + * @param offset_radians: offset in radians in [0,2*pi] + * @return: true if i_sensor in [0,kNumSensor_) + */ + bool setOffsetRadians(int i_sensor, float offset_radians) + { + return setOffsetRatio(i_sensor, offset_radians / kRadPerRev); + } + + /** + * Set offset for the first sensor + * @param offset_radians: offset in radians in [0,2*pi] + * @return: true if i_sensor in [0,kNumSensor_) + */ + bool setOffsetRadians(float offset_radians) + { + return setOffsetRadians(0, offset_radians); + } + + + + +protected: + + + + /** + * Select (low) chip, and wait 1 us (at least 350 ns) + */ + void SelectChip() { chip_.write(0); wait_us(1); } + + /** + * Deselect (high) chip, and wait 1 us (at least 350 ns) + */ + void DeselectChip() { chip_.write(1); wait_us(1); } + + /** + * SPI transfer between each of the daisy chained sensors + * @param cmd: Command to send + */ + void Transfer(As5048Command cmd) + { + SelectChip(); + for(int i=0; i<kNumSensors_; ++i){ + read_buffer_[i] = spi_.write(cmd>>8) << 8; + read_buffer_[i] |= spi_.write(cmd & 0x00FF); + } + DeselectChip(); + last_command_ = cmd; + } + + const int kNumSensors_; // number of sensors in daisy chain + DigitalOut chip_; // chip select port + SPI spi_; // mbed spi communiation object + + uint16_t* read_buffer_; // buffer for results from last transfer + uint16_t* angle_buffer_; // buffer for angle results from last transfer + uint16_t* angle_offset_; // offset array for each sensor + bool* directions_; // direction true positive, false negative + + As5048Command last_command_;// command sent during last Transfer + +}; +#endif \ No newline at end of file