Your baud rate is off, mate
As I wrote in my last post one of the things I wanted to try with the Agon was to build a MIDI interface for it and write some software to use it.
I started with a tiny prototype where I would simply connect the Agon Light's serial lines to an FTDI usb adapter so that I could open up a terminal on my desktop computer to see if I could get serial comms to work at all.
It was a bit of a steep learning curve but looking at a lot of other people's examples, I finally got it to work.
Frames shifting and other issues
Kind of. A bunch of weird things happened all at once and it made debugging really hard. At low baud rates (And low in that case meant MIDI speed and downwards), for the most part only the first byte would transfer correctly, everything after that would be mangled. I only later noticed that my terminal (RealTerm is a really great open source program for debugging serial connections) complained about so-called “frame errors”.
Also, even at higher baud rates, where I didn't get these frame errors, the last byte of my transmission would always be cut short. And with that I mean, literally, electrically. The transmission would just stop after 3 bits.
The latter one was easy to diagnose: I simply closed down the serial port too quickly and the UART part of the eZ80 would just stop pushing out the bits. Took me a while but I got there eventually.
The frame errors were a bit more complicated. My hunch was that somehow, something with the baud rate generation went wrong at lower baud rates. Didn't make a ton of sense to me but if you know how serial comms ala RS232 work, you know that one of the few things that can really throw it of is frequency drift.
This type of serial communications works with a single line (plus a ground connection for good measure). There is no additional clock signal. Instead both sender and receiver rely on somewhat stable timing of the signal. (Ben Eater, as usual, does a pretty good job of explaining this in one of his videos).
I didn't have any good tools to diagnose the signals. My analog oscilloscope would probably have trouble properly putting the bits on the screen to measure the timing and the only other alternative I had was my tiny DSO scope, where I was also struggling to get good measurements.
So I finally caved in and ordered one of these relatively cheap USB scopes, in my case the Hantek 6022BL. People hate these scopes for their shoddy quality but I'd argue that for the kind of stuff I usually do they are actually quite sufficient. The BL variant additionally has a logic analyser (16 channels) built in that luckily works with a chipset that is compatible with the Saleae logic analyser software, which is one of the better tools and one of the things you can do with Logic is automatically decode serial comms. I tried this with my code and Logic complained about frame errors as well. In Logic I was now able to measure the length of individual bits. If you do the math, at 9600 baud (Which I was testing with), each “bit” should take roughly 104 microseconds. “My” bits were consistently around 112 microseconds, so somewhere around 8% off.
So I asked on the AgonLight group if someone could explain this. At the same time, I dug into the source for the AgonLight firmware, called agon-mos to see how the calculation works and if I can find something there.
You see, with a microcontroller like the eZ80, which has two UARTS on board, you cannot simply say to the chip, hey, use 9600 baud. Instead, you have to calculate the ratio between the baud rate you need, the frequency the microcontroller is driven with and a factor of 16.
And there I saw something. The header file defining the constants used in that calculation had the processor clock defined as 20 MHz. But the oscillator that generates the clock runs at 18.4320 Hz. (A very interesting number, as we will see in a sec). If you do the math, the difference between these two numbers is, oddly enough, roughly 8%. Could it be?
I did some quick maths and tried to adjust in my requested baud rate for that specific error and got it to work reliably. So I made a comment to that effect on the group – Dean Belfield, the author of agon-mos was a bit dismissive at first, and rightly so, as we'll see, but later he confirmed that indeed, this was a mistake and he would fix it for the next version.
The fun of integer maths
Now, there's some interesting questions to be asked: The AgonLight heavily relies on UART usage. The ESP32 which acts as Agon Lights main I/O processor talks to the eZ80 via UART0. So why wasn't this noticed earlier? Shouldn't the error be present there as well?
Here we need to get into the details of the calculation I mentioned above: At the end of that calculation you end up with a number you'll write into a register on the processor. The higher your baud rate, the lower that number will be. And it will always be rounded to an integer number, of course. And, of course, agon-mos is trying to communicate with the ESP32 as quickly as possible which means that given the baud rates and the CPU frequency, that number will be either 1 or 3 (depending on how well that link works) and it really doesn't matter if you start with 20 MHz or 18.4320 MHz. Here's where that 18.4320 number has some interesting properties: It makes it so that all commonly used baud rates result in a non fractional number for this calculation. (This unfortunately isn't true for MIDIs odd baud rate, but I think we can make it work with the tiny error we'll get).
So, for very large baud rates, the error introduced by using 20 MHz is either non existing or neglegible and it was easy to overlook. I guess I was just the first one to try out lower baud rates.
I now have a test build in my hands and will do some additional testing to see if the baud rate error I'm getting for the MIDI baud rate has any real life effects. I hope it is not the case and I can continue with the project. The next steps are going to be both in hardware and software: I want to try to write a piece of assembler that will receive MIDI. for that I'll need to build a receiver circuit as well, which is a bit more involved than the sending circuit.
And then the next step would be to build a first version of the MIDI circuit on perfboard, after which I will hopefully be able to turn it into a real circuit board design. I will try to document by code and hardware design on this github project