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

Committer:
megrootens
Date:
Wed May 11 20:30:40 2016 +0000
Revision:
0:723d48642d5c
Child:
1:94b48453d13a
Library for SPI communication with the AMS AS5048 rotary sensor;

Who changed what in which revision?

UserRevisionLine numberNew contents of line
megrootens 0:723d48642d5c 1 #ifndef _AS5048_H_
megrootens 0:723d48642d5c 2 #define _AS5048_H_
megrootens 0:723d48642d5c 3
megrootens 0:723d48642d5c 4 #include "mbed.h"
megrootens 0:723d48642d5c 5
megrootens 0:723d48642d5c 6 const int kNumSensorBits = 14; // 14-bits sensor
megrootens 0:723d48642d5c 7 const uint16_t kCountsPerRev = 0x4000; // 2**NUM_SENSOR_BITS
megrootens 0:723d48642d5c 8 const uint16_t kMask = 0x3FFF; // 2**NUM_SENSOR_BITS - 1
megrootens 0:723d48642d5c 9 const int kParity = 1; // even parity
megrootens 0:723d48642d5c 10
megrootens 0:723d48642d5c 11 const int kSpiFrequency = 1000000; // AS5048 max 10 MHz
megrootens 0:723d48642d5c 12 const int kSpiBitsPerTransfer = 8;
megrootens 0:723d48642d5c 13 const int kSpiMode = 1;
megrootens 0:723d48642d5c 14
megrootens 0:723d48642d5c 15 const float kDegPerRev = 360.0f; // 360 degrees/rev
megrootens 0:723d48642d5c 16 const float kRadPerRev = 6.28318530718f; // 2*pi rad/rev
megrootens 0:723d48642d5c 17
megrootens 0:723d48642d5c 18 // AS5048 flags
megrootens 0:723d48642d5c 19 typedef enum {
megrootens 0:723d48642d5c 20 AS_FLAG_PARITY = 0x8000,
megrootens 0:723d48642d5c 21 AS_FLAG_READ = 0x4000,
megrootens 0:723d48642d5c 22 } As5048Flag;
megrootens 0:723d48642d5c 23
megrootens 0:723d48642d5c 24 // AS5048 commands
megrootens 0:723d48642d5c 25 typedef enum {
megrootens 0:723d48642d5c 26 AS_CMD_NOP = 0x0000,
megrootens 0:723d48642d5c 27 AS_CMD_ERROR = 0x0001 | AS_FLAG_READ, // Reads error register of sensor and clear error flags
megrootens 0:723d48642d5c 28 AS_CMD_DIAGNOSTICS = 0x3FFD | AS_FLAG_READ, // Reads automatic gain control and diagnostics info
megrootens 0:723d48642d5c 29 AS_CMD_MAGNITUDE = 0x3FFE | AS_FLAG_READ,
megrootens 0:723d48642d5c 30 AS_CMD_ANGLE = 0x3FFF | AS_FLAG_PARITY | AS_FLAG_READ,
megrootens 0:723d48642d5c 31 } As5048Command;
megrootens 0:723d48642d5c 32
megrootens 0:723d48642d5c 33 // AS5048 diagnostics
megrootens 0:723d48642d5c 34 typedef enum {
megrootens 0:723d48642d5c 35 AS_DIAG_CORDIC_OVERFLOW = 0x0200,
megrootens 0:723d48642d5c 36 AS_DIAG_HIGH_MAGNETIC = 0x0400,
megrootens 0:723d48642d5c 37 AS_DIAG_LOW_MAGNETIC = 0x0800,
megrootens 0:723d48642d5c 38 } As5048Diagnostics;
megrootens 0:723d48642d5c 39
megrootens 0:723d48642d5c 40
megrootens 0:723d48642d5c 41
megrootens 0:723d48642d5c 42
megrootens 0:723d48642d5c 43 /**
megrootens 0:723d48642d5c 44 * Interfacing with the AMS AS5048A magnetic rotary sensor using SPI protocol
megrootens 0:723d48642d5c 45 * AS5048 uses 16-bit transfer;
megrootens 0:723d48642d5c 46 * We use two 8-bit transfers for compatibility with 8-bit SPI master devices
megrootens 0:723d48642d5c 47 * SPI protocol:
megrootens 0:723d48642d5c 48 * Mode = 1:
megrootens 0:723d48642d5c 49 * clock polarity = 0 --> clock pulse is high
megrootens 0:723d48642d5c 50 * clock phase = 1 --> sample on falling edge of clock pulse
megrootens 0:723d48642d5c 51 * Code was succesfully tested on the FRDM KL25Z and K22F. The same code fails
megrootens 0:723d48642d5c 52 * on the K64F for some reason. Sampling using a logic analyzer does however
megrootens 0:723d48642d5c 53 * show the same results for al three boards.
megrootens 0:723d48642d5c 54 */
megrootens 0:723d48642d5c 55 class As5048 {
megrootens 0:723d48642d5c 56
megrootens 0:723d48642d5c 57 public:
megrootens 0:723d48642d5c 58
megrootens 0:723d48642d5c 59 /**
megrootens 0:723d48642d5c 60 * Creates an object of num_sensors daisy chained AS5048 sensors;
megrootens 0:723d48642d5c 61 * default number of sensors in chain is 1
megrootens 0:723d48642d5c 62 * @param mosi: pinname of the mosi pin of the spi communication
megrootens 0:723d48642d5c 63 * @param miso: pinname of the miso pin of the spi communication
megrootens 0:723d48642d5c 64 * @param sck: pinname of the clock pin of the spi communication
megrootens 0:723d48642d5c 65 * @param cs: pinname of the chip select pin of the spi communication
megrootens 0:723d48642d5c 66 * @param num_sensors = 1: number of sensors in daisy chain
megrootens 0:723d48642d5c 67 */
megrootens 0:723d48642d5c 68 As5048(PinName mosi, PinName miso, PinName sck, PinName cs, int num_sensors = 1):
megrootens 0:723d48642d5c 69 kNumSensors_(num_sensors),
megrootens 0:723d48642d5c 70 chip_(cs),
megrootens 0:723d48642d5c 71 spi_(mosi, miso, sck)
megrootens 0:723d48642d5c 72 {
megrootens 0:723d48642d5c 73 DeselectChip();
megrootens 0:723d48642d5c 74
megrootens 0:723d48642d5c 75 spi_.format(kSpiBitsPerTransfer, kSpiMode);
megrootens 0:723d48642d5c 76 spi_.frequency(kSpiFrequency);
megrootens 0:723d48642d5c 77
megrootens 0:723d48642d5c 78 read_buffer_ = new uint16_t[kNumSensors_];
megrootens 0:723d48642d5c 79 angle_buffer_ = new uint16_t[kNumSensors_];
megrootens 0:723d48642d5c 80 angle_offset_ = new uint16_t[kNumSensors_];
megrootens 0:723d48642d5c 81
megrootens 0:723d48642d5c 82 for (int i=0; i<kNumSensors_; ++i) {
megrootens 0:723d48642d5c 83 read_buffer_[i] = 0;
megrootens 0:723d48642d5c 84 angle_buffer_[i] = 0;
megrootens 0:723d48642d5c 85 angle_offset_[i] = 0;
megrootens 0:723d48642d5c 86 }
megrootens 0:723d48642d5c 87
megrootens 0:723d48642d5c 88 last_command_ = AS_CMD_NOP;
megrootens 0:723d48642d5c 89 }
megrootens 0:723d48642d5c 90
megrootens 0:723d48642d5c 91
megrootens 0:723d48642d5c 92 /**
megrootens 0:723d48642d5c 93 * Destructor, memory deallocation
megrootens 0:723d48642d5c 94 */
megrootens 0:723d48642d5c 95 ~As5048() {
megrootens 0:723d48642d5c 96 delete [] read_buffer_;
megrootens 0:723d48642d5c 97 delete [] angle_buffer_;
megrootens 0:723d48642d5c 98 delete [] angle_offset_;
megrootens 0:723d48642d5c 99 }
megrootens 0:723d48642d5c 100
megrootens 0:723d48642d5c 101 /**
megrootens 0:723d48642d5c 102 * Parity check
megrootens 0:723d48642d5c 103 * @param n: integer to check
megrootens 0:723d48642d5c 104 * @return: true if ok
megrootens 0:723d48642d5c 105 */
megrootens 0:723d48642d5c 106 static bool CheckParity(int n) {
megrootens 0:723d48642d5c 107 int parity = n;
megrootens 0:723d48642d5c 108 for(int i=1; i <= kNumSensorBits+1; ++i) {
megrootens 0:723d48642d5c 109 n >>= 1;
megrootens 0:723d48642d5c 110 parity ^= n;
megrootens 0:723d48642d5c 111 }
megrootens 0:723d48642d5c 112 return (parity & kParity) == 0;
megrootens 0:723d48642d5c 113 }
megrootens 0:723d48642d5c 114
megrootens 0:723d48642d5c 115 /**
megrootens 0:723d48642d5c 116 * Update the buffer with angular measurements
megrootens 0:723d48642d5c 117 * NOTE 1:
megrootens 0:723d48642d5c 118 * If the last command sent through Transfer was *not* AS_CMD_ANGLE
megrootens 0:723d48642d5c 119 * then we need an additional Transfer; this takes more time!
megrootens 0:723d48642d5c 120 * This should not occur, since Transfer is not *yet* used elsewhere.
megrootens 0:723d48642d5c 121 * NOTE 2:
megrootens 0:723d48642d5c 122 * We run a parity check on the results from the transfer. We only
megrootens 0:723d48642d5c 123 * update the angle_buffer_ with values that pass the parity check.
megrootens 0:723d48642d5c 124 * Measurement using Timer on K64F for last_command_ == AS_CMD_ANGLE
megrootens 0:723d48642d5c 125 * shows this function takes 87 or 88 us.
megrootens 0:723d48642d5c 126 */
megrootens 0:723d48642d5c 127 void UpdateAngleBuffer() {
megrootens 0:723d48642d5c 128 // ensure that the new results indeed will be angles
megrootens 0:723d48642d5c 129 if (last_command_ != AS_CMD_ANGLE) {
megrootens 0:723d48642d5c 130 Transfer(AS_CMD_ANGLE);
megrootens 0:723d48642d5c 131 }
megrootens 0:723d48642d5c 132
megrootens 0:723d48642d5c 133 // update the read buffer
megrootens 0:723d48642d5c 134 Transfer(AS_CMD_ANGLE);
megrootens 0:723d48642d5c 135
megrootens 0:723d48642d5c 136 // update the angle buffer with parity checked values
megrootens 0:723d48642d5c 137 for (int i=0; i<kNumSensors_; ++i) {
megrootens 0:723d48642d5c 138 if (CheckParity(read_buffer_[i])) {
megrootens 0:723d48642d5c 139 // only update angles when parity is correct
megrootens 0:723d48642d5c 140 angle_buffer_[i] = read_buffer_[i];
megrootens 0:723d48642d5c 141 }
megrootens 0:723d48642d5c 142 }
megrootens 0:723d48642d5c 143 }
megrootens 0:723d48642d5c 144
megrootens 0:723d48642d5c 145 /**
megrootens 0:723d48642d5c 146 * @return: pointer to read_buffer_
megrootens 0:723d48642d5c 147 */
megrootens 0:723d48642d5c 148 const uint16_t* get_read_buffer() { return read_buffer_; }
megrootens 0:723d48642d5c 149
megrootens 0:723d48642d5c 150 /**
megrootens 0:723d48642d5c 151 * @return: pointer to angle_buffer_
megrootens 0:723d48642d5c 152 */
megrootens 0:723d48642d5c 153 const uint16_t* get_angle_buffer() { return angle_buffer_; }
megrootens 0:723d48642d5c 154
megrootens 0:723d48642d5c 155 /**
megrootens 0:723d48642d5c 156 * @return: pointer to angle_offet_
megrootens 0:723d48642d5c 157 */
megrootens 0:723d48642d5c 158 const uint16_t* get_angle_offset() { return angle_offset_; }
megrootens 0:723d48642d5c 159
megrootens 0:723d48642d5c 160 /**
megrootens 0:723d48642d5c 161 * You get the angles from two UpdateAngleBuffer() calls before
megrootens 0:723d48642d5c 162 * @return: 14 bits absolute position
megrootens 0:723d48642d5c 163 */
megrootens 0:723d48642d5c 164 int getAngle(int i_sensor=0){
megrootens 0:723d48642d5c 165 return ((int) (angle_buffer_[i_sensor] & kMask)) - angle_offset_[i_sensor];
megrootens 0:723d48642d5c 166 }
megrootens 0:723d48642d5c 167
megrootens 0:723d48642d5c 168 /**
megrootens 0:723d48642d5c 169 * You get the angles from two UpdateAngleBuffer() calls before
megrootens 0:723d48642d5c 170 * @return: revolution ratio in [0,1]
megrootens 0:723d48642d5c 171 */
megrootens 0:723d48642d5c 172 float getAngleRatio(int i_sensor=0) { return (float) getAngle(i_sensor) / kCountsPerRev; }
megrootens 0:723d48642d5c 173
megrootens 0:723d48642d5c 174 /**
megrootens 0:723d48642d5c 175 * You get the angles from two UpdateAngleBuffer() calls before
megrootens 0:723d48642d5c 176 * @return: angle in degrees
megrootens 0:723d48642d5c 177 */
megrootens 0:723d48642d5c 178 float getAngleDegrees(int i_sensor=0) { return getAngleRatio(i_sensor) * kDegPerRev; }
megrootens 0:723d48642d5c 179
megrootens 0:723d48642d5c 180 /**
megrootens 0:723d48642d5c 181 * You get the angles from two UpdateAngleBuffer() calls before
megrootens 0:723d48642d5c 182 * @return: angle in radians
megrootens 0:723d48642d5c 183 */
megrootens 0:723d48642d5c 184 float getAngleRadians(int i_sensor=0) { return getAngleRatio(i_sensor) * kRadPerRev; }
megrootens 0:723d48642d5c 185
megrootens 0:723d48642d5c 186 /**
megrootens 0:723d48642d5c 187 * Set offset for a sensor
megrootens 0:723d48642d5c 188 * @param i_sensor: id of sensor for which the offset is to be set
megrootens 0:723d48642d5c 189 * @param offset: offset in counts [0,2**14-1]
megrootens 0:723d48642d5c 190 * @return: true if i_sensor in [0,kNumSensor_)
megrootens 0:723d48642d5c 191 */
megrootens 0:723d48642d5c 192 bool setOffset(int i_sensor, uint16_t offset) {
megrootens 0:723d48642d5c 193 if (i_sensor>-1 and i_sensor<kNumSensors_) {
megrootens 0:723d48642d5c 194 angle_offset_[i_sensor] = offset;
megrootens 0:723d48642d5c 195 return true;
megrootens 0:723d48642d5c 196 }
megrootens 0:723d48642d5c 197 return false;
megrootens 0:723d48642d5c 198 }
megrootens 0:723d48642d5c 199
megrootens 0:723d48642d5c 200 /**
megrootens 0:723d48642d5c 201 * Set offset for the first sensor
megrootens 0:723d48642d5c 202 * @param offset: offset in counts [0,2**14-1]
megrootens 0:723d48642d5c 203 * @return: true if i_sensor in [0,kNumSensor_)
megrootens 0:723d48642d5c 204 */
megrootens 0:723d48642d5c 205 bool setOffset(uint16_t offset) { return setOffset(0,offset); }
megrootens 0:723d48642d5c 206
megrootens 0:723d48642d5c 207 /**
megrootens 0:723d48642d5c 208 * Set offset for a sensor
megrootens 0:723d48642d5c 209 * @param i_sensor: id of sensor for which the offset is to be set
megrootens 0:723d48642d5c 210 * @param offset_ratio: offset in ratio in [0,1]
megrootens 0:723d48642d5c 211 * @return: true if i_sensor in [0,kNumSensor_)
megrootens 0:723d48642d5c 212 */
megrootens 0:723d48642d5c 213 bool setOffsetRatio (int i_sensor, float offset_ratio) {
megrootens 0:723d48642d5c 214 return setOffset(i_sensor,offset_ratio*kCountsPerRev);
megrootens 0:723d48642d5c 215 }
megrootens 0:723d48642d5c 216
megrootens 0:723d48642d5c 217 /**
megrootens 0:723d48642d5c 218 * Set offset for the first sensor
megrootens 0:723d48642d5c 219 * @param offset_ratio: offset in ratio in [0,1]
megrootens 0:723d48642d5c 220 * @return: true if i_sensor in [0,kNumSensor_)
megrootens 0:723d48642d5c 221 */
megrootens 0:723d48642d5c 222 bool setOffsetRatio(float offset_ratio) {
megrootens 0:723d48642d5c 223 return setOffsetRatio(0,offset_ratio);
megrootens 0:723d48642d5c 224 }
megrootens 0:723d48642d5c 225
megrootens 0:723d48642d5c 226 /**
megrootens 0:723d48642d5c 227 * Set offset for a sensor
megrootens 0:723d48642d5c 228 * @param i_sensor: id of sensor for which the offset is to be set
megrootens 0:723d48642d5c 229 * @param offset_degrees: offset in degrees in [0,360]
megrootens 0:723d48642d5c 230 * @return: true if i_sensor in [0,kNumSensor_)
megrootens 0:723d48642d5c 231 */
megrootens 0:723d48642d5c 232 bool setOffsetDegrees(int i_sensor, float offset_degrees) {
megrootens 0:723d48642d5c 233 return setOffsetRatio(i_sensor,offset_degrees / kDegPerRev);
megrootens 0:723d48642d5c 234 }
megrootens 0:723d48642d5c 235
megrootens 0:723d48642d5c 236 /**
megrootens 0:723d48642d5c 237 * Set offset for the first sensor
megrootens 0:723d48642d5c 238 * @param offset_degrees: offset in degrees in [0,360]
megrootens 0:723d48642d5c 239 * @return: true if i_sensor in [0,kNumSensor_)
megrootens 0:723d48642d5c 240 */
megrootens 0:723d48642d5c 241 bool setOffsetDegrees(float offset_degrees) {
megrootens 0:723d48642d5c 242 return setOffsetDegrees(0, offset_degrees);
megrootens 0:723d48642d5c 243 }
megrootens 0:723d48642d5c 244
megrootens 0:723d48642d5c 245 /**
megrootens 0:723d48642d5c 246 * Set offset for a sensor
megrootens 0:723d48642d5c 247 * @param i_sensor: id of sensor for which the offset is to be set
megrootens 0:723d48642d5c 248 * @param offset_radians: offset in radians in [0,2*pi]
megrootens 0:723d48642d5c 249 * @return: true if i_sensor in [0,kNumSensor_)
megrootens 0:723d48642d5c 250 */
megrootens 0:723d48642d5c 251 bool setOffsetRadians(int i_sensor, float offset_radians) {
megrootens 0:723d48642d5c 252 return setOffsetRatio(i_sensor, offset_radians / kRadPerRev);
megrootens 0:723d48642d5c 253 }
megrootens 0:723d48642d5c 254
megrootens 0:723d48642d5c 255 /**
megrootens 0:723d48642d5c 256 * Set offset for the first sensor
megrootens 0:723d48642d5c 257 * @param offset_radians: offset in radians in [0,2*pi]
megrootens 0:723d48642d5c 258 * @return: true if i_sensor in [0,kNumSensor_)
megrootens 0:723d48642d5c 259 */
megrootens 0:723d48642d5c 260 bool setOffsetRadians(float offset_radians) {
megrootens 0:723d48642d5c 261 return setOffsetRadians(0, offset_radians);
megrootens 0:723d48642d5c 262 }
megrootens 0:723d48642d5c 263
megrootens 0:723d48642d5c 264
megrootens 0:723d48642d5c 265 protected:
megrootens 0:723d48642d5c 266
megrootens 0:723d48642d5c 267 /**
megrootens 0:723d48642d5c 268 * Select (low) chip, and wait 1 us (at least 350 ns)
megrootens 0:723d48642d5c 269 */
megrootens 0:723d48642d5c 270 void SelectChip() { chip_.write(0); wait_us(1); }
megrootens 0:723d48642d5c 271
megrootens 0:723d48642d5c 272 /**
megrootens 0:723d48642d5c 273 * Deselect (high) chip, and wait 1 us (at least 350 ns)
megrootens 0:723d48642d5c 274 */
megrootens 0:723d48642d5c 275 void DeselectChip() { chip_.write(1); wait_us(1); }
megrootens 0:723d48642d5c 276
megrootens 0:723d48642d5c 277 /**
megrootens 0:723d48642d5c 278 * SPI transfer between each of the daisy chained sensors
megrootens 0:723d48642d5c 279 * @param cmd: Command to send
megrootens 0:723d48642d5c 280 */
megrootens 0:723d48642d5c 281 void Transfer(As5048Command cmd) {
megrootens 0:723d48642d5c 282 SelectChip();
megrootens 0:723d48642d5c 283 for(int i=0; i<kNumSensors_; ++i){
megrootens 0:723d48642d5c 284 read_buffer_[i] = spi_.write(cmd>>8) << 8;
megrootens 0:723d48642d5c 285 read_buffer_[i] |= spi_.write(cmd & 0x00FF);
megrootens 0:723d48642d5c 286 }
megrootens 0:723d48642d5c 287 DeselectChip();
megrootens 0:723d48642d5c 288 last_command_ = cmd;
megrootens 0:723d48642d5c 289 }
megrootens 0:723d48642d5c 290
megrootens 0:723d48642d5c 291 const int kNumSensors_; // number of sensors in daisy chain
megrootens 0:723d48642d5c 292 DigitalOut chip_; // chip select port
megrootens 0:723d48642d5c 293 SPI spi_; // mbed spi communiation object
megrootens 0:723d48642d5c 294
megrootens 0:723d48642d5c 295 uint16_t* read_buffer_; // buffer for results from last transfer
megrootens 0:723d48642d5c 296 uint16_t* angle_buffer_; // buffer for angle results from last transfer
megrootens 0:723d48642d5c 297 uint16_t* angle_offset_; // offset array for each sensor
megrootens 0:723d48642d5c 298
megrootens 0:723d48642d5c 299 As5048Command last_command_;// command sent during last Transfer
megrootens 0:723d48642d5c 300
megrootens 0:723d48642d5c 301 };
megrootens 0:723d48642d5c 302 #endif