home.social

#dds — Public Fediverse posts

Live and recent posts from across the Fediverse tagged #dds, aggregated by home.social.

fetched live
  1. Потоковая запись ADC семплов на STM32

    В этом тексте я показал как настроить потоковую запись ADC семплов на микроконтроллере STM32. ADC модель это основа любого электронного измерения. Основа любого DMM. Всё что за корпусом микроконтроллера - это аналоговый мир. ADC это портал который позволяет аналоговым сигналам просачиваться в мир цифры.

    habr.com/ru/articles/1025090/

    #ADC #stm32 #stm32f407ve #SAR_ADC #stm #FIFO #DMA #GPIO #timer #dds

  2. Consumer Tech News (Feb 23-27): US–Taiwan Trade Tensions Escalates, Amazon Invests $12B In US & More Mark Kelly emphasized the rapid workplace impact of artificial intelligence and called for...

    #AAPL #AI #AMD #AMZN #ASML #BABA #CRM #DDS #DOCU #Equities #FFAI

    Origin | Interest | Match
  3. Consumer Tech News (Feb 23-27): US–Taiwan Trade Tensions Escalates, Amazon Invests $12B In US & More Mark Kelly emphasized the rapid workplace impact of artificial intelligence and called for...

    #AAPL #AI #AMD #AMZN #ASML #BABA #CRM #DDS #DOCU #Equities #FFAI

    Origin | Interest | Match
  4. meetup.com/dublin-data-science

    Run a local LLM
    Hosted by Mick C.
    Dublin Data Science
    Speaker: Dave Curran
    Thu Oct 30 6:30pm
    You want to have a classifier that you control the upgrade cycle of and doesn't send your data off to some big company. In this talk I will walk through the basics of how to get an LLM running on your local computer and show you how you can use it to speed up your work, help you code or make your photos easier to find.

    CHQ Building, Custom House Quay
    #DDS #LLM #DataScience

  5. Когда осязание встречает виртуальность: мультисенсорная обратная связь в VR через тактильные перчатки и ROS 2

    В статье подробно рассматривается опыт интеграции высокоточных тактильных перчаток в VR‑окружение при помощи ROS 2. Автор делится практическими наблюдениями, описывает архитектуру системы, принципы синхронизации данных и пример реализации на C++ и Python. Материал будет интересен тем, кто хочет заглянуть «под капот» реального прототипа мультисенсорного взаимодействия и избежать типичных ловушек в организации низкоуровневой передачи тактильных сигналов.

    habr.com/ru/articles/927908/

    #vr #ros2 #dds #тактильные_перчатки #синхронизация #мультисенсорная_обратная_связь #latency #haptics #unity #unreal

  6. Arduino and AY-3-8910 – Part 3

    I suggested in Part 2 that it might be possible to do some simple modulation of the amplitude of the AY-3-8910 channels rather than drive frequencies directly. This is taking a look at the possibilities of some kind of lo-fi direct digital synthesis using that as a basis.

    https://makertube.net/w/uCSiBG5RBufGqspoHMYFPt

    Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

    These are the key tutorials for the main concepts used in this project:

    If you are new to Arduino, see the Getting Started pages.

    Parts list

    • Arduino Uno.
    • AY-3-8910 chip.
    • Either GadgetReboot’s PCB or patch using solderless breadboard or prototyping boards.
    • 5V compatible MIDI interface.
    • Jumper wires.

    Direct Digital Synthesis on the AY-3-8910

    I’ve talked about direct digital synthesis before, so won’t go into full detail again. For more, see Arduino R2R Digital Audio – Part 3 and Arduino PWM Sound Output.

    But the top-level idea is to set the level of the signal according to a value in a wavetable. If this value is updated at a useful audio rate then it will be interpreted as sound.

    There are some pretty major limitations with attempting to do this on the AY-3-8910 however. The biggest one being that there are only 15 levels for the output on each channel.

    So I’ll be working to the following properties:

    • 4-bit resolution for the output.
    • 8-bit wavetable.
    • 8.8 fixed point accumulator to index into the wavetable.
    • 8096 Hz sample rate.

    YouTuber https://www.youtube.com/@inazumadenki5588 had a look at this and showed that the AY-3-8910 needs to be set up as follows:

    • Frequency value for the channel should be set to the highest frequency possible.
    • All channels should be disabled.

    This is due to comments in the datasheet stating that the only way to fully disable a channel is to have 0 in the amplitude field.

    Note: for a 8192 sample rate, that means writing out a sample to the AY-3-8910 registers approximately once every 124uS. With a 256 value wavetable, it takes almost 32 mS to write a complete cycle at the native sample rate, which would be around a 30 Hz output.

    I’m not sure what the largest increment that would still give a useful signal might be, but say it was 8 values from the wavetable, then that would make the highest frequency supported around 1kHz. Not great, but certainly audible, so worth a try.

    Setting up for DDS

    I want a regular, reliable, periodic routine to output the levels from the wavetable, and the usual way to achieve this is using a timer and interrupt. As Timer 1 is already in use to generate the 1MHz clock for the AY-3-8910, I’m going to be configuring Timer 2 as follows:

    • Timer 2 is an 8-bit timer.
    • Use prescalar of 32 which gives a 500kHz clock source (16MHz/32).
    • Use CTC (clear timer on compare) mode.
    • Generate a compare match interrupt.
    • Do not enable any output pins.

    The appropriate ATMega328 registers to enable this are:

      // COM2A[1:0] = 00  No output
    // WGM2[2:0] = 010 CTC mode
    // CS2[2:0] = 011 Prescalar=32
    ASSR = 0;
    TCCR2A = _BV(WGM21);
    TCCR2B = _BV(CS21) | _BV(CS20);
    TCNT2 = 0;
    OCR2A = 60;
    TIMSK2 = _BV(OCIE2A);

    Although it is worth noting that enabling OC1A can be quite useful for debugging. The following toggles the OC2A output (on D11) every time there is a compare match. The frequency seen on D11 will thus be half the anticipated sample frequency.

    pinMode(11, OUTPUT);
    TCCR2A |= _BV(COM2A0); // COM2A[1:0] = 01 for OC2A toggle

    And this does indeed generate a signal. Here is a trace showing a timing GPIO pin and the AY-3-8910 output.

    The problem is that this is meant to be a 440Hz sine wave, and whilst the shape isn’t too bad (it is a little distorted as the amplitude isn’t a true linear shape), the frequency is much nearer 100Hz than 440.

    Analysis of Performance

    The clue is the other trace, which is a timing pin being toggled every time the Interrupt routine is called. This is showing a 1kHz frequency, which means the IRS is being called with a 2kHz frequency rather than the anticipated 8192Hz. Curiously though I am getting an accurate 4kHz toggle on the timer output pin OC1A indicating the timer is correctly counting with a 8kHz frequency.

    No matter how I configured things, the interrupt routine just would not do anything at a faster rate. I had to drop the frequency right down to 2kHz to get the output pin and interrupt routing running together. This means that something in the interrupt routine seems to be taking ~ 450uS to run.

    After a fair bit of prodding and probing and checking the ATMega328 datasheet and double checking the register values, I have to conclude that the AY3891x library is just too slow at updating the registers for it to be able to run from the interrupt routine at this speed.

    Taking a look at the register write() function in the library, which I need to use to update the channel level, I can see the following is happening:

    void AY3891x::write(byte regAddr, byte data) {
    latchAddressMode(regAddr);
    daPinsOutput(data);
    noInterrupts();
    mode010to110();
    mode110to010();
    interrupts();
    daPinsInput();
    }

    void AY3891x::latchAddressMode(byte regAddr) {
    mode010to000();
    daPinsOutput(_chipAddress | regAddr); // Register address is 4 lsb
    mode000to001();
    mode001to000();
    mode000to010();
    }

    void AY3891x::daPinsOutput(byte data) {
    byte i;

    for (i = 0; i < NUM_DA_LINES; i++) {
    if (_DA_pin[i] != NO_PIN) pinMode(_DA_pin[i], OUTPUT);
    }

    for (i = 0; i < NUM_DA_LINES; i++) {
    if (_DA_pin[i] != NO_PIN) {
    digitalWrite(_DA_pin[i], data & 0x01);
    data = data >> 1;
    }
    }
    }

    void AY3891x::daPinsInput() {
    byte i;

    for (i = 0; i < NUM_DA_LINES; i++) {
    if (_DA_pin[i] != NO_PIN) pinMode(_DA_pin[i], INPUT);
    }
    }

    And every one of those modeXXXtoYYY() functions is a call to digitalWrite(), so I make that 22 calls to ditigalWrite() in order to write a single register value, plus around 16 calls to pinMode(). There are also 5 loops each looping over 8 values.

    One person measured the Arduino Uno digitalWrite() function and concluded that it takes 3.4uS to run, so that is a minimum of 75uS of processing in every run through the interrupt routine just for those calls alone. That doesn’t include the calls and other logic going on. It could easily be more than twice that when everything is taken into account.

    Dropping in some temporary pin IO either side of the call to the AY write function itself, and I’m measuring just over 250uS for the register update to happen, and that is just for one channel. This means that anything with a period of that or faster is starving the processor from running at all.

    Measuring the Basic Performance

    At this point I took a step back and created a free-running test sketch to really see what is going on.

    #include "AY3891x.h"

    AY3891x psg( 17, 8, 7, 6, 5, 4, 3, 2, 16, 15, 14);

    #define AY_CLOCK 9 // D9
    void aySetup () {
    pinMode(AY_CLOCK, OUTPUT);
    digitalWrite(AY_CLOCK, LOW);

    TCCR1A = (1 << COM1A0);
    TCCR1B = (1 << WGM12) | (1 << CS10);
    TCCR1C = 0;
    TIMSK1 = 0;
    OCR1AH = 0;
    OCR1AL = 7; // 16MHz / 8 = 2MHz Counter

    psg.begin();

    // Output highest frequency on each channel, but set level to 0
    // Highest freq = 1000000 / (16 * 1) = 62500
    psg.write(AY3891x::ChA_Amplitude, 0);
    psg.write(AY3891x::ChA_Tone_Period_Coarse_Reg, 0);
    psg.write(AY3891x::ChA_Tone_Period_Fine_Reg, 0);
    psg.write(AY3891x::ChB_Amplitude, 0);
    psg.write(AY3891x::ChB_Tone_Period_Coarse_Reg, 0);
    psg.write(AY3891x::ChB_Tone_Period_Fine_Reg, 0);
    psg.write(AY3891x::ChC_Amplitude, 0);
    psg.write(AY3891x::ChC_Tone_Period_Coarse_Reg, 0);
    psg.write(AY3891x::ChC_Tone_Period_Fine_Reg, 0);

    // LOW = channel is in the mix.
    // Turn everything off..
    psg.write(AY3891x::Enable_Reg, 0xFF);
    }

    int toggle;
    void setup() {
    pinMode(11, OUTPUT);
    toggle = LOW;
    digitalWrite(11, toggle);
    aySetup();
    }

    void loop() {
    toggle = !toggle;
    digitalWrite(11, toggle);
    for (int i=0; i<16; i++) {
    psg.write(AY3891x::ChA_Amplitude, i);
    }
    }

    All this is doing is continually writing 0 to 15 to the channel A level register whilst toggling a GPIO pin. Putting an oscilloscope trace on the IO pin and the AY-3-8910 channel A output gives me the following:

    This is running with a period of 6.96mS, meaning each cycle of 16 writes takes 3.5mS, giving me almost 220uS per call to the AY write function which seems to align pretty well with what I was seeing before.

    And this is generating an audible tone at around 280Hz, so regardless of any timer settings or waveform processing, this is going to be the baseline frequency on which everything else would have to rest, which isn’t great.

    Optimising Register Writes

    So at this point I have the choice of attempting to write to the AY-3-8910 myself using PORT IO to eliminate the time it takes for all those loops and digitalWrite() calls. Or I could try some alternative libraries.

    The library I’m using aims for the most portable compatibility: “This library uses the generic digitalWrite() function instead of direct port manipulation, and should therefore work across most, if not all, processors supported by Arduino, so long as enough I/O pins are available for the interface to the PSG.”

    It is a deliberate design choice, but does require all three bus control signals to be used: BDIR, BC1, BC2.

    Alternatives are possible with less pin state changes, but much stricter timing requirements. Some options include:

    The following are projects that have not used a library, but just done their own thing:

    Unfortunately none of these really solves the problem as the PCB I’m using does not neatly map onto IO ports to allow the use of direct PORT IO for the data.

    So to improve things whilst using this same PCB will require me to re-write the library myself.

    As a test however, it is possible to take the IO pin definitions used with the PCB and write a bespoke, optimised register write routine as follows:

    void ayFastWrite (byte reg, byte val) {
    // Mode=Addr Latch
    digitalWrite(BC1, HIGH);
    digitalWrite(BDIR, HIGH);

    // Latch address
    // NB: Addresses are all in range 0..15 so don't need to
    // worry about writing out bits 6,7 - just ensure set to zero
    PORTD = (PORTD & 0x03) | ((reg & 0xCF)<<2);
    PORTB = (PORTB & 0xFE);
    PORTC = (PORTC & 0xF7);

    // Mode = Inactive
    digitalWrite(BC1, LOW);
    digitalWrite(BDIR, LOW);

    delayMicroseconds(10);

    // Mode = Write
    digitalWrite(BC1, LOW);
    digitalWrite(BDIR, HIGH);

    // Write data
    PORTD = (PORTD & 0x03) | ((val & 0xCF)<<2); // Shift bits 0:5 to 2:7
    PORTB = (PORTB & 0xFE) | ((val & 0x40)>>6); // Shift bit 6 to 0
    PORTC = (PORTC & 0xF7) | ((val & 0x80)>>4); // Shift bit 7 to 3

    // Mode = Inactive
    digitalWrite(BC1, LOW);
    digitalWrite(BDIR, LOW);
    }

    I’m using the following mapping of data pins to Arduino digital IO pins to PORTS:

    DA0-DA5D2-D7PORTD Bits 0-5DA6D8PORT B Bit 0DA7A3/D17PORT C Bit 3

    To make this happen I have to ensure that the right bits are set to OUTPUTs and that BC2 is held HIGH prior to using the fastWrite function.

      digitalWrite(BC2, HIGH);
    DDRD |= 0xFC;
    DDRC |= 0x04;
    DDRB |= 0x01;

    This now improves on that previous 280Hz and gives me 1600Hz performance.

    So can I do any better? Well there are still between 6 and 8 calls to digitalWrite going on to handle the control signals…

    #define BC1LOW  {PORTC &= 0xFE;} // A0 LOW
    #define BC1HIGH {PORTC |= 0x01;} // A0 HIGH
    #define BC2LOW {PORTC &= 0xFD;} // A1 LOW
    #define BC2HIGH {PORTC |= 0x02;} // A1 HIGH
    #define BDIRLOW {PORTC &= 0xFB;} // A2 LOW
    #define BDIRHIGH {PORTC |= 0x04;} // A2 HIGH

    void ayFastWrite (byte reg, byte val) {
    // Mode=Addr Latch
    BC1HIGH;
    BDIRHIGH;

    // Latch address
    PORTD = (PORTD & 0x03) | ((reg & 0xCF)<<2);
    PORTB = (PORTB & 0xFE);
    PORTC = (PORTC & 0xF7);

    // Need 400nS Min
    delayMicroseconds(1);

    // Mode = Inactive
    BC1LOW;
    BDIRLOW;

    // Need 100nS settle then 50nS preamble
    delayMicroseconds(1);

    // Mode = Write
    BC1LOW;
    BDIRHIGH;

    // Write data
    PORTD = (PORTD & 0x03) | ((val & 0xCF)<<2); // Shift bits 0:5 to 2:7
    PORTB = (PORTB & 0xFE) | ((val & 0x40)>>6); // Shift bit 6 to 0
    PORTC = (PORTC & 0xF7) | ((val & 0x80)>>4); // Shift bit 7 to 3

    // Need 500nS min
    delayMicroseconds(1);

    // Mode = Inactive
    BC1LOW;
    BDIRLOW;

    // Need 100nS min
    }

    The timings come from the AY-3-8910 datasheet:

    The actual minimum and maximum timings for the various “t” values are given in the preceeding table. Most have a minimum value, but tBD has to be noted: the “associative delay time” is 50nS. This means that any changing of BC1, BC2 and BDIR has to occur within 50nS to be considered part of the same action.

    There is no means of having a nano-second delay (well, other than just spinning code), so I’ve just used a delayMicroseconds(1) here and there. This isn’t reliably accurate on an Arduino, but as I’m have delays of around half of that as a maximum it seems to be fine.

    This now gives me the following:

    This is now supporting a natural “as fast as possible” frequency of around 24kHz, meaning each call to the write function is now around 3uS. That is almost a 100x improvement over using all those pinMode and digitalWrite calls.

    The downside of this method:

    • It is ATMega328 specific.
    • It is specific to the pin mappings and PORT usage of this PCB.
    • It does not support reading or other chip operations between the writes.

    It is also interesting to see that the traces also show the high frequency oscillation (62.5kHz) that is being modulated regardless of the channel frequency and enable settings.

    DDS Part 2

    Success! At least with a single channel. This is now playing a pretty well in tune 440Hz A.

    Notice how the frequency of the timing pin is now ~4.2kHz meaning that the ISR is now indeed firing at the required 8192 Hz.

    Here is a close-up of the output signal. The oscilloscope was struggling to get a clean frequency reading, but this is one time I caught it reading something close! I checked the sound itself with a tuning fork (see video). It is indeed 440Hz.

    Find it on GitHub here.

    Closing Thoughts

    I wanted to get something put together to allow me to drive a DSS wavetable over MIDI, with different waveforms, and so on, but it turned out to be a little more involved getting this far than I anticipated, so I’ll leave it here for now.

    But hopefully filling in the gaps won’t take too long and will be the subject of a further post.

    Now that I have something that works, I’m actually quite surprised by how well it is working.

    Kevin

    #arduinoNano #ay38910 #dds #define #directDigitalSynthesis #include #midi
  7. Arduino and AY-3-8910 – Part 3

    I suggested in Part 2 that it might be possible to do some simple modulation of the amplitude of the AY-3-8910 channels rather than drive frequencies directly. This is taking a look at the possibilities of some kind of lo-fi direct digital synthesis using that as a basis.

    https://makertube.net/w/uCSiBG5RBufGqspoHMYFPt

    Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

    These are the key tutorials for the main concepts used in this project:

    If you are new to Arduino, see the Getting Started pages.

    Parts list

    • Arduino Uno.
    • AY-3-8910 chip.
    • Either GadgetReboot’s PCB or patch using solderless breadboard or prototyping boards.
    • 5V compatible MIDI interface.
    • Jumper wires.

    Direct Digital Synthesis on the AY-3-8910

    I’ve talked about direct digital synthesis before, so won’t go into full detail again. For more, see Arduino R2R Digital Audio – Part 3 and Arduino PWM Sound Output.

    But the top-level idea is to set the level of the signal according to a value in a wavetable. If this value is updated at a useful audio rate then it will be interpreted as sound.

    There are some pretty major limitations with attempting to do this on the AY-3-8910 however. The biggest one being that there are only 15 levels for the output on each channel.

    So I’ll be working to the following properties:

    • 4-bit resolution for the output.
    • 8-bit wavetable.
    • 8.8 fixed point accumulator to index into the wavetable.
    • 8096 Hz sample rate.

    YouTuber https://www.youtube.com/@inazumadenki5588 had a look at this and showed that the AY-3-8910 needs to be set up as follows:

    • Frequency value for the channel should be set to the highest frequency possible.
    • All channels should be disabled.

    This is due to comments in the datasheet stating that the only way to fully disable a channel is to have 0 in the amplitude field.

    Note: for a 8192 sample rate, that means writing out a sample to the AY-3-8910 registers approximately once every 124uS. With a 256 value wavetable, it takes almost 32 mS to write a complete cycle at the native sample rate, which would be around a 30 Hz output.

    I’m not sure what the largest increment that would still give a useful signal might be, but say it was 8 values from the wavetable, then that would make the highest frequency supported around 1kHz. Not great, but certainly audible, so worth a try.

    Setting up for DDS

    I want a regular, reliable, periodic routine to output the levels from the wavetable, and the usual way to achieve this is using a timer and interrupt. As Timer 1 is already in use to generate the 1MHz clock for the AY-3-8910, I’m going to be configuring Timer 2 as follows:

    • Timer 2 is an 8-bit timer.
    • Use prescalar of 32 which gives a 500kHz clock source (16MHz/32).
    • Use CTC (clear timer on compare) mode.
    • Generate a compare match interrupt.
    • Do not enable any output pins.

    The appropriate ATMega328 registers to enable this are:

      // COM2A[1:0] = 00  No output
    // WGM2[2:0] = 010 CTC mode
    // CS2[2:0] = 011 Prescalar=32
    ASSR = 0;
    TCCR2A = _BV(WGM21);
    TCCR2B = _BV(CS21) | _BV(CS20);
    TCNT2 = 0;
    OCR2A = 60;
    TIMSK2 = _BV(OCIE2A);

    Although it is worth noting that enabling OC1A can be quite useful for debugging. The following toggles the OC2A output (on D11) every time there is a compare match. The frequency seen on D11 will thus be half the anticipated sample frequency.

    pinMode(11, OUTPUT);
    TCCR2A |= _BV(COM2A0); // COM2A[1:0] = 01 for OC2A toggle

    And this does indeed generate a signal. Here is a trace showing a timing GPIO pin and the AY-3-8910 output.

    The problem is that this is meant to be a 440Hz sine wave, and whilst the shape isn’t too bad (it is a little distorted as the amplitude isn’t a true linear shape), the frequency is much nearer 100Hz than 440.

    Analysis of Performance

    The clue is the other trace, which is a timing pin being toggled every time the Interrupt routine is called. This is showing a 1kHz frequency, which means the IRS is being called with a 2kHz frequency rather than the anticipated 8192Hz. Curiously though I am getting an accurate 4kHz toggle on the timer output pin OC1A indicating the timer is correctly counting with a 8kHz frequency.

    No matter how I configured things, the interrupt routine just would not do anything at a faster rate. I had to drop the frequency right down to 2kHz to get the output pin and interrupt routing running together. This means that something in the interrupt routine seems to be taking ~ 450uS to run.

    After a fair bit of prodding and probing and checking the ATMega328 datasheet and double checking the register values, I have to conclude that the AY3891x library is just too slow at updating the registers for it to be able to run from the interrupt routine at this speed.

    Taking a look at the register write() function in the library, which I need to use to update the channel level, I can see the following is happening:

    void AY3891x::write(byte regAddr, byte data) {
    latchAddressMode(regAddr);
    daPinsOutput(data);
    noInterrupts();
    mode010to110();
    mode110to010();
    interrupts();
    daPinsInput();
    }

    void AY3891x::latchAddressMode(byte regAddr) {
    mode010to000();
    daPinsOutput(_chipAddress | regAddr); // Register address is 4 lsb
    mode000to001();
    mode001to000();
    mode000to010();
    }

    void AY3891x::daPinsOutput(byte data) {
    byte i;

    for (i = 0; i < NUM_DA_LINES; i++) {
    if (_DA_pin[i] != NO_PIN) pinMode(_DA_pin[i], OUTPUT);
    }

    for (i = 0; i < NUM_DA_LINES; i++) {
    if (_DA_pin[i] != NO_PIN) {
    digitalWrite(_DA_pin[i], data & 0x01);
    data = data >> 1;
    }
    }
    }

    void AY3891x::daPinsInput() {
    byte i;

    for (i = 0; i < NUM_DA_LINES; i++) {
    if (_DA_pin[i] != NO_PIN) pinMode(_DA_pin[i], INPUT);
    }
    }

    And every one of those modeXXXtoYYY() functions is a call to digitalWrite(), so I make that 22 calls to ditigalWrite() in order to write a single register value, plus around 16 calls to pinMode(). There are also 5 loops each looping over 8 values.

    One person measured the Arduino Uno digitalWrite() function and concluded that it takes 3.4uS to run, so that is a minimum of 75uS of processing in every run through the interrupt routine just for those calls alone. That doesn’t include the calls and other logic going on. It could easily be more than twice that when everything is taken into account.

    Dropping in some temporary pin IO either side of the call to the AY write function itself, and I’m measuring just over 250uS for the register update to happen, and that is just for one channel. This means that anything with a period of that or faster is starving the processor from running at all.

    Measuring the Basic Performance

    At this point I took a step back and created a free-running test sketch to really see what is going on.

    #include "AY3891x.h"

    AY3891x psg( 17, 8, 7, 6, 5, 4, 3, 2, 16, 15, 14);

    #define AY_CLOCK 9 // D9
    void aySetup () {
    pinMode(AY_CLOCK, OUTPUT);
    digitalWrite(AY_CLOCK, LOW);

    TCCR1A = (1 << COM1A0);
    TCCR1B = (1 << WGM12) | (1 << CS10);
    TCCR1C = 0;
    TIMSK1 = 0;
    OCR1AH = 0;
    OCR1AL = 7; // 16MHz / 8 = 2MHz Counter

    psg.begin();

    // Output highest frequency on each channel, but set level to 0
    // Highest freq = 1000000 / (16 * 1) = 62500
    psg.write(AY3891x::ChA_Amplitude, 0);
    psg.write(AY3891x::ChA_Tone_Period_Coarse_Reg, 0);
    psg.write(AY3891x::ChA_Tone_Period_Fine_Reg, 0);
    psg.write(AY3891x::ChB_Amplitude, 0);
    psg.write(AY3891x::ChB_Tone_Period_Coarse_Reg, 0);
    psg.write(AY3891x::ChB_Tone_Period_Fine_Reg, 0);
    psg.write(AY3891x::ChC_Amplitude, 0);
    psg.write(AY3891x::ChC_Tone_Period_Coarse_Reg, 0);
    psg.write(AY3891x::ChC_Tone_Period_Fine_Reg, 0);

    // LOW = channel is in the mix.
    // Turn everything off..
    psg.write(AY3891x::Enable_Reg, 0xFF);
    }

    int toggle;
    void setup() {
    pinMode(11, OUTPUT);
    toggle = LOW;
    digitalWrite(11, toggle);
    aySetup();
    }

    void loop() {
    toggle = !toggle;
    digitalWrite(11, toggle);
    for (int i=0; i<16; i++) {
    psg.write(AY3891x::ChA_Amplitude, i);
    }
    }

    All this is doing is continually writing 0 to 15 to the channel A level register whilst toggling a GPIO pin. Putting an oscilloscope trace on the IO pin and the AY-3-8910 channel A output gives me the following:

    This is running with a period of 6.96mS, meaning each cycle of 16 writes takes 3.5mS, giving me almost 220uS per call to the AY write function which seems to align pretty well with what I was seeing before.

    And this is generating an audible tone at around 280Hz, so regardless of any timer settings or waveform processing, this is going to be the baseline frequency on which everything else would have to rest, which isn’t great.

    Optimising Register Writes

    So at this point I have the choice of attempting to write to the AY-3-8910 myself using PORT IO to eliminate the time it takes for all those loops and digitalWrite() calls. Or I could try some alternative libraries.

    The library I’m using aims for the most portable compatibility: “This library uses the generic digitalWrite() function instead of direct port manipulation, and should therefore work across most, if not all, processors supported by Arduino, so long as enough I/O pins are available for the interface to the PSG.”

    It is a deliberate design choice, but does require all three bus control signals to be used: BDIR, BC1, BC2.

    Alternatives are possible with less pin state changes, but much stricter timing requirements. Some options include:

    The following are projects that have not used a library, but just done their own thing:

    Unfortunately none of these really solves the problem as the PCB I’m using does not neatly map onto IO ports to allow the use of direct PORT IO for the data.

    So to improve things whilst using this same PCB will require me to re-write the library myself.

    As a test however, it is possible to take the IO pin definitions used with the PCB and write a bespoke, optimised register write routine as follows:

    void ayFastWrite (byte reg, byte val) {
    // Mode=Addr Latch
    digitalWrite(BC1, HIGH);
    digitalWrite(BDIR, HIGH);

    // Latch address
    // NB: Addresses are all in range 0..15 so don't need to
    // worry about writing out bits 6,7 - just ensure set to zero
    PORTD = (PORTD & 0x03) | ((reg & 0xCF)<<2);
    PORTB = (PORTB & 0xFE);
    PORTC = (PORTC & 0xF7);

    // Mode = Inactive
    digitalWrite(BC1, LOW);
    digitalWrite(BDIR, LOW);

    delayMicroseconds(10);

    // Mode = Write
    digitalWrite(BC1, LOW);
    digitalWrite(BDIR, HIGH);

    // Write data
    PORTD = (PORTD & 0x03) | ((val & 0xCF)<<2); // Shift bits 0:5 to 2:7
    PORTB = (PORTB & 0xFE) | ((val & 0x40)>>6); // Shift bit 6 to 0
    PORTC = (PORTC & 0xF7) | ((val & 0x80)>>4); // Shift bit 7 to 3

    // Mode = Inactive
    digitalWrite(BC1, LOW);
    digitalWrite(BDIR, LOW);
    }

    I’m using the following mapping of data pins to Arduino digital IO pins to PORTS:

    DA0-DA5D2-D7PORTD Bits 0-5DA6D8PORT B Bit 0DA7A3/D17PORT C Bit 3

    To make this happen I have to ensure that the right bits are set to OUTPUTs and that BC2 is held HIGH prior to using the fastWrite function.

      digitalWrite(BC2, HIGH);
    DDRD |= 0xFC;
    DDRC |= 0x04;
    DDRB |= 0x01;

    This now improves on that previous 280Hz and gives me 1600Hz performance.

    So can I do any better? Well there are still between 6 and 8 calls to digitalWrite going on to handle the control signals…

    #define BC1LOW  {PORTC &= 0xFE;} // A0 LOW
    #define BC1HIGH {PORTC |= 0x01;} // A0 HIGH
    #define BC2LOW {PORTC &= 0xFD;} // A1 LOW
    #define BC2HIGH {PORTC |= 0x02;} // A1 HIGH
    #define BDIRLOW {PORTC &= 0xFB;} // A2 LOW
    #define BDIRHIGH {PORTC |= 0x04;} // A2 HIGH

    void ayFastWrite (byte reg, byte val) {
    // Mode=Addr Latch
    BC1HIGH;
    BDIRHIGH;

    // Latch address
    PORTD = (PORTD & 0x03) | ((reg & 0xCF)<<2);
    PORTB = (PORTB & 0xFE);
    PORTC = (PORTC & 0xF7);

    // Need 400nS Min
    delayMicroseconds(1);

    // Mode = Inactive
    BC1LOW;
    BDIRLOW;

    // Need 100nS settle then 50nS preamble
    delayMicroseconds(1);

    // Mode = Write
    BC1LOW;
    BDIRHIGH;

    // Write data
    PORTD = (PORTD & 0x03) | ((val & 0xCF)<<2); // Shift bits 0:5 to 2:7
    PORTB = (PORTB & 0xFE) | ((val & 0x40)>>6); // Shift bit 6 to 0
    PORTC = (PORTC & 0xF7) | ((val & 0x80)>>4); // Shift bit 7 to 3

    // Need 500nS min
    delayMicroseconds(1);

    // Mode = Inactive
    BC1LOW;
    BDIRLOW;

    // Need 100nS min
    }

    The timings come from the AY-3-8910 datasheet:

    The actual minimum and maximum timings for the various “t” values are given in the preceeding table. Most have a minimum value, but tBD has to be noted: the “associative delay time” is 50nS. This means that any changing of BC1, BC2 and BDIR has to occur within 50nS to be considered part of the same action.

    There is no means of having a nano-second delay (well, other than just spinning code), so I’ve just used a delayMicroseconds(1) here and there. This isn’t reliably accurate on an Arduino, but as I’m have delays of around half of that as a maximum it seems to be fine.

    This now gives me the following:

    This is now supporting a natural “as fast as possible” frequency of around 24kHz, meaning each call to the write function is now around 3uS. That is almost a 100x improvement over using all those pinMode and digitalWrite calls.

    The downside of this method:

    • It is ATMega328 specific.
    • It is specific to the pin mappings and PORT usage of this PCB.
    • It does not support reading or other chip operations between the writes.

    It is also interesting to see that the traces also show the high frequency oscillation (62.5kHz) that is being modulated regardless of the channel frequency and enable settings.

    DDS Part 2

    Success! At least with a single channel. This is now playing a pretty well in tune 440Hz A.

    Notice how the frequency of the timing pin is now ~4.2kHz meaning that the ISR is now indeed firing at the required 8192 Hz.

    Here is a close-up of the output signal. The oscilloscope was struggling to get a clean frequency reading, but this is one time I caught it reading something close! I checked the sound itself with a tuning fork (see video). It is indeed 440Hz.

    Find it on GitHub here.

    Closing Thoughts

    I wanted to get something put together to allow me to drive a DSS wavetable over MIDI, with different waveforms, and so on, but it turned out to be a little more involved getting this far than I anticipated, so I’ll leave it here for now.

    But hopefully filling in the gaps won’t take too long and will be the subject of a further post.

    Now that I have something that works, I’m actually quite surprised by how well it is working.

    Kevin

    #arduinoNano #ay38910 #dds #define #directDigitalSynthesis #include #midi
  8. "The DDS had struggled in recent years to stay at full strength, buffeted by what employees said was political infighting, hiring freezes, travel restrictions and an increasing number of bureaucratic layers. A watchdog audit released in May 2024 also found that former DDS directors had granted unauthorized waivers for certain tech tools. But every employee interviewed said they wouldn’t have left if it wasn’t for DOGE.

    One former senior Pentagon official, who asked not to be named because of possible retaliation, described DOGE’s wider incursion into the Defense Department as damaging and unproductive

    “They’re not really using AI, they’re not really driving efficiency. What they’re doing is smashing everything,” the former official said.

    At the DDS, “The best way to put it, I think, is either we die quickly or we die slowly,” Hay said."

    politico.com/news/2025/04/15/p

    #USA #Trump #Musk #DOGE #Pentagon #DoD #DDS

  9. "The DDS had struggled in recent years to stay at full strength, buffeted by what employees said was political infighting, hiring freezes, travel restrictions and an increasing number of bureaucratic layers. A watchdog audit released in May 2024 also found that former DDS directors had granted unauthorized waivers for certain tech tools. But every employee interviewed said they wouldn’t have left if it wasn’t for DOGE.

    One former senior Pentagon official, who asked not to be named because of possible retaliation, described DOGE’s wider incursion into the Defense Department as damaging and unproductive

    “They’re not really using AI, they’re not really driving efficiency. What they’re doing is smashing everything,” the former official said.

    At the DDS, “The best way to put it, I think, is either we die quickly or we die slowly,” Hay said."

    politico.com/news/2025/04/15/p

    #USA #Trump #Musk #DOGE #Pentagon #DoD #DDS

  10. Ленточные накопители в домашнем ПК

    Приветствую всех! Однажды перед каждым из нас встаёт вопрос: где хранить сотни гигабайт столь важной информации? Кто-то продлевает подписку в облаке, кто-то покупает ещё один винт, кто-то собирает NAS, а кто-то, как и я, присматривается к чуть более экзотическим решениям. Вдохновившись постом двухгодичной давности про серверное железо в обычном ПК, я решил, что самое время рассказать про ещё один атрибут подобных систем — стримеры. Если в прошлый раз я рассказывал про древний аппарат, то в сегодняшней статье поговорим про более современные экземпляры, а главное — про использование таких девайсов в самых обычных компьютерах. Заодно разберёмся, стоит ли пытаться так делать, как заставить всё это работать, какой экземпляр лучше, а на какие не стоит даже смотреть.

    habr.com/ru/companies/timeweb/

    #timeweb_статьи #стример #магнитная_лента #scsi #sas #pcie #qic #dds #dlt #sat #lto #троллейбус_из_буханки_хлеба

  11. For a while I’ve wondered if it was possible to find something that could be used to learn about the basics of analog synthesis. This is the first part of a series of posts looking at the possibilities.

    • Part 1 – This introduction and high-level design principles.
    • Part 2 – Detailed design of an ESP32 based PCB.
    • Part 3 – Software design.
    • Part 4 – Mechanical assembly and final use – todo.

    Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments! Please note that I am not an electronics person – I’m only dabbling.

    Introduction

    There are several educational synths on the market – just searching for “educational synthesizer” brings up many such hits, such as the Tangible Waves Synth Explorers or Erica Synth’s Bullfrog. And whilst these aren’t ridiculously priced when you consider the build quality and functionality, they are not really the kind of thing I had in mind.

    Another great looking one is the mki x es.EDU DIY synth kit which is a collaboration between Mortiz Klein and Erica Synths. This is particularly interesting because it comes with very comprehensive teaching guides for each module to be built, walking through the electronics principles and design process behind each module, which is great if you want to learn about synthesis at the same time as learning about electronics.

    There are also some interesting takes on the “synthesis as lego block” idea – Korg’s Little Bits springs to mind here, but I don’t think they are available anymore. But many devices in this category start to look a little like a toy even if quite fully featured (e.g. Blipblox) if not careful.

    There are some interesting “all in one modular in a box” type devices too – I quite like the look of the Korg Volca modular for this. Others that might fit here too might be some of the Bastl Instruments devices (such as the Kastle), or any one of a number of semi-modular devices, but the prices are going up (these are quality instruments in their own right) and so is their complexity in moving away from the “basics”.

    There are also some excellent looking online tools too (Ableton’s “Learning Synths” is excellent), but I wanted something tangible and tactile. Something more akin to the old spring-based electronics kits you used to be able to buy.

    The closest thing I’ve found is perhaps MiniMo – the “mini modular synth” project, and whilst I like the idea of a single hardware platform that can be reconfigured via software into different modules, it does make for a rather clumsy user interface.

    Another angle is the “DIY solder kit” synth like the Atari Punk Console or similar, but they are more for learning to solder not learning about synths.

    So this has got me wondering what it would take to produce something that could support the absolute basics of an analog synth in a cheap (so 100x100mm) PCB with through-hole components that would be suitable to linking up to a solderless breadboard for further experiments.

    This is the start of my “thinking out loud” in this space seeing as this is one of those wheels I’ve yet to have a go at reinventing.

    Basic Idea and High-level Requirements

    Fundamentally I’d like to have something that allows the tactile experience of plugging in cables and twiddling knobs for learning about signals and sounds in a very “immediate feedback” kind of way.

    It also has to be cheap and relatively easy to get hold of and DIY build. This is more important than quality of output I feel. It also means it won’t be an issue if something gets fried whilst experimenting – just build a new one.

    Ideally there would be enough self-contained functionality that it won’t need to be plugged into anything else. Which might mean it could have simplified protection on external electronic connections – which, let’s face it, would otherwise be pretty critical if used for educational purposes and meant to be connected to other equipment!

    To me this all implies some kind of pseudo-analog synth – the user interface presents like a simple, standard analog synth, but the internals don’t have to be analog at all. And considering how much I know of electronics myself, a microcontroller with some basic circuitry is the obvious choice for the insides…

    Whilst not meant to be connected to anything else, it would be useful to be able to measure the individual signals with a scope, so this means “proper” wired links between modules – it can’t be a “logical” connection – it has to be a real one ideally with real measurable voltages.

    Design Requirements

    So, chewing over the above, this is leading me to ponder some kind of microcontroller-based system with real physical jumper wires – so something like the MiniMo is a good model, but ideally it could be USB powered – I don’t want to be stuck with just batteries, although a single 9V might be useful option to have.

    I’d want it to look more modular synth-like than the MiniMo, but having said that I think it would be fine to have all that within a single panel if its fits. I’m now wondering how much I could get into a 100x100mm sized front panel which means the panels could be made cheaply by PCB manufacturers as well as the PCB itself. The main limitation I think, will be size of potentiometers. Assuming I’m using Dupont-style jumper wires to join the bits together, then I won’t need much space for jacks.

    It would be great to squeeze everything on a single 100×100 PCB too rather than have the hardware “user interface” on a stacked board, as you often see in modular designs. Working on the basis that this could go in a box (wood, 3d printed, laser cut, etc) then external connectors – power, MIDI IN (maybe), audio output, don’t have to be part of the front panel.

    Ideally I’d like enough space to support the following as a minimum:

    • Dual voltage-controlled oscillators to allow for basic FM synthesis or modulation.
    • Ideally the oscillators would produce several waveforms.
    • Low-frequency oscillator, again possibly with a couple of waveforms.
    • An envelope generator – ideally ADSR if there is room for that many pots!
    • Single voltage-controlled amplifier.
    • Some kind of filter. Possibly voltage controlled too, but manually controlled at least.

    It would be good if each module supported either pot or CV control.

    Things I’m not planning to support:

    • Polyphony!
    • Keyboard.
    • Noise and effects.
    • Complex mixing.
    • Default internal patching – if there are no wires, there will be no sound!

    I’m thinking it would be a basic, single-voice chain, triggered externally (CV/gate or MIDI or both) and a single mono audio output. Being able to drive it over USB MIDI would be a bonus but not essential I think.

    At some point it might be nice to be able to produce a second module to expand the capabilities – e.g. alternative modulation possibilities, additional channels, effects, noise generator, filters, mixer, etc.

    Basic Design Principles

    As already mentioned, given my lack of expertise in proper electronics, the easiest way forward for me is to use a microcontroller with some support analog circuitry, so here is my list of design principles so far:

    • Microcontroller based.
    • All jumper inter-module links are at the microcontroller’s basic logic level – so that would limit it to 5V or 3V.
    • All jumper inter-module links would be real 0 to VCC signals. This means the microcontroller would need good analog input and output facilities.
    • System could be USB powered, but an external 9V (battery or DC) should be an option.
    • External connections include: serial MIDI (optional), USB MIDI (possibly), audio line output, speaker (possibly).
    • There will be some higher quality (i.e. requiring higher sampling rates) analog signals for audio, and lower quality (i.e. lower sampling rates) signals for control lines. There will be some digital gate or trigger signals required too, working at the MCU logic level.

    The MiniMo uses an ATtiny85 for each module, so I have wondered about taking a similar approach – a single, small microcontroller per module. But there are so many other more powerful possibilities that I think a single microcontroller acting as a range of independent modules would be more useful and cost effective.

    Taking a cue from my Selecting Microcontrollers for Music page it is worth noting any boards that support a DAC or I2S, both very useful for audio applications. The number of ADCs is relevant too.

    Other hardware options might consider the use of:

    • Analog multiplexers, such as the MCP3008, CD4051, CD4067, etc.
    • SPI DACs such as the MCP48[012][12] range (8,10,12-bit DAC; single or dual format).
    • Digital potentiometers, such as the X9Cxxx or MCP41x2.

    Following on from my series of ESP32 experiments, I’ve been really impressed with the capabilities of the original ESP32 module, so I’ve decided to use that as the basis for my board. I’m going with the original as I think the option for two DACs onboard could be really useful.

    I did consider a Raspberry Pi Pico, but decided the lack of ADCs and DACs meant I was already “running uphill” just to get started. It is dual core, but so is the ESP32 and multi-tasking appears directly available from the Arduino IDE. The ESP32 also has a floating point unit which might come in handy too. Another alternative might be a more modern Arduino, but I’m going with the ESP32 for now.

    I’m not after a software-defined synthesizer – all the routing and signal processing will effectively be done via hardware patching – so any actual modules of code are likely to be pretty straight forward. For this reason, I’m not planning on using any of the audio frameworks I’ve mentioned before beyond basic library support. I’m just aiming for some basic direct digital synthesis on the ESP32.

    Closing Thoughts

    That is the essential concept sketched out. In the follow-up parts of the series of posts, I’ll get into the detail of what I’ve actually done.

    Watch this space.

    Kevin

    https://diyelectromusic.wordpress.com/2024/05/07/educational-diy-synth-thing/

    #dds #esp32

  12. I’m continuing my look into the ESP32 and PWM. This time I’m adding in some analog control to introduce an element of frequency modulation to the synthesis.

    • Part 1 – All the theory and research around PWM and the ESP32.
    • Part 2 – Generating different waveforms on multiple channels.
    • Part 3 – Introducing analog control and frequency modulation.

    Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

    If you are new to microcontrollers, see the Getting Started pages.

    Parts list

    • ESP32 WROOM Module
    • 3x 1kΩ resistor
    • 2x 10kΩ potentiometers
    • 1x 10uF electrolytic capacitor
    • 2x 100nF capacitor
    • 1x TRS socket
    • Breadboard and jumper wires

    The Circuit

    This expands on the previous circuit to allow me to feed back the output of one of the PWM channels into an analog input controlling the other.

    It also allows the use of two potentiometers to control the frequencies independently.

    It’s not very easy to see what is going on with the Fritzing diagram, but there are essentially four circuits in play here.

    The first is the PWM filter and audio output stage from parts 1 and 2. This takes the 0-3V3 PWM signal and turns it into a more audio friendly +/- 800mV (ish) signal.

    The second is the PWM filter stage but without the voltage divider to reduce the pp voltage and without the coupling capacitor to remove the DC offset. The output of this is therefore a waveform with a 0-3V3 sweep. I’m leaving it like this as I want to be able to use it as a control voltage for an analog input stage, which brings me on to…

    The third is a simple potentiometer connected to one of the analog inputs.

    The last is another potentiometer input but that can also be modulated by the output of the second PWM signal – the 0-3V3 one.

    Now I think I’m ok to connect the output of the PWM stage to the input in the way shown above, but at this stage it might be a good time to remind you I’m not an electronics person and it is best to assume I don’t know what I’m doing. I’m using cheap development boards and effectively throw-away amplification, so am feeling quite free to experiment.

    I don’t believe there is any way to connect a high signal source to a low signal sink without going through a resistor to limit current, so I think this is ok for this kind of experiment. Basically I don’t think I’m slowly cooking my ESP32, but don’t take my word for it…

    Naturally if this was accepting any kind of input signal from elsewhere some kind of protection circuitry would be required and I’ve given no consideration here to things like impedance. This is me messing around – nothing more, but if you can see something wrong in what I’m saying feel free to let me know in the comments.

    ESP32 GPIO Usage:

    • GPIO13 – Sine wave output – connected to the audio output.
    • GPIO12 – Saw wave output – not used above.
    • GPIO14 – Triangle wave output – connected to the analog input for the sine wave.
    • GPIO27 – Square wave output – not used above.
    • GPIO39 – Analog input to control triangle/square wave frequency.
    • GPIO36 – Analog input to control sine/saw wave frequency.

    The Code

    This is still using four separate PWM outputs but I’ve added in potentiometer control for them in pairs. As described above there is one pot to control the frequency of the sine/saw waves and one for the frequency of the triangle/square waves. But as also already stated, the sine/saw frequency can also be modulated by the triangle wave output.

    The key feature to get this working is to use an analog reading as an input to the setFreq() function from before for the individual channels.

    My loop, which was previously empty, now looks like this.

    #define NUM_ADC_PINS 2
    int adc_pins[NUM_ADC_PINS] = {36, 39};
    uint16_t adcval[NUM_ADC_PINS];

    void loop () {
    for (int i=0; i<NUM_ADC_PINS; i++) {
    uint16_t algval = analogRead(adc_pins[i]);
    if (algval != adcval[i]) {
    setPotFreq(i, algval);
    }
    adcval[i] = algval;
    }
    }

    I’ve just added in some mapping via the setPotFreq() function to determine which PWM pins are associated with which potentiometer.

    int pot2pwm[NUM_PWM_PINS] = {0,0,1,1};

    void setPotFreq (int pot, unsigned freq) {
    for (int i=0; i<NUM_PWM_PINS; i++) {
    if (pot2pwm[i] == pot) {
    setPwmFreq(i, freq);
    }
    }
    }

    To change which PWM channels map to which pots, just update the pot2pwm[] array of values. To have four pots and each channel independent is just a case of adding the two extra pins to the adc_pins[] array and then updating pot2pwm to be {0,1,2,3}.

    I’ve not gone that far as I wasn’t too fussed about adding the extra circuitry required – my solderless breadboard is getting a little crowded as it is.

    I’m using the “simple” analogRead() functionality which is fully Arduino compatible, albeit with a higher resolution (12-bits). The ESP32 also has the option for a faster, continuous reading of the ADCs via a “Continuous mode driver”. More details can be found here. I did wonder about experimenting with that at some point. It is interesting to consider if that could sample at a similar frequency to the PWM output, giving the possibility of some kind of audio processing, but that is a set of experiments for another day.

    I’ve also seen mention of using the I2S driver with the ADC or DAC, but I must admit I don’t quite understand what is being said there. Another thing added to the “to be read/researched” pile.

    But I’ve kept it simple for now and that seems to have done the trick.

    Find it on GitHub here.

    Closing Thoughts

    As can be seen in the video it is possible to get quite an interesting range of sounds out of this already!

    The obvious question in all this is why the external electrical feedback from one wave output to the other? That is the kind of thing that can be relatively simply done in software. Indeed, the Mozzi FM synthesis code I used in ESP32 and Mozzi does exactly that and more!

    In truth, I quite liked the idea of having more of a “virtual modular” feel in that the ESP32 can have several functions, in this case control voltage inputs and voltage-to-digitally controlled oscillators, that could be patched together using jumper wires.

    I’m not sure where I’m going next, but an obvious next step might be some kind of envelope generation using the DAC outputs to modulate the PWM audio.

    I really ought to do some proper thinking about those component values though. The filters are working at the frequencies I’m messing around with at the moment, but I’ve not really done any proper calculations to see what the optimal values ought to be. And it would be useful to include a little protection on the inputs and possibly some buffering on the outputs. At best I’m being rather simplistic and somewhat optimistic in my approach so far.

    But this is the point (as I’ve said before) where I really need some kind of audio experimenters PCB, which I’m already sort of chewing over in the background. Watch this space.

    Kevin

    https://diyelectromusic.wordpress.com/2024/04/03/esp32-and-pwm-part-3/

    #dds #esp32 #fmSynthesis #pwm #wavetable

  13. In this second part of my look into the ESP32 and PWM I’ve updated the code to expand to several channels to make a sort of (fixed) simple signal generator.

    • Part 1 – All the theory and research around PWM and the ESP32.
    • Part 2 – Generating different waveforms on multiple channels.
    • Part 3 – Introducing analog control and frequency modulation.

    Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

    If you are new to microcontrollers, see the Getting Started pages.

    Parts list

    • ESP32 WROOM Module
    • 2x 1kΩ resistor
    • 1x 10uF electrolytic capacitor
    • 1x 100nF capacitor
    • 1x TRS socket
    • Breadboard and jumper wires

    The Circuit

    This is using exactly the same circuit as in part 1, but I’m now configuring PWM on the following ESP32 pins for convenience: GPIO 13, 12, 14, 27.

    I’ve not done anything special to combine the outputs. For these tests, I’m just moving the wire that connects a PWM pin to the output filter circuit, so I can only have one output at a time.

    The Code

    I’ve updated the code to maintain 4 accumulators and increment counters and it now supports four different wavetables.

    The code is still using a simple, fixed frequency, but I have included an option to provide a set of frequency multipliers to each output if I want to experiment with producing harmonics. The multipliers and wavetables are pre-configured at the start of the code, but could relatively easily be made dynamically configurable in response to GPIO inputs.

    #define NUM_PWM_PINS 4
    int pwm_pins[NUM_PWM_PINS] = {13, 12, 14, 27};

    uint16_t acc[NUM_PWM_PINS];
    uint16_t inc[NUM_PWM_PINS];

    uint16_t mul[NUM_PWM_PINS] = {1,1,1,1};
    uint8_t *pWT[NUM_PWM_PINS] = {sinedata, sawdata, tridata, sqdata};

    The above configuration sets up each different wave on one of each of the four GPIO PWM output pins. The following configuration would use sine waves on all pins but giving the first four harmonics:

    uint16_t mul[NUM_PWM_PINS] = {1,2,3,4};
    uint8_t *pWT[NUM_PWM_PINS] = {sinedata, sinedata, sinedata, sinedata};

    Recall, I’m using a 256 value, 8-bit wavetable and set everything up for a 10MHz timer triggering to give me 32768Hz sample rate, with 8-bit resolution for the PWM, giving me a PWM frequency of just over 313kHz.

    I’ve now added (calculated) wavetables for saw, triangle and square wave outputs in addition to the pre-calculated sine table. The new wavetables are calculated as follows:

    uint8_t sawdata[NUM_SAMPLES];
    uint8_t tridata[NUM_SAMPLES];
    uint8_t sqdata[NUM_SAMPLES];

    void setupWavetables () {
    for (int i=0; i<NUM_SAMPLES; i++) {
    sawdata[i] = i;
    if (i<NUM_SAMPLES/2) {
    tridata[i] = i*2;
    sqdata[i] = 255;
    } else {
    tridata[i] = 255-(i-128)*2;
    sqdata[i] = 0;
    }
    }
    }

    To glue it all together, I’ve updated my ddsOutput routine to take a “channel” number and then in the PWM interrupt routine I just call ddsOutput for all channels.

    void ddsUpdate (int ch) {
    acc[ch] += inc[ch];
    ledcWrite (pwm_pins[ch], pWT[ch][acc[ch] >> 8]);
    }

    void ARDUINO_ISR_ATTR timerIsr (void) {
    for (int i=0; i<NUM_PWM_PINS; i++) {
    ddsUpdate(i);
    }
    }

    void setup () {
    for (int i=0; i<NUM_PWM_PINS; i++) {
    ledcAttach(pwm_pins[i], PWM_FREQUENCY, PWM_RESOLUTION);
    }
    }

    The sample code still uses a fixed test base frequency 440Hz tone for now.

    Find it on GitHub here.

    Closing Thoughts

    I thought this would be a lot more complicated than it was, but I guess all the essential details had been worked out last time.

    I’m now wondering if I want to provide some way of mixing the PWM outputs or if that is best done in software. It would be nice to add some IO to control everything, but I think that is probably the stage where it would be worth putting together some kind of ESP32 audio experimenter PCB!

    Kevin

    https://diyelectromusic.wordpress.com/2024/04/01/esp32-and-pwm-part-2/

    #dds #esp32 #pwm #wavetable

  14. ESP32 and PWM – Part 2

    In this second part of my look into the ESP32 and PWM I’ve updated the code to expand to several channels to make a sort of (fixed) simple signal generator.

    • Part 1 – All the theory and research around PWM and the ESP32.
    • Part 2 – Generating different waveforms on multiple channels.
    • Part 3 – Introducing analog control and frequency modulation.

    Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

    If you are new to microcontrollers, see the Getting Started pages.

    Parts list

    • ESP32 WROOM Module
    • 2x 1kΩ resistor
    • 1x 10uF electrolytic capacitor
    • 1x 100nF capacitor
    • 1x TRS socket
    • Breadboard and jumper wires

    The Circuit

    This is using exactly the same circuit as in part 1, but I’m now configuring PWM on the following ESP32 pins for convenience: GPIO 13, 12, 14, 27.

    I’ve not done anything special to combine the outputs. For these tests, I’m just moving the wire that connects a PWM pin to the output filter circuit, so I can only have one output at a time.

    The Code

    I’ve updated the code to maintain 4 accumulators and increment counters and it now supports four different wavetables.

    The code is still using a simple, fixed frequency, but I have included an option to provide a set of frequency multipliers to each output if I want to experiment with producing harmonics. The multipliers and wavetables are pre-configured at the start of the code, but could relatively easily be made dynamically configurable in response to GPIO inputs.

    #define NUM_PWM_PINS 4
    int pwm_pins[NUM_PWM_PINS] = {13, 12, 14, 27};

    uint16_t acc[NUM_PWM_PINS];
    uint16_t inc[NUM_PWM_PINS];

    uint16_t mul[NUM_PWM_PINS] = {1,1,1,1};
    uint8_t *pWT[NUM_PWM_PINS] = {sinedata, sawdata, tridata, sqdata};

    The above configuration sets up each different wave on one of each of the four GPIO PWM output pins. The following configuration would use sine waves on all pins but giving the first four harmonics:

    uint16_t mul[NUM_PWM_PINS] = {1,2,3,4};
    uint8_t *pWT[NUM_PWM_PINS] = {sinedata, sinedata, sinedata, sinedata};

    Recall, I’m using a 256 value, 8-bit wavetable and set everything up for a 10MHz timer triggering to give me 32768Hz sample rate, with 8-bit resolution for the PWM, giving me a PWM frequency of just over 313kHz.

    I’ve now added (calculated) wavetables for saw, triangle and square wave outputs in addition to the pre-calculated sine table. The new wavetables are calculated as follows:

    uint8_t sawdata[NUM_SAMPLES];
    uint8_t tridata[NUM_SAMPLES];
    uint8_t sqdata[NUM_SAMPLES];

    void setupWavetables () {
    for (int i=0; i<NUM_SAMPLES; i++) {
    sawdata[i] = i;
    if (i<NUM_SAMPLES/2) {
    tridata[i] = i*2;
    sqdata[i] = 255;
    } else {
    tridata[i] = 255-(i-128)*2;
    sqdata[i] = 0;
    }
    }
    }

    To glue it all together, I’ve updated my ddsOutput routine to take a “channel” number and then in the PWM interrupt routine I just call ddsOutput for all channels.

    void ddsUpdate (int ch) {
    acc[ch] += inc[ch];
    ledcWrite (pwm_pins[ch], pWT[ch][acc[ch] >> 8]);
    }

    void ARDUINO_ISR_ATTR timerIsr (void) {
    for (int i=0; i<NUM_PWM_PINS; i++) {
    ddsUpdate(i);
    }
    }

    void setup () {
    for (int i=0; i<NUM_PWM_PINS; i++) {
    ledcAttach(pwm_pins[i], PWM_FREQUENCY, PWM_RESOLUTION);
    }
    }

    The sample code still uses a fixed test base frequency 440Hz tone for now.

    Find it on GitHub here.

    Closing Thoughts

    I thought this would be a lot more complicated than it was, but I guess all the essential details had been worked out last time.

    I’m now wondering if I want to provide some way of mixing the PWM outputs or if that is best done in software. It would be nice to add some IO to control everything, but I think that is probably the stage where it would be worth putting together some kind of ESP32 audio experimenter PCB!

    Kevin

    #dds #esp32 #pwm #wavetable

  15. I’m continuing reinventing wheels with my ESP32. In ESP32 and Mozzi I was using the built-in DAC for audio output. In this post I’m taking a detailed look at how to use PWM for audio output instead.

    • Part 1 – All the theory and research around PWM and the ESP32.
    • Part 2 – Generating different waveforms on multiple channels.
    • Part 3 – Introducing analog control and frequency modulation.

    Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

    These are the key tutorials for the main concepts used in this project:

    If you are new to microcontrollers, see the Getting Started pages.

    Parts list

    • ESP32 WROOM Module
    • 2x 1kΩ resistor
    • 1x 10uF electrolytic capacitor
    • 1x 100nF capacitor
    • 1x TRS socket
    • Breadboard and jumper wires

    ESP32 PWM

    The ESP32 has a PWM peripheral in the shape of the LED PWM Controller – LEDC. This is fully detailed in section 14 of the ESP32 Technical Reference Manual (“LED PWM Controller (LEDC)”)and has the following key features:

    • There are 8 high-speed channels and 8 low-speed channels.
    • There are 4 high-speed timers and 4 low-speed timers.
    • This makes a total of 16 PWM channels.
    • Any OUTPUT GPIO pin can be configured via the IO_MUX to connect to any of these 16 PWM channels.

    This makes for a very flexible PWM scheme.

    On my third party ESP32 WROOM development board, this means that all but four GPIO pins can be configured for PWM output. The four that can’t are INPUT only pins (34, 35, 36, 39).

    My plan is to configure several PWM audio channels using the same high speed timer and map those onto GPIO pins to generate independent PWM audio outputs that can then be externally mixed.

    The PWM peripherals can be linked to either or two clock sources. I’m planning to use the 80MHZ ABP_CLK (system application peripheral clock, which is derived from the system clock which can be up to 240MHz for the ESP32). It also supports the use of a clock divider, but I’m not planning on using it directly.

    There is one key tradeoff when using the LEDC however – PWM resolution vs PWM frequency. As the resolution increase the available range of frequencies decreases. This is all fully explained in the reference manual and there are several equations provided to work out the appropriate values.

    The PWM audio library provides an additional equation to calculate the PWM frequency from the desired resolution:

    The two sets of equations don’t seem to fully tally though from what I can see… I suspect there is some rounding going on with the provided “commonly-used frequencies and resolutions” table in the reference manual.

    Anyway, here are some sample resolutions and frequencies that I’m considering:

    8 bits (0 .. 255)312 kHz9 bits (0 .. 511)156 kHz10 bits (0 .. 1023)78 kHz11 bits (0 .. 2047)39 kHz12 bits (0 .. 4095)19 kHz

    Any of these should be fine.

    Esspresif’s Arduino ESP32 Core

    A note on versions of the Arduino ESP32 Core…

    At the time of writing the last official release was dated October 2023 at version 2.0.14. However there appear to have been a number of API changes since then that are reflected in the latest documentation which puts it quite out of sync with the SDK itself.

    Rather than attempt to find the documentation associated with the last official release, I switched over to the “development” strand of the core by changing the board manager URL I used.

    At the time of writing I’ve installed the “3.0.0-alpha3” version… wish me luck!

    ESP32 PWM Libraries for Audio

    As mentioned, the PWM peripheral on the ESP32 is the LEDC. There appear to be several levels of library/API associated with producing audio signals via PWM on the LEDC on an ESP32:

    As a general rule, the Arduino default PWM library isn’t really suitable for audio output, so PWM for audio on an Arduino tends to be done independently.

    The Espressif PWM IoT Solution Audio API does provide a relatively straight forward way to configure and use the LEDC peripheral on an ESP32 for PWM audio. But it doesn’t look like it was designed for use with Arduino, but the ESP-IDF.

    The Arduino ESP32 LEDC library includes additional functions for producing tones for simpler use-cases too.

    What isn’t clear to me however is if it is possible to configure a single PWM peripheral with several audio output channels via these APIs so I might end up using the Espressif SDK LEDC API directly myself.

    Checking PWM Frequencies

    One of the example sketches provided with the ESP32 core will run through all possible frequency combinations and print out a table of valid frequencies. Find it under Examples -> ESP32 -> AnalogOut -> ledcFrequency

    There will be loads of “invalid frequency” messages scrolling past the serial monitor (default baud is 115200) whilst it runs through all the values, but at the end there will be a table something like the following, which was the output for me:

    Bit resolution | Min Frequency [Hz] | Max Frequency [Hz]
    1 | 489 | 40078277
    2 | 245 | 20039138
    3 | 123 | 10019569
    4 | 62 | 5009784
    5 | 31 | 2504892
    6 | 16 | 1252446
    7 | 8 | 626223
    8 | 4 | 313111
    9 | 2 | 156555
    10 | 1 | 78277
    11 | 1 | 39138
    12 | 1 | 19569
    13 | 1 | 9784
    14 | 1 | 4892
    15 | 2 | 2446
    16 | 1 | 1223
    17 | 1 | 611
    18 | 2 | 305
    19 | 1 | 152
    20 | 1 | 76

    As we can see, this matches the aforementioned calculation pretty closely.

    PWM on the ESP32 in Practice

    I’m going to be using the LEDC peripheral for the PWM output and one of the general purpose timers to trigger a direct digital synthesis process to output the values from a wavetable.

    PWM can be configured as follows:

    ledcAttach(PWM_PIN, PWM_FREQUENCY, PWM_RESOLUTION);

    Recall that any OUTPUT pin can be used for PWM output.

    The timer to output samples can be configured as follows:

    #define TIMER_FREQ 10000000
    #define TIMER_RATE 305
    timer = timerBegin(TIMER_FREQ);
    timerAttachInterrupt(timer, &timerIsr);
    timerAlarm(timer, TIMER_RATE, true, 0);

    So what’s going on here? This is configuring a timer to run at 10,000,000 Hz, triggering an alarm every 305 “ticks” (so every 30.5uS), which in turn will trigger an interrupt running the timerIsr() routine.

    Why 305 ticks? Well this is to give me a sample rate of 32768Hz which has a period of 1 / 32768 = 30.5uS, so I need to be outputting a sample every 30.5uS to keep up.

    I’ve chosen that sample rate as I have a 256 byte wavetable so I wanted a sample rate that was a multiple of 256 to keep calculations easy.

    As with previous DDS projects, I have an accumulator which provides the index into the wavetable of the sample to play, and an increment which moves through the table in a way to give me the required frequency of output.

    I’m using 8.8 fixed point arithmetic again as that gives additional accuracy for the accumulator whilst making it very easy to scale to use as an index. Also, by using a unsigned 16-bit type for my 8.8 fixed point format I get automatic wrapping around of the accumulator too.

    # This code runs at the sample rate and assumes a 256 byte wavetable
    acc += inc;
    sample = wavetable[acc>>8];

    So how is the increment calculated from the required frequency? Again this is a calculation I’ve used many times now:

    // For direct digital synthesis from a wavetable
    // we have an accumulator to store the index into
    // the table and an increment based on the sample
    // rate and frequency.
    // Increment = Freq * (Number of Samples in wavetable / Sample Rate)
    // Increment = Freq * (256 / 32768)
    // Increment = Freq / 128
    //
    // But using a 8.8 fixed-point accumulator and increment:
    // Increment = 256 * Freq / 128
    // Increment = Freq * 2
    //
    #define FREQ2INC(f) (f*2)
    uint16_t acc;
    uint16_t inc;
    void setFreq (unsigned freq) {
    inc = FREQ2INC(freq);
    }

    Once the sample has been calculated it can be written out to the PWM hardware using:

    ledcWrite (PWM_PIN, sample);

    Choosing the PWM resolution and frequency

    As discussed, there is a tradeoff between resolution and PWM frequency. As I only have a 8-bit wavetable (i.e. the sinewave is defined with values between 0 and 255) then additional resolution is probably a little over the top.

    There are some additional considerations however. The full resolution will be equivalent to the full 3V3 range of the output voltage, so one easy way to get (approximate) audio line level outputs is to configure 10-bit resolution, but then only use my 8-bit wave table. This means that 10-bits (4095) corresponds to 3V3, but my highest value of 255 is only a quarter of that, giving me less then 1V peak to peak. But the slower frequency gives a less cleaner output.

    Sticking with an 8-bit (255) resolution gives me the full 3V3 range peak to peak and a much clearer output with the higher PWM frequency.

    In the following, the first is the 10-bit output and the second is the 8-bit output.

    I’ve gone with the 8-bit version, but if I want to end up with audio line-levels I will need a voltage divider to bring the voltage down from 3V3.

    The Circuit

    I’ve used the above filter circuit for the PWM output. The 1k/1k resistor divider drops the 3V3 peak to peak output down to half that. Then taking an equivalence value of 500Ω into a filter calculator (details here) with a 100nF value gives me a cutoff value of just over 3kHz. That could be considered a little low for audio frequencies, but is fine for my purposes right now.

    The Code

    The final code puts all this together with the following key functions:

    • setFreq – calculates and sets the increment for the required frequency.
    • ddsUpdate – calculates the next sample value and outputs it to the PWM hardware. It is called by the timer interrupt routine.

    As previously mentioned, I’m using a 256 value, 8-bit sine table and set everything up for a 10MHz timer triggering to give me 32768Hz sample rate, with 8-bit resolution for the PWM, giving me a PWM frequency of just over 313kHz.

    The sample code just uses a fixed test frequency 440Hz tone for now.

    Find it on GitHub here.

    Closing Thoughts

    When I started looking at this, I wasn’t quite sure how all the apparent complex parts fitted together, but actually once you get into it, it is relatively straight forward to set up audio PWM on an ESP32.

    The higher PWM frequencies in particular allow for a very smooth output. It isn’t as clean as the DAC of course, but it isn’t bad. It certainly looks like it would be fine for most of what I’d want to do with it.

    I’d now like to see what might be involved in getting several PWM outputs simultaneously, possibly with different waveforms, to make a simple signal generator for audio frequencies and levels.

    Kevin

    https://diyelectromusic.wordpress.com/2024/03/31/esp32-and-pwm/

    #dds #define #esp32 #pwm

  16. Стримеры. Эволюция ленточных накопителей от каменного века до наших дней

    Сейчас уже доподлинно неизвестно, кто именно первым догадался перетереть зерно в муку и испечь из нее хлеб, или взбить молоко, чтобы получить масло. Зато историки хорошо знают, кому пришло в голову нанести смесь растертого в порошок железа и клея на немагнитную основу для записи информации — это сделал в 1898 году датский инженер Вальдемар Поульсен. Он же изобрел звукозаписывающее устройство под названием «телеграфон», использовавшее вместо магнитной ленты проволоку.

    habr.com/ru/companies/serversp

    #UNIVAC #стример #IBM #System/360 #DECTape #Linear_TapeOpen_(LTO) #DDS #АрВид #QIC #DLT

  17. This is actually a collection of previous projects with the code tidied up a little and combined to show a single sketch that can be configured for either PWM, an R2R ladder or the MCP4725 I2C DAC.

    There isn’t really anything here that hasn’t been talked about before somewhere, but hopefully this can act as a single reference point for a range of direct digital synthesis techniques from now on.

    Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

    These are the key Arduino tutorials for the main concepts used in this project:

    If you are new to Arduino, see the Getting Started pages.

    Parts list

    The Circuit

    There is no specific circuit dedicated to this post, but it works well with the two audio experimenter PCBs listed above, which each contain options for the following:

    • PWM audio output on either D3 or D9.
    • R2R resistor DAC (in the case of the Uno experimenter PCB).
    • MPC4725 I2C DAC.

    Alternatively, the main elements for PWM operation can be put together on a solderless breadboard as follows.

    The Code

    As already mentioned the core elements of the code have largely been met before, but the main sections are described below.

    The general theory of direct digital synthesis operation is described fully in Arduino R2R Digital Audio – Part 3 so I won’t go over that again.

    This code implements simple additive synthesis by using potentiometers to set the amplitudes for a set of sine wave harmonics. The performance of the Arduino largely limits this to being a maximum of six sine waves to be added up, but that is enough for some simple experimentation.

    The code configures the first six harmonics: fundamental (f), f*2, f*3, f*4, f*5, f*6. There are some default ranges that can be used for testing without pots:

    #define SC  32
    int sine[MAXPOTS] = {SC*2,0,0,0,0,0};
    int saw[MAXPOTS] = {SC,SC/2,SC/4,SC/8,SC/16,SC/32};
    int squ[MAXPOTS] = {SC,0,SC/2,0,SC/4,0};

    There is no way to set the fundamental frequency – it is fixed at 440Hz. Making that controllable is left as an exercise for another day! The natural options are MIDI triggering or another pot.

    The basic properties of the synthesis code are as follows:

    • The sample rate depends on the technique, but in principle it could support 4096Hz, 8192Hz, 16384Hz or 32768Hz.
    • It uses a 256 entry, 8-bit wavetable to define the basic sine wave.
    • It uses 8.8 fixed point accumulators with the top 8-bits as the index into the wave table.
    • It uses a 16-bit sample value which is scaled down as required by the audio output method.

    The general pattern used in this code is as follows:

    // Audio output specific functions:
    dacSetup ()
    dacWrite (value)
    dacScan ()

    // Generic audio functions:
    dacPlayer ()
    Call dacWrite (last calculated sample value)
    For each potentiometer:
    Update accumulator for the DDS
    Add potval * sinetable[accumulator>>8] to the total

    setup ()
    Call dacSetup ()

    loop ()
    Call dacScan ()
    Every 10 loops update the pots

    Each audio output option will implement the three functions dacSetup, dacWrite and dacScan, but not all need to be used. Conditional compilation is used to select between audio output options by defining one of PWM_OUTPUT, DAC_OUTPUT or R2R_OUTPUT.

    Here are some notes for each option.

    PWM:

    • Output scaled to 8-bits for use with PWM.
    • PWM is configured to run at 65536Hz.
    • The TIMERn_OVF_vect interrupt is used to trigger sample updating via dacPlayer().
    • All four sample rates are possible so samples are not written out on every interrupt. For example, for a sample rate of 16384Hz a sample is written out on every fourth interrupt.
    • Can support either D9 (Timer 1) or D3 (Timer 2).
    • As updates are interrupt driven, dacScan () is empty.

    R2R:

    • Uses D8-D9, D2-D7 as bits 0 to 7 for the DAC output.
    • Output scaled to 8-bits for use.
    • PORT I/O is used to write to the data lines.
    • Code takes into account the fact that D0/D1 might be in use as the UART.
    • Updates are interrupt driven using the TimerOne library, calling dacPlayer() directly.
    • As updates are interrupt driven, dacScan () is empty.

    MCP4725:

    • The I2C address for the DAC is configured by defining MCP4725ADDR. It defaults to 0x60.
    • As the DAC can’t be written to from an interrupt routine, the output is set during dacScan() so the loop() has to run as fast as possible.
    • The sample rate is set by monitoring the micros() tick (note on an Arduino the resolution is 4uS at best).
    • Uses the non-blocking I2C library and fast analog read from Mozzi.
    • Uses the MCP4725 fast write mode, which only requires two bytes to be sent to the DAC.
    • The maximum sample rate is 8192Hz and even then it runs a little slow (i.e. the 440Hz tone is flat by around a semitone).

    General comments:

    • There is an optional timing pin that is configured by defining TIMING_TEST. This is toggled in dacPlayer().
    • There is an optional fixed set of amplitudes that can be used instead of potentiometers. These are set up in setDefaultAmplitudes() when DAC_TEST is defined.
    • The maximum number of pots supported is 6. The code skips using A4/A5 as these map onto I2C if the DAC is used. The number of pots to scan (and hence sine waves to add up) can be reduced by setting NUMPOTS to a number less than MAXPOTS (which is 6).
    • If the number of pots is reduced, then the scaling factors used to calculating the totals can be adjusted by changing SC and PSC accordingly. For 6 pots/waves they are set to 32 and 4 respectively. This means that analogReads have a maximum range of 0..31 which is set by
    val = analogRead(pot) >> PSC;

    Find it on GitHub here.

    Closing Thoughts

    This has been interesting to revisit. After all my experiments this is starting to finally make some sense. It has been interesting to contrast the three output methods both in terms of their computational performance and in terms of output waveform quality.

    The photo at the start shows the R2R output of the test sine wave. The photo below is the PWM output for a potentiometer-driven saw.

    This has also prompted me to revisit my Arduino PWM Output Filter Circuit and finally work out how to properly combine a low-pass filter and potential divider and still get something approximating the filter characteristics I wanted. I feel I understand quite a bit more about what is going on now.

    Now if I could just get a bit of a handle on impedance I might actually start to feel like I know a little about what I’d be talking about….

    Kevin

    https://diyelectromusic.wordpress.com/2024/03/06/arduino-direct-digital-additive-synthesis/

    #additiveSynthesis #arduinoNano #arduinoUno #dac #dds #mcp4725 #pwm #r2r

  18. #DDS (Dzisiaj Dowiedziałem Się): Apple w aplikacji Reminders używa otwartego standardu VTODO i synchronizuje je do kalendarza. Standard też wspiera Thunderbird oraz (prawdopodobnie) Planify na Linuxa (tego nie mam jak tymczasowo sprawdzić, ale kod sugeruje, że jest jakaś przynajmniej szczątkowa implementacja). Na telefonie można do tego skorzystać z aplikacji jtxBoard (na F-Droidzie) i synchronizacji dzięki DAVx5 (również na F-Droidzie)

    jeszcze tylko odkryć jakie aplikacje wspierają VJOURNAL (wspierane przez jtxBoard) i może zrezygnuję z części aplikacji i uproszczę sobie życie

  19. Gefeliciteerd @waag @marleenstikker met 30 jaar #DeDigitaleStad. #DDS
    The first free online community in the world, an early predecessor of social media.

  20. Gefeliciteerd @waag @marleenstikker met 30 jaar #DeDigitaleStad. #DDS
    The first free online community in the world, an early predecessor of social media.

  21. Glædelig fredag. Idag og weekenden står på #spejder . turen går til korpsrådsmøde #krm med det danske spejderkorps #dds på hotel #Legoland i Billund. Jeg glæder mig altid til denne weekend. Det føles lidt som en stor familie der samles og demokratiet i korpset kan tydeligt mærkes. I år er der valg til bestyrelsen og det glæder jeg mig til. Også på den spændende diskussion vi skal have omkring vedtægtsændringer bla omkring formand/forperson. Personligt hælder jeg mest til ordfører for bestyrelse. Det føler jeg ruller bedre på tungen. 🫶😁

  22. Glædelig fredag. Idag og weekenden står på #spejder . turen går til korpsrådsmøde #krm med det danske spejderkorps #dds på hotel #Legoland i Billund. Jeg glæder mig altid til denne weekend. Det føles lidt som en stor familie der samles og demokratiet i korpset kan tydeligt mærkes. I år er der valg til bestyrelsen og det glæder jeg mig til. Også på den spændende diskussion vi skal have omkring vedtægtsændringer bla omkring formand/forperson. Personligt hælder jeg mest til ordfører for bestyrelse. Det føler jeg ruller bedre på tungen. 🫶😁

  23. Feed Your Fasteners in Line, With a Bowl Feeder - If you spend much time around industrial processes, you may have seen a vibrating ... - hackaday.com/2023/09/26/feed-y #bowlfeeder #toolhacks #vibration #arduino #springs #dds

  24. Feed Your Fasteners in Line, With a Bowl Feeder - If you spend much time around industrial processes, you may have seen a vibrating ... - hackaday.com/2023/09/26/feed-y #bowlfeeder #toolhacks #vibration #arduino #springs #dds

  25. @murshedz Ah, The Genius Elon Musk is at it again. I think it will turn my Twitter into an exclusive club by limiting new users, unverified old users to read just so many tweets then cut them off. For my Verified Twitter users I will let them read a limit of 6,000 tweets a day. As Elon thinks “What can go wrong with these restrictions on my users.” As everyone in the Fedi-verse can see a whole lot of shit can go wrong ! Boy Genius at it again. #SpaticElon #Deadtwitter #DDS

  26. @murshedz Ah, The Genius Elon Musk is at it again. I think it will turn my Twitter into an exclusive club by limiting new users, unverified old users to read just so many tweets then cut them off. For my Verified Twitter users I will let them read a limit of 6,000 tweets a day. As Elon thinks “What can go wrong with these restrictions on my users.” As everyone in the Fedi-verse can see a whole lot of shit can go wrong ! Boy Genius at it again. #SpaticElon #Deadtwitter #DDS

  27. Met grote trots ontvingen we vanochtend @Unesco voor de officiële toetreding van De Digitale Stad tot het Memory of the World register. #dds #digitaalerfgoed 💾