Sunday, May 31, 2015

3D Printer Project, Part 3: Making My Bed

I started assembling my FolgerTech printer this weekend. I plan to write up notes as I go along, linked to the steps in the construction document. Steps 1 and 2 are just introduction and there is nothing to say. It may be a good time to mention the tools I've used. I needed a ruler for measurements and for sorting out the parts, a set of hex keys (Allen keys for Brits [1]) in metric sizes, a small spanner and a pair of fine nose pliers.

Step 3 (base frame); about 45 minutes. This was all pretty simple. I used a square to help align the corners, though it's not really needed.

Step 4 (Y carriage); about 20 minutes. I was unable to get the SK8s to hold onto the chrome rods at first. They just slid through them, no matter how tight I made them. I stuck a small piece of tape round the end of each rod, and this shimmed them out enough. I used some spare Kapton tape from the kit. The bearings feel a bit rough or graunchy when they move on the chrome rods, though this eases up a little after sliding them back and forth a few times.

Step 5 (upper frame); 15 mins, or an hour if you allow for the time for me to go out to Home Depot and buy a M3 hex key. No problems.

Step 6 (heat bed); probably 90 minutes. In case you need help identifying the printed parts, it's these ones:




The M3 bolts didn't go through the printed fittings very well at first. I found it helpful to screw the bolts through them first without attaching them to the bed, in order to clear out the bolt hole in the fitting. On my first attempt at assembling the bed on the bearings, it didn't move very smoothly. It wasn't that it didn't move at all, it just had a very rough feel. At this point I had to take it apart, as I had the cable ties the wrong way round, that is the smooth side was against the "ratchet" in the ties. On the first attempt I had screwed the nylock nuts down very hard. On the second try, I didn't do this and found the bed moved a bit more smoothly. The nuts aren't actually loose, of course, just not as tight as they had been. I positioned the the chrome rods by marking the center of the beams, and then measuring 70mm in each direction from the center. Slight changes in the position of the chrome rods also helps the smoothness.

Here is an underside view, before snipping the ends off the cable ties:

And here is a video which may give an idea of what the bed movement feels and sounds like:


That's all for now. More as the week goes on.

[1] As well as some metric Allen keys I bought for this project, I have an old set in British BA sizes which are close enough for most things. The only thing I couldn't find an equivalent for was M3. Incidentally, the set I have belonged to my grandfather who, by coincidence, was called Allen.

Saturday, May 30, 2015

3D Printer Project, Part 2: An Unexpected Arrival

Today I received the package from Folger containing my kit. There was a little confusion over this: they initially posted two USPS tracking number on my account page at their website, one for the filament and one for the printer kit. One of these number was never used. On Thursday, a package of filament arrived, but using the tracking number that was supposed to be for the kit. So now I couldn't tell if the kit had been sent on a number tracking number, not sent yet, or erroneously believed to be sent by them. Emails back and forward didn't make things much clearer. Fortunately, the kit arrived today. So that's 9 days from my order, including a 3-day holiday weekend.

All the parts seem to be there. One point of confusion is that the inventory on the web site lists four 2.6 kg/cm motors and one 4.8 kg/cm one. The kit contained only three of the 2.6 kg/com motors and one 4.8 kg/cm one. I think this is because the fourth motors is already assembled into the extruder, which is listed as a separate item on the inventory. So a little needless confusion there.

A few of the parts were a little hard to identify. For example, 623ZZ and F623ZZ bearings. A combination of looking them up on a major search engine and a process of elimination worked.

Here's a few pictures of the kit. The last picture shows all of the printed parts.




Wednesday, May 27, 2015

3D Printer Project, Part 1: Decisions, decisions

This post is the link I give on forums.reprap.org. Please use the navigation at the right to see other posts on the construction, tuning and use of my printer.

A while ago, I started to think about getting a 3D printer, and I decided to do it by building one. That way, even if I ended up not using it for much, I would have the fun of going through the process of making it and figuring it out. I wanted to keep the cost down, and that really pointed me at two options. One is the wooden Printrbot simple. It no longer shows up on the Printrbot web site; here is an example of it, though I think you can get it cheaper. It's a nice design, but it was at the limit of what I wanted to pay, and I didn't like the small size of the print area, and that it has an unheated print bed.

The other possibility is a Prusa Mendel i3. This comes from a family of printers known as Reprap printers. You can read about them here. The goal is to make printers for which many of the parts can themselves be printed. The Mendel is one of the more successful design, the Prusa Mendel is an improvement on the Mendel, and i3 means the third iteration of it. You can find Prusa Mendel kits quite cheaply on the web, many from Chinese vendors. Reading around the forums concerned me a bit. There were some criticisms of the quality of the parts, both printed and not. I take this with a grain of salt; sometimes, people assume parts sourced from China are automatically not so good, and in buying electronic components over the last few months, this has not been my experience. What I was more concerned about is that most of them have laser-cut acrylic frames. Acrylic is not a great material: it's brittle and so is easily broken in transit or on drilling out a hole, and it warps under load over time. There are a very few kits with wooden frames, and one from Replikeo which has an option of a steel (they call it iron) or aluminium frame.

