Saturday, December 31, 2022

This year's x-mas gift from Mikroelektronika: A compiler bug

I have spent the last month chasing down an extremely strange bug. When I added a new global variable to my DCO, calibration stopped working.

I first spent a long time trying to figure out what was going on, and after a couple of weeks I ended up posting a rather vague description to the MikroC forums. 

What I was seeing was very weird - I had a double loop, and the inner loop ran fine the first time around but the second time the inner loop ran it skipped the first part. The outer loop was never re-executed but sometimes the program continued in the mail function afterwards.

I did a lot of testing but the results were always similar, some code was skipped and sometimes everything crashed. It felt like the program pointer moved to a different address. I started suspecting some kind of memory leak.

After the post in the forums I was asked to provide a minimal example - I normally do this anyway but this time I had a hard time doing so, as everytime I changed the program it started working.

Finally, I resorted to placing all variables and functions at absolute positions, as I suspected that some kind of memory collision was the problem. It took a looong time to get this right, and it wasn't always possible, but at last I managed to get a fully absolute-position program that still bugged in a similar way.

Now I gradually stipped the program down, re-running the code for every small change to make sure it still had the bug and that it could be brought back to working by moving something. I placed empty variables at absolute positions to prevent other, used variables from moving around, and also made sure that the empty spacers were not called upon anywhere in the code that was executed BEFORE the spot where the bug showed up.

This took me every spare moment from the 17th of December and the 13 days. Finally, yesterday, I had a minimal program with only a single function call inside the loop. Removing a single spacer variable made a single library variable (FLASH_write.savedINTCON) move too, and the program would start working. 

I was totally exhaused and posted a new question to the forums.

As I waited for answers, I used a diff tool to see if the two generated asm outputs from the working and non-working programs were different. They were not. I then did the same for the machine code listing file (.lst). Now I could see a single changed line! In the non-working version, a single instruction - MOVLB 0 - was missing. I took a screenshot and prepared to write a follow-up question.

When I logged in to the forum again, the user Janni had just posted an answer - and that was the exact same thing that I found. Janni confirmed that this was actually the error, the instruction changes RAM banks and when it is missing the function returns to the wrong part of the program, exactly what I was seeing.

Adding 'asm movlb 0;' after the call to FLASH_write fixes everything.

So here we are - it turned out to be a compiler bug after all. I am completely drained but incredibly happy that the error was found as I can't have any errors in the DCO.

Unfortunately, it seems Mikroelektronika stopped developing the MikroC compiler in 2019 so it is unlikely that the bug will ever be fixed. At least now I know what to look for if this ever happens again!

Happy new year everybody!

MikroC: Absolute positioning

I've spent the last weeks debugging a strange problem in MikroC, and to do that I had to absolute position every function and variable to make sure the program did not change in RAM and ROM.

Here is how to do that:

Global and local variables

Add absolute 0xXXXX to place at address 0xXXXX. If you have an initialiser it must be placed BEFORE the absolute. E.g.:

    unsigned short a absolute 0x0123;

    unsigned short a = 1 absolute 0x0123;

    unsigned short a[32] absolute 0x0123;

Variables need to be used somewhere to prevent the compiler from removing them. Also, the compiled code is different if the assignment is done on a separate line vs if it is done inline, which changes the size of functions.

For global variables they may be used in main simply by assigning to them. For arrays it's enough to assign something to the first position. E.g.:

    a = 1;

    a[0] = 1;

Function parameters

I have not been able to place these at absolute addresses

Internal library variables and function parameters

I have not been able to place these at absolute addresses

Functions

These are postfixed with org 0xXXXX:

    void myFunc() org 0x0123 { ... }

You may also put the address after an extern declaration in an h file:

    extern void myFunc() org 0x0123;

Library functions

These may also be positioned, just skip the function body:

    void libraryFunction() org 0x0123;

Collisions

Variable collisions are common, in cases where the compiler has decided that they won't conflict. In those cases, it is not possible to absolute only one of the colliding variables, as the compiler won't place anything else on an address occupied by an absolute'd variable.

That means that you have to set all variables to absolute for that particular address - and if one of them is a function parameter you're out of luck it seems as those cannot be absolutely placed (or at least I don't know how)

Sunday, November 6, 2022

Wavemixing and loudness

When designing the continuous wavemixer, I thought a bit about the fact that as you turn the dial, the (perceived) loudness of the output changes. As the number of harmonics in the signal changes, from none with the sine wave, to “all” in the square wave, the output sounds progressively louder even if the signal amplitude is the same.

