Now with licence header and working for output of DROPSAW project
as5048.h
- Committer:
- cnckiwi31
- Date:
- 2020-10-26
- Revision:
- 6:1b84babf3042
- Parent:
- 5:9df31d15f3fa
File content as of revision 6:1b84babf3042:
/* Copyright 2017 Martijn Grootens Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #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