Sinusoid analog output implementation conflicts with Audio outputs

50 views
Skip to first unread message

Julien Carponcy

unread,
Nov 24, 2021, 9:59:20 AM11/24/21
to pyControl
Hello,

I was trying to figure out a way to control a LED (Plexon, separately powered by external power supply LEDD1B from Thorlabs) by applying voltage sinusoid to the LEDD1B. As things would be much simpler if i could generate them from the pyControl board (instead of having to rely on external DAC), i am currently trying to implement that. (I have already made the tiny amplifying circuit to turn 0-3.3V sinusoids into 0-5V ones, and that works well).

Figuring out my way into micropython docs, i am thus using examples provided here:

I managed so far to have it working, however I still have one big issue:

- if I use the audio board on port 4 (connected to DAC2), and send the sinusoid on DAC1, this does not work and output the sound rather than my much lower frequency sinusoid (which is fixed by removing audio outputs from the code).

This is very likely an issue that i don't understand, probably at the mixed between hardware and libraries level, and is fixed by just commenting every audio outputs of the task. I don't understand how signals from DAC1 can be seen on DAC2. Maybe there is also a conflict between the import of micropython originals that i used (DAC and array) and the same stuff already imported by the framework, but I don't see how they would differ. 

So far, beside commenting audio outputs, I tried to switch audio output and sinusoid ports, and also just to unplug the audio board itself without commenting, but that does not help, so it is not coming from sound generation or audio boards themselves. 
I have also updated to the latest version of pyControl.

I could use some help now, from someone who understand better the hardware, and the libraries, I guess. 

You will find attached the current version of my task file. if you search for "HELP" you will find the two lines  of speaker outputs for which commenting allow for correct sinusoid voltage generation.

Short term fix really appreciated, but I guess I will not be the only one in a more distant future trying to implement analog outputs signals from the board, so on the longer run, i hope it could lead to an easier implementation of that features both in the code and docs. Happy to contribute to the extent I will be able to.

Thank you very much in advance for your help, this community has already been very helpful to me very quickly, so I have no doubt it will be the case on this issue as well 


Best wishes, 

Julien
reaching_go_nogo_opto_sinusoid.py

alus...@gmail.com

unread,
Nov 24, 2021, 4:53:24 PM11/24/21
to pyControl
Hi Julien, 

