-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2f2b118
commit 5f2b1f6
Showing
9 changed files
with
645 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
// | ||
// FILE: PCF8591.cpp | ||
// AUTHOR: Rob Tillaart | ||
// DATE: 2020-03-12 | ||
// VERSION: 0.0.2 | ||
// PURPOSE: I2C PCF8591 library for Arduino | ||
// URL: https://github.com/RobTillaart/PCF8591 | ||
// | ||
// HISTORY: | ||
// 0.0.1 2020-03-12 initial version | ||
// 0.0.2 2020-07-22 testing, refactor, documentation and examples | ||
|
||
#include "PCF8591.h" | ||
|
||
PCF8591::PCF8591(const uint8_t address) | ||
{ | ||
_address = address; | ||
_control = 0; | ||
_dac = 0; | ||
for (uint8_t i = 0; i < 4; i++) _adc[i] = 0; | ||
_error = PCF8591_OK; | ||
} | ||
|
||
|
||
#if defined (ESP8266) || defined(ESP32) | ||
void PCF8591::begin(uint8_t sda, uint8_t scl, uint8_t val) | ||
{ | ||
Wire.begin(sda, scl); | ||
analogWrite(val); | ||
} | ||
#endif | ||
|
||
void PCF8591::begin(uint8_t val) | ||
{ | ||
Wire.begin(); | ||
analogWrite(val); | ||
} | ||
|
||
bool PCF8591::isConnected() | ||
{ | ||
Wire.beginTransmission(_address); | ||
_error = Wire.endTransmission(); // default == 0 == PCF8591_OK | ||
return( _error == PCF8591_OK); | ||
} | ||
|
||
// ADC PART | ||
uint8_t PCF8591::analogRead(uint8_t channel, uint8_t mode) | ||
{ | ||
if (mode > 3) | ||
{ | ||
_error = PCF8591_MODE_ERROR; | ||
return 0; | ||
} | ||
_control &= 0b01000100; // clear all except flags | ||
_control |= (mode << 4); | ||
|
||
_error = PCF8591_CHANNEL_ERROR; | ||
switch(mode) | ||
{ | ||
case 0: | ||
if (channel > 3) return 0; | ||
_control |= channel; | ||
break; | ||
case 1: | ||
if (channel > 2) return 0; | ||
_control |= (channel << 4); | ||
break; | ||
case 2: | ||
if (channel > 2) return 0; | ||
_control |= (channel << 4); | ||
break; | ||
case 3: | ||
if (channel > 1) return 0; | ||
_control |= (channel << 4); | ||
break; | ||
default: | ||
return 0; | ||
} | ||
_error = PCF8591_OK; | ||
|
||
// NOTE: one must read two values to get an up to date value. | ||
// Page 8 datasheet. | ||
Wire.beginTransmission(_address); | ||
Wire.write(_control); | ||
_error = Wire.endTransmission(); // default == 0 == PCF8591_OK | ||
if (_error != 0) return PCF8591_I2C_ERROR; | ||
if (Wire.requestFrom(_address, (uint8_t)2) != 2) | ||
{ | ||
_error = PCF8591_I2C_ERROR; | ||
return _adc[channel]; // known last value | ||
} | ||
Wire.read(); | ||
_adc[channel] = Wire.read(); | ||
return _adc[channel]; | ||
} | ||
|
||
|
||
void PCF8591::analogRead4() | ||
{ | ||
_control &= 0b01000100; // clear all except flags | ||
uint8_t channel = 0; | ||
_control |= channel; | ||
|
||
|
||
enableINCR(); | ||
Wire.beginTransmission(_address); | ||
Wire.write(_control); | ||
_error = Wire.endTransmission(); // default == 0 == PCF8591_OK | ||
if (_error != 0) | ||
{ | ||
disableINCR(); | ||
return PCF8591_I2C_ERROR; | ||
} | ||
if (Wire.requestFrom(_address, (uint8_t)5) != 5) | ||
{ | ||
_error = PCF8591_I2C_ERROR; | ||
disableINCR(); | ||
return; | ||
} | ||
|
||
Wire.read(); | ||
for (uint8_t i = 0; i < 4; i++) | ||
{ | ||
_adc[i] = Wire.read(); | ||
} | ||
_error = PCF8591_OK; | ||
disableINCR(); | ||
return; | ||
} | ||
|
||
|
||
// DAC PART | ||
void PCF8591::analogWrite(uint8_t value) | ||
{ | ||
_dac = value; | ||
Wire.beginTransmission(_address); | ||
Wire.write(_control); | ||
Wire.write(_dac); | ||
_error = Wire.endTransmission(); | ||
return; | ||
} | ||
|
||
// -- END OF FILE -- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
#pragma once | ||
// | ||
// FILE: PCF8591.h | ||
// AUTHOR: Rob Tillaart | ||
// DATE: 2020-03-12 | ||
// VERSION: 0.0.1 | ||
// PURPOSE: I2C PCF8591 library for Arduino | ||
// URL: https://github.com/RobTillaart/9581 | ||
// | ||
// HISTORY: | ||
// see PCF8591.cpp file | ||
// | ||
|
||
#include "Arduino.h" | ||
#include "Wire.h" | ||
|
||
#define PCF8591_LIB_VERSION "0.0.1" | ||
|
||
#define PCF8591_OK 0x00 | ||
#define PCF8591_PIN_ERROR 0x81 | ||
#define PCF8591_I2C_ERROR 0x82 | ||
#define PCF8591_MODE_ERROR 0x83 | ||
#define PCF8591_CHANNEL_ERROR 0x84 | ||
|
||
// INTERNAL USE ONLY | ||
#define PCF8591_DAC_FLAG 0x40 | ||
#define PCF8591_INCR_FLAG 0x04 | ||
|
||
class PCF8591 | ||
{ | ||
public: | ||
explicit PCF8591(const uint8_t address = 0x48); | ||
|
||
#if defined (ESP8266) || defined(ESP32) | ||
void begin(uint8_t sda, uint8_t scl, uint8_t val = 0); | ||
#endif | ||
void begin(uint8_t val = 0); | ||
|
||
bool isConnected(); | ||
|
||
// ADC PART | ||
// auto increment not tested ==> use with care! | ||
void enableINCR() { _control |= PCF8591_INCR_FLAG; }; | ||
void disableINCR() { _control &= ~PCF8591_INCR_FLAG; }; | ||
bool isINCREnabled() { return ((_control & PCF8591_INCR_FLAG) > 0); }; | ||
|
||
uint8_t analogRead(uint8_t channel, uint8_t mode = 0); | ||
void analogRead4(); | ||
uint8_t lastRead(uint8_t channel) { return _adc[channel]; }; | ||
|
||
// DAC PART | ||
void enableDAC() { _control |= PCF8591_DAC_FLAG; }; | ||
void disableDAC() { _control &= ~PCF8591_DAC_FLAG; }; | ||
bool isDACEnabled() { return ((_control & PCF8591_DAC_FLAG) > 0); }; | ||
|
||
void analogWrite(uint8_t value = 0); | ||
uint8_t lastWrite() { return _dac; }; | ||
|
||
int lastError() { return _error; }; | ||
|
||
private: | ||
uint8_t _address; | ||
uint8_t _control; | ||
uint8_t _dac; | ||
uint8_t _adc[4]; | ||
int _error; | ||
}; | ||
|
||
// END OF FILE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,71 @@ | ||
# PCF8591 | ||
|
||
Arduino Library for PCF8591 I2C 4 channel 8 bit ADC + 1 channel 8 bit DAC | ||
|
||
## Description | ||
|
||
**warning** during tests I could overclock the chip at 650 kHz but it is only specified | ||
to run at 100 kHz. After getting pretty hot it broke down. | ||
So overclocking is fun but not recommended. | ||
|
||
PCF8591 has one 8 bit ADC on board for 4 channels. The ADC is 8 bit and quite fast. | ||
At 100 kHz one gets \> 2000 reads per second for **analogRead()** and | ||
\> 2000 writes per second for **analogWrite()**. | ||
Note that most time is probably spend on I2C communication. | ||
|
||
The auto-increment functionality is used in the **analogRead4()** function. | ||
The library only supports it for the mode 0 (plain ADC, no differential). | ||
The **lastRead()** function is needed to get access to the values. | ||
First tests shows it is 2.6 x faster than 4 individual reads. | ||
|
||
**analogRead4()** needs investigation in the future for the other modi. | ||
|
||
## Interface | ||
|
||
#### generic | ||
|
||
- **PCF8591(const uint8_t address)** constructor with I2C address, default is 0x48 | ||
- **begin(uint8_t sda, uint8_t scl, uint8_t val = 0)** set wire pins for ESP series. | ||
Also set initial value for the DAC. | ||
- **begin(uint8_t val = 0)** Set initial value for the DAC. | ||
- **isConnected()** test to see if chip can be reached. | ||
|
||
#### ADC part | ||
|
||
The PCF8591 has four 8 bit ADC channels. | ||
|
||
- **enableINCR()** used in analogRead4(); Could become private in the future. | ||
- **disableINCR()** idem. | ||
- **isINCREnabled()** idem. | ||
- **analogRead(uint8_t channel, uint8_t mode = 0)** read one of the 4 analog ports. | ||
Default mode is single ports. For comparator modes test datasheet. | ||
- **analogRead4()** read all 4 channels in one call. | ||
Uses **enableINCR()** to do that efficiently. | ||
It is about 2.6 x faster than 4 individual **analogRead()**, although the latter | ||
allows for optimized timing per channel. | ||
Only 4x single ports mode supported for now, comparator modi needs investigation. | ||
- **lastRead(uint8_t channel)** get last read value from cache. | ||
This cache is filled both by **analogRead()** and **analogRead4()**. See example sketch. | ||
|
||
#### DAC PART | ||
|
||
The PCF8591 has one 8 bit DAC. | ||
|
||
- **enableDAC()** switch on the analog output | ||
- **disableDAC()** switch off the analog output (high impedance) Sort of energy saving mode. | ||
- **isDACEnabled()** check the modus operandi. | ||
- **analogWrite(uint8_t value = 0)** writes a value 0..255 to the DAC. CHeck datasheet for voltage. | ||
- **lastWrite()** get last written value from cache. | ||
- **lastError()** always check this value after a read / write to see if it was OK (== 0) | ||
|
||
## Future | ||
|
||
- Improve comparator modi support, how to do that? | ||
dedicated functions? return type int8_t? | ||
- **analogRead4()** needs investigation for the other modi. Does it work? | ||
Is it user understandable? | ||
|
||
|
||
## Operations | ||
|
||
See examples. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
// | ||
// FILE: PCF8591_demo.ino | ||
// AUTHOR: Rob Tillaart | ||
// VERSION: 0.1.0 | ||
// PURPOSE: demo | ||
// DATE: 2020-07-22 | ||
// URL: https://github.com/RobTillaart/PCF8591 | ||
|
||
#include "PCF8591.h" | ||
|
||
PCF8591 dev(0x48); | ||
|
||
void setup() | ||
{ | ||
Serial.begin(115200); | ||
Serial.println(__FILE__); | ||
|
||
Wire.begin(); | ||
Wire.setClock(100000UL); | ||
|
||
#if defined (ESP8266) || defined(ESP32) | ||
dev.begin(21, 22); | ||
#endif | ||
dev.begin(); | ||
} | ||
|
||
void loop() | ||
{ | ||
test_DAC(); | ||
delay(1000); | ||
test_ADC_mode(0); | ||
delay(1000); | ||
|
||
// differential modes, check datasheet | ||
test_ADC_mode(1); | ||
delay(1000); | ||
test_ADC_mode(2); | ||
delay(1000); | ||
test_ADC_mode(3); | ||
delay(1000); | ||
} | ||
|
||
|
||
void test_DAC() | ||
{ | ||
Serial.println(__FUNCTION__); | ||
Serial.println("--------------"); | ||
dev.enableDAC(); | ||
for (int i = 0; i < 5000; i++) | ||
{ | ||
uint8_t val = 127 + 127 * sin(i * 0.01); | ||
Serial.println(val); | ||
dev.analogWrite(val); | ||
delay(1); // just to slow the effect | ||
} | ||
dev.disableDAC(); | ||
Serial.println(); | ||
} | ||
|
||
void test_ADC_mode(uint8_t mode) | ||
{ | ||
uint8_t channels[] = {4, 3, 3, 2 }; // channels per mode | ||
Serial.println(__FUNCTION__); | ||
Serial.println("--------------"); | ||
Serial.println("CH0\tCH1\tCH2\tCH3"); | ||
for (int m = 0; m < 20; m++) | ||
{ | ||
for (uint8_t i = 0; i < channels[mode]; i++) | ||
{ | ||
Serial.print(dev.analogRead(i, mode)); | ||
Serial.print('\t'); | ||
} | ||
Serial.println(); | ||
} | ||
Serial.println(); | ||
} | ||
|
||
// -- END OF FILE -- |
Oops, something went wrong.