Driver for AK7451 Angle Senor(SPI).
Fork of AK7451 by
ak7451.cpp
- Committer:
- masahikofukasawa
- Date:
- 2017-04-28
- Revision:
- 3:1e0cfed43467
- Parent:
- 1:536cf25b0eb4
File content as of revision 3:1e0cfed43467:
#include "ak7451.h" // OPCODE Specification #define AK7451_OPCODE_WRITE_EEPROM 0x01 // Able to write a data on EEPROM #define AK7451_OPCODE_READ_EEPROM 0x02 // Able to read an EEPROM #define AK7451_OPCODE_WRITE_REGISTER 0x03 // Able to write a data on register #define AK7451_OPCODE_READ_REGISTER 0x04 // Able to read a register #define AK7451_OPCODE_CHANGE_MODE 0x05 // Able to change mode #define AK7451_OPCODE_ANGLE_MEASURE_CODE 0x08 // Able to read angle measure in user mode #define AK7451_OPCODE_READ_ANGLE 0x09 // Able to read an angle data // Register Address Map #define AK7451_REG_ANG 0x00 // 12bit angle data #define AK7451_REG_MAG 0x01 // Magnetic flux density strength(1LSB/mT) #define AK7451_REG_CHMD 0x02 // For mode state #define AK7451_REG_ERRMON 0x03 // This register will show what kind of error is. #define AK7451_REG_ID1 0x04 // For ID data (EEPROM Only) #define AK7451_REG_ID2 0x05 // For ID data (EEPROM Only) #define AK7451_REG_ZP 0x06 // For set up angle zero point #define AK7451_REG_RDABZ 0x07 // For set up "Rotation direction", #define AK7451_REG_MLK 0x08 // For memory lock #define AK7451_REG_ABNRM 0x09 // For set up abnormal detection disable #define AK7451_REG_UVW 0x0A // For set up "UVW output enable disable" #define AK7451_READ_ANGLE_STATE_MD 0x80 #define AK7451_READ_ANGLE_STATE_P1 0x40 #define AK7451_READ_ANGLE_STATE_P2 0x20 #define AK7451_READ_ANGLE_STATE_ER 0x10 #define AK7451_BIT_MASK_ABZ_E 0x80 // ABZ output enable #define AK7451_BIT_MASK_UVW_E 0x40 // UVW output enable #define AK7451_ABNORMAL_STATE_NORMAL 0x03 // /** * Constructor. * */ AK7451::AK7451(){ _spi = NULL; _cs = NULL; AK7451::operationMode = AK7451::AK7451_NORMAL_MODE; } /** * Destructor. * */ AK7451::~AK7451(){ if (_spi) delete _spi; if(_cs) delete _cs; } /** * begin * * @param *spi pointer to SPI instance * @param *cs pointer to DigitalOut instance for CS */ void AK7451::begin(SPI *spi, DigitalOut *cs){ if (_spi) delete _spi; _spi=spi; if (_cs) delete _cs; _cs=cs; _cs->write(1); } /** * Check the connection. * * @note Connection check is performed by reading a register which has a fixed value and verify it. * * @return Returns SUCCESS when succeeded, otherwise returns another code. */ AK7451::Status AK7451::checkConnection() { AK7451::Status status; status = AK7451::setOperationMode(AK7451::AK7451_USER_MODE); if(status != AK7451::SUCCESS) return status; char data[2]; // read current mode data[0] = 0x00; data[1] = 0x00; status = AK7451::readRegister(AK7451_REG_CHMD, data); if(status != AK7451::SUCCESS) return status; if(data[0] != 0x05 || data[1] != 0x0F) return AK7451::ERROR; return AK7451::SUCCESS; } /** * Writes data to EEPROM on the device. * @param address EEPROM address * @param data data to be written * @return Returns SUCCESS when succeeded, otherwise returns another. */ AK7451::Status AK7451::writeEEPROM(char address, const char *data){ if(AK7451::operationMode != AK7451::AK7451_USER_MODE) return AK7451::ERROR_IN_NORMAL_MODE; AK7451::Status status = AK7451::write(AK7451_OPCODE_WRITE_EEPROM, address, data); wait(0.005); // Wait Wt=5msec(MAX) return status; } /** * Reads data from EEPROM on the device. * @param address EEPROM address * @param data data to read * @return Returns SUCCESS when succeeded, otherwise returns another. */ AK7451::Status AK7451::readEEPROM(char address, char *data){ if(AK7451::operationMode != AK7451::AK7451_USER_MODE) return AK7451::ERROR_IN_NORMAL_MODE; AK7451::Status status = read(AK7451_OPCODE_READ_EEPROM, address, data); return status; } /** * Writes data to register on the device. * @param address register address * @param data data to be written * @return Returns SUCCESS when succeeded, otherwise returns another. */ AK7451::Status AK7451::writeRegister(char address, const char *data){ if(AK7451::operationMode != AK7451::AK7451_USER_MODE) return AK7451::ERROR_IN_NORMAL_MODE; AK7451::Status status = AK7451::write(AK7451_OPCODE_WRITE_REGISTER, address, data); return status; } /** * Reads data from register on the device. * @param address register address * @param data data to read * @return Returns SUCCESS when succeeded, otherwise returns another. */ AK7451::Status AK7451::readRegister(char address, char *data){ if(AK7451::operationMode != AK7451::AK7451_USER_MODE) return AK7451::ERROR_IN_NORMAL_MODE; AK7451::Status status = AK7451::read(AK7451_OPCODE_READ_REGISTER, address, data); return status; } /** * Sets device operation mode. * * @param mode device opration mode * * @return Returns SUCCESS when succeeded, otherwise returns another code. */ AK7451::Status AK7451::setOperationMode(AK7451::OperationMode mode){ char command[2]; command[0] = (char)(0x0F&(mode>>8)); command[1] = (char)(0x00FF&mode); AK7451::operationMode = mode; AK7451::Status status = AK7451::write(AK7451_OPCODE_CHANGE_MODE, AK7451_REG_CHMD, command); wait(0.01); return status; } /** * Gets device operation mode. * * @return Returns OperationMode. */ AK7451::OperationMode AK7451::getOperationMode(){ return AK7451::operationMode; } /** * Reads angle data from the device. * * @param angle pointer to read angle data buffer * * @return Returns SUCCESS when succeeded, otherwise returns another code. */ AK7451::Status AK7451::readAngle(char *angle) { char command[2]={0x00,0x00}; AK7451::read(AK7451_OPCODE_READ_ANGLE, 0x00, command); // status check if( (command[0] & AK7451_READ_ANGLE_STATE_MD) != 0 ){ // AK7451::setOperationMode(AK7451::AK7451_NORMAL_MODE); return AK7451::ERROR_IN_USER_MODE; }else if( (command[0] & AK7451_READ_ANGLE_STATE_ER) == 0 ){ return AK7451::ERROR_ABNORMAL_STRENGTH; } // parity check if( AK7451::parityCheck(command) != AK7451::SUCCESS ) return AK7451::ERROR_PARITY; // status check passed, set angle data angle[0] = command[0]&0x0F; angle[1] = command[1]; return AK7451::SUCCESS; } /** * Measures and reads angle, magnetic flux density and abnormal state code while in the user mode. * * @param angle pointer to angle data * @param density magnetic flux density * @param abnormal_state abnormal state * * @return Returns SUCCESS when succeeded, otherwise returns another code. */ AK7451::Status AK7451::readAngleMeasureCommand(char *angle, char *density, char *abnormal_state) { if(AK7451::operationMode != AK7451::AK7451_USER_MODE) return AK7451::ERROR_IN_NORMAL_MODE; AK7451::Status status; char data[2] = {0x00,0x00}; // perform measurement status = AK7451::write(AK7451_OPCODE_ANGLE_MEASURE_CODE, AK7451_REG_ANG, data); if(status != AK7451::SUCCESS) return status; wait(0.01); // angle read status = AK7451::readRegister(AK7451_REG_ANG, data); if(status != AK7451::SUCCESS) return status; angle[0] = data[0]&0x0F; angle[1] = data[1]; // mag density read status = AK7451::readRegister(AK7451_REG_MAG, data); if(status != AK7451::SUCCESS) return status; *density = data[1]; // abnormal_state code read status = AK7451::readRegister(AK7451_REG_ERRMON, data); if(status != AK7451::SUCCESS) return status; *abnormal_state = data[1]&0x03; return AK7451::SUCCESS; } /** * Measures current angle and sets the value to EEPROM as zero angle position. * * @return Returns SUCCESS when succeeded, otherwise returns another code. */ AK7451::Status AK7451::setAngleZero(){ AK7451::Status status = AK7451::SUCCESS; char angle[2] = {0x00,0x00}; char density; char abnormal_state; // store the original mode AK7451::OperationMode org = AK7451::operationMode; // set to user mode status = AK7451::setOperationMode(AK7451::AK7451_USER_MODE); if(status != AK7451::SUCCESS) return status; // initialize ZP register. Set ZP value to zero. status = AK7451::writeRegister(0x06,angle); if(status != AK7451::SUCCESS) return status; // read angle data with zero ZP offset status = AK7451::readAngleMeasureCommand(angle, &density, &abnormal_state); if(status != AK7451::SUCCESS) return status; if(abnormal_state != AK7451_ABNORMAL_STATE_NORMAL) return AK7451::ERROR_ABNORMAL_STRENGTH; // set read angle to ZP EEPROM status = AK7451::setAngleZero(angle); if(status != AK7451::SUCCESS) return status; // back to the original mode status = AK7451::setOperationMode(org); if(status != AK7451::SUCCESS) return status; return status; } /** * Sets the value to EEPROM as zero angle position. * * @param angle zero angle position * * @return Returns SUCCESS when succeeded, otherwise returns another code. */ AK7451::Status AK7451::setAngleZero(const char *angle){ // if(AK7451::operationMode != AK7451::AK7451_USER_MODE) return AK7451::ERROR_IN_NORMAL_MODE; AK7451::writeRegister(AK7451_REG_ZP, angle); return AK7451::writeEEPROM(AK7451_REG_ZP, angle); } //////////////////////////////////////////////////////// // private methods //////////////////////////////////////////////////////// /** * Reads data from device. * @param operation_code OPCODE * @param address memory/register addredd * @param *data pointer to the read buffer. length=2 fixed. * @return Returns SUCCESS when succeeded, otherwise returns another. */ AK7451::Status AK7451::read(char operation_code, char address, char *data) { char command[3]; command[0] = (operation_code<<4) | (address>>3); command[1] = 0xF0&(address<<5); command[2] = 0x00; _cs->write(0); _spi->write(command[0]); for(int i=0; i<2; i++){ data[i] = _spi->write(command[i+1]); } _cs->write(1); return AK7451::SUCCESS; } /** * Writes data to the device. * @param operation_code OPCODE * @param address memory/register addredd * @param *data pointer to the read buffer. length=2 fixed. * @return Returns SUCCESS when succeeded, otherwise returns another. */ AK7451::Status AK7451::write(char operation_code, char address, const char *data) { char command[3]; command[0] = (operation_code<<4) | (address>>3); command[1] = (0xF0&(address<<5)) | (0x0F&data[0]); command[2] = data[1]; _cs->write(0); for(int i=0; i<3; i++){ _spi->write(command[i]); } _cs->write(1); return AK7451::SUCCESS; } /** * Checks parity bits sub function * @param data data 12bit read data * @param parity parity bit status * @param error error bit status * @return Returns SUCCESS when succeeded, otherwise returns another. */ AK7451::Status AK7451::parityCheckSub(const char data, const char parity, const char error){ char sum = parity + error; for(int i=0; i<6; i++){ if( ((data>>i) & 0x01) !=0 )sum++; } if( (sum & 0x01) == 0) return AK7451::SUCCESS; else return AK7451::ERROR_PARITY; } /** * Checks parity bits * @param data 2 byte read data to chek parity * @return Returns SUCCESS when succeeded, otherwise returns another. */ AK7451::Status AK7451::parityCheck(const char *data){ char p1 = 0x01 & (data[0]>>6); char p2 = 0x01 & (data[0]>>5); char er = 0x01 & (data[0]>>4); char hi = ( (data[0]&0x0F)<<2 ) | ((data[1] & 0xC0)>>6); // Data[11:6] char lo = data[1] & 0x3F; // Data[5:0] AK7451::Status status; status = AK7451::parityCheckSub(hi, p1, er); if( status != AK7451::SUCCESS) return AK7451::ERROR_PARITY; status = parityCheckSub(lo, p2, er); if( status != AK7451::SUCCESS) return AK7451::ERROR_PARITY; return SUCCESS; }