I think what is happening is that both DACs are using Timer(6) as a trigger (more info here: https://docs.micropython.org/en/latest/library/pyb.DAC.html#pyb.DAC.write_timed). 
For short term solution, try specifying the Timer for the DAC writes. For example replace
dac.write_timed(buf, 2 * len(buf), mode=DAC.CIRCULAR)
with
dac2.write_timed(buf, pyb.Timer(7, freq=2 * len(buf)), mode=DAC.CIRCULAR)

I don't have access to a pyControl setup right now to confirm if this is the solution or not, but it may be worth a try. 
Also, this may get it working, but it probably isn't wise to hardcode using Timer(7). I think best practice would be to do something like hw.available_timers.pop() like what is used in the Audio_output class. 

-Andy

thoma...@neuro.fchampalimaud.org

unread,
Nov 24, 2021, 5:12:49 PM11/24/21
to pyControl
Hi Julien,

I think Andy is right -  the issue is probably happening because the DAC and the Audio_board are both trying to use the same Micropython hardware timer to clock their output.

The write_timed method of the pyb.DAC class takes a freq argument which can either be an integer specifying the frequency to write samples, or a Micropython pyb.Timer object initialised at the frequency.  If you provide an integer frequency it defaults to using timer number 6, so if you have two pyb.DAC objects both doing write_timed and both with the freq argument specified as an integer, they will both try and use the same Micropython Timer causing the output to be incorrect.  The pyControl Audio_board uses a pyb.DAC object and specifies the frequency as an integer, so when you try and do the same with annother DAC object in your task it does not work.

A workaround is to explicity instantiate a different timer object and use that with the DAC's write timed method.  The attached task file gives an example of how to do this.  As only some of the Micropython timers can be used with DACs I have specified the timer to use as timer 8, and then removed this timer from the list of available timers maintained by pyControl, to prevent pyControl from trying to use this timer for something else.

In the medium term it would be preferable to modify the pyControl audio module to avoid the user having to deal with this, and I have created an issue on github here for this.

best,

Thomas
DAC_test.py

Julien Carponcy

unread,
Nov 25, 2021, 5:54:39 AM11/25/21
to pyControl
Hi,

Thank you very much guys for your fast replies, it does make a great deal of sense with what i saw.
It is a noob mistake indeed, and did not pay enough attention to timer and I indeed missed that bit: 
 
"using Timer(6). Or it can be an already-initialised Timer object which is used to trigger the DAC sample. Valid timers are 2, 4, 5, 6, 7 and 8."

I fixed it and it works well now.

It is true that generally the pyControl framework make basic user "transparent" to hardware timer usage unlike arduino so i kind of forgot about them a bit.

I enjoy the thread to ask you a (probably?) not related issue regarding my sinusoidal voltage output of the board.
On some sinusoidal stim (of what i saw on the oscillo it appears only at low freq stimulation) my sinusoid start with one sample at max amplitude and then normally starts from 0 (again, watching with oscilloscope). It only happen occasionally but quite often for 2Hz stim and not that i saw for higher freq (but it might be harder to notice for me)
As a sanity check I print the buffer that i'm sending to the DAC and got the following numbers, alongside with the line of code which allow me to obtain them below. It may be that i'm doing something wrong with the math/programming of the trigonometric function but the output numbers looks roughly correct. 

Where do you think my max voltage sample at stim onset could come from ? I was thinking about the array type string in 12bit may be interpreted as a out of bounds value? Or maybe another interactions with task timers? I happen to begin to have quite a lot of these timers triggered (again, fortunately hardware timers are transparent to us when coding the task) and I ignore if they are dealt with properly in my task / not badly interacting with each other, as the all the other things regarding the tasks are running smoothly.

# create a buffer containing a sine-wave
buf = array('H', 2047 + int(2048 * math.sin(2 * math.pi * i / 1000)) for i in range(-249,750)) #  range(-249,750)) = phase offset introduced to make stim begin at 0V

array('H', [0, 0, 0, 0, 1, 1, 1, 2, 3, 4, 4, 5, 6, 7, 9, 10, 11, 13, 14, 16, 17, 19, 21, 23, 25, 27, 29, 31, 33, 36, 38, 41, 43, 46, 49, 52, 55, 58, 61, 64, 67, 70, 74, 77, 81, 84, 88, 92, 96, 100, 104, 108, 112, 116, 121, 125, 129, 134, 139, 143, 148, 153, 158, 163, 168, 173, 178, 184, 189, 194, 200, 206, 211, 217, 223, 229, 235, 241, 247, 253, 259, 265, 272, 278, 285, 291, 298, 305, 311, 318, 325, 332, 339, 346, 354, 361, 368, 376, 383, 391, 398, 406, 414, 421, 429, 437, 445, 453, 461, 469, 478, 486, 494, 503, 511, 520, 528, 537, 546, 555, 563, 572, 581, 590, 599, 608, 618, 627, 636, 646, 655, 664, 674, 684, 693, 703, 713, 722, 732, 742, 752, 762, 772, 782, 792, 802, 813, 823, 833, 844, 854, 865, 875, 886, 896, 907, 918, 928, 939, 950, 961, 972, 983, 994, 1005, 1016, 1027, 1038, 1050, 1061, 1072, 1083, 1095, 1106, 1118, 1129, 1141, 1152, 1164, 1176, 1187, 1199, 1211, 1222, 1234, 1246, 1258, 1270, 1282, 1294, 1306, 1318, 1330, 1342, 1354, 1366, 1378, 1390, 1402, 1415, 1427, 1439, 1451, 1464, 1476, 1488, 1501, 1513, 1526, 1538, 1551, 1563, 1576, 1588, 1601, 1613, 1626, 1638, 1651, 1664, 1676, 1689, 1702, 1714, 1727, 1740, 1753, 1765, 1778, 1791, 1804, 1816, 1829, 1842, 1855, 1868, 1880, 1893, 1906, 1919, 1932, 1945, 1957, 1970, 1983, 1996, 2009, 2022, 2035, 2047, 2059, 2072, 2085, 2098, 2111, 2124, 2137, 2149, 2162, 2175, 2188, 2201, 2214, 2226, 2239, 2252, 2265, 2278, 2290, 2303, 2316, 2329, 2341, 2354, 2367, 2380, 2392, 2405, 2418, 2430, 2443, 2456, 2468, 2481, 2493, 2506, 2518, 2531, 2543, 2556, 2568, 2581, 2593, 2606, 2618, 2630, 2643, 2655, 2667, 2679, 2692, 2704, 2716, 2728, 2740, 2752, 2764, 2776, 2788, 2800, 2812, 2824, 2836, 2848, 2860, 2872, 2883, 2895, 2907, 2918, 2930, 2942, 2953, 2965, 2976, 2988, 2999, 3011, 3022, 3033, 3044, 3056, 3067, 3078, 3089, 3100, 3111, 3122, 3133, 3144, 3155, 3166, 3176, 3187, 3198, 3208, 3219, 3229, 3240, 3250, 3261, 3271, 3281, 3292, 3302, 3312, 3322, 3332, 3342, 3352, 3362, 3372, 3381, 3391, 3401, 3410, 3420, 3430, 3439, 3448, 3458, 3467, 3476, 3486, 3495, 3504, 3513, 3522, 3531, 3539, 3548, 3557, 3566, 3574, 3583, 3591, 3600, 3608, 3616, 3625, 3633, 3641, 3649, 3657, 3665, 3673, 3680, 3688, 3696, 3703, 3711, 3718, 3726, 3733, 3740, 3748, 3755, 3762, 3769, 3776, 3783, 3789, 3796, 3803, 3809, 3816, 3822, 3829, 3835, 3841, 3847, 3853, 3859, 3865, 3871, 3877, 3883, 3888, 3894, 3900, 3905, 3910, 3916, 3921, 3926, 3931, 3936, 3941, 3946, 3951, 3955, 3960, 3965, 3969, 3973, 3978, 3982, 3986, 3990, 3994, 3998, 4002, 4006, 4010, 4013, 4017, 4020, 4024, 4027, 4030, 4033, 4036, 4039, 4042, 4045, 4048, 4051, 4053, 4056, 4058, 4061, 4063, 4065, 4067, 4069, 4071, 4073, 4075, 4077, 4078, 4080, 4081, 4083, 4084, 4085, 4087, 4088, 4089, 4090, 4090, 4091, 4092, 4093, 4093, 4093, 4094, 4094, 4094, 4094, 4095, 4094, 4094, 4094, 4094, 4093, 4093, 4093, 4092, 4091, 4090, 4090, 4089, 4088, 4087, 4085, 4084, 4083, 4081, 4080, 4078, 4077, 4075, 4073, 4071, 4069, 4067, 4065, 4063, 4061, 4058, 4056, 4053, 4051, 4048, 4045, 4042, 4039, 4036, 4033, 4030, 4027, 4024, 4020, 4017, 4013, 4010, 4006, 4002, 3998, 3994, 3990, 3986, 3982, 3978, 3973, 3969, 3965, 3960, 3955, 3951, 3946, 3941, 3936, 3931, 3926, 3921, 3916, 3910, 3905, 3900, 3894, 3888, 3883, 3877, 3871, 3865, 3859, 3853, 3847, 3841, 3835, 3829, 3822, 3816, 3809, 3803, 3796, 3789, 3783, 3776, 3769, 3762, 3755, 3748, 3740, 3733, 3726, 3718, 3711, 3703, 3696, 3688, 3680, 3673, 3665, 3657, 3649, 3641, 3633, 3625, 3616, 3608, 3600, 3591, 3583, 3574, 3566, 3557, 3548, 3539, 3531, 3522, 3513, 3504, 3495, 3486, 3476, 3467, 3458, 3448, 3439, 3430, 3420, 3410, 3401, 3391, 3381, 3372, 3362, 3352, 3342, 3332, 3322, 3312, 3302, 3292, 3281, 3271, 3261, 3250, 3240, 3229, 3219, 3208, 3198, 3187, 3176, 3166, 3155, 3144, 3133, 3122, 3111, 3100, 3089, 3078, 3067, 3056, 3044, 3033, 3022, 3011, 2999, 2988, 2976, 2965, 2953, 2942, 2930, 2918, 2907, 2895, 2883, 2872, 2860, 2848, 2836, 2824, 2812, 2800, 2788, 2776, 2764, 2752, 2740, 2728, 2716, 2704, 2692, 2679, 2667, 2655, 2643, 2630, 2618, 2606, 2593, 2581, 2568, 2556, 2543, 2531, 2518, 2506, 2493, 2481, 2468, 2456, 2443, 2430, 2418, 2405, 2392, 2380, 2367, 2354, 2341, 2329, 2316, 2303, 2290, 2278, 2265, 2252, 2239, 2226, 2214, 2201, 2188, 2175, 2162, 2149, 2137, 2124, 2111, 2098, 2085, 2072, 2059, 2047, 2035, 2022, 2009, 1996, 1983, 1970, 1957, 1945, 1932, 1919, 1906, 1893, 1880, 1868, 1855, 1842, 1829, 1816, 1804, 1791, 1778, 1765, 1753, 1740, 1727, 1714, 1702, 1689, 1676, 1664, 1651, 1638, 1626, 1613, 1601, 1588, 1576, 1563, 1551, 1538, 1526, 1513, 1501, 1488, 1476, 1464, 1451, 1439, 1427, 1415, 1402, 1390, 1378, 1366, 1354, 1342, 1330, 1318, 1306, 1294, 1282, 1270, 1258, 1246, 1234, 1222, 1211, 1199, 1187, 1176, 1164, 1152, 1141, 1129, 1118, 1106, 1095, 1083, 1072, 1061, 1050, 1038, 1027, 1016, 1005, 994, 983, 972, 961, 950, 939, 928, 918, 907, 896, 886, 875, 865, 854, 844, 833, 823, 813, 802, 792, 782, 772, 762, 752, 742, 732, 722, 713, 703, 693, 684, 674, 664, 655, 646, 636, 627, 618, 608, 599, 590, 581, 572, 563, 555, 546, 537, 528, 520, 511, 503, 494, 486, 478, 469, 461, 453, 445, 437, 429, 421, 414, 406, 398, 391, 383, 376, 368, 361, 354, 346, 339, 332, 325, 318, 311, 305, 298, 291, 285, 278, 272, 265, 259, 253, 247, 241, 235, 229, 223, 217, 211, 206, 200, 194, 189, 184, 178, 173, 168, 163, 158, 153, 148, 143, 139, 134, 129, 125, 121, 116, 112, 108, 104, 100, 96, 92, 88, 84, 81, 77, 74, 70, 67, 64, 61, 58, 55, 52, 49, 46, 43, 41, 38, 36, 33, 31, 29, 27, 25, 23, 21, 19, 17, 16, 14, 13, 11, 10, 9, 7, 6, 5, 4, 4, 3, 2, 1, 1, 1, 0, 0, 0, 0])


Thank you again and sorry for missing the timer line.

Best,

Julien
reaching_go_nogo_opto_sinusoid.py

thoma...@neuro.fchampalimaud.org

unread,
Nov 25, 2021, 4:20:30 PM11/25/21
to pyControl
Hi Julien,

I'm not sure exactly why those voltage spikes are occuring but I do think I know how to fix them.  The pyboard DACs have an op-amp buffer on the output which can optionally be enabled (see docs here).  When enabled the buffer reduces the output impedence of the DAC.  By default the buffer is enabled for DAC.write_timed() but disabled for DAC.write().  I think the spikes are occuring when the buffer is turned on, though I don't know why this would happen.  To completely disable the buffer you can use the argument bufferring=False when you initialise the DAC. 

In the attached task file, if I set buffering=None (the default) I get the voltage spikes you see, but if I set buffering=False they do not occur.

best,

Thomas
DAC_test2.py
Reply all
Reply to author
Forward
0 new messages