Files
2024-07-20 22:09:06 +08:00

229 lines
7.0 KiB
C++

/**
* PCF8591 Analog Port Expand
* https://www.mischianti.org/2019/01/03/pcf8591-i2c-analog-i-o-expander/
*
* The MIT License (MIT)
*
* Copyright (c) 2017 Renzo Mischianti www.mischianti.org All right reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "PCF8591.h"
#include "Wire.h"
/**
* Constructor
* @param address: i2c address
*/
PCF8591::PCF8591(uint8_t address){
_wire = &Wire;
_address = address;
};
#if !defined(__AVR) && !defined(__STM32F1__)
/**
*
* @param address: i2c address
* @param sda: sda pin
* @param scl: scl pin
*/
PCF8591::PCF8591(uint8_t address, uint8_t sda, uint8_t scl){
_wire = &Wire;
_address = address;
_sda = sda;
_scl = scl;
};
#ifdef ESP32
/**
* Constructor
* @param address: i2c address
*/
PCF8591::PCF8591(TwoWire *pWire, uint8_t address){
_wire = pWire;
_address = address;
};
/**
*
* @param address: i2c address
* @param sda: sda pin
* @param scl: scl pin
*/
PCF8591::PCF8591(TwoWire *pWire, uint8_t address, uint8_t sda, uint8_t scl){
_wire = pWire;
_address = address;
_sda = sda;
_scl = scl;
};
#endif
#endif
/**
* wake up i2c controller
*/
void PCF8591::begin(){
#ifndef __AVR
_wire->begin(_sda, _scl);
#else
// Default pin for AVR some problem on software emulation
// #define SCL_PIN _scl
// #define SDA_PIN _sda
_wire->begin();
#endif
}
/**
* Read all analog input in one trasmission
* @param readType read datasheet for info
* SINGLE_ENDED_INPUT
* TREE_DIFFERENTIAL_INPUT
* TWO_SINGLE_ONE_DIFFERENTIAL_INPUT
* TWO_DIFFERENTIAL_INPUT
* @return
*/
struct PCF8591::AnalogInput PCF8591::analogReadAll(byte readType){
DEBUG_PRINTLN("Begin trasmission");
_wire->beginTransmission(_address); // wake up PCF8591
byte operation = AUTOINCREMENT_READ | readType | (_outputStatus&OUTPUT_MASK);
DEBUG_PRINTLN("Write operation");
_wire->write(operation); // control byte: reads ADC0 then auto-increment
DEBUG_PRINTLN("End write (If code stop here add pullup resistor on SDA SCL)");
_wire->endTransmission(); // end tranmission
DEBUG_PRINTLN("Request response");
_wire->requestFrom(_address, (uint8_t)5);
/*uint8_t control =*/_wire->read();
analogInput.ain0=_wire->read();
analogInput.ain1=_wire->read();
analogInput.ain2=_wire->read();
analogInput.ain3=_wire->read();
return analogInput;
};
/**
* Read one specified channel
* @param channel channel or analog identify (if readType is SINGLE_ENDED_INPUT)
* @param readType read datasheet for info
* SINGLE_ENDED_INPUT
* TREE_DIFFERENTIAL_INPUT
* TWO_SINGLE_ONE_DIFFERENTIAL_INPUT
* TWO_DIFFERENTIAL_INPUT
* @return
*/
uint8_t PCF8591::analogRead(uint8_t channel, byte readType){
DEBUG_PRINTLN("Begin trasmission");
_wire->beginTransmission(_address); // wake up PCF8591
byte operation = channel | readType| (_outputStatus&OUTPUT_MASK);
DEBUG_PRINTLN("Write operation");
_wire->write(operation); // control byte: reads ADC0 then auto-increment
DEBUG_PRINTLN("End write (If code stop here add pullup resistor on SDA SCL)");
_wire->endTransmission(); // end tranmission
DEBUG_PRINTLN("Request response");
_wire->requestFrom(_address, (uint8_t)2);
/*uint8_t control = */_wire->read();
uint8_t ana=_wire->read();
return ana;
};
/**
* Read voltage of analog input
* @param analogPin (Analog identifier)
* @param microcontrollerReferenceVoltage get voltage from microcontroller voltage (only AVR no esp8266 for esp 3.3v fixed)
* @param referenceVoltage if microcontrollerReferenceVoltage false take this value
* @return
*/
float PCF8591::voltageRead(uint8_t analogPin, bool microcontrollerReferenceVoltage, float referenceVoltage){
float voltageRef = referenceVoltage;
if (microcontrollerReferenceVoltage) voltageRef = PCF8591::readVcc()/1000.0;
float ana = PCF8591::analogRead(analogPin);
return ana*voltageRef/255;
};
/**
* To write votlage on output
* @param value voltage to write
* @param microcontrollerReferenceVoltage get voltage from microcontroller voltage (only AVR no esp8266 for esp 3.3v fixed)
* @param referenceVoltage if microcontrollerReferenceVoltage false take this value
*/
void PCF8591::voltageWrite(float value, bool microcontrollerReferenceVoltage, float referenceVoltage){
if (microcontrollerReferenceVoltage) referenceVoltage = PCF8591::readVcc()/1000.0;
uint8_t ana = value*255/referenceVoltage;
PCF8591::analogWrite(ana);
};
/**
* Write value on output pin
* @param value votlage in volts
*/
void PCF8591::analogWrite(uint8_t value){
_outputStatus = ENABLE_OUTPUT;
DEBUG_PRINTLN("Begin trasmission");
_wire->beginTransmission(_address);
DEBUG_PRINTLN("Write operation");
_wire->write(ENABLE_OUTPUT); // sets the PCF8591 into a DA mode
DEBUG_PRINTLN("Write value");
_wire->write(value); // sets the output
DEBUG_PRINTLN("End write (If code stop here add pullup resistor on SDA SCL)");
_wire->endTransmission();
};
long PCF8591::readVcc(void) {
#ifdef __AVR
// Read 1.1V reference against AVcc
// set the reference to Vcc and the measurement to the internal 1.1V reference
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ADMUX = _BV(MUX5) | _BV(MUX0);
#else
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#endif
delay(2); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Start conversion
while (bit_is_set(ADCSRA, ADSC))
; // measuring
uint8_t low = ADCL; // must read ADCL first - it then locks ADCH
uint8_t high = ADCH; // unlocks both
long result = (high << 8) | low;
result = 1083630L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
// scale_constant = internal1.1Ref * 1023 * 1000
// internal1.1Ref = 1.1 * Vcc1 (per voltmeter) / Vcc2 (per readVcc() function)
return result; // Vcc in millivolts
#else
// float vdd = readVcc(); // ESP.getVdd33(); //ESP.getVcc();
return 3300;
#endif
}