As I'm preparing to put a 12bit DAC on the SPI bus shared with DCOs and the bit crusher, I had to make some changes to how to communicate with the DCO.
EDIT: I've written the code in a way that supports 3 bytes per DAC update so even 16bit DACs are supported in theory. For 12bit DACs, only two bytes are needed)
The DCO expected to receive three bytes with a 9us delay between sends and the CS line toggled only at the end of the last byte.
First I rewrote that, to accept single bytes. Then I changed the format so the first byte started with 1 and then had a fire bit command. The two last bits + 2 x 7 bits from the next made up the 16 bit frequency for example. That way, we can sync on the first byte instead, as it's the only one with a 1 as MSB.
Anyway, the bus can at best run at 4MHz when communicating with the DCO, or else each bit length becomes too short for the PIC16F. At 4MHz each bit is 0.25us long.
I want to run the DAC at 2ch * 48kHz = 96kHz. But if the DCO sends all its bytes in one go, it would take up too much time in a single period, preventing a stable DAC update frequency.
At the same time, I want to run the CV updater and DCO updates at 6kHz (well, ideally I wanted to update the CVs at 12kHz, but I can't seem to get a stable 192kHz interval (CV updating is split into 16 updates, 16 * 12kHz = 192kHz) so I think I have to settle for 96kHz. This fits well with the DCO which cannot be updated faster than 8kHz anyway, so 6kHz it is.
So, I came up with a system of 16 slots (96kHz/16 = 6kHz), each 1s/96000 or 10.4us long. The DAC can send its 3 bytes (with no delay between) in any slot, it would take slightly more than 6us.
That leaves around 4us. Sending a single byte will take around 2us, so we can let any other peripheral send on the second half of the slot. Also, since DAC and DCO will be mutually exclusive (per channel), the DAC may send at the start and any other device (e.g. BC) in the second half. Also, by separating DCO frequency updates and DCO commands (sync, range etc) into separate slots, we can send both within a single 16slot cycle.
Anyway, I implemented this - and then the nightmare started.
The DCO had massive errors to the slope - every cycle overflowed, the saw was no longer a saw.
I spent almost two hole days debugging. Was it the protocol change, some previously untested code changes to the DCO or something in my controller code?
Turns out, it's likely a problem with the MikroC compiler (again). When writing two chunks to the PIC internal FLASH rom, one with DAC values and one with DAC slopes, the values one got written to both positions! I am not sure if this started to happen now, but it would be weird if it has been that way for a long time.
The solution is to calculate the slope on the fly (it's simply a subtraction). But when I removed the code for writing the slope to FLASH, everything failed. So now I still calculate and write slopes during calibration, I just don't use it.
I truly hate the MikroC compiler. I really need to port everything to MLabs.
In the process of figuring out what was going on, a few other things happened. First of all, one of the DCOs got stuck at a single frequency. Turns out, one of the SPI pins got pushed in/up through the board (cold solder joint probably). Then, another DCO simply didn't want to calibrate properly. It took a long time to realize that this only happens at the bottom end of the frequency spectre, around 40Hz and lower. At the moment, I think the DAC simply cannot supply a low enough voltage, so every cycle overshoots.
Then disaster struck. I put the DCO in the programmer backwards, sending +12V into one of the SPI pins. The MCU still works, but SPI has stopped working so it's trash.
So right now I'm two DCOs short, and I have only one that can be calibrated down to the lowest frequency. Not sure what to do about that, but the others work down to around 40Hz which is good enough I guess. We'll see what the rest end up with.
Oh, and I managed to fix a long lasting bug - when changing to a lower frequency, the first cycle would end early because the previous interval's timer wasn't stopped. It seems to work fine now.
BTW: I had another weird issue that I didn't get to the bottom of. One of the DCOs managed to reach all the way down to 4-5Hz, while the others only managed 8. I think 8 is right. Maybe the first one ran at half the expected speed (16Mhz instead of 32MHz)?
BTW2: I first suspected that the overflowing waveform was caused by the interpolation between calibrated points being wrong. It did have something to it, the interpolation will be off by a few percent, bulging towards the end. But not as much as I saw. However, this lead me to try without any interpolation at all, just using the calibrated value for the start. That worked pretty well, it only sags by a few 10s of a volt at the end. Pretty interesting, by doubling the number of measured points we could maybe get away without the interpolation.
BTW3: There is something strange at the startup of the DCO, when hammering the synth with midi, it feels like the DCO lags/hangs, before suddenly doing all the changes. This has not been investigated further.
No comments:
Post a Comment