Open monte-monte opened 5 months ago
Ok, so sometimes it's better to think a bit longer before asking questions:) I guess you just want to have two registers with two voltages updated per every ADC interrupt, that's why injection mode.
But the second question stands: do ADC_JDISCEN
and ADC_JEOCIE
work together? And if they do, does injection group get sampled twice?
So, I have made a simple boost converter, with an inductor instead of flyback transformer to drive 12-15V leds from 5V. I played with tuning values but couldn't get rid of visible brightness fluctuations that are visible when led is running at low power. I concluded, that it was not ripple from PWM but the PID truing to compensate.
First I increased sample time for feedback reading and decreased for VDD. Then I removed ADC_JDISCEN
from ADC setup and now I don't have fluctuations even with simple version of your PID (that has only the proportional part).
I used ADC DMA example from ch32v003fun to adapt this code to use DMA. Now SetupADC
looks like this:
// Configure ADC.
// PD4 is analog input chl 7
GPIOD->CFGLR &= ~(0xf<<(4*4)); // CNF = 00: Analog, MODE = 00: Input
// Reset the ADC to init all regs
RCC->APB2PRSTR |= RCC_APB2Periph_ADC1;
RCC->APB2PRSTR &= ~RCC_APB2Periph_ADC1;
// ADCCLK = 12 MHz => RCC_ADCPRE divide by 4
RCC->CFGR0 &= ~RCC_ADCPRE; // Clear out the bis in case they were set
RCC->CFGR0 |= RCC_ADCPRE_DIV4; // set it to 010xx for /4.
// Set up single conversion on chl 7
ADC1->RSQR1 = (ADC_NUMCHLS-1) << 20;
ADC1->RSQR2 = 0;
ADC1->RSQR3 = (7<<(5*0)) | (8<<(5*1));
// Sampling time for channels. Careful: This has PID tuning implications.
// Note that with 3 and 3,the full loop (and injection) runs at 138kHz.
ADC1->SAMPTR2 = (1<<(3*7)) | (1<<(3*8));
// 0:7 => 3/9/15/30/43/57/73/241 cycles
// (4 == 43 cycles), (6 = 73 cycles) Note these are alrady /2, so
// setting this to 73 cycles actually makes it wait 256 total cycles
// @ 48MHz.
// Turn on ADC and set rule group to sw trig
// 0 = Use TRGO event for Timer 1 to fire ADC rule.
ADC1->CTLR2 = ADC_ADON | ADC_EXTTRIG | ADC_DMA;
// Reset calibration
ADC1->CTLR2 |= ADC_RSTCAL;
while(ADC1->CTLR2 & ADC_RSTCAL);
// Calibrate ADC
ADC1->CTLR2 |= ADC_CAL;
while(ADC1->CTLR2 & ADC_CAL);
// Turn on DMA
RCC->AHBPCENR |= RCC_AHBPeriph_DMA1;
//DMA1_Channel1 is for ADC
DMA1_Channel1->PADDR = (uint32_t)&ADC1->RDATAR;
DMA1_Channel1->MADDR = (uint32_t)adc_buffer;
DMA1_Channel1->CNTR = ADC_NUMCHLS;
DMA1_Channel1->CFGR =
DMA_M2M_Disable |
DMA_Priority_VeryHigh |
DMA_MemoryDataSize_HalfWord |
DMA_PeripheralDataSize_HalfWord |
DMA_MemoryInc_Enable |
DMA_Mode_Circular |
DMA_DIR_PeripheralSRC;
// Turn on DMA channel 1
DMA1_Channel1->CFGR |= DMA_CFGR1_EN;
// enable the ADC Conversion Complete IRQ
NVIC_EnableIRQ( ADC_IRQn );
// ADC_JEOCIE: Enable the End-of-conversion interrupt.
// ADC_SCAN: Allow scanning.
ADC1->CTLR1 = ADC_EOCIE | ADC_SCAN;
And in ADC1_IRQHandler
I read values from adc_buffer[0]
and adc_buffer[1]
respectively. Now everything works even with low sampling time, but with my hardware setup I still get better result from using just proportional part of PID. If I try to use full PID I get brightness fluctuations that are not reflected in voltage monitor in testnix
app. I guess I'd need to dive deeper into PID tuning for it to work perfectly for me, but for now I don't see much reason. But I'd like to better understand what causes this difference in behavior.
When using full PID it seems like timing of ADC reads plays a big role, such if I change sampling times I see different behavior, with only proportional I don't see any visible change.
The next step will be to try implement constant current mode with use of another ADC channel and built-in OPAMP.
Continuing tinkering I've soldered 3.3V LDO to supply power from the same source as the boost part of the circuit (before MCU was powered via WCH-link). Now I see a slight brightness ripple at a low power. I've concluded that noisy VDD readings are a culprit here. If I set the VDD hardcoded to 3.3V value the brightness ripple is gone. BUT! The strange part is even with live VDD readings the ripple is only present when testnix app is running. If I close the app, even without detaching a probe, the ripple is gone. So does DMDATA0 reads and writes introduce some timing lag, or what? I haven't got an oscilloscope, so I can't accurately test such fast signals.
EDIT: I've tried to increase sampling time for the VDD channel and also to decrease IIR rate, but no visible difference. Also I am thinking to try to take VDD readings at a different frequency, maybe it could help with the noise. So I guess I've found the working setup for my case, using external LDO I can trust that voltage is accurate enough, considering I don't use 10x transformer. I can use just a constant number for the VDD, or even update it once in a while for some unforeseen fluctuations.
I am so sorry, I have been so heads down on porting the ch32x035 stuff, I have been neglecting my github... But to summarize things for anyone else coming along,
Thinking forward - I wonder if there is some other scheme that would let you align all of your voltage readings. I.e. move everything to the injection, and chain it all off the rule? Or maybe manually reconfigure the ADC every interrupt to switch between the regular ADC reading and the VDD reading.
Also, also, if you know you have a fixed LDO, then there should be no need to monitor VDD at all, and you can simplify the system immensely.
SIDE NOTE: If you would like a much better place for discussion is the #ch32v003 channel in my discord... I can give you access if you want, my user name is cnlohr
@cnlohr no worries, I glad I mostly figured out myself, that's better for understanding :)
3. Looking at your code, I am still unclear what it's measuring with the rule and what it's measuring with the injection
In my code I don't use injection at all, that's what I was getting at. I read both channels in rule group one after another and use DMA to store the values in an array. I've found that without injection I can decrease sampling time and it somehow decreases noise on both ADC readings. Injection itself introduces delay when it is called automatically after rule reading. I guess it messes timing, because getting rid of it improved things in my case. I wonder if it will work with your hardware better/worse.
4. You MUST align your ADC sampling with your PWM switch
Yes, I get this, I've read your comments in code several dozens times while trying understand it :) In current setup ADC read starts on timer rollover, so is there any other way to adjust read timing apart from changing sampling time and/or introducing other channel reads before the one we measuring?
5. I have never seen issues writing/reading from DMDATA0 - but now you have me worried. It is entirely possible that there could be some unintuitive impact on the system by reading/writing from DMDATA0.
I don't have a clue, I just report what I see, can it somehow skew ADC read timings?
Also, also, if you know you have a fixed LDO, then there should be no need to monitor VDD at all, and you can simplify the system immensely.
That's what I figured out in the end :)
If you would like a much better place for discussion is the #ch32v003 channel in my discord
Should I add you in discord, or just give you my username here (montemonte)? I'm not very familiar with it specifics.
Re: No Injection: @monte-monte this is extremely interesting - how do you make sure to maintain alignment of the samples? Re: Rollover: You can also sync the timer off of a channel compare. You aren't limited to TRGO. Re: I don't have any idea how reading/write DMDATA0 could affect things. You may need to peel things back. I just did a bunch of testing last night and could not repro. My data seemed to be just fine? Re: Just add me on Discord. I will send you an invite.
how do you make sure to maintain alignment of the samples?
Alignment to what? As I understand, rollover happens -> TRGO triggers ADC conversions -> ADC converts 2 channels one after another in one go adding values to array via DMA -> interrupt is triggered. So basically all I did was: activated DMA and added second channel to rule group and removed code for injection. Now you make me think I am doing something wrong :) but hey, everything works (somehow).
You may need to peel things back. I just did a bunch of testing last night and could not repro
It may have something to do with hardware part that is different in my setup. So basically I see some shimmering of the LED only on the lowest values that keeps it lit, so it has to be a very low current at that point. Maybe it connected to inductor saturation, or how mosfet opens/closes at different conditions, idk my knowledge and experience is not enough to accurately evaluate this behavior. All I know it that when testnix monitor is running I have this shimmer and when I close it shimmer is gone. And it's only when using measured VCC as the baseline, if I hardcode the voltage value I see no issues. So I guess not really an issue after all, but an interesting thing to understand what causes it.
Alignment: I just meant if you wanted you could trigger off of a timer compare match on an unused timer compare channel instead of TRGO.
I really don't know what could be going on with your shimmer issue.
Hi. I'm reading your code, trying to adapt it for my needs. Can you explain why do you read Vref in a separate injection group? What difference does it make compared to measuring both channels in the rule group? Is it to read Vref on separate timer trigger after HVMON channel is converted? Also I am reading the reference manual and there is this note:
So does it mean that Vref is being read 2 times in a row? Also this line: https://github.com/cnlohr/cnixxi/blob/d3db1aef0dade1c4579eeff3b5d0570b02ada58f/firmware/nixitest1/nixitest1.c#L397 contradicts the second paragraph of the note above.
Sorry if it's annoying, I am just confused "a little" :)