home.social

#arduino-nano — Public Fediverse posts

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

fetched live
  1. Desklet готов!
    В постоянном режиме выводит данные о температуре и влажности на рабочий стол.

    Теперь надо упаковать это всё в какую-то коробочку или баночку (для защиты от котиков
    😸!)

    #diy #arduinonano #humidity_air

  2. Йеее ! 🎉
    скетч работает, данные* читает!

    *кстати, датчик показывает на 10 % больше чем китайский

    Продолжаем!

    #diy #arduinonano #humidity_air

  3. #diy #arduinonano #humidity_air

    Из далекого Хуижоу
    ​:02laugh:​ приехал наконец-то датчик влажности, масюсенький совсем!​:blobcatprettyplease:​

    Осталось найти гайд, как его подключить к Ардуино и дописать десклет для вывода влажности на экран. Конечно это будет голимый вайбкодинг, но думаю какой-нибудь дипсик справится!)))

  4. TD4 4-bit DIY CPU – Part 8

    Now that I’ve shown I could support more ROM if required using a microcontroller (see Part 6) I can start to ponder how that might be possible.

    • Part 1 – Introduction, Discussion and Analysis
    • Part 2 – Building and Hardware
    • Part 3 – Programming and Simple Programs
    • Part 4 – Some hardware enhancements
    • Part 5 – My own PCB version
    • Part 6 – Replacing the ROM with a microcontroller
    • Part 7 – Creating an Arduino “assembler” for the TD4
    • Part 8 – Extending the address space to 5-bits and an Arduino ROM PCB

    There are several other expansions to consider too. Other things I’m pondering are:

    • Can I find a way to add the two registers together?
    • Are there options to add another register?
    • Is 4-bit data still enough?
    • Could any extensions be added in a way that is backwards compatible with the existing instructions and behaviours?

    And probably a few other odds and ends as I go back and reconsider the schematic as it stands, but they can wait for a future post.

    TD4 Simulation

    Before I get stuck into the updates, I thought it would be useful to be able to simulate the TD4 to allow for quick turn-around experiments.

    I’ve used the “Digital” logic simulator which can be found here: https://github.com/hneemann/Digital

    I could have build the simulator from basic logic gates and that would perhaps have been more useful in helping to understand how the design works. But I wanted something that would be easy to fiddle about with to test enhancements, so I build it using the actual 74xx logic chips instead. This doesn’t make for such a readable simulation, as I’ve had to go with actual pinouts for chips rather than logical groupings of signals. But it does map more closely onto the final hardware which is handy for thinking in actual chip-usage rather than abstract logic.

    I’ve not bothered simulating the clock circuit, I’ve just wired in a clock source. I’ve also not added the ROM DIP switches, instead adding a ROM element and wiring it into the address and data lines. By right-clicking and viewing the attributes, it is possible to define a 16-byte ROM (4 address, 8 data lines) and edit the contents.

    The ROM element takes a multiplexed source and produces a multiplexed output, so I use a splitter/mixer function to turn that into D0-D7 as shown above. Similarly the output of the 74HC161 acting as the program counter (PC) has A0-A3 mixed into a single ADDR bus line.

    I’ve added outputs to the two registers to show their contents during execution. I’ve also added a DIP switch on the /RESET line to allow me to start and stop the simulation.

    The video below shows it running the above ROM contents, which is the same demo program I used in Part 6 with the microcontroller ROM.

    https://makertube.net/w/5njzGmYvqXiU3DLCMMtwqp

    Now I have an easier way of experimenting, onto the enhancements.

    Increasing the Address Space

    The address space is currently implemented as follows:

    • A 4-bit counter register based on a HC161 4-bit synchronous binary counter.
    • A HC154 4 to 16 line decoder/multiplexer for DIP switch selection.
    • A HC540 octal buffer/line driver to buffer (and invert) the data outputs.

    The counter auto increments on each clock pulse, thus moving through the address space, but it can also be a destination for the adder, allowing absolute jumps to specific addresses, thus implementing a JMP instruction.

    To increase the address space, there are a few considerations:

    • With more than 4 bits how should JMPs work? They will have remain 4-bits unless the data width is increased.
    • Each additional bit of address space will double the number of DIP switches required.
    • The next size of binary counter above 4-bits is typically 8-bits.

    One idea is to use the RCO pin of the 161. This is the “ripple carry out” and can be used to cascade counters for greater than 4-bit counting. As I understand things, RCO will be HIGH once all outputs are also HIGH, for a single clock pulse. This can be used to enable a following counter for that pulse. This is shown below (taken from the datasheet).

    And this is the sample application, again from the datasheet, showing how it would work, with extensions on to additional stages.

    A simple way to add an additional bit of address space might be to feed RCO into a flip-flop acting as a toggle in the configuration shown below.

    This can then be used to select between two HC154 4 to 16 decoders. As I already have an unused flip flop as part of the HC74 used for the CARRY, this could be quite an appealing solution and in simulation it does appear to work.

    There is one slight complication. As show above, A5 will toggle with A0-A3 = 1111 not as they change back to 0000. This is because the flip-flop toggles on the rising edge of the provided clock signal, which in this case is RCO from the 74HC161. Adding a NOT gate means that the rising edge happens as the 161’s RCO signal drops when it resets back to 0000.

    Whilst this solves the sequencing problem it does have the unfortunately side effect that the RESET state means that A5 is 1 on power up. That too could be solved with another NOT gate if required, or simply hanging A5 off the /Q output of the flip-flop rather than the Q output.

    Here is the additional wiring, in simulator form, to allow this to work.

    Note the addition of A4 which now comes from the spare flip-flop /1Q output, and the linking of RCO via a NOT gate to the flip-flop 1CP clock input. The rest of flip-flop 1 is configured in toggle mode, with /1RD and /1SD both tied high (inactive) and 1D linked to /1Q for the feedback. The non-inverting output 1Q is not used.

    Whilst this seems to require an additional logic gate (for the NOT) it turns out that there is a spare Schmidt trigger inverter on the 74HC14 that supports the clock circuit, so that is pretty convenient.

    The ROM has also been reconfigured for 5 address inputs with the same 8 data bits, creating a 32 x 8 bit ROM.

    There are a few issues with this though:

    • JMP/JNC only work within the same half of the memory, so JMP 4 in the first 16 locations will jump to location address 0x04, but JMP 4 in the second 16 locations will jump to location address 0x14.
    • A JMP 0 in the last location of each half will carry forward into the next half, as the counter ticks over at the same time as the load happens. So JMP 0 in address 0x0F will jump to address 0x10 and JMP 0 in address 0x1F will jump to address 0x00.

    But if one can program around those constraints this is quite a simple solution.

    An Alternative Solution

    There is a neat solution to adding a 5th address bit here: https://xyama.sakura.ne.jp/hp/4bitCPU_TD4.html#memory

    This uses the duplicate JMP/JNC instructions to encode a JMP2/JNC2 that results in the 5th address bit being set, this enabling a jump to the second half of the memory.

    In order to create the additional address line, there is a second PC register added – i.e. a 5th HC161 counter. As far as I can see the operation is as follows:

    • When the first PC register carries over, the second PC register counts up.
    • As only the first output of the second PC register is used, as it counts that output will simply alternate between 0 and 1.
    • The second PC register can take 1 as an input when the decoded instructions match JMP2 or JNC2 (D5 low, D6 and D7 high, with either D4 or CARRY), forcing A4 on when the first PC register is loaded with the 4-bit jump value, creating a JMP to the second half of the address space.
    • There is an A4 and /A4 signal which alternatively enable the two address decoders for the ROM.
    • This specific circuit uses four HC138 chips rather than two HC154, but the principle of operation is the same – generate one of 32 signals for the ROM from 5 bits of address line.

    The modifications to support this are fairly simple and it is neat how it uses redundancy in the instruction set to work, but it does require an additional 74HC161 chip.

    Combine the two?

    If additional logic can be used to address the second PC in the second solution above, then I’m wondering if that could also be used to deliberately set or reset the flip-flop in the first solution too.

    The key will be overriding the flip-flop state to preset A4 if the logic sequence for the spare JNC/JMP instructions turn up. If the /1SD input is active (LOW) then the output will be HIGH. If the /1RD input is active (LOW) then the output will be LOW.

    Here is the additional instruction decoding logic – I’m using NAND gates as the NOTs here, so I can just use a single quad NAND gate chip.

    So, the truth table for this is as follows:

    D4D5D6D7/C/LDPCENA400111011011X0101111001111X00XX00X10XX10X10XX01X10

    This corresponds to D7+D6 and either D4 or CARRY and NOT D5 causing the ENA4 signal to be true thus implementing the second JNC and JMP instructions (b1100 and b1101).

    Unfortunately, so far, I’ve not been able to figure out an option for driving the flip-flop where the logic pans out to correctly set A0-A3 and A4 to successfully load the PC + flip-flop as required by the new instruction, so I might have to leave that for now.

    TD4 Arduino 5-bit Address PCB

    At this point I thought I had enough to warrant building a new PCB for a microcontroller memory version of the TD4 with the option to support a 5-bit address bus with the limitations described above.

    I took the PCB from Part 5 as the starting point and replaced the ROM logic with an Arduino Nano and added in the flip-flop to create the 5-bit address bus.

    The ROM section is replaced with the Arduino as shown below.

    The CPU section now uses the spare NOT gate from the PWRCLK section and the spare flip-flop from the CPU section as shown below.

    I believe these were the only parts to change. I have included the option to disable the RESET button by cutting a solder jumper and replacing it with a link to an Arduino IO pin.

    I’ve also added headers to breakout the unused Arduino IO pins just in case that becomes useful at some point.

    The complete Arduino Nano pinout is as follows:

    TD4 SignalArduino Nano IOA0-A4A0-A4 (A4 optional)D0-D3D8-D11D4-D7D4-D7/RESETD12 (optional)

    The board can be powered either via the Arduinos USB port or via the PCB micro USB port.

    The PCB will be found on Github here once I know it all works.

    Conclusion

    I was hopeful I could add a 5th address line just using the spare components in the circuit and not adding to the chip count, and that is kind of possible as long as I’m ok with the limitations of the JMPs.

    Building all this onto a PCB will make further programming experiments quite a lot easier.

    But the next step is to see if the instruction set can be expanded. I am still in search of that illusive two-register add.

    Kevin

    #arduinoNano #pcb #td4

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

    My next bit of messing around with Arduno and AY-3-8910 takes my AY-3-8910 Experimenter PCB Design and adds some simple MIDI reception to create a 12-channel AY-3-8910 tone module.

    https://makertube.net/w/hLo4HLYcQkcGvf8N9XzgCS

    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

    The Code

    This is taking a combination of the following previous projects:

    I had the option of assigning unique MIDI channels to each of the 12 channels of the quad AY-3-8910s, but instead opted for a system that listens on all MIDI channels but assigns incoming notes to the next free channel.

    If there are no spare channels, the notes are ignored.

    I’ve included an option to respond to velocity, by translating a MIDI velocity value (0 to 127) into a AY-3-8910 amplitude level (0 to 15). But for now, I’m using it with a fixed velocity.

    In order to map a polyphonic note index onto a chip and channel, I use the following:

    void ayNoteOn (int chan, int pitch, int vel) {
    int ay = chan / 3;
    int ch = chan % 3;
    aySetFreq (ay, ch, pitch, vel);
    }

    The aySetFreq() function takes a MIDI nonte number and turns it into a course an fine frequency value for programming into the AY-3-8910.

    void aySetFreq (int ay, int ch, int note, int vel) {
    int vol = vel >> 3;
    uint16_t freq = 0;
    if (note != 0) {
    freq = pgm_read_word(&Notes[note-NOTE_START]);
    }

    switch (ch) {
    case 0:
    ayFastWrite (ay, AY38910Regs::A_TONE_C, freq >> 8);
    ayFastWrite (ay, AY38910Regs::A_TONE_F, freq & 0x0FF);
    ayFastWrite (ay, AY38910Regs::A_AMP, vol);
    break;
    }
    }

    Additional case statements are provided for channels 1 (B) and 2 (C). The Notes array is the list of frequencies calculated for a 1MHz clock using the equation provided in the data sheet:

    • Freq (tone) = Freq (clock) / (16 TP)

    Where TP is the 12-bit value placed in the course and fine frequency registers. So turning this around and plugging in the frequencies for MIDI notes, we can figure out the 12-bit values required to be programmed into the registers.

    In the end, I cheated and used the table already provided here: https://github.com/Andy4495/AY3891x/blob/main/src/AY3891x_sounds.h

    This covers all notes from C0 (MIDI 12) to B8 (MIDI 119).

    I should also note that I’ve now removed all of the original AY3891x library and am using my own fast-access routines now tailored for supporting four devices.

    As I’m using port IO though, this does mean there is a fair bit of hardcoded assumptions about Arduino PORT usage and GPIO pins.

    Find it on GitHub here.

    Closing Thoughts

    The video shows my, now, go-to test of anything linked to Arduinos and tones – a 12-channel arrangement of the end titles of Star Wars Episode IV – A New Hope.

    As the code will select the next free channel for incoming notes, sometimes consecutive notes sound slightly different due, presumably, to differences in the output channels of the devices. Something to look at, at some point.

    It would also be useful to have a “multi-track” version where each channel is an independent MIDI channel in its own right, but for now, using OMNI and “next free channel” is fine.

    I have to say, when the theme really gets going with those vintage 8-bit tone sounds, I could be sitting back in that 80s Star Wars vector graphics video arcade machine… (although apparently that used several Atari POKEY chips, not AY-3-891x- shame. I wonder if you can get hold of those too…)

    “The force will be with you. Always.”

    Kevin

    #arduinoNano #ay38910 #midi #tone

  6. AY-3-8910 Experimenter PCB Build Guide

    Here are the build notes for my AY-3-8910 Experimenter PCB Design.

    https://makertube.net/w/fULfpG9LNwpb3iCfavVkAp

    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 electronics and microcontrollers, see the Getting Started pages.

    Bill of Materials

    • AY-3-8910 Experimenter PCB (GitHub link below)
    • Arduino Nano
    • Up to 4x AY-3-8910 40-pin DIP devices (see notes here on obtaining devices: Arduino and AY-3-8910)
    • 1x 6N138 optoisolator
    • 1x 1N4148 or 1N914 signal diode
    • Resistors: 4x 220Ω, 1x 4K7, 14x 1K
    • 6x 100nF ceramic capacitors
    • 2x 1uF electrolytic capacitors (the PCB has 220uF on the sinkscreen)
    • 1x 100uF electrolytic capacitor
    • Either: 3x 3.5mm TRS PCB mount sockets
    • Or: 1x 3.5mm TRS PCB mount sockets and 2x 180 DIN PCB mount sockets
    • 1x 2.1mm barrel jack socket
    • 2x 15-way pin header sockets
    • 4x 40-way wide DIP sockets
    • Pin headers
    • Optional: 1x SPDT, 1x DPDT both with 2.54mm pitch connectors
    • Optional: 1x 8-way DIP socket

    Build Steps

    Taking a typical “low to high” soldering approach, this is the suggested order of assembly:

    • All resistors and diode.
    • DIP and TRS socket(s).
    • Disc capacitors.
    • Switches (if used).
    • Electrolytic capacitors.
    • 15-way pin header sockets.
    • Barrel jack socket.
    • DIN sockets (if used).

    It is necessary to add two additional 1K resistors as patch-links on the underside of the board. Details below.

    Here are some build photos.

    The DIP sockets should go on next before the TRS sockets.

    Pin headers and jumpers could be used for the MIDI on/off switch. The power switch could be bypassed with a wire link if not required.

    There are a number of optional pin header breakouts: power, UART, additional IO and all the IO for the four AY-3-8910 chips. For this build I’m not populating those.

    Errata Fixes

    As mentioned in the design notes, two additional resistors must be added to pull the audio outputs to GND as part of the output/mixer circuit. I used two additional 1K resistors.

    These can be added to the underside of the board as shown below.

    Testing

    I recommend performing the general tests described here: PCBs.

    Once everything appears electrically good, here is a test application that will play a chord on each of the devices at a different octave. If this works it should be possible to hear all 12 notes in the four chords across four octaves sounding.

    Find the code here: https://github.com/diyelectromusic/sdemp/tree/main/src/SDEMP/ArduinoAY38910QuadTest

    PCB Errata

    As already mentioned there are the following issues with this PCB:

    • The two 220uF capacitors should be replaced with 1uF capacitors.
    • Two additional resistors need to be patched into the audio output circuit.

    Enhancements:

    •  None

    Find it on GitHub here.

    Sample Applications

    Here are some applications to get started with:

    •  (on their way)

    Closing Thoughts

    It took quite a long time to realise the issue with the output channels. For ages, it appeared that the interface to the chip just wasn’t functioning correctly. With hindsight, some kind of register read/write test would have confirmed that a lot earlier.

    It was only when going back to the schematics of other designs and recognising that the output was always HIGH did the penny drop that the additional resistor was required. Then there was some experimentation to find something that would work with my board and not cause issues in use.

    But it seems like I got there in the end. Now I can get on with doing something a little more interesting MIDI and music wise.

    Kevin

    #arduinoNano #ay38910 #pcb

  7. Arduino and AY-3-8910 – Part 4

    After Part 3 I started to go back and add MIDI, and changed the waveform on the touch of a button, and then started to wonder if I could add envelopes and so on.

    And then it occurred to me, I didn’t really need to re-implement my own synthesis library, I could probably write a custom audio output function for Mozzi and get it to use the AY-3-8910 as a 4-bit DAC…

    https://makertube.net/w/ast3HQ2a3fCanKy9Pr6qUc

    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.

    Mozzi Custom Audio Output

    Mozzi supports a wide range of microcontrollers with a range of different output methods from PWM, built-in DACs, I2S, through to custom output options with DMA or something else.

    I’m not going to go over how Mozzi works here, but here are details of how to run with the different audio output modes here: https://sensorium.github.io/Mozzi/learn/output/

    The key option for me is MOZZI_OUTPUT_EXTERNAL_CUSTOM. There are a number of configuration options that must be set prior to include the main Mozzi file as follows:

    #include "MozziConfigValues.h"
    #define MOZZI_AUDIO_MODE MOZZI_OUTPUT_EXTERNAL_CUSTOM
    #define MOZZI_AUDIO_BITS 8
    #define MOZZI_CONTROL_RATE 64
    #define MOZZI_AUDIO_RATE 16384
    #define MOZZI_ANALOG_READ MOZZI_ANALOG_READ_NONE
    #include <Mozzi.h>
    #include <Oscil.h>
    #include <tables/cos2048_int8.h>
    #include <mozzi_midi.h>
    #include <mozzi_fixmath.h>

    This sets up the audio synthesis parameters to 8 bit audio with a sample rate of 16384Hz.

    Implementing a custom audio output this way requires two functions. One for the audio output and one to tell Mozzi when it is time to call the audio output function.

    I would rather have used MOZZI_OUTPUT_EXTERNAL_TIMED which handles the calling at the correct AUDIO_RATE for me, but that relies on the use of the ATMega328’s Timer 1, but in this case Timer 1 is providing the 1MHz clock for the AY-3-3810.

    But rather than implementing yet another timing routine, I just used the micros() counter to decide if it was time to generate audio or not.

    void audioOutput(const AudioOutput f)
    {
    int out = MOZZI_AUDIO_BIAS + f.l();
    ayOutput(0,out);
    }

    unsigned long lastmicros;
    bool canBufferAudioOutput() {
    unsigned long nowmicros = micros();
    if (nowmicros > lastmicros+58) {
    lastmicros=nowmicros;
    return true;
    }
    return false;
    }

    To get samples produced at the required 16384Hz sample rate means there needs to be one sample produced 16384 times a second. There thus needs to be a sample every 60uS. If I implement the above function checking for nowmicros > lastmicros + 60 then the resulting sound is slightly flat (in tuning). I’m guessing this is related to the overheads of the function call and logic, so I’ve gone with lastmicros+58 and that sounds pretty good to me.

    My ayOutput() routine takes an 8-bit sample and cuts it down to the 4-bits required for a level on the AY-3-8910.

    FM Synthesis on the AY-3-8910 (sort of)

    I wanted to try the FM synth mode just to see what would happen and thought it would be interesting to switch between the carrier sine wave signal and the modulated signal by pressing the button.

    Unfortunately, I just could not get the button logic to work, even though I could see the state of the pin (A5) changing.

    Finally after an hour or so of puzzling why such an apparently simple test of logic wasn’t working, I realised what the issue must be. Mozzi, for the AVR microcontrollers, has its own fast ADC routines. It turns out that these were interferrng with using A5 as a digital input pin.

    It is fairly easy to override the Mozzi fast ADC though by setting MOZZI_ANALOG_READ to NONE.

    The Mozzi code has a carrier and modulator waveform running at audio rate and an index running at the control rate to bring the modulator in and out.

    It is just about possible to see the FM modulation on the oscilloscope as shown below.

    Of course, the AY-3-8910 isn’t actually doing FM synthesis itself. It is just acting as a 4-bit DAC, but it is still quite fun to see.

    Find it on GitHub here.

    Closing Thoughts

    This is all getting a little pointless really, as there is nothing being done that the Arduino Nano couldn’t do better on its own, but it is a bit of fun to see where this thread ends up.

    There are a number of interesting angles now. One of which would be to utilise all three channels. This could provide a form of additive synthesis, it could perform some fixed interval additional oscillators, or it could be used for 3-note polyphony.

    Now that Mozzi is running it is also possible to do anything Mozzi can do, and that includes implementing envelope generation.

    Kevin

    #arduinoNano #ay38910 #include #mozzi

  8. 2025 One Hertz Challenge: Valvano Clock Makes the Seconds Count - A man named [Jim Valvano] once said “There are 86,400 seconds in a day. It’s up to... - hackaday.com/2025/07/14/2025-o #arduinonano #oleddisplay #clockhacks #ds3231rtc #contests

  9. 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 &= 0xF7;} // 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

  10. @fast_code_r_us I build this with (more for) my nephew with #arduinonano clone and an #SSD1306 128x32 #oled display.

    I used the #U8g2 to display the text and the u8g2_font_unifont_t_weather to display the thermometer icon.

  11. Adding Automatic Emergency Braking To An RC Car - Modern RC cars can be pretty darn fast. That’s fun and all, but it also makes it e... - hackaday.com/2025/07/02/adding #transportationhacks #radiocontrol #arduinonano #toyhacks #r/ccar

  12. EuroRack 6HP MCU Experimenter Module

    I’ve been following the work of HAGIWO for some time and always been really impressed with the breadth and range of modules they produce. I have noticed some patterns in their microcontroller based modules and so wondered if it was possible to build some kind of generic template PCB that would allow me to do some experimenting whilst reducing the number of links and connections required between components compared to using protoboard.

    • Note: HAGIWO now has a SMT general purpose 4HP Arduino EuroRack module of their own – the “MOD1” now available via Patreon, and a lot neater than mine – full details here: https://note.com/solder_state/n/nc05d8e8fd311

    This is the result.

    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 materials for the main concepts used in this project:

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

    The Circuit

    I wanted to include the following elements:

    • Microcontroller interface – ideally an Arduino Nano or a Raspberry Pi Pico.
    • PWM audio output stage.
    • Gate/Trigger input and output stage.
    • CV input and output stage.
    • EuroRack compatible power connector.

    I wanted suitable protection on the various stages, so have essentially correlated some of the circuits put up by HAGIWO and pulled out a range of elements into my schematic. I’ve been looking through the designs for the following:

    Although the basic template for many of HAGIWO’s designs are very similar, often specific component values might be tweaked, so I wanted to ensure there was nothing too prescriptive in the design here to prevent tailoring.

    In the end I used some specific component values from certain circuits in the schematic itself, but really they are just an example starting point. The values required will depend on the expected input and output levels and whether converting to/from 5V or 3V3 operation.

    For reference, common EuroRack voltages are (from the Doepfer Technical Specification):

    • Audio signals: 10Vpp i.e. -5V to +5V.
    • Modulating Control Voltages: 5Vpp i.e. -2.5V to +2.5V (LFOs); 8Vpp i.e. 0V to +8V (EGs).
    • Trigger/Gate: 0 to 5V, although up to 12V should be accepted.
    • Pitch Control Voltages: Typically 1V/octave.

    Key design principles for the control board:

    • Will allow for a 5V Arduino Nano or a 3V3 Raspberry Pi Pico, with the circuits VCC taken from the power output of the microcontroller (so 5V or 3V3 respectively).
    • Powered from the EuroRack +12V either into VIN of the Arduino Nano or via a L7805 generating 5V into VSYS of a Raspberry Pi Pico.
    • Anticipating that not everything will fit on a single board, there is a header interface between the microcontroller and everything else.
    • The connections to each of the circuit stages is via headers or blank solder pads, allowing patching across to any microcontroller pins as required.
    • There is no support for controls, pots or jacks on the board, it is assumed those will be added to a panel and patched across to the control board using headers.
    • Some mounting holes will be provided.
    • Only the components and circuit blocks required for a specific build will be used. If using an Arduino Nano, this includes being able to omit the L7805 and associated power circuitry as it can be powered from +12V into VIN.

    A future post of usage notes will go into the detail of each circuit element a little more.

    EuroRack Power

    I was pretty keen to include some protection for the EuroRack power side of things, following some of the advice from here: https://www.youtube.com/watch?v=cBGyEBQnIws.

    The problem is that although it is possible to protect the 10-pin version of the EuroRack power connector from reverse connection, it is a lot harder to do with the 16-pin version.

    Pinout from the Doepfler Technical Specification (note -12V is “pin 1” and usually denoted by the stripe on the cable – but not always!):

    For the 10-pin version we essentially just have to cope with +12V and -12V being the wrong way round. With the 16-pin version it isn’t that simple and there are several problems if a cable is plugged in the wrong way round:

    • The CV/Gate signals will overlap with -12V and GND.
    • The 5V connection will overlap with GND.
    • The +12V connection will overlap with GND.
    • As there are three sets of GND pins, typically all connected together, the +12V and +5V of the power supply and any other modules in the system will be connected together!

    That last one is very likely to cause damage to any module or power supply plugged into the rest of the rack. The worst-case scenario is that it could damage the PSU and ALL other modules in the rack… none are going to like receiving 12V when 5V is expected… err…

    As this is a PCB designed for DIY experimentation, I wanted to ensure that the chances of a serious problem with any other kit it might be hooked up to are minimised.

    But I should repeat at this point: do NOT use this module alongside any other expensive equipment or rack or power supply. It is meant for DIY experiments only and should only be connected to other DIY or otherwise sacrificial equipment.

    Still, I’ve only “wired up” the following of the EuroRack power support connector:

    EuroRack StandardPCB ConnectionInternal GATEN/CInternal CVN/C+5VN/C+12V+12VGNDGNDGNDN/CGNDN/C-12VN/C

    In particular note that I’m only using one of the GND connections – the others are left not connected or linked at all, and especially not to each other. This means that should a cable to plugged in the wrong way round, there is nothing on my PCB that will bridge +5V and +12V via GND; it also means I can include some basic protection against +12V and GND being the wrong way round.

    I’m also planning to use shrouded connectors, but of course that won’t guard against incorrectly wired cables or other weirdness.

    I was in two minds about attempting to use the +5V rail, but as I couldn’t find any advice online about how to protect that, I just opted for the use of a L7805 and the +12V rail, which I was planning to use anyway as it seemed more “universal” than expecting a 5V too.

    The final power related part of the schematic is thus:

    PCB Design

    I’ve deliberately designed this onto two boards – a MCU board and an IO board, with headers between them. The power circuit is part of the MCU board. The idea is that all of the components on the MCU board are on the underside; all the components on the IO board are on the top side; that they will link together; and that the power connector will be on the underside and all IO connections (jacks, pots, etc) on the top.

    I’ve also overlapped the footprints of the Nano and Pico and linked up GND and VCC (5V for the Nano and 3V3 for the Pico), so either could be used.

    I’ve deliberately kept the size of the PCBs below 30x100mm, but will get these built as two individual designs. But keeping it within 6HP and 100mm has made for a very packed board!

    In terms of silkscreen labels, each section of the schematic has a specific numbering scheme – for example, all components associated with the first PWM circuit are numbered between 100-199. Similarly for all the other sections.

    But this is not a PCB that is meant to be populated without referral to the schematic and some bespoke design on a per-board basis.

    Closing Thoughts

    This is not a board to be soldered up and just used. It is a template, meant to remove the need for some connections, with some commonly used “circuit patterns” allowing easy connection to a microcontroller and EuroRack power supply.

    But I’m quite pleased with how I’ve done my best to minimise the chances of this board causing a problem with other DIY modules in a rack.

    But for this more than any other boards – the usual warning applies. Do not use with expensive equipment. Test thoroughly, double check cable wiring, and do not use alongside other expensive modules in an expensive rack of kit.

    In a future article, I’ll go through some notes on how I expect to be using it.

    Kevin

    #arduinoNano #EuroRack #HAGIWO #pcb #raspberryPiPico

  13. И снова я сломал атмегу через все ту же #progisp + #usbisp :blobcatjoy: играя настройками фьюзов - Ext.CrystalOsc, но эта "игра" неспроста, после того как я заново оживил контроллер, решил прошить нормально через #usbasp и #avrdudeprog но тут же получил ошибку, даже калибровочные ячейки генератора считать не удалось. Пришлось искать методом тыка ту самую настройку внешнего кристалла в PROGISP :blobcatgrimacing: кажется теперь все серьезно, контроллер не откликается, хотя на выводах #rx_tx есть потенциал... Придется опять отложить контроллер в сторону или уже не суетиться и заказать новую #arduinonano или #arduinoUno или просто закупить #atmega328p и перепаять
    #embedded
    #troubleshootingfail
    #electronics

  14. 2024 Tiny Games Contest: Blind Maze Is Fun For All - If you think about it, even difficult mazes on paper are pretty easy. You can see ... - hackaday.com/2024/08/29/2024-t #2024tinygameschallenge #arduinonano #contests #games

  15. ШИР - Широтно-импульсный регулятор. Реализован не в #arduino среде, тут только #c #avrgcc #avrdude и #atmega328p Хотел проверить сервопривод, но он кажется мертв... Но, я надеюсь что со специальным устройством проверки он вдруг оживёт :ablobcatnod: AMEN
    #embedded
    #circuitry
    #avr
    #atmelstudio6
    #servo
    #pwm
    #sourcecode
    #oscilloscope
    #testproject
    #breadboard
    #arduinonano