0
\$\begingroup\$

I'm doing a project where I need to draw shapes on an oscilloscope by controlling the X and Y channels from a microcontroller, to eventually make a game which you can play on your oscilloscope.

I've come across an issue where, say I want to draw a line from (0,0) to (500,500) (by the way, these coordinates I'm giving are from 0-4096, because 4096 is the highest analogue output from my microcontroller) - I set the X and Y both to an analogue value of 0, and then set them both to 500, with code like this:

void loop() {
    analogWrite(dacpinX, 0);
    analogWrite(dacpinY, 0);
    analogWrite(dacpinX, 500);
    analogWrite(dacpinY, 500);
}

However instead of drawing a diagonal line, it draws a square. This is because it sets the X position before the Y, so supposing the beam is already at 0,0, it will first move right, then up, then left, then down, and so on.

A (bad) fix I came up with was to make a function called lineto(i, j), which draws a line from the current position to the position (i,j). This function just moves from the current position to (i,j), but in a set amount of steps. If I set the amount of steps to a few hundred, I get a relatively smooth line, but at the expense of a lot more calculation, and then it's not even really vector graphics anymore.

I've been looking at setting digital pins at the same time, and this is doable, but only if they're all in the same port. I was thinking of using this fact to control two external DACs at the exact same time, but since a port is only 8 bits, each DAC would only get 4 bits of information.

Does anyone have any ideas as to how I could achieve this?

Here are two images, the first one shows the XY plot of the signal when I try to do this, and the second shows the X and Y waveforms separately (X = bottom, Y = top).

XY

X bottom, Y top

\$\endgroup\$
18
  • \$\begingroup\$ Have you considered ramping from one value to the other? You'd need to do that in a proportional manner so that one axis didn't get to its final value before the other. \$\endgroup\$ – Transistor Feb 21 at 19:39
  • \$\begingroup\$ In a way I've tried that, with the lineto function I described in the question, where I basically go from one position to another in discreet steps. \$\endgroup\$ – Jacob Garby Feb 21 at 19:41
  • \$\begingroup\$ Sorry, I missed that. \$\endgroup\$ – Transistor Feb 21 at 19:42
  • 2
    \$\begingroup\$ Before you go too far with your game idea, there were video games circa 1980 that used a vector oscilloscope type display. Google cinematronics tail gunner to see what is possible. Note that you need a blanking/intensity input on your scope, sometimes called a Z input, if you want gaps between your objects. \$\endgroup\$ – Mattman944 Feb 21 at 20:30
  • 1
    \$\begingroup\$ @JacobGarby The lineto() approach can be implemented efficiently with a Bresenham algorithm (uses only integer arithmetic, relatively fast). You can pick a scale for the 'jaggies'. \$\endgroup\$ – Whit3rd Feb 22 at 5:59
1
\$\begingroup\$

You already found the reason why you see a square. Because each analogWrite() takes about the same amount of time to execute, each edge of the square is one transition in the outputs.

So it is clear how to resolve: Both X and Y outputs shall change at the same time.

To accomplish this you might like to think about a (logically) common register for both values, which can be loaded at any (slow) pace from your microcontroller, but will latch all bits with a single common signal. Perhaps your DACs already have such an input. Alternatively see for example the 74HC595 that has a shift clock and a register clock.

The code could be like this.

void loop() {
    analogLoad(dacpinX, 0);
    analogLoad(dacpinY, 0);
    analogLatch();
    analogLoad(dacpinX, 500);
    analogLoad(dacpinY, 500);
    analogLatch();
}

However, the time the signals use to get from an old value to a new value will be more shorter than the time they keep their values. And so you will have bright nodes with dim edges. To resolve this issue and to get bright edges, you will need to implement for example the Bresenham algorithm and generate each step by software. They will look "pixelated," though.

Another note: To separate objects of the planned game, you need to switch off the beam in the oscilloscope. This is done via the Z input. You will commonly use just a digital signal for it.

\$\endgroup\$
1
  • \$\begingroup\$ Thanks, this is useful. I will accept this answer as, even though the other two answers were also useful, this one goes into more depth. \$\endgroup\$ – Jacob Garby Feb 22 at 15:11
2
\$\begingroup\$

You could pause at each vertex. This will make the vertices brighter, but you could use the Z axis (beam brightness) to reduce that effect. So:

Set X1
Set Y1
Set Z=off
Pause
Set Z=on
Set X2
Set Y2
Set Z=off
Pause
Set Z=on
...

And repeat. Your line will be a lot dimmer, since only the strokes will be displayed, ratioed with the pause time. You might choose to slow the strokes in hardware. Pause time should just be long in comparison to your X-Y update delay. So if your update delay is 10us, then 10ms delay will reduce the squareness effect by 1000, and you still get 50 updates per second.

\$\endgroup\$
2
  • \$\begingroup\$ Thanks, I can't seem to get this working unfortunately though, I don't have the connector required to plug into my scope's Z input, but I've tried putting a 10ms delay between setting the initial position and and the next position, and I just get a dot at each end of the line. \$\endgroup\$ – Jacob Garby Feb 21 at 21:53
  • \$\begingroup\$ The 10ms can be a starting point for you to experiment. Try varying the pause lower: 1ms, 100us, 10us. Also try turning up the brightness. Looking at the second CRO picture, I can see the delay between setting X&Y is 1us, so maybe 100us is the pause you should look at. The dots will be brighter than the line by the ratio of the pause to the X-Y delay. You'll have to play around a bit. It will be hard to do if you can't control Z. Then you should increase the number of points along the line as others have suggested. \$\endgroup\$ – elchambro Feb 21 at 22:04
1
\$\begingroup\$

lineTo is the correct approach to get a line as opposed to two dots. The calculations required are just the cost of doing business.

Normally you would offload most of the calculation to vector graphics hardware; which would also guarantee simultaneous X and Y updates : this would make a nice FPGA project if you were so inclined (you would need to feed X and Y calculated in the FPGA to two DAC channels to get the analog voltage required).

\$\endgroup\$
3
  • \$\begingroup\$ Fair enough. I've never used an FPGA before and wouldn't have a clue how to do this sort of thing with one, I thought they were just used for digital circuits. \$\endgroup\$ – Jacob Garby Feb 21 at 21:56
  • \$\begingroup\$ @JacobGarby Yes FPGAs are digital, you'd need two DAC channels too. \$\endgroup\$ – Brian Drummond Feb 21 at 22:01
  • \$\begingroup\$ Okay, I might have a go at that at some point. Unfortunately this project has a deadline of late May, so I probably don't have long enough to properly learn, and buy the things I need to try that. Thanks anyway :) I suppose it's not the end of the world having to manually make small steps to draw lines, my microcontroller has a pretty decent clock speed. \$\endgroup\$ – Jacob Garby Feb 21 at 22:05
0
\$\begingroup\$

Your system has a DMA channel to the DAC you should probably be using that. that will allow you to sequence a pattern in RAM and have the DMA send it to the DAC while the CPU is constructing the next pattern. it should also fix the time skew between the DAC channels.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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