This is most pronounced when going from sine to saw, but triangle to saw also has a noticeable difference, so I’ve contemplated changing the input mix to compensate for this. As the cross fade is computer controlled, I could also compensate in the source mix vca, which is good since it can be done after the hardware has been made.


When studying “off the shelf” VCO chips with built-in waveshapers, I noticed that the different wave outputs indeed have different amplitudes. I have not been sure if this is just a byproduct of how the waves are generated in hardware, or actually a design feature.


Today I read the data sheet for the AS3394 (a CEM3394 clone) because I was curious about building a simpler synth voice, and noticed that it has a built in waveform mixer/selector. And in the text, it is clearly stated that the waveforms have different amplitudes to keep the loudness the same. Saw is 27% larger than triangle, and square is 27% larger than square. 


Unfortunately, I cannot see the same numbers in the waveform plots on page 4, but it shows that triangle output is 50mV, saw is 45mV and square is 30mV. Now, 27% larger than 30 is 38, and 27% larger than 38 is 48.4, so we are very close to the real value for square, but saw does not make sense. However, the saw output on pin 9 is stated as 45mV as well, so this could just be a copy/paste error. The 27% text is lifted straight from the CEM data sheet which does not have a similar plot so it’s hard to tell if the original text is wrong or if the plot is wrong without measuring. I have bought an AS3394 so maybe I’ll get around to checking it sometime.


CEM3340


Tri: 0-5v

Saw: 0-10v

Square: 0-12v


Meaning square is 20% larger than saw, but saw is 100 larger than tri. Also, they are not centred, meaning they should be centred using a capacitor (or with separate centring circuitry - caps mess with pulses, they won’t be centred around 0. Not uncommon in real designs though, and something that will happen in the filter anyways, one just need to take it into account when mixing several sources to prevent clipping).


SSI2130


All outputs are 0-2.5V, though pulse goes slightly below 0 (-34mV)



For more on the waves, see:


Tri:


https://en.m.wikipedia.org/wiki/Triangle_wave


Saw:


https://en.m.wikipedia.org/wiki/Sawtooth_wave


Square:


wikipedia.orghttps://en.m.wikipedia.org › wikiSquare wave - Wikipedia

Saturday, July 30, 2022

XM8 as a vocoder

I have thought about the posibility of using the XM8 as a vocoder for a long time, but had decided to not go ahead with it due to the added complexity.

However, while designing the voice cards I realised that by just adding tiny modifications, I could at least leave the door open to adding a vocoder daughterboard later. Let me explain.

Vocoder architecture

A vocoder has some central components. First of all, it takes two inputs:

 - one (usually a voice) that is analysed for frequency content by dividing the input into frequency bands using bandpass filters. The amplitude of each band is captured using an envelope follower.

- another one (usually an instrument sound rich in harmonics) which is divided into the same frequency bands as the first one, and where the amplitude is controlled by the amplitude of the first input.

The effect is that the second input "mimics" the first, making the instrument "talk".

In addition to the inputs, a noise source is used, and amplitude is sampled (analog sample and hold) at intervals set by a variable clock.

Here is the block schematics of the classic ETI Vocoder DIY kit:


It has three main parts - analysis and synthesis at the top, and sound generation at the bottom. The two oscillators are used in place of the second input.

How to implement this in the XM8

Now, we would need to implement the whole analysis, sample and hold and voiced/unvoiced detection on the daughterboard. The output from the board would be 16 CVs that can control VCAs. 

The second input will be either 
- External audio - if we want to use a different source for our "instrument"
- Output from the low pass filter on voice card 1 - that way we can use everything on the voice card except the state variable filter as a mono synth. 

We would then feed the second input as External input to all voice cards. By sending the input directly to the SVF - this is already part of the architecture - we can use the SVF in place of the second block of band pass filters in the schema above. The cutoff frequency for each filter is fixed and can be calibrated (part of the existing design) digitally. As an added bonus, the cutoff frequency may be moved to change the voice pitch (or even invert or reorder the bands).

There are two additional important things in the ETI vocoder design:
 - the first and last frequency bands are made using low pass and high pass filtering respectively. Fortunately, this is supported by the SVF.
- every second band is phase inverted. This is also supported by our architecture.

Changes needed to the voice cards

- Add a CV input going to the SVF. 
- Add an output from the low pass filter (pre VCA), this will be used on the first voice card to use as the second input.

Changes needed to the main/input board

- Add a switch between external input and voice card 1 LPF output (this is a good idea anyway)
- Add a switch between this input and a third input used for vocoder noise source. This would be controlled by the voiced/unvoiced detector on the vocoder analysis board.
- CV outputs going to the vocoder board to control voice input volume etc.
- Chaining between synths - either we need a separate analysis board on synth two or we need 8 VCA inputs. We need a separate Ext audio out, tapped after ext audio mux, to send voice card 1/noise to second synth.

