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.
Hi there,
I am using the DSP 28377D, and in my software I have one PWM interrupt (occurring when TBCTR = 0). When the interrupt occurs I need to read a the ADC value. I wanted to do use the software triggered ADC, but I am not sure if I am using it correctly. The relevant sections of my code are below:
ADC (channel A0) settings:
void SetupADC_A(Uint16 channel) { Uint16 acqps; //determine minimum acquisition window (in SYSCLKS) based on resolution (SYSCLK = 200MHz) if(ADC_RESOLUTION_12BIT == AdcaRegs.ADCCTL2.bit.RESOLUTION){ acqps = 19; //75ns ((14+1)*(1/200MHz) ) 14 is the default value } else { //resolution is 16-bit acqps = 63; //320ns ((63+1)*(1/200MHz)) } //Select the channels to convert and end of conversion flag EALLOW; AdcaRegs.ADCSOC0CTL.bit.CHSEL = 0; AdcaRegs.ADCSOC0CTL.bit.ACQPS = acqps; //sample window is 100 SYSCLK cycles AdcaRegs.ADCSOC0CTL.bit.TRIGSEL = 0; //forced trigger by software }
now, when the main software reaches the pwm interrupt:
interrupt void epwm6_isr(void) {
AdcaRegs.ADCSOCFRC1.bit.SOC0 = 1; // Software forced start of conversion in ADC A0 (input current)
i_read = (-0.004346)*((double)(AdcaResultRegs.ADCRESULT0)) + 8.217;
EPwm6Regs.ETCLR.bit.INT = 1; // clear EPWM6 INT flag
PieCtrlRegs.PIEACK.all = PIEACK_GROUP3;
}
In the pwm interrupt I would like to read the ADC content and scale the result to update the value of the current "i_read" as highlighted in the code.
Is there anything else needed to make sure that I get the correct result from the ADC?
Thank you very much for your answer.
Leo
Hi Gautam,
thank you for your kind answer.
Feeding the output of the PWM pin (having a duty ratio = 0.5) to the ADC input, I read the value 4095 in the expression window for my AdcaResultRegs.ADCRESULT0 as expected, since I am using trailing edge PWM, and the interrupt where I software-force the ADC at TBCTR = 0 (picture below).
It seems that the adc is converting the value correctly with the settings as per my previous question. What is your opinion? Do I need further settings to software-start the ADC?
Thanks a lot,
Leo
Hello Devin,
thanks a lot, your answer is very useful.
So, the way I read the adc inside the pwm interrupt (as in my top question) is not necessarily correct, as it does not ensure that the ADC has finished the conversion.
Now, the reality is that my software must execute 4 adc readings inside the interrupt. So I can do it as follows: (case 1)
interrupt void epwm6_isr(void) { //******************************************** // ADC measurements (input, output voltages) //******************************************** IO_04_ON(); AdcbRegs.ADCSOCFRC1.bit.SOC4 = 1; // Software forced start of conversion in ADC B4 (input voltage) AdcbResult4 = AdcbResultRegs.ADCRESULT4; vpv_r = (-0.03962) * ((double) (AdcbResult4)) + 74.92; // measurement #1 AdcaRegs.ADCSOCFRC1.bit.SOC2 = 1; // Software forced start of conversion in ADC A2 (input current) AdcaResult2 = AdcaResultRegs.ADCRESULT2; ipv_r = (-0.004346) * ((double) (AdcaResult2)) + 8.217; // measurement #2 AdcdRegs.ADCSOCFRC1.bit.SOC0 = 1; // Software forced start of conversion in ADC D0 (output voltage) AdcdResult0 = AdcdResultRegs.ADCRESULT0; vo_r = (-0.08432) * ((double) (AdcdResult0)) + 159.5; // measurement #3 AdccRegs.ADCSOCFRC1.bit.SOC2 = 1;// Software forced start of conversion in ADC C2 (output current) AdccResult2 = AdccResultRegs.ADCRESULT2; io_r = (-0.01759)*((double)(AdccResult2)) + 33.16; // measurement #4 IO_04_OFF(); // and the interrupt continues with control code, and ePWM CMPA/B update
but, if I understood correctly this is not the best way. A better way would be the following, in which I set the flags to make sure that the ADC has finished the conversion: (case 2)
interrupt void epwm6_isr(void) { //******************************************** // ADC measurements (input, output voltages) //******************************************** IO_04_ON(); AdcbRegs.ADCSOCFRC1.bit.SOC4 = 1; // Software forced start of conversion in ADC B4 (input voltage) while (AdcbRegs.ADCINTFLG.bit.ADCINT1 == 0) ; // wait for ADCB to complete, then acknowledge flag AdcbRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; AdcbResult4 = AdcbResultRegs.ADCRESULT4; vpv_r = (-0.03962) * ((double) (AdcbResult4)) + 74.92; // measurement #1 AdcaRegs.ADCSOCFRC1.bit.SOC2 = 1; // Software forced start of conversion in ADC A2 (input current) while (AdcaRegs.ADCINTFLG.bit.ADCINT1 == 0) ; // wait for ADCA to complete, then acknowledge flag AdcaRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; AdcaResult2 = AdcaResultRegs.ADCRESULT2; ipv_r = (-0.004346) * ((double) (AdcaResult2)) + 8.217; // measurement #2 AdcdRegs.ADCSOCFRC1.bit.SOC0 = 1; // Software forced start of conversion in ADC D0 (output voltage) while (AdcdRegs.ADCINTFLG.bit.ADCINT1 == 0) ; // wait for ADCD to complete, then acknowledge flag AdcdRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; AdcdResult0 = AdcdResultRegs.ADCRESULT0; vo_r = (-0.08432) * ((double) (AdcdResult0)) + 159.5; // measurement #3 AdccRegs.ADCSOCFRC1.bit.SOC2 = 1;// Software forced start of conversion in ADC C2 (output current) while (AdccRegs.ADCINTFLG.bit.ADCINT1 == 0) ; // wait for ADCC to complete, then acknowledge flag AdccRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; AdccResult2 = AdccResultRegs.ADCRESULT2; io_r = (-0.01759)*((double)(AdccResult2)) + 33.16; // measurement #4 IO_04_OFF(); //and the interrupt continues with control code, and ePWM CMPA/B update...
In these two examples I am using IO_04() to monitor how long the "measurement" part inside the interrupt lasts. The pictures below show the example of case 1
and case 2 (software triggered conversion with flags set up correctly) is below:
the adc conversion takes much more time in the case 2 where I set up the flags advising when the ADC conversion is finished. However this seems to be the correct way. I am just concerned that, since the ADC takes ~2us out of my 5us switching period, I will not have enough time left to run the control action and update the PWM value inside the pwm interrupt, if I want to execute all this calculations (measurements + control + CMP value update) inside 1 switching cycle.
In case 2 though, I am using 4 ADC modules sequentially, without taking advantage of the fact that the the conversions can be run in parallel since I am using 4 separate ADC modules. Hence the long time taken to convert my measured values.
I believe that the flow you suggested is probably the best idea. I.e. as you have said,
-ePWM event triggers ADC conversion(s).
-last ADC conversion triggers ADC ISR
-in the ADC ISR read the ADC sample, do your control logic, and update "i_read"
The issue is that I have 4 signals (4 adc: b, a , d, c) to convert. So I am still a bit confused on how to convert these 4 signals in parallel (taking the least amount of time possible). I think that:
1. I can set up a SOC on each adc to be triggered by the same PWM as you have suggested. I know how to set that up.
2. I need some help to understand the interrupt. I explain: say that I use ADC B4, ADC A2, ADC D0, ADC C2. I can set up the SOC for each one of these ADC to be triggered when the ePRM6 TBCTR = 0 for instance. At this stage I am not sure what interrupt to run/use. Should I, for instance, set that EOC (for adc C2, since this is the "last" adc conversion) triggers an ADC-C interrupt? and how to do this?
Thank you very much indeed for your time.
Leo
Hi Leo,
One quick note: SOCs are not tied to a specific channel. For example you could set SOC0 channel select to 5 - when the SOC0 trigger is received channel 5 will convert and the results will go into ADCRESULT0.
So the way you would set the ADC up is like so:
And then you just pick one ADC to trigger the end-of-conversion interrupt from SOC0. It doesn't matter which ADC, because all 4 run simultaneously and all have the same number of conversions.
The program works like this:
Also note that you will want to configure the ADC for "late interrupt mode"
Hi Devin,
Thank you, your answers really help.
So, each SOC is not tied to each channel, but if I use SOC0 or 1 or 2, the conversion result will be inside AdcxResultRegs.ADCRESULT0, or 1, or 2.
I have changed my code. In order to confirm that I am on the right path (i.e. that I use the 4 adc modules in parallel) I report my steps below.
1) I have configured the adc as follow:
void ConfigureADC_A(void) { EALLOW; //write configurations AdcaRegs.ADCCTL2.bit.PRESCALE = 6; // set ADCCLK divider to INPUT CLOCK /4 AdcSetMode(ADC_ADCA, ADC_RESOLUTION_12BIT, ADC_SIGNALMODE_SINGLE); //Set pulse positions to late AdcaRegs.ADCCTL1.bit.INTPULSEPOS = 1; //power up the ADC AdcaRegs.ADCCTL1.bit.ADCPWDNZ = 1; //delay for 1ms to allow ADC time to power up DELAY_US(1000); EDIS; }
same for adc B, C and D. The adc Setup look as follow:
void SetupADC_A(Uint16 channel) { Uint16 acqps; //determine minimum acquisition window (in SYSCLKS) based on resolution (SYSCLK = 200MHz) if(ADC_RESOLUTION_12BIT == AdcaRegs.ADCCTL2.bit.RESOLUTION){ acqps = 19; //(19+1)/200MHz = 100ns. The min is 75ns } else { //resolution is 16-bit acqps = 63; //320ns ((63+1)*(1/200MHz)) } //Select the channels to convert and end of conversion flag EALLOW; AdcaRegs.ADCSOC0CTL.bit.CHSEL = 2; AdcaRegs.ADCSOC0CTL.bit.ACQPS = acqps; //sample and hold window AdcaRegs.ADCSOC0CTL.bit.TRIGSEL = 15; // triggered by ePWM6 event AdcaRegs.ADCINTSEL1N2.bit.INT1SEL = 0; // EOC0 is trigger for ADCINT1 AdcaRegs.ADCINTSEL1N2.bit.INT1E = 1; //enable INT1 flag AdcaRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; //make sure INT1 flag is cleared } void SetupADC_B(Uint16 channel) { Uint16 acqps; //determine minimum acquisition window (in SYSCLKS) based on resolution if(ADC_RESOLUTION_12BIT == AdcbRegs.ADCCTL2.bit.RESOLUTION){ acqps = 19; // } else { //resolution is 16-bit acqps = 63; //320ns } //Select the channels to convert and end of conversion flag EALLOW; // Note channel B4 conversion related to EPWM6 (when TBCTR=TBPRD) AdcbRegs.ADCSOC0CTL.bit.CHSEL = 4; AdcbRegs.ADCSOC0CTL.bit.ACQPS = acqps; AdcbRegs.ADCSOC0CTL.bit.TRIGSEL = 15; } void SetupADC_C(Uint16 channel) { Uint16 acqps; //determine minimum acquisition window (in SYSCLKS) based on resolution if(ADC_RESOLUTION_12BIT == AdccRegs.ADCCTL2.bit.RESOLUTION){ acqps = 19; // } else { //resolution is 16-bit acqps = 63; //320ns } //Select the channels to convert and end of conversion flag EALLOW; AdccRegs.ADCSOC0CTL.bit.CHSEL = 2; AdccRegs.ADCSOC0CTL.bit.ACQPS = acqps; AdccRegs.ADCSOC0CTL.bit.TRIGSEL = 15; } void SetupADC_D(Uint16 channel) { Uint16 acqps; //determine minimum acquisition window (in SYSCLKS) based on resolution if(ADC_RESOLUTION_12BIT == AdcdRegs.ADCCTL2.bit.RESOLUTION){ acqps = 19; // } else { //resolution is 16-bit acqps = 63; //320ns } //Select the channels to convert and end of conversion flag EALLOW; AdcdRegs.ADCSOC0CTL.bit.CHSEL = 0; AdcdRegs.ADCSOC0CTL.bit.ACQPS = acqps; AdcdRegs.ADCSOC0CTL.bit.TRIGSEL = 15; }
Note that TRIGSEL =15 for all of them, meaning that the ADCSOC0 is triggered by an EPWM6 event. Also, I hope I understood correctly: only one of the adc modules is setup to release an EOC interrupt, and this is chosen arbitrarily as adc-A, whose setting above includes the code:
AdcaRegs.ADCINTSEL1N2.bit.INT1SEL = 0; // EOC0 is trigger for ADCINT1 AdcaRegs.ADCINTSEL1N2.bit.INT1E = 1; //enable INT1 flag AdcaRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; //make sure INT1 flag is cleared
2) Now, the relevant part of the PWM setup is the following:
EPwm6Regs.ETSEL.bit.SOCASEL = 1; // EPWM6 SOCA Pulse Generated TBCTR=0 EPwm6Regs.ETSEL.bit.SOCAEN = 1; // enable SOC on A group EPwm6Regs.ETPS.bit.SOCAPRD = 1; // Generate pulse on 1st event
where the line:
EPwm6Regs.ETSEL.bit.SOCASEL = 1; // EPWM6 SOCA Pulse Generated TBCTR=0
is saying that when EPWM6 TBCTR = 0, a start-of-conversion pulse will be generated (this pulse will be trigger for the ADC SOC).
3) In the main.c, I have one only interrupt, but this time is an adc interrupt, chosen arbitrarily for adc-a1 since the timing of each adc module is the same.
interrupt void adca1_isr(void) { //******************************************** // ADC measurements (input, output voltages) //******************************************** IO_04_ON(); AdcbResult0 = AdcbResultRegs.ADCRESULT0; AdcaResult0 = AdcaResultRegs.ADCRESULT0; AdcdResult0 = AdcdResultRegs.ADCRESULT0; AdccResult0 = AdccResultRegs.ADCRESULT0; vpv_r = (-0.03962) * ((double) (AdcbResult0)) + 74.92; ipv_r = (-0.004346) * ((double) (AdcaResult0)) + 8.217; vo_r = (-0.08432) * ((double) (AdcdResult0)) + 159.5; io_r = (-0.01564)*((double)(AdccResult0)) + 29.42; IO_04_OFF(); // and the interrupt continues by calculating the controls, and updating the CMP values for the PWM...
So, in this way I have used the 4 adc modules in parallel, rather than reading each adc sequentially like I was doing before. I hope my procedure is correct.
It would be great to have your opinion,
Thank you very much for your time.
Leo
Hi Leo,
Yeah, this looks good as far as I can tell. Next thing to do is test it to see if it works.
Hello Devin,
Thanks again for your great help.
I think that the software now works well. All the values of my variables vpv_r, ipv_r, vo_r, io_r seem to be correct and the time to execute the part of the interrupt I have reported in the previous question (i.e. between IO4_ON() and IO4_OFF(), where the update of AdcaResult0, AdcbResult0, AdccResult0, AdcdResult0 and calculation of vpv_r, ipv_r, vo_r, io_r occur) is shown by the green waveform below.
In the future though I will have to add other measurments. For instance now I am using
ADCA IN2 ePWM6
ADCB IN4 ePWM6
ADCC IN2 ePWM6
ADCD IN0 ePWM6
But soon I need to take singals from:
ADCA IN2 from ePWM6 and ADCA IN3 from ePWM7
ADCB IN4 from ePWM6 and ADCB IN5 from ePWM7
ADCC IN2 from ePWM6 and ADCC IN3 from ePWM7
ADCC IN0 from ePWM6 and ADCD IN1 from ePWM7
I.e. I will be using more than one input from each ADC module, with ePWM6 and 7 being in phase (i.e. exactly identical).
I was wondering how the flow will work with the start of conversion and end of conversion signals? For example:
- ePWM6 generates a SOCA pulse when TBCTR = 0.
- ADC inputs A2 B4 C2 and D0 will start the conversion upon receipt of the SOC0 pulse(?) while ADC inputs A3 B5 C3 and D1 will start the conversion upon receipt of a SOC1 pulse(?). These SOC0 and SOC1 pulses are activated ePWM6 counter reching TBCTR = 0 (I am not sure how the ADC's will work here, in which order...)
- one ADC arbitrarily chosen will trigger the EOC interrupt (I only put one interrupt in the code) where all the values inside the AdcxResultRegs.ADCRESULT will be copied into my defined variables and used to calculate the control etc.
If you have any suggestion it would be really great.
Thank you very much again.
Leo
Hi Devin,
thanks again for your kind answer.
I am interested in the case "read all the variables once per ePWM period all in the same ISR"... Should I do that by means of the same adc interrupt? or do I need to set a different type of interrupt?
Thank you very much again,
Leo