Welcome to Laser Pointer Forums - discuss green laser pointers, blue laser pointers, and all types of lasers

Buy Site Supporter Role (remove some ads) | LPF Donations

Links below open in new window

FrozenGate by Avery

ARDUINO & DAC for XY projector

After reading a bit, the Arduino Wire library is fixed at 100kHz, which is approx 10,000bps which is slow, and why the circle is steppy.

Have a read here
Faster I2C - Arduino Forum
which explains how to increase that four fold to 400kHz.

That'll give you four times the speed which will make it quite a bit better.

Another thing you may be able to do is to just use a resolution of 256 x 256 rather than 4k by 4k.

That would allow you to send one byte to each DAC rather than two, thereby doubling the speed.

(the DACs may refuse to work this way thought, I don't know. They might insist on both bytes being sent rather than assuming the missing byte consists of zeros which is what you want)

If you can do both of those things, (and they both work) that would give you an 8 times increase in the speed of writing the data to the DACs, and you can then decrease the 10 degree step down to 8, 5, 4, 3, etc until it looks good. (and doesn't flicker)
 





Remember to wrap your code in the
Code:
 tags.

That Arduino-specific code I posted was just written up from memory. I'm not sure what byte order the DAC expects for I2C.  The I2C stuff I've dealt with just used 8-bit data, so I didn't need to shift anything.

Though I haven't checked what the input format of the DAC is, your circle in the original code is probably steppy in the original because you were only shifting by 4-bits, not the full 8-bits.  So you were truncating off some of the lower bits.  You probably have enough points to represent the circle (or else it would just look rough, not steppy).  The integer representing your X and Y value looks like this:

[code]
             MSB         LSB
int x = [15, ..., 8 ] [7, ... 0]
        `-------- bits --------'

Shifting moves the bits to the right or left. Sending out the MSB and LSB requires:

Code:
char MSB = 0xFF & (x >> 8); // Take top 8-bits and move them into the lower 8-bit positions
char LSB = 0xFF & x; // Simply mask away the higher-order bits.

The masking might be unnecessary, but it's good for completeness, and necessary if the type is an int.

In the error that the variable "c" wasn't working, I think that might be because it needs to be an int type, not char. I actually rarely use those bit-structs, but I thought given the 12-bit-per-axis resolution of your DAC that you could fit all the point and color data into a single 32-bit structure. With more RAM it won't matter.

The buffer code helps give you consistent frame-rates because the calculations are pre-stored. If you're doing stuff like floating-point calculations per point it'll slow up the per-point graphics. However, I think the main problem with your code was that the bit shifting was incorrect (hence the steppy appearance), and therefore while you had enough points to draw a circle, it had to spend about 2x the number of points drawing the jagged edges. Use the full 8-bits of shifting and you should be able to reduce the number of points as well as remove the jagged appearance.

Finally, remember that you can do fast bit-wise integer multiplications by using right and left shifting. Especially for division this is very fast and reduces a lot of calculations if you can use it. Beware that this shouldn't be used on the floating point calcs. Ex:

Code:
int x = 170;

int x1 = x >> 1; // Right-shift is division by 2^n --> 170/2
int x2 = x >> 2; // Right-shift is division by 2^n --> 170/4
int x3 = x << 1; // Left-shift is multiplication by 2^n --> 170 * 2

Those optimizations above help a lot with slow MCUs like the ATmega. It's still good even on faster processors if you can use them.
 
After reading a bit, the Arduino Wire library is fixed at 100kHz, which is approx 10,000bps which is slow, and why the circle is steppy.

Have a read here
Faster I2C - Arduino Forum
which explains how to increase that four fold to 400kHz.
That'll give you four times the speed which will make it quite a bit better.
Another thing you may be able to do is to just use a resolution of 256 x 256 rather than 4k by 4k.
That would allow you to send one byte to each DAC rather than two, thereby doubling the speed.
If you can do both of those things, (and they both work) that would give you an 8 times increase in the speed of writing the data to the DACs, and you can then decrease the 10 degree step down to 8, 5, 4, 3, etc until it looks good. (and doesn't flicker)

I agree, the I2C bus is slow and the wire lib code to drive it even slower.
I still think the staircase effect is to do with the delay between sending out the X then the Y co-ordinate.
Speeding up the data transfer from 100khz to 400KHZ will help, but you'll still be lumbered with the wire.lib
Remember the galvo will responde in about a milisecond, so you must output the xy co-ord pair in a much shorter time than this to gat a straight line between the two.

It would be good to see you output 2 diaganol co-ordinates as mentioned before. There is no processor overhead in doing this, but will prove that the I2C interface / bit banged Wire lib is too slow for your application.

(It's not the speed / computing power of the ATMEGA either - I produced complex graphics and Logos with a 1 MHZ 6502 processor and G120D scanners about 20 years ago:yabbem: - this was using 8 bit DACs driven on a parallel bus).

Spark fun supply a nice 8 bit parallel DAC AD5330 and looking at the data sheet, there is a 10bit DAC AD5331 _ I bet you could desolder the 8 bit and hack on the 10 bit version for better resolution.
https://www.sparkfun.com/products/9719
These DACs will allow you to load the X and Y co-ords.... then using the LDAC pin, output the new xy pair exactly at the same time.
Cool project:)
ATB
MM
 
Last edited:
I'm still a bit puzzled by the staircase effect.

The code is doing "SetX" and then "SetY" which are being sent to the DACs.

Even at 9600bps, assuming the complete I2C command is (say) 10 bytes (which actually makes my statement about doubling the speed sending one byte rather than two rubbish!), that will take ~10mS to send the command.

Unless the mirror units can respond far, far quicker than 10mS (I'm not familiar with them, how quick do they operate?), I would expect to see the X mirror start moving, and then the Y mirror start moving, so a very short horizontal straight line, then diagonal, then very short vertical straight line (as the Y mirror finishes it's move) for each step.

It would be better if a single I2C command with X AND Y coordinates in it (32 bits), with a little bit of decoding at the other end to split the 32 bits into the X and Y again before sending to the DACs, which would start to drive both X and Y mirrors at the same time.

Or buy an Arduion Due which has two DACs onboard so could drive the motors (via small drivers I think) directly.
 
What I'm getting at is the points are not actually in a circle, they're alternating in a staircase. To me that looks/sounds more like an issue in the maths/coding more than a speed issue. Even if the scanners are too slow, the dots should still actually be in a circle.
 
I agree, unless the mirrors can move in a micro second, it shouldn't look like that!
 
;)
I'm still a bit puzzled by the staircase effect.

The code is doing "SetX" and then "SetY" which are being sent to the DACs.

Even at 9600bps, assuming the complete I2C command is (say) 10 bytes (which actually makes my statement about doubling the speed sending one byte rather than two rubbish!), that will take ~10mS to send the command.

Unless the mirror units can respond far, far quicker than 10mS (I'm not familiar with them, how quick do they operate?), I would expect to see the X mirror start moving, and then the Y mirror start moving, so a very short horizontal straight line, then diagonal, then very short vertical straight line (as the Y mirror finishes it's move) for each step.
.

Hi,

That's the problem,
The G120d scanners I used (20yrs ago!)had a step response of about 1ms ...I think these are the equivelent of the 50KPS sets now, so even the slower scan sets of 25kps will have a response time way shorter that 10ms.

I dont know enough about the modern scan sets / their capability, but I'd bet my mother in laws pants :) it is the delay between sending the X and then the Y co-ord thats the root problem.
ATB
MM


Edit - here is a previous thread regarding scan speed - I think it shows that the response time of even a basic set is much faster than 10ms
http://laserpointerforums.com/f47/question-about-galvo-speed-71998.html

Post #6 shows the step time for the GS120 scanner - 5 degree step in 1.1ms !!!
 
Last edited:
Yea, you are correct there. You can see clearly the scanners stepping in the X direction, then moving up Y, stepping X etc.
 
@ Fraggle,

I found that same article Faster I2C - Arduino Forum

That article tells to delete the files:
hardware/libraries/Wire/Wire.o
hardware/libraries/Wire/utility/twi.o

I could not find these files.
(I use the Arduino 023 version but checked the version 1 and these 2 files are also not there.)

Anyway, I tried it.

I modified it to:

Code:
#define TWI_FREQ 400000L

Restarted the IDE.

It made no difference.
 
Last edited:
@ Fraggle,

I found that same article ........
It made no difference.


Increasing the clock rate from 100khz to 400khz will only speed up the data transfer.

The delay between sending the X co-ord and Y-co-ord is dependant on how quickly the Wire.lib executes

Even if you could increase the I2C serial clock rate to Ghz, the delay would still be the same as the wire.lib won't be runninng any faster ;)


ATB
MM
 
Hi Camvo,

It may be possible to manipulate the port dirctly (not using the Wire lib) or use the TWI hardware on the ATmega328- but that's way above my software capability.
I've had a google round to see if anyone else has done something similar needing a high speed I2C but can't find anything that would help.

All I can suggest is as before, use a parallel DAC, but I don't know how fast the digital write function operates? You may hit a similar problem:thinking:

ATB
MM
 
I have been reading the Arduino playground about the I2C bus.

And found this:

The Wire library's I/O blocks. This means that when sending or receiving over the I2C bus, your application is prevented from running until the communication is complete.
In truth the operation of the TWI hardware in your processor is interrupt-driven, so your application should be free to run at full speed while the TWI communication takes place, but this capability is not utilized by the Wire library.

I think that the library was written for general purposes. So for sending and receiving data.
For this project, only data is sent. There is no feedback over the I2C bus from other items.

So I would think that if the receiving data part(s) in the library would be deleted, that certainly would speedup things.

How ever I am like you. Do not have the knowledge of howto.

Probably my thinking is too simple.....
 
Would this work better?

It seems this is a continuos running writing to the I2C bus without waiting for the end of the communication.
 
Last edited:
It sounds like he is adressing a big problem with the Wire library that might solve your delays, i would love to see your result after this library is implemented!
 
If I only could figure out how to use the I2C.write command...

It mentions

I2c.write(address, registerAddress, *data, numberBytes)

I have no idea of how to translate this from my original code:

Code:
 //Wire.beginTransmission(DACX);  
 Wire.send(64);                    // cmd to update the DAC  
 Wire.send(valx >> 4);             // the 8 most significant bits...
 Wire.send((valx & 15) << 4);      // the 4 least significant bits...
 Wire.endTransmission();


or the code from Bionic-Badger:

Code:
  Wire.beginTransmission(DACX);
  Wire.send(0xFF & (coord->x >> 8));    // MSB
  Wire.send(0xFF & coord->x);           // LSB
  Wire.endTransmission();


I do not have experience. :thinking:
 
Last edited:





Back
Top