home.social

#pwm — Public Fediverse posts

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

  1. 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

  2. One last thing I definitely wanted for my PicoDexed was the option for PWM output. This post is by way of a short coda detailing how to do PWM on a Raspberry Pi Pico.

    • Part 1 where I work out how to build Synth_Dexed using the Pico SDK and get some sounds coming out.
    • Part 2 where I take a detailed look at the performance with a diversion into the workings of the pico_audio library and floating point maths on the pico, on the way.
    • Part 3 where I managed to get up to 16-note polyphony, by overclocking, and some basic serial MIDI support.
    • Part 4 for the full MIDI implementation, voice loading, SysEx control and USB-MIDI.
    • Part 5 details different options for building hardware to run PicoDexed.

    The latest code can be found on GitHub here: https://github.com/diyelectromusic/picodexed

    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.

    PWM Output Circuit

    The official “Hardware Design with the RP2040” guide includes a PWM circuit as follows (see section 3.4.1).

    Several others have used a slightly simplified version of this circuit, essentially omitting the buffer stage (U3) and perhaps only implementing a single channel. See for example – Raspberry Pi Pico PWM Audio Output Circuit (Jeremy S. Cook) and “pico-pwm-audio” (Robin Grosset).

    An alternative is this circuit used by Tod Kurt for his Circuitpython-tricks“The output circuitry to get line-out levels is a simple 10:1 voltage-divider and a 1uF capacitor to recenter the signal around 0 volts”:

    I’m going with Jeremy S. Cook and Robin Grosset and using the following (diagram from here):

    This is my solderless breadboard version of the above:

    Raspberry Pi Pico Audio PWM

    There appears to be two ways of getting audio style PWM signals out of a Pico:

    • Use the PWM peripherals.
    • Use the PIO.

    It would appear that Raspberry Pi’s pico_audio library in the pico-extras repository uses PIO. I haven’t found a clear explanation as to why the built-in PWM peripherals haven’t been used.

    Here are some other resources that show alternative descriptions of PWM for audion on a Pico:

    As I’m using the pico_audio library for I2S audio, I’m going to use the default PWM pico_audio library too.

    To initialise the pico_audio PWM library requires the following:

    const audio_pwm_channel_config_t config =
    {
    .core = {
    .base_pin = base_pin,
    .dma_channel = dma_ch,
    .pio_sm = pio_sm
    },
    .pattern = 3,
    };

    const struct audio_format *output_format;
    output_format = audio_pwm_setup(pAudioFormat, -1, &config);
    bool status = audio_pwm_default_connect(pBufferPool, false);
    audio_pwm_set_enabled(true);

    Everything else is the same as for I2S.

    At present, overclocking the Pico causes the PWM frequencies to be messed up, so for now the recommended configuration is 8-note polyphony, 24000 sample rate and no overclocking.

    The build uses GPIO 20 for PWM.

    Closing Thoughts

    I’ve uploaded a prototype PWM UF2 file to GitHub too now in case anyone wants to give that a go too: https://github.com/diyelectromusic/picodexed

    Hopefully there is enough information in the above to get something up and running.

    Kevin

    https://diyelectromusic.wordpress.com/2024/02/19/raspberry-pi-pico-synth_dexed-part-6/

    #dx7 #midi #picodexed #pwm #raspberryPiPico #usbMidi

  3. Op Amp Challenge: An Op-Amp Buck Regulator - Switching regulators have delivered such convenience and efficiency compared to th... - hackaday.com/2023/05/01/op-amp #opampchallenge #buckconverter #contests #hardware #op-amp #pwm

  4. Mini MIDI Synth Uses Minimum Number of Parts - The 80s were the golden age of synthesizers in pop music. Hugely complicated setup... - hackaday.com/2022/05/01/mini-m #musicalhacks #eightchannel #synthesizer #eightbit #esp32 #music #synth #midi #i/o #pwm

  5. RCA Plug Plays Sixteen-Minute Chiptune Piece, All by Itself - Frequenters of arcades back in the golden age of video games will likely recall th... - hackaday.com/2021/05/05/rca-pl #lowpassfilter #musicalhacks #chiptune #attiny4 #padauk #asm #avr #pwm #rca