/********************************************************************** Copyright (C) 2018 MisfitTech LLC, All rights reserved. MisfitTech uses a dual license model that allows the software to be used under a standard GPL open source license, or a commercial license. The standard GPL license requires that all software statically linked with MisfitTec Code is also distributed under the same GPL V2 license terms. Details of both license options follow: - Open source licensing - MisfitTech is a free download and may be used, modified, evaluated and distributed without charge provided the user adheres to version two of the GNU General Public License (GPL) and does not remove the copyright notice or this text. The GPL V2 text is available on the gnu.org web site - Commercial licensing - Businesses and individuals that for commercial or other reasons cannot comply with the terms of the GPL V2 license must obtain a low cost commercial license before incorporating MisfitTech code into proprietary software for distribution in any form. Commercial licenses can be purchased from www.misfittech.net and do not require any source files to be changed. This code is distributed in the hope that it will be useful. You cannot use MisfitTech's code unless you agree that you use the software 'as is'. MisfitTech's code is provided WITHOUT ANY WARRANTY; without even the implied warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. MisfitTech LLC disclaims all conditions and terms, be they implied, expressed, or statutory. Written by Trampas Stern for MisfitTech. Misfit Tech invests time and resources providing this open source code, please support MisfitTech and open-source hardware by purchasing products from MisfitTech, www.misifittech.net! *********************************************************************/ #include #include "syslog.h" #include "as5047d.h" #include "SPI.h" #include #include "board.h" #define AS5047D_CMD_NOP (0x0000) #define AS5047D_CMD_ERRFL (0x0001) #define AS5047D_CMD_PROG (0x0003) #define AS5047D_CMD_DIAAGC (0x3FFC) #define AS5047D_CMD_MAG (0x3FFD) #define AS5047D_CMD_ANGLEUNC (0x3FFE) #define AS5047D_CMD_ANGLECOM (0x3FFF) #define AS5048A_CMD_NOP (0x0000) #define AS5048A_CMD_ERRFL (0x0001) #define AS5048A_CMD_PROG (0x0003) #define AS5048A_CMD_DIAAGC (0x3FFD) #define AS5048A_CMD_MAG (0x3FFE) #define AS5048A_CMD_ANGLE (0x3FFF) #pragma GCC push_options #pragma GCC optimize ("-Ofast") static int getBit(int16_t data, int bit) { return (data>>bit) & 0x01; } static int getParity(uint16_t data) { int i,bits; data=data & 0x7FFF; //mask out upper bit //count number of bits, brute force bits=0; for(i=0; i<16; i++) { if (0 != (data & ((0x0001)<0) { delay(1); t0--; if (t0==0) { ERROR("LF bit not set"); error=true; break; //return false; } LOG("AS5047D diag data is 0x%04X",data); data=readAddress(AS5047D_CMD_DIAAGC); } if (error) { error=false; uint16_t data=0,t0=100; while (getBit(data,8)==0 && t0>0) { delay(1); t0--; if (t0==0) { ERROR("AS5048A OCF bit not set"); error=true; return false; } data=readAddress(AS5048A_CMD_DIAAGC); LOG("AS5048A diag data is 0x%04X",data); } as5047d=false; } #ifdef NZS_AS5047_PIPELINE //read encoder a few times to flush the pipeline readEncoderAnglePipeLineRead(); readEncoderAnglePipeLineRead(); #endif return true; } //read the encoders int16_t AS5047D::readAddress(uint16_t addr) { uint16_t data; error=false; //make sure it is a read by setting bit 14 addr=addr | 0x4000; //add the parity to the command if (1 == getParity(addr)) { addr=(addr & 0x7FFF) | 0x8000; //add parity bit to make command even number of bits } digitalWrite(chipSelectPin, LOW); delayMicroseconds(1); //clock out the address to read SPI.transfer16(addr); digitalWrite(chipSelectPin, HIGH); delayMicroseconds(1); digitalWrite(chipSelectPin, LOW); //clock out zeros to read in the data from address data=SPI.transfer16(0x00); digitalWrite(chipSelectPin, HIGH); if (data & (1<<14)) { //if bit 14 is set then we have an error ERROR("read command 0x%04X failed",addr); error=true; return -1; } if (data>>15 != getParity(data)) { //parity did not match ERROR("read command parity error 0x%04X ",addr); error=true; return -2; } data=data & 0x3FFF; //mask off the error and parity bits return data; } //read the encoders int16_t AS5047D::readEncoderAngle(void) { if (as5047d) { return readAddress(AS5047D_CMD_ANGLECOM); } return readAddress(AS5048A_CMD_ANGLE); } //pipelined read of the encoder angle used for high speed reads, but value is always one read behind int16_t AS5047D::readEncoderAnglePipeLineRead(void) { int16_t data; int error, t0=10; GPIO_LOW(chipSelectPin);//(chipSelectPin, LOW); //delayMicroseconds(1); do { // doing two 8 bit transfers is faster than one 16 bit data =(uint16_t)SPI.transfer(0xFF)<<8 | ((uint16_t)SPI.transfer(0xFF) & 0x0FF); t0--; if (t0<=0) { ERROR("AS5047D problem"); break; } //data=SPI.transfer16(0xFFFF); //to speed things up we know the parity and address for the read }while(data & (1<<14)); //while error bit is set data=data & 0x3FFF; //mask off the error and parity bits GPIO_HIGH(chipSelectPin); //digitalWrite(chipSelectPin, HIGH); //TODO we really should check for errors and return a negative result or something return data; } void AS5047D::diagnostics(char *ptrStr) { int16_t data; int m,d; if (as5047d) { data=readAddress(AS5047D_CMD_DIAAGC); if (NULL == ptrStr) { LOG("DIAAGC: 0x%04X", data); LOG("MAGL: %d", getBit(data,11)); LOG("MAGH: %d", getBit(data,10)); LOG("COF: %d", getBit(data,9)); LOG("LFGL: %d", getBit(data,8)); LOG("AGC: %d", data & 0x0FF); data=readAddress(AS5047D_CMD_MAG); LOG("CMAG: 0x%04X(%d)",data,data); data=readAddress(AS5047D_CMD_ANGLEUNC); m=(int)((float)data*AS5047D_DEGREES_PER_BIT); d=(int)((float)data*AS5047D_DEGREES_PER_BIT*100 -m*100); LOG("CORDICANG: 0x%04X(%d) %d.%02d deg(est)",data,data,m,d); data=readAddress(AS5047D_CMD_ANGLECOM); m=(int)((float)data*AS5047D_DEGREES_PER_BIT); d=(int)((float)data*AS5047D_DEGREES_PER_BIT*100 -m*100); LOG("DAECANG: 0x%04X(%d) %d.%02d deg(est)",data,data,m,d); }else { sprintf(ptrStr,"DIAAGC: 0x%04X\n\r", data); sprintf(ptrStr,"%sMAGL: %d\n\r", ptrStr,getBit(data,11)); sprintf(ptrStr,"%sMAGH: %d\n\r", ptrStr,getBit(data,10)); sprintf(ptrStr,"%sCOF: %d\n\r", ptrStr, getBit(data,9)); sprintf(ptrStr,"%sLFGL: %d\n\r", ptrStr, getBit(data,8)); sprintf(ptrStr,"%sAGC: %d\n\r", ptrStr,data & 0x0FF); data=readAddress(AS5047D_CMD_MAG); sprintf(ptrStr,"%sCMAG: 0x%04X(%d)\n\r", ptrStr,data,data); data=readAddress(AS5047D_CMD_ANGLEUNC); m=(int)((float)data*AS5047D_DEGREES_PER_BIT); d=(int)((float)data*AS5047D_DEGREES_PER_BIT*100 -m*100); sprintf(ptrStr,"%sCORDICANG: 0x%04X(%d) %d.%02d deg(est)\n\r", ptrStr,data,data,m,d); data=readAddress(AS5047D_CMD_ANGLECOM); m=(int)((float)data*AS5047D_DEGREES_PER_BIT); d=(int)((float)data*AS5047D_DEGREES_PER_BIT*100 -m*100); sprintf(ptrStr,"%sDAECANG: 0x%04X(%d) %d.%02d deg(est)\n\r", ptrStr,data,data,m,d); } } else { data=readAddress(AS5048A_CMD_DIAAGC); sprintf(ptrStr,"AS5048A DIAAGC: 0x%04X\n\r", data); data=readAddress(AS5048A_CMD_MAG); sprintf(ptrStr,"%sMagnitude: %d\n\r", ptrStr,data); data=readAddress(AS5048A_CMD_ANGLE); sprintf(ptrStr,"%sAngle: %d\n\r", ptrStr,data); } } #pragma GCC pop_options