1
\$\begingroup\$

We are doing a school project making an oscilloscope. Our teacher told us that we can use assembly to take measurements faster.

\$\endgroup\$
3
  • 4
    \$\begingroup\$ (1) Yes and (2) yes. \$\endgroup\$ Jan 23, 2023 at 8:00
  • 3
    \$\begingroup\$ (2) It depends on what you mean by "run alongside". Can you use both assembler and C/C++? Yes. Can two pieces of code sxecute simultaneously? No. \$\endgroup\$
    – Oskar Skog
    Jan 23, 2023 at 8:21
  • 1
    \$\begingroup\$ Actual hard limit is how you output data from arduino. ADC on arduino is fast enough to completely saturate serial port (at 3M baud rate). You can achieve this with normal C/C++ code. \$\endgroup\$
    – Rokta
    Jan 23, 2023 at 11:32

3 Answers 3

16
\$\begingroup\$

I will assume that by "measurements", you mean the analogRead function.

The first point were you can gain significant time is by increasing the ADC clock frequency (by reducing the prescaler). This is discussed in this post on arduino forum. Please note that the (rather) slow default is not a random choice : it enables better noise immunity, and allows for enough time to charge the ADC's internal capacitor even if the input signal has "high" output impedance/resistance (up to 10kohms if I remember right). If you increase the clock frequency, make sure you have a low impedance signal (for example by adding an Op-amp as follower (it might also do amplification if you want). If you push it far, you can reach the point were the least significant bits become noisy (but most scopes are 8 bits, so you might accept to loose the 2 least significant bits).

Another point were you can gain lots of speed is that, as far as I remember, by default all 6 analog ports are read in turn : if you only need 2 of them, disabling the 4 others should improve speed.

Then, it is true that most arduino functions do lots of "useless" checks on the pins, so you can gain a bit of performance by doing direct port manipulation (skipping all the tests checking to which port the pin maps)

Finally, you can look in the datasheet of the Atmega328P for the different ADC modes : there might be some better suited than the one used by the Arduino library (for example, if I remember well, you can get an interrupt when conversion is finished instead of the blocking behavior from analogRead).

So globally, there are plenty of ways you can increase the speed of analogRead : the simplest consist in changing the ADC clock, then if still more performance is needed, you can start disabling pins, using simplified code and/or playing with different ADC modes. For all those solutions, you do not need to write assembly code yourself. You do however need to write registers (in C), and therefore either to follow tutorials or read the datasheet of the micro-controler (Atmega328P).

Writing directly assembly is probably not a good idea : excepted if you are an assembly expert, your code will probably worse than the one generated by the compiler (and you get a far less readable code, and spend far more time writing it). Nowadays, even for commercial products, assembly is seldomly used, and when used, it's only for short bottleneck sections that where previously identified.

Finally, for your question about running assembly along side arduino code :

  • if you mean "at the same time" : you can't, the arduino has only a single core

  • if you mean to mix both types of codes, you can, using the asm volatile construction (see this post ) :

    asm volatile ("
    
      ; your assembly code here
    
    ");
    
  • if you mean having them in pseudo parallel, you can use interrupts to execute some code (arduino style or assembly) when the interrupt is trigered (by timer, external interupt, ADC conversion finished, ...)

But don't forget : don't use assembly until everything else failed, and only in the most critical sections

\$\endgroup\$
5
  • 3
    \$\begingroup\$ Excellent answer; just to add: actually getting the data out through the serial port will consume a lot of cycles. So for best performance, capture enough data in a buffer to fill a screen, then pause the capture to flush the buffer through the serial port. \$\endgroup\$
    – Paul
    Jan 23, 2023 at 16:10
  • \$\begingroup\$ @Paul : good point. If only short recordings are required (short enough to fit in RAM), then buffering all data then sending it is definitively the best option. If longer storage is required, it starts to get tricky. Maybe using the ADC in non blocking way, and send each value individually, using the highest baudrate possible. Or maybe (or not) it is possible to do the whole reading in interrupt routines, and the sending in foreground (but there might be some no_interrupt in the serial routines prevening this) \$\endgroup\$
    – Sandro
    Jan 23, 2023 at 17:36
  • \$\begingroup\$ @Paul: Can binary data be sent through the serial port, in order to deliver data faster than just ASCII+newlines? A script could listen to the output, and either display the data as an oscilloscope, or write it down to a CSV file? Also, would it be faster to write data to the EEPROM instead of outputing it via Serial.print()? \$\endgroup\$ Jan 23, 2023 at 18:08
  • 2
    \$\begingroup\$ @EricDuminil : yes, you can send binary data using the Serial.write() functions. I usually do it that way as soon as I'm retrieving data from another program on the PC side (it's easier that processing strings). You can get quite fast on the serial port if using the right baudrate (as far as I remeber you can go to at least 1Mb/s, ie 125kB/s). EEPROM is very slow for writing (3.3ms), so not a good option. If really maximal write speed is needed, then an external parallel input memory would be the optimal solution : using port writes, it should only take 3 clock cycles to write 8bits. \$\endgroup\$
    – Sandro
    Jan 23, 2023 at 18:52
  • \$\begingroup\$ @Sandro : yes, this only works for short recordings, but you usually use a scope for repeating patterns (or an overlay of a few different versions of a pattern, like this keysight-h.assetsadobe.com/is/image/content/dam/keysight/en/img/…), so recording a single screen width at a time may be an acceptable limitation \$\endgroup\$
    – Paul
    Jan 24, 2023 at 15:19
10
\$\begingroup\$

The Arduino functions are very slow. For instance a digitalWrite() to a pin takes several 10s of cycles.

You could use assembly, but C code would be the first thing to try. That compiles to instructions that are usually as small as assembly would be.

It's always better to make working code go faster, than make fast code work.

This means code up your application in as high level code as is available, so using the Ardunio functions, and get it working properly. Then see which bits are taking the most time, and make just those small bits go faster by rewriting in C. Rinse and repeat, until it's fast enough.

For instance, get it going with digitalWrite(pin, data) for each bit in a port. Later, replace that with PORTD = D_data_byte, to update all 8 bits on port D in a single cycle. Leave the original working but slow code in the source, but commented out, so you know what you've done.

\$\endgroup\$
3
  • \$\begingroup\$ Arduino AnalogRead() is already a C function which may not require C++ and can't really be much faster if you rewrite it yourself or rewrite it in assembly language. The point is, if you select a channel, start conversion, wait conversion to complete and read the result will not improve things much \$\endgroup\$
    – Justme
    Jan 23, 2023 at 10:40
  • 4
    \$\begingroup\$ "Leave the original working but slow code in the source, but commented out, so you know what you've done." Shouldn't it be git's job? \$\endgroup\$ Jan 23, 2023 at 18:02
  • 1
    \$\begingroup\$ Direct port manipulation (in C) is faster than using high level C functions. \$\endgroup\$
    – Candid Moe
    Jan 24, 2023 at 14:01
7
\$\begingroup\$

Well written C or C++ code can be as fast as assembler.

But I can't say how efficient analogread() is compared to the same in assembler. and also I think analogread() locks you up for several clock cyctles while the ADC is running. This is a limitation you can escape by going direct to the hardware.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.