A four channel mux may be used, if we let the vocoder analysis board control A1 and input noise to input 3 and 4, the mainboard may control A0 to switch between external input and voice card 1 LPF. A1 would then effectively override the two inputs (and should have a pulldown resistor so that it is disabled if no vocoder daughterboard is fitted.


Usage: LPF filter VCA must be turned down on all voices, and Ext in turned up and connected to SVF for all. Voice card 1 may be used as input to the other sources.

As one XM8 probably ends up having only 8 voices, we need to chain two to get a full 16 band vocoder. Here is one way of doing that:

We need to have a single analysis board to be able to detect voiced/unvoiced properly. Bus chaining requires level matching between the synths, so ext bus input should probably have VCAs.

Things to test

I am unsure of the steepness of the bandpass in the ETI vocoder. The SVF has a 12dB bandpass.

Update: According to the Deliyannis bpf docs, one filter has 12dB cutoff, so two must have 24dB. That means that we may have to run the audio through both the LPF and the SVF (as HPF).

Deliyannis band pass filter

While reading about the human voice filter bank in the VP-330 (on the Oakleysound site) i realised that the ETI bandpass filter is called a Deliyannis bandpass filter or resonator, a calculator can be found here: 

https://www.changpuak.ch/electronics/Deliyannis_Bandpass.php

http://earmark.net/gesr/opamp/bpf.htm

Thursday, July 28, 2022

Teensy 4.0 bottom pins - finding a suitable header and connector

If I'm to use the Teensy 4.0 as my voice controller MCU, I need to use the 10 additional pins on the back to get to the second SPI.

This means I either have to solder pins to the 10 pads, or use a breakout board with castellated connections like https://forum.pjrc.com/threads/57672-Another-Teensy-4-0-Breakout-Board?highlight=Teensy+4.0+rear+pins

If I am to solder pins, I need to make sure that I can find a matching mating connector. As the SMD pin header is higher, the mating connector needs to be lower. Also, the pins need to be short enough to fit the connector.

I measured the pre-soldered pins on the Teensy, the bottom plastic is 2.54mm high, the pin is 5mm.

Normally, mating connectors for the single rows are 8.5mm high, with some saying that the pins may be 6.6mm long.

I am going to try out the following:

Pins (from Farnell):
Samtec TSM-105-04-L-DV
These have a 0.15" (3.81mm) height before the pins, or 1.27mm more than the ones on the Teensy. The pins are 3.05mm, which is a bit on the short side.


Update: Samtec TSM-105-01-F-DV has a pin height of 5.84 which may be more suitable.

GCT BG050-10A-0-0450-0737-0350-L-D
These have a height of 3.5mm before the pins, but the pins are 4.5mm. That looks very promising. Unfortunately, ordering these in Norway incurs a $13 fee because they are only in stock in the US.
F = 3.5mm, D=4.5mm


Female connectors (from Farnell):
Amphenol 76342-305LF
This is a 7mm high receptacle. combined with the GCT connector it would give a total height of 10.5mm, as opposed to the 11.04mm of the side connectors, leaving a 0.5mm gap. This is probably fine. Combined with the Samtec one the gap is reduced to 0.2mm, but the short pins (3mm) may not be long enough. The max pin length is not stated but from the "normal" connector we can guess around 4.9mm.



AMP 215307-5
Similarly, a 7mm high receptacle. Max pin length is not stated for this one either, but it says that the connection point is 2.2mm into the connector which is a tad too long for the 3mm Samtec pins.


From JLCPCB:
I managed to find a couple of 7mm headers from JLCPCB as well:

Liansheng FH-00097
part C2829902, though only 8 are in stock right now:

Wcon 2171-210SG0CUNT3
part C721788 - unfortunately, the mating position is  3.57mm down which again may be too far for the pins.

I will order the Farnell ones shortly to test.

Update:  Results from Farnell

14 pin socket:
Part 1593466 / 2212-14SG-85 
May pull pins 0.8mm out and still have good contact. Tested using pins that protude about 5.66mm and has 2.54mm plastic at the bottom

When pressed together, pins and header measure 10.87mm. Header is 8.42.

10 way SMD
Pins: 3551099 / Samtec TSM-105-01-F-DV (NB: F, not L). Plastic + bent pins measure 3.86mm. The pins measure 5.68mm so the total height is 9.66mm

Header: 1098050 / Amphenol 76342-305LF. Measures 6.97mm. Pins may be pulled out at least 1mm and still have good fit. Height with pins is 10.85mm

Header: 3419216 / AMP 215307-5. Measures 6.96mm. Pins may be pulled out at least 2mm and still have good fit. Height with pins is 10.84mm. Has a very nice, tight fit

Conclusion: The combination of either of the headers with the SMD pins will work, and the height is damn near spot on when used with the 14 pin socket and standard pins!

I still have the GCT BG050-10A-0-0450-0737-0350-L-D on order and will add the results when they get here, but it seems the Samtec ones work nicely.

Update 2:
Pins: 1798977 / GCT BG050-10A-0-0450-0737-0350-L-D. Plastic + bent pins measure 3.65mm. 

Combined with header the total is 10.62. The pins are at least 1mm shorter than the Samtec ones. They still fit quite well even when pulled back 0.2mm

Pins: 3585115 / Samtec TSM-105-04-L-DV. Pins above plastic is 3mm, which is too short to be comfortable. I would not recommend this.

Final conclusion:

The clear winner is the Samtec TSM-105-01-F-DV pins with either of the two headers. Since I've bought enough of the GCT pins already, and they work fairly well, I will go with those. The price is also between half and 1/3 of the Samtec, so if you're in the US they are probably a lot cheaper. Since I had to pay extra they are more or less the same. Had I not bought them I would have gone with the Samtec.

Both SHOULD work with the LCPCB headers too.

Tuesday, July 26, 2022

Itsy bitsy Teensy trouble - with Slave SPI

This is another one of those posts mostly meant for myself.

I've been hitting my head against the wall for four days, trying to get Slave SPI working on a Teensy 4.0, with a Teensy 4.1 as the master.

I started out with the https://github.com/tonton81/SPISlave_T4 library (and https://forum.pjrc.com/threads/66389-SPISlave_T4) , but could not make it work, so I stripped it down to the minimum. After a while (after removing a while-loop from the ISR) I got some response. However, it was not stable and it seemed like it would only receive every second byte.

After a lot more troubleshooting I got it working on every transfer, but only if writing 0 to the TX buffer. Any other values would give different troubles.

In the end, it turned out to most likely be a combination of things - mixing up MOSI and MISO pins as well as wrong impedance settings on the pins. Removing and attaching probes to the pins also made stuff fail.

Anyway, next time I would check the following:

- Make sure CFG1 is set to full duplex (00 or 11) 

- Check crossed vs straight connections between MISO and MOSI, matching CFG1

- Connect/disconnect logic probes on MISO/MOSI (works when bytes go missing)

- Make sure master deasserts PCS between frames, I wrote 16 bits without rising the PCS (or rather, rising for a too short time) between blocks of 8 bits when frame size was 8 bit.

- Make sure master speed is > 1MHz

I have yet to find a completely stable solution and I am worried that connecting multiple slaves to the same master may not work as intended. 

Learned

Anyway, here are some of the things I learned:

- The minimum SPI speed on the Teensy is 1MHz (without doing special stuff)

- To set SPI in slave mode: set bit 1 in CFG1 to 0 and update Clock gating register for the appropriate LPSPI, for example CCM_CCGR1 |= (3UL << 6); (lpspi4_clk_enable)

- When CFG1 bits 25-24 are 00, MOSI on the master connects to MISO on the slave and vice versa.

- When CFG1 bits 25-24 are 11, MOSI on the master connects to MOSI on the slave (and MISO to MISO).

- When communicating between two Teensy 4.x, the pin impedance must be changed on both slave and master. For example: 

IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_01 = IOMUXC_PAD_DSE(3) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE;

- Even when changing the impedance, I needed to experiment with connecting/disconnecting the logic probes.

- TX buffer empty flag is set immediately after the last TXFIFO byte is shifted out, at the same time an RX interrupt is triggered (if RXWATER is 0).

- The SPI documentation talks about different size descriptions:

    - Transfer - when sending data - Transfer complete IR is triggered when all data in TXFIFO has been sent (FIFO is empty)

    - Word - Up to 32bits, 0-padded if less than 32 bits received. FIFOs are 16 words long.

    - Frame - Frame complete IR is triggered when PCS (chip select) goes high (deasserts). A Frame may be from 8bits long, length must be divisible by 2 and may be longer than 32bits (span multiple words). If word length is reached, the frame is split across multiple words in the RXFIFO.

- TDF and RDF - Transmit and Receive Data flags are set by the system and reset by reading from/writing to the TX and RXFIFOs, not by clearing interrupt flags.

- The SPI slave example sets IER bit 1 which is Transmit Data Interrupt. It will be triggered when the number of bytes in the TXFIFO is less than TXWATER. With TXWATER 0 this will be triggered at the same time a byte is received. I don't understand why this is chosen instead of for example FCF.

- The SPI slave library, and also the more elaborate example at https://github.com/tonton81/SPI_MSTransfer_T4, uses a lot of looping and waiting inside the ISR, which cannot be a good way of doing things...

- With an 8bit frame and a single byte in the TX buffer, the following are set on RX: Frame completed, Word completed, RX data ready, TX data ready. RXFIFO in FSR is incremented. If another byte is received before a new byte is written to TX, we get a TX underrun.

Symptoms

Here are some of the symptoms I experienced:

- Missing or erroneous slave sends when anything but 0 was written to TDR - i suspect that we tried writing and reading from the same pins and that messed up stuff, though not entirely sure WHAT was going on.

- TX buffer underruns even if I wrote to the output buffer every time a byte was received

- Elements in the TX buffer disappeared (even if length was correct) as verified by logic probes. 

- When writing the same bytes over and over to TX, the same bytes disappeared every time.

- Swapping master and slave made DIFFERENT bytes disappear.

- Missing RX, some of the received bytes never ended up in the RXFIFO

- All receives worked when I only wrote 0 to TX buffer.

- It looked like I needed to receive 16bits and/or only every second byte was received.

- When unplugging either the MISO or MOSI cable, sends started working perfectly

- Removing probes sometimes made stuff work - but only after powercycling the Teensys.



Test code

My slave test code can be found at

https://github.com/xonik/teensy-spislave-test

Master code is

#include <SPI.h>

SPISettings settings(10000000, MSBFIRST, SPI_MODE0);


void setup() {

  IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_01 = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE; 

  IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_02 = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE; 

  IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_03 = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE; 

  IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_00 = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE; 


  Serial.begin(1000000);

  pinMode (10, OUTPUT);

  digitalWriteFast(10,HIGH);

  Serial.println("SPI Starting");


  SPI.begin();   

  SPI.beginTransaction(settings);  

}


void loop() {

  digitalWriteFast(1,HIGH);

  digitalWriteFast(1,LOW); 

  delay(500);


  digitalWriteFast(10,LOW);

  Serial.println(SPI.transfer(0xAA));

  digitalWriteFast(10,HIGH);  

}


Thursday, July 21, 2022

DAC8565

I had some DAC8565 (quad 16bit DAC) breakout boards made at JLCPCB, and just tested them, they work fine.

The datasheet says that SPI speed for < 3.6V Vdd is limited to 25MHz, above that it's 50MHz. I am able to run the SPI at 45MHz at 3v3 though, and it seems stable. 50MHz does NOT work.

Here are my connections for using the internal 2.5V reference. It is especially important to note that the VrefL must be connected to ground even if using the internal reference:

1: VoutA
2: VoutB
3: VrefOut: 150nF to GND
4: AVdd: 3v3 + 100nF decoupling cap to GND
5: VrefL: GND
6: GND
7: VoutC
8: VoutD
9: !SYNC: SPI CS
10: SCLK: SCLK
11: Din: MOSI
12 IOVdd: 3v3
13: !RST: 3v3
14: RSTSEL: GND (binary input)
15: !ENABLE: GND
16: LDAC: GND or pin on MCU if doing hardware LDAC, this can also be done using a command.



Wednesday, July 6, 2022

Teensy 4.x SPI

I did some testing of the hardware SPI support yesterday, to try to get async send working.

I thought that async sends were not implemented since all posts were discussions about how hard it was to use DMA etc etc. Turns out I've misunderstood. 8bit-per-byte is fully supported using the callback version of SPI.transfer!  When saying that 16bit (and more) transfers were not supported, they mean that 16bits per byte (or sending strings of 16bit ints) is not supported.

I only discovered this after trying to understand what was done in the example from Garug in this post: https://forum.pjrc.com/threads/67247-Teensy-4-0-DMA-SPI/page2

I deleted everything special and it still worked. Here are some outputs:


My main loop output without any SPI sends running, to see what speed I can get without anything else interfering.



Normal "one byte per transfer", blocking SPI makes the main loop output grind to a halt

SPI transfer using callbacks. Main loop speed is almost the same as with no load :-D (Spi transfers 0x00 and 0xFF which is shown as the slow output on line 2



Changed to transferring 8bytes at a time, to measure the delay between transfers.


8 byte transfers but with changing speeds between 2 and 8MHz. There is not much overhead doing the switch.

Retriggering send from callback

This seems to work fine:


Moving the start/end transaction so that it only happens when we need a speed change saves us a little time between blocks:


We still get a 1.5uS "penalty" here, which amount to 96uS per 64 blocks sent. In this time period, it looks like the SPI actually blocks the main thread too. That's not good. I have to look for ways to increase the speed here, but we're using attachImmediate which supposedly is the fastest callback.

Things to look at:
- There is a lot to save by writing a custom version of

 bool SPIClass::transfer(const void *buf, void *retbuf, size_t count, EventResponderRef event_responder) {

(found on line 1199 in SPI.cpp), one that does not use an EventResponder callback and that skips a lot of the checking the library version does. 

The best thing would be to make a version that does not require an interrupt for every three bytes sent, which controls the chip select/sync pin by itself. 

Update:
I've managed to do async SPI with 24bit transfers and automatic CS switching. This probably means I can update all 4 channels of the DAC in one go, I just need to make it async. Since I was only sending 3 bytes, doing DMA transfers were costly and unnecessary.

As for Teensy 4.0 vs 4.1: The 4.1 has three CS pins connected to the default SPI port, meaning I can switch between them. 4.0 on the other hand has only one. BUT - If I can manage to turn OFF CS handling after writing to DAC, I can do DCO CS manually. I will only write one 24bit block to DCOs so I would have had to do config changes between sends anyway.

Also, I discovered that the Teensy 4.0 actually exposes all four SPI1 pins after all (MISO1 and CS1 as pin 0 and 1) so I can use any of the two ports for either slave or master functionality.

Update:

From top: SPI Clock, Data out, CS for fast moving data (50MHz), CS for slow data (4MHz, a few glitches that probably are just from the probes), Interrupt indication (yellow) and main loop indication (blue).

The image above shows that background SPI transfers are working as they should, with speed and CS switching. Main loop speed is 20MHz approx. 

Updating 4 DAC channels, including time spent in ISR, takes 3uS. Updating two DCOs take 13,7. In total, 61.7uS for 64 channels + 2 DCOs. 

The max update speed is thus 1 000 000 / 61.7 = 16.2kHz when the DAC bus runs at 50MHz. 

Reducing speed to 25MHz makes the total time 93,7uS and the throughput drops to 10.6kHz which is a bit too slow.

Moving the DCO updates to a separate bus would mean DAC transfers at 25MHz would take 80uS giving us a throughput of 12.5kHz. That would be sufficient. 

If we choose to upgrade to an 8 channel DAC running at 50MHz we would get a total of 53,7uS for a speed of 18.6kHz.

Monday, July 4, 2022

Teensy 4.x and communication

The voice controller card needs to communicate both with the master controller and with its peripherials.

Master controller should use the voice card as slave and needs a hardware SPI to do so properly. Hopefully, one master may control multiple voice cards simultaneously if we just buffer the SPI output.

The voice card controller needs to control these three:

The CV DAC

Currently, this is done in software, at 50MHz. This eats up a lot of clock cycles. Instead, we should look into using DMA backed SPI - that would let us send 24bit in the background without blocking the controller:

 https://forum.pjrc.com/threads/67247-Teensy-4-0-DMA-SPI

Update: 

Using SPI.transfer((void *)txBuffer, nullptr, 8, callbackHandlerFast); sends a whole buffer in the background and may be exactly what we need.

The DCOs

These are connected to the hardware SPI today, but runs at max 4MHz, which means moving them to software would block for even longer periods than the DACs. Not entirely sure what to do here, perhaps some timer based scheme, or perhaps we can change the SPI speed and use the same SPI as for DAC?

I should really look into SPI transactions for this.

https://forum.pjrc.com/threads/57754-Teensy-4-0-SPI-Clock-Speed

Update: 

Switching transfer speed is painless and costs next to nothing. Using 

SPI.beginTransaction(fastSettings);

where fastSettings sets speed etc: 

SPISettings     fastSettings(8000000, MSBFIRST, SPI_MODE0);

This means that DAC and DCOs can share SPI bus.

Port expanders

Right now I use SPI for this, but I will move these to PCA95xx, I2C chips. We don't need high speed here so freeing up SPI from this task is good. Port expander changes will always (?) be on-demand so it doesn't matter that much if they block for a tiny amount of time.

Audio daughterboard

I have yet to try this, but it runs from a combo of I2S and I2C. Both are available and there is a separate I2C that can be used if I don't want to combine it with the port expanders even though the bus is just that, a bus.

Hardware

My current voice card uses a Teensy 4.1. It has SPI (SPI0) and SPI1 exposed as pins, and two I2C-busses readily available. SPI2 is used for SD card and memory on the teensy itself. I may be able to solder stuff directly to the pads but not sure how easy it will be to use them. 

The Teensy 4.0 only exposes SPI (0) as a pin, but adding SPI1 is not too hard. SPI2 is also available but requires soldering wires. It can be done but I would prefer not to.

Going with the Teensy 4.0 would save significant physical space on the voice card, and it is also about 50% cheaper than the 4.1 (with ethernet). I would however lose the posibility of using on board PSRAM and extra flash memory which MAY be nice if using an audio daughterboard?


Friday, July 1, 2022

Fatar Keybeds - where to buy

 So, I already have two fatar keybeds for my DIY projects, one 4 octaves and one 5, both bought from Doepfer. Unfortunately, none of them have aftertouch which I feel is a need for my big polyphonic - not because I will be able to utilise it myself, but since it feels weird building such a great big machine without it.

I did some googling yesterday and came across a few sources that may or may not be able to supply such a thing, more specifically a Fatar TP/9s 61keys keybed with aftertouch:

New Groove

https://www.newgroove.it/acquista/produttore/fatar/

Italian site, has exactly what I need but not sure if they ship outside of EU. 

Synthtaste

https://www.synthtaste.com/en/shop

Sells a kit for the OB-Xa etc that may be used, but is very expensive. Perhaps it is possible to ask for a keybed only.

TechsMechs - Lars Erlandsson

https://techsmechsvintagesynth.com/products/tm-8-oberheim-ob-x-xa-8-keyboard-replacement-kit

Swedish tech that also sells an OB-Xa kit. I have emailed him asking for a keybed only, and whether or not the keybed comes with aftertouch. We'll see.


I found New Groove and TechsMechs through this post: https://modwiggler.com/forum/viewtopic.php?t=241329


Spares can be found here: https://www.synth-parts.com/en/products/keyboard/fatar-keyboards/?p=1


Thursday, April 21, 2022

Have I shown you the UI yet?

I can't remember if I've actually posted what the XM8 UI looks like? 

I am doing a working midicontroller webapp that is at the same time a millimeter perfect SVG, this makes it possible for me to test the voice cards without having a physical controller, and at the same time decide on what the UI should look like.

Here is the first version:

I initially thought I would have an external screen/pad but decided that would be annoying, so the second version has a built-in 9 inch screen:




I can even develop the code for the screen and run it on my controller, as everything is typescript/react.js based:



Here are some shots from when I presented the synth at a conference. I made a fullsize mockup of the UI (bottom left), complete with a five octave fatar keybed:





The voice circuits. Everything gets better with a bit of bubbles, right?

Not a very good picture, but it does give an impression of the size of this thing:



Tuesday, April 19, 2022

Finally a china version of the Alpha endless pots!

Hah! Finally! I just discovered these:

https://www.globalsources.com/Rotary-potentiometer/Rotary-potentiometers-1148135774p.htm

https://www.globalsources.com/Rotary-potentiometer/Rotary-potentiometers-1148135774p.htm


It's a clone of the Alpha RV112FF-series endless pots, this one is by HongYu and named H111KGP+T4 360°Endless:

http://www.hongh.com/product/291.html


NB: HongYu also sells a non-360°Endless version named H111KGP+T4, so be sure to order the correct one!


Here is the original RV112FF datasheet:




Compared to the one for the H111KGP+T4:


I'd say it's a drop-in replacement.

The specs are almost the same too, it's like they just copied the datasheet ;-)


RV112FF


H111KGP+T4 360°Endless

The MOQ at globalsources is 1000, but for a price of around $0.5-0.8 I get 1000 for the price of around 200 of the Alpha one. Now, I've already ordered the original, but perhaps I should consider this as a backup.

Parts bought

Here is a list of stuff that I've bought in large enough quantities:

ICs:

AS3364 - Quad lin VCA - 115

AS3320 - Filter - 45

AS3340 - VCO - 25

V2164 - quad exp VCA - 16

AS2164 - quad exp VCA - 20

DG412 - quad SPST - 100

DG413 - quad SPST/dual SPDT - 304

DG408 - 8:1 mux - 45

MAX7221 - LED driver - 32

DAC8565 - on breakout board, 20x.


Modules:

CEM3340 VCO - 20

DCO v1.4 - 40

Bitcrusher - 20

Quad exp VCA - 20

Quad exp Mixer - 20

Memory - 76

Reconstruction filter - 60

 

Button/caps:

Pushbutton, no click - SPH2N1 - 200

Pushbutton, click - SPH2S1 - 200

Caps, transparent black 8mm (round) - YCA080C - 100

Caps, ice dome shape 7.4mm - YCA044A - 100

Caps, snow dome shape 7.4mm - YCA044B - 100

Caps, ice flat 7.4mm - YCA043A - 100

Caps, transparent black flat 7.4mm - YCA043C - 100

Caps, transparent black 6mm - YCA045C - 100

Caps, transparent black 5mm (round) - YCA052C - 100

Caps, black 8.1mm - YCA0865 - 100

Caps, black 10mm (round) - YCA1115 - 100


Other parts:

Knobs 15 x 17, black, d-shaft - 500

Knobs 10.5 x 16, black, square hole - 100

Knobs 10.5 x 16, silver, square hole - 20

Knobs 24 x 15, silver, d-shaft - 15

Knob, 17 x 14, black, knurled shaft - 100

Alpha RV112FF-40-25A-0B20K, Endless pot, plastic shaft - 100

Alpha RV112FF-40B1-15F-0B20K, Endless pot, metal shaft - 150

Light pipe, 3 x 15.9, dome shaped - 100

Light pipe, 3 x 6, flat / rear insert - 100

Sunday, April 17, 2022

Waveshapers redone

I redid both waveshapers today. It felt bad pulling apart the breadboarded circuits but in order to verify all fixes and test with full switching I had to do it. I don't have any DG413 switches yet so I can't test it, but I think it will work.


Saturday, April 16, 2022

VCO - work in progress

Next up on my protoboard - the full VCO circuit with FM and all. Happy easter!



Thursday, April 14, 2022

Voice card circuits

Now this is an interesting one. I have mostly completed the voice card layout - it is untested and will surely have a lot of adjustments before I route it and send it to production, but this is what I will base my breadboard testing on going forward. 

PS: There are a lot of comments here and nothing is guaranteed to work! 

Some quick facts:

  • 60 CVs
  • 50 digital control signals
  • 9 trim CVs or alternatively additional trim pots
  • 16-22 SPDT switches
  • 21-31 SPST switches

I have yet to see if I can have both OTA and ladder filters. We'll see.

Anyway, here is the complete voice card - but without the controller and CV generation. When doing the real PCB I suspect the controller/CVs and DCOs will be placed on a separate board just as with the prototype controller, it works very well and keeps most fast moving digital signals away from the analog circuitry.














Tuesday, April 12, 2022

DG/Vishay-switcher

I always wonder what switch does what, so here it is:

Vishay also have switches with the DG3xx prefix (and others)


8p:

DG417: SPST normally closed

DG418: SPST normally open

Dg419: SPDT 


16p:

DG401: 2 x SPST in DIL16

DG403: 2 x SPDT

Dg405: 2 x linked SPST

Dg408: 1:8 mux

DG409: 2 x Linked 1:4 mux

DG411: 4 switches, normally closed

DG412: 4 switches, normally open

DG413: 4 switches, 2 pos and 2 neg logic, May be used as SPDT/DPDT


NB: Max +/-8V men kan funke med single supply opp til 16v:

Dg4051: 1:8 mux

Dg4052: 2 x linked 1:4 mux

Dg4053: 3 x spdt 


28p wide

Dg406: 1:16 mux

Dg407: 2 x Linked 1:8 mux


Dg445 - quad spst - what’s The catch?


Per the 12th of April 2022 I have:

2 x DG403 (dual SPDT)

2 x DG408 (1:8)

1 x DG409 (2 x 1:4)

5 x DG412 (4 x SPST)

1 x DG418 (SPST normally open)

8 x DG419 (SPDT)


Update

I ordered 306 Harris DG413 from Digikey as they were the cheapest I could find (around NOK 8.5). I also ordered 45 DG408 from Farnell, not as cheap, and will order 100 Intersil DG412 from Rs components (around NOK 8.5 too).


Sunday, April 10, 2022

Analog switches and V logic

A quick note on the DG series analog switches:

I've successfully run at least one of these with a V_l (logic level voltage) of 15V while controlling the chip from a 5V MCU.

As I considered doing this for all switches (but with 3.3v logic level), I did a quick search that turned up these three posts:

"How do I connect the DG403 logic supply (VL) pin?"

"switches - VL input on analog switch"

"Analog Switch switching voltage (DG412)"

They don't give the exact same answer though. The Renesas one says to use a 5V supply (even for 3.3v logic), derived from the 15V using a resistor voltage divider with high value resistors.

Now, they may not be talking about the same chip as the DG series are produced by a variety of suppliers, but at least it sort of validates just using a voltage divider.

The stack exchange post starts talking about excessive current draw. 

The modwiggler post suggests using zener diodes or a resistor divider connected to an emitter follower. 

For now, I'll stick with V_l = 5V but I'll test doing it with a resistor divider.