Please ignore most of the ADC ISR code. It is terrible coding practice and should never be done the way I did it.
#include <Arduino.h>
#include <SPI.h>
const float Current_Command_Array[] = {0};
#define Time_Scale 5 //Seconds per element in Current_Command_Array
#define DEBUG false //DEBUG MODE
#define IGBT_PWM_FREQ 200000 //Frequency of the IGBT PWM signal
#define MCLK_INTERNAL true //true for internal clock, false for external
#define MCLK_FREQ 20000000 //disabled if MCLK_INTERNAL is true
#define OSR_SETTING OSR_128 //oversampling ratio
#define P 0.5 //Proportional Gain
#define I 0.1 //Integral Gain
#define Ramp_Rate 2000 //Amps per second
#define Shunt_R 0.0005 //Shunt Resistor Value
#define Current_Limit 200 //Amps
#define Battery_Voltage_Limit 2.90 //Volts
#define IGBT_Temp_Limit 100 //Celsius
#define Battery_Temp_Limit 60 //Celsius
//Calculated Values and variables
constexpr uint32_t IGBT_PWM_RES = 12;
constexpr uint32_t IGBT_PWM_MAX = (1 << IGBT_PWM_RES) - 1;
constexpr double Current_Sense_Gain = 1/(Shunt_R*20);
constexpr double Ramp_Rate_Per_Millisecond = (double)Ramp_Rate/1000;
//Unchanging Definitions, Objects, and Variables
enum OSR : uint8_t {
OSR_32 = 0x00,
OSR_64 = 0x01,
OSR_128 = 0x02,
OSR_256 = 0x03,
OSR_512 = 0x04,
OSR_1024 = 0x05,
OSR_2048 = 0x06,
OSR_4096 = 0x07,
OSR_8192 = 0x08,
OSR_16384 = 0x09,
OSR_20480 = 0x0A,
OSR_24576 = 0x0B,
OSR_40960 = 0x0C,
OSR_49152 = 0x0D,
OSR_81920 = 0x0E,
OSR_98304 = 0x0F
};
enum Channel : uint8_t {
SE_0 = 0x00,
SE_1 = 0x01,
SE_2 = 0x02,
SE_3 = 0x03,
SE_4 = 0x04,
SE_5 = 0x05,
SE_6 = 0x06,
SE_7 = 0x07,
DIFF_A = 0x08,
DIFF_B = 0x09,
DIFF_C = 0x0A,
DIFF_D = 0x0B,
TEMP = 0x0C,
AVDD = 0x0D,
VCM = 0x0E,
OFFSET = 0x0F
};
constexpr uint32_t SPI_Speed = 20000000;
constexpr uint8_t ADDR = 0b01000000;
constexpr uint8_t FAST_CMD = 0b00000000;
constexpr uint8_t CONV = 0b00101000;
constexpr uint8_t STBY = 0b00101100;
constexpr uint8_t RESET = 0b00111000;
constexpr uint8_t INC_WRITE = 0b00000010;
constexpr uint8_t CONFIG0_ADDR = 0b00000100;
constexpr uint8_t CONFIG1_ADDR = 0b00001000;
constexpr uint8_t CONFIG2_ADDR = 0b00001100;
constexpr uint8_t CONFIG3_ADDR = 0b00010000;
constexpr uint8_t IRQ_ADDR = 0b00010100;
constexpr uint8_t MUX_ADDR = 0b00011000;
constexpr uint8_t SCAN_ADDR = 0b00011100;
constexpr uint8_t TIMER_ADDR = 0b00100000;
constexpr uint8_t OFFSETCAL_ADDR= 0b00100100;
constexpr uint8_t GAINCAL_ADDR = 0b00101000;
constexpr uint8_t LOCK_ADDR = 0b00110100;
constexpr uint8_t READ = 0b00000001;
constexpr uint8_t INC_READ = 0b00000011;
constexpr uint8_t ADC_REG = 0b00000000;
constexpr uint8_t CONFIG0 = 0b11010010 | (MCLK_INTERNAL <<5);
constexpr uint8_t CONFIG1 = 0b00000000 | (OSR_SETTING <<2);
constexpr uint8_t CONFIG2 = 0b10001011; //Contains Gain
constexpr uint8_t CONFIG3 = 0b11110000; //Contains Mode
constexpr uint8_t IRQ = 0b01110110; //contains IRQ stuff
constexpr uint8_t MUX = 0b01111000; //contains channel selection
constexpr uint32_t SCAN = 0b010000000000000000001111; //contains scan stuff 24 bits
constexpr uint32_t TIMER = 0; //contains timer stuff 24 bits
constexpr uint8_t OFFSETCAL = 0; //contains offset stuff
constexpr uint8_t GAINCAL = 0; //contains gain calibration stuff
volatile double Current_Command = 0;
volatile double Integral = 0;
volatile double Battery_Current = 0;
volatile double Battery_Voltage = 0;
volatile double IGBT_Temp = 0;
volatile double Battery_Temp = 0;
volatile uint32_t Time = 0;
volatile double Current_Array[4] = {0};
volatile uint32_t Current_Array_Index = 0;
volatile double Voltage_Array[2] = {0};
volatile uint32_t Voltage_Array_Index = 0;
bool Running = false;
uint32_t Last_Time = 0;
uint32_t i = 0;
double Current_Command_Temp = 0;
uint32_t Time_Delta_OLD = 0;
#define MCLK_PIN 25
#define IGBT_PWM_PIN 36
#define ADC_INT_PIN 41
#define SPI_CS_PIN 10
//Function Prototypes
void ADC_ISR();
void ADC_Init();
//Setup
void setup()
{
delay(1000);
pinMode(IGBT_PWM_PIN, OUTPUT);
analogWriteFrequency(IGBT_PWM_PIN, IGBT_PWM_FREQ);
analogWriteResolution(IGBT_PWM_RES);
analogWrite(IGBT_PWM_PIN, 0);
ADC_Init();
pinMode(ADC_INT_PIN, INPUT);
attachInterrupt(digitalPinToInterrupt(ADC_INT_PIN), ADC_ISR, FALLING);
#if !MCLK_INTERNAL
pinMode(MCLK_PIN, OUTPUT);
analogWriteFrequency(MCLK_PIN, MCLK_FREQ);
analogWrite(MCLK_PIN, IGBT_PWM_MAX/2);
#endif
}
void ADC_Init()
{
SPI.begin();
SPI.usingInterrupt(digitalPinToInterrupt(ADC_INT_PIN));
pinMode(SPI_CS_PIN, OUTPUT);
delay(100);
SPI.beginTransaction(SPISettings(SPI_Speed, MSBFIRST, SPI_MODE0));
digitalWriteFast(SPI_CS_PIN, LOW);
delayNanoseconds(100);
SPI.transfer(ADDR | RESET | FAST_CMD);
digitalWriteFast(SPI_CS_PIN, HIGH);
delayMicroseconds(1);
SPI.endTransaction();
delay(1);
SPI.beginTransaction(SPISettings(SPI_Speed, MSBFIRST, SPI_MODE0));
digitalWriteFast(SPI_CS_PIN, LOW);
delayNanoseconds(500);
SPI.transfer(ADDR | CONFIG0_ADDR | INC_WRITE);
SPI.transfer(CONFIG0);
SPI.transfer(CONFIG1);
SPI.transfer(CONFIG2);
SPI.transfer(CONFIG3);
SPI.transfer(IRQ);
SPI.transfer(MUX);
SPI.transfer(0xFF&(SCAN>>16));
SPI.transfer(0xFF&(SCAN>>8));
SPI.transfer(0xFF&(SCAN));
digitalWriteFast(SPI_CS_PIN, HIGH);
delayMicroseconds(1);
SPI.endTransaction();
delay(1);
}
//Loop
void loop()
{
if(Serial.available())
{
char c = Serial.read();
switch (c)
{
case 'r':
SPI.beginTransaction(SPISettings(SPI_Speed, MSBFIRST, SPI_MODE0));
digitalWriteFast(SPI_CS_PIN, LOW);
delayNanoseconds(500);
SPI.transfer(ADDR | STBY | FAST_CMD);
digitalWriteFast(SPI_CS_PIN, HIGH);
delayMicroseconds(1);
SPI.endTransaction();
Running = false;
c = 0;
Last_Time = micros();
Time = 0;
i=0;
break;
case 's':
SPI.beginTransaction(SPISettings(SPI_Speed, MSBFIRST, SPI_MODE0));
digitalWriteFast(SPI_CS_PIN, LOW);
delayNanoseconds(500);
SPI.transfer(ADDR | CONV | FAST_CMD);
digitalWriteFast(SPI_CS_PIN, HIGH);
delayMicroseconds(1);
SPI.endTransaction();
Running = true;
c = 0;
Last_Time = micros();
Time = 0;
i=0;
break;
default:
break;
}
}
if(Battery_Current > Current_Limit)
{
Running = false;
}
if(Battery_Voltage < Battery_Voltage_Limit)
{
Running = false;
}
if(IGBT_Temp > IGBT_Temp_Limit)
{
Running = false;
}
if(Battery_Temp > Battery_Temp_Limit)
{
Running = false;
}
if(Running == true)
{
if(i >= sizeof(Current_Command_Array)/sizeof(Current_Command_Array[0]))
{
Current_Command = 0;
analogWrite(IGBT_PWM_PIN, 0);
SPI.beginTransaction(SPISettings(SPI_Speed, MSBFIRST, SPI_MODE0));
digitalWriteFast(SPI_CS_PIN, LOW);
delayNanoseconds(500);
SPI.transfer(ADDR | STBY | FAST_CMD);
digitalWriteFast(SPI_CS_PIN, HIGH);
delayMicroseconds(1);
SPI.endTransaction();
Running = false;
i=0;
}
else
{
uint32_t Time_Delta = micros() - Time_Delta_OLD;
i = (Time/Time_Scale)/1000000;
Current_Command_Temp = Current_Command_Temp + Ramp_Rate_Per_Millisecond*Time_Delta/1000.0;
Time_Delta_OLD = micros();
Current_Command_Temp = constrain(Current_Command_Temp, 0, Current_Command_Array[i]);
noInterrupts();
Current_Command =constrain(Current_Command_Temp, 0, (uint32_t)Current_Limit);
interrupts();
}
}
else
{
noInterrupts();
Current_Command = 0;
interrupts();
analogWrite(IGBT_PWM_PIN, 0);
}
noInterrupts();
Time = micros() - Last_Time;
interrupts();
delayMicroseconds(5);
}
void ADC_ISR()
{
SPI.beginTransaction(SPISettings(SPI_Speed, MSBFIRST, SPI_MODE0));
digitalWriteFast(SPI_CS_PIN, LOW);
SPI.transfer(ADDR | ADC_REG | READ);
uint32_t ADC_DATA = SPI.transfer32(0);
digitalWriteFast(SPI_CS_PIN, HIGH);
SPI.endTransaction();
double Battery_Current_Temp = 0;
double Battery_Voltage_Temp = 0;
bool Update_Current = false;
switch ((ADC_DATA&0xF0000000)>>28)
{
case SE_0:
Battery_Current_Temp = 0.93*Current_Sense_Gain*3.3*((int32_t)(ADC_DATA & 0x01FFFFFF)<<7)/0x3FFFFFFF;
Current_Array[Current_Array_Index] = Battery_Current_Temp;
Current_Array_Index++;
if(Current_Array_Index >= sizeof(Current_Array)/sizeof(Current_Array[0]))
{
Current_Array_Index = 0;
}
Battery_Current = 0;
for(uint32_t i = 0; i < sizeof(Current_Array)/sizeof(Current_Array[0]); i++)
{
Battery_Current += Current_Array[i];
}
Battery_Current = Battery_Current/(sizeof(Current_Array)/sizeof(Current_Array[0]));
Update_Current = true;
break;
case SE_1:
Battery_Voltage_Temp = 1.0264341429*2*3.3*((int32_t)(ADC_DATA & 0x01FFFFFF)<<7)/0x3FFFFFFF;
Voltage_Array[Voltage_Array_Index] = Battery_Voltage_Temp;
Voltage_Array_Index++;
if(Voltage_Array_Index >= sizeof(Voltage_Array)/sizeof(Voltage_Array[0]))
{
Voltage_Array_Index = 0;
}
Battery_Voltage = 0;
for(uint32_t i = 0; i < sizeof(Voltage_Array)/sizeof(Voltage_Array[0]); i++)
{
Battery_Voltage += Voltage_Array[i];
}
Battery_Voltage = Battery_Voltage/(sizeof(Voltage_Array)/sizeof(Voltage_Array[0]));
break;
case SE_2:
IGBT_Temp = 3.3*((int32_t)(ADC_DATA & 0x01FFFFFF)<<7)/0x3FFFFFFF;
IGBT_Temp = IGBT_Temp/((3.3-IGBT_Temp)/10000);
IGBT_Temp = -(81439.6725*log(0.0002*IGBT_Temp)-85825)/(3433+298.15*log(0.0002*IGBT_Temp));
break;
case SE_3:
Battery_Temp = 3.3*((int32_t)(ADC_DATA & 0x01FFFFFF)<<7)/0x3FFFFFFF;
break;
default:
break;
}
if(Update_Current)
{
double Error = Current_Command - Battery_Current;
Integral += I*Error;
Integral = constrain(Integral, -4095, 4095);
int32_t PWM_Command = P*Error + Integral + 1405*pow(Current_Command,0.041)+0.01*pow(Current_Command,1.95);
if(Current_Command == 0)
{
PWM_Command = 0;
Integral = 0;
}
analogWrite(IGBT_PWM_PIN, constrain(PWM_Command, 0, 4095));
Serial.printf("%f,%li,%f,%f,%f,%f,%f\r\n", (float)Time/1000000, constrain(PWM_Command, 0, 4095), Current_Command, Battery_Current, Battery_Voltage, IGBT_Temp, Battery_Temp);
}
}