This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

ADC coding for SCR gate controller

Other Parts Discussed in Thread: CONTROLSUITE

 Hello,

I am trying to make firmware code for a SCR controller.

Need to work how I can make my  8 analog input  voltage to ADC and generate PWM (for R,S,T phase) considering zero crossing and firing angle correction.

Few information are follows.

1. I need to use pin ADCIN_A0 to ADCIN_B1. Here ADCIN_A1 and ADCIN_A3 should be exclude, because I am ignoring AC current and voltage part.(see the schematic)

2. My input  DC_volt_ADC and DC_current_ADC signals  are 0-3 volt and 20mA . Which might be single ended input.

3. If you look in the schematic Spare2_ADC, Spare1_ADC, Voltage_control signals are depends on M_Select_1, M_Select_2, M_Select_3(external Relay controlled)  in I/O pin 77-89.

4. The switching logics are,(

When M_Select_1--> HIGH ,Voltage_control -->HIGH and others (Spare2_ADC, Spare1_ADC) are LOW..

At this time M_Select_2, M_Select_3 are LOW.

Alternatively, M_Select_2, M_Select_3  pins could be HIGH when M_Select_1 is kept LOW  thus  Spare2_ADC, Spare1_ADC would be HIGH.

5.    3.3V REF Voltage_ADC is applied to ADCIN_A7 to ADCIN_B1.

I am following this code ( some part of it from ATmel ).

Kindly help me to write code in the required logic.

#include "DSP28x_Project.h" // Device Headerfile and Examples Include File

interrupt void adc_isr(void);

// Global variables used in this example:
Uint16 LoopCount;
Uint16 ConversionCount;
Uint16 Voltage1[10];
Uint16 Voltage2[10];
Uint16 Voltage3[10];
Uint16 Voltage4[10];
Uint16 Voltage5[10];
Uint16 Voltage6[10];
Uint16 Voltage7[10];
Uint16 Voltage8[10];
Uint16 Voltage9[10];

main()
{


InitSysCtrl();


EALLOW;
#if (CPU_FRQ_150MHZ) // Default - 150 MHz SYSCLKOUT
#define ADC_MODCLK 0x3 // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 150/(2*3) = 25.0 MHz
#endif
#if (CPU_FRQ_100MHZ)
#define ADC_MODCLK 0x2 // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 100/(2*2) = 25.0 MHz
#endif
EDIS;

EALLOW;
SysCtrlRegs.HISPCP.all = ADC_MODCLK;
EDIS;


DINT;
Gpio_select();
InitPieCtrl();

// Disable CPU interrupts and clear all CPU interrupt flags:
IER = 0x0000;
IFR = 0x0000;

InitPieVectTable();

EALLOW; // This is needed to write to EALLOW protected register
PieVectTable.ADCINT = &adc_isr;
EDIS; // This is needed to disable write to EALLOW protected registers


InitAdc(); // For this example, init the ADC

AdcRegs.ADCTRL1.all = 0;
AdcRegs.ADCTRL1.bit.ACQ_PS = 7;
AdcRegs.ADCTRL1.bit.SEQ_CASC =1;
AdcRegs.ADCTRL1.bit.CPS = 0;
AdcRegs.ADCTRL1.bit.CONT_RUN = 0;

AdcRegs.ADCTRL2.all = 0;
AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 1
AdcRegs.ADCTRL2.bit.EPWM_SOCA_SEQ1 =1;
AdcRegs.ADCTRL2.bit.INT_MOD_SEQ1 = 0;

AdcRegs.ADCTRL3.bit.ADCCLKPS = 3;

AdcRegs.ADCMAXCONV.all = 0; // 8 double conv's (16 total)

AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0;// Setup ADCINA0 as 1st SEQ1 conv
AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 1;//Setup ADCINA1 as 2nd SEQ1 conv

EPwm2Regs.TBCTL.all = 0xC030;
EPwm2Regs.TBPRD = 2999;
EPwm2Regs.ETPS.all = 0x0100;
EPwm2Regs.ETSEL.all = 0x0A00;

// Enable ADCINT in PIE
PieCtrlRegs.PIEIER1.bit.INTx6 = 1;
IER |= M_INT1; // Enable CPU Interrupt 1
EINT; // Enable Global interrupt INTM
ERTM; // Enable Global realtime interrupt DBGM

LoopCount = 0;
ConversionCount = 0;

asm(" RPT #10 || NOP"); // Short delay for ADC reset to take effect

void configure_adc_voltage(void)
{
struct adc_config conf_adc;

adc_get_config_defaults(&conf_adc);

conf_adc.reference = ADC_REFERENCE_INT3V;
conf_adc.positive_input = ADC_POSITIVE_INPUT_VOLTAGE;
conf_adc.negative_input = ADC_NEGATIVE_INPUT_GND;
conf_adc.sample_length = ADC_VOLTAGE_SAMPLE_LENGTH;

adc_init(&adc_instance, ADC, &conf_adc);

ADC->AVGCTRL.reg = ADC_AVGCTRL_ADJRES(2) | ADC_AVGCTRL_SAMPLENUM_4;

adc_enable(&adc_instance);
}

float calculate_voltage(uint16_t raw_code)
{
float VADC; /* Voltage calculation using ADC result for Coarse voltage calculation */
float VADCM; /* Voltage calculation using ADC result for Fine voltage calculation. */
float INT3VM; /* Voltage calculation for reality INT3V value during the ADC conversion */

VADC = ((float)raw_code * INT3V_VALUE_FLOAT)/ADC_12BIT_FULL_SCALE_VALUE_FLOAT;

/* Coarse Temp Calculation by assume INT1V=1V for this ADC conversion */
coarse_voltage = voltageR + (((voltageH - voltageR)/(VADCH - VADCR)) * (VADC - VADCR));

/* Calculation to find the real INT1V value during the ADC conversion */
INT3VM = INT3VR + (((INT3VH - INT3VR) * (coarse_voltage - voltageR))/(voltageH - voltageR));

VADCM = ((float)raw_code * INT1VM)/ADC_12BIT_FULL_SCALE_VALUE_FLOAT;

/* Fine Temp Calculation by replace INT3V=3V by INT3V = INT3Vm for ADC
conversion */
fine_voltage = voltageR + (((voltagepH - voltageR)/(VADCH - VADCR)) * (VADCM - VADCR));

return fine_voltage;

}

// Mux input
int16_t adcReadData(uint16_t mux)
{
#if defined(__AVR_AT90USB162__)
return 0;
#else
uint16_t low;

ADCSRA = (1<<ADEN) | ADC_PRESCALER; // enable ADC
ADCSRB = (1<<ADHSM) | (mux & 0x20); // high speed mode
ADMUX = aref | (mux & 0x1F); // configure mux input
ADCSRA = (1<<ADEN) | ADC_PRESCALER | (1<<ADSC); // start the conversion
while (ADCSRA & (1<<ADSC)) ; // wait for result
low = ADCL; // must read LSB first
return (ADCH << 8) | low; // must read MSB only once!
#endif
}

float adc_value, inst_current;
float acc_load_current; // accumulator = (I1*I1 + I2*I2 + ... + In*In)
double rms_current;

// Calculate the real instantanous value from the ADC reading
inst_current = (adc_value/1024)*2.5; // 10bit ADC, Voltage ref. 2.5V, so formula is: x=(adc/1024)*2.5V

// Update the RMS value with the new instananous value:
// Substract 1 sample from the accumulator (sample size is 512, so divide accumulator by 512 and substract it from the accumulator)
acc_load_current -= (acc_load_current / 512);
inst_current *= inst_current; // square the instantanous current
acc_load_current += inst_current; // Add it to the accumulator

rms_current = (acc_load_current / 512); // Get the mean square value. (sample size is 512)
rms_current = sqrt(rms_current); // Get RMS value

// Now the < rms_current > is the real RMS current

// Clear SampleTable
for (i=0; i<BUF_SIZE; i++)
{
SampleTable[i] = 0;
}

// Start SEQ1
AdcRegs.ADCTRL2.all = 0x2000;

for(;;)
{ // Take ADC data and log them in SampleTable array

// Initialize the array index. This points to the current
// location within the SampleTable
array_index = 0;

for (i=0; i<(BUF_SIZE/16); i++)
{
// Wait for int1
while (AdcRegs.ADCST.bit.INT_SEQ1== 0){}
GpioDataRegs.GPBSET.bit.GPIO34 = 1; // Set GPIO34 for monitoring -optional

AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1;


// Wait for ADC interrupt
for(;;)
{
LoopCount++;
}

}


interrupt void adc_isr(void)
{

Voltage1[ConversionCount] = AdcRegs.ADCRESULT0 >>4;
Voltage2[ConversionCount] = AdcRegs.ADCRESULT1 >>4;
Voltage3[ConversionCount] = AdcRegs.ADCRESULT2 >>4;
Voltage4[ConversionCount] = AdcRegs.ADCRESULT3 >>4;
Voltage5[ConversionCount] = AdcRegs.ADCRESULT4 >>4;
Voltage6[ConversionCount] = AdcRegs.ADCRESULT5 >>4;
Voltage7[ConversionCount] = AdcRegs.ADCRESULT6 >>4;
Voltage8[ConversionCount] = AdcRegs.ADCRESULT7 >>4;
Voltage9[ConversionCount] = AdcRegs.ADCRESULT8 >>4;


// If 40 conversions have been logged, start over
if(ConversionCount == 9)
{
ConversionCount = 0;
}
else ConversionCount++;

// Reinitialize for next ADC sequence
AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1; // Reset SEQ1
AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1; // Clear INT SEQ1 bit
AdcRegs.ADCTRL2.bit.RST_SEQ2 = 1; // Reset SEQ1
AdcRegs.ADCST.bit.INT_SEQ2_CLR = 1; // Clear INT SEQ1 bit
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // Acknowledge interrupt to PIE

return;
}spru812a.pdf

  • Hi MD,

    It isn't clear from your post where you are running into difficulties.

    For the ADC sampling, I think a good place to start would be with the ADC examples in ControlSUITE. Try to run this example to get it to work, then try to expand it to encompass all the channels you need to sample. (You will also want to ready the ADC documentation thoroughly)

    Once you are familiar with how to setup the ADC to sample all your channels, next try the ePWM example. Run the example and get it to work, and then expand it so that you can control (manually) the 3 phases in your system. (You will also want to read the ePWM documentation thoroughly)

    Once you have a good understanding of the two core peripherals for your application, you can start your real application software (probably starting with the expanded ePWM example). Once you get both the ADC and ePWM working in the same project, you can start to add the control logic to close the feedback loop.
  • Dear Sir,

    First of all thank you very much for answering my post.

    Its a nice suggestion at this moment. Yes, things should be done periodically. I am fully focused on SPRU812a and related example in control-suite.

    Right at this moment, I am trying to feed my DC input signal to ADC.
    Difficulties are conditioning my input signal.
    Should I use Buffer and MUX?

    Take a look this structure ATMEL for the Mux input

    / Mux input
    int16_t adcReadData(uint16_t mux)
    {
    #if defined(__AVR_AT90USB162__)
    return 0;
    #else
    uint16_t low;

    ADCSRA = (1<<ADEN) | ADC_PRESCALER; // enable ADC
    ADCSRB = (1<<ADHSM) | (mux & 0x20); // high speed mode
    ADMUX = aref | (mux & 0x1F); // configure mux input
    ADCSRA = (1<<ADEN) | ADC_PRESCALER | (1<<ADSC); // start the conversion
    while (ADCSRA & (1<<ADSC)) ; // wait for result
    low = ADCL; // must read LSB first
    return (ADCH << 8) | low; // must read MSB only once!
    #endif
    }

    From another program of TMS buffer input


    //============================================================================================
    // Predefine
    //--------------------------------------------------------------------------------------------
    #include "DSP28x_Project.h" // Device Headerfile and Examples Include File
    //============================================================================================
    #define BUFFER_LENGTH 384
    #pragma DATA_SECTION(ADC_buffer, "DMARAML7"); // locate ADC buffer in user define section(DAMRAML7)

    //============================================================================================
    // Global variable
    //--------------------------------------------------------------------------------------------
    Uint16 ADC_cnt, i;
    Uint16 ADC_buffer[2][BUFFER_LENGTH];
    //============================================================================================


    //============================================================================================
    // start of main function
    //============================================================================================
    void main(void)
    {
    //============================================================================================
    // Step 1. Disable Global Interrupt
    //--------------------------------------------------------------------------------------------
    DINT;
    //============================================================================================


    //============================================================================================
    // Step 2. initial system control
    //--------------------------------------------------------------------------------------------
    InitSysCtrl();
    EALLOW;
    SysCtrlRegs.HISPCP.bit.HSPCLK = 1; // HSPCLK = SYSCLKOUT/(HISPCP*2)
    EDIS; // HSPCLK = 150MHz/(1*2) = 75MHz
    //============================================================================================


    //============================================================================================
    // Step 3. ADC initialzation
    //--------------------------------------------------------------------------------------------
    InitAdc();

    // ADC setting
    AdcRegs.ADCTRL3.bit.ADCCLKPS = 2; // ADCCLK = HSPCLK/(ADCCLKPS*2)/(CPS+1)
    AdcRegs.ADCTRL1.bit.CPS = 0; // ADCCLK = 75MHz/(2*2)/(0+1) = 25MHz
    AdcRegs.ADCTRL1.bit.ACQ_PS = 0; // sample/hold cycle = ACQ_PS + 1 = 2
    AdcRegs.ADCTRL1.bit.SEQ_CASC = 1; // setting sequence mode : serial mode (0: parallel, 1:serial)
    AdcRegs.ADCTRL3.bit.SMODE_SEL = 1; // dual channel
    AdcRegs.ADCMAXCONV.bit.MAX_CONV1 = 0; // the number of ADC channel : 1(=MAX_CONV+1)
    AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 4; // sequence of ADC : ADCINA4 and ADCINB4
    AdcRegs.ADCTRL1.bit.CONT_RUN = 1; // Continuous Mode
    AdcRegs.ADCTRL1.bit.SEQ_OVRD = 1;
    //============================================================================================

    //============================================================================================
    // Step 4. Initialize Application Variables
    //--------------------------------------------------------------------------------------------
    ADC_cnt = 0;
    for(i=0;i<BUFFER_LENGTH;i++){
    ADC_buffer[0][i] = 0;
    ADC_buffer[1][i] = 0;
    }
    //============================================================================================


    //============================================================================================
    // Enable global Interrupts and higher priority real-time debug events:
    //--------------------------------------------------------------------------------------------
    EINT; // Enable Global interrupt INTM
    ERTM; // Enable Global realtime interrupt DBGM
    //============================================================================================


    //============================================================================================
    // IDLE loop. Just sit and loop forever :
    //--------------------------------------------------------------------------------------------
    AdcRegs.ADCTRL2.bit.SOC_SEQ1 = 1; // Start of ADC sequence

    for(;;)
    {
    ADC_buffer[0][ADC_cnt] = AdcRegs.ADCRESULT0; // ADC(I) result
    ADC_buffer[1][ADC_cnt] = AdcRegs.ADCRESULT1; // ADC(Q) result
    if(ADC_cnt == BUFFER_LENGTH-1) {
    ADC_cnt = 0;
    for(i=0;i<BUFFER_LENGTH;i++){
    ADC_buffer[0][i] = 0;
    ADC_buffer[1][i] = 0;
    }
    }else ADC_cnt++; // count of ADC samples
    }




    From the floating-point (float) variables some people use this format ,

    loat calculate_voltage(uint16_t raw_code)
    {
    float VADC; /* Voltage calculation using ADC result for Coarse voltage calculation */
    float VADCM; /* Voltage calculation using ADC result for Fine voltage calculation. */
    float INT3VM; /* Voltage calculation for reality INT3V value during the ADC conversion */

    VADC = ((float)raw_code * INT3V_VALUE_FLOAT)/ADC_12BIT_FULL_SCALE_VALUE_FLOAT;

    /* Coarse Temp Calculation by assume INT1V=1V for this ADC conversion */
    coarse_voltage = voltageR + (((voltageH - voltageR)/(VADCH - VADCR)) * (VADC - VADCR));

    /* Calculation to find the real INT1V value during the ADC conversion */
    INT3VM = INT3VR + (((INT3VH - INT3VR) * (coarse_voltage - voltageR))/(voltageH - voltageR));

    VADCM = ((float)raw_code * INT1VM)/ADC_12BIT_FULL_SCALE_VALUE_FLOAT;

    /* Fine Temp Calculation by replace INT3V=3V by INT3V = INT3Vm for ADC
    conversion */
    fine_voltage = voltageR + (((voltagepH - voltageR)/(VADCH - VADCR)) * (VADCM - VADCR));

    return fine_voltage;

    }



    Kindly recommend me a suitable structure.
  • Hi MD,

    In the ControlSUITE example, one of the lines that configure the input mux is here:

    AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x3; // Setup ADCINA3 as 1st SEQ1 conv.

    As far as signal conditioning, at this stage you should probably just worry about getting the signal into the ADC input range.  For this device it is 0 to 3.0V.  Once you are familiar with the software, and have some application sketch, then you can do the due diligence and thoroughly design your input signal conditioning circuits, factoring in things like power sequencing, settling speed, bandwidth, etc.

  • It was a nice suggestion.