With the JeeLabs Energy Monitor prototype hooked up in the meter cabinet, it’s now very easy to explore things a bit. One interesting test is to look at the analog signal from the AC power supply + divider, which is connected to pin PA0.
Here is some code which samples that signal and prints it out until a key is pressed:
: a1 +adc begin pa0 adc . key? until ;
a1 4029 4031 4030 4030 [...] 1268 1343 1763 2330 ok.
The ADC is initialised, and then we acquire a value, print it, and loop until some key is pressed. The readings are easily copied to a spreadsheet and graphed:
Some observations:
- as expected, we’re only reading the negative excursions of the supply voltage
- the scale is working out nicely, over 70% of the ADC’s 12-bit resolution is used
- it looks like we’re capturing about 25 samples per 50 Hz cycle
- this loop timing is partly limited by the 115,200 baud rate of the (polled!) serial output
- i.e. 10 bits per char, 5 chars per ADC (4 digits and a space) @ 115,200 baud takes 434 µs
- plus some time for the ADC - hmmm… not sure where the remaining 300+ µs went!
The current polled ADC acquisition code itself is very fast. It’s easy to measure this:
: a2 micros pa0 adc micros nip swap - . ; ok.
a2 41 ok.
So it takes 41 µs to acquire one ADC sample (less actually, the micros
code has some overhead). To improve on this, we can first acquire samples in
a quick loop, and then print them out:
4000 buffer: adata ok.
: a3 1000 0 do pa0 adc i cells adata + ! loop ; ok.
: a4 +adc micros a3 micros swap - . ; ok.
: a5 1000 0 do i cells adata + @ . loop ; ok.
a4 32915 ok.
a5 1333 1334 1334 [...] 4026 4028 4027 ok.
Which means that we’ve captured 33 ms of data, i.e. more than one full 50 Hz cycle:
Both half-wave captures consist of 338 readings, with 269 readings in between, when the ADC returns 4030 or 4031. Which means that the zero crossings are indeed included in our data, and that there is enough information to reconstruct a full sine wave for CT power calculations later.
Note that the sine wave has somewhat flattened peaks and there also appears to be some noise.
These readings were taken with a µC running at 8 MHz, which gives enough
resolution for now. At 72 MHz, a2
reports 6 µs and a4
reports 4949 µs - this
translates to a polled ADC acquisition rate of over 200,000 samples per second.
The flip side is that at this higher rate we can only capture some 5 ms with a
1000-entry buffer, considerably less than a full 50 Hz AC cycle:
For tests, the polled approach is fine - and very convenient, given the interactive command-line access provided by the ESP-Link - but the acquisition rate will not be constant due to occasional interrupts, and the µC would be tied up most of the time, which is clearly not very practical.
The solution is to use DMA-based ADC acquisition. Then, we only need to deal with acquired data in large chunks. The STM32F103 µC’s DMA hardware has two nice interrupts for this: when the buffer is half full, and when it reaches the end and acquisition starts over at the beginning.
Let’s plan ahead a bit and consider the rates and time + memory resources needed:
- there are 4 ADC channels: 1 for supply voltage and 3 for the current transformers
- we could collect 1600 samples per channel, that’s 800 samples per interrupt
- this requires 4 x 1600 x 2 bytes = 12.8 KB, which is ok - the Olimexino has 20 KB RAM
- if we sample at 25,000 Hz, each 800-sample block will cover 32 ms of time
- this is enough to always capture more than one full wave, i.e. 3 .. 4 zero crossings
- we’ll get one interrupt every 32 ms, with a 800-sample block ready for each channel
- so during this interrupt, the code will need to process 4 x 800 = 3200 data points
Even if our calculations take more than 32 ms, this would be ok: we could simply ignore some acquisition cycles. The only constraint is that the buffered data needs to be processed (or moved out of the way) before the DMA engine overwrites it with new data, i.e. within 32 ms.
The beauty of DMA is that it completely frees the CPU. It “only” has to deal with ≈ 30 interrupts per second, with all ADC values acquired exactly 40 µs apart and stored in the DMA buffer.
There is still plenty of work to do… such as dealing with phase and finding those zero crossings.