Martijn Grootens / AS5048

Dependents:   heros_leg_readout_torque_addition heros_leg_readout_torque_addition heros_leg_readout_torque_addition_V3

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers as5048.h Source File

as5048.h

00001 #ifndef _AS5048_H_
00002 #define _AS5048_H_
00003 
00004 #include "mbed.h"
00005 /**
00006  * Interfacing with the AMS AS5048A magnetic rotary sensor using SPI protocol
00007  * AS5048 uses 16-bit transfer;
00008  * We use two 8-bit transfers for compatibility with 8-bit SPI master devices
00009  * SPI protocol:
00010  *   Mode = 1: 
00011  *   clock polarity = 0 --> clock pulse is high
00012  *   clock phase = 1 --> sample on falling edge of clock pulse
00013  * Code was succesfully tested on the FRDM KL25Z and K22F. The same code fails
00014  * on the K64F for some reason. Sampling using a logic analyzer does however
00015  * show the same results for al three boards.
00016  */
00017 class As5048 {
00018 
00019 
00020 public:
00021 
00022     static const int kNumSensorBits        = 14;      // 14-bits sensor
00023     static const uint16_t kCountsPerRev    = 0x4000;  // 2**NUM_SENSOR_BITS
00024     static const uint16_t kMask            = 0x3FFF;  // 2**NUM_SENSOR_BITS - 1
00025     static const int kParity               = 1;       // even parity
00026     
00027     static const int kSpiFrequency         = 1000000; // AS5048 max 10 MHz
00028     static const int kSpiBitsPerTransfer   = 8;
00029     static const int kSpiMode              = 1;
00030     
00031     static const float kDegPerRev          = 360.0f;  // 360 degrees/rev
00032     static const float kRadPerRev          = 6.28318530718f; // 2*pi rad/rev
00033 
00034     // AS5048 flags
00035     typedef enum {
00036         AS_FLAG_PARITY          = 0x8000,
00037         AS_FLAG_READ            = 0x4000,
00038     } As5048Flag;
00039     
00040     // AS5048 commands
00041     typedef enum {
00042         AS_CMD_NOP              = 0x0000,
00043         AS_CMD_ERROR            = 0x0001 | AS_FLAG_READ,   // Reads error register of sensor and clear error flags
00044         AS_CMD_DIAGNOSTICS      = 0x3FFD | AS_FLAG_READ,   // Reads automatic gain control and diagnostics info
00045         AS_CMD_MAGNITUDE        = 0x3FFE | AS_FLAG_READ,
00046         AS_CMD_ANGLE            = 0x3FFF | AS_FLAG_PARITY | AS_FLAG_READ,
00047     } As5048Command;
00048     
00049     // AS5048 diagnostics
00050     typedef enum {
00051         AS_DIAG_CORDIC_OVERFLOW = 0x0200,
00052         AS_DIAG_HIGH_MAGNETIC   = 0x0400,
00053         AS_DIAG_LOW_MAGNETIC    = 0x0800,
00054     } As5048Diagnostics;
00055 
00056     /**
00057      * Creates an object of num_sensors daisy chained AS5048 sensors;
00058      * default number of sensors in chain is 1
00059      * @param mosi: pinname of the mosi pin of the spi communication
00060      * @param miso: pinname of the miso pin of the spi communication
00061      * @param sck: pinname of the clock pin of the spi communication
00062      * @param cs: pinname of the chip select pin of the spi communication
00063      * @param num_sensors = 1: number of sensors in daisy chain
00064      */
00065     As5048(PinName mosi, PinName miso, PinName sck, PinName cs, int num_sensors = 1):
00066         kNumSensors_(num_sensors),
00067         chip_(cs),
00068         spi_(mosi, miso, sck) 
00069     {
00070         DeselectChip();
00071         
00072         spi_.format(kSpiBitsPerTransfer, kSpiMode);
00073         spi_.frequency(kSpiFrequency);
00074         
00075         read_buffer_  = new uint16_t[kNumSensors_];
00076         angle_buffer_ = new uint16_t[kNumSensors_];
00077         angle_offset_ = new uint16_t[kNumSensors_];
00078         directions_ = new bool[kNumSensors_];
00079         
00080         for (int i=0; i<kNumSensors_; ++i) {
00081             read_buffer_[i] = 0;
00082             angle_buffer_[i] = 0;
00083             angle_offset_[i] = 0;
00084             directions_[i] = true;
00085         }
00086         
00087         last_command_ = AS_CMD_NOP;
00088     }
00089 
00090     
00091     /**
00092      * Destructor, memory deallocation
00093      */
00094     ~As5048() 
00095     {
00096         delete [] read_buffer_;
00097         delete [] angle_buffer_;
00098         delete [] angle_offset_;
00099         delete [] directions_;
00100     }
00101         
00102     /**
00103      * Parity check
00104      * @param n: integer to check
00105      * @return: true if ok
00106      */
00107     static bool CheckParity(int n) 
00108     {
00109         int parity = n;
00110         for(int i=1; i <= kNumSensorBits+1; ++i) {
00111             n >>= 1;
00112             parity ^= n;
00113         }
00114         return (parity & kParity) == 0;
00115     }
00116     
00117     /**
00118      * Update the buffer with angular measurements
00119      * NOTE 1:
00120      *  If the last command sent through Transfer was *not* AS_CMD_ANGLE
00121      *  then we need an additional Transfer; this takes more time!
00122      *  This should not occur, since Transfer is not *yet* used elsewhere.
00123      * NOTE 2:
00124      *  We run a parity check on the results from the transfer. We only 
00125      *  update the angle_buffer_ with values that pass the parity check.
00126      * Measurement using Timer on K64F  for last_command_ == AS_CMD_ANGLE
00127      * shows this function takes 87 or 88 us.
00128      */
00129     void UpdateAngleBuffer() 
00130     {
00131         // ensure that the new results indeed will be angles
00132         if (last_command_ != AS_CMD_ANGLE) {
00133             Transfer(AS_CMD_ANGLE);
00134         }
00135         
00136         // update the read buffer
00137         Transfer(AS_CMD_ANGLE); 
00138         
00139         // update the angle buffer with parity checked values
00140         for (int i=0; i<kNumSensors_; ++i) {
00141             if (CheckParity(read_buffer_[i])) {
00142                 // only update angles when parity is correct
00143                 angle_buffer_[i] = read_buffer_[i];
00144             }
00145         }
00146     }
00147     
00148     /**
00149      * @return: pointer to read_buffer_
00150      */
00151     const uint16_t* get_read_buffer ()  { return read_buffer_; }
00152     
00153     /**
00154      * @return: pointer to angle_buffer_
00155      */
00156     const uint16_t* get_angle_buffer () { return angle_buffer_; }
00157     
00158     /**
00159      * @return: pointer to angle_offet_
00160      */
00161     const uint16_t* get_angle_offset () { return angle_offset_; }
00162     
00163     /**
00164      * @return: pointer to directions_
00165      */
00166     const bool * get_directions_ () { return directions_;}
00167     
00168     /**
00169      * You get the angles from two UpdateAngleBuffer() calls before
00170      * @return: 14 bits absolute position
00171      */
00172     int getAngle(int i_sensor=0)
00173     { 
00174         int ans = ((int) (angle_buffer_[i_sensor] & kMask)) - angle_offset_[i_sensor];
00175         return directions_[i_sensor]?ans:-ans;
00176     }
00177     
00178     /**
00179      * You get the angles from two UpdateAngleBuffer() calls before
00180      * @return: revolution ratio in [0,1]
00181      */
00182     float getAngleRatio(int i_sensor=0)      { return (float) getAngle(i_sensor) / kCountsPerRev; }
00183     
00184     /**
00185      * You get the angles from two UpdateAngleBuffer() calls before
00186      * @return: angle in degrees
00187      */
00188     float getAngleDegrees(int i_sensor=0)    { return getAngleRatio(i_sensor) * kDegPerRev; }
00189     
00190     /**
00191      * You get the angles from two UpdateAngleBuffer() calls before
00192      * @return: angle in radians
00193      */
00194     float getAngleRadians(int i_sensor=0)    { return getAngleRatio(i_sensor) * kRadPerRev; }
00195     
00196     /**
00197      * Set direction for a sensor
00198      * @param i_sensor: id of sensor for which the offset is to be set
00199      * @param dir: true positive, false negative
00200      * @return: true if i_sensor in [0,kNumSensor_)
00201      */
00202     bool setDirection(int i_sensor, bool dir) 
00203     {
00204         if (i_sensor>-1 and i_sensor<kNumSensors_) {
00205             directions_[i_sensor] = dir;
00206             return true;
00207         }
00208         return false;
00209     }
00210     
00211     /**
00212      * Set direction for the first sensor
00213      * @param dir: true positive, false negative
00214      * @return: true if i_sensor in [0,kNumSensor_)
00215      */
00216     bool setDirection(bool dir) 
00217     {
00218         return setDirection(0,dir);
00219     }
00220     
00221     
00222     /**
00223      * Set offset for a sensor
00224      * @param i_sensor: id of sensor for which the offset is to be set
00225      * @param offset: offset in counts [0,2**14-1]
00226      * @return: true if i_sensor in [0,kNumSensor_)
00227      */
00228     bool setOffset(int i_sensor, uint16_t offset) 
00229     {
00230         if (i_sensor>-1 and i_sensor<kNumSensors_) {
00231             angle_offset_[i_sensor] = offset;
00232             return true;
00233         }
00234         return false;
00235     }
00236     
00237     /**
00238      * Set offset for the first sensor
00239      * @param offset: offset in counts [0,2**14-1]
00240      * @return: true if i_sensor in [0,kNumSensor_)
00241      */
00242     bool setOffset(uint16_t offset) { return setOffset(0,offset); }
00243     
00244     /**
00245      * Set offset for a sensor
00246      * @param i_sensor: id of sensor for which the offset is to be set
00247      * @param offset_ratio: offset in ratio in [0,1]
00248      * @return: true if i_sensor in [0,kNumSensor_)
00249      */
00250     bool setOffsetRatio (int i_sensor, float offset_ratio) 
00251     {
00252         return setOffset(i_sensor,offset_ratio*kCountsPerRev);
00253     }
00254     
00255     /**
00256      * Set offset for the first sensor
00257      * @param offset_ratio: offset in ratio in [0,1]
00258      * @return: true if i_sensor in [0,kNumSensor_)
00259      */
00260     bool setOffsetRatio(float offset_ratio) 
00261     { 
00262         return setOffsetRatio(0,offset_ratio); 
00263     }
00264     
00265     /**
00266      * Set offset for a sensor
00267      * @param i_sensor: id of sensor for which the offset is to be set
00268      * @param offset_degrees: offset in degrees in [0,360]
00269      * @return: true if i_sensor in [0,kNumSensor_)
00270      */
00271     bool setOffsetDegrees(int i_sensor, float offset_degrees) 
00272     {
00273         return setOffsetRatio(i_sensor,offset_degrees / kDegPerRev);
00274     }
00275     
00276     /**
00277      * Set offset for the first sensor
00278      * @param offset_degrees: offset in degrees in [0,360]
00279      * @return: true if i_sensor in [0,kNumSensor_)
00280      */
00281     bool setOffsetDegrees(float offset_degrees) 
00282     {
00283         return setOffsetDegrees(0, offset_degrees);
00284     }
00285     
00286     /**
00287      * Set offset for a sensor
00288      * @param i_sensor: id of sensor for which the offset is to be set
00289      * @param offset_radians: offset in radians in [0,2*pi]
00290      * @return: true if i_sensor in [0,kNumSensor_)
00291      */
00292     bool setOffsetRadians(int i_sensor, float offset_radians) 
00293     {
00294         return setOffsetRatio(i_sensor, offset_radians / kRadPerRev);
00295     }
00296     
00297     /**
00298      * Set offset for the first sensor
00299      * @param offset_radians: offset in radians in [0,2*pi]
00300      * @return: true if i_sensor in [0,kNumSensor_)
00301      */
00302     bool setOffsetRadians(float offset_radians) 
00303     {
00304         return setOffsetRadians(0, offset_radians);
00305     }
00306     
00307     
00308    
00309 
00310 protected:
00311 
00312 
00313 
00314     /**
00315      * Select (low) chip, and wait 1 us (at least 350 ns)
00316      */
00317     void SelectChip()   { chip_.write(0); wait_us(1); }
00318     
00319     /**
00320      * Deselect (high) chip, and wait 1 us (at least 350 ns)
00321      */
00322     void DeselectChip() { chip_.write(1); wait_us(1); }
00323 
00324     /**
00325      * SPI transfer between each of the daisy chained sensors
00326      * @param cmd: Command to send
00327      */
00328     void Transfer(As5048Command cmd) 
00329     {
00330         SelectChip();
00331         for(int i=0; i<kNumSensors_; ++i){
00332             read_buffer_[i]  = spi_.write(cmd>>8) << 8;
00333             read_buffer_[i] |= spi_.write(cmd & 0x00FF);
00334         }
00335         DeselectChip();
00336         last_command_ = cmd;
00337     }
00338 
00339     const int kNumSensors_;     // number of sensors in daisy chain
00340     DigitalOut chip_;           // chip select port
00341     SPI spi_;                   // mbed spi communiation object
00342     
00343     uint16_t* read_buffer_;     // buffer for results from last transfer
00344     uint16_t* angle_buffer_;    // buffer for angle results from last transfer
00345     uint16_t* angle_offset_;    // offset array for each sensor
00346     bool* directions_;          // direction true positive, false negative
00347     
00348     As5048Command last_command_;// command sent during last Transfer
00349     
00350 };
00351 #endif