Here's what I made a few years ago, I never did build the H-bridge part yet but the arduino worked.
This uses a rotary encoder and a 16x2 lcd with IIC to parallel converter, temp sensor, current sensor and a few voltage dividers for protection.
Maybe you can use some of the code out of this, it does go 20hz-400hz and a duty cycle of 10%-90%
/* ARDUINO NANO
* ------|USB|------
* <|FAN |___| D12|>
* <| D11|>
* <| PWM2|>
* <|IWI1 PWM1|>
* <|IWI2 D8|>
* <|THS1 D7|>
* <|THS2 D6|>
* <|IIC SDA D5|>
* <|IIC SCL ENS|>
* <|VWI ENB|>
* <|GDV ENA|>
* <|5V GND|>
* <| |>
* <|GND RX |>
* <|VIN TX |>
* -----------------
*/
//Libraries
#include <Wire.h>
#include <EEPROM.h>
#include <TimerOne.h>
#include <LiquidCrystal_I2C.h>
// ADC
#define VREF 5.003 // Measued value of refference voltage
#define ADC_V VREF/1024.0
// Temperature Sensor:
// MCP9701A Tc=19.5mV/°C, V0°C=400mV
// (VADC - V0°C)/Tc = Ta
// Ex. (1.5V - 0.4V)/0.0195V/°C = 56°C
#define Tc 1/0.0195 // Reciprocal to use quicker multiplication, Temperature coefficient from datasheet of sensor
#define V0C 0.4 // Voltage at 0°C from datasheet of sensor
// Current Sensor:
// ACS770-ECB-200U Ic=20mV/A Vidle=0.5V
// (VADC - Vidle)/Ic = Iweld
// Ex. (1.5V - 0.5V)/0.02V/A = 50A Welder output current
#define Ic 1/0.02 // Reciprocal to use quicker multiplication, from datasheet of sensor
#define Vidle 0.5 // Idle Output Voltage from datasheet of sensor
// Resistor divider network for welder voltage
// Vmax = Rtop/Rbottom*Vref
// Vweld = Rtop/Rbottom * ADCV
// Ex. 247K/10K*1.5V = 37V Welder output voltage
#define WRtop 247.0 // 247K resistor
#define WRbottom 10.0 // 10K resistor
// Resistor divider network for Gate Driver voltage
// Vmax = Rtop/Rbottom*Vref
// VGD = Rtop/Rbottom * ADCV
// Ex. 40K/10K*3.75V = 15V Gate Drive voltage
#define GDRtop 40.0 // 40K resistor
#define GDRbottom 10.0 // 10K resistor
// Input Declarations
#define pinA 2 // Encoder phase A on hardware digital interrupt pin 2
#define pinB 3 // Encoder phase B on hardware digital interrupt pin 3
#define BUTTON 4 // Encoder Button on digital pin 4
#define FAN 13 // Fan ON/OFF on digital pin 13
#define Weld_Current_1 A0 // Welder input current 1 on analog pin 0
#define Weld_Current_2 A1 // Welder input current 2 on analog pin 1
#define Temp_HS_1 A2 // Temperature of heatsink 1 on analog pin 2
#define Temp_HS_2 A3 // Temperature of heatsink 2 on analog pin 3
#define Weld_Volts A6 // Welder input voltage on analog pin 6
#define GD_Volts A7 // Gate Driver voltage on anlog pin 7
// Variables
volatile byte aFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile byte reading = 0; // somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent
volatile int frequency;
volatile int oldfrequency = 0;
volatile byte ac_balance;
volatile byte oldac_balance = 0;
volatile byte button = 0;
volatile byte oldbutton = 0;
unsigned int period = 0;
unsigned int dutycycle = 0;
int weld_current_1 = 0;
int weld_current_2 = 0;
int weld_current_total = 0;
int temp_hs_1 = 0;
int temp_hs_2 = 0;
int Ta = 0;
int weld_volts = 0;
float gd_volts = 0;
byte menu = 0;
unsigned long last_time = millis();
unsigned long old_time = 0;
unsigned long fan_time = 0;
byte run_once = 0;
// Set the LCD I2C address and pinout
// address,EN,RW,RS,D4,D5,D6,D6,BL,Polarity
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
//Custom °C symbol
byte degtempC[8] = {
B11000, // HH...
B11000, // HH...
B00111, // ..HHH
B01000, // .H...
B01000, // .H...
B01000, // .H...
B00111, // ..HHH
B00000, // .....
};
void PinA() {
cli(); //stop interrupts happening before we read pin values
reading = PIND & 0xC; // read all eight pin values then strip away all but pinA and pinB's values
if (reading == B00001100 && aFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
if (button == 1 && frequency > 20)frequency --; // If frequency is selected in menu and frequency is above 20Hz decrement frequency
if (button == 2 && ac_balance > 10)ac_balance --; // if AC Balance is selected in menu and AC Balance is above 10% decrement AC Balance
bFlag = 0; //reset flags for the next turn
aFlag = 0; //reset flags for the next turn
}
else if (reading == B00000100) bFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation
sei(); //restart interrupts
}
void PinB() {
cli(); //stop interrupts happening before we read pin values
reading = PIND & 0xC; //read all eight pin values then strip away all but pinA and pinB's values
if (reading == B00001100 && bFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
if (button == 1 && frequency < 400)frequency ++; // If frequency is selected in menu and frequency is below 400Hz increment frequency
if (button == 2 && ac_balance < 90)ac_balance ++; // if AC Balance is selected in menu and AC Balance is below 90% increment AC Balance
bFlag = 0; //reset flags for the next turn
aFlag = 0; //reset flags for the next turn
}
else if (reading == B00001000) aFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation
sei(); //restart interrupts
}
void Button(){
if (digitalRead(BUTTON) == LOW && oldbutton == 1) { // Make sure the button is only read once when its low (button debounce)
if (button >= 2){
button = 0; // 3 level menu loop, level 0 is main, level 1 is Frequancy select, level 2 is AC Balance select
old_time = millis();
}
else button ++;
if (button == 1) { // Frequency has been selected
lcd.setCursor(15, 0); // Goto the end of the line
lcd.write(0x7F); // Put an arrow to signify that Frequency is selected
}
else {
EEPROM.put(0, frequency); // Save the new Frequency to eeprom only if its different
lcd.setCursor(15, 0); // Goto the end of the line
lcd.print(" "); // Clear the arrow to signify that Frequency is unselected
}
if (button == 2) { // AC Balance has been selected
lcd.setCursor(15, 1); // Goto the end of the line
lcd.write(0x7F); // Put an arrow to signify that AC Balance is selected
}
else {
EEPROM.update(2, ac_balance); // Save the new AC Balance to eeprom only if its different
lcd.setCursor(15, 1); // Goto the end of the line
lcd.print(" "); // lear the arrow to signify that AC Balance is unselected
}
oldbutton = 0; // Reset so we can watch for another button press
}
if (digitalRead(BUTTON) == HIGH) oldbutton = 1; // Reset the button so it can be read again
}
void Frequency(){
if (oldfrequency != frequency) { // See if anything changed from last time
period = 1000000/frequency; // Convert frequency to micro seconds for timer 1 to use
Timer1.setPeriod(period); // Update timer 1's period for realtime updating
Timer1.pwm(9, dutycycle); // Apply duty cycle for realtime updating
Timer1.pwm(10, dutycycle); // Apply duty cycle for realtime updating
oldfrequency = frequency; // Update old value to new value
}
}
void AC_Balance(){
if (oldac_balance != ac_balance) { // See if anything changed from last time
dutycycle = map(ac_balance, 0, 100, 0, 1023); // Convert and scale from human readable to 10 bit number
dutycycle += 1; // Calibration
dutycycle = constrain(dutycycle, 102, 922); // Limit duty cycle to 10% - 90%
Timer1.pwm(9, dutycycle); // Apply new duty cycle for realtime updating
Timer1.pwm(10, dutycycle); // Apply new duty cycle for realtime updating
oldac_balance = ac_balance; // Update old value to new value
}
}
void Current(){ // Parallel connection of current transducers, Reading, Converting, Summing.
weld_current_1 = ((analogRead(Weld_Current_1) + 1) * ADC_V - Vidle) * Ic; // Current transducer 1 transfer function to Amps
weld_current_2 = ((analogRead(Weld_Current_2) + 1) * ADC_V - Vidle) * Ic; // Current transducer 2 transfer function to Amps
weld_current_total = weld_current_1 + weld_current_2; // Add parallel currents together
weld_current_total = constrain(weld_current_total, 0, 400); // Keep readings within sensors readable range
}
void Temperature(){
temp_hs_1 = ((analogRead(Temp_HS_1) + 1) * ADC_V - V0C) * Tc; // Heatsink 1 temperature sensor Transfer function to °C
temp_hs_2 = ((analogRead(Temp_HS_2) + 1) * ADC_V - V0C) * Tc; // Heatsink 2 temperature sensor Transfer function to °C
if(temp_hs_1 > temp_hs_2)Ta = temp_hs_1; // If temperature of heatsink 1 is greater than 2, than temperature ambient = temperature heatsink 1
else Ta = temp_hs_2; // If temperature of heatsink 2 is greater than 1, than temperature ambient = temperature heatsink 2
Ta = constrain(Ta, -40, 125); // Keep readings within sensors readable range
if(Ta >= 40 || weld_current_total > 20){ // If Heatsink temperatures get above 40°C or weld current is >20amps turn fan on
digitalWrite(FAN, HIGH);
fan_time = millis();
}
else if(Ta < 35 && millis() - fan_time > 5000)digitalWrite(FAN, LOW); // 5°C hysteresis before turning fan off to prevent the fan from oscillating ON/OFF at the set point
}
void Voltage() {
weld_volts = (analogRead(Weld_Volts) + 1)* ADC_V * (WRtop/WRbottom); // Get output voltage from welder
gd_volts = (analogRead(GD_Volts) + 2) * ADC_V * (GDRtop/GDRbottom); // Get Gate Drive power supply voltage
}
void Protection(){
if(gd_volts < 10){ // If Gate Drive Voltage falls below 10V turn off the H-Bridge and notify the operator till resolved
Timer1.pwm(9, 0); // Turn off PWM
Timer1.pwm(10, 0); // Turn off PWM
lcd.clear();
lcd.setCursor(1, 0); // Go to first character on line 1
lcd.print("LOW GATE DRIVE");
lcd.setCursor(1, 1); // Go to first character on line 2
lcd.print("VOLTAGE");
while(gd_volts < 12){ // When Gate Drive Voltage exceeds 12V re-enable H-Bridge
Voltage(); // Make sure to update the voltage so it can be displayed below in realtime
Temperature(); // Make sure to update Temperature so the fan can START/STOP
if(millis() - last_time >= 100){ // Refresh rate (10hz)
lcd.setCursor(9, 1); // Go to first character on line 2
if(gd_volts < 10)lcd.print(" "); // Text alaginment
lcd.print(gd_volts,2); // Display updated Gate Drive Voltage
lcd.print("V");
last_time = millis(); // Part of refresh rate
}
}
lcd.clear();
Timer1.pwm(9, dutycycle); // Return PWM to previous state
Timer1.pwm(10, dutycycle); // Return PWM to previous state
}
if(Ta >= 90){ // If Heatsinks get above 90°C turn off the H-Bridge and notify the operator till resolved
Timer1.pwm(9, 0); // Turn off PWM
Timer1.pwm(10, 0); // Turn off PWM
lcd.clear();
lcd.setCursor(0, 0); // Go to first character on line 1
lcd.print("OVER TEMPERATURE");
while(Ta > 80){ // When Heatsinks drop below 80°C re-enable H-Bridge
Temperature(); // Make sure to update Temperature so it can be displayed below and so the fan can START/STOP
if(millis() - last_time >= 100){ // Refresh rate (10hz)
lcd.setCursor(6, 1); // Go to first character on line 2
if(Ta < 100)lcd.print(" ");
lcd.print(Ta); // Display updated Heatsink Temperature
lcd.write(byte(0)); // Write °C
last_time = millis(); // Part of refresh rate
}
}
lcd.clear();
Timer1.pwm(9, dutycycle); // Return PWM to previous state
Timer1.pwm(10, dutycycle); // Return PWM to previous state
}
if(weld_current_total > 210){ // If Welder output current is set too high turn off the H-Bridge and notify the operator to reduce the output current
Timer1.pwm(9, 0); // Turn off PWM
Timer1.pwm(10, 0); // Turn off PWM
lcd.clear();
lcd.setCursor(2, 0); // Go to first character on line 1
lcd.print("OVER CURRENT");
lcd.setCursor(4, 1); // Go to first character on line 2
lcd.print("MAX 200A");
oldbutton = 0; // Required to prevent menu selection while acknoledging over current message
while(digitalRead(BUTTON) == HIGH){ // Wait till the operator acknoledges the over current condition and presses the button to re-enable H-Bridge
Temperature(); // Make sure to update Temperature so the fan can START/STOP
}
lcd.clear();
Timer1.pwm(9, dutycycle); // Return PWM to previous state
Timer1.pwm(10, dutycycle); // Return PWM to previous state
}
} // End of Protection
void setup() {
pinMode(pinA, INPUT_PULLUP); // set pinA as an input with week pull up
pinMode(pinB, INPUT_PULLUP); // set pinB as an input with weak pull up
pinMode(BUTTON, INPUT_PULLUP); // set button as an input with weak pull up
pinMode(10, OUTPUT); // set pin 10 as output for pwm
pinMode(FAN, OUTPUT); // set pin 11 as output for fan
attachInterrupt(0, PinA, RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below)
attachInterrupt(1, PinB, RISING); // set an interrupt on PinB, looking for a rising edge signal and executing the "PinB" Interrupt Service Routine (below)
EEPROM.get(0, frequency); // Get last Frequancy value used from eeprom (two bytes)
period = 1000000/frequency; // convert frequency from Hz to microseconds
Timer1.initialize(period); // initialize timer 1 with the frequency we got from eeprom
TCCR1A |= 0x30; // enable pin 10 inverted pwm output
ac_balance = EEPROM.read(2); // Read last AC balance value used from eeprom (single byte)
dutycycle += 1; // Calibration
dutycycle = constrain(dutycycle, 102, 922);
//Serial.begin(115200); // start the serial monitor link
lcd.begin(16, 2); // Initiatize the LCD for 16 chars 2 lines
lcd.setBacklight (HIGH); // Turn backlight on
lcd.home(); // Go to first character on line 1 (0,0)
lcd.createChar(0, degtempC); // Load custom °C character into LCD
if(digitalRead(BUTTON) == HIGH){
menu = 1; // Normal operating mode
run_once = 1;
}
else{
menu = 0; // Diagnositics operating mode
lcd.setCursor(2, 0); // Go to first character on line 2
lcd.print("DIAGNOSITICS");
lcd.setCursor(6, 1); // Go to first character on line 2
lcd.print("MODE");
delay(2000);
lcd.clear();
Timer1.setPeriod(period);
Timer1.pwm(9, dutycycle);
Timer1.pwm(10, dutycycle);
}
}
void loop() {
Temperature();
Current();
Voltage();
Protection();
if(menu == 0 && millis() - last_time >= 100){ // Refresh rate (10hz)
lcd.setCursor(0, 0);
if(weld_volts < 10)lcd.print(" ");
else if(weld_volts < 100)lcd.print(" ");
lcd.print(weld_volts);
lcd.print("V ");
lcd.setCursor(5, 0);
if(weld_current_1 < -9);
else if(weld_current_1 < 0)lcd.print(" ");
else if(weld_current_1 < 10)lcd.print(" ");
else if(weld_current_1 < 100)lcd.print(" ");
lcd.print(weld_current_1);
lcd.print("A");
lcd.setCursor(10, 0);
if(weld_current_2 < -9);
else if(weld_current_2 < 0)lcd.print(" ");
else if(weld_current_2 < 10)lcd.print(" ");
else if(weld_current_2 < 100)lcd.print(" ");
lcd.print(weld_current_2);
lcd.print("A");
lcd.setCursor(0, 1);
if(temp_hs_1 < -9);
else if(temp_hs_1 < 0)lcd.print(" ");
else if(temp_hs_1 < 10)lcd.print(" ");
else if(temp_hs_2 < 100)lcd.print(" ");
lcd.print(temp_hs_1);
lcd.write(byte(0)); // Write °C
lcd.setCursor(5, 1);
if(temp_hs_2 < -9);
else if(temp_hs_2 < 0)lcd.print(" ");
else if(temp_hs_2 < 10)lcd.print(" ");
else if(temp_hs_2 < 100)lcd.print(" ");
lcd.print(temp_hs_2);
lcd.write(byte(0)); // Write °C
lcd.setCursor(10, 1);
if(gd_volts < 10)lcd.print(" ");
lcd.print(gd_volts,2);
lcd.print("V");
last_time = millis(); // Part of refresh rate
}
if(millis() - old_time >= 10000 && button == 0 && menu == 1){
if(millis() - last_time >= 100){
lcd.setCursor(0, 0);
if (frequency < 100)lcd.print(" ");
lcd.print(frequency);
lcd.print("Hz ");
lcd.print(ac_balance);
lcd.print("% ");
if(Ta < 0)lcd.print(" ");
else if(Ta < 10)lcd.print(" ");
else if(Ta < 100)lcd.print(" ");
lcd.print(Ta);
lcd.write(byte(0)); // Write °C
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("WELDER ");
if(weld_volts < 10)lcd.print(" ");
else if(weld_volts < 100)lcd.print(" ");
lcd.print(weld_volts);
lcd.print("V ");
lcd.setCursor(12, 1);
if(weld_current_total < 10)lcd.print(" ");
else if(weld_current_total < 100)lcd.print(" ");
lcd.print(weld_current_total);
lcd.print("A");
}
}
if(menu == 1 || run_once == 1){
Button();
Frequency();
AC_Balance();
if(millis() - last_time >= 100 && button >= 1 || run_once == 1){ // Refresh rate (10hz)
lcd.setCursor(0, 0);
lcd.print("FREQUENCY "); // Write static characters to the display
lcd.setCursor(0, 1); // Go to first character on line 2
lcd.print("AC BALANCE "); // Write static characters to the display
lcd.setCursor(10, 0);
if (frequency < 100)lcd.print(" ");
lcd.print(frequency);
lcd.print("Hz");
lcd.setCursor(11, 1);
lcd.print(ac_balance);
lcd.print("% ");
last_time = millis(); // Part of refresh rate
run_once = 0;
}
}
}