Then I stumbled across a US based company called Folger Tech. They has an acrylic frame kit at a good price, and just at the point I looked they started to advertise a forthcoming kit with an aluminium frame. The design deviates a bit from the Prusa Mendel i3 (and there are some people on the forums who say it shouldn't be called a Prusa), but overall it looked good and was remarkably cheap. As I started to search around more, I found a hugely long thread on the reprap forum about building the acrylic version. Several people made the point that it was more like a kit of parts than a kit, and you were on your own for figuring out how to set it up. Then more recently, Folger produced a manual to help with building and configuring it. The new aluminium model was due to go on sale just about the time I was looking and I decided on it. My kit should arrive this week, and Folger have published a new construction guide for it. Initial reports from people who have built them are positive. I've also found dealing with the company good - they've replied to emails within a day, with helpful responses.

I aim to write further posts about how it goes when I try to build and use it. I think it might be a challenge. If I suddenly go silent, it means something went horribly wrong and I've either gone down to the end of Venice pier and thrown it in the ocean, or consigned it to the Cave Of Forgotten Dreams, also known as the garage.

Friday, May 22, 2015

XMOS Parallel DAC, version 2

Here is a revised version of the parallel DAC code for the XMOS StartKit. It uses the timer and timed ports. Originally I though I would need the more complex variant they give in the manual with a timing producer + buffer + timing consumer/signal generator, but it isn't needed for this simple case. It uses the 32 bit port on pins 7-12 and 17-19 (since these are brought out to one of the GPIO headers).



/*
 * ParaDAC2.xc
 *
 *  Created on: May 21, 2015
 *      Author: David
 *  Drawing on chapter 11 of the programming guide.
 *  Limit for square waves around 3MHz (with lots of signal degradation).
 *  Limit for others around 200kHz.
 */

#include <math.h>
#include <xs1.h>

// 32 bit port, using lines 7-12 and 17-19.
port p32 = XS1_PORT_32A;

unsigned max_value = 511;
double freq = 4000000; // Approximate

#define NSAMPLES 64
unsigned samples[NSAMPLES];
#define PI 3.14156
unsigned max_samples = NSAMPLES;

enum {
    TRIANGLE, SINE, SQUARE
} signal_type = SQUARE;

void set(port p, unsigned value) {
    unsigned v = ((value & 0x1c0) << 11) | ((value & 0x3f) << 7);
    p <: v;
}

[[combinable]]
void siggen(port p, int delay_ticks) {  // ticks are 100Mhz.
    timer tmr;
    unsigned t;
    unsigned val = 0;
    unsigned sample = 0;
    tmr :> t;
    while (1) {
        select {
            // This case will event when the timer moves past (t + delay_ticks ) i.e
            // delay_ticks after when we took the timestamp t
            case tmr when timerafter (t + delay_ticks ) :> void :
                    t += delay_ticks;
                    set(p, val);
                    val = samples[sample++];
                    if (sample >= max_samples) sample = 0;
                    break;
        }
    }
}

int main () {
    // Ticks are at 100MHz, freq is in hz.
    if (signal_type == TRIANGLE) {
        for (unsigned i = 0; i < NSAMPLES; ++i) {
            samples[i] = (max_value * i + 0.5)/NSAMPLES;
        }
    } else if (signal_type == SINE) {
        for (unsigned i = 0; i < NSAMPLES; ++i) {
            samples[i] = (unsigned)(max_value * (1 + sin((float)i * 2 * PI / NSAMPLES))/2);
        }
    } else {
        max_samples = 2;
        samples[0] = 0;
        samples[1] = max_value;
    }
    unsigned delay_ticks = 100000000 / freq / max_samples;

    siggen(p32, delay_ticks);
    return 0;
}

Tuesday, May 19, 2015

XMOS Parallel DAC, version 1

I adapted the Arduino version of the parallel DAC described in the last blog post to work with the XMOS StartKit. This has a faster processor, so we should be able to crank up the rate a bit more. The resistor ladder gives us a 9 bit DAC, so I used two 4 bits ports and a 1 bit port. There is a 32 bit port, and I may use that in the next step.

Again, some sample waveforms. First, a sine wave at 10kHz. Because of how I implemented it, using delay_microseconds, the fastest sine wave I could get with a non-zero delay was 32kHz. Removing the delay statement altogether got it to 153kHz.

Next, a triangle wave at 100kHz:

Square wave at 100kHz:
You can see it is starting to round off, presumably due to capacitance. At 300kHz, this is more pronounced:
Finally, I took out all the delays and output only the top 3 bits, to see how fast I could drive it. The waveform is pretty nasty...
... but it is at around 25MHz.

For the next step, I want to use the 32 bit port, but more especially to use the real power of the XMOS chip, but setting precise times for setting the values on the ports.

The code:

#include <math.h>
#include <xs1.h>

// Ports for top 4, middle 4 and lowest bits.
port top4 = XS1_PORT_4C;
port mid4 = XS1_PORT_4D;
port bot1 = XS1_PORT_4E;

int max_value = 511;
double freq = 300000; // Approximate

#define NSAMPLES 64
int samples[NSAMPLES];
#define PI 3.14156

void set(int value) {
    top4 <: ((value >> 5) & 0x0f);
    //mid4 <: ((value >> 1) & 0x0f);
    //bot1 <: (value & 0x01);
}

int main(void) {
#if 0
    // Sampled waveform
    for (int i = 0; i < NSAMPLES; ++i) {
        // Triangle
        // samples[i] = (max_value * i)/NSAMPLES;
        // Sine
        samples[i] = (short)(max_value * (1 + sin((float)i * 2 * PI / NSAMPLES))/2);
    }
    int delay_us = (int)((1000000.0/freq)/NSAMPLES);
#else
    // Square wave
    //int delay_us = (int)((1000000.0/freq)/2);
#endif

    while (1) {
#if 0
        // Sampled waveforms
        for (int i = 0; i < NSAMPLES; ++i) {
            set(samples[i]);
            delay_microseconds(delay_us);
        }
#else
        // Square wave
        //set(0);
        top4 <: 0;
        //delay_microseconds(delay_us);
        top4 <: 0xf;
        //set(511);
        //delay_microseconds(delay_us);
#endif
    }
    return 0;
}

Sunday, May 17, 2015

A simple parallel DAC

In previous posts, I have described some experiments with a serial DAC - one that uses a SPI interface to load the values. I noted that the speed of the serial communication put a limit on how fast you can drive it. How fast could we go with a parallel DAC? There are plenty of parallel DAC ICs available, but there is also a simple alternative that can be used for doing speed tests, and that is a resistor ladder. Wikipedia has a page about them and there is another tutorial here. In the resistor ladder, you need two values, one twice the other (call these R and 2R). I used R=10k. I didn't have enough resistors lying around to use 10K throughout, so for the 2R resistors, I used a 12k and a 8.2k in series. This gave me enough for a 9 bit DAC.

For a first, experiment, I connected it up to an Arduino Uno, with the top 6 bits on pins 8-13 (port B) and the bottom three on pins 5-7 (port D). It's fastest to drive the ports directly, rather than using digitalWrite. The code is below. It has multiple options in it: sampled triangle and sine waves, and a square wave, using a delay to get the frequency approximately right; and a version which drives the ports as fast as possible.

The results are remarkably good. Here are example triangle and sine waves at about 10kHz:


For square waves, they still look OK at 100kHz:




The fastest square (-ish) wave I could get was about 250kHz:

In the speed test version, I could get it up to around 2.63MHz, though of course the signal is nothing like a square wave at the point:

By ignoring the lower order bits on port D, I pushed it up to 4.17MHz, with the waveform looking not much different to above.

Note that this isn't a practical design for a real DAC. It works in these experiments because the scope doesn't put much load on it. In a real design, you would put some buffering such as an opamp on the output, and also use resistors with tighter tolerances and more exact values

Here's the code. XMOS version to follow in another post.


#define NSAMPLES 64
short samples[NSAMPLES];
long max_value = 511;
double freq = 400000; // Approximate, due to rounding and overheads
#define PI 3.14156

void setup() {
  // Use pins 5 to 13 (low to high bit)
  DDRD = 0xe0;  // Pins 5-7
  DDRB = 0x3f;  // Pins 8-13
  for (int i = 0; i < NSAMPLES; ++i) {
    // Triangle
    //samples[i] = (max_value * i)/NSAMPLES;
    // Sine
    samples[i] = (short)(max_value * (1 + sin((float)i * 2 * PI / NSAMPLES))/2);
  }
}

void set(unsigned short n) {
  PORTD = (n & 0x07) << 5;
  PORTB = (n & 0x1f8) >> 3;
}

// True for a sampled waveform, false for a square wave.
bool sampled = false;

// Set for max speed square wave.
#define MAXSPEED

void loop() {
#ifdef MAXSPEED
    while (1) {
      PORTD = 0;
      PORTB = 0;
      PORTD = 0xe0;
      PORTB = 0x3f;
    }
#else
  if (sampled) {
    int delay_us = (int)((1000000.0/freq)/NSAMPLES);
    while (1) {
      for (int i = 0; i < NSAMPLES; ++i) {
        set(samples[i]);
        delayMicroseconds(delay_us);
      }
    }
  } else {
    int delay_us = (int)((1000000.0/freq)/2);
    while (1) {
      set(0);
      delayMicroseconds(delay_us);
      set(max_value);
      delayMicroseconds(delay_us);
    }
  }
#endif
}

Tuesday, May 12, 2015

XMOS and DACs

Some while ago, I wrote about experiments with driving a MCP4921 DAC from an Arduino. The idea was to make an arbitrary signal generator. I was able to get square waves up to about 20kHz, limited by how fast the Arduino could send sample values to it. To generate anything other than square waves, you probably need at least 50 samples per cycle, so this would limit arbitrary waveforms to 400Hz. Note that this DAC takes serial input, with two bytes per sample. So in principle, a parallel DAC might get you about 16 times this, that is 6.4kHz.

The XMOS is much faster than the Arduino. It has a clever hardware trick which allows you to control the exact timing of when values appear on its output ports. So if you can get a good frequency from it, you could also have high precision on the timing. The MCP4921 can be set to buffer the new samples and only apply them on toggling a pin called -LDAC. So you could connect this to a port of the XMOS and use it to control precisely when the samples take effect. I didn't actually do this, it's just something to note.

So how fast can you drive the DAC from XMOS? The first thing I did was to see what the maximum rate I could get on an output port is. I wrote a very simple program which just toggles a port between zero and one and loops round. This gives 14MHz.

The DAC uses the SPI interface, and XMOS provides a library for controlling it. There are synchronous and asynchronous flavors. Note that although we are only writing to the DAC (using the MOSI), you do also have to read from it (MISO). However, the SPI protocol is supposed to do both simultaneously, so I don't think this affects the speed. For the synchronous version, I got 25kHz, and for the asynchronous one 33KHz. These figures are quite disappointing, and suggest that the library has quite a lot of overhead. I found another SPI library on Github, and this got me to 128kHz. Note that at this frequency, we are well beyond the settling time of the DAC, and so even toggling the DAC value between it minimum and maximum of 4095, the output looks triangular. For the MCP4921, the settling time is 4.5us, which is close to the half-wave period at 128kHz. Note that this means we are sending bits over the serial line at 2MHz - a long way off the tight loop value of 14MHz, but in a believable range.

Here's a summary of the numbers, with estimates of the frequency at 50 samples per cycle, and for a parallel DAC, calculated as above.

Platform Max freq Max @ 50 samples Parallel @ 50 samples
Arduino 20 kHz 400 Hz 6400 kHz
XMOS sync 25 kHz 500 Hz 8000 Hz
XMOS async 33 kHz 660 Hz 10560 Hz
XMOS Github library 128 kHz 2560 Hz 40960 Hz

Thursday, May 07, 2015

XMOS Startkit

The XMOS Startkit is a development board for the (or one variant of the) XMOS multicore processor. It has two tiles, each with 8 32-bit cores giving a total of 500 MIPS. It has some very nice features such as the ability to set output ports at a specific and very precise time, and similarly to timestamp its input. The Startkit has the CPU, some LEDs and touch sensors and lots of connectors. There's a nice development studio which runs on the major platforms and connects to the board over USB. Amazingly, these board cost on $15 from Digikey in the US and can be bought for something similar in the UK. I got hold of one recently, with a plan to continue some of the DAC work I reported a few weeks back. However, as a first step I connected up a simple LED and made it flash with a variant of a program from the XMOS tutorials. It's wired up like this:

  • 0V from the J6 connector on the board to the cathode (short leg) of a small red LED.
  • Pin 1 of connector J7 to a 1k resistor.
  • The other end of the resistor to the anode (long led) of the LED.
A lower resistor would make the LED brighter, but I didn't want to risk drawing too much current. The pin operates at 3.3V.

Pin 1 of J7 is labelled D13, and it is connector to port 1F of the processor. The 1 here means this is a 1 bit port; there are wider ones as well. The code then looks like this:

#include <platform.h>
#include <xs1.h>
#include <timer.h>

port p = XS1_PORT_1A;
port p = XS1_PORT_1F;  // first pin on the connector

#define DELAY 200

int main() {
  while (1) {
     p <: 0;
     delay_milliseconds(DELAY);
     p <: 1;
     delay_milliseconds(DELAY);
  }
  return 0;
}
This is, of course, not super interesting. The main point I wanted to check is whether I could drive a LED directly.