Dear Ben,
I am a new user of the quarto. I find the quarto a real excellent idea put in practice by you. Congratulations.
My question refers to the following.
How is it possible to see the values while the A/D and D/A are processing. I modified your low pass filter program to stream the data to the output and the program crashed.
Ideally, what I would like to do is to look at the data via USB in the host computer (a PC) to store it. At the same time I would like to send data via the USB to process it by the quarto. However, the first step would be to look at it or even plot it. I wrote a short program to acquire and use the plot program and that works but, as I said, when trying to do the same in a more complex program such as the low pass filter. it failed.
Thanks so much for your advice,
Francisco

Thanks! If you haven't, take a look at Quarto Troubleshooting Guide as it goes over what can cause the Quarto to crash and some solutions. The short version is to keep sending / receive USB data stuff out of interrupts.

If you just want to look at the most recent data point collected by the ADC, the easiest solution is to store it in a global and then read it back on demand. Something like:

double latestADC;
void getADC1(void) {
  static double outputA = 0;
  static double outputB = 0;
  latestADC= readADC1_from_ISR(); //read ADC voltage
  
outputA = alphaA * latestADC + (1-alphaA) * outputA; //apply IIR filter on multiplied value outputB = alphaB * latestADC + (1-alphaB) * outputB; //apply IIR filter on multiplied value writeDAC(1,outputA); writeDAC(2,outputB); } void readADC(qCommand& qC, Stream& S) { S.printf("The latest ADC value is %f\n",latestADC); }

If you setup qCommand to map that readADC function, you can read the latest ADC value on demand. If, instead, you want you print out the data at a specific internal, you can use a timer (see Pulsed Output Example for more details). Or you can put the print functionality in the main loop and it will print out data as fast as it can. If you want to look at, say, 100 ADC readings in a row, then you can use the same approach: create a global array to store all the 100 data points. Then in the getADC1 interrupt store each data point in the next element in that global array (but stop when full) and then another function can, on demand, read back the entire content of the array. And maybe another function is used to start taking another dataset.

If, however, you want to stream back all the read data over USB instead of just a selection of it, this becomes trickier. The fastest the Quarto can collect data is once per us. And that data is 16-bits, so to stream all the data back, you need the USB to be able to send 16Mbps. The USB has the bandwidth to stream this out, but it doesn't have much in excess of this bandwidth. This means that storing the ADC data as a float (4 bytes) instead of its raw int16 (2 bytes) doubles the data that needs to be sent per second. If you print out the data in ASCII the data rate gets even higher. Also important is that USB is efficient when sending lots of data in one go, but it very inefficient and stream lots of small data packets. So if your code has lots of small print lines, what happens is:

USB Start (Overhead)
Send 4 Bytes
USB End (Overhead)
USB Start (Overhead)
Send 4 Bytes
USB End (Overhead)
USB Start (Overhead)
Send 4 Bytes
USB End (Overhead)

If instead you buffer a chunk of data and send it in one burst with something like Serial.write(&array,1024); then that USB overhead is spread over the 1024 bytes that are sent in one go instead of just, say, 4 bytes and you can send data much faster.

In short, if you want to stream back all the ADC data, I would recommend sending it back in binary form (ideally at int16) and in chucks of at least 1k.

One way to do this is to create two arrays for storing data. The getADC1 ISR will store all new data in the first array until that array is full and then it will switch to the second array. When it does this, it sets a global that says the first array is full. When the second array is full, it sets a different global to signal that the second array is full and it switches to the beginning of the first array for storing data and so go. Meanwhile, in the main loop you constantly check for that global signal (just a bool probably) to see if either array is full and if one is, you steam it back over USB and clear the global that the ISR set. This basically lets the Quarto wait until there is enough data and once there is, sends it all back in one go. By splitting the data storage into two buckets we can send data back while new data is coming in to a different section in memory.

I hope this is helpful, but please let me know if you have more questions and want more details.

Dear Ben,
Thanks so much. Extremely helpful. I will start testing all of these methods including the ping-pong buffer that you mentioned.
Francisco

5 days later

Dear Ben,
I have most of this working now. I have one more question. Is it possible to turn the ADC on and off at will? The reason is that after I acquire at full speed a burst of data, I would like to process it and then transfer the burst to the host but if the AD is still at 1 us/point, it seems to interfere with the transmission. I tried to change the rate but it does not appear to change.
Thanks,
Francisco

Your timing is perfect. The answer is yes, but using a new feature introduced in the latest firmware and only just today published on the website. You can setup the Quarto to use an external ADC clock (input on trigger 1 or 2) and then you can pulse it on-demand as needed to acquire ADC data.

I will post a news blurb on the new feature soon, but for now take a look at the External Clocks software functions and the ADC Timing App Note.

This will require a firmware upgrade for your Quarto, please contact me directly and I'll give you instructions for upgrading.

8 days later

More details on acquiring ADC data on demand and controlling it either externally or programatically with the Quarto at News Update: Support for External ADC Clock. Also take a look at the new program example OnDemandADCTrigger listed under Examples, Clocks. You will need to first upgrade to the board release 1.7.1 to see this new example.