Skip to content
Snippets Groups Projects
Sam Calisch's avatar
Sam Calisch authored
3d4b5a7a
History
Name Last commit Last update
fw
img
model
pcb
README.md

Precision coil winding

For making small electromagnetic actuators, winding coils can be a significant time sink. Industrially, this process is performed at high rates of production at tiny scales, just look at the selection of minute wire-wound inductors on Digikey. Achieving good results with small diameter wire is thus possible, but not readily achieved with a general purpose winding machine.

This project is an attempt at a customizable micro-traverse for very precisely laying coils onto a magnetic core. In the application I'm working on currently, I need a pair of coils wound onto a common core with opposite handedness. I could wind them one at a time, but the results have been cleaner and more balanced if they are wound simultaneously. Thus, I decided to make a dual wire device. I started by mimicking coil winding traverses I had seen for larger coils, which use pulleys to handle the wire.

These pulleys ended up being too bulky to get really close to my cores and didn't guide the wire precisly enough. I searched around for micro-coil winding machines, and saw that guide tubes are used to get very close to the winding surface. I decided to use this approach, but I wanted interchangeable tips for different wire diameters, adjusting the wear of the wire, etc. There are endless varieties of inexpensive dispensing tips available (e.g., from McMaster-Carr) with varying lengths, diameters, and materials but sharing a common connection type: the Luer-Lok. This connection uses a single pitch of threads and a mating taper. I re-printed my traverse with a connection for these tips, using a pair of dispensing tips matched to some 32 gauge wire.

For the motion system, I'm practicing my flexural and exact constraint design. Each tip rides on a tall hinge flexure, oriented so 3D printer filament traverses the flexure. By adjusting the lengths between the lead screw nut and the flexure and the flexure and the dispensing tip, we can very easily change the resolution and travel range of the device.

Each hinge is actuated by a NEMA11 stepper motor sitting at the back of the device. The stepper shaft is coupled to a 100 TPI microadjusting screw and the mating bushing is pressed into the 3D print. These microadjusting screws are very inexpensive for the precision they provide. I buy them from Kozak Micro. This screw comes with a hardened ball at its end to provide a repeatable contacting surface. I use this ball against the planar end of the stepper shaft. Any misalignment of the bushing, which translates into a gyration of the ball, doesn't crease any axial displacement, as the ball simply travels over the planar surface. The screw is coupled to the shaft using some thick heat shrink, which allows this radial movement, while providing a surprisingly stiff torsional coupling. The entire mechanism is preloaded (e.g., the ball against the shaft end) by a tension spring.

This micro-traverse is designed to be used interactively, as a user presses a foot pedal to rotate the magnetic core and wind the wire. Because of this, the traverse trajectory needs to be synced to an unpredictable rotational trajectory. Luckily, the coil winder we have provides a 100 pulse per revolution encoder output via the RJ45 cable. I designed the electronics so you just unplug this cable from the macro-traverse and plug it into the micro-traverse.

I trigger a Timer/Counter in the Xmega on each rising and falling edge of the pulse train from the coil winder. The value of this timer then represents the current position, onto which we map the traverse trajectory, calculated using wire diameter, number of wraps per layer, and total number of layers:

#include <avr/io.h>
#include <stdio.h>
#include <avr/sleep.h>
#include <util/delay.h>


#define CLOCK_DIV_bm  TC_CLKSEL_DIV64_gc
#define CLOCK_DIV     64

#define LED1_mask PIN2_bm
#define LED2_mask PIN3_bm
#define encoder_mask PIN0_bm

//#define wire_d 202000 //32 AWG, magnet wire diameter in nanometers
#define wire_d 180000 //33 AWG, magnet wire diameter in nanometers
//#define wire_d 160000 //34 AWG, magnet wire diameter in nanometers
#define n_ticks_per_rev 200 //number of encoder edges per revolution
#define nm_per_step 1270 //full steps for now
#define n_layers 10 //how many layers total
#define n_wraps_per_layer 8 //how many coil wraps per layer before reversing

uint32_t max_x = n_wraps_per_layer*wire_d; //maximum distance traversed
uint32_t n_ticks_per_layer = n_ticks_per_rev * n_wraps_per_layer; //how many encoder ticks per layer

//state variables
uint32_t n_layers_done = 0;
uint32_t n_wraps_this_layer = 0;
uint32_t n_ticks_this_rev = 0;
uint32_t x_desired = 0; //where we want the tip, measured in nanometers

void update_desired_x(){
	//function to implement wraps and layering.
	//ticks: current number of encoder ticks as measured from the starting point.  
	// one tick is an edge of the encoder signal
	n_layers_done = TCC0.CNT / n_ticks_per_layer;
	n_wraps_this_layer = (TCC0.CNT % n_ticks_per_layer) / n_ticks_per_rev;
	n_ticks_this_rev = (TCC0.CNT % n_ticks_per_rev);
	x_desired = n_wraps_this_layer * wire_d + (n_ticks_this_rev * wire_d) / n_ticks_per_rev;
	if (n_layers_done % 2 == 1){ 
	  x_desired = max_x - x_desired; //flip odd layer directions
	} 
}

int main(void) {
   // set up clock
   OSC.CTRL = OSC_RC32MEN_bm; // enable 32MHz clock
   while (!(OSC.STATUS & OSC_RC32MRDY_bm)); // wait for clock to be ready
   CCP = CCP_IOREG_gc; // enable protected register change
   CLK.CTRL = CLK_SCLKSEL_RC32M_gc; // switch to 32MHz clock
   
  PORTE.DIRSET = LED1_mask | LED2_mask;

  // Set up Event system channel zero to trigger on both edges of encoder (PC0) signal.
  //PORTC.PIN0CTRL = PORT_ISC_BOTHEDGES_gc;
  PORTC.PIN0CTRL = PORT_ISC_BOTHEDGES_gc;
  PORTC.DIRCLR = PIN0_bm;
  EVSYS.CH0MUX = EVSYS_CHMUX_PORTC_PIN0_gc;  //let's use event channel zero for encoder input  
  // Set up TCC0 to take event system channel zero as clock source
  TCC0.CTRLA = ( TCC0.CTRLA & ~TC0_CLKSEL_gm ) | TC_CLKSEL_EVCH0_gc; //configure EVENT Channel zero as clock source
  //TCC0.CTRLD = ( TCC0.CTRLD & ~( TC0_EVSEL_gm | TC0_EVACT_gm ) ) | TC_EVSEL_CH0_gc | TC_EVACT_CAPT_gc;
  TCC0.PER = 0xFFFF; //TODO: set this to the end of winding?

  //todo: cascade two timers so our count of ticks can have 32-bits intead of 16?  
  //if 200 ticks/rev, and 100 revs in a coil, we are dangerously close to overflow.

  while (1) {
    //test with LED
  	if (TCC0.CNT % 200 == 0){ PORTE.OUTSET = LED1_mask; }
  	if (TCC0.CNT % 200 == 100){ PORTE.OUTCLR = LED1_mask; }
    }
}