-
-
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
0 parents
commit cc2caf3
Showing
7 changed files
with
485 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,11 @@ | ||
.other | ||
*.pem | ||
.cache | ||
*.log | ||
*.workspace | ||
*.code-workspace | ||
*.*-workspace | ||
.npm | ||
.yarn | ||
.pnpm | ||
.env.local |
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,21 @@ | ||
The MIT License (MIT) | ||
|
||
Copyright (c) 2021 Nicholas Berlette <nick@berlette.com> | ||
|
||
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. |
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,29 @@ | ||
# `ECA: Ethanol Content Analyzer` | ||
|
||
Ethanol Content Analyzer for Arduino Nano or Uno. | ||
|
||
Converts digital FlexFuel sensor data (`50~150 Hz`) into analog (`0-5v` or `0.5-4.5v`), for tuners, datalogs, and in-dash gauges. | ||
|
||
## `Pins` | ||
|
||
| Type | Pin | Description | | ||
| ---------- | -------- | ------------------- | | ||
| Sensor In | `D8` | `TIMER1 / ICP1` | | ||
| PWM Output | `D3/D11` | Built-in PWM driver | | ||
| DAC Output | `A4/A5` | MCP4725 12bit DAC | | ||
|
||
## `Hz -> E % -> V` | ||
|
||
| Input (Hz) | E (%) | Output (V) | | ||
| :--------- | :----: | :------------------------- | | ||
| `50 hz` | ` 0 %` | `0.50v` | | ||
| `100 hz` | `50 %` | `2.25v` | | ||
| `150 hz` | `100%` | `4.50v` | | ||
| **Errors** | | | | ||
| `< 50 hz` | `---` | `4.80v` - contaminated | | ||
| `> 150 hz` | `---` | `4.90v` - high water level | | ||
| `<= 0 hz` | `---` | `0.10v` - disconnected | | ||
|
||
## `License` | ||
|
||
[MIT](https://mit-license.org) © [Nicholas Berlette](https://nick.berlette.com) |
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,148 @@ | ||
#include "Arduino.h" | ||
#include <Wire.h> | ||
#include "MCP4725.h" | ||
/*See page 19 of the MCP4726 DataSheet. | ||
*[C2,C1]=00 -- Fast Mode | ||
*[C2,C1,C0]=010 --- Write to DAC register only | ||
*[C2,C1,C0]=011 --- Write to DAC and EEPROM registers | ||
*[PD1,PD0]=00 -- Power Normal mode | ||
*[PD1,PD0]=01 -- Power Down mode with 1k pull down | ||
*[PD1,PD0]=10 -- Power Down mode with 100k pull down | ||
*[PD1,PD0]=11 -- Power Down mode with 500k pull down | ||
*The contents of bytes 2nd,3rd,4th are orgnized differently | ||
*based on whether the DAC is operated in Normal-Speed or Fast-Speed. Let | ||
*D denote data byte, x denote don't-care. Then: | ||
* | ||
*For Normal-Speed: | ||
*[Addr.Byte]+[C2,C1,C0,x,x,PD1,PD0,x]+[D11,D10,D9,D8,D7,D6,D5,D4]+[D3,D2,D1,D0,x,x,x,x] | ||
* | ||
*For Fast-Speed: | ||
*[Addr.Byte]+[C2,C1,PD1,PD0,D11,D10,D9,D8],[D7,D6,D5,D4,D3,D2,D1,D0] | ||
* | ||
*The address byte for our dac is (1,1,0,0,0,1,A0). By default A0=GND, but we can connect it to VCC. | ||
*For A0=GND the hex value of address is 0x62 (1100010) | ||
*For A0=VCC the hex value of address is 0x63 (1100011) | ||
*We must pass the address in our Arduino code in the argument of the function .begin(). But the argument must | ||
*be an 8-bit value and the address is only 7-bit long. What is happening internally is left shifting by 1 bit. | ||
*You can see this if you trace the definition of the .begin() function and the definition of the twi_setAddress() | ||
*in the file twi.c. The register TWAR is set to (address << 1). The R/W bit (bit 8) is actually determined based | ||
*on the function you send after .begin() | ||
*/ | ||
MCP4725::MCP4725() {} | ||
void MCP4725::begin(uint8_t addr) { //in the Arduino code, we'd send 0x62 as arumenment when A0_bit=GND (default). | ||
_i2caddr = addr; //or if A0_bit=vcc then we would pass 0x63 as argument | ||
Wire.begin(); | ||
} | ||
void MCP4725::setFastMode(){ | ||
#ifdef TWBR // in case this library is used on a chip that does not have TWBR reg defined. | ||
TWBR = ((F_CPU / 400000L) - 16) / 2; // Set I2C frequency for the ATMega to 400kHz | ||
#endif | ||
} | ||
/* | ||
*Wire.write() takes an 8-bit unsigned int. We thus need to convert our 16-bit | ||
*argument to 2 8-bit variables. 16 to 8-bit, conversion keeps the 8 LSBs. | ||
*/ | ||
void MCP4725::setVoltageAndSave(uint16_t output){ | ||
/*For Normal-Speed: (Only normal speed includes the C3 bit, needed for writing to EEPROM) | ||
*[Addr.Byte]+[C2,C1,C0,x,x,PD1,PD0,x]+[D11,D10,D9,D8,D7,D6,D5,D4]+[D3,D2,D1,D0,x,x,x,x] */ | ||
Wire.beginTransmission(_i2caddr); | ||
Wire.write(WRITEDACEEPROM); //[C2,C1,C0,x,x,PD1,PD0,x]=[0,1,1,0,0,0,0,0] | ||
/*The 12-bit output is in 16-bit form (0,0,0,0,D11.D10.D9.D8.D7.D6.D5.D4,D3.D2.D1.D0).*/ | ||
uint8_t firstbyte=(output>>4);//(0,0,0,0,0,0,0,0,D11.D10.D9.D8.D7.D6.D5.D4) of which only the 8 LSB's survive | ||
output = output << 12; //(D3.D2.D1.D0,0,0,0,0,0,0,0,0,0,0,0,0) | ||
uint8_t secndbyte=(output>>8);//(0,0,0,0,0,0,0,0,D3,D2,D1,D0,0,0,0,0) of which only the 8 LSB's survive. | ||
Wire.write(firstbyte); | ||
Wire.write(secndbyte); | ||
Wire.endTransmission(); | ||
} | ||
void MCP4725::setVoltage(uint16_t output){ | ||
/*For Normal-Speed: | ||
*[Addr.Byte]+[C2,C1,C0,x,x,PD1,PD0,x]+[D11,D10,D9,D8,D7,D6,D5,D4]+[D3,D2,D1,D0,x,x,x,x] */ | ||
Wire.beginTransmission(_i2caddr); | ||
Wire.write(WRITEDAC); //[C2,C1,C0,x,x,PD1,PD0,x]=[0,1,0,0,0,0,0,0] | ||
/*The 12-bit output is in 16-bit form (0,0,0,0,D11.D10.D9.D8.D7.D6.D5.D4,D3.D2.D1.D0).*/ | ||
uint8_t firstbyte=(output>>4);//(0,0,0,0,0,0,0,0,D11.D10.D9.D8.D7.D6.D5.D4) of which only the 8 LSB's survive | ||
output = output << 12; //(D3.D2.D1.D0,0,0,0,0,0,0,0,0,0,0,0,0) | ||
uint8_t secndbyte=(output>>8);//(0,0,0,0,0,0,0,0,D3,D2,D1,D0,0,0,0,0) of which only the 8 LSB's survive. | ||
Wire.write(firstbyte); | ||
Wire.write(secndbyte); | ||
Wire.endTransmission(); | ||
} | ||
void MCP4725::setVoltageFast( uint16_t output){ | ||
/*For Fast-Speed: | ||
*[Addr.Byte]+[C2,C1,PD1,PD0,D11,D10,D9,D8],[D7,D6,D5,D4,D3,D2,D1,D0] */ | ||
Wire.beginTransmission(_i2caddr); | ||
//output is a 12-bit value in 16-bit form, namely: [0,0,0,0,D11,D10,D9,D8,D7,D6,D5,D4,D3,D2,D1,D0] | ||
uint8_t firstbyte=(output>>8); //[0,0,0,0,0,0,0,0,0,0,0,0,D11,D10,D9,D8] only the 8 LSB's survive | ||
uint8_t secndbyte=(output); //only the 8 LSB's survive. | ||
Wire.write(firstbyte); // Upper data bits (0,0,0,0,D11,D10,D9,D8) | ||
Wire.write(secndbyte); // Lower data bits (D7,D6,D5,D4,D3,D2,D1,D0) | ||
Wire.endTransmission(); | ||
} | ||
void MCP4725::powerDown1kPullDown(){ //[PD1,PD0]=01; [C2,C1,C0]=010 - Write to DAC only | ||
/*For Normal-Speed: | ||
*[Addr.Byte]+[C2,C1,C0,x,x,PD1,PD0,x]+[D11,D10,D9,D8,D7,D6,D5,D4]+[D3,D2,D1,D0,x,x,x,x] */ | ||
Wire.beginTransmission(_i2caddr); | ||
Wire.write(0b01000010); | ||
Wire.write(0b00000000); | ||
Wire.write(0b00000000); | ||
Wire.endTransmission(); | ||
} | ||
void MCP4725::powerDown100kPullDown(){//[PD1,PD0]=10; [C2,C1,C0]=010 - Write to DAC only | ||
/*For Normal-Speed: | ||
*[Addr.Byte]+[C2,C1,C0,x,x,PD1,PD0,x]+[D11,D10,D9,D8,D7,D6,D5,D4]+[D3,D2,D1,D0,x,x,x,x] */ | ||
Wire.beginTransmission(_i2caddr); | ||
Wire.write(0b01000100); | ||
Wire.write(0b00000000); | ||
Wire.write(0b00000000); | ||
Wire.endTransmission(); | ||
} | ||
void MCP4725::powerDown500kPullDown(){//[PD1,PD0]=11; [C2,C1,C0]=010 - Write to DAC only | ||
/*For Normal-Speed: | ||
*[Addr.Byte]+[C2,C1,C0,x,x,PD1,PD0,x]+[D11,D10,D9,D8,D7,D6,D5,D4]+[D3,D2,D1,D0,x,x,x,x] */ | ||
Wire.beginTransmission(_i2caddr); | ||
Wire.write(0b01000110); | ||
Wire.write(0b00000000); | ||
Wire.write(0b00000000); | ||
Wire.endTransmission(); | ||
} | ||
|
||
uint16_t MCP4725::readCurrentDacVal(){ | ||
Wire.requestFrom(_i2caddr, (uint8_t) 5); | ||
while(Wire.available()!=5){ | ||
Serial.println("Waiting for readValFromEEPROM() to complete."); | ||
//just wait for a while until the DAC sends the data to the reabuffer | ||
} | ||
Wire.read(); //status | ||
uint8_t upper8bits = Wire.read(); //DAC Register data (D11,D10,D9,D8,D7,D6,D5,D4) | ||
uint8_t lower8bits = Wire.read(); //DAC Register data (D3,D2,D1,D0,0,0,0,0) | ||
Wire.read(); //forth returned byte is EEPROM data (x,PD1,PD0,x,D11,D10,D9,D8) | ||
Wire.read(); //fifth returned byte is EEPROM data (D7,D6,D5,D4,D3,D2,D1,D0) | ||
|
||
/*We now need to return the value (0,0,0,0,D11,D10,D9,D8,D7,D6,D5,D4,D3,D2,D1,D0). */ | ||
return (upper8bits<<4) | (lower8bits>>4); //This is how we get the 16-bit result we want to return. | ||
} | ||
|
||
|
||
uint16_t MCP4725::readValFromEEPROM(){ | ||
Wire.requestFrom(_i2caddr, (uint8_t) 5); | ||
while(Wire.available()!=5){ | ||
Serial.println("Waiting for readValFromEEPROM() to complete."); | ||
//just wait for a while until the DAC sends the data to the reabuffer | ||
} | ||
uint8_t statusBit = Wire.read() >> 7; | ||
Wire.read(); //secnd returned byte is DAC Register data (upper 8 bits) | ||
Wire.read(); //third returned byte is DAC Register data (lower 4 bits + 0000) | ||
uint8_t upper8bits = Wire.read(); //forth returned byte is EEPROM data (x,PD1,PD0,x,D11,D10,D9,D8) | ||
uint8_t lower8bits = Wire.read(); //fifth returned byte is EEPROM data (D7,D6,D5,D4,D3,D2,D1,D0) | ||
|
||
if(statusBit==0){ | ||
Serial.println("Currently writing to EEPROM. Trying to read again..."); | ||
return readValFromEEPROM(); | ||
} | ||
else{ | ||
/*We now need to return the value (0,0,0,0,D11,D10,D9,D8,D7,D6,D5,D4,D3,D2,D1,D0). */ | ||
upper8bits = upper8bits & 0b00001111; //clear the first 4 bits. | ||
return (upper8bits<<8) | lower8bits; //This is how we get the 16-bit result we want to return. | ||
} | ||
} |
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,22 @@ | ||
#include "Arduino.h" | ||
#include <Wire.h> | ||
|
||
#define WRITEDAC (0x40) //(0100 0000) Writes to the DAC | ||
#define WRITEDACEEPROM (0x60) //(0110 0000) Writes to the DAC and the EEPROM (persisting the assigned value after reset) | ||
|
||
class MCP4725{ | ||
public: | ||
MCP4725(); | ||
void begin(uint8_t a); | ||
void setFastMode(); | ||
void setVoltageAndSave(uint16_t output); | ||
void setVoltage(uint16_t output); | ||
void setVoltageFast(uint16_t output); | ||
void powerDown500kPullDown(); | ||
void powerDown100kPullDown(); | ||
void powerDown1kPullDown(); | ||
uint16_t readValFromEEPROM(); | ||
uint16_t readCurrentDacVal(); | ||
private: | ||
uint8_t _i2caddr; | ||
}; |
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,23 @@ | ||
#include "Arduino.h" | ||
#include <Wire.h> | ||
#include "MCP4725.h" | ||
MCP4725 dac; //create a dac object | ||
|
||
const PROGMEM uint16_t DACLookup_FullSine_6Bit[64] ={ | ||
2048, 2248, 2447, 2642, 2831, 3013, 3185, 3346, 3495, 3630, 3750, 3853, 3939, 4007, 4056, 4085, 4095, 4085, 4056, 4007, 3939, 3853, 3750, 3630, | ||
3495, 3346, 3185, 3013, 2831, 2642, 2447, 2248, 2048, 1847, 1648, 1453, 1264, 1082, 910, 749, 600, 465, 345, 242, 156, 88, 39, 10, | ||
0, 10, 39, 88, 156, 242, 345, 465, 600, 749, 910, 1082, 1264, 1453, 1648, 1847}; | ||
|
||
void setup(void) { | ||
dac.begin(0x62); //addres for the dac is 0x62 (default) or 0x63 (A0 pin tied to VCC) | ||
dac.setFastMode(); //comment this out to unset fastmode | ||
} | ||
void loop(void) { | ||
uint16_t i; | ||
for (i = 0; i < 64; i++){ | ||
//dac.setVoltage(pgm_read_word(&(DACLookup_FullSine_6Bit[i]))); //gives: 37Hz with FastMode unset; 110Hz with FastMode set. | ||
dac.setVoltageFast(pgm_read_word(&(DACLookup_FullSine_6Bit[i]))); //gives: 48Hz with FastMode unset; 143Hz with FastMode set. | ||
} | ||
dac.powerDown500kPullDown(); | ||
delay(5); | ||
} |
Oops, something went wrong.