diff --git a/README.md b/README.md index e0d61aa..323d68b 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ The following table the currently supported devices within this project: | A/D | SPI | | [MCP3008 A/D 10bit converter](src/main/java/com/pi4j/devices/mcp3008/README.md) | A/D | SPI | | [MCP3208 A/D 12bit converter](src/main/java/com/pi4j/devices/mcp3208/README.md) | D/A | I2C | | [MCP4725 12 bit DAC](src/main/java/com/pi4j/devices/mcp4725/README.md) +| D/A | I2C | | [MCP4728 12 bit DAC 4-ch](src/main/java/com/pi4j/devices/mcp4728/README.md) | D/A | SPI | | [MCP4921 12 bit DAC](src/main/java/com/pi4j/devices/mcp4921/README.md) | D/A | SPI | | [MCP4922 12 bit DAC](src/main/java/com/pi4j/devices/mcp4922/README.md) | HAT | | | [Raspberry SenseHat](src/main/java/com/pi4j/examples/hat/raspberry/sensehat/) diff --git a/assets/runMcp4728.sh b/assets/runMcp4728.sh new file mode 100644 index 0000000..20e88ee --- /dev/null +++ b/assets/runMcp4728.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash + java -classpath ./*:../classes com.pi4j.devices.mcp4728.MCP4728App $@ diff --git a/pom.xml b/pom.xml index 9319175..b4292f8 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ 2.0.17 4.0.1 - + 1.6.0 diff --git a/src/main/java/com/pi4j/devices/mcp4728/MCP4728App.java b/src/main/java/com/pi4j/devices/mcp4728/MCP4728App.java new file mode 100644 index 0000000..34bfdc3 --- /dev/null +++ b/src/main/java/com/pi4j/devices/mcp4728/MCP4728App.java @@ -0,0 +1,260 @@ + + +package com.pi4j.devices.mcp4728; + +import com.pi4j.Pi4J; +import com.pi4j.context.Context; +import com.pi4j.drivers.io.da.mcp472x.Mcp4728Driver; +import com.pi4j.io.i2c.I2C; +import com.pi4j.io.i2c.I2CImplementation; +import com.pi4j.util.Console; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Support MCP4728 DAC. + *

+ * Support the single channel at a time functions of the chip. Example: you cannot set + * all four channels vref bit in a single call. Rather this would require + * four separate calls to the driver code. + *

+ *

+ * The MCP4728 ships with device address 0x60. To set other address the DAC config registers + * must be updated. The update process requires the LDAC pin be toggled at a specific point + * in the I2C command as it is clocked into the chip. This GPIO control within the I2C + * traffic is not possible with Pi4j. To accomplish this would require ordering the chip with + * the address programmed, or use of an MCU. + *

+ * @see MCP4728 + * + * + * The MCP4728 chip contains an EEPROM. When Powered-on or RESET, the EEPROM contents are used to update + * the DAC volatile registers, restoring the configured output voltages. The program arguments that result + * in the EEPROM being updated that code examples include the statement dacChip.setEepromEnabled(true);. + * After this method all subsequent methods will ensure the EEPROM is updated. To stop this behavior the + * examples call dacChip.setEepromEnabled(false); + * + * The example uses the letters A B C D to specify the channel. This conforms to the chips Datasheet documentation. + * Within the driver method signatures and code the channel is specified with an int. + * + */ +public class MCP4728App { + + static void main(String[] args) { + System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "INFO"); + Logger logger = LoggerFactory.getLogger(MCP4728App.class); + logger.trace(">>> Enter: init"); + + Context pi4j = Pi4J.newAutoContext(); + + final Console console = new Console(); + console.print("=============================================================="); + console.print("startup MCP4728App "); + console.print("=============================================================="); + + + + int busNum = 1; + int address = MCP4728_Declares._MCP4728_DEFAULT_ADDRESS; + int channel = 0; + boolean doReset = false; + int registerData = 0; + double vref = 0; + float volts = 0; + int onlyOne = 0; // numerous parms a mutually exclusive + int gainBit = 0; + int vrefBit = 1; + boolean setVrefBit = false; + boolean setGainBit = false; + boolean setOutputDigital = false; + boolean setOutputVotage = false; + boolean persistValues = false; + + + + String helpString = " parms: -b 0x? hex value bus -a 0x?? hex value address \n " + + " -r reset chip -ch channel A B C D -d digital value (0 ... 4095) \n" + + " -v voltage (0 ... vdd) -pu persist values true/false -vdd decimal reference voltage \n" + + " -sv vrefBit 0 or 1 -sg gainBit 0 or 1 \n" + + "-d -v -sv -sg mutually exclusive \n" ; + ; + for (int i = 0; i < args.length; i++) { + String o = args[i]; + if (o.contentEquals("-b")) { // bus + String a = args[i + 1]; + busNum = Integer.parseInt(a.substring(2), 16); + i++; + } else if (o.contentEquals("-a")) { // device address + String a = args[i + 1]; + i++; + address = Integer.parseInt(a.substring(2), 16); + } else if (o.contentEquals("-sv")) { + String a = args[i + 1]; + vrefBit = Integer.parseInt(a); + setVrefBit = true; + if ( (gainBit <0) || (gainBit > 1) ){ + console.println("vrefBit must be in range 0..1"); + System.exit(55); + } + i++; + onlyOne ++; + } else if (o.contentEquals("-sg")) { + String a = args[i + 1]; + gainBit = Integer.parseInt(a); + setGainBit = true; + if ( (gainBit <0) || (gainBit > 1) ){ + console.println("gainBit must be in range 0..1"); + System.exit(55); + } + i++; + onlyOne ++; + } else if (o.contentEquals("-ch")) { + String a = args[i + 1]; + i++; + if (a.equalsIgnoreCase("A")) { + channel = 0; + } else if (a.equalsIgnoreCase("B")) { + channel = 1; + } else if (a.equalsIgnoreCase("C")) { + channel = 2; + } else if (a.equalsIgnoreCase("D")) { + channel = 3; + } else { + console.println(" -ch invalid "); + System.exit(53); + } + } else if (o.contentEquals("-vdd")) { // reference voltage + String a = args[i + 1]; + i++; + vref = Float.parseFloat(a); + } else if (o.contentEquals("-r")) { + doReset = true; + }else if (o.contentEquals("-pv")) { + String a = args[i + 1]; + persistValues = Boolean.parseBoolean(a); + i++; + } else if (o.contentEquals("-d")) { + String a = args[i + 1]; + i++; + registerData = Integer.parseInt(a); + setOutputDigital = true; + if (registerData < 0 || registerData > 4095) { + console.println("-rde must be in range 0..4095"); + System.exit(36); + } + onlyOne ++; + } else if (o.contentEquals("-h")) { + console.println(helpString); + System.exit(39); + } else if (o.contentEquals("-v")) { // eeprom volts + String a = args[i + 1]; + i++; + setOutputVotage = true; + volts = Float.parseFloat(a); + onlyOne ++; + } else { + console.println(" !!! Invalid Parm " + o); + console.println(helpString); + System.exit(42); + } + } + + if (onlyOne > 1 ) { + console.println(" mutually exclusive parms used."); + console.println(helpString); + System.exit(43); + } + + if ( (vref == 0) || (vref < 0) ){ + console.println("-vdd is zero or less than zero"); + System.exit(50); + + } + if ( (volts > vref) || (volts < 0) ) { + console.println("-v greater than -vdd, or less than zero"); + System.exit(51); + + } + + if ( (volts > vref) || (volts < 0) ) { + console.println("-ef greater than -vdd, or less than zero"); + System.exit(52); + + } + + Mcp4728Driver dacChip ; + I2C genCallDevice = null ; + + + I2C i2cDev = createI2cDevice("MCP4728", busNum, address, I2CImplementation.DIRECT, pi4j) ; + dacChip = new Mcp4728Driver( i2cDev, vref); + + + if (doReset) { + genCallDevice = createI2cDevice("GenCallReset", busNum,0x00, I2CImplementation.SMBUS, pi4j); + dacChip.resetChip(genCallDevice); + } + + logger.info("\nBefore state : \n" + dacChip); + + dacChip.setEepromEnabled(persistValues); + + if (setGainBit) { + dacChip.setGain(channel,gainBit); + } + + if (setVrefBit) { + dacChip.setVref(channel,vrefBit); + } + + if (setOutputDigital) { + dacChip.setDigitalValue(channel, registerData); + } + + if (setOutputVotage) { + dacChip.setVoltage(channel, volts); + } + + + + logger.info("\nCompletion state : \n" + dacChip); + + + if (genCallDevice != null) { + genCallDevice.close(); + } + if (i2cDev != null) { + i2cDev.close(); + } + + + } + + + + /** + * + * @param chipType String Part numer + * @param bus INT + * @param address INT + * @param pi4j Context + * @return I2C device + */ + static I2C createI2cDevice( String chipType, int bus, int address, I2CImplementation impType, Context pi4j) { + String id = String.format("0X%02x: ", bus); + String name = String.format("0X%02x: ", address); + var i2cDeviceConfig = I2C.newConfigBuilder(pi4j) + .bus(bus) + .device(address) + .id(chipType + id + " " + name) + .name(name) + .i2cImplementation(impType) + .build(); + + return pi4j.create(i2cDeviceConfig); + + + } + +} + diff --git a/src/main/java/com/pi4j/devices/mcp4728/MCP4728_Declares.java b/src/main/java/com/pi4j/devices/mcp4728/MCP4728_Declares.java new file mode 100644 index 0000000..3a89744 --- /dev/null +++ b/src/main/java/com/pi4j/devices/mcp4728/MCP4728_Declares.java @@ -0,0 +1,69 @@ + + +package com.pi4j.devices.mcp4728; + + + +public class MCP4728_Declares { + public static final int _MCP4728_DEFAULT_ADDRESS = 0x60; + + public static final int _MCP4728_SET_EEPROM_SIZE = 0x06; + public static final int _MCP4728_SET_FAST_SIZE = 0x04; + public static final int _MCP4728_CHIP_READ_SIZE = 0x05; + + // Table 5.1 + public static final byte _MCP4728_WRITE_CMD_FAST = 0b00000000; + public static final byte _MCP4728_WRITE_CMD_MULTI_DAC_EEPROM = 0b01000000; + public static final byte _MCP4728_WRITE_CMD_SNGL_DAC_EEPROM = 0b01011000; + + public static final byte _MCP4728_WRITE_CMD_I2C_ADDR = 0b01100000; + + + public static final byte _MCP4728_WRITE_CMD_SEQ_DAC_EEPROM = 0b01010000; + + public static final byte _MCP4728_WRITE_CMD_VREF_SLCT_DAC = (byte) 0b10000000; + public static final byte _MCP4728_WRITE_CMD_GAIN_SLCT_DAC = (byte) 0b11000000; + public static final byte _MCP4728_WRITE_CMD_PD_BITS_DAC = (byte) 0b10100000; + + + + + + + + public static final byte _MCP4728_PD_MODE_NORMAL = 0b00000000; + public static final byte _MCP4728_PD_MODE_INOPT = 0b00000110; + + public static final byte _MCP4728_DAC_PD0_MODE_MASK = 0b00000010; + public static final byte _MCP4728_DAC_PD1_MODE_MASK = 0b00000100; + + public static final int _MCP4728_EEPROM_PD1_MODE_MASK = 0b10000000; + public static final byte _MCP4728_EEPROM_PD0_MODE_MASK = 0b01000000; + + // Indicate EEPROM write/update status + public static final byte _MCP4728_READ_CMD_RDY_BSY_MSK = (byte) 0b10000000; + public static final byte _MCP4728_READ_CMD_IS_COMPLT = (byte) 0b10000000; + public static final byte _MCP4728_READ_CMD_IS_NOT_COMPLT = (byte) 0b00000000; + + // POR device load EEPROM + public static final byte _MCP4728_GEN_CALL_RESET_CMD = (byte) 0b00000110; + + // POR device PD1 and PD0 set to 0 for normal operation + public static final byte _MCP4728_GEN_CALL_WAKEUP_CMD = (byte) 0b00001001; + + + // Write + // C2=0 C1=1 C0=0 DAC only + // C2=0 C1=1 C0=1 DAC and EEPROM + // byte1 byte2 byte3 byte4 + // address r/w C2 C1 C0 x x PD1 PD0 X D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 X X X X + + // Read DAC + // byte1 byte2 byte3 byte4 + // address r/w RDY/BSY C1 C0 x x PD1 PD0 X D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 X X X X + + // EEPROM + // byte5 byte6 + // X PD1 PD0 X D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 + +} diff --git a/src/main/java/com/pi4j/devices/mcp4728/README.md b/src/main/java/com/pi4j/devices/mcp4728/README.md new file mode 100644 index 0000000..7222386 --- /dev/null +++ b/src/main/java/com/pi4j/devices/mcp4728/README.md @@ -0,0 +1,146 @@ +### This repository contains device specific support for various devices. + +### This is an example implementation and will need to be adjusted to fit your needs. + +Project by Tom Aarts +========================================================================== + + Adafruit MCP4728 + +Pi 3.3V Vin +Pi Grnd GND +Pi Pin5 SCL +Pi Pin3 SDA +Pi Grnd LDAC + Ch B MCP3008 ch6 + Ch D MCP3008 ch1 + + + +1. ./mvnw clean package +2. cd target/distribution +3. sudo ./runMcp4728.sh parms........ + +-b 0x? hex value bus +-a 0x?? hex value address +-vdd float reference voltage +-pv true/false. Persist values, default false. +-r reset chip +All following parms effect channel -ch +-ch channel A B C D +-d digital input 0 - 4095 +-v voltage 0 - vdd +-sv vref 0 or 1 +-sg gain 0 or 1" + + +Note -v -d -sv -sg mutually exclusive + +String helpString = " parms: -b 0x? hex value bus -a 0x?? hex value address \n " + +" -r reset chip -ch channel A B C D -d digital value (0 ... 4095) \n" + +" -v voltage (0 ... vdd) -pv persist values true/false -vdd decimal reference voltage \n" + +" -sv vrefBit 0 or 1 -sg gainBit 0 or 1 \n" + +"-d -v -sv -sg mutually exclusive \n" ; + + +https://datasheetspdf.com/pdf-file/634126/MicrochipTechnology/MCP4728/1 + +-rde anf -rdf both have max input value 0x0FFF + +-ef and -ev cannot exceed -vdd + + + + Read output votage on channel 1 of MCP3008 +sudo ./runMcp4728.sh -b 0x01 -a 0x60 -rde 2047 -vdd 3.3 -ch D + + + All remainig examples read output votage on channel 6 of MCP3008 + + +Assume starting with channel B vrefBit 1 use 2.048V, gainBit 0 1x +Updates using raw binary value +To Update DAC and EEPROM, set to 50 % of reference voltage Vin of 3.3 volts + +vrefBit 1 by default vout = 1.0v +sudo ./runMcp4728.sh -b 0x01 -a 0x60 -d 2047 -vdd 3.3 -ch B + +vrefBit 1 by default vout = 1.0v persist values +sudo ./runMcp4728.sh -b 0x01 -a 0x60 -d 2047 -vdd 3.3 -ch B -pv true + + +set vrefBit 0, uses vdd +sudo ./runMcp4728.sh -b 0x01 -a 0x60 -sv 0 -vdd 3.3 -ch B -pv true + +vrefBit 0 persisted previous test vout = 1.6 v +sudo ./runMcp4728.sh -b 0x01 -a 0x60 -d 2047 -vdd 3.3 -ch B + + +set vrefBit 1, uses 2.048 +sudo ./runMcp4728.sh -b 0x01 -a 0x60 -sv 1 -vdd 3.3 -ch B -pv true + +set gainBit 1, set gain to 2x +sudo ./runMcp4728.sh -b 0x01 -a 0x60 -sg 1 -vdd 3.3 -ch B -pv true + +To Update DAC Fast, set to 50 % of reference voltage with 2x gain +50% 2.048 = 1.0v 2x 1.0 = 2.0v +vrefBit 0, gain 2x vout 2.0v +sudo ./runMcp4728.sh -b 0x01 -a 0x60 -d 2047 -vdd 3.3 -ch B + + +set vrefBit 0, uses vdd +sudo ./runMcp4728.sh -b 0x01 -a 0x60 -sv 0 -vdd 3.3 -ch B -pv true + +To Update DAC Fast, set to 100 % of reference voltage +vrefBit 0, vout 3.2v 2x gain not used when vrefBit 0 (use vdd) +sudo ./runMcp4728.sh -b 0x01 -a 0x60 -d 4095 -r -vdd 3.3 -ch B + +Updates using voltage value + +To Update DAC Fast, set to 1.8v vout 1.8v +sudo ./runMcp4728.sh -b 0x01 -a 0x60 -v 1.8 -vdd 3.3 -ch B + +To Update DAC and EEPROM, set to 3.3v +sudo ./runMcp4728.sh -b 0x01 -a 0x60 -v 3.3 -vdd 3.3 -ch B -pv true + +To set vrefBit 1 +sudo ./runMcp4728.sh -b 0x01 -a 0x60 -sv 1 -vdd 3.3 -ch B -pv true + +To Update DAC and EEPROM, set to 3.3v ???? +sudo ./runMcp4728.sh -b 0x01 -a 0x60 -v 3.3 -vdd 3.3 -ch B -pv true + + +To Update DAC Fast, reset chip, set to 3.1 +sudo ./runMcp4728.sh -b 0x01 -a 0x60 -v 3.1 -r -vdd 3.3 -ch B + + + + + +UNIT TEST +Validate dumps registers show bits updated correctly + +To Update vref bit channel 1 of MCP3008 +sudo ./runMcp4728.sh -b 0x01 -a 0x60 -sv 0 -vdd 3.3 -ch D + +To Update gain bit channel 1 of MCP3008 +sudo ./runMcp4728.sh -b 0x01 -a 0x60 -sg 1 -vdd 3.3 -ch D + + + + + + +vref 0 = vdd + 1 = 2.048 + +gain 0 = 1 + 1 = 2x + + + +Test program. Loop indefinitely executing the shell command + +./runLoop.sh program.sh args ...... + +Example usage: +./runLoop.sh ./runMcp3008.sh -vref 3.3 -p 0x0? \ No newline at end of file