Request for Comments: DAC Interface

C programming, build, interpreter/VM.
Target audience: MicroPython Developers.
ul5255
Posts: 11
Joined: Thu Oct 23, 2014 9:08 am

Re: Request for Comments: DAC Interface

Post by ul5255 » Thu Feb 19, 2015 12:17 pm

I can confirm that the bit masking is not necessary. The final version in dac.c is

Code: Select all

HAL_DAC_SetValue(&DAC_Handle, self->dac_channel, DAC_ALIGN_12B_R, mp_obj_get_int(val));
It can be tested with this sequence:

Code: Select all

# place jumper between X6 and X19

a = pyb.ADC(pyb.Pin.board.X19)
d = pyb.DAC(2)

d.write(0)
assert(a.read() < 10)
d.write(4095)
assert(a.read() > 4085)
d.write(4096)
assert(a.read() < 10)
With this change DAC.write() offers the same resolution as ADC.read(). Since ADC.read() doesn't offer a keyword to select the resolution I *think* DAC.write() should also not offer a keyword. The disadvantage is of course that such a change will by backwards incompatible. Should I create a pull request to have an "official" discussion of this change? If yes: How do I create such a pull request? I git clone'd the master repository and made the changes there. From the top level of my local repository:

Code: Select all

[ul5255@speedy-gonzales micropython]$ pwd
/home/ul5255/sandbox/micropython
[ul5255@speedy-gonzales micropython]$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   stmhal/dac.c

no changes added to commit (use "git add" and/or "git commit -a")
[ul5255@speedy-gonzales micropython]$ git diff
diff --git a/stmhal/dac.c b/stmhal/dac.c
index e8ed752..545078f 100644
--- a/stmhal/dac.c
+++ b/stmhal/dac.c
@@ -223,7 +223,7 @@ STATIC mp_obj_t pyb_dac_triangle(mp_obj_t self_in, mp_obj_t freq) {
 STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_dac_triangle_obj, pyb_dac_triangle);
 
 /// \method write(value)
-/// Direct access to the DAC output (8 bit only at the moment).
+/// Direct access to the DAC output. Resolution is 12 bit.
 STATIC mp_obj_t pyb_dac_write(mp_obj_t self_in, mp_obj_t val) {
     pyb_dac_obj_t *self = self_in;
 
@@ -235,7 +235,7 @@ STATIC mp_obj_t pyb_dac_write(mp_obj_t self_in, mp_obj_t val) {
         self->state = 1;
     }
 
-    HAL_DAC_SetValue(&DAC_Handle, self->dac_channel, DAC_ALIGN_8B_R, mp_obj_get_int(val));
+    HAL_DAC_SetValue(&DAC_Handle, self->dac_channel, DAC_ALIGN_12B_R, mp_obj_get_int(val));
     HAL_DAC_Start(&DAC_Handle, self->dac_channel);
 
     return mp_const_none;

ul5255
Posts: 11
Joined: Thu Oct 23, 2014 9:08 am

DAC.write_timed() w/ 12 bit resolution

Post by ul5255 » Thu Feb 19, 2015 2:25 pm

I *think* I have a working version of DAC.write_timed() which supports
12 bit resolution (currently hard-coded).
So I set out to test it:

Code: Select all

# place jumper between X6 and X19
import array
import math

a = pyb.ADC(pyb.Pin.board.X19)
d = pyb.DAC(2)

# sine wave output buffer
b = array.array('H', [0]*100)
for i in range(len(b)):
    b[i] = 2048 + int(2047 * math.sin(2 * math.pi * i / len(b)))

# sampling buffer for one period of the signal
# we A/D convert w/ 'osr' times oversampling
osr = 1
s = array.array('H', [0]*osr*len(b))

# frequency
f = 10

# output sine wave at 10Hz
d.write_timed(b, f*len(b), mode=pyb.DAC.CIRCULAR)

# sample the sine wave with osr values per sine value
a.read_timed(s, f*len(s))
In short: I sample the generated wave form with one of the ADC pins.
I have provisions for a variable oversampling ratio but for the sake of
demonstration I set osr=1. What I would expect is an approximation of
the generated waveform which is phase-shifted because the DAC and
ADC are not triggered at the same time. What I get instead is a *partial* waveform like in
the attachment. I first thought that perhaps the DAC in circular mode
needs some time to jump back to the beginning of the buffer but this
seemed a bit unlikely. Then I went ahead and studied the code in
adc.c and lo and behold it seems to use the same TIM6 timer like
in the DAC.write_timed() implementation.
I will go back and flash a official firmware and redo that experiment
with the unmodified 8bit DAC but I am pretty sure right now one can
not use the timed functionality of ADC and DAC in parallel.
Attachments
chart.png
chart.png (29.85 KiB) Viewed 4463 times
Last edited by ul5255 on Tue Apr 28, 2015 11:03 am, edited 2 times in total.

ul5255
Posts: 11
Joined: Thu Oct 23, 2014 9:08 am

Back to the Drawing Board ...

Post by ul5255 » Thu Feb 19, 2015 3:07 pm

well, I reflashed the original firmware from 18-FEB-2015 and
ran this code:

Code: Select all

# place jumper between X6 and X19
import array
import math

a = pyb.ADC(pyb.Pin.board.X19)
d = pyb.DAC(2)

# sine wave output buffer
#b = array.array('H', [0]*100)
b = bytearray(100)
for i in range(len(b)):
#    b[i] = 2048 + int(2047 * math.sin(2 * math.pi * i / len(b)))
    b[i] = 128 + int(127 * math.sin(2 * math.pi * i / len(b)))

# sampling buffer for one period of the signal
# we ADC w/ 'osr' times oversampling
osr = 1
s = array.array('H', [0]*osr*len(b))

# frequency
f = 10

# output sine wave at 10Hz
d.write_timed(b, f*len(b), mode=pyb.DAC.CIRCULAR)

# sample the sine wave with osr values per sine value
a.read_timed(s, f*len(s))
This gives me a reasonable captured wave form like in
the attachment. I still think using the same timer TIM6
for ADC and DAC is a problem but it is not a straightforward
explanation for the garbled waveforms I captured with
my modified firmware ...
Attachments
chart-1.png
chart-1.png (18.06 KiB) Viewed 4459 times

ul5255
Posts: 11
Joined: Thu Oct 23, 2014 9:08 am

Spectra/Bug

Post by ul5255 » Sun Feb 22, 2015 4:12 pm

I went ahead and measured the DAC output spectrum of the unmodified MicroPython firmware. Setup is a Steinberg UR22 on the PyBoard pin X6, software is AudioMeter v. 3.6.1. I generated a 1kHz sine wave with the code from my previous post. The screenshot shows the output spectrum with lots of harmonics which are 45dBc and less. This is not stellar but in the ball park range for a 8bit DAC.
8bit-dac-unmodified-firmware.png
8bit-dac-unmodified-firmware.png (32.92 KiB) Viewed 4433 times
However as soon as I issue a

Code: Select all

ADC.read_timed()
on pin X19 (no jumper between X6 and X19) the signal from X6 is gone.
8bit-dac-unmodified-firmware-after-adc-read_timed.png
8bit-dac-unmodified-firmware-after-adc-read_timed.png (12.06 KiB) Viewed 4433 times
I consider the usage of the same timer TIM6 to be the problem here. I want to see if I can change DAC.c such that it works with timer 7 instead of timer 6 and see if that fixes the problem.

Post Reply