Library to read/write Sensirion SF04 based gas/liquid flow sensors.
Dependents: TestBenchSerenity-proto_F429ZI TestBenchFlow HSPFLOW1 TestBenchFlow1 ... more
sensirion_sf04.cpp
- Committer:
- dmwahl
- Date:
- 2021-09-16
- Revision:
- 7:0c1bbd80bec3
- Parent:
- 5:1243b362ece8
File content as of revision 7:0c1bbd80bec3:
#include "sensirion_sf04.h" //=========================================================================== // Default constructor SF04::SF04(I2C &i2c, int i2cAddress, int calField, int resolution) : i2c(i2c), mi2cAddress(i2cAddress << 1) //=========================================================================== { u8t error = false; ready = false; int16_t wait = 200; // Unsure why, but without a wait between commands the values read out don't make sense. wait_us(wait); error |= softReset(); wait_us(wait); error |= ReadSerialNumber(&serialNumber); wait_us(wait); error |= ReadScaleFactor(&scaleFactor); wait_us(wait); error |= ReadFlowUnit(flowUnitStr); wait_us(wait); error |= setMeasResolution(resolution); // 9-16 bit wait_us(wait); error |= setCalibrationField(calField); wait_us(wait); if (error == false) { ready = true; } } //=========================================================================== // Set measurement resolution (9-16 bit), format is eSF04_RES_12BIT u8t SF04::setMeasResolution(u16t resolution) //=========================================================================== { ready = false; u8t error = false; //variable for error code nt16 registerValue; // variable for register value switch(resolution) { case 9 : resolution = eSF04_RES_9BIT; break; case 10 : resolution = eSF04_RES_10BIT; break; case 11 : resolution = eSF04_RES_11BIT; break; case 12 : resolution = eSF04_RES_12BIT; break; case 13 : resolution = eSF04_RES_13BIT; break; case 14 : resolution = eSF04_RES_14BIT; break; case 15 : resolution = eSF04_RES_15BIT; break; case 16 : resolution = eSF04_RES_16BIT; break; default: resolution = eSF04_RES_16BIT; break; } ReadRegister(SF04_ADV_USER_REG,®isterValue); // Read out current register value registerValue.u16 = (registerValue.u16 & ~eSF04_RES_MASK) | resolution; // Set resolution in Advanced User Register [11:9] WriteRegister(SF04_ADV_USER_REG,®isterValue); // Write out new register value ready = true; return error; } //=========================================================================== // Set calibration field (0-4) u8t SF04::setCalibrationField(u8t calfield) //=========================================================================== { ready = false; u8t error = false; //variable for error code nt16 registerValue; // variable for register value if (calfield > 8) { calfield = 0; } ReadRegister(SF04_USER_REG,®isterValue); // Read out current register value registerValue.u16 = (registerValue.u16 & ~eSF04_CF_MASK) | (calfield << 4); // Set calibration field in User Register [6:4] WriteRegister(SF04_USER_REG,®isterValue); // Write out new register value ready = true; return error; } //=========================================================================== // Enable Temperature/Vdd correction u8t SF04::setTempVddCorrectionBit(u8t value) //=========================================================================== { ready = false; u8t error = false; //variable for error code nt16 registerValue; // variable for register value if (value != 1) { value = 0; // Ensure value is 1 or 0 } ReadRegister(SF04_USER_REG,®isterValue); // Read out current register value registerValue.u16 = ((registerValue.u16 & ~0x400) | (value << 10)); // Set calibration field in User Register [bit 10] WriteRegister(SF04_USER_REG,®isterValue); // Write out new register value ready = true; return error; } //=========================================================================== // Force sensor reset bool SF04::softReset() //=========================================================================== { ready = false; char data_write = SF04_SOFT_RESET; if (i2c.write(mi2cAddress, &data_write, 1, false) == 0) { ready = true; return true; } else { ready = true; return false; } } //=========================================================================== // Compute CRC and return true if correct and false if not u8t SF04::checkCRC(u8t data[], u8t numBytes, u8t checksum) //=========================================================================== { u8t crc = 0; u8t byteCtr; for (byteCtr = 0; byteCtr < numBytes; byteCtr++) { crc ^= (data[byteCtr]); for (u8t bit = 8; bit > 0; bit--) { if (crc & 0x80) crc = (crc << 1) ^ SF04_POLYNOMIAL; else crc = (crc << 1); } } if (crc != checksum) return SF04_CHECKSUM_ERROR; else return 0; } //=========================================================================== u8t SF04::ReadRegister(etSF04Register eSF04Register, nt16 *pRegisterValue) //=========================================================================== { ready = false; u8t checksum; //variable for checksum byte u8t error = false; //variable for error code char dataWrite[1] = {eSF04Register}; char dataRead[3]; u8t data[2]; //data array for checksum verification int writeAddr = (mi2cAddress | I2C_WRITE); int readAddr = (mi2cAddress | I2C_READ); i2c.write(writeAddr, dataWrite, 1, true); i2c.read(readAddr, dataRead, 3, false); checksum = dataRead[2]; data[0] = dataRead[0]; data[1] = dataRead[1]; error |= checkCRC(data,2,checksum); pRegisterValue->s16.u8H = dataRead[0]; pRegisterValue->s16.u8L = dataRead[1]; ready = true; return error; } //=========================================================================== u8t SF04::WriteRegister(etSF04Register eSF04Register, nt16 *pRegisterValue) //=========================================================================== { ready = false; u8t error = 1; //variable for error code //-- check if selected register is writable -- if(!(eSF04Register == SF04_READ_ONLY_REG1 || eSF04Register == SF04_READ_ONLY_REG2)) { error=0; //-- write register to sensor -- int writeAddr = (mi2cAddress | I2C_WRITE); char dataWrite[3] = {static_cast<char>((eSF04Register & ~0x01 | I2C_WRITE)), pRegisterValue->s16.u8H, pRegisterValue->s16.u8L}; i2c.write(writeAddr, dataWrite, 3); } ready = true; return error; } //=========================================================================== u8t SF04::ReadEeprom(u16t eepromStartAdr, u16t size, nt16 eepromData[]) //=========================================================================== { ready = false; u8t checksum; //checksum u8t data[2]; //data array for checksum verification u8t error = 0; //error variable u16t i; //counting variable nt16 eepromStartAdrTmp; //variable for eeprom adr. as nt16 eepromStartAdrTmp.u16=eepromStartAdr; //-- write I2C sensor address and command -- i2c.start(); error |= i2c.write(mi2cAddress | I2C_WRITE); error |= i2c.write(SF04_EEPROM_R); eepromStartAdrTmp.u16=(eepromStartAdrTmp.u16<<4); // align eeprom adr left error |= i2c.write(eepromStartAdrTmp.s16.u8H); error |= i2c.write(eepromStartAdrTmp.s16.u8L); //-- write I2C sensor address for read -- i2c.start(); error |= i2c.write(mi2cAddress | I2C_READ); //-- read eeprom data and verify checksum -- for(i=0; i<size; i++) { eepromData[i].s16.u8H = data[0] = i2c.read(1); eepromData[i].s16.u8L = data[1] = i2c.read(1); checksum=i2c.read( (i < size-1) ? 1 : 0 ); //NACK for last byte error |= checkCRC(data,2,checksum); } i2c.stop(); ready = true; return error; } //=========================================================================== u8t SF04::Measure(etSF04MeasureType eSF04MeasureType)//, nt16 *pMeasurement) //=========================================================================== { ready = false; u8t checksum; //checksum u8t error=0; //error variable char dataWrite[1]; char dataRead[3]; u8t data[2]; //data array for checksum verification nt16 *pMeasurement; //-- write measurement command -- switch(eSF04MeasureType) { case FLOW : dataWrite[0] = SF04_TRIGGER_FLOW_MEASUREMENT; pMeasurement = &flow; setTempVddCorrectionBit(0); break; case TEMP : dataWrite[0] = SF04_TRIGGER_TEMP_MEASUREMENT; pMeasurement = &temperature; setTempVddCorrectionBit(1); break; case VDD : dataWrite[0] = SF04_TRIGGER_VDD_MEASUREMENT; pMeasurement = &vdd; setTempVddCorrectionBit(1); break; default: break; } int writeAddr = (mi2cAddress | I2C_WRITE); int readAddr = (mi2cAddress | I2C_READ); i2c.write(writeAddr, dataWrite, 1, true); i2c.read(readAddr, dataRead, 3, false); checksum = dataRead[2]; data[0] = dataRead[0]; data[1] = dataRead[1]; if (checkCRC(data,2,checksum) == 0) { pMeasurement->s16.u8H = dataRead[0]; pMeasurement->s16.u8L = dataRead[1]; } else { error |= SF04_CHECKSUM_ERROR; } ready = true; return error; } //=========================================================================== u8t SF04::ReadSerialNumber( nt32 *serialNumber ) //=========================================================================== { ready = false; nt16 registerValue; //register value for register u16t eepromBaseAdr; //eeprom base address of active calibration field u16t eepromAdr; //eeprom address of SF04's scale factor nt16 eepromData[2]; //serial number u8t error=0; //error variable //-- read "Read-Only Register 2" to find out the active configuration field -- error |= ReadRegister(SF04_READ_ONLY_REG2,®isterValue); //-- calculate eeprom address of product serial number -- eepromBaseAdr=(registerValue.u16 & 0x0007)*0x0300; //RO_REG2 bit<2:0>*0x0300 eepromAdr= eepromBaseAdr + SF04_EE_ADR_SN_PRODUCT; //-- read product serial number from SF04's eeprom-- error |= ReadEeprom( eepromAdr, 2, eepromData); serialNumber->s32.u16H=eepromData[0].u16; serialNumber->s32.u16L=eepromData[1].u16; ready = true; return error; } //=========================================================================== u8t SF04::ReadScaleFactor(nt16 *scaleFactor) //=========================================================================== { ready = false; u8t error = 0; //variable for error code nt16 registerValue; //register value for user register u16t eepromBaseAdr; //eeprom base address of active calibration field u16t eepromAdr; //eeprom address of SF04's scale factor //-- read "User Register " to find out the active calibration field -- error |= ReadRegister(SF04_USER_REG ,®isterValue); //-- calculate eeprom address of scale factor -- eepromBaseAdr = ((registerValue.u16 & 0x0070) >> 4) * 0x0300; //UserReg bit<6:4>*0x0300 eepromAdr = eepromBaseAdr + SF04_EE_ADR_SCALE_FACTOR; //-- read scale factor from SF04's eeprom-- error |= ReadEeprom( eepromAdr, 1, scaleFactor); ready = true; return error; } //=========================================================================== u8t SF04::ReadFlowUnit(char *flowUnitStr) //=========================================================================== { ready = false; //-- table for unit dimension, unit time, unit volume (x=not defined) -- const char *unitDimension[]= {"x","x","x","n","u","m","c","d","","-","h","k", "M","G","x","x" }; const char *unitTimeBase[] = {"","us","ms","s","min","h","day","x","x","x","x", "x","x","x","x","x" }; const char *unitVolume[] = {"ln","sl","x","x","x","x","x","x","l","g","x", "x","x","x","x","x","Pa","bar","mH2O","inH2O", "x","x","x","x","x","x","x","x","x","x","x","x" }; //-- local variables -- nt16 registerValue; //register value for user register u16t eepromBaseAdr; //eeprom base address of active calibration field u16t eepromAdr; //eeprom address of SF04's flow unit word nt16 flowUnit; //content of SF04's flow unit word u8t tableIndex; //index of one of the unit arrays u8t error=0; //-- read "User Register" to find out the active calibration field -- error |= ReadRegister(SF04_USER_REG ,®isterValue); //-- calculate eeprom address of flow unit-- eepromBaseAdr=((registerValue.u16 & 0x0070)>>4)*0x0300; //UserReg bit<6:4>*0x0300 eepromAdr= eepromBaseAdr + SF04_EE_ADR_FLOW_UNIT; //-- read flow unit from SF04's eeprom-- error |= ReadEeprom( eepromAdr, 1, &flowUnit); //-- get index of corresponding table and copy it to unit string -- tableIndex=(flowUnit.u16 & 0x000F)>>0; //flowUnit bit <3:0> strcpy(flowUnitStr, unitDimension[tableIndex]); tableIndex=(flowUnit.u16 & 0x1F00)>>8; //flowUnit bit <8:12> strcat(flowUnitStr, unitVolume[tableIndex]); tableIndex=(flowUnit.u16 & 0x00F0)>>4; //flowUnit bit <4:7> if(unitTimeBase[tableIndex] != "") { //check if time base is defined strcat(flowUnitStr, "/"); strcat(flowUnitStr, unitTimeBase[tableIndex]); } //-- check if unit string is feasible -- if(strchr(flowUnitStr,'x') != NULL ) error |= SF04_UNIT_ERROR; ready = true; return error; }