Using the words in max7219defs.fs and max7219cmd.fs you can control the digits, using either BCD, or controlling the segments individually. Using BCD make your programming much simpler but only gives you four letters to work with (besides the number characters).
I used individual segment control to make this simple demo which lights up each segment in sequence. Here is a video — sorry for the fuzzy image:
My apologies to the Beethoven fans as I had to do some violence to the tune in order to make it monophonic and to fit it inside the 3 octaves provided by my code. I also had to apply some filtering to the recording to try to remove background noise in my apartment.
Here is the tune in source code:
create furelise-p1
d_16th ne o5 == d_16th nd# o5 == d_16th ne o5 == d_16th nd# o5 ==
d_16th ne o5 == d_16th nb o5 == d_16th nd o5 == d_16th nc o5 ==
d_8th na o5 == d_16th nr nr == d_16th nc o4 == d_16th ne o4 ==
d_16th na o5 == d_8th nb o5 == d_16th nr nr == d_16th ne o4 ==
d_16th ng# o4 == d_16th nb o5 == d_8th nc o5 == d_16th nr nr ==
d_16th ne o4 == d_16th ne o5 == d_16th nd# o5 == d_16th ne o5 ==
d_16th nd# o5 == d_16th ne o5 == d_16th nb o5 == d_16th nd o5 ==
d_16th nc o5 == d_8th na o5 == d_16th nr nr == d_16th nc o4 ==
d_16th ne o4 == d_16th na o5 == d_8th nb o5 == d_16th nr nr ==
d_16th ne o4 == d_16th nc o5 == d_16th nb o5 == end-score
create furelise-p2 d_quarter na o5 == end-score
create furelise-p3
d_8th na o5 == d_16th nr nr == d_16th nb o5 == d_16th nc o5 ==
d_16th nd o5 == end-score
create furelise-p4
d_dt-8th ne o5 == d_16th ng o4 == d_16th nf o5 == d_16th ne o5 ==
d_dt-8th nd o5 == d_16th nf o4 == d_16th ne o5 == d_16th nd o5 ==
d_dt-8th nc o5 == d_16th ne o4 == d_16th nd o5 == d_16th nc o5 ==
d_16th nb o5 == d_16th ne o4 == d_16th ne o5 == d_16th ne o4 ==
d_16th ne o5 == d_16th ne o4 == d_16th ne o5 == d_16th ne o4 ==
d_16th ne o5 == d_16th ne o5 == d_16th ne o6 == d_16th nf# o4 ==
d_16th ng o4 == d_16th nd# o5 == d_16th ne o5 == d_16th nf# o4 ==
d_16th ng o4 == d_16th nd# o5 == d_16th ne o5 == d_16th nd# o5 ==
d_16th ne o5 == d_16th nd# o5 == d_16th ne o5 == d_16th nb o5 ==
d_16th nd o5 == d_16th nc o5 == d_16th na o5 == d_16th ne o4 ==
d_16th na o4 == d_16th nc o4 == d_16th ne o4 == d_16th na o5 ==
d_16th nb o5 == d_16th ne o4 == d_16th ng# o4 == d_16th ne o4 ==
d_16th ng# o4 == d_16th nb o5 == d_16th nc o5 == d_16th ne o4 ==
d_16th na o4 == d_16th ne o4 == d_16th ne o5 == d_16th nd# o5 ==
d_16th ne o5 == d_16th nd# o5 == d_16th ne o5 == d_16th nb o5 ==
d_16th nd o5 == d_16th nc o5 == d_16th na o5 == d_16th ne o4 ==
d_16th na o4 == d_16th nc o4 == d_16th ne o4 == d_16th na o5 ==
d_16th nb o5 == d_16th ne o4 == d_16th ng# o4 == d_16th ne o4 ==
d_16th nc o5 == d_16th nb o5 == end-score
create furelise-p5 d_dt-quarter na o5 == end-score
That occupied 326 bytes of memory, which was the score bytes along with the bytes needed for the word headers (furelise-p1, etc.) It was divided into five parts to save memory, since some of the parts are repeated.
Free software source code and documentation are available here:
There is no Arduino IDE package for Guix. I asked why once, but I forgot what the reason was — something to do with the way that Arduino build process works I think. Anyway, this is how I get it running on my systems. I suspect this is not the best way, but it works for me. The process is a little convoluted, but I don’t use the IDE very often, so I haven’t been motivated to figure out something better. Typically all I need is avrdude, in order to load Forth firmware onto the chips, and there is an avrdude package available in Guix, as well as a avr-toolchain package and a few other tools.
First you need to make a clone of the git repo:
git clone https://github.com/arduino/Arduino.git
Specifically I am using the some old commit bf24880d7c559751765a43cd1669d893bba267e8, which is the commit for Arduino IDE version 1.8.14, but maybe a newer version would work also.
After changing into the build directory, you must setup the proper build/run environment. I do this by having the following manifest file saved at ~/Manifests/arduino-ide-run.scm:
If you want to use the same environment as I am using right now, prefix the command above with guix time-machine --commit=079a7f2c65c51da7b53b0e5ef44c516dc8eaab6e --. (I’m running Gnome DE, but I’m not sure if that makes any difference.)
Then run the command ant run &. In a minute or so you should see the IDE appear.
I problem I have at this point is that, although the IDE runs, you are not able to compile sketches, because the executables in the avr-gcc toolchain are looking for linker & glibc stuff in the wrong place on your system. I fix this by running the following script after the IDE starts, which I have saved in the file ~/Scripts/arduino-patchelf.sh:
You will need to adjust the variables on top according to the location of things on your system. Unfortunately, it is necessary to run this script every time you call ant run, or at least I haven’t figured out a way to get around that. I’ve been told it is possible instead to just set a symbolic link from the “standard” location glibc location to the actual location, but I didn’t want to do this for fear that there might be some effect on the purity of my other build environments.
Hopefully somebody will put together a Guix package soon, but this is a workaround in the meantime.
I’m not getting paid to say this, but I bought this ISP Shield for Arduino and found it to be convenient:
DIYMORE AVR ISP Shield attached to an UNO, with chip loaded.
This setup is the same as programming using two Arduino boards and the Arduino ISP sketch, but a more compact and convenient, since you don’t have to figure out where the wires go, and you can clip the chip into the ZIF socket.
I like this better than the other programmers I have used, at least for burning 328P chips. To use it, you must (one time) load the Arduino as an ISP sketch onto the 328P chip, and then afterwards attach the shield. When burning chips with avrdude, use the stk500v1 programmer type.
So far, I have a demo-pitches word that plays c4, e4, g4, and c5 frequencies, but I can set other frequencies ranging from a4 to g#6, using the words I have so far. There is no system yet for setting duration, so I can’t play notes proper. But it was fun setting up a system for setting the equal temperament pitches.
In microcontroller programming, it is much easier to justify efforts to save memory. I needed to store the precalculated frequency register data values for each pitch. Originally I had a table like so:
With each line (two 16 bits words) representing one pitch. However, the first two bits in each 16-bit word are actually not frequency data, but register addressing bits, which can easily be added in later. Also, the second 16-bit word in each pair has (in my application) only two bits of actual frequency data, which are the two right most bits. So, I cut the table memory usage in half by packing those two bits into the left-most two bits of the first word in each pair:
\ packaged freq register data for equal temp
\ pitches from A4, A#4, B4 , ... , G#6
\ format numbered from lsb to msb:
\ 0-13: the 14 LSBs of the lsb register load
\ 14-15: the 2 LSBs of the msb register load
create packed-pitches
$1274 , $138d ,
$14b7 , $15f2 ,
$1740 , $18a2 ,
$1a19 , $1ba7 ,
...
Now each line in the table is actually two separate pitches. Then I just need a few words to separate the bits back out into separate words, and recombine them with the frequency register addressing bits:
In testing, I have some trouble with some pitches getting distorted when I set the volume to higher levels. I suspect the problem is that I am still using the USB power supply and need to feed in my +5V from an external PS. I plan to try that next week, God willing.