GP8413 Arduino ESP32 NO Library

by AfricanCNC in Circuits > Arduino

11 Views, 0 Favorites, 0 Comments

GP8413 Arduino ESP32 NO Library

Screenshot 2025-06-10 at 16.33.14.png

Here is an English translation of the DataSheet!

black_on_trans.png

Comparison and Improvement of GP8413 DAC Implementation VS DGRobot Library.


This implementation demonstrates a good understanding of the GP8413 DAC control; however, let's compare my code with the DFRobot library approach and highlight the improvements that enhance reliability and improve generic I2C bus sharing.


Key Differences

Bitbanging vs Hardware I2C:

My code uses the hardware I2C (Wire library)

DFRobot library uses bitbanging (software I2C)


Register Handling:

My Code is directly written to registers

DFRobot library abstracts this with higher-level functions


Error Handling:

DFRobot adds robust error checking


Improved Implementation

My code is an enhanced version that maintains hardware I2C but adds better bus sharing capabilities:

cpp

#include <Wire.h>


#define GP8413_ADDRESS 0x59

//#define SDA_PIN 22 // if reversed uncomment

//#define SCL_PIN 21 // if reversed uncomment


// Register definitions

#define GP8413_REG_CONFIG 0x01

#define GP8413_REG_CH0 0x02

#define GP8413_REG_CH1 0x04

#define GP8413_REG_STORE 0x06


// Configuration values

#define OUTPUT_RANGE_5V 0x00

#define OUTPUT_RANGE_10V 0x01


class GP8413 {

public:

GP8413(uint8_t address) : _address(address) {}


bool begin() {

Wire.begin(SDA_PIN, SCL_PIN); // known I2C bus handling now we can share the bus with this DAC

Wire.setClock(400000); // 400kHz // Max speed being able to make an 1kHz PWM


// Verify device presence

if (!isConnected()) {

return false;

}


// Set default range (0-5V)

return setOutputRange(OUTPUT_RANGE_5V);

}


bool isConnected() {

Wire.beginTransmission(_address);

return (Wire.endTransmission() == 0);

}


bool setOutputRange(uint8_t range) {

if (range > OUTPUT_RANGE_10V) return false;


Wire.beginTransmission(_address);

Wire.write(GP8413_REG_CONFIG);

Wire.write(range);

return (Wire.endTransmission() == 0);

}


bool setVoltage(uint16_t millivolts, uint8_t channel) {

if (channel > 1) return false;


// Convert mV to DAC value (0-5V range)

uint16_t dacValue = map(millivolts, 0, 5000, 0, 32767);

return setDACValue(dacValue, channel);

}


bool setDACValue(uint16_t value, uint8_t channel) {

if (channel > 1) return false;

value = constrain(value, 0, 32767);


uint8_t reg = (channel == 0) ? GP8413_REG_CH0 : GP8413_REG_CH1;

uint8_t lsb = value & 0xFF;

uint8_t msb = (value >> 8) & 0xFF;


Wire.beginTransmission(_address);

Wire.write(reg);

Wire.write(lsb);

Wire.write(msb);

return (Wire.endTransmission() == 0);

}


bool storeSettings() {

Wire.beginTransmission(_address);

Wire.write(GP8413_REG_STORE);

Wire.write(0x01); // Store command

return (Wire.endTransmission() == 0);

}


private:

uint8_t _address;

};


GP8413 dac(GP8413_ADDRESS);


void setup() {

Serial.begin(115200);

delay(1000);


Serial.println("Initializing GP8413 DAC...");


if (!dac.begin()) {

Serial.println("Failed to initialize DAC!");

while(1);

}


Serial.println("DAC initialized successfully");


// Test sequence

testDAC();

}


void testDAC() {

Serial.println("Starting test sequence...");


// Ramp up channel 0

for (uint16_t mv = 0; mv <= 5000; mv += 100) {

dac.setVoltage(mv, 0);

Serial.printf("Channel 0: %dmV\n", mv);

delay(50);

}


// Ramp up channel 1

for (uint16_t mv = 0; mv <= 5000; mv += 100) {

dac.setVoltage(mv, 1);

Serial.printf("Channel 1: %dmV\n", mv);

delay(50);

}


// Store current settings (0V on both channels)

dac.setVoltage(0, 0);

dac.setVoltage(0, 1);

dac.storeSettings();

Serial.println("Stored zero settings in DAC memory");

}


void loop() {

// Generate PWM-like output

static bool state = false;

state = !state;ยง


if (state) {

dac.setVoltage(5000, 0); // 5V

dac.setVoltage(2500, 1); // 2.5V

} else {

dac.setVoltage(0, 0);

dac.setVoltage(0, 1);

}


delay(10); // 0.5Hz PWM

}


Key Improvements:


Proper Class Implementation:

Encapsulates all DAC functionality in a clean class

Better separation of concerns

Enhanced Error Handling:

Methods return success/failure status

Input validation

Voltage-Level Interface:

Works in millivolts for easier use

Handles conversion to DAC values internally

Bus Sharing Friendly:

Uses the standard Wire library

Proper beginning/end transactions

Doesn't hold the bus unnecessarily

Non-Blocking:

No long delays in critical sections

Suitable for multi-device I2C buses

Configuration Storage:

Proper implementation of the store function

Recommendations for I2C Bus Sharing

Add Bus Recovery:

cpp

void recoverI2CBus() {

Wire.end();

pinMode(SDA_PIN, INPUT);

pinMode(SCL_PIN, INPUT);

// Clock out any stuck devices
for (int i = 0; i < 10; i++) {
digitalWrite(SCL_PIN, LOW);
delayMicroseconds(5);
digitalWrite(SCL_PIN, HIGH);
delayMicroseconds(5);
}

Wire.begin(SDA_PIN, SCL_PIN);

}

Add Transaction Wrapper:

cpp

bool safeI2CTransaction(void (*transaction)(), uint8_t retries = 3) {

for (uint8_t i = 0; i < retries; i++) {

transaction();

if (Wire.endTransmission() == 0) {

return true;

}

delay(1);

}

recoverI2CBus();

return false;

}

This implementation provides the reliability of the DFRobot library while maintaining direct control over the I2C bus, which is crucial when sharing the bus with other devices, such as the INA228 and EEPROM or any other generic I2C device.