home.social

#arduino-uno — Public Fediverse posts

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

fetched live
  1. Bu fotoğrafları tekrar paylaşmak istiyorum.
    Bu benim iki Arduino UNO R3'üm. Birinin üzerinde 2.8 Dokunmatik Ekran takılı!

    Artık Arduino'yu Qualcomm satın aldı ve ikonik Arduino UNO'nun sonuna yeni sahibini işaret eden "Q" harfini ekleyip Debian Linux çalıştıran bir tek kart bilgisayara dönüştürdüler!

    Henüz dönüşüm aşaması tamamlanmadı ve ben de tüm Arduino severler gibi sonucu bekliyorum!

    Eğer iyi olduğuna inanırsam 8GB RAM olanı çıkınca alabilirim.

    Eğer inanırsam!

    #Arduino #ArduinoUNO

  2. Minimalist Lo-Fi Minimalism – Part 2

    Last year I programmed up an Arduino with my Pi Day MIDI Sequencer to play a short extract from Philip Glass’ Einstein on the Beach. You can read all about that here: Minimalist Lo-Fi Minimalism.

    Having spent a fair bit of the time since then playing around with an Arduino and SP0256A-AL2 and finally getting it hooked up to MIDI, I parked an idle though that it might be interesting to get it “singing” part of Einstein on the Beach. This post comes back to that idea.

    https://makertube.net/w/4upxhBNemynPiFKfdzzea2

    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

    I did wonder about combining both the Pi Day sequencer and SP0256A-AL2 shield into a single device, but then decided it would be simpler to treat them as two separate devices and use MIDI to pass control between them.

    Consequently I’ve ended up as follows:

    • Pi Sequencer -> MIDI TRS -> SL0256A-AL2 -> MIDI TRS -> synth

    Where the Pi sequencer is running the code for Minimalist Lo-Fi Minimalism with a small update to send additional MIDI messages to be interpreted by the SL0256A-AL2. All other MIDI is passed through the SL0256A onto a MIDI synth as before.

    The Code

    Having decided on the basic architecture, the next decision was how to encode the information destined for the speech synthesizer. I need to be able to tell it a pitch and a number to be “sung”. I toyed with the following ideas:

    • MIDI NoteOn with pitch, but using note velocity to encode the number to sing.
    • MIDI Program Control to select the number, then MIDI pitch and velocity as normal.
    • Use the MIDI channel for the number, and pitch and velocity as normal.

    I opted for the last option, treating each number as a different instrument on a different MIDI channel. This is a bit wasteful but was a lot easier for testing than attempting to get a specific velocity or having to send MIDI PC messages to keep reselecting the “voice” before each note.

    As described previously in Minimalist Lo-Fi Minimalism MIDI channels 1,2 and 3 are already used for the bass and two voice lines, so I’m using MIDI channels 4 through to 13 for the numbers one to ten.

    The code for the Glass already has pitch and which number encoded into the data structures. To trigger the “singing” via the speech synthesizer I’m using the Soprano voice pitches, but an octave lower, so it is a relatively simple matter of adding in an additional MIDI send as follows:

        MIDI.sendNoteOn(sop[i], 64, MIDI_CHANNEL_SOP);              // Original code
    MIDI.sendNoteOn(sop[i]-12, 64, MIDI_CHANNEL_NUM-1+num[i]); // New code
    lastsop = sop[i]; // Original code

    It is using the “NUM” MIDI channel – 1 as I’m encoding the numbers 1 to 10 which are stored in the num[] array. As the speech synthesis is essentially running at a fixed duration for each utterance, I’m not even bothering with a Note Off message.

    On the speech synth side, again essentially all of the code is the same as for the Arduino and SP0256A-AL2 – Part 6 MIDI code, but instead of singing “do, re, mi”, etc linked to pitch, I need to take the word from the MIDI channel. To do this, I’ve expanded the speak() function to include the number and call it as follows from the NoteOn callback:

      speak(channel-MIDI_CH2NUM, pitch);

    This will result in a number from 1 to 10 and a MIDI pitch which can then be used to select the playback frequency as before and then say the allophones corresponding to the received number.

    void speak (uint8_t num, uint8_t note) {
    midi2clock (note);
    switch(num){
    case 1: // One
    spAllo(WW1);
    spAllo(AX1);
    spAllo(NN1);
    break;

    case 2: // Two
    spAllo(TT2);
    spAllo(UW2);
    break;

    }
    spAllo(PA3);
    }

    The SP0256A-AL2 datasheet lists the allophones to use for the basic numbers.

    I’ve used, in a few cases, slightly shortened versions of the numbers from one to ten. In particular I’ve removed the duplicate allophones for six and seven to make them a little shorter to playback.

    The allophones for “One” includes the use of “SX” but I can find no other mention of that in the datasheet, so I’ve ignored it.

    One final change was to tweak the timings of the original playback. I’ve had to slow it down a lot to give the SP0256A-AL2 time to say each number, and I’ve also introduced a small delay between sending the MIDI messages and updating the numbers on the display to allow them to sync up a little better.

    If the MIDI went to the synth directly from the Pi Day sequencer then a delay would probably be required there too, but as it has to go through the SP0256A-AL2 and get sent back out using the “software THRU” of the Arduino MIDI library, it has a natural slight delay already and isn’t too noticeably out of sync.

    Closing Thoughts

    I always knew there would be a few limitations, not least of which due to the time it takes to play back the allophones, but in essence I believe this works. I’d rather it was a little more up tempo, but sometimes one just has to work with what is available.

    I think it is certainly keeping within the spirit of attempting an extract of the original opera on 8-bit microcontrollers, so it’s not doing too badly.

    Kevin

    #arduinoUno #midi #Minimalism #PhilipGlass #piday #sp0256aal2
  3. SP0256A-AL2 Sings the Twelve Days of Christmas

    Partly prompted by a programming challenge by Futzle on Mastodon, I thought it might be interesting to try to get my Arduino and SP0256A-AL2 to sing the Twelve Days of Christmas.

    https://makertube.net/w/diBkzrhkh3tekAPBhGzrVj

    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 Code

    I wasn’t aiming for a clever code solution, as can be seen submitted to the programming competition already, but I was hoping to get the computer to do at least some of the work for me.

    The first issue is getting a useful range for the song. My original MIDI SP0256 project managed around an octave and a half. Well it so happens that the 12 Days of Christmas is just over 1 octave, so if I can position that to the top end of the range, I should be ok.

    This is the most compact notation of the tune that I know of. This is from the “Christmas Praise” Brass Band carol book, which seems to be one of the most common references for any band out caroling at this time of year.

    Ignoring the introduction, I’m going to transpose this into concert Bb (i.e. up a minor third) which gives me a note range from F2 through to G3. This allows me to use a sensible range of the SP0256A without it getting too slow down at the bottom end of things.

    The main text/lyric structure will be set up in a series of functions as follows:

    intro() - "on the nth day of Christmas my true love gave to me"
    nth() - "first", "second", etc.
    one() - "a partridge in a pear tree"
    two() - "two turtle doves"
    ...
    twelve() - "twelve drummers drumming"

    Some variations are required as follows:

    • intro() – needs to be able to switch to “and a partridge” for all verses apart from verse 1.
    • two(), three(), four() – have a different melody from verse 5 onwards.
    • five() – ideally this should get slower and more drawn out with each repetition.
    • one() – ideally this would slow down on the last time through.

    In order to capture the variations and repeats for each verse, I’ve opted to use an increasing bitmask for each time through to trigger the various phrases. Bit 0 always indicates one() must be called. Bit 1 indicates two(), and so on. It starts with the value 1 but will keep adding another bit as the verses change, so will have the values 1, 3, 7, 15, 31, 63, etc. thus allowing me to build up the verses with one bit per verse.

    one() takes bool parameters to indicate first time and last time through. two(), three(), four() take a bool parameter to indicate if it is a “post five gold rings” verse. Using the bitmask this is pretty easy as it just means any of the bits 0x0FF0 will be set for verses 5+.

    Here is the main code loop.

    uint16_t phrase=1;
    for (int verse=1; verse<=12 ; verse++) {
    intro(verse);
    delay(200);
    if (phrase & 0x0800) twelve();
    if (phrase & 0x0400) eleven();
    if (phrase & 0x0200) ten();
    if (phrase & 0x0100) nine();
    if (phrase & 0x0080) eight();
    if (phrase & 0x0040) seven();
    if (phrase & 0x0020) six();
    if (phrase & 0x0010) five(verse);
    if (phrase & 0x0008) four(phrase & 0x0FF0);
    if (phrase & 0x0004) three(phrase & 0x0FF0);
    if (phrase & 0x0002) two(phrase & 0x0FF0);
    if (phrase & 0x0001) one(phrase == 1, verse == 12);
    phrase = (phrase<<1) + 1;
    delay(1000);
    }

    The timings were really quite tricky as I have several variables to work to:

    • Each allophone takes a different time to be said.
    • Each allophone also has a recommended delay time (this might be to account for the time to say them – I’m not sure).
    • When I change the frequency, the time to say any specific allophone also changes, with lower frequencies significantly slower than higher ones.
    • I naturally need to account for the musical rhythm changes too.

    In principle I could probably work out the slowest interval and work back from there to get some accurate timings, but I just did a bit of trial and error on each phrase until it was close enough. The unevenness remaining was because I didn’t want to slow the whole thing down to the slowest phrases being “sung”. It is slow enough already!

    Also, as it doesn’t look much, I’ve added an LED to light up at the start of an allophone and to go off whenever a pause is encountered.

    Find it on GitHub here.

    Closing Thoughts

    I’m actually really pleased with this. It is a good balance of close enough to show the principle without me undertaking complex timing and endless fiddling.

    I could probably get a bit cleverer with the code, but that wasn’t really the point for me. I’ve used an algorithm where it mattered to me, and was quite happy to spell out the allophones, frequencies, and timings by hand as I went.

    Lastly, let me wish you the compliments of the season, to you and yours.

    Kevin

    #arduinoUno #sp0256aAl2

  4. Atari Synth Cart Controller

    This project uses my Atari 2600 Controller Shield PCB in reverse, to allow the Arduino to act as an Atari keypad controller and thus allow it to control the Atari Synth Cart.

    https://makertube.net/w/ryCciwFyQQpcs1Q4Wwy52x

    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

    In this use of my Atari 2600 Controller Shield PCB the 9-pin d-type connectors are directly connected to the Atari console.

    There is one issue however. Which device should provide 5V? The Atari pinout has 5V present on pin 7 and this is hooked up to the Arduino 5V line.

    It may be possible to run the Arduino off the Atari 5V line, which would be really convenient if so. But I’ve not found a definitive statement of the maximum current draw through the 5V pin of the d-type connector from an Atari 2600.

    From the schematics for the 2600, it appears that the 5V comes directly from a 7805 regulator. This may or may not go via a single inductor depending on what version of the schematic I’m looking at (yes, for the schematics on Atari Age, below, no for the schematic in the field service manual).

    From the BOM in the field service manual, this would appear to be a 78M05 which I believe has a maximum current output of 500mA with a suitable heatsink.

    But that has to power the entire console of course. In a not particularly scientific measurement, I believe my 2600 jnr was drawing around 360mA and my “woody” perhaps around 320mA. I don’t know how this changes with different games or controllers.

    According to this post, an Arduino Uno with no IO pin current draw, will pull around 50mA when powered from the 5V line directly. Curiously, a bare-bones ATMega328P can apparently drop to almost 15mA when not driving regulators, a USB chip and a power LED…

    From powering up a number of Arduino projects I had lying around, most hardly registered on a simple USB current meter using 10mA units. Some with LEDs got to show 0.02A. Some with screens got up to 0.2A.

    So just thinking about it in a somewhat hand-wavy kind of way, I suspect that powering an Uno would probably be ok…

    If not, then the 5V line to the d-type connectors will have to be cut and the Arduino independently powered. But then the signals for the keypad will be set at the Arduino’s idea of what 5V looks like, not the Atari. They ought to be essentially the same, but it can’t be guaranteed.

    Ideally to go this route, the Arduino would isolated from the Atari and be switching the Atari’s 5V line on and off for the input signals as required, but that would require a new design of the PCB.

    So time for an IMPORTANT WARNING: This could well damage the Atari 2600 console as I’m essentially making it up as I go at this point.

    I have a cheap Atari 2600 junior that I picked up a while back that I’m happy to experiment with. I’m not using my own, original “woody”. In the end I used the following:

    Volca <– 3.5 mm jack to jack –> Arduino TRS MIDI + Atari Shield <– 9-pin to 9-pin –> 2600

    Here is the full setup using a Video Touch Pad as the second controller. Note the Arduino is fully powered from the Atari at this point, but the Volca is running on batteries.

    The Code

    The basic principle for the Arduino is to monitor the Atari’s “row” signals via INPUT pins and when detected drive any OUTPUTs LOW to indicate keys being pressed. The basic algorithm is as follows:

    rows[] = IO INPUT pins connected to D-type pins 1, 2, 3, 4
    cols[] = IO OUTPUT pins connected to D-type pins 5, 6, 9

    FOREACH row[]:
    IF row is scanning LOW THEN
    Set cols[] HIGH or LOW in turn according to required keypress

    REPEAT for second controller

    The keypad row/column map was shown in Arduino Atari MIDI Keypad and reproduced here:

    Active (pressed) keys will show as LOW in a column when that row is scanned, so to emulate 6 being pressed, when row on pin 2 is detected as being LOW, column on pin 6 should be driven LOW and columns on pins 5 and 9 should be driven HIGH.

    I need to map these keys over onto MIDI notes. One issue is that the Atari doesn’t have a natural scale as generating the frequencies for notes is pretty limited.

    Still, I’ve mapped the 12 keys, with their non-natural scale, over onto MIDI notes 60-71 – i.e. all semitones up from middle C.

    I’m using PORT IO in an interrupt driven scanning routine to ensure the Arduino is as responsive as it can be to ROW scanning. I also pre-compute the actual column bit values when a MIDI note is received, meaning the scanning routine only has to write out the preset values.

    The main functions that achieve this are shown below, with the PORT IO values set up for the second Atari controller connector on my Atari 2600 Controller Shield PCB.

    uint8_t row[ROWS];
    uint8_t colbits[COLS] = {
    0x02, // C0 = A1 PORTC
    0x01, // C1 = A0 PORTC
    0x10 // C2 = D12 PORTB
    };

    void keyOn (int r, int c) {
    if (r < ROWS && c < COLS) {
    // Clear the bit as need active LOW
    row[r] &= (~colbits[c]);
    }
    }

    void keyOff (int r, int c) {
    if (r < ROWS && c < COLS) {
    // Set the bit as need active LOW
    row[r] |= colbits[c];
    }
    }

    void scanKeypad (void) {
    // ROWS: D11-D8 = ROW1-ROW4
    if ((PINB & 0x08) == 0) { // D11
    PORTB = (PORTB & (~0x10)) | (row[0] & 0x10); // COL D12
    PORTC = (PORTC & (~0x03)) | (row[0] & 0x03); // COL A0, A1
    }
    if ((PINB & 0x04) == 0) { // D10
    PORTB = (PORTB & (~0x10)) | (row[1] & 0x10);
    PORTC = (PORTC & (~0x03)) | (row[1] & 0x03);
    }
    if ((PINB & 0x02) == 0) { // D9
    PORTB = (PORTB & (~0x10)) | (row[2] & 0x10);
    PORTC = (PORTC & (~0x03)) | (row[2] & 0x03);
    }
    if ((PINB & 0x01) == 0) { // D8
    PORTB = (PORTB & (~0x10)) | (row[3] & 0x10);
    PORTC = (PORTC & (~0x03)) | (row[3] & 0x03);
    }
    }

    As the Synth Cart notes are mostly controlled from the left controller, I’m only coding up for the Arduino to drive one controller. I’m using a genuine Video Touch Pad for the second controller.

    Update: The code now includes a controller pass-through mode. If a keypad is plugged into the second socket on the Arduino, then either MIDI or the keypad can be used to drive the Synth Cart.

    To achieve this, there are now two sets of row[] arrays containing the column values and when it comes to writing them out, they are combined as follows:

    if ((PINB & 0x08) == 0) {  // D11
    PORTB = (PORTB & (~0x10)) | (row[0][0] & row[1][0] & 0x10);
    PORTC = (PORTC & (~0x03)) | (row[0][0] & row[1][0] & 0x03);
    }

    As the signals are active LOW, the two values need to be logically ANDed together to get the correct result. I could have simply called the same noteOn/noteOff routines for the keypad, but then I’d have the situation where if both keys for a note are active, the first one released will stop the sound. By combining them in this way, the note will keep sounding until both keys are released.

    Find it on GitHub here.

    Closing Thoughts

    I really ought to map the pitches of the Atari notes onto their respective MIDI notes, but then I’m not sure what to do for the gaps.

    In principle I could wire up both controllers and then use MIDI pads on a MIDI controller just as MIDI-driving control keys rather than actual keyboard notes, but this shows the principle.

    I’m still not sure about the power issues, but it seems to work. I guess it will keep working until one day it might not and I’ll be looking for a new Atari 2600 junior.

    The slight stuttering in the video is when I end up touching a couple of the volca’s keys at the same time. I suspect I could do something a bit more robust in code to prevent repeat triggering, but this is all fine for now as a proof of concept.

    Kevin

    #arduinoUno #atari #atari2600 #midi #synthcart

  5. terra has @[email protected] app lab for the uno q now

    you can grab it from your app store right now! we hope you have a fun time with it and your uno q

    if you'd rather have a bad time you should look at how we had to build this thing

    https://github.com/terrapkg/packages/tree/frawhide/anda/tools/arduino-app-lab-bin

    #tech #linux #terra #fyralabs #arduino #unoq #arduinounoq #arduinouno #arduinoapplab #fedora #ultramarinelinux

  6. TD4 4-bit DIY CPU – Part 7

    Once the idea was floated, in Part 6 of creating an Arduino “direct to ROM” assembler, I had to just do it, so this post is a little diversion from the hardware discussion into how that could work.

    • 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

    Basic Concepts

    This relies on using an Arduino as the ROM as described in Part 6, but the Arduino now has the option to change the ROM contents independently of the TD4 itself.

    The Arduino sketch will do the following:

    • Run the TD4 ROM routine off a timer interrupt so that it is always running and responsive.
    • Take input over the Arduino serial port to allow basic control, e.g. list, clear, etc.
    • Allow the direct input of assembler instructions, such as MOVE A,B or OUT B and so on.
    • Provide a means of selecting which line of the program to change.

    The code will thus have a number of key sections:

    • The TD4 ROM routine.
    • Some kind of serial-port command-line interpreter.
    • Handler routines for all the commands.
    • An assembler.
    • A disassembler.

    The TD4 ROM routine has already been fully described in Part 6. The only difference is that the scanning routine will be driven from a 1mS timer using the TimerOne library.

    As I want to still support a built-in demo, I now have the concept of ROM being the demo code and RAM being the “live” code to pass onto the TD4. The Arduino will initialise the RAM on startup from the ROM.

    As far as the TD4 is concerned of course, this is all still ROM.

    Command Line Interpreter

    The standard Arduino Serial routines will be used to scan for input via the serial port. It will support a line-oriented input as follows:

    bool cmdRunner (void) {
    while (Serial.available()) {
    char c = Serial.read();
    if (c == '\n') {
    strcpy(cmdSaved, cmdInput);
    cmdIdx = 0;
    return true;
    }
    else if (cmdIdx < CMD_BUFFER-1) {
    cmdInput[cmdIdx++] = c;
    cmdInput[cmdIdx] = '\0';
    }
    }
    return false;
    }

    This will keep adding any received characters to the cmdInput buffer until a newline is received, at which point the command is saved in cmdSaved and the routine will return true indicating a full line is ready to be processed.

    Once a complete line is received, then a processing function will parse it.

    Key to the processing of commands is a command table that stores the text to match and the handler function to call on finding a valid command. There is an additional parameter that will be passed into the handler function to allow the same handler function to support several commands. This will be used in the assembler itself later.

    struct cmd_t {
    char cmd[CMD_BUFFER+1];
    hdlr_t pFn;
    uint8_t idx;
    };

    const cmd_t PROGMEM cmdTable[NUM_CMDS] = {
    {"H", hdlrHelp, 0},
    {"L", hdlrList, 0},
    {"G", hdlrGoto, 0},
    };

    The algorithm for parsing commands is as follows:

    cmdProcess:
    Look for a space or newline
    IF found a space THEN
    This is the start of the parameter

    Look for the command in the command table
    IF command found THEN
    Call the handler function with the parameters

    The implementation is a bit complex, as it uses string pointers and has to chop and parse strings as it goes. It is also detailing with the command table in the Arduino’s PROGMEM which is an additional complication too.

    In order to be able to use the same command line interpreter for the input of assembler instructions, I’ve had to simplify the syntax. There are no spaces in opcodes and there has to be a space between the opcode and immediate value if used.

    Here are some examples:

    IN A       -> INA
    MOVE A,B -> MOVAB
    OUT im -> OUT im
    JNC im -> JNC im
    ADD A,im -> ADDA im

    Handler Routines

    All handler routines have the following prototype:

    typedef void (*hdlr_t)(int idx, char *param);

    void hdlrHelp(int idx, char *pParam) {
    Serial.print("\nHelp\n----\n");
    Serial.println("H: Help");
    }

    The idx parameter is the number in the last field of the command table. pParam will be a pointer to the parameter string for the command (if used).

    As we’re dealing with strings all the time, there are a number of helper functions to do things like convert strings to numbers as well as others to print numbers in various formats.

    Number formats are assumed to be as follows:

    0..9   - decimal digits
    0x0..F - hex digits
    b0..1 - binary digits

    The code provides the following:

    • str2num – the basic string parsing routine to recognise all three number formats as strings.
    • printbin – print a number in b0..1 format.
    • printhex – print a number in 0x0..F format, allowing for a possible leading zero if required.
    • printins – print an instruction in textual format.
    • printop – print an instruction in binary and hex opcode format.
    • printline – print a line number in a consistent binary and hex format.

    The code supports the following commands, so each has its own handler function:

    • H – help – show the list of commands.
    • L – list – show the disassembly of the whole working memory (RAM).
    • G – goto – set the working line number.
    • C – clear – reset all working memory (RAM) to zeros.
    • R – restore – restore the working memory (RAM) to the pre-build demo code (ROM).
    • O – opcodes – list the supported opcodes.

    Assembler

    As already mentioned, I’m using the same command line interpreter code to create the assembler. To do this, each opcode has an entry in the command table:

    const cmd_t PROGMEM cmdTable[NUM_CMDS] = {
    // Assembly commands - must be first
    {"ADDA", hdlrAsm, 0},
    {"MOVAB", hdlrAsm, 1},
    {"INA", hdlrAsm, 2},
    {"MOVA", hdlrAsm, 3},
    {"MOVBA", hdlrAsm, 4},
    {"ADDB", hdlrAsm, 5},
    {"INB", hdlrAsm, 6},
    {"MOVB", hdlrAsm, 7},
    {"OUTB", hdlrAsm, 8},
    {"OUT2B", hdlrAsm, 9},
    {"OUT", hdlrAsm, 10},
    {"OUT2", hdlrAsm, 11},
    {"JNCB", hdlrAsm, 12},
    {"JMPB", hdlrAsm, 13},
    {"JNC", hdlrAsm, 14},
    {"JMP", hdlrAsm, 15},

    // Other commands
    {"H", hdlrHelp, 0},
    {"L", hdlrList, 0},
    {"G", hdlrGoto, 0},
    {"C", hdlrClear, 0},
    {"R", hdlrRestore, 0},
    {"O", hdlrOpcodes, 0},
    };

    The order corresponds to the opcode command value, as does the parameter. As these are at the start of the table, I can assume that the position in the table is the same as the command value. This does mean that I also need to account for the duplicated instructions even if I don’t need to use them.

    I’m making the following design decisions:

    • There is the concept of a “current line” which can be set with the G (goto) command.
    • Entering a valid opcode automatically moves the current line on by 1.
    • No line information is entered as part of the opcode.

    The main logic of the assembler handler is as follows:

    Assembler:
    Command value is the provided index parameter
    Determine the im value from the provided string parameter
    RAM[line] = cmd << 4 + im
    Increment current line

    Disassembler

    Disassembly is really largely a look-up table matching opcode command values to text. This is all hidden away behind the two print routines printins() and printop().

    void printins (uint8_t ins) {
    uint8_t cmd = ins >> 4;
    uint8_t im = ins & 0x0F;

    Serial.print(FSH(cmdTable[cmd].cmd));
    if (HASIM(cmd)) {
    Serial.print(" b");
    printbin(im,4);
    } else {
    Serial.print(" ");
    }
    }

    void printop (uint8_t op) {
    uint8_t cmd = op >> 4;
    uint8_t im = op & 0x0F;

    Serial.print("b");
    printbin(cmd,4);
    Serial.print(" ");
    printbin(im,4);
    Serial.print("\t0x");
    printhex(op,2);
    }

    The main complexity is pulling the strings out of the command table. I’ve had to include a macro to provide access to the strings from the Arduino’s PROGMEM:

    #define FSH(x) ((const __FlashStringHelper *)x)

    This feels like a bit of a hack, but apparently this is how it should be done for the kind of thing I need to do!

    There is another macro here that needs explaining:

    #define HASIM(op) (op==0||op==3||op==5||op==7||op>9)

    This is a set of conditions that if true means that the command supports an immediate value. This is used in a few places to know how to parse the commands.

    Whilst in principle all commands could use the immediate value, the “official” statement of how they work assumes im=0 in many cases. So, for example, OUT B does not require an immediate value, but if one is provided then OUT B becomes OUT B+im.

    I’m not really supporting that with this code at the moment.

    Putting it all together

    Here is a serial output log of a session using the assembler.

    > H
    Help
    ----
    H: Help
    L: List
    G: Goto
    C: Clear
    R: Restore
    O: Opcodes
    OpCode
    OpCode im

    Current line: b0000 [0]

    > L
    RAM Disassembly

    b0000 [0]: JNC b1000b1110 10000xE8
    b0001 [1]: JMP b0011b1111 00110xF3
    b0010 [2]: OUT b0010b1010 00100xA2
    b0011 [3]: ADDB b0001b0101 00010x51
    b0100 [4]: OUT b0100b1010 01000xA4
    b0101 [5]: ADDA b0001b0000 00010x01
    b0110 [6]: OUT b1000b1010 10000xA8
    b0111 [7]: ADDB b0001b0101 00010x51
    b1000 [8]: OUT b0100b1010 01000xA4
    b1001 [9]: ADDA b0001b0000 00010x01
    b1010 [A]: OUT b0010b1010 00100xA2
    b1011 [B]: ADDB b0001b0101 00010x51
    b1100 [C]: JMP b0000b1111 00000xF0
    b1101 [D]: ADDA b0000b0000 00000x00
    b1110 [E]: ADDA b0000b0000 00000x00
    b1111 [F]: ADDA b0000b0000 00000x00
    Current line: b0010 [2]

    > G 13
    Goto line 13
    Current line: b1101 [D]

    > OUTB
    Assemble:
    b1101 [D] OUTB b1000 00000x80
    Current line: b1110 [E]

    > L
    RAM Disassembly

    b0000 [0]: JNC b1000b1110 10000xE8
    b0001 [1]: JMP b0011b1111 00110xF3
    b0010 [2]: OUT b0010b1010 00100xA2
    b0011 [3]: ADDB b0001b0101 00010x51
    b0100 [4]: OUT b0100b1010 01000xA4
    b0101 [5]: ADDA b0001b0000 00010x01
    b0110 [6]: OUT b1000b1010 10000xA8
    b0111 [7]: ADDB b0001b0101 00010x51
    b1000 [8]: OUT b0100b1010 01000xA4
    b1001 [9]: ADDA b0001b0000 00010x01
    b1010 [A]: OUT b0010b1010 00100xA2
    b1011 [B]: ADDB b0001b0101 00010x51
    b1100 [C]: JMP b0000b1111 00000xF0
    b1101 [D]: OUTB b1000 00000x80
    b1110 [E]: ADDA b0000b0000 00000x00
    b1111 [F]: ADDA b0000b0000 00000x00
    Current line: b1110 [E]

    > O
    Supported OpCodes:
    b0000 dataADDA im
    b0001 0000MOVAB
    b0010 0000INA
    b0011 dataMOVA im
    b0100 0000MOVBA
    b0101 dataADDB im
    b0110 0000INB
    b0111 dataMOVB im
    b1000 0000OUTB
    b1001 0000OUT2B
    b1010 dataOUT im
    b1011 dataOUT2 im
    b1100 dataJNCB im
    b1101 dataJMPB im
    b1110 dataJNC im
    b1111 dataJMP im

    > C
    Clearing RAM ... Done

    Find the code on GitHub here.

    Conclusion

    The basics for this actually came together fairly quickly, but I must admit to spending a fair bit of time fiddling about with output formats and refactoring various bits of code to try to give some consistency in terms of when newlines are applied, what is shown in binary, what in hex, and so on.

    I can’t guarantee everything has been caught, but I’ve typed in all the code (using the newer, limited syntax) from Part 3 and they all seem to work.

    It would be nice to be able to automatically reset the TD4 from the Arduino, but for now, pressing the button when required is fine.

    For the most part, unless there is a loop to get caught in, the code will cycle back to the start anyway.

    In terms of possible updates and enhancements, there are a few on my mind:

    • It would be nice to support the undocumented use of immediate values somehow.
    • It might be nice to have a way to save/load the code. It only needs to be a string of 16 2-byte hex codes.
    • It might be nice to have several demo programs to choose from.

    If I expand the instruction set and architecture, then I’ll have to think again about chunks of this code, but for now, it seems to work pretty well.

    Kevin

    #4bit #arduinoUno #define #td4

  7. TD4 4-bit DIY CPU – Part 6

    Having now successfully built my own version of the TD4 4-bit CPU in Part 5, I’m now chewing over some of the ways I’d like to try to expand it.

    • 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

    I already have a list of others extended projects at the end of Part 4, so I might be drawing on some of them for inspiration moving forward. Many of these are very similar projects, but with a completely different architecture. But really at this stage rather than build a different, more capable, 4-bit CPU from someone else’s design, I’m interested in seeing how far the TD4 design can go. So, ultimately, like all my projects, the fun here is in the reinventing and learning on the way.

    One of the questions I have is can I replace the DIP switches with something that can provide the data in a better way? This would be particularly critical if I expand the address space in the future. A ROM is the obvious option, but something more dynamic might be an interesting experiment too.

    This post looks at options for replacing the DIP switches with microcontrollers.

    Now I feel like I really ought to state right up front that this is a pretty ludicrous thing to do.

    At the more charitable end of the endeavor I’m using a 16MHz 8-bit AVR microcontroller with 2kB of RAM to serve up 16 8-bit values to a 10Hz 4-bit CPU.

    At the most extreme end I’m using a 125MHz, dual-core, 32-bit ARM Cortex M0+ CPU with 264 kB of RAM running an entire interpreted programming environment requiring (probably) millions of lines of low-level code to implement it, to do the same thing.

    So why bother? Well – why not?

    TD4 without the ROM

    To interface to a microcontroller, I’m after two things:

    • Ability to read the 4 address lines.
    • Ability to drive the 8 data lines.

    The best place to get at these signals is on the interface to the ROM itself – the 74HC540 octal line driver, and 74HC154 4-to-8 line decoder.

    Conveniently, these signals can be broken out quite easily on my board as shown below.

    The pink shaded area shows which components are needed for a ROM-less build. The two yellow highlights show where headers should be soldered to permit access to the address lines (top) and data lines (bottom).

    In this build, the following components are omitted from the full board:

    • 74HC154
    • 74HC540
    • 16x 8-way DIP switches
    • 128x small signal diodes
    • 8x 10k pull-up resistors

    I’ve used 6-way and 10-way pin header sockets to allow me to patch in a microcontroller. This allows for each header to conveniently include 5V and GND too. I’ve included the USB socket for power to the PCB but expect I’ll probably power the board via these 5V and GND links from the microcontroller.

    Using Arduino

    The natural choice here is to use one of the older Arduino boards, as these are all 5V IO which makes interfacing with the 4-bit CPU fairly straight forward.

    Using Arduino direct PORTIO should also make it pretty trivial to read address lines and write the data. I’ve configured the connections as follows:

    TD4 SignalArduino GPIOArduino PORTIOA0A0PORTC:0A1A1PORTC:1A2A2PORTC:2A3A3PORTC:3D0D8PORTB:0D1D9PORTB:1D2D10PORTB:2D3D11PORTB:3D4D4PORTD:4D5D5PORTD:5D6D6PORTD:6D7D7PORTD:7

    I’m avoiding D0/D1 (PORTD[0:1]) and D13 as they all have other hardware attached (serial port and LED in this case).

    Accessing the data corresponding to any specific address is as simple as follows:

    uint8_t ROM[16];

    loop:
    unt8_t addr = PINC & 0x0F
    PORTB = (PORTD & ~(0x0F)) | (ROM[addr] & 0x0F);
    PORTD = (PORTD & ~(0xF0)) | (ROM[addr] & 0xF0);

    The code could be simplified if I didn’t mind trashing whatever is configured for the other GPIO pins via the PORTIO, but it is good practice to preserve those values when only writing to a subset of the IO ports.

    In the final code below, I’ve included a toggle for A5 which allows me to do some timing measurements too.

    uint8_t ROM[16] = {
    0xB1, 0x01, 0xB2, 0x51,
    0xB4, 0x01, 0xB8, 0x51,
    0xB4, 0x01, 0xB2, 0x51,
    0xF0, 0x00, 0x00, 0x00
    };

    void setup() {
    DDRB |= 0x0F;
    DDRD |= 0xF0;
    DDRC |= 0x20;
    }

    int toggle;
    void loop() {
    if (toggle == 0) {
    toggle = 1;
    PORTC |= 0x20;
    } else {
    toggle = 0;
    PORTC &= ~(0x20);
    }

    uint8_t addr = PINC & 0x0F;
    PORTB = (PORTD & ~(0x0F)) | (ROM[addr] & 0x0F);
    PORTD = (PORTD & ~(0xF0)) | (ROM[addr] & 0xF0);
    }

    Running the code in a loop like this gives a scan frequency of around 500kHz and a response time of something like 2-3 uS for each read. That seems pretty responsive and I’m sure will be fine for a 10Hz CPU. And it is – it works great!

    Using Circuitpython

    One thing that would be really nice is a workflow that allows more of a “direct save to the CPU” approach to programming it. One option is to use a more modern microcontroller that supports a filesystem.

    The obvious choice here is a 32-bit microcontroller that supports Circuitpython. But will IO in Circuitpython be fast enough to respond to the CPU? There is one obvious way to find out – give it a try.

    There is another complication too – most Circuitpython boards run at 3.3V not 5V so that needs to be addressed too.

    Level Shifting

    I’m going to use a 74LVC245. The Adafruit product page puts it best:

    “essentially: connect VCC to your logic level you want to convert to (say 3.3V), Ground connects to Ground. Wire OE (output enable) to ground to enable the device and DIR (direction) to VCC. Then digital logic on the A pins up to 5V will appear on the B pins shifted down to the VCC logic.”

    This is an 8-way bi-directional bus transceiver and should be powered by 3V3, then the direction pin will determine the direction of the conversion as shown[ below.

    Two devices will be required. The address lines will need a 5V to 3V3 conversion; the data lines will need 3V3 o 5V.

    Here is how I’ve wired these up for a Raspberry Pi Pico:

    The Pico is connected as follows:

    • INPUT: GPIO 10-13 = A0-A3
    • OUTPUT: GPIO 2-9 = D7-D0 (not the ordering!)

    CircuitPython ROM

    The basic algorithm will be as follows:

    ROM = [16 command byte values]

    LOOP:
    Read four address lines
    Set data lines from ROM[address]

    For performance reasons it would be best to optimise both the reading of the address lines and the writing of the data lines, ideally into a single access. But as this is for a CPU that runs at a maximum of 10Hz, so for now, I’m just going with simple and see how it goes.

    import board
    import digitalio

    ROM = [
    0xB1, 0x01, 0xB2, 0x51,
    0xB4, 0x01, 0xB8, 0x51,
    0xB4, 0x01, 0xB2, 0x51,
    0xF0, 0x00, 0x00, 0x00
    ]

    Tpin = digitalio.DigitalInOut(board.GP21)
    Tpin.direction = digitalio.Direction.OUTPUT

    A0pin = digitalio.DigitalInOut(board.GP10)
    A1pin = digitalio.DigitalInOut(board.GP11)
    A2pin = digitalio.DigitalInOut(board.GP12)
    A3pin = digitalio.DigitalInOut(board.GP13)

    D0pin = digitalio.DigitalInOut(board.GP2)
    D0pin.direction = digitalio.Direction.OUTPUT
    D1pin = digitalio.DigitalInOut(board.GP3)
    D1pin.direction = digitalio.Direction.OUTPUT
    D2pin = digitalio.DigitalInOut(board.GP4)
    D2pin.direction = digitalio.Direction.OUTPUT
    D3pin = digitalio.DigitalInOut(board.GP5)
    D3pin.direction = digitalio.Direction.OUTPUT
    D4pin = digitalio.DigitalInOut(board.GP6)
    D4pin.direction = digitalio.Direction.OUTPUT
    D5pin = digitalio.DigitalInOut(board.GP7)
    D5pin.direction = digitalio.Direction.OUTPUT
    D6pin = digitalio.DigitalInOut(board.GP8)
    D6pin.direction = digitalio.Direction.OUTPUT
    D7pin = digitalio.DigitalInOut(board.GP9)
    D7pin.direction = digitalio.Direction.OUTPUT

    def doOutput (data):
    if (data & 0x01):
    D0pin.value = True
    else:
    D0pin.value = False

    if (data & 0x02):
    D1pin.value = True
    else:
    D1pin.value = False

    if (data & 0x04):
    D2pin.value = True
    else:
    D2pin.value = False

    if (data & 0x08):
    D3pin.value = True
    else:
    D3pin.value = False

    if (data & 0x10):
    D4pin.value = True
    else:
    D4pin.value = False

    if (data & 0x20):
    D5pin.value = True
    else:
    D5pin.value = False

    if (data & 0x40):
    D6pin.value = True
    else:
    D6pin.value = False

    if (data & 0x80):
    D7pin.value = True
    else:
    D7pin.value = False

    while True:
    Tpin.value = True
    addr = 0
    if (A0pin.value == True):
    addr = addr + 1
    if (A1pin.value == True):
    addr = addr + 2
    if (A2pin.value == True):
    addr = addr + 4
    if (A3pin.value == True):
    addr = addr + 8

    Tpin.value = False
    doOutput(ROM[addr])

    I’ve included a timing pin to GPIO21 so I can see how long it takes to access the IO.

    It turns out that it takes something of the order of 50-60uS to read the four address lines and something in the region of 70-80uS to write out the 8 data lines. The above simple Circuitpython code to do this is running with a frequency of around 7kHz.

    Now at this point I ought to be reading through the datasheets for the ICs used in the CPU to check response times and timing tolerances so see if this is ok. But I didn’t bother with any of that as it all appears to work!

    Conclusion

    The Circuitpython is obviously a lot slower than the Arduino running optimised PORTIO code, even though the Circuitpython is running on a 125MHz processor compared to the Arduino’s 16MHz. Of course, if performance was critical then switching to direct GPIO access in C on the Pico would be a lot faster again. Even just having a way to do a single block-access of GPIO would probably make quite a difference.

    But for this application, either as they are seem to work absolutely fine.

    The ability to quickly edit the ROM contents is pretty useful with the Circuitpython. But I am now wondering how difficult it would be to have some kind of uploader to the Arduino over the serial port. There are only 16 bytes to transfer after all.

    In fact it might even be possible to create a simple interactive assembler that allows code to be typed in over the serial port using proper word-based op-codes (like ADD, IN, OUT, etc). At the very least a simple serial port interface to type in numeric values would be relatively straight forward I think. It might also be possible to allow the microcontroller to reset the CPU too.

    I’m not sure the added complications of logic shifting, etc, make it worth carrying on with a Pico version at this stage, so I think improving the Arduino is probably the way to go for now.

    Kevin

    #4bit #arduinoUno #circuitpython #PORTIO #raspberryPiPico #TD4

  8. Arduino AY-3-8910 Shield Build Guide

    Here are the build notes for my Arduino AY-3-8910 Shield Design.

    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

    • Arduino AY-3-8910 Shield PCB (GitHub link below)
    • 2x AY-3-8910 or YM2419 devices (see notes on sourcing here: Arduino and AY-3-8910)
    • 8x 1KΩ resistors
    • 2x 100nF ceramic capacitors
    • 1x or 2x 1uF electrolytic capacitors
    • 1x 3.5mm stereo TRS socket (see photos and PCB for footprint)
    • Arduino headers: 1x 6-way; 2x 8-way; 1x 10-way pin or extended headers as required
    • Optional: 2x 40 pin wide DIP sockets (highly recommended)

    If both chips audio outputs are to be combined, using the solder bridges, then only one 1uF electrolytic capacitor should be used.

    Build Steps

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

    • Resistors.
    • Disc capacitors (see notes below).
    • DIP sockets (if used) and TRS socket.
    • Electrolytic capacitors.
    • Arduino headers.

    There are two solder bridge jumpers which can be used for the following:

    1. To mix all channels from both chips onto the same output.
    2. To combine left and right channels for the TRS socket.

    By default, one chip goes to the left audio output and one goes to the right, but it is possible to combine them into a single mono output. But then there is another choice: combine the left and right audio channels (tip and ring) for the TRS socket; or leave all outputs just to the tip of the socket.

    If these options are being considered, then one of the output electrolytic capacitors should be omitted too. More details below.

    Here are some build photos.

    The ceramic capacitors are actually shown as being installed on the underside of the board, but depending on the 40 pin DIP socket used (or not) it may be possible to install them on the top side of the board as I’ve done below.

    I’ve used “extended headers” which give me a breakout for the Arduino GPIO on the top of the board. If simple pin headers are used, then care should be taken about the height of the board and avoiding the possibility of the resistors shorting out on the USB socket of the Arduino.

    Solder jumper options

    For mono operation:

    • Only install electrolytic capacitor highlighted in PURPLE. Do not install the capacitor with the YELLOW cross.
    • Bridge the solder jumper highlighted in RED.

    For mono socket operation, i.e. TIP and GROUND only, leave the solder bridge highlighted in ORANGE unbridged. This allows a mono jack lead to be used as RING is unconnected in the socket and can be ignored.

    To take the mono signal into a stereo socket, i.e. TIP, RING and GROUND but with TIP and RING having the same mono output signal, solder the bridge highlighted in ORANGE. This allows a stereo jack lead to be used and both channels will received the same output signal.

    Testing

    I recommend performing the general tests described here: PCBs.

    Once everything appears electrically good, a variation of the test application from my AY-3-8910 Experimenter PCB Build Guide can be used that will play a chord on both of the devices at a different octave.

    Note: the GPIO usage of the Arduino is printed on the back of the PCB and listed in the Arduino AY-3-8910 Shield Design.

    Find the code on GitHub here.

    PCB Errata

    There are no known issues with the PCB at present. 

    Enhancements:

    • None 

    Find it on GitHub here.

    Closing Thoughts

    This seems to work fine and is a lot simpler than my quad board if some simple experimentation is required.

    I still haven’t gotten around to building some real applications for any of these boards yet though, so ought to get on to that.

    Kevin

    #arduinoUno #ay38910 #pcb

  9. Arduino AY-3-8910 Shield Design

    Having build my AY-3-8910 Experimenter PCB I thought a slightly simpler format board would be useful, so I’ve put together an Uno shield-format PCB that can support one or two AY-3-8910 chips.

    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.

    The Circuit

    This is simply the two-chip version of my AY-3-8910 Experimenter PCB Design. I fixed the “reset on D13” thing though, so the connections are now as follows:

    ArduinoAY-3-8910D2-D9D0-D7D10CLOCKD11/RESETA0/A1BC1/BDIR for device AA2/A3BC1/BDIR for device B

    This leaves A4/A5 free for analog IO or I2C, D0/D1 free for the UART, and D12/D13 free for other uses including the on-board LED on D13.

    I trimmed down the output audio stage, but arranged one chip on the L channel and one chip on the R channel, but left the solder jumpers in to allow the mixing of both devices onto single or dual channels too.

    I’ve also added the two pull-downs, opting for the same arrangement as the patch on the previous board, and I’ve correctly the silkscreen capacitor values.

    PCB Design

    I’ve just managed to squeeze everything into the Uno shield format. I put the chips smoothing capacitors on the underside of the board to allow them to sit close to the chips’ power pins.

    I’ve broken out the spare GPIO pins to additional headers, partly to make it clear which pins are spare.

    I’ve also listed the GPIO usage for each chip on the underside of the board.

    I was tempted to remove the mounting holes at the shaped end of the board, but left them in the end. One is a bit close to the additional breakout headers for A4/A5, but I have the option not to add those if I want to.

    I have extended the board slightly though compared to the traditional Arduino shield shape just to accommodate the length of the 40-pin devices a bit more easily.

    Closing Thoughts

    I must admit I wasn’t sure if I could get two 40-pin wide DIP devices onto a shield, but it just about fits.

    Fingers crossed, having a four-device version already, this will be a little easier to get going than the last one!

    Kevin

    #arduinoUno #ay38910 #pcb

  10. Arduino and SP0256A-AL2 – Part 6

    Finally I’m doing something arguably slightly musical with my SP0256A-AL2! Playing it over MIDI.

    • Part 1 – Basic introduction and getting started
    • Part 2 – Arduino programmable clock
    • Part 3 – Using a Raspberry Pi Pico as a programmable clock
    • Part 4 – Using a HC4046 PLL as the clock
    • Part 5 – Using an I2C SI5351 programmable clock
    • Part 6 – Adding MIDI

    https://makertube.net/w/wLRxeVHtMatYo7NGWCw54U

    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.

    The Circuit

    This is using my Arduino SP0256A-AL2 Shield with an SI5351 programmable clock module, so this is essentially the circuit from Arduino and SP0256A-AL2 – Part 5 with the addition of a MIDI module.

    I also used my Arduino MIDI Proto Shield but any MIDI RX circuit on the Arduino RX pin would work.

    The same circuit can be made on solderless breadboard, but I’ve opted to use my PCBs for convenience as that is why I designed them in the first place.

    The Code

    I’m using all the main code from Arduino and SP0256A-AL2 – Part 5 but I’ve split the speaking into “start” and “stop” parts so I can drive them from MIDI NoteOn and NoteOff messages.

    The main “speak” code now looks like the following:

    void speak (uint8_t note) {
    if (note == 0) {
    spAllo(PA3); // Speaking off
    return;
    }

    if ((note < MIDI_NOTE_START) || (note > MIDI_NOTE_END)) {
    return;
    }

    midi2clock (note);
    switch(note){
    case 36: // Do
    case 37:
    case 48:
    case 49:
    spAllo(DD1);
    spAllo(OW1);
    break;

    case 38: // Re
    case 39:
    case 50:
    case 51:
    spAllo(RR1);
    spAllo(EY1);
    break;

    ...

    So when called with note==0 it will stop the speaking by sending PA3, but for any note between 36 and 56 it will set the frequency and then say the appropriate “Do, Re, Mi” word. I’ve used the same word for the natural and sharp equivalent. I’ve also allowed for two octaves. This is why there are four note values that result in the word “Do” being said: MIDI notes 36, 37 (C2 and C#2), 48, and 49 (C3 and C#3).

    This can now be called from the code that handles MIDI Note On and Note Off messages.

    Find it on GitHub here.

    Closing Thoughts

    It works, but it isn’t very responsive due to the time it takes to say each allophone.

    I guess when I started thinking about doing this, I thought I’d get more of a frequency range from the device and wasn’t anticipating it being so “low” in the musical range either.

    But it is quite fun to see it finally being driven by a music keyboard, even if it isn’t particularly practical!

    I know there is emulation of an SP0256A-AL2 in the RC2040 (an emulation of the RC2014 on a Raspberry Pi Pico) so I might at some point look at seeing if I can just drop the hardware and doing it all in software at some point by adjusting sample and playback rates.

    I suspect that will work a lot better, but it isn’t quite the same as driving the real thing 🙂

    Kevin

    #arduinoUno #midi #si5351 #sp0256aAl2

  11. Arduino SP0256A-AL2 Shield PCB Build Guide

    Here are the build notes for my Arduino SP0256A-AL2 Shield Design.

    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

    • Arduino SP0256A-AL2 Shield PCB (GitHub link below)
    • 1x SP0256A-AL2 (see notes here on sourcing: SP0256A-AL2 Speech Synthesis)
    • Either 1x 3.579545 MHz oscillator (4-pin in 8-pin DIP footprint – see photos)
    • Or 1x SI5351 breakout board (see photos)
    • 2x 1KΩ resistors
    • 3x 100nF ceramic capacitors
    • 1x 1uF electrolytic capacitor
    • 1x 3.5mm stereo TRS socket (see photos and PCB for footprint)
    • 1x set of Arduino headers: 1x 10 pin; 2x 8 pin; 1x 6 pin
    • Optional: 1x 28 pin wide DIP socket

    Build Steps

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

    • Resistors
    • DIP socket (if used) and TRS socket.
    • Disc capacitors.
    • Electrolytic capacitor.
    • 3-way jumper headers.
    • Oscillator (if used)
    • SI5351 pin headers (if used)
    • Arduino headers

    It should be decided up front if the board will use a fixed oscillator or a SI5351 programmable clock. Both could be installed, but there is a solder bridge that has to be used to determined which will be used. It isn’t possible to use both at the same time.

    This shows the solder bridge configured to use the SI5351. This might be easiest to do prior to soldering other components on the board.

    Here are some build photos.

    Here is a photo with the oscillator installed, and one with the SI5351 instead.

    Note: I didn’t solder the pin headers of the SI5351 to the PCB, but instead used the “fishing line trick” to push the board into the PCB without soldering. This means I have the option of reusing the board again for something else in the future.

    By default the SP0256A-AL2 /RESET line is connected to the Arduino RESET pin. It is possible to break this link by cutting the solder bridge shown below and wiring the RESET pad to another Arduino GPIO pin.

    Testing

    I recommend performing the general tests described here: PCBs.

    PCB Errata

    There are no known issues with this PCB at this time.

    Enhancements:

    •  With hindsight it might have been useful to have a jumper option to select the clock mode rather than a solder bridge. But it seemed quite a fundamental choice at the time of designing the PCB that I thought it perhaps shouldn’t be quite so easy to change. Now I’m not so sure!

    Find it on GitHub here.

    Sample Applications

    Here are some applications to get started with:

    Closing Thoughts

    Now it is a bit easier to experiment I can explore a few other musical possibilities.

    But this has shown up a slight issue with the chips I have. The highest frequency that the chip can support without locking up seems to be somewhat device dependent.

    I’ve seen nothing in the datasheet, application manual, or design guide that suggests it will function at all with anything other than a 3.12MHz oscillator, so I am running it quite a bit out of specification now.

    Kevin

    #arduinoUno #pcb #si5351 #sp0256aal2

  12. Arduino SP0256A-AL2 Shield Design

    Having spent quite a bit of time with an Arduino hooked up to an SP0256A-AL2 on a solderless breadboard, and now that I’ve got some ideas for how I want to sort out the clock, I thought it time to create a PCB to save the unreliable wiring getting in the way!

    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.

    The Circuit

    This is the basic circuit I’ve been using since Part 1 but I’ve added in an option to support the SI5351 from Part 5 instead of the oscillator, too. A solder jumper selects the clock source – there is no default setting.

    There is also a (default closed) solder jumper on the RESET line in case there is a need to separate out the reset of the SP0256A-AL2 from the Arduino.

    PCB Design

    The PCB design is relatively straight forward. Using an Arduino Uno Shield template, all the components have fitted in quite well.

    I had to create a custom symbol and footprint for both the SP0256A-AL2 itself and the SI5351 module I’m using.

    The following GPIO pins are in use.

    ArduinoSP0256A-AL2D2-D7A1-A6D8/ALDD9SBYGNDVSS, A7, A8, TEST, OSC2+5VVDD, VDI, SE, /SBY_RESET/RESET/RESETSI5351 (Optional)A4SDAA5SCLGNDGND+5VVIN

    As already mentioned, the link between the two /RESET pins is via a solder jumper and pin header connection which can be broken if required.

    I’ve added in additional breakout headers for all GPIO that isn’t connected to the SP0256A-AL2: D0-D1, D10-D13, A0-A3.

    I’ve not included A4-A5 as these are connected to the SI5351 (if used).

    Closing Thoughts

    Hopefully this is a fairly straight forward design with no major surprises.

    Kevin

    #arduinoUno #pcb #si5351 #sp0256aal2

  13. Ekranı i2C Karakter LCD olan örnek var da, o ekranlar ile yapılabilecekler çok sınırlı olduğu için istemiyorum.

    #arduino #arduinouno

    youtube.com/shorts/EYGTKiw--Wk

  14. İki tâne Arduino var. Köle olan ekranlıyı sürmek için gerekli pinlerden hâriç bir sürü pin boşta kalıyor. Onlarla da istediğini yaptır, ekranda sonuçları yazdır!

    Ben daha yeni öğreniyorum!
    Neden her şeyi zorlaştırıyorsunuz?

    Uzaktan Kumandalı RDS'li FM Radyomu yaparken de hiç örnek yoktu!

    Ben yapıp paylaştıktan sonra YouTube'da bir sürü örnek çıktı!

    Benim gibi acemiden öğrenip paylaşmaya utanmıyor musunuz?

    ..devamı yanıtta..

    #arduino #arduinouno

  15. Arduino bu yaaa!.. Bir tâne Âllah'ın kulu yapıp da YouTube'a video ve GitHub yada GoogleDrive'a kodunu atmamış!

    Bunlar bir tek benim aklıma geliyor olamaz!

    Niye yapmıyorsunuz arkadaş?

    Ekrana veri yazdırmak eğlenceli bir şey değil mi?

    YouTube'da Arduino içerik paylaşan zilyon tâne hesap var, birinin de mi aklına gelmemiş yada ilgisini çekmemiş?

    ..devamı yanıtta..

    #arduino #arduinouno

  16. Lehimle içeriden hâlledilebilir, ama o pinleri ekranın kullanmaması lazım!

    Ekran takılı Arduino UNO'nun muhafaza kutusunu delme fikri hoşuma gitmediği için proje kalmıştı!

    Fakat şimdi yeni çıkan R4'lerde WiFi ve Bluetooth var!

    Sonuçta Arduino UNO'dan bahsediyoruz. Hareketli gif bile oynatamadığı için,

    Acaba iki tane Arduino® UNO R4 WiFi'yi Bluetooth ile (mecbûr kalmadıkça WiFi istemiyorum!) hâberleştirip, ekranı sürebilir miyim?

    ..devamı yanıtta...

    #arduino #arduinouno

  17. Arduino candır dedim ve şu benim eski Arduino UNO R3'lerden 2.8 Dokunmatik TFT Ekranlı olan ile diğerini haberleştirip, ekranlı olanı monitör gibi kullanma projemi tamamlayayım istedim.

    Fakat neden yarım kaldığını hatırladım!

    Ekran takılı olan UNO'da haberleşme için boşta uygun pin kalmıyordu!

    ..devamı yanıtta..

    #arduino #arduinouno

  18. Arduino and SP0256A-AL2 – Part 5

    This looks at another of the options from Part 4 – the I2C programmable Si5351 clock source.

    • Part 1 – Basic introduction and getting started
    • Part 2 – Arduino programmable clock
    • Part 3 – Using a Raspberry Pi Pico as a programmable clock
    • Part 4 – Using a HC4046 PLL as the clock
    • Part 5 – Using an I2C SI5351 programmable clock
    • Part 6 – Adding MIDI

    https://makertube.net/w/bX5QNRRkAomFJQo7qY17he

    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.

    I2C Si5351 Programmable Clock

    From the datasheet of the Si5351:

    “The Si5351 is an I2C configurable clock generator that is ideally suited for replacing crystals, crystal oscillators, VCXOs, phase-locked loops (PLLs), and fanout buffers in cost-sensitive applications. Based on a PLL/VCXO + high resolution MultiSynth fractional divider architecture, the Si5351 can generate any frequency up to 160 MHz on each of its outputs with 0 ppm error.”

    The device itself requires a 3V to 3.6V supply, but typical breakouts seem to include a LDO regulator meaning it can be powered from 3V to 5V. Logic outputs are always 3V but the I2C lines will be the same as the power supply.

    The Circuit

    I’m using a breakout board like the one shown above. This has header pins for the three clock outputs, power and ground, and I2C. Although the si5351 device itself is a 3V3 device, most breakouts like this seem to include components to allow them to be powered by either 3V3 or 5V. I’m using 5V in my circuit.

    I’m only using one of the clocks, so output 0 is fed into the OSC1 input of the SP0256A-AL2. Otherwise the rest of the SP0256A-AL2/Arduino circuit is the same as for part 1.

    The Code

    There are two libraries I’ve found for this:

    The Adafruit library is a fairly low-level interface to the device. The device basically has a multiplier which is used to set a PLL clock to somewhere between 600 and 900MHZ; and then a divisor to drop that back down to something useful.

    But the reality of setting the parameters is actually quite complicated. There is a full discussion of how to do it, with some example Arduino code, here: https://rfzero.net/tutorials/si5351a/

    It is not for the faint hearted!

    Thankfully the second library mentioned above, by “EtherKit”, has a ‘set_freq()’ function that does it all for us. The code to use it is therefore fairly straight forward:

    #include <si5351.h>
    #include <Wire.h>

    Si5351 si5351;

    void clockSetup () {
    si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
    si5351.set_freq(300000000ULL, SI5351_CLK0);
    si5351.update_status();
    }

    void setClock (uint64_t freq) {
    si5351.set_freq(freq, SI5351_CLK0);
    si5351.update_status();
    delay(300);
    }

    The initialisation function requires three things:

    • Which of 6, 8 or 10pF capacitors are used with the external oscillator.
    • Frequency of the external oscillator. Passing in 0 uses the default, 25MHz.
    • A parameter for frequency correction. I’m just using 0.

    The set_freq() function takes a 64-bit value which gives a frequency in 0.01Hz units, so 3MHz is 3 followed by 8 zeros. This is an “unsigned long long” type, hence the “ULL” initialiser after the value in the code above. The other parameter states which clock to use – I’m just using clock 0.

    It is possible to wire up a pot to an analog input and set the frequency using something like the following:

    int alglast = 0;
    void checkClock (void) {
    int algval = analogRead(ALG_IN);
    if (algval != alglast) {
    uin64_t freq = 1000 + 5 * analogRead(ALG_IN); // in kHz
    setClock(freq*100000); // Convert to 0.01Hz units
    }
    alglast = algval;
    }

    This maps a pot reading onto frequency values between 1MHz and just over 6MHz in units of 5kHz.

    Alternatively, it is possible to set pitches for individual allophones. I’ve found that loosely speaking, 3MHz seems to correspond to talking at the pitch of G#2 (MIDI note 44) which is an audio pitch frequency of round 98Hz. 6MHz is, as you might expect, G#3 (MIDI note 56 at 196Hz).

    This means that the I can calculate the required clock frequency for a MIDI note M using the formula:

    • Freq = 3MHz * 2 ^ (M – 44)/12

    This function will set the clock based on the MIDI note number:

    void midi2clock (int m) {
    if (m < 36 || m > 56) {
    return;
    }

    freq = 300000000 * pow (2.0, (((double)m-44.0)/12.0));
    setClock (freq);
    }

    Note how I’ve limited the range to between C2 (36) and G#3 (56), which means frequencies of 1.889MHz to 6MHz.

    It would probably go a bit lower – down to 1MHz is probably practical from the point of view of the chip functioning, but not so useful from the point of view of how long it would take to say a single allophone. Anything higher will cause the SP0256A-AL2 to lock up in a funny state.

    But that gives me a good octave and a fifth, which is quite a useful and practical range.

    Warning: The top frequency seems to be device dependent for me. I have one that can support the full 1.5 octaves and one that locks up after just an octave, so some experimentation is required!

    Find it on GitHub here.

    Closing Thoughts

    This is the most accurate and simplest to set up manner of providing a programmable clock I’ve found so far. The device itself is actually quite complex to use, but all that complexity has been hidden away in the Si5351 library published by EtherKit.

    The device sometimes seemed to get stuck in a weird state where is wasn’t recognised on the I2C bus. A power cycle or reset, or some combination of both, was usually required to get it going again. I don’t know if that was dodgy cables somewhere, but when it got stuck, curiously my nearby FM radio receiver lost its signal… Hmm.

    The downside of using the Arduino for both clock and speech control is that it isn’t possible to adjust the clock whilst the speech is happening. That would need some kind of parallel execution to manage that – either adding in another microcontroller, or maybe moving to a dual-core microcontroller.

    But as you can hear from the end of the video, this could still be pretty useful and I wish I’d had it for my Electric Lo-Fi Orchestra Concerto for a Rainy Day.

    Kevin

    #arduinoUno #electricLofiOrchestra #include #si5351 #sp0256aAl2

  19. Arduino and SP0256A-AL2 – Part 4

    Having now tried both an Arduino and Raspberry Pi Pico as programmable clocks for the SP0256A-AL2, this post starts to look at some alternative solutions, starting with a HC4046 phase-locked loop, voltage-controlled oscillator clock.

    • Part 1 – Basic introduction and getting started
    • Part 2 – Arduino programmable clock
    • Part 3 – Using a Raspberry Pi Pico as a programmable clock
    • Part 4 – Using a HC4046 PLL as the clock
    • Part 5 – Using an I2C SI5351 programmable clock
    • Part 6 – Adding MIDI

    https://makertube.net/w/8eRTaCvqb3bsmZEJFy94E3

    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.

    Some Ideas

    Whilst trawling around the Internet (and remembering a comment from my earlier posts), I found two existing projects where changing the frequency was used as a way to add pitch to the SP0256A-AL2:

    Both use a VCO approach. The latter using a LTC6903 programmable oscillator; and the former using a CD4046 PLL device.

    The LTC6903 looks like it would be a lot more complicated for me to use (especially being SMT), but I have some DIP 4046 devices already in the parts drawer so that is definitely an option for me.

    I’ve also found (and now ordered) some breakout boards based on the SI5351 based programmable clock module. They all seem to follow Adafruit’s lead: https://learn.adafruit.com/adafruit-si5351-clock-generator-breakout/overview

    One last thought – use a 555. No really! There are some versions that can go up o 3, 4 or even 5MHz apparently, see: https://en.wikipedia.org/wiki/555_timer_IC#Derivatives Of course having that in any way programmable might be a challenge, but it isn’t out of the question.

    4046 VCO/PLL

    A voltage-controlled oscillator is just what it sounds like. An oscillator whose frequency is related to the level of a control voltage on its input.

    Here is a pinout and functional view of the 4046 from the TI 74HC4046/74HCT4046 datasheet. This provides several phase comparators and a voltage-controlled oscillator to create a phased-locked loop controller.

    A key statement is the following:

    “The VCO requires one external capacitor C1 (between C1A and C1B) and one external resistor R1 (between R1 and GND) or two external resistors R1 and R2 (between R1 and GND, and R2 and GND). Resistor R1 and capacitor C1 determine the frequency range of the VCO. Resistor R2 enables the VCO to have a frequency offset if required.”

    The “application information” section describes the “with offset” operation:

    There are a lot of graphs of characteristics in the datasheet for the various combinations of R1, R2 and C1 – these are the “figure 27-32” mentioned above. Actually, to cover all options, there are figures 10 through to 43, but most of those cover other situations.

    The criteria for selecting R1, R2 and C are:

    • R1 Between 3kΩ and 300kΩ
    • R2 Between 3kΩ and 300kΩ
    • R1 + R2 Parallel Value > 2.7kΩ
    • C1 Greater Than 40pF

    For the MIDI Narrator, the values being used are as follows:

    • R1 = 1.5K to 2.5K (adjustable)
    • R2 = 12K
    • C1 = 1nF

    Which I believe puts it in the following areas of the performance curves, although they aren’t strictly within the state required ranges…:

    So without detailed calculations and measurements, I think this puts the (centre) offset frequency at a low-number of MHz (extrapolating for 12K at 5V) and the Max/Min ratio (R2/R1) at something around 4 to 8.

    I believe, from looking at the other charts, that a higher R1 or C value gives a lower frequency. There are graphs for the following with the stated approx frequency ranges at 4.5V:

    • 1M5, 100nF: 0 < Freq <100Hz
    • 150K, 100nF: 0 < Freq < 1kHz
    • 5K6, 100nF: 0 < Freq < 20kHz
    • 150K, 50pF: 0 < Freq < 1.5MHz

    One last thing to note from figure 32 – it states that a Vin of 0.95 VCC will generate the maximum frequency and 0V for the minimum.

    It turns out I have some CD4046 in my parts box, not a HC or HCT 4046. On initial inspection, there doesn’t seem to be a lot of difference that would significantly affects my messing around. Some of the pins (that I’m not using) have different functions:

    But as I’m not using those pins, that probably won’t matter. But then on closer inspection, I can see a key difference that definitely will affect my messing about – the operating frequency ranges:

    • HC/HCT4096: Up to 18MHz at VCC=5V.
    • 4096: Up to 1.4MHz at VDD=10V.

    And digging into the detailed spec for the CD4046, when running at 5V the maximum is down to just 0.8MHz typical.

    So I could use them to test out some general principles but need a HC/HCT version to really achieve what I’d like to do.

    At this point my understanding was starting to struggle, and those graphs weren’t helping much. So I just went ahead and built the version of the circuit from the Rarewaves MIDI Narrator. I really wasn’t having much luck with it as is however, so started fiddling about with different components and an oscilloscope to see if I could make any sense from it.

    There is a good article about the basics of a 4046 based VCO-driven PLL oscillator output here: https://hackaday.com/2015/08/07/logic-noise-4046-voltage-controlled-oscillator-part-one/

    I was finding that the output wasn’t a particularly clean square wave, and at higher frequencies was pretty much triangular. At this stage I don’t know how much of that is my measuring equipment. I have a scope probe with a 10pF capacitance apparently, so a 10K resistor and 10pF capacitor do create a low-pass filter that significantly rounds off the signal.

    But more significantly, the peak to peak voltage was greatly reduced to around 1.5Vpp with a significant BC bias. That wasn’t much use as a clock signal for me – from Part 3 I know the SP0256A-AL2 requires <0.6V for a LOW and >2.5V for a HIGH. Basically scope or on scope the output was pretty unreliable for me.

    I’d included the 10K resistor between the 4046 and SPA256A-AL2 as per the circuit for the MIDI narrator. But without that resistor, the clock is much cleaner and has much more useful levels, so I removed it. Later experiments show that a 1K resistor seems to work fine too.

    I also found that the suggested 1nF capacitor wasn’t giving me a particularly useful range. In the end, 4.7nF seemed to work well for me. Eventually after testing a range of different resistor values, I went back to 12K and 1.5K + 1K pot from the original circuit. This gives me:

    • The frequency range set by a combination of 4.7nF and 12K.
    • A frequency offset of between 1K and 2.5K is configurable.

    I believe this will pull the frequency offset down slightly from the previous values to something between 100kHz and 600kHz, according to the charts, but it gives me a very useful range between around 1MHz and 5MHz once tuned slightly with the trimpot.

    When going through the LM358 the usable voltage range for the VCO seems to be between ~2V and 3V for the 1MHz to 5MHz range. 0V is currently giving me 120kHz, tweaking of the offset resistor (R2 = 12K) helps here – 3K3 makes that around 500kHz, making the useful input voltage now around 1.5V to 3V. The issue is that reducing R2 starts to get to the point where the condition that R1||R2 > 2K7 no longer holds true. But either seems fine for some experimenting for now.

    My final circuit used for now is as follows:

    The CLK output was plugged directly into the SP0256A-AL2 OSC 1 input, with OSC 2 connected to GND.

    The control voltage pot goes through a LM358 which might not be strictly necessary in this case, but helps to at least stabilise the input voltage to the VCO. It might also be useful when I switch to programmatically driving the control voltage from a microcontroller.

    The usable clock frequency range is adjusted using the trimpot. Eventually, as mentioned, I settled on a range of around 1MHz to 5MHz. It will go lower, but that isn’t so useful for driving the SP0256A-AL2.

    Arduino PWM Voltage Control

    To get the Arduino to set the control voltage, requires generating a PWM signal which can be pretty simply done with analogWrite().

    The default PWM frequency is around 400kHz without messing about directly with Arduino timers. This will need filtering though, but a simple low-pass filter should easily work here, especially as the frequency won’t be changing very often.

    I just went back to the MIDI narrator circuit and could see that a 100K/220nF filter was used. Putting these into a low-pass filter calculator gives a cut-off frequency of just under 8Hz. This generates a pretty smooth output.

    There is a balance between the frequency range and offset chosen for the 4046 by the selection of R1, R2 and C, and the voltage range sent out of the Arduino. After a fair bit of messing around, I settled on the following:

    • R1 = 3K3
    • R2 = 10K
    • C = 560pF

    Then used analogWrite() with values between 60 and 200 to generate voltages and frequencies as follows:

    • analogWrite() value range: 60 to 200
    • PWM smoothed voltage range: 1.3V to 3.6V
    • LM258 output voltage range: 1.3V to 3.3V
    • Clock frequency range: 2.2MHz to 5MHz

    Setting up the clock via PWM voltage control is done as follows:

    #define SP0_CLOCK 10 // D10
    void clockSetup () {
    pinMode(SP0_CLOCK, OUTPUT);
    analogWrite(SP0_CLOCK, 128); // Approx 2.5V output
    }

    void setClock (int clock) {
    analogWrite(SP0_CLOCK, clock);
    }

    Closing Thoughts

    This seems to work really well. The control using a pot isn’t particularly reliable, but that is quite different if generating a control voltage from a microcontroller.

    I still need to do some calibrations to work out what voltages create which frequencies and then what pitch of voice reproduction that seems to produce, but that can come later. For now it is enough to know it appears to work albeit in still quite a hand-wavy kind of way.

    As an aside, whilst trawling around the Internet for this one I stumbled across the CTS256A-AL2 device. This is a microcontroller (I think it is 8051 based, based on a PIC7041) that performs text to allophone conversion for pairing with a SP0256A-AL2 for a complete text to speech system.

    There are some details, and an interesting DIY board, here: https://www.smbaker.com/cts256a-al2-text-to-speech-board and an older project here: https://8051bits.com/misc-projects/speech%20-%20music%20synthesizers/speech-mus-syn.html

    Unfortunately these seem very hard to come by these days, but there is some interesting emulation code here: https://github.com/GmEsoft/SP0256_CTS256A-AL2

    And a pretty thorough discussion about the device and its running code here: https://www.retrobrewcomputers.org/forum/index.php?t=msg&th=816&goto=10847

    Kevin

    #74HC4046 #arduinoUno #define #pll #sp0256aAl2

  20. Arduino and SP0256A-AL2 – Part 3

    Following on from using an Arduino as a variable clock in Arduino and SP0256A-AL2 – Part 2, I have some ideas for a few options, but this post looks in detail at using a Raspberry Pi Pico as the clock source.

    Spoilers: it kind of works, but isn’t quite the answer I need yet…

    • Part 1 – Basic introduction and getting started
    • Part 2 – Arduino programmable clock
    • Part 3 – Using a Raspberry Pi Pico as a programmable clock
    • Part 4 – Using a HC4046 PLL as the clock
    • Part 5 – Using an I2C SI5351 programmable clock
    • Part 6 – Adding MIDI

    https://makertube.net/w/bxBYCqHrZvQLwLuwYa5Z9r

    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.

    Using a RPi Pico

    The RP2040 can be overclocked quite a bit, so generating a variable square wave up in the few MHz range should presumably be relatively straight forward. Using the built-in PIO state machines for a square wave is fairly simple and it can be done from Circuitpython or Micropython too.

    This is a complete square wave generator for GP2 that steps down from 4MHz to 2MHz in steps of 100kHz. It can optionally overclock the RPi to 250 MHz too if required.

    import time
    import microcontroller
    import board
    import rp2pio
    import adafruit_pioasm

    square = adafruit_pioasm.assemble ("""
    .program square
    set pins, 1
    set pins, 0
    """)

    RP2040Freq = 125_000_000
    #RP2040Freq = 250_000_000

    print ("RP2040 Frequency = ", microcontroller.cpu.frequency)
    microcontroller.cpu.frequency = RP2040Freq
    time.sleep(1)
    print ("New RP2040 Frequency = ", microcontroller.cpu.frequency)

    while True:
    for freq in range (4000000, 2000000, -100000):
    print("\nReqd frequency = ", freq*2)
    print("Sq frequency = ", freq)

    sm = rp2pio.StateMachine(
    square,
    frequency=freq*2,
    first_set_pin=board.GP2,
    )
    print("Actual freq = {:d}".format(sm.frequency))
    print("Actual sq freq = {:d}".format(int(sm.frequency/2)))

    time.sleep(5)
    sm.deinit()

    The PIO program itself has two instruction steps, so takes two cycles to complete, so the running frequency has to be twice the desired frequency of the square wave. It automatically keeps looping, so no additional instructions are required there.

    The state machine will run at the system speed with a 16.8 fixed point fractional clock divider. Full details can be found in section 3.5.5 “Clock Dividers” in the RP2040 datasheet.

    For certain values there might be some jitter:

    If the system clock is faster though, the amount of jitter will be less I suspect, so it is advantageous to overclock the Pico for more accurate frequencies.

    The problem with this approach is that whilst I get a nice accurate clock source with quite a good resolution across its range, every time the state machine is recreated to change the frequency, there is a “blip” in the audio from the SP0256A-AL2 whilst its clock temporarily disappears!

    An alternative approach is to use a fixed state machine frequency but include a counter in the PIO program to allow for a configurable number of steps per scan of the PIO without having to stop and restart the clock.

    The problem with this is that I am limited to a unit of the instruction time for the PIO state machine which gives a fixed overhead, in terms of the instructions required for a minimal loop, and a flexible overhead, in terms of the counter I can pass in.

    The upshot of this is that I’m tied to a certain resolution of frequency change.

    I have the following PIO code:

    .program blink
    .side_set 1
    .wrap_target
    pull noblock
    mov x, osr
    mov y, x
    set pins, 1
    lp1:
    jmp y-- lp1
    nop
    nop
    mov y, x
    set pins, 0
    lp2:
    jmp y-- lp2
    .wrap

    The “pull” will update the output shift register (OSR) either with any new value written to the state machine or the last value of the X register. This value gets copied to Y to use as a counter. This happens twice, once for when the pin is set at 1 and once for when the pin is set at 0.

    There are two nops whilst the pin is set at 1 to balance for the original pull and mov instructions at the end of the pin set to 0 cycle.

    As the Y counter value is used twice, the flexible overhead of the timing is essentially proportional to count * 2. It counts for the pin being HIGH and then for the pin being LOW.

    The fixed overhead is the cost of the original pull, two moves, the pin sets, and a single jmp per pin state – so by using the two nops to ensure the HIGH and LOW times are the same, that is 10 instruction cycles.

    I was hoping to use the “side” instruction to eliminate the two set instructions, but so far I’ve not managed to get that to work. I still don’t understand PIO…

    So for now the timing of the PIO routine is = 10 + 2 * count and the unit is the time for a single instruction, which is 1 / frequency of the PIO execution, up to a maximum frequency of the Pico’s system clock frequency.

    Using an overclocked Pico at 250MHz, the frequency range would start at the following:

    • Execution freq = Pico Sys Clock / (10 + 2 * count)
    • So when count = 0; execution freq = 250MHZ / 10 = 25 MHz

    That is far too fast for the SP0256A-AL5. In fact, I’ve found that anything over around 5MHz causes the chip problems.

    For this reason, I’m using a minimum count of 20:

    • Max execution freq = 250MHz / (10 + 2 * 20) = 5 MHz

    Plotting execution frequency per “count” value (starting from 20) gives the following:

    We can see the limits of the resolution at the top-end, and in fact, the first few equivalent frequencies in that range are as follows:

    CountEquivalent Frequency205,000,000214,807,692224,629,629234,464,285244,310,344

    That is giving me something like a 150-200kHz jump each time, which isn’t great, but is probably the best I can do. I would be larger if I wasn’t overclocking the Pico. It does get smaller as the count increases, but it is only really worth going down to a count value of around 120, which is around 1MHz for the resulting clock. Anything lower than that and the SP0256A-AL2 isn’t particularly useful.

    Here is the full Circuitpython code which attaches a pot to GP26 to control the frequency in the range of around 900kHz up to 5MHz. Note the scaling of the pot value (0 to 65535) by 600 prior to its use to add to the count.

    import array
    import time
    import board
    import rp2pio
    import microcontroller
    import adafruit_pioasm

    from analogio import AnalogIn
    algin = AnalogIn(board.GP26) # ADC0

    blink = adafruit_pioasm.assemble(
    """
    .program blink
    .side_set 1
    .wrap_target
    pull noblock
    mov x, osr
    mov y, x
    set pins, 1
    lp1:
    jmp y-- lp1
    nop
    nop
    mov y, x
    set pins, 0
    lp2:
    jmp y-- lp2
    .wrap

    """
    )

    RP2040Freq = 250_000_000

    microcontroller.cpu.frequency = RP2040Freq
    time.sleep(1)

    oldalgval = 0

    sm = rp2pio.StateMachine(
    blink,
    frequency=RP2040Freq,
    first_set_pin=board.GP2
    )
    sm.write(bytes(16))

    while True:
    algval = algin.value
    if (algval != oldalgval):
    oldalgval = algval
    count = 20 + int(algval / 600)
    freq = int (RP2040Freq / (10 + count*2))
    data = array.array("I", [count])
    sm.write(data)

    time.sleep(0.2)

    One problem will be the 3V3 operating levels of the Pico. The SP0256A-AL2 datasheet states the following:

    So whilst a “high logic” value for the oscillator has a minimum level of 2.5V, it also states that a minimum of 3.9V is required if driven from an external source.

    If required, something like a 74HCT14, powered by 5V, can be used to level shift the 3V3 output of the Pico to a 5V signal for use with the SP0256A-AL2.

    But in practice, I was finding the Pico worked fine as is. It is important to ensure both the Pico, Arduino and SP0256A-AL2 all have their grounds connected.

    A this point I’m just using the Pico as a programmable clock, but if I was to go this route, then it would make sense to have the Pico drive the SP0256A-AL2 too and forgo the Arduino.

    Closing Thoughts

    So I have two choices if I want to use a Raspberry Pi Pico:

    • Go for smooth changes of frequency, but with less resolution, especially at the higher frequencies.
    • Go for more accurate resolution across the range but accept there will be blips when the clock changes which will be heard in the audio.

    Neither is a perfect solution, but it shows the principles are valid. Also, using two microcontrollers is a bit over the top, so if I was to move to using a Pico, I’d probably want to find a way to drive the SP0256A from the Pico directly too and skip using an Arduino.

    One benefit of that would be that I can time the frequency changes to coincide with silence in the speaking should I wish to, avoiding the possibility of major audio blips.

    But I also have a few other options to try, which I’ll come back to in a future post.

    Kevin

    #arduinoUno #pio #raspberryPiPico #sp0256aAl2

  21. Arduino with Multiple Displays – Part 3

    Whilst messing around a little more with my Arduino with Multiple Displays – Part 2, I’ve optimised the code a little and found out a bit more about these displays!

    In this part, I’m actually using a PCB that can hold four displays, powered by a Waveshare Zero device. More on that here: Waveshare Zero Multi Display PCB Design.

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

    Parts list

    Recall that I’m using displays that look like this – note the order of the pins.

    Although even with displays that look exactly the same, it appears there can be differences in how they are used software wise. More on that later.

    The Circuit

    For two displays, I can reuse the circuit from Arduino with Multiple Displays – Part 2. For more displays, it is possible to cascade more displays using jumper wires, but I’ve used my PCB.

    The pins to be used for various Waveshare Zero boards is covered in part 2.

    The Code

    Whilst using these displays, I found that the colours can be inverted in some of them compared to others. Typically, I’ve found that I might have to use either of the following two options to drive them correctly:

    tft.initR(INITR_MINI160x80);
    tft.initR(INITR_MINI160x80_PLUGIN);

    These represent different Adafruit displays as before, but they generally work for me.

    However there is another thing to watch out for. These displays are 16-bit colour displays, which means each colour value is a 16-bit word with red, green and blue elements represented by 5, 6 and 5 bits. This means two of the colours have a resolution of 0 to 31, and one has 0 to 63.

    But the ordering seems different for different displays. The default Adafruit library appears to assume RGB ordering, but my displays seem to be BGR. This means that if I use the provided short-cuts for colours, the red and blue elements are swapped.

    Consequently, I defined my own colours along with a macro to allow me to provide RGB values and turn it into the device-specific 16-bit value as required.

    In the following, I define the bit-shift number for each of red, green and blue and the use that in a macro “ST_COL” shifting the value to the correct place in the 5-6-5 format. Red and blue are the 5-bit colours and green is the 6-bit colour, so in each case I take the most significant bits which means each colour can still be defined in terms of 0..255 RGB values.

    // Format is 16-bit 5-6-5 B-G-R
    // Allow 0..255 in component values, by only taking
    // most significant bits (5 or 6) from each value.
    // bbbbbggggggrrrrr
    #define ST_COL(r,g,b) (((r&0xF8)>>3)|((g&0xFC)<<3)|((b&0xF8)<<8))
    #define ST_BLACK ST_COL(0,0,0)
    #define ST_GREY ST_COL(64,64,64)
    #define ST_WHITE ST_COL(255,255,255)
    #define ST_BLUE ST_COL(0,0,255)
    #define ST_GREEN ST_COL(0,255,0)
    #define ST_RED ST_COL(255,0,0)
    #define ST_YELLOW ST_COL(255,255,0)
    #define ST_MAGENTA ST_COL(255,0,255)
    #define ST_CYAN ST_COL(0,255,255)

    I’m also building up to seeing if I can drive more than four displays, so I’ve also changed the code to allow me to iterate across a number of displays.

    #define NUM_TFTS 4
    int tftTypes[NUM_TFTS] = {
    INITR_MINI160x80, INITR_MINI160x80,
    INITR_MINI160x80, INITR_MINI160x80,
    };

    int tftCS[NUM_TFTS] = {SPI_SS, 6, 5, 4};
    #define TFT_RST 7
    #define TFT_DC 11

    Adafruit_ST7735 *tft[NUM_TFTS];

    void setup() {
    int rstPin = TFT_RST;0
    for (int i=0; i<NUM_TFTS; i++) {
    tft[i] = new Adafruit_ST7735(&MySPI, tftCS[i], TFT_DC, rstPin);
    rstPin = -1;
    tft[i]->initR(tftTypes[i]);
    tft[i]->setRotation(3);
    tft[i]->fillScreen(ST_BLACK);
    }
    }

    void loop() {
    for (int i=0; i<NUM_TFTS; i++) {
    unsigned long time = millis();
    tft[i]->fillRect(10, 20, tft[i]->width(), 20, ST_BLACK);
    tft[i]->setTextColor(ST_GREEN);
    tft[i]->setCursor(10, 20);
    tft[i]->print(i);
    tft[i]->print(":");
    tft[i]->print(time, DEC);
    }
    }

    Each instance of the display code is now created dynamically and stored in an array which can then be iterated over when it comes to putting things on each display.

    Notice how the reset pin definition is set to -1 after the first initialisation. This ensures that subsequent instantiations won’t reset displays that have already been set up.

    The final code actually allows up to eight displays to be included by setting NUM_TFTS at the top to two or four.

    The GPIO usage being assumed is described here: Waveshare Zero Multi Display PCB Build Guide.

    Find it on GitHub here.

    Closing Thoughts

    Approaching the code in this way allows me to experiment more easily with more than four displays.

    If my PCB works as I’m hoping I should be able to cascade them to get eight displays – assuming the Waveshare Zero is up to driving eight of course.

    Kevin

    #arduinoUno #define #esp32c3 #ESP32s3 #rp2040 #st7735 #tftDisplay #WaveshareZero

  22. 2025 One Hertz Challenge: Digital Clock Built With Analog Timer - You can use a microcontroller to build a clock. After all, a clock is just somethi... - hackaday.com/2025/08/13/2025-o #clockhacks #arduinouno #555timer #clock

  23. Arduino and SP0256A-AL2 – Part 2

    After getting the Arduino and SP0256A-AL2 working I wondered if I could repeat the trick from the Arduino and AY-3-8910 and have the Arduino generate the required clock signal for the SP0256A-AL2. This post starts to look at that question.

    • Part 1 – Basic introduction and getting started
    • Part 2 – Arduino programmable clock
    • Part 3 – Using a Raspberry Pi Pico as a programmable clock
    • Part 4 – Using a HC4046 PLL as the clock
    • Part 5 – Using an I2C SI5351 programmable clock
    • Part 6 – Adding MIDI

    https://makertube.net/w/paGX4eiCAHgLU9mK2x3uyq

    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 Arduino, see the Getting Started pages.

    Parts list

    • Arduino Uno
    • SP0256A-AL2 speech synthesizer chip
    • 2x 1KΩ resistors
    • 2x 100nF capacitors
    • 1x 1uF capacitor
    • 1x 3.5mm jack socket
    • Breadboard and jumper wires

    The Circuit

    This is basically the same setup as the previous post, but instead of the 3.579345 MHz oscillator, the clock input of the SP0256A-AL2 is connected to the Arduino D10 pin.

    D10 is internally connected to OC1B, or the “B” compare match output of Timer 1 of the ATMega328P.

    The Code

    To get a clock signal of any kind on one of the output pins requires use of the ATMega328’s timers – in this case Timer 1. This is a 16-bit timer.

    To generate an automatic (i.e. with no code driving it once it is set up) square wave signal on the Arduino’s output pin requires the timer to be connected to an output pin to generate a 50% duty cycle PWM signal.

    The PWM peripherals can be configured to toggle an output pin when the timer expires, but that therefore requires the PWM to be running at least twice as fast as the required frequency.

    From the SP0256A-AL2 datasheet, it has this to say about clocks.

    There isn’t a lot to go on here – basically it assumes a 3.12MHz clock with a 48-52% duty cycle for the square wave.

    A typical application circuit would be the following (again from the datasheet):

    And the final word on oscillators is in the pin descriptions:

    The options for creating a 3.12 MHz clock with an Arduino PWM output are pretty limited, due to the following:

    • The faster clock for PWM is related to the system IO clock speed of 16MHz, when running with no “prescalar” to drop it down in frequency.
    • When running at 16MHz, lower frequencies are obtained by using the “compare match” approach. The timer will reset and the output pin will toggle when a counter match occurs. The counter will count in integer units of the core 16MHz frequency.
    • As the output pin will toggle on compare match, the timer must run at twice the frequency of the required clock signal. This allows one period for a HIGH output and one period for a LOW output to generate the required square wave output.

    The PWM square wave frequency is given by the following formula (as per the ATMega328P datasheet for Timer 1,3,4):

    • Freq = FreqClk / (2 * prescalar * (OCR1B + 1))

    Given the above, with a FreqClk of 16MHz, the following are the only real options for a PWM-driven square wave as a clock for this application:

    OCR1B compare valueResulting clock frequency08 MHz14 MHz22.7 MHz32 MHz41.6 MHz51.3 MHz

    So there isn’t a lot of choice, and nothing particularly close to the required 3.12 MHz, although 2.7 MHz is the closest and would probably do if required.

    To configure the above requires the following code:

    #define SP0_CLOCK 10 // D10
    void clockSetup () {
    pinMode(SP0_CLOCK, OUTPUT);
    digitalWrite(SP0_CLOCK, LOW);

    TCCR1A = (1 << COM1B0);
    TCCR1B = (1 << WGM12) | (1 << CS10);
    TCCR1C = 0;
    TIMSK1 = 0;
    OCR1AH = 0;
    OCR1AL = 1;
    }

    This configures Timer 1 in Clear-Timer-on-Compare (CTC) mode with no prescalar, toggling OC1B on compare match with the value in OCR1A.

    Note: even though there are two pin outputs – OC1A on D9 and OC1B on D10 – which is used is determined by the COM1A/COM1B settings. Even when using OC1B, as I am here, it is still the match value in OCR1A that determines when the counter has reached its “top” value.

    I updated the code to try each of the above values for OCR1A in turn and the result is the video at the start of this post.

    void setClock (int clock) {
    OCR1AH = 0;
    OCR1AL = clock;
    }

    Closing Thoughts

    It is interesting to note that the Arduino can in fact provide a clock that allows the SP0256A-AL2 to function, even if it is not particularly true to the original at this point.

    But it does mean that there isn’t as much control over the pitch as I’d hoped. If I want to get to more of that granularity between 4 MHz and 2 MHz, I’ll have to find another way.

    Kevin

    #arduinoUno #define #sp0256aAl2